Преглед на файлове

Merge branch 'master' of http://47.101.159.18:3000/Muchinfo/MTP2.0_WEB

huangbin преди 4 години
родител
ревизия
9719aff7d4

+ 5 - 4
src/common/components/echart/echart-base/index.less

@@ -1,10 +1,11 @@
 .echart {
-    position: relative;
-    overflow: hidden;
+    display       : flex;
+    flex-direction: column;
+    position      : relative;
+    overflow      : hidden;
 
     &__container {
-        width : 100%;
-        height: 100%;
+        flex: 1;
 
         .tooltip {
             &-title {

+ 15 - 14
src/common/components/echart/echart-base/index.vue

@@ -1,6 +1,8 @@
 <template>
-    <div class="echart" :style="{ width: width, height: height }">
-        <div ref="chartElement" class="echart__container"></div>
+    <div ref="chartElement" class="echart" :style="{ width: width, height: height }">
+        <template v-for="i in options.length" :key="i">
+            <div class="echart__container"></div>
+        </template>
     </div>
 </template>
 
@@ -22,35 +24,34 @@ export default defineComponent({
             type: String,
             default: '100%',
         },
-        // 图表配置项
+        // 图表配置项,支持多图表(待完善...)
         options: {
-            type: Object as PropType<EChartsOption>,
-            required: true,
+            type: Array as PropType<EChartsOption[]>,
+            default: [],
         },
         loading: {
             type: Boolean,
             default: true,
         },
     },
-    setup(props, context) {
+    setup(props) {
         // 图表容器元素
         const chartElement = ref<HTMLElement>();
 
         onMounted(() => {
-            const { echart, setOptions, showLoading } = useEchart(chartElement.value!, context);
+            const { setOptions, showLoading } = useEchart(chartElement.value!);
 
             watchEffect(() => {
-                if (props.loading) {
-                    echart.clear();
-                    showLoading(true);
-                }
+                showLoading(props.loading);
             });
 
             watch(
                 () => props.options,
-                (option) => {
-                    // 设置图表配置项
-                    setOptions(option);
+                (val) => {
+                    if (!props.loading) {
+                        // 设置图表配置项
+                        setOptions(val);
+                    }
                 }
             );
         });

+ 43 - 34
src/common/components/echart/echart-base/setup.ts

@@ -15,54 +15,62 @@ const createIframe = (parent: HTMLElement): HTMLIFrameElement => {
 }
 
 type UseEchart = {
-    echart: echarts.ECharts,
-    setOptions: (options: echarts.EChartsOption, notMerge?: boolean) => void,
+    setOptions: (options: echarts.EChartsOption[], notMerge?: boolean) => void,
     showLoading: (loading: boolean) => void,
 }
 
-export function useEchart(el: HTMLElement, ctx: SetupContext): UseEchart {
-    const iframe = createIframe(el.parentElement || el);
-    // 初始化图表
-    const echart = echarts.init(el);
+export function useEchart(el: HTMLElement): UseEchart {
+    const iframe = createIframe(el);
+    const echartMap: echarts.ECharts[] = [];
+
+    el.querySelectorAll('.echart__container').forEach((item) => {
+        // 初始化图表
+        const echart = echarts.init(item as HTMLElement);
+        echartMap.push(echart);
+    })
 
     // 显示加载动画
     const showLoading = (loading: boolean) => {
         if (loading) {
-            // 当前主题
-            const theme = getTheme();
-            switch (theme.value) {
-                case ThemeEnum.default:
-                case ThemeEnum.dark:
-                    echart.showLoading({
-                        text: '加载中...',
-                        textColor: '#fff',
-                        color: 'rgba(255, 255, 255, 0.75)',
-                        maskColor: 'transparent',
-                    });
-                    break;
-                default:
-                    echart.showLoading({
-                        text: '加载中...',
-                        maskColor: 'transparent',
-                    });
-            }
+            echartMap.forEach((echart) => {
+                echart.clear(); // 清空图表
+                const theme = getTheme(); // 当前主题
+                switch (theme.value) {
+                    case ThemeEnum.default:
+                    case ThemeEnum.dark:
+                        echart.showLoading({
+                            text: '加载中...',
+                            textColor: '#fff',
+                            color: 'rgba(255, 255, 255, 0.75)',
+                            maskColor: 'transparent',
+                        });
+                        break;
+                    default:
+                        echart.showLoading({
+                            text: '加载中...',
+                            maskColor: 'transparent',
+                        });
+                }
+            })
         }
     };
 
     // 图表配置项
-    const setOptions = (options: echarts.EChartsOption, notMerge = false) => {
-        echart.setOption(options, notMerge);
-        echart.hideLoading();
-        ctx.emit('update:loading', false);
+    const setOptions = (options: echarts.EChartsOption[], notMerge = false) => {
+        options.forEach((opt, i) => {
+            echartMap[i].setOption(opt, notMerge);
+            echartMap[i].hideLoading();
+        });
     };
 
     const onresize = () => {
+        // 防抖函数待优化,同时调用多次timeoutId可能相同,导致后面的覆盖前面
         debounce(() => {
-            // 如果 canvas 宽高和父元素不一致,重新渲染
-            if (echart.getWidth() !== el.clientWidth || echart.getHeight() !== el.clientHeight) {
+            // 重置图表大小
+            echartMap.forEach((echart) => {
                 echart.resize && echart.resize();
-            }
-        }, 50)
+            })
+        }, 50);
     };
 
     const addEventListener = () => {
@@ -78,8 +86,10 @@ export function useEchart(el: HTMLElement, ctx: SetupContext): UseEchart {
     addEventListener();
 
     onUnmounted(() => {
+        echartMap.forEach((echart) => {
+            echart.dispose();
+        })
         removeEventListener();
-        echart.dispose();
     })
 
     // 针对 keepalive 缓存组件
@@ -93,7 +103,6 @@ export function useEchart(el: HTMLElement, ctx: SetupContext): UseEchart {
     })
 
     return {
-        echart,
         setOptions,
         showLoading,
     }

+ 121 - 43
src/common/components/echart/echart-kline/index.vue

@@ -1,5 +1,5 @@
 <template>
-    <echart-base :options="options" v-model:loading="loading"></echart-base>
+    <echart-base :options="[options]" v-model:loading="loading"></echart-base>
 </template>
 
 <script lang="ts">
@@ -17,15 +17,20 @@ export default defineComponent({
         EchartBase,
     },
     props: {
+        // 实时行情数据
+        quoteData: {
+            type: Object as PropType<QueryQuoteDayRsp>,
+            required: true,
+        },
         // 周期类型
         cycleType: {
             type: Number as PropType<CycleType>,
             required: true,
         },
-        // 实时行情数据
-        quoteData: {
-            type: Object as PropType<QueryQuoteDayRsp>,
-            required: true,
+        // 指标类型
+        seriesType: {
+            type: String,
+            default: 'MACD',
         },
     },
     setup(props) {
@@ -37,17 +42,20 @@ export default defineComponent({
         const handleData = (rawData: QueryHistoryDatasRsp[]): void => {
             historyIndexs.length = 0; // 清空数据
             const datas: number[][] = [],
-                times: string[] = [];
+                times: string[] = [],
+                volume: number[] = [];
 
             rawData.forEach((item, index) => {
-                const { o, c, h, l, ts } = item;
-                datas.push([o, c, h, l]);
-                times.push(moment(ts).format('MM-DD HH:mm'));
+                const { o, c, h, l, ts, tv } = item;
+                datas.push([o, c, l, h]);
+                times.push(moment(ts).format('YYYY/MM/DD HH:mm:ss'));
+                volume.push(tv);
                 if (!item.f) historyIndexs.push(index); // 排除补充数据
             });
 
             chartData.datas = datas;
             chartData.times = times;
+            chartData.volume = volume;
             chartData.ma5 = calcMA(datas, 5);
             chartData.ma10 = calcMA(datas, 10);
             chartData.ma15 = calcMA(datas, 15);
@@ -56,54 +64,56 @@ export default defineComponent({
             chartData.macd = macd;
             chartData.dif = dif;
             chartData.dea = dea;
+
+            chartData.kdj = calcKDJ(datas);
+            chartData.cci = clacCCI(datas);
         };
 
         // 计算平均线
         const calcMA = (data: number[][], count: number) => {
-            const result: string[] = [];
+            const ma: string[] = [];
             if (data.length >= count) {
                 // 均线起始位置
                 const startIndex = historyIndexs[count - 1];
-                data.forEach((item, index) => {
-                    if (index < startIndex) {
-                        result.push('-');
+                for (let i = 0; i < data.length; i++) {
+                    if (i < startIndex) {
+                        ma.push('-');
                     } else {
-                        const j = historyIndexs.findIndex((val) => val === index);
+                        const j = historyIndexs.findIndex((val) => val === i);
                         // 判断是否补充数据
                         if (j === -1) {
                             // 取上个平均值
-                            result.push(result[result.length - 1]);
+                            ma.push(ma[ma.length - 1]);
                         } else {
                             // 向后取MA数
                             const maIndexs = historyIndexs.slice(j - (count - 1), j + 1);
                             // 计算总价,data[val][1]=收盘价
                             const total = maIndexs.reduce((sum, val) => sum + data[val][1], 0);
                             // 计算均线
-                            result.push((total / count).toFixed(2));
+                            ma.push((total / count).toFixed(2));
                         }
                     }
-                });
+                }
             }
-            return result;
+            return ma;
         };
 
         // 计算EMA
         const calcEMA = (close: number[], n: number) => {
-            const result: number[] = [],
+            const ema: number[] = [],
                 a = 2 / (n + 1); // 平滑系数
-
             for (let i = 0; i < close.length; i++) {
                 if (i === 0) {
                     //第一个EMA(n)是前n个收盘价代数平均
-                    const ema = close.slice(0, n).reduce((sum, val) => sum + val, 0) / n;
-                    result.push(ema);
+                    const result = close.slice(0, n).reduce((sum, val) => sum + val, 0) / n;
+                    ema.push(result);
                 } else {
                     // EMA(n) = α × Close + (1 - α) × EMA(n - 1)
-                    const ema = a * close[i] + (1 - a) * result[i - 1];
-                    result.push(ema);
+                    const result = a * close[i] + (1 - a) * ema[i - 1];
+                    ema.push(result);
                 }
             }
-            return result;
+            return ema;
         };
 
         // 计算DEA
@@ -113,21 +123,21 @@ export default defineComponent({
 
         // 计算DIF
         const calcDIF = (close: number[]) => {
-            const result: number[] = [],
+            const dif: number[] = [],
                 emaShort = calcEMA(close, 12),
                 emaLong = calcEMA(close, 26);
 
             for (let i = 0; i < close.length; i++) {
-                const dif = emaShort[i] - emaLong[i];
-                result.push(dif);
+                const result = emaShort[i] - emaLong[i];
+                dif.push(result);
             }
-            return result;
+            return dif;
         };
 
         // 计算MACD
         const calcMACD = (data: number[][]) => {
             const macd = [],
-                close = data.map((item) => item[1]), // 收盘价数据
+                close = data.map((val) => val[1]), // 收盘价数据
                 dif = calcDIF(close),
                 dea = calcDEA(dif);
 
@@ -141,9 +151,61 @@ export default defineComponent({
             };
         };
 
+        // 计算KDJ
+        const calcKDJ = (data: number[][]) => {
+            const kdj: string[][] = []; // [[k,d,j]]
+            for (let i = 0; i < data.length; i++) {
+                if (i < 8) {
+                    kdj.push(['-', '-', '-']);
+                } else {
+                    let rsv = 50; // 如果最低价等于最高价,RSV默认值为50
+                    if (data[i][2] !== data[i][3]) {
+                        const n9 = data.slice(i - 8, i + 1).map((val) => val[1]), // 取前9个收盘价
+                            max = Math.max(...n9),
+                            min = Math.min(...n9);
+                        // 计算RSV
+                        rsv = ((data[i][1] - min) / (max - min)) * 100;
+                    }
+
+                    const yestK = Number(kdj[kdj.length - 1][0]); // 取前一日K值
+                    const yestD = Number(kdj[kdj.length - 1][1]); // 取前一日D值
+
+                    if (isNaN(yestK) || isNaN(yestD)) {
+                        // 如果前一日的K值或D值不存在则默认值为50
+                        kdj.push(['50', '50', '50']);
+                    } else {
+                        const k = (2 / 3) * yestK + (1 / 3) * rsv,
+                            d = (2 / 3) * yestD + (1 / 3) * yestK,
+                            j = 3 * k - 2 * d;
+
+                        kdj.push([k.toFixed(2), d.toFixed(2), j.toFixed(2)]);
+                    }
+                }
+            }
+            return kdj;
+        };
+
+        const clacCCI = (data: number[][]) => {
+            const cci: string[] = [];
+            for (let i = 0; i < data.length; i++) {
+                if (i < 13) {
+                    cci.push('-');
+                } else {
+                    const tp = (data[i][1] + data[i][2] + data[i][3]) / 3, // (收盘价 + 最低价 + 最高价) / 3
+                        n14 = data.slice(i - 13, i + 1), // 取前14条数据
+                        ma = n14.reduce((sum, val) => sum + (val[1] + val[2] + val[3]) / 3, 0) / 14, // 计算前14条数据的(TP)价总和÷N
+                        md = n14.reduce((sum, val) => sum + Math.abs(ma - (val[1] + val[2] + val[3]) / 3), 0) / 14, // 计算前14条数据的(MA-TP)价总和÷N
+                        result = (tp - ma) / md / 0.015;
+
+                    cci.push(result.toFixed(2));
+                }
+            }
+            return cci;
+        };
+
         // 更新图表K线数据
         const updateChartData = () => {
-            const { datas, times } = chartData,
+            const { datas, times, volume, kdj } = chartData,
                 lastIndex = datas.length - 1, // 历史行情最后索引位置
                 lastTime = moment(times[times.length - 1]), // 历史行情最后时间
                 newTime = moment(props.quoteData.lasttime), // 实时行情最新时间
@@ -176,8 +238,9 @@ export default defineComponent({
             // 判断时间差是否大于周期时间
             if (diffTime > cycleMilliseconds) {
                 lastTime.add(cycleMilliseconds, 'ms');
-                times.push(lastTime.format('MM-DD HH:mm')); // 添加历史行情时间
+                times.push(lastTime.format('YYYY/MM/DD HH:mm:ss')); // 添加历史行情时间
                 datas.push([newPrice, newPrice, newPrice, newPrice]); // 添加历史行情数据
+                volume.push(0);
                 historyIndexs.push(lastIndex + 1); // 添加历史行情索引
             } else {
                 const lastPrice = datas[lastIndex];
@@ -191,21 +254,25 @@ export default defineComponent({
                 datas[lastIndex] = lastPrice; // 更新历史行情数据
             }
 
-            // 延迟图表更新,减少卡顿
-            debounce(() => {
-                chartData.ma5 = calcMA(datas, 5);
-                chartData.ma10 = calcMA(datas, 10);
-                chartData.ma15 = calcMA(datas, 15);
+            chartData.ma5 = calcMA(datas, 5);
+            chartData.ma10 = calcMA(datas, 10);
+            chartData.ma15 = calcMA(datas, 15);
 
-                const { dif, dea, macd } = calcMACD(datas);
-                chartData.dif = dif;
-                chartData.dea = dea;
-                chartData.macd = macd;
+            const { dif, dea, macd } = calcMACD(datas);
+            chartData.dif = dif;
+            chartData.dea = dea;
+            chartData.macd = macd;
+
+            chartData.kdj = calcKDJ(datas);
+            chartData.cci = clacCCI(datas);
 
-                updateOptions();
+            // 延迟图表更新,减少卡顿
+            debounce(() => {
+                updateOptions(props.seriesType);
             }, 1000);
         };
 
+        // 监听行情最新价推送
         watch(
             () => props.quoteData.last,
             () => {
@@ -215,6 +282,16 @@ export default defineComponent({
             }
         );
 
+        // 监听指标类型
+        watch(
+            () => props.seriesType,
+            (val) => {
+                if (!loading.value) {
+                    updateOptions(val);
+                }
+            }
+        );
+
         // 监听周期选择变化
         watchEffect(() => {
             loading.value = true;
@@ -225,10 +302,11 @@ export default defineComponent({
             };
             // 查询K线数据
             queryHistoryDatas(params).then((res) => {
+                loading.value = false;
                 // 日期升序排序
                 const kdata = res.sort((a, b) => moment(a.ts).valueOf() - moment(b.ts).valueOf());
                 handleData(kdata);
-                initOptions();
+                initOptions(props.seriesType);
             });
         });
 

+ 154 - 34
src/common/components/echart/echart-kline/setup.ts

@@ -13,18 +13,24 @@ type Colors = {
     yAxisLineColor: string,
     seriesMarkLabelColor: string,
     seriesMarkLineColor: string,
+    upColor: string,
+    downColor: string,
+    equalColor: string,
 }
 
 // 图表数据
 export type ChartData = {
     datas: number[][], // 历史数据
     times: string[], // 历史日期
-    ma5: (number | string)[], //均线数据
-    ma10: (number | string)[], //均线数据
-    ma15: (number | string)[], //均线数据
-    dif: (number | string)[],
-    dea: (number | string)[],
-    macd: (number | string)[],
+    ma5: string[], //均线数据
+    ma10: string[], //均线数据
+    ma15: string[], //均线数据
+    dif: string[],
+    dea: string[],
+    macd: string[],
+    volume: number[],
+    kdj: string[][], // [[k,d,j]]
+    cci: string[]
 }
 
 export function handleEchart() {
@@ -40,14 +46,15 @@ export function handleEchart() {
         ma15: [],
         dif: [],
         dea: [],
-        macd: []
+        macd: [],
+        volume: [],
+        kdj: [],
+        cci: [],
     };
-    // 图表当前指示数据
-    let chartDataIndex = 0;
 
     // 初始化图表配置
-    const initOptions = () => {
-        const { datas, times, ma5, ma10, ma15, dea, dif, macd } = chartData;
+    const initOptions = (seriesType: string) => {
+        const { datas, times, ma5, ma10, ma15, dea, dif, macd, volume, kdj, cci } = chartData;
         const option: EChartsOption = {
             animation: false,
             axisPointer: {
@@ -73,11 +80,11 @@ export function handleEchart() {
                 {
                     //图例控件,点击图例控制哪些系列不显示
                     type: 'scroll',
-                    data: ['DIF', 'DEA'],
+                    data: [],
                     itemWidth: 14,
                     itemHeight: 2,
                     left: '5%',
-                    top: '70%',
+                    top: '68%',
                     textStyle: {
                         fontSize: 12,
                     },
@@ -98,9 +105,11 @@ export function handleEchart() {
                 className: 'tooltip',
                 formatter: (params: any) => {
                     let result = '';
-                    params.forEach((item: any) => {
+                    params.forEach((item: any, index: number) => {
+                        if (index === 0) {
+                            result += '<div class="tooltip-title">' + moment(item.name).format('YYYY-MM-DD HH:mm') + '</div>';
+                        }
                         if (item.seriesType === 'candlestick') {
-                            result += '<div class="tooltip-title">' + item.name + '</div>';
                             item.value.forEach((data: number[], i: number) => {
                                 if (i > 0) {
                                     result += '<div class="tooltip-item"><span><i style="background-color:' + item.color + ';"></i>';
@@ -121,7 +130,7 @@ export function handleEchart() {
                                     result += '</span><span>' + data + '</span></div>';
                                 }
                             })
-                        } else if (item.seriesType === 'line') {
+                        } else {
                             result += '<div class="tooltip-item"><span><i style="background-color:' + item.color + ';"></i>' + item.seriesName + '</span><span>' + item.value + '</span></div>';
                         }
                     })
@@ -136,37 +145,42 @@ export function handleEchart() {
                     right: '8%',
                     height: '55%',
                 },
-                // MACD
+                // MACD、VOL、KDJ
                 {
-                    top: '75%',
+                    top: '73%',
                     left: '8%',
                     right: '8%',
                     height: '20%',
                 }
             ],
             xAxis: [
+                // K线时间轴
                 {
                     type: 'category',
-                    // X轴时间线
                     data: times,
                     axisLabel: {
                         formatter: (val: any) => moment(val).format('YYYY/MM/DD'),
                     },
                     splitLine: {
-                        // 坐标分隔线
                         show: true,
                     },
                 },
+                // MACD、VOL、KDJ时间轴
                 {
                     type: 'category',
-                    // X轴时间线
                     data: times,
                     gridIndex: 1,
                     axisLabel: {
                         show: false,
+                        formatter: (val: any) => moment(val).format('YYYY/MM/DD'),
+                    },
+                    axisLine: {
+                        show: false,
+                    },
+                    axisTick: {
+                        show: false,
                     },
                     splitLine: {
-                        // 坐标分隔线
                         show: true,
                     },
                 }
@@ -216,7 +230,7 @@ export function handleEchart() {
                         },
                         data: [
                             {
-                                // 最新
+                                // 当前
                                 yAxis: datas[datas.length - 1][1],
                             },
                         ],
@@ -259,10 +273,19 @@ export function handleEchart() {
                     },
                 },
                 {
+                    name: 'VOL',
+                    type: 'bar',
+                    sampling: 'average',
+                    data: seriesType === 'VOL' ? volume : [],
+                    xAxisIndex: 1,
+                    yAxisIndex: 1,
+                    barWidth: '60%',
+                },
+                {
                     name: 'MACD',
                     type: 'bar',
                     sampling: 'average',
-                    data: macd,
+                    data: seriesType === 'MACD' ? macd : [],
                     xAxisIndex: 1,
                     yAxisIndex: 1,
                     barWidth: '20%',
@@ -271,7 +294,7 @@ export function handleEchart() {
                     name: 'DIF',
                     type: 'line',
                     sampling: 'average',
-                    data: dif,
+                    data: seriesType === 'MACD' ? dif : [],
                     xAxisIndex: 1,
                     yAxisIndex: 1,
                     smooth: true,
@@ -285,7 +308,63 @@ export function handleEchart() {
                     name: 'DEA',
                     type: 'line',
                     sampling: 'average',
-                    data: dea,
+                    data: seriesType === 'MACD' ? dea : [],
+                    xAxisIndex: 1,
+                    yAxisIndex: 1,
+                    smooth: true,
+                    symbol: 'none',
+                    lineStyle: {
+                        width: 1,
+                        opacity: 0.8,
+                    },
+                },
+                {
+                    name: 'K',
+                    type: 'line',
+                    sampling: 'average',
+                    data: seriesType === 'KDJ' ? kdj.map((val) => val[0]) : [],
+                    xAxisIndex: 1,
+                    yAxisIndex: 1,
+                    smooth: true,
+                    symbol: 'none',
+                    lineStyle: {
+                        width: 1,
+                        opacity: 0.8,
+                    },
+                },
+                {
+                    name: 'D',
+                    type: 'line',
+                    sampling: 'average',
+                    data: seriesType === 'KDJ' ? kdj.map((val) => val[1]) : [],
+                    xAxisIndex: 1,
+                    yAxisIndex: 1,
+                    smooth: true,
+                    symbol: 'none',
+                    lineStyle: {
+                        width: 1,
+                        opacity: 0.8,
+                    },
+                },
+                {
+                    name: 'J',
+                    type: 'line',
+                    sampling: 'average',
+                    data: seriesType === 'KDJ' ? kdj.map((val) => val[2]) : [],
+                    xAxisIndex: 1,
+                    yAxisIndex: 1,
+                    smooth: true,
+                    symbol: 'none',
+                    lineStyle: {
+                        width: 1,
+                        opacity: 0.8,
+                    },
+                },
+                {
+                    name: 'CCI',
+                    type: 'line',
+                    sampling: 'average',
+                    data: seriesType === 'CCI' ? cci : [],
                     xAxisIndex: 1,
                     yAxisIndex: 1,
                     smooth: true,
@@ -302,8 +381,8 @@ export function handleEchart() {
     };
 
     // 动态更新数据
-    const updateOptions = () => {
-        const { datas, times, ma5, ma10, ma15, macd, dif, dea } = chartData;
+    const updateOptions = (seriesType: string) => {
+        const { datas, times, ma5, ma10, ma15, macd, dif, dea, volume, kdj, cci } = chartData;
         if (datas.length) {
             options.value = {
                 xAxis: [
@@ -321,7 +400,7 @@ export function handleEchart() {
                         markLine: {
                             data: [
                                 {
-                                    // 最新
+                                    // 当前
                                     yAxis: datas[datas.length - 1][1],
                                 },
                             ],
@@ -340,16 +419,36 @@ export function handleEchart() {
                         data: ma15,
                     },
                     {
+                        name: 'VOL',
+                        data: seriesType === 'VOL' ? volume : [],
+                    },
+                    {
                         name: 'MACD',
-                        data: macd,
+                        data: seriesType === 'MACD' ? macd : [],
                     },
                     {
                         name: 'DIF',
-                        data: dif,
+                        data: seriesType === 'MACD' ? dif : [],
                     },
                     {
                         name: 'DEA',
-                        data: dea,
+                        data: seriesType === 'MACD' ? dea : [],
+                    },
+                    {
+                        name: 'K',
+                        data: seriesType === 'KDJ' ? kdj.map((val) => val[0]) : [],
+                    },
+                    {
+                        name: 'D',
+                        data: seriesType === 'KDJ' ? kdj.map((val) => val[1]) : [],
+                    },
+                    {
+                        name: 'J',
+                        data: seriesType === 'KDJ' ? kdj.map((val) => val[2]) : [],
+                    },
+                    {
+                        name: 'CCI',
+                        data: seriesType === 'CCI' ? cci : [],
                     },
                 ],
             };
@@ -433,13 +532,28 @@ export function handleEchart() {
                     name: 'MA15',
                 },
                 {
+                    name: 'VOL',
+                    itemStyle: {
+                        color: (params: any) => {
+                            const i = params.dataIndex;
+                            const data = chartData.datas[i];
+                            // 判断收盘价是否高于或等于开盘价
+                            if (data[1] >= data[0]) {
+                                return colors.upColor;
+                            } else {
+                                return colors.downColor;
+                            }
+                        },
+                    }
+                },
+                {
                     name: 'MACD',
                     itemStyle: {
                         color: (params: any) => {
                             if (params.data > 0) {
-                                return '#eb5454';
+                                return colors.upColor;
                             } else {
-                                return '#47b262';
+                                return colors.downColor;
                             }
                         },
                     }
@@ -461,6 +575,9 @@ export function handleEchart() {
                     yAxisLineColor: '#171B1D',
                     seriesMarkLabelColor: '#3C454B',
                     seriesMarkLineColor: '#33393D',
+                    upColor: '#eb5454',
+                    downColor: '#47b262',
+                    equalColor: '#fff',
                 });
             case ThemeEnum.light:
                 return setColors({
@@ -471,6 +588,9 @@ export function handleEchart() {
                     yAxisLineColor: '#DAE5EC',
                     seriesMarkLabelColor: '#ACB8C0',
                     seriesMarkLineColor: '#ACB8C0',
+                    upColor: '#eb5454',
+                    downColor: '#47b262',
+                    equalColor: '#333',
                 });
         }
     }

+ 13 - 10
src/common/components/echart/echart-timeline/index.vue

@@ -1,9 +1,9 @@
 <template>
-    <echart-base :options="options" v-model:loading="loading"></echart-base>
+    <echart-base :options="[options]" v-model:loading="loading"></echart-base>
 </template>
 
 <script lang="ts">
-import { defineComponent, ref, watch, PropType } from 'vue';
+import { defineComponent, ref, watch, PropType, onMounted } from 'vue';
 import { QueryTSDataRsp, QueryQuoteDayRsp } from '@/services/go/quote/interface';
 import { QueryTSData } from '@/services/go/quote';
 import { debounce, getRangeTime } from '@/utils/time';
@@ -25,7 +25,7 @@ export default defineComponent({
         },
     },
     setup(props) {
-        const loading = ref(true);
+        const loading = ref(false);
         const historyIndexs: number[] = []; // 行情历史数据中所有非补充数据的索引位置(用于计算均线)
         const { chartData, options, updateOptions, initOptions } = handleEchart();
 
@@ -41,7 +41,7 @@ export default defineComponent({
             rawData.historyDatas.forEach((item, index) => {
                 const { c, ts } = item;
                 datas.push(c);
-                times.push(moment(ts).format('MM-DD HH:mm'));
+                times.push(moment(ts).format('YYYY/MM/DD HH:mm:ss'));
                 if (!item.f) historyIndexs.push(index);
             });
 
@@ -138,7 +138,7 @@ export default defineComponent({
             // 判断时间差是否大于周期时间
             if (diffTime > cycleMilliseconds) {
                 lastTime.add(cycleMilliseconds, 'ms');
-                times.push(lastTime.format('MM-DD HH:mm')); // 添加历史行情时间
+                times.push(lastTime.format('YYYY/MM/DD HH:mm:ss')); // 添加历史行情时间
                 datas.push(newPrice); // 添加历史行情数据
                 historyIndexs.push(lastIndex + 1); // 添加历史行情索引
             } else {
@@ -151,15 +151,18 @@ export default defineComponent({
                 chartData.value.ma5 = calcMA(datas, 5, chartData.value.decimal);
                 chartData.value.min = min;
                 chartData.value.max = max;
-
                 updateOptions();
             }, 1000);
         };
 
-        // 查询分时数据
-        QueryTSData(props.quoteData.goodscode).then((res) => {
-            handleData(res);
-            initOptions();
+        onMounted(() => {
+            loading.value = true;
+            // 查询分时数据
+            QueryTSData(props.quoteData.goodscode).then((res) => {
+                handleData(res);
+                initOptions();
+                loading.value = false;
+            });
         });
 
         watch(

+ 11 - 13
src/common/components/echart/echart-timeline/setup.ts

@@ -4,6 +4,7 @@ import { getTheme, ThemeEnum } from '@/common/config/theme';
 import { deepMerge } from '@/utils/objHandle'
 import { EChartsOption } from 'echarts';
 import * as echarts from 'echarts';
+import moment from 'moment';
 
 // 命名待优化
 type Colors = {
@@ -84,7 +85,7 @@ export function handleEchart() {
                 },
                 formatter: (params: any) => {
                     const i = params[0].dataIndex;
-                    let result = '<div class="tooltip-title">' + times[i] + '</div>';
+                    let result = '<div class="tooltip-title">' + moment(times[i]).format('YYYY-MM-DD HH:mm') + '</div>';
                     result += '<div class="tooltip-item"><span><i style="background-color:' + params[0].color + ';"></i>收盘</span><span>' + datas[i] + '</span></div>';
                     result += '<div class="tooltip-item"><span><i style="background-color:' + params[2].color + ';"></i>均价</span><span>' + ma5[i] + '</span></div>';
                     result += '<div class="tooltip-item"><span><i></i>涨幅</span><span>' + calcRatio(datas[i]) + '</span></div>';
@@ -142,7 +143,7 @@ export function handleEchart() {
                     name: '分时',
                     type: 'line',
                     yAxisId: 'leftPrice',
-                    data: chartData.value.datas,
+                    data: datas,
                     smooth: true,
                     symbol: 'none', //中时有小圆点
                     lineStyle: {
@@ -155,8 +156,8 @@ export function handleEchart() {
                         // 标线标签样式
                         data: [
                             {
-                                // 最新
-                                yAxis: datas[datas.length - 1],
+                                // 当前
+                                yAxis: datas[datas.length - 1] ?? 0,
                             },
                             {
                                 // 昨结价
@@ -164,16 +165,13 @@ export function handleEchart() {
                                 lineStyle: {
                                     color: '#666',
                                 },
-                                label: {
-                                    show: false,
-                                }
                             },
                         ],
                     },
                 },
                 {
                     type: 'line',
-                    yAxisId: 'rightRatio', // 关联Y轴右侧标签
+                    yAxisId: 'rightRatio', // 关联Y轴右侧涨幅标签
                     data: datas,
                     symbol: 'none',
                     lineStyle: {
@@ -223,23 +221,23 @@ export function handleEchart() {
                         markLine: {
                             data: [
                                 {
-                                    // 最新
+                                    // 当前
                                     yAxis: datas[datas.length - 1],
                                 },
                                 {
                                     // 昨结价
                                     yAxis: yestclose,
                                     lineStyle: {
-                                        color: '#333',
+                                        color: '#666',
                                     },
-                                    label: {
-                                        show: false,
-                                    }
                                 },
                             ],
                         },
                     },
                     {
+                        data: datas,
+                    },
+                    {
                         name: '均价',
                         data: ma5,
                     },

+ 13 - 2
src/views/market/spot_trade/spot_trade_reference_market/components/chart/index.less

@@ -32,6 +32,10 @@
         }
 
         &__tabs {
+            &:last-child:not(:first-child) {
+                margin-left: 20px;
+            }
+
             .ant-radio-button-wrapper {
                 height          : 22px;
                 line-height     : 20px;
@@ -78,8 +82,15 @@
     }
 
     .chart-tips {
-        width : 300px;
-        height: 100%;
+        display       : flex;
+        flex-direction: column;
+        width         : 300px;
+        height        : 100%;
+
+        &__tik {
+            flex      : 1;
+            overflow-y: auto;
+        }
 
         &__nav {
             display        : flex;

+ 24 - 15
src/views/market/spot_trade/spot_trade_reference_market/components/chart/index.vue

@@ -3,14 +3,20 @@
     <div class="chart-container">
         <div class="chart-content">
             <div class="chart-content__header">
-                <a-radio-group class="chart-content__tabs" v-model:value="activeCycleType" @change="selectCycleType">
+                <a-radio-group class="chart-content__tabs" v-model:value="activeCycleType">
                     <template v-for="item in chartType" :key="item.type">
                         <a-radio-button :value="item.type">{{ item.name }}</a-radio-button>
                     </template>
                 </a-radio-group>
+                <a-radio-group class="chart-content__tabs" v-model:value="activeSeriesType" v-if="activeCycleType !== CycleType.time">
+                    <a-radio-button value="MACD">MACD</a-radio-button>
+                    <a-radio-button value="VOL">VOL</a-radio-button>
+                    <a-radio-button value="KDJ">KDJ</a-radio-button>
+                    <a-radio-button value="CCI">CCI</a-radio-button>
+                </a-radio-group>
             </div>
             <echart-time class="chart-content__main" :quote-data="selectedRow" v-if="activeCycleType === CycleType.time"></echart-time>
-            <echart-kline class="chart-content__main" :quote-data="selectedRow" :cycle-type="activeCycleType" v-else></echart-kline>
+            <echart-kline class="chart-content__main" :quote-data="selectedRow" :cycle-type="activeCycleType" :series-type="activeSeriesType" v-else></echart-kline>
             <!-- <component :is="componentId" v-if="componentId" :quote-data="selectedRow"></component> -->
             <div class="chart-content__footer"></div>
         </div>
@@ -43,13 +49,14 @@
                     </a-row>
                 </div>
             </div>
-            <!-- <div>
+            <div class="chart-tips__tik">
+                <div class="title">分时成交</div>
                 <div class="chartRow" v-for="(item, index) in tradedList" :key="index + '11'">
                     <div class="left">{{ formatTime(item.TS, 'hm') }}</div>
                     <div class="middle">{{ item.PE }}</div>
                     <div class="right">{{ item.Vol }}</div>
                 </div>
-            </div> -->
+            </div>
             <div class="chart-tips__info">
                 <a-row>
                     <a-col :span="4">最新</a-col>
@@ -77,7 +84,7 @@
                 </a-row>
                 <a-row>
                     <a-col :span="4">金额</a-col>
-                    <a-col :span="8" style="color: #0d96ff">{{ selectedRow.totalturnover }}</a-col>
+                    <a-col :span="8" style="color: #0d96ff">{{ changeUnit(selectedRow.totalturnover) }}</a-col>
                     <a-col :span="4">量比</a-col>
                     <a-col :span="8">{{ '--' }}</a-col>
                 </a-row>
@@ -120,13 +127,15 @@
 <script lang="ts">
 import { defineComponent } from '@/common/export/commonTable';
 import { _closeModal } from '@/common/setup/modal/modal';
-import { PropType, ref, computed } from 'vue';
+import { PropType, ref } from 'vue';
 import { QueryQuoteDayRsp, CycleType } from '@/services/go/quote/interface';
 import { QueryHistoryTikDatas } from '@/services/go/quote';
 import { formatTime } from '@/common/methods';
+import { changeUnit } from '@/utils/qt/common';
 import { ComponentType } from '../setup';
 import { EchartKline, EchartTime } from '@/common/components/echart';
 import { handleQuotePriceColor, quoteChange, quoteAmplitude } from '@/common/setup/table/tableQuote';
+import { useQueryData } from '@/common/setup/request';
 
 export default defineComponent({
     emits: ['cancel', 'update'],
@@ -144,6 +153,8 @@ export default defineComponent({
     setup(props, context) {
         const { visible, cancel } = _closeModal(context);
         const activeCycleType = ref<CycleType>(CycleType.time);
+        const activeSeriesType = ref('MACD');
+
         function watchMore() {
             context.emit('update', ComponentType.tradeDetail);
         }
@@ -160,24 +171,22 @@ export default defineComponent({
             { name: '日 K', type: CycleType.days },
         ];
 
-        // 选择图表周期类型
-        const selectCycleType = () => {
-            // console.log(activeCycleType.value);
-        };
-
-        // 成交
-        // const { list: tradedList } = useQueryData(QueryHistoryTikDatas, { goodsCode: goodscode });
+        const { list: tradedList } = useQueryData(QueryHistoryTikDatas, {
+            goodsCode: goodscode,
+            count: 10,
+        });
 
         return {
             cancel,
             visible,
             chartType,
-            tradedList: [],
+            tradedList,
             CycleType,
             activeCycleType,
+            activeSeriesType,
+            changeUnit,
             watchMore,
             formatTime,
-            selectCycleType,
             quoteChange,
             quoteAmplitude,
             handleQuotePriceColor,