| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- <template>
- <echart-base :options="[options]" :empty="isEmpty" v-model:loading="loading"></echart-base>
- </template>
- <script lang="ts">
- import { defineComponent, ref, watch, PropType, onMounted, computed } from 'vue';
- import { QueryTSDataRsp, QueryQuoteDayRsp } from '@/services/go/quote/interface';
- import { QueryTSData } from '@/services/go/quote';
- import { _debounce, getRangeTime } from '@/utils/time';
- import { toDecimalFull } from '@/utils/number';
- import EchartBase from '../echart-base/index.vue';
- import { handleEchart } from './setup';
- import moment from 'moment';
- export default defineComponent({
- name: 'EchartTime',
- emits: ['change'],
- components: {
- EchartBase,
- },
- props: {
- // 实时行情数据
- quoteData: {
- type: Object as PropType<QueryQuoteDayRsp>,
- required: true,
- },
- },
- setup(props, { emit }) {
- const loading = ref(false);
- const isEmpty = ref(false);
- const historyIndexs: number[] = []; // 行情历史数据中所有非补充数据的索引位置(用于计算均线)
- const { chartData, options, updateOptions, initOptions } = handleEchart();
- // 处理图表数据
- const handleData = (rawData: QueryTSDataRsp): void => {
- historyIndexs.length = 0; // 清空数据
- const datas: number[] = [],
- times: string[] = [],
- xAxisTimes: string[] = [],
- yestclose = rawData.preSettle,
- decimal = rawData.decimalPlace;
- // 历史行情日期
- rawData.historyDatas.forEach((item, index) => {
- const { c, ts } = item;
- datas.push(c);
- times.push(moment(ts).format('YYYY-MM-DD HH:mm:ss'));
- if (!item.f) historyIndexs.push(index);
- });
- // 时间轴(开盘交易时间)
- rawData.runSteps.forEach((item) => {
- const { start, end } = item;
- const rangeTime = getRangeTime(start, end, 'HH:mm', 'm');
- xAxisTimes.push(...rangeTime);
- });
- chartData.value = {
- yestclose,
- decimal,
- rawTime: times,
- ...calcDataLine(datas, yestclose),
- source: {
- time: xAxisTimes,
- data: datas,
- ma5: calcMA(datas, 5, decimal),
- },
- };
- };
- // 计算图表最高低指标线
- const calcDataLine = (datas: number[], yestclose: number) => {
- let max = Math.max(...datas); // 取历史行情最高价
- let min = Math.min(...datas); // 取历史行情最低价
- const last = datas[datas.length - 1], // 历史行情最后收盘价
- a = yestclose - min, // 计算收盘价和最低价的差值
- b = max - yestclose; // 计算收盘价和最高价的差值
- // 比较差值大小
- if (a > b) {
- max = yestclose + a;
- if (last > max) {
- const c = last - max;
- max += c;
- min -= c;
- }
- } else {
- min = min - (b - a);
- if (min > last) {
- const c = min - last;
- max += c;
- min -= c;
- }
- }
- return {
- max: max,
- min: min,
- };
- };
- // 计算平均线
- const calcMA = (data: number[], count: number, decimal: number) => {
- const result: string[] = [];
- if (data.length >= count) {
- // 均线起始位置
- const startIndex = historyIndexs[count - 1];
- for (let i = 0; i < data.length; i++) {
- if (startIndex === undefined || i < startIndex) {
- result.push('-');
- } else {
- const j = historyIndexs.findIndex((val) => val === i);
- // 判断是否补充数据
- if (j === -1) {
- // 取上个平均值
- result.push(result[result.length - 1]);
- } else {
- // 向后取MA数
- const maIndexs = historyIndexs.slice(j - (count - 1), j + 1);
- // 计算总价
- const total = maIndexs.reduce((sum, val) => sum + data[val], 0);
- // 计算均线
- const ma = toDecimalFull(total / count, decimal);
- result.push(ma);
- }
- }
- }
- }
- return result;
- };
- // 更新分时数据
- const updateChartData = () => {
- const { source, rawTime } = chartData.value,
- lastIndex = source.data.length - 1, // 历史行情最后索引位置
- lastTime = moment(rawTime[rawTime.length - 1]), // 历史行情最后时间
- newTime = moment(props.quoteData.lasttime), // 实时行情最新时间
- newPrice = props.quoteData.last; // 实时行情最新价
- const cycleMilliseconds = 60 * 1000; // 周期毫秒数
- const diffTime = newTime.valueOf() - lastTime.valueOf(); // 计算时间差
- // 判断时间差是否大于周期时间
- if (diffTime > cycleMilliseconds) {
- lastTime.add(cycleMilliseconds, 'ms');
- rawTime.push(lastTime.format('YYYY-MM-DD HH:mm:ss')); // 添加历史行情时间
- source.data.push(newPrice); // 添加历史行情数据
- historyIndexs.push(lastIndex + 1); // 添加历史行情索引
- } else {
- source.data[lastIndex] = newPrice; // 更新历史行情数据
- }
- source.ma5 = calcMA(source.data, 5, chartData.value.decimal);
- const { min, max } = calcDataLine(source.data, chartData.value.yestclose);
- chartData.value.min = min;
- chartData.value.max = max;
- // 延迟图表更新,减少卡顿
- _debounce(() => {
- updateOptions();
- }, 1000);
- };
- onMounted(() => {
- loading.value = true;
- // 查询分时数据
- QueryTSData(props.quoteData.goodscode)
- .then((res) => {
- if (res.historyDatas.length) {
- isEmpty.value = false;
- handleData(res);
- // 调用父级函数查询tik数据 (不合理的逻辑处理,待优化)
- emit('change', res.startTime, res.endTime);
- } else {
- isEmpty.value = true;
- }
- initOptions();
- })
- .catch(() => {
- isEmpty.value = true;
- })
- .finally(() => {
- loading.value = false;
- });
- });
- watch(
- () => props.quoteData.last,
- () => {
- if (!loading.value) {
- updateChartData();
- }
- }
- );
- return {
- loading,
- isEmpty,
- options,
- };
- },
- });
- </script>
|