li.shaoyi 3 năm trước cách đây
mục cha
commit
80f670cf2c

+ 0 - 1
src/business/account/index.ts

@@ -37,7 +37,6 @@ export function useAccount() {
                         LoginPWD: cryptojs.SHA256(res.data + account.LoginPWD).toString(),
                     },
                     success: (res) => {
-                        console.log(res)
                         sessionCache.setValue('loginInfo', res);
 
                         initBaseData().then(() => {

+ 25 - 0
src/business/common/index.ts

@@ -1,3 +1,4 @@
+import { computed } from 'vue'
 import { useRoute } from 'vue-router'
 import { queryGoodsList } from '@/services/api/goods'
 import { queryTaAccounts } from '@/services/api/account'
@@ -86,4 +87,28 @@ export function getAccountAuth(filtered: string[] = [], reverse = false) {
     }
 
     return result;
+}
+
+/**
+ * 通过 goodscode 获取盘面实时行情
+ * @param goodscode 
+ * @returns 
+ */
+export function getQuoteDayInfoByCode(goodscode: string) {
+    return computed(() => {
+        const quoteList = globalState.getValue('quoteList');
+        return quoteList.find((e) => e.goodscode.toUpperCase() === goodscode.toUpperCase());
+    })
+}
+
+/**
+ * 通过 goodscode 获取商品实时报价
+ * @param goodscode 
+ * @returns 
+ */
+export function getGoodsPriceByCode(goodscode: string) {
+    return computed(() => {
+        const quote = getQuoteDayInfoByCode(goodscode).value;
+        return quote?.last ?? 0;
+    })
 }

+ 36 - 36
src/business/quote/index.ts

@@ -184,46 +184,46 @@ export const quote = new (class {
 
         // 接收行情服务断线重连成功通知
         this.quoteServerReconnectNotify = eventBus.$on('quoteServerReconnectNotify', () => {
-            startSubscribe();
+            this.subscribe();
         })
     }
-})
 
-/**
- * 开始订阅
- */
-export function startSubscribe() {
-    const subscribeData: Proto.GoodsQuoteReq[] = [];
+    /**
+     * 开始订阅
+     */
+    subscribe() {
+        const subscribeData: Proto.GoodsQuoteReq[] = [];
 
-    quote.subscribeMap.forEach((value) => {
-        const item = value.map((code) => ({
-            goodsCode: code,
-            exchangeCode: 250,
-            subState: 0
-        }))
-        subscribeData.push(...item);
-    })
+        this.subscribeMap.forEach((value) => {
+            const item = value.map((code) => ({
+                goodsCode: code,
+                exchangeCode: 250,
+                subState: 0
+            }))
+            subscribeData.push(...item);
+        })
 
-    if (subscribeData.length) {
-        console.log('开始行情订阅', subscribeData)
-        quoteServerRequest({
-            data: subscribeData,
-            success: (res) => {
-                if (res.length) {
-                    console.log('行情订阅成功', res)
-                } else {
-                    console.error('行情订阅失败')
+        if (subscribeData.length) {
+            console.log('开始行情订阅', subscribeData)
+            quoteServerRequest({
+                data: subscribeData,
+                success: (res) => {
+                    if (res.length) {
+                        console.log('行情订阅成功', res)
+                    } else {
+                        console.error('行情订阅失败')
+                    }
+                },
+                fail: (err) => {
+                    console.error('行情订阅失败', err)
                 }
-            },
-            fail: (err) => {
-                console.error('行情订阅失败', err)
-            }
-        })
-    } else {
-        // 没有订阅商品的时候,断开连接
-        socket.closeQuoteServer();
+            })
+        } else {
+            // 没有订阅商品的时候,断开连接
+            socket.closeQuoteServer();
+        }
     }
-}
+})
 
 /**
  * 添加订阅
@@ -237,14 +237,14 @@ export function addSubscribe(goodsCodes: string[], key?: string) {
 
     // 对相同 key 订阅的商品进行合并处理
     quote.subscribeMap.set(uuid, [...value, ...goodsCodes]);
-    startSubscribe();
+    quote.subscribe();
 
     return {
         uuid,
         stop: () => {
             console.log('删除订阅', uuid);
             quote.subscribeMap.delete(uuid);
-            startSubscribe();
+            quote.subscribe();
         },
     }
 }
@@ -262,5 +262,5 @@ export function removeSubscribe(...keys: string[]) {
     } else {
         quote.subscribeMap.clear();
     }
-    startSubscribe();
+    quote.subscribe();
 }

+ 0 - 0
src/packages/pc/components/base/mask/index.less → src/components/base/mask/index.less


+ 4 - 3
src/packages/pc/components/base/mask/index.ts → src/components/base/mask/index.ts

@@ -23,13 +23,14 @@ export function useMask(isShow: boolean, delay = 200) {
     }));
 
     // 动画切换
-    const transition = (isShow: boolean) => {
-        setTimeout(() => {
+    const transition = (isShow: boolean, callback?: () => void) => {
+        window.setTimeout(() => {
             transitionClass.value = isShow ? 'is-show' : 'is-hide';
             // 阻止鼠标事件,防止动画过程中触发事件
             pointerEvents.value = true;
-            setTimeout(() => {
+            window.setTimeout(() => {
                 pointerEvents.value = false;
+                callback && callback();
             }, delay);
         }, 20);
     }

+ 1 - 1
src/packages/pc/components/base/mask/index.vue → src/components/base/mask/index.vue

@@ -30,7 +30,7 @@ const props = defineProps({
   // 遮罩堆叠顺序
   zIndex: {
     type: Number,
-    default: 3000,
+    default: 1000,
   },
   // 窗口动画时间
   delay: {

+ 0 - 0
src/packages/pc/components/base/mask/interface.ts → src/components/base/mask/interface.ts


+ 0 - 8
src/packages/pc/components/base/modal/index.less → src/components/base/modal/index.less

@@ -12,13 +12,6 @@
         overflow           : hidden;
     }
 
-    &__header,
-    &__footer {
-        &:empty {
-            display: none;
-        }
-    }
-
     &__body {
         flex       : 1;
         overflow-y : auto;
@@ -48,7 +41,6 @@
 
     &.center &__container {
         transition-property: transform, opacity;
-        margin-bottom      : 10%;
 
         &:not(.is-show) {
             opacity  : 0;

+ 13 - 11
src/packages/pc/components/base/modal/index.vue → src/components/base/modal/index.vue

@@ -2,25 +2,21 @@
 <template>
   <app-mask :class="['app-modal', direction]" :show="show" :delay="delay" @mask="emit('mask')">
     <div :class="['app-modal__container', transitionClass]" :style="transitionStyles">
-      <div class="app-modal__header">
-        <slot name="header"></slot>
-      </div>
+      <slot name="header"></slot>
       <div class="app-modal__body">
         <slot></slot>
       </div>
-      <div class="app-modal__footer">
-        <slot name="footer"></slot>
-      </div>
+      <slot name="footer"></slot>
     </div>
   </app-mask>
 </template>
 
 <script lang="ts" setup>
 import { watch, PropType } from 'vue'
-import { useMask } from '../mask';
-import AppMask from '../mask/index.vue';
+import { useMask } from '../mask'
+import AppMask from '../mask/index.vue'
 
-const emit = defineEmits(['mask']);
+const emit = defineEmits(['mask', 'opened', 'closed']);
 
 const props = defineProps({
   show: {
@@ -37,12 +33,18 @@ const props = defineProps({
     type: String as PropType<'full' | 'center' | 'left' | 'right' | 'top' | 'bottom' | 'left-top' | 'left-bottom' | 'right-top' | 'right-bottom'>,
     default: 'center',
   },
-});
+})
 
 const { transitionClass, transitionStyles, transition } = useMask(props.show, props.delay);
 
 watch(() => props.show, (isShow) => {
-  transition(isShow);
+  transition(isShow, () => {
+    if (isShow) {
+      emit('opened');
+    } else {
+      emit('closed');
+    }
+  })
 })
 </script>
 

+ 1 - 1
src/filters/index.ts

@@ -2,7 +2,7 @@
  * 处理价格颜色的显示
  */
 export function handlePriceColor(curValue: number, preValue: number) {
-    if (curValue === preValue) {
+    if (!curValue || curValue === preValue) {
         return '';
     } else if (curValue > preValue) {
         return 'g-color--up';

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

@@ -19,7 +19,7 @@ export function useComponent(callback?: () => void) {
     }
 
     // 关闭组件
-    const closeComponent = (isRefresh: boolean) => {
+    const closeComponent = (isRefresh?: boolean) => {
         componentId.value = '';
         // 是否刷新数据
         if (isRefresh && callback) {

+ 4 - 0
src/packages/mobile/assets/themes/style.less

@@ -34,6 +34,10 @@
         overflow-y                : auto;
         -webkit-overflow-scrolling: touch;
     }
+
+    &__footer {
+        margin-top: auto;
+    }
 }
 
 .g-color {

+ 17 - 4
src/packages/mobile/components/base/table/index.less

@@ -6,10 +6,23 @@
         text-align      : center;
         background-color: #fff;
         margin-bottom   : .2rem;
+        border          : 0;
+    }
+
+    & thead &__row {
+        background-color: #d9e2ed;
+    }
+
+    & thead &__cell {
+        padding: .12rem 0;
+    }
+
+    & tbody &__row {
+        background-color: #fff
+    }
 
-        th,
-        td {
-            padding: .1rem 0;
-        }
+    & tbody &__cell {
+        color  : #666;
+        padding: .12rem 0;
     }
 }

+ 12 - 8
src/packages/mobile/components/base/table/index.vue

@@ -1,30 +1,31 @@
 <template>
     <div class="app-table">
-        <table class="app-table__body">
+        <table class="app-table__body" cellspacing="0" cellpadding="0">
             <thead>
-                <Draggable :list="columns" tag="tr" item-key="key">
+                <Draggable class="app-table__row" :list="columns" tag="tr" item-key="key">
                     <template #header v-if="$slots.expand">
-                        <th></th>
+                        <th class="app-table__cell expand"></th>
                     </template>
                     <template #item="{ element }">
-                        <th>{{ element.label }}</th>
+                        <th class="app-table__cell">{{ element.label }}</th>
                     </template>
                 </Draggable>
             </thead>
             <tbody>
                 <template v-for="(row, i) in dataSource" :key="i">
                     <tr class="app-table__row" @click="rowClick(i)">
-                        <td v-if="$slots.expand">
+                        <td class="app-table__cell expand" v-if="$slots.expand">
                             <Icon name="arrow-down" v-if="selectedIndex === i" />
                             <Icon name="arrow" v-else />
                         </td>
-                        <td :class="column.className" v-for="(column, n) in columns" :key="i + n.toString()">
+                        <td class="app-table__cell" :class="column.className" v-for="(column, n) in columns"
+                            :key="i + n.toString()">
                             <slot :name="column.key" :value="row[column.key]" :row="row">{{ row[column.key] }}</slot>
                         </td>
                     </tr>
                     <!-- expand -->
                     <tr class="app-table__row expand" v-show="selectedIndex === i" v-if="$slots.expand">
-                        <td :colspan="columns.length + 1">
+                        <td class="app-table__cell" :colspan="columns.length + 1">
                             <slot name="expand" :row="row"></slot>
                         </td>
                     </tr>
@@ -40,7 +41,9 @@ import { Icon } from 'vant'
 import { TableColumn } from './interface'
 import Draggable from 'vuedraggable'
 
-defineProps({
+const emit = defineEmits(['rowClick'])
+
+const props = defineProps({
     // 数据列表
     dataSource: {
         type: Array,
@@ -60,6 +63,7 @@ const rowClick = (index: number) => {
     } else {
         selectedIndex.value = index;
     }
+    emit('rowClick', index, props.dataSource[index]);
 }
 </script>
 

+ 1 - 1
src/packages/mobile/components/layouts/navbar/index.vue

@@ -38,7 +38,7 @@ import { useRouter } from 'vue-router'
 import { Icon } from 'vant'
 import client from '@/utils/client'
 
-const emit = defineEmits(['ready', 'back']);
+const emit = defineEmits<{ (event: string, ...args: unknown[]): void }>()
 
 const props = defineProps({
   // 导航栏标题

+ 7 - 0
src/packages/mobile/components/modules/trade/index.less

@@ -0,0 +1,7 @@
+.app-trade {
+    .app-modal__container {
+        width           : 100%;
+        height          : 100%;
+        background-color: #f7f7f7;
+    }
+}

+ 31 - 0
src/packages/mobile/components/modules/trade/index.vue

@@ -0,0 +1,31 @@
+<template>
+    <app-modal class="app-trade" direction="right" :show="showModal">
+        <template #header>
+            <app-navbar title="挂牌求购" @back="closed" />
+        </template>
+        <AddressEdit show-postal show-delete show-set-default show-search-result
+            :area-columns-placeholder="['请选择', '请选择', '请选择']" />
+    </app-modal>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue'
+import { AddressEdit } from 'vant'
+import AppModal from '@/components/base/modal/index.vue'
+
+const showModal = ref(true);
+
+// 关闭弹窗
+const closed = () => {
+    showModal.value = false;
+}
+
+// 暴露组件属性给父组件调用
+defineExpose({
+    closed,
+})
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 1 - 1
src/packages/mobile/router/animateRouter.ts

@@ -112,7 +112,7 @@ export default new (class {
             back();
         }
 
-        router.afterEach((to) => {
+        router.beforeEach((to) => {
             this.handleState(to);
         })
 

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

@@ -5,7 +5,7 @@
       <Grid :column-num="3" style="margin-bottom: .2rem;">
         <GridItem icon="photo-o" text="商品" :to="{ name: 'order' }" v-for="index in 6" :key="index" />
       </Grid>
-      <AppTable :data-source="quoteList" :columns="columns">
+      <app-table :data-source="quoteList" :columns="columns" @row-click="rowClick">
         <template #last="{ row }">
           <span :class="handlePriceColor(row.last, row.presettle)">{{ handleNoneValue(row.last) }}</span>
         </template>
@@ -21,10 +21,10 @@
         <template #askvolume="{ row }">
           <span :class="handlePriceColor(row.ask, row.presettle)">{{ handleNoneValue(row.askvolume) }}</span>
         </template>
-      </AppTable>
+      </app-table>
       <Button @click="showAction = true" plain block>更换主题</Button>
     </div>
-    <ActionSheet v-model:show="showAction" :actions="actions" teleport="body" cancel-text="取消" @select="onSelect" />
+    <ActionSheet v-model:show="showAction" :actions="actions" teleport="body" cancel-text="取消" @select="themeChange" />
   </div>
 </template>
 
@@ -58,7 +58,7 @@ const actions: ActionSheetAction[] = [
   { name: '深色' }
 ]
 
-const onSelect = (action: ActionSheetAction) => {
+const themeChange = (action: ActionSheetAction) => {
   switch (action.name) {
     case '默认': {
       theme.setTheme('default');
@@ -76,6 +76,10 @@ const onSelect = (action: ActionSheetAction) => {
   showAction.value = false;
 }
 
+const rowClick = (index: number) => {
+  console.log('row', index);
+}
+
 onActivated(() => {
   addSubscribe(['pp2205', 'pp2206', 'b2205', 'b2206'], uuid);
 })

+ 1 - 1
src/packages/mobile/views/home/index.vue

@@ -3,7 +3,7 @@
     <keep-alive>
       <component class="g-flex__body" :is="components[componentId]"></component>
     </keep-alive>
-    <app-tabbar :data-source="tabList" @change="onChange" fixed></app-tabbar>
+    <AppTabbar :data-source="tabList" @change="onChange" fixed />
   </div>
 </template>
 

+ 5 - 0
src/packages/mobile/views/order/detail/index.less

@@ -0,0 +1,5 @@
+.order-detail {
+    &__footer {
+        padding: .28rem;
+    }
+}

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

@@ -1,5 +1,36 @@
 <template>
   <div class="order-detail g-flex">
     <app-navbar title="详情" />
+    <div class="order-detail__footer g-flex__footer">
+      <Button @click="openComponent('trade')" type="primary" round block>挂牌求购</Button>
+    </div>
+    <component ref="childComponent" :is="components[componentId]" @closed="closeComponent" />
   </div>
-</template>
+</template>
+
+<script lang="ts" setup>
+import { defineAsyncComponent, ref } from 'vue'
+import { onBeforeRouteLeave } from 'vue-router'
+import { Button } from 'vant'
+import { useComponent } from '@/hooks/component'
+
+const components = {
+  trade: defineAsyncComponent(() => import('@mobile/components/modules/trade/index.vue')),
+}
+
+const { componentId, openComponent, closeComponent } = useComponent();
+const childComponent = ref();
+
+onBeforeRouteLeave((to, from, next) => {
+  if (componentId.value) {
+    childComponent.value?.closed(); // 调用子组件方法
+    next(false);
+  } else {
+    next();
+  }
+})
+</script>
+
+<style lang="less">
+@import './index.less';
+</style>

+ 1 - 1
src/packages/mobile/views/order/index/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="order g-flex">
-    <app-navbar title="订单列表">
+    <app-navbar title="行情">
       <template #footer>
         <Search v-model="state.keyword" placeholder="请输入搜索关键词" shape="round" />
       </template>

+ 5 - 11
src/packages/pc/components/base/drawer/index.vue

@@ -1,8 +1,8 @@
 <!-- 抽屉弹框组件 -->
 <template>
-  <app-modal :class="['app-drawer', border && 'border']" :show="showDrawer" :delay="delay" @mask="closed">
+  <app-modal :class="['app-drawer', border && 'border']" :show="showModal" @mask="closed">
     <template #header>
-      <div class="app-drawer__title">{{title}}</div>
+      <div class="app-drawer__title">{{ title }}</div>
       <div class="app-drawer__close" @click="closed">关闭</div>
     </template>
     <template #default>
@@ -16,9 +16,7 @@
 
 <script lang="ts" setup>
 import { ref } from 'vue'
-import AppModal from '../modal/index.vue'
-
-const emit = defineEmits(['closed']);
+import AppModal from '@/components/base/modal/index.vue'
 
 const props = defineProps({
   title: {
@@ -35,16 +33,12 @@ const props = defineProps({
   },
 })
 
-const showDrawer = ref(true);
-const delay = ref(200);
+const showModal = ref(true);
 
 // 关闭弹窗
 const closed = () => {
   if (!props.loading) {
-    showDrawer.value = false;
-    setTimeout(() => {
-      emit('closed');
-    }, delay.value);
+    showModal.value = false;
   }
 }
 </script>

+ 1 - 1
src/packages/pc/views/market/quote/components/detail/index.vue

@@ -13,7 +13,7 @@
 <script lang="ts" setup>
 import { ref } from 'vue'
 import { queryGoodsList } from '@/services/api/goods'
-import AppModal from '@pc/components/base/modal/index.vue'
+import AppModal from '@/components/base/modal/index.vue'
 
 const emit = defineEmits(['closed'])
 

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

@@ -18,8 +18,8 @@
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent, onBeforeUnmount } from 'vue';
-import { queryGoodsList } from '@/services/api/goods';
+import { defineAsyncComponent, onBeforeUnmount } from 'vue'
+import { queryGoodsList } from '@/services/api/goods'
 import { getAccountAuth } from '@/business/common'
 import { useComponent } from '@/hooks/component'
 import { useTable } from '@pc/components/base/table'

+ 2 - 1
src/services/socket/trade/index.ts

@@ -33,7 +33,7 @@ function getProtoHeader(funCode: keyof typeof FunCode, header?: IMessageHead, ma
  */
 function tradeServerMiddleware<Req, Rsp>(params: TradeRequest<Req, Rsp>, reqCode: keyof typeof FunCode, rspCode: keyof typeof FunCode, marketId?: number): Promise<Rsp> {
     params.data.Header = getProtoHeader(reqCode, params.data.Header, marketId);
-    console.log(reqCode, params.data);
+    console.log(reqCode, FunCode[reqCode], params.data);
 
     return new Promise((resolve, reject) => {
         Protobuf.requestEncode(reqCode, params.data).then((res) => {
@@ -45,6 +45,7 @@ function tradeServerMiddleware<Req, Rsp>(params: TradeRequest<Req, Rsp>, reqCode
                 success: (raw) => {
                     if (raw.funCode === FunCode[rspCode]) {
                         Protobuf.responseDecode<Rsp>(rspCode, raw.content).then((res) => {
+                            console.log(rspCode, FunCode[rspCode], res);
                             resolve(res);
                         }).catch((msg) => {
                             console.warn(rspCode, msg);