更改
This commit is contained in:
+8
-153
@@ -1,160 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<router-view />
|
||||||
<el-card class="box-card">
|
|
||||||
<template #header>
|
|
||||||
<div class="card-header">
|
|
||||||
<span>📈 股票涨停回落分析工具</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<div class="filter-bar">
|
|
||||||
<el-form :inline="true">
|
|
||||||
<el-form-item label="开始日期">
|
|
||||||
<el-date-picker
|
|
||||||
v-model="startDate"
|
|
||||||
type="date"
|
|
||||||
placeholder="请选择"
|
|
||||||
format="YYYY-MM-DD"
|
|
||||||
value-format="YYYY-MM-DD"
|
|
||||||
:disabled-date="disabledStartDate"
|
|
||||||
/>
|
|
||||||
</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="disabledEndDate"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item>
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
@click="handleSearch"
|
|
||||||
:loading="loading"
|
|
||||||
icon="Search"
|
|
||||||
>
|
|
||||||
查询分析
|
|
||||||
</el-button>
|
|
||||||
<el-button @click="resetFilter">重置</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<el-table
|
|
||||||
:data="tableData"
|
|
||||||
style="width: 100%; margin-top: 10px"
|
|
||||||
v-loading="loading"
|
|
||||||
stripe
|
|
||||||
:default-sort="{ prop: 'drop_pct', order: 'descending' }"
|
|
||||||
>
|
|
||||||
<el-table-column prop="code" label="代码" width="100" />
|
|
||||||
<el-table-column prop="name" label="名称" width="120" />
|
|
||||||
<el-table-column prop="day_open" label="当日开盘" />
|
|
||||||
<el-table-column prop="max_high" label="区间最高" sortable />
|
|
||||||
<el-table-column prop="max_date" label="最高日期" width="120" />
|
|
||||||
<el-table-column prop="post_min_low" label="见顶后最低" />
|
|
||||||
|
|
||||||
<el-table-column prop="drop_pct" label="最大回落幅%" sortable>
|
|
||||||
<template #default="scope">
|
|
||||||
<b style="color: #f56c6c">{{ scope.row.drop_pct?.toFixed(2) }}%</b>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
<el-table-column prop="rebound_pct" label="低位反弹%" sortable>
|
|
||||||
<template #default="scope">
|
|
||||||
<span
|
|
||||||
:style="{
|
|
||||||
color: scope.row.rebound_pct > 0 ? '#67c23a' : '#909399',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
{{ scope.row.rebound_pct?.toFixed(2) }}%
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from "vue";
|
// 全局入口,无需额外逻辑
|
||||||
import axios from "axios";
|
|
||||||
import { ElMessage } from "element-plus";
|
|
||||||
|
|
||||||
// 1. 将范围数组拆分为两个独立状态
|
|
||||||
const startDate = ref("2024-09-24");
|
|
||||||
const endDate = ref("2026-04-23");
|
|
||||||
const tableData = ref([]);
|
|
||||||
const loading = ref(false);
|
|
||||||
|
|
||||||
// 2. 逻辑约束:开始日期不能晚于结束日期
|
|
||||||
const disabledStartDate = (time) => {
|
|
||||||
if (!endDate.value) return false;
|
|
||||||
return time.getTime() > new Date(endDate.value).getTime();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 3. 逻辑约束:结束日期不能早于开始日期
|
|
||||||
const disabledEndDate = (time) => {
|
|
||||||
if (!startDate.value) return false;
|
|
||||||
return time.getTime() < new Date(startDate.value).getTime();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 重置功能
|
|
||||||
const resetFilter = () => {
|
|
||||||
startDate.value = "";
|
|
||||||
endDate.value = "";
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSearch = async () => {
|
|
||||||
// 校验非空
|
|
||||||
if (!startDate.value || !endDate.value) {
|
|
||||||
ElMessage.warning("请完整选择开始和结束日期");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
loading.value = true;
|
|
||||||
try {
|
|
||||||
const response = await axios.get(
|
|
||||||
"http://localhost:18080/api/v1/stock/analyze",
|
|
||||||
{
|
|
||||||
params: {
|
|
||||||
start_date: startDate.value,
|
|
||||||
end_date: endDate.value,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
tableData.value = response.data;
|
|
||||||
ElMessage.success(`查询成功,共计 ${tableData.value.length} 条记录`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
ElMessage.error("获取数据失败,请检查后端服务");
|
|
||||||
} finally {
|
|
||||||
loading.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style>
|
||||||
.container {
|
body {
|
||||||
padding: 20px;
|
margin: 0;
|
||||||
background-color: #f5f7fa;
|
background-color: #f0f2f5;
|
||||||
min-height: 100vh;
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
}
|
}
|
||||||
.card-header {
|
</style>
|
||||||
font-size: 18px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.filter-bar {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
/* 调整表单间距 */
|
|
||||||
:deep(.el-form-item) {
|
|
||||||
margin-right: 20px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
const request = axios.create({
|
||||||
|
baseURL: 'http://localhost:18080/api/v1', // 指向你的 Go 后端
|
||||||
|
timeout: 10000
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取涨停回落分析数据
|
||||||
|
export const getAnalyzeData = (params) => {
|
||||||
|
return request.get('/stock/analyze', { params })
|
||||||
|
}
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 8.5 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 496 B |
@@ -1,95 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import viteLogo from '../assets/vite.svg'
|
|
||||||
import heroImg from '../assets/hero.png'
|
|
||||||
import vueLogo from '../assets/vue.svg'
|
|
||||||
|
|
||||||
const count = ref(0)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<section id="center">
|
|
||||||
<div class="hero">
|
|
||||||
<img :src="heroImg" class="base" width="170" height="179" alt="" />
|
|
||||||
<img :src="vueLogo" class="framework" alt="Vue logo" />
|
|
||||||
<img :src="viteLogo" class="vite" alt="Vite logo" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h1>Get started</h1>
|
|
||||||
<p>Edit <code>src/App.vue</code> and save to test <code>HMR</code></p>
|
|
||||||
</div>
|
|
||||||
<button type="button" class="counter" @click="count++">
|
|
||||||
Count is {{ count }}
|
|
||||||
</button>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<div class="ticks"></div>
|
|
||||||
|
|
||||||
<section id="next-steps">
|
|
||||||
<div id="docs">
|
|
||||||
<svg class="icon" role="presentation" aria-hidden="true">
|
|
||||||
<use href="/icons.svg#documentation-icon"></use>
|
|
||||||
</svg>
|
|
||||||
<h2>Documentation</h2>
|
|
||||||
<p>Your questions, answered</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<a href="https://vite.dev/" target="_blank">
|
|
||||||
<img class="logo" :src="viteLogo" alt="" />
|
|
||||||
Explore Vite
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://vuejs.org/" target="_blank">
|
|
||||||
<img class="button-icon" :src="vueLogo" alt="" />
|
|
||||||
Learn more
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div id="social">
|
|
||||||
<svg class="icon" role="presentation" aria-hidden="true">
|
|
||||||
<use href="/icons.svg#social-icon"></use>
|
|
||||||
</svg>
|
|
||||||
<h2>Connect with us</h2>
|
|
||||||
<p>Join the Vite community</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<a href="https://github.com/vitejs/vite" target="_blank">
|
|
||||||
<svg class="button-icon" role="presentation" aria-hidden="true">
|
|
||||||
<use href="/icons.svg#github-icon"></use>
|
|
||||||
</svg>
|
|
||||||
GitHub
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://chat.vite.dev/" target="_blank">
|
|
||||||
<svg class="button-icon" role="presentation" aria-hidden="true">
|
|
||||||
<use href="/icons.svg#discord-icon"></use>
|
|
||||||
</svg>
|
|
||||||
Discord
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://x.com/vite_js" target="_blank">
|
|
||||||
<svg class="button-icon" role="presentation" aria-hidden="true">
|
|
||||||
<use href="/icons.svg#x-icon"></use>
|
|
||||||
</svg>
|
|
||||||
X.com
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://bsky.app/profile/vite.dev" target="_blank">
|
|
||||||
<svg class="button-icon" role="presentation" aria-hidden="true">
|
|
||||||
<use href="/icons.svg#bluesky-icon"></use>
|
|
||||||
</svg>
|
|
||||||
Bluesky
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<div class="ticks"></div>
|
|
||||||
<section id="spacer"></section>
|
|
||||||
</template>
|
|
||||||
+10
-1
@@ -1,8 +1,17 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
import ElementPlus from 'element-plus'
|
import ElementPlus from 'element-plus'
|
||||||
import 'element-plus/dist/index.css'
|
import 'element-plus/dist/index.css'
|
||||||
|
// 引入中文语言包
|
||||||
|
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
app.use(ElementPlus)
|
|
||||||
|
app.use(router)
|
||||||
|
// 全局配置 Element Plus 使用中文
|
||||||
|
app.use(ElementPlus, {
|
||||||
|
locale: zhCn,
|
||||||
|
})
|
||||||
|
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
redirect: '/stock/analyze'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/stock/analyze',
|
||||||
|
name: 'StockAnalyze',
|
||||||
|
component: () => import('../views/stock/Analyze.vue')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
<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-date-picker
|
||||||
|
v-model="startDate"
|
||||||
|
type="date"
|
||||||
|
placeholder="请选择"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
style="width: 140px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="filter-item">
|
||||||
|
<span class="label-text">结束日期:</span>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="endDate"
|
||||||
|
type="date"
|
||||||
|
placeholder="请选择"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
style="width: 140px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="fetchData"
|
||||||
|
:loading="loading"
|
||||||
|
style="margin-left: 12px"
|
||||||
|
>
|
||||||
|
查询数据
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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' }"
|
||||||
|
>
|
||||||
|
<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 prop="drop_pct" label="最大回落幅度%" sortable align="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<span class="text-success">{{ scope.row.drop_pct.toFixed(2) }}%</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column prop="rebound_pct" label="低位反弹%" sortable align="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<span :class="scope.row.rebound_pct > 0 ? 'text-danger' : 'text-gray'">
|
||||||
|
{{ scope.row.rebound_pct.toFixed(2) }}%
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { getAnalyzeData } from '@/api/stock'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
// 修改默认日期为 2026-04-22
|
||||||
|
const startDate = ref('2026-04-22')
|
||||||
|
const endDate = ref('2026-04-23')
|
||||||
|
const tableData = ref([])
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
if (!startDate.value || !endDate.value) {
|
||||||
|
return ElMessage.warning('请选择日期')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new Date(startDate.value) > new Date(endDate.value)) {
|
||||||
|
return ElMessage.warning('选定日期不能晚于结束日期')
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await getAnalyzeData({
|
||||||
|
start_date: startDate.value,
|
||||||
|
end_date: endDate.value
|
||||||
|
})
|
||||||
|
tableData.value = res.data
|
||||||
|
ElMessage.success(`加载成功: ${res.data.length} 条数据`)
|
||||||
|
} catch (err) {
|
||||||
|
ElMessage.error('数据获取失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</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 {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
.filter-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.label-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-success {
|
||||||
|
color: #67c23a;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-danger {
|
||||||
|
color: #f56c6c;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-gray {
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table .cell) {
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+10
-2
@@ -1,7 +1,15 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
// https://vite.dev/config/
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [vue()],
|
plugins: [vue()],
|
||||||
})
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': path.resolve(__dirname, './src')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
port: 5173
|
||||||
|
}
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user