|
|
@ -10,7 +10,8 @@ import { marked } from 'marked'; // 引入marked库 |
|
|
|
import katex from 'katex'; // 引入 KaTeX 库 |
|
|
|
import { htmlToText } from 'html-to-text'; |
|
|
|
import { Howl, Howler } from 'howler'; |
|
|
|
import KLine from './Echarts/KLine.vue' |
|
|
|
import KLine from './Echarts/KLine.vue'; |
|
|
|
import * as echarts from 'echarts' |
|
|
|
const chatStore = useChatStore() |
|
|
|
const audioStore = useAudioStore() |
|
|
|
const dataStore = useDataStore() |
|
|
@ -179,127 +180,152 @@ watch( |
|
|
|
|
|
|
|
const AIcontent = ref(""); |
|
|
|
// 处理不同的 answer 字段 |
|
|
|
if (ans.value.answerG !== "") { |
|
|
|
AIcontent.value = ans.value.answerG; |
|
|
|
const code = ans.value.code; |
|
|
|
const market = ans.value.market; |
|
|
|
const data = JSON.parse(ans.value.duobaoData); |
|
|
|
|
|
|
|
console.log(data, "data"); |
|
|
|
const Kline20 = { |
|
|
|
name: data.data.HomePage.StockInformation.Name, |
|
|
|
Kline: data.data.AIBull.KLine20 |
|
|
|
} |
|
|
|
dataStore.setKlineData(Kline20); |
|
|
|
const status = JSON.parse(ans.value.resp); |
|
|
|
console.log(status, "status") |
|
|
|
if (status.code == 200) { |
|
|
|
if (ans.value.answerG !== "") { |
|
|
|
AIcontent.value = ans.value.answerG; |
|
|
|
const code = ans.value.code; |
|
|
|
const market = ans.value.market; |
|
|
|
const data = JSON.parse(ans.value.duobaoData); |
|
|
|
|
|
|
|
console.log(data, "data"); |
|
|
|
|
|
|
|
const Kline20 = { |
|
|
|
name: data.data.HomePage.StockInformation.Name, |
|
|
|
Kline: data.data.AIBull.KLine20 |
|
|
|
} |
|
|
|
|
|
|
|
chatStore.messages.pop(); |
|
|
|
dataStore.setKlineData(Kline20); |
|
|
|
|
|
|
|
chatStore.messages.push({ |
|
|
|
sender: "ai", |
|
|
|
type: "kline", |
|
|
|
}); |
|
|
|
// chatStore.chartData.push({ |
|
|
|
// data: Kline20.Kline |
|
|
|
// }); |
|
|
|
// for (let i = 0; i < chatStore.chartData.length; i++) { |
|
|
|
// console.log(chatStore.chartData[i], "chatStore.chartData[i]") |
|
|
|
// } |
|
|
|
|
|
|
|
chatStore.messages.push({ |
|
|
|
sender: "ai", |
|
|
|
content: "AI正在思考中..." |
|
|
|
}); |
|
|
|
chatStore.messages.pop(); |
|
|
|
|
|
|
|
console.log(Kline20, "Kline20"); |
|
|
|
chatStore.messages.push({ |
|
|
|
sender: "ai", |
|
|
|
type: "kline", |
|
|
|
chartRef: Kline20.name, // 唯一标识符 |
|
|
|
chartData: Kline20.Kline, // 图表数据 |
|
|
|
}); |
|
|
|
|
|
|
|
console.log(code, "code"); |
|
|
|
console.log(market, "market"); |
|
|
|
console.log(data, "data"); |
|
|
|
|
|
|
|
} else if (ans.value.answerN !== "") { |
|
|
|
AIcontent.value = ans.value.answerN; |
|
|
|
} else if (ans.value.answerO !== "") { |
|
|
|
AIcontent.value = ans.value.answerO; |
|
|
|
} |
|
|
|
chatStore.messages.push({ |
|
|
|
sender: "ai", |
|
|
|
content: "AI正在思考中..." |
|
|
|
}); |
|
|
|
|
|
|
|
// // 使用marked库将Markdown转换为HTML |
|
|
|
// AIcontent.value = marked(AIcontent.value,); |
|
|
|
// // 使用 KaTeX 渲染数学公式 |
|
|
|
// const katexRegex = /\$\$(.*?)\$\$/g; |
|
|
|
// AIcontent.value = AIcontent.value.replace(katexRegex, (match, formula) => { |
|
|
|
// try { |
|
|
|
// return katex.renderToString(formula, { throwOnError: false }); |
|
|
|
// } catch (error) { |
|
|
|
// console.error('KaTeX 渲染错误:', error); |
|
|
|
// return match; |
|
|
|
// } |
|
|
|
// }); |
|
|
|
|
|
|
|
// chatStore.messages.push({ |
|
|
|
// sender: "ai", |
|
|
|
// content: AIcontent.value |
|
|
|
// }) |
|
|
|
|
|
|
|
// 修改后的消息处理逻辑 |
|
|
|
const processedContent = marked(AIcontent.value); |
|
|
|
const katexRegex = /\$\$(.*?)\$\$/g; |
|
|
|
const plainTextContent = htmlToText(processedContent); |
|
|
|
|
|
|
|
// 获取音频数据 |
|
|
|
const TTSResult = (await TTSAPI({ |
|
|
|
language: "cn", |
|
|
|
content: plainTextContent |
|
|
|
})).json() |
|
|
|
console.log(Kline20, "Kline20"); |
|
|
|
|
|
|
|
const tts = ref(); |
|
|
|
await TTSResult.then((res) => { |
|
|
|
tts.value = JSON.parse(res.data); |
|
|
|
}) |
|
|
|
console.log(code, "code"); |
|
|
|
console.log(market, "market"); |
|
|
|
console.log(data, "data"); |
|
|
|
|
|
|
|
const ttsUrl = ref(); |
|
|
|
if (tts.value.tts_cn !== null) { |
|
|
|
ttsUrl.value = tts.value.tts_cn.url; |
|
|
|
} else if (tts.value.tts_en !== null) { |
|
|
|
ttsUrl.value = tts.value.tts_en.url; |
|
|
|
} |
|
|
|
} else if (ans.value.answerN !== "") { |
|
|
|
AIcontent.value = ans.value.answerN; |
|
|
|
} else if (ans.value.answerO !== "") { |
|
|
|
AIcontent.value = ans.value.answerO; |
|
|
|
} |
|
|
|
|
|
|
|
// // 使用marked库将Markdown转换为HTML |
|
|
|
// AIcontent.value = marked(AIcontent.value,); |
|
|
|
// // 使用 KaTeX 渲染数学公式 |
|
|
|
// const katexRegex = /\$\$(.*?)\$\$/g; |
|
|
|
// AIcontent.value = AIcontent.value.replace(katexRegex, (match, formula) => { |
|
|
|
// try { |
|
|
|
// return katex.renderToString(formula, { throwOnError: false }); |
|
|
|
// } catch (error) { |
|
|
|
// console.error('KaTeX 渲染错误:', error); |
|
|
|
// return match; |
|
|
|
// } |
|
|
|
// }); |
|
|
|
|
|
|
|
// chatStore.messages.push({ |
|
|
|
// sender: "ai", |
|
|
|
// content: AIcontent.value |
|
|
|
// }) |
|
|
|
|
|
|
|
// 修改后的消息处理逻辑 |
|
|
|
const processedContent = marked(AIcontent.value); |
|
|
|
const katexRegex = /\$\$(.*?)\$\$/g; |
|
|
|
const plainTextContent = htmlToText(processedContent); |
|
|
|
|
|
|
|
// 获取音频数据 |
|
|
|
const TTSResult = (await TTSAPI({ |
|
|
|
language: "cn", |
|
|
|
content: plainTextContent |
|
|
|
})).json() |
|
|
|
|
|
|
|
const tts = ref(); |
|
|
|
await TTSResult.then((res) => { |
|
|
|
tts.value = JSON.parse(res.data); |
|
|
|
}) |
|
|
|
|
|
|
|
const ttsUrl = ref(); |
|
|
|
if (tts.value.tts_cn !== null) { |
|
|
|
ttsUrl.value = tts.value.tts_cn.url; |
|
|
|
} else if (tts.value.tts_en !== null) { |
|
|
|
ttsUrl.value = tts.value.tts_en.url; |
|
|
|
} |
|
|
|
|
|
|
|
if (ttsUrl.value) { |
|
|
|
nextTick(() => { |
|
|
|
if (audioStore.isVoiceEnabled) { |
|
|
|
console.log("ttsUrl.value", ttsUrl.value) |
|
|
|
// 播放音频 |
|
|
|
playAudio(ttsUrl.value) |
|
|
|
if (ttsUrl.value) { |
|
|
|
nextTick(() => { |
|
|
|
if (audioStore.isVoiceEnabled) { |
|
|
|
console.log("ttsUrl.value", ttsUrl.value) |
|
|
|
// 播放音频 |
|
|
|
playAudio(ttsUrl.value) |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
chatStore.messages.pop(); |
|
|
|
// 先推送初始消息 |
|
|
|
const aiMessage = reactive({ |
|
|
|
sender: "ai", |
|
|
|
content: "", |
|
|
|
isTyping: true, |
|
|
|
}); |
|
|
|
chatStore.messages.push(aiMessage); |
|
|
|
|
|
|
|
let index = 0; |
|
|
|
const typingInterval = setInterval(() => { |
|
|
|
if (index < processedContent.length) { |
|
|
|
aiMessage.content += processedContent.charAt(index); |
|
|
|
index++; |
|
|
|
|
|
|
|
} else { |
|
|
|
clearInterval(typingInterval); |
|
|
|
aiMessage.isTyping = false; |
|
|
|
|
|
|
|
// 延迟处理KaTeX确保DOM已更新 |
|
|
|
nextTick(() => { |
|
|
|
aiMessage.content = aiMessage.content.replace(katexRegex, (match, formula) => { |
|
|
|
try { |
|
|
|
return katex.renderToString(formula, { throwOnError: false }); |
|
|
|
} catch (error) { |
|
|
|
console.error('KaTeX 渲染错误:', error); |
|
|
|
return match; |
|
|
|
} |
|
|
|
}); |
|
|
|
chatStore.setLoading(false); |
|
|
|
}); |
|
|
|
} |
|
|
|
}, 50); // 调整速度为50ms/字符 |
|
|
|
} else { |
|
|
|
chatStore.messages.pop(); |
|
|
|
chatStore.messages.push({ |
|
|
|
sender: "ai", |
|
|
|
content: status.msg |
|
|
|
}); |
|
|
|
chatStore.setLoading(false); |
|
|
|
} |
|
|
|
|
|
|
|
chatStore.messages.pop(); |
|
|
|
// 先推送初始消息 |
|
|
|
const aiMessage = reactive({ |
|
|
|
sender: "ai", |
|
|
|
content: "", |
|
|
|
isTyping: true, |
|
|
|
}); |
|
|
|
chatStore.messages.push(aiMessage); |
|
|
|
|
|
|
|
let index = 0; |
|
|
|
const typingInterval = setInterval(() => { |
|
|
|
if (index < processedContent.length) { |
|
|
|
aiMessage.content += processedContent.charAt(index); |
|
|
|
index++; |
|
|
|
|
|
|
|
} else { |
|
|
|
clearInterval(typingInterval); |
|
|
|
aiMessage.isTyping = false; |
|
|
|
|
|
|
|
// 延迟处理KaTeX确保DOM已更新 |
|
|
|
nextTick(() => { |
|
|
|
aiMessage.content = aiMessage.content.replace(katexRegex, (match, formula) => { |
|
|
|
try { |
|
|
|
return katex.renderToString(formula, { throwOnError: false }); |
|
|
|
} catch (error) { |
|
|
|
console.error('KaTeX 渲染错误:', error); |
|
|
|
return match; |
|
|
|
} |
|
|
|
}); |
|
|
|
chatStore.setLoading(false); |
|
|
|
}); |
|
|
|
} |
|
|
|
}, 50); // 调整速度为50ms/字符 |
|
|
|
} |
|
|
|
catch (e) { |
|
|
|
console.error('请求失败:', e); |
|
|
@ -309,14 +335,38 @@ watch( |
|
|
|
}); |
|
|
|
chatStore.setLoading(false); |
|
|
|
} |
|
|
|
finally { |
|
|
|
await chatStore.getUserCount(); |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
{ deep: true } |
|
|
|
); |
|
|
|
|
|
|
|
// var kLineCharts = document.getElementsByClassName("kLineChart"); |
|
|
|
// for (var i = 0; i < kLineCharts.length; i++) { |
|
|
|
// (function (i) { |
|
|
|
// const data = datatok.Kline |
|
|
|
// // 切割数据方法 |
|
|
|
// const spliteDate = (a) => { |
|
|
|
// const categoryData = [] |
|
|
|
// let value = [] |
|
|
|
// for (let i = 0; i < a.length; i++) { |
|
|
|
// categoryData.push(a[i][0]) |
|
|
|
// value.push([a[i][1], a[i][2], a[i][3], a[i][4]]) |
|
|
|
// } |
|
|
|
// return { categoryData, value } |
|
|
|
// } |
|
|
|
// const dealData = spliteDate(data) |
|
|
|
// var myChart = echarts.init(kLineCharts[i]); |
|
|
|
|
|
|
|
// }) |
|
|
|
// } |
|
|
|
|
|
|
|
|
|
|
|
// 初始化随机GIF |
|
|
|
onMounted(() => { |
|
|
|
const random = Math.floor(Math.random() * 4) + 1; |
|
|
|
const random = Math.floor(Math.random() * 6) + 1; |
|
|
|
currentGif.value = `src/assets/img/AIchat/AIgif${random}.gif`; |
|
|
|
|
|
|
|
getQuestionsList(); |
|
|
@ -362,6 +412,8 @@ onMounted(() => { |
|
|
|
<div v-for="(msg, index) in chatMsg" :key="index" :class="['message-bubble', msg.sender]"> |
|
|
|
<div v-if="msg.type === 'kline'" class="kline-container"> |
|
|
|
<KLine /> |
|
|
|
<!-- <KLine :key="msg.timestamp" /> --> |
|
|
|
<!-- <div class="kLineChart"></div> --> |
|
|
|
</div> |
|
|
|
<div v-else v-html="msg.content"></div> |
|
|
|
</div> |
|
|
@ -377,6 +429,7 @@ onMounted(() => { |
|
|
|
.chat-container { |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
overflow-x: hidden; |
|
|
|
} |
|
|
|
|
|
|
|
.gif-area { |
|
|
@ -445,20 +498,34 @@ onMounted(() => { |
|
|
|
} |
|
|
|
|
|
|
|
.top { |
|
|
|
animation: marquee 15s linear infinite; |
|
|
|
animation: marquee 25s linear infinite; |
|
|
|
} |
|
|
|
|
|
|
|
.bottom { |
|
|
|
animation: marquee 15s linear infinite reverse; |
|
|
|
} |
|
|
|
|
|
|
|
/* 添加PC端专用速度 */ |
|
|
|
@media (min-width: 768px) { |
|
|
|
.top { |
|
|
|
animation-duration: 35s; |
|
|
|
/* PC端改为35秒 */ |
|
|
|
} |
|
|
|
|
|
|
|
.bottom { |
|
|
|
animation-duration: 35s; |
|
|
|
/* PC端改为35秒 */ |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
@keyframes marquee { |
|
|
|
0% { |
|
|
|
transform: translateX(100%); |
|
|
|
} |
|
|
|
|
|
|
|
100% { |
|
|
|
transform: translateX(-150%); |
|
|
|
transform: translateX(-250%); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|