feat(frontend):实时市场资讯信息提醒功能

- 新增 NewsPush 函数用于推送市场资讯
- 在 App.vue 中添加新闻推送的事件监听
- 在 settings 中增加启用新闻推送的选项
- 修改 README.md,添加实时市场资讯信息提醒的更新说明
This commit is contained in:
ArvinLovegood 2025-06-18 14:23:32 +08:00
parent 1ef950b961
commit a2fee361e7
8 changed files with 54 additions and 9 deletions

View File

@ -55,7 +55,7 @@
## 👀 更新日志 ## 👀 更新日志
### 2025.06.18 更新内置股票基础数据 ### 2025.06.18 更新内置股票基础数据,软件内实时市场资讯信息提醒
### 2025.06.15 添加公司公告信息搜索/查看功能 ### 2025.06.15 添加公司公告信息搜索/查看功能
### 2025.06.15 添加个股研报到弹出菜单 ### 2025.06.15 添加个股研报到弹出菜单
### 2025.06.13 添加个股研报功能 ### 2025.06.13 添加个股研报功能

15
app.go
View File

@ -178,6 +178,9 @@ func (a *App) domReady(ctx context.Context) {
} }
entryID, err := a.cron.AddFunc(fmt.Sprintf("@every %ds", interval+10), func() { entryID, err := a.cron.AddFunc(fmt.Sprintf("@every %ds", interval+10), func() {
news := data.NewMarketNewsApi().GetNewTelegraph(30) news := data.NewMarketNewsApi().GetNewTelegraph(30)
if config.EnablePushNews {
go a.NewsPush(news)
}
go runtime.EventsEmit(a.ctx, "newTelegraph", news) go runtime.EventsEmit(a.ctx, "newTelegraph", news)
}) })
if err != nil { if err != nil {
@ -188,6 +191,9 @@ func (a *App) domReady(ctx context.Context) {
entryIDSina, err := a.cron.AddFunc(fmt.Sprintf("@every %ds", interval+10), func() { entryIDSina, err := a.cron.AddFunc(fmt.Sprintf("@every %ds", interval+10), func() {
news := data.NewMarketNewsApi().GetSinaNews(30) news := data.NewMarketNewsApi().GetSinaNews(30)
if config.EnablePushNews {
go a.NewsPush(news)
}
go runtime.EventsEmit(a.ctx, "newSinaNews", news) go runtime.EventsEmit(a.ctx, "newSinaNews", news)
}) })
if err != nil { if err != nil {
@ -292,6 +298,15 @@ func (a *App) domReady(ctx context.Context) {
} }
func (a *App) NewsPush(news *[]models.Telegraph) {
for _, telegraph := range *news {
//if telegraph.IsRed {
go runtime.EventsEmit(a.ctx, "newsPush", telegraph)
go data.NewAlertWindowsApi("go-stock", telegraph.Source+" "+telegraph.Time, telegraph.Content, string(icon)).SendNotification()
//}
}
}
func (a *App) AddCronTask(follow data.FollowedStock) func() { func (a *App) AddCronTask(follow data.FollowedStock) func() {
return func() { return func() {
go runtime.EventsEmit(a.ctx, "warnMsg", "开始自动分析"+follow.Name+"_"+follow.StockCode) go runtime.EventsEmit(a.ctx, "warnMsg", "开始自动分析"+follow.Name+"_"+follow.StockCode)

View File

@ -34,6 +34,7 @@ type Settings struct {
DarkTheme bool `json:"darkTheme"` DarkTheme bool `json:"darkTheme"`
BrowserPoolSize int `json:"browserPoolSize"` BrowserPoolSize int `json:"browserPoolSize"`
EnableFund bool `json:"enableFund"` EnableFund bool `json:"enableFund"`
EnablePushNews bool `json:"enablePushNews"`
} }
func (receiver Settings) TableName() string { func (receiver Settings) TableName() string {
@ -78,6 +79,7 @@ func (s SettingsApi) UpdateConfig() string {
"enable_news": s.Config.EnableNews, "enable_news": s.Config.EnableNews,
"dark_theme": s.Config.DarkTheme, "dark_theme": s.Config.DarkTheme,
"enable_fund": s.Config.EnableFund, "enable_fund": s.Config.EnableFund,
"enable_push_news": s.Config.EnablePushNews,
}) })
} else { } else {
logger.SugaredLogger.Infof("未找到配置,创建默认配置:%+v", s.Config) logger.SugaredLogger.Infof("未找到配置,创建默认配置:%+v", s.Config)
@ -105,6 +107,7 @@ func (s SettingsApi) UpdateConfig() string {
EnableNews: s.Config.EnableNews, EnableNews: s.Config.EnableNews,
DarkTheme: s.Config.DarkTheme, DarkTheme: s.Config.DarkTheme,
EnableFund: s.Config.EnableFund, EnableFund: s.Config.EnableFund,
EnablePushNews: s.Config.EnablePushNews,
}) })
} }
return "保存成功!" return "保存成功!"

View File

@ -10,7 +10,7 @@ import {
} from '../wailsjs/runtime' } from '../wailsjs/runtime'
import {h, onBeforeMount, onBeforeUnmount, onMounted, ref} from "vue"; import {h, onBeforeMount, onBeforeUnmount, onMounted, ref} from "vue";
import {RouterLink, useRouter} from 'vue-router' import {RouterLink, useRouter} from 'vue-router'
import {darkTheme, NIcon, NText,dateZhCN,zhCN} from 'naive-ui' import {createDiscreteApi,darkTheme,lightTheme , NIcon, NText,dateZhCN,zhCN} from 'naive-ui'
import { import {
AlarmOutline, AlarmOutline,
AnalyticsOutline, AnalyticsOutline,
@ -29,6 +29,9 @@ import {
} from '@vicons/ionicons5' } from '@vicons/ionicons5'
import {GetConfig, GetGroupList} from "../wailsjs/go/main/App"; import {GetConfig, GetGroupList} from "../wailsjs/go/main/App";
const router = useRouter() const router = useRouter()
const loading = ref(true) const loading = ref(true)
const loadingMsg = ref("加载数据中...") const loadingMsg = ref("加载数据中...")
@ -430,6 +433,7 @@ onBeforeUnmount(() => {
EventsOff("realtime_profit") EventsOff("realtime_profit")
EventsOff("loadingMsg") EventsOff("loadingMsg")
EventsOff("telegraph") EventsOff("telegraph")
EventsOff("newsPush")
}) })
window.onerror = function (msg, source, lineno, colno, error) { window.onerror = function (msg, source, lineno, colno, error) {
@ -515,9 +519,17 @@ onMounted(() => {
enableNews.value = true enableNews.value = true
} }
enableFund.value = res.enableFund enableFund.value = res.enableFund
const {notification } =createDiscreteApi(["notification"], {
configProviderProps: {
theme: enableDarkTheme.value ? darkTheme : lightTheme ,
max: 3,
},
})
EventsOn("newsPush", (data) => {
notification.create({ title: data.time, content: data.content,duration:1000*60 })
})
}) })
}) })
</script> </script>
<template> <template>
<n-config-provider ref="containerRef" :theme="enableDarkTheme" :locale="zhCN" :date-locale="dateZhCN"> <n-config-provider ref="containerRef" :theme="enableDarkTheme" :locale="zhCN" :date-locale="dateZhCN">

View File

@ -45,6 +45,7 @@ const formValue = ref({
enableNews:false, enableNews:false,
darkTheme:true, darkTheme:true,
enableFund:false, enableFund:false,
enablePushNews:false,
}) })
const promptTemplates=ref([]) const promptTemplates=ref([])
onMounted(()=>{ onMounted(()=>{
@ -78,6 +79,7 @@ onMounted(()=>{
formValue.value.enableNews = res.enableNews formValue.value.enableNews = res.enableNews
formValue.value.darkTheme = res.darkTheme formValue.value.darkTheme = res.darkTheme
formValue.value.enableFund = res.enableFund formValue.value.enableFund = res.enableFund
formValue.value.enablePushNews = res.enablePushNews
//console.log(res) //console.log(res)
}) })
@ -118,6 +120,7 @@ function saveConfig(){
enableNews:formValue.value.enableNews, enableNews:formValue.value.enableNews,
darkTheme:formValue.value.darkTheme, darkTheme:formValue.value.darkTheme,
enableFund:formValue.value.enableFund, enableFund:formValue.value.enableFund,
enablePushNews:formValue.value.enablePushNews
}) })
@ -195,6 +198,7 @@ function importConfig(){
formValue.value.enableNews = config.enableNews formValue.value.enableNews = config.enableNews
formValue.value.darkTheme = config.darkTheme formValue.value.darkTheme = config.darkTheme
formValue.value.enableFund = config.enableFund formValue.value.enableFund = config.enableFund
formValue.value.enablePushNews = config.enablePushNews
// formRef.value.resetFields() // formRef.value.resetFields()
}; };
reader.readAsText(file); reader.readAsText(file);
@ -288,7 +292,7 @@ function deletePrompt(ID){
<n-form-item-gi :span="10" label="浏览器安装路径:" path="browserPath" > <n-form-item-gi :span="10" label="浏览器安装路径:" path="browserPath" >
<n-input type="text" placeholder="浏览器安装路径" v-model:value="formValue.browserPath" clearable /> <n-input type="text" placeholder="浏览器安装路径" v-model:value="formValue.browserPath" clearable />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi :span="6" label="是否启用指数基金:" path="enableFund" > <n-form-item-gi :span="6" label="指数基金:" path="enableFund" >
<n-switch v-model:value="formValue.enableFund" /> <n-switch v-model:value="formValue.enableFund" />
</n-form-item-gi> </n-form-item-gi>
</n-grid> </n-grid>
@ -297,18 +301,21 @@ function deletePrompt(ID){
<n-gi :span="24"> <n-gi :span="24">
<n-text type="success" style="font-size: 25px;font-weight: bold">通知设置</n-text> <n-text type="success" style="font-size: 25px;font-weight: bold">通知设置</n-text>
</n-gi> </n-gi>
<n-form-item-gi :span="6" label="是否启用钉钉推送:" path="dingPush.enable" > <n-form-item-gi :span="4" label="钉钉推送:" path="dingPush.enable" >
<n-switch v-model:value="formValue.dingPush.enable" /> <n-switch v-model:value="formValue.dingPush.enable" />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi :span="6" label="是否启用本地推送:" path="localPush.enable" > <n-form-item-gi :span="4" label="本地推送:" path="localPush.enable" >
<n-switch v-model:value="formValue.localPush.enable" /> <n-switch v-model:value="formValue.localPush.enable" />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi :span="5" label="弹幕功能:" path="enableDanmu" > <n-form-item-gi :span="4" label="弹幕功能:" path="enableDanmu" >
<n-switch v-model:value="formValue.enableDanmu" /> <n-switch v-model:value="formValue.enableDanmu" />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi :span="5" label="是否显示滚动快讯(重启生效)" path="enableNews" > <n-form-item-gi :span="4" label="显示滚动快讯" path="enableNews" >
<n-switch v-model:value="formValue.enableNews" /> <n-switch v-model:value="formValue.enableNews" />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi :span="4" label="市场资讯提醒:" path="enablePushNews" >
<n-switch v-model:value="formValue.enablePushNews" />
</n-form-item-gi>
<n-form-item-gi :span="22" v-if="formValue.dingPush.enable" label="钉钉机器人接口地址:" path="dingPush.dingRobot" > <n-form-item-gi :span="22" v-if="formValue.dingPush.enable" label="钉钉机器人接口地址:" path="dingPush.dingRobot" >
<n-input placeholder="请输入钉钉机器人接口地址" v-model:value="formValue.dingPush.dingRobot"/> <n-input placeholder="请输入钉钉机器人接口地址" v-model:value="formValue.dingPush.dingRobot"/>
<n-button type="primary" @click="sendTestNotice">发送测试通知</n-button> <n-button type="primary" @click="sendTestNotice">发送测试通知</n-button>
@ -319,7 +326,7 @@ function deletePrompt(ID){
<n-gi :span="24"> <n-gi :span="24">
<n-text type="success" style="font-size: 25px;font-weight: bold">OpenAI设置</n-text> <n-text type="success" style="font-size: 25px;font-weight: bold">OpenAI设置</n-text>
</n-gi> </n-gi>
<n-form-item-gi :span="3" label="是否启用AI诊股" path="openAI.enable" > <n-form-item-gi :span="3" label="AI诊股" path="openAI.enable" >
<n-switch v-model:value="formValue.openAI.enable" /> <n-switch v-model:value="formValue.openAI.enable" />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi :span="9" v-if="formValue.openAI.enable" label="openAI 接口地址:" path="openAI.baseUrl" > <n-form-item-gi :span="9" v-if="formValue.openAI.enable" label="openAI 接口地址:" path="openAI.baseUrl" >

View File

@ -65,6 +65,8 @@ export function LongTigerRank(arg1:string):Promise<any>;
export function NewChatStream(arg1:string,arg2:string,arg3:string,arg4:any):Promise<void>; export function NewChatStream(arg1:string,arg2:string,arg3:string,arg4:any):Promise<void>;
export function NewsPush(arg1:any):Promise<void>;
export function ReFleshTelegraphList(arg1:string):Promise<any>; export function ReFleshTelegraphList(arg1:string):Promise<any>;
export function RemoveGroup(arg1:number):Promise<string>; export function RemoveGroup(arg1:number):Promise<string>;

View File

@ -126,6 +126,10 @@ export function NewChatStream(arg1, arg2, arg3, arg4) {
return window['go']['main']['App']['NewChatStream'](arg1, arg2, arg3, arg4); return window['go']['main']['App']['NewChatStream'](arg1, arg2, arg3, arg4);
} }
export function NewsPush(arg1) {
return window['go']['main']['App']['NewsPush'](arg1);
}
export function ReFleshTelegraphList(arg1) { export function ReFleshTelegraphList(arg1) {
return window['go']['main']['App']['ReFleshTelegraphList'](arg1); return window['go']['main']['App']['ReFleshTelegraphList'](arg1);
} }

View File

@ -322,6 +322,7 @@ export namespace data {
darkTheme: boolean; darkTheme: boolean;
browserPoolSize: number; browserPoolSize: number;
enableFund: boolean; enableFund: boolean;
enablePushNews: boolean;
static createFrom(source: any = {}) { static createFrom(source: any = {}) {
return new Settings(source); return new Settings(source);
@ -357,6 +358,7 @@ export namespace data {
this.darkTheme = source["darkTheme"]; this.darkTheme = source["darkTheme"];
this.browserPoolSize = source["browserPoolSize"]; this.browserPoolSize = source["browserPoolSize"];
this.enableFund = source["enableFund"]; this.enableFund = source["enableFund"];
this.enablePushNews = source["enablePushNews"];
} }
convertValues(a: any, classs: any, asMap: boolean = false): any { convertValues(a: any, classs: any, asMap: boolean = false): any {