stock-ai-agent/frontend/js/app.js.backup
2026-02-03 10:08:15 +08:00

220 lines
7.0 KiB
Plaintext

// Vue 3 应用
const { createApp } = Vue;
createApp({
data() {
return {
messages: [],
userInput: '',
loading: false,
sessionId: null,
showSkillPanel: false,
skills: [],
charts: {}
};
},
mounted() {
this.loadSkills();
// 生成会话ID
this.sessionId = this.generateSessionId();
},
methods: {
async sendMessage() {
if (!this.userInput.trim() || this.loading) return;
const message = this.userInput.trim();
this.userInput = '';
// 添加用户消息
this.messages.push({
role: 'user',
content: message,
timestamp: new Date()
});
// 滚动到底部
this.$nextTick(() => {
this.scrollToBottom();
});
// 发送请求
this.loading = true;
try {
const response = await fetch('/api/chat/message', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
message: message,
session_id: this.sessionId
})
});
if (!response.ok) {
throw new Error('请求失败');
}
const data = await response.json();
// 添加助手消息
const assistantMessage = {
role: 'assistant',
content: data.message,
timestamp: new Date(),
metadata: data.metadata
};
this.messages.push(assistantMessage);
// 如果有图表数据,渲染图表
if (data.metadata && data.metadata.type === 'chart') {
this.$nextTick(() => {
const index = this.messages.length - 1;
this.renderChart(index, data.metadata.data);
});
}
// 滚动到底部
this.$nextTick(() => {
this.scrollToBottom();
});
} catch (error) {
console.error('发送消息失败:', error);
this.messages.push({
role: 'assistant',
content: '抱歉,发送消息失败,请稍后重试。',
timestamp: new Date()
});
} finally {
this.loading = false;
}
},
async loadSkills() {
try {
const response = await fetch('/api/skills/');
if (!response.ok) {
throw new Error('加载技能失败');
}
const data = await response.json();
this.skills = data.skills;
} catch (error) {
console.error('加载技能失败:', error);
}
},
async toggleSkill(skillName, enabled) {
try {
const endpoint = enabled ? 'enable' : 'disable';
const response = await fetch(`/api/skills/${skillName}/${endpoint}`, {
method: 'POST'
});
if (!response.ok) {
throw new Error('切换技能失败');
}
// 重新加载技能列表
await this.loadSkills();
} catch (error) {
console.error('切换技能失败:', error);
// 恢复原状态
await this.loadSkills();
}
},
renderChart(index, chartData) {
const containerId = `chart-${index}`;
const container = document.getElementById(containerId);
if (!container || !chartData) return;
try {
// 创建图表
const chart = LightweightCharts.createChart(container, {
width: container.clientWidth,
height: 400,
layout: {
background: { color: '#ffffff' },
textColor: '#333',
},
grid: {
vertLines: { color: '#f0f0f0' },
horzLines: { color: '#f0f0f0' },
},
timeScale: {
borderColor: '#cccccc',
},
});
// 添加K线图
if (chartData.candlestick_data) {
const candlestickSeries = chart.addCandlestickSeries({
upColor: '#26a69a',
downColor: '#ef5350',
borderVisible: false,
wickUpColor: '#26a69a',
wickDownColor: '#ef5350',
});
candlestickSeries.setData(chartData.candlestick_data);
}
// 添加成交量
if (chartData.volume_data) {
const volumeSeries = chart.addHistogramSeries({
color: '#26a69a',
priceFormat: {
type: 'volume',
},
priceScaleId: '',
scaleMargins: {
top: 0.8,
bottom: 0,
},
});
volumeSeries.setData(chartData.volume_data);
}
// 自适应大小
chart.timeScale().fitContent();
// 保存图表实例
this.charts[containerId] = chart;
// 窗口大小改变时调整图表
window.addEventListener('resize', () => {
if (this.charts[containerId]) {
this.charts[containerId].applyOptions({
width: container.clientWidth
});
}
});
} catch (error) {
console.error('渲染图表失败:', error);
}
},
scrollToBottom() {
const container = this.$refs.messagesContainer;
if (container) {
container.scrollTop = container.scrollHeight;
}
},
formatTime(timestamp) {
const date = new Date(timestamp);
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
return `${hours}:${minutes}`;
},
generateSessionId() {
return 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
}
}).mount('#app');