Compare commits
9 Commits
v2025.8.9.
...
v2025.8.26
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6dff9d95c4 | ||
|
|
06967420f8 | ||
|
|
6f4eb0ac86 | ||
|
|
f59255cc6c | ||
|
|
4f4fa46338 | ||
|
|
05bf35fdf4 | ||
|
|
567c81ae7c | ||
|
|
86f4e54d13 | ||
|
|
71e6ff4233 |
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
||||
build-name: ${{ matrix.build.name }}
|
||||
build-platform: ${{ matrix.build.platform }}
|
||||
package: true
|
||||
go-version: '1.24'
|
||||
go-version: '1.25'
|
||||
build-tags: ${{ github.ref_name }}
|
||||
build-commit-message: ${{ steps.get_commit_message.outputs.commit_message }}
|
||||
build-statement: ${{ env.OFFICIAL_STATEMENT }}
|
||||
|
||||
24
app.go
24
app.go
@@ -7,8 +7,6 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/cryptor"
|
||||
"github.com/inconshreveable/go-update"
|
||||
"go-stock/backend/data"
|
||||
"go-stock/backend/db"
|
||||
"go-stock/backend/logger"
|
||||
@@ -18,6 +16,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/duke-git/lancet/v2/cryptor"
|
||||
"github.com/inconshreveable/go-update"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/coocood/freecache"
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
@@ -464,7 +465,16 @@ func (a *App) domReady(ctx context.Context) {
|
||||
//检查新版本
|
||||
go func() {
|
||||
a.CheckUpdate(0)
|
||||
a.CheckStockBaseInfo(a.ctx)
|
||||
count := int64(0)
|
||||
db.Dao.Model(&data.StockBasic{}).Count(&count)
|
||||
if count <= 0 {
|
||||
go a.CheckStockBaseInfo(a.ctx)
|
||||
}
|
||||
|
||||
a.cron.AddFunc("0 0 2 * * *", func() {
|
||||
logger.SugaredLogger.Errorf("Checking for updates...")
|
||||
a.CheckStockBaseInfo(a.ctx)
|
||||
})
|
||||
a.cron.AddFunc("30 05 8,12,20 * * *", func() {
|
||||
logger.SugaredLogger.Errorf("Checking for updates...")
|
||||
a.CheckUpdate(0)
|
||||
@@ -1219,6 +1229,14 @@ func (a *App) GetGroupList() []data.Group {
|
||||
return data.NewStockGroupApi(db.Dao).GetGroupList()
|
||||
}
|
||||
|
||||
func (a *App) UpdateGroupSort(id int, newSort int) bool {
|
||||
return data.NewStockGroupApi(db.Dao).UpdateGroupSort(id, newSort)
|
||||
}
|
||||
|
||||
func (a *App) InitializeGroupSort() bool {
|
||||
return data.NewStockGroupApi(db.Dao).InitializeGroupSort()
|
||||
}
|
||||
|
||||
func (a *App) GetGroupStockList(groupId int) []data.GroupStock {
|
||||
return data.NewStockGroupApi(db.Dao).GetGroupStockByGroupId(groupId)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package data
|
||||
|
||||
import "C"
|
||||
import (
|
||||
"github.com/go-toast/toast"
|
||||
"go-stock/backend/logger"
|
||||
|
||||
"github.com/go-toast/toast"
|
||||
)
|
||||
|
||||
// AlertWindowsApi @Author spark
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package data
|
||||
|
||||
import (
|
||||
"github.com/go-toast/toast"
|
||||
"go-stock/backend/logger"
|
||||
"testing"
|
||||
|
||||
"github.com/go-toast/toast"
|
||||
)
|
||||
|
||||
// @Author spark
|
||||
|
||||
@@ -419,6 +419,15 @@ func (receiver StockDataApi) Follow(stockCode string) string {
|
||||
}
|
||||
|
||||
stockCode = strings.ToLower(stockCode)
|
||||
|
||||
// 检查是否已经关注过该股票
|
||||
var existingStock FollowedStock
|
||||
result := db.Dao.Model(&FollowedStock{}).Where("stock_code = ? AND is_del = ?", stockCode, 0).First(&existingStock)
|
||||
if result.Error == nil {
|
||||
// 股票已经关注过
|
||||
return "已经关注了"
|
||||
}
|
||||
|
||||
maxSort := int64(0)
|
||||
db.Dao.Model(&FollowedStock{}).Raw("select max(sort) as sort from followed_stock").Scan(&maxSort)
|
||||
|
||||
|
||||
@@ -39,17 +39,74 @@ func NewStockGroupApi(dao *gorm.DB) *StockGroupApi {
|
||||
}
|
||||
|
||||
func (receiver StockGroupApi) AddGroup(group Group) bool {
|
||||
err := receiver.dao.Where("name = ?", group.Name).FirstOrCreate(&group).Updates(&Group{
|
||||
Name: group.Name,
|
||||
Sort: group.Sort,
|
||||
}).Error
|
||||
// 检查是否已存在相同sort的组
|
||||
var existingGroup Group
|
||||
err := receiver.dao.Where("sort = ?", group.Sort).First(&existingGroup).Error
|
||||
|
||||
// 如果存在相同sort的组,则将该组及之后的所有组向后移动一位
|
||||
if err == nil {
|
||||
// 处理sort冲突:将相同sort值及之后的所有组向后移动一位
|
||||
receiver.dao.Model(&Group{}).Where("sort >= ?", group.Sort).Update("sort", gorm.Expr("sort + ?", 1))
|
||||
}
|
||||
|
||||
// 创建新组
|
||||
err = receiver.dao.Create(&group).Error
|
||||
return err == nil
|
||||
}
|
||||
func (receiver StockGroupApi) GetGroupList() []Group {
|
||||
var groups []Group
|
||||
receiver.dao.Find(&groups)
|
||||
receiver.dao.Order("sort ASC").Find(&groups)
|
||||
return groups
|
||||
}
|
||||
func (receiver StockGroupApi) UpdateGroupSort(id int, newSort int) bool {
|
||||
// First, get the current group to check if it exists
|
||||
var currentGroup Group
|
||||
if err := receiver.dao.First(¤tGroup, id).Error; err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// If the new sort is the same as current, no need to update
|
||||
if currentGroup.Sort == newSort {
|
||||
return true
|
||||
}
|
||||
|
||||
// Get all groups ordered by sort
|
||||
var allGroups []Group
|
||||
receiver.dao.Order("sort ASC").Find(&allGroups)
|
||||
|
||||
// Adjust sort numbers to make space for the new sort value
|
||||
if newSort > currentGroup.Sort {
|
||||
// Moving down: decrease sort of groups between old and new position
|
||||
receiver.dao.Model(&Group{}).Where("sort > ? AND sort <= ? AND id != ?", currentGroup.Sort, newSort, id).Update("sort", gorm.Expr("sort - ?", 1))
|
||||
} else {
|
||||
// Moving up: increase sort of groups between new and old position
|
||||
receiver.dao.Model(&Group{}).Where("sort >= ? AND sort < ? AND id != ?", newSort, currentGroup.Sort, id).Update("sort", gorm.Expr("sort + ?", 1))
|
||||
}
|
||||
|
||||
// Update the target group's sort
|
||||
err := receiver.dao.Model(&Group{}).Where("id = ?", id).Update("sort", newSort).Error
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// InitializeGroupSort initializes sort order for all groups based on created time
|
||||
func (receiver StockGroupApi) InitializeGroupSort() bool {
|
||||
// Get all groups ordered by created time
|
||||
var groups []Group
|
||||
err := receiver.dao.Order("created_at ASC").Find(&groups).Error
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Update each group with new sort value based on their position
|
||||
for i, group := range groups {
|
||||
newSort := i + 1
|
||||
err := receiver.dao.Model(&Group{}).Where("id = ?", group.ID).Update("sort", newSort).Error
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
func (receiver StockGroupApi) GetGroupStockByGroupId(groupId int) []GroupStock {
|
||||
var stockGroup []GroupStock
|
||||
receiver.dao.Preload("GroupInfo").Where("group_id = ?", groupId).Find(&stockGroup)
|
||||
|
||||
@@ -3,10 +3,11 @@ package data
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/go-ego/gse"
|
||||
"go-stock/backend/logger"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/go-ego/gse"
|
||||
)
|
||||
|
||||
// 金融情感词典,包含股票市场相关的专业词汇
|
||||
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
Wallet, WarningOutline,
|
||||
} from '@vicons/ionicons5'
|
||||
import {AnalyzeSentiment, GetConfig, GetGroupList,GetVersionInfo} from "../wailsjs/go/main/App";
|
||||
import {Dragon, Fire, Gripfire, Robot} from "@vicons/fa";
|
||||
import {Dragon, Fire, FirefoxBrowser, Gripfire, Robot} from "@vicons/fa";
|
||||
import {ReportSearch} from "@vicons/tabler";
|
||||
import {LocalFireDepartmentRound} from "@vicons/material";
|
||||
import {BoxSearch20Regular, CommentNote20Filled} from "@vicons/fluent";
|
||||
@@ -369,6 +369,28 @@ const menuOptions = ref([
|
||||
key: 'market11',
|
||||
icon: renderIcon(BoxSearch20Regular),
|
||||
},
|
||||
{
|
||||
label: () =>
|
||||
h(
|
||||
RouterLink,
|
||||
{
|
||||
href: '#',
|
||||
to: {
|
||||
name: 'market',
|
||||
query: {
|
||||
name: "名站优选",
|
||||
}
|
||||
},
|
||||
onClick: () => {
|
||||
activeKey.value = 'market'
|
||||
EventsEmit("changeMarketTab", {ID: 0, name: '名站优选'})
|
||||
},
|
||||
},
|
||||
{default: () => '名站优选',}
|
||||
),
|
||||
key: 'market12',
|
||||
icon: renderIcon(FirefoxBrowser),
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import {h, onBeforeMount, onMounted, onUnmounted, ref} from 'vue'
|
||||
import {SearchStock, GetHotStrategy, OpenURL} from "../../wailsjs/go/main/App";
|
||||
import {SearchStock, GetHotStrategy, OpenURL, Follow, GetFollowList} from "../../wailsjs/go/main/App";
|
||||
import {useMessage, NText, NTag, NButton} from 'naive-ui'
|
||||
import {Environment} from "../../wailsjs/runtime"
|
||||
import {RefreshCircleSharp} from "@vicons/ionicons5";
|
||||
import {EventsEmit} from "../../wailsjs/runtime";
|
||||
|
||||
const message = useMessage()
|
||||
const search = ref('')
|
||||
@@ -11,6 +12,32 @@ const columns = ref([])
|
||||
const dataList = ref([])
|
||||
const hotStrategy = ref([])
|
||||
const traceInfo = ref('')
|
||||
const tableScrollX = ref(2800) // 默认滚动宽度
|
||||
|
||||
// 计算表格总宽度
|
||||
function calculateTableWidth(cols) {
|
||||
let totalWidth = 0;
|
||||
|
||||
cols.forEach(col => {
|
||||
if (col.children && col.children.length > 0) {
|
||||
// 有子列的情况
|
||||
let childrenWidth = 0;
|
||||
col.children.forEach(child => {
|
||||
childrenWidth += child.width || child.minWidth || 100;
|
||||
});
|
||||
// 取标题列宽度和子列总宽度的较大值
|
||||
totalWidth += Math.max(col.width || col.minWidth || 200, childrenWidth);
|
||||
} else {
|
||||
// 没有子列的情况
|
||||
totalWidth += col.width || col.minWidth || 120;
|
||||
}
|
||||
});
|
||||
|
||||
// 加上操作列的宽度
|
||||
totalWidth += 100;
|
||||
|
||||
return Math.max(totalWidth, 1200); // 最小宽度1200
|
||||
}
|
||||
|
||||
function Search() {
|
||||
if (!search.value) {
|
||||
@@ -71,11 +98,32 @@ function Search() {
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
})
|
||||
columns.value.push({
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 80,
|
||||
fixed: 'right', // 固定在右侧
|
||||
render: (row) => {
|
||||
return h(
|
||||
NButton,
|
||||
{
|
||||
strong: true,
|
||||
tertiary: true,
|
||||
size: 'small',
|
||||
type: 'warning', // 橙色按钮
|
||||
style: 'font-size: 14px; padding: 0 10px;', // 稍微大一点的按钮
|
||||
onClick: () => handleFollow(row)
|
||||
},
|
||||
{ default: () => '关注' }
|
||||
)
|
||||
}
|
||||
});
|
||||
dataList.value = res.data.result.dataList
|
||||
console.log("sss"+columns.value. length)
|
||||
// 计算并设置表格宽度
|
||||
tableScrollX.value = calculateTableWidth(columns.value);
|
||||
} else {
|
||||
message.error(res.msg)
|
||||
}
|
||||
@@ -84,6 +132,18 @@ function Search() {
|
||||
})
|
||||
}
|
||||
|
||||
// 修改handleFollow方法,使用stock.vue的AddStock逻辑
|
||||
function handleFollow(row) {
|
||||
let code=row.MARKET_SHORT_NAME.toLowerCase()+row.SECURITY_CODE
|
||||
Follow(code).then(result => {
|
||||
if (result === "关注成功") {
|
||||
message.success(result)
|
||||
} else {
|
||||
message.error(result)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function isNumeric(value) {
|
||||
return !isNaN(parseFloat(value)) && isFinite(value);
|
||||
}
|
||||
@@ -188,7 +248,7 @@ function openCenteredWindow(url, width, height) {
|
||||
:columns="columns"
|
||||
:data="dataList"
|
||||
:pagination="{pageSize: 10}"
|
||||
:scroll-x="1800"
|
||||
:scroll-x="tableScrollX"
|
||||
:render-cell="(value, rowData, column) => {
|
||||
|
||||
if(column.key=='SECURITY_CODE'||column.key=='SERIAL'){
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<script setup>
|
||||
import {computed, h, onBeforeMount, onBeforeUnmount, onMounted, reactive, ref} from 'vue'
|
||||
import {computed, h, nextTick, onBeforeMount, onBeforeUnmount, onMounted, reactive, ref, watch} from 'vue'
|
||||
import * as echarts from 'echarts';
|
||||
import {
|
||||
AddGroup,
|
||||
AddStockGroup,
|
||||
Follow,
|
||||
GetAiConfigs,
|
||||
GetAIResponseResult,
|
||||
GetConfig,
|
||||
GetFollowList,
|
||||
@@ -15,11 +16,15 @@ import {
|
||||
GetStockMinutePriceLineData,
|
||||
GetVersionInfo,
|
||||
Greet,
|
||||
InitializeGroupSort,
|
||||
NewChatStream,
|
||||
OpenURL,
|
||||
RemoveGroup,
|
||||
RemoveStockGroup,
|
||||
SaveAIResponseResult,
|
||||
SaveAsMarkdown,
|
||||
SaveImage,
|
||||
SaveWordFile,
|
||||
SendDingDingMessageByType,
|
||||
SetAlarmChangePercent,
|
||||
SetCostPriceAndVolume,
|
||||
@@ -27,10 +32,7 @@ import {
|
||||
SetStockSort,
|
||||
ShareAnalysis,
|
||||
UnFollow,
|
||||
OpenURL,
|
||||
SaveImage,
|
||||
SaveWordFile,
|
||||
GetAiConfigs
|
||||
UpdateGroupSort
|
||||
} from '../../wailsjs/go/main/App'
|
||||
import {
|
||||
NAvatar,
|
||||
@@ -68,7 +70,6 @@ import vueDanmaku from 'vue3-danmaku'
|
||||
import {keys, padStart} from "lodash";
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
import MoneyTrend from "./moneyTrend.vue";
|
||||
import {TaskTools} from "@vicons/carbon";
|
||||
import StockSparkLine from "./stockSparkLine.vue";
|
||||
|
||||
const route = useRoute()
|
||||
@@ -170,22 +171,137 @@ const sortedResults = computed(() => {
|
||||
|
||||
const groupResults = computed(() => {
|
||||
const group = {}
|
||||
for (const key in sortedResults.value) {
|
||||
if (stocks.value.includes(sortedResults.value[key]['股票代码'])) {
|
||||
group[key] = sortedResults.value[key]
|
||||
if (currentGroupId.value === 0) {
|
||||
return sortedResults.value
|
||||
} else {
|
||||
for (const key in sortedResults.value) {
|
||||
if (stocks.value.includes(sortedResults.value[key]['股票代码'])) {
|
||||
group[key] = sortedResults.value[key]
|
||||
}
|
||||
}
|
||||
return group
|
||||
}
|
||||
return group
|
||||
})
|
||||
const showPopover = ref(false)
|
||||
// 拖拽相关变量
|
||||
const dragSourceIndex = ref(null)
|
||||
const dragTargetIndex = ref(null)
|
||||
|
||||
// 拖拽处理函数
|
||||
function handleTabDragStart(event, name) {
|
||||
// "全部"标签(name=0)不应该触发拖拽
|
||||
if (name === 0) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
dragSourceIndex.value = name;
|
||||
event.dataTransfer.effectAllowed = 'move';
|
||||
event.target.classList.add('tab-dragging');
|
||||
}
|
||||
|
||||
|
||||
function handleTabDragOver(event) {
|
||||
event.preventDefault()
|
||||
event.dataTransfer.dropEffect = 'move'
|
||||
}
|
||||
|
||||
function handleTabDragEnter(event, name) {
|
||||
event.preventDefault();
|
||||
// "全部"标签(name=0)不应该作为拖拽目标
|
||||
if (name > 0) {
|
||||
dragTargetIndex.value = name;
|
||||
if (event.target.classList) {
|
||||
// 查找最近的标签元素并添加高亮样式
|
||||
let tabElement = event.target.closest('.n-tabs-tab');
|
||||
if (tabElement) {
|
||||
tabElement.classList.add('tab-drag-over');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleTabDragLeave(event) {
|
||||
// 查找最近的标签元素并移除高亮样式
|
||||
let tabElement = event.target.closest('.n-tabs-tab')
|
||||
if (tabElement && tabElement.classList) {
|
||||
tabElement.classList.remove('tab-drag-over')
|
||||
}
|
||||
// 不要重置 dragTargetIndex,因为可能会在元素间快速移动
|
||||
}
|
||||
|
||||
function handleTabDrop(event) {
|
||||
event.preventDefault();
|
||||
|
||||
// 移除所有高亮样式
|
||||
const tabs = document.querySelectorAll('.n-tabs-tab');
|
||||
tabs.forEach(tab => {
|
||||
tab.classList.remove('tab-drag-over');
|
||||
});
|
||||
|
||||
if (dragSourceIndex.value !== null && dragTargetIndex.value !== null &&
|
||||
dragSourceIndex.value !== dragTargetIndex.value) {
|
||||
|
||||
// 确保索引有效(排除"全部"选项卡)
|
||||
if (dragSourceIndex.value > 0 && dragTargetIndex.value > 0) {
|
||||
// 查找源分组和目标分组
|
||||
const sourceGroup = groupList.value.find(g => g.ID === dragSourceIndex.value);
|
||||
const targetGroup = groupList.value.find(g => g.ID === dragTargetIndex.value);
|
||||
|
||||
if (sourceGroup && targetGroup) {
|
||||
// 计算新的位置序号(使用目标分组的sort值)
|
||||
const newSortPosition = targetGroup.sort;
|
||||
|
||||
// 调用后端API更新组排序
|
||||
UpdateGroupSort(sourceGroup.ID, newSortPosition).then(result => {
|
||||
if (result) {
|
||||
message.success('分组排序更新成功');
|
||||
// 重新获取分组列表以更新界面
|
||||
GetGroupList().then(result => {
|
||||
groupList.value = result;
|
||||
});
|
||||
} else {
|
||||
message.error('分组排序更新失败');
|
||||
}
|
||||
}).catch(error => {
|
||||
message.error('分组排序更新失败: ' + error.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重置状态
|
||||
dragSourceIndex.value = null;
|
||||
dragTargetIndex.value = null;
|
||||
}
|
||||
|
||||
function handleTabDragEnd(event) {
|
||||
// 移除所有高亮样式
|
||||
const tabs = document.querySelectorAll('.n-tabs-tab')
|
||||
tabs.forEach(tab => {
|
||||
tab.classList.remove('tab-drag-over', 'tab-dragging')
|
||||
})
|
||||
|
||||
dragSourceIndex.value = null
|
||||
dragTargetIndex.value = null
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
GetGroupList().then(result => {
|
||||
groupList.value = result
|
||||
if (route.query.groupId) {
|
||||
message.success("切换分组:" + route.query.groupName)
|
||||
currentGroupId.value = Number(route.query.groupId)
|
||||
//console.log("route.params",route.query)
|
||||
// 检查是否存在相同的序号
|
||||
const sorts = result.map(item => item.sort);
|
||||
const uniqueSorts = new Set(sorts);
|
||||
// 如果存在重复的序号,则重新初始化序号
|
||||
if (sorts.length !== uniqueSorts.size) {
|
||||
// 调用InitializeGroupSort重新初始化序号
|
||||
// 然后重新获取分组列表
|
||||
fetchGroupList();
|
||||
} else {
|
||||
// 没有重复序号,继续正常流程
|
||||
if (route.query.groupId) {
|
||||
message.success("切换分组:" + route.query.groupName)
|
||||
currentGroupId.value = Number(route.query.groupId)
|
||||
}
|
||||
}
|
||||
})
|
||||
GetStockList("").then(result => {
|
||||
@@ -214,20 +330,168 @@ onBeforeMount(() => {
|
||||
sysPromptOptions.value = promptTemplates.value.filter(item => item.type === '模型系统Prompt')
|
||||
userPromptOptions.value = promptTemplates.value.filter(item => item.type === '模型用户Prompt')
|
||||
|
||||
//console.log("userPromptOptions",userPromptOptions.value)
|
||||
//console.log("sysPromptOptions",sysPromptOptions.value)
|
||||
})
|
||||
|
||||
GetAiConfigs().then(res=>{
|
||||
GetAiConfigs().then(res => {
|
||||
aiConfigs.value = res
|
||||
data.aiConfigId =res[0].ID
|
||||
data.aiConfigId = res[0].ID
|
||||
})
|
||||
|
||||
EventsOn("loadingDone", (data) => {
|
||||
message.loading("刷新股票基础数据...")
|
||||
GetStockList("").then(result => {
|
||||
stockList.value = result
|
||||
options.value = result.map(item => {
|
||||
return {
|
||||
label: item.name + " - " + item.ts_code,
|
||||
value: item.ts_code
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
EventsOn("refresh", (data) => {
|
||||
message.success(data)
|
||||
})
|
||||
|
||||
EventsOn("showSearch", (data) => {
|
||||
addBTN.value = data === 1;
|
||||
})
|
||||
|
||||
EventsOn("stock_price", (data) => {
|
||||
updateData(data)
|
||||
})
|
||||
|
||||
EventsOn("refreshFollowList", (data) => {
|
||||
|
||||
WindowReload()
|
||||
})
|
||||
|
||||
EventsOn("newChatStream", async (msg) => {
|
||||
data.loading = false
|
||||
if (msg === "DONE") {
|
||||
SaveAIResponseResult(data.code, data.name, data.airesult, data.chatId, data.question, data.aiConfigId)
|
||||
message.info("AI分析完成!")
|
||||
message.destroyAll()
|
||||
} else {
|
||||
if (msg.chatId) {
|
||||
data.chatId = msg.chatId
|
||||
}
|
||||
if (msg.question) {
|
||||
data.question = msg.question
|
||||
}
|
||||
if (msg.content) {
|
||||
data.airesult = data.airesult + msg.content
|
||||
}
|
||||
if (msg.extraContent) {
|
||||
data.airesult = data.airesult + msg.extraContent
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
EventsOn("changeTab", async (msg) => {
|
||||
currentGroupId.value = Number(msg.ID)
|
||||
nextTick(() => {
|
||||
updateTab(currentGroupId.value);
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
EventsOn("updateVersion", async (msg) => {
|
||||
const githubTimeStr = msg.published_at;
|
||||
// 创建一个 Date 对象
|
||||
const utcDate = new Date(githubTimeStr);
|
||||
// 获取本地时间
|
||||
const date = new Date(utcDate.getTime());
|
||||
const year = date.getFullYear();
|
||||
// getMonth 返回值是 0 - 11,所以要加 1
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||
|
||||
const formattedDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
|
||||
notify.info({
|
||||
avatar: () =>
|
||||
h(NAvatar, {
|
||||
size: 'small',
|
||||
round: false,
|
||||
src: icon.value
|
||||
}),
|
||||
title: '发现新版本: ' + msg.tag_name,
|
||||
content: () => {
|
||||
//return h(MdPreview, {theme:'dark',modelValue:msg.commit?.message}, null)
|
||||
return h('div', {
|
||||
style: {
|
||||
'text-align': 'left',
|
||||
'font-size': '14px',
|
||||
}
|
||||
}, {default: () => msg.commit?.message})
|
||||
},
|
||||
duration: 5000,
|
||||
meta: "发布时间:" + formattedDate,
|
||||
action: () => {
|
||||
return h(NButton, {
|
||||
type: 'primary',
|
||||
size: 'small',
|
||||
onClick: () => {
|
||||
Environment().then(env => {
|
||||
switch (env.platform) {
|
||||
case 'windows':
|
||||
window.open(msg.html_url)
|
||||
break
|
||||
default :
|
||||
OpenURL(msg.html_url)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, {default: () => '查看'})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
EventsOn("warnMsg", async (msg) => {
|
||||
notify.error({
|
||||
avatar: () =>
|
||||
h(NAvatar, {
|
||||
size: 'small',
|
||||
round: false,
|
||||
src: icon.value
|
||||
}),
|
||||
title: '警告',
|
||||
duration: 5000,
|
||||
content: () => {
|
||||
return h('div', {
|
||||
style: {
|
||||
'text-align': 'left',
|
||||
'font-size': '14px',
|
||||
}
|
||||
}, {default: () => msg})
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
message.loading("Loading...")
|
||||
nextTick(() => {
|
||||
initDraggableTabs();
|
||||
});
|
||||
|
||||
// 监听分组列表变化,重新初始化拖拽
|
||||
const unwatch = watch(groupList, () => {
|
||||
nextTick(() => {
|
||||
initDraggableTabs();
|
||||
});
|
||||
});
|
||||
|
||||
// 在组件卸载时清理监听器
|
||||
onBeforeUnmount(() => {
|
||||
unwatch();
|
||||
});
|
||||
message.loading("Loading...")
|
||||
GetFollowList(currentGroupId.value).then(result => {
|
||||
|
||||
followList.value = result
|
||||
@@ -246,7 +510,6 @@ onMounted(() => {
|
||||
message.destroyAll()
|
||||
})
|
||||
|
||||
|
||||
GetVersionInfo().then((res) => {
|
||||
icon.value = res.icon;
|
||||
});
|
||||
@@ -272,6 +535,48 @@ onMounted(() => {
|
||||
//console.log('WebSocket 连接已关闭');
|
||||
};
|
||||
})
|
||||
// 清理拖拽事件监听器
|
||||
// 清理拖拽事件监听器
|
||||
function cleanupDraggableTabs() {
|
||||
const tabs = document.querySelectorAll('.n-tabs-tab');
|
||||
tabs.forEach((tab) => {
|
||||
// 移除所有可能的拖拽事件监听器
|
||||
tab.removeEventListener('dragstart', handleTabDragStart);
|
||||
tab.removeEventListener('dragover', handleTabDragOver);
|
||||
tab.removeEventListener('dragenter', handleTabDragEnter);
|
||||
tab.removeEventListener('dragleave', handleTabDragLeave);
|
||||
tab.removeEventListener('drop', handleTabDrop);
|
||||
tab.removeEventListener('dragend', handleTabDragEnd);
|
||||
// 移除draggable属性
|
||||
tab.removeAttribute('draggable');
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化可拖拽选项卡
|
||||
function initDraggableTabs() {
|
||||
// 移除之前可能添加的事件监听器
|
||||
cleanupDraggableTabs();
|
||||
|
||||
// 添加拖拽事件监听器到选项卡元素
|
||||
setTimeout(() => {
|
||||
const tabs = document.querySelectorAll('.n-tabs-tab');
|
||||
tabs.forEach((tab, index) => {
|
||||
const dataIndex = tab.getAttribute('data-name');
|
||||
const name = parseInt(dataIndex);
|
||||
|
||||
// 只为分组标签(name > 0)添加拖拽功能
|
||||
if (name > 0) {
|
||||
tab.setAttribute('draggable', 'true');
|
||||
tab.addEventListener('dragstart', (e) => handleTabDragStart(e, name));
|
||||
tab.addEventListener('dragover', handleTabDragOver);
|
||||
tab.addEventListener('dragenter', (e) => handleTabDragEnter(e, name));
|
||||
tab.addEventListener('dragleave', handleTabDragLeave);
|
||||
tab.addEventListener('drop', handleTabDrop);
|
||||
tab.addEventListener('dragend', handleTabDragEnd);
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
// //console.log(`the component is now unmounted.`)
|
||||
@@ -290,146 +595,9 @@ onBeforeUnmount(() => {
|
||||
EventsOff("updateVersion")
|
||||
EventsOff("warnMsg")
|
||||
EventsOff("loadingDone")
|
||||
})
|
||||
|
||||
EventsOn("loadingDone", (data) => {
|
||||
message.loading("刷新股票基础数据...")
|
||||
GetStockList("").then(result => {
|
||||
stockList.value = result
|
||||
options.value = result.map(item => {
|
||||
return {
|
||||
label: item.name + " - " + item.ts_code,
|
||||
value: item.ts_code
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
cleanupDraggableTabs()
|
||||
|
||||
EventsOn("refresh", (data) => {
|
||||
message.success(data)
|
||||
})
|
||||
|
||||
EventsOn("showSearch", (data) => {
|
||||
addBTN.value = data === 1;
|
||||
})
|
||||
|
||||
EventsOn("stock_price", (data) => {
|
||||
updateData(data)
|
||||
})
|
||||
|
||||
EventsOn("refreshFollowList", (data) => {
|
||||
|
||||
WindowReload()
|
||||
})
|
||||
|
||||
EventsOn("newChatStream", async (msg) => {
|
||||
////console.log("newChatStream:->",data.airesult)
|
||||
data.loading = false
|
||||
////console.log(msg)
|
||||
if (msg === "DONE") {
|
||||
SaveAIResponseResult(data.code, data.name, data.airesult, data.chatId, data.question,data.aiConfigId)
|
||||
message.info("AI分析完成!")
|
||||
message.destroyAll()
|
||||
} else {
|
||||
if (msg.chatId) {
|
||||
data.chatId = msg.chatId
|
||||
}
|
||||
if (msg.question) {
|
||||
data.question = msg.question
|
||||
}
|
||||
if (msg.content) {
|
||||
data.airesult = data.airesult + msg.content
|
||||
}
|
||||
if (msg.extraContent) {
|
||||
data.airesult = data.airesult + msg.extraContent
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
EventsOn("changeTab", async (msg) => {
|
||||
//console.log("changeTab",msg)
|
||||
currentGroupId.value = msg.ID
|
||||
updateTab(currentGroupId.value)
|
||||
})
|
||||
|
||||
|
||||
EventsOn("updateVersion", async (msg) => {
|
||||
const githubTimeStr = msg.published_at;
|
||||
// 创建一个 Date 对象
|
||||
const utcDate = new Date(githubTimeStr);
|
||||
// 获取本地时间
|
||||
const date = new Date(utcDate.getTime());
|
||||
const year = date.getFullYear();
|
||||
// getMonth 返回值是 0 - 11,所以要加 1
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||
|
||||
const formattedDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
|
||||
//console.log("GitHub UTC 时间:", utcDate);
|
||||
//console.log("转换后的本地时间:", formattedDate);
|
||||
notify.info({
|
||||
avatar: () =>
|
||||
h(NAvatar, {
|
||||
size: 'small',
|
||||
round: false,
|
||||
src: icon.value
|
||||
}),
|
||||
title: '发现新版本: ' + msg.tag_name,
|
||||
content: () => {
|
||||
//return h(MdPreview, {theme:'dark',modelValue:msg.commit?.message}, null)
|
||||
return h('div', {
|
||||
style: {
|
||||
'text-align': 'left',
|
||||
'font-size': '14px',
|
||||
}
|
||||
}, {default: () => msg.commit?.message})
|
||||
},
|
||||
duration: 5000,
|
||||
meta: "发布时间:" + formattedDate,
|
||||
action: () => {
|
||||
return h(NButton, {
|
||||
type: 'primary',
|
||||
size: 'small',
|
||||
onClick: () => {
|
||||
Environment().then(env => {
|
||||
switch (env.platform) {
|
||||
case 'windows':
|
||||
window.open(msg.html_url)
|
||||
break
|
||||
default :
|
||||
OpenURL(msg.html_url)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, {default: () => '查看'})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
EventsOn("warnMsg", async (msg) => {
|
||||
notify.error({
|
||||
avatar: () =>
|
||||
h(NAvatar, {
|
||||
size: 'small',
|
||||
round: false,
|
||||
src: icon.value
|
||||
}),
|
||||
title: '警告',
|
||||
duration: 5000,
|
||||
content: () => {
|
||||
return h('div', {
|
||||
style: {
|
||||
'text-align': 'left',
|
||||
'font-size': '14px',
|
||||
}
|
||||
}, {default: () => msg})
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
//判断是否是A股交易时间
|
||||
@@ -452,6 +620,23 @@ function isTradingTime() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 添加一个获取分组列表的函数,用于处理初始化逻辑
|
||||
function fetchGroupList() {
|
||||
InitializeGroupSort().then(initResult => {
|
||||
if (initResult) {
|
||||
GetGroupList().then(result => {
|
||||
groupList.value = result
|
||||
if (route.query.groupId) {
|
||||
message.success("切换分组:" + route.query.groupName)
|
||||
currentGroupId.value = Number(route.query.groupId)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
message.error("初始化分组序号失败")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function AddStock() {
|
||||
if (!data?.code) {
|
||||
message.error("请输入有效股票代码");
|
||||
@@ -1395,7 +1580,7 @@ function aiReCheckStock(stock, stockCode) {
|
||||
//
|
||||
|
||||
//message.info("sysPromptId:"+data.sysPromptId)
|
||||
NewChatStream(stock, stockCode, data.question,data.aiConfigId, data.sysPromptId, enableTools.value)
|
||||
NewChatStream(stock, stockCode, data.question, data.aiConfigId, data.sysPromptId, enableTools.value)
|
||||
}
|
||||
|
||||
function aiCheckStock(stock, stockCode) {
|
||||
@@ -1499,14 +1684,14 @@ function saveAsImage(name, code) {
|
||||
}
|
||||
|
||||
async function saveCanvasImage(name) {
|
||||
const element = document.querySelector('.md-editor-preview'); // 要截图的 DOM 节点
|
||||
const element = document.querySelector('.md-editor-preview'); // 要截图的 DOM 节点
|
||||
const canvas = await html2canvas(element)
|
||||
|
||||
const dataUrl = canvas.toDataURL('image/png') // base64 格式
|
||||
const base64 = dataUrl.replace(/^data:image\/png;base64,/, '')
|
||||
|
||||
// 调用 Go 后端保存文件(Wails 绑定方法)
|
||||
await SaveImage(name,base64).then(result => {
|
||||
await SaveImage(name, base64).then(result => {
|
||||
message.success(result)
|
||||
})
|
||||
}
|
||||
@@ -1568,7 +1753,7 @@ AI赋能股票分析:自选股行情获取,成本盈亏展示,涨跌报警
|
||||
`
|
||||
// landscape就是横着的,portrait是竖着的,默认是竖屏portrait。
|
||||
const blob = await asBlob(value, {orientation: 'portrait'})
|
||||
const { platform } = await Environment()
|
||||
const {platform} = await Environment()
|
||||
switch (platform) {
|
||||
case 'windows':
|
||||
const a = document.createElement('a')
|
||||
@@ -1580,13 +1765,13 @@ AI赋能股票分析:自选股行情获取,成本盈亏展示,涨跌报警
|
||||
a.remove()
|
||||
break
|
||||
default:
|
||||
const arrayBuffer = await blob.arrayBuffer()
|
||||
const uint8Array = new Uint8Array(arrayBuffer)
|
||||
const binary = uint8Array.reduce((data, byte) => data + String.fromCharCode(byte), '')
|
||||
const base64 = btoa(binary)
|
||||
await SaveWordFile(`${data.name}[${data.code}]-ai-analysis-result.docx`, base64).then(result => {
|
||||
message.success(result)
|
||||
})
|
||||
const arrayBuffer = await blob.arrayBuffer()
|
||||
const uint8Array = new Uint8Array(arrayBuffer)
|
||||
const binary = uint8Array.reduce((data, byte) => data + String.fromCharCode(byte), '')
|
||||
const base64 = btoa(binary)
|
||||
await SaveWordFile(`${data.name}[${data.code}]-ai-analysis-result.docx`, base64).then(result => {
|
||||
message.success(result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1649,9 +1834,11 @@ function AddStockGroupInfo(groupId, code, name) {
|
||||
|
||||
function updateTab(name) {
|
||||
stocks.value = []
|
||||
currentGroupId.value = Number(name)
|
||||
GetFollowList(currentGroupId.value).then(result => {
|
||||
const tabId= Number(name)
|
||||
currentGroupId.value = tabId;
|
||||
GetFollowList(tabId).then(result => {
|
||||
followList.value = result
|
||||
|
||||
for (const followedStock of result) {
|
||||
if (followedStock.StockCode.startsWith("us")) {
|
||||
followedStock.StockCode = "gb_" + followedStock.StockCode.replace("us", "").toLowerCase()
|
||||
@@ -1724,8 +1911,9 @@ function searchStockReport(stockCode) {
|
||||
</template>
|
||||
</vue-danmaku>
|
||||
<n-tabs type="card" style="--wails-draggable:no-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="'全部'">
|
||||
:value="String(currentGroupId)" @add="addTab" @update:value="updateTab" placement="top" @close="(key)=>{delTab(key)}">
|
||||
|
||||
<n-tab-pane closable 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"
|
||||
@@ -1864,7 +2052,7 @@ function searchStockReport(stockCode) {
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane closable v-for="group in groupList" :group-id="group.ID" :name="group.ID" :tab="group.name">
|
||||
<n-tab-pane closable v-for="group in groupList" :group-id="group.ID" :name="String(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"
|
||||
@@ -2011,6 +2199,7 @@ function searchStockReport(stockCode) {
|
||||
</n-grid>
|
||||
</n-tab-pane>
|
||||
</n-tabs>
|
||||
|
||||
<div style="position: fixed;bottom: 18px;right:5px;z-index: 10;width: 400px">
|
||||
<!-- <n-card :bordered="false">-->
|
||||
<n-input-group>
|
||||
@@ -2228,4 +2417,38 @@ function searchStockReport(stockCode) {
|
||||
border-color: red;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
/* 所有标签的通用样式 */
|
||||
:deep(.n-tabs-nav .n-tabs-tab) {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 可拖拽标签的样式 */
|
||||
:deep(.n-tabs-nav .n-tabs-tab[draggable="true"]) {
|
||||
user-select: none;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.tab-drag-over {
|
||||
background-color: #e6f7ff !important;
|
||||
border: 2px dashed #1890ff !important;
|
||||
transform: scale(1.02);
|
||||
transition: all 0.2s ease;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.tab-drag-over::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.tab-dragging {
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
4
frontend/wailsjs/go/main/App.d.ts
vendored
4
frontend/wailsjs/go/main/App.d.ts
vendored
@@ -88,6 +88,8 @@ export function HotTopic(arg1:number):Promise<Array<any>>;
|
||||
|
||||
export function IndustryResearchReport(arg1:string):Promise<Array<any>>;
|
||||
|
||||
export function InitializeGroupSort():Promise<boolean>;
|
||||
|
||||
export function InvestCalendarTimeLine(arg1:string):Promise<Array<any>>;
|
||||
|
||||
export function LongTigerRank(arg1:string):Promise<any>;
|
||||
@@ -139,3 +141,5 @@ export function UnFollow(arg1:string):Promise<string>;
|
||||
export function UnFollowFund(arg1:string):Promise<string>;
|
||||
|
||||
export function UpdateConfig(arg1:data.SettingConfig):Promise<string>;
|
||||
|
||||
export function UpdateGroupSort(arg1:number,arg2:number):Promise<boolean>;
|
||||
|
||||
@@ -170,6 +170,10 @@ export function IndustryResearchReport(arg1) {
|
||||
return window['go']['main']['App']['IndustryResearchReport'](arg1);
|
||||
}
|
||||
|
||||
export function InitializeGroupSort() {
|
||||
return window['go']['main']['App']['InitializeGroupSort']();
|
||||
}
|
||||
|
||||
export function InvestCalendarTimeLine(arg1) {
|
||||
return window['go']['main']['App']['InvestCalendarTimeLine'](arg1);
|
||||
}
|
||||
@@ -273,3 +277,7 @@ export function UnFollowFund(arg1) {
|
||||
export function UpdateConfig(arg1) {
|
||||
return window['go']['main']['App']['UpdateConfig'](arg1);
|
||||
}
|
||||
|
||||
export function UpdateGroupSort(arg1, arg2) {
|
||||
return window['go']['main']['App']['UpdateGroupSort'](arg1, arg2);
|
||||
}
|
||||
|
||||
12
go.mod
12
go.mod
@@ -28,7 +28,7 @@ require (
|
||||
github.com/wailsapp/wails/v2 v2.10.1
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/net v0.38.0
|
||||
golang.org/x/sys v0.33.0
|
||||
golang.org/x/sys v0.35.0
|
||||
golang.org/x/text v0.26.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gorm.io/gorm v1.25.12
|
||||
@@ -40,12 +40,12 @@ require (
|
||||
git.sr.ht/~jackmordaunt/go-toast v1.1.2 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/bep/debounce v1.2.1 // indirect
|
||||
github.com/bytedance/sonic v1.13.2 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
||||
github.com/bytedance/sonic v1.14.0 // indirect
|
||||
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/chromedp/cdproto v0.0.0-20241022234722-4d5d5faf59fb // indirect
|
||||
github.com/chromedp/sysutil v1.1.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/cloudwego/eino-ext/libs/acl/openai v0.0.0-20250804062529-6e67726a4b3f // indirect
|
||||
github.com/cohesion-org/deepseek-go v1.3.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
@@ -72,7 +72,7 @@ require (
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/labstack/echo/v4 v4.13.3 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/leaanthony/go-ansi-parser v1.6.1 // indirect
|
||||
@@ -119,7 +119,7 @@ require (
|
||||
github.com/wailsapp/mimetype v1.4.1 // indirect
|
||||
github.com/yargevad/filepathx v1.0.0 // indirect
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
golang.org/x/arch v0.11.0 // indirect
|
||||
golang.org/x/arch v0.20.0 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect
|
||||
gopkg.in/sourcemap.v1 v1.0.5 // indirect
|
||||
|
||||
29
go.sum
29
go.sum
@@ -16,11 +16,10 @@ github.com/bugsnag/bugsnag-go v1.4.0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqR
|
||||
github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||
github.com/bytedance/mockey v1.2.14 h1:KZaFgPdiUwW+jOWFieo3Lr7INM1P+6adO3hxZhDswY8=
|
||||
github.com/bytedance/mockey v1.2.14/go.mod h1:1BPHF9sol5R1ud/+0VEHGQq/+i2lN+GTsr3O2Q9IENY=
|
||||
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
|
||||
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
||||
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
|
||||
github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA=
|
||||
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
|
||||
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/certifi/gocertifi v0.0.0-20190105021004-abcd57078448/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
@@ -32,8 +31,8 @@ github.com/chromedp/chromedp v0.11.2/go.mod h1:lr8dFRLKsdTTWb75C/Ttol2vnBKOSnt0B
|
||||
github.com/chromedp/sysutil v1.1.0 h1:PUFNv5EcprjqXZD9nJb9b/c9ibAbxiYo4exNWZyipwM=
|
||||
github.com/chromedp/sysutil v1.1.0/go.mod h1:WiThHUdltqCNKGc4gaU50XgYjwjYIhKWoHGPTUfWTJ8=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||
github.com/cloudwego/eino v0.4.1 h1:Jy9KWpCvd+Z75oIynhHsT9dEECUuCW8IPZlVjHgVu9s=
|
||||
github.com/cloudwego/eino v0.4.1/go.mod h1:wUjz990apdsaOraOXdh6CdhVXq8DJsOvLsVlxNTcNfY=
|
||||
github.com/cloudwego/eino-ext/components/model/ark v0.1.19 h1:XYnOeszXA28T1gxYOpTIjOjLCPO2gjexK+ShSan9u/8=
|
||||
@@ -44,7 +43,6 @@ github.com/cloudwego/eino-ext/components/model/openai v0.0.0-20250804092122-8845
|
||||
github.com/cloudwego/eino-ext/components/model/openai v0.0.0-20250804092122-8845979a2228/go.mod h1:3uBZ/GzJzh1izfY2w62282FZrQG3ISs6T/jTmmPffvE=
|
||||
github.com/cloudwego/eino-ext/libs/acl/openai v0.0.0-20250804062529-6e67726a4b3f h1:J1tQBg6RDftrtm3vsv+ozlupdlNV+WGslpXiTDr/2xI=
|
||||
github.com/cloudwego/eino-ext/libs/acl/openai v0.0.0-20250804062529-6e67726a4b3f/go.mod h1:4EBgz8+68n1iuKyWC37Tu9NG1WJkPm+yLxvyLik28Us=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/cohesion-org/deepseek-go v1.3.2 h1:WTZ/2346KFYca+n+DL5p+Ar1RQxF2w/wGkU4jDvyXaQ=
|
||||
github.com/cohesion-org/deepseek-go v1.3.2/go.mod h1:bOVyKj38r90UEYZFrmJOzJKPxuAh8sIzHOCnLOpiXeI=
|
||||
github.com/coocood/freecache v1.2.4 h1:UdR6Yz/X1HW4fZOuH0Z94KwG851GWOSknua5VUbb/5M=
|
||||
@@ -162,10 +160,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
@@ -343,8 +339,8 @@ go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
|
||||
golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
|
||||
golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
@@ -413,8 +409,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -512,4 +508,3 @@ modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
|
||||
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
|
||||
17
main.go
17
main.go
@@ -5,6 +5,14 @@ import (
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go-stock/backend/data"
|
||||
"go-stock/backend/db"
|
||||
log "go-stock/backend/logger"
|
||||
"go-stock/backend/models"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
"github.com/wailsapp/wails/v2"
|
||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||
@@ -13,13 +21,6 @@ import (
|
||||
"github.com/wailsapp/wails/v2/pkg/options/mac"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/windows"
|
||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||
"go-stock/backend/data"
|
||||
"go-stock/backend/db"
|
||||
log "go-stock/backend/logger"
|
||||
"go-stock/backend/models"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//go:embed frontend/dist
|
||||
@@ -125,7 +126,7 @@ func main() {
|
||||
err = wails.Run(&options.App{
|
||||
Title: "go-stock:AI赋能股票分析✨",
|
||||
Width: width * 4 / 5,
|
||||
Height: 950,
|
||||
Height: 920,
|
||||
MinWidth: minWidth,
|
||||
MinHeight: minHeight,
|
||||
//MaxWidth: width,
|
||||
|
||||
Reference in New Issue
Block a user