spark e21ba1b800 feat(frontend/backend):添加日K线数据天数设置功能
- 在前端设置页面添加日 K 线数据天数配置选项
- 在后端 OpenAI 配置中添加 KDays 字段
- 调整股票数据分析时的历史数据时间范围
2025-02-18 12:32:34 +08:00

295 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import {onMounted, ref, watch} from "vue";
import {ExportConfig, GetConfig, SendDingDingMessageByType, UpdateConfig} from "../../wailsjs/go/main/App";
import {useMessage} from "naive-ui";
import {data} from "../../wailsjs/go/models";
import {EventsEmit} from "../../wailsjs/runtime";
const message = useMessage()
const formRef = ref(null)
const formValue = ref({
ID:1,
tushareToken:'',
dingPush:{
enable:false,
dingRobot: ''
},
localPush:{
enable:true,
},
updateBasicInfoOnStart:false,
refreshInterval:1,
openAI:{
enable:false,
baseUrl: 'https://api.deepseek.com',
apiKey: '',
model: 'deepseek-chat',
temperature: 0.1,
maxTokens: 1024,
prompt:"",
timeout: 5,
questionTemplate: "{{stockName}}分析和总结",
crawlTimeOut:30,
kDays:30,
}
})
onMounted(()=>{
GetConfig().then(res=>{
formValue.value.ID = res.ID
formValue.value.tushareToken = res.tushareToken
formValue.value.dingPush = {
enable:res.dingPushEnable,
dingRobot:res.dingRobot
}
formValue.value.localPush = {
enable:res.localPushEnable,
}
formValue.value.updateBasicInfoOnStart = res.updateBasicInfoOnStart
formValue.value.refreshInterval = res.refreshInterval
formValue.value.openAI = {
enable:res.openAiEnable,
baseUrl: res.openAiBaseUrl,
apiKey:res.openAiApiKey,
model:res.openAiModelName,
temperature:res.openAiTemperature,
maxTokens:res.openAiMaxTokens,
prompt:res.prompt,
timeout:res.openAiApiTimeOut,
questionTemplate:res.questionTemplate?res.questionTemplate:'{{stockName}}分析和总结',
crawlTimeOut:res.crawlTimeOut,
kDays:res.kDays,
}
console.log(res)
})
//message.info("加载完成")
})
function saveConfig(){
let config= new data.Settings({
ID:formValue.value.ID,
dingPushEnable:formValue.value.dingPush.enable,
dingRobot:formValue.value.dingPush.dingRobot,
localPushEnable:formValue.value.localPush.enable,
updateBasicInfoOnStart:formValue.value.updateBasicInfoOnStart,
refreshInterval:formValue.value.refreshInterval,
openAiEnable:formValue.value.openAI.enable,
openAiBaseUrl:formValue.value.openAI.baseUrl,
openAiApiKey:formValue.value.openAI.apiKey,
openAiModelName:formValue.value.openAI.model,
openAiMaxTokens:formValue.value.openAI.maxTokens,
openAiTemperature:formValue.value.openAI.temperature,
tushareToken:formValue.value.tushareToken,
prompt:formValue.value.openAI.prompt,
openAiApiTimeOut:formValue.value.openAI.timeout,
questionTemplate:formValue.value.openAI.questionTemplate,
crawlTimeOut:formValue.value.openAI.crawlTimeOut,
kDays:formValue.value.openAI.kDays
})
//console.log("Settings",config)
UpdateConfig(config).then(res=>{
message.success(res)
})
}
function getHeight() {
return document.documentElement.clientHeight
}
function sendTestNotice(){
let markdown="### go-stock test\n"+new Date()
let msg='{' +
' "msgtype": "markdown",' +
' "markdown": {' +
' "title":"go-stock'+new Date()+'",' +
' "text": "'+markdown+'"' +
' },' +
' "at": {' +
' "isAtAll": true' +
' }' +
' }'
SendDingDingMessageByType(msg, "test-"+new Date().getTime(),1).then(res=>{
message.info(res)
})
}
function exportConfig(){
ExportConfig().then(res=>{
message.info(res)
})
}
function importConfig(){
let input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.onchange = (e) => {
let file = e.target.files[0];
let reader = new FileReader();
reader.onload = (e) => {
let config = JSON.parse(e.target.result);
console.log(config)
formValue.value.ID = config.ID
formValue.value.tushareToken = config.tushareToken
formValue.value.dingPush = {
enable:config.dingPushEnable,
dingRobot:config.dingRobot
}
formValue.value.localPush = {
enable:config.localPushEnable,
}
formValue.value.updateBasicInfoOnStart = config.updateBasicInfoOnStart
formValue.value.refreshInterval = config.refreshInterval
formValue.value.openAI = {
enable:config.openAiEnable,
baseUrl: config.openAiBaseUrl,
apiKey:config.openAiApiKey,
model:config.openAiModelName,
temperature:config.openAiTemperature,
maxTokens:config.openAiMaxTokens,
prompt:config.prompt,
timeout:config.openAiApiTimeOut,
questionTemplate:config.questionTemplate,
crawlTimeOut:config.crawlTimeOut,
kDays:config.kDays
}
// formRef.value.resetFields()
};
reader.readAsText(file);
};
input.click();
}
window.onerror = function (event, source, lineno, colno, error) {
console.log(event, source, lineno, colno, error)
// 将错误信息发送给后端
EventsEmit("frontendError", {
page: "settings.vue",
message: event,
source: source,
lineno: lineno,
colno: colno,
error: error ? error.stack : null
});
//message.error("发生错误:"+event)
return true;
};
</script>
<template>
<n-flex justify="left" style="margin-top: 12px;padding-left: 12px">
<n-form ref="formRef" :label-placement="'left'" :label-align="'left'">
<n-grid :cols="24" :x-gap="24" style="text-align: left">
<n-gi :span="24">
<n-text type="default" style="font-size: 25px;font-weight: bold">基础设置</n-text>
</n-gi>
<n-form-item-gi :span="10" label="Tushare api token" path="tushareToken" >
<n-input type="text" placeholder="Tushare api token" v-model:value="formValue.tushareToken" clearable />
</n-form-item-gi>
<n-form-item-gi :span="6" label="启动时更新A股/指数信息:" path="updateBasicInfoOnStart" >
<n-switch v-model:value="formValue.updateBasicInfoOnStart" />
</n-form-item-gi>
<n-form-item-gi :span="6" label="数据刷新间隔(重启生效)" path="refreshInterval" >
<n-input-number v-model:value="formValue.refreshInterval" placeholder="请输入数据刷新间隔(秒)">
<template #suffix>
</template>
</n-input-number>
</n-form-item-gi>
</n-grid>
<n-grid :cols="24" :x-gap="24" style="text-align: left">
<n-gi :span="24">
<n-text type="default" style="font-size: 25px;font-weight: bold">通知设置</n-text>
</n-gi>
<n-form-item-gi :span="6" label="是否启用钉钉推送:" path="dingPush.enable" >
<n-switch v-model:value="formValue.dingPush.enable" />
</n-form-item-gi>
<n-form-item-gi :span="6" label="是否启用本地推送:" path="localPush.enable" >
<n-switch v-model:value="formValue.localPush.enable" />
</n-form-item-gi>
<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-button type="primary" @click="sendTestNotice">发送测试通知</n-button>
</n-form-item-gi>
</n-grid>
<n-grid :cols="24" :x-gap="24" style="text-align: left;">
<n-gi :span="24">
<n-text type="default" style="font-size: 25px;font-weight: bold">OpenAI设置</n-text>
</n-gi>
<n-form-item-gi :span="3" label="是否启用AI诊股" path="openAI.enable" >
<n-switch v-model:value="formValue.openAI.enable" />
</n-form-item-gi>
<n-form-item-gi :span="9" v-if="formValue.openAI.enable" label="openAI 接口地址:" path="openAI.baseUrl">
<n-input type="text" placeholder="AI接口地址" v-model:value="formValue.openAI.baseUrl" clearable />
</n-form-item-gi>
<n-form-item-gi :span="5" v-if="formValue.openAI.enable" label="AI Timeout(秒)" title="AI请求超时时间(秒)" path="openAI.timeout">
<n-input-number min="60" step="1" placeholder="AI请求超时时间(秒)" v-model:value="formValue.openAI.timeout" />
</n-form-item-gi>
<n-form-item-gi :span="5" v-if="formValue.openAI.enable" label="Crawler Timeout(秒)" title="资讯采集超时时间(秒)" path="openAI.crawlTimeOut">
<n-input-number min="30" step="1" placeholder="资讯采集超时时间(秒)" v-model:value="formValue.openAI.crawlTimeOut" />
</n-form-item-gi>
<n-form-item-gi :span="12" v-if="formValue.openAI.enable" label="openAI 令牌(apiKey)" path="openAI.apiKey">
<n-input type="text" placeholder="apiKey" v-model:value="formValue.openAI.apiKey" clearable />
</n-form-item-gi>
<n-form-item-gi :span="10" v-if="formValue.openAI.enable" label="AI模型名称" path="openAI.model">
<n-input type="text" placeholder="AI模型名称" v-model:value="formValue.openAI.model" clearable />
</n-form-item-gi>
<n-form-item-gi :span="12" v-if="formValue.openAI.enable" label="openAI temperature" path="openAI.temperature" >
<n-input-number placeholder="temperature" v-model:value="formValue.openAI.temperature"/>
</n-form-item-gi>
<n-form-item-gi :span="5" v-if="formValue.openAI.enable" label="openAI maxTokens" path="openAI.maxTokens">
<n-input-number placeholder="maxTokens" v-model:value="formValue.openAI.maxTokens"/>
</n-form-item-gi>
<n-form-item-gi :span="5" v-if="formValue.openAI.enable" title="天数越多消耗tokens越多" label="日K线数据(天)" path="openAI.maxTokens">
<n-input-number min="30" step="1" max="365" placeholder="日K线数据(天)" title="天数越多消耗tokens越多" v-model:value="formValue.openAI.kDays"/>
</n-form-item-gi>
<n-form-item-gi :span="22" v-if="formValue.openAI.enable" label="模型系统 Prompt" path="openAI.prompt">
<n-input v-model:value="formValue.openAI.prompt"
type="textarea"
:show-count="true"
placeholder="请输入系统prompt"
:autosize="{
minRows: 5,
maxRows: 8
}"
/>
</n-form-item-gi>
<n-form-item-gi :span="22" v-if="formValue.openAI.enable" label="模型用户 Prompt" path="openAI.questionTemplate">
<n-input v-model:value="formValue.openAI.questionTemplate"
type="textarea"
:show-count="true"
placeholder="请输入用户prompt:例如{{stockName}}[{{stockCode}}]分析和总结"
:autosize="{
minRows: 2,
maxRows: 5
}"
/>
</n-form-item-gi>
</n-grid>
<n-gi :span="24">
<n-space justify="center">
<n-button type="primary" @click="saveConfig">
保存
</n-button>
<n-button type="info" @click="exportConfig">
导出
</n-button>
<n-button type="error" @click="importConfig">
导入
</n-button>
</n-space>
</n-gi>
</n-form>
</n-flex>
</template>
<style scoped>
</style>