|
|
@@ -1,6 +1,6 @@
|
|
|
<script lang="ts" setup>
|
|
|
-import { onShow } from '@dcloudio/uni-app'
|
|
|
import { getConfigByCode } from '@/api/common'
|
|
|
+import { toPage } from '@/utils/page'
|
|
|
import { openH5WhatsApp } from '@/utils/social'
|
|
|
import { toast } from '@/utils/toast'
|
|
|
|
|
|
@@ -8,10 +8,13 @@ const props = defineProps<{
|
|
|
bottomOffset?: number
|
|
|
}>()
|
|
|
|
|
|
-const STORAGE_KEY = 'customer_service_fab_position_v1'
|
|
|
+const active = ref<boolean>(false)
|
|
|
|
|
|
-const x = ref<number>(0)
|
|
|
-const y = ref<number>(0)
|
|
|
+const TABBAR_PAGES = [
|
|
|
+ 'pages/index/index',
|
|
|
+ 'pages/income/income',
|
|
|
+ 'pages/mine/mine',
|
|
|
+]
|
|
|
|
|
|
function rpxToPx(rpx: number) {
|
|
|
const sys = uni.getSystemInfoSync()
|
|
|
@@ -19,89 +22,33 @@ function rpxToPx(rpx: number) {
|
|
|
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 rightInsetPx = rpxToPx(24)
|
|
|
- const bottomInsetPx = rpxToPx(-86)
|
|
|
- const bottomOffsetPx = getBottomLimitPx()
|
|
|
- x.value = Math.max(0, windowWidth - sizePx - rightInsetPx)
|
|
|
- y.value = Math.max(0, windowHeight - sizePx - bottomOffsetPx - bottomInsetPx)
|
|
|
-}
|
|
|
-
|
|
|
-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 getCurrentRoutePath() {
|
|
|
+ const pages = getCurrentPages() as any[]
|
|
|
+ const last = pages?.[pages.length - 1]
|
|
|
+ const route = String(last?.route || '')
|
|
|
+ return route.replace(/^\//, '')
|
|
|
}
|
|
|
|
|
|
-function persistPosition() {
|
|
|
- try {
|
|
|
- uni.setStorageSync(STORAGE_KEY, JSON.stringify({ x: x.value, y: y.value }))
|
|
|
- }
|
|
|
- catch {
|
|
|
- }
|
|
|
+function isTabbarPage() {
|
|
|
+ const route = getCurrentRoutePath()
|
|
|
+ return TABBAR_PAGES.includes(route)
|
|
|
}
|
|
|
|
|
|
-function persistPositionClamped() {
|
|
|
- clampPosition()
|
|
|
- persistPosition()
|
|
|
+function getBottomOffsetPx() {
|
|
|
+ const sys = uni.getSystemInfoSync() as any
|
|
|
+ const extraOffsetPx = rpxToPx(props.bottomOffset ?? 0)
|
|
|
+ const safeBottomPx = Number(sys?.safeAreaInsets?.bottom) || 0
|
|
|
+ const tabbarHeightPx = isTabbarPage() ? 50 : 0
|
|
|
+ return extraOffsetPx + safeBottomPx + tabbarHeightPx
|
|
|
}
|
|
|
|
|
|
-onShow(() => {
|
|
|
- restorePosition()
|
|
|
+const fabCustomStyle = computed(() => {
|
|
|
+ const bottomPx = getBottomOffsetPx()
|
|
|
+ return `bottom: ${bottomPx}px;`
|
|
|
})
|
|
|
|
|
|
-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() {
|
|
|
+ active.value = false
|
|
|
try {
|
|
|
const res = await getConfigByCode({ code: 'live_chat' })
|
|
|
const value = res?.data?.valueInfo
|
|
|
@@ -115,54 +62,40 @@ async function openCustomerService() {
|
|
|
toast.info('客服暂不可用')
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+function openHelpCenter() {
|
|
|
+ active.value = false
|
|
|
+ toPage({ url: '/pages/mine/helpCenter' })
|
|
|
+}
|
|
|
</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>
|
|
|
+ <wd-fab v-model:active="active" position="right-bottom" :z-index="999" direction="top" :custom-style="fabCustomStyle" :expandable="false" :draggable="true">
|
|
|
+ <template #trigger>
|
|
|
+ <view class="w-80rpx">
|
|
|
+ <view class="customer-fab-action mb-12rpx" @click="openCustomerService">
|
|
|
+ <image src="/static/icons/whatsapp.png" class="customer-fab-action__icon" mode="heightFix" />
|
|
|
+ </view>
|
|
|
+ <view class="customer-fab-action" @click="openHelpCenter">
|
|
|
+ <image src="/static/icons/icon-info.png" class="customer-fab-action__icon" mode="heightFix" />
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </template>
|
|
|
+ </wd-fab>
|
|
|
</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;
|
|
|
+.customer-fab-action {
|
|
|
+ box-sizing: border-box;
|
|
|
width: 80rpx;
|
|
|
height: 80rpx;
|
|
|
+ border-radius: 50%;
|
|
|
+ padding: 0;
|
|
|
+ background-color: #fff;
|
|
|
}
|
|
|
|
|
|
-.customer-fab-trigger__icon {
|
|
|
- box-shadow: 0 12rpx 30rpx rgba(0, 0, 0, 0.16);
|
|
|
- height: 80rpx;
|
|
|
+.customer-fab-action__icon {
|
|
|
width: 80rpx;
|
|
|
- border-radius: 50%;
|
|
|
+ height: 80rpx;
|
|
|
}
|
|
|
</style>
|