feat: fix frontend
This commit is contained in:
parent
7cf5070288
commit
236364a10f
|
|
@ -0,0 +1,31 @@
|
|||
# 构建阶段
|
||||
FROM node:18-alpine AS build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 复制package.json和package-lock.json
|
||||
COPY package*.json ./
|
||||
|
||||
# 安装依赖
|
||||
RUN npm ci --only=production
|
||||
|
||||
# 复制源代码
|
||||
COPY . .
|
||||
|
||||
# 构建应用
|
||||
RUN npm run build
|
||||
|
||||
# 生产阶段
|
||||
FROM nginx:alpine
|
||||
|
||||
# 复制自定义nginx配置
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
# 复制构建结果到nginx目录
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
|
||||
# 暴露端口
|
||||
EXPOSE 80
|
||||
|
||||
# 启动nginx
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
user nginx;
|
||||
worker_processes auto;
|
||||
error_log /var/log/nginx/error.log;
|
||||
pid /run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
types_hash_max_size 2048;
|
||||
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# 开启gzip压缩
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1k;
|
||||
gzip_buffers 4 16k;
|
||||
gzip_http_version 1.0;
|
||||
gzip_comp_level 6;
|
||||
gzip_types text/plain text/css text/javascript application/json application/javascript application/xml+rss application/atom+xml image/svg+xml;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
|
||||
# 处理单页应用的路由
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# API代理
|
||||
location /api/ {
|
||||
proxy_pass http://backend:8080/api/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# WebSocket代理
|
||||
location /ws/ {
|
||||
proxy_pass http://backend:8080/ws/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# 静态资源缓存
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# 安全配置
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -7,32 +7,33 @@
|
|||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"build:prod": "vite build --mode production",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
|
||||
"format": "prettier --write src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.3.0",
|
||||
"vue": "^3.4.0",
|
||||
"vue-router": "^4.2.0",
|
||||
"vuex": "^4.0.2",
|
||||
"element-plus": "^2.3.0",
|
||||
"element-plus": "^2.8.0",
|
||||
"echarts": "^5.4.0",
|
||||
"axios": "^1.4.0",
|
||||
"dayjs": "^1.11.0",
|
||||
"dayjs": "^1.11.10",
|
||||
"lodash": "^4.17.21",
|
||||
"sockjs-client": "^1.6.1",
|
||||
"stompjs": "^2.3.3",
|
||||
"@element-plus/icons-vue": "^2.1.0"
|
||||
"@element-plus/icons-vue": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.2.0",
|
||||
"vite": "^4.3.0",
|
||||
"@vitejs/plugin-vue": "^5.0.0",
|
||||
"vite": "^5.0.0",
|
||||
"eslint": "^8.39.0",
|
||||
"eslint-plugin-vue": "^9.11.0",
|
||||
"prettier": "^2.8.8",
|
||||
"sass": "^1.62.0",
|
||||
"unplugin-auto-import": "^0.16.0",
|
||||
"unplugin-vue-components": "^0.25.0"
|
||||
"unplugin-auto-import": "^0.17.0",
|
||||
"unplugin-vue-components": "^0.26.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import request from '@/utils/request'
|
|||
// 获取最新的市场分析数据
|
||||
export function getLatestMarketAnalysis() {
|
||||
return request({
|
||||
url: '/api/market/latest',
|
||||
url: '/market/latest',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ export function getLatestMarketAnalysis() {
|
|||
// 获取指定日期范围的市场分析数据
|
||||
export function getMarketAnalysisByDateRange(startDate, endDate) {
|
||||
return request({
|
||||
url: '/api/market/range',
|
||||
url: '/market/range',
|
||||
method: 'get',
|
||||
params: {
|
||||
startDate,
|
||||
|
|
@ -27,7 +27,7 @@ export function getMarketAnalysisByDateRange(startDate, endDate) {
|
|||
// 获取最近N天的市场分析数据
|
||||
export function getRecentMarketAnalysis(days) {
|
||||
return request({
|
||||
url: `/api/market/recent/${days}`,
|
||||
url: `/market/recent/${days}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import request from '@/utils/request'
|
|||
// 获取实时股票数据
|
||||
export function getRealtimeStockData() {
|
||||
return request({
|
||||
url: '/api/stock/realtime',
|
||||
url: '/stock/realtime',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ export function getRealtimeStockData() {
|
|||
// 获取股票历史数据
|
||||
export function getStockHistory(stockCode, startDate, endDate) {
|
||||
return request({
|
||||
url: `/api/stock/history/${stockCode}`,
|
||||
url: `/stock/history/${stockCode}`,
|
||||
method: 'get',
|
||||
params: {
|
||||
startDate,
|
||||
|
|
@ -27,7 +27,7 @@ export function getStockHistory(stockCode, startDate, endDate) {
|
|||
// 获取涨幅排行榜
|
||||
export function getGrowthRanking(limit = 10) {
|
||||
return request({
|
||||
url: '/api/stock/ranking/growth',
|
||||
url: '/stock/ranking/growth',
|
||||
method: 'get',
|
||||
params: { limit }
|
||||
})
|
||||
|
|
@ -36,7 +36,7 @@ export function getGrowthRanking(limit = 10) {
|
|||
// 获取市值排行榜
|
||||
export function getMarketCapRanking(limit = 10) {
|
||||
return request({
|
||||
url: '/api/stock/ranking/market-cap',
|
||||
url: '/stock/ranking/market-cap',
|
||||
method: 'get',
|
||||
params: { limit }
|
||||
})
|
||||
|
|
@ -45,7 +45,7 @@ export function getMarketCapRanking(limit = 10) {
|
|||
// 获取成交量排行榜
|
||||
export function getVolumeRanking(limit = 10) {
|
||||
return request({
|
||||
url: '/api/stock/ranking/volume',
|
||||
url: '/stock/ranking/volume',
|
||||
method: 'get',
|
||||
params: { limit }
|
||||
})
|
||||
|
|
@ -54,7 +54,7 @@ export function getVolumeRanking(limit = 10) {
|
|||
// 获取股票趋势分析
|
||||
export function getStockTrend(stockCode, days = 30) {
|
||||
return request({
|
||||
url: `/api/stock/trend/${stockCode}`,
|
||||
url: `/stock/trend/${stockCode}`,
|
||||
method: 'get',
|
||||
params: { days }
|
||||
})
|
||||
|
|
@ -63,7 +63,7 @@ export function getStockTrend(stockCode, days = 30) {
|
|||
// 获取市场综合分析
|
||||
export function getMarketAnalysis() {
|
||||
return request({
|
||||
url: '/api/stock/market-analysis',
|
||||
url: '/stock/market-analysis',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -71,7 +71,7 @@ export function getMarketAnalysis() {
|
|||
// 获取股票预测数据
|
||||
export function getStockPrediction(stockCode, days = 7) {
|
||||
return request({
|
||||
url: `/api/stock/prediction/${stockCode}`,
|
||||
url: `/stock/prediction/${stockCode}`,
|
||||
method: 'get',
|
||||
params: { days }
|
||||
})
|
||||
|
|
@ -80,7 +80,7 @@ export function getStockPrediction(stockCode, days = 7) {
|
|||
// 获取股票详情
|
||||
export function getStockDetail(stockCode) {
|
||||
return request({
|
||||
url: `/api/stock/detail/${stockCode}`,
|
||||
url: `/stock/detail/${stockCode}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
@ -88,7 +88,7 @@ export function getStockDetail(stockCode) {
|
|||
// 搜索股票
|
||||
export function searchStocks(keyword) {
|
||||
return request({
|
||||
url: '/api/stock/search',
|
||||
url: '/stock/search',
|
||||
method: 'get',
|
||||
params: { keyword }
|
||||
})
|
||||
|
|
@ -97,7 +97,7 @@ export function searchStocks(keyword) {
|
|||
// 保存股票数据
|
||||
export function saveStockData(stockData) {
|
||||
return request({
|
||||
url: '/api/stock/save',
|
||||
url: '/stock/save',
|
||||
method: 'post',
|
||||
data: stockData
|
||||
})
|
||||
|
|
@ -106,7 +106,7 @@ export function saveStockData(stockData) {
|
|||
// 批量保存股票数据
|
||||
export function batchSaveStockData(stockDataList) {
|
||||
return request({
|
||||
url: '/api/stock/batch-save',
|
||||
url: '/stock/batch-save',
|
||||
method: 'post',
|
||||
data: stockDataList
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,10 +1,29 @@
|
|||
import axios from 'axios'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
// 根据构建模式获取API基础地址
|
||||
const getBaseURL = () => {
|
||||
// 首先尝试从环境变量获取
|
||||
if (import.meta.env.VITE_APP_BASE_API) {
|
||||
return import.meta.env.VITE_APP_BASE_API
|
||||
}
|
||||
|
||||
// 如果是开发环境且在localhost:3000,使用代理
|
||||
if (import.meta.env.DEV && window.location.hostname === 'localhost') {
|
||||
return '/api'
|
||||
}
|
||||
|
||||
// 生产环境或其他情况下的默认配置
|
||||
return import.meta.env.PROD
|
||||
? 'http://192.168.1.36:8080/api' // 生产环境
|
||||
: 'http://localhost:8080/api' // 开发环境
|
||||
}
|
||||
|
||||
// 创建axios实例
|
||||
const service = axios.create({
|
||||
baseURL: import.meta.env.VITE_APP_BASE_API || 'http://localhost:8080', // 后端API地址
|
||||
timeout: 15000 // 请求超时时间
|
||||
baseURL: getBaseURL(),
|
||||
timeout: 15000, // 请求超时时间
|
||||
withCredentials: false // 允许跨域请求携带凭证
|
||||
})
|
||||
|
||||
// 请求拦截器
|
||||
|
|
|
|||
|
|
@ -42,9 +42,9 @@
|
|||
</el-table-column>
|
||||
<el-table-column prop="stockCode" label="股票代码" width="100" />
|
||||
<el-table-column prop="stockName" label="股票名称" width="150" />
|
||||
<el-table-column prop="currentPrice" label="当前价格" width="100">
|
||||
<el-table-column prop="closePrice" label="当前价格" width="100">
|
||||
<template #default="scope">
|
||||
¥{{ scope.row.currentPrice?.toFixed(2) }}
|
||||
¥{{ scope.row.closePrice?.toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="changePercent" label="涨跌幅" width="120">
|
||||
|
|
@ -118,9 +118,9 @@
|
|||
</el-table-column>
|
||||
<el-table-column prop="stockCode" label="股票代码" width="100" />
|
||||
<el-table-column prop="stockName" label="股票名称" width="150" />
|
||||
<el-table-column prop="currentPrice" label="当前价格" width="100">
|
||||
<el-table-column prop="closePrice" label="当前价格" width="100">
|
||||
<template #default="scope">
|
||||
¥{{ scope.row.currentPrice?.toFixed(2) }}
|
||||
¥{{ scope.row.closePrice?.toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="marketCap" label="市值" width="150">
|
||||
|
|
@ -189,9 +189,9 @@
|
|||
</el-table-column>
|
||||
<el-table-column prop="stockCode" label="股票代码" width="100" />
|
||||
<el-table-column prop="stockName" label="股票名称" width="150" />
|
||||
<el-table-column prop="currentPrice" label="当前价格" width="100">
|
||||
<el-table-column prop="closePrice" label="当前价格" width="100">
|
||||
<template #default="scope">
|
||||
¥{{ scope.row.currentPrice?.toFixed(2) }}
|
||||
¥{{ scope.row.closePrice?.toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="volume" label="成交量" width="150">
|
||||
|
|
|
|||
|
|
@ -56,9 +56,9 @@
|
|||
<el-table :data="searchResults" stripe @row-click="viewStockDetail">
|
||||
<el-table-column prop="stockCode" label="股票代码" width="120" />
|
||||
<el-table-column prop="stockName" label="股票名称" width="200" />
|
||||
<el-table-column prop="currentPrice" label="当前价格" width="120">
|
||||
<el-table-column prop="closePrice" label="当前价格" width="120">
|
||||
<template #default="scope">
|
||||
¥{{ scope.row.currentPrice?.toFixed(2) }}
|
||||
¥{{ scope.row.closePrice?.toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="changePercent" label="涨跌幅" width="120">
|
||||
|
|
@ -155,9 +155,9 @@
|
|||
<el-table :data="paginatedRealtimeData" v-loading="realtimeLoading" stripe>
|
||||
<el-table-column prop="stockCode" label="股票代码" width="100" />
|
||||
<el-table-column prop="stockName" label="股票名称" width="150" />
|
||||
<el-table-column prop="currentPrice" label="当前价格" width="100">
|
||||
<el-table-column prop="closePrice" label="当前价格" width="100">
|
||||
<template #default="scope">
|
||||
¥{{ scope.row.currentPrice?.toFixed(2) }}
|
||||
¥{{ scope.row.closePrice?.toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="changePercent" label="涨跌幅" width="100">
|
||||
|
|
|
|||
|
|
@ -6,7 +6,20 @@ import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
|||
import { resolve } from 'path'
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
export default defineConfig({
|
||||
export default defineConfig(({ mode }) => {
|
||||
// 根据构建模式确定后端服务器地址
|
||||
const getBackendUrl = () => {
|
||||
if (mode === 'production') {
|
||||
return 'http://192.168.1.36:8080' // 生产环境后端地址
|
||||
}
|
||||
return 'http://localhost:8080' // 开发环境后端地址
|
||||
}
|
||||
|
||||
const backendUrl = getBackendUrl()
|
||||
const apiBaseUrl = `${backendUrl}/api`
|
||||
console.log(`构建模式: ${mode}, 后端地址: ${backendUrl}, API基础地址: ${apiBaseUrl}`)
|
||||
|
||||
return {
|
||||
plugins: [
|
||||
vue(),
|
||||
AutoImport({
|
||||
|
|
@ -18,12 +31,14 @@ export default defineConfig({
|
|||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
},
|
||||
},
|
||||
define: {
|
||||
// 为了兼容一些依赖包
|
||||
global: 'globalThis',
|
||||
// 定义环境变量
|
||||
__VITE_APP_BASE_API__: JSON.stringify(apiBaseUrl),
|
||||
},
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
|
|
@ -31,12 +46,12 @@ export default defineConfig({
|
|||
open: true,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8080',
|
||||
target: backendUrl,
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
'/ws': {
|
||||
target: 'ws://localhost:8080',
|
||||
target: backendUrl.replace('http', 'ws'),
|
||||
ws: true,
|
||||
changeOrigin: true,
|
||||
},
|
||||
|
|
@ -68,7 +83,20 @@ export default defineConfig({
|
|||
'element-plus',
|
||||
'@element-plus/icons-vue',
|
||||
'echarts',
|
||||
'dayjs'
|
||||
'dayjs',
|
||||
'dayjs/plugin/customParseFormat',
|
||||
'dayjs/plugin/advancedFormat',
|
||||
'dayjs/plugin/localeData',
|
||||
'dayjs/plugin/weekOfYear',
|
||||
'dayjs/plugin/weekYear',
|
||||
'dayjs/plugin/dayOfYear',
|
||||
'dayjs/plugin/isSameOrAfter',
|
||||
'dayjs/plugin/isSameOrBefore',
|
||||
'dayjs/plugin/utc',
|
||||
'dayjs/plugin/timezone'
|
||||
],
|
||||
exclude: [],
|
||||
force: true
|
||||
},
|
||||
}
|
||||
})
|
||||
Loading…
Reference in New Issue