This commit is contained in:
Mrx
2026-05-21 17:05:09 +08:00
parent 6137c69034
commit d18feb3090
18 changed files with 577 additions and 544 deletions

View File

@@ -1,8 +1,9 @@
<script setup lang="ts">
import { onShareAppMessage, onShareTimeline, onShow } from '@dcloudio/uni-app'
import { onUnmounted, ref } from 'vue'
import { clearAuthStorage, getUserDetail, postAuthSendSmsBindMobile, postUserBindMobile } from '@/api'
import { hasToken, saveAuthSession } from '@/utils/session'
import { ref } from 'vue'
import { getUserDetail } from '@/api'
import { hasToken } from '@/utils/session'
import { useBindMobile } from '@/composables/useBindMobile'
definePage({
style: {
@@ -30,14 +31,22 @@ const userDesc = ref('')
const hasBoundMobile = ref(false)
const wxNickStorage = ref('')
const bindModalOpen = ref(false)
const bindPhone = ref('')
const bindCode = ref('')
const bindSending = ref(false)
const bindSubmitting = ref(false)
const bindCountingDown = ref(false)
const bindCountdown = ref(60)
let bindSmsTimer: ReturnType<typeof setInterval> | null = null
const {
bindVisible: bindModalOpen,
bindPhone,
bindCode,
bindSending,
bindSubmitting,
bindCountingDown,
bindCountdown,
isBindPhoneValid,
openBindModal,
closeBindModal,
onBindPhoneInput,
onBindCodeInput,
sendBindSms,
submitBindMobile,
} = useBindMobile(refreshUserCard)
const coopModalOpen = ref(false)
@@ -68,30 +77,10 @@ function loadWxNickFromStorage() {
}
}
function clearBindSmsTimer() {
if (bindSmsTimer) {
clearInterval(bindSmsTimer)
bindSmsTimer = null
}
function openBindModalFromProfile() {
openBindModal()
}
function startBindCountdown() {
clearBindSmsTimer()
bindCountingDown.value = true
bindCountdown.value = 60
bindSmsTimer = setInterval(() => {
if (bindCountdown.value > 0) {
bindCountdown.value--
}
else {
clearBindSmsTimer()
bindCountingDown.value = false
}
}, 1000)
}
const isBindPhoneValid = () => /^1[3-9]\d{9}$/.test(bindPhone.value)
async function refreshUserCard() {
isLogin.value = hasToken()
if (!isLogin.value) {
@@ -148,19 +137,6 @@ onShow(() => {
function handleUserTap() {
if (isLogin.value) {
uni.showActionSheet({
itemList: ['退出登录'],
success(res) {
if (res.tapIndex === 0) {
clearAuthStorage()
isLogin.value = false
nickname.value = ''
userDesc.value = ''
hasBoundMobile.value = false
uni.showToast({ title: '已退出', icon: 'none' })
}
},
})
return
}
uni.navigateTo({ url: '/pages/login' })
@@ -193,77 +169,8 @@ async function syncWxNickname() {
// #endif
}
function openBindModal() {
bindPhone.value = ''
bindCode.value = ''
bindModalOpen.value = true
}
function closeBindModal() {
bindModalOpen.value = false
clearBindSmsTimer()
bindCountingDown.value = false
}
function onBindPhoneInput(e: { detail?: { value?: string } }) {
const raw = e.detail?.value ?? ''
bindPhone.value = String(raw).replace(/\D/g, '').slice(0, 11)
}
function onBindCodeInput(e: { detail?: { value?: string } }) {
const raw = e.detail?.value ?? ''
bindCode.value = String(raw).replace(/\D/g, '').slice(0, 6)
}
async function sendBindSms() {
if (bindSending.value || bindCountingDown.value)
return
if (!isBindPhoneValid()) {
uni.showToast({ title: '请输入正确手机号', icon: 'none' })
return
}
bindSending.value = true
try {
const res = await postAuthSendSmsBindMobile({ mobile: bindPhone.value }) as { code?: number }
if (res && res.code === 200) {
uni.showToast({ title: '验证码已发送', icon: 'none' })
startBindCountdown()
}
}
finally {
bindSending.value = false
}
}
async function submitBindMobile() {
if (!isBindPhoneValid()) {
uni.showToast({ title: '请输入正确手机号', icon: 'none' })
return
}
if (bindCode.value.length < 6) {
uni.showToast({ title: '请输入 6 位验证码', icon: 'none' })
return
}
bindSubmitting.value = true
try {
const res = await postUserBindMobile({
mobile: bindPhone.value,
code: bindCode.value,
}) as { code?: number, data?: { accessToken: string, refreshAfter: number | string, accessExpire: number | string } }
if (res && res.code === 200 && res.data) {
saveAuthSession(res.data)
uni.showToast({ title: '绑定成功', icon: 'success' })
closeBindModal()
await refreshUserCard()
}
}
finally {
bindSubmitting.value = false
}
}
function goHistoryReport() {
uni.switchTab({ url: '/pages/report' })
uni.navigateTo({ url: '/pages/report' })
}
function goFreeValuation() {
@@ -296,11 +203,11 @@ function goLegalAuthorization() {
}
function goIllegalCode() {
uni.showToast({ title: '敬请期待', icon: 'none' })
uni.navigateTo({ url: '/pages/toolbox/query?key=jtwfcode' })
}
function goOilPrice() {
uni.showToast({ title: '敬请期待', icon: 'none' })
uni.navigateTo({ url: '/pages/toolbox/query?key=oilprice' })
}
function goHelp() {
@@ -319,10 +226,6 @@ function goSettings() {
function goServiceFallback() {
uni.showToast({ title: '请在微信小程序内使用在线客服', icon: 'none' })
}
onUnmounted(() => {
clearBindSmsTimer()
})
</script>
<template>
@@ -349,7 +252,7 @@ onUnmounted(() => {
</view>
<!-- 已登录未绑定手机 -->
<view v-if="isLogin && !hasBoundMobile" class="profile-actions">
<view class="action-btn" @tap.stop="openBindModal">
<view class="action-btn" @tap.stop="openBindModalFromProfile">
<view class="action-icon i-carbon-phone" />
<text class="action-text">绑定手机号</text>
</view>
@@ -406,20 +309,18 @@ onUnmounted(() => {
<!-- 区块3: 常用工具 -->
<view class="section">
<view class="section-title">常用工具</view>
<view class="tool-list">
<view class="tool-row" @tap="goOilPrice">
<view class="tool-row-icon-wrap" style="background: rgba(23,104,255,0.08)">
<view class="tool-row-icon i-carbon-gas-station" style="color: #1768ff" />
<view class="quick-grid">
<view class="quick-item" @tap="goOilPrice">
<view class="quick-icon-wrap" style="background: rgba(23,104,255,0.08)">
<view class="quick-icon i-carbon-gas-station" style="color: #1768ff" />
</view>
<text class="tool-row-name">实时油价查询</text>
<text class="tool-row-arrow"></text>
<text class="quick-name">实时油价查询</text>
</view>
<view class="tool-row" @tap="goIllegalCode">
<view class="tool-row-icon-wrap" style="background: rgba(250,140,22,0.08)">
<view class="tool-row-icon i-carbon-search" style="color: #fa8c16" />
<view class="quick-item" @tap="goIllegalCode">
<view class="quick-icon-wrap" style="background: rgba(250,140,22,0.08)">
<view class="quick-icon i-carbon-search" style="color: #fa8c16" />
</view>
<text class="tool-row-name">违章代码查询</text>
<text class="tool-row-arrow"></text>
<text class="quick-name">违章代码查询</text>
</view>
</view>
</view>
@@ -427,57 +328,52 @@ onUnmounted(() => {
<!-- 区块4: 服务与支持 -->
<view class="section">
<view class="section-title">服务与支持</view>
<view class="tool-list">
<button class="tool-row tool-row-btn no-border" open-type="contact" hover-class="tool-row-hover">
<view class="tool-row-icon-wrap" style="background: rgba(19,194,94,0.08)">
<view class="tool-row-icon i-carbon-chat" style="color: #13c25e" />
</view>
<view class="tool-row-text">
<text class="tool-row-name">在线客服</text>
<text class="tool-row-sub">周一至周日 9:00-20:00</text>
<view class="quick-grid">
<!-- #ifdef MP-WEIXIN -->
<button class="quick-item quick-item-btn" open-type="contact" hover-class="quick-item-hover">
<view class="quick-icon-wrap" style="background: rgba(19,194,94,0.08)">
<view class="quick-icon i-carbon-chat" style="color: #13c25e" />
</view>
<text class="quick-name">在线客服</text>
</button>
<view class="tool-row" @tap="goHelp">
<view class="tool-row-icon-wrap" style="background: rgba(114,46,209,0.08)">
<view class="tool-row-icon i-carbon-help" style="color: #722ed1" />
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<view class="quick-item" @tap="goServiceFallback">
<view class="quick-icon-wrap" style="background: rgba(19,194,94,0.08)">
<view class="quick-icon i-carbon-chat" style="color: #13c25e" />
</view>
<text class="tool-row-name">帮助中心</text>
<text class="tool-row-arrow"></text>
<text class="quick-name">在线客服</text>
</view>
<view class="tool-row" @tap="goAbout">
<view class="tool-row-icon-wrap" style="background: rgba(78,89,105,0.08)">
<view class="tool-row-icon i-carbon-information" style="color: #4e5969" />
<!-- #endif -->
<view class="quick-item" @tap="goHelp">
<view class="quick-icon-wrap" style="background: rgba(114,46,209,0.08)">
<view class="quick-icon i-carbon-help" style="color: #722ed1" />
</view>
<text class="tool-row-name">关于我们</text>
<text class="tool-row-arrow"></text>
<text class="quick-name">帮助中心</text>
</view>
</view>
</view>
<!-- 区块5: 法律条款 -->
<view class="section">
<view class="section-title">法律条款</view>
<view class="tool-list">
<view class="tool-row" @tap="goLegalUserAgreement">
<view class="tool-row-icon-wrap" style="background: rgba(78,89,105,0.06)">
<view class="tool-row-icon i-carbon-document-blank" style="color: #86909c" />
<view class="quick-item" @tap="goAbout">
<view class="quick-icon-wrap" style="background: rgba(78,89,105,0.08)">
<view class="quick-icon i-carbon-information" style="color: #4e5969" />
</view>
<text class="tool-row-name">用户协议</text>
<text class="tool-row-arrow"></text>
<text class="quick-name">关于我们</text>
</view>
<view class="tool-row" @tap="goLegalPrivacyPolicy">
<view class="tool-row-icon-wrap" style="background: rgba(78,89,105,0.06)">
<view class="tool-row-icon i-carbon-security" style="color: #86909c" />
<view class="quick-item" @tap="goLegalUserAgreement">
<view class="quick-icon-wrap" style="background: rgba(78,89,105,0.06)">
<view class="quick-icon i-carbon-document-blank" style="color: #86909c" />
</view>
<text class="tool-row-name">隐私政策</text>
<text class="tool-row-arrow"></text>
<text class="quick-name">用户协议</text>
</view>
<view class="tool-row no-border" @tap="goLegalAuthorization">
<view class="tool-row-icon-wrap" style="background: rgba(78,89,105,0.06)">
<view class="tool-row-icon i-carbon-certificate" style="color: #86909c" />
<view class="quick-item" @tap="goLegalPrivacyPolicy">
<view class="quick-icon-wrap" style="background: rgba(78,89,105,0.06)">
<view class="quick-icon i-carbon-security" style="color: #86909c" />
</view>
<text class="tool-row-name">授权书</text>
<text class="tool-row-arrow"></text>
<text class="quick-name">隐私政策</text>
</view>
<view class="quick-item" @tap="goLegalAuthorization">
<view class="quick-icon-wrap" style="background: rgba(78,89,105,0.06)">
<view class="quick-icon i-carbon-certificate" style="color: #86909c" />
</view>
<text class="quick-name">授权书</text>
</view>
</view>
</view>
@@ -735,7 +631,7 @@ onUnmounted(() => {
.quick-icon-wrap {
width: 80rpx;
height: 80rpx;
border-radius: 24rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;