新增三码适配

This commit is contained in:
2026-04-01 14:12:14 +08:00
parent 337a92d8c1
commit 9ca377907b
12 changed files with 741 additions and 676 deletions
-5
View File
@@ -11,9 +11,6 @@
// 注意:语言初始化已移至 main.js,确保每次 reLaunch 都能正确加载新语言 // 注意:语言初始化已移至 main.js,确保每次 reLaunch 都能正确加载新语言
}, },
onShow: async function() { onShow: async function() {
console.log('========================================')
console.log('=== App onShow 被调用 ===')
console.log('时间戳:', new Date().toLocaleTimeString())
// 检查启动路径,如果设置了启动路径则跳转 // 检查启动路径,如果设置了启动路径则跳转
try { try {
@@ -81,8 +78,6 @@
} catch (e) { } catch (e) {
console.error('App onShow - 语言检查失败:', e) console.error('App onShow - 语言检查失败:', e)
} }
console.log('========================================')
}, },
onHide: function() { onHide: function() {
console.log('App Hide') console.log('App Hide')
+20 -6
View File
@@ -36,7 +36,9 @@ const request = (option) => {
method: option.method, method: option.method,
data: option.data, data: option.data,
header: { header: {
"Content-Type": option.headers && option.headers["Content-Type"] ? option.headers["Content-Type"] : (option.method && option.method.toUpperCase() === 'POST' ? 'application/json' : 'application/x-www-form-urlencoded'), "Content-Type": option.headers && option.headers["Content-Type"] ? option.headers[
"Content-Type"] : (option.method && option.method.toUpperCase() === 'POST' ?
'application/json' : 'application/x-www-form-urlencoded'),
...option.headers, ...option.headers,
'appid': platformAppid, 'appid': platformAppid,
'Authorization': "Bearer " + uni.getStorageSync('token'), 'Authorization': "Bearer " + uni.getStorageSync('token'),
@@ -56,7 +58,9 @@ const request = (option) => {
return return
} }
reject({msg: `请求失败,状态码:${res.statusCode}`}) reject({
msg: `请求失败,状态码:${res.statusCode}`
})
return return
} }
@@ -68,12 +72,22 @@ const request = (option) => {
// 计算重定向地址 // 计算重定向地址
const pages = getCurrentPages() const pages = getCurrentPages()
const current = pages && pages.length ? pages[pages.length - 1] : null const current = pages && pages.length ? pages[pages.length - 1] : null
const route = current && current.route ? ('/' + current.route) : '/pages/index/index' const route = current && current.route ? ('/' + current.route) :
const query = current && current.options ? Object.keys(current.options).map(k => `${k}=${encodeURIComponent(current.options[k])}`).join('&') : '' '/pages/index/index'
const query = current && current.options ? Object.keys(current.options).map(
k => `${k}=${encodeURIComponent(current.options[k])}`).join('&') :
''
const redirect = encodeURIComponent(query ? `${route}?${query}` : route) const redirect = encodeURIComponent(query ? `${route}?${query}` : route)
// console.log(redirect, "===========");
// 跳转到登录页 // 跳转到登录页
uni.reLaunch({ url: `/subPackages/user/login/index?redirect=${redirect}` }) uni.reLaunch({
} catch (e) {} url: "/subPackages/user/login/index"
})
} catch (e) {
uni.reLaunch({
url: "/subPackages/user/login/index"
})
}
} }
// 检查业务状态码 // 检查业务状态码
-26
View File
@@ -71,10 +71,6 @@ function getI18nInstance() {
// 每次都重新读取当前语言 // 每次都重新读取当前语言
const currentLang = getSavedLanguage() const currentLang = getSavedLanguage()
console.log('=== getI18nInstance 被调用 ===')
console.log('缓存中的语言:', currentLang)
console.log('当前 i18n 实例存在?', !!i18nInstance)
console.log('当前 i18n.global.locale:', i18nInstance?.global?.locale)
// 检查是否需要更新语言 // 检查是否需要更新语言
if (i18nInstance && i18nInstance.global.locale !== currentLang) { if (i18nInstance && i18nInstance.global.locale !== currentLang) {
@@ -85,18 +81,11 @@ function getI18nInstance() {
// 直接更新 locale(这应该会触发所有组件重新渲染) // 直接更新 locale(这应该会触发所有组件重新渲染)
i18nInstance.global.locale = currentLang i18nInstance.global.locale = currentLang
console.log('i18n.global.locale 已更新为:', i18nInstance.global.locale)
console.log('测试翻译 (common.loading):', i18nInstance.global.t('common.loading'))
console.log('测试翻译 (home.title):', i18nInstance.global.t('home.title'))
console.log('===================================')
return i18nInstance return i18nInstance
} }
// 首次创建实例 // 首次创建实例
if (!i18nInstance) { if (!i18nInstance) {
console.log('=== 首次创建 i18n 实例 ===')
i18nInstance = createI18n({ i18nInstance = createI18n({
legacy: true, // 使用 Legacy API 模式,支持全局 $t legacy: true, // 使用 Legacy API 模式,支持全局 $t
locale: currentLang, locale: currentLang,
@@ -110,20 +99,12 @@ function getI18nInstance() {
silentFallbackWarn: true silentFallbackWarn: true
}) })
console.log('i18n 实例已创建,语言:', currentLang)
console.log('测试翻译 (common.loading):', i18nInstance.global.t('common.loading'))
console.log('测试翻译 (home.title):', i18nInstance.global.t('home.title'))
console.log('============================')
} }
return i18nInstance return i18nInstance
} }
export function createApp() { export function createApp() {
console.log('========================================')
console.log('=== createApp 被调用 ===')
console.log('时间戳:', new Date().toLocaleTimeString())
console.log('========================================')
const app = createSSRApp(App) const app = createSSRApp(App)
@@ -153,13 +134,6 @@ export function createApp() {
} }
} }
console.log('=== Vue 3 应用创建完成 ===')
console.log('最终 locale:', i18n.global.locale)
console.log('app.config.globalProperties.$t 存在?', !!app.config.globalProperties.$t)
console.log('app.config.globalProperties.$i18n 存在?', !!app.config.globalProperties.$i18n)
console.log('测试 $t 调用:', i18n.global.t('common.loading'))
console.log('===========================')
return { return {
app app
} }
+4 -3
View File
@@ -93,9 +93,10 @@
} }
}, },
"router" : { "router" : {
"mode" : "hash", "mode" : "history",
"base" : "./" "base" : "/"
} },
"title" : "FDZPower"
}, },
"uniStatistics" : { "uniStatistics" : {
"enable" : false "enable" : false
+6
View File
@@ -61,6 +61,12 @@
"navigationBarBackgroundColor": "#ffffff", "navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black" "navigationBarTextStyle": "black"
} }
},
{
"path": "toProgram",
"style": {
"navigationStyle": "custom"
}
} }
], ],
"subPackages": [ "subPackages": [
+5 -10
View File
@@ -181,7 +181,6 @@
// 生命周期 onLoad 钩子 // 生命周期 onLoad 钩子
onLoad(async (options) => { onLoad(async (options) => {
console.log('options', options)
// 普通链接二维码进入时,参数通常在 options.q(且为编码后的完整 URL // 普通链接二维码进入时,参数通常在 options.q(且为编码后的完整 URL
if (!options.deviceNo && options.q) { if (!options.deviceNo && options.q) {
@@ -232,11 +231,6 @@
isAlipayMiniProgram.value = false isAlipayMiniProgram.value = false
isH5.value = true isH5.value = true
// #endif // #endif
console.log('当前运行环境:', {
isWechatMiniProgram: isWechatMiniProgram.value,
isAlipayMiniProgram: isAlipayMiniProgram.value,
isH5: isH5.value
})
await checkUserPhone() await checkUserPhone()
await fetchDeviceInfo() await fetchDeviceInfo()
}) })
@@ -366,7 +360,7 @@
const fetchDeviceInfo = async () => { const fetchDeviceInfo = async () => {
try { try {
loading.value = true loading.value = true
console.log(deviceId.value); // console.log(deviceId.value);
const res = await getDeviceInfo(deviceId.value) const res = await getDeviceInfo(deviceId.value)
if (res.code == 200) { if (res.code == 200) {
deviceInfo.value = res.data.device || {} deviceInfo.value = res.data.device || {}
@@ -404,9 +398,10 @@
} }
} }
}else{ }else{
uni.reLaunch({
url:'/pages/index/index' // uni.reLaunch({
}) // url:'/pages/index/index'
// })
} }
}catch(error){ }catch(error){
console.error('获取设备信息失败:', error) console.error('获取设备信息失败:', error)
+46 -28
View File
@@ -21,18 +21,18 @@
<!-- 全屏地图组件 --> <!-- 全屏地图组件 -->
<!-- 支付宝小程序使用专用组件 --> <!-- 支付宝小程序使用专用组件 -->
<!-- #ifdef MP-ALIPAY --> <!-- #ifdef MP-ALIPAY -->
<MapComponentAlipay v-if="!isLoading && userLocation && !locationPermissionDenied" ref="mapRef" :userLocation="userLocation" <MapComponentAlipay v-if="!isLoading && userLocation && !locationPermissionDenied" ref="mapRef"
:positionList="positionList" :filteredPositions="filteredPositions" :searchKeyword="searchKeyword" :userLocation="userLocation" :positionList="positionList" :filteredPositions="filteredPositions"
:enableMarkers="true" :bannerImages="bannerImages" :searchKeyword="searchKeyword" :enableMarkers="true" :bannerImages="bannerImages"
:hideMapOverlays="showGuidePopup || showNoticePopup || showActivityPopup" @relocate="handleRelocate" :hideMapOverlays="showGuidePopup || showNoticePopup || showActivityPopup" @relocate="handleRelocate"
@scan="handleScan" @showList="showLocationList" @markerTap="selectPosition" @scan="handleScan" @showList="showLocationList" @markerTap="selectPosition"
@mapCenterChange="onMapCenterChange" @bannerClick="handleBannerClick" @guide="openGuidePopup" /> @mapCenterChange="onMapCenterChange" @bannerClick="handleBannerClick" @guide="openGuidePopup" />
<!-- #endif --> <!-- #endif -->
<!-- 非支付宝小程序使用通用组件 --> <!-- 非支付宝小程序使用通用组件 -->
<!-- #ifndef MP-ALIPAY --> <!-- #ifndef MP-ALIPAY -->
<MapComponent v-if="!isLoading && userLocation && !locationPermissionDenied" ref="mapRef" :userLocation="userLocation" <MapComponent v-if="!isLoading && userLocation && !locationPermissionDenied" ref="mapRef"
:positionList="positionList" :filteredPositions="filteredPositions" :searchKeyword="searchKeyword" :userLocation="userLocation" :positionList="positionList" :filteredPositions="filteredPositions"
:enableMarkers="true" :bannerImages="bannerImages" :searchKeyword="searchKeyword" :enableMarkers="true" :bannerImages="bannerImages"
:hideMapOverlays="showGuidePopup || showNoticePopup || showActivityPopup" @relocate="handleRelocate" :hideMapOverlays="showGuidePopup || showNoticePopup || showActivityPopup" @relocate="handleRelocate"
@scan="handleScan" @showList="showLocationList" @markerTap="selectPosition" @scan="handleScan" @showList="showLocationList" @markerTap="selectPosition"
@mapCenterChange="onMapCenterChange" @bannerClick="handleBannerClick" @guide="openGuidePopup" /> @mapCenterChange="onMapCenterChange" @bannerClick="handleBannerClick" @guide="openGuidePopup" />
@@ -125,13 +125,8 @@
<!-- 支付宝先授权 phoneNumber再调用 my.getPhoneNumber上报后端解密并校验一致性 --> <!-- 支付宝先授权 phoneNumber再调用 my.getPhoneNumber上报后端解密并校验一致性 -->
<!-- #ifdef MP-ALIPAY --> <!-- #ifdef MP-ALIPAY -->
<button <button class="auth-btn" open-type="getAuthorize" scope="phoneNumber"
class="auth-btn" @getAuthorize="onAliAuthorizePhoneNumber" @error="onAliAuthorizePhoneNumberError">
open-type="getAuthorize"
scope="phoneNumber"
@getAuthorize="onAliAuthorizePhoneNumber"
@error="onAliAuthorizePhoneNumberError"
>
<text>{{ $t('auth.getPhoneNumber') }}</text> <text>{{ $t('auth.getPhoneNumber') }}</text>
</button> </button>
<!-- #endif --> <!-- #endif -->
@@ -314,11 +309,15 @@
const navbarStyle = computed(() => { const navbarStyle = computed(() => {
// #ifdef MP-ALIPAY // #ifdef MP-ALIPAY
// 支付宝小程序:不设置 paddingTop // 支付宝小程序:不设置 paddingTop
return { paddingTop: '0px' } return {
paddingTop: '0px'
}
// #endif // #endif
// #ifndef MP-ALIPAY // #ifndef MP-ALIPAY
// 非支付宝小程序:设置 statusBarHeight // 非支付宝小程序:设置 statusBarHeight
return { paddingTop: statusBarHeight.value + 'px' } return {
paddingTop: statusBarHeight.value + 'px'
}
// #endif // #endif
}) })
@@ -326,21 +325,29 @@
const topInfoSectionStyle = computed(() => { const topInfoSectionStyle = computed(() => {
// #ifdef MP-ALIPAY // #ifdef MP-ALIPAY
// 支付宝小程序:使用 paddingTop // 支付宝小程序:使用 paddingTop
return { top: ( navBarHeight.value) + 'px' } return {
top: (navBarHeight.value) + 'px'
}
// #endif // #endif
// #ifndef MP-ALIPAY // #ifndef MP-ALIPAY
// 非支付宝小程序:使用 top // 非支付宝小程序:使用 top
return { top: (statusBarHeight.value + navBarHeight.value) + 'px' } return {
top: (statusBarHeight.value + navBarHeight.value) + 'px'
}
// #endif // #endif
}) })
// 主内容区域样式:不同端适配不同的 paddingTop // 主内容区域样式:不同端适配不同的 paddingTop
const mainContentStyle = computed(() => { const mainContentStyle = computed(() => {
// #ifdef MP-ALIPAY // #ifdef MP-ALIPAY
return { paddingTop: (navBarHeight.value + noticeHeight.value) + 'px' } return {
paddingTop: (navBarHeight.value + noticeHeight.value) + 'px'
}
// #endif // #endif
// #ifndef MP-ALIPAY // #ifndef MP-ALIPAY
return { paddingTop: (statusBarHeight.value + navBarHeight.value + noticeHeight.value) + 'px' } return {
paddingTop: (statusBarHeight.value + navBarHeight.value + noticeHeight.value) + 'px'
}
// #endif // #endif
}) })
@@ -372,7 +379,7 @@
`${k}=${encodeURIComponent(current.options[k])}`).join('&') : '' `${k}=${encodeURIComponent(current.options[k])}`).join('&') : ''
const redirect = encodeURIComponent(query ? `${route}?${query}` : route) const redirect = encodeURIComponent(query ? `${route}?${query}` : route)
uni.reLaunch({ uni.reLaunch({
url: `/subPackages/user/login/index?redirect=${redirect}` url: redirect
}) })
} catch (e) { } catch (e) {
uni.reLaunch({ uni.reLaunch({
@@ -437,8 +444,8 @@
// 调用接口获取广告内容 // 调用接口获取广告内容
const res = await getCurrentAdvertisement({ const res = await getCurrentAdvertisement({
appPlatform: appPlatform, // 微信平台 appPlatform: appPlatform, // 微信平台
appType: 'user' ,// 用户端 appType: 'user', // 用户端
pictureLocation:'home_banner' pictureLocation: 'home_banner'
}) })
console.log('首页广告响应:', res) console.log('首页广告响应:', res)
@@ -1336,19 +1343,25 @@
uni.navigateBack({ uni.navigateBack({
success: () => { success: () => {
setTimeout(() => { setTimeout(() => {
uni.navigateTo({ url }); uni.navigateTo({
url
});
}, 100); }, 100);
} }
}); });
} else { } else {
uni.navigateTo({ url }); uni.navigateTo({
url
});
} }
}; };
if (deviceInfo.feeConfig) { if (deviceInfo.feeConfig) {
try { try {
const feeConfig = JSON.parse(deviceInfo.feeConfig) const feeConfig = JSON.parse(deviceInfo.feeConfig)
closeScanPageAndNavigate(`/pages/device/detail?deviceNo=${deviceNo}&feeConfig=${encodeURIComponent(deviceInfo.feeConfig)}`); closeScanPageAndNavigate(
`/pages/device/detail?deviceNo=${deviceNo}&feeConfig=${encodeURIComponent(deviceInfo.feeConfig)}`
);
} catch (e) { } catch (e) {
closeScanPageAndNavigate(`/pages/device/detail?deviceNo=${deviceNo}`); closeScanPageAndNavigate(`/pages/device/detail?deviceNo=${deviceNo}`);
} }
@@ -1435,7 +1448,9 @@
// #ifdef MP-ALIPAY // #ifdef MP-ALIPAY
try { try {
console.log('[ALIPAY] getAuthorize(phoneNumber) event:', e) console.log('[ALIPAY] getAuthorize(phoneNumber) event:', e)
uni.showLoading({ title: t('common.processing') }) uni.showLoading({
title: t('common.processing')
})
const res = await getAlipayUserPhoneNumber() const res = await getAlipayUserPhoneNumber()
const aliPhone = res?.data?.phoneNumber || res?.data?.phone || res?.phoneNumber || '' const aliPhone = res?.data?.phoneNumber || res?.data?.phone || res?.phoneNumber || ''
if (!aliPhone) { if (!aliPhone) {
@@ -1453,7 +1468,10 @@
} }
// 缓存本次校验结果(避免每次扫码都弹) // 缓存本次校验结果(避免每次扫码都弹)
uni.setStorageSync('alipay_phone_verified', { phone: aliPhone, ts: Date.now() }) uni.setStorageSync('alipay_phone_verified', {
phone: aliPhone,
ts: Date.now()
})
showPhoneAuthPopup.value = false showPhoneAuthPopup.value = false
if (pendingScan.value) { if (pendingScan.value) {
@@ -1477,7 +1495,7 @@
// 使用指南弹窗控制 // 使用指南弹窗控制
const openPopup = () => { const openPopup = () => {
uni.navigateTo({ uni.navigateTo({
url:'/subPackages/business/device-goods' url: '/subPackages/business/device-goods'
}) })
// try { // try {
// showGuidePopup.value = true // showGuidePopup.value = true
+3 -1
View File
@@ -182,7 +182,9 @@
if (opts && opts.redirect) { if (opts && opts.redirect) {
try { try {
redirect.value = decodeURIComponent(opts.redirect) redirect.value = decodeURIComponent(opts.redirect)
} catch (_) {} } catch (err) {
}
} }
// #ifdef H5 // #ifdef H5
isHTML5.value = true isHTML5.value = true
+16 -10
View File
@@ -2,7 +2,8 @@
<view class="my-page"> <view class="my-page">
<view class="user-card" @click="navigateTo('/subPackages/user/userProfile/index')"> <view class="user-card" @click="navigateTo('/subPackages/user/userProfile/index')">
<view class="avatar-box"> <view class="avatar-box">
<image class="avatar" v-if="userInfo.avatar" :src="userInfo.avatar" mode="aspectFill" lazy-load="true"></image> <image class="avatar" v-if="userInfo.avatar" :src="userInfo.avatar" mode="aspectFill" lazy-load="true">
</image>
<image v-else class="avatar" src="@/static/head.png" mode="aspectFill" lazy-load="true"></image> <image v-else class="avatar" src="@/static/head.png" mode="aspectFill" lazy-load="true"></image>
</view> </view>
<view class="user-text"> <view class="user-text">
@@ -29,7 +30,8 @@
<swiper class="banner-swiper" :indicator-dots="bannerImages.length > 1" <swiper class="banner-swiper" :indicator-dots="bannerImages.length > 1"
:autoplay="bannerImages.length > 1" :circular="true" :interval="3000"> :autoplay="bannerImages.length > 1" :circular="true" :interval="3000">
<swiper-item v-for="(image, index) in bannerImages" :key="index"> <swiper-item v-for="(image, index) in bannerImages" :key="index">
<image class="banner-image" :src="image" mode="aspectFill" @click="handleBannerClick(index)" lazy-load="true"> <image class="banner-image" :src="image" mode="aspectFill" @click="handleBannerClick(index)"
lazy-load="true">
</image> </image>
</swiper-item> </swiper-item>
</swiper> </swiper>
@@ -78,7 +80,8 @@
</view> </view>
<view class="list-item" @click="navigateTo('/subPackages/service/help/index')"> <view class="list-item" @click="navigateTo('/subPackages/service/help/index')">
<view class="left"> <view class="left">
<image class="icon" src="/static/customer-service.png" mode="aspectFit" lazy-load="true"></image> <image class="icon" src="/static/customer-service.png" mode="aspectFit" lazy-load="true">
</image>
<text class="title">{{ $t('user.customerService') }}</text> <text class="title">{{ $t('user.customerService') }}</text>
</view> </view>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon> <uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
@@ -118,9 +121,11 @@
<view class="footer-agreements"> <view class="footer-agreements">
<view class="link-box"> <view class="link-box">
<text class="link" @click="navigateTo('/subPackages/other/legal/agreement')">{{ $t('user.userAgreement') }}</text> <text class="link"
@click="navigateTo('/subPackages/other/legal/agreement')">{{ $t('user.userAgreement') }}</text>
<text class="sep"></text> <text class="sep"></text>
<text class="link" @click="navigateTo('/subPackages/other/legal/privacy')">{{ $t('user.privacyPolicy') }}</text> <text class="link"
@click="navigateTo('/subPackages/other/legal/privacy')">{{ $t('user.privacyPolicy') }}</text>
</view> </view>
<view class="version">{{ $t('user.version') }}{{ appVersion }}</view> <view class="version">{{ $t('user.version') }}{{ appVersion }}</view>
</view> </view>
@@ -199,7 +204,7 @@
const res = await getCurrentAdvertisement({ const res = await getCurrentAdvertisement({
appPlatform: appPlatform, // 微信平台 appPlatform: appPlatform, // 微信平台
appType: 'user', // 用户端 appType: 'user', // 用户端
pictureLocation:'userProfile_banner' pictureLocation: 'userProfile_banner'
}) })
if (res && res.code === 200 && res.data) { if (res && res.code === 200 && res.data) {
@@ -267,7 +272,7 @@
uni.setNavigationBarTitle({ uni.setNavigationBarTitle({
title: t('user.personalCenter') title: t('user.personalCenter')
}) })
getInfo(); // getInfo();
initVersion(); initVersion();
getBannerImages(); // 加载广告 getBannerImages(); // 加载广告
}); });
@@ -340,11 +345,12 @@
const query = current && current.options ? Object.keys(current.options).map(k => const query = current && current.options ? Object.keys(current.options).map(k =>
`${k}=${encodeURIComponent(current.options[k])}`).join('&') : '' `${k}=${encodeURIComponent(current.options[k])}`).join('&') : ''
const redirect = encodeURIComponent(query ? `${route}?${query}` : route) const redirect = encodeURIComponent(query ? `${route}?${query}` : route)
uni.reLaunch({ console.log(redirect);
url: `/subPackages/user/login/index?redirect=${redirect}` uni.redirectTo({
url: redirect
}) })
} catch (e) { } catch (e) {
uni.reLaunch({ uni.redirectTo({
url: '/subPackages/user/login/index' url: '/subPackages/user/login/index'
}) })
} }
+56 -44
View File
@@ -45,10 +45,14 @@
</template> </template>
<script> <script>
import { getUserInfo } from '@/util/index.js' import {
import { URL } from '@/config/url' getUserInfo
} from '@/util/index.js'
import {
URL
} from '@/config/url'
export default { export default {
data() { data() {
return { return {
userInfo: {}, userInfo: {},
@@ -74,11 +78,17 @@ export default {
const pages = getCurrentPages() const pages = getCurrentPages()
const current = pages && pages.length ? pages[pages.length - 1] : null const current = pages && pages.length ? pages[pages.length - 1] : null
const route = current && current.route ? ('/' + current.route) : '/pages/index/index' const route = current && current.route ? ('/' + current.route) : '/pages/index/index'
const query = current && current.options ? Object.keys(current.options).map(k => `${k}=${encodeURIComponent(current.options[k])}`).join('&') : '' const query = current && current.options ? Object.keys(current.options).map(k =>
`${k}=${encodeURIComponent(current.options[k])}`).join('&') : ''
const redirect = encodeURIComponent(query ? `${route}?${query}` : route) const redirect = encodeURIComponent(query ? `${route}?${query}` : route)
uni.reLaunch({ url: `/subPackages/user/login/index?redirect=${redirect}` }) console.log(redirect);
uni.reLaunch({
url: redirect
})
} catch (e) { } catch (e) {
uni.reLaunch({ url: '/subPackages/user/login/index' }) uni.reLaunch({
url: '/subPackages/user/login/index'
})
} }
} else if (res.code === 200) { } else if (res.code === 200) {
this.userInfo = res.data this.userInfo = res.data
@@ -92,7 +102,9 @@ export default {
} }
}, },
navigateTo(url) { navigateTo(url) {
uni.navigateTo({ url }) uni.navigateTo({
url
})
}, },
handleLogout() { handleLogout() {
uni.showModal({ uni.showModal({
@@ -117,17 +129,17 @@ export default {
}) })
} }
} }
} }
</script> </script>
<style> <style>
.container { .container {
padding: 20rpx; padding: 20rpx;
background-color: #f5f5f5; background-color: #f5f5f5;
min-height: 100vh; min-height: 100vh;
} }
.user-card { .user-card {
background-color: #fff; background-color: #fff;
border-radius: 16rpx; border-radius: 16rpx;
padding: 30rpx; padding: 30rpx;
@@ -135,99 +147,99 @@ export default {
align-items: center; align-items: center;
margin-bottom: 20rpx; margin-bottom: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
} }
.avatar { .avatar {
width: 120rpx; width: 120rpx;
height: 120rpx; height: 120rpx;
border-radius: 60rpx; border-radius: 60rpx;
overflow: hidden; overflow: hidden;
margin-right: 30rpx; margin-right: 30rpx;
} }
.avatar image { .avatar image {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.user-info { .user-info {
flex: 1; flex: 1;
} }
.nickname { .nickname {
font-size: 32rpx; font-size: 32rpx;
font-weight: bold; font-weight: bold;
color: #333; color: #333;
margin-bottom: 10rpx; margin-bottom: 10rpx;
display: block; display: block;
} }
.phone { .phone {
font-size: 28rpx; font-size: 28rpx;
color: #666; color: #666;
} }
.balance-card { .balance-card {
background-color: #fff; background-color: #fff;
border-radius: 16rpx; border-radius: 16rpx;
padding: 30rpx; padding: 30rpx;
margin-bottom: 20rpx; margin-bottom: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
} }
.balance-title { .balance-title {
font-size: 28rpx; font-size: 28rpx;
color: #666; color: #666;
margin-bottom: 20rpx; margin-bottom: 20rpx;
} }
.balance-amount { .balance-amount {
font-size: 48rpx; font-size: 48rpx;
font-weight: bold; font-weight: bold;
color: #333; color: #333;
margin-bottom: 10rpx; margin-bottom: 10rpx;
} }
.balance-desc { .balance-desc {
font-size: 24rpx; font-size: 24rpx;
color: #999; color: #999;
} }
.menu-list { .menu-list {
background-color: #fff; background-color: #fff;
border-radius: 16rpx; border-radius: 16rpx;
margin-bottom: 20rpx; margin-bottom: 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
} }
.menu-item { .menu-item {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 30rpx; padding: 30rpx;
border-bottom: 1rpx solid #f5f5f5; border-bottom: 1rpx solid #f5f5f5;
} }
.menu-item:last-child { .menu-item:last-child {
border-bottom: none; border-bottom: none;
} }
.menu-icon { .menu-icon {
font-size: 36rpx; font-size: 36rpx;
margin-right: 20rpx; margin-right: 20rpx;
} }
.menu-text { .menu-text {
flex: 1; flex: 1;
font-size: 28rpx; font-size: 28rpx;
color: #333; color: #333;
} }
.menu-arrow { .menu-arrow {
font-size: 28rpx; font-size: 28rpx;
color: #999; color: #999;
} }
.logout-btn { .logout-btn {
margin-top: 40rpx; margin-top: 40rpx;
background-color: #fff; background-color: #fff;
border-radius: 16rpx; border-radius: 16rpx;
@@ -236,5 +248,5 @@ export default {
color: #ff4d4f; color: #ff4d4f;
font-size: 28rpx; font-size: 28rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
} }
</style> </style>
+103 -93
View File
@@ -2,7 +2,8 @@
<view class="profile-page"> <view class="profile-page">
<view class="avatar-section"> <view class="avatar-section">
<view class="avatar-container"> <view class="avatar-container">
<image class="avatar" v-if="userInfo.avatar" :src="userInfo.avatar" mode="aspectFill" lazy-load="true"></image> <image class="avatar" v-if="userInfo.avatar" :src="userInfo.avatar" mode="aspectFill" lazy-load="true">
</image>
<image v-else class="avatar" src="@/static/head.png" mode="aspectFill" lazy-load="true"></image> <image v-else class="avatar" src="@/static/head.png" mode="aspectFill" lazy-load="true"></image>
<!-- 覆盖在头像上的微信选择头像授权按钮仅小程序生效 --> <!-- 覆盖在头像上的微信选择头像授权按钮仅小程序生效 -->
<!-- #ifdef MP-WEIXIN --> <!-- #ifdef MP-WEIXIN -->
@@ -27,13 +28,8 @@
<!-- 昵称编辑输入框展开状态 --> <!-- 昵称编辑输入框展开状态 -->
<view class="nickname-edit-area" v-if="isEditingNickname"> <view class="nickname-edit-area" v-if="isEditingNickname">
<input <input class="nickname-input" v-model="newNickname" :placeholder="$t('userProfile.enterNickname')"
class="nickname-input" maxlength="20" :focus="true" />
v-model="newNickname"
:placeholder="$t('userProfile.enterNickname')"
maxlength="20"
:focus="true"
/>
<view class="edit-buttons"> <view class="edit-buttons">
<button class="cancel-btn" @click="cancelEditNickname">{{ $t('common.cancel') }}</button> <button class="cancel-btn" @click="cancelEditNickname">{{ $t('common.cancel') }}</button>
<button class="save-btn" @click="saveNickname">{{ $t('common.save') }}</button> <button class="save-btn" @click="saveNickname">{{ $t('common.save') }}</button>
@@ -43,11 +39,12 @@
<view class="form-item"> <view class="form-item">
<view class="label">{{ $t('userProfile.phone') }}</view> <view class="label">{{ $t('userProfile.phone') }}</view>
<view class="value"> <view class="value">
<text class="value-text">{{ userInfo.phone ? maskPhone(userInfo.phone) : $t('userProfile.notBound') }}</text> <text
class="value-text">{{ userInfo.phone ? maskPhone(userInfo.phone) : $t('userProfile.notBound') }}</text>
</view> </view>
</view> </view>
<!-- <view class="form-item" v-if="userInfo.balanceAmount !== undefined"> <!-- <view class="form-item" v-if="userInfo.balanceAmount !== undefined">
<view class="label">{{ $t('userProfile.balance') }}</view> <view class="label">{{ $t('userProfile.balance') }}</view>
<view class="value"> <view class="value">
<text class="value-text amount">¥{{ userInfo.balanceAmount || '0.00' }}</text> <text class="value-text amount">¥{{ userInfo.balanceAmount || '0.00' }}</text>
@@ -58,33 +55,45 @@
</template> </template>
<script setup> <script setup>
import { ref, reactive, onMounted } from 'vue'; import {
import { getMyIndexInfo, uploadUserAvatar, updateUserInfo } from '@/config/api/user.js'; ref,
import { useI18n } from '@/utils/i18n.js' reactive,
onMounted
} from 'vue';
import {
getMyIndexInfo,
uploadUserAvatar,
updateUserInfo
} from '@/config/api/user.js';
import {
useI18n
} from '@/utils/i18n.js'
const { t } = useI18n() const {
t
} = useI18n()
// 响应式状态 // 响应式状态
const userInfo = ref({ const userInfo = ref({
nickName: '', nickName: '',
phone: '', phone: '',
avatar: '', avatar: '',
balanceAmount: '0.00' balanceAmount: '0.00'
}); });
const newNickname = ref(''); const newNickname = ref('');
const isEditingNickname = ref(false); const isEditingNickname = ref(false);
// 页面加载时初始化 // 页面加载时初始化
onMounted(() => { onMounted(() => {
uni.setNavigationBarTitle({ uni.setNavigationBarTitle({
title: t('userProfile.title') title: t('userProfile.title')
}) })
loadUserInfo(); loadUserInfo();
}); });
// 获取用户信息 // 获取用户信息
const loadUserInfo = async () => { const loadUserInfo = async () => {
try { try {
const res = await getMyIndexInfo(); const res = await getMyIndexInfo();
console.log('User info response:', res); console.log('User info response:', res);
@@ -109,10 +118,10 @@ const loadUserInfo = async () => {
icon: 'none' icon: 'none'
}); });
} }
}; };
// 跳转登录 // 跳转登录
const redirectToLogin = () => { const redirectToLogin = () => {
try { try {
const pages = getCurrentPages(); const pages = getCurrentPages();
const current = pages && pages.length ? pages[pages.length - 1] : null; const current = pages && pages.length ? pages[pages.length - 1] : null;
@@ -121,18 +130,18 @@ const redirectToLogin = () => {
`${k}=${encodeURIComponent(current.options[k])}`).join('&') : ''; `${k}=${encodeURIComponent(current.options[k])}`).join('&') : '';
const redirect = encodeURIComponent(query ? `${route}?${query}` : route); const redirect = encodeURIComponent(query ? `${route}?${query}` : route);
uni.reLaunch({ uni.reLaunch({
url: `/subPackages/user/login/index?redirect=${redirect}` url: redirect
}); });
} catch (e) { } catch (e) {
uni.reLaunch({ uni.reLaunch({
url: '/subPackages/user/login/index' url: '/subPackages/user/login/index'
}); });
} }
}; };
// 小程序原生选择头像回调 // 小程序原生选择头像回调
const onChooseAvatar = async (e) => { const onChooseAvatar = async (e) => {
console.log(e.detail.avatarUrl,'获取头像详情'); console.log(e.detail.avatarUrl, '获取头像详情');
try { try {
const token = uni.getStorageSync('token'); const token = uni.getStorageSync('token');
@@ -175,22 +184,22 @@ console.log(e.detail.avatarUrl,'获取头像详情');
} finally { } finally {
uni.hideLoading(); uni.hideLoading();
} }
}; };
// 开始编辑昵称 // 开始编辑昵称
const startEditNickname = () => { const startEditNickname = () => {
newNickname.value = userInfo.value.nickName || ''; newNickname.value = userInfo.value.nickName || '';
isEditingNickname.value = true; isEditingNickname.value = true;
}; };
// 取消编辑昵称 // 取消编辑昵称
const cancelEditNickname = () => { const cancelEditNickname = () => {
isEditingNickname.value = false; isEditingNickname.value = false;
newNickname.value = ''; newNickname.value = '';
}; };
// 保存昵称 // 保存昵称
const saveNickname = async () => { const saveNickname = async () => {
if (!newNickname.value || !newNickname.value.trim()) { if (!newNickname.value || !newNickname.value.trim()) {
uni.showToast({ uni.showToast({
title: t('userProfile.nicknameRequired'), title: t('userProfile.nicknameRequired'),
@@ -249,46 +258,46 @@ const saveNickname = async () => {
icon: 'none' icon: 'none'
}); });
} }
}; };
// 手机号掩码函数 // 手机号掩码函数
function maskPhone(phone) { function maskPhone(phone) {
if (!phone) return ''; if (!phone) return '';
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2'); return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.profile-page { .profile-page {
min-height: 100vh; min-height: 100vh;
background-color: #f5f5f5; background-color: #f5f5f5;
padding-bottom: env(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom);
} }
.avatar-section { .avatar-section {
background: linear-gradient(180deg, #D1FFE1 0%, #ffffff 100%); background: linear-gradient(180deg, #D1FFE1 0%, #ffffff 100%);
padding: 60rpx 0 40rpx; padding: 60rpx 0 40rpx;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
} }
.avatar-container { .avatar-container {
position: relative; position: relative;
margin-bottom: 20rpx; margin-bottom: 20rpx;
} }
.avatar { .avatar {
width: 160rpx; width: 160rpx;
height: 160rpx; height: 160rpx;
border-radius: 80rpx; border-radius: 80rpx;
background-color: #f0f0f0; background-color: #f0f0f0;
display: block; display: block;
} }
/* 仅小程序端存在,此按钮覆盖在头像上捕获点击以触发选择头像 */ /* 仅小程序端存在,此按钮覆盖在头像上捕获点击以触发选择头像 */
/* #ifdef MP-WEIXIN || MP-ALIPAY */ /* #ifdef MP-WEIXIN || MP-ALIPAY */
.avatar-choose-btn { .avatar-choose-btn {
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
@@ -300,81 +309,83 @@ function maskPhone(phone) {
margin: 0; margin: 0;
opacity: 0; opacity: 0;
border-radius: 80rpx; border-radius: 80rpx;
} }
/* #endif */
.avatar-tip { /* #endif */
.avatar-tip {
font-size: 24rpx; font-size: 24rpx;
color: #999999; color: #999999;
} }
.form-section { .form-section {
margin: 20rpx 30rpx; margin: 20rpx 30rpx;
background-color: #ffffff; background-color: #ffffff;
border-radius: 20rpx; border-radius: 20rpx;
overflow: hidden; overflow: hidden;
} }
.form-item { .form-item {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 32rpx 30rpx; padding: 32rpx 30rpx;
border-bottom: 1rpx solid #f5f5f5; border-bottom: 1rpx solid #f5f5f5;
transition: all 0.3s ease; transition: all 0.3s ease;
} }
.form-item:last-child { .form-item:last-child {
border-bottom: none; border-bottom: none;
} }
.form-item.nickname-item.editing { .form-item.nickname-item.editing {
border-bottom: none; border-bottom: none;
padding-bottom: 20rpx; padding-bottom: 20rpx;
} }
.label { .label {
font-size: 30rpx; font-size: 30rpx;
color: #333333; color: #333333;
font-weight: 500; font-weight: 500;
} }
.value { .value {
display: flex; display: flex;
align-items: center; align-items: center;
} }
.value-text { .value-text {
font-size: 28rpx; font-size: 28rpx;
color: #666666; color: #666666;
margin-right: 10rpx; margin-right: 10rpx;
} }
.value-text.amount { .value-text.amount {
color: #e2231a; color: #e2231a;
font-weight: 600; font-weight: 600;
font-size: 32rpx; font-size: 32rpx;
} }
/* 昵称编辑区域样式 */ /* 昵称编辑区域样式 */
.nickname-edit-area { .nickname-edit-area {
padding: 0 30rpx 30rpx 30rpx; padding: 0 30rpx 30rpx 30rpx;
border-bottom: 1rpx solid #f5f5f5; border-bottom: 1rpx solid #f5f5f5;
animation: slideDown 0.3s ease; animation: slideDown 0.3s ease;
} }
@keyframes slideDown { @keyframes slideDown {
from { from {
opacity: 0; opacity: 0;
transform: translateY(-20rpx); transform: translateY(-20rpx);
} }
to { to {
opacity: 1; opacity: 1;
transform: translateY(0); transform: translateY(0);
} }
} }
.nickname-input { .nickname-input {
width: 100%; width: 100%;
height: 80rpx; height: 80rpx;
border: 1rpx solid #e0e0e0; border: 1rpx solid #e0e0e0;
@@ -385,16 +396,16 @@ function maskPhone(phone) {
box-sizing: border-box; box-sizing: border-box;
margin-bottom: 20rpx; margin-bottom: 20rpx;
background-color: #fafafa; background-color: #fafafa;
} }
.edit-buttons { .edit-buttons {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
gap: 20rpx; gap: 20rpx;
} }
.cancel-btn, .cancel-btn,
.save-btn { .save-btn {
padding: 0 40rpx; padding: 0 40rpx;
height: 64rpx; height: 64rpx;
line-height: 64rpx; line-height: 64rpx;
@@ -403,16 +414,15 @@ function maskPhone(phone) {
font-size: 28rpx; font-size: 28rpx;
border: none; border: none;
min-width: 120rpx; min-width: 120rpx;
} }
.cancel-btn { .cancel-btn {
background-color: #f5f5f5; background-color: #f5f5f5;
color: #666666; color: #666666;
} }
.save-btn { .save-btn {
background: linear-gradient(135deg, #42d392 0%, #28c76f 100%); background: linear-gradient(135deg, #42d392 0%, #28c76f 100%);
color: #ffffff; color: #ffffff;
} }
</style> </style>
+32
View File
@@ -0,0 +1,32 @@
<template>
</template>
<script setup>
import {
onLoad
} from '@dcloudio/uni-app'
import { onMounted } from 'vue';
onLoad((options) => {
let deviceNo;
if (options) {
deviceNo = options.deviceNo;
uni.reLaunch({
url: `/pages/device/detail?deviceNo=${deviceNo}`
})
}
})
// onMounted((options)=>{
// let deviceNo;
// if (options) {
// deviceNo = options.deviceNo;
// uni.reLaunch({
// url: `/pages/device/detail?deviceNo=${deviceNo}`
// })
// }
// })
</script>
<style>
</style>