feat(stock): 添加股票排序功能- 新增 SetStockSort 函数用于设置股票排序

- 在前端增加股票排序的输入和显示逻辑
- 修改后端数据库,增加股票排序字段
- 优化股票列表的渲染,支持按排序值进行排序
This commit is contained in:
spark 2025-01-07 13:29:16 +08:00
parent 9a41560bee
commit daa29b37a5
5 changed files with 121 additions and 34 deletions

4
app.go
View File

@ -113,7 +113,9 @@ func (a *App) SetCostPriceAndVolume(stockCode string, price float64, volume int6
func (a *App) SetAlarmChangePercent(val, alarmPrice float64, stockCode string) string { func (a *App) SetAlarmChangePercent(val, alarmPrice float64, stockCode string) string {
return data.NewStockDataApi().SetAlarmChangePercent(val, alarmPrice, stockCode) return data.NewStockDataApi().SetAlarmChangePercent(val, alarmPrice, stockCode)
} }
func (a *App) SetStockSort(sort int64, stockCode string) {
data.NewStockDataApi().SetStockSort(sort, stockCode)
}
func (a *App) SendDingDingMessage(message string, stockCode string) string { func (a *App) SendDingDingMessage(message string, stockCode string) string {
ttl, _ := a.cache.TTL([]byte(stockCode)) ttl, _ := a.cache.TTL([]byte(stockCode))
logger.SugaredLogger.Infof("stockCode %s ttl:%d", stockCode, ttl) logger.SugaredLogger.Infof("stockCode %s ttl:%d", stockCode, ttl)

View File

@ -287,12 +287,15 @@ func (receiver StockDataApi) Follow(stockCode string) string {
} }
price, _ := convertor.ToFloat(stockInfo.Price) price, _ := convertor.ToFloat(stockInfo.Price)
db.Dao.Model(&FollowedStock{}).FirstOrCreate(&FollowedStock{ db.Dao.Model(&FollowedStock{}).FirstOrCreate(&FollowedStock{
StockCode: stockCode, StockCode: stockCode,
Name: stockInfo.Name, Name: stockInfo.Name,
Price: price, Price: price,
Time: time.Now(), Time: time.Now(),
ChangePercent: 0, ChangePercent: 0,
PriceChange: 0, PriceChange: 0,
Sort: 0,
AlarmChangePercent: 3,
AlarmPrice: price + 1,
}, &FollowedStock{StockCode: stockCode}) }, &FollowedStock{StockCode: stockCode})
return "关注成功" return "关注成功"
} }
@ -323,6 +326,10 @@ func (receiver StockDataApi) SetAlarmChangePercent(val, alarmPrice float64, stoc
return "设置成功" return "设置成功"
} }
func (receiver StockDataApi) SetStockSort(sort int64, stockCode string) {
db.Dao.Model(&FollowedStock{}).Where("stock_code = ?", stockCode).Update("sort", sort)
}
func (receiver StockDataApi) GetFollowList() []FollowedStock { func (receiver StockDataApi) GetFollowList() []FollowedStock {
var result []FollowedStock var result []FollowedStock
db.Dao.Model(&FollowedStock{}).Order("sort asc,time desc").Find(&result) db.Dao.Model(&FollowedStock{}).Order("sort asc,time desc").Find(&result)

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import {onBeforeMount, onBeforeUnmount, onMounted, reactive, ref} from 'vue' import {computed, onBeforeMount, onBeforeUnmount, onMounted, reactive, ref} from 'vue'
import { import {
Follow, Follow,
GetFollowList, GetFollowList,
@ -7,11 +7,11 @@ import {
Greet, Greet,
SendDingDingMessage, SendDingDingMessage,
SetAlarmChangePercent, SetAlarmChangePercent,
SetCostPriceAndVolume, SetCostPriceAndVolume, SetStockSort,
UnFollow UnFollow
} from '../../wailsjs/go/main/App' } from '../../wailsjs/go/main/App'
import {NButton, NFlex, NForm, NFormItem, NInputNumber, NText, useMessage, useModal} from 'naive-ui' import {NButton, NFlex, NForm, NFormItem, NInputNumber, NText, useMessage, useModal} from 'naive-ui'
import {EventsOn, WindowFullscreen, WindowUnfullscreen} from '../../wailsjs/runtime' import {EventsOn, WindowFullscreen, WindowReload, WindowUnfullscreen} from '../../wailsjs/runtime'
import {Add, Search} from '@vicons/ionicons5' import {Add, Search} from '@vicons/ionicons5'
const message = useMessage() const message = useMessage()
@ -34,6 +34,7 @@ const formModel = ref({
volume: 0, volume: 0,
alarm: 0, alarm: 0,
alarmPrice:0, alarmPrice:0,
sort:999,
}) })
const data = reactive({ const data = reactive({
@ -45,6 +46,15 @@ const data = reactive({
fullscreen: false, fullscreen: false,
}) })
const sortedResults = computed(() => {
console.log("computed",sortedResults.value)
const sortedKeys =Object.keys(results.value).sort();
const sortedObject = {};
sortedKeys.forEach(key => {
sortedObject[key] = results.value[key];
});
return sortedObject
});
onBeforeMount(()=>{ onBeforeMount(()=>{
GetStockList("").then(result => { GetStockList("").then(result => {
@ -95,17 +105,19 @@ EventsOn("showSearch",(data)=>{
EventsOn("refreshFollowList",(data)=>{ EventsOn("refreshFollowList",(data)=>{
message.loading("refresh...")
GetFollowList().then(result => { WindowReload()
followList.value = result // message.loading("refresh...")
for (const followedStock of result) { // GetFollowList().then(result => {
if (!stocks.value.includes(followedStock.StockCode)) { // followList.value = result
stocks.value.push(followedStock.StockCode) // for (const followedStock of result) {
} // if (!stocks.value.includes(followedStock.StockCode)) {
} // stocks.value.push(followedStock.StockCode)
monitor() // }
message.destroyAll // }
}) // monitor()
// message.destroyAll
// })
}) })
//A //A
@ -142,10 +154,15 @@ function AddStock(){
function removeMonitor(code,name) { function removeMonitor(code,name,key) {
// console.log("removeMonitor",name,code) console.log("removeMonitor",name,code,key)
stocks.value.splice(stocks.value.indexOf(code),1) stocks.value.splice(stocks.value.indexOf(code),1)
delete results.value[name] console.log("removeMonitor-key",key)
console.log("removeMonitor-v",results.value[key])
delete results.value[key]
console.log("removeMonitor-v",results.value[key])
UnFollow(code).then(result => { UnFollow(code).then(result => {
message.success(result) message.success(result)
}) })
@ -205,15 +222,35 @@ async function monitor() {
result.profitType="success" result.profitType="success"
} }
if(result["当前价格"]){ if(result["当前价格"]){
if((res[0].AlarmChangePercent>0&&Math.abs(roundedNum)>res[0].AlarmChangePercent)||(res[0].AlarmPrice>0&&result["当前价格"]>res[0].AlarmPrice)){ if(res[0].AlarmChangePercent>0&&Math.abs(roundedNum)>=res[0].AlarmChangePercent){
SendMessage(result) SendMessage(result,1)
}
if(res[0].AlarmPrice>0&&result["当前价格"]>=res[0].AlarmPrice){
SendMessage(result,2)
}
if(res[0].CostPrice>0&&result["当前价格"]>=res[0].CostPrice){
SendMessage(result,3)
} }
} }
} }
results.value[result["股票名称"]]=result result.key=GetSortKey(result.Sort,result["股票代码"])
results.value[GetSortKey(result.Sort,result["股票代码"])]=result
}) })
} }
} }
//0
function padZero(num, length) {
return (Array(length).join('0') + num).slice(-length);
}
function GetSortKey(sort,code){
return padZero(sort,6)+"_"+code
}
function onSelect(item) { function onSelect(item) {
//console.log("onSelect",item) //console.log("onSelect",item)
@ -240,6 +277,7 @@ function setStock(code,name){
formModel.value.costPrice=res[0].CostPrice formModel.value.costPrice=res[0].CostPrice
formModel.value.alarm=res[0].AlarmChangePercent formModel.value.alarm=res[0].AlarmChangePercent
formModel.value.alarmPrice=res[0].AlarmPrice formModel.value.alarmPrice=res[0].AlarmPrice
formModel.value.sort=res[0].Sort
modalShow.value=true modalShow.value=true
} }
@ -258,6 +296,13 @@ function showK(code,name){
function updateCostPriceAndVolumeNew(code,price,volume,alarm,formModel){ function updateCostPriceAndVolumeNew(code,price,volume,alarm,formModel){
if(formModel.sort){
SetStockSort(formModel.sort,code).then(result => {
//message.success(result)
})
}
if(alarm||formModel.alarmPrice){ if(alarm||formModel.alarmPrice){
SetAlarmChangePercent(alarm,formModel.alarmPrice,code).then(result => { SetAlarmChangePercent(alarm,formModel.alarmPrice,code).then(result => {
//message.success(result) //message.success(result)
@ -288,9 +333,12 @@ function fullscreen(){
data.fullscreen=!data.fullscreen data.fullscreen=!data.fullscreen
} }
function SendMessage(result){
//type : 1 ;2 3
function SendMessage(result,type){
let typeName=getTypeName(type)
let img='http://image.sinajs.cn/newchart/min/n/'+result["股票代码"]+'.gif'+"?t="+Date.now() let img='http://image.sinajs.cn/newchart/min/n/'+result["股票代码"]+'.gif'+"?t="+Date.now()
let markdown="### go-stock市场行情\n\n"+ let markdown="### go-stock ["+typeName+"]\n\n"+
"### "+result["股票名称"]+"("+result["股票代码"]+")\n" + "### "+result["股票名称"]+"("+result["股票代码"]+")\n" +
"- 当前价格: "+result["当前价格"]+" "+result.s+"\n" + "- 当前价格: "+result["当前价格"]+" "+result.s+"\n" +
"- 最高价: "+result["今日最高价"]+" "+result.highRate+"\n" + "- 最高价: "+result["今日最高价"]+" "+result.highRate+"\n" +
@ -301,10 +349,12 @@ function SendMessage(result){
"- 成本数量: "+result.volume+"股\n" + "- 成本数量: "+result.volume+"股\n" +
"- 日期: "+result["日期"]+" "+result["时间"]+"\n\n"+ "- 日期: "+result["日期"]+" "+result["时间"]+"\n\n"+
"![image]("+img+")\n" "![image]("+img+")\n"
let title=result["股票名称"]+"("+result["股票代码"]+") "+result["当前价格"]+" "+result.s
let msg='{' + let msg='{' +
' "msgtype": "markdown",' + ' "msgtype": "markdown",' +
' "markdown": {' + ' "markdown": {' +
' "title":"'+result["股票名称"]+"("+result["股票代码"]+") "+result["当前价格"]+" "+result.s+'",' + ' "title":"['+typeName+"]"+title+'",' +
' "text": "'+markdown+'"' + ' "text": "'+markdown+'"' +
' },' + ' },' +
' "at": {' + ' "at": {' +
@ -314,6 +364,19 @@ function SendMessage(result){
SendDingDingMessage(msg,result["股票代码"]) SendDingDingMessage(msg,result["股票代码"])
} }
function getTypeName(type){
switch (type)
{
case 1:
return "涨跌报警"
case 2:
return "股价报警"
case 3:
return "成本价报警"
default:
return ""
}
}
// //
function getHeight() { function getHeight() {
@ -323,8 +386,8 @@ function getHeight() {
<template> <template>
<n-grid :x-gap="8" :cols="3" :y-gap="8" > <n-grid :x-gap="8" :cols="3" :y-gap="8" >
<n-gi v-for="result in results" > <n-gi v-for="result in sortedResults" >
<n-card :data-code="result['股票代码']" :bordered="false" :title="result['股票名称']" :closable="true" @close="removeMonitor(result['股票代码'],result['股票名称'])"> <n-card :data-code="result['股票代码']" :bordered="false" :title="result['股票名称']" :closable="true" @close="removeMonitor(result['股票代码'],result['股票名称'],result.key)">
<n-grid :cols="1" :y-gap="6"> <n-grid :cols="1" :y-gap="6">
<n-gi> <n-gi>
<n-text :type="result.type" >{{result["当前价格"]}}</n-text><n-text style="padding-left: 10px;" :type="result.type">{{ result.s}}</n-text>&nbsp; <n-text :type="result.type" >{{result["当前价格"]}}</n-text><n-text style="padding-left: 10px;" :type="result.type">{{ result.s}}</n-text>&nbsp;
@ -387,7 +450,13 @@ function getHeight() {
</n-affix> </n-affix>
<n-modal transform-origin="center" size="small" v-model:show="modalShow" :title="formModel.name" style="width: 400px" :preset="'card'"> <n-modal transform-origin="center" size="small" v-model:show="modalShow" :title="formModel.name" style="width: 400px" :preset="'card'">
<n-form :model="formModel" :rules="{ costPrice: { required: true, message: '请输入成本'}, volume: { required: true, message: '请输入数量'},alarm:{required: true, message: '涨跌报警值'} }" label-placement="left" label-width="80px"> <n-form :model="formModel" :rules="{
costPrice: { required: true, message: '请输入成本'},
volume: { required: true, message: '请输入数量'},
alarm:{required: true, message: '涨跌报警值'} ,
alarmPrice: { required: true, message: '请输入报警价格'},
sort: { required: true, message: '请输入排序值'},
}" label-placement="left" label-width="80px">
<n-form-item label="股票成本" path="costPrice"> <n-form-item label="股票成本" path="costPrice">
<n-input-number v-model:value="formModel.costPrice" min="0" placeholder="请输入股票成本" > <n-input-number v-model:value="formModel.costPrice" min="0" placeholder="请输入股票成本" >
<template #suffix> <template #suffix>
@ -416,7 +485,10 @@ function getHeight() {
</template> </template>
</n-input-number> </n-input-number>
</n-form-item> </n-form-item>
<n-form-item label="股票排序" path="sort">
<n-input-number v-model:value="formModel.sort" min="0" placeholder="请输入股价排序值" >
</n-input-number>
</n-form-item>
</n-form> </n-form>
<template #footer> <template #footer>
<n-button type="primary" @click="updateCostPriceAndVolumeNew(formModel.code,formModel.costPrice,formModel.volume,formModel.alarm,formModel)">保存</n-button> <n-button type="primary" @click="updateCostPriceAndVolumeNew(formModel.code,formModel.costPrice,formModel.volume,formModel.alarm,formModel)">保存</n-button>

View File

@ -16,4 +16,6 @@ export function SetAlarmChangePercent(arg1:number,arg2:number,arg3:string):Promi
export function SetCostPriceAndVolume(arg1:string,arg2:number,arg3:number):Promise<string>; export function SetCostPriceAndVolume(arg1:string,arg2:number,arg3:number):Promise<string>;
export function SetStockSort(arg1:number,arg2:string):Promise<void>;
export function UnFollow(arg1:string):Promise<string>; export function UnFollow(arg1:string):Promise<string>;

View File

@ -30,6 +30,10 @@ export function SetCostPriceAndVolume(arg1, arg2, arg3) {
return window['go']['main']['App']['SetCostPriceAndVolume'](arg1, arg2, arg3); return window['go']['main']['App']['SetCostPriceAndVolume'](arg1, arg2, arg3);
} }
export function SetStockSort(arg1, arg2) {
return window['go']['main']['App']['SetStockSort'](arg1, arg2);
}
export function UnFollow(arg1) { export function UnFollow(arg1) {
return window['go']['main']['App']['UnFollow'](arg1); return window['go']['main']['App']['UnFollow'](arg1);
} }