mirror of
https://github.com/ArvinLovegood/go-stock.git
synced 2025-07-19 00:00:09 +08:00
feat(frontend):添加股票资金趋势功能
- 在前端添加了股票资金趋势页面组件 - 在后端实现了获取股票资金趋势数据的接口 - 优化了前端界面布局,增加了资金趋势按钮
This commit is contained in:
parent
4fd5cbf8e6
commit
2a274db7ae
5
app.go
5
app.go
@ -1131,3 +1131,8 @@ func (a *App) GetMoneyRankSina(sort string) []map[string]any {
|
|||||||
res := data.NewMarketNewsApi().GetMoneyRankSina(sort)
|
res := data.NewMarketNewsApi().GetMoneyRankSina(sort)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) GetStockMoneyTrendByDay(stockCode string, days int) []map[string]any {
|
||||||
|
res := data.NewMarketNewsApi().GetStockMoneyTrendByDay(stockCode, days)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
@ -275,3 +275,21 @@ func (m MarketNewsApi) GetMoneyRankSina(sort string) []map[string]any {
|
|||||||
}
|
}
|
||||||
return *res
|
return *res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m MarketNewsApi) GetStockMoneyTrendByDay(stockCode string, days int) []map[string]any {
|
||||||
|
url := fmt.Sprintf("http://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/MoneyFlow.ssl_qsfx_zjlrqs?page=1&num=%d&sort=opendate&asc=0&daima=%s", days, stockCode)
|
||||||
|
|
||||||
|
response, _ := resty.New().SetTimeout(time.Duration(5)*time.Second).R().
|
||||||
|
SetHeader("Host", "vip.stock.finance.sina.com.cn").
|
||||||
|
SetHeader("Referer", "https://finance.sina.com.cn").
|
||||||
|
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.60").Get(url)
|
||||||
|
js := string(response.Body())
|
||||||
|
res := &[]map[string]any{}
|
||||||
|
err := json.Unmarshal([]byte(js), &res)
|
||||||
|
if err != nil {
|
||||||
|
logger.SugaredLogger.Error(err)
|
||||||
|
return *res
|
||||||
|
}
|
||||||
|
return *res
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -49,3 +49,10 @@ func TestGetMoneyRankSina(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetStockMoneyTrendByDay(t *testing.T) {
|
||||||
|
res := NewMarketNewsApi().GetStockMoneyTrendByDay("sh600438", 120)
|
||||||
|
for i, re := range res {
|
||||||
|
logger.SugaredLogger.Debugf("key: %+v, value: %+v", i, re)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -446,7 +446,7 @@ onBeforeMount(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
contentStyle.value = "max-height: calc(90vh);overflow: hidden"
|
contentStyle.value = "max-height: calc(92vh);overflow: hidden"
|
||||||
GetConfig().then((res) => {
|
GetConfig().then((res) => {
|
||||||
if (res.enableNews) {
|
if (res.enableNews) {
|
||||||
enableNews.value = true
|
enableNews.value = true
|
||||||
@ -488,6 +488,7 @@ onMounted(() => {
|
|||||||
</n-tag>
|
</n-tag>
|
||||||
</n-marquee>
|
</n-marquee>
|
||||||
<n-scrollbar :style="contentStyle">
|
<n-scrollbar :style="contentStyle">
|
||||||
|
<n-skeleton v-if="loading" height="calc(100vh)" />
|
||||||
<RouterView/>
|
<RouterView/>
|
||||||
</n-scrollbar>
|
</n-scrollbar>
|
||||||
</n-spin>
|
</n-spin>
|
||||||
|
279
frontend/src/components/moneyTrend.vue
Normal file
279
frontend/src/components/moneyTrend.vue
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {onMounted, ref} from "vue";
|
||||||
|
import {GetStockMoneyTrendByDay} from "../../wailsjs/go/main/App";
|
||||||
|
import * as echarts from "echarts";
|
||||||
|
|
||||||
|
const {code, name, darkTheme, days, chartHeight} = defineProps({
|
||||||
|
code: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
days: {
|
||||||
|
type: Number,
|
||||||
|
default: 14
|
||||||
|
},
|
||||||
|
chartHeight: {
|
||||||
|
type: Number,
|
||||||
|
default: 500
|
||||||
|
},
|
||||||
|
darkTheme: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const LineChartRef = ref(null);
|
||||||
|
|
||||||
|
onMounted(
|
||||||
|
() => {
|
||||||
|
handleLine(code, days)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const handleLine = (code, days) => {
|
||||||
|
GetStockMoneyTrendByDay(code, days).then(result => {
|
||||||
|
console.log("GetStockMoneyTrendByDay", result)
|
||||||
|
const chart = echarts.init(LineChartRef.value);
|
||||||
|
const categoryData = [];
|
||||||
|
const netamount_values = [];
|
||||||
|
const trades_values = [];
|
||||||
|
let volume = []
|
||||||
|
|
||||||
|
let min = 0
|
||||||
|
let max = 0
|
||||||
|
for (let i = 0; i < result.length; i++) {
|
||||||
|
let resultElement = result[i]
|
||||||
|
categoryData.push(resultElement.opendate)
|
||||||
|
let netamount = (resultElement.netamount / 10000).toFixed(2);
|
||||||
|
netamount_values.push(netamount)
|
||||||
|
let price = Number(resultElement.trade);
|
||||||
|
trades_values.push(price)
|
||||||
|
|
||||||
|
if (min === 0 || min > price) {
|
||||||
|
min = price
|
||||||
|
}
|
||||||
|
if (max < price) {
|
||||||
|
max = price
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i > 0) {
|
||||||
|
let b=(result[i].netamount - result[i - 1].netamount)/10000
|
||||||
|
volume.push(b)
|
||||||
|
} else {
|
||||||
|
volume.push(result[i].netamount/10000)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
console.log("trades_values", trades_values)
|
||||||
|
|
||||||
|
let option = {
|
||||||
|
darkMode: darkTheme,
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'cross',
|
||||||
|
animation: false,
|
||||||
|
label: {
|
||||||
|
backgroundColor: '#505765'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisPointer: {
|
||||||
|
link: [
|
||||||
|
{
|
||||||
|
xAxisIndex: 'all'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
label: {
|
||||||
|
backgroundColor: '#888'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: true,
|
||||||
|
data: ['当日净流入','累计净流入', '股价'],
|
||||||
|
selected: {
|
||||||
|
'当日净流入': true,
|
||||||
|
'累计净流入': false,
|
||||||
|
'股价': true,
|
||||||
|
},
|
||||||
|
//orient: 'vertical',
|
||||||
|
textStyle: {
|
||||||
|
color: darkTheme ? '#ccc' : '#456'
|
||||||
|
},
|
||||||
|
right: 150,
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
show: true,
|
||||||
|
realtime: true,
|
||||||
|
start: 70,
|
||||||
|
end: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
realtime: true,
|
||||||
|
start: 86,
|
||||||
|
end: 100
|
||||||
|
}
|
||||||
|
],
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: categoryData
|
||||||
|
},
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
name: '净流入',
|
||||||
|
type: 'value',
|
||||||
|
axisLine: {
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '股价',
|
||||||
|
type: 'value',
|
||||||
|
min: min - 1,
|
||||||
|
max: max + 1,
|
||||||
|
minInterval: 0.01,
|
||||||
|
axisLine: {
|
||||||
|
show: true
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '当日净流入',
|
||||||
|
data: netamount_values,
|
||||||
|
smooth: true,
|
||||||
|
showSymbol: false,
|
||||||
|
lineStyle: {
|
||||||
|
width: 2
|
||||||
|
},
|
||||||
|
markPoint: {
|
||||||
|
symbol: 'arrow',
|
||||||
|
symbolRotate: 90,
|
||||||
|
symbolSize: [10, 20],
|
||||||
|
symbolOffset: [10, 0],
|
||||||
|
itemStyle: {
|
||||||
|
color: '#FC290D'
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
position: 'right',
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{type: 'max', name: 'Max'},
|
||||||
|
{type: 'min', name: 'Min'}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
markLine: {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
type: 'average',
|
||||||
|
name: 'Average',
|
||||||
|
lineStyle: {
|
||||||
|
color: '#ff001e',
|
||||||
|
width: 0.5
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
type: 'line'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '累计净流入',
|
||||||
|
data: volume,
|
||||||
|
smooth: true,
|
||||||
|
showSymbol: false,
|
||||||
|
lineStyle: {
|
||||||
|
width: 2
|
||||||
|
},
|
||||||
|
markPoint: {
|
||||||
|
symbol: 'arrow',
|
||||||
|
symbolRotate: 90,
|
||||||
|
symbolSize: [10, 20],
|
||||||
|
symbolOffset: [10, 0],
|
||||||
|
itemStyle: {
|
||||||
|
color: '#FC290D'
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
position: 'right',
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{type: 'max', name: 'Max'},
|
||||||
|
{type: 'min', name: 'Min'}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
markLine: {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
type: 'average',
|
||||||
|
name: 'Average',
|
||||||
|
lineStyle: {
|
||||||
|
color: '#ff001e',
|
||||||
|
width: 0.5
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
type: 'line'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '股价',
|
||||||
|
type: 'line',
|
||||||
|
data: trades_values,
|
||||||
|
yAxisIndex: 1,
|
||||||
|
smooth: true,
|
||||||
|
showSymbol: false,
|
||||||
|
lineStyle: {
|
||||||
|
width: 3
|
||||||
|
},
|
||||||
|
markPoint: {
|
||||||
|
symbol: 'arrow',
|
||||||
|
symbolRotate: 90,
|
||||||
|
symbolSize: [10, 20],
|
||||||
|
symbolOffset: [10, 0],
|
||||||
|
itemStyle: {
|
||||||
|
color: '#0940f3'
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
position: 'right',
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{type: 'max', name: 'Max'},
|
||||||
|
{type: 'min', name: 'Min'}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
markLine: {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
type: 'average',
|
||||||
|
name: 'Average',
|
||||||
|
lineStyle: {
|
||||||
|
color: '#0940f3',
|
||||||
|
width: 0.5
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
chart.setOption(option);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="LineChartRef" style="width: 100%;height: auto;" :style="{height:chartHeight+'px'}"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -63,6 +63,7 @@ import {asBlob} from 'html-docx-js-typescript';
|
|||||||
import vueDanmaku from 'vue3-danmaku'
|
import vueDanmaku from 'vue3-danmaku'
|
||||||
import {keys, pad, padStart} from "lodash";
|
import {keys, pad, padStart} from "lodash";
|
||||||
import {useRoute, useRouter} from 'vue-router'
|
import {useRoute, useRouter} from 'vue-router'
|
||||||
|
import MoneyTrend from "./moneyTrend.vue";
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
@ -98,6 +99,7 @@ const modalShow = ref(false)
|
|||||||
const modalShow2 = ref(false)
|
const modalShow2 = ref(false)
|
||||||
const modalShow3 = ref(false)
|
const modalShow3 = ref(false)
|
||||||
const modalShow4 = ref(false)
|
const modalShow4 = ref(false)
|
||||||
|
const modalShow5 = ref(false)
|
||||||
const addBTN = ref(true)
|
const addBTN = ref(true)
|
||||||
const formModel = ref({
|
const formModel = ref({
|
||||||
name: "",
|
name: "",
|
||||||
@ -1241,6 +1243,12 @@ function handleKLine(){
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
function showMoney(code,name){
|
||||||
|
data.code=code
|
||||||
|
data.name=name
|
||||||
|
modalShow5.value=true
|
||||||
|
}
|
||||||
|
|
||||||
function showK(code,name){
|
function showK(code,name){
|
||||||
data.code=code
|
data.code=code
|
||||||
data.name=name
|
data.name=name
|
||||||
@ -1710,6 +1718,8 @@ function delStockGroup(code,name,groupId){
|
|||||||
<n-button size="tiny" type="info" @click="setStock(result['股票代码'],result['股票名称'])"> 成本 </n-button>
|
<n-button size="tiny" type="info" @click="setStock(result['股票代码'],result['股票名称'])"> 成本 </n-button>
|
||||||
<n-button size="tiny" type="success" @click="showFenshi(result['股票代码'],result['股票名称'])"> 分时 </n-button>
|
<n-button size="tiny" type="success" @click="showFenshi(result['股票代码'],result['股票名称'])"> 分时 </n-button>
|
||||||
<n-button size="tiny" type="error" @click="showK(result['股票代码'],result['股票名称'])"> 日K </n-button>
|
<n-button size="tiny" type="error" @click="showK(result['股票代码'],result['股票名称'])"> 日K </n-button>
|
||||||
|
<n-button size="tiny" type="error" v-if="result['买一报价']>0" @click="showMoney(result['股票代码'],result['股票名称'])"> 资金 </n-button>
|
||||||
|
|
||||||
<n-button size="tiny" type="warning" @click="search(result['股票代码'],result['股票名称'])"> 详情 </n-button>
|
<n-button size="tiny" type="warning" @click="search(result['股票代码'],result['股票名称'])"> 详情 </n-button>
|
||||||
<n-dropdown trigger="click" :options="groupList" key-field="ID" label-field="name" @select="(groupId) => AddStockGroupInfo(groupId,result['股票代码'],result['股票名称'])">
|
<n-dropdown trigger="click" :options="groupList" key-field="ID" label-field="name" @select="(groupId) => AddStockGroupInfo(groupId,result['股票代码'],result['股票名称'])">
|
||||||
<n-button type="success" size="tiny">设置分组</n-button>
|
<n-button type="success" size="tiny">设置分组</n-button>
|
||||||
@ -1821,6 +1831,8 @@ function delStockGroup(code,name,groupId){
|
|||||||
<n-button size="tiny" type="info" @click="setStock(result['股票代码'],result['股票名称'])"> 成本 </n-button>
|
<n-button size="tiny" type="info" @click="setStock(result['股票代码'],result['股票名称'])"> 成本 </n-button>
|
||||||
<n-button size="tiny" type="success" @click="showFenshi(result['股票代码'],result['股票名称'],result.changePercent)"> 分时 </n-button>
|
<n-button size="tiny" type="success" @click="showFenshi(result['股票代码'],result['股票名称'],result.changePercent)"> 分时 </n-button>
|
||||||
<n-button size="tiny" type="error" @click="showK(result['股票代码'],result['股票名称'])"> 日K </n-button>
|
<n-button size="tiny" type="error" @click="showK(result['股票代码'],result['股票名称'])"> 日K </n-button>
|
||||||
|
<n-button size="tiny" type="error" v-if="result['买一报价']>0" @click="showMoney(result['股票代码'],result['股票名称'])"> 资金 </n-button>
|
||||||
|
|
||||||
<n-button size="tiny" type="warning" @click="search(result['股票代码'],result['股票名称'])"> 详情 </n-button>
|
<n-button size="tiny" type="warning" @click="search(result['股票代码'],result['股票名称'])"> 详情 </n-button>
|
||||||
<n-dropdown trigger="click" :options="groupList" key-field="ID" label-field="name" @select="(groupId) => AddStockGroupInfo(groupId,result['股票代码'],result['股票名称'])">
|
<n-dropdown trigger="click" :options="groupList" key-field="ID" label-field="name" @select="(groupId) => AddStockGroupInfo(groupId,result['股票代码'],result['股票名称'])">
|
||||||
<n-button type="success" size="tiny">设置分组</n-button>
|
<n-button type="success" size="tiny">设置分组</n-button>
|
||||||
@ -1988,6 +2000,9 @@ function delStockGroup(code,name,groupId){
|
|||||||
</n-flex>
|
</n-flex>
|
||||||
</template>
|
</template>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
|
<n-modal v-model:show="modalShow5" :title="data.name+'资金趋势'" style="width: 1000px" :preset="'card'" @after-enter="getMoneyTrendLine">
|
||||||
|
<money-trend :code="data.code" :name="data.name" :days="365" :dark-theme="data.darkTheme" :chart-height="500"></money-trend>
|
||||||
|
</n-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
2
frontend/wailsjs/go/main/App.d.ts
vendored
2
frontend/wailsjs/go/main/App.d.ts
vendored
@ -49,6 +49,8 @@ export function GetStockList(arg1:string):Promise<Array<data.StockBasic>>;
|
|||||||
|
|
||||||
export function GetStockMinutePriceLineData(arg1:string,arg2:string):Promise<Record<string, any>>;
|
export function GetStockMinutePriceLineData(arg1:string,arg2:string):Promise<Record<string, any>>;
|
||||||
|
|
||||||
|
export function GetStockMoneyTrendByDay(arg1:string,arg2:number):Promise<Array<Record<string, any>>>;
|
||||||
|
|
||||||
export function GetTelegraphList(arg1:string):Promise<any>;
|
export function GetTelegraphList(arg1:string):Promise<any>;
|
||||||
|
|
||||||
export function GetVersionInfo():Promise<models.VersionInfo>;
|
export function GetVersionInfo():Promise<models.VersionInfo>;
|
||||||
|
@ -94,6 +94,10 @@ export function GetStockMinutePriceLineData(arg1, arg2) {
|
|||||||
return window['go']['main']['App']['GetStockMinutePriceLineData'](arg1, arg2);
|
return window['go']['main']['App']['GetStockMinutePriceLineData'](arg1, arg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function GetStockMoneyTrendByDay(arg1, arg2) {
|
||||||
|
return window['go']['main']['App']['GetStockMoneyTrendByDay'](arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
export function GetTelegraphList(arg1) {
|
export function GetTelegraphList(arg1) {
|
||||||
return window['go']['main']['App']['GetTelegraphList'](arg1);
|
return window['go']['main']['App']['GetTelegraphList'](arg1);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user