Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
ArvinLovegood 2025-06-25 09:40:35 +08:00
commit 22b4fcdffb
2 changed files with 825 additions and 715 deletions

View File

@ -400,7 +400,13 @@ func (receiver StockDataApi) Follow(stockCode string) string {
logger.SugaredLogger.Error(err)
return "关注失败"
}
if strings.HasPrefix(stockCode, "us") {
stockCode = strings.Replace(stockCode, "us", "gb_", 1)
}
if strings.HasPrefix(stockCode, "US") {
stockCode = strings.Replace(stockCode, "US", "gb_", 1)
}
stockCode = strings.ToLower(stockCode)
maxSort := int64(0)
db.Dao.Model(&FollowedStock{}).Raw("select max(sort) as sort from followed_stock").Scan(&maxSort)
@ -463,15 +469,64 @@ func (receiver StockDataApi) SetAlarmChangePercent(val, alarmPrice float64, stoc
return "设置成功"
}
func (receiver StockDataApi) SetStockSort(sort int64, stockCode string) {
if strutil.HasPrefixAny(stockCode, []string{"gb_"}) {
stockCode = strings.ToLower(stockCode)
stockCode = strings.Replace(stockCode, "gb_", "us", 1)
func (receiver StockDataApi) SetStockSort(newSort int64, stockCode string) {
//if strutil.HasPrefixAny(stockCode, []string{"gb_"}) {
// stockCode = strings.ToLower(stockCode)
// stockCode = strings.Replace(stockCode, "gb_", "us", 1)
//}
// 获取当前排序值
var currentStock FollowedStock
if err := db.Dao.Model(&FollowedStock{}).Where("stock_code = ?", strings.ToLower(stockCode)).First(&currentStock).Error; err != nil {
logger.SugaredLogger.Error("找不到当前股票: ", err.Error())
return
}
err := db.Dao.Model(&FollowedStock{}).Where("stock_code = ?", strings.ToLower(stockCode)).Update("sort", sort).Error
if err != nil {
logger.SugaredLogger.Error(err.Error())
oldSort := currentStock.Sort
// 如果排序值没有变化,直接返回
if oldSort == newSort {
return
}
// 检查新排序位置是否被占用
var count int64
if err := db.Dao.Model(&FollowedStock{}).Where("sort = ?", newSort).Count(&count).Error; err != nil {
logger.SugaredLogger.Error("检查新排序位置被占用失败: ", err.Error())
return
}
if count == 0 {
// 新位置未被占用,直接更新当前记录
if err := db.Dao.Model(&FollowedStock{}).
Where("stock_code = ?", strings.ToLower(stockCode)).
Update("sort", newSort).Error; err != nil {
logger.SugaredLogger.Error("更新排序位置失败: ", err.Error())
}
} else {
// 新位置已被占用,需要移动其他记录
if newSort < oldSort {
// 向前移动:将中间记录向后移动
if err := db.Dao.Model(&FollowedStock{}).
Where("sort >= ? AND sort < ?", newSort, oldSort).
Update("sort", gorm.Expr("sort + 1")).Error; err != nil {
logger.SugaredLogger.Error("向前排序更新失败: ", err.Error())
}
} else {
// 向后移动:将中间记录向前移动
if err := db.Dao.Model(&FollowedStock{}).
Where("sort > ? AND sort <= ?", oldSort, newSort).
Update("sort", gorm.Expr("sort - 1")).Error; err != nil {
logger.SugaredLogger.Error("向后排序更新失败: ", err.Error())
}
}
// 更新目标记录的排序
if err := db.Dao.Model(&FollowedStock{}).
Where("stock_code = ?", strings.ToLower(stockCode)).
Update("sort", newSort).Error; err != nil {
logger.SugaredLogger.Error("更新股票排序失败: ", err.Error())
}
}
}
func (receiver StockDataApi) SetStockAICron(cron string, stockCode string) {
if strutil.HasPrefixAny(stockCode, []string{"gb_"}) {

View File

@ -2,28 +2,31 @@
import {computed, h, onBeforeMount, onBeforeUnmount, onMounted, reactive, ref} from 'vue'
import * as echarts from 'echarts';
import {
AddGroup,
AddStockGroup,
Follow,
GetAIResponseResult,
GetConfig,
GetFollowList,
GetGroupList,
GetPromptTemplates,
GetStockKLine,
GetStockList,
GetStockMinutePriceLineData,
GetVersionInfo,
Greet,
NewChatStream,
RemoveGroup,
RemoveStockGroup,
SaveAIResponseResult,
SaveAsMarkdown,
SendDingDingMessageByType,
SetAlarmChangePercent,
SetCostPriceAndVolume,
SetStockSort,
UnFollow,
ShareAnalysis,
SaveAsMarkdown,
GetPromptTemplates,
SetStockAICron,
AddGroup,
GetGroupList,
AddStockGroup,
RemoveStockGroup, RemoveGroup, GetStockKLine, GetStockMinutePriceLineData
SetStockSort,
ShareAnalysis,
UnFollow
} from '../../wailsjs/go/main/App'
import {
NAvatar,
@ -35,7 +38,6 @@ import {
NText,
useDialog,
useMessage,
useModal,
useNotification
} from 'naive-ui'
import {
@ -46,11 +48,8 @@ import {
WindowReload,
WindowUnfullscreen
} from '../../wailsjs/runtime'
import {
Add,
ChatboxOutline,
} from '@vicons/ionicons5'
import {MdPreview,MdEditor } from 'md-editor-v3';
import {Add, ChatboxOutline,} from '@vicons/ionicons5'
import {MdEditor, MdPreview} from 'md-editor-v3';
// preview.cssstyle.css
//import 'md-editor-v3/lib/preview.css';
import 'md-editor-v3/lib/style.css';
@ -61,9 +60,10 @@ import html2canvas from "html2canvas";
import {asBlob} from 'html-docx-js-typescript';
import vueDanmaku from 'vue3-danmaku'
import {keys, pad, padStart} from "lodash";
import {keys, padStart} from "lodash";
import {useRoute, useRouter} from 'vue-router'
import MoneyTrend from "./moneyTrend.vue";
const route = useRoute()
const router = useRouter()
@ -139,7 +139,6 @@ const feishiInterval= ref(null)
const currentGroupId = ref(0)
const theme = computed(() => {
return data.darkTheme ? 'dark' : 'light'
})
@ -151,9 +150,7 @@ const danmakuColor = computed(()=> {
const icon = ref('https://raw.githubusercontent.com/ArvinLovegood/go-stock/master/build/appicon.png');
const sortedResults = computed(() => {
////console.log("computed",sortedResults.value)
const sortedKeys = keys(results.value).sort();
////console.log("sortedKeys",sortedKeys)
const sortedObject = {};
sortedKeys.forEach(key => {
sortedObject[key] = results.value[key];
@ -215,14 +212,6 @@ onBeforeMount(()=>{
onMounted(() => {
message.loading("Loading...")
// //console.log(`the component is now mounted.`)
// ticker.value=setInterval(() => {
// if(isTradingTime()){
// //monitor()
// //data.fenshiURL='http://image.sinajs.cn/newchart/min/n/'+data.code+'.gif'+"?t="+Date.now()
// }
// }, 3500)
GetFollowList(currentGroupId.value).then(result => {
@ -232,7 +221,6 @@ onMounted(() => {
followedStock.StockCode = "gb_" + followedStock.StockCode.replace("us", "").toLowerCase()
}
if (!stocks.value.includes(followedStock.StockCode)) {
////console.log("followList",followedStock.StockCode)
stocks.value.push(followedStock.StockCode)
}
}
@ -314,17 +302,6 @@ EventsOn("stock_price",(data)=>{
EventsOn("refreshFollowList", (data) => {
WindowReload()
// message.loading("refresh...")
// GetFollowList().then(result => {
// followList.value = result
// for (const followedStock of result) {
// if (!stocks.value.includes(followedStock.StockCode)) {
// stocks.value.push(followedStock.StockCode)
// }
// }
// monitor()
// message.destroyAll
// })
})
EventsOn("newChatStream", async (msg) => {
@ -457,6 +434,7 @@ function AddStock(){
if (!stocks.value.includes(data.code)) {
Follow(data.code).then(result => {
if (result === "关注成功") {
data.code= "gb_" + data.code.replace("us", "").toLowerCase()
stocks.value.push(data.code)
message.success(result)
GetFollowList(currentGroupId.value).then(result => {
@ -473,7 +451,6 @@ function AddStock(){
}
function removeMonitor(code, name, key) {
//console.log("removeMonitor",name,code,key)
stocks.value.splice(stocks.value.indexOf(code), 1)
@ -499,7 +476,6 @@ function SendDanmu(){
function getStockList(value) {
// //console.log("getStockList",value)
let result;
result = stockList.value.filter(item => item.name.includes(value) || item.ts_code.includes(value))
@ -524,8 +500,6 @@ function getStockList(value){
}
}
function blinkBorder(findId) {
@ -587,13 +561,16 @@ async function updateData(result) {
}
// result.key=result.sort
results.value = Object.fromEntries(
Object.entries(results.value).filter(
([key]) => !key.includes(result["股票代码"])
));
result.key = GetSortKey(result.sort, result["股票代码"])
results.value[GetSortKey(result.sort,result["股票代码"])]=result
results.value[result.key] = result
if (!stocks.value.includes(result["股票代码"])) {
delete results.value[result.key]
}
////console.log("updateData",result)
}
@ -602,7 +579,7 @@ async function monitor() {
showPopover.value = true
}
for (let code of stocks.value) {
// //console.log(code)
Greet(code).then(result => {
updateData(result)
})
@ -612,7 +589,6 @@ async function monitor() {
function GetSortKey(sort, code) {
let sortKey = padStart(sort, 8, '0') + "_" + code
////console.log("GetSortKey:",sortKey)
return sortKey
}
@ -638,6 +614,7 @@ function search(code,name){
//window.open("https://www.iwencai.com/chat/?question="+code)
}, 500)
}
function setStock(code, name) {
let res = followList.value.filter(item => item.StockCode === code)
////console.log("res:",res)
@ -651,10 +628,12 @@ function setStock(code,name){
formModel.value.cron = res[0].Cron
modalShow.value = true
}
function clearFeishi() {
//console.log("clearFeishi")
clearInterval(feishiInterval.value)
}
function showFsChart(code, name) {
data.name = name
data.code = code
@ -921,6 +900,7 @@ function calculateMA(dayCount,values) {
}
return result;
}
function handleKLine() {
GetStockKLine(data.code, data.name, 365).then(result => {
//console.log("GetStockKLine",result)
@ -1246,6 +1226,7 @@ function handleKLine(){
});
})
}
function showMoney(code, name) {
data.code = code
data.name = name
@ -1268,10 +1249,7 @@ function showK(code,name){
}
function updateCostPriceAndVolumeNew(code, price, volume, alarm, formModel) {
if (formModel.sort) {
SetStockSort(formModel.sort, code).then(result => {
//message.success(result)
@ -1293,6 +1271,7 @@ function updateCostPriceAndVolumeNew(code,price,volume,alarm,formModel){
message.success(result)
GetFollowList(currentGroupId.value).then(result => {
followList.value = result
stocks.value = []
for (const followedStock of result) {
if (!stocks.value.includes(followedStock.StockCode)) {
stocks.value.push(followedStock.StockCode)
@ -1344,6 +1323,7 @@ function SendMessage(result,type){
// SendDingDingMessage(msg,result[""])
SendDingDingMessageByType(msg, result["股票代码"], type)
}
function aiReCheckStock(stock, stockCode) {
data.modelName = ""
data.airesult = ""
@ -1360,6 +1340,7 @@ function aiReCheckStock(stock,stockCode) {
//message.info("sysPromptId:"+data.sysPromptId)
NewChatStream(stock, stockCode, data.question, data.sysPromptId)
}
function aiCheckStock(stock, stockCode) {
GetAIResponseResult(stockCode).then(result => {
if (result.content) {
@ -1397,8 +1378,7 @@ function aiCheckStock(stock,stockCode){
}
function getTypeName(type) {
switch (type)
{
switch (type) {
case 1:
return "涨跌报警"
case 2:
@ -1461,11 +1441,13 @@ async function copyToClipboard() {
message.error('复制失败: ' + err);
}
}
function saveAsMarkdown() {
SaveAsMarkdown(data.code, data.name).then(result => {
message.success(result)
})
}
function saveAsMarkdown_old() {
const blob = new Blob([data.airesult], {type: 'text/markdown;charset=utf-8'});
const link = document.createElement('a');
@ -1475,6 +1457,7 @@ function saveAsMarkdown_old() {
URL.revokeObjectURL(link.href);
link.remove()
}
function getHtml(ref) {
if (ref.value) {
// MdPreview
@ -1539,14 +1522,17 @@ function share(code,name){
})
})
}
const addTabModel = ref({
name: '',
sort: 1,
})
const addTabPane = ref(false)
function addTab() {
addTabPane.value = true
}
function saveTabPane() {
AddGroup(addTabModel.value).then(result => {
message.info(result)
@ -1556,6 +1542,7 @@ function saveTabPane(){
})
})
}
function AddStockGroupInfo(groupId, code, name) {
if (code.startsWith("gb_")) {
code = "us" + code.replace("gb_", "").toLowerCase()
@ -1568,11 +1555,11 @@ function AddStockGroupInfo(groupId,code,name){
})
}
function updateTab(name) {
currentGroupId.value = Number(name)
GetFollowList(currentGroupId.value).then(result => {
stocks.value = []
//console.log("GetFollowList",result)
followList.value = result
for (const followedStock of result) {
if (followedStock.StockCode.startsWith("us")) {
@ -1585,6 +1572,7 @@ function updateTab(name){
message.destroyAll()
})
}
function delTab(name) {
let infos = groupList.value = groupList.value.filter(item => item.ID === Number(name))
dialog.create({
@ -1603,6 +1591,7 @@ function delTab(name){
}
})
}
function delStockGroup(code, name, groupId) {
RemoveStockGroup(code, name, groupId).then(result => {
updateTab(groupId)
@ -1619,6 +1608,7 @@ function searchNotice(stockCode){
},
})
}
function searchStockReport(stockCode) {
router.push({
name: 'market',
@ -1631,26 +1621,35 @@ function searchStockReport(stockCode){
</script>
<template>
<vue-danmaku v-model:danmus="danmus" useSlot style="height:100px; width:100%;z-index: 9;position:absolute; top: 400px; pointer-events: none;" >
<vue-danmaku v-model:danmus="danmus" useSlot
style="height:100px; width:100%;z-index: 9;position:absolute; top: 400px; pointer-events: none;">
<template v-slot:dm="{ index, danmu }">
<n-gradient-text type="info">
<n-icon :component="ChatboxOutline"/>{{ danmu }}
<n-icon :component="ChatboxOutline"/>
{{ danmu }}
</n-gradient-text>
</template>
</vue-danmaku>
<n-tabs type="card" style="--wails-draggable:drag" animated addable :data-currentGroupId="currentGroupId" :value="currentGroupId" @add="addTab" @update-value="updateTab" placement="top" @close="(key)=>{delTab(key)}">
<n-tabs type="card" style="--wails-draggable:drag" animated addable :data-currentGroupId="currentGroupId"
:value="currentGroupId" @add="addTab" @update-value="updateTab" placement="top" @close="(key)=>{delTab(key)}">
<n-tab-pane :name="0" :tab="'全部'">
<n-grid :x-gap="8" :cols="3" :y-gap="8">
<n-gi :id="result['股票代码']+'_gi'" v-for="result in sortedResults" style="margin-left: 2px;">
<n-card :data-sort="result.sort" :id="result['股票代码']" :data-code="result['股票代码']" :bordered="true" :title="result['股票名称']" :closable="false" @close="removeMonitor(result['股票代码'],result['股票名称'],result.key)">
<n-card :data-sort="result.sort" :id="result['股票代码']" :data-code="result['股票代码']" :bordered="true"
:title="result['股票名称']" :closable="false"
@close="removeMonitor(result['股票代码'],result['股票名称'],result.key)">
<n-grid :cols="1" :y-gap="6">
<n-gi>
<n-text :type="result.type">
<n-number-animation :duration="1000" :precision="2" :from="result['上次当前价格']" :to="Number(result['当前价格'])" />
<n-tag size="small" :type="result.type" :bordered="false" v-if="result['盘前盘后']>0">({{result['盘前盘后']}} {{result['盘前盘后涨跌幅']}}%)</n-tag>
<n-number-animation :duration="1000" :precision="2" :from="result['上次当前价格']"
:to="Number(result['当前价格'])"/>
<n-tag size="small" :type="result.type" :bordered="false" v-if="result['盘前盘后']>0">
({{ result['盘前盘后'] }} {{ result['盘前盘后涨跌幅'] }}%)
</n-tag>
</n-text>
<n-text style="padding-left: 10px;" :type="result.type">
<n-number-animation :duration="1000" :precision="3" :from="0" :to="result.changePercent" />%
<n-number-animation :duration="1000" :precision="3" :from="0" :to="result.changePercent"/>
%
</n-text>&nbsp;
<n-text size="small" v-if="result.costVolume>0" :type="result.type">
<n-number-animation :duration="1000" :precision="2" :from="0" :to="result.profitAmountToday"/>
@ -1720,10 +1719,12 @@ function searchStockReport(stockCode){
<template #header-extra>
<n-tag size="small" :bordered="false">{{ result['股票代码'] }}</n-tag>&nbsp;
<n-button size="tiny" secondary type="primary" @click="removeMonitor(result['股票代码'],result['股票名称'],result.key)">
<n-button size="tiny" secondary type="primary"
@click="removeMonitor(result['股票代码'],result['股票名称'],result.key)">
取消关注
</n-button>&nbsp;
<n-button size="tiny" v-if="data.openAiEnable" secondary type="warning" @click="aiCheckStock(result['股票名称'],result['股票代码'])">
<n-button size="tiny" v-if="data.openAiEnable" secondary type="warning"
@click="aiCheckStock(result['股票名称'],result['股票代码'])">
AI分析
</n-button>
@ -1732,20 +1733,33 @@ function searchStockReport(stockCode){
<n-flex justify="center">
<n-text :type="'info'">{{ result["日期"] + " " + result["时间"] }}</n-text>
<n-tag size="small" v-if="result.volume>0" :type="result.profitType">{{ result.volume + "股" }}</n-tag>
<n-tag size="small" v-if="result.costPrice>0" :type="result.profitType">{{"成本:"+result.costPrice+"*"+result.costVolume+" "+result.profit+"%"+" ( "+result.profitAmount+" ¥ )"}}</n-tag>
<n-tag size="small" v-if="result.costPrice>0" :type="result.profitType">
{{ "成本:" + result.costPrice + "*" + result.costVolume + " " + result.profit + "%" + " ( " + result.profitAmount + " ¥ )" }}
</n-tag>
</n-flex>
</template>
<template #action>
<n-flex justify="left">
<n-button size="tiny" type="warning" @click="setStock(result['股票代码'],result['股票名称'])"> 成本 </n-button>
<n-button size="tiny" type="error" @click="showFenshi(result['股票代码'],result['股票名称'],result.changePercent)"> 分时 </n-button>
<n-button size="tiny" type="warning" @click="setStock(result['股票代码'],result['股票名称'])"> 成本
</n-button>
<n-button size="tiny" type="error"
@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" v-if="result['买一报价']>0" @click="showMoney(result['股票代码'],result['股票名称'])"> 资金 </n-button>
<n-button size="tiny" type="success" @click="search(result['股票代码'],result['股票名称'])"> 详情 </n-button>
<n-button v-if="result['买一报价']>0" size="tiny" type="success" @click="searchNotice(result['股票代码'])"> 公告 </n-button>
<n-button v-if="result['买一报价']>0" size="tiny" type="success" @click="searchStockReport(result['股票代码'])"> 研报 </n-button>
<n-button size="tiny" type="error" v-if="result['买一报价']>0"
@click="showMoney(result['股票代码'],result['股票名称'])"> 资金
</n-button>
<n-button size="tiny" type="success" @click="search(result['股票代码'],result['股票名称'])"> 详情
</n-button>
<n-button v-if="result['买一报价']>0" size="tiny" type="success"
@click="searchNotice(result['股票代码'])"> 公告
</n-button>
<n-button v-if="result['买一报价']>0" size="tiny" type="success"
@click="searchStockReport(result['股票代码'])"> 研报
</n-button>
<n-flex justify="right">
<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="warning" size="tiny">设置分组</n-button>
</n-dropdown>
</n-flex>
@ -1758,15 +1772,21 @@ function searchStockReport(stockCode){
<n-tab-pane closable v-for="group in groupList" :group-id="group.ID" :name="group.ID" :tab="group.name">
<n-grid :x-gap="8" :cols="3" :y-gap="8">
<n-gi :id="result['股票代码']+'_gi'" v-for="result in groupResults" style="margin-left: 2px;">
<n-card :data-sort="result.sort" :id="result['股票代码']" :data-code="result['股票代码']" :bordered="true" :title="result['股票名称']" :closable="false" @close="removeMonitor(result['股票代码'],result['股票名称'],result.key)">
<n-card :data-sort="result.sort" :id="result['股票代码']" :data-code="result['股票代码']" :bordered="true"
:title="result['股票名称']" :closable="false"
@close="removeMonitor(result['股票代码'],result['股票名称'],result.key)">
<n-grid :cols="1" :y-gap="6">
<n-gi>
<n-text :type="result.type">
<n-number-animation :duration="1000" :precision="2" :from="result['上次当前价格']" :to="Number(result['当前价格'])" />
<n-tag size="small" :type="result.type" :bordered="false" v-if="result['盘前盘后']>0">({{result['盘前盘后']}} {{result['盘前盘后涨跌幅']}}%)</n-tag>
<n-number-animation :duration="1000" :precision="2" :from="result['上次当前价格']"
:to="Number(result['当前价格'])"/>
<n-tag size="small" :type="result.type" :bordered="false" v-if="result['盘前盘后']>0">
({{ result['盘前盘后'] }} {{ result['盘前盘后涨跌幅'] }}%)
</n-tag>
</n-text>
<n-text style="padding-left: 10px;" :type="result.type">
<n-number-animation :duration="1000" :precision="3" :from="0" :to="result.changePercent" />%
<n-number-animation :duration="1000" :precision="3" :from="0" :to="result.changePercent"/>
%
</n-text>&nbsp;
<n-text size="small" v-if="result.costVolume>0" :type="result.type">
<n-number-animation :duration="1000" :precision="2" :from="0" :to="result.profitAmountToday"/>
@ -1836,32 +1856,49 @@ function searchStockReport(stockCode){
<template #header-extra>
<n-tag size="small" :bordered="false">{{ result['股票代码'] }}</n-tag>&nbsp;
<n-button size="tiny" secondary type="primary" @click="removeMonitor(result['股票代码'],result['股票名称'],result.key)">
<n-button size="tiny" secondary type="primary"
@click="removeMonitor(result['股票代码'],result['股票名称'],result.key)">
取消关注
</n-button>&nbsp;
<n-button size="tiny" v-if="data.openAiEnable" secondary type="warning" @click="aiCheckStock(result['股票名称'],result['股票代码'])">
<n-button size="tiny" v-if="data.openAiEnable" secondary type="warning"
@click="aiCheckStock(result['股票名称'],result['股票代码'])">
AI分析
</n-button>&nbsp;
<n-button secondary type="error" size="tiny" @click="delStockGroup(result['股票代码'],result['股票名称'],group.ID)">移出分组</n-button>
<n-button secondary type="error" size="tiny"
@click="delStockGroup(result['股票代码'],result['股票名称'],group.ID)">移出分组
</n-button>
</template>
<template #footer>
<n-flex justify="center">
<n-text :type="'info'">{{ result["日期"] + " " + result["时间"] }}</n-text>
<n-tag size="small" v-if="result.volume>0" :type="result.profitType">{{ result.volume + "股" }}</n-tag>
<n-tag size="small" v-if="result.costPrice>0" :type="result.profitType">{{"成本:"+result.costPrice+"*"+result.costVolume+" "+result.profit+"%"+" ( "+result.profitAmount+" ¥ )"}}</n-tag>
<n-tag size="small" v-if="result.costPrice>0" :type="result.profitType">
{{ "成本:" + result.costPrice + "*" + result.costVolume + " " + result.profit + "%" + " ( " + result.profitAmount + " ¥ )" }}
</n-tag>
</n-flex>
</template>
<template #action>
<n-flex justify="left">
<n-button size="tiny" type="warning" @click="setStock(result['股票代码'],result['股票名称'])"> 成本 </n-button>
<n-button size="tiny" type="error" @click="showFenshi(result['股票代码'],result['股票名称'],result.changePercent)"> 分时 </n-button>
<n-button size="tiny" type="warning" @click="setStock(result['股票代码'],result['股票名称'])"> 成本
</n-button>
<n-button size="tiny" type="error"
@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" v-if="result['买一报价']>0" @click="showMoney(result['股票代码'],result['股票名称'])"> 资金 </n-button>
<n-button size="tiny" type="success" @click="search(result['股票代码'],result['股票名称'])"> 详情 </n-button>
<n-button v-if="result['买一报价']>0" size="tiny" type="success" @click="searchNotice(result['股票代码'])"> 公告 </n-button>
<n-button v-if="result['买一报价']>0" size="tiny" type="success" @click="searchStockReport(result['股票代码'])"> 研报 </n-button>
<n-button size="tiny" type="error" v-if="result['买一报价']>0"
@click="showMoney(result['股票代码'],result['股票名称'])"> 资金
</n-button>
<n-button size="tiny" type="success" @click="search(result['股票代码'],result['股票名称'])"> 详情
</n-button>
<n-button v-if="result['买一报价']>0" size="tiny" type="success"
@click="searchNotice(result['股票代码'])"> 公告
</n-button>
<n-button v-if="result['买一报价']>0" size="tiny" type="success"
@click="searchStockReport(result['股票代码'])"> 研报
</n-button>
<n-flex justify="right">
<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="warning" size="tiny">设置分组</n-button>
</n-dropdown>
</n-flex>
@ -1900,7 +1937,8 @@ function searchStockReport(stockCode){
</n-input-group>
<!-- </n-card>-->
</div>
<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: '请输入数量'},
@ -1945,7 +1983,10 @@ function searchStockReport(stockCode){
</n-form-item>
</n-form>
<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>
</template>
</n-modal>
@ -1954,12 +1995,14 @@ function searchStockReport(stockCode){
:model="addTabModel"
size="medium"
label-placement="left"
> <n-grid :cols="2" >
>
<n-grid :cols="2">
<n-form-item-gi label="分组名称:" path="name" :span="5">
<n-input v-model:value="addTabModel.name" style="width: 100%" placeholder="请输入分组名称"/>
</n-form-item-gi>
<n-form-item-gi label="分组排序:" path="sort" :span="5">
<n-input-number v-model:value="addTabModel.sort" style="width: 100%" min="0" placeholder="请输入分组排序值" ></n-input-number>
<n-input-number v-model:value="addTabModel.sort" style="width: 100%" min="0"
placeholder="请输入分组排序值"></n-input-number>
</n-form-item-gi>
</n-grid>
</n-form>
@ -1974,28 +2017,36 @@ function searchStockReport(stockCode){
</n-flex>
</template>
</n-modal>
<n-modal v-model:show="modalShow2" :title="data.name+' '+ data.changePercent+'%'" style="width: 1000px" :preset="'card'" @after-enter="handleFeishi" @after-leave="clearFeishi">
<n-modal v-model:show="modalShow2" :title="data.name+' '+ data.changePercent+'%'" style="width: 1000px"
:preset="'card'" @after-enter="handleFeishi" @after-leave="clearFeishi">
<!-- <n-image :src="data.fenshiURL" />-->
<div ref="kLineChartRef2" style="width: 1000px; height: 500px;"></div>
</n-modal>
<n-modal v-model:show="modalShow3" :title="data.name" style="width: 1000px" :preset="'card'" @after-enter="handleKLine">
<n-modal v-model:show="modalShow3" :title="data.name" style="width: 1000px" :preset="'card'"
@after-enter="handleKLine">
<!-- <n-image :src="data.kURL" />-->
<div ref="kLineChartRef" style="width: 1000px; height: 500px;"></div>
</n-modal>
<n-modal transform-origin="center" v-model:show="modalShow4" preset="card" style="width: 800px;" :title="'['+data.name+']AI分析结果'" >
<n-modal transform-origin="center" v-model:show="modalShow4" preset="card" style="width: 800px;"
:title="'['+data.name+']AI分析结果'">
<n-spin size="small" :show="data.loading">
<MdEditor v-if="enableEditor" :toolbars="toolbars" ref="mdEditorRef" style="height: 440px;text-align: left" :modelValue="data.airesult" :theme="theme">
<MdEditor v-if="enableEditor" :toolbars="toolbars" ref="mdEditorRef" style="height: 440px;text-align: left"
:modelValue="data.airesult" :theme="theme">
<template #defToolbars>
<ExportPDF :file-name="data.name+'['+data.code+']AI分析报告'" style="text-align: left" :modelValue="data.airesult" @onProgress="handleProgress" />
<ExportPDF :file-name="data.name+'['+data.code+']AI分析报告'" style="text-align: left"
:modelValue="data.airesult" @onProgress="handleProgress"/>
</template>
</MdEditor>
<MdPreview v-if="!enableEditor" ref="mdPreviewRef" style="height: 440px;text-align: left" :modelValue="data.airesult" :theme="theme"/>
<MdPreview v-if="!enableEditor" ref="mdPreviewRef" style="height: 440px;text-align: left"
:modelValue="data.airesult" :theme="theme"/>
</n-spin>
<template #footer>
<n-flex justify="space-between" ref="tipsRef">
<n-text type="info" v-if="data.time">
<n-tag v-if="data.modelName" type="warning" round :title="data.chatId" :bordered="false">{{data.modelName}}</n-tag>
<n-tag v-if="data.modelName" type="warning" round :title="data.chatId" :bordered="false">
{{ data.modelName }}
</n-tag>
{{ data.time }}
</n-text>
<n-text type="error">*AI分析结果仅供参考请以实际行情为准投资需谨慎风险自担</n-text>
@ -2004,8 +2055,10 @@ function searchStockReport(stockCode){
<template #action>
<n-flex justify="space-between" style="margin-bottom: 10px">
<n-select style="width: 49%" v-model:value="data.sysPromptId" label-field="name" value-field="ID" :options="sysPromptOptions" placeholder="请选择系统提示词" />
<n-select style="width: 49%" v-model:value="data.question" label-field="name" value-field="content" :options="userPromptOptions" placeholder="请选择用户提示词" />
<n-select style="width: 49%" v-model:value="data.sysPromptId" label-field="name" value-field="ID"
:options="sysPromptOptions" placeholder="请选择系统提示词"/>
<n-select style="width: 49%" v-model:value="data.question" label-field="name" value-field="content"
:options="userPromptOptions" placeholder="请选择用户提示词"/>
</n-flex>
<n-flex justify="right">
<n-input v-model:value="data.question" style="text-align: left" clearable
@ -2028,7 +2081,8 @@ function searchStockReport(stockCode){
</template>
</n-modal>
<n-modal v-model:show="modalShow5" :title="data.name+'资金趋势'" style="width: 1000px" :preset="'card'">
<money-trend :code="data.code" :name="data.name" :days="360" :dark-theme="data.darkTheme" :chart-height="500"></money-trend>
<money-trend :code="data.code" :name="data.name" :days="360" :dark-theme="data.darkTheme"
:chart-height="500"></money-trend>
</n-modal>
</template>
@ -2040,6 +2094,7 @@ function searchStockReport(stockCode){
.md-editor-preview p {
text-align: left !important;
}
/* 添加闪烁效果的CSS类 */
.blink-border {
animation: blink-border 1s linear infinite;