Browse Source

优化该bug,k线图和语音问题还没改

hxl
hongxilin 2 months ago
parent
commit
06a8fe10dc
  1. BIN
      src/assets/img/AIchat/AIgif5.gif
  2. BIN
      src/assets/img/AIchat/AIgif6.gif
  3. BIN
      src/assets/img/AIchat/AIgif7.gif
  4. 18
      src/store/chat.js
  5. 287
      src/views/AIchat.vue
  6. 5
      src/views/Echarts/KLine.vue
  7. 136
      src/views/homePage.vue

BIN
src/assets/img/AIchat/AIgif5.gif

After

Width: 750  |  Height: 550  |  Size: 5.8 MiB

BIN
src/assets/img/AIchat/AIgif6.gif

After

Width: 750  |  Height: 550  |  Size: 4.4 MiB

BIN
src/assets/img/AIchat/AIgif7.gif

After

Width: 750  |  Height: 550  |  Size: 4.5 MiB

18
src/store/chat.js

@ -1,11 +1,19 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { getUserCountAPI } from '@/api/AIxiaocaishen'
export const useChatStore = defineStore('chat', { export const useChatStore = defineStore('chat', {
state: () => ({ state: () => ({
messages: [], messages: [],
isLoading: false // 新增加载状态
isLoading: false, // 新增加载状态
UserCount: 0,
chartData: [],
}), }),
actions: { actions: {
async getUserCount() {
const result = await getUserCountAPI({
token: localStorage.getItem('localToken')
})
this.UserCount = result.data.hasCount
},
setLoading(status) { setLoading(status) {
this.isLoading = status this.isLoading = status
}, },
@ -14,6 +22,9 @@ export const useChatStore = defineStore('chat', {
}, },
isLoadingF() { isLoadingF() {
this.isLoading = false; this.isLoading = false;
},
addKLineData(data) {
this.kLineData.push(data);
} }
}, },
persist: { persist: {
@ -21,7 +32,8 @@ export const useChatStore = defineStore('chat', {
strategies: [ strategies: [
{ {
key: 'chat_messages', key: 'chat_messages',
storage: localStorage
storage: localStorage,
paths: ['messages', 'kLineData']
} }
] ]
} }

287
src/views/AIchat.vue

@ -10,7 +10,8 @@ import { marked } from 'marked'; // 引入marked库
import katex from 'katex'; // KaTeX import katex from 'katex'; // KaTeX
import { htmlToText } from 'html-to-text'; import { htmlToText } from 'html-to-text';
import { Howl, Howler } from 'howler'; 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 chatStore = useChatStore()
const audioStore = useAudioStore() const audioStore = useAudioStore()
const dataStore = useDataStore() const dataStore = useDataStore()
@ -179,127 +180,152 @@ watch(
const AIcontent = ref(""); const AIcontent = ref("");
// answer // 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正在思考中..."
});
// // 使markedMarkdownHTML
// 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;
}
// // 使markedMarkdownHTML
// 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;
// KaTeXDOM
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;
// KaTeXDOM
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) { catch (e) {
console.error('请求失败:', e); console.error('请求失败:', e);
@ -309,14 +335,38 @@ watch(
}); });
chatStore.setLoading(false); chatStore.setLoading(false);
} }
finally {
await chatStore.getUserCount();
}
} }
}, },
{ deep: true } { 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 // GIF
onMounted(() => { 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`; currentGif.value = `src/assets/img/AIchat/AIgif${random}.gif`;
getQuestionsList(); getQuestionsList();
@ -362,6 +412,8 @@ onMounted(() => {
<div v-for="(msg, index) in chatMsg" :key="index" :class="['message-bubble', msg.sender]"> <div v-for="(msg, index) in chatMsg" :key="index" :class="['message-bubble', msg.sender]">
<div v-if="msg.type === 'kline'" class="kline-container"> <div v-if="msg.type === 'kline'" class="kline-container">
<KLine /> <KLine />
<!-- <KLine :key="msg.timestamp" /> -->
<!-- <div class="kLineChart"></div> -->
</div> </div>
<div v-else v-html="msg.content"></div> <div v-else v-html="msg.content"></div>
</div> </div>
@ -377,6 +429,7 @@ onMounted(() => {
.chat-container { .chat-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow-x: hidden;
} }
.gif-area { .gif-area {
@ -445,20 +498,34 @@ onMounted(() => {
} }
.top { .top {
animation: marquee 15s linear infinite;
animation: marquee 25s linear infinite;
} }
.bottom { .bottom {
animation: marquee 15s linear infinite reverse; 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 { @keyframes marquee {
0% { 0% {
transform: translateX(100%); transform: translateX(100%);
} }
100% { 100% {
transform: translateX(-150%);
transform: translateX(-250%);
} }
} }

5
src/views/Echarts/KLine.vue

@ -46,7 +46,7 @@ function KlineCanvsEcharts(datatok) {
let value = [] let value = []
for (let i = 0; i < a.length; i++) { for (let i = 0; i < a.length; i++) {
categoryData.push(a[i][0]) categoryData.push(a[i][0])
value.push([a[i][1],a[i][2],a[i][3],a[i][4]])
value.push([a[i][1], a[i][2], a[i][3], a[i][4]])
} }
return { categoryData, value } return { categoryData, value }
} }
@ -58,7 +58,7 @@ function KlineCanvsEcharts(datatok) {
// Canvs // Canvs
text: datatok.name, text: datatok.name,
top: 20, top: 20,
left: 20
left: 20
}, },
tooltip: { tooltip: {
trigger: 'axis', // trigger: 'axis', //
@ -206,6 +206,7 @@ function KlineCanvsEcharts(datatok) {
onMounted(() => { onMounted(() => {
// fnGetData() // fnGetData()
}) })
</script> </script>
<style scoped> <style scoped>

136
src/views/homePage.vue

@ -39,6 +39,7 @@ const tabs = computed(() => [
// setActiveTab forceAIchat // setActiveTab forceAIchat
const setActiveTab = (tab, index, forceAIchat = false) => { const setActiveTab = (tab, index, forceAIchat = false) => {
isScrolling.value = false; //
isAnnouncementVisible.value = false; isAnnouncementVisible.value = false;
if (forceAIchat && activeTab.value !== "AIchat") { if (forceAIchat && activeTab.value !== "AIchat") {
activeTab.value = "AIchat"; activeTab.value = "AIchat";
@ -51,6 +52,7 @@ const setActiveTab = (tab, index, forceAIchat = false) => {
sessionStorage.setItem("activeTabAI", tab); sessionStorage.setItem("activeTabAI", tab);
sessionStorage.setItem("activeIndexAI", index.toString()); sessionStorage.setItem("activeIndexAI", index.toString());
} }
console.log(tab, index, "tab, index");
setHeight(document.getElementById("testId")); // setHeight(document.getElementById("testId")); //
}; };
@ -68,12 +70,13 @@ const ensureAIchat = () => {
}; };
// //
const UserCount = ref(0);
const getUserCount = async () => {
const result = await getUserCountAPI({ token: localStorage.getItem('localToken') });
UserCount.value = result.data.hasCount;
// const UserCount = ref(0);
// const getUserCount = async () => {
// const result = await getUserCountAPI({ token: localStorage.getItem('localToken') });
// UserCount.value = result.data.hasCount;
// };
const UserCount = computed(() => chatStore.UserCount)
};
const getCount = () => { const getCount = () => {
console.log('点击了获取次数的按钮') console.log('点击了获取次数的按钮')
@ -98,6 +101,7 @@ const updateMessage = (title) => {
// console.log("updateMessage :", title); // console.log("updateMessage :", title);
}; };
const sendMessage = async () => { const sendMessage = async () => {
isScrolling.value = false;
// ensureAIchat AIchat // ensureAIchat AIchat
ensureAIchat(); ensureAIchat();
@ -132,8 +136,10 @@ const isAnnouncementVisible = ref(false);
const showAnnouncement = () => { const showAnnouncement = () => {
console.log("打开公告"); console.log("打开公告");
isScrolling.value = false; //
setActiveTab('', -1); //
isAnnouncementVisible.value = true; // isAnnouncementVisible.value = true; //
activeTab.value = 'Announcement`'
}; };
// //
@ -147,48 +153,39 @@ const showCount = () => {
console.log("dialogVisible 的值:", dialogVisible.value); // console.log("dialogVisible 的值:", dialogVisible.value); //
}; };
// //
const chatStore = useChatStore() const chatStore = useChatStore()
const tabContent = ref(null); const tabContent = ref(null);
const isScrolling = ref(false); // const isScrolling = ref(false); //
const smoothScrollToBottom = async () => { const smoothScrollToBottom = async () => {
await nextTick();
console.log('调用滚动到底部的方法')
// await nextTick();
const container = tabContent.value; const container = tabContent.value;
console.log(container, 'container')
console.log(isScrolling.value, 'isScrolling.value')
if (!container) return; if (!container) return;
if (!isScrolling.value)
container.scrollTop = container.scrollHeight - container.offsetHeight;
requestAnimationFrame(() => {
await nextTick(); // DOM
if (!isScrolling.value) {
container.scrollTop = container.scrollHeight - container.offsetHeight; container.scrollTop = container.scrollHeight - container.offsetHeight;
});
// container.scrollTop = container.scrollHeight;
// container.scrollTop = container.offsetHeight;
// container.scrollTop = container.scrollHeight + container.offsetHeight;
console.log(container.scrollHeight, container.offsetHeight,container.scrollHeight-container.offsetHeight, container.scrollTop, "总长度", "可视长度", "位置")
}
} }
const throttledSmoothScrollToBottom = _.throttle(smoothScrollToBottom, 500, { trailing: false }); const throttledSmoothScrollToBottom = _.throttle(smoothScrollToBottom, 500, { trailing: false });
const handleScroll = function () {
const scrollContainer = tabContent.value
const scrollTop = scrollContainer.scrollTop
const scrollHeight = scrollContainer.scrollHeight
const offsetHeight = scrollContainer.offsetHeight
// console.log(scrollTop, scrollHeight, offsetHeight, "scrollTop, scrollHeight, offsetHeight");
if (scrollTop + offsetHeight < scrollHeight) {
//
isScrolling.value = true
} else {
//
isScrolling.value = false
}
// console.log(isScrolling.value)
}
watch( watch(
() => chatStore.messages, () => chatStore.messages,
() => { () => {
console.log('messages变化了')
throttledSmoothScrollToBottom(); throttledSmoothScrollToBottom();
// setTimeout(throttledSmoothScrollToBottom, 100);
}, },
{ deep: true, immediate: true } { deep: true, immediate: true }
); );
@ -196,9 +193,15 @@ watch(
watch( watch(
activeTab, activeTab,
async (newVal) => {
async () => {
console.log('activeTab变化了', activeTab.value)
isScrolling.value = false; //
await nextTick(); // DOM
throttledSmoothScrollToBottom(); throttledSmoothScrollToBottom();
});
// setTimeout(throttledSmoothScrollToBottom, 100);
},
{ deep: true, immediate: true }
);
// setTimeout // setTimeout
@ -234,12 +237,60 @@ const fnGetToken = () => {
useAppBridge().packageFun('JWwebReady', () => { }, 5, {}) useAppBridge().packageFun('JWwebReady', () => { }, 5, {})
} }
// const tabContainer = tabContent.value
// let befortop = 0
// tabContainer.addEventListener('scroll', () => {
// const aftertop = tabContainer.scrollTop
// if (aftertop - befortop > 0) {
// console.log('')
// isScrolling.value = true
// } else {
// console.log('')
// isScrolling.value = false
// }
// befortop = aftertop
// })
const heightListener = () => {
const tabContainer = tabContent.value;
let befortop = 0;
const scrollHandler = () => {
const aftertop = tabContainer.scrollTop;
//
const isBottom = aftertop + tabContainer.offsetHeight + 50 >= tabContainer.scrollHeight;
if (activeTab.value === 'AIchat') {
if (aftertop - befortop > 0) {
console.log('向下滚动');
isScrolling.value = true;
} else {
console.log('向上滚动');
isScrolling.value = true;
}
//
if (isBottom) {
console.log('滚动到底部');
isScrolling.value = false;
}
}
befortop = aftertop;
};
console.log(isScrolling.value, 'isScrolling.value')
tabContainer.addEventListener('scroll', scrollHandler);
};
const throttledHeightListener = _.throttle(heightListener, 500, { trailing: false });
onMounted(async () => { onMounted(async () => {
setHeight(document.getElementById("testId")); // setHeight(document.getElementById("testId")); //
getUserCount();
await chatStore.getUserCount();
throttledSmoothScrollToBottom(); throttledSmoothScrollToBottom();
//
tabContent.value.addEventListener('scroll', handleScroll)
throttledHeightListener();
}) })
</script> </script>
@ -270,7 +321,7 @@ onMounted(async () => {
<section class="tab-section"> <section class="tab-section">
<div class="tab-container"> <div class="tab-container">
<div v-for="(tab, index) in tabs" :key="tab.name" @click="setActiveTab(tab.name, index)" <div v-for="(tab, index) in tabs" :key="tab.name" @click="setActiveTab(tab.name, index)"
:class="['tab-item', { active: activeIndex === index }]">
:class="['tab-item', { active: activeIndex === index && !isAnnouncementVisible }]">
<span>{{ tab.label }}</span> <span>{{ tab.label }}</span>
</div> </div>
</div> </div>
@ -398,6 +449,7 @@ onMounted(async () => {
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center; background-position: center;
display: flex; display: flex;
overflow: auto;
} }
.homepage .el-container { .homepage .el-container {
@ -405,7 +457,7 @@ onMounted(async () => {
flex-direction: column; flex-direction: column;
/* 明确纵向排列 */ /* 明确纵向排列 */
display: flex; display: flex;
overflow: hidden;
overflow: auto;
} }
.el-container .el-header { .el-container .el-header {
@ -469,11 +521,19 @@ onMounted(async () => {
.homepage-right-group .announcement-btn { .homepage-right-group .announcement-btn {
cursor: pointer; cursor: pointer;
transition: transform 0.2s;
transition: transform 0.3s;
} }
/* @keyframes tilt {
0% { transform: rotate(0deg); }
50% { transform: rotate(10deg); }
100% { transform: rotate(-10deg); }
130% { transform: rotate(0deg); }
} */
.homepage-right-group .announcement-btn:hover { .homepage-right-group .announcement-btn:hover {
transform: scale(1.05);
transform: scale(1.3);
/* animation: tilt 1s ease-in-out; */
} }
.homepage-body { .homepage-body {

Loading…
Cancel
Save