修改
This commit is contained in:
Generated
+32
@@ -10,6 +10,7 @@
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.2",
|
||||
"axios": "^1.15.2",
|
||||
"echarts": "^6.0.0",
|
||||
"element-plus": "^2.13.7",
|
||||
"vue": "^3.5.32",
|
||||
"vue-router": "^4.6.4"
|
||||
@@ -765,6 +766,22 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/echarts": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/echarts/-/echarts-6.0.0.tgz",
|
||||
"integrity": "sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": "2.3.0",
|
||||
"zrender": "6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/echarts/node_modules/tslib": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
|
||||
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/element-plus": {
|
||||
"version": "2.13.7",
|
||||
"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.13.7.tgz",
|
||||
@@ -1642,6 +1659,21 @@
|
||||
"peerDependencies": {
|
||||
"vue": "^3.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/zrender": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/zrender/-/zrender-6.0.0.tgz",
|
||||
"integrity": "sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"tslib": "2.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/zrender/node_modules/tslib": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
|
||||
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
|
||||
"license": "0BSD"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.2",
|
||||
"axios": "^1.15.2",
|
||||
"echarts": "^6.0.0",
|
||||
"element-plus": "^2.13.7",
|
||||
"vue": "^3.5.32",
|
||||
"vue-router": "^4.6.4"
|
||||
|
||||
+1
-9
@@ -2,14 +2,6 @@
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// 全局入口,无需额外逻辑
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
background-color: #f0f2f5;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
body { margin: 0; background-color: #f5f7fa; }
|
||||
</style>
|
||||
@@ -0,0 +1,4 @@
|
||||
import request from '@/utils/request'
|
||||
export function getTradeCalendar() {
|
||||
return request({ url: '/common/trade-calendar', method: 'get' })
|
||||
}
|
||||
+14
-8
@@ -1,11 +1,17 @@
|
||||
import axios from 'axios'
|
||||
import request from '@/utils/request'
|
||||
|
||||
const request = axios.create({
|
||||
baseURL: 'http://localhost:18080/api/v1', // 指向你的 Go 后端
|
||||
timeout: 10000
|
||||
export function getAnalyzeData(data) {
|
||||
return request({
|
||||
url: '/stock/analyze',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 新增:获取历史K线
|
||||
export function getKlineData(code) {
|
||||
return request({
|
||||
url: `/stock/kline/${code}`,
|
||||
method: 'get'
|
||||
})
|
||||
|
||||
// 获取涨停回落分析数据
|
||||
export const getAnalyzeData = (params) => {
|
||||
return request.get('/stock/analyze', { params })
|
||||
}
|
||||
+13
-2
@@ -3,13 +3,24 @@ import App from './App.vue'
|
||||
import router from './router'
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
// 引入中文语言包
|
||||
|
||||
// 引入 Element Plus 中文语言包
|
||||
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
|
||||
|
||||
// 引入图标
|
||||
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
// 全局注册 Element Plus 图标组件
|
||||
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||||
app.component(key, component)
|
||||
}
|
||||
|
||||
// 注册路由
|
||||
app.use(router)
|
||||
// 全局配置 Element Plus 使用中文
|
||||
|
||||
// 注册 Element Plus 并配置中文国际化
|
||||
app.use(ElementPlus, {
|
||||
locale: zhCn,
|
||||
})
|
||||
|
||||
+2
-1
@@ -8,7 +8,8 @@ const routes = [
|
||||
{
|
||||
path: '/stock/analyze',
|
||||
name: 'StockAnalyze',
|
||||
component: () => import('../views/stock/Analyze.vue')
|
||||
component: () => import('@/views/stock/Analyze.vue'),
|
||||
meta: { title: '涨停回落分析' }
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import axios from 'axios'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const service = axios.create({
|
||||
baseURL: '/api/v1',
|
||||
timeout: 15000
|
||||
})
|
||||
|
||||
service.interceptors.response.use(
|
||||
response => response.data,
|
||||
error => {
|
||||
ElMessage.error(error.response?.data?.error || '请求失败')
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default service
|
||||
+281
-128
@@ -1,178 +1,331 @@
|
||||
<template>
|
||||
<div class="analyze-container">
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<div class="header-title">📊 涨停后回落分析</div>
|
||||
</template>
|
||||
|
||||
<div class="filter-section">
|
||||
<div class="filter-item">
|
||||
<span class="label-text">选定日期:</span>
|
||||
<el-row :gutter="15" class="top-row">
|
||||
<el-col :span="12">
|
||||
<div class="glass-card filter-container">
|
||||
<el-form :inline="true" class="pure-form">
|
||||
<el-form-item label="起算">
|
||||
<el-date-picker
|
||||
v-model="startDate"
|
||||
type="date"
|
||||
placeholder="请选择"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
:disabled-date="disableDates"
|
||||
:editable="false"
|
||||
placeholder="点击选择"
|
||||
style="width: 140px"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="filter-item">
|
||||
<span class="label-text">结束日期:</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="截止">
|
||||
<el-date-picker
|
||||
v-model="endDate"
|
||||
type="date"
|
||||
placeholder="请选择"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
:disabled-date="disableDates"
|
||||
:editable="false"
|
||||
placeholder="点击选择"
|
||||
style="width: 140px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleQuery" :loading="loading" plain>开始分析</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="fetchData"
|
||||
:loading="loading"
|
||||
style="margin-left: 12px"
|
||||
>
|
||||
查询数据
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="glass-card info-container">
|
||||
<div class="stat-group">
|
||||
<div class="stat-box">
|
||||
<span class="label">样本总数</span>
|
||||
<span class="value">{{ tableData.length }}</span>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="stat-box">
|
||||
<span class="label">强势占比</span>
|
||||
<span class="value" :style="{ color: parseFloat(strongPercent) > 50 ? '#f56c6c' : '#409EFF' }">
|
||||
{{ strongPercent }}%
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-card class="main-card" shadow="never">
|
||||
<el-table
|
||||
:data="tableData"
|
||||
v-loading="loading"
|
||||
stripe
|
||||
style="width: 100%; margin-top: 20px"
|
||||
:header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
|
||||
:default-sort="{ prop: 'drop_pct', order: 'descending' }"
|
||||
border
|
||||
height="calc(100vh - 180px)"
|
||||
style="width: 100%"
|
||||
:header-cell-style="{ background: '#f8f9fb', color: '#444', textAlign: 'center' }"
|
||||
>
|
||||
<el-table-column prop="code" label="代码" width="100" />
|
||||
<el-table-column prop="name" label="名称" width="120" />
|
||||
<el-table-column prop="day_open" label="当日开盘" align="right" />
|
||||
<el-table-column
|
||||
prop="max_high"
|
||||
label="区间最高"
|
||||
sortable
|
||||
align="right"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="max_date"
|
||||
label="最高日期"
|
||||
width="120"
|
||||
align="center"
|
||||
/>
|
||||
<el-table-column prop="post_min_low" label="见顶后最低" align="right" />
|
||||
<el-table-column type="index" label="ID" width="55" fixed="left" align="center" />
|
||||
|
||||
<el-table-column
|
||||
prop="drop_pct"
|
||||
label="最大回落幅度%"
|
||||
sortable
|
||||
align="right"
|
||||
>
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.drop_pct !== null" class="text-success">
|
||||
{{ scope.row.drop_pct.toFixed(2) }}%
|
||||
</span>
|
||||
<span v-else class="text-gray">--</span>
|
||||
<el-table-column label="股票详情 / 7日趋势" width="260" fixed="left">
|
||||
<template #default="{ row }">
|
||||
<div class="stock-combined-box">
|
||||
<div class="meta-info clickable" @click="openKlineDialog(row)" title="点击查看历史K线">
|
||||
<span class="c-code">{{ row.code.split('.')[0] }}</span>
|
||||
<span class="c-name">{{ row.name }}</span>
|
||||
</div>
|
||||
<div class="sparkline-container">
|
||||
<svg width="110" height="34" class="sparkline-svg">
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#409EFF"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
:d="generateSparklinePath(row.close_prices)"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="rebound_pct"
|
||||
label="低位反弹%"
|
||||
sortable
|
||||
align="right"
|
||||
>
|
||||
<template #default="scope">
|
||||
<span
|
||||
v-if="scope.row.rebound_pct !== null"
|
||||
:class="scope.row.rebound_pct > 0 ? 'text-danger' : 'text-gray'"
|
||||
>
|
||||
{{ scope.row.rebound_pct.toFixed(2) }}%
|
||||
</span>
|
||||
<span v-else class="text-gray">--</span>
|
||||
<el-table-column label="价格参考" align="center">
|
||||
<el-table-column prop="day_high" label="涨停价" width="100" align="center">
|
||||
<template #default="{row}">
|
||||
<span class="num-text">{{ row.day_high.toFixed(2) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="overall_max_high" label="区间最高" width="100" align="center">
|
||||
<template #default="{row}">
|
||||
<span class="num-text bold">{{ row.overall_max_high.toFixed(2) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="overall_max_date" label="最高日期" width="110" align="center" />
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="形态核心分析" align="center">
|
||||
<el-table-column prop="drop_pct" label="最大回落%" sortable width="130" align="center">
|
||||
<template #default="{ row }">
|
||||
<div v-if="row.drop_pct !== null" class="status-tag tag-green">
|
||||
{{ row.drop_pct.toFixed(2) }}%
|
||||
</div>
|
||||
<div v-else class="status-tag tag-blue">强势不破</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="rebound_pct" label="涨幅 / 反弹%" sortable min-width="130" align="center">
|
||||
<template #default="{ row }">
|
||||
<div :class="['status-tag', row.drop_pct === null ? 'tag-orange' : 'tag-red']">
|
||||
{{ row.rebound_pct.toFixed(2) }}%
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="dialogTitle"
|
||||
width="85%"
|
||||
top="5vh"
|
||||
destroy-on-close
|
||||
draggable
|
||||
>
|
||||
<div id="kline-chart" style="width: 100%; height: 550px;"></div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { getAnalyzeData } from "@/api/stock";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { ref, computed, onMounted, nextTick } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import dayjs from 'dayjs'
|
||||
import { getTradeCalendar } from '@/api/common'
|
||||
import { getAnalyzeData, getKlineData } from '@/api/stock'
|
||||
|
||||
const startDate = ref("2026-03-02");
|
||||
const endDate = ref("2026-04-23");
|
||||
const tableData = ref([]);
|
||||
const loading = ref(false);
|
||||
// 响应式数据
|
||||
const startDate = ref('2026-04-22')
|
||||
const endDate = ref('')
|
||||
const tableData = ref([])
|
||||
const loading = ref(false)
|
||||
const openDatesSet = ref(new Set())
|
||||
const maxTradeDate = ref('')
|
||||
|
||||
const fetchData = async () => {
|
||||
if (!startDate.value || !endDate.value) {
|
||||
return ElMessage.warning("请选择日期");
|
||||
}
|
||||
// 弹窗与图表变量
|
||||
const dialogVisible = ref(false)
|
||||
const dialogTitle = ref('')
|
||||
let chartInstance = null
|
||||
|
||||
if (new Date(startDate.value) > new Date(endDate.value)) {
|
||||
return ElMessage.warning("选定日期不能晚于结束日期");
|
||||
}
|
||||
// 计算属性:统计强势占比
|
||||
const strongPercent = computed(() => {
|
||||
if (!tableData.value.length) return '0.0'
|
||||
const count = tableData.value.filter(i => i.drop_pct === null).length
|
||||
return ((count / tableData.value.length) * 100).toFixed(1)
|
||||
})
|
||||
|
||||
loading.value = true;
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const res = await getAnalyzeData({
|
||||
start_date: startDate.value,
|
||||
end_date: endDate.value,
|
||||
});
|
||||
tableData.value = res.data;
|
||||
ElMessage.success(`加载成功: ${res.data.length} 条数据`);
|
||||
const res = await getTradeCalendar()
|
||||
openDatesSet.value = new Set(res.open_dates)
|
||||
maxTradeDate.value = res.max_date
|
||||
if (!endDate.value) endDate.value = res.max_date
|
||||
|
||||
// 局域网访问提示:建议 axios baseURL 使用 `http://${window.location.hostname}:8080/api/v1`
|
||||
console.log("当前主机访问地址:", window.location.hostname)
|
||||
} catch (err) {
|
||||
ElMessage.error("数据获取失败");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
console.error("初始化数据失败")
|
||||
}
|
||||
})
|
||||
|
||||
// 日期禁用逻辑
|
||||
const disableDates = (time) => {
|
||||
const d = dayjs(time).format('YYYY-MM-DD')
|
||||
if (maxTradeDate.value && d > maxTradeDate.value) return true
|
||||
return openDatesSet.value.size > 0 ? !openDatesSet.value.has(d) : false
|
||||
}
|
||||
|
||||
// 分析查询
|
||||
const handleQuery = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getAnalyzeData({ start_date: startDate.value, end_date: endDate.value })
|
||||
tableData.value = res
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 生成 Sparkline 路径
|
||||
const generateSparklinePath = (prices) => {
|
||||
if (!prices || prices.length < 2) return ""
|
||||
const min = Math.min(...prices), max = Math.max(...prices), range = max - min || 1
|
||||
const w = 110, h = 34, p = 4
|
||||
const points = prices.map((val, i) => ({
|
||||
x: (i / (prices.length - 1)) * w,
|
||||
y: h - p - ((val - min) / range) * (h - 2 * p)
|
||||
}))
|
||||
return `M ${points[0].x} ${points[0].y} ` + points.slice(1).map(pt => `L ${pt.x} ${pt.y}`).join(" ")
|
||||
}
|
||||
|
||||
// 渲染 K 线图
|
||||
const openKlineDialog = async (row) => {
|
||||
dialogTitle.value = `${row.name} (${row.code}) - 自 2024-09-24 以来走势`
|
||||
dialogVisible.value = true
|
||||
|
||||
try {
|
||||
const rawData = await getKlineData(row.code)
|
||||
|
||||
await nextTick()
|
||||
const chartDom = document.getElementById('kline-chart')
|
||||
if (!chartDom) return
|
||||
|
||||
if (chartInstance) chartInstance.dispose()
|
||||
chartInstance = echarts.init(chartDom)
|
||||
|
||||
// 数据提取 (匹配后端 JSON 标签)
|
||||
const dates = rawData.map(d => dayjs(d.trade_date).format('YYYY-MM-DD'))
|
||||
const values = rawData.map(d => [
|
||||
d.open,
|
||||
d.close,
|
||||
d.low,
|
||||
d.high,
|
||||
d.pct_chg // 传递给 Tooltip
|
||||
])
|
||||
|
||||
const option = {
|
||||
animation: false,
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross' },
|
||||
formatter: function (params) {
|
||||
const d = params[0]
|
||||
const pct = d.value[5] || 0
|
||||
const color = pct >= 0 ? '#f56c6c' : '#67c23a'
|
||||
return `
|
||||
<div style="font-size:12px; min-width:120px;">
|
||||
<b>${d.name}</b><hr style="margin:5px 0;border:none;border-top:1px solid #eee"/>
|
||||
涨跌幅: <span style="color:${color};font-weight:bold;">${pct > 0 ? '+' : ''}${pct.toFixed(2)}%</span><br/>
|
||||
开盘价: ${d.value[1].toFixed(2)}<br/>
|
||||
收盘价: ${d.value[2].toFixed(2)}<br/>
|
||||
最低价: ${d.value[3].toFixed(2)}<br/>
|
||||
最高价: ${d.value[4].toFixed(2)}
|
||||
</div>`
|
||||
}
|
||||
},
|
||||
grid: { left: '60', right: '30', bottom: '10%', top: '40' },
|
||||
xAxis: { type: 'category', data: dates, scale: true, boundaryGap: false },
|
||||
yAxis: { scale: true, splitArea: { show: true } },
|
||||
dataZoom: [
|
||||
{ type: 'inside', start: 80, end: 100 },
|
||||
{ show: true, type: 'slider', top: '92%' }
|
||||
],
|
||||
series: [{
|
||||
name: '日K',
|
||||
type: 'candlestick',
|
||||
data: values,
|
||||
itemStyle: {
|
||||
color: '#f56c6c', color0: '#67c23a',
|
||||
borderColor: '#f56c6c', borderColor0: '#67c23a'
|
||||
}
|
||||
}]
|
||||
}
|
||||
chartInstance.setOption(option)
|
||||
} catch (err) {
|
||||
console.error("加载K线详情失败", err)
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.analyze-container {
|
||||
padding: 24px;
|
||||
background-color: #f0f2f5;
|
||||
min-height: calc(100vh - 48px);
|
||||
}
|
||||
.header-title {
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
color: #303133;
|
||||
}
|
||||
.filter-section {
|
||||
.analyze-container { padding: 20px; background-color: #f4f7f9; min-height: 100vh; }
|
||||
|
||||
.top-row { margin-bottom: 20px; }
|
||||
.glass-card {
|
||||
background: #fff;
|
||||
height: 70px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
padding: 0 20px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.03);
|
||||
}
|
||||
.filter-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.label-text {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.text-success {
|
||||
color: #67c23a;
|
||||
|
||||
.stat-group { display: flex; width: 100%; justify-content: space-around; align-items: center; }
|
||||
.stat-box { display: flex; flex-direction: column; align-items: center; }
|
||||
.stat-box .label { font-size: 12px; color: #999; margin-bottom: 4px; }
|
||||
.stat-box .value { font-size: 24px; font-weight: 800; color: #333; }
|
||||
.divider { width: 1px; height: 30px; background: #eee; }
|
||||
|
||||
.stock-combined-box { display: flex; align-items: center; justify-content: space-between; width: 100%; }
|
||||
.meta-info { display: flex; flex-direction: column; }
|
||||
.meta-info.clickable { cursor: pointer; }
|
||||
/* 鼠标滑过代码名称时,给出视觉反馈 */
|
||||
.meta-info.clickable:hover .c-name { color: #409EFF; text-decoration: underline; }
|
||||
|
||||
.c-code { font-family: 'JetBrains Mono', monospace; font-weight: bold; color: #409EFF; font-size: 14px; }
|
||||
.c-name { font-size: 13px; color: #666; transition: color 0.2s; }
|
||||
.sparkline-container { border-left: 1px dashed #eee; padding-left: 10px; margin-left: 10px; }
|
||||
|
||||
/* 日历光标强制为手型 */
|
||||
:deep(.el-input__wrapper) { cursor: pointer !important; }
|
||||
:deep(.el-input__inner) { cursor: pointer !important; user-select: none; }
|
||||
|
||||
/* 弹窗标题栏可移动提示 */
|
||||
:deep(.el-dialog__header) { cursor: move; margin-right: 0; padding-bottom: 15px; border-bottom: 1px solid #f5f5f5; }
|
||||
|
||||
.status-tag {
|
||||
display: inline-block;
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
min-width: 85px;
|
||||
}
|
||||
.text-danger {
|
||||
color: #f56c6c;
|
||||
font-weight: bold;
|
||||
}
|
||||
.text-gray {
|
||||
color: #909399;
|
||||
}
|
||||
:deep(.el-table .cell) {
|
||||
padding: 0 12px;
|
||||
}
|
||||
.tag-green { background: #f0f9eb; color: #67c23a; }
|
||||
.tag-blue { background: #ecf5ff; color: #409EFF; }
|
||||
.tag-orange { background: #fdf6ec; color: #e6a23c; }
|
||||
.tag-red { background: #fef0f0; color: #f56c6c; }
|
||||
|
||||
.num-text { font-family: 'JetBrains Mono', monospace; }
|
||||
.bold { font-weight: bold; }
|
||||
.main-card { border-radius: 12px; border: none; }
|
||||
|
||||
:deep(.el-form-item) { margin-bottom: 0; }
|
||||
</style>
|
||||
+9
-4
@@ -5,11 +5,16 @@ import path from 'path'
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src')
|
||||
}
|
||||
alias: { '@': path.resolve(__dirname, 'src') }
|
||||
},
|
||||
server: {
|
||||
port: 5173
|
||||
host: '0.0.0.0', // 监听所有地址,包括局域网 IP
|
||||
port: 5173, // 你的端口号
|
||||
proxy: {
|
||||
'/api/v1': {
|
||||
target: 'http://localhost:18080',
|
||||
changeOrigin: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user