Эх сурвалжийг харах

fix: 修复页面未登录不可进入问题及悬浮窗

liangan 1 сар өмнө
parent
commit
e913e959cc

+ 166 - 0
src/components/CustomerServiceFab.vue

@@ -0,0 +1,166 @@
+<script lang="ts" setup>
+import { onShow } from '@dcloudio/uni-app'
+import { getConfigByCode } from '@/api/common'
+import { openH5WhatsApp } from '@/utils/social'
+import { toast } from '@/utils/toast'
+
+const props = defineProps<{
+  bottomOffset?: number
+}>()
+
+const STORAGE_KEY = 'customer_service_fab_position_v1'
+
+const x = ref<number>(0)
+const y = ref<number>(0)
+
+function rpxToPx(rpx: number) {
+  const sys = uni.getSystemInfoSync()
+  const windowWidth = sys.windowWidth || 375
+  return (rpx * windowWidth) / 750
+}
+
+function getBottomLimitPx() {
+  const sys = uni.getSystemInfoSync() as any
+  const bottomOffsetPx = rpxToPx(props.bottomOffset ?? 0)
+  const safeBottomPx = Number(sys?.safeAreaInsets?.bottom) || 0
+  return bottomOffsetPx + safeBottomPx
+}
+
+function initDefaultPosition() {
+  const sys = uni.getSystemInfoSync()
+  const windowWidth = sys.windowWidth || 375
+  const windowHeight = sys.windowHeight || 667
+  const sizePx = rpxToPx(80)
+  const bottomOffsetPx = getBottomLimitPx()
+  x.value = Math.max(0, windowWidth - sizePx)
+  y.value = Math.max(0, windowHeight - sizePx - bottomOffsetPx)
+}
+
+function clampPosition() {
+  const sys = uni.getSystemInfoSync()
+  const windowWidth = sys.windowWidth || 375
+  const windowHeight = sys.windowHeight || 667
+  const sizePx = rpxToPx(80)
+  const bottomOffsetPx = getBottomLimitPx()
+  const maxX = Math.max(0, windowWidth - sizePx)
+  const maxY = Math.max(0, windowHeight - sizePx - bottomOffsetPx)
+  x.value = Math.min(Math.max(0, x.value), maxX)
+  y.value = Math.min(Math.max(0, y.value), maxY)
+}
+
+function restorePosition() {
+  try {
+    const raw = uni.getStorageSync(STORAGE_KEY)
+    if (!raw) {
+      initDefaultPosition()
+      return
+    }
+    const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw
+    const nx = Number(parsed?.x)
+    const ny = Number(parsed?.y)
+    if (Number.isFinite(nx) && Number.isFinite(ny)) {
+      x.value = nx
+      y.value = ny
+      clampPosition()
+      return
+    }
+    initDefaultPosition()
+  }
+  catch {
+    initDefaultPosition()
+  }
+}
+
+function persistPosition() {
+  try {
+    uni.setStorageSync(STORAGE_KEY, JSON.stringify({ x: x.value, y: y.value }))
+  }
+  catch {
+  }
+}
+
+function persistPositionClamped() {
+  clampPosition()
+  persistPosition()
+}
+
+onShow(() => {
+  restorePosition()
+})
+
+function handleMoveChange(e: any) {
+  const detail = e?.detail
+  const nx = Number(detail?.x)
+  const ny = Number(detail?.y)
+  if (Number.isFinite(nx))
+    x.value = nx
+  if (Number.isFinite(ny))
+    y.value = ny
+  clampPosition()
+}
+
+async function openCustomerService() {
+  try {
+    const res = await getConfigByCode({ code: 'live_chat' })
+    const value = res?.data?.valueInfo
+    if (!value) {
+      toast.info('客服暂不可用')
+      return
+    }
+    openH5WhatsApp('live_chat', value)
+  }
+  catch {
+    toast.info('客服暂不可用')
+  }
+}
+</script>
+
+<template>
+  <movable-area class="customer-fab-area">
+    <movable-view
+      :x="x"
+      :y="y"
+      direction="all"
+      :inertia="false"
+      :animation="false"
+      :out-of-bounds="false"
+      class="customer-fab-movable"
+      @change="handleMoveChange"
+      @touchend="persistPositionClamped"
+      @touchcancel="persistPositionClamped"
+    >
+      <image
+        src="/static/icons/whatsapp.png"
+        class="customer-fab-trigger__icon"
+        mode="heightFix"
+        @click="openCustomerService"
+      />
+    </movable-view>
+  </movable-area>
+</template>
+
+<style lang="scss" scoped>
+.customer-fab-area {
+  position: fixed;
+  left: 0;
+  top: 0;
+  width: 100vw;
+  height: 100vh;
+  pointer-events: none;
+  z-index: 999;
+}
+
+.customer-fab-movable {
+  pointer-events: auto;
+  transition: none;
+  width: 80rpx;
+  height: 80rpx;
+}
+
+.customer-fab-trigger__icon {
+  box-shadow: 0 12rpx 30rpx rgba(0, 0, 0, 0.16);
+  height: 80rpx;
+  width: 80rpx;
+  border-radius: 50%;
+}
+</style>

+ 5 - 0
src/layouts/default.vue

@@ -1,6 +1,8 @@
 <script lang="ts" setup>
 import type { ConfigProviderThemeVars } from 'wot-design-uni'
 
+import CustomerServiceFab from '@/components/CustomerServiceFab.vue'
+
 const themeVars: ConfigProviderThemeVars = {
   // colorTheme: 'red',
   // buttonPrimaryBgColor: '#07c160',
@@ -11,6 +13,9 @@ const themeVars: ConfigProviderThemeVars = {
 <template>
   <wd-config-provider :theme-vars="themeVars">
     <slot />
+    <!-- #ifdef H5 -->
+    <CustomerServiceFab :bottom-offset="120" />
+    <!-- #endif -->
     <wd-toast />
     <wd-message-box />
   </wd-config-provider>

+ 5 - 0
src/layouts/tabbar.vue

@@ -1,5 +1,7 @@
 <script lang="ts" setup>
 import type { ConfigProviderThemeVars } from 'wot-design-uni'
+import CustomerServiceFab from '@/components/CustomerServiceFab.vue'
+
 import FgTabbar from './fg-tabbar/fg-tabbar.vue'
 
 const themeVars: ConfigProviderThemeVars = {
@@ -12,6 +14,9 @@ const themeVars: ConfigProviderThemeVars = {
 <template>
   <wd-config-provider :theme-vars="themeVars">
     <slot />
+    <!-- #ifdef H5 -->
+    <CustomerServiceFab :bottom-offset="180" />
+    <!-- #endif -->
     <FgTabbar />
     <wd-toast />
     <wd-message-box />

+ 12 - 0
src/pages.json

@@ -97,6 +97,7 @@
       "path": "pages/mine/addressBook",
       "type": "page",
       "layout": "default",
+      "needLogin": true,
       "style": {
         "navigationBarTitleText": "%addressBook.title%",
         "navigationBarBackgroundColor": "#fff"
@@ -106,6 +107,7 @@
       "path": "pages/mine/addressBookOperate",
       "type": "page",
       "layout": "default",
+      "needLogin": true,
       "style": {
         "navigationBarTitleText": "%addressBook.title%",
         "navigationBarBackgroundColor": "#fff"
@@ -123,6 +125,7 @@
       "path": "pages/mine/myFavorite",
       "type": "page",
       "layout": "default",
+      "needLogin": true,
       "style": {
         "navigationBarTitleText": "%mine.pages.myFavorite.title%",
         "navigationBarBackgroundColor": "#fff"
@@ -132,6 +135,7 @@
       "path": "pages/mine/myProfile",
       "type": "page",
       "layout": "default",
+      "needLogin": true,
       "style": {
         "navigationBarTitleText": "%myProfile.title%",
         "navigationBarBackgroundColor": "#fff"
@@ -150,6 +154,7 @@
       "path": "pages/mine/share",
       "type": "page",
       "layout": "default",
+      "needLogin": true,
       "style": {
         "navigationStyle": "custom",
         "navigationBarTitleText": "%mine.pages.share.title%"
@@ -169,6 +174,7 @@
       "path": "pages/myOrders/myOrders",
       "type": "page",
       "layout": "default",
+      "needLogin": true,
       "style": {
         "navigationBarTitleText": "%myOrders.title%",
         "navigationBarBackgroundColor": "#fff"
@@ -178,6 +184,7 @@
       "path": "pages/myOrders/orderDetail",
       "type": "page",
       "layout": "default",
+      "needLogin": true,
       "style": {
         "navigationBarTitleText": "%orderDetail.title%",
         "navigationBarBackgroundColor": "#fff"
@@ -257,6 +264,7 @@
       "path": "pages/wallet/frozenRecord",
       "type": "page",
       "layout": "default",
+      "needLogin": true,
       "style": {
         "navigationBarTitleText": "%wallet.frozenRecord.title%",
         "navigationBarBackgroundColor": "#fff"
@@ -276,6 +284,7 @@
       "path": "pages/wallet/recharge",
       "type": "page",
       "layout": "default",
+      "needLogin": true,
       "style": {
         "navigationBarTitleText": "%wallet.recharge.title%",
         "navigationBarBackgroundColor": "#fff",
@@ -296,6 +305,7 @@
       "path": "pages/wallet/rechargeRecord",
       "type": "page",
       "layout": "default",
+      "needLogin": true,
       "style": {
         "navigationBarTitleText": "%wallet.rechargeRecord.title%",
         "navigationBarBackgroundColor": "#fff"
@@ -305,6 +315,7 @@
       "path": "pages/wallet/withdraw",
       "type": "page",
       "layout": "default",
+      "needLogin": true,
       "style": {
         "navigationStyle": "custom",
         "navigationBarTitleText": "%wallet.withdraw.title%"
@@ -314,6 +325,7 @@
       "path": "pages/wallet/withdrawRecord",
       "type": "page",
       "layout": "default",
+      "needLogin": true,
       "style": {
         "navigationBarTitleText": "%wallet.withdrawRecord.title%",
         "navigationBarBackgroundColor": "#fff"

+ 1 - 0
src/pages/mine/addressBook.vue

@@ -1,6 +1,7 @@
 <route lang="json5" type="page">
 {
   layout: 'default',
+  needLogin: true,
   style: {
     navigationBarTitleText: '%addressBook.title%',
     navigationBarBackgroundColor: '#fff',

+ 1 - 0
src/pages/mine/addressBookOperate.vue

@@ -1,6 +1,7 @@
 <route lang="json5" type="page">
 {
   layout: 'default',
+  needLogin: true,
   style: {
     navigationBarTitleText: '%addressBook.title%',
     navigationBarBackgroundColor: '#fff',

+ 1 - 1
src/pages/mine/mine.vue

@@ -103,7 +103,7 @@ const menuList = computed(() => {
     { name: t('mine.menu.share'), url: '/pages/mine/share', icon: '/static/icons/share.png' },
     { name: t('mine.menu.favorite'), url: '/pages/mine/myFavorite', icon: '/static/icons/my-favorite.png' },
     { name: t('mine.menu.chat'), config: 'live_chat', icon: '/static/icons/live-chat.png' },
-    { name: t('mine.menu.activity'), config: 'activity_group', icon: '/static/icons/activity-group.png' },
+    // { name: t('mine.menu.activity'), config: 'activity_group', icon: '/static/icons/activity-group.png' },
   ]
 })
 

+ 1 - 0
src/pages/mine/myFavorite.vue

@@ -1,6 +1,7 @@
 <route lang="json5">
 {
   layout: 'default',
+  needLogin: true,
   style: {
     navigationBarTitleText: '%mine.pages.myFavorite.title%',
     navigationBarBackgroundColor: '#fff'

+ 1 - 0
src/pages/mine/myProfile.vue

@@ -1,6 +1,7 @@
 <route lang="json5" type="page">
 {
   layout: 'default',
+  needLogin: true,
   style: {
     navigationBarTitleText: '%myProfile.title%',
     navigationBarBackgroundColor: '#fff',

+ 1 - 0
src/pages/mine/share.vue

@@ -1,6 +1,7 @@
 <route lang="json5">
 {
   layout: 'default',
+  needLogin: true,
   style: {
     navigationStyle: 'custom',
     navigationBarTitleText: '%mine.pages.share.title%'

+ 1 - 0
src/pages/myOrders/myOrders.vue

@@ -1,6 +1,7 @@
 <route lang="json5" type="page">
 {
   layout: 'default',
+  needLogin: true,
   style: {
     navigationBarTitleText: '%myOrders.title%',
     navigationBarBackgroundColor: '#fff',

+ 1 - 0
src/pages/myOrders/orderDetail.vue

@@ -1,6 +1,7 @@
 <route lang="json5" type="page">
 {
   layout: 'default',
+  needLogin: true,
   style: {
     navigationBarTitleText: '%orderDetail.title%',
     navigationBarBackgroundColor: '#fff',

+ 1 - 0
src/pages/wallet/frozenRecord.vue

@@ -1,6 +1,7 @@
 <route lang="json5" type="page">
 {
   layout: 'default',
+  needLogin: true,
   style: {
     navigationBarTitleText: '%wallet.frozenRecord.title%',
     navigationBarBackgroundColor: '#fff',

+ 1 - 0
src/pages/wallet/recharge.vue

@@ -1,6 +1,7 @@
 <route lang="json5" type="page">
 {
   layout: 'default',
+  needLogin: true,
   style: {
     navigationBarTitleText: '%wallet.recharge.title%',
     navigationBarBackgroundColor: '#fff',

+ 1 - 0
src/pages/wallet/rechargeRecord.vue

@@ -1,6 +1,7 @@
 <route lang="json5" type="page">
 {
   layout: 'default',
+  needLogin: true,
   style: {
     navigationBarTitleText: '%wallet.rechargeRecord.title%',
     navigationBarBackgroundColor: '#fff',

+ 1 - 0
src/pages/wallet/withdraw.vue

@@ -1,6 +1,7 @@
 <route lang="json5" type="page">
 {
   layout: 'default',
+  needLogin: true,
   style: {
     navigationStyle: 'custom',
     navigationBarTitleText: '%wallet.withdraw.title%',

+ 1 - 0
src/pages/wallet/withdrawRecord.vue

@@ -1,6 +1,7 @@
 <route lang="json5" type="page">
 {
   layout: 'default',
+  needLogin: true,
   style: {
     navigationBarTitleText: '%wallet.withdrawRecord.title%',
     navigationBarBackgroundColor: '#fff',

+ 1 - 1
src/utils/social.ts

@@ -118,7 +118,7 @@ export function openH5WhatsApp(type: 'live_chat' | 'activity_group', value: stri
   const phone = (value || '').replace(/\s+/g, '')
   const url = type === 'activity_group'
     ? `https://chat.whatsapp.com/${encodeURIComponent(value || '')}`
-    : `https://wa.me/${encodeURIComponent(phone)}`
+    : `https://wa.me/message/${encodeURIComponent(phone)}`
 
   if (typeof window !== 'undefined' && typeof window.open === 'function')
     window.open(url, '_blank')