Parcourir la source

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

huangbin il y a 4 ans
Parent
commit
f7b8ffc226

+ 1 - 1
package.json

@@ -52,4 +52,4 @@
         "prettier": "^1.19.1",
         "typescript": "^4.2.3"
     }
-}
+}

+ 1 - 1
public/config/app.config.json

@@ -1,3 +1,3 @@
 {
-    "apiUrl": "http://192.168.31.135:8080/cfg?key=test_135"
+    "apiUrl": "http://192.168.31.139:8080/cfg?key=test_139"
 }

+ 1 - 0
src/common/components/echart/echart-base/setup.ts

@@ -79,6 +79,7 @@ export function useEchart(el: HTMLElement, ctx: SetupContext): UseEchart {
 
     onUnmounted(() => {
         removeEventListener();
+        echart.dispose();
     })
 
     // 针对 keepalive 缓存组件

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

@@ -6,8 +6,9 @@
 import { defineComponent, PropType, ref, watch, watchEffect } from 'vue';
 import { QueryHistoryDatasRsp, QueryQuoteDayRsp, QueryHistoryDatas, CycleType } from '@/services/go/quote/interface';
 import { QueryHistoryDatas as queryHistoryDatas } from '@/services/go/quote';
-import { handleEchart, ChartData } from './setup';
+import { debounce } from '@/utils/time';
 import EchartBase from '../echart-base/index.vue';
+import { handleEchart } from './setup';
 import moment from 'moment';
 
 export default defineComponent({
@@ -29,55 +30,57 @@ export default defineComponent({
     },
     setup(props) {
         const loading = ref(true);
+        const historyIndexs: number[] = []; // 行情历史数据中所有非补充数据的索引位置(用于计算均线)
         const { chartData, options, updateOptions, initOptions } = handleEchart();
 
         // 处理图表数据
-        const handleData = (rawData: QueryHistoryDatasRsp[]): ChartData => {
-            const datas: number[][] = [],
-                times: string[] = [];
+        const handleData = (rawData: QueryHistoryDatasRsp[]): void => {
+            const datas: number[][] = [];
+            const times: string[] = [];
+            historyIndexs.length = 0; // 清空数据
 
-            rawData.forEach((item) => {
+            rawData.forEach((item, index) => {
                 const { o, c, h, l, ts } = item;
-                times.push(moment(ts).format('YYYY-MM-DD HH:mm:ss'));
                 datas.push([o, c, h, l]);
+                times.push(moment(ts).format('YYYY-MM-DD HH:mm:ss'));
+                // 排除补充数据
+                if (!item.f) {
+                    historyIndexs.push(index);
+                }
             });
 
-            return {
-                datas,
-                times,
-                ma5: calcMA(rawData, 5),
-                ma10: calcMA(rawData, 10),
-                ma15: calcMA(rawData, 15),
-                ...calcMACD(datas),
-            };
+            chartData.datas = datas;
+            chartData.times = times;
+            chartData.ma5 = calcMA(datas, 5);
+            chartData.ma10 = calcMA(datas, 10);
+            chartData.ma15 = calcMA(datas, 15);
+
+            const { macd, dif, dea } = calcMACD(datas);
+            chartData.macd = macd;
+            chartData.dif = dif;
+            chartData.dea = dea;
         };
 
         // 计算平均线
-        const calcMA = (rawData: QueryHistoryDatasRsp[], count: number) => {
+        const calcMA = (data: number[][], count: number) => {
             const result: string[] = [];
-            if (rawData.length >= count) {
-                // 所有非补充数据的索引id
-                const dataIndexs = rawData.reduce((prev: number[], cur, index) => {
-                    if (!cur.f) prev.push(index);
-                    return prev;
-                }, []);
+            if (data.length >= count) {
                 // 均线起始位置
-                const startIndex = dataIndexs[count - 1];
-
-                rawData.forEach((item, index) => {
+                const startIndex = historyIndexs[count - 1];
+                data.forEach((item, index) => {
                     if (index < startIndex) {
                         result.push('-');
                     } else {
-                        const j = dataIndexs.findIndex((val) => val === index);
+                        const j = historyIndexs.findIndex((val) => val === index);
                         // 判断是否补充数据
                         if (j === -1) {
                             // 取上个平均值
                             result.push(result[result.length - 1]);
                         } else {
                             // 向后取MA数
-                            const splitIndexs = dataIndexs.slice(j - (count - 1), j + 1);
-                            // 计算总价
-                            const total = splitIndexs.reduce((sum, val) => sum + rawData[val].c, 0);
+                            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));
                         }
@@ -87,30 +90,19 @@ export default defineComponent({
             return result;
         };
 
-        // 计算最后一根平均线
-        const calcLastMA = (rawData: number[][], count: number): string | undefined => {
-            if (rawData.length >= count) {
-                const index = rawData.length - 1;
-                // 向后取MA数
-                const datas = rawData.slice(index - (count - 1), index + 1);
-                // 计算总价,val[1]=收盘价
-                const total = datas.reduce((sum, val) => sum + val[1], 0);
-                // 计算均线
-                return (total / count).toFixed(2);
-            }
-        };
-
         // 计算EMA
-        const calcEMA = (data: number[], n: number) => {
+        const calcEMA = (close: number[], n: number) => {
             const result: number[] = [],
                 a = 2 / (n + 1); // 平滑系数
 
-            for (let i = 0; i < data.length; i++) {
+            for (let i = 0; i < close.length; i++) {
                 if (i === 0) {
-                    result.push(data[i]);
+                    //第一个EMA(n)是前n个收盘价代数平均
+                    const ema = close.slice(0, n).reduce((sum, val) => sum + val, 0) / n;
+                    result.push(ema);
                 } else {
                     // EMA(n) = α × Close + (1 - α) × EMA(n - 1)
-                    const ema = a * data[i] + (1 - a) * result[i - 1];
+                    const ema = a * close[i] + (1 - a) * result[i - 1];
                     result.push(ema);
                 }
             }
@@ -123,12 +115,12 @@ export default defineComponent({
         };
 
         // 计算DIF
-        const calcDIF = (data: number[]) => {
+        const calcDIF = (close: number[]) => {
             const result: number[] = [],
-                emaShort = calcEMA(data, 12),
-                emaLong = calcEMA(data, 26);
+                emaShort = calcEMA(close, 12),
+                emaLong = calcEMA(close, 26);
 
-            for (let i = 0; i < data.length; i++) {
+            for (let i = 0; i < close.length; i++) {
                 const dif = emaShort[i] - emaLong[i];
                 result.push(dif);
             }
@@ -136,9 +128,9 @@ export default defineComponent({
         };
 
         // 计算MACD
-        const calcMACD = (rawData: number[][]) => {
+        const calcMACD = (data: number[][]) => {
             const macd = [],
-                close = rawData.map((item) => item[1]), // 收盘价数据
+                close = data.map((item) => item[1]), // 收盘价数据
                 dif = calcDIF(close),
                 dea = calcDEA(dif);
 
@@ -152,90 +144,97 @@ export default defineComponent({
             };
         };
 
+        // 更新图表K线数据
+        const updateChartData = () => {
+            const { datas, times } = chartData,
+                lastIndex = datas.length - 1, // 历史行情最后索引位置
+                lastTime = moment(times[times.length - 1]), // 历史行情最后时间
+                newTime = moment(props.quoteData.lasttime), // 实时行情最新时间
+                newPrice = props.quoteData.last; // 实时行情最新价
+
+            let cycleMilliseconds = 60 * 1000; // 周期毫秒数
+
+            switch (props.cycleType) {
+                case CycleType.minutes5:
+                    cycleMilliseconds *= 5;
+                    break;
+                case CycleType.minutes30:
+                    cycleMilliseconds *= 30;
+                    break;
+                case CycleType.minutes60:
+                    cycleMilliseconds *= 60;
+                    break;
+                case CycleType.hours2:
+                    cycleMilliseconds *= 2 * 60;
+                    break;
+                case CycleType.Hours4:
+                    cycleMilliseconds *= 4 * 60;
+                    break;
+                case CycleType.days:
+                    cycleMilliseconds *= 24 * 60;
+                    break;
+            }
+
+            const diffTime = newTime.valueOf() - lastTime.valueOf(); // 计算时间差
+            // 判断时间差是否大于周期时间
+            if (diffTime > cycleMilliseconds) {
+                lastTime.add(cycleMilliseconds, 'ms');
+                times.push(lastTime.format('YYYY-MM-DD HH:mm:ss')); // 添加历史行情时间
+                datas.push([newPrice, newPrice, newPrice, newPrice]); // 添加历史行情数据
+                historyIndexs.push(lastIndex + 1); // 添加历史行情索引
+            } else {
+                const lastPrice = datas[lastIndex];
+                if (lastPrice[2] > newPrice) {
+                    lastPrice[2] = newPrice; //更新最低价
+                }
+                if (lastPrice[3] < newPrice) {
+                    lastPrice[3] = newPrice; //更新最高价
+                }
+                lastPrice[1] = newPrice; //更新收盘价
+                datas[lastIndex] = lastPrice; // 更新历史行情数据
+            }
+
+            // 延迟图表更新,减少卡顿
+            debounce(() => {
+                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;
+
+                updateOptions();
+            }, 1000);
+        };
+
+        watch(
+            () => props.quoteData.last,
+            () => {
+                if (!loading.value) {
+                    updateChartData();
+                }
+            }
+        );
+
         // 监听周期选择变化
         watchEffect(() => {
             loading.value = true;
             const params: QueryHistoryDatas = {
                 cycleType: props.cycleType,
                 goodsCode: props.quoteData.goodscode,
-                isAsc: true,
+                count: 2000,
             };
             // 查询K线数据
             queryHistoryDatas(params).then((res) => {
-                chartData.value = handleData(res);
+                // 日期升序排序
+                const kdata = res.sort((a, b) => moment(a.ts).valueOf() - moment(b.ts).valueOf());
+                handleData(kdata);
                 initOptions();
             });
         });
 
-        watch(
-            () => props.quoteData.last,
-            (val) => {
-                if (!loading.value) {
-                    console.log('lastTime', props.quoteData.lasttime);
-                    const { datas, times, ma5, ma10, ma15 } = chartData.value,
-                        lastTime = moment(times[times.length - 1]), // 行情最后时间
-                        newTime = moment(props.quoteData.lasttime); // 实时最新时间
-
-                    let cycleMilliseconds = 60 * 1000; // 周期毫秒数
-
-                    switch (props.cycleType) {
-                        case CycleType.minutes5:
-                            cycleMilliseconds *= 5;
-                            break;
-                        case CycleType.minutes30:
-                            cycleMilliseconds *= 30;
-                            break;
-                        case CycleType.minutes60:
-                            cycleMilliseconds *= 60;
-                            break;
-                        case CycleType.hours2:
-                            cycleMilliseconds *= 2 * 60;
-                            break;
-                        case CycleType.Hours4:
-                            cycleMilliseconds *= 4 * 60;
-                            break;
-                        case CycleType.days:
-                            cycleMilliseconds *= 24 * 60;
-                            break;
-                    }
-
-                    const diffTime = newTime.valueOf() - lastTime.valueOf(); // 计算时间差
-                    // 判断时间差是否大于周期时间
-                    if (diffTime > cycleMilliseconds) {
-                        lastTime.add(cycleMilliseconds, 'ms');
-                        times.push(lastTime.format());
-                        datas.push([val, val, val, val]);
-                    } else {
-                        const lastIndex = datas.length - 1,
-                            lastData = datas[lastIndex];
-                        if (lastData[2] > val) {
-                            lastData[2] = val; //更新最低价
-                        }
-                        if (lastData[3] < val) {
-                            lastData[3] = val; //更新最高价
-                        }
-                        lastData[1] = val; //更新收盘价
-                        datas[lastIndex] = lastData; // 更新数据
-                    }
-
-                    const _ma5 = calcLastMA(datas, 5),
-                        _ma10 = calcLastMA(datas, 10),
-                        _ma15 = calcLastMA(datas, 15);
-
-                    if (_ma5) ma5[ma5.length - 1] = _ma5;
-                    if (_ma10) ma10[ma10.length - 1] = _ma10;
-                    if (_ma15) ma15[ma15.length - 1] = _ma15;
-
-                    const { dif, dea, macd } = calcMACD(datas);
-                    chartData.value.dif = dif;
-                    chartData.value.dea = dea;
-                    chartData.value.macd = macd;
-
-                    updateOptions();
-                }
-            }
-        );
-
         return {
             loading,
             options,

+ 19 - 10
src/common/components/echart/echart-kline/setup.ts

@@ -32,7 +32,7 @@ export function handleEchart() {
     // 当前主题
     const theme = getTheme();
     // 图表数据
-    const chartData = ref<ChartData>({
+    const chartData: ChartData = {
         datas: [],
         times: [],
         ma5: [],
@@ -41,14 +41,15 @@ export function handleEchart() {
         dif: [],
         dea: [],
         macd: []
-    });
+    };
     // 图表当前指示数据
     let chartDataIndex = 0;
 
     // 初始化图表配置
     const initOptions = () => {
-        const { datas, times, ma5, ma10, ma15, dea, dif, macd } = chartData.value;
+        const { datas, times, ma5, ma10, ma15, dea, dif, macd } = chartData;
         const option: EChartsOption = {
+            animation: false,
             axisPointer: {
                 link: [
                     {
@@ -60,7 +61,7 @@ export function handleEchart() {
                 {
                     //图例控件,点击图例控制哪些系列不显示
                     type: 'scroll',
-                    data: ['K线', 'MA5', 'MA10', 'MA15'],
+                    data: ['MA5', 'MA10', 'MA15'],
                     itemWidth: 14,
                     itemHeight: 2,
                     left: '5%',
@@ -72,7 +73,7 @@ export function handleEchart() {
                 {
                     //图例控件,点击图例控制哪些系列不显示
                     type: 'scroll',
-                    data: ['MACD', 'DIF', 'DEA'],
+                    data: ['DIF', 'DEA'],
                     itemWidth: 14,
                     itemHeight: 2,
                     left: '5%',
@@ -183,11 +184,10 @@ export function handleEchart() {
                 {
                     type: 'inside',
                     xAxisIndex: [0, 1],
-                    // 起始显示K线条数(最新200条)
-                    startValue: times.length - 200,
+                    startValue: times.length - 120, // 起始显示K线条数(最新120条)
                     endValue: times.length,
-                    // 限制窗口缩放显示最少数据条数
-                    minValueSpan: 30,
+                    minValueSpan: 60, // 限制窗口缩放显示最少数据条数
+                    maxValueSpan: 400, // 限制窗口缩放显示最大数据条数
                 },
                 {
                     show: false,
@@ -265,6 +265,7 @@ export function handleEchart() {
                     data: macd,
                     xAxisIndex: 1,
                     yAxisIndex: 1,
+                    barWidth: '20%',
                 },
                 {
                     name: 'DIF',
@@ -302,9 +303,17 @@ export function handleEchart() {
 
     // 动态更新数据
     const updateOptions = () => {
-        const { datas, ma5, ma10, ma15, macd, dif, dea } = chartData.value;
+        const { datas, times, ma5, ma10, ma15, macd, dif, dea } = chartData;
         if (datas.length) {
             options.value = {
+                xAxis: [
+                    {
+                        data: times,
+                    },
+                    {
+                        data: times,
+                    },
+                ],
                 series: [
                     {
                         name: 'K线',

+ 1 - 0
src/common/components/echart/echart-timeline/setup.ts

@@ -60,6 +60,7 @@ export function handleEchart() {
     const initOptions = () => {
         const { datas, times, yestclose, last, min, max, ma5, decimal } = chartData.value;
         const option: EChartsOption = {
+            animation: false,
             legend: {
                 //图例控件,点击图例控制哪些系列不显示
                 type: 'scroll',