From 3326b448eb7999a1b564017f89682bf9f65db815 Mon Sep 17 00:00:00 2001 From: no99 <17663930442@163.com> Date: Thu, 17 Apr 2025 15:24:46 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=95=E5=85=A5AI=E6=8E=A2=E7=89=9B=EF=BC=8C?= =?UTF-8?q?=E5=A4=BA=E5=AE=9D=E5=88=A9=E5=89=91=EF=BC=8C=E9=9F=B3=E9=A2=91?= =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E6=92=AD=E6=94=BE=EF=BC=8C=E9=9F=B3=E9=A2=91?= =?UTF-8?q?=E9=80=9F=E5=BA=A61.2=EF=BC=8C=E6=8E=A5=E5=85=A5=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E8=82=A1=E7=A5=A8=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + package-lock.json | 10 + package.json | 1 + src/api/AIxiaocaishen.js | 322 +-- src/assets/img/Feedback/back.png | Bin 0 -> 7554 bytes src/assets/img/Feedback/purpleDot.png | Bin 0 -> 717 bytes src/store/audio.js | 4 +- src/views/AIchat.vue | 3554 ++++++++++++++++++++++++++++++--- src/views/Announcement.vue | 40 +- src/views/Feedback.vue | 141 +- src/views/homePage.vue | 14 +- vite.config.js | 50 +- 12 files changed, 3656 insertions(+), 481 deletions(-) create mode 100644 src/assets/img/Feedback/back.png create mode 100644 src/assets/img/Feedback/purpleDot.png diff --git a/README.md b/README.md index b015e02..9570926 100644 --- a/README.md +++ b/README.md @@ -18,3 +18,4 @@ npm install html-to-text npm install echarts npm install lodash 安装 lodash 组件,解决数据处理问题 npm install vue-device-detect 安装 vue-device-detect 组件,解决移动端适配问题 +npm install moment 安装 moment 组件,解决时间处理问题 \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 15ec7ce..e04a679 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "lodash": "^4.17.21", "marked": "^15.0.7", "mitt": "^3.0.1", + "moment": "^2.30.1", "pinia": "^2.3.1", "pinia-plugin-persistedstate": "^1.0.3", "reset-css": "^5.0.2", @@ -5233,6 +5234,15 @@ "random": "bin/random" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://mirrors.huaweicloud.com/repository/npm/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", diff --git a/package.json b/package.json index 3a451c3..70957f2 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "lodash": "^4.17.21", "marked": "^15.0.7", "mitt": "^3.0.1", + "moment": "^2.30.1", "pinia": "^2.3.1", "pinia-plugin-persistedstate": "^1.0.3", "reset-css": "^5.0.2", diff --git a/src/api/AIxiaocaishen.js b/src/api/AIxiaocaishen.js index 3f6bd76..db64fe5 100644 --- a/src/api/AIxiaocaishen.js +++ b/src/api/AIxiaocaishen.js @@ -1,177 +1,213 @@ -import request from '../utils/request' +import request from "../utils/request"; -const APIurl = import.meta.env.VITE_APP_API_BASE_URL -const MJAPIurl = import.meta.env.VITE_APP_MJ_API_BASE_URL +const APIurl = import.meta.env.VITE_APP_API_BASE_URL; +const MJAPIurl = import.meta.env.VITE_APP_MJ_API_BASE_URL; //各个模块权限code接口 export const pessionAPI = function (params) { - return request({ - url: `${APIurl}/api/brain/privilege`, - method: 'post', - data: new URLSearchParams(params), - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - }) -} + return request({ + url: `${APIurl}/api/brain/privilege`, + method: "post", + data: new URLSearchParams(params), + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }); +}; //数据接口 export const dataListAPI = function (params) { - // URLSearchParams只接受全部字符串的数据 - // 将传入数据转化成字符串 - const StringParams = new URLSearchParams( - Object.entries(params).map(([key, value]) => [key, String(value)]) - ) - return request({ - url: `${APIurl}/api/brain/data`, - method: 'post', - data: StringParams, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - }) -} + // URLSearchParams只接受全部字符串的数据 + // 将传入数据转化成字符串 + const StringParams = new URLSearchParams( + Object.entries(params).map(([key, value]) => [key, String(value)]) + ); + return request({ + url: `${APIurl}/api/brain/data`, + method: "post", + data: StringParams, + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }); +}; //统计用户行为接口 export const computedUsersAPI = function (params) { - return request({ - url: `${APIurl}/BrainStatistics/getStatistic`, - method: 'post', - data: new URLSearchParams(params), - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - }) -} + return request({ + url: `${APIurl}/BrainStatistics/getStatistic`, + method: "post", + data: new URLSearchParams(params), + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }); +}; // 首次进入小财神 export const useAiGodAPI = function (params) { - return request({ - url: `${APIurl}/api/ai_god/useAiGod`, - method: 'post', - data: new URLSearchParams(params), - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - }) -} + return request({ + url: `${APIurl}/api/ai_god/useAiGod`, + method: "post", + data: new URLSearchParams(params), + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }); +}; // 停留时间 export const updateStayTimeAPI = function (params) { - return request({ - url: `${APIurl}/api/ai_god/updateStayTime`, - method: 'post', - data: new URLSearchParams(params), - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - }) -} + return request({ + url: `${APIurl}/api/ai_god/updateStayTime`, + method: "post", + data: new URLSearchParams(params), + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }); +}; // 获取新闻接口 export const getNewsAPI = function () { - return request({ - url: `${APIurl}/api/ai_god/news`, - method: 'POST' - }) -} + return request({ + url: `${APIurl}/api/ai_god/news`, + method: "POST", + }); +}; // 获取引导搜索词接口 export const getQuestionAPI = function () { - return request({ - url: `${APIurl}/api/ai_god/shows`, - method: 'POST', - data: new URLSearchParams({ - "type": "1", - "num": "10", - "state": "1" - }) - }) -} + return request({ + url: `${APIurl}/api/ai_god/shows`, + method: "POST", + data: new URLSearchParams({ + type: "1", + num: "10", + state: "1", + }), + }); +}; // 获取公告接口 export const getAnnouncementAPI = function () { - return request({ - url: `${APIurl}/api/ai_god/shows`, - method: 'POST', - data: new URLSearchParams({ - "type": "3", - "num": "1", - "state": "1" - }) - }) -} + return request({ + url: `${APIurl}/api/ai_god/shows`, + method: "POST", + data: new URLSearchParams({ + type: "3", + num: "1", + state: "1", + }), + }); +}; // 获取用户次数接口 export const getUserCountAPI = function (params) { - return request({ - url: `${APIurl}/api/ai_god/userUsageInfo`, - method: 'POST', - data: new URLSearchParams(params), - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - }) -} + return request({ + url: `${APIurl}/api/ai_god/userUsageInfo`, + method: "POST", + data: new URLSearchParams(params), + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }); +}; // 推荐问题/每日复盘/小财神简介点击事件接口 export const qsArpAamClickAPI = function (params) { - return request({ - url: `${APIurl}/api/ai_god/shows/click`, - method: 'POST', - data: new URLSearchParams(params), - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - }) -} + return request({ + url: `${APIurl}/api/ai_god/shows/click`, + method: "POST", + data: new URLSearchParams(params), + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }); +}; // 财经新闻点击事件接口 export const newsClickAPI = function (params) { - return request({ - url: `${APIurl}/api/ai_god/news/click`, - method: 'POST', - data: new URLSearchParams(params), - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - }) -} + return request({ + url: `${APIurl}/api/ai_god/news/click`, + method: "POST", + data: new URLSearchParams(params), + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }); +}; // 获取回复接口 export const getReplyAPI = function (params) { - return fetch('https://api.coze.cn/v1/workflow/run', { - method: 'POST', - body: JSON.stringify({ - "workflow_id": "7484443705572556826", - "parameters": params, - }), - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer pat_TJbuxUiZdl6U3oiiSeceQnHg5XdaZsWpxc6oIozc2Auhd9YuyBvFslJJQUFUym1F' - } - }) -} + return fetch("https://api.coze.cn/v1/workflow/run", { + method: "POST", + body: JSON.stringify({ + workflow_id: "7491496473373540363", + parameters: params, + }), + headers: { + "Content-Type": "application/json", + Authorization: + "Bearer pat_DLMr7u1d6pmgC2demIYksrOm0r2k2w9XDxKmHvBvOQ5Lw5AYrByJ2IZpdwoJPYGi", + }, + }); +}; // 获取回复接口流式 export const getReplyStreamAPI = function (params) { - return fetch(`https://api.coze.cn/v1/workflow/stream_run`, - { - method: 'POST', - body: JSON.stringify({ - "workflow_id": "7481159261435854860", - "parameters": params, - }), - headers: { - 'Content-Type': 'application/json', - Authorization: 'Bearer pat_TJbuxUiZdl6U3oiiSeceQnHg5XdaZsWpxc6oIozc2Auhd9YuyBvFslJJQUFUym1F' - } - } - ) -} + return fetch(`https://api.coze.cn/v1/workflow/stream_run`, { + method: "POST", + body: JSON.stringify({ + workflow_id: "7481159261435854860", + parameters: params, + }), + headers: { + "Content-Type": "application/json", + Authorization: + "Bearer pat_DLMr7u1d6pmgC2demIYksrOm0r2k2w9XDxKmHvBvOQ5Lw5AYrByJ2IZpdwoJPYGi", + }, + }); +}; // 接受音频 export const TTSAPI = function (params) { - return fetch('https://api.coze.cn/v1/workflow/run', { - method: 'POST', - body: JSON.stringify({ - "workflow_id": "7481639836165275702", - "parameters": params, - }), - headers: { - 'Authorization': 'Bearer pat_TJbuxUiZdl6U3oiiSeceQnHg5XdaZsWpxc6oIozc2Auhd9YuyBvFslJJQUFUym1F', - 'Content-Type': 'application/json' - } - }) -} \ No newline at end of file + return fetch("https://api.coze.cn/v1/workflow/run", { + method: "POST", + body: JSON.stringify({ + workflow_id: "7481639836165275702", + parameters: params, + }), + headers: { + Authorization: + "Bearer pat_DLMr7u1d6pmgC2demIYksrOm0r2k2w9XDxKmHvBvOQ5Lw5AYrByJ2IZpdwoJPYGi", + "Content-Type": "application/json", + }, + }); +}; + +// 反馈前台-用户提交反馈接口 +export const addFeedbackAPI = function (params) { + return request({ + url: `${APIurl}/api/ai_god/feedback/add`, + method: "POST", + data: new URLSearchParams(params), + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }); +}; +// 反馈前台-查询该用户提交的全部反馈内容 +export const getFeedbackAPI = function (params) { + return request({ + url: `${APIurl}/api/ai_god/feedback/select`, + method: "POST", + data: new URLSearchParams(params), + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }); +}; + +// 公告-查询市场和股票 +export const getMarketAndCodeAPI = function (params) { + return request({ + url: `${APIurl}/api/ai_god/market/list`, + method: "POST", + data: new URLSearchParams(params), + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }); + }; diff --git a/src/assets/img/Feedback/back.png b/src/assets/img/Feedback/back.png new file mode 100644 index 0000000000000000000000000000000000000000..2804aeac9968b33a23414c62900c5fd0d3dec793 GIT binary patch literal 7554 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 O70}ndhO5%NLHr-Dbk`gJ literal 0 HcmV?d00001 diff --git a/src/assets/img/Feedback/purpleDot.png b/src/assets/img/Feedback/purpleDot.png new file mode 100644 index 0000000000000000000000000000000000000000..45512936a345d44e1d72fd6d3f32aa000e922a78 GIT binary patch literal 717 zcmV;;0y6!HP)Px%heKnW;Gh@3@@Vy(C7~ZTEZc{bt^u znISgt3F1T!nf%X{jGJofHDXq%WMG)8np*{gQ?OLt56bh|9i4^T1PTPz$~{{NykT|1 z_y_6OUXfiCoj4m&6-vvhn0Z+DllPD-ar#Jru`zXI%h*p_>H~kTv=wdhe<_e@pUb!5 z%j6_i*E3%iz`LTfI*WsbW)}dn*|<%5Yh@R>lAiG%vDobE4*+jOdz`l5sI|8HF9WYJ zH^wYg%BgqW6u&k8SOXBQ-02QZZFlfmz$(O2>Sg0j)B(br_mqmq4bATDTzRuEEBk%1 zJ^iLu6LkRZx(-wRp>4={D}F1wAH4_s5DKdRVXHA!I@7Y+iLmecY0UtLK|o=VhHe%h z%*fcc0A?Wsu@qr<1nn=v7G*eO#z*{qI_Qf>XU0(HC7uP4jnL*vJ-6aBK!w>GE~dNEuXt;Cd9Uj_Cn6^tkut!L z#G#D@uW3k4TVOgW9pS|E{mN^4SN&t84n#U91}NT)+4d;Yyr_E+!n#=ZllYl;M$4yV z?9M0=WiErdxS7@0wc=CJK92Nl%@2-`7SF-ZjTFJ$2efZX0N_`_-iWld@;QF3bRn+y z2|fYPu{8j!3c3_yYySg)q*IW15hmV-i4UU0M{(kZQR2sOZAc_~AqgQ8zmio`k>sVc zo{A)|<#mW8A0{=3M4x7j$7f=X(?*DY`8@vxS?bkPgP*yX00000NkvXXu0mjf7%@%; literal 0 HcmV?d00001 diff --git a/src/store/audio.js b/src/store/audio.js index da1c41a..a33154c 100644 --- a/src/store/audio.js +++ b/src/store/audio.js @@ -7,7 +7,9 @@ export const useAudioStore = defineStore('audio', { isVoiceEnabled: true, // 新增声音开关状态 playbackPosition: 0, // 新增播放位置存储 lastVoiceState: null, - ttsUrl:'' + ttsUrl:'', + isNewInstance: false, // 新增是否是新实例的标志 + nowSound:'' }), actions: { // 设置音频实例 diff --git a/src/views/AIchat.vue b/src/views/AIchat.vue index cf4e105..9fdeb2d 100644 --- a/src/views/AIchat.vue +++ b/src/views/AIchat.vue @@ -94,27 +94,41 @@ const playAudio = (url) => { return; } - const handlePlay = () => { // if (audioStore.soundInstance) { // Howler.unload(); // 添加清理旧实例 // // audioStore.soundInstance.stop() // } - const newSound = new Howl({ - src: [url], - html5: true, // 强制HTML5 Audio解决iOS兼容问题 - format: ['mp3', 'acc'], - 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.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() @@ -142,7 +156,7 @@ const pauseAudio = () => { if (audioStore.soundInstance) { // 添加移动端特殊处理 if (/iPhone|iPad|iPod/.test(navigator.userAgent)) { - Howler.stop(); // iOS需要强制停止音频上下文 + Howler.pause(); // iOS需要强制停止音频上下文 } else { audioStore.soundInstance.pause(); } @@ -275,184 +289,197 @@ watch( // 解析 data 字段中的 JSON ans.value = JSON.parse(res.data); }) - // console.log(ans.value, "ans"); + console.log(ans.value, "ans"); const AIcontent = ref(""); // 处理不同的 answer 字段 - if (ans.value.platform !== "" && ans.value.platform !== null) { // 判断是否是平台问题 - AIcontent.value = ans.value.platform; - } else if (ans.value.news !== "" && ans.value.news !== null) { // 判断是否是新闻问题 - AIcontent.value = ans.value.news; - } else if (ans.value.other !== "" && ans.value.other !== null) { // 判断是否是其他问题 - AIcontent.value = ans.value.other; - } else { // 判断是否是股票问题 - var status; - if (ans.value.resp !== "") { - status = JSON.parse(ans.value.resp); - } else { - status = null; - } - console.log(status, "status") - if (status === null || status.code == 200) { // 判断是否有剩余次数 - if (ans.value.stock !== "") { - AIcontent.value = ans.value.stock; - const code = ans.value.code; - const market = ans.value.market; - const data = JSON.parse(ans.value.data); - - console.log("处理 K 线数据 - 开始"); - console.log(data, "data"); - - const Kline20 = { - name: data.data.HomePage.StockInformation.Name, - Kline: data.data.AIBull.KLine20 - } - - // 打印K线数据结构 - console.log("K线数据结构:", Kline20); - console.log("K线数据名称:", Kline20.name); - console.log("K线数据数组长度:", Kline20.Kline ? Kline20.Kline.length : 0); - - // 设置数据有效标志 - hasValidData.value = true; - console.log("hasValidData设置为:", hasValidData.value); + if (ans.value.resp !== "" && ans.value.resp !== null) { + console.log('执行回绝话术') + AIcontent.value = ans.value.resp; + + // 修改后的消息处理逻辑 + const processedContent = marked(AIcontent.value); + const katexRegex = /\$\$(.*?)\$\$/g; + // const plainTextContent = htmlToText(processedContent); + + const aiContent = processedContent.replace(katexRegex, (match, formula) => { + try { + return katex.renderToString(formula, { throwOnError: false }); + } catch (error) { + console.error('KaTeX 渲染错误:', error); + return match; + } + }); - chatStore.messages.pop(); + console.log(aiContent, 'aiContent'); - // 先推送K线图消息 - const klineMessageId = `kline-${Date.now()}`; - console.log("生成K线消息ID:", klineMessageId); + chatStore.messages.pop(); - chatStore.messages.push({ - sender: "ai", - type: "kline", - chartData: Kline20, - messageId: klineMessageId, - hasValidData: true // 添加hasValidData标志 - }); + chatStore.messages.push({ + sender: "ai", + content: aiContent, + }); - console.log("K线消息已添加到聊天列表"); + chatStore.setLoading(false); - // 再推送文字分析内容的消息 - chatStore.messages.push({ - sender: "ai", - content: "AI正在思考中..." - }); + } else { // 判断是否是股票问题 + if (ans.value.answer !== "") { + AIcontent.value = ans.value.answer; + const type = ans.value.type; + const data = JSON.parse(ans.value.data); + + console.log("处理 K 线数据 - 开始"); + console.log(data, "data"); + + const Kline20 = { + name: data.data.HomePage.StockInformation.Name, + Kline: data.data, + type: type + } - // 在渲染完成后初始化图表 - 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 === klineMessageId) { - klineIndex = i; - break; - } - } + // 打印K线数据结构 + console.log("K线数据结构:", Kline20); + console.log("K线数据名称:", Kline20.name); + console.log("K线数据类型:", Kline20.type); + console.log("K线数据:", Kline20.Kline ? Kline20.Kline : null); - console.log("找到的K线消息索引:", klineIndex); + // 设置数据有效标志 + hasValidData.value = true; + console.log("hasValidData设置为:", hasValidData.value); - if (klineIndex !== -1) { - const containerId = `kline-container-${klineIndex}`; - console.log("图表容器ID:", containerId); + chatStore.messages.pop(); - // 确保DOM已经渲染完成 - setTimeout(() => { - console.log("延时执行,确保DOM已渲染"); - KlineCanvsEcharts(containerId); - }, 100); // 短暂延时确保DOM已渲染 - } else { - console.warn("未找到K线消息"); - } - }); - } else { - AIcontent.value = status.msg; - } - } - } + // 先推送K线图消息 + const klineMessageId = `kline-${Date.now()}`; + console.log("生成K线消息ID:", klineMessageId); - // 修改后的消息处理逻辑 - const processedContent = marked(AIcontent.value); - const katexRegex = /\$\$(.*?)\$\$/g; - const plainTextContent = htmlToText(processedContent); + chatStore.messages.push({ + sender: "ai", + type: "kline", + chartData: Kline20, + messageId: klineMessageId, + hasValidData: true // 添加hasValidData标志 + }); - // 获取音频数据 - const TTSResult = (await TTSAPI({ - language: "cn", - content: plainTextContent - })).json() + console.log("K线消息已添加到聊天列表"); - const tts = ref(); - await TTSResult.then((res) => { - tts.value = JSON.parse(res.data); - }) + // 再推送文字分析内容的消息 + chatStore.messages.push({ + sender: "ai", + content: "AI正在思考中..." + }); - const ttsUrl = ref(); - if (tts.value.tts_cn !== null) { - audioStore.ttsUrl = tts.value.tts_cn.url; - ttsUrl.value = tts.value.tts_cn.url; - } else if (tts.value.tts_en !== null) { - audioStore.ttsUrl = tts.value.tts_en.url; - ttsUrl.value = tts.value.tts_en.url; - } + // 在渲染完成后初始化图表 + 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 === klineMessageId) { + klineIndex = i; + break; + } + } - if (ttsUrl.value) { - nextTick(() => { - if (audioStore.isVoiceEnabled) { - console.log("ttsUrl.value", ttsUrl.value) - // 播放音频 - playAudio(ttsUrl.value) - } - }); - } + console.log("找到的K线消息索引:", klineIndex); - chatStore.messages.pop(); - // 先推送初始消息 - const aiMessage = reactive({ - sender: "ai", - content: "", - isTyping: true, - }); - chatStore.messages.push(aiMessage); + if (klineIndex !== -1) { + const containerId = `kline-container-${klineIndex}`; + console.log("图表容器ID:", containerId); - let index = 0; - const typingInterval = setInterval(() => { - if (index < processedContent.length) { - aiMessage.content += processedContent.charAt(index); - index++; + // 确保DOM已经渲染完成 + setTimeout(() => { + console.log("延时执行,确保DOM已渲染"); + KlineCanvsEcharts(containerId); + }, 100); // 短暂延时确保DOM已渲染 + } else { + console.warn("未找到K线消息"); + } + }); + } - } else { - clearInterval(typingInterval); - aiMessage.isTyping = false; + // 修改后的消息处理逻辑 + 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; + } - // 延迟处理KaTeX确保DOM已更新 + if (ttsUrl.value) { 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); + if (audioStore.isVoiceEnabled) { + console.log("ttsUrl.value", ttsUrl.value) + // 播放音频 + playAudio(ttsUrl.value) + } }); } - }, 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/字符 + // } else { + // chatStore.messages.pop(); + // chatStore.messages.push({ + // sender: "ai", + // content: status.msg + // }); + + // chatStore.setLoading(false); + // } + } } catch (e) { console.error('请求失败:', e); @@ -473,6 +500,12 @@ watch( ); function KlineCanvsEcharts(containerId) { + + function vwToPx(vw) { + console.log((window.innerWidth * vw) / 100, 'vwToPx'); + return (window.innerWidth * vw) / 100 + } + console.log('KLine渲染: 开始处理数据, 容器ID:', containerId); // 从 chatStore 中获取数据 @@ -529,25 +562,24 @@ function KlineCanvsEcharts(containerId) { return; } - const data = klineData.Kline; - console.log('KLine渲染: Kline数据长度', data.length); + const data = klineData.Kline.AIGoldBull; + console.log('KLine渲染: Kline数据', data); // 切割数据方法 - const spliteDate = (a) => { + const splitData = (a) => { console.log('KLine渲染: 开始数据切割'); const categoryData = []; - let value = []; + let values = []; 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]]); + values.push([a[i][1], a[i][2], a[i][3], a[i][4]]); } console.log('KLine渲染: 日期数据点数量', categoryData.length); - console.log('KLine渲染: 值数据点数量', value.length); - return { categoryData, value }; + console.log('KLine渲染: 值数据点数量', values.length); + return { categoryData, values }; }; - const dealData = spliteDate(data); - console.log('KLine渲染: 数据处理完成', dealData); + var KlineOption = {}; // 检测设备类型 const isMobile = window.innerWidth < 768; @@ -556,142 +588,3072 @@ function KlineCanvsEcharts(containerId) { // 给配置项 console.log('KLine渲染: 开始配置图表选项'); - const KlineOption = { - title: { - text: klineData.name, - left: 50, - }, - tooltip: { - trigger: 'axis', - formatter: function (a, b, d) { - let def = - a[0].name + - '
' + - '开盘价' + - a[0].data[1] + - '
' + - '收盘价' + - a[0].data[2] + - '
' + - '最低价' + - a[0].data[3] + - '
' + - '最高价' + - a[0].data[4]; - - if (a[1] && a[1].seriesName) { - def += '
' + a[1].seriesName + ':' + a[1].value; + + if (klineData.type === 1) { + console.log('进入第一分类') + + const arr1 = [] + const arr2 = [] + const arr3 = [] + const arr4 = [] + + // 动态K线 + 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]) + } + }) + }) + } + } + 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) + + console.log('dealData', dealData); + console.log('dealData1', dealData1); + console.log('dealData2', dealData2); + console.log('dealData3', dealData3); + console.log('dealData4', dealData4); + + var dealGnBullData = data.JN //金牛版成交量数据 + // 处理MA数据,创建两段重叠的数据 + 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)' }) + } + } } - return def; - }, - axisPointer: { - animation: false, - type: 'line', - lineStyle: { - color: '#376df4', - width: 2, - opacity: 1 + 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 } - }, - confine: true // 确保提示框不超出画布 - }, - //控制坐标轴 - grid: { - left: '12%', - right: '10%', - bottom: '10%', - top: '18%' - }, - xAxis: { - type: 'category', - data: dealData.categoryData, - axisLine: { lineStyle: { color: '#8392A5' } } - }, - yAxis: { - scale: !0, //true - // 自定义纵坐标现实的数据 - axisLabel: { - formatter: function (value) { - return value // 返回原始值 + } + // 分离红绿数据,包含过渡点 + 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] + } + } - }, - axisLine: { lineStyle: { color: '#8392A5' } }, - splitLine: { - show: !1 + }) + + return { + greenData: greenData, + redData: redData, + yellowData: yellowData, + singleTypeGreen: singleTypeGreen, + singleTypeRed: singleTypeRed, + singleTypeYellow: singleTypeYellow } - }, - dataZoom: [ - { - textStyle: { - color: '#8392A5' + } + const maData = processMAData(data.FCX) + console.log('maData', maData) + // 牵牛绳的数据 + + // 如果第一个字段的第一个数据为-1的话,不展示牵牛绳 + if (data.FCX[0][1] == '-1') { + maData.greenData = [] + maData.redData = [] + maData.yellowData = [] + } + // 生成文字标注 + const processBarData = (data) => { + const barData = [] + const markPointData = [] + data.forEach((item) => { + let color + // 根据 item[4] 设置不同的颜色 + 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 = { + // 底部选项 + // animation: false, + // 手放上去显示的内容 + legend: [ + { + //图例文字的样式 + textStyle: { + color: 'black', //图例文字颜色 + fontSize: window.innerWidth > 768 ? 15 : vwToPx(2.8) // 响应式字体大小 + }, + width: '100%', // 确保有足够的宽度容纳图例 + 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)' + } + } + ] }, - handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z', - handleSize: '80%', - dataBackground: { - areaStyle: { - color: '#8392A5' + { + //图例文字的样式 + textStyle: { + color: 'black', //图例文字颜色 + fontSize: window.innerWidth > 768 ? 15 : vwToPx(2.8) // 响应式字体大小 }, - lineStyle: { - opacity: 0.8, - color: '#8392A5' + orient: 'horizontal', // 设置图例水平布局 + top: '5%', + 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: '虫线' + } + ] + } + ], + tooltip: { + // 调用接口之后方法 + formatter: function (a, b, d) { + if (a[0].seriesIndex == 0) { + const KlineTag = ref([]) // 判断几根K线 + const AIBullTag = ref([]) + + // 找到第一个满足条件的数据 + KlineTag.value = a.find((item) => item.data[1])?.data || [] + + // 找到第一个满足条件的非 '-' 数据 + AIBullTag.value = a.slice(4).find((item) => item.data[1] !== '-')?.data || [] + return ( + a[0].name + + '
' + + '开盘价' + + ':' + + KlineTag.value[1] + + '
' + + '收盘价' + + ':' + + KlineTag.value[2] + + '
' + + '最低价' + + ':' + + KlineTag.value[3] + + '
' + + '最高价' + + ':' + + KlineTag.value[4] + + '
' + + '牵牛绳' + + ':' + + AIBullTag.value[1] + ) + } else { + // 格式化成交量显示 + 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 + '
' + '成交量' + ':' + formattedVolume } }, - handleStyle: { - color: '#fff', - shadowBlur: 3, - shadowColor: 'rgba(0, 0, 0, 0.6)', - shadowOffsetX: 2, - shadowOffsetY: 2 + trigger: 'axis', + axisPointer: { + //坐标轴指示器配置项 + type: 'cross' //‘line’直线指示器,‘cross’十字准星指示器,‘shadow’阴影指示器 + }, + backgroundColor: 'rgba(119, 120, 125, 0.6)', // 提示框浮层的边框颜色。 + borderWidth: 1, // 提示框浮层的边框宽。 + borderColor: '#77787D', // 提示框浮层的边框颜色。 + padding: 10, // 提示框浮层内边距, + textStyle: { + //提示框浮层上的文字样式 + color: '#fff' } }, - { - type: 'inside', - start: 1, - end: 100, - zoomOnMouseWheel: true, - moveOnMouseMove: true - } - ], - animation: false, - series: [ - { - type: 'candlestick', - name: '\u65e5K', - data: dealData.value, - itemStyle: { - normal: { - color0: '#FD1050', - color: '#0CF49B', - borderColor0: '#FD1050', - borderColor: '#0CF49B' - } - }, - // barWidth: isMobile ? 6 : isTablet ? 8 : 10 + // 手放上去时拉的框 + axisPointer: { + link: [ + { + xAxisIndex: 'all' // 同时触发所有图形的 x 坐标轴指示器 + } + ], + label: { + backgroundColor: '#77787D' // 文本标签的背景颜色 + } }, - { - name: 'MA5', - type: 'line', - data: (function (a) { - for (var MA5 = [], d = 0, g = dealData.value.length; d < g; d++) { - if (d < a) { - MA5.push('-') - } else { - for (var f = 0, e = 0; e < a; e++) { - f += dealData.value[d - e][1] + toolbox: { + show: false + }, + grid: [ + { + left: window.innerWidth > 768 ? '8%' : '15%', + right: window.innerWidth > 768 ? '4%' : '2.5%', + height: window.innerWidth > 768 ? '65%' : '57%', + top: window.innerWidth > 768 ? '8%' : '12%', + // height: '33%', + containLabel: false + }, + { + left: window.innerWidth > 768 ? '8%' : '15%', + right: window.innerWidth > 768 ? '4%' : '2.5%', + top: window.innerWidth > 768 ? '75%' : '73%', + height: '20%', + containLabel: false + } + ], + xAxis: [ + { + type: 'category', + data: dealData.categoryData, + boundaryGap: true, // 坐标轴两边是否留空,false表示不留空(通常用于K线图) + axisLine: { onZero: false }, // 设置坐标轴是否通过零点,onZero:false表示不强制穿过零点 + splitLine: { show: false }, // 是否显示分隔线,false表示不显示 + min: 'dataMin', // 坐标轴最小值,'dataMin'表示从数据的最小值开始 + max: 'dataMax', // 坐标轴最大值,'dataMax'表示从数据的最大值开始 + axisPointer: { + z: 100 // 坐标轴指示器的层级,较大的值会让它显示在其他元素上方 + }, + axisLine: { + lineStyle: { + color: 'black' // 坐标轴线的颜色 + } + }, // + axisLabel: { show: false }, // 隐藏刻度标签 + axisTick: { show: false } // 隐藏刻度线 + }, + // 下方成交量图的X轴 + { + type: 'category', + gridIndex: 1, + data: dealData.categoryData, + boundaryGap: true, + axisLine: { lineStyle: { color: 'black' } }, + axisLabel: { + show: true, + // fontSize: window.innerWidth > 768 ? 10 : 8, + interval: 'auto' + }, + axisTick: { show: false } // 隐藏刻度线 + } + ], + // 控制纵坐标展示数据 + 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 } + }, + // 下方成交量图的Y轴 + { + scale: true, + gridIndex: 1, + splitNumber: 4, // 增加分割数以获得更好的间距 + 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' } - MA5.push((f / a).toFixed(2)) + return value.toFixed(0) + } + }, + axisLine: { show: true, lineStyle: { color: 'black' } }, + axisTick: { show: false }, + splitLine: { show: true, lineStyle: { type: 'dashed' } }, // 添加分割线以提高可读性 + boundaryGap: ['20%', '20%'] // 为坐标轴边界添加内边距 + } + ], + // 下拉条 + dataZoom: [ + { + type: 'inside', + xAxisIndex: [0, 1], + start: 55, + end: 100 + }, + { + show: true, + xAxisIndex: [0, 1], + type: 'slider', + top: window.innerWidth > 768 ? '95%' : '91%', + left: window.innerWidth > 768 ? '10%' : '8%', + start: 98, + end: 100 + } + ], + visualMap: [ + { + type: 'piecewise', + show: false, + pieces: maData.singleTypeGreen, + outOfRange: { + color: 'green' + }, + dimension: 0, + seriesIndex: 6 + }, + { + type: 'piecewise', + show: false, + pieces: maData.singleTypeRed, + outOfRange: { + color: 'red' + }, + dimension: 0, + seriesIndex: 7 + }, + { + type: 'piecewise', + show: false, + pieces: maData.singleTypeYellow, + outOfRange: { + color: 'yellow' + }, + dimension: 0, + seriesIndex: 8 + } + ], + series: [ + // 第一条K线 + { + name: '进攻K线', + type: 'candlestick', + barWidth: '50%', // 设置和上方图表一致的柱子宽度 + data: dealData1.values, + xAxisIndex: 0, // 使用第一个 X 轴 + yAxisIndex: 0, // 使用第一个 Y 轴 + 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, // 使用第一个 X 轴 + yAxisIndex: 0, // 使用第一个 Y 轴 + 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, // 使用第一个 X 轴 + yAxisIndex: 0, // 使用第一个 Y 轴 + itemStyle: { + normal: { + color: 'orange', // 默认颜色 + color0: 'orange', + borderColor: 'orange', + borderColor0: 'orange' + } + }, + gridIndex: 0 + }, + { + name: '撤退K线', + type: 'candlestick', + barWidth: '50%', // 设置和上方图表一致的柱子宽度 + data: dealData4.values, + xAxisIndex: 0, // 使用第一个 X 轴 + yAxisIndex: 0, // 使用第一个 Y 轴 + 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 // 确保隐藏标记的值 } } - return MA5 - })(5), - smooth: true, - lineStyle: { - width: isMobile ? 1 : 2, - opacity: 0.8 + }, + { + name: '{green|━}{red|━} ' + '牵牛绳', // 将牵牛绳样式应用到文本前缀 + type: 'line', + data: [], // 设置为空数组,不显示数据点 + smooth: true, + symbol: 'none', + xAxisIndex: 0, // 线图与第一个 K 线图共享 X 轴 + yAxisIndex: 0, // 线图与第一个 K 线图共享 Y 轴 + showSymbol: false, // 隐藏符号 + lineStyle: { + opacity: 0 // 使线条透明 + }, + itemStyle: { + normal: { + color: 'green' + } + }, + gridIndex: 0 // 确保线图与第一个K线图共享网格 + }, + { + name: '虫线', + type: 'line', + data: maData.greenData, + smooth: true, + symbol: 'none', + xAxisIndex: 0, // 线图与第一个 K 线图共享 X 轴 + yAxisIndex: 0, // 线图与第一个 K 线图共享 Y 轴 + itemStyle: { + normal: { + color: 'green', + lineStyle: { + // color: 'orange', // 线的颜色 + width: 2, // 线宽 + type: 'solid' // 线类型 + } + } + }, + gridIndex: 0 // 确保线图与第一个K线图共享网格 + }, + { + name: '龙线', + type: 'line', + data: maData.redData, + smooth: true, + symbol: 'none', + xAxisIndex: 0, // 线图与第一个 K 线图共享 X 轴 + yAxisIndex: 0, // 线图与第一个 K 线图共享 Y 轴 + itemStyle: { + normal: { + color: 'red', + lineStyle: { + // color: 'orange', // 线的颜色 + width: 2, // 线宽 + type: 'solid' // 线类型 + } + } + }, + gridIndex: 0 // 确保线图与第一个K线图共享网格 + }, + { + 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 } + ] + // graphic: { + // markPointData : generateGraphicmarkPointData(data11111) + // } + } + } else if (klineData.type === 2) { + console.log('进入第二分类') + + 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 + + '
' + + '开盘价' + + ':' + + KlineTag.value[1] + + '
' + + '收盘价' + + ':' + + KlineTag.value[2] + + '
' + + '最低价' + + ':' + + KlineTag.value[3] + + '
' + + '最高价' + + ':' + + KlineTag.value[4] + + '
' + + '牵牛绳' + + ':' + + 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 + '
' + '成交量' + ':' + formattedVolume + } + if ([10, 11, 12].includes(a[0].seriesIndex)) { + const duchiData = a.find((item) => item.data && item.data[1] !== '-') + return duchiData + ? a[0].axisValue + '
' + '度牛尺' + ':' + 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 }] + } + } + ] + } + + } else if (klineData.type === 3 || klineData.type === 4 || klineData.type === 8 || klineData.type === 9 || klineData.type === 10) { + console.log('进入3,4,8,9,10分类') + + const arr1 = [] + const arr2 = [] + const arr3 = [] + const arr4 = [] + // k线的数据 + // 动态K线 + const changeColorKline = (QSXH, KLine20) => { + console.log(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]) + } + }) + }) + } + } + 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) + // 处理MA数据,创建两段重叠的数据 + 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] + } + } + }) + // console.log('greenData', greenData) + // console.log('redData', redData) + // console.log('yellowData', yellowData) + // console.log('green', singleTypeGreen) + // console.log('red', singleTypeRed) + // console.log('yellow', singleTypeYellow) + return { + greenData: greenData, + redData: redData, + yellowData: yellowData, + singleTypeGreen: singleTypeGreen, + singleTypeRed: singleTypeRed, + singleTypeYellow: singleTypeYellow + } + } + const maData = processMAData(data.FCX) + // 牵牛绳的数据 + + // 如果第一个字段的第一个数据为-1的话,不展示牵牛绳 + if (data.FCX[0][1] == '-1') { + maData.greenData = [] + maData.redData = [] + maData.yellowData = [] + } + // K线图距离顶部的距离 + // 配置项 + KlineOption = { + // 底部选项 + // animation: false, + // 手放上去显示的内容 + legend: [ + { + //图例文字的样式 + textStyle: { + color: 'black', //图例文字颜色 + fontSize: window.innerWidth > 768 ? 15 : vwToPx(1.8) // 响应式字体大小 + }, + width: '100%', // 确保有足够的宽度容纳图例 + 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 ? '8%' : '8%', + 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: '虫线' + } + ] + } + ], + tooltip: { + // 调用接口之后方法 + formatter: function (a, b, d) { + if (a[0].seriesIndex == 0) { + const KlineTag = ref([]) // 判断几根K线 + const AIBullTag = ref([]) + + // 找到第一个满足条件的数据 + KlineTag.value = a.find((item) => item.data[1])?.data || [] + + // 找到第一个满足条件的非 '-' 数据 + AIBullTag.value = a.slice(4).find((item) => item.data[1] !== '-')?.data || [] + return ( + a[0].name + + '
' + + '开盘价' + + ':' + + KlineTag.value[1] + + '
' + + '收盘价' + + ':' + + KlineTag.value[2] + + '
' + + '最低价' + + ':' + + KlineTag.value[3] + + '
' + + '最高价' + + ':' + + KlineTag.value[4] + + '
' + + '牵牛绳' + + ':' + + AIBullTag.value[1] + ) + // }else if(a[0].seriesIndex == 5){ + // return a[0].name+ "
" + a[0].seriesName + ":" + a[0].value + "
" + a[1].seriesName + ":" + a[1].value + "
" + a[2].seriesName + ":" + a[2].value + "
" + a[3].seriesName + ":" + a[3].value + "
" + a[4].seriesName + ":" + a[4].value + "
" + a[5].seriesName + ":" + a[5].value + } else { + return ( + a[0].name + + '
' + + '开盘价' + + ':' + + a[0].data[1] + + '
' + + '收盘价' + + ':' + + a[0].data[2] + + '
' + + '最低价' + + ':' + + a[0].data[3] + + '
' + + '最高价' + + ':' + + a[0].data[4] + + '
' + ) + } + }, + trigger: 'axis', + axisPointer: { + //坐标轴指示器配置项 + type: 'cross' //‘line’直线指示器,‘cross’十字准星指示器,‘shadow’阴影指示器 + }, + backgroundColor: 'rgba(119, 120, 125, 0.6)', // 提示框浮层的边框颜色。 + borderWidth: 1, // 提示框浮层的边框宽。 + borderColor: '#77787D', // 提示框浮层的边框颜色。 + padding: 10, // 提示框浮层内边距, + textStyle: { + //提示框浮层上的文字样式 + color: '#fff' + } + }, + // 手放上去时拉的框 + axisPointer: { + link: [ + { + xAxisIndex: 'all' // 同时触发所有图形的 x 坐标轴指示器 + } + ], + label: { + backgroundColor: '#77787D' // 文本标签的背景颜色 + } + }, + toolbox: { + show: false + }, + grid: [ + { + // left: window.innerWidth > 768 ? '8%' : '15%', + // right: window.innerWidth > 768 ? '7%' : '2.5%', + height: window.innerWidth > 768 ? '40%' : '37%', + top: window.innerWidth > 768 ? '10%' : '12%', + containLabel: false + }, + { + // left: window.innerWidth > 768 ? '8%' : '15%', + // right: window.innerWidth > 768 ? '7%' : '2.5%', + top: '52%', + height: window.innerWidth > 768 ? '40%' : '37%', + containLabel: false + } + ], + xAxis: [ + { + type: 'category', + data: dealData.categoryData, + boundaryGap: true, // 坐标轴两边是否留空,false表示不留空(通常用于K线图) + axisLine: { onZero: false }, // 设置坐标轴是否通过零点,onZero:false表示不强制穿过零点 + splitLine: { show: false }, // 是否显示分隔线,false表示不显示 + min: 'dataMin', // 坐标轴最小值,'dataMin'表示从数据的最小值开始 + max: 'dataMax', // 坐标轴最大值,'dataMax'表示从数据的最大值开始 + axisPointer: { + z: 100 // 坐标轴指示器的层级,较大的值会让它显示在其他元素上方 + }, + axisLine: { + lineStyle: { + color: 'black' // 坐标轴线的颜色 + } + }, // + axisLabel: { show: false }, // 隐藏刻度标签 + axisTick: { show: false } // 隐藏刻度线 + }, + { + type: 'category', + gridIndex: 1, + data: dealData.categoryData, + boundaryGap: true, + axisLine: { + show: false + }, + axisTick: { show: false } // 隐藏刻度线 // 只保留一个 axisLine 定义 + // 对于第二个类别轴,通常也不需要设置 min 和 max + } + ], + // 控制纵坐标展示数据 + yAxis: [ + { + scale: true, + gridIndex: 0, + axisLabel: { + show: true, + fontSize: window.innerWidth > 768 ? 15 : 10 + }, + axisLine: { + lineStyle: { + fontSize: '', + color: 'black' + } + }, + axisTick: { show: false }, + splitLine: { show: false } + }, + { + scale: true, + gridIndex: 1, + splitNumber: 2, + axisLabel: { show: true, fontSize: window.innerWidth > 768 ? 15 : 10 }, + axisLine: { + lineStyle: { + color: 'black' + } + }, + axisTick: { show: false }, + splitLine: { show: false } + } + ], + // 下拉条 + dataZoom: [ + { + type: 'inside', + xAxisIndex: [0, 1], + start: 55, + end: 100 + }, + { + show: true, + xAxisIndex: [0, 1], + type: 'slider', + top: window.innerWidth > 768 ? '92%' : '91%', + left: window.innerWidth > 768 ? '10%' : '8%', + start: 98, + end: 100 + } + ], + visualMap: [ + { + type: 'piecewise', + show: false, + pieces: maData.singleTypeGreen, + outOfRange: { + color: 'green' + }, + dimension: 0, + seriesIndex: 6 + }, + { + type: 'piecewise', + show: false, + pieces: maData.singleTypeRed, + outOfRange: { + color: 'red' + }, + dimension: 0, + seriesIndex: 7 + }, + { + type: 'piecewise', + show: false, + pieces: maData.singleTypeYellow, + outOfRange: { + color: 'yellow' + }, + dimension: 0, + seriesIndex: 8 + } + ], + series: [ + // 第一条K线 + { + name: '进攻K线', + type: 'candlestick', + data: dealData1.values, + xAxisIndex: 0, // 使用第一个 X 轴 + yAxisIndex: 0, // 使用第一个 Y 轴 + 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', + data: dealData2.values, + xAxisIndex: 0, // 使用第一个 X 轴 + yAxisIndex: 0, // 使用第一个 Y 轴 + 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', + data: dealData3.values, + xAxisIndex: 0, // 使用第一个 X 轴 + yAxisIndex: 0, // 使用第一个 Y 轴 + itemStyle: { + normal: { + color: 'orange', // 默认颜色 + color0: 'orange', + borderColor: 'orange', + borderColor0: 'orange' + } + }, + gridIndex: 0 + }, + { + name: '撤退K线', + type: 'candlestick', + data: dealData4.values, + xAxisIndex: 0, // 使用第一个 X 轴 + yAxisIndex: 0, // 使用第一个 Y 轴 + itemStyle: { + normal: { + color: 'rgb(84,252,252)', // 默认颜色 + color0: 'rgb(84,252,252)', + borderColor: 'rgb(84,252,252)', + borderColor0: 'rgb(84,252,252)' + } + }, + gridIndex: 0 + }, + // 第二条K线 + { + type: 'candlestick', + name: '日K', + xAxisIndex: 1, // 使用第二个 X 轴 + yAxisIndex: 1, // 使用第二个 Y 轴 + data: dealData.values, + itemStyle: { + normal: { + color0: 'red', + color: 'green', + borderColor0: 'red', + borderColor: 'green' + } + }, + gridIndex: 1 + }, + { + name: '{green|━}{red|━} ' + '牵牛绳', // 将牵牛绳样式应用到文本前缀 + type: 'line', + data: [], // 设置为空数组,不显示数据点 + smooth: true, + symbol: 'none', + xAxisIndex: 0, // 线图与第一个 K 线图共享 X 轴 + yAxisIndex: 0, // 线图与第一个 K 线图共享 Y 轴 + showSymbol: false, // 隐藏符号 + lineStyle: { + opacity: 0 // 使线条透明 + }, + itemStyle: { + normal: { + color: 'green' + } + }, + gridIndex: 0 // 确保线图与第一个K线图共享网格 + }, + { + name: '虫线', + type: 'line', + data: maData.greenData, + smooth: true, + symbol: 'none', + xAxisIndex: 0, // 线图与第一个 K 线图共享 X 轴 + yAxisIndex: 0, // 线图与第一个 K 线图共享 Y 轴 + itemStyle: { + normal: { + color: 'green', + lineStyle: { + // color: 'orange', // 线的颜色 + width: 2, // 线宽 + type: 'solid' // 线类型 + } + } + }, + gridIndex: 0 // 确保线图与第一个K线图共享网格 + }, + { + name: '龙线', + type: 'line', + data: maData.redData, + smooth: true, + symbol: 'none', + xAxisIndex: 0, // 线图与第一个 K 线图共享 X 轴 + yAxisIndex: 0, // 线图与第一个 K 线图共享 Y 轴 + itemStyle: { + normal: { + color: 'red', + lineStyle: { + // color: 'orange', // 线的颜色 + width: 2, // 线宽 + type: 'solid' // 线类型 + } + } + }, + gridIndex: 0 // 确保线图与第一个K线图共享网格 + }, + { + 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 + } + ] + } + + } else if (klineData.type === 7) { + console.log('进入第7分类') + + const upColor = '#00da3c' + const downColor = '#ec0000' + function calculateMA(index, data) { + let result = [] + if (data.FTLINE) { + data.FTLINE.forEach((item) => { + result.push(item[index]) + }) + } + return result + } + var dealData = splitData(data.KLine20) + var bodongliang = splitData(data.WAVEVOL) + function bodongliangData(values, i) { + return values.map((subArray) => subArray[i]) + } + KlineOption = { + animation: false, + legend: { + textStyle: { + fontSize: window.innerWidth > 768 ? 15 : vwToPx(1.8) + }, + left: 'center', + data: [ + { + name: '天线' + }, + { + name: '飞线', + itemStyle: { + color: '#000' + } + }, + { + name: '中线' + }, + { + name: '流线' + } + ] + }, + tooltip: [ + { + trigger: 'axis', + formatter: function (a, b, d) { + if (a[0].seriesIndex == 0) { + return ( + a[0].name + + '
' + + '开盘价' + + ':' + + a[0].data[1] + + '
' + + '收盘价' + + ':' + + a[0].data[2] + + '
' + + '最低价' + + ':' + + a[0].data[3] + + '
' + + '最高价' + + ':' + + a[0].data[4] + + '
' + + a[3].marker + + a[3].seriesName + + ':' + + a[3].value + + '
' + + a[1].marker + + a[1].seriesName + + ':' + + a[1].value + + '
' + + a[2].marker + + a[2].seriesName + + ':' + + a[2].value + + '
' + + a[4].marker + + a[4].seriesName + + ':' + + a[4].value + ) + // }else if(a[0].seriesIndex == 5){ + // console.log(a[0].seriesIndex,'a[0].seriesIndex') + // return a[0].name+ "
" + a[0].seriesName + ":" + a[0].value + "
" + a[1].seriesName + ":" + a[1].value + "
" + a[2].seriesName + ":" + a[2].value + "
" + a[3].seriesName + ":" + a[3].value + "
" + a[4].seriesName + ":" + a[4].value + "
" + a[5].seriesName + ":" + a[5].value + } else { + return ( + a[0].name + + '
' + + a[0].marker + + a[0].seriesName + + ':' + + a[0].value + + '
' + + a[1].marker + + a[1].seriesName + + ':' + + a[1].value + + '
' + + a[2].marker + + a[2].seriesName + + ':' + + a[2].value + + '
' + + a[3].marker + + a[3].seriesName + + ':' + + a[3].value + + '
' + + a[4].marker + + a[4].seriesName + + ':' + + a[4].value + + '
' + ) + } + }, + axisPointer: { + type: 'cross' + }, + backgroundColor: '#fff', + borderWidth: 1, + borderColor: '#ccc', + padding: 10, + textStyle: { + color: '#000' + } + } + ], + axisPointer: { + link: [ + { + xAxisIndex: 'all' + } + ], + label: { + backgroundColor: '#777' + } + }, + toolbox: { + show: false + }, + + visualMap: { + show: false, + seriesIndex: 5, + dimension: 2, + pieces: [ + { + value: 1, + color: downColor + }, + { + value: -1, + color: upColor + } + ] + }, + grid: [ + { + top: '8%', + // left: window.innerWidth > 768 ? '10%' : '17%', + right: '8%', + height: '40%', + containLabel: false + }, + { + // left: window.innerWidth > 768 ? '10%' : '17%', + right: '8%', + top: '10%', + height: '1%', + containLabel: false + }, + { + // left: window.innerWidth > 768 ? '10%' : '17%', + right: '8%', + top: window.innerWidth > 768 ? '60%' : '63%', + height: '30%', + 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: '#8392A5' + } + } + }, + { + type: 'category', + gridIndex: 1, + data: [], + boundaryGap: true, + axisLine: { onZero: false }, + axisTick: { show: false }, + splitLine: { show: false }, + axisLabel: { show: false }, + min: 'dataMin', + max: 'dataMax', + show: false + }, + { + type: 'category', + gridIndex: 2, + data: dealData.categoryData, + boundaryGap: true, + axisLine: { + onZero: true, + lineStyle: { + color: '#8392A5' + } + }, + axisTick: { show: false }, + splitLine: { show: false }, + axisLabel: { show: false }, + min: 'dataMin', + max: 'dataMax' + } + ], + yAxis: [ + { + scale: true, + splitArea: { + show: false + }, + splitLine: { + show: !1 + }, + axisLine: { + lineStyle: { + color: '#8392A5' + } + } + }, + { + scale: true, + gridIndex: 1, + splitNumber: 2, + min: '0', + max: '100', + axisLabel: { show: false }, + + axisLine: { show: false }, + axisTick: { show: false }, + splitLine: { show: false } + }, + { + scale: true, + gridIndex: 2, + splitNumber: 2, + axisLabel: { show: true }, + axisLine: { onZero: true }, + axisLine: { + show: true, + onZero: false, + lineStyle: { + color: '#8392A5' + } + }, + axisTick: { show: true }, + splitLine: { show: false } + } + ], + dataZoom: [ + { + type: 'inside', + xAxisIndex: [0, 1, 2], + start: 50, + end: 100, + textStyle: { + color: '#8392A5' + } + }, + { + show: true, + xAxisIndex: [0, 1, 2], + type: 'slider', + top: '85%', + start: 50, + end: 100, + textStyle: { + color: '#8392A5' + } + } + ], + series: [ + { + name: 'Dow-Jones index', + type: 'candlestick', + data: dealData.values, + itemStyle: { + normal: { + color0: '#ec0000', + color: '#00da3c', + borderColor0: '#ec0000', + borderColor: '#00da3c' + } + } + }, + { + name: '飞线', + type: 'line', + data: calculateMA(1, data), + smooth: true, + symbol: 'none', + itemStyle: { + normal: { + color: '#00a32e', + lineStyle: { + color: '#00a32e', + width: 2, + type: 'solid' + } + } + } + }, + { + name: '中线', + type: 'line', + data: calculateMA(2, data), + smooth: true, + symbol: 'none', + itemStyle: { + normal: { + color: '#de0000', + lineStyle: { + color: '#de0000', + width: 2, + type: 'solid' + } + } + } + }, + { + name: '天线', + type: 'line', + data: calculateMA(3, data), + smooth: true, + symbol: 'none', + itemStyle: { + normal: { + color: '#ffb300', + lineStyle: { + color: '#ffb300', + width: 2, + type: 'solid' + } + } + } + }, + { + name: '流线', + type: 'line', + data: calculateMA(4, data), + smooth: true, + symbol: 'none', + itemStyle: { + normal: { + color: '#00c8ff', + lineStyle: { + color: '#00c8ff', + width: 2, + type: 'solid' + } + } + } + }, + { + name: '买盘', + data: bodongliangData(bodongliang.values, 1), + xAxisIndex: 2, + yAxisIndex: 2, + type: 'bar', + stack: '1', + itemStyle: { + normal: { + color: '#ec0000' + } + } + }, + { + name: '卖盘', + data: bodongliangData(bodongliang.values, 0), + xAxisIndex: 2, + yAxisIndex: 2, + type: 'bar', + stack: '1', + itemStyle: { + normal: { + color: '#00ffff' + } + } + }, + + { + name: 'CJ', + data: bodongliangData(bodongliang.values, 2), + xAxisIndex: 2, + yAxisIndex: 2, + type: 'line', + smooth: !0, + showSymbol: false, + itemStyle: { + normal: { + color: '#B0B0B0' + } + }, + lineStyle: { + width: 2 + } + }, + { + name: 'CD', + data: bodongliangData(bodongliang.values, 3), + xAxisIndex: 2, + yAxisIndex: 2, + type: 'line', + smooth: !0, + showSymbol: false, + itemStyle: { + normal: { + color: '#ffb300' + } + }, + lineStyle: { + width: 2 + } + }, + { + name: 'CK', + data: bodongliangData(bodongliang.values, 4), + xAxisIndex: 2, + yAxisIndex: 2, + type: 'line', + smooth: !0, + showSymbol: false, + itemStyle: { + normal: { + color: '#ff00ff' + } + }, + lineStyle: { + width: 2 + } + } + ] + } + + } else { + console.log('其他分类') + + var dealData = splitData(data.KLine20) + console.log('dealData', dealData) + + KlineOption = { + title: { + text: klineData.name, + left: 50, + }, + tooltip: { + trigger: 'axis', + formatter: function (a, b, d) { + let def = + a[0].name + + '
' + + '开盘价' + + a[0].data[1] + + '
' + + '收盘价' + + a[0].data[2] + + '
' + + '最低价' + + a[0].data[3] + + '
' + + '最高价' + + a[0].data[4]; + + if (a[1] && a[1].seriesName) { + def += '
' + a[1].seriesName + ':' + a[1].value; + } + return def; + }, + axisPointer: { + animation: false, + type: 'line', + lineStyle: { + color: '#376df4', + width: 2, + opacity: 1 + } + }, + confine: true // 确保提示框不超出画布 + }, + //控制坐标轴 + grid: { + left: '12%', + right: '10%', + bottom: '10%', + top: '18%' + }, + xAxis: { + type: 'category', + data: dealData.categoryData, + axisLine: { lineStyle: { color: '#8392A5' } } + }, + yAxis: { + scale: !0, //true + // 自定义纵坐标现实的数据 + axisLabel: { + formatter: function (value) { + return value // 返回原始值 + } + }, + axisLine: { lineStyle: { color: '#8392A5' } }, + splitLine: { + show: !1 + } + }, + dataZoom: [ + { + textStyle: { + color: '#8392A5' + }, + handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z', + handleSize: '80%', + dataBackground: { + areaStyle: { + color: '#8392A5' + }, + lineStyle: { + opacity: 0.8, + color: '#8392A5' + } + }, + handleStyle: { + color: '#fff', + shadowBlur: 3, + shadowColor: 'rgba(0, 0, 0, 0.6)', + shadowOffsetX: 2, + shadowOffsetY: 2 + } + }, + { + type: 'inside', + start: 1, + end: 100, + zoomOnMouseWheel: true, + moveOnMouseMove: true + } + ], + animation: false, + series: [ + { + type: 'candlestick', + name: '\u65e5K', + data: dealData.values, + itemStyle: { + normal: { + color0: '#FD1050', + color: '#0CF49B', + borderColor0: '#FD1050', + borderColor: '#0CF49B' + } + }, + // barWidth: isMobile ? 6 : isTablet ? 8 : 10 + }, + { + name: 'MA5', + type: 'line', + data: (function (a) { + for (var MA5 = [], d = 0, g = dealData.values.length; d < g; d++) { + if (d < a) { + MA5.push('-') + } else { + for (var f = 0, e = 0; e < a; e++) { + f += dealData.values[d - e][1] + } + MA5.push((f / a).toFixed(2)) + } + } + return MA5 + })(5), + smooth: true, + lineStyle: { + width: isMobile ? 1 : 2, + opacity: 0.8 + } + } + ] + }; + + } + + + + + console.log('KLine渲染: 图表配置完成'); diff --git a/src/views/Announcement.vue b/src/views/Announcement.vue index 49a9a4d..db2e460 100644 --- a/src/views/Announcement.vue +++ b/src/views/Announcement.vue @@ -1,6 +1,17 @@ @@ -136,8 +171,7 @@ onMounted(() => { color: #4591E7; } -.code{ +.code { cursor: pointer; } - diff --git a/src/views/Feedback.vue b/src/views/Feedback.vue index 2ddb55a..a684fb3 100644 --- a/src/views/Feedback.vue +++ b/src/views/Feedback.vue @@ -1,7 +1,7 @@