You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

3262 lines
91 KiB

<script setup>
import { ref, onMounted, watch, nextTick, reactive, onUnmounted } from "vue";
import { ElDialog } from "element-plus";
import {
getReplyStreamAPI,
getReplyAPI,
TTSAPI,
qsArpAamClickAPI,
dbqbFirstAPI,
dbqbSecondOneAPI,
dbqbSecondTwoAPI,
dbqbSecondThreeAPI,
dbqbSecondFourAPI,
dataListAPI,
} from "../api/AIxiaocaishen";
import { useUserStore } from "../store/userPessionCode";
import { useChatStore } from "../store/chat";
import { useAudioStore } from "../store/audio";
import { useDataStore } from "@/store/dataList.js";
import { marked } from "marked"; // 引入marked库
import katex from "katex"; // 引入 KaTeX 库
import { htmlToText } from "html-to-text";
import { Howl, Howler } from "howler";
import * as echarts from "echarts";
import _, { add } from "lodash";
import moment from "moment";
import AIgif1 from "@/assets/img/AIchat/AIgif1.gif";
import AIgif2 from "@/assets/img/AIchat/AIgif2.gif";
import AIgif3 from "@/assets/img/AIchat/AIgif3.gif";
import AIgif4 from "@/assets/img/AIchat/AIgif4.gif";
import AIgif5 from "@/assets/img/AIchat/AIgif5.gif";
import AIgif6 from "@/assets/img/AIchat/AIgif6.gif";
import AIgif7 from "@/assets/img/AIchat/AIgif7.gif";
import title1 from "@/assets/img/AIchat/核心价值评估标题.png";
import title2 from "@/assets/img/AIchat/主力作战.png";
import title3 from "@/assets/img/AIchat/攻防三维.png";
import title4 from "@/assets/img/AIchat/综合作战.png";
const gifList = [AIgif1, AIgif2, AIgif3, AIgif4, AIgif5, AIgif6, AIgif7];
const chatStore = useChatStore();
const audioStore = useAudioStore();
const dataStore = useDataStore();
// 随机GIF
const currentGif = ref("");
const renderer = new marked.Renderer();
// 重写 del 方法,让删除线不生效
renderer.del = function (text) {
// 处理各种数据类型
console.log("text", text);
return "~" + text.tokens[0].raw + "<br>" + text.tokens[2].raw + "~";
};
// 定义自定义事件
const emit = defineEmits(["updateMessage", "sendMessage"]);
// 音频播放方法
const playAudio = (url) => {
// 添加空值校验
if (!url) {
console.warn("音频URL为空,跳过播放");
audioStore.isPlaying = false;
return;
}
const handlePlay = () => {
// if (audioStore.soundInstance) {
// Howler.unload(); // 添加清理旧实例
// // audioStore.soundInstance.stop()
// }
if (audioStore.isNewInstance) {
const newSound = new Howl({
src: [url],
html5: true, // 强制HTML5 Audio解决iOS兼容问题
format: ["mp3", "acc"],
rate: 1.2, // 调整播放速度
onplay: () => {
audioStore.isPlaying = true; // 改为更新store状态
newSound.volume(1); // 添加音量设置
},
onend: () => (audioStore.isPlaying = false),
onstop: () => (audioStore.isPlaying = false),
onloaderror: (id, err) => {
console.error("音频加载失败:", err);
ElMessage.error("音频播放失败,请检查网络连接");
},
});
if (audioStore.nowSound) {
audioStore.nowSound.stop();
}
audioStore.nowSound = newSound;
audioStore.isNewInstance = false;
console.log("新音频");
} else {
console.log("已经有音频");
}
const newSound = audioStore.nowSound;
// 添加立即播放逻辑
newSound.play();
audioStore.setAudioInstance(newSound);
Howler._howls.push(newSound); // 强制注册到全局管理
// // 处理浏览器自动播放策略
// if (Howler.autoUnlock) {
// Howler.autoUnlock();
// }
};
// if (/iPhone|iPad|iPod/.test(navigator.userAgent)) {
// document.addEventListener('touchstart', handlePlay, { once: true });
// ElMessage.info('请轻触屏幕以启用音频播放');
// } else {
// handlePlay();
// }
handlePlay();
// Howler.volume(1.0) // 添加全局音量设置
};
// 新增暂停方法
const pauseAudio = () => {
if (audioStore.soundInstance) {
// 添加移动端特殊处理
// if (/iPhone|iPad|iPod/.test(navigator.userAgent)) {
// Howler.pause(); // iOS需要强制停止音频上下文
// } else {
// audioStore.soundInstance.pause();
// }
audioStore.soundInstance.pause();
audioStore.isPlaying = false;
// // 添加状态同步
// nextTick(() => {
// Howler.unload();
// audioStore.soundInstance = null;
// });
}
};
// 音频轮流播放方法
const playAudioSequence = (audioUrls) => {
if (!audioUrls || audioUrls.length === 0) {
console.warn("音频URL列表为空,跳过播放");
return;
}
let currentIndex = 0;
const playNext = () => {
if (currentIndex >= audioUrls.length) {
console.log("所有音频播放完成");
return;
}
const currentUrl = audioUrls[currentIndex];
console.log(`正在播放第${currentIndex + 1}个音频:`, currentUrl);
// 停止当前播放的音频
if (audioStore.nowSound) {
audioStore.nowSound.stop();
}
const sound = new Howl({
src: [currentUrl],
html5: true,
format: ["mp3", "acc"],
rate: 1.2,
onplay: () => {
audioStore.isPlaying = true;
console.log(`开始播放音频 ${currentIndex + 1}`);
},
onend: () => {
audioStore.isPlaying = false;
console.log(`音频 ${currentIndex + 1} 播放完成`);
currentIndex++;
// 播放下一个音频
setTimeout(() => {
playNext();
}, 500); // 间隔500ms播放下一个
},
onstop: () => {
audioStore.isPlaying = false;
},
onloaderror: (id, err) => {
console.error(`音频 ${currentIndex + 1} 加载失败:`, err);
currentIndex++;
// 跳过失败的音频,播放下一个
setTimeout(() => {
playNext();
}, 100);
}
});
audioStore.nowSound = sound;
audioStore.setAudioInstance(sound);
sound.play();
};
// 开始播放第一个音频
playNext();
};
// 获取消息
const chatMsg = computed(() => chatStore.messages);
const props = defineProps({
messages: Array,
chartData: {
type: Object,
default: null,
},
index: {
type: Number,
required: true,
},
});
// 打字机效果
const typewriterContent = ref("");
const isTyping = ref(false);
const typeWriter = (text, callback) => {
let index = 0;
isTyping.value = true;
typewriterContent.value = "";
const typingInterval = setInterval(() => {
if (index < text.length) {
typewriterContent.value += text.charAt(index);
index++;
// 自动滚动到底部
nextTick(() => {
const container = document.querySelector(".message-area");
if (container) container.scrollTop = container.scrollHeight;
});
} else {
clearInterval(typingInterval);
isTyping.value = false;
if (callback) callback();
}
}, 50); // 调整速度(毫秒)
};
const fnGetData = (data) => {
const YaLiZhiChengLuoPan = data.YaLiZhiChengLuoPan;
let sz = ref(5.5); // 进行判断
const yl = YaLiZhiChengLuoPan.Yali; // 压力位
const zc = YaLiZhiChengLuoPan.ZhiCheng; // 支撑位
console.log("yl", yl, "zc", zc);
if (yl == "较大" && zc == "较弱") {
sz.value = 0.5; // 极高风险
} else if (yl == "一般" && zc == "较弱") {
sz.value = 1.5; // 弱撑中压区
} else if (yl == "较弱" && zc == "较弱") {
sz.value = 2.5; // 弱撑弱压区
} else if (yl == "较大" && zc == "较大") {
sz.value = 3.5; // 强撑强压区
} else if (yl == "一般" && zc == "较大") {
sz.value = 4.5; // 强撑中压
} else if (yl == "较弱" && zc == "较大") {
sz.value = 5.5; // 强撑弱压区
} else if (yl == "较大" && zc == "一般") {
sz.value = 0.2;
} else if (yl == "一般" && zc == "一般") {
sz.value = 3;
} else if (yl == "较弱" && zc == "一般") {
sz.value = 5.8;
}
return sz.value;
};
const typingQueue = ref([]);
const isTypingInProgress = ref(false);
// 创建打字机效果的Promise函数
const createTypingEffect = (message, content, speed) => {
return new Promise((resolve) => {
chatStore.messages.push(message);
if (content != "") {
let index = 0;
message.content = "";
message.isTyping = true;
const typingInterval = setInterval(() => {
if (index < content.length) {
message.content += content.charAt(index);
index++;
} else {
clearInterval(typingInterval);
message.isTyping = false;
// 处理KaTeX渲染(如果需要)
nextTick(() => {
if (message.content.includes("$$")) {
message.content = message.content.replace(
katexRegex,
(match, formula) => {
try {
return katex.renderToString(formula, {
throwOnError: false,
});
} catch (error) {
console.error("KaTeX 渲染错误:", error);
return match;
}
}
);
}
resolve(); // 完成后resolve
});
}
}, speed);
} else {
if (message.kline) {
if (message.klineType == 1) {
console.log("六色罗盘消息已添加到聊天列表");
// 在渲染完成后初始化图表
nextTick(() => {
console.log("nextTick开始 - 准备渲染图表");
console.log("消息列表:", chatStore.messages);
// 寻找最新添加的K线消息索引
let klineIndex = -1;
for (let i = 0; i < chatStore.messages.length; i++) {
if (chatStore.messages[i].messageId === message.messageId) {
klineIndex = i;
break;
}
}
console.log("找到的K线消息索引:", klineIndex);
if (klineIndex !== -1) {
const containerId = `kline-container-${klineIndex}`;
console.log("图表容器ID:", containerId);
// 确保DOM已经渲染完成
setTimeout(() => {
console.log("延时执行,确保DOM已渲染");
KlineCanvsEcharts(containerId);
}, 100); // 短暂延时确保DOM已渲染
} else {
console.warn("未找到K线消息");
}
});
} else {
console.log("K线消息已添加到聊天列表");
// 在渲染完成后初始化图表
nextTick(() => {
console.log("nextTick开始 - 准备渲染图表");
console.log("消息列表:", chatStore.messages);
// 寻找最新添加的K线消息索引
let klineIndex = -1;
for (let i = 0; i < chatStore.messages.length; i++) {
if (chatStore.messages[i].messageId === message.messageId) {
klineIndex = i;
break;
}
}
console.log("找到的K线消息索引:", klineIndex);
if (klineIndex !== -1) {
const containerId = `kline-container-${klineIndex}`;
console.log("图表容器ID:", containerId);
// 确保DOM已经渲染完成
setTimeout(() => {
console.log("延时执行,确保DOM已渲染");
KlineCanvsEcharts(containerId);
}, 100); // 短暂延时确保DOM已渲染
} else {
console.warn("未找到K线消息");
}
});
}
// 延时1秒后resolve
setTimeout(() => {
resolve();
}, 1000);
} else {
// 延时1秒后resolve
setTimeout(() => {
resolve();
}, 1000);
}
}
});
};
// 队列处理函数
const processTypingQueue = async () => {
if (isTypingInProgress.value || typingQueue.value.length === 0) {
return;
}
isTypingInProgress.value = true;
while (typingQueue.value.length > 0) {
const task = typingQueue.value.shift();
await createTypingEffect(task.message, task.content, task.speed);
}
isTypingInProgress.value = false;
};
// 添加打字机任务到队列
const addTypingTask = (message, content, speed = 50) => {
typingQueue.value.push({ message, content, speed });
processTypingQueue();
};
const hasValidData = ref(false);
// 创建一个非响应式的对象来存储图表实例
const chartInstancesMap = {};
// 存储上一次的消息的length
const previousMessagesLength = ref(0);
watch(
() => props.messages,
async (newVal, oldVal) => {
// console.log(newVal, 'newVal')
// console.log(oldVal, 'oldVal')
// console.log(previousMessagesLength.value, 'previousMessagesLength')
// console.log(newVal.length, 'newVal.length')
// // 添加空值判断
if (!newVal?.length || newVal === previousMessagesLength.value) return;
previousMessagesLength.value = newVal.length;
if (newVal.length > 0) {
console.log("消息列表已更新,最新消息:", newVal[newVal.length - 1]);
chatStore.messages.push(newVal[newVal.length - 1]);
console.log("消息列表已更新,最新消息:", chatMsg[chatMsg.length - 1]);
console.log("token", localStorage.getItem("localToken"));
// 获取权限
const userStore = useUserStore();
const params1 = {
content: newVal[newVal.length - 1].content,
language: "cn",
marketList: "usa,sg,my,hk,cn,can,vi,th,in",
token: localStorage.getItem("localToken"),
// language: "cn",
// marketList: "hk,cn,usa,my,sg,vi,in,gb"
// token: "+SsksARQgUHIbIG3rRnnbZi0+fEeMx8pywnIlrmTxo5EOPR/wjWDV7w7+ZUseiBtf9kFa/atmNx6QfSpv5w",
};
// 标志
let flag = true;
const codeData = ref();
// 第一阶段,意图识别
try {
// 调用工作流获取回复
const result = await dbqbFirstAPI(params1);
codeData.value = result.data;
console.log(codeData.value, "codeData");
// 根据意图识别结果判断
if (result.code == 200) {
chatStore.messages.push({
class: "ing",
type: "ing",
flag: flag,
content: result.data.kaishi,
});
} else {
flag = false;
console.log("执行回绝话术");
const AIcontent = ref(result.msg);
// 修改后的消息处理逻辑
const processedContent = marked(AIcontent.value);
const katexRegex = /\$\$(.*?)\$\$/g;
const aiContent = processedContent.replace(
katexRegex,
(match, formula) => {
try {
return katex.renderToString(formula, { throwOnError: false });
} catch (error) {
console.error("KaTeX 渲染错误:", error);
return match;
}
}
);
console.log(aiContent, "aiContent");
chatStore.messages.push({
class: "ing",
type: "ing",
flag: flag,
content: aiContent,
});
chatStore.setLoading(false);
}
} catch (e) {
console.log(e, "意图识别失败");
}
if (flag) {
const params2 = {
content: newVal[newVal.length - 1].content,
language: "cn",
marketList: "usa,sg,my,hk,cn,can,vi,th,in",
token: localStorage.getItem("localToken"),
// language: "cn",
// marketList: "hk,cn,usa,my,sg,vi,in,gb"
// token: "+SsksARQgUHIbIG3rRnnbZi0+fEeMx8pywnIlrmTxo5EOPR/wjWDV7w7+ZUseiBtf9kFa/atmNx6QfSpv5w",
name: codeData.value.name,
code: codeData.value.code,
market: codeData.value.market,
};
try {
const result20 = await dataListAPI({
token: localStorage.getItem("localToken"),
market: codeData.value.market,
code: codeData.value.code,
language: "cn", //t.value.suoxie,
// brainPrivilegeState: 1,
// swordPrivilegeState: 1,
// stockForecastPrivilegeState: 1,
// spaceForecastPrivilegeState: 1,
// aibullPrivilegeState: 1,
// aigoldBullPrivilegeState: 1,
// airadarPrivilegeState: 1,
// marketList: 1,
brainPrivilegeState: userStore.brainPerssion,
swordPrivilegeState: userStore.swordPerssion,
stockForecastPrivilegeState: userStore.pricePerssion,
spaceForecastPrivilegeState: userStore.timePerssion,
aibullPrivilegeState: userStore.aibullPerssion,
aigoldBullPrivilegeState: userStore.aiGnbullPerssion,
airadarPrivilegeState: userStore.airadarPerssion,
marketList: userStore.aiGoldMarketList,
});
const HomePage = result20.data.HomePage;
const AIGoldBull = result20.data.AIGoldBull;
const result21 = await dbqbSecondOneAPI(params2);
const result22 = await dbqbSecondTwoAPI(params2);
const result23 = await dbqbSecondThreeAPI(params2);
const result24 = await dbqbSecondFourAPI(params2);
// 收集所有音频URL
const audioUrls = [];
if (result21.data.url) audioUrls.push(result21.data.url.trim());
if (result22.data.url) audioUrls.push(result22.data.url.trim());
if (result23.data.url) audioUrls.push(result23.data.url.trim());
if (result24.data.url) audioUrls.push(result24.data.url.trim());
// 开始轮流播放音频
if (audioUrls.length > 0 && audioStore.isVoiceEnabled) {
playAudioSequence(audioUrls);
}
const katexRegex = /\$\$(.*?)\$\$/g;
// 删除正在为您生成信息
chatStore.messages.pop();
// 添加报告头和时间
addTypingTask(
{
sender: "ai",
class: "title1",
type: "title1",
content: codeData.value.name + "全景作战报告",
date: moment().format("MM/DD/YYYY"),
},
"",
50
);
// chatStore.messages.push({
// sender: "ai",
// class: "title1",
// type: "title1",
// content: codeData.value.name + "全景作战报告",
// date: moment().format("MM/DD/YYYY"),
// });
// 添加股票信息框
const pc1 = marked(
result21.data.name +
"\n" +
result21.data.price +
"\n" +
result21.data.date
);
const ac1 = pc1.replace(katexRegex, (match, formula) => {
try {
return katex.renderToString(formula, { throwOnError: false });
} catch (error) {
console.error("KaTeX 渲染错误:", error);
return match;
}
});
// 先推送初始消息
const aiMessage1 = reactive({
sender: "ai",
class: "content1",
type: "content1",
content: "",
isTyping: true,
});
// chatStore.messages.push(aiMessage1);
// let index1 = 0;
// const typingInterval1 = setInterval(() => {
// if (index1 < ac1.length) {
// aiMessage1.content += ac1.charAt(index1);
// index1++;
// } else {
// clearInterval(typingInterval1);
// aiMessage1.isTyping = false;
// }
// }, 50); // 调整速度为50ms/字符
addTypingTask(aiMessage1, ac1, 50);
// chatStore.messages.push({
// sender: "ai",
// class: "content1",
// type: "content1",
// content: ac1,
// });
// 添加六色罗盘
const LiuSeData = JSON.parse(JSON.stringify(toRaw(HomePage)));
const sz = fnGetData(LiuSeData);
if (sz) {
hasValidData.value = true;
console.log("hasValidData设置为:", hasValidData.value);
}
// 先推送K线图消息
const klineMessageId1 = `kline-${Date.now()}`;
console.log("生成K线消息ID:", klineMessageId1);
addTypingTask(
{
sender: "ai",
class: "content1",
type: "content1",
kline: true,
chartData: sz,
messageId: klineMessageId1,
hasValidData: true,
klineType: 1,
},
"",
50
);
// chatStore.messages.push({
// sender: "ai",
// class: "content1",
// type: "content1",
// kline: true,
// chartData: sz,
// messageId: klineMessageId1,
// hasValidData: true,
// klineType: 1,
// });
// console.log("六色罗盘消息已添加到聊天列表");
// // 在渲染完成后初始化图表
// nextTick(() => {
// console.log("nextTick开始 - 准备渲染图表");
// console.log("消息列表:", chatStore.messages);
// // 寻找最新添加的K线消息索引
// let klineIndex = -1;
// for (let i = 0; i < chatStore.messages.length; i++) {
// if (chatStore.messages[i].messageId === klineMessageId1) {
// klineIndex = i;
// break;
// }
// }
// console.log("找到的K线消息索引:", klineIndex);
// if (klineIndex !== -1) {
// const containerId = `kline-container-${klineIndex}`;
// console.log("图表容器ID:", containerId);
// // 确保DOM已经渲染完成
// setTimeout(() => {
// console.log("延时执行,确保DOM已渲染");
// KlineCanvsEcharts(containerId);
// }, 100); // 短暂延时确保DOM已渲染
// } else {
// console.warn("未找到K线消息");
// }
// });
// 度牛尺K线图
const AIGoldBullData = JSON.parse(JSON.stringify(toRaw(AIGoldBull)));
const HomePageData = JSON.parse(JSON.stringify(toRaw(HomePage)));
console.log("处理 K 线数据 - 开始");
console.log("AIGoldBullData", AIGoldBullData);
console.log("HomePageData", HomePageData);
const Kline20 = {
name: HomePageData.StockInformation.Name,
Kline: AIGoldBullData,
};
// 打印K线数据结构
console.log("K线数据结构:", Kline20);
console.log("K线数据名称:", Kline20.name);
console.log("K线数据:", Kline20.Kline ? Kline20.Kline : null);
// 设置数据有效标志
hasValidData.value = true;
console.log("hasValidData设置为:", hasValidData.value);
// chatStore.messages.pop();
// 先推送K线图消息
const klineMessageId2 = `kline-${Date.now() + 1}`;
console.log("生成K线消息ID:", klineMessageId2);
// chatStore.messages.push({
// sender: "ai",
// class: "content2",
// type: "content2",
// kline: true,
// chartData: Kline20,
// messageId: klineMessageId2,
// hasValidData: true, // 添加hasValidData标志
// klineType: 2,
// });
addTypingTask(
{
sender: "ai",
class: "content2",
type: "content2",
kline: true,
chartData: Kline20,
messageId: klineMessageId2,
hasValidData: true, // 添加hasValidData标志
klineType: 2,
},
"",
50
);
// console.log("K线消息已添加到聊天列表");
// // 在渲染完成后初始化图表
// nextTick(() => {
// console.log("nextTick开始 - 准备渲染图表");
// console.log("消息列表:", chatStore.messages);
// // 寻找最新添加的K线消息索引
// let klineIndex = -1;
// for (let i = 0; i < chatStore.messages.length; i++) {
// if (chatStore.messages[i].messageId === klineMessageId2) {
// klineIndex = i;
// break;
// }
// }
// console.log("找到的K线消息索引:", klineIndex);
// if (klineIndex !== -1) {
// const containerId = `kline-container-${klineIndex}`;
// console.log("图表容器ID:", containerId);
// // 确保DOM已经渲染完成
// setTimeout(() => {
// console.log("延时执行,确保DOM已渲染");
// KlineCanvsEcharts(containerId);
// }, 100); // 短暂延时确保DOM已渲染
// } else {
// console.warn("未找到K线消息");
// }
// });
// 添加标题2
addTypingTask(
{
sender: "ai",
class: "title2",
type: "title2",
content: "",
},
"",
50
);
// chatStore.messages.push({
// sender: "ai",
// class: "title2",
// type: "title2",
// content: "",
// });
// 添加内容框1
const pc2 = marked(result22.data.hxjzpg);
console.log(pc2, "pc2");
const ac2 = pc2.replace(katexRegex, (match, formula) => {
try {
return katex.renderToString(formula, { throwOnError: false });
} catch (error) {
console.error("KaTeX 渲染错误:", error);
return match;
}
});
// 先推送初始消息
const aiMessage2 = reactive({
sender: "ai",
class: "content3",
type: "content3",
content: "",
isTyping: true,
});
// chatStore.messages.push(aiMessage2);
// let index2 = 0;
// const typingInterval2 = setInterval(() => {
// if (index2 < ac2.length) {
// aiMessage2.content += ac2.charAt(index2);
// index2++;
// } else {
// clearInterval(typingInterval2);
// aiMessage2.isTyping = false;
// }
// }, 50); // 调整速度为50ms/字符
addTypingTask(aiMessage2, ac2, 50);
// chatStore.messages.push({
// sender: "ai",
// class: "content3",
// type: "content3",
// content: ac2,
// });
// 添加标题3-2
addTypingTask(
{
sender: "ai",
class: "title3",
type: "title3",
content: title2,
},
"",
50
);
// chatStore.messages.push({
// sender: "ai",
// class: "title3",
// type: "title3",
// content: title2,
// });
// 添加内容框2
// const pc3 = marked(result23.data.zhuli1+'\n'+result23.data.zhuli2+'\n'+result23.data.zhuli3);
// const ac3 = pc3.replace(
// katexRegex,
// (match, formula) => {
// try {
// return katex.renderToString(formula, { throwOnError: false });
// } catch (error) {
// console.error("KaTeX 渲染错误:", error);
// return match;
// }
// }
// );
const ac3 = `<p style="margin:0;color:#FADC0C;display:flex;justify-content:center;font-size:28px">【主力行为】</p><p>${result23.data.zhuli1}</p><p>${result23.data.zhuli2}</p><p>${result23.data.zhuli3}</p>`;
// 先推送初始消息
const aiMessage3 = reactive({
sender: "ai",
class: "content3",
type: "content3",
content: "",
isTyping: true,
});
// chatStore.messages.push(aiMessage3);
// let index3 = 0;
// const typingInterval3 = setInterval(() => {
// if (index3 < ac3.length) {
// aiMessage3.content += ac3.charAt(index3);
// index3++;
// } else {
// clearInterval(typingInterval3);
// aiMessage3.isTyping = false;
// }
// }, 50); // 调整速度为50ms/字符
addTypingTask(aiMessage3, ac3, 50);
// chatStore.messages.push({
// sender: "ai",
// class: "content3",
// type: "content3",
// content: ac3,
// });
// 添加标题3-3
addTypingTask(
{
sender: "ai",
class: "title3",
type: "title3",
content: title3,
},
"",
50
);
// chatStore.messages.push({
// sender: "ai",
// class: "title3",
// type: "title3",
// content: title3,
// });
// 添加内容框3
const arr = result23.data.kongjian.split(",");
const kongjian = `<p style="margin:0;color:#FADC0C;display:flex;justify-content:center;font-size:28px">【空间维度】</p><p style="display:flex;justify-content:center;">${arr[0]},${arr[1]}</p><p style="display:flex;justify-content:center;">${arr[2]},${arr[3]}</p>`;
const shijian = `<p style="margin:0;color:#FADC0C;display:flex;justify-content:center;font-size:28px">【时间维度】</p><p style="display:flex;justify-content:center;">${result23.data.shijian}</p>`;
const nengliang = `<p style="margin:0;color:#FADC0C;display:flex;justify-content:center;font-size:28px">【能量维度】</p><p>${result23.data.nengliang}</p>`;
const ac4 = kongjian + shijian + nengliang;
// const pc4 = marked(
// kongjian +
// "\n" +
// result23.data.shijian +
// "\n" +
// result23.data.nengliang
// );
// const ac4 = pc4.replace(katexRegex, (match, formula) => {
// try {
// return katex.renderToString(formula, { throwOnError: false });
// } catch (error) {
// console.error("KaTeX 渲染错误:", error);
// return match;
// }
// });
// 先推送初始消息
const aiMessage4 = reactive({
sender: "ai",
class: "content3",
type: "content3",
content: "",
isTyping: true,
});
// chatStore.messages.push(aiMessage4);
// let index4 = 0;
// const typingInterval4 = setInterval(() => {
// if (index4 < ac4.length) {
// aiMessage4.content += ac4.charAt(index4);
// index4++;
// } else {
// clearInterval(typingInterval4);
// aiMessage4.isTyping = false;
// }
// }, 50); // 调整速度为50ms/字符
addTypingTask(aiMessage4, ac4, 50);
// chatStore.messages.push({
// sender: "ai",
// class: "content3",
// type: "content3",
// content: ac4,
// });
// 添加标题3-4
addTypingTask(
{
sender: "ai",
class: "title3",
type: "title3",
content: title4,
},
"",
50
);
chatStore.messages.push({
sender: "ai",
class: "title3",
type: "title3",
content: title4,
});
// 添加内容框4
const cftj = `<p style="margin:0;color:#FADC0C;display:flex;justify-content:center;font-size:28px">【触发条件】</p><p>${result24.data.cftl}</p>`;
const gfzl = `<p style="margin:0;color:#FADC0C;display:flex;justify-content:center;font-size:28px">【攻防指令】</p><p>${result24.data.gfzl}</p>`;
const ac5 = cftj + gfzl;
// const pc5 = marked(result24.data.cftl + "/n" + result24.data.gfzl);
// const ac5 = pc5.replace(katexRegex, (match, formula) => {
// try {
// return katex.renderToString(formula, { throwOnError: false });
// } catch (error) {
// console.error("KaTeX 渲染错误:", error);
// return match;
// }
// });
// 先推送初始消息
const aiMessage5 = reactive({
sender: "ai",
class: "content3",
type: "content3",
content: "",
isTyping: true,
});
// chatStore.messages.push(aiMessage5);
// let index5 = 0;
// const typingInterval5 = setInterval(() => {
// if (index5 < ac5.length) {
// aiMessage5.content += ac5.charAt(index5);
// index5++;
// } else {
// clearInterval(typingInterval5);
// aiMessage5.isTyping = false;
// }
// }, 50); // 调整速度为50ms/字符
addTypingTask(aiMessage5, ac5, 50);
// chatStore.messages.push({
// sender: "ai",
// class: "content3",
// type: "content3",
// content: ac5,
// });
const ac6 = "内容由AI生成,请注意甄别";
// 先推送初始消息
const aiMessage6 = reactive({
sender: "ai",
class: "mianze",
type: "mianze",
content: "",
isTyping: true,
});
// chatStore.messages.push(aiMessage6);
// let index6 = 0;
// const typingInterval6 = setInterval(() => {
// if (index6 < ac6.length) {
// aiMessage6.content += ac6.charAt(index6);
// index6++;
// } else {
// clearInterval(typingInterval6);
// aiMessage6.isTyping = false;
// }
// }, 50); // 调整速度为50ms/字符
addTypingTask(aiMessage6, ac6, 100);
// chatStore.messages.push({
// sender: "ai",
// class: "mianze",
// type: "mianze",
// content: "内容由AI生成,请注意甄别",
// });
// // 修改后的消息处理逻辑
// 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) {
// audioStore.ttsUrl = tts.value.tts_cn.url;
// ttsUrl.value = tts.value.tts_cn.url;
// audioStore.isNewInstance = true;
// } else if (tts.value.tts_en !== null) {
// audioStore.ttsUrl = tts.value.tts_en.url;
// ttsUrl.value = tts.value.tts_en.url;
// audioStore.isNewInstance = true;
// }
// 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);
// // }
// }
} catch (e) {
console.error("请求失败:", e);
hasValidData.value = false; // 请求失败时设置数据无效
// chatStore.messages.pop();
// chatStore.messages.push({
// sender: "ai",
// content: "AI思考失败,请稍后再试... ",
// });
// chatStore.setLoading(false);
} finally {
chatStore.setLoading(false);
await chatStore.getUserCount();
}
}
}
},
{ deep: true }
);
function KlineCanvsEcharts(containerId) {
function vwToPx(vw) {
console.log((window.innerWidth * vw) / 100, "vwToPx");
return (window.innerWidth * vw) / 100;
}
console.log("KLine渲染: 开始处理数据, 容器ID:", containerId);
// 从 chatStore 中获取数据
const messages = chatStore.messages;
console.log("KLine渲染: 获取到的消息:", messages);
let klineMessageIndex = -1;
let klineData = null;
// 查找最近的 K线 消息
// for (let i = messages.length - 1; i >= 0; i--) {
// console.log('KLine渲染: 检查消息:', messages[i]);
// if (messages[i].type === 'kline' && messages[i].chartData) {
// klineMessageIndex = i;
// klineData = messages[i].chartData;
// console.log('KLine渲染: 找到K线消息索引:', klineMessageIndex);
// console.log('KLine渲染: 找到K线数据:', klineData);
// break;
// }
// }
klineMessageIndex = containerId.split("-")[2];
console.log("KLine渲染: 找到K线消息索引:", klineMessageIndex);
if (
messages[klineMessageIndex].kline &&
messages[klineMessageIndex].chartData
) {
klineData = messages[klineMessageIndex].chartData;
}
var KlineOption = {};
// 检测设备类型
const isMobile = window.innerWidth < 768;
const isTablet = window.innerWidth >= 768 && window.innerWidth < 1024;
console.log(
"KLine渲染: 设备类型",
isMobile ? "移动设备" : isTablet ? "平板设备" : "桌面设备"
);
if (messages[klineMessageIndex].klineType == 1) {
if (!klineData) {
console.warn("六色罗盘渲染: 数据无效 - 在chatStore中找不到有效的K线数据");
return;
}
// 获取容器元素
const container = document.getElementById(containerId);
if (!container) {
console.error("六色罗盘渲染: 找不到容器元素:", containerId);
return;
}
// 创建图表实例
console.log("六色罗盘渲染: 创建图表实例");
try {
// 如果已有实例,先销毁
if (chartInstancesMap[containerId]) {
console.log("六色罗盘渲染: 销毁已有图表实例");
chartInstancesMap[containerId].dispose();
delete chartInstancesMap[containerId];
}
// 使用普通变量存储实例
chartInstancesMap[containerId] = echarts.init(container);
console.log("六色罗盘渲染: 图表实例创建成功");
} catch (error) {
console.error("六色罗盘渲染: 图表实例创建失败:", error);
return;
}
const name = ref("六色罗盘");
const size = ref(16);
// PC版字体大小
if (window.innerWidth > 768) {
size.value = 25;
}
KlineOption = {
tooltip: {
show: !1,
},
series: [
{
name: "\u4eea\u8868\u76d8",
type: "gauge",
center: ["50%", "50%"],
radius: "90%",
startAngle: 140,
endAngle: -140,
min: 0,
max: 6,
precision: 0,
splitNumber: 30, // 分成30份
axisLine: {
show: !0,
lineStyle: {
color: [
[0.17, "#FC4407"],
[0.33, "#FDC404"],
[0.5, "#2D8FFD"],
[0.67, "#87CCE7"],
[0.83, "#C1F478"],
[1, "#8FEB8D"],
],
width: 20,
},
},
axisTick: {
show: !0,
splitNumber: 9,
length: 8,
lineStyle: {
color: "#eee",
width: 1,
type: "solid",
},
},
axisLabel: {
show: true,
formatter: function (v) {},
textStyle: {
color: "auto",
},
},
// 中途切割
splitLine: {
show: !0,
length: 20,
lineStyle: {
color: "#eee",
width: 2,
type: "solid",
},
},
pointer: {
length: "80%",
width: 8,
color: "auto",
},
title: {
show: !0,
offsetCenter: ["-65%", -10],
textStyle: {
color: "#333",
fontSize: 15,
},
},
detail: {
show: !0,
backgroundColor: "rgba(0,0,0,0)",
borderWidth: 0,
borderColor: "#ccc",
width: 100,
height: 40,
offsetCenter: ["-90%", 0], // name位置
formatter: function () {
return name.value;
},
textStyle: {
color: "auto",
fontSize: size.value, // 字体尺寸
},
},
data: [{ value: klineData }],
},
],
};
} else if (messages[klineMessageIndex].klineType == 2) {
if (!klineData || !klineData.Kline) {
console.warn("KLine渲染: 数据无效 - 在chatStore中找不到有效的K线数据");
return;
}
// 获取容器元素
const container = document.getElementById(containerId);
if (!container) {
console.error("KLine渲染: 找不到容器元素:", containerId);
return;
}
// 创建图表实例
console.log("KLine渲染: 创建图表实例");
try {
// 如果已有实例,先销毁
if (chartInstancesMap[containerId]) {
console.log("KLine渲染: 销毁已有图表实例");
chartInstancesMap[containerId].dispose();
delete chartInstancesMap[containerId];
}
// 使用普通变量存储实例
chartInstancesMap[containerId] = echarts.init(container);
console.log("KLine渲染: 图表实例创建成功");
} catch (error) {
console.error("KLine渲染: 图表实例创建失败:", error);
return;
}
const data = klineData.Kline;
console.log("KLine渲染: Kline数据", data);
// 切割数据方法
const splitData = (a) => {
console.log("KLine渲染: 开始数据切割");
const categoryData = [];
let values = [];
for (let i = 0; i < a.length; i++) {
categoryData.push(a[i][0]);
values.push([a[i][1], a[i][2], a[i][3], a[i][4]]);
}
console.log("KLine渲染: 日期数据点数量", categoryData.length);
console.log("KLine渲染: 值数据点数量", values.length);
return { categoryData, values };
};
// 给配置项
console.log("KLine渲染: 开始配置图表选项");
const arr1 = [];
const arr2 = [];
const arr3 = [];
const arr4 = [];
const changeColorKline = (QSXH, KLine20) => {
if (QSXH) {
QSXH.map((item) => {
KLine20.map((kline_item) => {
if (item[1] == 1 && item[0] == kline_item[0]) {
arr1.push(kline_item);
arr2.push([
kline_item[0],
null,
null,
null,
null,
null,
null,
null,
]);
arr3.push([
kline_item[0],
null,
null,
null,
null,
null,
null,
null,
]);
arr4.push([
kline_item[0],
null,
null,
null,
null,
null,
null,
null,
]);
}
if (item[1] == 2 && item[0] == kline_item[0]) {
arr2.push(kline_item);
arr1.push([
kline_item[0],
null,
null,
null,
null,
null,
null,
null,
]);
arr3.push([
kline_item[0],
null,
null,
null,
null,
null,
null,
null,
]);
arr4.push([
kline_item[0],
null,
null,
null,
null,
null,
null,
null,
]);
}
if (item[1] == 3 && item[0] == kline_item[0]) {
arr3.push(kline_item);
arr2.push([
kline_item[0],
null,
null,
null,
null,
null,
null,
null,
]);
arr1.push([
kline_item[0],
null,
null,
null,
null,
null,
null,
null,
]);
arr4.push([
kline_item[0],
null,
null,
null,
null,
null,
null,
null,
]);
}
if (item[1] == 4 && item[0] == kline_item[0]) {
arr4.push(kline_item);
arr2.push([
kline_item[0],
null,
null,
null,
null,
null,
null,
null,
]);
arr3.push([
kline_item[0],
null,
null,
null,
null,
null,
null,
null,
]);
arr1.push([
kline_item[0],
null,
null,
null,
null,
null,
null,
null,
]);
}
});
});
}
};
console.log(arr1, arr2, arr3, arr4);
changeColorKline(data.QSXH, data.KLine20);
var dealData = splitData(data.KLine20);
var dealData1 = splitData(arr1);
var dealData2 = splitData(arr2);
var dealData3 = splitData(arr3);
var dealData4 = splitData(arr4);
var dealGnBullData = data.JN;
function processMAData(data) {
let processedData = [];
data.forEach((item, idx) => {
processedData.push({
date: item[0],
value: item[1],
type: item[2],
});
});
// 当某一种type只存在一天,设置另一种type透明
let singleTypeRed = [{ min: 0, max: 0, color: "#000" }];
let singleTypeYellow = [{ min: 0, max: 0, color: "#000" }];
let singleTypeGreen = [{ min: 0, max: 0, color: "#000" }];
for (let i = 1; i < processedData.length; i++) {
if (processedData[i].type !== processedData[i - 1].type) {
if (
i == processedData.length - 1 ||
(processedData[i].type !== processedData[i + 1].type &&
processedData[i - 1].type === processedData[i + 1].type)
) {
if (processedData[i - 1].type === 0) {
singleTypeGreen.push({
min: i - 1,
max: i,
color: "rgba(0,0,0,0)",
});
} else if (processedData[i - 1].type === 1) {
singleTypeRed.push({
min: i - 1,
max: i,
color: "rgba(0,0,0,0)",
});
} else if (processedData[i - 1].type === 2) {
singleTypeYellow.push({
min: i - 1,
max: i,
color: "rgba(0,0,0,0)",
});
}
}
}
if (processedData[i].type !== processedData[i - 1].type) {
if (processedData[i].type == 0) {
processedData[i - 1].isTransitionGreen = 1;
} else if (processedData[i].type == 1) {
processedData[i - 1].isTransitionRed = 1;
} else if (processedData[i].type == 2) {
processedData[i - 1].isTransitionYellow = 1;
}
// // 创建过渡点,使用前一个点的值
// processedData[i - 1].isTransition = true
}
}
let greenData = [];
let redData = [];
let yellowData = [];
processedData.forEach((item, idx) => {
const point = [item.date, item.value];
if (item.type === 0) {
greenData.push(point);
redData.push([item.date, "-"]);
yellowData.push([item.date, "-"]);
// if (item.isTransition) {
// redData[redData.length - 1] = [processedData[idx].date, processedData[idx].value]
// yellowData[yellowData.length - 1] = [processedData[idx].date, processedData[idx].value]
// }
if (item.isTransitionGreen) {
greenData[greenData.length - 1] = [
processedData[idx].date,
processedData[idx].value,
];
} else if (item.isTransitionRed) {
redData[redData.length - 1] = [
processedData[idx].date,
processedData[idx].value,
];
} else if (item.isTransitionYellow) {
yellowData[yellowData.length - 1] = [
processedData[idx].date,
processedData[idx].value,
];
}
} else if (item.type === 1) {
redData.push(point);
greenData.push([item.date, "-"]);
yellowData.push([item.date, "-"]);
// if (item.isTransition) {
// greenData[greenData.length - 1] = [processedData[idx].date, processedData[idx].value]
// yellowData[yellowData.length - 1] = [processedData[idx].date, processedData[idx].value]
// }
if (item.isTransitionGreen) {
greenData[greenData.length - 1] = [
processedData[idx].date,
processedData[idx].value,
];
} else if (item.isTransitionRed) {
redData[redData.length - 1] = [
processedData[idx].date,
processedData[idx].value,
];
} else if (item.isTransitionYellow) {
yellowData[yellowData.length - 1] = [
processedData[idx].date,
processedData[idx].value,
];
}
} else if (item.type === 2) {
redData.push([item.date, "-"]);
greenData.push([item.date, "-"]);
yellowData.push(point);
// if (item.isTransition) {
// greenData[greenData.length - 1] = [processedData[idx].date, processedData[idx].value]
// redData[redData.length - 1] = [processedData[idx].date, processedData[idx].value]
// }
if (item.isTransitionGreen) {
greenData[greenData.length - 1] = [
processedData[idx].date,
processedData[idx].value,
];
} else if (item.isTransitionRed) {
redData[redData.length - 1] = [
processedData[idx].date,
processedData[idx].value,
];
} else if (item.isTransitionYellow) {
yellowData[yellowData.length - 1] = [
processedData[idx].date,
processedData[idx].value,
];
}
}
});
return {
greenData: greenData,
redData: redData,
yellowData: yellowData,
singleTypeGreen: singleTypeGreen,
singleTypeRed: singleTypeRed,
singleTypeYellow: singleTypeYellow,
};
}
const maData = processMAData(data.FCX);
const maDuchiData = processMAData(data.DNC);
if (data.FCX[0][1] == "-1") {
maData.greenData = [];
maData.redData = [];
maData.yellowData = [];
}
const processBarData = (data) => {
const barData = [];
const markPointData = [];
data.forEach((item) => {
let color;
switch (item[4]) {
case 1:
color = "#13E113";
break;
case 2:
color = "#FF0E00";
break;
case 3:
color = "#0000FE";
break;
case 4:
color = "#1397FF";
break;
}
barData.push({
value: item[5],
itemStyle: {
normal: {
color: color,
},
},
});
if (item[1] === 1) {
markPointData.push({
coord: [item[0], item[5]],
symbol:
"image://https://d31zlh4on95l9h.cloudfront.net/images/5iujb101000d5si3v3hr7w2vg0h43z1u.png",
symbolSize: [30, 30],
label: {
normal: {
color: "rgba(0, 0, 0, 0)",
},
},
});
}
if (item[2] === 1) {
markPointData.push({
coord: [item[0], item[5] / 2],
symbol:
"image://https://d31zlh4on95l9h.cloudfront.net/images/5iujaz01000d5si016bxdf6vh0377d2h.png",
symbolSize: [30, 30],
label: {
normal: {
color: "rgba(0, 0, 0, 0)",
},
},
});
}
if (item[3] === 1) {
markPointData.push({
coord: [item[0], 0],
symbol:
"image://https://d31zlh4on95l9h.cloudfront.net/images/5iujb001000d5shzls0tmd4vs0e5tdrw.png",
symbolSize: [30, 30],
label: {
normal: {
color: "rgba(0, 0, 0, 0)",
},
},
});
}
});
return { barData, markPointData };
};
const { barData, markPointData } = processBarData(dealGnBullData);
KlineOption = {
legend: [
{
textStyle: {
color: "black",
fontSize: window.innerWidth > 768 ? 15 : vwToPx(1.8),
},
width: "100%",
top: window.innerWidth > 768 ? "0%" : "-1%",
left: "center",
itemGap: window.innerWidth > 768 ? 20 : 10,
itemWidth: 10,
itemHeight: 10,
data: [
{
name: "进攻K线",
itemStyle: {
color: "rgb(255,0,0)",
},
},
{
name: "防守K线",
itemStyle: {
color: "red",
},
},
{
name: "推进K线",
itemStyle: {
color: "orange",
},
},
{
name: "撤退K线",
itemStyle: {
color: "rgb(84,252,252)",
},
},
],
},
{
textStyle: {
color: "black",
fontSize: window.innerWidth > 768 ? 15 : vwToPx(1.8),
},
orient: "horizontal",
top: window.innerWidth > 768 ? "3%" : "2%",
width: "100%",
left: "center",
itemGap: 15,
data: [
{
name: "{green|━}{red|━} " + "牵牛绳",
icon: "none",
textStyle: {
rich: {
green: {
color: "green",
fontSize: window.innerWidth > 768 ? 20 : 10,
},
red: {
color: "red",
fontSize: window.innerWidth > 768 ? 20 : 10,
},
},
},
},
{
name: "龙线",
},
{
name: "虫线",
},
],
},
{
textStyle: {
color: "black",
fontSize: window.innerWidth > 768 ? 15 : vwToPx(1.8),
},
orient: "horizontal",
top: window.innerWidth > 768 ? "72%" : "64%",
width: "100%",
left: "center",
itemGap: 15,
data: [
{
name: "{green|━}{red|━} " + "度牛尺",
icon: "none",
textStyle: {
rich: {
green: {
color: "green",
fontSize: window.innerWidth > 768 ? 20 : 10,
},
red: {
color: "red",
fontSize: window.innerWidth > 768 ? 20 : 10,
},
},
},
},
],
},
],
tooltip: {
formatter: function (a, b, d) {
if (a[0].seriesIndex == 0) {
const KlineTag = ref([]);
const AIBullTag = ref([]);
KlineTag.value = a.find((item) => item.data[1])?.data || [];
AIBullTag.value =
a.slice(4).find((item) => item.data[1] !== "-")?.data || [];
// console.log(AIBullTag.value)
return (
a[0].name +
"<br/>" +
"开盘价" +
":" +
KlineTag.value[1] +
"<br/>" +
"收盘价" +
":" +
KlineTag.value[2] +
"<br/>" +
"最低价" +
":" +
KlineTag.value[3] +
"<br/>" +
"最高价" +
":" +
KlineTag.value[4] +
"<br/>" +
"牵牛绳" +
":" +
AIBullTag.value[1]
);
}
if (a[0].seriesIndex == 4) {
let formattedVolume;
if (a[0].data.value >= 10000) {
formattedVolume = (a[0].data.value / 10000).toFixed(2) + "w";
} else {
formattedVolume = a[0].data.value;
}
return a[0].name + "<br/>" + "成交量" + ":" + formattedVolume;
}
if ([10, 11, 12].includes(a[0].seriesIndex)) {
const duchiData = a.find(
(item) => item.data && item.data[1] !== "-"
);
return duchiData
? a[0].axisValue + "<br/>" + "度牛尺" + ":" + duchiData.data[1]
: null;
}
},
trigger: "axis",
axisPointer: {
type: "cross",
},
backgroundColor: "rgba(119, 120, 125, 0.6)",
borderWidth: 1,
borderColor: "#77787D",
padding: 10,
textStyle: {
color: "#fff",
},
},
axisPointer: {
link: [
{
xAxisIndex: "all",
},
],
label: {
backgroundColor: "#77787D",
},
},
toolbox: {
show: false,
},
grid: [
{
// left: window.innerWidth > 768 ? '8%' : '15%',
// right: window.innerWidth > 768 ? '4%' : '2.5%',
top: window.innerWidth > 768 ? "10%" : "5%",
height: window.innerWidth > 768 ? "36%" : "34%",
containLabel: false,
},
{
// left: window.innerWidth > 768 ? '8%' : '15%',
// right: window.innerWidth > 768 ? '4%' : '2.5%',
top: window.innerWidth > 768 ? "50%" : "42%",
height: window.innerWidth > 768 ? "20%" : "22%",
containLabel: false,
},
{
// left: window.innerWidth > 768 ? '8%' : '15%',
// right: window.innerWidth > 768 ? '4%' : '2.5%',
top: window.innerWidth > 768 ? "78%" : "70%",
height: window.innerWidth > 768 ? "20%" : "22%",
containLabel: false,
},
],
xAxis: [
{
type: "category",
data: dealData.categoryData,
boundaryGap: true,
axisLine: { onZero: false },
splitLine: { show: false },
min: "dataMin",
max: "dataMax",
axisPointer: {
z: 100,
},
axisLine: {
lineStyle: {
color: "black",
},
}, //
axisLabel: { show: false },
axisTick: { show: false },
},
{
type: "category",
gridIndex: 1,
data: dealData.categoryData,
boundaryGap: true,
axisLine: { lineStyle: { color: "black" } },
axisLabel: {
show: false,
interval: "auto",
},
},
{
type: "category",
gridIndex: 2,
data: dealData.categoryData,
boundaryGap: true,
axisLine: { lineStyle: { color: "black" } },
axisLabel: {
show: true,
interval: "auto",
},
},
],
yAxis: [
{
scale: true,
gridIndex: 0,
position: "left",
axisLabel: {
inside: false,
align: "right",
fontSize: window.innerWidth > 768 ? 15 : 10,
},
axisLine: {
show: true,
lineStyle: {
fontSize: "",
color: "black",
},
},
axisTick: { show: false },
splitLine: { show: false },
},
{
scale: true,
gridIndex: 1,
splitNumber: 4,
min: 0,
minInterval: 1,
axisLabel: {
show: true,
fontSize: window.innerWidth > 768 ? 15 : 10,
margin: 8,
formatter: (value) => {
if (value >= 1000000000) {
return (value / 1000000000).toFixed(1) + "B";
} else if (value >= 1000000) {
return (value / 1000000).toFixed(1) + "M";
} else if (value >= 10000) {
return (value / 10000).toFixed(1) + "W";
}
return value.toFixed(0);
},
},
axisLine: { show: true, lineStyle: { color: "black" } },
axisTick: { show: false },
splitLine: { show: true, lineStyle: { type: "dashed" } },
boundaryGap: ["20%", "20%"],
},
{
type: "value",
gridIndex: 2,
min: 0,
max: 100,
axisLabel: {
show: true,
fontSize: window.innerWidth > 768 ? 15 : 10,
formatter: function (value) {
var customValues = [0, 20, 50, 80, 100];
return customValues.indexOf(value) > -1 ? value : "";
},
},
axisLine: {
show: true,
lineStyle: {
color: "black",
},
},
axisTick: {
show: false,
},
splitNumber: 10,
splitLine: {
show: true,
lineStyle: {
type: "dashed",
color: "#fff",
width: 1,
},
interval: function (index, value) {
return [20, 50, 80, 100].indexOf(value) > -1;
},
},
},
],
dataZoom: [
{
type: "inside",
xAxisIndex: [0, 1, 2],
start: 55,
end: 100,
},
{
show: true,
xAxisIndex: [0, 1, 2],
type: "slider",
top: window.innerWidth > 768 ? "95%" : "96%",
left: window.innerWidth > 768 ? "10%" : "8%",
start: 98,
end: 100,
},
],
visualMap: [
{
type: "piecewise",
show: false,
pieces: maData.singleTypeGreen,
outOfRange: {
color: "green",
},
dimension: 0,
seriesIndex: 7,
},
{
type: "piecewise",
show: false,
pieces: maData.singleTypeRed,
outOfRange: {
color: "red",
},
dimension: 0,
seriesIndex: 8,
},
{
type: "piecewise",
show: false,
pieces: maData.singleTypeYellow,
outOfRange: {
color: "yellow",
},
dimension: 0,
seriesIndex: 9,
},
],
series: [
{
name: "进攻K线",
type: "candlestick",
barWidth: "50%",
data: dealData1.values,
xAxisIndex: 0,
yAxisIndex: 0,
itemStyle: {
normal: {
color: "rgb(255,0,0)",
color0: "rgb(255,0,0)",
borderColor: "rgb(255,0,0)",
borderColor0: "rgb(255,0,0)",
},
},
gridIndex: 0,
},
//
{
name: "推进K线",
type: "candlestick",
barWidth: "50%",
data: dealData2.values,
xAxisIndex: 0,
yAxisIndex: 0,
itemStyle: {
normal: {
color: "rgb(0,0,252)",
color0: "rgb(0,0,252)",
borderColor: "rgb(0,0,252)",
borderColor0: "rgb(0,0,252)",
},
},
gridIndex: 0,
},
{
name: "防守K线",
type: "candlestick",
barWidth: "50%",
data: dealData3.values,
xAxisIndex: 0,
yAxisIndex: 0,
itemStyle: {
normal: {
color: "orange",
color0: "orange",
borderColor: "orange",
borderColor0: "orange",
},
},
gridIndex: 0,
},
{
name: "撤退K线",
type: "candlestick",
barWidth: "50%",
data: dealData4.values,
xAxisIndex: 0,
yAxisIndex: 0,
itemStyle: {
normal: {
color: "rgb(84,252,252)",
color0: "rgb(84,252,252)",
borderColor: "rgb(84,252,252)",
borderColor0: "rgb(84,252,252)",
},
},
gridIndex: 0,
},
{
name: "成交量",
type: "bar",
barWidth: "70%",
xAxisIndex: 1,
yAxisIndex: 1,
data: barData,
markPoint: {
data: markPointData,
label: {
show: false,
},
},
},
{
name: "{green|━}{red|━} " + "牵牛绳",
type: "line",
data: [],
smooth: true,
symbol: "none",
xAxisIndex: 0,
yAxisIndex: 0,
showSymbol: false,
lineStyle: {
opacity: 0,
},
itemStyle: {
normal: {
color: "green",
},
},
gridIndex: 0,
},
{
name: "{green|━}{red|━} " + "度牛尺",
type: "line",
data: [],
smooth: true,
symbol: "none",
xAxisIndex: 0,
yAxisIndex: 0,
showSymbol: false,
lineStyle: {
opacity: 0,
},
itemStyle: {
normal: {
color: "green",
},
},
gridIndex: 0,
},
{
name: "虫线",
type: "line",
data: maData.greenData,
smooth: true,
symbol: "none",
xAxisIndex: 0,
yAxisIndex: 0,
itemStyle: {
normal: {
color: "green",
lineStyle: {
width: 2,
type: "solid",
},
},
},
gridIndex: 0,
},
{
name: "龙线",
type: "line",
data: maData.redData,
smooth: true,
symbol: "none",
xAxisIndex: 0,
yAxisIndex: 0,
itemStyle: {
normal: {
color: "red",
lineStyle: {
width: 2,
type: "solid",
},
},
},
gridIndex: 0,
},
{
name: "黄色",
type: "line",
data: maData.yellowData,
smooth: true,
symbol: "none",
xAxisIndex: 0,
yAxisIndex: 0,
itemStyle: {
normal: {
color: "yellow",
lineStyle: {
width: 2,
type: "solid",
},
},
},
gridIndex: 0,
},
{
name: "背景区域",
type: "line",
data: [],
xAxisIndex: 2,
yAxisIndex: 2,
markArea: {
silent: true,
itemStyle: {
normal: {
opacity: 1,
},
},
label: {
normal: {
show: true,
position: "insideRight",
fontSize: window.innerWidth > 768 ? 16 : 12,
fontWeight: "bold",
color: "#13E113",
distance: 10,
},
},
data: [
[
{
yAxis: 0,
itemStyle: {
normal: {
color: "#CFFFCF",
},
},
label: {
normal: {
formatter: "度牛区",
},
},
},
{
yAxis: 20,
},
],
[
{
yAxis: 20,
itemStyle: {
normal: {
color: "#A6FFFF",
},
},
},
{
yAxis: 40,
},
],
[
{
yAxis: 40,
itemStyle: {
normal: {
color: "#FFF686",
},
},
},
{
yAxis: 60,
},
],
[
{
yAxis: 60,
itemStyle: {
normal: { color: "#FFD2B3" },
},
},
{
yAxis: 80,
},
],
[
{
yAxis: 80,
itemStyle: {
normal: { color: "#FFB8B8" },
},
label: {
normal: {
formatter: "度牛区",
color: "#FF0000",
position: "insideLeft",
distance: 10,
},
},
},
{
yAxis: 100,
},
],
],
},
},
{
name: "度牛尺",
type: "line",
data: maDuchiData.greenData,
symbol: "none",
xAxisIndex: 2,
yAxisIndex: 2,
itemStyle: {
normal: {
color: "green",
lineStyle: {
width: 2,
type: "solid",
},
},
},
gridIndex: 2,
markPoint: {
symbol: "rect",
symbolSize: (value, params) => {
const width = window.innerWidth;
const baseHeight = 36;
if (width <= 375) {
return [2, 16];
} else if (width <= 768) {
return [2, 24];
}
return [2, baseHeight];
},
itemStyle: {
normal: {
label: {
show: false,
},
},
},
data: [
...maDuchiData.greenData
.map((item) => {
if (item[1] === 0) {
return {
coord: [item[0], 20],
symbolOffset: window.innerWidth > 768 ? [0, 20] : [0, 12],
itemStyle: {
color: "#00ff00",
},
};
}
})
.filter(Boolean),
],
},
},
{
type: "line",
data: maDuchiData.redData,
// smooth: true,
symbol: "none",
xAxisIndex: 2,
yAxisIndex: 2,
itemStyle: {
normal: {
color: "red",
lineStyle: {
width: 2,
type: "solid",
},
},
},
gridIndex: 2,
markPoint: {
symbol: "rect",
symbolSize: (value, params) => {
const width = window.innerWidth;
const baseHeight = 36;
if (width <= 375) {
return [2, 16];
} else if (width <= 768) {
return [2, 24];
}
return [2, baseHeight];
},
itemStyle: {
normal: {
label: {
show: false,
},
},
},
data: [
...maDuchiData.redData
.map((item) => {
if (item[1] === 100) {
return {
coord: [item[0], 80],
symbolOffset:
window.innerWidth > 768 ? [0, -20] : [0, -12],
itemStyle: {
color: "#ff0000",
},
};
}
})
.filter(Boolean),
],
},
},
{
name: "辅助线",
type: "line",
data: [],
xAxisIndex: 2,
yAxisIndex: 2,
markLine: {
silent: true,
symbol: "none",
lineStyle: {
color: "#000000",
width: 3,
type: "solid",
},
data: [{ yAxis: 20 }],
},
},
{
name: "辅助线",
type: "line",
data: [],
xAxisIndex: 2,
yAxisIndex: 2,
markLine: {
silent: true,
symbol: "none",
lineStyle: {
color: "#000000",
width: 3,
type: "solid",
},
data: [{ yAxis: 50 }],
},
},
{
name: "辅助线",
type: "line",
data: [],
xAxisIndex: 2,
yAxisIndex: 2,
markLine: {
silent: true,
symbol: "none",
lineStyle: {
color: "#000000",
width: 3,
type: "solid",
},
data: [{ yAxis: 80 }],
},
},
{
name: "辅助线",
type: "line",
data: [],
xAxisIndex: 2,
yAxisIndex: 2,
markLine: {
silent: true,
symbol: "none",
lineStyle: {
color: "#000000",
width: 3,
type: "solid",
},
data: [{ yAxis: 100 }],
},
},
],
};
}
console.log("KLine渲染: 图表配置完成");
try {
// 应用配置
console.log("KLine渲染: 开始设置图表选项");
chartInstancesMap[containerId].setOption(KlineOption);
console.log("KLine渲染: 图表选项设置成功");
// 窗口大小变化时重新渲染图表
const resizeFunc = _.throttle(
function () {
console.log("窗口大小改变,调整图表大小");
if (
chartInstancesMap[containerId] &&
!chartInstancesMap[containerId].isDisposed()
) {
// 如果设备类型发生变化,重新渲染
const newIsMobile = window.innerWidth < 768;
const newIsTablet =
window.innerWidth >= 768 && window.innerWidth < 1024;
if (newIsMobile !== isMobile || newIsTablet !== isTablet) {
console.log("设备类型变化,重新渲染图表");
KlineCanvsEcharts(containerId);
return;
}
chartInstancesMap[containerId].resize();
}
},
1000,
{ trailing: false }
);
// 给resize事件绑定一个特定的函数名,便于后续移除
window[`resize_${containerId}`] = resizeFunc;
// 绑定resize事件
window.removeEventListener("resize", window[`resize_${containerId}`]);
window.addEventListener("resize", window[`resize_${containerId}`]);
console.log("KLine渲染: 图表渲染完成");
} catch (error) {
console.error("KLine渲染: 图表渲染出错", error);
}
}
watch(
() => audioStore.isVoiceEnabled,
(newVal) => {
// 添加状态锁定逻辑
if (newVal === audioStore.lastVoiceState) return;
audioStore.lastVoiceState = newVal;
if (newVal) {
console.log("开启语音播放");
// 添加重试机制
const tryPlay = () => {
if (!audioStore.ttsUrl) return; // 新增空值判断
if (audioStore.soundInstance?.playing()) return;
playAudio(audioStore.ttsUrl);
setTimeout(() => {
if (!audioStore.soundInstance?.playing()) {
Howler.unload();
}
}, 1000);
};
tryPlay();
} else {
console.log("关闭语音播放");
pauseAudio();
// 强制停止并释放资源
// Howler.stop();
// Howler.unload();
// if (audioStore.soundInstance) {
// audioStore.soundInstance.off(); // 移除所有事件监听
// audioStore.soundInstance = null;
// }
}
},
{ immediate: true }
);
watch(
() => dataStore.activeTabIndex,
(newVal) => {
setTimeout(() => {
console.log("activeTabIndex变化:", newVal);
// 当标签页切换回来时,重新渲染所有图表
if (newVal === 0) {
console.log("切换到AI聊天页,重新渲染图表");
// 延迟执行以确保DOM已渲染
renderAllKlineCharts();
}
}, 300);
},
{ immediate: true } // 添加immediate属性,确保初始化时执行一次
);
// 添加渲染所有K线图的方法
function renderAllKlineCharts() {
console.log("重新渲染所有K线图");
// 查找所有K线消息
const messages = chatStore.messages;
for (let i = 0; i < messages.length; i++) {
if (messages[i].kline && messages[i].chartData) {
const containerId = `kline-container-${i}`;
console.log(`尝试渲染K线图: ${containerId}`);
// 确保DOM已经渲染
const container = document.getElementById(containerId);
if (container) {
// 渲染图表
KlineCanvsEcharts(containerId);
} else {
console.warn(`找不到容器: ${containerId}`);
}
}
}
}
// 初始化随机GIF
onMounted(() => {
// 初始化marked组件
marked.setOptions({
breaks: true, // 支持换行符转换为 <br>
gfm: true, // 启用 GitHub Flavored Markdown
sanitize: false, // 不清理 HTML(谨慎使用)
smartLists: true, // 智能列表
smartypants: true, // 智能标点符号
xhtml: false, // 不使用 XHTML 输出
renderer: renderer,
});
const random = Math.floor(Math.random() * 6) + 1;
currentGif.value = gifList[random];
console.log("组件挂载完成");
// 添加DOM变化监听器
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === "childList" && mutation.addedNodes.length) {
// 检查是否添加了图表容器
const containers = document.querySelectorAll(
'[id^="kline-container-"]'
);
if (containers.length) {
// console.log("DOM变化监听到K线容器:", Array.from(containers).map(el => el.id));
}
}
});
});
// 开始监听DOM变化
observer.observe(document.body, { childList: true, subtree: true });
});
// 组件卸载时清理所有图表实例和事件监听器
onUnmounted(() => {
// 清理所有图表实例
Object.keys(chartInstancesMap).forEach((key) => {
if (chartInstancesMap[key]) {
// 移除resize事件监听
if (window[`resize_${key}`]) {
window.removeEventListener("resize", window[`resize_${key}`]);
delete window[`resize_${key}`];
}
// 销毁图表实例
chartInstancesMap[key].dispose();
delete chartInstancesMap[key];
}
});
});
</script>
<template>
<div class="chat-container">
<!-- GIF区域 -->
<div class="gif-area">
<img :src="currentGif" alt="AI动画" />
</div>
<div
v-for="(msg, index) in chatMsg"
:key="index"
:class="{
'message-bubble': true,
[msg.sender]: msg.sender,
[msg.class]: msg.class,
}"
>
<div v-if="msg.type === 'kline'" class="kline-container">
<div :id="'kline-container-' + index" class="chart-mount-point">
<div v-if="!msg.hasValidData" class="no-data-message">
<p>暂无K线数据</p>
</div>
</div>
</div>
<div v-else-if="msg.type == 'ing'">
<div v-if="msg.flag">
<span>{{ msg.content }}</span>
<span class="loading-dots">
<span class="dot">.</span>
<span class="dot">.</span>
<span class="dot">.</span>
<span class="dot">.</span>
<span class="dot">.</span>
<span class="dot">.</span>
</span>
</div>
<div v-else v-html="msg.content"></div>
</div>
<div v-else-if="msg.type == 'title1'" style="display: flex; width: 100%">
<div class="mainTitle">
{{ msg.content }}
</div>
<div class="date">
{{ msg.date }}
</div>
</div>
<div v-else-if="msg.type == 'title2'" class="title2">
<img class="title1Img" :src="title1" alt="出错了" />
</div>
<div v-else-if="msg.type == 'title3'" class="title3">
<img class="title2Img" :src="msg.content" alt="出错了" />
</div>
<div v-else-if="msg.type == 'content1'" class="content1">
<div v-if="msg.kline" class="kline-container content1chart">
<div :id="'kline-container-' + index" class="chart-mount-point">
<div v-if="!msg.hasValidData" class="no-data-message">
<p>暂无数据</p>
</div>
</div>
</div>
<div v-else class="content1Text">
<div v-html="msg.content" class="text1"></div>
</div>
</div>
<div v-else-if="msg.type == 'content2'" class="content2">
<div class="kline-container content2chart">
<div :id="'kline-container-' + index" class="chart-mount-point">
<div v-if="!msg.hasValidData" class="no-data-message">
<p>暂无数据</p>
</div>
</div>
</div>
</div>
<div v-else-if="msg.type == 'content3'" class="content3">
<div class="content3Text">
<div v-html="msg.content" class="text3"></div>
</div>
</div>
<div v-else-if="msg.type == 'mianze'" class="mianze">
<div v-html="msg.content"></div>
</div>
<div v-else v-html="msg.content"></div>
</div>
</div>
</template>
<style scoped>
.chat-container {
display: flex;
flex-direction: column;
overflow: hidden;
}
.gif-area {
/* position: relative; */
/* height: 30vh; */
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex-shrink: 0;
/* 防止GIF区域被压缩 */
}
.gif-area img {
width: 30%;
/* 改为百分比单位 */
min-width: 200px;
/* 最小尺寸 */
max-width: 400px;
/* 最大尺寸 */
height: auto;
left: 50%;
transition: all 0.3s;
/* 添加过渡效果 */
}
.message-area {
margin-top: 2%;
flex: 1;
/* 消息区域占据剩余空间 */
overflow-y: auto;
padding: 20px;
display: flex;
flex-direction: column;
gap: 15px;
}
.marquee-container {
/* position: absolute; */
bottom: 0;
width: 100%;
/* ga */
}
.marquee-row {
white-space: nowrap;
overflow: visible;
padding: 8px 0;
width: 100%;
}
.marquee-item {
display: inline-block;
margin: 0 15px;
padding: 8px 20px;
background: rgba(255, 255, 255, 0.9);
/* 白色背景 */
border-radius: 10px;
/* 圆角矩形 */
color: #333;
/* 文字颜色改为深色 */
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
/* 添加阴影 */
transition: all 0.3s;
transition: color 0.3s;
}
.top {
animation: marquee 25s linear infinite;
/* 默认动画是运行状态 */
animation-play-state: running;
}
.bottom {
animation: marquee 15s linear infinite reverse;
/* 默认动画是运行状态 */
animation-play-state: running;
}
/* 添加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(-250%);
}
}
.loading-dots {
display: inline-block;
}
.dot {
opacity: 0.4;
animation: loading 1.4s infinite;
}
.dot:nth-child(1) {
animation-delay: 0s;
}
.dot:nth-child(2) {
animation-delay: 0.2s;
}
.dot:nth-child(3) {
animation-delay: 0.4s;
}
.dot:nth-child(4) {
animation-delay: 0.6s;
}
.dot:nth-child(5) {
animation-delay: 0.8s;
}
.dot:nth-child(6) {
animation-delay: 1s;
}
@keyframes loading {
0%,
60%,
100% {
opacity: 0.4;
}
30% {
opacity: 1;
}
}
.message-bubble {
max-width: 80%;
margin: 10px 0px;
padding: 15px 20px;
position: relative;
}
.message-bubble.user {
color: #6d22f8;
background: white;
font-weight: bold;
margin-left: auto;
border-radius: 10px;
margin-right: 20px;
/* border-bottom-right-radius: 5px; */
}
.message-bubble.ai {
background: #2b378d;
color: #ffffff;
margin: 0 auto;
/* border-bottom-left-radius: 5px; */
}
.message-bubble.ing {
background: #ffffff;
color: #000000;
font-weight: bold;
border-radius: 10px;
margin-left: 20px;
margin-right: auto;
/* border-bottom-left-radius: 5px; */
}
.message-bubble.ai.title1 {
width: 100%;
display: flex;
border-radius: 10px 10px 0px 0px;
/* border-bottom-left-radius: 5px; */
}
.mainTitle {
font-size: 16px;
font-weight: bold;
background-image: url("@/assets/img/AiEmotion/bk01.png");
background-repeat: no-repeat;
background-size: 100% 100%;
min-width: 200px;
width: 20vw;
height: 50px;
padding: 5px 0px 0px 0px;
display: flex;
justify-content: center;
align-items: center;
}
.date {
font-size: 18px;
font-weight: bold;
margin-left: auto;
/* width: 100px; */
display: flex;
justify-content: center;
align-items: center;
}
.message-bubble.ai.title2 {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.title1Img {
max-width: 500px;
width: 80vw;
}
.message-bubble.ai.title3 {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.title2Img {
max-width: 500px;
width: 90vw;
}
.message-bubble.ai.content1 {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.content1chart {
background-image: url("@/assets/img/AIchat/罗盘边框.png");
background-repeat: no-repeat;
background-size: 100% 100%;
width: 50vw;
min-width: 350px;
display: flex;
justify-content: center;
align-items: center;
}
.content1Text {
background-image: url("@/assets/img/AIchat/框.png");
background-repeat: no-repeat;
background-size: 100% 100%;
width: 50vw;
min-width: 350px;
/* height: 20vw; */
/* max-height: 400px; */
padding: 5% 0;
}
.text1 {
font-weight: bold;
/* margin-left: 6%; */
/* margin-bottom: 10px; */
margin: 0px 6% 10px 6%;
font-size: 30px;
}
.message-bubble.ai.content2 {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.content2chart {
background-image: url("@/assets/img/AIchat/PCbackPic.png");
background-repeat: no-repeat;
background-size: 100% 100%;
width: 50vw;
min-width: 350px;
display: flex;
justify-content: center;
align-items: center;
}
.message-bubble.ai.content3 {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.content3Text {
background-image: url("@/assets/img/AIchat/边框.png");
background-repeat: no-repeat;
background-size: 100% 100%;
width: 50vw;
min-width: 350px;
/* height: 20vw; */
/* max-height: 400px; */
padding: 5% 0px;
}
.text3 {
/* font-weight: bold; */
/* margin-left: 6%; */
/* margin-bottom: 10px; */
margin: 0px 6% 10px 6%;
font-size: 30px;
}
.message-bubble.ai.mianze {
width: 100%;
text-align: center;
font-weight: bold;
font-size: 24px;
border-radius: 0px 0px 10px 10px;
}
.kline-container {
margin-top: 10px;
/* 最小高度 */
min-height: 320px;
/* 视口高度单位 */
height: 40vh;
width: 50vw;
}
@media (max-width: 768px) {
.kline-container {
min-width: 75vw;
}
.content1Text {
width: 77vw;
min-width: 0px;
/* height: 20vw; */
/* min-height: 150px; */
}
.text1 {
font-size: 20px;
}
.content2chart {
background-image: url("@/assets/img/AIchat/new-app-bgc.png") !important;
height: 100vw;
}
.content3Text {
width: 77vw;
min-width: 0px;
/* height: 20vw; */
/* min-height: 150px; */
}
.text3 {
font-size: 20px;
}
.message-bubble.ai.mianze {
font-size: 18px;
}
}
.kline-container .chart-mount-point {
display: flex;
justify-content: center;
align-items: center;
height: 80%;
width: 90%;
}
</style>