feat:国际化多语言适配

This commit is contained in:
2025-10-29 15:48:40 +08:00
parent 985d739324
commit 3d67dc928d
41 changed files with 2636 additions and 2801 deletions
+24 -20
View File
@@ -2,27 +2,27 @@
<view class="deposit-container">
<!-- 押金金额卡片 -->
<view class="deposit-card">
<view class="title">押金余额</view>
<view class="title">{{ $t('deposit.depositBalance') }}</view>
<view class="amount">¥{{ depositAmount }}</view>
<button class="withdraw-btn" @click="handleWithdraw" :disabled="depositAmount <= 0">提现</button>
<button class="withdraw-btn" @click="handleWithdraw" :disabled="depositAmount <= 0">{{ $t('deposit.withdraw') }}</button>
</view>
<!-- 提现说明 -->
<view class="notice-card">
<view class="notice-title">
<view class="dot"></view>
<text>提现说明</text>
<text>{{ $t('deposit.withdrawNotice') }}</text>
</view>
<view class="notice-content">
<view class="notice-item">1. 提现金额将原路退回支付账户</view>
<view class="notice-item">2. 提现申请提交后预计0-7个工作日到账</view>
<view class="notice-item">3. 如超时未收到请联系客服处理</view>
<view class="notice-item">1. {{ $t('deposit.withdrawNotice1') }}</view>
<view class="notice-item">2. {{ $t('deposit.withdrawNotice2') }}</view>
<view class="notice-item">3. {{ $t('deposit.withdrawNotice3') }}</view>
</view>
</view>
<!-- 押金记录 -->
<view class="record-card" v-if="records.length > 0">
<view class="record-title">押金记录</view>
<view class="record-title">{{ $t('deposit.depositRecord') }}</view>
<view class="record-list">
<view class="record-item" v-for="(item, index) in records" :key="index">
<view class="record-info">
@@ -53,6 +53,10 @@ export default {
}
},
onLoad() {
// 设置页面标题
uni.setNavigationBarTitle({
title: this.$t('deposit.title')
})
// this.loadUserInfo()
},
onShow() {
@@ -84,7 +88,7 @@ export default {
} catch (error) {
console.error('获取用户信息失败:', error)
uni.showToast({
title: '获取用户信息失败',
title: this.$t('user.getUserInfoFailed'),
icon: 'none'
})
}
@@ -92,7 +96,7 @@ export default {
async handleWithdraw() {
if (parseFloat(this.depositAmount) <= 0) {
uni.showToast({
title: '无可提现余额',
title: this.$t('deposit.noBalance'),
icon: 'none'
})
return
@@ -111,12 +115,12 @@ export default {
// }
uni.showModal({
title: '确认提现',
content: '押金将原路退回,预计0-7个工作日到账',
title: this.$t('deposit.confirmWithdraw'),
content: this.$t('deposit.withdrawDesc'),
success: async (res) => {
if (res.confirm) {
uni.showLoading({
title: '提现中...'
title: this.$t('deposit.withdrawing')
})
try {
@@ -127,7 +131,7 @@ export default {
if (result.code === 200) {
uni.hideLoading()
uni.showToast({
title: '提现申请已提交',
title: this.$t('deposit.withdrawSubmitted'),
icon: 'success'
})
@@ -144,26 +148,26 @@ export default {
this.loadUserInfo()
}, 1500)
} else {
throw new Error(result.msg || '提现失败')
throw new Error(result.msg || this.$t('deposit.withdrawFailed'))
}
} catch (error) {
console.error('提现失败:', error)
uni.hideLoading()
// 更详细的错误处理
let errorMessage = '提现失败,请稍后再试';
let errorMessage = this.$t('deposit.withdrawFailed');
// 如果有具体错误信息,使用它
if (error.message) {
// 常见错误消息处理
if (error.message.includes('尚未归还')) {
errorMessage = '当前订单尚未归还,请归还后再提现';
errorMessage = this.$t('deposit.orderNotReturned');
} else if (error.message.includes('已退还')) {
errorMessage = '押金已退还,无需重复提现';
errorMessage = this.$t('deposit.alreadyRefunded');
} else if (error.message.includes('处理中')) {
errorMessage = '押金退还处理中,请耐心等待';
errorMessage = this.$t('deposit.refundProcessing');
} else if (error.message.includes('余额为0')) {
errorMessage = '账户余额为0,无法提现';
errorMessage = this.$t('deposit.noBalance');
} else {
// 使用后端返回的具体错误消息
errorMessage = error.message;
@@ -172,7 +176,7 @@ export default {
// 显示错误提示
uni.showModal({
title: '提现失败',
title: this.$t('deposit.withdrawFailed'),
content: errorMessage,
showCancel: false
})
+55 -49
View File
@@ -12,7 +12,7 @@
</view>
</view>
<view class="device-id">
<text class="id-label">设备号</text>
<text class="id-label">{{ $t('device.deviceNo') }}</text>
<text class="id-value">{{ deviceId }}</text>
</view>
</view>
@@ -20,17 +20,17 @@
<!-- 计费规则 -->
<view class="card pricing-card">
<view class="card-header">
<text class="card-title">计费规则</text>
<text class="card-title">{{ $t('device.pricingRules') }}</text>
</view>
<view class="pricing-banner">
<view class="pricing-main">
<text class="price-symbol">¥</text>
<text class="price">{{ deviceFeeConfig.maxHourPrice || '5.00' }}</text>
<text class="unit">/小时</text>
<text class="unit">/{{ $t('time.hour') }}</text>
</view>
<view class="cap-badge">
<text class="cap-text">{{ deviceInfo.depositAmount || '99' }}元封顶</text>
<text class="cap-text">{{ deviceInfo.depositAmount || '99' }}{{ $t('device.capLimit') }}</text>
</view>
</view>
@@ -51,20 +51,20 @@
<!-- 使用说明 -->
<view class="card notice-card">
<view class="card-header">
<text class="card-title">使用说明</text>
<text class="card-title">{{ $t('device.usageInstructions') }}</text>
</view>
<view class="notice-items">
<view class="notice-item">
<view class="notice-dot"></view>
<text class="notice-text">请在使用前检查设备是否完好</text>
<text class="notice-text">{{ $t('device.checkBeforeUse') }}</text>
</view>
<view class="notice-item">
<view class="notice-dot"></view>
<text class="notice-text">超出使用时间将自动按小时计费</text>
<text class="notice-text">{{ $t('device.autoChargeOvertime') }}</text>
</view>
<view class="notice-item">
<view class="notice-dot"></view>
<text class="notice-text">请在指定区域内使用设备</text>
<text class="notice-text">{{ $t('device.useInDesignatedArea') }}</text>
</view>
</view>
</view>
@@ -73,11 +73,11 @@
<view class="footer">
<button class="rent-button" :class="{ 'return-button': hasActiveOrder }"
@click="handleRent('wx-score-pay')">
<text>{{ hasActiveOrder ? '归还设备' : '免押金租借' }}</text>
<text>{{ hasActiveOrder ? $t('order.returnDevice') : $t('device.rentDepositFree') }}</text>
</button>
<view class="wechat-credit">
<image src="/static/images/wxpayflag.png" mode="aspectFit" class="wx-icon"></image>
<text class="credit-text">微信支付分 <text class="divider">|</text> 550分以上优享</text>
<text class="credit-text">{{ $t('device.wxPayScoreDesc') }}</text>
</view>
</view>
@@ -86,17 +86,17 @@
<view class="popup-mask" @click.stop></view>
<view class="popup-content">
<view class="popup-header">
<text class="popup-title">授权获取手机号</text>
<text class="popup-title">{{ $t('auth.authTitle') }}</text>
</view>
<view class="popup-body">
<view class="auth-desc">
<text>为了提供更好的服务需要授权获取您的手机号</text>
<text>{{ $t('auth.authDescShort') }}</text>
</view>
<button class="auth-btn" open-type="getPhoneNumber" @getphonenumber="onGetPhoneNumber">
一键获取手机号
{{ $t('auth.getPhoneNumber') }}
</button>
<view class="auth-cancel" @click="showPhoneAuthPopup = false">
<text>暂不授权</text>
<text>{{ $t('auth.notNow') }}</text>
</view>
</view>
</view>
@@ -133,6 +133,9 @@
getUserInfo,
getUserPhoneNumber
} from '@/util/index.js'
import { useI18n } from '@/utils/i18n.js'
const { t: $t } = useI18n()
// 响应式状态
const deviceInfo = ref({})
@@ -143,7 +146,7 @@
const batteryLevel = ref(95)
const hasActiveOrder = ref(false)
const deviceStatus = reactive({
text: '可使用',
get text() { return $t('device.available') },
class: 'available'
})
const isLoggedIn = ref(true)
@@ -164,6 +167,9 @@
})
onMounted(async () => {
uni.setNavigationBarTitle({
title: $t('device.deviceInfo')
})
await checkUserPhone()
await fetchDeviceInfo()
})
@@ -195,7 +201,7 @@
// 用户拒绝授权的情况
if (e.detail.errMsg && e.detail.errMsg.includes('deny')) {
uni.showToast({
title: '需要授权手机号才能使用设备',
title: $t('auth.phoneRequired'),
icon: 'none'
})
return
@@ -204,7 +210,7 @@
// 获取到授权code
if (e.detail.code) {
uni.showLoading({
title: '获取中...'
title: $t('auth.getting')
})
console.log('获取到的授权code:', e.detail.code)
@@ -236,15 +242,15 @@
showPhoneAuthPopup.value = false
uni.showToast({
title: '手机号获取成功',
title: $t('auth.phoneSuccess'),
icon: 'success'
})
} else {
// 记录详细信息,不抛出错误
console.warn('获取手机号响应异常:', res.msg || '未知错误')
uni.showModal({
title: '获取手机号异常',
content: `状态码: ${res.code}, 消息: ${res.msg || '无'}`,
title: $t('auth.phoneError'),
content: `${$t('common.statusCode')}: ${res.code}, ${$t('common.message')}: ${res.msg || $t('common.none')}`,
showCancel: false
})
}
@@ -256,8 +262,8 @@
// 显示更详细的错误信息
let errMsg = err.message || err.toString()
uni.showModal({
title: '获取手机号失败',
content: '错误信息: ' + errMsg,
title: $t('auth.phoneGetFailed'),
content: $t('common.errorInfo') + ': ' + errMsg,
showCancel: false
})
})
@@ -265,14 +271,14 @@
uni.hideLoading()
console.error('获取手机号外部错误:', outerError)
uni.showModal({
title: '意外错误',
content: '处理过程发生异常: ' + (outerError.message || outerError),
title: $t('common.unexpectedError'),
content: $t('common.processException') + ': ' + (outerError.message || outerError),
showCancel: false
})
}
} else {
uni.showToast({
title: '获取授权码失败',
title: $t('auth.authCodeFailed'),
icon: 'none'
})
}
@@ -296,16 +302,16 @@
deviceLocation.value = res.data.position.name
}
// 更新设备状态
if (deviceInfo.value.status) {
if (deviceInfo.value.status === 'online') {
deviceStatus.text = '可使用'
deviceStatus.class = 'available'
} else if (deviceInfo.value.status === 'offline') {
deviceStatus.text = '离线'
deviceStatus.class = 'offline'
}
// 更新设备状态
if (deviceInfo.value.status) {
if (deviceInfo.value.status === 'online') {
deviceStatus.text = $t('device.available')
deviceStatus.class = 'available'
} else if (deviceInfo.value.status === 'offline') {
deviceStatus.text = $t('device.offline')
deviceStatus.class = 'offline'
}
}
if (deviceInfo.value.feeConfig) {
deviceFeeConfig.value = JSON.parse(deviceInfo.value.feeConfig)[0] || {}
} else {
@@ -321,9 +327,9 @@
// 显示登录提示
const showLoginTip = () => {
uni.showModal({
title: '提示',
content: '请先登录后再操作',
confirmText: '去登录',
title: $t('common.tips'),
content: $t('common.loginRequired'),
confirmText: $t('auth.goToLogin'),
success: (res) => {
if (res.confirm) {
uni.navigateTo({
@@ -358,7 +364,7 @@
}
} catch (error) {
uni.showToast({
title: '订单状态查询失败',
title: $t('order.getOrderStatusFailed'),
icon: 'none'
})
}
@@ -444,6 +450,9 @@
// 提交租借订单
const submitRentOrder = async (payWay) => {
try {
uni.showLoading({
title: $t('common.processing')
})
// --- 第一步:先请求订阅消息(必须在用户点击的同步上下文中)---
if (payWay === 'wx-score-pay') {
console.log('准备请求订阅消息(在异步操作之前),时间:', new Date().toLocaleTimeString());
@@ -471,14 +480,11 @@
}
// --- 订阅消息请求完成 ---
uni.showLoading({
title: '处理中'
})
console.log(deviceId.value);
// 调用设备租借接口
const rentResult = await rentPowerBank(deviceId.value, phoneNumber.value)
if (rentResult.code !== 200) {
throw new Error(rentResult.msg || '设备租借失败')
throw new Error(rentResult.msg || $t('device.rentFailed'))
}
// 获取后端返回的订单信息
@@ -556,14 +562,14 @@
// 用户取消授权,需要取消订单
try {
uni.showLoading({
title: '取消订单中'
title: $t('order.cancelling')
});
const cancelRes = await cancelOrder({ orderId: order.orderNo });
console.log('订单取消结果:', cancelRes);
uni.hideLoading();
uni.showToast({
title: '已取消订单',
title: $t('order.orderCancelled'),
icon: 'none',
duration: 2000
});
@@ -578,7 +584,7 @@
console.error('取消订单失败:', cancelError);
uni.hideLoading();
uni.showToast({
title: '取消订单失败,请联系客服',
title: $t('order.cancelFailedContactService'),
icon: 'none'
});
}
@@ -589,7 +595,7 @@
// 支付分调用异常,也需要取消订单
try {
uni.showLoading({
title: '取消订单中'
title: $t('order.cancelling')
});
const cancelRes = await cancelOrder({ orderId: order.orderNo });
console.log('订单取消结果:', cancelRes);
@@ -600,7 +606,7 @@
}
uni.showToast({
title: '支付分调用失败,订单已取消',
title: $t('device.payScoreFailedCancelled'),
icon: 'none'
});
@@ -612,7 +618,7 @@
}
} else {
uni.showToast({
title: res?.msg || '获取支付参数失败',
title: res?.msg || $t('device.getPayParamsFailed'),
icon: 'none'
});
}
@@ -620,7 +626,7 @@
} catch (error) {
uni.hideLoading()
uni.showToast({
title: error.message || '租借失败,请重试',
title: error.message || $t('device.rentFailedRetry'),
icon: 'none'
})
}
+34 -23
View File
@@ -3,32 +3,32 @@
<!-- 订单摘要卡片 -->
<view class="order-summary-card" v-if="orderInfo.orderNo">
<view class="summary-row">
<view class="label">订单号</view>
<view class="label">{{ $t('order.orderNo') }}</view>
<view class="value">{{ orderInfo.orderNo }}</view>
</view>
<view class="summary-row" v-if="orderInfo.deviceNo">
<view class="label">设备号</view>
<view class="label">{{ $t('order.deviceNo') }}</view>
<view class="value">{{ orderInfo.deviceNo }}</view>
</view>
<view class="summary-row" v-if="orderInfo.startTime">
<view class="label">开放时间</view>
<view class="label">{{ $t('express.openTime') }}</view>
<view class="value">{{ orderInfo.startTime }}</view>
</view>
</view>
<!-- 表单区域 -->
<view class="form-section">
<view class="form-title">填写快递归还信息</view>
<view class="form-title">{{ $t('express.fillExpressInfo') }}</view>
<view class="input-wrapper">
<view class="input-label">联系电话</view>
<view class="input-label">{{ $t('express.contactPhone') }}</view>
<input class="input-field" type="number" v-model="phone" maxlength="20" />
</view>
<view class="input-wrapper">
<view class="input-label">快递单号</view>
<view class="input-label">{{ $t('express.expressNo') }}</view>
<input class="input-field" type="text" v-model="trackingNumber"
:placeholder="isFillMode ? '请输入需要补填的快递单号' : '请输入快递单号(可先留空)'" maxlength="40" />
:placeholder="isFillMode ? $t('express.fillTrackingPlaceholder') : $t('express.trackingPlaceholder')" maxlength="40" />
</view>
<view class="tips" v-if="tipsText">{{ tipsText }}</view>
@@ -36,7 +36,7 @@
<!-- 提交按钮 -->
<view class="submit-btn" :class="{ disabled: submitting }" @click="!submitting && handleSubmit()">
{{ isFillMode ? '确认补填' : '提交信息' }}
{{ isFillMode ? $t('express.confirmFill') : $t('express.submitInfo') }}
</view>
</view>
</template>
@@ -44,7 +44,9 @@
<script setup>
import {
ref,
reactive
reactive,
getCurrentInstance,
onMounted
} from 'vue'
import {
onLoad
@@ -58,6 +60,15 @@
getExpressReturnDetail,
fillExpressTrackingNumber
} from '@/config/api/expressReturn.js'
import { useI18n } from '@/utils/i18n.js'
const { t: $t } = useI18n()
onMounted(() => {
uni.setNavigationBarTitle({
title: $t('express.fillExpress')
})
})
const orderId = ref('')
const recordId = ref('')
@@ -85,7 +96,7 @@
if (!orderId.value) {
uni.showToast({
title: '缺少订单号',
title: $t('express.orderNoMissing'),
icon: 'none'
})
setTimeout(() => {
@@ -100,7 +111,7 @@
const loadOrder = async () => {
try {
uni.showLoading({
title: '加载中'
title: $t('common.loading')
})
const res = await queryById(orderId.value)
if (res?.code === 200 && res.data) {
@@ -110,11 +121,11 @@
// 默认联系电话可回填订单上的手机号(若有)
if (res.data.phone && !phone.value) phone.value = res.data.phone
} else {
throw new Error(res?.msg || '获取订单失败')
throw new Error(res?.msg || $t('order.getOrderFailed'))
}
} catch (e) {
uni.showToast({
title: e.message || '加载失败',
title: e.message || $t('express.loadFailed'),
icon: 'none'
})
} finally {
@@ -125,7 +136,7 @@
const loadRecordAndOrderByRecord = async () => {
try {
uni.showLoading({
title: '加载中'
title: $t('common.loading')
})
const res = await getExpressReturnDetail(recordId.value)
if (res?.code === 200 && res.data) {
@@ -135,11 +146,11 @@
}
if (res.data.userPhone && !phone.value) phone.value = res.data.userPhone
} else {
throw new Error(res?.msg || '获取记录失败')
throw new Error(res?.msg || $t('express.getRecordFailed'))
}
} catch (e) {
uni.showToast({
title: e.message || '加载失败',
title: e.message || $t('express.loadFailed'),
icon: 'none'
})
} finally {
@@ -155,10 +166,10 @@
if (rec.status === 0) {
recordId.value = rec.id
uni.showModal({
title: '提示',
content: '已存在快递归还申请,是否前往补填快递单号?',
confirmText: '去补填',
cancelText: '取消',
title: $t('common.tips'),
content: $t('express.existingReturnNotice'),
confirmText: $t('express.goToFill'),
cancelText: $t('common.cancel'),
success: (r) => {
if (r.confirm) {
uni.redirectTo({
@@ -170,7 +181,7 @@
return
} else {
uni.showToast({
title: '已有归还记录',
title: $t('express.alreadyHasRecord'),
icon: 'none'
})
setTimeout(() => {
@@ -190,14 +201,14 @@
const digits = (phone.value || '').replace(/\D/g, '')
if (!digits || digits.length < 5) {
uni.showToast({
title: '请填写有效联系电话',
title: $t('express.pleaseEnterValidPhone'),
icon: 'none'
})
return false
}
if (isFillMode.value && !trackingNumber.value) {
uni.showToast({
title: '请填写快递单号',
title: $t('express.pleaseEnterTrackingNo'),
icon: 'none'
})
return false
+36 -29
View File
@@ -16,23 +16,23 @@
<!-- 快递信息卡片 -->
<view class="info-card">
<view class="card-title">
<text class="title-text">快递信息</text>
<text class="title-text">{{ $t('express.expressInfo') }}</text>
</view>
<view class="info-list">
<view class="info-item">
<text class="label">快递公司</text>
<text class="label">{{ $t('express.expressCompany') }}</text>
<text class="value">{{ detailData.expressCompany }}</text>
</view>
<view class="info-item">
<text class="label">运单号</text>
<text class="label">{{ $t('express.trackingNo') }}</text>
<text class="value tracking-number">{{ detailData.trackingNumber }}</text>
</view>
<view class="info-item">
<text class="label">包裹类型</text>
<text class="label">{{ $t('express.packageType') }}</text>
<text class="value">{{ detailData.packageType }}</text>
</view>
<view class="info-item">
<text class="label">包裹重量</text>
<text class="label">{{ $t('express.packageWeight') }}</text>
<text class="value">{{ detailData.weight }}</text>
</view>
</view>
@@ -41,23 +41,23 @@
<!-- 归还信息卡片 -->
<view class="info-card">
<view class="card-title">
<text class="title-text">归还信息</text>
<text class="title-text">{{ $t('express.returnInfo') }}</text>
</view>
<view class="info-list">
<view class="info-item">
<text class="label">归还地址</text>
<text class="label">{{ $t('express.returnAddress') }}</text>
<text class="value address">{{ detailData.returnAddress }}</text>
</view>
<view class="info-item">
<text class="label">归还时间</text>
<text class="label">{{ $t('express.returnTime') }}</text>
<text class="value">{{ detailData.returnTime }}</text>
</view>
<view class="info-item">
<text class="label">处理时间</text>
<text class="label">{{ $t('express.processTime') }}</text>
<text class="value">{{ detailData.processTime || '--' }}</text>
</view>
<view class="info-item">
<text class="label">完成时间</text>
<text class="label">{{ $t('express.completeTime') }}</text>
<text class="value">{{ detailData.completeTime || '--' }}</text>
</view>
</view>
@@ -66,7 +66,7 @@
<!-- 备注信息卡片 -->
<view class="info-card" v-if="detailData.remark">
<view class="card-title">
<text class="title-text">备注信息</text>
<text class="title-text">{{ $t('express.remarkInfo') }}</text>
</view>
<view class="remark-content">
<text class="remark-text">{{ detailData.remark }}</text>
@@ -76,10 +76,10 @@
<!-- 操作按钮 -->
<view class="action-buttons">
<button class="action-btn primary" @click="handleCopyTracking">
<text class="btn-text">复制运单号</text>
<text class="btn-text">{{ $t('express.copyTrackingNo') }}</text>
</button>
<button class="action-btn secondary" @click="handleContactService">
<text class="btn-text">联系客服</text>
<text class="btn-text">{{ $t('user.customerService') }}</text>
</button>
</view>
</view>
@@ -89,6 +89,9 @@
import { ref, onMounted } from 'vue'
import { getExpressReturnDetail } from '@/config/api/expressReturn.js'
import { getCustomerPhone } from '@/util/index.js'
import { useI18n } from '@/utils/i18n.js'
const { t: $t } = useI18n()
// 详情数据
const detailData = ref({
@@ -128,21 +131,21 @@ const getStatusIcon = (status) => {
// 获取状态文本
const getStatusText = (status) => {
const textMap = {
'completed': '归还完成',
'processing': '处理中',
'pending': '待处理'
'completed': $t('express.returnCompleted'),
'processing': $t('express.processing'),
'pending': $t('express.pending')
}
return textMap[status] || '待处理'
return textMap[status] || $t('express.pending')
}
// 获取状态描述
const getStatusDesc = (status) => {
const descMap = {
'completed': '您的快递已成功归还',
'processing': '正在处理您的归还请求',
'pending': '等待处理归还申请'
'completed': $t('express.returnCompletedDesc'),
'processing': $t('express.processingDesc'),
'pending': $t('express.pendingDesc')
}
return descMap[status] || '等待处理归还申请'
return descMap[status] || $t('express.pendingDesc')
}
// 复制运单号
@@ -151,7 +154,7 @@ const handleCopyTracking = () => {
data: detailData.value.trackingNumber,
success: () => {
uni.showToast({
title: '运单号已复制',
title: $t('express.trackingNoCopied'),
icon: 'success'
})
}
@@ -162,10 +165,10 @@ const handleCopyTracking = () => {
const handleContactService = () => {
const customerPhone = getCustomerPhone()
uni.showModal({
title: '联系客服',
content: `客服电话:${customerPhone}\n工作时间:周一至周日 09:00-22:00`,
confirmText: '拨打',
cancelText: '取消',
title: $t('user.customerService'),
content: `${$t('help.phone')}${customerPhone}\n${$t('help.workingHours')}${$t('express.workingHours')}`,
confirmText: $t('express.call'),
cancelText: $t('common.cancel'),
success: (res) => {
if (res.confirm) {
uni.makePhoneCall({
@@ -178,12 +181,16 @@ const handleContactService = () => {
// 页面加载时获取详情数据
onMounted(async () => {
uni.setNavigationBarTitle({
title: $t('express.returnDetail')
})
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
const options = currentPage.options || {}
if (!options.id) return
try {
uni.showLoading({ title: '加载中' })
uni.showLoading({ title: $t('common.loading') })
const res = await getExpressReturnDetail(options.id)
if (res && res.code === 200 && res.data) {
const r = res.data
@@ -201,10 +208,10 @@ onMounted(async () => {
remark: r.remark || ''
}
} else {
throw new Error(res?.msg || '获取详情失败')
throw new Error(res?.msg || $t('express.getDetailFailed'))
}
} catch (e) {
uni.showToast({ title: e.message || '加载失败', icon: 'none' })
uni.showToast({ title: e.message || $t('express.loadFailed'), icon: 'none' })
} finally {
uni.hideLoading()
}
+31 -22
View File
@@ -2,13 +2,13 @@
<view class="express-return-container">
<!-- 收件信息卡片 -->
<view class="recipient-info-card">
<view class="info-header">收件信息</view>
<view class="info-header">{{ $t('express.recipientInfo') }}</view>
<view class="info-content">
<text class="recipient-name">风电者 18163601305</text>
<text class="recipient-address">湖南省长沙市岳麓区麓谷街道新长海尖科技园A2栋623</text>
<text class="recipient-name">{{ $t('express.recipientName') }}</text>
<text class="recipient-address">{{ $t('express.recipientAddress') }}</text>
</view>
<view class="copy-all-btn" @click="copyAllInfo">
<text class="btn-text">一键复制全部信息</text>
<text class="btn-text">{{ $t('express.copyAllInfo') }}</text>
</view>
</view>
@@ -26,9 +26,9 @@
<text class="status-label">{{ getStatusText(item.status) }}</text>
</view>
<view class="content-body">
<text class="info-text">订单号{{ item.orderId || '' }}</text>
<text class="info-text">快递单号{{ item.trackingNumber || '待填写' }}</text>
<text class="info-text">用户电话{{ item.userPhone || '' }}</text>
<text class="info-text">{{ $t('order.orderNo') }}{{ item.orderId || '' }}</text>
<text class="info-text">{{ $t('express.expressNo') }}{{ item.trackingNumber || $t('express.toFill') }}</text>
<text class="info-text">{{ $t('express.userPhone') }}{{ item.userPhone || '' }}</text>
</view>
</view>
@@ -44,7 +44,7 @@
<!-- 空状态 -->
<view class="empty-state" v-if="returnList.length === 0">
<view class="empty-icon">📦</view>
<text class="empty-text">暂无归还记录</text>
<text class="empty-text">{{ $t('express.noReturnRecord') }}</text>
</view>
</view>
</template>
@@ -52,11 +52,21 @@
<script setup>
import { ref, onMounted } from 'vue'
import { getExpressReturnList } from '@/config/api/expressReturn.js'
import { useI18n } from '@/utils/i18n.js'
const { t: $t } = useI18n()
const returnList = ref([])
const loading = ref(false)
const query = ref({ pageNum: 1, pageSize: 20 })
onMounted(() => {
uni.setNavigationBarTitle({
title: $t('express.returnRecord')
})
loadList()
})
// 收件信息
const recipientName = '风电者 18163601305'
const recipientAddress = '湖南省长沙市岳麓区麓谷街道新长海尖科技园A2栋623'
@@ -83,10 +93,10 @@ const loadList = async () => {
remark: r.remark
}))
} else {
throw new Error(res?.msg || '获取列表失败')
throw new Error(res?.msg || $t('express.getListFailed'))
}
} catch (e) {
uni.showToast({ title: e.message || '加载失败', icon: 'none' })
uni.showToast({ title: e.message || $t('express.loadFailed'), icon: 'none' })
} finally {
loading.value = false
}
@@ -108,27 +118,27 @@ const getStatusClass = (status) => ({
}[status] || 'status-pending')
const getStatusText = (status) => ({
'completed': '暂停计费中',
'processing': '暂停计费中',
'pending': '暂停计费中'
}[status] || '暂停计费中')
'completed': $t('express.billingPaused'),
'processing': $t('express.billingPaused'),
'pending': $t('express.billingPaused')
}[status] || $t('express.billingPaused'))
const getStatusBadge = (status) => ({
'completed': '已完成',
'processing': '处理中',
'pending': '待处理'
}[status] || '待处理')
'completed': $t('express.completed'),
'processing': $t('express.processing'),
'pending': $t('express.pending')
}[status] || $t('express.pending'))
// 一键复制全部信息
const copyAllInfo = () => {
const allInfo = `收件人:${recipientName}\n收件地址${recipientAddress}`
const allInfo = `${$t('express.recipient')}${recipientName}\n${$t('express.recipientAddressLabel')}${recipientAddress}`
uni.setClipboardData({
data: allInfo,
success: () => {
uni.showToast({ title: '全部信息已复制', icon: 'success' })
uni.showToast({ title: $t('express.copySuccess'), icon: 'success' })
},
fail: () => {
uni.showToast({ title: '复制失败', icon: 'none' })
uni.showToast({ title: $t('express.copyFailed'), icon: 'none' })
}
})
}
@@ -144,7 +154,6 @@ const handleItemClick = (item) => {
}
}
onMounted(loadList)
</script>
<style lang="scss">
+51 -41
View File
@@ -1,58 +1,59 @@
<template>
<view class="feedback-container">
<!-- <form> -->
<!-- 问题类型选择 -->
<view class="type-section">
<view class="section-title">问题类型</view>
<view class="type-grid">
<view v-for="(type, index) in types" :key="index" class="type-item"
:class="{ active: selectedType === index }" @click="selectType(index)">
{{ type }}
</view>
<!-- 问题类型选择 -->
<view class="type-section">
<view class="section-title">{{ $t('feedback.issueType') }}</view>
<view class="type-grid">
<view v-for="(type, index) in types" :key="index" class="type-item"
:class="{ active: selectedType === index }" @click="selectType(index)">
{{ type }}
</view>
</view>
</view>
<!-- 问题描述 -->
<view class="description-section">
<view class="section-title">问题描述</view>
<textarea class="description-input" v-model="description" placeholder="请详细描述您遇到的问题,以便我们更好地为您解决"
maxlength="500" name="description" />
<view class="word-count">{{ description.length }}/500</view>
</view>
<!-- 问题描述 -->
<view class="description-section">
<view class="section-title">{{ $t('feedback.issueDescription') }}</view>
<textarea class="description-input" v-model="description" :placeholder="$t('feedback.placeholder')"
maxlength="500" name="description" />
<view class="word-count">{{ description.length }}/500</view>
</view>
<!-- 图片上传 -->
<view class="upload-section">
<view class="section-title">图片上传选填</view>
<!-- 图片上传 -->
<view class="upload-section">
<view class="section-title">{{ $t('feedback.imageUpload') }}</view>
<view class="upload-grid">
<view class="upload-item" v-for="(img, index) in images" :key="index">
<image :src="img" mode="aspectFill" />
<view class="delete-btn" @click="deleteImage(index)">×</view>
</view>
<view class="upload-btn" @click="chooseImage" v-if="images.length < 3">
<text class="plus">+</text>
<text class="tip">上传图片</text>
</view>
<view class="upload-btn" @click="chooseImage" v-if="images.length < 3">
<text class="plus">+</text>
<text class="tip">{{ $t('feedback.uploadImage') }}</text>
</view>
</view>
</view>
<!-- 联系方式 -->
<view class="contact-section">
<view class="section-title">联系方式</view>
<input class="contact-input" v-model="contact" placeholder="请留下您的手机号,方便我们联系您" type="number"
maxlength="11" name="contact" />
</view>
<!-- 联系方式 -->
<view class="contact-section">
<view class="section-title">{{ $t('feedback.contactInfo') }}</view>
<input class="contact-input" v-model="contact" :placeholder="$t('feedback.contactPlaceholder')" type="number"
maxlength="11" name="contact" />
</view>
<!-- 提交按钮 -->
<view class="submit-section">
<view class="submit-btn" @click="submitFeedback">提交反馈</view>
</view>
<!-- 提交按钮 -->
<view class="submit-section">
<view class="submit-btn" @click="submitFeedback">{{ $t('feedback.submit') }}</view>
</view>
<!-- </form> -->
</view>
</template>
<script setup>
import {
ref
ref,
onMounted
} from 'vue'
import {
URL,
@@ -64,9 +65,18 @@
import {
addUserFeedback
} from '../../config/api/feedback'
import { useI18n } from '@/utils/i18n.js'
const { t: $t } = useI18n()
onMounted(() => {
uni.setNavigationBarTitle({
title: $t('feedback.title')
})
})
// 响应式数据
const types = ref(['设备故障', '收费问题', '使用建议', '其他'])
const types = ref([$t('feedback.deviceFault'), $t('feedback.chargingIssue'), $t('feedback.usageSuggestion'), $t('feedback.other')])
const selectedType = ref(-1)
const paramsType = ref('')
const description = ref('')
@@ -113,7 +123,7 @@
const submitFeedback = async () => {
if (selectedType.value === -1) {
uni.showToast({
title: '请选择问题类型',
title: $t('feedback.pleaseSelectType'),
icon: 'none'
})
return
@@ -121,7 +131,7 @@
if (!description.value.trim()) {
uni.showToast({
title: '请描述您的问题',
title: $t('feedback.pleaseDescribe'),
icon: 'none'
})
return
@@ -129,13 +139,13 @@
if (!contact.value) {
uni.showToast({
title: '请留下联系方式',
title: $t('feedback.pleaseContact'),
icon: 'none'
})
return
}
if (types.value[selectedType.value] == '设备故障' || types.value[selectedType.value] == '收费问题') {
if (types.value[selectedType.value] == $t('feedback.deviceFault') || types.value[selectedType.value] == $t('feedback.chargingIssue')) {
paramsType.value = 'complain'
} else {
paramsType.value = 'suggestion'
@@ -164,7 +174,7 @@
// 兼容后端返回 { code: 200 } 或 HTTP 200 情况
if ((res.statusCode === 200) && ((res.data && res.data.code === 200) || res.data === true || res.data?.success === true)) {
uni.showToast({
title: '反馈成功',
title: $t('feedback.submitSuccess'),
icon: 'success'
})
setTimeout(() => {
@@ -173,14 +183,14 @@
return
}
uni.showToast({
title: (res.data && (res.data.msg || res.data.message)) || '反馈失败',
title: (res.data && (res.data.msg || res.data.message)) || $t('feedback.submitFailed'),
icon: 'none'
})
},
fail: (err) => {
console.error('feedback request failed:', err)
uni.showToast({
title: '网络错误,请稍后重试',
title: $t('error.networkError'),
icon: 'none'
})
}
+7 -3
View File
@@ -20,14 +20,14 @@
<!-- 联系客服 -->
<view class="contact-card">
<view class="contact-title">{{ HELP_CONTENT.CONTACT.TITLE }}</view>
<view class="contact-title">{{ $t('help.contactUs') }}</view>
<view class="contact-content">
<view class="contact-item">
<text class="label">{{ HELP_CONTENT.CONTACT.PHONE.LABEL }}</text>
<text class="label">{{ $t('help.phone') }}</text>
<text class="value" @click="makePhoneCall">{{ customerPhone }}</text>
</view>
<view class="contact-item">
<text class="label">{{ HELP_CONTENT.CONTACT.SERVICE_TIME.LABEL }}</text>
<text class="label">{{ $t('help.workingHours') }}</text>
<text class="value">{{ HELP_CONTENT.CONTACT.SERVICE_TIME.VALUE }}</text>
</view>
</view>
@@ -51,6 +51,10 @@ export default {
}
},
onLoad() {
// 设置页面标题
uni.setNavigationBarTitle({
title: this.$t('help.title')
})
// 从缓存读取客服电话
this.customerPhone = getCustomerPhone()
},
+60 -34
View File
@@ -1,19 +1,23 @@
<template>
<view class="container fullscreen">
<!-- 自定义导航栏 -->
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="navbar-content" :style="{ height: navBarHeight + 'px' }">
<text class="navbar-title">风电者共享风扇&暖手充电宝</text>
</view>
<!-- 自定义导航栏 -->
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="navbar-content" :style="{ height: navBarHeight + 'px' }">
<text class="navbar-title">{{ $t('home.title') }}</text>
</view>
<view class="map-notice" v-if="noticeText" @click="openNoticePopup">
</view>
<!-- 顶部信息区域通知招商等 -->
<view class="top-info-section" :style="{ top: (statusBarHeight + navBarHeight) + 'px' }">
<!-- 通知栏 -->
<view class="notice-wrapper" v-if="noticeText" @click="openNoticePopup">
<uv-notice-bar :text="noticeText" :speed="50" :show-icon="true" color="#07c160" bg-color="#E8F8EF"
icon="volume"></uv-notice-bar>
</view>
</view>
<!-- 内容区域 -->
<view class="main-content" :style="{ paddingTop: (statusBarHeight) + 'px' }">
<!-- 内容区域 -->
<view class="main-content" :style="{ paddingTop: (statusBarHeight + navBarHeight + noticeHeight) + 'px' }">
<!-- 全屏地图组件 -->
<MapComponent v-if="!isLoading && userLocation" ref="mapRef" :userLocation="userLocation"
:positionList="positionList" :filteredPositions="filteredPositions" :searchKeyword="searchKeyword"
@@ -25,7 +29,7 @@
<view v-if="isLoading || !userLocation" class="map-loading-placeholder">
<view class="loading-content">
<view class="loading-spinner"></view>
<text>正在获取位置信息...</text>
<text>{{ $t('common.loadingLocation') }}</text>
</view>
</view>
</view>
@@ -36,28 +40,28 @@
<view class="icon-wrap">
<image class="action-icon" src="/static/map.png" mode="aspectFit" />
</view>
<text class="action-label">附近设备</text>
<text class="action-label">{{ $t('home.nearbyDevices') }}</text>
</view> -->
<view class="action-btn secondary small btn-nearby" @click="openPopup">
<view class="icon-wrap">
<image src="/static/use_help.png" class="action-icon" mode="aspectFit"></image>
</view>
<text class="action-label">使用指南</text>
<text class="action-label">{{ $t('home.useGuide') }}</text>
</view>
<view class="action-btn primary btn-scan" @click="handleScan">
<view class="icon-wrap">
<image class="action-icon" src="/static/scan-icon.png" mode="aspectFill" />
</view>
<text class="primary-label">扫码使用</text>
<text class="primary-label">{{ $t('home.scanToUse') }}</text>
</view>
<view class="action-btn secondary small btn-my" @click="goMy">
<view class="icon-wrap">
<image class="action-icon" src="/static/user.png" mode="aspectFit" />
</view>
<text class="action-label">个人中心</text>
<text class="action-label">{{ $t('home.personalCenter') }}</text>
</view>
</view>
@@ -67,7 +71,7 @@
:expanded="isExpanded"
:positions="filteredPositions"
:isLoading="isLoading"
title="附近设备场地"
:title="$t('home.nearbyDeviceLocation')"
@close="hideLocationList"
@select="selectPositionFromPopup"
@navigate="navigateToPosition"
@@ -77,7 +81,7 @@
<view class="loading-overlay" v-if="isLoading">
<view class="loading-content">
<view class="loading-spinner"></view>
<text>正在获取场地信息...</text>
<text>{{ $t('common.loadingPosition') }}</text>
</view>
</view>
@@ -86,17 +90,17 @@
<view class="popup-mask" @click.stop="showPhoneAuthPopup = false"></view>
<view class="popup-content">
<view class="popup-header">
<text class="popup-title">授权获取手机号</text>
<text class="popup-title">{{ $t('auth.authTitle') }}</text>
</view>
<view class="popup-body">
<view class="auth-desc">
<text>为了提供更好的服务和紧急联系需要授权获取您的手机号</text>
<text>{{ $t('auth.authDesc') }}</text>
</view>
<button class="auth-btn" open-type="getPhoneNumber" @getphonenumber="onGetPhoneNumber">
<text>一键获取手机号</text>
<text>{{ $t('auth.getPhoneNumber') }}</text>
</button>
<view class="auth-cancel" @click="showPhoneAuthPopup = false">
<text>暂不授权</text>
<text>{{ $t('auth.notNow') }}</text>
</view>
</view>
</view>
@@ -106,7 +110,7 @@
<uv-popup ref="guidePopup" mode="center" round="24" :overlay="true" :closeOnClickOverlay="false" :safeAreaInsetBottom="false">
<view class="guide-popup">
<view class="guide-header">
<text class="guide-title">使用指南</text>
<text class="guide-title">{{ $t('guide.title') }}</text>
<!-- <view class="guide-close" @click="closeGuidePopup">
<uv-icon name="close" size="20"></uv-icon>
</view> -->
@@ -115,8 +119,8 @@
<view class="guide-step" v-for="(step, idx) in guideSteps" :key="idx">
<view class="step-index">{{ idx + 1 }}</view>
<view class="step-info">
<view class="step-title">{{ step.title }}</view>
<view class="step-desc">{{ step.desc }}</view>
<view class="step-title">{{ $t('guide.step' + (idx + 1) + 'Title') }}</view>
<view class="step-desc">{{ $t('guide.step' + (idx + 1) + 'Desc') }}</view>
</view>
</view>
</view>
@@ -133,7 +137,7 @@
<uv-popup ref="noticePopup" mode="center" round="24" :overlay="true" :closeOnClickOverlay="true" :safeAreaInsetBottom="false">
<view class="notice-popup">
<view class="notice-header">
<text class="notice-title">通知公告</text>
<text class="notice-title">{{ $t('home.noticeTitle') }}</text>
</view>
<view class="notice-content">
<text class="notice-text">{{ noticeText }}</text>
@@ -182,6 +186,8 @@
// 注意:从 pages/index/ 目录访问 components/ 需要使用 ../../components/ 路径
import MapComponent from '../../components/MapComponent.vue'
import LocationListSheet from '../../components/LocationListSheet.vue'
import { useI18n } from '../../utils/i18n.js'
// 开启右上角分享菜单(仅 mp-weixin 有效)
// #ifdef MP-WEIXIN
wx.showShareMenu({
@@ -190,6 +196,8 @@
})
// #endif
const { t: $t } = useI18n()
// 响应式数据
const searchKeyword = ref('')
const userLocation = ref(null)
@@ -205,6 +213,7 @@
// 导航栏高度相关
const statusBarHeight = ref(0)
const navBarHeight = ref(44) // 默认导航栏内容高度
const noticeHeight = ref(0) // 通知栏高度
// 使用指南步骤
const guideSteps = ref([
@@ -240,6 +249,11 @@
const res = await getNoticeTextData(parasm);
noticeText.value = res.data.noticeContent;
// 设置通知栏高度
if (res.data.noticeContent) {
noticeHeight.value = 50 // 通知栏高度约50px
}
// 将通知内容存储到本地缓存
try {
uni.setStorageSync('noticeContent', res.data.noticeContent);
@@ -400,7 +414,7 @@ const noticePopup = ref(null)
} catch (error) {
console.error('获取位置失败:', error)
uni.showToast({
title: '获取位置失败,显示默认地图',
title: $t('home.getLocationFailed'),
icon: 'none'
})
}
@@ -578,7 +592,7 @@ const noticePopup = ref(null)
uni.hideLoading()
uni.showToast({
title: '定位成功',
title: $t('home.locateSuccess'),
icon: 'success',
duration: 1500
})
@@ -587,7 +601,7 @@ const noticePopup = ref(null)
uni.hideLoading()
uni.showToast({
title: e.errMsg || '定位失败,请检查定位权限',
title: e.errMsg || $t('home.locateFailed'),
icon: 'none',
duration: 2000
})
@@ -691,7 +705,7 @@ const noticePopup = ref(null)
if (!deviceNo) {
uni.showToast({
title: '无效的设备二维码',
title: $t('home.invalidQRCode'),
icon: 'none'
})
return
@@ -830,16 +844,18 @@ const closeNoticePopup = () => {
export default {
// 分享给朋友
onShareAppMessage() {
const $t = this.$t || ((key) => key)
return {
title: '风电者 - 共享风扇暖手充电宝',
title: $t('share.title'),
path: '/pages/index/index',
// imageUrl: '/static/logo.png'
}
},
// 朋友圈
onShareTimeline() {
const $t = this.$t || ((key) => key)
return {
title: '风电者 - 共享风扇暖手充电宝',
title: $t('share.title'),
query: '',
// imageUrl: '/static/logo.png'
}
@@ -889,6 +905,8 @@ const closeNoticePopup = () => {
position: relative;
width: 100%;
height: 100%;
padding-bottom: 180rpx; /* 为底部按钮留出空间 */
box-sizing: border-box;
}
/* 顶部Logo和通知栏 */
@@ -1376,7 +1394,7 @@ const closeNoticePopup = () => {
top: 0;
left: 0;
right: 0;
bottom: 0;
bottom: 180rpx; /* 为底部按钮留出空间 */
background: #f6f7fb;
display: flex;
align-items: center;
@@ -1502,11 +1520,19 @@ const closeNoticePopup = () => {
line-height: 1;
}
.map-notice {
/* 顶部信息区域 */
.top-info-section {
position: fixed;
left: 0;
right: 0;
z-index: 998;
background: transparent;
}
.notice-wrapper {
margin: 0 20rpx;
margin-top: 10rpx;
padding: 10rpx 0;
border-radius: 20rpx;
z-index: 15;
}
/* 使用指南弹窗样式 */
+7 -1
View File
@@ -10,6 +10,9 @@
ref,
onMounted
} from 'vue'
import { useI18n } from '@/utils/i18n.js'
const { t: $t } = useI18n()
// 外部网页地址
const webUrl = ref('https://joininvestment.gxfs123.com/')
@@ -24,13 +27,16 @@
const handleError = (e) => {
console.error('web-view 加载错误:', e)
uni.showToast({
title: '页面加载失败',
title: $t('join.pageLoadFailed'),
icon: 'none',
duration: 2000
})
}
onMounted(() => {
uni.setNavigationBarTitle({
title: $t('join.title')
})
console.log('招商页面加载,外部网址:', webUrl.value)
})
</script>
+13 -4
View File
@@ -1,8 +1,8 @@
<template>
<view class="legal-page">
<view class="header">
<view class="title">用户协议</view>
<view class="subtitle">适用于风电者共享风扇租借服务最后更新{{ effectiveDate }}</view>
<view class="title">{{ $t('legal.agreement') }}</view>
<view class="subtitle">{{ $t('legal.applicableToService') }}{{ $t('legal.lastUpdate') }}{{ effectiveDate }}</view>
</view>
<scroll-view class="content" scroll-y>
@@ -71,12 +71,21 @@
<view class="p">15.2 协议条款如被认定无效或不可执行不影响其他条款的效力与执行</view>
</scroll-view>
<view class="footer">如对本协议有疑问请前往我的-客服咨询</view>
<view class="footer">{{ $t('legal.footerNotice') }}</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { ref, onMounted } from 'vue'
import { useI18n } from '@/utils/i18n.js'
const { t: $t } = useI18n()
onMounted(() => {
uni.setNavigationBarTitle({
title: $t('legal.agreement')
})
})
const brandName = '风电者'
const companyName = '深圳乐慕智云科技有限公司'
+14 -4
View File
@@ -1,8 +1,8 @@
<template>
<view class="legal-page">
<view class="header">
<view class="title">隐私政策</view>
<view class="subtitle">适用于风电者共享风扇租借服务最后更新{{ effectiveDate }}</view>
<view class="title">{{ $t('legal.privacy') }}</view>
<view class="subtitle">{{ $t('legal.applicableToService') }}{{ $t('legal.lastUpdate') }}{{ effectiveDate }}</view>
</view>
<view class="card notice">
@@ -56,14 +56,24 @@
<view class="p">10.1 您可通过我的-客服与我们联系以行使前述权利或就本政策提出疑问</view>
</scroll-view>
<view class="footer">如对本政策有疑问请前往我的-客服咨询</view>
<view class="footer">{{ $t('legal.footerNoticePolicy') }}</view>
</view>
</template>
<script setup>
import {
ref
ref,
onMounted
} from 'vue'
import { useI18n } from '@/utils/i18n.js'
const { t: $t } = useI18n()
onMounted(() => {
uni.setNavigationBarTitle({
title: $t('legal.privacy')
})
})
const brandName = '风电者'
const companyName = '深圳乐慕智云科技有限公司'
+41 -31
View File
@@ -2,18 +2,18 @@
<view class="login-container">
<view class="logo">
<image src="/static/logo.png" mode="aspectFit" />
<text class="app-name">风电者共享风扇&充电宝</text>
<text class="app-name">{{ $t('app.slogan') }}</text>
</view>
<view class="title">登录您的账号</view>
<view class="subtitle">为保障使用体验请先完成登录</view>
<view class="title">{{ $t('auth.loginTitle') }}</view>
<view class="subtitle">{{ $t('auth.loginDesc') }}</view>
<!-- 微信一键手机号快捷登录推荐 -->
<button v-if="!isAgreed" class="btn primary" @click="handleLoginClick">
手机号快捷登录
{{ $t('auth.getPhoneNumber') }}
</button>
<button v-else class="btn primary" open-type="getPhoneNumber" @getphonenumber="onGetPhoneNumber">
手机号快捷登录
{{ $t('auth.getPhoneNumber') }}
</button>
<!-- 仅微信登录不授权手机号时使用 -->
@@ -24,10 +24,10 @@
<label class="agreement-label">
<checkbox value="agreed" :checked="isAgreed" color="#07c160" class="agreement-checkbox" />
<text class="agreement-text">
我已阅读并同意
<text class="link" @tap.stop="go('/pages/legal/agreement')">用户协议</text>
<text class="link" @tap.stop="go('/pages/legal/privacy')">隐私政策</text>
{{ $t('auth.agreeToTerms') }}
<text class="link" @tap.stop="go('/pages/legal/agreement')">{{ $t('user.userAgreement') }}</text>
{{ $t('common.and') }}
<text class="link" @tap.stop="go('/pages/legal/privacy')">{{ $t('user.privacyPolicy') }}</text>
</text>
</label>
</checkbox-group>
@@ -36,9 +36,19 @@
</template>
<script setup>
import { ref } from 'vue'
import { ref, onMounted } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { wxLogin, getUserPhoneNumber, getUserInfo } from '../../util/index.js'
import { useI18n } from '@/utils/i18n.js'
const { t: $t } = useI18n()
// 设置页面标题
onMounted(() => {
uni.setNavigationBarTitle({
title: $t('auth.loginTitle')
})
})
const redirect = ref('/pages/index/index')
const isAgreed = ref(false) // 是否同意协议
@@ -67,23 +77,23 @@
return
}
// 未勾选,弹窗提示
uni.showModal({
title: '温馨提示',
content: '请先阅读并同意《用户协议》和《隐私政策》',
confirmText: '同意',
cancelText: '取消',
success: (res) => {
if (res.confirm) {
// 用户点击同意,自动勾选
isAgreed.value = true
resolve()
} else {
// 用户点击取消
reject(new Error('需要同意协议才能登录'))
}
// 未勾选,弹窗提示
uni.showModal({
title: $t('common.tips'),
content: $t('auth.pleaseAgreeToTerms'),
confirmText: $t('common.confirm'),
cancelText: $t('common.cancel'),
success: (res) => {
if (res.confirm) {
// 用户点击同意,自动勾选
isAgreed.value = true
resolve()
} else {
// 用户点击取消
reject(new Error('需要同意协议才能登录'))
}
})
}
})
})
}
@@ -108,8 +118,8 @@
// 先检查是否同意协议
await checkAgreement()
await wxLogin()
uni.showToast({ title: '登录成功', icon: 'success' })
await wxLogin()
uni.showToast({ title: $t('auth.loginSuccess'), icon: 'success' })
await navigateAfterLogin()
} catch (error) {
if (error.message !== '需要同意协议才能登录') {
@@ -120,7 +130,7 @@
const onGetPhoneNumber = async (e) => {
if (!e || e.detail.errMsg !== 'getPhoneNumber:ok') {
uni.showToast({ title: '已取消手机号授权', icon: 'none' })
uni.showToast({ title: $t('auth.phoneCancelled'), icon: 'none' })
return
}
@@ -129,10 +139,10 @@
await wxLogin()
// 再用微信返回的临时 code 换取手机号
await getUserPhoneNumber(e.detail.code)
uni.showToast({ title: '登录成功', icon: 'success' })
uni.showToast({ title: $t('auth.loginSuccess'), icon: 'success' })
await navigateAfterLogin()
} catch (error) {
uni.showToast({ title: error.message || '登录失败', icon: 'none' })
uni.showToast({ title: error.message || $t('auth.loginFailed'), icon: 'none' })
}
}
+28 -22
View File
@@ -6,8 +6,8 @@
<image v-else class="avatar" src="@/static/head.png" mode="aspectFill"></image>
</view>
<view class="user-text">
<view class="nickname">{{ userInfo.nickName || '点击登录' }}</view>
<view class="subtext">{{ userInfo.phone ? maskPhone(userInfo.phone) : '授权登录后可查看订单与资产' }}</view>
<view class="nickname">{{ userInfo.nickName || $t('user.clickToLogin') }}</view>
<view class="subtext">{{ userInfo.phone ? maskPhone(userInfo.phone) : $t('user.loginPrompt') }}</view>
</view>
<uv-icon type="right" size="16" color="#999"></uv-icon>
</view>
@@ -32,56 +32,56 @@
<view class="list-item" @click="handleQuickReturn">
<view class="left">
<image class="icon" src="/static/express_return.png" mode="aspectFit"></image>
<text class="title">快速归还<text style="font-size: 18rpx;">直接查看使用中的订单</text></text>
<text class="title">{{ $t('user.quickReturn') }}<text style="font-size: 18rpx;">{{ $t('user.quickReturnDesc') }}</text></text>
</view>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view>
<view class="list-item" @click="navigateTo('/pages/expressReturn/index')" v-if="showMenuItem">
<view class="left">
<image class="icon" src="/static/express.png" mode="aspectFit"></image>
<text class="title">快递归还记录</text>
<text class="title">{{ $t('user.expressReturn') }}</text>
</view>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view>
<view class="list-item" @click="navigateTo('/pages/order/index')">
<view class="left">
<image class="icon" src="/static/orderList.png" mode="aspectFit"></image>
<text class="title">我的订单</text>
<text class="title">{{ $t('user.myOrders') }}</text>
</view>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view>
<view class="list-item" @click="navigateTo('/pages/help/index')">
<view class="left">
<image class="icon" src="/static/customer-service.png" mode="aspectFit"></image>
<text class="title">客服中心</text>
<text class="title">{{ $t('user.customerService') }}</text>
</view>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view>
<view class="list-item" @click="navigateTo('/pages/feedback/index')">
<view class="left">
<image class="icon" src="/static/suggess.png" mode="aspectFit"></image>
<text class="title">投诉与建议</text>
<text class="title">{{ $t('user.feedback') }}</text>
</view>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view>
<!-- <view class="list-item" @click="navigateTo('/pages/legal/agreement')">
<view class="left">
<image class="icon" src="/static/business-licence.png" mode="aspectFit"></image>
<text class="title">营业资质</text>
<text class="title">{{ $t('user.businessLicense') }}</text>
</view>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view> -->
<view class="list-item" @click="navigateTo('/pages/join/index')">
<view class="left">
<image class="icon" src="/static/peopleInWork.png" mode="aspectFit"></image>
<text class="title">合作加盟</text>
<text class="title">{{ $t('user.cooperation') }}</text>
</view>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view>
<view class="list-item" @click="navigateTo('/pages/setting/index')">
<view class="left">
<image class="icon" src="/static/setting.png" mode="aspectFit"></image>
<text class="title">设置</text>
<text class="title">{{ $t('user.settings') }}</text>
</view>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view>
@@ -90,11 +90,11 @@
<view class="footer-agreements">
<view class="link-box">
<text class="link" @click="navigateTo('/pages/legal/agreement')">用户协议</text>
<text class="link" @click="navigateTo('/pages/legal/agreement')">{{ $t('user.userAgreement') }}</text>
<text class="sep"></text>
<text class="link" @click="navigateTo('/pages/legal/privacy')">隐私政策</text>
<text class="link" @click="navigateTo('/pages/legal/privacy')">{{ $t('user.privacyPolicy') }}</text>
</view>
<view class="version">v{{ appVersion }}</view>
<view class="version">{{ $t('user.version') }}{{ appVersion }}</view>
</view>
<!-- 保留授权弹窗暂不启用 -->
@@ -133,8 +133,11 @@ import {
import {
URL
} from '../../config/url.js'
import { useI18n } from '@/utils/i18n.js'
// 设置页执行退出登录,此页不再直接调用
const { t: $t } = useI18n()
// 响应式状态
const userInfo = ref({});
const deposit = ref('0.00');
@@ -147,6 +150,9 @@ import {
// 页面加载时初始化
onMounted(() => {
uni.setNavigationBarTitle({
title: $t('user.personalCenter')
})
getInfo();
initVersion();
});
@@ -186,7 +192,7 @@ import {
} catch (error) {
console.error('获取用户信息失败:', error);
uni.showToast({
title: '获取用户信息失败',
title: $t('user.getUserInfoFailed'),
icon: 'none'
});
}
@@ -269,7 +275,7 @@ import {
});
} else {
uni.showToast({
title: '暂无使用中的订单',
title: $t('order.noOrder'),
icon: 'none'
});
}
@@ -277,7 +283,7 @@ import {
uni.hideLoading();
console.error('获取使用中订单失败:', error);
uni.showToast({
title: '获取订单失败',
title: $t('order.getOrderFailed'),
icon: 'none'
});
}
@@ -300,7 +306,7 @@ import {
// #endif
// #ifndef MP-WEIXIN
uni.showToast({
title: '请在微信小程序中使用此功能',
title: $t('auth.pleaseUseInWechat'),
icon: 'none'
})
// #endif
@@ -404,7 +410,7 @@ import {
// #ifndef MP-WEIXIN
uni.showToast({
title: '请在微信小程序中使用此功能',
title: $t('auth.pleaseUseInWechat'),
icon: 'none'
});
closeAuthPopup();
@@ -433,7 +439,7 @@ import {
// });
uni.showToast({
title: '信息更新成功',
title: $t('user.updateSuccess'),
icon: 'success'
});
@@ -442,7 +448,7 @@ import {
} catch (error) {
console.error('更新用户信息失败:', error);
uni.showToast({
title: '更新用户信息失败',
title: $t('user.updateFailed'),
icon: 'none'
});
}
@@ -511,7 +517,7 @@ import {
// 关于我们
const handleAboutUs = () => {
uni.showToast({
title: '功能开发中',
title: $t('help.functionDeveloping'),
icon: 'none'
});
};
@@ -519,7 +525,7 @@ import {
// 隐私政策
const handlePrivacyPolicy = () => {
uni.showToast({
title: '功能开发中',
title: $t('help.functionDeveloping'),
icon: 'none'
});
};
+58 -53
View File
@@ -19,9 +19,9 @@
<view class="info-col">
<view class="info-value-wrapper">
<text class="info-value-large">{{ getOrderFee() }}</text>
<text class="info-value-unit"></text>
<text class="info-value-unit">{{ $t('unit.yuan') }}</text>
</view>
<view class="info-label">订单金额</view>
<view class="info-label">{{ $t('order.totalAmount') }}</view>
</view>
</view>
<view class="fee-rule">
@@ -31,39 +31,39 @@
<!-- 租借信息卡片 -->
<view class="rent-card">
<view class="rent-title">租借信息</view>
<view class="rent-title">{{ $t('order.rentInfo') }}</view>
<view class="rent-item">
<view class="rent-label">订单编号</view>
<view class="rent-label">{{ $t('order.orderNo') }}</view>
<view class="rent-value">{{ orderInfo.orderNo || '-' }}</view>
</view>
<view class="rent-item">
<view class="rent-label">风扇编号</view>
<view class="rent-label">{{ $t('order.fanNo') }}</view>
<view class="rent-value">{{ deviceId || '-' }}</view>
</view>
<view class="rent-item">
<view class="rent-label">租借时间</view>
<view class="rent-label">{{ $t('order.rentTime') }}</view>
<view class="rent-value">{{ orderInfo.startTime || '-' }}</view>
</view>
<view class="rent-item">
<view class="rent-label">租借地点</view>
<view class="rent-value">{{ orderInfo.positionName || '新佳宜(九天银河店)' }}</view>
<view class="rent-label">{{ $t('order.rentLocation') }}</view>
<view class="rent-value">{{ orderInfo.positionName || '-' }}</view>
</view>
<view class="rent-item">
<view class="rent-label">租借方式</view>
<view class="rent-label">{{ $t('order.rentMethod') }}</view>
<view class="rent-value">{{ getPayWayText() }}</view>
</view>
<view class="rent-item" v-if="isOrderCompleted() && orderInfo.endTime">
<view class="rent-label">归还时间</view>
<view class="rent-label">{{ $t('order.returnTime') }}</view>
<view class="rent-value">{{ orderInfo.endTime }}</view>
</view>
<view class="rent-item" v-if="isOrderCompleted() && orderInfo.returnPosition">
<view class="rent-label">归还地点</view>
<view class="rent-value">{{ orderInfo.returnPosition || '新佳宜(九天银河店)' }}</view>
<view class="rent-label">{{ $t('order.returnLocation') }}</view>
<view class="rent-value">{{ orderInfo.returnPosition || '-' }}</view>
</view>
<view class="rent-paid" v-if="isOrderCompleted()">
<text class="paid-label">已支付</text>
<text class="paid-label">{{ $t('order.paid') }}</text>
<text class="paid-value">{{ orderInfo.currentFee || orderInfo.payAmount || '10' }}</text>
<text class="paid-unit"></text>
<text class="paid-unit">{{ $t('unit.yuan') }}</text>
</view>
</view>
@@ -73,16 +73,16 @@
<template v-if="orderInfo.orderStatus === 'in_used'">
<view class="bottom-icon-btn" @click="contactService">
<image src="/static/customer-service.png" class="icon" mode="aspectFit"></image>
<text>客服中心</text>
<text>{{ $t('user.customerService') }}</text>
</view>
<view v-if="!showExpressAction" class="countdown-btn">
{{ formatCountdown(countdownRemaining) }}后可快递归还
{{ formatCountdown(countdownRemaining) }}{{ $t('order.canExpressReturn') }}
</view>
<view v-if="showExpressAction" class="action-btn secondary" @click="expressRetrunOrder">
暂停计费
{{ $t('order.pauseBilling') }}
</view>
<view v-if="showExpressAction" class="action-btn primary" @click="quickReturn">
快速归还
{{ $t('order.quickReturn') }}
</view>
</template>
@@ -90,26 +90,26 @@
<template v-if="isOrderCompleted()">
<view class="bottom-icon-btn" @click="handleWithdraw" v-if="!orderInfo.isWithdrawn && orderInfo.refundAmount > 0">
<image src="/static/suggess.png" class="icon" mode="aspectFit"></image>
<text>费用申诉</text>
<text>{{ $t('order.feeAppeal') }}</text>
</view>
<view class="bottom-icon-btn" @click="contactService">
<image src="/static/customer-service.png" class="icon" mode="aspectFit"></image>
<text>客服中心</text>
<text>{{ $t('user.customerService') }}</text>
</view>
<view class="action-btn primary" @click="rentAgain">
再次租借
{{ $t('order.rentAgain') }}
</view>
</template>
<!-- 待支付状态 -->
<template v-if="orderInfo.orderStatus === 'waiting_for_payment'">
<view class="action-btn secondary" @click="handleCancelOrder">取消订单</view>
<view class="action-btn primary" @click="handlePayment">立即支付</view>
<view class="action-btn secondary" @click="handleCancelOrder">{{ $t('order.cancelOrder') }}</view>
<view class="action-btn primary" @click="handlePayment">{{ $t('order.payNow') }}</view>
</template>
<!-- 已取消状态 -->
<template v-if="orderInfo.orderStatus === 'order_cancelled'">
<view class="action-btn primary full-width" @click="goToHome">返回首页</view>
<view class="action-btn primary full-width" @click="goToHome">{{ $t('order.backToHome') }}</view>
</template>
</view>
</view>
@@ -168,6 +168,11 @@
onLoad(options) {
console.log('订单详情页加载,参数:', JSON.stringify(options))
// 设置页面标题
uni.setNavigationBarTitle({
title: this.$t('order.orderDetail')
})
this.isPageActive = true
// 从缓存读取通知内容(计费规则)
@@ -241,27 +246,27 @@
// 获取订单状态文字
getOrderStatusText() {
const statusMap = {
'waiting_for_payment': '待支付',
'payment_in_progress': '支付中',
'payment_successful': '支付成功',
'in_used': '使用中',
'payment_failed': '支付失败',
'order_cancelled': '已取消',
'used_done': '已完成',
'used_down': '已完成'
'waiting_for_payment': this.$t('order.waitingForPayment'),
'payment_in_progress': this.$t('order.paymentInProgress'),
'payment_successful': this.$t('order.paymentSuccess'),
'in_used': this.$t('order.inUse'),
'payment_failed': this.$t('order.paymentFailed'),
'order_cancelled': this.$t('order.cancelled'),
'used_done': this.$t('order.finished'),
'used_down': this.$t('order.finished')
}
return statusMap[this.orderInfo.orderStatus] || '订单详情'
return statusMap[this.orderInfo.orderStatus] || this.$t('order.orderDetail')
},
// 获取状态描述
getStatusDesc() {
const descMap = {
'waiting_for_payment': '请尽快完成支付',
'in_used': '请妥善保管设备,使用完毕后及时归还',
'used_done': '您的风扇已归还,感谢使用',
'used_down': '您的风扇已归还,感谢使用',
'order_cancelled': '订单已取消',
'payment_failed': '支付失败,请重新支付'
'waiting_for_payment': this.$t('order.pleasePaySoon'),
'in_used': this.$t('order.pleaseReturnInTime'),
'used_done': this.$t('order.returnedThankYou'),
'used_down': this.$t('order.returnedThankYou'),
'order_cancelled': this.$t('order.orderCancelled'),
'payment_failed': this.$t('order.paymentFailedRetry')
}
return descMap[this.orderInfo.orderStatus] || ''
},
@@ -293,11 +298,11 @@
// 获取支付方式文本
getPayWayText() {
const payWayMap = {
'wx_score_pay': '免押租借',
'wx_member_pay': '会员订单',
'wx_pay': '押金租借'
'wx_score_pay': this.$t('order.depositFree'),
'wx_member_pay': this.$t('order.memberOrder'),
'wx_pay': this.$t('order.depositPay')
}
return payWayMap[this.orderInfo.payWay] || '免押租借'
return payWayMap[this.orderInfo.payWay] || this.$t('order.depositFree')
},
// 格式化倒计时(显示为 HH:MM:SS 格式)
@@ -393,7 +398,7 @@
// 获取使用时长标签文本
getUsedTimeLabel() {
// 使用中状态显示"已使用",已完成状态显示"使用时长"
return this.orderInfo.orderStatus === 'in_used' ? '已使用' : '使用时长'
return this.orderInfo.orderStatus === 'in_used' ? this.$t('order.used') : this.$t('order.duration')
},
// 获取订单费用(不含单位)
@@ -549,7 +554,7 @@
try {
if (!this.orderInfo.orderId) {
throw new Error('订单ID不能为空')
throw new Error(this.$t('order.orderIdRequired'))
}
const result = await queryById(this.orderInfo.orderId)
@@ -762,13 +767,13 @@
// 取消订单
handleCancelOrder() {
uni.showModal({
title: '确认取消',
content: '确定要取消此订单吗?',
title: this.$t('order.confirmCancel'),
content: this.$t('order.confirmCancelContent'),
success: async (res) => {
if (res.confirm) {
try {
uni.showLoading({
title: '处理中'
title: this.$t('common.processing')
})
const result = await cancelOrder({
orderId: this.orderInfo.orderId
@@ -776,17 +781,17 @@
if (result.code === 200) {
uni.hideLoading()
uni.showToast({
title: '订单已取消',
title: this.$t('order.cancelSuccess'),
icon: 'success'
})
await this.getOrderDetails()
} else {
throw new Error(result.msg || '取消订单失败')
throw new Error(result.msg || this.$t('order.cancelFailed'))
}
} catch (error) {
uni.hideLoading()
uni.showToast({
title: error.message || '取消订单失败',
title: error.message || this.$t('order.cancelFailed'),
icon: 'none'
})
}
@@ -821,7 +826,7 @@
if (res.statusCode === 200 && res.data.code === 200) {
uni.showToast({
title: '退款申请成功',
title: this.$t('order.refundSuccess'),
icon: 'success'
})
@@ -837,7 +842,7 @@
} catch (error) {
console.error('退款申请错误:', error)
uni.showToast({
title: error.message || '退款申请失败',
title: error.message || this.$t('order.refundFailed'),
icon: 'none'
})
} finally {
+42 -32
View File
@@ -10,12 +10,12 @@
<!-- 订单列表 -->
<view class="order-list">
<view class="empty-state" v-if="orderList.length === 0">
<view class="empty-icon">
<image src="/static/orderList.png" mode="aspectFill" class="empty-icon"></image>
</view>
<text class="empty-text">暂无订单记录</text>
<view class="empty-state" v-if="orderList.length === 0">
<view class="empty-icon">
<image src="/static/orderList.png" mode="aspectFill" class="empty-icon"></image>
</view>
<text class="empty-text">{{ $t('order.noOrderRecord') }}</text>
</view>
<OrderItemCard
v-for="(order, index) in orderList"
@@ -56,6 +56,16 @@
import {
URL
} from '../../config/url.js';
import { useI18n } from '@/utils/i18n.js'
const { t: $t } = useI18n()
// 设置页面标题
onMounted(() => {
uni.setNavigationBarTitle({
title: $t('order.myOrders')
})
})
// 初始化状态
const currentTab = ref(0);
@@ -64,62 +74,62 @@
// 订单状态映射
const orderStatusMap = reactive({
'0': {
text: '待支付',
get text() { return $t('order.waitingForPayment') },
class: 'status-waiting'
},
'1': {
text: '使用中',
get text() { return $t('order.inUse') },
class: 'status-using'
},
'2': {
text: '已完成',
get text() { return $t('order.finished') },
class: 'status-finished'
},
'3': {
text: '已取消',
get text() { return $t('order.cancelled') },
class: 'status-cancelled'
},
'waiting_for_payment': {
text: '待支付',
get text() { return $t('order.waitingForPayment') },
class: 'status-waiting'
},
'in_used': {
text: '使用中',
get text() { return $t('order.inUse') },
class: 'status-using'
},
'used_done': {
text: '已完成',
get text() { return $t('order.finished') },
class: 'status-finished'
},
'order_cancelled': {
text: '已取消',
get text() { return $t('order.cancelled') },
class: 'status-cancelled'
},
'express_return': {
text: '快递归还',
get text() { return $t('express.title') },
class: 'status-express-return'
}
});
// 订单状态标签
const orderStatusTabs = reactive([{
text: '全部',
get text() { return $t('common.all') },
status: []
},
{
text: '待付款',
get text() { return $t('order.waitingForPayment') },
status: ['waiting_for_payment']
},
{
text: '使用中',
get text() { return $t('order.inUse') },
status: ['in_used']
},
{
text: '已完成',
get text() { return $t('order.finished') },
status: ['used_done']
},
{
text: '已取消',
get text() { return $t('order.cancelled') },
status: ['order_cancelled']
}
]);
@@ -212,7 +222,7 @@
} catch (error) {
console.error('获取订单列表失败:', error);
uni.showToast({
title: '获取订单列表失败',
title: $t('order.getOrderListFailed'),
icon: 'none'
});
}
@@ -224,14 +234,14 @@
const res = await getOrderByOrderNoScorePayStatus(order.orderNo);
if (res.code === 200) {
uni.showToast({
title: '状态同步成功',
title: $t('order.syncSuccess'),
icon: 'success'
});
await loadOrderList(orderStatusTabs[currentTab.value].status);
}
} catch (error) {
uni.showToast({
title: '同步状态失败',
title: $t('order.syncFailed'),
icon: 'none'
});
}
@@ -258,7 +268,7 @@
const handlePayment = async (order) => {
try {
uni.showLoading({
title: '处理中'
title: $t('common.processing')
});
// 调用后端创建微信支付订单接口
@@ -279,7 +289,7 @@
...payParams,
success: async () => {
uni.showToast({
title: '支付成功',
title: $t('payment.paymentSuccess'),
icon: 'success'
});
@@ -295,7 +305,7 @@
},
fail: (err) => {
console.error('支付失败:', err);
throw new Error('支付失败,请重试');
throw new Error($t('payment.paymentFailedRetry'));
}
});
} else {
@@ -306,7 +316,7 @@
} catch (error) {
uni.hideLoading();
uni.showToast({
title: error.message || '支付失败',
title: error.message || $t('payment.paymentFailed'),
icon: 'none'
});
}
@@ -316,12 +326,12 @@
const handleCancelOrder = async (order) => {
try {
uni.showModal({
title: '确认取消',
content: '确定要取消此订单吗?',
title: $t('order.confirmCancel'),
content: $t('order.confirmCancelContent'),
success: async (res) => {
if (res.confirm) {
uni.showLoading({
title: '处理中'
title: $t('common.processing')
});
const result = await cancelOrder({
@@ -331,14 +341,14 @@
if (result) {
uni.hideLoading();
uni.showToast({
title: '订单已取消',
title: $t('order.cancelSuccess'),
icon: 'success'
});
// 刷新订单列表
await loadOrderList();
} else {
throw new Error(result.msg || '取消订单失败');
throw new Error(result.msg || $t('order.cancelFailed'));
}
}
}
@@ -346,7 +356,7 @@
} catch (error) {
uni.hideLoading();
uni.showToast({
title: error.message || '取消订单失败',
title: error.message || $t('order.cancelFailed'),
icon: 'none'
});
}
+29 -24
View File
@@ -9,38 +9,38 @@
<!-- 订单信息 -->
<view class="order-card">
<view class="card-title">订单信息</view>
<view class="card-title">{{ $t('payment.orderInfo') }}</view>
<view class="info-item">
<text class="label">订单号</text>
<text class="label">{{ $t('order.orderNo') }}</text>
<text class="value">{{ orderInfo.orderNo || '-' }}</text>
</view>
<view class="info-item">
<text class="label">设备号</text>
<text class="label">{{ $t('order.deviceNo') }}</text>
<text class="value">{{ orderInfo.deviceNo || '-' }}</text>
</view>
<view class="info-item">
<text class="label">创建时间</text>
<text class="label">{{ $t('payment.createTime') }}</text>
<text class="value">{{ orderInfo.createTime || '-' }}</text>
</view>
<view class="info-item">
<text class="label">联系电话</text>
<text class="label">{{ $t('payment.contactPhone') }}</text>
<text class="value">{{ orderInfo.phone || '-' }}</text>
</view>
</view>
<!-- 费用信息 -->
<view class="price-card">
<view class="card-title">费用信息</view>
<view class="card-title">{{ $t('payment.feeInfo') }}</view>
<view class="price-item">
<text class="label">押金</text>
<text class="label">{{ $t('payment.deposit') }}</text>
<text class="value">{{ orderInfo.deposit || '99.00' }}</text>
</view>
<view class="price-item">
<text class="label">套餐</text>
<text class="value">{{ packageInfo.price }}/{{ packageInfo.time }}小时</text>
<text class="label">{{ $t('payment.package') }}</text>
<text class="value">{{ packageInfo.price }}{{ $t('unit.yuan') }}/{{ packageInfo.time }}{{ $t('time.hour') }}</text>
</view>
<view class="price-item total">
<text class="label">合计</text>
<text class="label">{{ $t('payment.total') }}</text>
<text class="value">{{ totalAmount }}</text>
</view>
</view>
@@ -51,10 +51,10 @@
<!-- 底部操作栏 -->
<view class="bottom-bar">
<view class="total-amount">
<text>合计</text>
<text>{{ $t('payment.total') }}</text>
<text class="amount">{{ totalAmount }}</text>
</view>
<view class="pay-btn" @click="handlePayment">立即支付</view>
<view class="pay-btn" @click="handlePayment">{{ $t('payment.payNow') }}</view>
</view>
</view>
</template>
@@ -81,8 +81,8 @@ export default {
passedTotalAmount: null,
passedDepositAmount: null,
orderStatus: {
text: '等待支付',
desc: '请在15分钟内完成支付',
get text() { return this.$t('payment.waitingForPayment') },
get desc() { return this.$t('payment.pleasePayIn15Min') },
class: 'waiting'
}
}
@@ -117,6 +117,11 @@ export default {
}
},
onLoad(options) {
// 设置页面标题
uni.setNavigationBarTitle({
title: this.$t('payment.orderPayment')
})
if (options && options.orderId) {
this.orderId = options.orderId
@@ -141,9 +146,9 @@ export default {
}
this.loadOrderInfo()
} else {
} else {
uni.showToast({
title: '订单信息不存在',
title: this.$t('order.orderNotExist'),
icon: 'none'
})
setTimeout(() => {
@@ -158,7 +163,7 @@ export default {
async loadOrderInfo() {
try {
uni.showLoading({
title: '加载中'
title: this.$t('common.loading')
})
const res = await queryById(this.orderId)
@@ -238,7 +243,7 @@ export default {
async handlePayment() {
try {
uni.showLoading({
title: '处理中'
title: this.$t('common.processing')
})
// 调用后端创建微信支付订单接口
@@ -259,7 +264,7 @@ export default {
...payParams,
success: async () => {
uni.showToast({
title: '支付成功',
title: this.$t('payment.paymentSuccess'),
icon: 'success'
});
@@ -279,7 +284,7 @@ export default {
},
fail: (err) => {
console.error('支付失败:', err)
throw new Error('支付失败,请重试')
throw new Error(this.$t('payment.paymentFailedRetry'))
}
})
} else {
@@ -298,7 +303,7 @@ export default {
async sendRentCommand() {
try {
uni.showLoading({
title: '处理中'
title: this.$t('common.processing')
})
// 调用发送租借指令的接口
@@ -307,7 +312,7 @@ export default {
if (res.code === 200) {
uni.hideLoading()
uni.showToast({
title: '租借成功',
title: this.$t('device.rentSuccess'),
icon: 'success'
})
@@ -318,12 +323,12 @@ export default {
})
}, 1500)
} else {
throw new Error(res.msg || '租借失败')
throw new Error(res.msg || this.$t('device.rentFailed'))
}
} catch (error) {
uni.hideLoading()
uni.showToast({
title: error.message || '租借失败',
title: error.message || this.$t('device.rentFailed'),
icon: 'none'
})
}
+41 -36
View File
@@ -3,82 +3,82 @@
<!-- 支付成功状态 -->
<view class="status-card">
<view class="status-icon success"></view>
<view class="status-text">归还成功</view>
<view class="status-desc">您的风扇已归还费用已从押金中扣除</view>
<view class="status-text">{{ $t('success.returnSuccess') }}</view>
<view class="status-desc">{{ $t('success.returnSuccessDesc') }}</view>
</view>
<!-- 订单信息 -->
<view class="order-card">
<view class="card-title">订单信息</view>
<view class="card-title">{{ $t('success.orderInfo') }}</view>
<view class="info-item">
<text class="label">订单号</text>
<text class="label">{{ $t('order.orderNo') }}</text>
<text class="value">{{ orderInfo.orderNo || '-' }}</text>
</view>
<view class="info-item">
<text class="label">设备号</text>
<text class="label">{{ $t('order.deviceNo') }}</text>
<text class="value">{{ orderInfo.deviceNo || '-' }}</text>
</view>
<view class="info-item">
<text class="label">使用时长</text>
<text class="label">{{ $t('success.usedTime') }}</text>
<text class="value">{{ orderInfo.usedTime || '-' }}</text>
</view>
<view class="info-item">
<text class="label">套餐时长</text>
<text class="value">{{ orderInfo.packageTime || '1小时' }}</text>
<text class="label">{{ $t('success.packageTime') }}</text>
<text class="value">{{ orderInfo.packageTime || '1' + $t('time.hour') }}</text>
</view>
<view class="info-item">
<text class="label">超出时长</text>
<text class="value">{{ orderInfo.extraTime || '0分钟' }}</text>
<text class="label">{{ $t('success.extraTime') }}</text>
<text class="value">{{ orderInfo.extraTime || '0' + $t('time.minute') }}</text>
</view>
<view class="info-item">
<text class="label">归还时间</text>
<text class="label">{{ $t('success.returnTime') }}</text>
<text class="value">{{ orderInfo.endTime || '-' }}</text>
</view>
</view>
<!-- 费用信息 -->
<view class="refund-card">
<view class="card-title">费用信息</view>
<view class="card-title">{{ $t('payment.feeInfo') }}</view>
<view class="info-item">
<text class="label">套餐费用</text>
<text class="label">{{ $t('success.packageFee') }}</text>
<text class="value">{{ orderInfo.packagePrice || '0.00' }}</text>
</view>
<view class="info-item">
<text class="label">超时费用</text>
<text class="label">{{ $t('success.extraFee') }}</text>
<text class="value">{{ orderInfo.extraFee || '0.00' }}</text>
</view>
<view class="info-item">
<text class="label">总费用</text>
<text class="label">{{ $t('success.totalFee') }}</text>
<text class="value">{{ orderInfo.currentFee || '0.00' }}</text>
</view>
<view class="info-item">
<text class="label">押金</text>
<text class="label">{{ $t('success.depositAmount') }}</text>
<text class="value">{{ orderInfo.deposit || '99.00' }}</text>
</view>
<view class="info-item">
<text class="label">退还金额</text>
<text class="label">{{ $t('success.refundAmount') }}</text>
<text class="value highlight">{{ orderInfo.refundAmount || '99.00' }}</text>
</view>
<view class="info-item">
<text class="label">退还状态</text>
<text class="label">{{ $t('success.refundStatus') }}</text>
<text class="value" :class="orderInfo.withdrawStatus || 'waiting'">{{ getWithdrawStatusText() }}</text>
</view>
</view>
<!-- 退款说明卡片 -->
<view class="notice-card">
<view class="card-title">退款说明</view>
<view class="card-title">{{ $t('success.refundNotice') }}</view>
<view class="notice-content">
<view>1. 押金剩余金额需要您手动申请提现</view>
<view>2. 提现申请提交后将在1-3个工作日内退还到原支付账户</view>
<view>3. 如有疑问请联系客服</view>
<view>1. {{ $t('success.refundNotice1') }}</view>
<view>2. {{ $t('success.refundNotice2') }}</view>
<view>3. {{ $t('success.refundNotice3') }}</view>
</view>
</view>
<!-- 操作按钮 -->
<view class="button-group">
<button class="primary-btn" @click="handleWithdraw" v-if="!orderInfo.isWithdrawn && orderInfo.refundAmount > 0">申请退款</button>
<button class="primary-btn" @click="goToHome">返回首页</button>
<button class="primary-btn" @click="handleWithdraw" v-if="!orderInfo.isWithdrawn && orderInfo.refundAmount > 0">{{ $t('success.applyRefund') }}</button>
<button class="primary-btn" @click="goToHome">{{ $t('success.backToHome') }}</button>
</view>
</view>
</template>
@@ -107,12 +107,17 @@ export default {
}
},
onLoad(options) {
// 设置页面标题
uni.setNavigationBarTitle({
title: this.$t('success.returnSuccess')
})
if (options && options.orderId) {
this.orderId = options.orderId;
this.loadOrderInfo();
} else {
uni.showToast({
title: '订单ID不能为空',
title: this.$t('order.orderIdRequired'),
icon: 'none'
});
setTimeout(() => {
@@ -124,18 +129,18 @@ export default {
// 获取退款状态文本
getWithdrawStatusText() {
const statusMap = {
'waiting': '待申请',
'processing': '处理中',
'success': '已退款',
'failed': '退款失败'
'waiting': this.$t('success.refundWaiting'),
'processing': this.$t('success.refundProcessing'),
'success': this.$t('success.refundSuccess'),
'failed': this.$t('success.refundFailed')
};
return statusMap[this.orderInfo.withdrawStatus] || '待申请';
return statusMap[this.orderInfo.withdrawStatus] || this.$t('success.refundWaiting');
},
// 加载订单信息
async loadOrderInfo() {
try {
uni.showLoading({ title: '加载中' });
uni.showLoading({ title: this.$t('common.loading') });
const result = await queryById(this.orderId);
if (result.code === 200 && result.data) {
@@ -216,7 +221,7 @@ export default {
} catch (error) {
console.error('加载订单信息错误:', error);
uni.showToast({
title: error.message || '获取订单信息失败',
title: error.message || this.$t('order.getOrderFailed'),
icon: 'none'
});
} finally {
@@ -227,7 +232,7 @@ export default {
// 申请退款
async handleWithdraw() {
try {
uni.showLoading({ title: '处理中' });
uni.showLoading({ title: this.$t('common.processing') });
const res = await uni.request({
url: `${URL || 'http://127.0.0.1:8080'}/app/withdraw/add/${this.orderInfo.orderNo}`,
@@ -241,7 +246,7 @@ export default {
if (res.statusCode === 200 && res.data.code === 200) {
uni.showToast({
title: '退款申请成功',
title: this.$t('order.refundSuccess'),
icon: 'success'
});
@@ -254,12 +259,12 @@ export default {
this.loadOrderInfo();
}, 1500);
} else {
throw new Error(res.data.msg || '退款申请失败');
throw new Error(res.data.msg || this.$t('order.refundFailed'));
}
} catch (error) {
console.error('退款申请错误:', error);
uni.showToast({
title: error.message || '退款申请失败',
title: error.message || this.$t('order.refundFailed'),
icon: 'none'
});
} finally {
+26 -19
View File
@@ -3,27 +3,27 @@
<!-- 支付成功状态 -->
<view class="status-card">
<view class="status-icon success"></view>
<view class="status-text">支付成功</view>
<view class="status-desc">您的订单已支付成功</view>
<view class="status-text">{{ $t('success.paymentSuccess') }}</view>
<view class="status-desc">{{ $t('success.paymentSuccessDesc') }}</view>
</view>
<!-- 订单信息 -->
<view class="order-card">
<view class="card-title">订单信息</view>
<view class="card-title">{{ $t('success.orderInfo') }}</view>
<view class="info-item">
<text class="label">订单号</text>
<text class="label">{{ $t('order.orderNo') }}</text>
<text class="value">{{ orderInfo.orderNo || '-' }}</text>
</view>
<view class="info-item">
<text class="label">设备号</text>
<text class="label">{{ $t('order.deviceNo') }}</text>
<text class="value">{{ orderInfo.deviceNo || '-' }}</text>
</view>
<view class="info-item">
<text class="label">支付金额</text>
<text class="label">{{ $t('success.paymentAmount') }}</text>
<text class="value">{{ orderInfo.amount || '0.00' }}</text>
</view>
<view class="info-item">
<text class="label">支付时间</text>
<text class="label">{{ $t('success.paymentTime') }}</text>
<text class="value">{{ orderInfo.payTime || '-' }}</text>
</view>
</view>
@@ -38,8 +38,8 @@
<!-- 操作按钮 -->
<view class="button-group">
<button class="primary-btn" @click="goToHome">返回首页</button>
<button class="secondary-btn" @click="goToOrderList">查看订单</button>
<button class="primary-btn" @click="goToHome">{{ $t('success.backToHome') }}</button>
<button class="secondary-btn" @click="goToOrderList">{{ $t('success.viewOrder') }}</button>
</view>
</view>
</template>
@@ -54,11 +54,18 @@ export default {
orderId: '',
orderInfo: {},
isLoading: true,
deviceMessage: '正在准备您的设备,请稍候...',
deviceMessage: '',
hasTriggeredDevice: false
}
},
onLoad(options) {
// 设置页面标题
uni.setNavigationBarTitle({
title: this.$t('success.paymentSuccess')
})
this.deviceMessage = this.$t('success.preparingDevice')
if (options && options.orderId) {
this.orderId = options.orderId
this.loadOrderInfo()
@@ -70,7 +77,7 @@ export default {
})
} else {
uni.showToast({
title: '订单信息不存在',
title: this.$t('order.orderNotExist'),
icon: 'none'
})
setTimeout(() => {
@@ -82,7 +89,7 @@ export default {
async loadOrderInfo() {
try {
uni.showLoading({
title: '加载中'
title: this.$t('common.loading')
})
const res = await queryById(this.orderId)
@@ -118,7 +125,7 @@ export default {
} catch (error) {
uni.hideLoading()
uni.showToast({
title: error.message || '获取订单信息失败',
title: error.message || this.$t('order.getOrderFailed'),
icon: 'none'
})
}
@@ -134,7 +141,7 @@ export default {
this.hasTriggeredDevice = true
uni.$emit('orderSuccess:' + this.orderId)
this.isLoading = true
this.deviceMessage = '正在准备您的设备,请稍候...'
this.deviceMessage = this.$t('success.preparingDevice')
try {
console.log(`准备触发弹出风扇,orderId: ${this.orderId}`)
@@ -144,19 +151,19 @@ export default {
console.log('确认支付并弹出风扇结果:', JSON.stringify(result))
if (result && result.code === 200) {
this.deviceMessage = '设备已弹出,请取走您的风扇'
this.deviceMessage = this.$t('success.deviceReady')
uni.showToast({
title: '风扇已弹出',
title: this.$t('success.deviceReady'),
icon: 'success'
})
} else {
throw new Error((result && result.msg) || '弹出风扇失败')
throw new Error((result && result.msg) || this.$t('success.deviceFailed'))
}
} catch (error) {
console.error('弹出风扇错误:', error)
this.deviceMessage = '弹出设备失败,请联系客服'
this.deviceMessage = this.$t('success.deviceFailed')
uni.showToast({
title: error.message || '弹出风扇失败,请联系客服',
title: error.message || this.$t('success.deviceFailed'),
icon: 'none'
})
} finally {
+20 -20
View File
@@ -8,7 +8,7 @@
<!-- 场地信息卡片 -->
<view class="info-card">
<!-- 场地名称 -->
<view class="position-name">{{ positionInfo.name || '加载中...' }}</view>
<view class="position-name">{{ positionInfo.name || $t('common.loading') }}</view>
<!-- 地址信息 -->
<view class="info-item" v-if="positionInfo.location">
@@ -19,30 +19,30 @@
<!-- 营业时间 -->
<view class="info-item" v-if="positionInfo.workTime && positionInfo.workTime !== '0'">
<image src="/static/device-time.png" class="item-icon" mode="aspectFit"></image>
<text class="item-text">营业时间{{ positionInfo.workTime }}</text>
<text class="item-text">{{ $t('location.businessHours') }}{{ positionInfo.workTime }}</text>
</view>
<!-- 计费信息 -->
<view class="info-item">
<image src="/static/device-price.png" class="item-icon" mode="aspectFit"></image>
<text class="item-text">计费{{ pricingText }}</text>
<text class="item-text">{{ $t('device.pricing') }}{{ pricingText }}</text>
</view>
<!-- 按钮组 -->
<view class="button-group">
<view style="display: flex;flex-direction: row;gap: 10rpx;">
<view class="status-btn" v-if="isRentable">可租借</view>
<view class="status-btn" v-if="isReturnable">可归还</view>
<view class="status-btn" v-if="isRentable">{{ $t('location.rent') }}</view>
<view class="status-btn" v-if="isReturnable">{{ $t('location.return') }}</view>
</view>
<view class="nav-btn" @click.stop="navigateToPosition">导航去这</view>
<view class="nav-btn" @click.stop="navigateToPosition">{{ $t('location.navigateHere') }}</view>
</view>
</view>
<!-- 底部操作按钮 -->
<view class="footer-actions">
<button class="action-btn btn-outline" @click="reportError">设备报错</button>
<button class="action-btn btn-primary" @click="scanCode">扫码使用</button>
<button class="action-btn btn-outline" @click="reportError">{{ $t('device.reportError') }}</button>
<button class="action-btn btn-primary" @click="scanCode">{{ $t('device.scanToUse') }}</button>
</view>
</view>
</template>
@@ -93,7 +93,7 @@
const loadPositionDetail = async () => {
try {
uni.showLoading({
title: '加载中...'
title: $t('common.loading')
})
const res = await uni.request({
@@ -111,10 +111,10 @@
if (position) {
positionInfo.value = position
} else {
uni.showToast({
title: '场地不存在',
icon: 'none'
})
uni.showToast({
title: this.$t('location.notExist'),
icon: 'none'
})
}
} else if (res.statusCode === 401 || res.data?.code === 401 || res.data?.code === 40101) {
uni.reLaunch({
@@ -124,7 +124,7 @@
} catch (e) {
console.error('加载场地详情失败:', e)
uni.showToast({
title: '加载失败',
title: $t('common.loadFailed'),
icon: 'none'
})
} finally {
@@ -135,7 +135,7 @@
const navigateToPosition = () => {
if (!positionInfo.value.latitude || !positionInfo.value.longitude) {
uni.showToast({
title: '该场地坐标信息异常',
title: $t('location.coordinateError'),
icon: 'none'
})
return
@@ -150,7 +150,7 @@
longitude < -180 || longitude > 180 ||
(latitude === 0 && longitude === 0)) {
uni.showToast({
title: '该场地坐标信息异常',
title: $t('location.coordinateError'),
icon: 'none'
})
return
@@ -185,10 +185,10 @@
},
fail: (err) => {
console.error('扫码失败:', err)
uni.showToast({
title: '扫码失败',
icon: 'none'
})
uni.showToast({
title: this.$t('home.scanFailed'),
icon: 'none'
})
}
})
}
+63 -58
View File
@@ -4,13 +4,13 @@
<view class="order-card">
<view class="order-header">
<text class="title">{{ getOrderStatusText() }}</text>
<text class="order-no">订单号{{ orderInfo.orderNo || '-' }}</text>
<text class="order-no">{{ $t('order.orderNo') }}{{ orderInfo.orderNo || '-' }}</text>
</view>
<view class="device-info">
<view class="device-left">
<view class="device-name">共享风扇</view>
<view class="device-id">设备号{{ deviceId }}</view>
<view class="device-name">{{ $t('device.sharedFan') }}</view>
<view class="device-id">{{ $t('order.deviceNo') }}{{ deviceId }}</view>
</view>
<!-- 支付方式标识 -->
@@ -19,41 +19,41 @@
<view class="payment-badge wx-score" v-if="orderInfo.payWay == 'wx_score_pay'">
<image src="/static/images/wxpayflag.png" mode="aspectFit" class="badge-icon"></image>
<view class="badge-text">
<text>微信支付分</text>
<text>{{ $t('order.wxPayScore') }}</text>
<text class="divider">|</text>
<text class="highlight">免押租借</text>
<text class="highlight">{{ $t('order.depositFree') }}</text>
</view>
</view>
<!-- 会员订单标识 -->
<view class="payment-badge member" v-else-if="orderInfo.payWay == 'wx_member_pay'">
<text class="badge-text">会员订单</text>
<text class="badge-text">{{ $t('order.memberOrder') }}</text>
</view>
<!-- 微信支付押金标识 -->
<view class="payment-badge deposit" v-else-if="orderInfo.payWay == 'wx_pay'">
<text class="badge-text">押金租借</text>
<text class="badge-text">{{ $t('order.depositPay') }}</text>
</view>
</view>
</view>
<view class="time-info">
<view class="time-item">
<text class="label">开始时间</text>
<text class="label">{{ $t('order.startTime') }}</text>
<text class="value">{{ orderInfo.startTime }}</text>
</view>
<view class="time-item" v-if="orderInfo.endTime">
<text class="label">结束时间</text>
<text class="label">{{ $t('order.endTime') }}</text>
<text class="value">{{ orderInfo.endTime }}</text>
</view>
<view class="time-item" v-if="orderInfo.orderStatus === 'in_used'">
<text class="label">已使用时长</text>
<text class="label">{{ $t('order.used') }}</text>
<text class="value highlight">{{ orderInfo.usedTime }}</text>
</view>
<view class="time-item" v-if="orderInfo.orderStatus === 'in_used'">
<text class="label">当前费用</text>
<text class="label">{{ $t('order.currentFee') }}</text>
<text class="value">{{ orderInfo.currentFee }}</text>
</view>
<view class="time-item" v-if="orderInfo.phone">
<text class="label">联系电话</text>
<text class="label">{{ $t('payment.contactPhone') }}</text>
<text class="value">{{ orderInfo.phone }}</text>
</view>
</view>
@@ -69,42 +69,42 @@
<!-- 费用信息卡片 -->
<view class="notice-card" v-if="orderInfo.depositAmount || orderInfo.packageTime">
<view class="notice-title">费用信息</view>
<view class="notice-title">{{ $t('payment.feeInfo') }}</view>
<view class="notice-list">
<view class="notice-item" v-if="orderInfo.depositAmount">
<view class="dot"></view>
<text>押金{{ orderInfo.depositAmount }}</text>
<text>{{ $t('payment.deposit') }}{{ orderInfo.depositAmount }}</text>
</view>
<view class="notice-item" v-if="orderInfo.packageTime && orderInfo.packagePrice">
<view class="dot"></view>
<text>套餐{{ orderInfo.packagePrice }} / {{ formatTime(orderInfo.packageTime) }}</text>
<text>{{ $t('payment.package') }}{{ orderInfo.packagePrice }}{{ $t('unit.yuan') }} / {{ formatTime(orderInfo.packageTime) }}</text>
</view>
<view class="notice-item">
<view class="dot"></view>
<text>合计{{ orderInfo.payAmount || 0 }}</text>
<text>{{ $t('payment.total') }}{{ orderInfo.payAmount || 0 }}</text>
</view>
</view>
</view>
<!-- 归还说明 -->
<view class="notice-card" v-if="orderInfo.orderStatus === 'in_used'">
<view class="notice-title">归还说明</view>
<view class="notice-title">{{ $t('order.returnInstructions') }}</view>
<view class="notice-list">
<view class="notice-item">
<view class="dot"></view>
<text>请确保设备完好无损</text>
<text>{{ $t('order.ensureDeviceIntact') }}</text>
</view>
<view class="notice-item">
<view class="dot"></view>
<text>将风扇插入原位置或空闲插口</text>
<text>{{ $t('order.insertFanBack') }}</text>
</view>
<view class="notice-item">
<view class="dot"></view>
<text>系统将自动检测归还并处理退款</text>
<text>{{ $t('order.autoDetectReturn') }}</text>
</view>
<view class="notice-item">
<view class="dot"></view>
<text>归还成功后将自动跳转到成功页面</text>
<text>{{ $t('order.autoJumpAfterReturn') }}</text>
</view>
</view>
</view>
@@ -117,25 +117,25 @@
<view class="bottom-bar">
<!-- 使用中状态显示归还相关操作 -->
<view v-if="orderInfo.orderStatus === 'in_used'" class="action-item secondary" @click="checkReturnStatus">
刷新状态</view>
{{ $t('order.refreshStatus') }}</view>
<view v-if="orderInfo.orderStatus === 'in_used' && !showExpressAction" class="action-item primary">
倒计时{{ formatHms(countdownRemaining) }}
{{ $t('order.countdown') }}{{ formatHms(countdownRemaining) }}
</view>
<view v-if="orderInfo.orderStatus === 'in_used' && showExpressAction" class="action-item primary" @click="expressRetrunOrder">
暂停计费快递归还
{{ $t('order.pauseAndExpress') }}
</view>
<!-- 已完成状态显示查看详情和返回首页 -->
<view v-if="orderInfo.orderStatus === 'used_done' || orderInfo.orderStatus === 'used_down'"
class="action-item secondary" @click="goToHome">返回首页</view>
class="action-item secondary" @click="goToHome">{{ $t('order.backToHome') }}</view>
<view v-if="orderInfo.orderStatus === 'used_done' || orderInfo.orderStatus === 'used_down'"
class="action-item primary" @click="viewOrderDetails">查看详情</view>
class="action-item primary" @click="viewOrderDetails">{{ $t('order.viewDetails') }}</view>
<!-- 待支付状态显示支付和取消操作 -->
<view v-if="orderInfo.orderStatus === 'waiting_for_payment'" class="action-item secondary"
@click="handleCancelOrder">取消订单</view>
@click="handleCancelOrder">{{ $t('order.cancelOrder') }}</view>
<view v-if="orderInfo.orderStatus === 'waiting_for_payment'" class="action-item primary"
@click="handlePayment">立即支付</view>
@click="handlePayment">{{ $t('order.payNow') }}</view>
</view>
</view>
</template>
@@ -190,6 +190,11 @@
onLoad(options) {
console.log('Return page loaded with options:', JSON.stringify(options))
// 设置页面标题
uni.setNavigationBarTitle({
title: this.$t('order.orderDetail')
})
// 标记页面为活跃状态
this.isPageActive = true
@@ -209,7 +214,7 @@
} else {
// 缺少必要参数
uni.showToast({
title: '缺少订单信息',
title: this.$t('order.orderInfoMissing'),
icon: 'none'
})
@@ -406,9 +411,9 @@
// 显示归还成功弹窗
uni.showModal({
title: '归还成功',
content: '风扇已归还成功,剩余押金将退还到您的账户',
confirmText: '查看详情',
title: this.$t('order.returnSuccess'),
content: this.$t('order.returnSuccessMessage'),
confirmText: this.$t('order.viewDetails'),
success: (res) => {
if (res.confirm) {
// 跳转到归还成功页面查看详情
@@ -428,17 +433,17 @@
// 根据订单状态获取对应的文字显示
getOrderStatusText() {
const statusMap = {
'waiting_for_payment': '待支付',
'payment_in_progress': '支付中',
'payment_successful': '支付成功',
'in_used': '使用中',
'payment_failed': '支付失败',
'order_cancelled': '已取消',
'used_done': '已完成',
'used_down': '已完成'
'waiting_for_payment': this.$t('order.waitingForPayment'),
'payment_in_progress': this.$t('order.paymentInProgress'),
'payment_successful': this.$t('order.paymentSuccess'),
'in_used': this.$t('order.inUse'),
'payment_failed': this.$t('order.paymentFailed'),
'order_cancelled': this.$t('order.cancelled'),
'used_done': this.$t('order.finished'),
'used_down': this.$t('order.finished')
}
return statusMap[this.orderInfo.orderStatus] || '使用中'
return statusMap[this.orderInfo.orderStatus] || this.$t('order.inUse')
},
// 获取订单详情
@@ -453,7 +458,7 @@
// uni.showLoading({ title: '加载中' })
if (!this.orderInfo.orderId) {
throw new Error('订单ID不能为空')
throw new Error(this.$t('order.orderIdRequired'))
}
const result = await queryById(this.orderInfo.orderId)
@@ -527,7 +532,7 @@
} catch (error) {
console.error('获取订单详情错误:', error)
uni.showToast({
title: error.message || '获取订单信息失败',
title: error.message || this.$t('order.getOrderFailed'),
icon: 'none'
})
@@ -679,12 +684,12 @@
if (this.currentStatusChecks >= this.maxStatusChecks) {
this.clearStatusCheckTimer()
// 提示用户手动刷新
uni.showToast({
title: '请手动刷新查看归还状态',
icon: 'none',
duration: 3000
})
// 提示用户手动刷新
uni.showToast({
title: this.$t('order.pleaseRefreshManually'),
icon: 'none',
duration: 3000
})
}
} else {
console.log('页面已不活跃,停止状态检查计时器')
@@ -701,7 +706,7 @@
// uni.showLoading({ title: '加载中' })
if (!this.deviceId) {
throw new Error('设备号不能为空')
throw new Error(this.$t('device.deviceNoRequired'))
}
// 这里调用API查询该设备的使用中订单
@@ -742,12 +747,12 @@
// 获取详细订单信息
this.getOrderDetails()
} else {
throw new Error('未找到使用中的订单')
throw new Error(this.$t('order.noOrderInUse'))
}
} catch (error) {
console.error('通过设备号查询订单失败:', error)
uni.showToast({
title: error.message || '获取订单信息失败',
title: error.message || this.$t('order.getOrderFailed'),
icon: 'none'
})
@@ -795,13 +800,13 @@
// 取消订单
handleCancelOrder() {
uni.showModal({
title: '确认取消',
content: '确定要取消此订单吗?',
title: this.$t('order.confirmCancel'),
content: this.$t('order.confirmCancelContent'),
success: async (res) => {
if (res.confirm) {
try {
uni.showLoading({
title: '处理中'
title: this.$t('common.processing')
});
const result = await cancelOrder({
orderId: this.orderInfo.orderId
@@ -809,17 +814,17 @@
if (result.code === 200) {
uni.hideLoading();
uni.showToast({
title: '订单已取消',
title: this.$t('order.cancelSuccess'),
icon: 'success'
});
await this.getOrderDetails();
} else {
throw new Error(result.msg || '取消订单失败');
throw new Error(result.msg || this.$t('order.cancelFailed'));
}
} catch (error) {
uni.hideLoading();
uni.showToast({
title: error.message || '取消订单失败',
title: error.message || this.$t('order.cancelFailed'),
icon: 'none'
});
}
+28 -21
View File
@@ -12,8 +12,8 @@
<view class="list-wrap">
<view class="panel">
<view class="filter-tabs">
<view class="tab" :class="{ active: activeTab === 'rent' }" @click="setTab('rent')">可租借</view>
<view class="tab" :class="{ active: activeTab === 'return' }" @click="setTab('return')">可归还</view>
<view class="tab" :class="{ active: activeTab === 'rent' }" @click="setTab('rent')">{{ $t('location.rent') }}</view>
<view class="tab" :class="{ active: activeTab === 'return' }" @click="setTab('return')">{{ $t('location.return') }}</view>
</view>
<scroll-view class="list-scroll" scroll-y="true">
<view class="card" :class="{ available: isRentable(item), invalid: !isValidCoordinate(item.latitude, item.longitude) }"
@@ -28,16 +28,16 @@
<view class="row sub" v-if="item.location">
<text class="addr">{{ item.location }}</text>
</view>
<view class="row meta" v-if="item.workTime && item.workTime !== '0'">
<text class="time">营业时间{{ item.workTime }}</text>
</view>
<view class="row meta" v-if="!isValidCoordinate(item.latitude, item.longitude)">
<text class="time" style="color: #ff6b6b;">坐标信息异常</text>
</view>
<view class="tags">
<view class="tag rent" v-if="isRentable(item)">可租借</view>
<view class="tag return" v-if="isReturnable(item)">可归还</view>
</view>
<view class="row meta" v-if="item.workTime && item.workTime !== '0'">
<text class="time">{{ $t('location.businessHours') }}{{ item.workTime }}</text>
</view>
<view class="row meta" v-if="!isValidCoordinate(item.latitude, item.longitude)">
<text class="time" style="color: #ff6b6b;">{{ $t('location.coordinateError') }}</text>
</view>
<view class="tags">
<view class="tag rent" v-if="isRentable(item)">{{ $t('location.rent') }}</view>
<view class="tag return" v-if="isReturnable(item)">{{ $t('location.return') }}</view>
</view>
</view>
<view class="actions">
@@ -50,10 +50,10 @@
</view>
</view>
<view class="empty-state" v-if="!isLoading && (!positionList || positionList.length === 0)">
<image class="empty-icon" src="/static/scan-icon.png" mode="aspectFit" />
<text class="empty-text">附近暂无设备</text>
</view>
<view class="empty-state" v-if="!isLoading && (!positionList || positionList.length === 0)">
<image class="empty-icon" src="/static/scan-icon.png" mode="aspectFit" />
<text class="empty-text">{{ $t('home.noNearbyDevice') }}</text>
</view>
</scroll-view>
</view>
</view>
@@ -75,6 +75,16 @@
getRegeo,
calculateDistanceSync
} from '../../utils/mapUtils.js'
import { useI18n } from '@/utils/i18n.js'
const { t: $t } = useI18n()
onMounted(() => {
uni.setNavigationBarTitle({
title: $t('search.title')
})
init()
})
const userLocation = ref(null)
const positionList = ref([])
@@ -208,7 +218,7 @@
const navigateToPosition = (position) => {
if (!isValidCoordinate(position.latitude, position.longitude)) {
uni.showToast({
title: '该位置坐标无效',
title: $t('search.invalidCoordinate'),
icon: 'none'
})
return
@@ -226,7 +236,7 @@
const goToPositionDetail = (position) => {
if (!position.positionId) {
uni.showToast({
title: '场地信息异常',
title: $t('search.positionInfoError'),
icon: 'none'
})
return
@@ -236,9 +246,6 @@
})
}
onMounted(() => {
init()
})
</script>
<style lang="scss" scoped>
+10 -4
View File
@@ -25,15 +25,21 @@
},
async onLoad(option) {
console.log('bagCheck onLoad option:', option);
// 设置页面标题
uni.setNavigationBarTitle({
title: this.$t('device.checking')
})
try {
uni.showLoading({
title: '处理中...',
title: this.$t('common.processing'),
mask: true
});
// 检查是否传入设备编号
if (!option || !option.deviceNo) {
throw new Error('未识别到设备编号');
throw new Error(this.$t('device.deviceNoNotRecognized'));
}
const deviceNo = option.deviceNo;
@@ -97,10 +103,10 @@
error.message.includes('未识别到设备编号') ||
error.message.includes('网络请求失败') ||
error.message.includes('服务器错误')
)) {
) ) {
console.error('扫码检查订单失败:', error);
uni.showToast({
title: error.message || '处理失败,请稍后重试',
title: error.message || this.$t('device.processFailed'),
icon: 'none',
duration: 2000
});
+110 -6
View File
@@ -1,18 +1,27 @@
<template>
<view class="setting-page">
<view class="group">
<view class="item" @click="showLanguageSelector">
<text class="label">{{ $t('settings.language') }}</text>
<view class="right">
<text class="value">{{ currentLanguageText }}</text>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view>
</view>
</view>
<view class="group">
<view class="item" @click="navigateTo('/pages/legal/agreement')">
<text class="label">用户协议</text>
<text class="label">{{ $t('user.userAgreement') }}</text>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view>
<view class="item" @click="navigateTo('/pages/legal/privacy')">
<text class="label">隐私政策</text>
<text class="label">{{ $t('user.privacyPolicy') }}</text>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view>
</view>
<view class="group">
<view class="item" @click="handleLogout">
<text class="label">退出登录</text>
<text class="label">{{ $t('user.logout') }}</text>
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
</view>
</view>
@@ -20,21 +29,105 @@
</template>
<script setup>
import { ref, computed, onMounted, getCurrentInstance } from 'vue'
import { userLogout } from '@/config/api/user.js'
import { useI18n } from '@/utils/i18n.js'
const { t: $t } = useI18n()
// 获取全局 i18n 实例
const instance = getCurrentInstance()
const globalI18n = instance?.appContext?.config?.globalProperties?.$i18n
// 设置页面标题
onMounted(() => {
uni.setNavigationBarTitle({
title: $t('settings.title')
})
})
// 当前语言
const currentLanguage = ref(uni.getStorageSync('language') || 'zh-CN')
// 当前语言文本显示
const currentLanguageText = computed(() => {
return currentLanguage.value === 'zh-CN' ? '简体中文' : 'English'
})
const navigateTo = (url) => {
uni.navigateTo({ url })
}
// 显示语言选择器
const showLanguageSelector = () => {
uni.showActionSheet({
itemList: ['简体中文', 'English'],
success: (res) => {
const lang = res.tapIndex === 0 ? 'zh-CN' : 'en-US'
if (lang !== currentLanguage.value) {
console.log('========================================')
console.log('=== 用户选择切换语言 ===')
console.log('旧语言:', currentLanguage.value)
console.log('新语言:', lang)
// 1. 保存到缓存
uni.setStorageSync('language', lang)
console.log('✓ 语言已保存到缓存')
// 2. 立即更新 i18n 实例(重要!)
if (globalI18n) {
console.log('✓ 正在更新 globalI18n.locale...')
console.log(' 更新前:', globalI18n.locale)
globalI18n.locale = lang
console.log(' 更新后:', globalI18n.locale)
console.log('✓ 测试翻译:', globalI18n.t('common.loading'))
} else {
console.warn('✗ globalI18n 不存在!')
console.warn(' instance:', !!instance)
console.warn(' appContext:', !!instance?.appContext)
console.warn(' globalProperties:', !!instance?.appContext?.config?.globalProperties)
}
// 3. 更新当前语言状态
currentLanguage.value = lang
console.log('========================================')
// 4. 提示用户
uni.showToast({
title: lang === 'zh-CN' ? '语言已切换,正在刷新...' : 'Language switched, refreshing...',
icon: 'none',
duration: 800
})
// 5. 延迟后重新加载应用(确保 i18n 更新已生效)
setTimeout(() => {
console.log('=== 准备 reLaunch 到首页 ===')
// 使用 reLaunch 完全重启应用
uni.reLaunch({
url: '/pages/index/index',
success: () => {
console.log('✓ 页面已重新加载')
},
fail: (err) => {
console.error('✗ 页面重载失败:', err)
}
})
}, 800)
}
}
})
}
const handleLogout = async () => {
uni.showModal({
title: '提示',
content: '确定要退出登录吗?',
title: $t('common.tips'),
content: $t('user.confirmLogout'),
success: async (res) => {
if (res.confirm) {
const response = await userLogout();
if (response.code == 200) {
uni.showToast({ title: '退出成功', icon: 'none' })
uni.showToast({ title: $t('user.logoutSuccess'), icon: 'none' })
setTimeout(() => {
uni.removeStorageSync('token')
uni.removeStorageSync('userInfo')
@@ -77,4 +170,15 @@ const handleLogout = async () => {
font-size: 30rpx;
color: #333333;
}
.right {
display: flex;
align-items: center;
gap: 8rpx;
}
.value {
font-size: 28rpx;
color: #999999;
}
</style>
+27 -15
View File
@@ -6,40 +6,40 @@
<image :src="userInfo.avatar || '/static/images/default-avatar.png'" mode="aspectFill"></image>
</view>
<view class="user-info">
<text class="nickname">{{ userInfo.nickName || '未登录' }}</text>
<text class="phone">{{ userInfo.phone || '未绑定手机号' }}</text>
<text class="nickname">{{ userInfo.nickName || $t('user.notLoggedIn') }}</text>
<text class="phone">{{ userInfo.phone || $t('user.phoneNotBound') }}</text>
</view>
</view>
<!-- 余额卡片 -->
<view class="balance-card">
<view class="balance-title">余额</view>
<view class="balance-title">{{ $t('userProfile.balance') }}</view>
<view class="balance-amount">{{ userInfo.balanceAmount || '0.00' }}</view>
<view class="balance-desc">可用于租借设备</view>
<view class="balance-desc">{{ $t('user.balanceDesc') }}</view>
</view>
<!-- 功能菜单 -->
<view class="menu-list">
<view class="menu-item" @click="navigateTo('/pages/order/list')">
<view class="menu-item" @click="navigateTo('/pages/order/index')">
<text class="menu-icon">📋</text>
<text class="menu-text">我的订单</text>
<text class="menu-text">{{ $t('user.myOrders') }}</text>
<text class="menu-arrow">></text>
</view>
<view class="menu-item" @click="navigateTo('/pages/user/feedback')">
<view class="menu-item" @click="navigateTo('/pages/feedback/index')">
<text class="menu-icon">💬</text>
<text class="menu-text">意见反馈</text>
<text class="menu-text">{{ $t('user.feedback') }}</text>
<text class="menu-arrow">></text>
</view>
<view class="menu-item" @click="navigateTo('/pages/user/about')">
<view class="menu-item" @click="navigateTo('/pages/help/index')">
<text class="menu-icon"></text>
<text class="menu-text">关于我们</text>
<text class="menu-text">{{ $t('help.title') }}</text>
<text class="menu-arrow">></text>
</view>
</view>
<!-- 退出登录按钮 -->
<view class="logout-btn" @click="handleLogout" v-if="isLogin">
<text>退出登录</text>
<text>{{ $t('user.logout') }}</text>
</view>
</view>
</template>
@@ -55,6 +55,12 @@ export default {
isLogin: false
}
},
onLoad() {
// 设置页面标题
uni.setNavigationBarTitle({
title: this.$t('user.personalCenter')
})
},
onShow() {
this.loadUserInfo()
},
@@ -90,16 +96,22 @@ export default {
},
handleLogout() {
uni.showModal({
title: '提示',
content: '确定要退出登录吗?',
title: this.$t('common.tips'),
content: this.$t('user.confirmLogout'),
success: (res) => {
if (res.confirm) {
uni.removeStorageSync('token')
uni.removeStorageSync('userInfo')
this.isLogin = false
uni.redirectTo({
url: '/pages/login/index'
uni.showToast({
title: this.$t('user.logoutSuccess'),
icon: 'success'
})
setTimeout(() => {
uni.redirectTo({
url: '/pages/login/index'
})
}, 500)
}
}
})
+26 -20
View File
@@ -9,15 +9,15 @@
<button class="avatar-choose-btn" open-type="chooseAvatar" @chooseavatar="onChooseAvatar"></button>
<!-- #endif -->
</view>
<view class="avatar-tip">点击头像更换</view>
</view>
<view class="avatar-tip">{{ $t('userProfile.clickToChange') }}</view>
</view>
<view class="form-section">
<!-- 昵称编辑区域 -->
<view class="form-item nickname-item" :class="{ editing: isEditingNickname }">
<view class="label">昵称</view>
<view class="label">{{ $t('userProfile.nickname') }}</view>
<view class="value" v-if="!isEditingNickname" @click="startEditNickname">
<text class="value-text">{{ userInfo.nickName || '未设置' }}</text>
<text class="value-text">{{ userInfo.nickName || $t('userProfile.notSet') }}</text>
<uv-icon name="edit-pen" size="16" color="#999999"></uv-icon>
</view>
</view>
@@ -27,25 +27,25 @@
<input
class="nickname-input"
v-model="newNickname"
placeholder="请输入新昵称"
:placeholder="$t('userProfile.enterNickname')"
maxlength="20"
:focus="true"
/>
<view class="edit-buttons">
<button class="cancel-btn" @click="cancelEditNickname">取消</button>
<button class="save-btn" @click="saveNickname">保存</button>
<button class="cancel-btn" @click="cancelEditNickname">{{ $t('common.cancel') }}</button>
<button class="save-btn" @click="saveNickname">{{ $t('common.save') }}</button>
</view>
</view>
<view class="form-item">
<view class="label">手机号</view>
<view class="label">{{ $t('userProfile.phone') }}</view>
<view class="value">
<text class="value-text">{{ userInfo.phone ? maskPhone(userInfo.phone) : '未绑定' }}</text>
<text class="value-text">{{ userInfo.phone ? maskPhone(userInfo.phone) : $t('userProfile.notBound') }}</text>
</view>
</view>
<view class="form-item" v-if="userInfo.balanceAmount !== undefined">
<view class="label">余额</view>
<view class="label">{{ $t('userProfile.balance') }}</view>
<view class="value">
<text class="value-text amount">¥{{ userInfo.balanceAmount || '0.00' }}</text>
</view>
@@ -57,6 +57,9 @@
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { getMyIndexInfo, uploadUserAvatar, updateUserInfo } from '../../config/api/user.js';
import { useI18n } from '@/utils/i18n.js'
const { t: $t } = useI18n()
// 响应式状态
const userInfo = ref({
@@ -71,6 +74,9 @@ const isEditingNickname = ref(false);
// 页面加载时初始化
onMounted(() => {
uni.setNavigationBarTitle({
title: $t('userProfile.title')
})
loadUserInfo();
});
@@ -96,7 +102,7 @@ const loadUserInfo = async () => {
} catch (error) {
console.error('获取用户信息失败:', error);
uni.showToast({
title: '获取用户信息失败',
title: $t('user.getUserInfoFailed'),
icon: 'none'
});
}
@@ -132,13 +138,13 @@ const onChooseAvatar = async (e) => {
const avatarLocalPath = e?.detail?.avatarUrl;
if (!avatarLocalPath) {
uni.showToast({
title: '未选择头像',
title: $t('user.noAvatar'),
icon: 'none'
});
return;
}
uni.showLoading({
title: '上传中...',
title: $t('userProfile.uploading'),
mask: true
});
const uploadRes = await uploadUserAvatar(avatarLocalPath);
@@ -151,14 +157,14 @@ const onChooseAvatar = async (e) => {
uni.setStorageSync('userInfo', userInfo.value);
}
uni.showToast({
title: '头像更新成功',
title: $t('user.avatarUpdated'),
icon: 'success'
});
await loadUserInfo();
} catch (err) {
console.error('选择/上传头像失败:', err);
uni.showToast({
title: '头像更新失败',
title: $t('user.avatarUploadFailed'),
icon: 'none'
});
} finally {
@@ -182,7 +188,7 @@ const cancelEditNickname = () => {
const saveNickname = async () => {
if (!newNickname.value || !newNickname.value.trim()) {
uni.showToast({
title: '昵称不能为空',
title: $t('userProfile.nicknameRequired'),
icon: 'none'
});
return;
@@ -190,7 +196,7 @@ const saveNickname = async () => {
try {
uni.showLoading({
title: '保存中...',
title: $t('userProfile.saving'),
mask: true
});
@@ -222,19 +228,19 @@ const saveNickname = async () => {
uni.hideLoading();
uni.showToast({
title: '昵称修改成功',
title: $t('userProfile.nicknameUpdated'),
icon: 'success'
});
isEditingNickname.value = false;
} else {
throw new Error(res.message || '修改失败');
throw new Error(res.message || $t('userProfile.updateFailed'));
}
} catch (error) {
console.error('修改昵称失败:', error);
uni.hideLoading();
uni.showToast({
title: error.message || '修改失败',
title: error.message || $t('userProfile.updateFailed'),
icon: 'none'
});
}
+11 -5
View File
@@ -1,6 +1,6 @@
<template>
<view class="waiting-container">
<view class="title">设备弹出中请稍候</view>
<view class="title">{{ $t('waiting.title') }}</view>
<view class="progress-wrapper">
<view class="progress-circle">
<view class="progress-left">
@@ -11,12 +11,12 @@
</view>
<view class="progress-inner">
<text class="percent">{{ progress }}%</text>
<text class="hint">正在为您弹出设备</text>
<text class="hint">{{ $t('waiting.preparing') }}</text>
</view>
</view>
</view>
<view class="desc">若长时间未弹出请联系现场工作人员或稍后重试</view>
<view class="desc">{{ $t('waiting.longTimeNotice') }}</view>
</view>
</template>
@@ -24,6 +24,9 @@
import { ref, onMounted, onUnmounted } from 'vue'
import { onLoad, onUnload } from '@dcloudio/uni-app'
import { getOrderByOrderNoScorePayStatus, cancelOrder } from '@/config/api/order.js'
import { useI18n } from '@/utils/i18n.js'
const { t: $t } = useI18n()
const progress = ref(0)
const leftRotateDeg = ref(0)
@@ -83,7 +86,7 @@
await cancelOrder({ orderId: orderNo.value })
}
} catch (e) {}
uni.showToast({ title: '设备租借失败,订单已取消', icon: 'none' })
uni.showToast({ title: $t('waiting.rentFailed'), icon: 'none' })
setTimeout(() => {
uni.switchTab({ url: '/pages/index/index' })
}, 800)
@@ -111,7 +114,7 @@
// 超时保护:例如 60 秒
timeoutTimer = setTimeout(() => {
stopAllTimers()
uni.showToast({ title: '等待超时,请稍后重试', icon: 'none' })
uni.showToast({ title: $t('waiting.timeout'), icon: 'none' })
setTimeout(() => {
uni.switchTab({ url: '/pages/index/index' })
}, 800)
@@ -119,6 +122,9 @@
}
onLoad((query) => {
uni.setNavigationBarTitle({
title: $t('waiting.title')
})
if (query && query.orderNo) {
orderNo.value = query.orderNo
}