li.shaoyi 2 سال پیش
والد
کامیت
21c8d3ea55
43فایلهای تغییر یافته به همراه2287 افزوده شده و 479 حذف شده
  1. 26 0
      file/天津麦顿.svg
  2. 20 60
      oem/gstj/config/router.json
  3. BIN
      oem/tjmd/app/splashscreen/1080x1920.png
  4. BIN
      oem/tjmd/app/splashscreen/480x853.png
  5. BIN
      oem/tjmd/app/splashscreen/720x1280.png
  6. BIN
      oem/tjmd/favicon.ico
  7. 2 0
      package.json
  8. 2 2
      public/config/appconfig.json
  9. 0 246
      src/components/base/echarts@next/@next.ts
  10. 2 2
      src/components/base/echarts@next/core.ts
  11. 397 0
      src/components/base/echarts@next/dataset.ts
  12. 22 5
      src/components/base/echarts@next/demo.vue
  13. 2 2
      src/components/base/echarts@next/index.vue
  14. 98 0
      src/components/base/echarts@next/options.ts
  15. 1212 0
      src/components/base/hqchart/index.less
  16. 2 1
      src/hooks/echarts/candlestick/dataset.ts
  17. 1 2
      src/hooks/echarts/candlestick/options.ts
  18. 2 2
      src/hooks/echarts/candlestick/types.ts
  19. 98 0
      src/hooks/echarts@next/line/index.ts
  20. 118 0
      src/hooks/echarts@next/line/options.ts
  21. 25 0
      src/hooks/echarts@next/line/types.ts
  22. 4 0
      src/packages/gstj/views/order/delivery/Index.vue
  23. 0 12
      src/packages/gstj/views/order/list/Index.vue
  24. 0 3
      src/packages/mobile/components/modules/echarts-line/index.less
  25. 0 55
      src/packages/mobile/components/modules/echarts-line/index.vue
  26. 3 0
      src/packages/mobile/components/modules/echarts/line/index.less
  27. 33 0
      src/packages/mobile/components/modules/echarts/line/index.vue
  28. 2 2
      src/packages/mobile/views/order/delivery/Index.vue
  29. 3 3
      src/packages/mobile/views/order/delivery/components/online/detail/Index.vue
  30. 1 1
      src/packages/mobile/views/order/delivery/components/online/list/Index.vue
  31. 16 12
      src/packages/mobile/views/swap/list/Index.vue
  32. 0 1
      src/packages/pc/main.ts
  33. 7 4
      src/packages/pc/views/footer/spot/position/index.vue
  34. 23 30
      src/packages/pc/views/market/trade/swap/index.vue
  35. 5 5
      src/packages/tc/views/order/delivery/Index.vue
  36. BIN
      src/packages/tjmd/assets/images/login-logo.png
  37. 95 9
      src/packages/tjmd/views/home/main/Index.vue
  38. 40 0
      src/packages/tjmd/views/home/main/index.less
  39. 9 16
      src/packages/tjmd/views/order/delivery/Index.vue
  40. 5 1
      src/packages/tjmd/views/user/login/Index.vue
  41. 9 0
      src/packages/tjmd/views/user/login/index.less
  42. 2 2
      src/packages/zrwyt/views/order/delivery/Index.vue
  43. 1 1
      src/services/http/index.ts

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 26 - 0
file/天津麦顿.svg


+ 20 - 60
oem/gstj/config/router.json

@@ -55,7 +55,8 @@
                         "sort": 3,
                         "title": "委托",
                         "code": "bottom_goods_order",
-                        "component": "views/footer/goods/order/index.vue"
+                        "component": "views/footer/goods/order/index.vue",
+                        "hidden": true
                     },
                     {
                         "authType": 2,
@@ -67,59 +68,16 @@
                     {
                         "authType": 2,
                         "sort": 5,
-                        "title": "交收",
-                        "code": "bottom_goods_delivery",
-                        "component": "views/footer/goods/delivery/index.vue"
-                    }
-                ]
-            },
-            {
-                "authType": 2,
-                "sort": 2,
-                "title": "预售转让",
-                "code": "bottom_presell",
-                "component": "views/footer/index.vue",
-                "hidden": true,
-                "children": [
-                    {
-                        "authType": 2,
-                        "sort": 1,
-                        "title": "预售认购",
-                        "code": "bottom_presell_presellposition",
-                        "component": "views/footer/presell/presellposition/index.vue",
-                        "children": []
-                    },
-                    {
-                        "authType": 2,
-                        "sort": 2,
-                        "title": "转让持仓",
-                        "code": "bottom_presell_transferposition",
-                        "component": "views/footer/presell/transferposition/index.vue",
-                        "children": []
-                    },
-                    {
-                        "authType": 2,
-                        "sort": 3,
-                        "title": "转让委托",
-                        "code": "bottom_presell_transferorder",
-                        "component": "views/footer/presell/transferorder/index.vue",
-                        "children": []
-                    },
-                    {
-                        "authType": 2,
-                        "sort": 4,
-                        "title": "转让成交",
-                        "code": "bottom_presell_transfertrader",
-                        "component": "views/footer/presell/transfertrader/index.vue",
-                        "children": []
+                        "title": "点选交收",
+                        "code": "bottom_presell_onlinedelivery",
+                        "component": "views/footer/presell/onlinedelivery/index.vue"
                     },
                     {
                         "authType": 2,
-                        "sort": 5,
-                        "title": "点选交收",
-                        "code": "bottom_presell_onlinedelivery",
-                        "component": "views/footer/presell/onlinedelivery/index.vue",
-                        "children": []
+                        "sort": 6,
+                        "title": "线下交收",
+                        "code": "bottom_goods_delivery",
+                        "component": "views/footer/goods/delivery/index.vue"
                     }
                 ]
             },
@@ -142,14 +100,16 @@
                         "sort": 2,
                         "title": "挂单",
                         "code": "bottom_spot_order",
-                        "component": "views/footer/spot/order/index.vue"
+                        "component": "views/footer/spot/order/index.vue",
+                        "hidden": true
                     },
                     {
                         "authType": 2,
                         "sort": 3,
                         "title": "成交",
                         "code": "bottom_spot_trade",
-                        "component": "views/footer/spot/trade/index.vue"
+                        "component": "views/footer/spot/trade/index.vue",
+                        "hidden": true
                     },
                     {
                         "authType": 2,
@@ -250,20 +210,20 @@
                                 "code": "market_trade_goods_50102",
                                 "component": "views/market/trade/goods/list/index.vue"
                             },
-							 {
+                            {
                                 "authType": 2,
                                 "sort": 2,
-                                "title": "现货挂牌",
-                                "code": "market_trade_spot",
-                                "component": "views/market/trade/spot/index.vue"
+                                "title": "协议转让",
+                                "code": "market_trade_goods_50101",
+                                "component": "views/market/trade/goods/list/index.vue"
                             },
                             {
                                 "authType": 2,
                                 "sort": 3,
-                                "title": "协议转让",
-                                "code": "market_trade_goods_50101",
+                                "title": "全款挂牌",
+                                "code": "market_trade_goods_16201",
                                 "component": "views/market/trade/goods/list/index.vue"
-                            }                           
+                            }
                         ]
                     }
                 ]

BIN
oem/tjmd/app/splashscreen/1080x1920.png


BIN
oem/tjmd/app/splashscreen/480x853.png


BIN
oem/tjmd/app/splashscreen/720x1280.png


BIN
oem/tjmd/favicon.ico


+ 2 - 0
package.json

@@ -14,6 +14,7 @@
     "dev:tc": "vue-cli-service serve --mode tc",
     "dev:tjmd": "vue-cli-service serve --mode tjmd",
     "dev:tjmd@pc": "vue-cli-service serve --mode tjmd@pc",
+    "dev:gstj@pc": "vue-cli-service serve --mode gstj@pc",
     "build:mobile": "set \"NODE_ENV=production\" && vue-cli-service build --mode mobile",
     "build:pc": "set \"NODE_ENV=production\" && vue-cli-service build --mode pc",
     "build:gstj": "set \"NODE_ENV=production\" && vue-cli-service build --mode gstj",
@@ -27,6 +28,7 @@
     "build:qdhs@pc": "set \"NODE_ENV=production\" && vue-cli-service build --mode qdhs@pc",
     "build:tc": "set \"NODE_ENV=production\" && vue-cli-service build --mode tc",
     "build:tc@pc": "set \"NODE_ENV=production\" && vue-cli-service build --mode tc@pc",
+    "build:tjmd": "set \"NODE_ENV=production\" && vue-cli-service build --mode tjmd",
     "build:tjmd@pc": "set \"NODE_ENV=production\" && vue-cli-service build --mode tjmd@pc",
     "lint": "vue-cli-service lint",
     "dev": "electron .",

+ 2 - 2
public/config/appconfig.json

@@ -1,10 +1,10 @@
 {
   "appId": "com.muchinfo.app",
-  "appName": "多元世纪",
+  "appName": "多元世纪交易中心",
   "version": "1.0.5",
   "versionCode": "10005",
   "apiUrl": "http://192.168.31.204:8080/cfg?key=test_204",
-  "tradeChannel": "http",
+  "tradeChannel": "ws",
   "modules": [
     "register",
     "delivery"

+ 0 - 246
src/components/base/echarts@next/@next.ts

@@ -1,246 +0,0 @@
-import { reactive, toRefs, computed } from 'vue'
-import { queryHistoryDatas } from '@/services/api/quote'
-import { ChartCycleType } from '@/constants/chart'
-import { echarts, ECOption } from '@/components/base/echarts/core'
-import { useFuturesStore } from '@/stores'
-import { useDataset } from '@/hooks/echarts/candlestick/dataset'
-import moment from 'moment'
-
-export function useCandlestick(goodsCode: string) {
-    const { dataset, handleData, clearData } = useDataset()
-    const { quoteWatch } = useFuturesStore()
-
-    const state = reactive({
-        loading: false,
-        dataIndex: -1,
-        cycleType: ChartCycleType.Minutes
-    })
-
-    // 当前选中的数据项
-    const selectedItem = computed(() => {
-        if (state.dataIndex > -1 && dataset.candlestick.source.length > state.dataIndex) {
-            const { open, close, highest, lowest, ma5, ma10, ma15 } = dataset.candlestick.source[state.dataIndex]
-            const { macd, dif, dea } = dataset.macd.source[state.dataIndex]
-            const { vol } = dataset.vol.source[state.dataIndex]
-            const { k, d, j } = dataset.kdj.source[state.dataIndex]
-            const { cci } = dataset.cci.source[state.dataIndex]
-            return { open, close, highest, lowest, ma5, ma10, ma15, macd, dif, dea, vol, k, d, j, cci }
-        } else {
-            return { open: '--', close: '--', highest: '--', lowest: '--', ma5: '--', ma10: '--', ma15: '--', macd: '--', dif: '--', dea: '--', vol: '--', k: '--', d: '--', j: '--', cci: '--' }
-        }
-    })
-
-    // 获取历史行情
-    const getHistoryData = async () => {
-        try {
-            clearData()
-            state.dataIndex = -1
-            state.loading = true
-            const res = await queryHistoryDatas({
-                data: {
-                    goodsCode,
-                    cycleType: state.cycleType,
-                    count: 1440,
-                }
-            })
-            // 日期升序排序
-            const data = res.data.sort((a, b) => moment(a.ts).valueOf() - moment(b.ts).valueOf())
-            state.dataIndex = data.length - 1
-            handleData(data)
-        } finally {
-            state.loading = false
-        }
-    }
-
-    let chartInstance: echarts.ECharts
-    let asyncHistoryData = getHistoryData()
-
-    const createCandlestick = (chart: echarts.ECharts) => {
-        chartInstance = chart
-        asyncHistoryData.then(() => {
-            const { source, dimensions } = dataset.candlestick
-            const opt: ECOption = {
-                grid: [
-                    {
-                        top: '0',
-                        height: "70%"
-                    },
-                    {
-                        top: '70%',
-                        height: "18%"
-                    }
-                ],
-                xAxis: [
-                    {
-                        type: 'category',
-                        axisLabel: {
-                            formatter: (val: string) => {
-                                if (val) {
-                                    switch (state.cycleType) {
-                                        case ChartCycleType.Day: {
-                                            return moment(val).format('YYYY-MM-DD')
-                                        }
-                                        default: {
-                                            return moment(val).format('HH:mm')
-                                        }
-                                    }
-                                }
-                                return ''
-                            }
-                        }
-                    },
-                    {
-                        type: 'category',
-                        gridIndex: 1
-                    }
-                ],
-                yAxis: [
-                    {
-                        type: 'value',
-                        scale: true,
-                    },
-                    {
-                        type: 'value',
-                        scale: true,
-                        gridIndex: 1
-                    }
-                ],
-                dataZoom: {
-                    type: 'inside',
-                    startValue: source.length - 60, // 起始显示K线条数(最新60条)
-                    end: 100,
-                    minValueSpan: 30, // 限制窗口缩放显示最少数据条数
-                    maxValueSpan: 200, // 限制窗口缩放显示最大数据条数
-                    xAxisIndex: [0, 1]
-                },
-                dataset: {
-                    dimensions,
-                    source,
-                },
-                series: [
-                    {
-                        name: 'KLine',
-                        type: 'candlestick'
-                    },
-                    {
-                        name: 'MACD',
-                        type: 'bar',
-                        xAxisIndex: 1,
-                        yAxisIndex: 1,
-                    }
-                ]
-            }
-            chart.setOption(opt)
-        })
-    }
-
-    const initChart = (cycleType: ChartCycleType) => {
-        state.cycleType = cycleType
-        asyncHistoryData = getHistoryData()
-    }
-
-    /**
-     * 格式化日期
-     * @param value 
-     * @returns 
-     */
-    const formatDate = (value: string) => {
-        switch (state.cycleType) {
-            case ChartCycleType.Day: {
-                return moment(value).format('YYYY-MM-DD')
-            }
-            case ChartCycleType.Hours2:
-            case ChartCycleType.Hours4: {
-                return moment(value).format('YYYY-MM-DD HH:00:00')
-            }
-            default: {
-                return moment(value).format('YYYY-MM-DD HH:mm:00')
-            }
-        }
-    }
-
-    quoteWatch(goodsCode, (q) => {
-        if (chartInstance) {
-            asyncHistoryData.then(() => {
-                const { last, lasttime } = q
-                if (last && lasttime) {
-                    const { candlestick, macd, vol, kdj, cci } = dataset
-                    const lastIndex = candlestick.source.length - 1 // 历史数据最后索引位置
-
-                    const oldTime = lastIndex === -1 ? moment(lasttime) : moment(candlestick.source[lastIndex].date) // 历史行情最后时间
-                    const diffTime = moment(lasttime).valueOf() - oldTime.valueOf() // 计算时间差
-
-
-                    // 判断时间差是否大于周期时间
-                    if (lastIndex === -1 || diffTime > 60 * 1000) {
-                        const newtime = formatDate(lasttime)
-                        // 新增K线数据
-                        candlestick.source.push({
-                            date: newtime,
-                            open: last,
-                            close: last,
-                            lowest: last,
-                            highest: last,
-                            ma5: '-',
-                            ma10: '-',
-                            ma15: '-',
-                        })
-
-                        // 新增MACD数据
-                        macd.source.push({
-                            date: newtime,
-                            ema12: 0,
-                            ema26: 0,
-                            dif: 0,
-                            dea: 0,
-                            macd: 0,
-                        })
-
-                        // 新增VOL数据
-                        vol.source.push({
-                            date: newtime,
-                            vol: 0,
-                        })
-
-                        // 新增KDJ数据
-                        kdj.source.push({
-                            date: newtime,
-                            k: '-',
-                            d: '-',
-                            j: '-',
-                        })
-
-                        // 新增CCI数据
-                        cci.source.push({
-                            date: newtime,
-                            cci: '-',
-                        })
-                    } else {
-                        // 更新列表中最后一条记录的数据
-                        const record = candlestick.source[lastIndex]
-                        if (record.lowest > last) {
-                            record.lowest = last // 更新最低价
-                        }
-                        if (record.highest < last) {
-                            record.highest = last // 更新最高价
-                        }
-                        record.close = last // 更新收盘价
-                    }
-
-                    // chartInstance.setOption({
-                    //     dataset: {
-                    //         source: candlestick.source
-                    //     }
-                    // })
-                }
-            })
-        }
-    })
-
-    return {
-        ...toRefs(state),
-        selectedItem,
-        createCandlestick,
-        initChart
-    }
-}

+ 2 - 2
src/components/base/echarts@next/core.ts

@@ -3,7 +3,7 @@
  */
 import * as echarts from 'echarts/core'
 import { CandlestickChart, CandlestickSeriesOption, BarChart, BarSeriesOption, LineChart, LineSeriesOption } from 'echarts/charts'
-import { MarkLineComponent, DatasetComponent, DataZoomComponent, GridComponent, GridComponentOption, TooltipComponent, DataZoomComponentOption } from 'echarts/components'
+import { MarkLineComponent, DatasetComponent, DataZoomComponent, GridComponent, GridComponentOption, TooltipComponent, DataZoomComponentOption, DatasetComponentOption } from 'echarts/components'
 import { CanvasRenderer } from 'echarts/renderers'
 
 echarts.use([
@@ -18,7 +18,7 @@ echarts.use([
     TooltipComponent
 ])
 
-type ECOption = echarts.ComposeOption<CandlestickSeriesOption | BarSeriesOption | LineSeriesOption | GridComponentOption | DataZoomComponentOption>
+type ECOption = echarts.ComposeOption<CandlestickSeriesOption | BarSeriesOption | LineSeriesOption | GridComponentOption | DataZoomComponentOption | DatasetComponentOption>
 
 export {
     echarts,

+ 397 - 0
src/components/base/echarts@next/dataset.ts

@@ -0,0 +1,397 @@
+import { reactive, toRefs, computed } from 'vue'
+import { queryHistoryDatas } from '@/services/api/quote'
+import { ChartCycleType } from '@/constants/chart'
+import { EchartsDataset, Candlestick, MACD } from '@/hooks/echarts/candlestick/types'
+import moment from 'moment'
+
+export function useDataset() {
+    const dataset: EchartsDataset = {
+        invalid: [],
+        decimal: 0,
+        cycleType: ChartCycleType.Minutes,
+        candlestick: {
+            dimensions: ['date', 'open', 'close', 'lowest', 'highest', 'ma5', 'ma10', 'ma15'],
+            source: [],
+        },
+        macd: {
+            dimensions: ['date', 'macd', 'dif', 'dea'],
+            source: [],
+        },
+        vol: {
+            dimensions: ['date', 'vol'],
+            source: [],
+        },
+        kdj: {
+            dimensions: ['date', 'k', 'd', 'j'],
+            source: [],
+        },
+        cci: {
+            dimensions: ['date', 'cci'],
+            source: [],
+        }
+    }
+
+    // 清空数据
+    const clearData = () => {
+        dataset.invalid = [];
+        dataset.candlestick.source = [];
+        dataset.macd.source = [];
+        dataset.vol.source = [];
+        dataset.kdj.source = [];
+        dataset.cci.source = [];
+    }
+
+    // 处理行情数据
+    const handleData = (rawData: Model.HistoryDatasRsp[]) => {
+        for (let i = 0; i < rawData.length; i++) {
+            const { o, c, h, l, ts: date, f, tv } = rawData[i];
+            if (f) dataset.invalid.push(i); // 添加补充数据的索引位置
+
+            dataset.candlestick.source.push({
+                date,
+                open: o,
+                close: c,
+                lowest: l,
+                highest: h,
+                ma5: '-',
+                ma10: '-',
+                ma15: '-',
+            })
+
+            dataset.macd.source.push({
+                date,
+                ema12: 0,
+                ema26: 0,
+                dif: 0,
+                dea: 0,
+                macd: 0,
+            })
+
+            dataset.vol.source.push({
+                date,
+                vol: tv,
+            })
+
+            dataset.kdj.source.push({
+                date,
+                k: '-',
+                d: '-',
+                j: '-'
+            })
+
+            dataset.cci.source.push({
+                date,
+                cci: '-',
+            })
+        }
+        // 计算各种指标
+        calcIndicator()
+    }
+
+    // 计算MA
+    const calcMA = (count: 5 | 10 | 15, startIndex = 0) => {
+        const key: keyof Candlestick = count === 5 ? 'ma5' : count === 10 ? 'ma10' : 'ma15';
+        const candlestickSource = dataset.candlestick.source;
+
+        for (let i = startIndex; i < candlestickSource.length; i++) {
+            // 判断是否补充数据
+            if (dataset.invalid.includes(i)) {
+                const value = i > 0 ? candlestickSource[i - 1][key] : '-'; // 如果存在,取上一条记录的MA值
+                candlestickSource[i][key] = value;
+            } else {
+                let n = 0;
+                const tmpList: Candlestick[] = [];
+
+                for (let j = i; j >= 0; j--) {
+                    if (dataset.invalid.includes(j)) continue; // 如果是补充数据,跳过本次循环
+                    if (n === count) break; // 如果 n 等于计数,结束循环
+                    tmpList.push(candlestickSource[j]);
+                    n++;
+                }
+
+                if (n === count) {
+                    // 计算总价(收盘价总和)
+                    const total = tmpList.reduce((res, e) => res + e.close, 0);
+                    // 计算均价
+                    candlestickSource[i][key] = (total / count).toFixed(2);
+                } else {
+                    candlestickSource[i][key] = '-';
+                }
+            }
+        }
+    }
+
+    // 计算EMA
+    const calcEMA = (count: 12 | 26, startIndex = 0) => {
+        const key: keyof MACD = count === 12 ? 'ema12' : 'ema26';
+        const macdSource = dataset.macd.source;
+        const candlestickSource = dataset.candlestick.source;
+        const a = 2 / (count + 1); // 平滑系数
+
+        for (let i = startIndex; i < candlestickSource.length; i++) {
+            const close = candlestickSource[i].close; // 收盘价
+
+            if (i === 0) {
+                macdSource[i][key] = close; // 初始EMA取收盘价
+            } else {
+                const prevEMA = macdSource[i - 1][key]; // 昨日EMA
+                const value = a * close + (1 - a) * Number(prevEMA);
+
+                macdSource[i][key] = value;
+            }
+        }
+    }
+
+    // 计算MACD
+    const calcMACD = (startIndex = 0) => {
+        // 先计算EMA
+        calcEMA(12, startIndex); // EMA(12) = 2 ÷ 13 * 今日收盘价(12) + 11 ÷ 13 * 昨日EMA(12)
+        calcEMA(26, startIndex);
+
+        const macdSource = dataset.macd.source;
+
+        for (let i = startIndex; i < macdSource.length; i++) {
+            const { ema12, ema26 } = macdSource[i];
+            const prevDEA = i > 0 ? macdSource[i - 1].dea : 0; // 昨日DEA
+
+            const dif = ema12 - ema26; // DIF = 今日EMA(12) - 今日EMA(26)
+            const dea = prevDEA * 8 / 10 + dif * 2 / 10; // DEA = 昨日DEA × 8 ÷ 10 + 今日DIF × 2 ÷ 10
+            const macd = (dif - dea) * 2; // MACD = (DIFF - DEA) × 2
+
+            macdSource[i].dif = Number(dif.toFixed(2));
+            macdSource[i].dea = Number(dea.toFixed(2));
+            macdSource[i].macd = Number(macd.toFixed(2));
+        }
+    }
+
+    // 计算KDJ
+    const calcKDJ = (startIndex = 0) => {
+        const count = 9; // 以9日周期计算
+        const candlestickSource = dataset.candlestick.source;
+        const kdjSource = dataset.kdj.source;
+
+        for (let i = startIndex; i < candlestickSource.length; i++) {
+            let n = 0;
+            const tmpList: Candlestick[] = [];
+
+            for (let j = i; j >= 0; j--) {
+                if (n === count) break; // 如果 n 等于计数,结束循环
+                tmpList.push(candlestickSource[j]);
+                n++;
+            }
+
+            if (n === count) {
+                const close = candlestickSource[i].close, // 收盘价
+                    n9 = tmpList.map((item) => item.close),
+                    h9 = Math.max(...n9), // 9天内最高价
+                    l9 = Math.min(...n9); // 9天内最低价
+
+                const rsv = (close - l9) / (h9 - l9) * 100, // RSV = (Ct-L9) ÷ (H9-L9) * 100
+                    prevK = Number(kdjSource[i - 1].k), // 昨日K值
+                    prevD = Number(kdjSource[i - 1].d); // 昨日D值
+
+                const kValue = isNaN(prevK) ? 50 : prevK; // 若无昨日K值,则可用50来代替
+                const dValue = isNaN(prevD) ? 50 : prevD; // 若无昨日D值,则可用50来代替
+
+                const k = (2 / 3) * kValue + (1 / 3) * rsv, // K = 2 ÷ 3 × 昨日K值 + 1 ÷ 3 × RSV
+                    d = (2 / 3) * dValue + (1 / 3) * k, // D = 2 ÷ 3 × 昨日D值 + 1 ÷ 3 × K值
+                    j = 3 * k - 2 * d; // J = 3 * K值 - 2 * D值
+
+                kdjSource[i].k = k.toFixed(2);
+                kdjSource[i].d = d.toFixed(2);
+                kdjSource[i].j = j.toFixed(2);
+            }
+        }
+    }
+
+    // 计算CCI
+    const calcCCI = (startIndex = 0) => {
+        const count = 14; // 以14日周期计算
+        const candlestickSource = dataset.candlestick.source;
+        const cciSource = dataset.cci.source;
+
+        for (let i = startIndex; i < candlestickSource.length; i++) {
+            let n = 0;
+            const tmpList: Candlestick[] = [];
+
+            for (let j = i; j >= 0; j--) {
+                if (n === count) break; // 如果 n 等于计数,结束循环
+                tmpList.push(candlestickSource[j]);
+                n++;
+            }
+
+            if (n === count) {
+                const calcTP = (item: Candlestick) => (item.highest + item.lowest + item.close) / 3; // TP = (最高价 + 最低价 + 收盘价) ÷ 3
+                const tp = calcTP(candlestickSource[i]),
+                    ma = tmpList.reduce((res, e) => res + calcTP(e), 0) / count, // MA = 近N日TP的累计之和 ÷ N
+                    md = tmpList.reduce((res, e) => res + Math.abs(calcTP(e) - ma), 0) / count; // MD = 近N日TP的绝对值的累计之和 ÷ N
+
+                const cci = (tp - ma) / (md * 0.015);
+                cciSource[i].cci = cci.toFixed(2);
+            }
+        }
+    }
+
+    // 计算各项指标
+    const calcIndicator = (startIndex = 0) => {
+        calcMA(5, startIndex);
+        calcMA(10, startIndex);
+        calcMA(15, startIndex);
+        calcMACD(startIndex);
+        calcKDJ(startIndex);
+        calcCCI(startIndex);
+    }
+
+    return {
+        dataset,
+        handleData,
+        clearData,
+        calcIndicator,
+    }
+}
+
+export function useCandlestick(goodsCode: string) {
+    const { dataset, handleData, clearData } = useDataset()
+
+    const state = reactive({
+        loading: false,
+        dataIndex: -1
+    })
+
+    // 当前选中的数据项
+    const selectedItem = computed(() => {
+        if (state.dataIndex > -1 && dataset.candlestick.source.length > state.dataIndex) {
+            const { open, close, highest, lowest, ma5, ma10, ma15 } = dataset.candlestick.source[state.dataIndex]
+            const { macd, dif, dea } = dataset.macd.source[state.dataIndex]
+            const { vol } = dataset.vol.source[state.dataIndex]
+            const { k, d, j } = dataset.kdj.source[state.dataIndex]
+            const { cci } = dataset.cci.source[state.dataIndex]
+            return { open, close, highest, lowest, ma5, ma10, ma15, macd, dif, dea, vol, k, d, j, cci }
+        } else {
+            return { open: '--', close: '--', highest: '--', lowest: '--', ma5: '--', ma10: '--', ma15: '--', macd: '--', dif: '--', dea: '--', vol: '--', k: '--', d: '--', j: '--', cci: '--' }
+        }
+    })
+
+    // 获取历史行情
+    const getHistoryData = async () => {
+        try {
+            clearData()
+            state.dataIndex = -1
+            state.loading = true
+            const res = await queryHistoryDatas({
+                data: {
+                    goodsCode,
+                    cycleType: dataset.cycleType,
+                    count: 1440,
+                }
+            })
+            // 日期升序排序
+            const data = res.data.sort((a, b) => moment(a.ts).valueOf() - moment(b.ts).valueOf())
+            state.dataIndex = data.length - 1
+            handleData(data)
+            return dataset
+        } finally {
+            state.loading = false
+        }
+    }
+
+    let onLoadData = getHistoryData()
+
+    const initData = (cycleType: ChartCycleType) => {
+        dataset.cycleType = cycleType
+        onLoadData = getHistoryData()
+    }
+
+    // 格式化日期
+    const formatDate = (value: string) => {
+        switch (dataset.cycleType) {
+            case ChartCycleType.Day: {
+                return moment(value).format('YYYY-MM-DD')
+            }
+            case ChartCycleType.Hours2:
+            case ChartCycleType.Hours4: {
+                return moment(value).format('YYYY-MM-DD HH:00:00')
+            }
+            default: {
+                return moment(value).format('YYYY-MM-DD HH:mm:00')
+            }
+        }
+    }
+
+    const updateData = (q: Partial<Model.QuoteDayRsp>) => {
+        const { last, lasttime } = q
+        if (last && lasttime) {
+            const { candlestick, macd, vol, kdj, cci } = dataset
+            const lastIndex = candlestick.source.length - 1 // 历史数据最后索引位置
+
+            const oldTime = lastIndex === -1 ? moment(lasttime) : moment(candlestick.source[lastIndex].date) // 历史行情最后时间
+            const diffTime = moment(lasttime).valueOf() - oldTime.valueOf() // 计算时间差
+
+
+            // 判断时间差是否大于周期时间
+            if (lastIndex === -1 || diffTime > 60 * 1000) {
+                const newtime = formatDate(lasttime)
+                // 新增K线数据
+                candlestick.source.push({
+                    date: newtime,
+                    open: last,
+                    close: last,
+                    lowest: last,
+                    highest: last,
+                    ma5: '-',
+                    ma10: '-',
+                    ma15: '-',
+                })
+
+                // 新增MACD数据
+                macd.source.push({
+                    date: newtime,
+                    ema12: 0,
+                    ema26: 0,
+                    dif: 0,
+                    dea: 0,
+                    macd: 0,
+                })
+
+                // 新增VOL数据
+                vol.source.push({
+                    date: newtime,
+                    vol: 0,
+                })
+
+                // 新增KDJ数据
+                kdj.source.push({
+                    date: newtime,
+                    k: '-',
+                    d: '-',
+                    j: '-',
+                })
+
+                // 新增CCI数据
+                cci.source.push({
+                    date: newtime,
+                    cci: '-',
+                })
+            } else {
+                // 更新列表中最后一条记录的数据
+                const record = candlestick.source[lastIndex]
+                if (record.lowest > last) {
+                    record.lowest = last // 更新最低价
+                }
+                if (record.highest < last) {
+                    record.highest = last // 更新最高价
+                }
+                record.close = last // 更新收盘价
+            }
+        }
+        return dataset
+    }
+
+    return {
+        ...toRefs(state),
+        onLoadData,
+        selectedItem,
+        initData,
+        updateData
+    }
+}

+ 22 - 5
src/components/base/echarts@next/demo.vue

@@ -4,9 +4,11 @@
 
 <script lang="ts" setup>
 import { PropType, watch } from 'vue'
-import { echarts } from '@/components/base/echarts/core'
 import { ChartCycleType } from '@/constants/chart'
-import { useCandlestick } from './@next'
+import { useFuturesStore } from '@/stores'
+import { useCandlestick } from './dataset'
+import { echarts } from './core'
+import { useOptions } from './options'
 import AppEcharts from './index.vue'
 
 const props = defineProps({
@@ -26,15 +28,30 @@ const props = defineProps({
     },
 })
 
-const { initChart, createCandlestick, dataIndex } = useCandlestick('XAUUSD')
+let chartInstance: echarts.ECharts // 图表实例
+const { quoteWatch } = useFuturesStore()
+const { loading, onLoadData, dataIndex, initData, updateData } = useCandlestick('XAUUSD')
+const { getOption, updateOption } = useOptions()
 
 const onReady = (chart: echarts.ECharts) => {
-    createCandlestick(chart)
+    chartInstance = chart
+    onLoadData.then((data) => {
+        const option = getOption(data)
+        chartInstance.setOption(option, true)
+    })
 }
 
+quoteWatch(props.goodsCode, (q) => {
+    if (!loading.value) {
+        const data = updateData(q)
+        const option = updateOption(data)
+        chartInstance.setOption(option)
+    }
+})
+
 // 监听周期选择变化
 watch(() => props.cycleType, (val) => {
-    initChart(val)
+    initData(val)
 })
 </script>
 

+ 2 - 2
src/components/base/echarts@next/index.vue

@@ -12,12 +12,12 @@ const props = defineProps({
   // 图表宽度
   width: {
     type: String,
-    default: '1000px',
+    default: '100%',
   },
   // 图表高度
   height: {
     type: String,
-    default: '600px',
+    default: '100%',
   },
   // 图表配置项
   option: {

+ 98 - 0
src/components/base/echarts@next/options.ts

@@ -0,0 +1,98 @@
+import { ChartCycleType } from '@/constants/chart'
+import { ECOption } from '@/components/base/echarts/core'
+import { EchartsDataset, } from '@/hooks/echarts/candlestick/types'
+import moment from 'moment'
+
+export function useOptions() {
+    const getOption = (dataset: EchartsDataset) => {
+        const { source, dimensions } = dataset.candlestick
+        const option: ECOption = {
+            grid: [
+                {
+                    top: '0',
+                    height: "70%"
+                },
+                {
+                    top: '70%',
+                    height: "18%"
+                }
+            ],
+            xAxis: [
+                {
+                    type: 'category',
+                    axisLabel: {
+                        formatter: (val: string) => {
+                            if (val) {
+                                switch (dataset.cycleType) {
+                                    case ChartCycleType.Day: {
+                                        return moment(val).format('YYYY-MM-DD')
+                                    }
+                                    default: {
+                                        return moment(val).format('HH:mm')
+                                    }
+                                }
+                            }
+                            return ''
+                        }
+                    }
+                },
+                {
+                    type: 'category',
+                    gridIndex: 1
+                }
+            ],
+            yAxis: [
+                {
+                    type: 'value',
+                    scale: true,
+                },
+                {
+                    type: 'value',
+                    scale: true,
+                    gridIndex: 1
+                }
+            ],
+            dataZoom: {
+                type: 'inside',
+                startValue: source.length - 60, // 起始显示K线条数(最新60条)
+                end: 100,
+                minValueSpan: 30, // 限制窗口缩放显示最少数据条数
+                maxValueSpan: 200, // 限制窗口缩放显示最大数据条数
+                xAxisIndex: [0, 1]
+            },
+            dataset: {
+                dimensions,
+                source,
+            },
+            series: [
+                {
+                    name: 'KLine',
+                    type: 'candlestick'
+                },
+                {
+                    name: 'MACD',
+                    type: 'bar',
+                    xAxisIndex: 1,
+                    yAxisIndex: 1,
+                }
+            ]
+        }
+        return option
+    }
+
+    const updateOption = (dataset: EchartsDataset) => {
+        const { source, dimensions } = dataset.candlestick
+        const option: ECOption = {
+            dataset: {
+                dimensions,
+                source,
+            },
+        }
+        return option
+    }
+
+    return {
+        getOption,
+        updateOption
+    }
+}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1212 - 0
src/components/base/hqchart/index.less


+ 2 - 1
src/hooks/echarts/candlestick/dataset.ts

@@ -4,7 +4,8 @@ import { EchartsDataset, Candlestick, MACD } from './types'
 export function useDataset() {
     const dataset = reactive<EchartsDataset>({
         invalid: [],
-        precision: 0,
+        decimal: 0,
+        cycleType: 1,
         candlestick: {
             dimensions: ['date', 'open', 'close', 'lowest', 'highest', 'ma5', 'ma10', 'ma15'],
             source: [],

+ 1 - 2
src/hooks/echarts/candlestick/options.ts

@@ -151,7 +151,6 @@ export function useOptions(dataset: EchartsDataset) {
                 {
                     name: 'K线',
                     type: 'candlestick',
-                    barWidth: '80%',
                     itemStyle: {
                         color: upColor,
                         color0: downColor,
@@ -245,7 +244,7 @@ export function useOptions(dataset: EchartsDataset) {
                     name: 'MACD',
                     type: 'bar',
                     sampling: 'average',
-                    barWidth: '60%',
+                    barWidth: '25%',
                     itemStyle: {
                         color: ({ data }) => {
                             const { macd } = data as { macd: number };

+ 2 - 2
src/hooks/echarts/candlestick/types.ts

@@ -1,4 +1,3 @@
-
 import { ECOption } from '@/components/base/echarts/core'
 
 /**
@@ -6,7 +5,8 @@ import { ECOption } from '@/components/base/echarts/core'
  */
 export interface EchartsDataset {
     invalid: number[]; // 行情历史数据中所有补充数据的索引位置(用于指标计算)
-    precision: number; // 小数位精度
+    decimal: number; // 小数位精度
+    cycleType: number; // 周期类型
     candlestick: Dataset<Candlestick>;
     macd: Dataset<MACD>;
     vol: Dataset<VOL>;

+ 98 - 0
src/hooks/echarts@next/line/index.ts

@@ -0,0 +1,98 @@
+import { reactive, toRefs } from 'vue'
+import { queryHistoryDatas } from '@/services/api/quote'
+import { ChartCycleType } from '@/constants/chart'
+import { EchartsDataset } from './types'
+import { useFuturesStore } from '@/stores'
+import moment from 'moment'
+
+export function useLineChart(goodsCode: string) {
+    const futuresStore = useFuturesStore()
+    const goods = futuresStore.getGoods(goodsCode)
+
+    const dataset: EchartsDataset = {
+        line: {
+            dimensions: ['date', 'price'],
+            source: {
+                date: [],
+                price: [],
+            },
+        },
+        decimal: goods?.decimalplace ?? 2,
+        yAxisMin: (value) => value.min,
+        yAxisMax: (value) => value.max,
+    }
+
+    const state = reactive({
+        loading: false,
+        dataIndex: -1
+    })
+
+    // 清空数据
+    const clearData = () => {
+        dataset.line.source = {
+            date: [],
+            price: []
+        }
+    }
+
+    // 获取历史行情
+    const getHistoryData = async () => {
+        try {
+            clearData()
+            state.dataIndex = -1
+            state.loading = true
+            const res = await queryHistoryDatas({
+                data: {
+                    goodsCode,
+                    cycleType: ChartCycleType.Day
+                }
+            })
+            // 日期升序排序
+            const data = res.data.sort((a, b) => moment(a.ts).valueOf() - moment(b.ts).valueOf())
+            data.forEach((e) => {
+                const date = moment(e.ts).format('YYYY-MM-DD')
+                dataset.line.source.date.push(date)
+                dataset.line.source.price.push(e.c)
+            })
+            state.dataIndex = data.length - 1
+            return dataset
+        } finally {
+            state.loading = false
+        }
+    }
+
+    let onLoadData = getHistoryData()
+
+    const initData = () => {
+        onLoadData = getHistoryData()
+    }
+
+    const updateData = (q: Partial<Model.QuoteDayRsp>) => {
+        const { last, lasttime } = q
+        if (last && lasttime) {
+            const { price, date } = dataset.line.source
+            const lastIndex = price.length - 1 // 历史数据最后索引位置
+
+            const oldTime = lastIndex === -1 ? moment(lasttime) : moment(date[lastIndex]) // 历史行情最后时间
+            const diffTime = moment(lasttime).valueOf() - oldTime.valueOf() // 计算时间差
+
+            // 判断时间差是否大于周期时间
+            if (lastIndex === -1 || diffTime > 60 * 1000 * 24 * 60) {
+                price.push(last)
+                date.push(moment(lasttime).format('YYYY-MM-DD'))
+            } else {
+                // 更新列表中最后一条记录的数据
+                price[lastIndex] = last
+                date[lastIndex] = lasttime
+            }
+        }
+        return dataset
+    }
+
+    return {
+        ...toRefs(state),
+        onLoadData,
+        initData,
+        updateData
+    }
+}

+ 118 - 0
src/hooks/echarts@next/line/options.ts

@@ -0,0 +1,118 @@
+import moment from 'moment'
+import { echarts, ECOption } from '@/components/base/echarts/core'
+import { EchartsDataset } from './types'
+
+export function useOptions() {
+    const getOption = (dataset: EchartsDataset) => {
+        const { line, decimal, yAxisMax, yAxisMin } = dataset
+        const option: ECOption = {
+            dataset: line,
+            animation: false,
+            grid: {
+                left: '12px',
+                right: '12px',
+                top: '24px',
+                bottom: '12px',
+                containLabel: true,
+            },
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    type: 'cross'
+                },
+                className: 'tooltip',
+                formatter: (params: unknown[]) => {
+                    let result = ''
+                    // eslint-disable-next-line
+                    params.forEach((item: any, index: number) => {
+                        const [time, value] = item.data
+                        if (index === 0) {
+                            result += '<div class="tooltip-title">' + time + '</div>'
+                        }
+                        result += '<div class="tooltip-item"><span><i style="background-color:' + item.color + ';"></i>结算价</span><span>' + value?.toFixed(decimal) + '</span></div>'
+                    })
+                    return result
+                },
+            },
+            axisPointer: {
+                label: {
+                    color: '#555',
+                    backgroundColor: 'rgba(242,242,242,.95)',
+                    margin: 7,
+                }
+            },
+            xAxis: {
+                type: 'category',
+                axisLabel: {
+                    color: '#ccc',
+                    formatter: (val: string) => {
+                        return moment(val).format('MM-DD')
+                    },
+                    margin: 12
+                },
+                axisLine: {
+                    lineStyle: {
+                        color: '#ccc'
+                    }
+                }
+            },
+            yAxis: {
+                min: yAxisMin,
+                max: yAxisMax,
+                axisPointer: {
+                    show: false, // 不显示坐标指示器
+                },
+                splitLine: {
+                    lineStyle: {
+                        // 坐标分隔线颜色
+                        color: '#f2f2f2',
+                    },
+                },
+                splitNumber: 3,
+                axisLabel: {
+                    color: '#ccc',
+                    formatter: (val: number) => val?.toFixed(decimal),
+                }
+            },
+            // series 中不指定 yAxisId 或 yAxisIndex 默认关联 yAxis 第一个配置,xAxis 配置同理
+            series: [
+                {
+                    type: 'line',
+                    symbol: 'none', //中时有小圆点
+                    lineStyle: {
+                        color: '#FF9A2A'
+                    },
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(0, 0, 0, 1,
+                            [
+                                {
+                                    offset: 0,
+                                    color: 'rgba(240, 168, 75, .2)',
+                                },
+                                {
+                                    offset: 1,
+                                    color: 'rgba(240, 168, 75, 0)',
+                                },
+                            ],
+                            false
+                        ),
+                        opacity: 1,
+                    },
+                },
+            ]
+        }
+        return option
+    }
+
+    const updateOption = (dataset: EchartsDataset) => {
+        const option: ECOption = {
+            dataset: dataset.line,
+        }
+        return option
+    }
+
+    return {
+        getOption,
+        updateOption
+    }
+}

+ 25 - 0
src/hooks/echarts@next/line/types.ts

@@ -0,0 +1,25 @@
+/**
+ * 图表数据集
+ */
+export interface EchartsDataset {
+    line: Dataset;
+    decimal: number;
+    yAxisMin: (value: { min: number; max: number; }) => number; // Y轴最大刻度
+    yAxisMax: (value: { min: number; max: number; }) => number; // Y轴最小刻度
+}
+
+/**
+ * 图表数据源
+ */
+export interface Dataset {
+    dimensions: (keyof Line)[];
+    source: Line;
+}
+
+/**
+ * 折线
+ */
+export type Line = {
+    date: string[], // xAxis数据,必须是第一个属性
+    price: number[], // 价格
+}

+ 4 - 0
src/packages/gstj/views/order/delivery/Index.vue

@@ -4,6 +4,9 @@
             <app-navbar title="交收提货" />
         </template>
         <Tabs class="van-tabs--list" v-model:active="active" :swipe-threshold="4">
+            <Tab title="点选交收单">
+                <component :is="componentMap.get('online')" />
+            </Tab>
             <Tab title="线下交收单">
                 <component :is="componentMap.get('offline')" />
             </Tab>
@@ -19,6 +22,7 @@ import { shallowRef, defineAsyncComponent } from 'vue'
 import { Tab, Tabs } from 'vant'
 
 const componentMap = new Map<string, unknown>([
+    ['online', defineAsyncComponent(() => import('@mobile/views/order/delivery/components/online/list/Index.vue'))], // 点选交收单
     ['offline', defineAsyncComponent(() => import('@mobile/views/order/delivery/components/offline/list/Index.vue'))], // 线下交收单
     ['spot', defineAsyncComponent(() => import('@mobile/views/order/delivery/components/spot/list/Index.vue'))], // 现货提货单
 ])

+ 0 - 12
src/packages/gstj/views/order/list/Index.vue

@@ -38,18 +38,6 @@ const components = [
         title: '订单成交',
         component: defineAsyncComponent(() => import('@mobile/views/order/list/components/goodstrade/list/Index.vue')),
         history: defineAsyncComponent(() => import('@mobile/views/order/list/components/goodstrade/history/Index.vue')),
-    },
-    {
-        name: 'listingorder',
-        title: '挂牌委托',
-        component: defineAsyncComponent(() => import('@mobile/views/order/list/components/listingorder/list/Index.vue')),
-        history: defineAsyncComponent(() => import('@mobile/views/order/list/components/listingorder/history/Index.vue')),
-    },
-    {
-        name: 'listingtrade',
-        title: '挂牌成交',
-        component: defineAsyncComponent(() => import('@mobile/views/order/list/components/listingtrade/list/Index.vue')),
-        history: defineAsyncComponent(() => import('@mobile/views/order/list/components/listingtrade/history/Index.vue')),
     }
 ]
 

+ 0 - 3
src/packages/mobile/components/modules/echarts-line/index.less

@@ -1,3 +0,0 @@
-.app-echats-line {
-    height: 4rem;
-}

+ 0 - 55
src/packages/mobile/components/modules/echarts-line/index.vue

@@ -1,55 +0,0 @@
-<template>
-    <div class="app-echats-line">
-        <template v-if="loading">
-            <div class="app-echats-line__tip">正在加载...</div>
-        </template>
-        <template v-else-if="isEmpty">
-            <div class="app-echats-line__tip">暂无数据</div>
-        </template>
-        <template v-else>
-            <!-- <ul class="legend">
-                <li class="legend-item">收盘: {{ selectedItem.price }}</li>
-            </ul> -->
-            <app-echarts :option="options.line" v-model:dataIndex="dataIndex" @ready="initOptions" />
-        </template>
-    </div>
-</template>
-
-<script lang="ts" setup>
-import { shallowRef, PropType, watch } from 'vue'
-import { useLineChart } from '@/hooks/echarts/line'
-import { EchartsDataset } from '@/hooks/echarts/line/types'
-import AppEcharts from '@/components/base/echarts/index.vue'
-
-const props = defineProps({
-    loading: {
-        type: Boolean,
-        default: false
-    },
-    dataList: {
-        type: Object as PropType<EchartsDataset['line']['source']>,
-        required: true
-    },
-})
-
-const { options, dataIndex, initData, initOptions } = useLineChart()
-const isEmpty = shallowRef(false)
-
-watch(() => props.loading, (status) => {
-    if (status) {
-        isEmpty.value = false
-    } else {
-        if (props.dataList.price.length) {
-            initData(props.dataList)
-        } else {
-            isEmpty.value = true
-        }
-    }
-}, {
-    immediate: true
-})
-</script>
-
-<style lang="less">
-@import './index.less';
-</style>

+ 3 - 0
src/packages/mobile/components/modules/echarts/line/index.less

@@ -0,0 +1,3 @@
+.app-linechart {
+    height: 3.8rem;
+}

+ 33 - 0
src/packages/mobile/components/modules/echarts/line/index.vue

@@ -0,0 +1,33 @@
+<template>
+    <div class="app-linechart">
+        <app-echarts v-model:dataIndex="dataIndex" @ready="onReady" />
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { echarts } from '@/components/base/echarts@next/core'
+import { useLineChart } from '@/hooks/echarts@next/line'
+import { useOptions } from '@/hooks/echarts@next/line/options'
+import AppEcharts from '@/components/base/echarts@next/index.vue'
+
+const props = defineProps({
+    goodscode: {
+        type: String,
+        required: true
+    }
+})
+
+const { onLoadData, dataIndex } = useLineChart(props.goodscode)
+const { getOption } = useOptions()
+
+const onReady = (chart: echarts.ECharts) => {
+    onLoadData.then((data) => {
+        const option = getOption(data)
+        chart.setOption(option, true)
+    })
+}
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 2 - 2
src/packages/mobile/views/order/delivery/Index.vue

@@ -4,7 +4,7 @@
             <app-navbar title="交收提货" />
         </template>
         <Tabs class="van-tabs--list" v-model:active="active" :swipe-threshold="4">
-            <Tab title="线上交收单">
+            <Tab title="点选交收单">
                 <component :is="componentMap.get('online')" />
             </Tab>
             <Tab title="线下交收单">
@@ -22,7 +22,7 @@ import { shallowRef, defineAsyncComponent } from 'vue'
 import { Tab, Tabs } from 'vant'
 
 const componentMap = new Map<string, unknown>([
-    ['online', defineAsyncComponent(() => import('./components/online/list/Index.vue'))], // 线上交收单
+    ['online', defineAsyncComponent(() => import('./components/online/list/Index.vue'))], // 点选交收单
     ['offline', defineAsyncComponent(() => import('./components/offline/list/Index.vue'))], // 线下交收单
     ['spot', defineAsyncComponent(() => import('./components/spot/list/Index.vue'))], // 现货提货单
 ])

+ 3 - 3
src/packages/mobile/views/order/delivery/components/online/detail/Index.vue

@@ -1,12 +1,12 @@
-<!-- 交收提货-线上交收单-详情 -->
+<!-- 交收提货-点选交收单-详情 -->
 <template>
     <app-modal direction="right" height="100%" v-model:show="showModal">
         <app-view class="g-form">
             <template #header>
-                <app-navbar title="线上交收单" @back="closed" />
+                <app-navbar title="点选交收单" @back="closed" />
             </template>
             <div class="g-form__container">
-                <CellGroup title="线上交收单信息">
+                <CellGroup title="点选交收单信息">
                     <Cell title="商品名称" :value="selectedRow.wrtypename" />
                     <Cell title="交收数量" :value="selectedRow.deliveryqty" />
                     <Cell title="订货价" :value="selectedRow.xdeliveryprice" />

+ 1 - 1
src/packages/mobile/views/order/delivery/components/online/list/Index.vue

@@ -1,4 +1,4 @@
-<!-- 交收提货-线上交收单 -->
+<!-- 交收提货-点选交收单 -->
 <template>
     <app-pull-refresh ref="pullRefreshRef" v-model:loading="loading" v-model:error="error" v-model:pageIndex="pageIndex"
         :page-count="pageCount" @refresh="run">

+ 16 - 12
src/packages/mobile/views/swap/list/Index.vue

@@ -39,7 +39,7 @@
 
 <script lang="ts" setup>
 import { computed, onUnmounted } from 'vue'
-import { parsePercent, handleNumberValue } from '@/filters'
+import { parsePercent, handleNumberValue, formatDecimal } from '@/filters'
 import { useRequest } from '@/hooks/request'
 import { useNavigation } from '@mobile/router/navigation'
 import { queryQuoteGoodsList } from '@/services/api/swap'
@@ -59,7 +59,8 @@ const futuresStore = useFuturesStore()
 const userStore = useUserStore()
 const subscribe = quoteSocket.createSubscribe()
 
-const { dataList } = useRequest(queryQuoteGoodsList, {
+const { dataList, run } = useRequest(queryQuoteGoodsList, {
+    manual: true,
     params: {
         usertype: userStore.userType ?? 0,
         marketids: userStore.getMarketId('TRADEMODE_TJMD').toString()
@@ -71,23 +72,24 @@ const { dataList } = useRequest(queryQuoteGoodsList, {
 })
 
 const tableList = computed(() => {
-    return dataList.value.map((item) => {
-        const quote = futuresStore.getGoodsQuote(item.refgoodscode)
-        const { goodsname, lastColor, openedColor, lowestColor, highestColor, last, presettle, rise, change, amplitude, highest, lowest, opened } = quote.value ?? {}
+    return dataList.value.map(({ refgoodsid, goodscode, goodsname }) => {
+        const quote = futuresStore.getGoodsQuote(refgoodsid)
+        const { lastColor, openedColor, lowestColor, highestColor, last = 0, presettle = 0, rise = 0, change, amplitude, highest = 0, lowest = 0, opened = 0, decimalplace } = quote.value ?? {}
         return {
-            ...item,
+            refgoodsid,
+            goodscode,
             goodsname,
             lastColor,
             openedColor,
             lowestColor,
             highestColor,
-            last: handleNumberValue(last),
-            rise: handleNumberValue(rise?.toFixed(item.decimalplace)),
+            last: handleNumberValue(formatDecimal(last, decimalplace)),
+            rise: handleNumberValue(formatDecimal(rise, decimalplace)),
             change: parsePercent(change),
-            opened: handleNumberValue(opened),
-            presettle: handleNumberValue(presettle),
-            lowest: handleNumberValue(lowest),
-            highest: handleNumberValue(highest),
+            opened: handleNumberValue(formatDecimal(opened, decimalplace)),
+            presettle: handleNumberValue(formatDecimal(presettle, decimalplace)),
+            lowest: handleNumberValue(formatDecimal(lowest, decimalplace)),
+            highest: handleNumberValue(formatDecimal(highest, decimalplace)),
             amplitude: parsePercent(amplitude),
         }
     })
@@ -110,5 +112,7 @@ const rowClick = (row: Model.QuoteGoodsListRsp) => {
     router.push({ name: 'swap-detail' })
 }
 
+futuresStore.onResponse(() => run())
+
 onUnmounted(() => subscribe.stop())
 </script>

+ 0 - 1
src/packages/pc/main.ts

@@ -6,7 +6,6 @@ import layouts from "./components/layouts" // 布局组件
 import ElementPlus from 'element-plus'
 import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
 import * as ElementIcons from '@element-plus/icons-vue'
-import 'hqchart/src/jscommon/umychart.resource/css/tools.css'
 import 'hqchart/src/jscommon/umychart.resource/font/iconfont.css'
 import 'element-plus/dist/index.css'
 import './assets/themes/style.less' // 主题样式

+ 7 - 4
src/packages/pc/views/footer/spot/position/index.vue

@@ -9,7 +9,8 @@
         <!-- 展开行 -->
         <template #expand="{ row }">
             <div class="buttonbar">
-                <el-button type="primary" size="small" @click="showComponent('listing', row)">挂牌</el-button>
+                <el-button type="primary" size="small" @click="showComponent('listing', row)"
+                    v-if="hasTradeMode17">挂牌</el-button>
                 <el-button type="danger" size="small" @click="showComponent('pickup', row)">提货</el-button>
             </div>
         </template>
@@ -27,15 +28,18 @@ import { useRequest } from '@/hooks/request'
 import { useComponent } from '@/hooks/component'
 import { queryHoldLB } from '@/services/api/order'
 import { useComposeTable } from '@pc/components/base/table'
+import { useUserStore } from '@/stores'
 import AppTable from '@pc/components/base/table/index.vue'
 
-const { loading, dataList, run } = useRequest(queryHoldLB, {})
-
 const componentMap = new Map<string, unknown>([
     ['pickup', defineAsyncComponent(() => import('./components/pickup/index.vue'))],
     ['listing', defineAsyncComponent(() => import('./components/listing/index.vue'))],
 ])
 
+const userStore = useUserStore()
+const hasTradeMode17 = userStore.hasMarket('TRADEMODE_SPOT_WRTRADE')
+const { loading, dataList, run } = useRequest(queryHoldLB)
+
 const { componentRef, componentId, openComponent, closeComponent } = useComponent(() => {
     run()
 })
@@ -59,5 +63,4 @@ const showComponent = (componentName: string, row: Model.HoldLBRsp) => {
     selectedRow.value = row
     openComponent(componentName)
 }
-
 </script>

+ 23 - 30
src/packages/pc/views/market/trade/swap/index.vue

@@ -3,11 +3,11 @@
     <app-table :data="tableList" v-model:columns="tableColumns" @row-click="onRowClick" showIndex>
         <!-- 当前价 -->
         <template #last="{ row }">
-            <span :class="row.lastColor">{{ handleNumberValue(formatDecimal(row.last, row.decimalplace)) }}</span>
+            <span :class="row.lastColor">{{ row.last }}</span>
         </template>
         <!-- 涨跌 -->
         <template #rise="{ row }">
-            <span :class="row.lastColor">{{ formatDecimal(row.rise, row.decimalplace) }}</span>
+            <span :class="row.lastColor">{{ row.rise }}</span>
         </template>
         <!-- 涨跌幅 -->
         <template #change="{ row }">
@@ -15,19 +15,19 @@
         </template>
         <!-- 开盘 -->
         <template #opened="{ row }">
-            <span :class="row.openedColor">{{ handleNumberValue(formatDecimal(row.opened, row.decimalplace)) }}</span>
+            <span :class="row.openedColor">{{ row.opened }}</span>
         </template>
         <!-- 最低 -->
         <template #lowest="{ row }">
-            <span :class="row.lowestColor">{{ handleNumberValue(formatDecimal(row.lowest, row.decimalplace)) }}</span>
+            <span :class="row.lowestColor">{{ row.lowest }}</span>
         </template>
         <!-- 最高 -->
         <template #highest="{ row }">
-            <span :class="row.highestColor">{{ handleNumberValue(formatDecimal(row.highest, row.decimalplace)) }}</span>
+            <span :class="row.highestColor">{{ row.highest }}</span>
         </template>
         <template #footer>
-            <component ref="componentRef" v-bind="{ goodsId: selectedGoodsId, selectedRow }" :is="componentMap.get(componentId)"
-                @closed="closeComponent" v-if="componentId" />
+            <component ref="componentRef" v-bind="{ goodsId: futuresStore.selectedGoodsId, selectedRow }"
+                :is="componentMap.get(componentId)" @closed="closeComponent" v-if="componentId" />
         </template>
     </app-table>
 </template>
@@ -43,12 +43,9 @@ import quoteSocket from '@/services/websocket/quote'
 import AppTable from '@pc/components/base/table/index.vue'
 
 const subscribe = quoteSocket.createSubscribe()
-const { onResponse } = useFuturesStore()
 const futuresStore = useFuturesStore()
 const userStore = useUserStore()
 const selectedRow = shallowRef<Model.QuoteGoodsListRsp>()
-const { $toRefs } = useFuturesStore()
-const { selectedGoodsId } = $toRefs()
 
 const { componentRef, componentId, openComponent, closeComponent } = useComponent(() => true, false)
 
@@ -56,7 +53,8 @@ const componentMap = new Map<string, unknown>([
     ['detail', defineAsyncComponent(() => import('./detail/index.vue'))], // 详情
 ])
 
-const { dataList } = useRequest(queryQuoteGoodsList, {
+const { dataList, run } = useRequest(queryQuoteGoodsList, {
+    manual: true,
     params: {
         usertype: userStore.userType ?? 0,
         marketids: userStore.getMarketId('TRADEMODE_TJMD').toString()
@@ -68,23 +66,24 @@ const { dataList } = useRequest(queryQuoteGoodsList, {
 })
 
 const tableList = computed(() => {
-    return dataList.value.map((item) => {
-        const quote = futuresStore.getGoodsQuote(item.refgoodscode)
-        const { goodsname, lastColor, openedColor, lowestColor, highestColor, last, presettle, rise, change, amplitude, highest, lowest, opened } = quote.value ?? {}
+    return dataList.value.map(({ refgoodsid, goodscode, goodsname }) => {
+        const quote = futuresStore.getGoodsQuote(refgoodsid)
+        const { lastColor, openedColor, lowestColor, highestColor, last = 0, presettle = 0, rise = 0, change, amplitude, highest = 0, lowest = 0, opened = 0, decimalplace } = quote.value ?? {}
         return {
-            ...item,
+            refgoodsid,
+            goodscode,
             goodsname,
             lastColor,
             openedColor,
             lowestColor,
             highestColor,
-            last: handleNumberValue(last),
-            rise: handleNumberValue(rise?.toFixed(item.decimalplace)),
+            last: handleNumberValue(formatDecimal(last, decimalplace)),
+            rise: handleNumberValue(formatDecimal(rise, decimalplace)),
             change: parsePercent(change),
-            opened: handleNumberValue(opened),
-            presettle: handleNumberValue(presettle),
-            lowest: handleNumberValue(lowest),
-            highest: handleNumberValue(highest),
+            opened: handleNumberValue(formatDecimal(opened, decimalplace)),
+            presettle: handleNumberValue(formatDecimal(presettle, decimalplace)),
+            lowest: handleNumberValue(formatDecimal(lowest, decimalplace)),
+            highest: handleNumberValue(formatDecimal(highest, decimalplace)),
             amplitude: parsePercent(amplitude),
         }
     })
@@ -104,18 +103,12 @@ const tableColumns = shallowRef<Model.TableColumn[]>([
 ])
 
 const onRowClick = (row: Model.QuoteGoodsListRsp) => {
-    selectedGoodsId.value = row.refgoodsid
+    futuresStore.selectedGoodsId = row.refgoodsid
     selectedRow.value = row
     openComponent('detail')
 }
 
-onResponse(() => {
-    const goodsCodes = dataList.value.map((e) => e.refgoodscode)
-    subscribe.start(...goodsCodes)
-})
-
-onUnmounted(() => {
-    subscribe.stop()
-})
+futuresStore.onResponse(() => run())
 
+onUnmounted(() => subscribe.stop())
 </script>

+ 5 - 5
src/packages/tc/views/order/delivery/Index.vue

@@ -4,12 +4,12 @@
             <app-navbar title="交收提货" />
         </template>
         <Tabs class="van-tabs--list" v-model:active="active" :swipe-threshold="4">
-            <Tab title="线上交收单">
+            <Tab title="点选交收单">
                 <component :is="componentMap.get('online')" />
             </Tab>
-            <Tab title="线下交收单">
+            <!-- <Tab title="线下交收单">
                 <component :is="componentMap.get('offline')" />
-            </Tab>
+            </Tab> -->
             <Tab title="现货提货单">
                 <component :is="componentMap.get('spot')" />
             </Tab>
@@ -22,8 +22,8 @@ import { shallowRef, defineAsyncComponent } from 'vue'
 import { Tab, Tabs } from 'vant'
 
 const componentMap = new Map<string, unknown>([
-    ['online', defineAsyncComponent(() => import('@mobile/views/order/delivery/components/online/list/Index.vue'))], // 线上交收单
-    ['offline', defineAsyncComponent(() => import('@mobile/views/order/delivery/components/offline/list/Index.vue'))], // 线下交收单
+    ['online', defineAsyncComponent(() => import('@mobile/views/order/delivery/components/online/list/Index.vue'))], // 点选交收单
+    //['offline', defineAsyncComponent(() => import('@mobile/views/order/delivery/components/offline/list/Index.vue'))], // 线下交收单
     ['spot', defineAsyncComponent(() => import('@mobile/views/order/delivery/components/spot/list/Index.vue'))], // 现货提货单
 ])
 

BIN
src/packages/tjmd/assets/images/login-logo.png


+ 95 - 9
src/packages/tjmd/views/home/main/Index.vue

@@ -8,6 +8,29 @@
       <app-block>
         <Cell title="通知公告" value="更多" :to="{ name: 'notice-list' }" icon="volume" is-link />
       </app-block>
+      <app-block class="home-main__chart" v-if="loginStore.token">
+        <Swipe :show-indicators="false" :loop="false">
+          <SwipeItem v-for="(list, i) in goodsGroup" :key="i">
+            <ul>
+              <template v-for="(item, n) in list" :key="n">
+                <li :class="item.refgoodscode === goodsCode ? 'is-active' : ''" @click="onTabChange(item.refgoodscode)">
+                  <div>
+                    <b>{{ item.goodsname }}</b>
+                  </div>
+                  <div :class="item.lastColor">
+                    <b>{{ item.last }}</b>
+                  </div>
+                  <div>
+                    <span :class="item.lastColor">{{ item.rise }}</span>
+                    <span :class="item.lastColor">{{ item.change }}</span>
+                  </div>
+                </li>
+              </template>
+            </ul>
+          </SwipeItem>
+        </Swipe>
+        <component :is="LineChart" :goodscode="goodsCode" v-if="showChart" />
+      </app-block>
       <app-block class="home-main__news">
         <CellGroup class="article">
           <Cell class="home-main__titlebar" title="市场资讯" value="更多" icon="fire" :to="{ name: 'news-list' }" is-link />
@@ -22,18 +45,61 @@
 </template>
 
 <script lang="ts" setup>
-import { shallowRef } from "vue";
-import { Cell, CellGroup, PullRefresh } from "vant";
-import { formatDate } from "@/filters";
-import { queryImageConfigs } from "@/services/api/common";
-import { queryNewTitles } from "@/services/api/news";
-import { useGlobalStore } from '@/stores'
+import { onUnmounted, shallowRef, onActivated, defineAsyncComponent, computed, nextTick } from 'vue'
+import { Cell, CellGroup, PullRefresh, Swipe, SwipeItem, Grid, GridItem } from 'vant'
+import { formatDate, parsePercent, handleNumberValue, formatDecimal } from '@/filters'
+import { queryQuoteGoodsList } from '@/services/api/swap'
+import { queryImageConfigs } from '@/services/api/common'
+import { queryNewTitles } from "@/services/api/news"
+import { useGlobalStore, useLoginStore, useUserStore, useFuturesStore } from '@/stores'
+import quoteSocket from '@/services/websocket/quote'
 import Banner from '@mobile/components/base/banner/index.vue'
 
+const LineChart = defineAsyncComponent(() => import('@mobile/components/modules/echarts/line/index.vue'))
+
+const subscribe = quoteSocket.createSubscribe()
 const globalStore = useGlobalStore()
-const refreshing = shallowRef(false); // 是否处于加载中状态
-const topBanners = shallowRef<string[]>([]); // 轮播图列表
-const newsList = shallowRef<Model.NewTitlesRsp[]>([]); // 资讯列表
+const loginStore = useLoginStore()
+const userStore = useUserStore()
+const futuresStore = useFuturesStore()
+const refreshing = shallowRef(false) // 是否处于加载中状态
+const topBanners = shallowRef<string[]>([]) // 轮播图列表
+const newsList = shallowRef<Model.NewTitlesRsp[]>([]) // 资讯列表
+const goodsList = shallowRef<Model.QuoteGoodsListRsp[]>([]) // 掉期商品列表
+const goodsCode = shallowRef('')
+const showChart = shallowRef(false)
+
+// 掉期商品组
+const goodsGroup = computed(() => {
+  const arr = []
+  const list = goodsList.value.map(({ refgoodscode, goodsname }) => {
+    const quote = futuresStore.getGoodsQuote(refgoodscode)
+    const { lastColor, last = 0, rise = 0, change, decimalplace } = quote.value ?? {}
+    return {
+      refgoodscode,
+      goodsname,
+      lastColor,
+      last: handleNumberValue(formatDecimal(last, decimalplace)),
+      rise: handleNumberValue(formatDecimal(rise, decimalplace)),
+      change: parsePercent(change)
+    }
+  })
+  // 分割成三个一组
+  for (let i = 0; i < list.length; i += 3) {
+    arr.push(list.slice(i, i + 3))
+  }
+  return arr
+})
+
+// 切换掉期商品
+const onTabChange = (value: string) => {
+  showChart.value = false
+  goodsCode.value = value
+
+  nextTick(() => {
+    showChart.value = true
+  })
+}
 
 // 下拉刷新
 const onRefresh = () => {
@@ -60,6 +126,26 @@ const onRefresh = () => {
 }
 
 onRefresh()
+
+onActivated(() => {
+  if (!goodsList.value.length && loginStore.token) {
+    // 掉期商品
+    queryQuoteGoodsList({
+      data: {
+        usertype: userStore.userType ?? 0,
+        marketids: userStore.getMarketId('TRADEMODE_TJMD').toString()
+      }
+    }).then((res) => {
+      const goodsCodes = res.data.map((e) => e.refgoodscode)
+      goodsList.value = res.data
+      goodsCode.value = goodsCodes[0]
+      showChart.value = true
+      subscribe.start(...goodsCodes)
+    })
+  }
+})
+
+onUnmounted(() => subscribe.stop())
 </script>
 
 <style lang="less">

+ 40 - 0
src/packages/tjmd/views/home/main/index.less

@@ -86,4 +86,44 @@
             }
         }
     }
+
+    &__chart {
+        ul {
+            display: flex;
+            text-align: center;
+            line-height: .44rem;
+
+            li {
+                position: relative;
+                flex: 1;
+                padding: .12rem 0;
+
+                &::before {
+                    content: '';
+                    position: absolute;
+                    bottom: 0;
+                    left: 50%;
+                    height: .04rem;
+                    width: 50%;
+                    transform: translateX(-50%);
+                }
+
+                &.is-active {
+                    background-color: #fbfbfb;
+
+                    &::before {
+                        background-color: transparent
+                    }
+                }
+
+                span {
+                    font-size: .24rem;
+
+                    +span {
+                        margin-left: .1rem;
+                    }
+                }
+            }
+        }
+    }
 }

+ 9 - 16
src/packages/tjmd/views/order/delivery/Index.vue

@@ -3,25 +3,18 @@
         <template #header>
             <app-navbar title="交收提货" />
         </template>
-        <Tabs class="van-tabs--list" v-model:active="active" :swipe-threshold="4">
-            <Tab title="线下交收单">
-                <component :is="componentMap.get('offline')" />
-            </Tab>
-            <Tab title="现货提货单">
-                <component :is="componentMap.get('spot')" />
-            </Tab>
-        </Tabs>
+        <component :is="components[0].component" />
     </app-view>
 </template>
 
 <script lang="ts" setup>
-import { shallowRef, defineAsyncComponent } from 'vue'
-import { Tab, Tabs } from 'vant'
+import { defineAsyncComponent } from 'vue'
 
-const componentMap = new Map<string, unknown>([
-    ['offline', defineAsyncComponent(() => import('@mobile/views/order/delivery/components/offline/list/Index.vue'))], // 线下交收单
-    ['spot', defineAsyncComponent(() => import('@mobile/views/order/delivery/components/spot/list/Index.vue'))], // 现货提货单
-])
-
-const active = shallowRef(0)
+const components = [
+    {
+        name: 'goods',
+        title: '现货提货单',
+        component: defineAsyncComponent(() => import('@mobile/views/order/delivery/components/spot/list/Index.vue')),
+    }
+]
 </script>

+ 5 - 1
src/packages/tjmd/views/user/login/Index.vue

@@ -10,4 +10,8 @@ import logoImage from '../../../assets/images/login-logo.png'
 
 onMounted(() => plus.setStatusBarStyle('dark'))
 onUnmounted(() => plus.setStatusBarStyle('light'))
-</script>
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 9 - 0
src/packages/tjmd/views/user/login/index.less

@@ -0,0 +1,9 @@
+.login {
+    background: linear-gradient(35deg, #b0e1fa, #fff 65%, #e8f7f1);
+
+    &-logo {
+        img {
+            height: 1.6rem;
+        }
+    }
+}

+ 2 - 2
src/packages/zrwyt/views/order/delivery/Index.vue

@@ -4,7 +4,7 @@
             <app-navbar title="交收提货" />
         </template>
         <Tabs class="van-tabs--list" v-model:active="active" :swipe-threshold="4">
-            <Tab title="线上交收单">
+            <Tab title="点选交收单">
                 <component :is="componentMap.get('online')" />
             </Tab>
             <!-- <Tab title="线下交收单">
@@ -22,7 +22,7 @@ import { shallowRef, defineAsyncComponent } from 'vue'
 import { Tab, Tabs } from 'vant'
 
 const componentMap = new Map<string, unknown>([
-    ['online', defineAsyncComponent(() => import('@mobile/views/order/delivery/components/online/list/Index.vue'))], // 线上交收单
+    ['online', defineAsyncComponent(() => import('@mobile/views/order/delivery/components/online/list/Index.vue'))], // 点选交收单
     ['offline', defineAsyncComponent(() => import('@mobile/views/order/delivery/components/offline/list/Index.vue'))], // 线下交收单
     ['spot', defineAsyncComponent(() => import('@mobile/views/order/delivery/components/spot/list/Index.vue'))], // 现货提货单
 ])

+ 1 - 1
src/services/http/index.ts

@@ -132,7 +132,7 @@ export default new (class {
      * @returns 
      */
     async commonRequest<T>(config: AxiosRequestConfig, errMsg?: string) {
-        const baseUrl = 'http://192.168.30.172:8082/api'//service.getConfig('goCommonSearchUrl')
+        const baseUrl = service.getConfig('goCommonSearchUrl')
         config.url = baseUrl + config.url
         const res = await this.request<CommonResult<T>>(config, errMsg)
         switch (res.code) {

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است