li.shaoyi il y a 2 ans
Parent
commit
70bb38e8c8

+ 14 - 0
package-lock.json

@@ -20,6 +20,7 @@
         "html5-qrcode": "^2.2.5",
         "long": "^5.2.0",
         "moment": "^2.29.3",
+        "photoswipe": "^5.3.6",
         "protobufjs": "^6.11.2",
         "qrcode": "^1.5.1",
         "sortablejs": "^1.15.0",
@@ -9327,6 +9328,14 @@
         "node": ">=8"
       }
     },
+    "node_modules/photoswipe": {
+      "version": "5.3.6",
+      "resolved": "https://registry.npmmirror.com/photoswipe/-/photoswipe-5.3.6.tgz",
+      "integrity": "sha512-v7e8iMfaPUujTACYsK5HBCCtFoW9n2dMZmjIlbvFS2oSpTQmPrfc3PrWnGx8OGY3jNOKho8JC8L277+m+9ag9Q==",
+      "engines": {
+        "node": ">= 0.12.0"
+      }
+    },
     "node_modules/picocolors": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -19849,6 +19858,11 @@
       "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
       "dev": true
     },
+    "photoswipe": {
+      "version": "5.3.6",
+      "resolved": "https://registry.npmmirror.com/photoswipe/-/photoswipe-5.3.6.tgz",
+      "integrity": "sha512-v7e8iMfaPUujTACYsK5HBCCtFoW9n2dMZmjIlbvFS2oSpTQmPrfc3PrWnGx8OGY3jNOKho8JC8L277+m+9ag9Q=="
+    },
     "picocolors": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",

+ 1 - 0
package.json

@@ -22,6 +22,7 @@
     "html5-qrcode": "^2.2.5",
     "long": "^5.2.0",
     "moment": "^2.29.3",
+    "photoswipe": "^5.3.6",
     "protobufjs": "^6.11.2",
     "qrcode": "^1.5.1",
     "sortablejs": "^1.15.0",

+ 30 - 31
src/business/login/index.ts

@@ -26,14 +26,6 @@ export function useLogin() {
         DeviceID: ''
     })
 
-    const loadBaseData = async () => {
-        await service.onReady() // 等待服务初始化
-        await Promise.all([
-            errorInfoStore.actions.getErrorInfoList(),
-            enumStore.actions.getAllEnumList(),
-        ])
-    }
-
     const loadUserData = async () => {
         await checkToken() // 令牌校验
         await userStore.actions.getUserData()
@@ -59,28 +51,16 @@ export function useLogin() {
         eventBus.$emit('LoginNotify') // 登录成功通知
     }
 
-    // 初始化业务数据
-    const initBaseData = async (autoLogin = false) => {
-        logining.value = true
+    // 自动登录
+    const autoLogin = async () => {
         try {
-            // 等待加载业务数据
-            await loadBaseData()
-            // 自动登录
-            if (autoLogin) {
-                const encryptedData = localData.getValue('autoLoginEncryptedData')
-                if (encryptedData) {
-                    try {
-                        const decryptedString = decryptAES(encryptedData)
-                        return await loginAction(JSON.parse(decryptedString))
-                    } catch (err) {
-                        console.error(err)
-                        eventBus.$emit('LogoutNotify')
-                    }
-                }
-            }
-            if (token.value) {
-                await loadUserData()
+            logining.value = true
+            const encryptedData = localData.getValue('autoLoginEncryptedData')
+            if (encryptedData) {
+                const decryptedString = decryptAES(encryptedData)
+                return await loginAction(JSON.parse(decryptedString))
             }
+            return Promise.reject('自动登录失败')
         } finally {
             logining.value = false
         }
@@ -92,7 +72,6 @@ export function useLogin() {
         formData.ClientType = clientType
         try {
             const params = { ...formData }
-            await loadBaseData()
             await queryLoginId({
                 username: formData.LoginID
             }).then((res) => {
@@ -109,15 +88,35 @@ export function useLogin() {
     const userLogout = (callback?: () => void) => {
         socket.closeAll()
         timerTask.clearAll()
-        sessionData.reset()
-        localData.reset('autoLoginEncryptedData')
+        sessionData.reset('loginInfo', 'userRoutes')
         callback && callback()
     }
 
+    // 初始化业务数据
+    const initBaseData = async (enableAutoLogin = false) => {
+        try {
+            logining.value = true
+            await service.onReady() // 等待服务初始化
+            await Promise.all([
+                errorInfoStore.actions.getErrorInfoList(),
+                enumStore.actions.getAllEnumList(),
+            ])
+            if (token.value) {
+                await loadUserData()
+            } else if (enableAutoLogin) {
+                await autoLogin()
+            }
+            logining.value = false
+        } catch {
+            logining.value = false
+        }
+    }
+
     return {
         logining,
         formData,
         initBaseData,
+        autoLogin,
         userLogin,
         userLogout,
     }

+ 61 - 0
src/packages/mobile/components/base/html-container/index.vue

@@ -0,0 +1,61 @@
+<template>
+    <div ref="htmlRef" v-html="formatHtmlString(context)"></div>
+</template>
+  
+<script lang="ts" setup>
+import { shallowRef, onMounted, onUnmounted } from 'vue'
+import { formatHtmlString } from '@/filters'
+import plus from '@/utils/h5plus'
+import PhotoSwipeLightbox, { SlideData } from 'photoswipe/lightbox'
+import 'photoswipe/style.css'
+
+defineProps({
+    context: {
+        type: String,
+        default: ''
+    }
+})
+
+let lightbox: PhotoSwipeLightbox = null
+const htmlRef = shallowRef<HTMLElement>()
+
+onMounted(() => {
+    const el = htmlRef.value
+    if (el) {
+        el.querySelectorAll('a').forEach((e) => {
+            const href = e.href
+            e.onclick = () => plus.openURL(href)
+            e.removeAttribute('href')
+        })
+
+        if (!lightbox) {
+            lightbox = new PhotoSwipeLightbox({
+                gallery: el,
+                children: 'img',
+                pswpModule: () => import('photoswipe'),
+            })
+
+            // https://photoswipe.com/data-sources/#custom-html-markup
+            lightbox.addFilter('domItemData', (itemData: SlideData, el: HTMLImageElement) => {
+                if (el) {
+                    itemData.src = el.src
+                    itemData.w = el.width
+                    itemData.h = el.height
+                    itemData.msrc = el.src
+                }
+                return itemData
+            })
+
+            lightbox.init()
+        }
+    }
+})
+
+onUnmounted(() => {
+    if (lightbox) {
+        lightbox.destroy()
+        lightbox = null
+    }
+})
+</script>
+  

+ 13 - 2
src/packages/mobile/views/home/index.vue

@@ -9,10 +9,11 @@
 
 <script lang="ts" setup>
 import { shallowRef, onActivated, onMounted } from 'vue'
-import { dialog } from '@/utils/vant'
+import { fullloading, dialog } from '@/utils/vant'
 import { Tabbar } from '@mobile/components/base/tabbar/interface'
 import { GetAppUpdateInfo } from '@/services/api/common'
 import { useNavigation } from '@/hooks/navigation'
+import { useLogin } from '@/business/login'
 import { loginStore } from '@/stores'
 import plus from '@/utils/h5plus'
 import AppTabbar from '@mobile/components/base/tabbar/index.vue'
@@ -29,6 +30,7 @@ const components = {
 }
 
 const { token } = loginStore.$mapGetters()
+const { autoLogin } = useLogin()
 const { routerTo, getGlobalUrlParams } = useNavigation()
 const componentId = shallowRef('home')
 const tabIndex = shallowRef(0)
@@ -65,7 +67,16 @@ const onTabClick = ({ name }: Tabbar, index: number) => {
     tabIndex.value = index
     componentId.value = name
   } else {
-    routerTo('UserLogin')
+    fullloading((hideLoading) => {
+      autoLogin().then(() => {
+        tabIndex.value = index
+        componentId.value = name
+      }).catch(() => {
+        routerTo('UserLogin')
+      }).finally(() => {
+        hideLoading()
+      })
+    }, '加载中...')
   }
 }
 

+ 2 - 0
src/packages/mobile/views/mine/main/index.vue

@@ -127,6 +127,7 @@ import { fullloading, dialog } from '@/utils/vant'
 import { getFileUrl } from '@/filters'
 import { useNavigation } from '@/hooks/navigation'
 import { loginStore, accountStore, userStore } from '@/stores'
+import { localData } from '@/stores/storage'
 import AppIconfont from '@mobile/components/base/iconfont/index.vue'
 import { queryBankAccountSign } from '@/services/api/bank'
 import { queryUserAccount } from '@/services/api/account'
@@ -194,6 +195,7 @@ const userLogout = () => {
     message: '是否退出当前账号?',
     showCancelButton: true
   }).then(() => {
+    localData.reset('autoLoginEncryptedData') // 清除自动登录数据
     eventBus.$emit('LogoutNotify')
   })
 }

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

@@ -20,7 +20,7 @@
                 <span>{{ formatDate(data.publishdate, 'YYYY-MM-DD') }}</span>
                 <span style="margin-left: auto;">阅览数:{{ data.hits }}</span>
             </h4>
-            <p ref="htmlRef" v-html="formatHtmlString(data.context)"></p>
+            <HtmlContainer :context="data.context" v-if="data.context" />
             <p style="color: #999;margin-top: .24rem;font-size: .24rem;" v-if="data.author">作者:{{ data.author }}</p>
         </section>
         <template v-if="dataList.length">
@@ -37,16 +37,15 @@
 </template>
 
 <script lang="ts" setup>
-import { shallowRef, nextTick } from 'vue'
-import { CellGroup, Cell, Divider, showImagePreview, showFailToast, showToast, Icon } from 'vant'
-import { formatDate, formatHtmlString, getImageSrc, getFileUrl, getNewsShareUrl } from '@/filters'
+import { CellGroup, Cell, Divider, showFailToast, showToast, Icon } from 'vant'
+import { formatDate, getFileUrl, getNewsShareUrl } from '@/filters'
 import { useNavigation } from '@/hooks/navigation'
 import { useRequest } from '@/hooks/request'
 import { queryNewTitles, queryNewContents } from '@/services/api/news'
 import plus from '@/utils/h5plus'
+import HtmlContainer from '@mobile/components/base/html-container/index.vue'
 
 const { getQueryString } = useNavigation()
-const htmlRef = shallowRef<HTMLElement>()
 const newsId = getQueryString('id')
 
 const { dataList, run } = useRequest(queryNewTitles, {
@@ -67,26 +66,6 @@ const { data } = useRequest(queryNewContents, {
         if (res.data.length) {
             const details = res.data[0]
             data.value = details
-
-            nextTick(() => {
-                const el = htmlRef.value
-                if (el) {
-                    el.querySelectorAll('img').forEach((e, i) => {
-                        e.onclick = () => {
-                            showImagePreview({
-                                images: getImageSrc(details.context),
-                                startPosition: i,
-                            })
-                        }
-                    })
-                    el.querySelectorAll('a').forEach((e) => {
-                        const href = e.href
-                        e.onclick = () => plus.openURL(href)
-                        e.removeAttribute('href')
-                    })
-                }
-            })
-
             run({ columnid: details.columnid })
         } else {
             run()

+ 4 - 24
src/packages/mobile/views/share/news/index.vue

@@ -11,7 +11,7 @@
                 <span>{{ formatDate(data.publishdate, 'YYYY-MM-DD') }}</span>
                 <span style="margin-left: auto;">阅览数:{{ data.hits }}</span>
             </h4>
-            <p ref="htmlRef" v-html="formatHtmlString(data.context)"></p>
+            <HtmlContainer :context="data.context" v-if="data.context" />
             <p style="color: #999;margin-top: .24rem;font-size: .24rem;" v-if="data.author">作者:{{ data.author }}</p>
         </section>
         <div class="share-news__footer">
@@ -21,16 +21,15 @@
 </template>
 
 <script lang="ts" setup>
-import { shallowRef, nextTick } from 'vue'
-import { Button, showImagePreview, } from 'vant'
-import { formatDate, formatHtmlString, getImageSrc, getFileUrl } from '@/filters'
+import { Button } from 'vant'
+import { formatDate, getFileUrl } from '@/filters'
 import { useNavigation } from '@/hooks/navigation'
 import { useRequest } from '@/hooks/request'
 import { queryNewContents } from '@/services/api/news'
 import plus from '@/utils/h5plus'
+import HtmlContainer from '@mobile/components/base/html-container/index.vue'
 
 const { router, getQueryString } = useNavigation()
-const htmlRef = shallowRef<HTMLElement>()
 
 const { data } = useRequest(queryNewContents, {
     params: {
@@ -40,25 +39,6 @@ const { data } = useRequest(queryNewContents, {
         if (res.data.length) {
             const details = res.data[0]
             data.value = details
-
-            nextTick(() => {
-                const el = htmlRef.value
-                if (el) {
-                    el.querySelectorAll('img').forEach((e, i) => {
-                        e.onclick = () => {
-                            showImagePreview({
-                                images: getImageSrc(details.context),
-                                startPosition: i,
-                            })
-                        }
-                    })
-                    el.querySelectorAll('a').forEach((e) => {
-                        const href = e.href
-                        e.onclick = () => plus.openURL(href)
-                        e.removeAttribute('href')
-                    })
-                }
-            })
         }
     }
 })

+ 2 - 0
src/services/socket/index.ts

@@ -7,6 +7,7 @@ import { parseReceivePush } from './quote/build/decode'
 import protobuf from './trade/protobuf'
 import { checkToken, stopCheckToken, checkTokenLoop } from '@/business/common'
 import { errorInfoStore } from '@/stores'
+import { localData } from '@/stores/storage'
 import service from '@/services'
 import eventBus from '@/services/bus'
 
@@ -44,6 +45,7 @@ export default new (class {
                     protobuf.responseDecode<Proto.LogoutRsp>(FunCode[funCode], content).then(({ RetCode, RetDesc }) => {
                         const msg = errorInfoStore.actions.getErrorInfoByCode(RetCode)
                         const error = (RetDesc || RetCode).toString();
+                        localData.reset('autoLoginEncryptedData'); // 清除自动登录数据
                         eventBus.$emit('LogoutNotify', msg ?? error);
                     })
                     break;

+ 8 - 13
src/stores/modules/enum.ts

@@ -34,19 +34,14 @@ export const enumStore = createStore({
     actions: {
         // 获取所有枚举列表
         async getAllEnumList() {
-            await new Promise<void>((resolve) => {
-                if (this.state.allEnums.length) {
-                    resolve()
-                } else {
-                    this.state.loading = true
-                    queryAllEnums().then((res) => {
-                        this.state.allEnums = res.data
-                        resolve()
-                    }).finally(() => {
-                        this.state.loading = false
-                    })
-                }
-            })
+            if (!this.state.allEnums.length) {
+                this.state.loading = true
+                await queryAllEnums().then((res) => {
+                    this.state.allEnums = res.data
+                }).finally(() => {
+                    this.state.loading = false
+                })
+            }
             // 清空列表数据
             for (const item of enumMap.values()) {
                 item.value = []

+ 1 - 20
src/stores/modules/login.ts

@@ -1,6 +1,5 @@
-import { encryptAES, decryptAES } from '@/utils/crypto'
 import { createStore } from '../base'
-import { sessionData, localData } from '../storage'
+import { sessionData } from '../storage'
 
 /**
  * 登录存储对象
@@ -36,23 +35,5 @@ export const loginStore = createStore({
         getLoginInfo<K extends keyof Proto.LoginRsp>(key: K) {
             return this.state.loginInfo[key]
         },
-        // 获取自动登录数据
-        getAutoLoginData(data: Proto.LoginReq) {
-            const encryptedData = encryptAES(JSON.stringify(data)) // 数据加密
-            localData.setValue('autoLoginEncryptedData', encryptedData)
-        },
-        // 设置自动登录数据
-        setAutoLoginData(): Proto.LoginRsp {
-            const encryptedData = localData.getValue('autoLoginEncryptedData')
-            if (encryptedData) {
-                const decryptedString = decryptAES(encryptedData)
-                return JSON.parse(decryptedString)
-            }
-            return Object.create(null)
-        },
-        // 清除自动登录数据
-        clearAutoLoginData() {
-            localData.reset('autoLoginEncryptedData')
-        }
     }
 })