li.shaoyi il y a 2 ans
Parent
commit
02f91aa4eb

+ 0 - 2
src/hooks/component/index.ts

@@ -31,7 +31,6 @@ export function useComponent(callback?: (componentName?: string) => void) {
      * @param componentName 
      */
     const openComponent = (componentName: string) => {
-        console.log('打开组件:' + componentName)
         components.add(componentName)
         componentId.value = componentName
     }
@@ -43,7 +42,6 @@ export function useComponent(callback?: (componentName?: string) => void) {
     const closeComponent = (isCallback?: boolean) => {
         const componentName = componentId.value
         if (componentName) {
-            console.log('关闭组件:' + componentName)
             components.delete(componentName)
             componentId.value = [...components.values()].pop()
 

+ 11 - 9
src/hooks/navigation/index.ts

@@ -1,4 +1,3 @@
-import { watch } from 'vue'
 import { useRoute, useRouter, onBeforeRouteLeave } from 'vue-router'
 import animateRouter from '@mobile/router/animateRouter'
 
@@ -70,17 +69,20 @@ export function useNavigation() {
     // }
 
     // 返回主页
-    const backHomePage = <T extends object>(params?: T, callback?: () => void) => {
+    const backHome = () => {
         const { state } = animateRouter
         const delta = state.history.length - 1
-        setGlobalUrlParams(params)
+        const params = { tabIndex: 0 }
+
         if (delta) {
+            setGlobalUrlParams(params)
             router.go(-delta)
-            watch(() => route.path, () => {
-                callback && callback()
-            })
         } else {
-            callback && callback()
+            const page = state.history[0]
+            if (page?.name !== 'home-index') {
+                setGlobalUrlParams(params)
+                router.replace({ name: 'home-index' })
+            }
         }
     }
 
@@ -99,7 +101,7 @@ export function useNavigation() {
         }
     }
 
-    // 导航守卫
+    // 路由守卫
     const beforeRouteLeave = (callback: () => boolean) => {
         onBeforeRouteLeave((to, from, next) => {
             if (callback()) {
@@ -119,7 +121,7 @@ export function useNavigation() {
         getParamString,
         getQueryString,
         getQueryStringToNumber,
-        backHomePage,
+        backHome,
         routerBack,
         routerTo,
         beforeRouteLeave,

+ 36 - 1
src/hooks/request/index.ts

@@ -1,4 +1,4 @@
-import { ref, onUnmounted } from 'vue'
+import { ref, onUnmounted, UnwrapRef } from 'vue'
 import { CommonResult } from '@/services/http/interface'
 import { useDataTable } from '../datatable'
 
@@ -100,4 +100,39 @@ export function useRequest<TParams extends object, TResponse>(request: (params:
         run,
         cancel,
     }
+}
+
+// 待完善
+export function useDataList<T>(pageSize: number) {
+    const dataList = ref<T[]>([])
+    const currentPage = ref(1) // 当前点击所在的页码
+    const startIndex = ref(0) // 当前页码的起始位置,用于数组 splice 替换或删除
+
+    const getItem = (index: number) => {
+        currentPage.value = Math.ceil((index + 1) / pageSize)
+        startIndex.value = (currentPage.value - 1) * pageSize
+        return dataList.value[index]
+    }
+
+    // 向列表中追加数据
+    const appendData = (data: UnwrapRef<T[]>, empty = false) => {
+        if (empty) {
+            dataList.value = []
+        }
+        dataList.value.push(...data)
+    }
+
+    // 页面返回刷新列表
+    const updateData = (data: UnwrapRef<T[]>) => {
+        dataList.value.splice(startIndex.value, pageSize, ...data)
+        // 去重
+    }
+
+    return {
+        dataList,
+        currentPage,
+        getItem,
+        appendData,
+        updateData,
+    }
 }

+ 5 - 21
src/packages/mobile/App.vue

@@ -1,26 +1,15 @@
 <template>
-  <router-view v-if="isRouterAlive" />
+  <router-view />
 </template>
 
 <script lang="ts" setup>
-import { shallowRef, nextTick } from 'vue'
 import { useNavigation } from '@/hooks/navigation'
 import { dialog } from '@/utils/vant'
 import { useLogin } from '@/business/login'
 import eventBus from '@/services/bus'
 
 const { userLogout } = useLogin()
-const { backHomePage } = useNavigation()
-const isRouterAlive = shallowRef(true) // 用于页面重新渲染
-
-const quit = (showLogin = false) => {
-  isRouterAlive.value = false
-  backHomePage({ tabName: 'home', showLogin }, () => {
-    nextTick(() => {
-      isRouterAlive.value = true
-    })
-  })
-}
+const { backHome } = useNavigation()
 
 // 接收用户登出通知
 eventBus.$on('LogoutNotify', (msg) => {
@@ -28,16 +17,11 @@ eventBus.$on('LogoutNotify', (msg) => {
     if (msg) {
       dialog({
         message: msg as string,
-        showCancelButton: true,
-        cancelButtonText: '退出',
-        confirmButtonText: '重新登录'
-      }).then(() => {
-        quit(true)
-      }).catch(() => {
-        quit()
+      }).finally(() => {
+        backHome()
       })
     } else {
-      quit()
+      backHome()
     }
   })
 })

+ 11 - 15
src/packages/mobile/components/layouts/page/index.backup.less → src/packages/mobile/components/base/router-transition/index.backup.less

@@ -1,8 +1,10 @@
-.route-in-leave-from,
-.route-in-enter-from,
-.route-out-enter-from,
-.route-out-enter-from {
-    will-change: transform;
+/* 动画时长 */
+@transition-duration: 220ms;
+
+/* 无过渡效果 */
+.delay-enter-active,
+.delay-leave-active {
+    transition-duration: @transition-duration;
 }
 
 .route-in-enter-active,
@@ -11,7 +13,8 @@
 .route-out-leave-active {
     pointer-events: none;
     position: absolute;
-    transition: transform 200ms;
+    transition-property: transform;
+    transition-duration: @transition-duration;
     background-color: #fff;
 }
 
@@ -26,18 +29,11 @@
 }
 
 .route-out-leave-active {
-    //transition-delay: 35ms;
     transform: translate3d(100%, 0, 0);
+    transition-timing-function: ease-in;
 }
 
 .route-in-leave-active {
-    //transition-delay: 35ms;
     transform: translate3d(-100%, 0, 0);
-}
-
-.app-page {
-    width: 100%;
-    height: 100%;
-    overflow-y: auto;
-    background-color: #f6f6f6;
+    transition-timing-function: ease-in;
 }

+ 83 - 0
src/packages/mobile/components/base/router-transition/index.less

@@ -0,0 +1,83 @@
+/* 动画时长 */
+@transition-duration: 220ms;
+
+/* 无过渡效果 */
+.delay-enter-active,
+.delay-leave-active {
+    transition-duration: @transition-duration;
+}
+
+.route-in-leave-from,
+.route-in-enter-from,
+.route-out-enter-from,
+.route-out-enter-from {
+    will-change: transform, opacity;
+}
+
+.route-in-leave-to,
+.route-in-enter-active,
+.route-out-leave-active,
+.route-out-enter-to {
+    pointer-events: none;
+    transition: transform @transition-duration;
+}
+
+.route-in-leave-from {
+    &::after {
+        content: '';
+        opacity: 0;
+    }
+}
+
+.route-in-leave-to {
+    &::after {
+        content: '';
+        position: absolute;
+        z-index: 100;
+        top: 0;
+        left: 0;
+        width: 100vw;
+        height: 100vh;
+        background-color: rgba(0, 0, 0, .5);
+        opacity: 1;
+        transition: opacity @transition-duration;
+    }
+}
+
+.route-in-enter-from {
+    transform: translate3d(100%, 0, 0);
+}
+
+.route-in-enter-active {
+    position: absolute;
+    z-index: 1000;
+    top: 0;
+}
+
+.route-out-leave-active {
+    position: absolute;
+    z-index: 1000;
+    transform: translate3d(100%, 0, 0);
+}
+
+.route-out-enter-from {
+    &::after {
+        content: '';
+        opacity: 1;
+    }
+}
+
+.route-out-enter-to {
+    &::after {
+        content: '';
+        position: absolute;
+        z-index: 100;
+        top: 0;
+        left: 0;
+        width: 100vw;
+        height: 100vh;
+        background-color: rgba(0, 0, 0, .5);
+        opacity: 0;
+        transition: opacity @transition-duration;
+    }
+}

+ 18 - 0
src/packages/mobile/components/base/router-transition/index.vue

@@ -0,0 +1,18 @@
+<template>
+    <transition :name="transitionName">
+        <slot></slot>
+    </transition>
+</template>
+  
+<script lang="ts" setup>
+defineProps({
+    transitionName: {
+        type: String,
+        default: 'delay'
+    }
+})
+</script>
+  
+<style lang="less">
+@import './index.less';
+</style>

+ 1 - 1
src/packages/mobile/components/base/tabbar/index.vue

@@ -59,7 +59,7 @@ const styles = computed(() => ({
 
 const onClick = (index: number) => {
   if (props.dataIndex !== index) {
-    emit('click', props.dataList[index], index)
+    emit('click', index)
   }
 }
 </script>

+ 0 - 75
src/packages/mobile/components/layouts/page/index.less

@@ -1,78 +1,3 @@
-.route-in-leave-from,
-.route-in-enter-from,
-.route-out-enter-from,
-.route-out-enter-from {
-    will-change: transform, opacity;
-}
-
-.route-in-leave-to,
-.route-in-enter-active,
-.route-out-leave-active,
-.route-out-enter-to {
-    pointer-events: none;
-    transition: transform 220ms;
-}
-
-.route-in-leave-from {
-    &::after {
-        content: '';
-        opacity: 0;
-    }
-}
-
-.route-in-leave-to {
-    &::after {
-        content: '';
-        position: absolute;
-        z-index: 100;
-        top: 0;
-        left: 0;
-        width: 100vw;
-        height: 100vh;
-        background-color: rgba(0, 0, 0, .5);
-        opacity: 1;
-        transition: opacity 220ms;
-    }
-}
-
-.route-in-enter-from {
-    transform: translate3d(100%, 0, 0);
-}
-
-.route-in-enter-active {
-    position: absolute;
-    z-index: 1000;
-    top: 0;
-}
-
-.route-out-leave-active {
-    position: absolute;
-    z-index: 1000;
-    transform: translate3d(100%, 0, 0);
-}
-
-.route-out-enter-from {
-    &::after {
-        content: '';
-        opacity: 1;
-    }
-}
-
-.route-out-enter-to {
-    &::after {
-        content: '';
-        position: absolute;
-        z-index: 100;
-        top: 0;
-        left: 0;
-        width: 100vw;
-        height: 100vh;
-        background-color: rgba(0, 0, 0, .5);
-        opacity: 0;
-        transition: opacity 220ms;
-    }
-}
-
 .app-page {
     width: 100%;
     height: 100%;

+ 13 - 4
src/packages/mobile/components/layouts/page/index.vue

@@ -1,16 +1,18 @@
 <template>
   <router-view class="app-page" v-slot="{ Component, route }">
-    <transition :name="state.transitionName" @leave="onLeave" @after-enter="onAfterEnter">
+    <RouterTransition :transition-name="state.transitionName" @leave="onLeave" @after-enter="onAfterEnter">
+      <!-- 缓存组件,前进刷新,后退缓存 -->
       <keep-alive :exclude="state.excludeViews">
-        <component :is="handleComponent(Component, route)" :key="$route.fullPath" />
+        <component :is="handleComponent(Component, route)" :key="getRouteKey(route)" />
       </keep-alive>
-    </transition>
+    </RouterTransition>
   </router-view>
 </template>
 
 <script lang="ts" setup>
-import { RouteRecordNormalized, RouteRecordName } from 'vue-router'
+import { RouteLocationNormalized, RouteRecordNormalized, RouteRecordName } from 'vue-router'
 import animateRouter from '@mobile/router/animateRouter'
+import RouterTransition from '@mobile/components/base/router-transition/index.vue'
 import http from '@/services/http'
 
 const { state } = animateRouter
@@ -23,6 +25,13 @@ const handleComponent = (component: Record<'type', { name: RouteRecordName | und
   return component
 }
 
+// key 的作用是当 $route.query 参数发生变化时会刷新页面
+// 不直接使用 $route.fullPath 的原因是每次都会刷新页面,导致嵌套路由缓存失效
+const getRouteKey = (route: RouteLocationNormalized) => {
+  const qs = Object.keys(route.query)
+  return qs.length ? route.fullPath : undefined
+}
+
 const onLeave = () => {
   http.loading = true
 }

+ 26 - 4
src/packages/mobile/router/index.ts

@@ -30,11 +30,33 @@ const routes: Array<RouteRecordRaw> = [
     children: [
       {
         path: "",
-        name: "Home",
+        name: "home",
         component: () => import("../views/home/index.vue"),
-        meta: {
-          ignoreAuth: true,
-        },
+        children: [
+          {
+            path: '',
+            name: 'home-index',
+            component: () => import('../views/home/main/index.vue'),
+            meta: {
+              ignoreAuth: true,
+            },
+          },
+          {
+            path: 'purchase',
+            name: 'home-purchase',
+            component: () => import('../views/purchase/list/index.vue'),
+          },
+          {
+            path: 'supply-demand',
+            name: 'home-supply-demand',
+            component: () => import('../views/supply-demand/list/index.vue'),
+          },
+          {
+            path: 'mine',
+            name: 'home-mine',
+            component: () => import('../views/mine/main/index.vue'),
+          }
+        ]
       },
     ],
   },

+ 2 - 2
src/packages/mobile/views/boot/index.vue

@@ -63,11 +63,11 @@ const skip = (promise: Promise<void>) => {
     if (redirect) {
       router.replace(redirect.toString())
     } else {
-      router.replace({ name: 'Home' })
+      router.replace({ name: 'home' })
     }
   }).catch((err) => {
     if (service.isReady) {
-      router.replace({ name: 'Home' })
+      router.replace({ name: 'home' })
     } else {
       tryInit(err)
     }

+ 5 - 14
src/packages/mobile/views/credit/signin/index.vue

@@ -83,7 +83,7 @@
                         </div>
                     </div>
                     <div class="list-item__button">
-                        <Button type="primary" round @click="toPurchase">去完成</Button>
+                        <Button type="primary" round @click="navigationBack(1)">去完成</Button>
                     </div>
                 </dd>
                 <dd class="list-item">
@@ -97,7 +97,7 @@
                         </div>
                     </div>
                     <div class="list-item__button">
-                        <Button type="primary" round @click="toSupplyDemand">去完成</Button>
+                        <Button type="primary" round @click="navigationBack(2)">去完成</Button>
                     </div>
                 </dd>
             </dl>
@@ -135,18 +135,9 @@ const getScoreConfig = (value: number) => {
     return item?.parma1 ?? 0
 }
 
-// 跳转到采购
-const toPurchase = () => {
-    routerBack({
-        tabName: 'purchase'
-    })
-}
-
-// 跳转到供求
-const toSupplyDemand = () => {
-    routerBack({
-        tabName: 'supplyDemand'
-    })
+// 页面返回
+const navigationBack = (index: number) => {
+    routerBack({ tabIndex: index })
 }
 
 const userSignin = () => {

+ 54 - 56
src/packages/mobile/views/home/index.vue

@@ -1,14 +1,19 @@
 <template>
   <div class="home g-flex">
-    <keep-alive>
-      <component class="g-flex__body" :is="components[componentId]"></component>
-    </keep-alive>
-    <app-tabbar class="home-tabbar" :data-list="tabList" :dataIndex="tabIndex" @click="onTabClick" />
+    <router-view v-slot="{ Component }">
+      <RouterTransition :css="cssTransition">
+        <!-- 缓存所有组件 -->
+        <keep-alive>
+          <component class="g-flex__body" :is="Component" />
+        </keep-alive>
+      </RouterTransition>
+    </router-view>
+    <app-tabbar class="home-tabbar" :data-list="tabList" :data-index="currentTab" @click="onTabClick" />
   </div>
 </template>
 
 <script lang="ts" setup>
-import { shallowRef, onActivated, onMounted, onUnmounted } from 'vue'
+import { shallowRef, nextTick, watch, onMounted } from 'vue'
 import { fullloading, dialog } from '@/utils/vant'
 import { Tabbar } from '@mobile/components/base/tabbar/interface'
 import { GetAppUpdateInfo } from '@/services/api/common'
@@ -17,60 +22,53 @@ import { useLogin } from '@/business/login'
 import { loginStore } from '@/stores'
 import plus from '@/utils/h5plus'
 import AppTabbar from '@mobile/components/base/tabbar/index.vue'
-import Home from './components/main/index.vue'
-import Purchase from '@mobile/views/purchase/list/index.vue'
-import SupplyDemand from '@mobile/views/supply-demand/list/index.vue'
-import Mine from '@mobile/views/mine/main/index.vue'
-
-const components = {
-  home: Home,
-  purchase: Purchase,
-  supplyDemand: SupplyDemand,
-  mine: Mine,
-}
+import RouterTransition from '@mobile/components/base/router-transition/index.vue'
 
 const { token } = loginStore.$mapGetters()
+const { route, routerTo, getGlobalUrlParams } = useNavigation()
 const { autoLogin } = useLogin()
-const { routerTo, getGlobalUrlParams } = useNavigation()
-const componentId = shallowRef('home')
-const tabIndex = shallowRef(0)
+const cssTransition = shallowRef(true) // 是否使用css动画
+const currentTab = shallowRef(0)
 
 const tabList: Tabbar[] = [
   {
-    name: 'home',
+    name: 'home-index',
     label: '首页',
     icon: 'icon-home',
     activeIcon: 'icon-home-active',
   },
   {
-    name: 'purchase',
+    name: 'home-purchase',
     label: '采购',
     icon: 'icon-purchase',
     activeIcon: 'icon-purchase-active',
   },
   {
-    name: 'supplyDemand',
+    name: 'home-supply-demand',
     label: '供求',
     icon: 'icon-supply-demand',
     activeIcon: 'icon-supply-demand-active',
   },
   {
-    name: 'mine',
+    name: 'home-mine',
     label: '我的',
     icon: 'icon-mine',
     activeIcon: 'icon-mine-active',
   }
 ]
 
-const onTabClick = ({ name }: Tabbar, index: number) => {
-  if (name === 'home' || token.value) {
-    tabIndex.value = index
-    componentId.value = name
+const onTabClick = (index: number) => {
+  const { name } = tabList[index]
+  cssTransition.value = false
+
+  if (name === 'home-index' || token.value) {
+    currentTab.value = index
+    routerTo(name, true)
   } else {
     fullloading((hideLoading) => {
       autoLogin().then(() => {
-        tabIndex.value = index
-        componentId.value = name
+        currentTab.value = index
+        routerTo(name, true)
       }).catch(() => {
         routerTo('UserLogin')
       }).finally(() => {
@@ -95,26 +93,29 @@ const versionToNumber = (value: number | string) => {
   return 0
 }
 
-const ondownload = plus.onDownload((filename: string, progress: number) => {
-  if (progress === 100) {
-    dialog({
-      message: '新版本下载完成,是否安装?',
-      showCancelButton: true,
-      confirmButtonText: '安装'
-    }).then(() => {
-      plus.installApp(filename)
-    }).catch(() => {
-      plus.deleteFile(filename)
-    })
-  }
-})
-
 onMounted(() => {
+  currentTab.value = tabList.findIndex((e) => e.name === route.name)
+
   const os = plus.getSystemInfo('os')
   const currentVersion = plus.getSystemInfo('version')
   const currentVersionCode = plus.getSystemInfo('versionCode')
 
   if (os === 'Android') {
+    // 监听下载进度
+    const ondownload = plus.onDownload((filename, progress) => {
+      if (progress === 100) {
+        dialog({
+          message: '新版本下载完成,是否安装?',
+          showCancelButton: true,
+          confirmButtonText: '安装'
+        }).then(() => {
+          plus.installApp(filename)
+        }).catch(() => {
+          plus.deleteFile(filename)
+        })
+      }
+    })
+
     // 获取应用更新信息
     GetAppUpdateInfo().then((res) => {
       const data = JSON.parse(res)
@@ -127,6 +128,8 @@ onMounted(() => {
             confirmButtonText: '下载'
           }).then(() => {
             plus.createDownload(ApkUrl)
+          }).catch(() => {
+            ondownload.cancel()
           })
         }
       }
@@ -154,19 +157,14 @@ onMounted(() => {
   }
 })
 
-onUnmounted(() => {
-  ondownload.cancel()
-})
-
-onActivated(() => {
-  const { tabName, showLogin } = getGlobalUrlParams()
-  const index = tabList.findIndex((e) => e.name === tabName)
-  if (index > -1) {
-    tabIndex.value = index
-    componentId.value = tabList[index].name
-  }
-  if (showLogin) {
-    routerTo('UserLogin')
+watch(() => route.name, () => {
+  const { tabIndex } = getGlobalUrlParams()
+  if (tabIndex > -1) {
+    onTabClick(tabIndex)
+  } else {
+    nextTick(() => {
+      cssTransition.value = true
+    })
   }
 })
 </script>

+ 0 - 0
src/packages/mobile/views/home/components/main/index.less → src/packages/mobile/views/home/main/index.less


+ 0 - 0
src/packages/mobile/views/home/components/main/index.vue → src/packages/mobile/views/home/main/index.vue


+ 1 - 1
src/packages/mobile/views/market/spot/index.vue

@@ -63,6 +63,6 @@ const onRowClick = (row: Model.ThjSpotQuoteConfigRsp) => {
 }
 </script>
 
-<style lang="less">
+<style lang="less" scoped>
 @import './index.less';
 </style>

+ 1 - 1
src/packages/mobile/views/news/details/index.vue

@@ -30,7 +30,7 @@
                 <template v-for="(item, index) in dataList" :key="index">
                     <Cell class="article-item" title-class="article-item__title" value-class="article-item__time"
                         :title="item.title" :value="formatDate(item.publishdate, 'MM/DD')"
-                        :to="{ name: 'news-details', query: { id: item.id, t: new Date().getTime() } }" replace />
+                        :to="{ name: 'news-details', query: { id: item.id } }" replace />
                 </template>
             </CellGroup>
         </template>

+ 3 - 0
src/packages/pc/components/base/table/index.less

@@ -1,4 +1,7 @@
 .app-table {
+    width: 100%;
+    line-height: normal;
+
     &:not(:first-child) {
         margin-top: 20px;
     }

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

@@ -4,6 +4,7 @@ import { FunCode } from '@/constants/funcode'
 import { loginStore, errorInfoStore } from '@/stores'
 import { IMessageHead } from './protobuf/proto'
 import { TradeParams, TradeResponse } from './interface'
+import cryptojs from 'crypto-js'
 import Protobuf from './protobuf'
 import socket from '../index'
 
@@ -88,8 +89,10 @@ export async function tradeServerRequest<Req, Rsp>(reqKey: keyof typeof FunCode,
             }
             case 12018: {
                 if (RetDesc) {
+                    const { Utf8, Base64 } = cryptojs.enc
+                    const word = Base64.parse(RetDesc)
                     // 解析base64
-                    res.RetDesc = Buffer.from(RetDesc, 'base64').toString();
+                    res.RetDesc = Utf8.stringify(word)
                 }
                 return Promise.reject(res.RetDesc);
             }

+ 9 - 0
src/utils/h5plus/index.ts

@@ -88,6 +88,15 @@ export default new (class {
     }
 
     /**
+     * 退出应用程序
+     */
+    quit() {
+        this.onPlusReady((plus) => {
+            plus.runtime.quit()
+        })
+    }
+
+    /**
      * 获取系统信息
      * @param prop 
      * @returns