Преглед изворни кода

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

marymelisa пре 4 година
родитељ
комит
139e288d6b
26 измењених фајлова са 2744 додато и 583 уклоњено
  1. 705 47
      src/assets/styles/index.css
  2. 60 30
      src/assets/styles/index.less
  3. 75 0
      src/common/components/echart/echart-base/index.vue
  4. 99 0
      src/common/components/echart/echart-base/setup.ts
  5. 125 0
      src/common/components/echart/echart-kline/index.vue
  6. 288 0
      src/common/components/echart/echart-kline/setup.ts
  7. 150 0
      src/common/components/echart/echart-timeline/index.vue
  8. 392 0
      src/common/components/echart/echart-timeline/setup.ts
  9. 7 0
      src/common/components/echart/index.ts
  10. 6 7
      src/common/setup/table/tableQuote.ts
  11. 5 4
      src/common/setup/trade/index.ts
  12. 4 4
      src/services/go/quote/index.ts
  13. 149 148
      src/services/go/quote/interface.ts
  14. 1 1
      src/services/socket/quota/adapter/index.ts
  15. 27 0
      src/utils/number/index.ts
  16. 0 14
      src/utils/qt/common.ts
  17. 32 14
      src/utils/time/index.ts
  18. 72 78
      src/views/market/spot_trade/spot_trade_order_transaction/spot_trade_order_transaction_swap/index.vue
  19. 4 4
      src/views/market/spot_trade/spot_trade_order_transaction/spot_trade_order_transaction_swap/setup.ts
  20. 163 0
      src/views/market/spot_trade/spot_trade_reference_market/components/chart/index.less
  21. 155 44
      src/views/market/spot_trade/spot_trade_reference_market/components/chart/index.vue
  22. 60 75
      src/views/market/spot_trade/spot_trade_reference_market/components/container/index.vue
  23. 119 108
      src/views/order/commodity_contract/components/commodity_contract_summary/components/commodity_contract_summary_deal_closed/index.vue
  24. 7 1
      src/views/order/commodity_contract/components/commodity_contract_summary/components/commodity_contract_summary_deal_closed/interface.ts
  25. 38 3
      src/views/order/commodity_contract/components/commodity_contract_summary/components/commodity_contract_summary_deal_closed/setup.ts
  26. 1 1
      src/views/order/commodity_contract/components/commodity_contract_summary/index.vue

Разлика између датотеке није приказан због своје велике величине
+ 705 - 47
src/assets/styles/index.css


+ 60 - 30
src/assets/styles/index.less

@@ -25,32 +25,37 @@ body {
 // 布局组件样式初始化
 .ant-layout {
     background: @m-white0;
+
     aside {
         background: @m-white0;
+
         .ant-layout-sider-children {
-            section {
-            }
+            section {}
         }
     }
-    > section {
+
+    >section {
         header {
             background: @m-white0;
             padding: 0;
         }
+
         main {
             background: @m-white0;
-            header {
-            }
-            main {
-            }
+
+            header {}
+
+            main {}
         }
     }
 }
 
 .ant-card .ant-card-head {
     border: none;
+
     .ant-card-head-wrapper {
         border-bottom: 1px solid @m-white2;
+
         .ant-card-head-title {
             text-align: left;
             font-size: 16px;
@@ -61,25 +66,29 @@ body {
     }
 }
 
-.ant-steps .ant-steps-item:not(.ant-steps-item-active) > .ant-steps-item-container[role='button']:hover .ant-steps-item-title,
-.ant-steps .ant-steps-item:not(.ant-steps-item-active) > .ant-steps-item-container[role='button']:hover .ant-steps-item-subtitle,
-.ant-steps .ant-steps-item:not(.ant-steps-item-active) > .ant-steps-item-container[role='button']:hover .ant-steps-item-description {
+.ant-steps .ant-steps-item:not(.ant-steps-item-active)>.ant-steps-item-container[role='button']:hover .ant-steps-item-title,
+.ant-steps .ant-steps-item:not(.ant-steps-item-active)>.ant-steps-item-container[role='button']:hover .ant-steps-item-subtitle,
+.ant-steps .ant-steps-item:not(.ant-steps-item-active)>.ant-steps-item-container[role='button']:hover .ant-steps-item-description {
     color: @m-green2;
 }
-.ant-steps .ant-steps-item:not(.ant-steps-item-active):not(.ant-steps-item-process) > .ant-steps-item-container[role='button']:hover .ant-steps-item-icon {
+
+.ant-steps .ant-steps-item:not(.ant-steps-item-active):not(.ant-steps-item-process)>.ant-steps-item-container[role='button']:hover .ant-steps-item-icon {
     border-color: @m-green2;
 }
+
 .ant-modal-title {
     font-size: 18px;
     font-family: Source Han Sans CN;
     font-weight: 500;
     color: @m-green3;
 }
+
 .submit {
     background: @m-green3;
     border-radius: 3px;
     color: @m-white0;
 }
+
 .ant-btn-primary {
     background-color: @m-green3;
 }
@@ -94,8 +103,8 @@ body {
 .ant-menu-submenu-title:hover,
 .ant-menu-submenu-selected,
 .ant-menu-item-selected,
-.ant-menu-item-selected > a,
-.ant-menu-item-selected > a:hover {
+.ant-menu-item-selected>a,
+.ant-menu-item-selected>a:hover {
     color: @m-green3;
 }
 
@@ -109,7 +118,7 @@ body {
 .ant-checkbox-checked::after,
 .ant-checkbox-wrapper:hover .ant-checkbox-inner,
 .ant-checkbox:hover .ant-checkbox-inner,
-.ant-checkbox-input:focus + .ant-checkbox-inner,
+.ant-checkbox-input:focus+.ant-checkbox-inner,
 .ant-input-affix-wrapper:hover,
 .ant-input-affix-wrapper:focus,
 .ant-select:not(.ant-select-disabled):hover .ant-select-selector,
@@ -152,19 +161,22 @@ body {
     overflow: hidden;
 }
 
-.ant-spin-nested-loading > div > .ant-spin {
+.ant-spin-nested-loading>div>.ant-spin {
     // height: 90%;
     max-height: 100%;
 }
+
 .ant-spin-nested-loading {
     width: 100%;
     height: 100%;
+
     .ant-spin-container {
         width: 100%;
         height: 100%;
         display: flex;
     }
 }
+
 .ant-switch-checked {
     background-color: @theme;
 }
@@ -198,6 +210,7 @@ body {
     width: 120px;
     display: inline-block;
 }
+
 .ellipsis_temp {
     white-space: nowrap;
     overflow: hidden;
@@ -210,32 +223,36 @@ body {
 .ant-menu-dark,
 .ant-menu-dark {
     background-color: @m-grey18;
+
     .ant-menu-submenu-title {
         font-size: 16px;
         text-align: left;
     }
+
     .ant-menu-sub {
         background-color: @m-black4;
+
         .ant-menu-item {
             font-size: 14px;
             font-family: @menu-item-family;
             color: @menu-item-color;
             text-align: left;
         }
+
         .ant-menu-item-selected {
             color: @m-white0;
         }
     }
 }
 
-.ant-menu-horizontal > .ant-menu-item-active,
-.ant-menu-horizontal > .ant-menu-item-open,
-.ant-menu-horizontal > .ant-menu-item-selected,
-.ant-menu-horizontal > .ant-menu-item:hover,
-.ant-menu-horizontal > .ant-menu-submenu-active,
-.ant-menu-horizontal > .ant-menu-submenu-open,
-.ant-menu-horizontal > .ant-menu-submenu-selected,
-.ant-menu-horizontal > .ant-menu-submenu:hover {
+.ant-menu-horizontal>.ant-menu-item-active,
+.ant-menu-horizontal>.ant-menu-item-open,
+.ant-menu-horizontal>.ant-menu-item-selected,
+.ant-menu-horizontal>.ant-menu-item:hover,
+.ant-menu-horizontal>.ant-menu-submenu-active,
+.ant-menu-horizontal>.ant-menu-submenu-open,
+.ant-menu-horizontal>.ant-menu-submenu-selected,
+.ant-menu-horizontal>.ant-menu-submenu:hover {
     color: @theme;
 }
 
@@ -259,23 +276,28 @@ body {
         }
 
 
-        > tr:first-child {
-            > th:last-child, > th:first-child {
+        >tr:first-child {
+
+            >th:last-child,
+            >th:first-child {
                 border-radius: 0px;
 
             }
         }
     }
 
-    .ant-table-tbody > tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected) > td {
+    .ant-table-tbody>tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected)>td {
         background: @m-blue3;
     }
+
     .ant-table-body tr td {
         background-color: @m-black2;
         color: white;
         cursor: pointer;
     }
-    .ant-table-bordered .ant-table-thead > tr > th, .ant-table-bordered .ant-table-tbody > tr > td {
+
+    .ant-table-bordered .ant-table-thead>tr>th,
+    .ant-table-bordered .ant-table-tbody>tr>td {
         padding: 0;
         height: 34px;
         line-height: 34px;
@@ -283,12 +305,13 @@ body {
         font-family: Adobe Heiti Std;
         font-size: 16px;
     }
+
     .ant-table-fixed {
         width: max-content !important;
         color: @m-white1;
         background: @m-black2;
-        border-top-color: @m-black2 !important;
-        border-left-color: @m-black9 !important;
+        border-top-color: @m-black2  !important;
+        border-left-color: @m-black9  !important;
     }
 }
 
@@ -303,7 +326,8 @@ body {
 
 .iframe-container {
     height: calc(100% - 40px);
-    > iframe {
+
+    >iframe {
         border: 0;
         width: 100%;
         height: 100%;
@@ -314,6 +338,12 @@ body {
 .up-quote-color {
     color: @m-red1;
 }
+
 .down-quote-color {
     color: @m-green4;
+}
+
+// 不可复制
+.not-copy {
+    user-select: none
 }

+ 75 - 0
src/common/components/echart/echart-base/index.vue

@@ -0,0 +1,75 @@
+<template>
+    <div class="echart" :style="{ width: width, height: height }">
+        <div ref="chartElement" class="echart__container"></div>
+    </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref, onMounted, PropType, watch, watchEffect } from 'vue';
+import { EChartsOption } from 'echarts';
+import { useEchart } from './setup';
+
+export default defineComponent({
+    name: 'Echart',
+    props: {
+        // 图表宽度
+        width: {
+            type: String,
+            default: '100%',
+        },
+        // 图表高度
+        height: {
+            type: String,
+            default: '100%',
+        },
+        // 图表配置项
+        options: {
+            type: Object as PropType<EChartsOption>,
+            required: true,
+        },
+        loading: {
+            type: Boolean,
+            default: true,
+        },
+    },
+    setup(props, context) {
+        // 图表容器元素
+        const chartElement = ref<HTMLElement>();
+
+        onMounted(() => {
+            const { echart, setOptions, showLoading } = useEchart(chartElement.value!, context);
+
+            watchEffect(() => {
+                if (props.loading) {
+                    echart.clear();
+                    showLoading(true);
+                }
+            });
+
+            watch(
+                () => props.options,
+                (option) => {
+                    // 设置图表配置项
+                    setOptions(option);
+                }
+            );
+        });
+
+        return {
+            chartElement,
+        };
+    },
+});
+</script>
+
+<style lang="less">
+.echart {
+    position: relative;
+    overflow: hidden;
+
+    &__container {
+        width: 100%;
+        height: 100%;
+    }
+}
+</style>

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

@@ -0,0 +1,99 @@
+import { onActivated, onDeactivated, onUnmounted, SetupContext } from "vue";
+import { debounce } from "@/utils/time"
+import { getTheme, ThemeEnum } from '@/common/config/theme';
+import * as echarts from 'echarts'
+
+// 创建 iframe 对象
+const createIframe = (parent: HTMLElement): HTMLIFrameElement => {
+    const iframe = document.createElement('iframe');
+    // 设置 iframe 样式
+    iframe.style.cssText = 'position: absolute; z-index: -1000; left: 0; top: 0; width: 100%; height: 100%; border: 0; visibility: hidden; pointer-events: none;';
+    // 添加 iframe 节点
+    parent.appendChild(iframe);
+
+    return iframe;
+}
+
+type UseEchart = {
+    echart: echarts.ECharts,
+    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);
+
+    // 显示加载动画
+    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',
+                    });
+            }
+        }
+    };
+
+    // 图表配置项
+    const setOptions = (options: echarts.EChartsOption, notMerge = false) => {
+        echart.setOption(options, notMerge);
+        echart.hideLoading();
+        ctx.emit('update:loading', false);
+    };
+
+    const onresize = () => {
+        debounce(() => {
+            // 如果 canvas 宽高和父元素不一致,重新渲染
+            if (echart.getWidth() !== el.clientWidth || echart.getHeight() !== el.clientHeight) {
+                echart.resize && echart.resize();
+            }
+        }, 50)
+    };
+
+    const addEventListener = () => {
+        // 监听 iframe 变化重置图表尺寸
+        iframe.contentWindow?.addEventListener('resize', onresize);
+    }
+
+    const removeEventListener = () => {
+        // 移除 iframe 监听事件
+        iframe.contentWindow?.removeEventListener('resize', onresize);
+    }
+
+    addEventListener();
+
+    onUnmounted(() => {
+        removeEventListener();
+    })
+
+    // 针对 keepalive 缓存组件
+    onActivated(() => {
+        addEventListener();
+    })
+
+    // 针对 keepalive 缓存组件
+    onDeactivated(() => {
+        removeEventListener();
+    })
+
+    return {
+        echart,
+        setOptions,
+        showLoading,
+    }
+}

+ 125 - 0
src/common/components/echart/echart-kline/index.vue

@@ -0,0 +1,125 @@
+<template>
+    <echart-base :options="options" v-model:loading="loading"></echart-base>
+</template>
+
+<script lang="ts">
+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 EchartBase from '../echart-base/index.vue';
+import moment from 'moment';
+
+export default defineComponent({
+    name: 'EchartKline',
+    components: {
+        EchartBase,
+    },
+    props: {
+        // 周期类型
+        cycleType: {
+            type: Number as PropType<CycleType>,
+            required: true,
+        },
+        // 实时行情数据
+        quoteData: {
+            type: Object as PropType<QueryQuoteDayRsp>,
+            required: true,
+        },
+    },
+    setup(props) {
+        const loading = ref(true);
+        const { chartData, options, updateOptions, initOptions } = handleEchart();
+
+        // 处理图表数据
+        const handleData = (rawData: QueryHistoryDatasRsp[]): ChartData => {
+            const datas: number[][] = [],
+                times: string[] = [];
+
+            rawData.forEach((item) => {
+                const { o, c, h, l, ts } = item;
+                const t = moment(ts).format('YYYY/MM/DD');
+                times.push(t);
+                datas.push([o, c, h, l]);
+            });
+
+            const { last, lasttime: lastTime } = props.quoteData;
+
+            return {
+                datas,
+                times,
+                last,
+                lastTime,
+                ma5: calcMA(rawData, 5),
+                ma10: calcMA(rawData, 10),
+                ma15: calcMA(rawData, 15),
+            };
+        };
+
+        // 计算平均线
+        const calcMA = (rawData: QueryHistoryDatasRsp[], 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;
+                }, []);
+                // 均线起始位置
+                const startIndex = dataIndexs[count - 1];
+
+                rawData.forEach((item, index) => {
+                    if (index < startIndex) {
+                        result.push('-');
+                    } else {
+                        const j = dataIndexs.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);
+                            // 计算均线
+                            result.push((total / count).toFixed(2));
+                        }
+                    }
+                });
+            }
+            return result;
+        };
+
+        // 监听周期变化
+        watchEffect(() => {
+            loading.value = true;
+            const params: QueryHistoryDatas = {
+                cycleType: props.cycleType,
+                goodsCode: props.quoteData.goodscode,
+                isAsc: true,
+            };
+            // 查询K线数据
+            queryHistoryDatas(params).then((res) => {
+                chartData.value = handleData(res);
+                initOptions();
+            });
+        });
+
+        watch(
+            () => props.quoteData.last,
+            (val) => {
+                if (!loading.value) {
+                    chartData.value.last = val;
+                    updateOptions();
+                }
+            }
+        );
+
+        return {
+            loading,
+            options,
+        };
+    },
+});
+</script>

+ 288 - 0
src/common/components/echart/echart-kline/setup.ts

@@ -0,0 +1,288 @@
+import { ref, watch } from "vue";
+import { getTheme, ThemeEnum } from '@/common/config/theme';
+import { EChartsOption } from 'echarts';
+
+// 命名待优化
+type Colors = {
+    backgroundColor: string, // 图表背景颜色
+    axisPointerLabelColor: string,
+    legendTextColor: string,
+    xAxisLineColor: string
+    yAxisLineColor: string,
+    seriesMarkLabelColor: string,
+    seriesMarkLineColor: string,
+}
+
+// 图表数据
+export type ChartData = {
+    datas: number[][], // 历史数据
+    times: string[], // 历史日期
+    last: number, // 最新行情价
+    lastTime: string, // 最新行情时间
+    ma5: (number | string)[], //均线数据
+    ma10: (number | string)[], //均线数据
+    ma15: (number | string)[], //均线数据
+}
+
+export function handleEchart() {
+    const options = ref<EChartsOption>();
+    // 当前主题
+    const theme = getTheme();
+    // 图表数据
+    const chartData = ref<ChartData>({
+        datas: [],
+        times: [],
+        last: 0,
+        lastTime: '',
+        ma5: [],
+        ma10: [],
+        ma15: [],
+    });
+
+    // 初始化图表配置
+    const initOptions = () => {
+        const { datas, times, last, ma5, ma10, ma15 } = chartData.value;
+        options.value = {
+            legend: {
+                //图例控件,点击图例控制哪些系列不显示
+                type: 'scroll',
+                itemWidth: 14,
+                itemHeight: 2,
+                left: '5%',
+                top: 0,
+                textStyle: {
+                    fontSize: 12,
+                },
+            },
+            // 悬浮框
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    type: 'cross',
+                },
+                backgroundColor: 'rgba(255,255,255,.9)',
+                borderWidth: 1,
+                borderRadius: 3,
+                textStyle: {
+                    color: '#4d535c',
+                },
+                // eslint-disable-next-line
+                formatter: (params: any) => {
+                    const firstParams = params[0];
+                    const data = firstParams.data;
+                    const result = `<div><span style="display: inline-block; width: 40px">日期:</span><span style="display: inline-block; width: 100px; text-align: right">${firstParams.name}</span></div>
+    <div><span style="display: inline-block; width: 40px">开盘价:</span><span style="display: inline-block; width: 100px; text-align: right">${data[1]}</span></div>
+    <div><span style="display: inline-block; width: 40px">收盘价:</span><span style="display: inline-block; width: 100px; text-align: right">${data[2]}</span></div>
+    <div><span style="display: inline-block; width: 40px">最低价:</span><span style="display: inline-block; width: 100px; text-align: right">${data[3]}</span></div>
+    <div><span style="display: inline-block; width: 40px">最高价:</span><span style="display: inline-block; width: 100px; text-align: right">${data[4]}</span></div>`;
+                    return result;
+                },
+            },
+            grid: {
+                top: '8%',
+                left: '8%',
+                right: '8%',
+                bottom: '8%',
+            },
+            xAxis: {
+                type: 'category',
+                // X轴时间线
+                data: times,
+                splitLine: {
+                    // 坐标分隔线
+                    show: true,
+                },
+            },
+            yAxis: {
+                scale: true,
+            },
+            dataZoom: [
+                {
+                    type: 'inside',
+                    // 起始显示K线条数(最新200条)
+                    startValue: times.length - 200,
+                    endValue: times.length,
+                    // 限制窗口缩放显示最少数据条数
+                    minValueSpan: 30,
+                },
+                {
+                    show: false,
+                    type: 'slider',
+                },
+            ],
+            series: [
+                {
+                    name: 'K线',
+                    type: 'candlestick',
+                    // Y轴数据
+                    data: datas,
+                    markLine: {
+                        animation: false,
+                        // 标线两端图标
+                        symbol: 'none',
+                        // 标线标签样式
+                        label: {
+                            fontWeight: 'bold',
+                            position: 'end',
+                        },
+                        // 标线样式
+                        lineStyle: {
+                            type: 'dashed',
+                        },
+                        data: [
+                            {
+                                // 最新价
+                                yAxis: last,
+                            },
+                        ],
+                    },
+                },
+                {
+                    name: 'MA5',
+                    type: 'line',
+                    data: ma5,
+                    smooth: true,
+                    symbol: 'none',
+                    lineStyle: {
+                        width: 1,
+                        opacity: 0.8,
+                    },
+                },
+                {
+                    name: 'MA10',
+                    type: 'line',
+                    data: ma10,
+                    smooth: true,
+                    symbol: 'none',
+                    lineStyle: {
+                        width: 1,
+                        opacity: 0.8,
+                    },
+                },
+                {
+                    name: 'MA15',
+                    type: 'line',
+                    data: ma15,
+                    smooth: true,
+                    symbol: 'none',
+                    lineStyle: {
+                        width: 1,
+                        opacity: 0.8,
+                    },
+                },
+            ],
+        };
+
+        // 先用 setTimeout 处理,待优化深度合并
+        setTimeout(() => {
+            options.value = getColors(theme.value);
+        }, 0);
+    };
+
+    // 动态更新数据
+    const updateOptions = () => {
+        const { datas, last } = chartData.value;
+        if (datas.length) {
+            options.value = {
+                series: [
+                    {
+                        markLine: {
+                            data: [
+                                {
+                                    // 最新价
+                                    yAxis: last,
+                                },
+                            ],
+                        },
+                    },
+                ],
+            };
+        }
+    };
+
+    // 设置图表样式
+    const setColors = (colors: Colors): EChartsOption => {
+        return {
+            // 图表背景颜色
+            backgroundColor: colors.backgroundColor,
+            axisPointer: {
+                label: {
+                    color: colors.axisPointerLabelColor,
+                },
+            },
+            legend: {
+                textStyle: {
+                    color: colors.legendTextColor,
+                },
+            },
+            xAxis: {
+                splitLine: {
+                    lineStyle: {
+                        // 坐标分隔线颜色
+                        color: colors.xAxisLineColor,
+                    },
+                },
+            },
+            yAxis: {
+                splitLine: {
+                    lineStyle: {
+                        // 坐标分隔线颜色
+                        color: colors.xAxisLineColor,
+                    },
+                },
+            },
+            series: [
+                {
+                    markLine: {
+                        // 标线标签样式
+                        label: {
+                            color: colors.seriesMarkLabelColor,
+                        },
+                        // 标线样式
+                        lineStyle: {
+                            color: colors.seriesMarkLineColor,
+                        },
+                    },
+                }
+            ]
+        }
+    }
+
+    // 获取图表样式配置
+    const getColors = (theme: ThemeEnum) => {
+        switch (theme) {
+            case ThemeEnum.default:
+            case ThemeEnum.dark:
+                return setColors({
+                    backgroundColor: 'transparent',
+                    axisPointerLabelColor: '#fff',
+                    legendTextColor: '#0e99e2',
+                    xAxisLineColor: '#171B1D',
+                    yAxisLineColor: '#171B1D',
+                    seriesMarkLabelColor: '#3C454B',
+                    seriesMarkLineColor: '#33393D',
+                });
+            case ThemeEnum.light:
+                return setColors({
+                    backgroundColor: 'transparent',
+                    axisPointerLabelColor: '#fff',
+                    legendTextColor: '#FC9618',
+                    xAxisLineColor: '#DAE5EC',
+                    yAxisLineColor: '#DAE5EC',
+                    seriesMarkLabelColor: '#ACB8C0',
+                    seriesMarkLineColor: '#ACB8C0',
+                });
+        }
+    }
+
+    watch(theme, (val) => {
+        options.value = getColors(val);
+    });
+
+    return {
+        chartData,
+        options,
+        initOptions,
+        updateOptions,
+    }
+}

+ 150 - 0
src/common/components/echart/echart-timeline/index.vue

@@ -0,0 +1,150 @@
+<template>
+    <echart-base :options="options" v-model:loading="loading"></echart-base>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref, watch, PropType } from 'vue';
+import { QueryTSDataRsp, QueryQuoteDayRsp } from '@/services/go/quote/interface';
+import { QueryTSData } from '@/services/go/quote';
+import { getGoodsByCode } from '@/services/bus/goods';
+import { handleEchart, ChartData } from './setup';
+import EchartBase from '../echart-base/index.vue';
+import { getRangeTime } from '@/utils/time';
+import { toDecimalFull } from '@/utils/number';
+
+export default defineComponent({
+    name: 'EchartTime',
+    components: {
+        EchartBase,
+    },
+    props: {
+        // 实时行情数据
+        quoteData: {
+            type: Object as PropType<QueryQuoteDayRsp>,
+            required: true,
+        },
+    },
+    setup(props) {
+        const loading = ref(true);
+        const { chartData, options, updateOptions, initOptions } = handleEchart();
+
+        // 处理图表数据
+        const handleData = (rawData: QueryTSDataRsp): ChartData => {
+            const datas = rawData.historyDatas!.map((item) => item.c),
+                startTime = rawData.startTime!,
+                endTime = rawData.endTime!,
+                times = getRangeTime(startTime, endTime, 'HH:mm', 'm'),
+                yestclose = rawData.preSettle!,
+                decimal = rawData.decimalPlace!;
+
+            const { last, lasttime: lastTime } = props.quoteData;
+            const { min, max } = calcDataLine(datas, yestclose, last);
+
+            return {
+                datas,
+                times,
+                yestclose,
+                last,
+                lastTime,
+                decimal,
+                min,
+                max,
+                ma5: calcMA(rawData, 5),
+            };
+        };
+
+        // 计算图表最高低指标线
+        const calcDataLine = (datas: number[], yestclose: number, last: number) => {
+            let max = Math.max(...datas, last); // 取历历史数据最高价
+            let min = Math.min(...datas, last); // 取历史数据最低价
+            const a = yestclose - min; // 计算收盘价和最低价的差值
+            const 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 + max * 0.01,
+                min: min - min * 0.01,
+            };
+        };
+
+        // 计算平均线
+        const calcMA = (rawData: QueryTSDataRsp, count: number) => {
+            const datas = rawData.historyDatas!,
+                decimal = rawData.decimalPlace!;
+
+            const result: string[] = [];
+            if (datas.length >= count) {
+                // 所有非补充数据的索引id
+                const dataIndexs = datas.reduce((prev: number[], cur, index) => {
+                    if (!cur.f) prev.push(index);
+                    return prev;
+                }, []);
+                // 均线起始位置
+                const startIndex = dataIndexs[count - 1];
+
+                datas.forEach((item, index) => {
+                    if (index < startIndex) {
+                        result.push('-');
+                    } else {
+                        const j = dataIndexs.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 + datas[val].c, 0);
+                            // 计算均线
+                            const average = toDecimalFull(total / count, decimal);
+                            result.push(average);
+                        }
+                    }
+                });
+            }
+            return result;
+        };
+
+        // 查询分时数据
+        QueryTSData(props.quoteData.goodscode).then((res) => {
+            chartData.value = handleData(res);
+            initOptions();
+        });
+
+        watch(
+            () => props.quoteData.last,
+            (val) => {
+                if (!loading.value) {
+                    const { min, max } = calcDataLine(chartData.value.datas, chartData.value.yestclose, val);
+                    chartData.value.last = val;
+                    chartData.value.min = min;
+                    chartData.value.max = max;
+                    updateOptions();
+                }
+            }
+        );
+
+        return {
+            loading,
+            options,
+        };
+    },
+});
+</script>

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

@@ -0,0 +1,392 @@
+import { ref, watch } from "vue";
+import { toDecimalFull } from '@/utils/number';
+import { getTheme, ThemeEnum } from '@/common/config/theme';
+import { EChartsOption } from 'echarts';
+import * as echarts from 'echarts';
+
+// 命名待优化
+type Colors = {
+    backgroundColor: string, // 图表背景颜色
+    axisPointerLabelColor: string,
+    legendTextColor: string,
+    xAxisLineColor: string
+    yAxisLineColor: string,
+    seriesMarkLabelColor: string,
+    seriesMarkLineColor: string,
+    upColor: string,
+    downColor: string,
+    seriesLineColor: string,
+    seriesAreaGradients: echarts.LinearGradientObject,
+}
+
+// 图表数据
+export type ChartData = {
+    datas: number[], // 历史数据
+    times: string[], // 历史日期
+    yestclose: number, // 昨日收盘价
+    last: number, // 最新行情价
+    lastTime: string, // 最新行情时间
+    decimal: number, // 保留小数位
+    min: number, // Y轴最低指标线
+    max: number, // Y轴最高指标线
+    ma5: (number | string)[], //均线数据
+}
+
+export function handleEchart() {
+    const options = ref<EChartsOption>({});
+    // 当前主题
+    const theme = getTheme();
+    // 图表数据
+    const chartData = ref<ChartData>({
+        datas: [],
+        times: [],
+        yestclose: 0,
+        last: 0,
+        lastTime: '',
+        decimal: 0,
+        min: 0,
+        max: 0,
+        ma5: []
+    });
+
+    // 计算涨跌幅百分比
+    const calcRatio = (val: number) => {
+        const result = (Number(val) - chartData.value.yestclose) / chartData.value.yestclose * 100;
+        return toDecimalFull(result) + '%';
+    }
+
+    // 初始化图表配置
+    const initOptions = () => {
+        const { datas, times, yestclose, last, min, max, ma5, decimal } = chartData.value;
+        options.value = {
+            legend: {
+                //图例控件,点击图例控制哪些系列不显示
+                type: 'scroll',
+                itemWidth: 14,
+                itemHeight: 2,
+                left: '5%',
+                top: 0,
+                textStyle: {
+                    fontSize: 12,
+                },
+            },
+            // 悬浮框
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    type: 'cross',
+                },
+                backgroundColor: 'rgba(255,255,255,.9)',
+                borderWidth: 1,
+                borderRadius: 3,
+                textStyle: {
+                    color: '#4d535c',
+                },
+                // eslint-disable-next-line
+                formatter: (params: any) => {
+                    const i = params[0].dataIndex;
+                    const result = `
+    <div><span style="display: inline-block; width: 40px">当前价:</span><span style="display: inline-block; width: 100px; text-align: right">${datas[i]}</span></div>
+    <div><span style="display: inline-block; width: 40px">均价:</span><span style="display: inline-block; width: 100px; text-align: right">${ma5[i]}</span></div>
+    <div><span style="display: inline-block; width: 40px">涨幅:</span><span style="display: inline-block; width: 100px; text-align: right">${calcRatio(datas[i])}</span></div>`;
+                    return result;
+                },
+            },
+            grid: {
+                top: '8%',
+                left: '8%',
+                right: '8%',
+                bottom: '8%',
+            },
+            xAxis: {
+                type: 'category',
+                // X轴时间线
+                data: times,
+                splitLine: {
+                    // 坐标分隔线
+                    show: true,
+                },
+            },
+            yAxis: [
+                // Y轴左侧标签
+                {
+                    id: 'leftPrice',
+                    scale: true,
+                    min: min,
+                    max: max,
+                    axisLabel: {
+                        formatter: (val: number) => toDecimalFull(val, decimal),
+                    }
+                },
+                // Y轴右侧标签
+                {
+                    id: 'rightRatio',
+                    scale: true,
+                    min: min,
+                    max: max,
+                    splitLine: {
+                        show: false,
+                    },
+                    axisLabel: {
+                        formatter: (val: number) => calcRatio(val),
+                    }
+                },
+            ],
+            // series 中不指定 yAxisId 或 yAxisIndex 默认关联 yAxis 第一个配置,xAxis 配置同理
+            series: [
+                {
+                    name: '当前价',
+                    type: 'line',
+                    yAxisId: 'leftPrice',
+                    data: chartData.value.datas,
+                    smooth: true,
+                    symbol: 'circle', //中时有小圆点
+                    lineStyle: {
+                        opacity: 0.8,
+                        width: 1,
+                    },
+                    markLine: {
+                        // 标线两端图标
+                        symbol: 'none',
+                        // 标线标签样式
+                        data: [
+                            {
+                                // 最新价
+                                yAxis: last,
+                            },
+                            {
+                                // 昨结价
+                                yAxis: yestclose,
+                                lineStyle: {
+                                    color: '#333',
+                                },
+                                label: {
+                                    show: false,
+                                }
+                            },
+                        ],
+                    },
+                },
+                {
+                    type: 'line',
+                    yAxisId: 'rightRatio', // 关联Y轴右侧标签
+                    data: datas,
+                    symbol: 'none',
+                    lineStyle: {
+                        width: 0,
+                    },
+                },
+                {
+                    name: '均价',
+                    type: 'line',
+                    data: ma5,
+                    smooth: true,
+                    symbol: 'none',
+                    lineStyle: {
+                        width: 1,
+                        opacity: 0.8,
+                    },
+                },
+            ],
+        };
+
+        // 先用 setTimeout 处理,待优化深度合并
+        setTimeout(() => {
+            options.value = getColors(theme.value);
+        }, 0);
+    };
+
+    // 动态更新数据
+    const updateOptions = () => {
+        const { datas, last, yestclose, min, max } = chartData.value;
+        if (datas.length) {
+            options.value = {
+                yAxis: [
+                    // Y轴左侧标签
+                    {
+                        id: 'leftPrice',
+                        min: min,
+                        max: max,
+                    },
+                    // Y轴右侧标签
+                    {
+                        id: 'rightRatio',
+                        min: min,
+                        max: max,
+                    },
+                ],
+                series: [
+                    {
+                        markLine: {
+                            data: [
+                                {
+                                    // 最新价
+                                    yAxis: last,
+                                },
+                                {
+                                    // 昨结价
+                                    yAxis: yestclose,
+                                    lineStyle: {
+                                        color: '#333',
+                                    },
+                                    label: {
+                                        show: false,
+                                    }
+                                },
+                            ],
+                        },
+                    },
+                ],
+            };
+        }
+    };
+
+    // 设置图表样式
+    const setColors = (colors: Colors): EChartsOption => {
+        const { yestclose } = chartData.value;
+        return {
+            // 图表背景颜色
+            backgroundColor: colors.backgroundColor,
+            axisPointer: {
+                label: {
+                    color: colors.axisPointerLabelColor,
+                },
+            },
+            legend: {
+                textStyle: {
+                    color: colors.legendTextColor,
+                },
+            },
+            xAxis: {
+                splitLine: {
+                    lineStyle: {
+                        // 坐标分隔线颜色
+                        color: colors.xAxisLineColor,
+                    },
+                },
+            },
+            yAxis: [
+                // Y轴左侧标签
+                {
+                    id: 'leftPrice',
+                    axisLabel: {
+                        color: (val: any) => {
+                            if (val > yestclose) return colors.upColor;
+                            if (val < yestclose) return colors.downColor;
+                            return '#3C454B';
+                        },
+                    },
+                    splitLine: {
+                        lineStyle: {
+                            // 坐标分隔线颜色
+                            color: colors.yAxisLineColor,
+                        },
+                    },
+                },
+                // Y轴右侧标签
+                {
+                    id: 'rightRatio',
+                    axisLabel: {
+                        color: (val: any) => {
+                            if (val > yestclose) return colors.upColor;
+                            if (val < yestclose) return colors.downColor;
+                            return '#3C454B';
+                        },
+                    },
+                },
+            ],
+            series: [
+                {
+                    lineStyle: {
+                        color: colors.seriesLineColor,
+                    },
+                    areaStyle: {
+                        color: colors.seriesAreaGradients,
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    },
+                    markLine: {
+                        // 标线标签样式
+                        label: {
+                            color: colors.seriesMarkLabelColor,
+                        },
+                        // 标线样式
+                        lineStyle: {
+                            color: colors.seriesMarkLineColor,
+                        },
+                    },
+                }
+            ]
+        }
+    }
+
+    // 获取图表样式配置
+    const getColors = (theme: ThemeEnum) => {
+        switch (theme) {
+            case ThemeEnum.default:
+            case ThemeEnum.dark:
+                return setColors({
+                    backgroundColor: 'transparent',
+                    axisPointerLabelColor: '#fff',
+                    legendTextColor: '#0e99e2',
+                    xAxisLineColor: '#171B1D',
+                    yAxisLineColor: '#171B1D',
+                    seriesMarkLabelColor: '#3C454B',
+                    seriesMarkLineColor: '#33393D',
+                    upColor: '#FF2B2B',
+                    downColor: '#1FF195',
+                    seriesLineColor: '#39afe6',
+                    seriesAreaGradients: new echarts.graphic.LinearGradient(0, 0, 0, 1,
+                        [
+                            {
+                                offset: 0,
+                                color: 'rgba(0, 136, 212, 0.7)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(0, 136, 212, 0.02)',
+                            },
+                        ],
+                        false
+                    ),
+                });
+            case ThemeEnum.light:
+                return setColors({
+                    backgroundColor: 'transparent',
+                    axisPointerLabelColor: '#fff',
+                    legendTextColor: '#FC9618',
+                    xAxisLineColor: '#DAE5EC',
+                    yAxisLineColor: '#DAE5EC',
+                    seriesMarkLabelColor: '#666',
+                    seriesMarkLineColor: '#ACB8C0',
+                    upColor: '#FF2B2B',
+                    downColor: '#00A843',
+                    seriesLineColor: '#3864d7',
+                    seriesAreaGradients: new echarts.graphic.LinearGradient(0, 0, 0, 1,
+                        [
+                            {
+                                offset: 0,
+                                color: 'rgba(0, 136, 212, 0.3)',
+                            },
+                            {
+                                offset: 1,
+                                color: 'rgba(0, 136, 212, 0.3)',
+                            },
+                        ],
+                        false
+                    ),
+                });
+        }
+    }
+
+    watch(theme, (val) => {
+        options.value = getColors(val);
+    });
+
+    return {
+        chartData,
+        options,
+        initOptions,
+        updateOptions,
+    }
+}

+ 7 - 0
src/common/components/echart/index.ts

@@ -0,0 +1,7 @@
+import EchartKline from './echart-kline/index.vue';
+import EchartTime from './echart-timeline/index.vue';
+
+export {
+    EchartKline,
+    EchartTime,
+}

+ 6 - 7
src/common/setup/table/tableQuote.ts

@@ -122,17 +122,16 @@ function isPrice(price1: number | string, price2: number | string) {
 
 /**
  * 处理行情价格颜色
- * @param price1
- * @param price2
+ * @param value
+ * @param presettle 昨结价
  * @returns
  */
-export function handleQuotePriceColor(record: QueryQuoteDayRsp) {
-    const { presettle, bid } = record
+export function handleQuotePriceColor(value: number, presettle: number) {
     let result = ''
-    if (isPrice(bid, presettle)) {
-        if (bid === presettle) {
+    if (isPrice(value, presettle)) {
+        if (value === presettle) {
             result = ''
-        } else if (bid > presettle) {
+        } else if (value > presettle) {
             result = 'up-quote-color'
         } else {
             result = 'down-quote-color'

+ 5 - 4
src/common/setup/trade/index.ts

@@ -38,7 +38,7 @@ export function useTradeRule() {
 export function useBuyFixedPricMaxNum<T extends CommomTradeForm>(formState: T, canUseMoney: Function) {
     let result = 0
     // 可用资金
-    const money = canUseMoney()
+    const money = canUseMoney() > 0 ? canUseMoney() : 0
     const { isRatioInBuyMarginType, buymarginvalue } = useTradeRule()
 
     // 最大数量 = 可用资金/(履约保证金*挂牌价格)
@@ -55,7 +55,7 @@ export function useBuyFixedPricMaxNum<T extends CommomTradeForm>(formState: T, c
 export function useSellFixedPriceMaxNum<T extends CommomTradeForm>(formState: T, canUseMoney: Function) {
     let result = 0
     // 可用资金
-    const money = canUseMoney()
+    const money = canUseMoney() > 0 ? canUseMoney() : 0
     const { isRationInSellMarginType, sellmarginvalue } = useTradeRule()
     // 最大数量 = 可用资金/(履约保证金*挂牌价格)
     const marginValue = isRationInSellMarginType() ? (sellmarginvalue * formState.FixedPrice) : (sellmarginvalue + formState.FixedPrice)
@@ -96,7 +96,7 @@ export function useListingTradeNumAndPrice<T extends CommomTradeForm>({ formStat
 
     function getMaxNum(value: number, isBuy: boolean) {
         // 可用资金
-        const money = canUseMoney()
+        const money = canUseMoney() > 0 ? canUseMoney() : 0
         // 挂牌 最大数量=可用资金/(买方履约保证金比例*挂牌价格)
         let result = 0
         if (isFloat()) {
@@ -112,6 +112,7 @@ export function useListingTradeNumAndPrice<T extends CommomTradeForm>({ formStat
 
     // 挂买 最大数量
     function getListingBuyMaxNum() {
+        debugger
         const { buymarginvalue } = useTradeRule()
         return getMaxNum(buymarginvalue, true)
     }
@@ -161,7 +162,7 @@ export function useListingTradeNumAndPrice<T extends CommomTradeForm>({ formStat
 export function useBuyDelistingRatioMaxNum(OrderQty: number, canUseMoney: Function) {
     let result = 0;
     const { buymarginvalue } = useTradeRule()
-    const money = canUseMoney()
+    const money = canUseMoney() > 0 ? canUseMoney() : 0
     if (money && !isNaN(money)) {
         const num = +(money / buymarginvalue).toFixed(0);
         // 买 最大可摘数量=min{挂牌数量,可用资金/(履约保证金比例)}

+ 4 - 4
src/services/go/quote/index.ts

@@ -6,7 +6,7 @@ import * as type from './interface';
  * @param param QueryHistoryDatas
  * @returns QueryHistoryDatasRsp
  */
-export function QueryHistoryDatas(param: type.QueryHistoryDatas): Promise<type.QueryHistoryDatasRsp> {
+export function QueryHistoryDatas(param: type.QueryHistoryDatas): Promise<type.QueryHistoryDatasRsp[]> {
     return commonSearch_go('/Quote/QueryHistoryDatas', param)
 }
 
@@ -33,9 +33,9 @@ export function QueryQuoteDay(goodsCodes: string): Promise<string> {
 
 /**
  * 分时图数据查询
- * @param goodsCodes 商品代码
+ * @param goodsCode 商品代码
  * @returns
  */
-export function QueryTSData(goodsCodes: string): Promise<type.QueryTSDataRsp> {
-    return commonSearch_go('/Quote/QueryTSData', { goodsCodes })
+export function QueryTSData(goodsCode: string): Promise<type.QueryTSDataRsp> {
+    return commonSearch_go('/Quote/QueryTSData', { goodsCode })
 }

+ 149 - 148
src/services/go/quote/interface.ts

@@ -1,12 +1,12 @@
-// 周期类型, 0-秒 1: 1分钟 2: 5分钟 3: 30分钟 4: 60分钟 120: 2小时 240: 4小时 11: 日线
-export type CycleType = 0 | 1 | 2 | 3 | 120 | 240 | 11;
+// 周期类型,-1:分时 0-秒 1: 1分钟 2: 5分钟 3: 30分钟 4: 60分钟 120: 2小时 240: 4小时 11: 日线
+export type CycleType = -1 | 0 | 1 | 2 | 3 | 4 | 120 | 240 | 11;
 
 export interface QueryHistoryTikDatas {
     goodsCode: string, // 商品代码
     startTime?: string, // 开始时间,格式:yyyy-MM-dd HH:mm:ss
     endTime?: string, // 结束时间,格式:yyyy-MM-dd HH:mm:ss
     count?: number, // 条数
-    isAsc? : boolean, // 是否按时间顺序排序(默认为时间倒序排序)
+    isAsc?: boolean, // 是否按时间顺序排序(默认为时间倒序排序)
 }
 
 export interface QueryHistoryDatas extends QueryHistoryTikDatas {
@@ -15,6 +15,7 @@ export interface QueryHistoryDatas extends QueryHistoryTikDatas {
 
 export interface QueryHistoryDatasRsp {
     c: number; //收盘价
+    f: boolean; //是否补充数据
     h: number; //最高价
     hv: number; //持仓量
     l: number; //最低价
@@ -53,155 +54,155 @@ export interface historyDatas {
     tv: number;
 }
 export interface QueryTSDataRsp {
-    Count: number;
-  decimalPlace: number;
-  endTime: string;
-  goodsCode: string;
-   outGoodsCode: string;
-  preSettle: number;
-  startTime: string;
-  tradeDate: string;
-  historyDatas: historyDatas[],
-  RunSteps: RunSteps[],
+    Count?: number;
+    decimalPlace?: number;
+    endTime?: string;
+    goodsCode?: string;
+    outGoodsCode?: string;
+    preSettle?: number;
+    startTime?: string;
+    tradeDate?: string;
+    historyDatas?: historyDatas[];
+    RunSteps?: RunSteps[];
 }
 
 export interface RunSteps {
-      end: string;
-      endflag: number;
-      endtime: string;
-      endweekday: number;
-      groupid: number;
-      runstep: number;
-      sectionid: number;
-      start: string;
-      startflag: number;
-      starttime: string;
-      startweekday: number;
-      tradeweekday: number;
+    end: string;
+    endflag: number;
+    endtime: string;
+    endweekday: number;
+    groupid: number;
+    runstep: number;
+    sectionid: number;
+    start: string;
+    startflag: number;
+    starttime: string;
+    startweekday: number;
+    tradeweekday: number;
 }
 
 export interface QueryQuoteDayRsp {
-  ask: number;
-  ask1number: number;
-  ask2: number;
-  ask3: number;
-  ask4: number;
-  ask5: number;
-  ask6: number;
-  ask7: number;
-  ask8: number;
-  ask9: number;
-  askorderid: number;
-  askorderid2: number;
-  askorderid3: number;
-  askorderid4: number;
-  askorderid5: number;
-  askordervolume: number;
-  askordervolume1number: number;
-  askordervolume2: number;
-  askordervolume3: number;
-  askordervolume4: number;
-  askordervolume5: number;
-  askordervolume6: number;
-  askordervolume7: number;
-  askordervolume8: number;
-  askordervolume9: number;
-  askqueueinfo :string;
-  askvolume: number;
-  askvolume1number: number;
-  askvolume2: number;
-  askvolume3: number;
-  askvolume4: number;
-  askvolume5: number;
-  askvolume6: number;
-  askvolume7: number;
-  askvolume8: number;
-  askvolume9: number;
-  averageprice: number;
-  bid: number;
-  bid1number: number;
-  bid2: number;
-  bid3: number;
-  bid4: number;
-  bid5: number;
-  bid6: number;
-  bid7: number;
-  bid8: number;
-  bid9: number;
-  bidorderid: number;
-  bidorderid2: number;
-  bidorderid3: number;
-  bidorderid4: number;
-  bidorderid5: number;
-  bidordervolume: number;
-  bidordervolume1number: number;
-  bidordervolume2: number;
-  bidordervolume3: number;
-  bidordervolume4: number;
-  bidordervolume5: number;
-  bidordervolume6: number;
-  bidordervolume7: number;
-  bidordervolume8: number;
-  bidordervolume9: number;
-  bidqueueinfo: string;
-  bidvolume: number;
-  bidvolume1number: number;
-  bidvolume2: number;
-  bidvolume3: number;
-  bidvolume4: number;
-  bidvolume5: number;
-  bidvolume6: number;
-  bidvolume7: number;
-  bidvolume8: number;
-  bidvolume9: number;
-  calloptionpremiums: number;
-  calloptionpremiums2: number;
-  calloptionpremiums3: number;
-  calloptionpremiums4: number;
-  calloptionpremiums5: number;
-  cleartime: number;
-  exchangecode: number;
-  exchangedate: number;
-  goodscode: string;
-  grepmarketprice: number;
-  highest: number;
-  holdincrement: number;
-  holdvolume: number;
-  iep: number;
-  iev: number;
-  inventory: number;
-  iscleared: number;
-  issettled: number;
-  last: number;
-  lastlot: number;
-  lasttime: string;
-  lastturnover: number;
-  lastvolume: number;
-  limitdown: number;
-  limitup: number;
-  lowest: number;
-  nontotalholdervolume: number;
-  nontotallot: number;
-  nontotalturnover: number;
-  nontotalvolume: number;
-  opened: number;
-  opentime: string;
-  orderid: number;
-  preclose: number;
-  preholdvolume: number;
-  presettle: number;
-  publictradetype: string;
-  putoptionpremiums: number;
-  putoptionpremiums2: number;
-  putoptionpremiums3: number;
-  putoptionpremiums4: number;
-  putoptionpremiums5: number;
-  settle: number;
-  strikeprice: number;
-  totalaskvolume: number;
-  totalbidvolume: number;
-  totallot: number;
-  totalturnover: number;
-  totalvolume: number;
-  utclasttime: string;
+    ask: number;
+    ask1number: number;
+    ask2: number;
+    ask3: number;
+    ask4: number;
+    ask5: number;
+    ask6: number;
+    ask7: number;
+    ask8: number;
+    ask9: number;
+    askorderid: number;
+    askorderid2: number;
+    askorderid3: number;
+    askorderid4: number;
+    askorderid5: number;
+    askordervolume: number;
+    askordervolume1number: number;
+    askordervolume2: number;
+    askordervolume3: number;
+    askordervolume4: number;
+    askordervolume5: number;
+    askordervolume6: number;
+    askordervolume7: number;
+    askordervolume8: number;
+    askordervolume9: number;
+    askqueueinfo: string;
+    askvolume: number;
+    askvolume1number: number;
+    askvolume2: number;
+    askvolume3: number;
+    askvolume4: number;
+    askvolume5: number;
+    askvolume6: number;
+    askvolume7: number;
+    askvolume8: number;
+    askvolume9: number;
+    averageprice: number;
+    bid: number;
+    bid1number: number;
+    bid2: number;
+    bid3: number;
+    bid4: number;
+    bid5: number;
+    bid6: number;
+    bid7: number;
+    bid8: number;
+    bid9: number;
+    bidorderid: number;
+    bidorderid2: number;
+    bidorderid3: number;
+    bidorderid4: number;
+    bidorderid5: number;
+    bidordervolume: number;
+    bidordervolume1number: number;
+    bidordervolume2: number;
+    bidordervolume3: number;
+    bidordervolume4: number;
+    bidordervolume5: number;
+    bidordervolume6: number;
+    bidordervolume7: number;
+    bidordervolume8: number;
+    bidordervolume9: number;
+    bidqueueinfo: string;
+    bidvolume: number;
+    bidvolume1number: number;
+    bidvolume2: number;
+    bidvolume3: number;
+    bidvolume4: number;
+    bidvolume5: number;
+    bidvolume6: number;
+    bidvolume7: number;
+    bidvolume8: number;
+    bidvolume9: number;
+    calloptionpremiums: number;
+    calloptionpremiums2: number;
+    calloptionpremiums3: number;
+    calloptionpremiums4: number;
+    calloptionpremiums5: number;
+    cleartime: number;
+    exchangecode: number;
+    exchangedate: number;
+    goodscode: string;
+    grepmarketprice: number;
+    highest: number;
+    holdincrement: number;
+    holdvolume: number;
+    iep: number;
+    iev: number;
+    inventory: number;
+    iscleared: number;
+    issettled: number;
+    last: number;
+    lastlot: number;
+    lasttime: string;
+    lastturnover: number;
+    lastvolume: number;
+    limitdown: number;
+    limitup: number;
+    lowest: number;
+    nontotalholdervolume: number;
+    nontotallot: number;
+    nontotalturnover: number;
+    nontotalvolume: number;
+    opened: number;
+    opentime: string;
+    orderid: number;
+    preclose: number;
+    preholdvolume: number;
+    presettle: number;
+    publictradetype: string;
+    putoptionpremiums: number;
+    putoptionpremiums2: number;
+    putoptionpremiums3: number;
+    putoptionpremiums4: number;
+    putoptionpremiums5: number;
+    settle: number;
+    strikeprice: number;
+    totalaskvolume: number;
+    totalbidvolume: number;
+    totallot: number;
+    totalturnover: number;
+    totalvolume: number;
+    utclasttime: string;
 }

+ 1 - 1
src/services/socket/quota/adapter/index.ts

@@ -129,7 +129,7 @@ export function parseReceivePush(quotationData: any) {
                 const fn = (value: number) => +(value / num).toFixed(decimalplace)
                 goodsQuoteTik.decimalplace = decimalplace
                 goodsQuoteTik.last = goodsQuoteTik.last ? fn(goodsQuoteTik.last) : '--'
-                goodsQuoteTik.presettle = goodsQuoteTik.presettle ? (fn(goodsQuoteTik.presettle)) : '--'
+                // goodsQuoteTik.presettle = goodsQuoteTik.presettle ? (fn(goodsQuoteTik.presettle)) : '--'
                 goodsQuoteTik.ask = goodsQuoteTik.ask ? (fn(goodsQuoteTik.ask)) : '--'
                 goodsQuoteTik.ask2 = goodsQuoteTik.ask2 ? (fn(goodsQuoteTik.ask2)) : '--'
                 goodsQuoteTik.ask3 = goodsQuoteTik.ask3 ? (fn(goodsQuoteTik.ask3)) : '--'

+ 27 - 0
src/utils/number/index.ts

@@ -19,3 +19,30 @@ export function getDecimalsNum(val: any, decimal = 2, maxCount = 6) {
     }
     return result
 }
+
+/**
+ * 强制保留小数位,零位四舍五入取整
+ * @param val 
+ * @param decimal 需要保留小数位 默认2 
+ * @returns 
+ */
+export function toDecimalFull(val: number, decimal = 2) {
+    if (decimal <= 0) {
+        return Math.round(val).toString();
+    } else {
+        let str = val.toString();
+        const num = str.indexOf('.');
+        if (num < 0) {
+            // 小数位自动补零
+            if (decimal > 0) {
+                const count = str.length;
+                str += '.';
+                while (str.length <= count + decimal) {
+                    str += '0';
+                }
+            }
+            return str;
+        }
+        return val.toFixed(decimal);
+    }
+}

+ 0 - 14
src/utils/qt/common.ts

@@ -1,17 +1,3 @@
-import timerUtil, { TimeoutTimerNames } from '@/utils/timer/timerUtil';
-/**
- * 防抖(debounce)
- * @param fn 需要防抖的函数
- * @param wait 毫秒,防抖期限值
- * @returns
- */
-export function debounce(fn: () => void, wait: number, timer: keyof TimeoutTimerNames = 'debounce'): void {
-    return (function () {
-        timerUtil.clearTimeout(timer);
-        timerUtil.setTimeout(fn, wait, timer);
-    })();
-}
-
 /**
  * 生成UUID
  */

+ 32 - 14
src/utils/time/index.ts

@@ -1,4 +1,5 @@
-import moment, { Moment } from "moment";
+import timerUtil, { TimeoutTimerNames } from '@/utils/timer/timerUtil';
+import moment, { DurationInputArg1, Moment, unitOfTime } from "moment";
 
 /**
  * 获取number类型时间戳
@@ -14,26 +15,43 @@ type Time = string | Moment | Date
  * @param val1 时间1
  * @param val2 时间2
  * @param type 时间格式化类型
+ * @param unit 时间单位
+ * @param amount 时间区间间隔数
  * @returns string[]类型
  */
-export function getRangeTime(val1: Time, val2: Time, type = 'YYYYMMDD'): string[] {
-    const fn = (val: Time) => moment(val).format(type)
-    const result: string[] = []
-    // 处理开始时间和结束时间
-    let startTime = fn(val1)
-    let endTime = fn(val2)
-    const isSame = () => moment(startTime).isSame(moment(endTime))
+export function getRangeTime(val1: Time, val2: Time, type = 'YYYYMMDD', unit: unitOfTime.DurationConstructor = 'd', amount: DurationInputArg1 = 1): string[] {
+    const fn = (val: Moment) => val.format(type);
+    // 处理开始时间和结束时间
+    let startTime = moment(val1);
+    let endTime = moment(val2);
+
+    const result: string[] = [];
+    const isSame = () => startTime.isSame(endTime, unit);
+
     if (isSame()) {
-        return [startTime, startTime]
+        return [fn(startTime), fn(startTime)];
     } else {
-        if (moment(startTime).isAfter(moment(endTime))) {
+        if (startTime.isAfter(endTime)) {
             [startTime, endTime] = [endTime, startTime]
         }
         while (!isSame()) {
-            result.push(startTime)
-            startTime = moment(startTime).add(1, 'd').format(type)
+            result.push(fn(startTime));
+            startTime = startTime.add(amount, unit);
         }
-        result.push(endTime)
-        return result
+        result.push(fn(endTime));
+        return result;
     }
+}
+
+/**
+ * 防抖(debounce)
+ * @param fn 需要防抖的函数
+ * @param wait 毫秒,防抖期限值
+ * @returns
+ */
+export function debounce(fn: () => void, wait: number, timer: keyof TimeoutTimerNames = 'debounce'): void {
+    return (function () {
+        timerUtil.clearTimeout(timer);
+        timerUtil.setTimeout(fn, wait, timer);
+    })();
 }

+ 72 - 78
src/views/market/spot_trade/spot_trade_order_transaction/spot_trade_order_transaction_swap/index.vue

@@ -1,85 +1,79 @@
 <template>
-    <!--订单交易 挂牌转让-->
-    <div class="topTableHeight">
-        <div class="filterTable">
-            <div class="filter-custom-table">
-                <a-select
-                    class="conditionSelect"
-                    :style="{width: '180px', maxHeight: '400px', overflow: 'auto' }"
-                    @change="goodsChange"
-                    placeholder="全部标的合约"
-                >
-                    <a-select-option
-                        v-for="item in getRefGoodsList()"
-                        :value="item"
-                        :key="item"
-                    >{{item}}</a-select-option>
-                </a-select>
-                <!-- <a-button class="btnDeafault"
+  <!--订单交易 挂牌转让-->
+  <div class="topTableHeight">
+    <div class="filterTable">
+      <div class="filter-custom-table">
+        <a-select class="conditionSelect"
+                  :style="{width: '180px', maxHeight: '400px', overflow: 'auto' }"
+                  @change="goodsChange"
+                  placeholder="全部标的合约">
+          <a-select-option v-for="item in getRefGoodsList()"
+                           :value="item"
+                           :key="item">{{item}}</a-select-option>
+        </a-select>
+        <!-- <a-button class="btnDeafault"
                 @click="search(true)"
                 style="width: 80px">筛选</a-button>-->
-            </div>
-        </div>
-        <a-table
-            :columns="columns"
-            :class="['srcollYTable', isBottom ? 'secondTabTable' : 'secondTabTableNoBottom', goodsList.length ? 'noPlaceHolder' : 'hasPlaceHolder']"
-            :scroll="{ x: '100%', y: isBottom ? 'calc(100vh- 407px)' : 'calc(100vh - 167px)' }"
-            :pagination="false"
-            :loading="loading"
-            :expandedRowKeys="expandedRowKeys"
-            :customRow="Rowclick"
-            rowKey="goodscode"
-            ref="tableRef"
-            :data-source="goodsList"
-        >
-            <template #totalturnover="{ text }">
-                <span>{{changeUnit(text)}}</span>
-            </template>
-            <!-- 涨跌 -->
-            <template #change="{ record }">
-                <span>{{quoteChange(record, record.decimalplace)}}</span>
-            </template>
-            <!-- 幅度 -->
-            <template #amplitude="{ record }">
-                <span>{{quoteAmplituOfVibration(record, record.decimalplace)}}</span>
-            </template>
-            <!-- 振幅 -->
-            <template #vibration="{ record }">
-                <span>{{quoteAmplituOfVibration(record, record.decimalplace)}}</span>
-            </template>
-            <template #index="{ index }">
-                <span>{{index + 1}}</span>
-            </template>
-            <!-- 买价 -->
-            <template #bid="{ text, record }">
-                <span :class="handleQuotePriceColor(text, record.presettle)">{{text}}</span>
-            </template>
-            <!-- 卖价 -->
-            <template #ask="{ text, record }">
-                <span :class="handleQuotePriceColor(text, record.presettle)">{{text}}</span>
-            </template>
-            <!-- 最新价 -->
-            <template #last="{ text, record }">
-                <span :class="handleQuotePriceColor(text, record.presettle)">{{text}}</span>
-            </template>
-            <!-- 最低价 -->
-            <template #lowest="{ text, record }">
-                <span :class="handleQuotePriceColor(text, record.presettle)">{{text}}</span>
-            </template>
-            <!-- 最高价 -->
-            <template #highest="{ text, record }">
-                <span :class="handleQuotePriceColor(text, record.presettle)">{{text}}</span>
-            </template>
-        </a-table>
-        <!-- 右键 -->
-        <contextMenu :contextMenu="contextMenu" @cancel="closeContext" :list="btnList"></contextMenu>
-        <component
-            :is="componentId"
-            v-if="componentId"
-            :selectedRow="selectedRow"
-            @cancel="closeComponent"
-        ></component>
+      </div>
     </div>
+    <a-table :columns="columns"
+             :class="['srcollYTable', isBottom ? 'secondTabTable' : 'secondTabTableNoBottom', goodsList.length ? 'noPlaceHolder' : 'hasPlaceHolder']"
+             :scroll="{ x: '100%', y: isBottom ? 'calc(100vh- 407px)' : 'calc(100vh - 167px)' }"
+             :pagination="false"
+             :loading="loading"
+             :expandedRowKeys="expandedRowKeys"
+             :customRow="Rowclick"
+             rowKey="goodscode"
+             ref="tableRef"
+             :data-source="goodsList">
+      <template #totalturnover="{ text }">
+        <span>{{changeUnit(text)}}</span>
+      </template>
+      <!-- 涨跌 -->
+      <template #change="{ record }">
+        <span>{{quoteChange(record, record.decimalplace)}}</span>
+      </template>
+      <!-- 幅度 -->
+      <template #amplitude="{ record }">
+        <span>{{quoteAmplituOfVibration(record, record.decimalplace)}}</span>
+      </template>
+      <!-- 振幅 -->
+      <template #vibration="{ record }">
+        <span>{{quoteAmplituOfVibration(record, record.decimalplace)}}</span>
+      </template>
+      <template #index="{ index }">
+        <span>{{index + 1}}</span>
+      </template>
+      <!-- 买价 -->
+      <template #bid="{ text, record }">
+        <span :class="handleQuotePriceColor(text, record.presettle)">{{text}}</span>
+      </template>
+      <!-- 卖价 -->
+      <template #ask="{ text, record }">
+        <span :class="handleQuotePriceColor(text, record.presettle)">{{text}}</span>
+      </template>
+      <!-- 最新价 -->
+      <template #last="{ text, record }">
+        <span :class="handleQuotePriceColor(text, record.presettle)">{{text}}</span>
+      </template>
+      <!-- 最低价 -->
+      <template #lowest="{ text, record }">
+        <span :class="handleQuotePriceColor(text, record.presettle)">{{text}}</span>
+      </template>
+      <!-- 最高价 -->
+      <template #highest="{ text, record }">
+        <span :class="handleQuotePriceColor(text, record.presettle)">{{text}}</span>
+      </template>
+    </a-table>
+    <!-- 右键 -->
+    <contextMenu :contextMenu="contextMenu"
+                 @cancel="closeContext"
+                 :list="btnList"></contextMenu>
+    <component :is="componentId"
+               v-if="componentId"
+               :selectedRow="selectedRow"
+               @cancel="closeComponent"></component>
+  </div>
 </template>
 
 <script lang="ts">

+ 4 - 4
src/views/market/spot_trade/spot_trade_order_transaction/spot_trade_order_transaction_swap/setup.ts

@@ -75,10 +75,10 @@ export const columnsList = [
 
     { title: '标的合约', key: 'refgoodsname' },
 
-    { title: '买价', key: 'bid' },
-    { title: '买量', key: 'bidvolume' },
-    { title: '卖价', key: 'ask' },
-    { title: '卖量', key: 'askvolume' },
+    // { title: '买价', key: 'bid' },
+    // { title: '买量', key: 'bidvolume' },
+    // { title: '卖价', key: 'ask' },
+    // { title: '卖量', key: 'askvolume' },
 
     { title: '当前价', key: 'last' },
     { title: '涨跌', key: 'change' }, // 最新价 - 昨结价

+ 163 - 0
src/views/market/spot_trade/spot_trade_reference_market/components/chart/index.less

@@ -0,0 +1,163 @@
+.chart-container {
+    [theme='light'] & {
+        --bgcolor: #fff;
+        --tab-border-color: #dae5ec;
+        --tab-checked-color: #0866b8;
+        --tab-checked-bgcolor: #d4e0ff;
+        --slider-border-color: #b2c4dd;
+        --slider-bgcolor: #edf2f7;
+        --slider-button-color: #b2c4dd;
+    }
+
+    display: flex;
+    width: 100%;
+    height: calc(100% - 41px);
+    background-color: var(--bgcolor, #0e0e0f);
+
+    .chart-content {
+        display: flex;
+        flex-direction: column;
+        flex: 1;
+        height: 100%;
+
+        &__main {
+            flex: 1;
+        }
+
+        &__header {
+            padding: 4px 0;
+        }
+
+        &__tabs {
+            .ant-radio-button-wrapper {
+                height: 22px;
+                line-height: 20px;
+                color: #7a8a94;
+                border-color: var(--tab-border-color, #22292c) !important;
+                background-color: transparent;
+
+                &:not(:first-child)::before {
+                    background-color: var(--tab-border-color, #22292c) !important;
+                }
+            }
+            .ant-radio-button-wrapper-checked {
+                color: var(--tab-checked-color, #0866b8);
+                background-color: var(--tab-checked-bgcolor, #0e2f4c);
+
+                &:not(.ant-radio-button-wrapper-disabled)::before {
+                    background-color: var(--tab-border-color, #22292c) !important;
+                }
+
+                &:not(.ant-radio-button-wrapper-disabled):focus-within {
+                    box-shadow: none;
+                }
+            }
+        }
+    }
+
+    .chart-slider {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        width: 6px;
+        border-left: 1px solid var(--slider-border-color, #33393d);
+        border-right: 1px solid var(--slider-border-color, #33393d);
+        background-color: var(--slider-bgcolor, #0e0e0f);
+
+        &__button {
+            width: 2px;
+            height: 30px;
+            font-size: 0;
+            background-color: var(--slider-button-color, #3c454b);
+            border-radius: 1px;
+        }
+    }
+
+    .chart-tips {
+        width: 300px;
+        height: 100%;
+
+        &__nav {
+            display: flex;
+            justify-content: center;
+            align-items: center;
+
+            .content {
+                &:first-child:not(:last-child) {
+                    margin-right: 16px;
+                }
+
+                &--left {
+                    font-size: 16px;
+                    color: #3c454b;
+                }
+
+                &--right {
+                    font-size: 24px;
+                    color: #fc9618;
+                }
+            }
+        }
+
+        &__last {
+            display: flex;
+            justify-content: center;
+            align-items: center;
+
+            .content {
+                &--left {
+                    font-size: 24px;
+                    margin-right: 16px;
+                }
+
+                &--right {
+                    display: flex;
+                    flex-direction: column;
+                    align-items: flex-start;
+                }
+            }
+        }
+
+        &__volume {
+            .ant-row {
+                border-top: 1px solid #dae5ec;
+                padding: 8px;
+
+                .ant-col {
+                    text-align: left;
+
+                    &:first-child {
+                        color: #acb8c0;
+                    }
+
+                    &:last-child {
+                        color: #3c454b;
+                        text-align: right;
+                    }
+                }
+            }
+        }
+
+        &__info {
+            border-top: 1px solid #dae5ec;
+            padding: 8px 8px 8px 0;
+
+            .ant-row {
+                margin-top: 8px;
+
+                .ant-col {
+                    text-align: left;
+
+                    &:nth-child(odd) {
+                        color: #acb8c0;
+                        padding-left: 8px;
+                    }
+
+                    &:nth-child(even) {
+                        text-align: right;
+                    }
+                }
+            }
+        }
+    }
+}

+ 155 - 44
src/views/market/spot_trade/spot_trade_reference_market/components/chart/index.vue

@@ -1,83 +1,194 @@
 <template>
-  <!-- 交易图表  -->
-  <div class="chart-container">
-    <div class="chart-content"></div>
-    <div class="chart-tips">
-      <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 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">
+                    <template v-for="item in chartType" :key="item.type">
+                        <a-radio-button :value="item.type">{{ item.name }}</a-radio-button>
+                    </template>
+                </a-radio-group>
+            </div>
+            <echart-time class="chart-content__main" :quote-data="selectedRow" v-if="activeCycleType === -1"></echart-time>
+            <echart-kline class="chart-content__main" :quote-data="selectedRow" :cycle-type="activeCycleType" v-else></echart-kline>
+            <!-- <component :is="componentId" v-if="componentId" :series-data="chartData" :quote-data="selectedRow"></component> -->
+            <div class="chart-content__footer"></div>
+        </div>
+        <div class="chart-slider">
+            <div class="chart-slider__button"></div>
+        </div>
+        <div class="chart-tips">
+            <div>
+                <div class="chart-tips__nav">
+                    <div class="content content--left">{{ selectedRow.goodscode }}</div>
+                    <div class="content content--right">{{ selectedRow.goodsname }}</div>
+                </div>
+                <div class="chart-tips__last">
+                    <div :class="['content content--left', priceColor]">{{ selectedRow.last }}</div>
+                    <div class="content content--right">
+                        <span :class="priceColor">{{ quoteChange(selectedRow) }}</span>
+                        <span :class="priceColor">{{ quoteAmplitude(selectedRow) }}</span>
+                    </div>
+                </div>
+                <div class="chart-tips__volume">
+                    <a-row>
+                        <a-col :span="8">卖一</a-col>
+                        <a-col :span="8">{{ selectedRow.ask }}</a-col>
+                        <a-col :span="8">{{ selectedRow.askvolume }}</a-col>
+                    </a-row>
+                    <a-row>
+                        <a-col :span="8">买一</a-col>
+                        <a-col :span="8">{{ selectedRow.bid }}</a-col>
+                        <a-col :span="8">{{ selectedRow.bidvolume }}</a-col>
+                    </a-row>
+                </div>
+            </div>
+            <!-- <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 class="chart-tips__info">
+                <a-row>
+                    <a-col :span="4">最新</a-col>
+                    <a-col :span="8">{{ selectedRow.last }}</a-col>
+                    <a-col :span="4">均价</a-col>
+                    <a-col :span="8">{{ selectedRow.averageprice }}</a-col>
+                </a-row>
+                <a-row>
+                    <a-col :span="4">涨跌</a-col>
+                    <a-col :span="8">{{ quoteChange(selectedRow) }}</a-col>
+                    <a-col :span="4">今开</a-col>
+                    <a-col :span="8">{{ selectedRow.opened }}</a-col>
+                </a-row>
+                <a-row>
+                    <a-col :span="4">涨幅</a-col>
+                    <a-col :span="8">{{ quoteAmplitude(selectedRow) }}</a-col>
+                    <a-col :span="4">最高</a-col>
+                    <a-col :span="8">{{ selectedRow.highest }}</a-col>
+                </a-row>
+                <a-row>
+                    <a-col :span="4">总量</a-col>
+                    <a-col :span="8">{{ selectedRow.totalvolume }}</a-col>
+                    <a-col :span="4">最低</a-col>
+                    <a-col :span="8">{{ selectedRow.lowest }}</a-col>
+                </a-row>
+                <a-row>
+                    <a-col :span="4">金额</a-col>
+                    <a-col :span="8">{{ selectedRow.totalturnover }}</a-col>
+                    <a-col :span="4">量比</a-col>
+                    <a-col :span="8">{{ '--' }}</a-col>
+                </a-row>
+                <a-row>
+                    <a-col :span="4">涨停</a-col>
+                    <a-col :span="8">{{ selectedRow.limitup }}</a-col>
+                    <a-col :span="4">跌停</a-col>
+                    <a-col :span="8">{{ selectedRow.limitdown }}</a-col>
+                </a-row>
+                <a-row>
+                    <a-col :span="4">外盘</a-col>
+                    <a-col :span="8">{{ selectedRow.totalbidvolume }}</a-col>
+                    <a-col :span="4">内盘</a-col>
+                    <a-col :span="8">{{ selectedRow.totalaskvolume }}</a-col>
+                </a-row>
+                <a-row>
+                    <a-col :span="4">持仓</a-col>
+                    <a-col :span="8">{{ selectedRow.holdvolume }}</a-col>
+                    <a-col :span="4">结算</a-col>
+                    <a-col :span="8">{{ selectedRow.settle }}</a-col>
+                </a-row>
+                <a-row>
+                    <a-col :span="4">仓差</a-col>
+                    <a-col :span="8">{{ '--' }}</a-col>
+                    <a-col :span="4">前结</a-col>
+                    <a-col :span="8">{{ '--' }}</a-col>
+                </a-row>
+                <a-row>
+                    <a-col :span="4">日增</a-col>
+                    <a-col :span="8">{{ '--' }}</a-col>
+                    <a-col :span="4">开平</a-col>
+                    <a-col :span="8">{{ '--' }}</a-col>
+                </a-row>
+            </div>
+            <!-- <div @click="watchMore" class="watchMore">查看更多</div> -->
         </div>
-      </div>
-      <div @click="watchMore"
-           class="watchMore">查看更多</div>
     </div>
-  </div>
 </template>
 
 <script lang="ts">
-import { defineAsyncComponent, defineComponent } from '@/common/export/commonTable';
+import { defineComponent } from '@/common/export/commonTable';
 import { _closeModal } from '@/common/setup/modal/modal';
-import { PropType, ref } from 'vue';
-
-import { QueryHistoryTikDatasRsp, QueryQuoteDayRsp } from '@/services/go/quote/interface';
-import { QueryDeliveryRelationRsp } from '@/services/go/delivery/interface';
+import { PropType, ref, computed } from 'vue';
+import { QueryQuoteDayRsp, CycleType } from '@/services/go/quote/interface';
 import { QueryHistoryTikDatas } from '@/services/go/quote';
-import { WrTradeOrderDetailReq } from '@/services/go/wrtrade/interface';
-import { useQueryData } from '@/common/setup/request';
-import { queryWrTradeOrderDetail } from '@/services/go/wrtrade';
 import { formatTime } from '@/common/methods';
 import { ComponentType } from '../setup';
+import { EchartKline, EchartTime } from '@/common/components/echart';
+import { handleQuotePriceColor, quoteChange, quoteAmplitude } from '@/common/setup/table/tableQuote';
 
 export default defineComponent({
     emits: ['cancel', 'update'],
     name: 'stock-exchange',
+    components: {
+        EchartKline,
+        EchartTime,
+    },
     props: {
         selectedRow: {
             type: Object as PropType<QueryQuoteDayRsp>,
             default: {},
         },
     },
-
     setup(props, context) {
-        const loading = ref<boolean>(false);
         const { visible, cancel } = _closeModal(context);
-        const activeKey = ref('1');
+        const activeCycleType = ref<CycleType>(-1);
         function watchMore() {
             context.emit('update', ComponentType.tradeDetail);
         }
         const { goodscode } = props.selectedRow;
 
+        const priceColor = computed(() => {
+            return handleQuotePriceColor(props.selectedRow.last, props.selectedRow.presettle);
+        });
+
+        // 周期类型
+        const chartType = [
+            { name: '分时', type: -1 },
+            { name: '1分钟', type: 1 },
+            { name: '5分钟', type: 2 },
+            { name: '30分钟', type: 3 },
+            { name: '60分钟', type: 4 },
+            { name: '4小时', type: 240 },
+            { name: '日 K', type: 11 },
+        ];
+
+        // 选择图表周期类型
+        const selectCycleType = () => {
+            // console.log(activeCycleType.value);
+        };
+
         // 成交
-        const { list: tradedList } = useQueryData(QueryHistoryTikDatas, { goodsCode: goodscode });
+        // const { list: tradedList } = useQueryData(QueryHistoryTikDatas, { goodsCode: goodscode });
+
         return {
             cancel,
             visible,
-
-            tradedList,
-            loading,
-            activeKey,
+            chartType,
+            tradedList: [],
+            activeCycleType,
             watchMore,
             formatTime,
+            selectCycleType,
+            quoteChange,
+            quoteAmplitude,
+            priceColor,
         };
     },
 });
 </script>
+
 <style lang="less">
-.chart-container {
-    display: flex;
-    width: 100%;
-    height: calc(100% - 41px);
-    .chart-content {
-        flex: 1;
-        height: 100%;
-    }
-    .chart-tips {
-        width: 300px;
-        height: 100%;
-    }
-}
+@import './index.less';
 </style>

+ 60 - 75
src/views/market/spot_trade/spot_trade_reference_market/components/container/index.vue

@@ -1,83 +1,68 @@
 <template>
-  <!-- 买卖大厅 -->
-  <div class="buy-sell-market">
-    <div class="buy-sell-market-title">
-      <a class="backIcon"
-         @click="cancelAction">
-        <LeftOutlined />
-      </a>
-      <div class="titleBtn">
-        <div class="name">{{selectedRow.goodscode}} {{selectedRow.goodsname}}</div>
-        <div class="arrowRightIcon"></div>
-      </div>
-      <div class="inlineBar">
-        <div class="valNums bdf1 ml10">
-          <!-- 最新价 -->
-          <div class="firstNum start "
-               :class="handleQuotePriceColor(selectedRow.last, selectedRow.presettle)">
-            {{selectedRow.last}}</div>
-          <div class="
-               lastNum
-               start">
-            <!-- 涨跌值 -->
-            <div :class="handleQuotePriceColor(selectedRow.last, selectedRow.presettle)">
-              {{quoteChange(selectedRow, selectedRow.decimalplace)}}</div>
-            <!-- 涨跌幅 -->
-            <div class="ml20"
-                 :class="handleQuotePriceColor(selectedRow.last, selectedRow.presettle)">
-              {{quoteAmplituOfVibration(selectedRow, selectedRow.decimalplace)}}</div>
-          </div>
-        </div>
-        <div class="priceBar ml20">
-          <div class="inlineBar start">
-            <div class="greenBar green">
-              <div class="numBlock ml15">
-                <div class="first">卖价</div>
-                <div class="last"
-                     :class="handleQuotePriceColor(selectedRow.ask, selectedRow.presettle)">
-                  {{selectedRow.ask}}</div>
-              </div>
-              <div class="numBlock">
-                <div class="first">卖量</div>
-                <div class="last">{{selectedRow.askvolume}}</div>
-              </div>
-            </div>
-            <div class="greenBar green">
-              <div class="numBlock ml15">
-                <div class="first">卖价</div>
-                <div class="last">{{selectedRow.sellprice}}</div>
-              </div>
-              <div class="numBlock">
-                <div class="first">卖量</div>
-                <div class="last">{{selectedRow.sellqty}}</div>
-              </div>
+    <!-- 买卖大厅 -->
+    <div class="buy-sell-market">
+        <div class="buy-sell-market-title">
+            <a class="backIcon" @click="cancelAction">
+                <LeftOutlined />
+            </a>
+            <div class="titleBtn">
+                <div class="name">{{ selectedRow.goodscode }} {{ selectedRow.goodsname }}</div>
+                <div class="arrowRightIcon"></div>
             </div>
-          </div>
-          <div class="inlineBar start">
-            <div class="redBar red1">
-              <div class="numBlock">
-                <div class="first">买价</div>
-                <div class="last"
-                     :class="handleQuotePriceColor(selectedRow.bid, selectedRow.presettle)">
-                  {{selectedRow.bid}}</div>
-              </div>
-              <div class="numBlock">
-                <div class="first">买量</div>
-                <div class="last">{{selectedRow.bidvolume}}</div>
-              </div>
+            <div class="inlineBar">
+                <div class="valNums bdf1 ml10">
+                    <!-- 最新价 -->
+                    <div class="firstNum start" :class="handleQuotePriceColor(selectedRow.last, selectedRow.presettle)"> {{ selectedRow.last }}</div>
+                    <div class="lastNum start">
+                        <!-- 涨跌值 -->
+                        <div :class="handleQuotePriceColor(selectedRow.last, selectedRow.presettle)"> {{ quoteChange(selectedRow, selectedRow.decimalplace) }}</div>
+                        <!-- 涨跌幅 -->
+                        <div class="ml20" :class="handleQuotePriceColor(selectedRow.last, selectedRow.presettle)"> {{ quoteAmplituOfVibration(selectedRow, selectedRow.decimalplace) }}</div>
+                    </div>
+                </div>
+                <div class="priceBar ml20">
+                    <div class="inlineBar start">
+                        <div class="greenBar green">
+                            <div class="numBlock ml15">
+                                <div class="first">卖价</div>
+                                <div class="last" :class="handleQuotePriceColor(selectedRow.ask, selectedRow.presettle)"> {{ selectedRow.ask }}</div>
+                            </div>
+                            <div class="numBlock">
+                                <div class="first">卖量</div>
+                                <div class="last">{{ selectedRow.askvolume }}</div>
+                            </div>
+                        </div>
+                        <div class="greenBar green">
+                            <div class="numBlock ml15">
+                                <div class="first">卖价</div>
+                                <div class="last">{{ selectedRow.sellprice }}</div>
+                            </div>
+                            <div class="numBlock">
+                                <div class="first">卖量</div>
+                                <div class="last">{{ selectedRow.sellqty }}</div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="inlineBar start">
+                        <div class="redBar red1">
+                            <div class="numBlock">
+                                <div class="first">买价</div>
+                                <div class="last" :class="handleQuotePriceColor(selectedRow.bid, selectedRow.presettle)"> {{ selectedRow.bid }}</div>
+                            </div>
+                            <div class="numBlock">
+                                <div class="first">买量</div>
+                                <div class="last">{{ selectedRow.bidvolume }}</div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
             </div>
-          </div>
         </div>
-      </div>
+        <!-- 交易图表 -->
+        <Chart v-if="showComponentsId === ComponentType.chart" @update="changeComponent" :selectedRow="selectedRow" />
+        <!-- 成交明细 -->
+        <StockExchange :selectedRow="selectedRow" v-if="showComponentsId === ComponentType.tradeDetail" />
     </div>
-    <!-- 交易图表 -->
-    <Chart v-if="showComponentsId === ComponentType.chart"
-           @update="changeComponent"
-           :selectedRow="selectedRow" />
-    <!-- 成交明细 -->
-    <StockExchange :selectedRow="selectedRow"
-                   v-if="showComponentsId === ComponentType.tradeDetail" />
-  </div>
 </template>
 
 <script lang="ts">

+ 119 - 108
src/views/order/commodity_contract/components/commodity_contract_summary/components/commodity_contract_summary_deal_closed/index.vue

@@ -1,25 +1,26 @@
 <template>
-    <!-- 协议平仓-->
-    <Drawer
-        :title="'协议平仓'"
-        :placement="'right'"
-        class="bottom486"
-        :visible="visible"
-        @cancel="cancel"
-    >
-        <div class="listed c_c_s_s">
-            <a-form class="inlineForm dialogForm" ref="formRef">
-                <div class="formBar">
-                    <div class="formtop">
-                        <div class="firstTitle">
-                            <span>合约:{{selectedRow.goodscode}}/{{selectedRow.goodsname}}</span>
-                        </div>
-                        <div class="secondLine">
-                            <div class="left">持仓单号/方向</div>
-                            <div class="middle">数量/价格/金额</div>
-                            <div class="right">到期日/盈亏</div>
-                        </div>
-                        <!-- <a-checkbox-group class="commonCheckboxGroup"
+  <!-- 协议平仓-->
+  <Drawer :title="'协议平仓'"
+          :placement="'right'"
+          class="bottom486"
+          :visible="visible"
+          @cancel="cancel">
+    <div class="listed c_c_s_s">
+      <a-form class="inlineForm dialogForm"
+              ref="formRef"
+              :model="formState"
+              :rules="rules">
+        <div class="formBar">
+          <div class="formtop">
+            <div class="firstTitle">
+              <span>合约:{{selectedRow.goodscode}}/{{selectedRow.goodsname}}</span>
+            </div>
+            <div class="secondLine">
+              <div class="left">持仓单号/方向</div>
+              <div class="middle">数量/价格/金额</div>
+              <div class="right">到期日/盈亏</div>
+            </div>
+            <!-- <a-checkbox-group class="commonCheckboxGroup"
                               v-model:value="checked"
                               @change="checkGroupChange">
               <div class="lineBar"
@@ -57,79 +58,67 @@
                 </div>
               </div>
                         </a-checkbox-group>-->
-                        <a-checkbox-group class="commonCheckboxGroup">
-                            <div class="lineBar">
-                                <div class="line1">
-                                    <div class>
-                                        <a-checkbox></a-checkbox>
-                                    </div>
-                                    <div class="name">32266556655555</div>
-                                    <div class="date">2021-10-30</div>
-                                </div>
-                                <div class="line2">
-                                    <div class="left">买入</div>
-                                    <div class="middle">
-                                        <div>10</div>
-                                        <div>850.5</div>
-                                        <div>8505.5</div>
-                                    </div>
-                                    <div class="right red">+100</div>
-                                </div>
-                            </div>
-                            <div class="lineBar">
-                                <div class="line1">
-                                    <div class>
-                                        <a-checkbox></a-checkbox>
-                                    </div>
-                                    <div class="name">32266556655555</div>
-                                    <div class="date">2021-10-30</div>
-                                </div>
-                                <div class="line2">
-                                    <div class="left">买入</div>
-                                    <div class="middle">
-                                        <div>10</div>
-                                        <div>850.5</div>
-                                        <div>8505.5</div>
-                                    </div>
-                                    <div class="right red">+100</div>
-                                </div>
-                            </div>
-                        </a-checkbox-group>
-                    </div>
+            <a-checkbox-group class="commonCheckboxGroup"
+                              v-model:value="checked"
+                              @change="checkGroupChange">
+              <div class="lineBar"
+                   v-for="item in tableList"
+                   :key="item.tradeid">
+                <div class="line1">
+                  <div class>
+                    <a-checkbox @change="checkboxChange(item)"
+                                :value="item.tradeid"></a-checkbox>
+                  </div>
+                  <div class="name">{{item.tradeid}}</div>
+                  <div class="date">{{item.tradetime}}</div>
                 </div>
-                <div class="fixedBtns">
-                    <a-row :gutter="24">
-                        <a-col :span="24" class="mt12">
-                            <a-form-item label="协议价" name class="inputIconBox mb10">
-                                <a-input-number
-                                    class="commonInput"
-                                    style="width: 200px"
-                                    :min="0"
-                                    :max="100"
-                                />
-                                <MinusOutlined />
-                                <PlusOutlined />
-                            </a-form-item>
-                        </a-col>
-                        <a-col :span="24">
-                            <a-form-item label="平仓金额" class="mb10">
-                                <span class="white">50400.20</span>
-                            </a-form-item>
-                        </a-col>
-                    </a-row>
-                    <a-form-item class="btnCenter mt10">
-                        <a-button
-                            class="listedBtn"
-                            :loading="loading"
-                            :disabled="loading"
-                            @click="submit"
-                        >提交</a-button>
-                        <a-button class="ml10 cancelBtn">取消</a-button>
-                    </a-form-item>
+                <div class="line2">
+                  <div class="left">{{item.buyorsell === BuyOrSell.buy ? '买入' : '卖出'}}</div>
+                  <div class="middle">
+                    <div>{{item.holderqty}}</div>
+                    <div>{{item.holderprice}}</div>
+                    <div>{{item.holderamount}}</div>
+                  </div>
+                  <div class="right red">+100</div>
                 </div>
-            </a-form>
+              </div>
+            </a-checkbox-group>
+          </div>
         </div>
-    </Drawer>
+        <div class="fixedBtns">
+          <a-row :gutter="24">
+            <a-col :span="24"
+                   class="mt12">
+              <a-form-item label="协议价"
+                           name="price"
+                           class="inputIconBox mb10 not-copy">
+                <a-input-number class="commonInput not-copy"
+                                v-model:value="formState.price"
+                                style="width: 200px"
+                                :min="0" />
+                <MinusOutlined @click="decreasePrice" />
+                <PlusOutlined @click="increasePirce" />
+              </a-form-item>
+            </a-col>
+            <a-col :span="24">
+              <a-form-item label="平仓金额"
+                           class="mb10 not-copy ">
+                <span class="white">50400.20</span>
+              </a-form-item>
+            </a-col>
+          </a-row>
+          <a-form-item class="btnCenter mt10">
+            <a-button class="listedBtn"
+                      :loading="loading"
+                      :disabled="loading"
+                      @click="submit">提交</a-button>
+            <a-button class="ml10 cancelBtn"
+                      @click="cancel">取消</a-button>
+          </a-form-item>
+        </div>
+      </a-form>
+    </div>
+  </Drawer>
 </template>
 
 <script lang="ts">
@@ -160,8 +149,9 @@ import { queryTradeHolderDetail } from '@/services/go/order';
 import { QueryTradeHolderDetailReq } from '@/services/go/order/interface';
 import { requestResultLoadingAndInfo } from '@/common/methods/request/resultInfo';
 import { validateAction } from '@/common/setup/form';
-import { FormState } from './interface';
-import { handleForm } from './setup';
+import { FormState, BargainList } from './interface';
+import { handleForm, usePrice, useCheckd } from './setup';
+import { handleSubcriteQuote } from '@/common/setup/table/tableQuote';
 
 export default defineComponent({
     name: ModalEnum.commodity_contract_summary_settlement,
@@ -180,32 +170,45 @@ export default defineComponent({
     setup(props, context) {
         const { visible, cancel } = _closeModal(context);
         const { rules, formState, formRef } = handleForm();
+        // 选中逻辑
+        const { checked, selected, checkGroupChange, checkboxChange } = useCheckd();
+
         // 表格列表数据
-        const { loading, tableList, queryTable } = queryTableList<QueryTjmdTransferApplyRsp>();
+        const { loading, tableList, queryTable } = queryTableList<BargainList>();
         const param: QueryTradeHolderDetailReq = {
             buyorsell: props.selectedRow.buyorsell,
             userid: getUserId(),
             goodsid: props.selectedRow.goodsid,
         };
-        queryTable(queryTradeHolderDetail, param);
+        queryTable(queryTradeHolderDetail, param).then((res) => {
+            tableList.value = res.map((e, i) => {
+                if (i) {
+                    return { ...e, checked: false };
+                } else {
+                    // 默认勾选第一个
+                    checked.value = e.tradeid;
+                    const result = { ...e, checked: true };
+                    return result;
+                }
+            });
+        });
 
         const toFixed0 = (value: number) => +value.toFixed(0);
 
         function submit() {
-            // validateAction<FormState>(formRef, formState).then((res) => {
-            const item = tableList.value[0];
-            const param: TradeHoldTransferApplyReq = {
-                TradeID: Long.fromString(item.tradeid),
-                BuyorSell: item.buyorsell,
-                TransferPrice: 100,
-                ApplySrc: 2,
-                ApplicantID: geLoginID_number()!,
-                Remark: '',
-            };
-            requestResultLoadingAndInfo(tradeHoldTransferApply, param, loading, ['协议平仓成功', '协议平仓失败:']).then(() => {
-                cancel(true);
+            validateAction<FormState>(formRef, formState).then((res) => {
+                const param: TradeHoldTransferApplyReq = {
+                    TradeID: Long.fromString(selected.value!.tradeid),
+                    BuyorSell: selected.value!.buyorsell,
+                    TransferPrice: res.price,
+                    ApplySrc: 2,
+                    ApplicantID: geLoginID_number()!,
+                    Remark: '',
+                };
+                requestResultLoadingAndInfo(tradeHoldTransferApply, param, loading, ['协议平仓成功', '协议平仓失败:']).then(() => {
+                    cancel(true);
+                });
             });
-            // });
         }
         return {
             visible,
@@ -214,6 +217,14 @@ export default defineComponent({
             tableList,
             loading,
             toFixed0,
+            rules,
+            formState,
+            formRef,
+            ...usePrice(formState),
+            checked,
+            checkGroupChange,
+            checkboxChange,
+            BuyOrSell,
         };
     },
 });

+ 7 - 1
src/views/order/commodity_contract/components/commodity_contract_summary/components/commodity_contract_summary_deal_closed/interface.ts

@@ -1,4 +1,10 @@
+import { QueryTradeHolderDetailRsp } from "@/services/go/order/interface";
 
 export interface FormState {
-    num: number,
+    price: number,
 }
+
+
+export interface BargainList extends QueryTradeHolderDetailRsp {
+    checked: boolean
+}

+ 38 - 3
src/views/order/commodity_contract/components/commodity_contract_summary/components/commodity_contract_summary_deal_closed/setup.ts

@@ -1,7 +1,7 @@
 import { validateCommon } from "@/common/setup/validate";
 import { RuleObject } from "ant-design-vue/lib/form/interface";
 import { reactive, ref, UnwrapRef } from "vue";
-import { FormState } from "./interface";
+import { BargainList, FormState } from "./interface";
 
 export function handleForm() {
     const formRef = ref();
@@ -9,12 +9,47 @@ export function handleForm() {
         return validateCommon(value, '请输入协议价');
     };
     const formState: UnwrapRef<FormState> = reactive({
-        num: 0,
+        price: 0,
     })
     const rules = {
-        num: [
+        price: [
             { require, message: '请输入协议价', trigger: 'blur', type: 'number', validator: v_num },
         ],
     }
     return { rules, formState, formRef }
+}
+
+export function usePrice(formState: UnwrapRef<FormState>) {
+    function increasePirce() {
+        formState.price++
+    }
+    function decreasePrice() {
+        if (formState.price) {
+            formState.price--
+        }
+    }
+    return { increasePirce, decreasePrice }
+}
+
+export function useCheckd() {
+    const checked = ref<string>();
+    const selected = ref<BargainList>()
+    function checkGroupChange(checkedValue: string[]) {
+        checked.value = checkedValue[checkedValue.length - 1];
+    }
+    function checkboxChange(item: BargainList) {
+        selected.value = item;
+    }
+    return { checked, selected, checkGroupChange, checkboxChange }
+}
+
+// 计算盈亏
+export function useProfit(item: BargainList) {
+    const { buyorsell, holderprice, agreeunit, holderqty } = item
+    // let result = '--'
+    // if(buyorsell === BuyOrSell.buy) {
+
+    // } else {
+
+    // }
 }

+ 1 - 1
src/views/order/commodity_contract/components/commodity_contract_summary/index.vue

@@ -15,7 +15,7 @@
       <!-- 额外的展开行 -->
       <template v-if="btnList.length"
                 #expandedRowRender="{ record }">
-        <BtnList :btnList="handleBtnList(record, btnList)"
+        <BtnList :btnList="handleBtnList(record,btnList)"
                  :record="record"
                  class="btn-list-sticky"
                  @click="openComponent" />

Неке датотеке нису приказане због велике количине промена