li.shaoyi 3 tahun lalu
induk
melakukan
53b5c144a7

+ 4 - 4
src/components/base/echarts/index.ts

@@ -18,7 +18,7 @@ const defaultOption: ECOption = {
 }
 
 export function useEcharts() {
-    const instance = getCurrentInstance();
+    const context = getCurrentInstance();
     const chartElement = ref<HTMLElement>(); // chart 元素
     let chart: echarts.ECharts; // chart 对象
 
@@ -42,7 +42,7 @@ export function useEcharts() {
                 if (chart.containPixel('grid', pointInPixel)) {
                     const pointInGrid = chart.convertFromPixel({ seriesIndex: 0 }, pointInPixel)
                     const dataIndex = pointInGrid[0];
-                    instance?.emit('update:dataIndex', dataIndex);
+                    context?.emit('update:dataIndex', dataIndex);
                 }
             })
 
@@ -52,8 +52,8 @@ export function useEcharts() {
             }, 50);
 
             // 监听元素变化
-            new ResizeObserver(() => resize()).observe(el);
-            instance?.emit('ready', chart);
+            new ResizeObserver(resize).observe(el);
+            context?.emit('ready', chart);
         }
     })
 

+ 79 - 0
src/components/base/tab-component/index.ts

@@ -0,0 +1,79 @@
+import { defineAsyncComponent, shallowRef } from 'vue'
+import { useMenu } from '@/hooks/menu'
+import { DynamicComponent } from './interface'
+
+/**
+ * @param menucode 
+ * @returns 
+ */
+export function useDynamicComponent(menucode?: string) {
+    const { getChildrenMenu } = useMenu();
+    const dynamicComponents: DynamicComponent[] = [];
+    const currentAuth: Ermcp.AccountMenu.Auth[] = []; // 当前操作权限
+    const currentTabComponent = shallowRef<DynamicComponent>(); // 当前选中的子组件
+
+    /**
+     * 转换动态组件
+     * @param items 
+     * @returns 
+     */
+    const parseComponent = (items: Ermcp.AccountMenu[]) => {
+        let result: DynamicComponent[] = [];
+
+        items.forEach((menu) => {
+            const { title, code, component, children, auth } = menu;
+            if (component) {
+                const componentString = component.replace(/^\/+/, ''); // 过滤字符串前面所有 '/' 字符
+                const componentPath = componentString.replace(/\.\w+$/, ''); // 过滤后缀名,为了让 import 加入 .vue ,不然会有警告提示...
+                const asyncComponent = defineAsyncComponent(() => import('/' + process.env.VUE_APP_ROOT + componentPath + '.vue'));
+
+                const dynamicComponent: DynamicComponent = {
+                    title: title,
+                    code: code,
+                    auth: auth?.map((e) => ({
+                        label: e.label,
+                        code: e.code,
+                    })) ?? [],
+                    component: asyncComponent
+                }
+
+                if (children?.length) {
+                    dynamicComponent.children = parseComponent(children);
+                }
+
+                result = [...result, dynamicComponent];
+            }
+        })
+
+        return result;
+    }
+
+    /**
+     * 切换组件
+     * @param index 
+     */
+    const componentChange = (index: number) => {
+        currentTabComponent.value = dynamicComponents[index];
+    }
+
+    const childrenMenu = getChildrenMenu(menucode);
+
+    if (childrenMenu) {
+        const { auth, children } = childrenMenu;
+        const components = parseComponent(children ?? []);
+
+        currentAuth.push(...auth ?? []);
+        dynamicComponents.push(...components);
+
+        if (components.length) {
+            componentChange(0);
+        }
+    }
+
+    return {
+        dynamicComponents,
+        currentAuth,
+        currentTabComponent,
+        componentChange,
+    }
+}

+ 1 - 1
src/components/base/tab-component/index.vue

@@ -16,7 +16,7 @@
 
 <script lang="ts" setup>
 import { PropType } from 'vue'
-import { useDynamicComponent } from '@/hooks/component'
+import { useDynamicComponent } from './index'
 
 const props = defineProps({
     code: String,

+ 0 - 0
src/hooks/component/interface.ts → src/components/base/tab-component/interface.ts


+ 1 - 79
src/hooks/component/index.ts

@@ -1,6 +1,4 @@
-import { defineAsyncComponent, ref, shallowRef } from 'vue'
-import { useMenu } from '@/hooks/menu'
-import { DynamicComponent } from './interface'
+import { ref } from 'vue'
 
 /**
  * @param callback 组件关闭时的回调
@@ -40,80 +38,4 @@ export function useComponent<T>(callback?: () => void) {
         openComponent,
         closeComponent,
     }
-}
-
-/**
- * @param menucode 
- * @returns 
- */
-export function useDynamicComponent(menucode?: string) {
-    const { getChildrenMenu } = useMenu();
-    const dynamicComponents: DynamicComponent[] = [];
-    const currentAuth: Ermcp.AccountMenu.Auth[] = []; // 当前操作权限
-    const currentTabComponent = shallowRef<DynamicComponent>(); // 当前选中的子组件
-
-    /**
-     * 转换动态组件
-     * @param items 
-     * @returns 
-     */
-    const parseComponent = (items: Ermcp.AccountMenu[]) => {
-        let result: DynamicComponent[] = [];
-
-        items.forEach((menu) => {
-            const { title, code, component, children, auth } = menu;
-            if (component) {
-                const componentString = component.replace(/^\/+/, ''); // 过滤字符串前面所有 '/' 字符
-                const componentPath = componentString.replace(/\.\w+$/, ''); // 过滤后缀名,为了让 import 加入 .vue ,不然会有警告提示...
-                const asyncComponent = defineAsyncComponent(() => import('/' + process.env.VUE_APP_ROOT + componentPath + '.vue'));
-
-                const dynamicComponent: DynamicComponent = {
-                    title: title,
-                    code: code,
-                    auth: auth?.map((e) => ({
-                        label: e.label,
-                        code: e.code,
-                    })) ?? [],
-                    component: asyncComponent
-                }
-
-                if (children?.length) {
-                    dynamicComponent.children = parseComponent(children);
-                }
-
-                result = [...result, dynamicComponent];
-            }
-        })
-
-        return result;
-    }
-
-    /**
-     * 切换组件
-     * @param index 
-     */
-    const componentChange = (index: number) => {
-        currentTabComponent.value = dynamicComponents[index];
-    }
-
-    const childrenMenu = getChildrenMenu(menucode);
-
-    if (childrenMenu) {
-        const { auth, children } = childrenMenu;
-        const components = parseComponent(children ?? []);
-
-        currentAuth.push(...auth ?? []);
-        dynamicComponents.push(...components);
-
-        if (components.length) {
-            componentChange(0);
-        }
-    }
-
-    return {
-        dynamicComponents,
-        currentAuth,
-        currentTabComponent,
-        componentChange,
-    }
 }

+ 3 - 3
src/hooks/echarts/candlestick/index.ts

@@ -15,7 +15,7 @@ export function useCandlestickChart(goodscode: string) {
     const isEmpty = ref(true);
     const dataIndex = ref(-1); // 当前数据索引值
     const cycleType = ref(ChartCycleType.minutes);
-    const quote = getQuoteDayInfoByCode(goodscode);
+    const quote = getQuoteDayInfoByCode(goodscode); // 实时行情
 
     // 当前选中的数据项
     const selectedItem = computed(() => {
@@ -106,7 +106,7 @@ export function useCandlestickChart(goodscode: string) {
                 return milliseconds * 24 * 60;
             }
             default: {
-                return 0;
+                return milliseconds;
             }
         }
     }
@@ -194,7 +194,7 @@ export function useCandlestickChart(goodscode: string) {
     }
 
     // 监听行情推送
-    watch(quote, () => {
+    watch(() => quote.value?.last, () => {
         if (!loading.value && !isEmpty.value) {
             updateChart();
         }

+ 2 - 2
src/hooks/echarts/timeline/index.ts

@@ -13,7 +13,7 @@ export function useTimelineChart(goodscode: string) {
     const loading = ref(false);
     const isEmpty = ref(false);
     const dataIndex = ref(-1); // 当前数据索引值
-    const quote = getQuoteDayInfoByCode(goodscode);
+    const quote = getQuoteDayInfoByCode(goodscode); // 实时行情
 
     // 当前选中的数据项
     const selectedItem = computed(() => {
@@ -90,7 +90,7 @@ export function useTimelineChart(goodscode: string) {
     }
 
     // 监听行情推送
-    watch(quote, () => {
+    watch(() => quote.value?.last, () => {
         if (!loading.value && !isEmpty.value) {
             updateChart();
         }

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

@@ -3,6 +3,7 @@ import App from './App.vue'
 import router from './router'
 import directives from '@/directives' // 自定义指令集
 //import 'default-passive-events'
+import '@/services/subscribe' // 全局订阅通知
 import '@/mock' // 模拟数据
 import '@/utils/h5plus' // 加载html5+
 import '@/utils/client' // 适配客户端屏幕

+ 3 - 3
src/packages/mobile/views/home/components/market/index.vue

@@ -35,12 +35,12 @@
 import { ref, reactive, onActivated, onDeactivated } from 'vue'
 import { v4 } from 'uuid'
 import { Grid, GridItem, Button, ActionSheet, ActionSheetAction } from 'vant'
-import { addSubscribe, removeSubscribe } from '@/business/quote'
 import { globalState } from '@/store'
 import { handlePriceColor, handleNoneValue } from '@/filters'
 import theme from '@/hooks/theme'
 import AppTable from '@mobile/components/base/table/index.vue'
 import { TableColumn } from '@mobile/components/base/table/interface'
+import subscribe from '@/services/subscribe'
 
 const uuid = v4();
 const showAction = ref(false);
@@ -84,11 +84,11 @@ const rowClick = (index: number) => {
 }
 
 onActivated(() => {
-  addSubscribe(['cu2206', 'cu2207', 'cu2208', 'cu2209', 'cu2301', 'cu2303', 'cu2304'], uuid);
+  subscribe.addQuoteSubscribe(['cu2206', 'cu2207', 'cu2208', 'cu2209', 'cu2301', 'cu2303', 'cu2304'], uuid);
 })
 
 onDeactivated(() => {
-  removeSubscribe(uuid)
+  subscribe.removeQuoteSubscribe(uuid)
 })
 </script>
 

+ 8 - 4
src/packages/mobile/views/home/index.vue

@@ -11,12 +11,16 @@
 import { ref } from 'vue'
 import { Tabbar } from '@mobile/components/base/tabbar/interface'
 import AppTabbar from '@mobile/components/base/tabbar/index.vue'
+import Home from './components/main/index.vue'
+import Market from './components/market/index.vue'
+import Order from './components/order/index.vue'
+import Mine from './components/mine/index.vue'
 
 const components = {
-  Home: require('./components/main/index.vue').default,
-  Market: require('./components/market/index.vue').default,
-  Order: require('./components/order/index.vue').default,
-  Mine: require('./components/mine/index.vue').default,
+  Home,
+  Market,
+  Order,
+  Mine,
 }
 
 const componentId = ref('Home');

+ 2 - 2
src/packages/mobile/views/order/detail/index.vue

@@ -3,8 +3,8 @@
     <app-navbar title="详情" />
     <div class="g-flex__body">
       <app-tab theme="menu" :data-list="chartCycleTypeList" :data-index="1" @change="tabChange" />
-      <component :is="components.echartsTimeline" v-if="selectedCycleType === ChartCycleType.time" />
-      <component :is="components.echartsKline" :cycle-type="selectedCycleType" v-else />
+      <component :is="components.echartsTimeline" goodscode="cu2208" v-if="selectedCycleType === ChartCycleType.time" />
+      <component :is="components.echartsKline" goodscode="cu2208" :cycle-type="selectedCycleType" v-else />
     </div>
     <div class="order-detail__footer">
       <Button @click="openComponent('trade')" type="primary" round block>挂牌求购</Button>

+ 2 - 2
src/packages/pc/components/base/table-filter/index.ts

@@ -5,7 +5,7 @@ import { FilterOption, FilterSelect, FilterInput, FilterButton } from './interfa
  * 参考https://element-plus.gitee.io/zh-CN/component/table.html
  */
 export function useFilter<T>() {
-    const instance = getCurrentInstance();
+    const context = getCurrentInstance();
 
     const state: { selectList: FilterSelect<T>[], inputList: FilterInput<T>[], buttonList: FilterButton[] } = reactive({
         selectList: [],
@@ -40,7 +40,7 @@ export function useFilter<T>() {
             }
         })
 
-        instance?.emit('search', options);
+        context?.emit('search', options);
     }
 
     // 重置查询

+ 10 - 2
src/packages/pc/components/modules/echarts-kline/index.vue

@@ -61,17 +61,18 @@
 </template>
 
 <script lang="ts" setup>
-import { ref, PropType, watch } from 'vue'
+import { ref, PropType, watch, onBeforeUnmount } from 'vue'
 import { echarts } from '@/components/base/echarts/core'
 import { ChartCycleType, ChartSeriesType, chartSeriesTypeList } from '@/constants/enum/chart'
 import { useCandlestickChart } from '@/hooks/echarts/candlestick'
 import AppEcharts from '@/components/base/echarts/index.vue'
 import AppTab from '@/components/base/tab/index.vue'
+import subscribe from '@/services/subscribe'
 
 const props = defineProps({
   goodscode: {
     type: String,
-    default: '',
+    required: true,
   },
   // 周期类型
   cycleType: {
@@ -107,12 +108,19 @@ const indicatorReady = (chart: echarts.ECharts) => {
   echarts.connect([...chartGroup.values()]); // 图表联动
 }
 
+// 行情订阅
+const sub = subscribe.addQuoteSubscribe([props.goodscode]);
+
 // 监听周期选择变化
 watch(() => props.cycleType, (val) => {
   initData(val);
 }, {
   immediate: true
 })
+
+onBeforeUnmount(() => {
+  sub.stop();
+})
 </script>
 
 <style lang="less">

+ 1 - 1
src/packages/pc/components/modules/echarts-timeline/index.vue

@@ -23,7 +23,7 @@ import AppEcharts from '@/components/base/echarts/index.vue'
 const props = defineProps({
   goodscode: {
     type: String,
-    default: '',
+    required: true,
   },
 })
 

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

@@ -2,6 +2,7 @@ import { createApp } from 'vue'
 import App from './App.vue'
 import router from './router'
 import directives from '@/directives' // 自定义指令集
+import '@/services/subscribe' // 全局订阅通知
 import '@/mock' // 模拟数据
 import '@/hooks/theme' // 加载主题
 import layouts from "./components/layouts" // 布局组件

+ 78 - 87
src/packages/pc/router/dynamicRouter.ts

@@ -1,98 +1,89 @@
 import { RouteRecordRaw } from 'vue-router';
 import { queryAccountMenu } from '@/services/api/account';
 import { sessionCache } from '@/store';
-import router from '@pc/router';
+import router from '../router';
 
-/**
- * 添加404页面
- */
-const addNotFound = () => {
-    router.addRoute({
-        path: '/:pathMatch(.*)*',
-        name: 'Error',
-        component: () => import('../views/error/404.vue'),
-    })
-}
+export default new (class {
+    /** 防止动态路由无限加载 */
+    isReady = false;
 
-/**
- * 动态添加路由
- */
-const addRoutes = (routes: Ermcp.AccountMenu[], parentName = ''): void => {
-    routes.forEach((item) => {
-        if (item.component && item.path) {
-            let component;
-            switch (item.component) {
-                case 'Page': {
-                    component = () => import('../components/layouts/page/index.vue');
-                    break;
-                }
-                default: {
-                    const componentString = item.component.replace(/^\/+/, ''); // 过滤字符串前面所有 '/' 字符
-                    const componentPath = componentString.replace(/\.\w+$/, ''); // 过滤后缀名,为了让 import 加入 .vue ,不然会有警告提示...
-                    component = () => import('../' + componentPath + '.vue');
-                }
-            }
+    /**
+     * 添加404页面
+     */
+    private addNotFound() {
+        router.addRoute({
+            path: '/:pathMatch(.*)*',
+            name: 'Error',
+            component: () => import('../views/error/404.vue'),
+        })
+    }
 
-            const route: RouteRecordRaw = {
-                path: item.path,
-                name: item.code,
-                component,
-                meta: {
-                    title: item.title,
-                    icon: item.icon,
-                    url: item.url,
-                    auth: item.auth ?? [],
-                    remark: item.remark,
-                },
-            }
-
-            parentName ? router.addRoute(parentName, route) : router.addRoute(route);
+    /**
+     * 动态添加路由
+     * @param routes 
+     * @param parentName 
+     */
+    private addRoutes(routes: Ermcp.AccountMenu[], parentName = '') {
+        routes.forEach((item) => {
+            if (item.component && item.path) {
+                let component;
+                switch (item.component) {
+                    case 'Page': {
+                        component = () => import('../components/layouts/page/index.vue');
+                        break;
+                    }
+                    default: {
+                        const componentString = item.component.replace(/^\/+/, ''); // 过滤字符串前面所有 '/' 字符
+                        const componentPath = componentString.replace(/\.\w+$/, ''); // 过滤后缀名,为了让 import 加入 .vue ,不然会有警告提示...
+                        component = () => import('../' + componentPath + '.vue');
+                    }
+                }
 
-            if (item.children && item.children.length) {
-                addRoutes(item.children, item.code);
-            }
-        }
-    })
-}
+                const route: RouteRecordRaw = {
+                    path: item.path,
+                    name: item.code,
+                    component,
+                    meta: {
+                        title: item.title,
+                        icon: item.icon,
+                        url: item.url,
+                        auth: item.auth ?? [],
+                        remark: item.remark,
+                    },
+                }
 
-// 防止动态路由无限加载
-let routerComplete = false;
+                parentName ? router.addRoute(parentName, route) : router.addRoute(route);
 
-/**
- * 注册路由
- */
-export const registerRoutes = (): Promise<boolean> => {
-    const menus = sessionCache.getValue('menus');
-    return new Promise((resolve, reject) => {
-        addNotFound();
-        if (menus.length) {
-            addRoutes(menus);
-            resolve(true);
-        } else {
-            queryAccountMenu({
-                success: (res) => {
-                    sessionCache.setValue('menus', res.data);
-                    addRoutes(res.data);
-                    resolve(true);
-                },
-                fail: (err) => {
-                    reject(err);
+                if (item.children && item.children.length) {
+                    this.addRoutes(item.children, item.code);
                 }
-            })
-        }
-    })
-}
-
-/**
- * 获取动态路由加载状态
- */
-export function getRouterIsComplete(): boolean {
-    return routerComplete;
-}
+            }
+        })
+    }
 
-/**
- * 设置动态路由加载状态
- */
-export function setRouterComplete(status: boolean): void {
-    routerComplete = status;
-}
+    /**
+     * 注册路由
+     * @returns 
+     */
+    registerRoutes() {
+        const menus = sessionCache.getValue('menus');
+        return new Promise((resolve, reject) => {
+            this.addNotFound();
+            if (menus.length) {
+                this.addRoutes(menus);
+                resolve(true);
+            } else {
+                queryAccountMenu({
+                    success: (res) => {
+                        sessionCache.setValue('menus', res.data);
+                        this.addRoutes(res.data);
+                        resolve(true);
+                    },
+                    fail: (err) => {
+                        reject(err);
+                    }
+                })
+            }
+        })
+    }
+})

+ 6 - 6
src/packages/pc/router/index.ts

@@ -1,6 +1,6 @@
 import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
 import { getLoginInfo } from '@/business/common'
-import { registerRoutes, getRouterIsComplete, setRouterComplete } from './dynamicRouter'
+import dynamicRouter from './dynamicRouter'
 import service from '@/services'
 
 const routes: Array<RouteRecordRaw> = [
@@ -40,7 +40,7 @@ router.beforeEach((to, from, next) => {
     // 判断服务是否加载完成
     if (service.isReady) {
         if (getLoginInfo('Token')) {
-            if (getRouterIsComplete()) {
+            if (dynamicRouter.isReady) {
                 if (isLoginOrRegister) {
                     next('/');
                 } else {
@@ -48,15 +48,15 @@ router.beforeEach((to, from, next) => {
                 }
             } else {
                 // 注册动态路由
-                registerRoutes().then(() => {
-                    setRouterComplete(true);
+                dynamicRouter.registerRoutes().then(() => {
+                    dynamicRouter.isReady = true;
                     next({ ...to, replace: true });
                 }).catch(() => {
                     // 404?
                 })
             }
         } else {
-            setRouterComplete(false);
+            dynamicRouter.isReady = false;
             if (isLoginOrRegister) {
                 next();
             } else {
@@ -76,6 +76,6 @@ router.beforeEach((to, from, next) => {
             });
         }
     }
-});
+})
 
 export default router

+ 4 - 2
src/packages/pc/views/market/goods/index.vue

@@ -3,8 +3,10 @@
     <div>全局共享数据:{{ goodsList }}</div>
     <el-button @click="clearGoods">清空</el-button>
     <app-tab theme="menu" :data-list="chartCycleTypeList" :data-index="1" @change="tabChange" />
-    <component :is="components.echartsTimeline" style="height:70%;" v-if="selectedCycleType === ChartCycleType.time" />
-    <component :is="components.echartsKline" :cycle-type="selectedCycleType" style="height:70%;" v-else />
+    <component :is="components.echartsTimeline" goodscode="cu2208" style="height:70%;"
+      v-if="selectedCycleType === ChartCycleType.time" />
+    <component :is="components.echartsKline" goodscode="cu2208" :cycle-type="selectedCycleType" style="height:70%;"
+      v-else />
     <!-- <div ref="scrollEl" style="height:300px;overflow-y:auto;">
       <div style="height:3000px;"></div>
     </div> -->

+ 3 - 3
src/packages/pc/views/market/quote/index.vue

@@ -27,9 +27,9 @@ import { defineAsyncComponent, onBeforeUnmount } from 'vue'
 import { queryGoodsList } from '@/services/api/goods'
 import { useComponent } from '@/hooks/component'
 import { useMenu } from '@/hooks/menu'
-import { addSubscribe } from '@/business/quote'
 import { useTable } from '@pc/components/base/table'
 import socket from '@/services/socket'
+import subscribe from '@/services/subscribe'
 import AppTable from '@pc/components/base/table/index.vue'
 import AppContextmenu from '@pc/components/base/contextmenu/index.vue'
 import AppButtonGroup from '@pc/components/modules/button-group/index.vue'
@@ -76,7 +76,7 @@ const handleButton = (item?: Ermcp.Goods) => {
   }
 }
 
-const subscribe = addSubscribe(['cu2206', 'cu2207', 'cu2208', 'cu2209', 'cu2301', 'cu2303', 'cu2304']);
+const sub = subscribe.addQuoteSubscribe(['cu2206', 'cu2207', 'cu2208', 'cu2209', 'cu2301', 'cu2303', 'cu2304']);
 
 queryGoodsList({
   success: (res) => {
@@ -85,7 +85,7 @@ queryGoodsList({
 })
 
 onBeforeUnmount(() => {
-  subscribe.stop();
+  sub.stop();
 })
 </script>
 

+ 44 - 43
src/business/quote/index.ts → src/services/subscribe/index.ts

@@ -6,11 +6,12 @@ import eventBus from '@/services/bus'
 import socket from '@/services/socket'
 
 /**
- * 行情订阅
+ * 订阅通知
  */
-export const quote = new (class {
-    /** 订阅列表 */
-    subscribeMap = new Map<string, string[]>();
+export default new (class {
+    /** 行情订阅列表 */
+    private quoteSubscribeMap = new Map<string, string[]>();
+
     quotePushNotify;
     quoteServerReconnectNotify;
 
@@ -184,17 +185,17 @@ export const quote = new (class {
 
         // 接收行情服务断线重连成功通知
         this.quoteServerReconnectNotify = eventBus.$on('quoteServerReconnectNotify', () => {
-            this.subscribe();
+            this.quoteSubscribe();
         })
     }
 
     /**
-     * 开始订阅
+     * 开始行情订阅
      */
-    subscribe() {
+    private quoteSubscribe() {
         const subscribeData: Proto.GoodsQuoteReq[] = [];
 
-        this.subscribeMap.forEach((value) => {
+        this.quoteSubscribeMap.forEach((value) => {
             const item = value.map((code) => ({
                 goodsCode: code,
                 exchangeCode: 250,
@@ -223,44 +224,44 @@ export const quote = new (class {
             socket.closeQuoteServer();
         }
     }
-})
 
-/**
- * 添加订阅
- * @param goodsCodes 
- * @param key 
- * @returns 
- */
-export function addSubscribe(goodsCodes: string[], key?: string) {
-    const uuid = key ?? v4();
-    const value = quote.subscribeMap.get(uuid) ?? [];
+    /**
+     * 添加行情订阅
+     * @param goodsCodes 
+     * @param key 
+     * @returns 
+     */
+    addQuoteSubscribe(goodsCodes: string[], key?: string) {
+        const uuid = key ?? v4();
+        const value = this.quoteSubscribeMap.get(uuid) ?? [];
 
-    // 对相同 key 订阅的商品进行合并处理
-    quote.subscribeMap.set(uuid, [...value, ...goodsCodes]);
-    quote.subscribe();
+        // 对相同 key 订阅的商品进行合并处理
+        this.quoteSubscribeMap.set(uuid, [...value, ...goodsCodes]);
+        this.quoteSubscribe();
 
-    return {
-        uuid,
-        stop: () => {
-            console.log('删除订阅', uuid);
-            quote.subscribeMap.delete(uuid);
-            quote.subscribe();
-        },
+        return {
+            uuid,
+            stop: () => {
+                console.log('删除订阅', uuid);
+                this.quoteSubscribeMap.delete(uuid);
+                this.quoteSubscribe();
+            },
+        }
     }
-}
 
-/**
- * 删除订阅
- * @param keys 
- */
-export function removeSubscribe(...keys: string[]) {
-    if (keys.length) {
-        console.log('删除订阅', keys);
-        keys.forEach((key) => {
-            quote.subscribeMap.delete(key);
-        })
-    } else {
-        quote.subscribeMap.clear();
+    /**
+     * 删除行情订阅
+     * @param keys 
+     */
+    removeQuoteSubscribe(...keys: string[]) {
+        if (keys.length) {
+            console.log('删除订阅', keys);
+            keys.forEach((key) => {
+                this.quoteSubscribeMap.delete(key);
+            })
+        } else {
+            this.quoteSubscribeMap.clear();
+        }
+        this.quoteSubscribe();
     }
-    quote.subscribe();
-}
+})