fix:修复bug
This commit is contained in:
+132
-150
@@ -26,11 +26,11 @@
|
||||
<view class="record-list">
|
||||
<view class="record-item" v-for="(item, index) in records" :key="index">
|
||||
<view class="record-info">
|
||||
<text class="record-type">{{ item.type }}</text>
|
||||
<text class="record-type">{{ item.typeText }}</text>
|
||||
<text class="record-time">{{ item.time }}</text>
|
||||
</view>
|
||||
<text class="record-amount" :class="item.type === '退还' ? 'refund' : ''">
|
||||
{{ item.type === '退还' ? '+' : '-' }}¥{{ item.amount }}
|
||||
<text class="record-amount" :class="item.type === 'refund' ? 'refund' : ''">
|
||||
{{ item.type === 'refund' ? '+' : '-' }}¥{{ item.amount }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -38,163 +38,145 @@
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import { getUserInfo } from '../../util/index.js'
|
||||
import { withdrawDeposit } from '../../config/api/user.js'
|
||||
import { queryById } from '../../config/api/order.js'
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
depositAmount: '0.00',
|
||||
orderNo: '',
|
||||
records: [],
|
||||
orderId:''
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
// 设置页面标题
|
||||
uni.setNavigationBarTitle({
|
||||
title: this.$t('deposit.title')
|
||||
})
|
||||
// this.loadUserInfo()
|
||||
},
|
||||
onShow() {
|
||||
this.loadUserInfo()
|
||||
},
|
||||
methods: {
|
||||
async loadUserInfo() {
|
||||
try {
|
||||
const res = await getUserInfo()
|
||||
console.log('loadUserInfo',res);
|
||||
if (res.code === 200) {
|
||||
this.depositAmount = res.data.balanceAmount || '0.00'
|
||||
this.orderNo = res.data.latestOrderNo || ''
|
||||
this.orderId = res.data.latestOrderId||''
|
||||
|
||||
// 如果存在余额,获取押金记录
|
||||
if (parseFloat(this.depositAmount) > 0 && this.orderNo) {
|
||||
this.records = [
|
||||
{
|
||||
type: '支付',
|
||||
time: this.formatDate(new Date()),
|
||||
amount: this.depositAmount
|
||||
}
|
||||
]
|
||||
} else {
|
||||
this.records = []
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取用户信息失败:', error)
|
||||
uni.showToast({
|
||||
title: this.$t('user.getUserInfoFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
async handleWithdraw() {
|
||||
if (parseFloat(this.depositAmount) <= 0) {
|
||||
uni.showToast({
|
||||
title: this.$t('deposit.noBalance'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
if(this.orderId.length!=0||this.orderNo.length!=0){
|
||||
const res = await queryById(Number(this.orderId))
|
||||
console.log(res);
|
||||
}
|
||||
|
||||
// if(this.orderNo.length!=0){
|
||||
// uni.showToast({
|
||||
// title:'当前存在进行中的订单',
|
||||
// icon:'none'
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
const { t } = useI18n()
|
||||
|
||||
const depositAmount = ref('0.00')
|
||||
const orderNo = ref('')
|
||||
const orderId = ref('')
|
||||
const records = ref([])
|
||||
|
||||
onMounted(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: t('deposit.title')
|
||||
})
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
loadUserInfo()
|
||||
})
|
||||
|
||||
const loadUserInfo = async () => {
|
||||
try {
|
||||
const res = await getUserInfo()
|
||||
if (res.code === 200) {
|
||||
depositAmount.value = res.data.balanceAmount || '0.00'
|
||||
orderNo.value = res.data.latestOrderNo || ''
|
||||
orderId.value = res.data.latestOrderId || ''
|
||||
|
||||
uni.showModal({
|
||||
title: this.$t('deposit.confirmWithdraw'),
|
||||
content: this.$t('deposit.withdrawDesc'),
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showLoading({
|
||||
title: this.$t('deposit.withdrawing')
|
||||
// 如果存在余额,获取押金记录
|
||||
if (parseFloat(depositAmount.value) > 0 && orderNo.value) {
|
||||
records.value = [
|
||||
{
|
||||
type: 'pay',
|
||||
typeText: t('deposit.payRecord'),
|
||||
time: formatDate(new Date()),
|
||||
amount: depositAmount.value
|
||||
}
|
||||
]
|
||||
} else {
|
||||
records.value = []
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: t('user.getUserInfoFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleWithdraw = async () => {
|
||||
if (parseFloat(depositAmount.value) <= 0) {
|
||||
uni.showToast({
|
||||
title: t('deposit.noBalance'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
uni.showModal({
|
||||
title: t('deposit.confirmWithdraw'),
|
||||
content: t('deposit.withdrawDesc'),
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showLoading({
|
||||
title: t('deposit.withdrawing')
|
||||
})
|
||||
|
||||
try {
|
||||
const result = await withdrawDeposit(orderNo.value)
|
||||
|
||||
if (result.code === 200) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: t('deposit.withdrawSubmitted'),
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
try {
|
||||
console.log('发起提现请求,订单号:', this.orderNo)
|
||||
const result = await withdrawDeposit(this.orderNo)
|
||||
console.log('提现响应:', result)
|
||||
|
||||
if (result.code === 200) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: this.$t('deposit.withdrawSubmitted'),
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
// 更新余额为0
|
||||
this.depositAmount = '0.00'
|
||||
this.records.push({
|
||||
type: '退还',
|
||||
time: this.formatDate(new Date()),
|
||||
amount: this.depositAmount
|
||||
})
|
||||
|
||||
// 重新加载用户信息
|
||||
setTimeout(() => {
|
||||
this.loadUserInfo()
|
||||
}, 1500)
|
||||
} else {
|
||||
throw new Error(result.msg || this.$t('deposit.withdrawFailed'))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提现失败:', error)
|
||||
uni.hideLoading()
|
||||
|
||||
// 更详细的错误处理
|
||||
let errorMessage = this.$t('deposit.withdrawFailed');
|
||||
|
||||
// 如果有具体错误信息,使用它
|
||||
if (error.message) {
|
||||
// 常见错误消息处理
|
||||
if (error.message.includes('尚未归还')) {
|
||||
errorMessage = this.$t('deposit.orderNotReturned');
|
||||
} else if (error.message.includes('已退还')) {
|
||||
errorMessage = this.$t('deposit.alreadyRefunded');
|
||||
} else if (error.message.includes('处理中')) {
|
||||
errorMessage = this.$t('deposit.refundProcessing');
|
||||
} else if (error.message.includes('余额为0')) {
|
||||
errorMessage = this.$t('deposit.noBalance');
|
||||
} else {
|
||||
// 使用后端返回的具体错误消息
|
||||
errorMessage = error.message;
|
||||
}
|
||||
}
|
||||
|
||||
// 显示错误提示
|
||||
uni.showModal({
|
||||
title: this.$t('deposit.withdrawFailed'),
|
||||
content: errorMessage,
|
||||
showCancel: false
|
||||
})
|
||||
// 更新记录
|
||||
records.value.push({
|
||||
type: 'refund',
|
||||
typeText: t('deposit.refundRecord'),
|
||||
time: formatDate(new Date()),
|
||||
amount: depositAmount.value
|
||||
})
|
||||
// 更新余额为0
|
||||
depositAmount.value = '0.00'
|
||||
|
||||
// 重新加载用户信息
|
||||
setTimeout(() => {
|
||||
loadUserInfo()
|
||||
}, 1500)
|
||||
} else {
|
||||
throw new Error(result.msg || t('deposit.withdrawFailed'))
|
||||
}
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
|
||||
// 更详细的错误处理
|
||||
let errorMessage = t('deposit.withdrawFailed');
|
||||
|
||||
if (error.message) {
|
||||
if (error.message.includes('尚未归还')) {
|
||||
errorMessage = t('deposit.orderNotReturned');
|
||||
} else if (error.message.includes('已退还')) {
|
||||
errorMessage = t('deposit.alreadyRefunded');
|
||||
} else if (error.message.includes('处理中')) {
|
||||
errorMessage = t('deposit.refundProcessing');
|
||||
} else if (error.message.includes('余额为0')) {
|
||||
errorMessage = t('deposit.noBalance');
|
||||
} else {
|
||||
errorMessage = error.message;
|
||||
}
|
||||
}
|
||||
|
||||
uni.showModal({
|
||||
title: t('deposit.withdrawFailed'),
|
||||
content: errorMessage,
|
||||
showCancel: false
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
formatDate(date) {
|
||||
const year = date.getFullYear()
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = date.getDate().toString().padStart(2, '0')
|
||||
const hours = date.getHours().toString().padStart(2, '0')
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0')
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const formatDate = (date) => {
|
||||
const year = date.getFullYear()
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = date.getDate().toString().padStart(2, '0')
|
||||
const hours = date.getHours().toString().padStart(2, '0')
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0')
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -338,4 +320,4 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
+50
-46
@@ -131,7 +131,9 @@
|
||||
import {
|
||||
getOrderByOrderNoScore,
|
||||
getOrderByOrderNo,
|
||||
cancelOrder
|
||||
cancelOrder,
|
||||
getInUseOrder,
|
||||
getUnpaidOrder
|
||||
} from '@/config/api/order.js'
|
||||
import {
|
||||
initiateWeChatScorePayment,
|
||||
@@ -143,7 +145,7 @@
|
||||
} from '@/utils/i18n.js'
|
||||
|
||||
const {
|
||||
t: $t
|
||||
t
|
||||
} = useI18n()
|
||||
|
||||
// 响应式状态
|
||||
@@ -154,7 +156,7 @@
|
||||
const deviceLocation = ref('一号教学楼大厅')
|
||||
const hasActiveOrder = ref(false)
|
||||
const deviceStatus = reactive({
|
||||
text: $t('device.available'),
|
||||
text: t('device.available'),
|
||||
class: 'available'
|
||||
})
|
||||
const isLoggedIn = ref(true)
|
||||
@@ -175,7 +177,7 @@
|
||||
|
||||
onMounted(async () => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t('device.deviceInfo')
|
||||
title: t('device.deviceInfo')
|
||||
})
|
||||
await checkUserPhone()
|
||||
await fetchDeviceInfo()
|
||||
@@ -219,7 +221,7 @@
|
||||
// 用户拒绝授权的情况
|
||||
if (e.detail.errMsg && e.detail.errMsg.includes('deny')) {
|
||||
uni.showToast({
|
||||
title: $t('auth.phoneRequired'),
|
||||
title: t('auth.phoneRequired'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
@@ -228,7 +230,7 @@
|
||||
// 获取到授权code
|
||||
if (e.detail.code) {
|
||||
uni.showLoading({
|
||||
title: $t('auth.getting')
|
||||
title: t('auth.getting')
|
||||
})
|
||||
|
||||
console.log('获取到的授权code:', e.detail.code)
|
||||
@@ -260,15 +262,15 @@
|
||||
showPhoneAuthPopup.value = false
|
||||
|
||||
uni.showToast({
|
||||
title: $t('auth.phoneSuccess'),
|
||||
title: t('auth.phoneSuccess'),
|
||||
icon: 'success'
|
||||
})
|
||||
} else {
|
||||
// 记录详细信息,不抛出错误
|
||||
console.warn('获取手机号响应异常:', res.msg || '未知错误')
|
||||
uni.showModal({
|
||||
title: $t('auth.phoneError'),
|
||||
content: `${$t('common.statusCode')}: ${res.code}, ${$t('common.message')}: ${res.msg || $t('common.none')}`,
|
||||
title: t('auth.phoneError'),
|
||||
content: `${t('common.statusCode')}: ${res.code}, ${t('common.message')}: ${res.msg || t('common.none')}`,
|
||||
showCancel: false
|
||||
})
|
||||
}
|
||||
@@ -280,8 +282,8 @@
|
||||
// 显示更详细的错误信息
|
||||
let errMsg = err.message || err.toString()
|
||||
uni.showModal({
|
||||
title: $t('auth.phoneGetFailed'),
|
||||
content: $t('common.errorInfo') + ': ' + errMsg,
|
||||
title: t('auth.phoneGetFailed'),
|
||||
content: t('common.errorInfo') + ': ' + errMsg,
|
||||
showCancel: false
|
||||
})
|
||||
})
|
||||
@@ -289,14 +291,14 @@
|
||||
uni.hideLoading()
|
||||
console.error('获取手机号外部错误:', outerError)
|
||||
uni.showModal({
|
||||
title: $t('common.unexpectedError'),
|
||||
content: $t('common.processException') + ': ' + (outerError.message || outerError),
|
||||
title: t('common.unexpectedError'),
|
||||
content: t('common.processException') + ': ' + (outerError.message || outerError),
|
||||
showCancel: false
|
||||
})
|
||||
}
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: $t('auth.authCodeFailed'),
|
||||
title: t('auth.authCodeFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
@@ -325,10 +327,10 @@
|
||||
// 更新设备状态
|
||||
if (deviceInfo.value.status) {
|
||||
if (deviceInfo.value.status === 'online') {
|
||||
deviceStatus.text = $t('device.available')
|
||||
deviceStatus.text = t('device.available')
|
||||
deviceStatus.class = 'available'
|
||||
} else if (deviceInfo.value.status === 'offline') {
|
||||
deviceStatus.text = $t('device.offline')
|
||||
deviceStatus.text = t('device.offline')
|
||||
deviceStatus.class = 'offline'
|
||||
}
|
||||
}
|
||||
@@ -349,9 +351,9 @@
|
||||
// 显示登录提示
|
||||
const showLoginTip = () => {
|
||||
uni.showModal({
|
||||
title: $t('common.tips'),
|
||||
content: $t('common.loginRequired'),
|
||||
confirmText: $t('auth.goToLogin'),
|
||||
title: t('common.tips'),
|
||||
content: t('common.loginRequired'),
|
||||
confirmText: t('auth.goToLogin'),
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.navigateTo({
|
||||
@@ -374,27 +376,29 @@
|
||||
const checkOrderStatus = async () => {
|
||||
try {
|
||||
// 调用接口检查是否有进行中的订单
|
||||
const result = await uni.$api.checkActiveOrder()
|
||||
const inUseRes = await getInUseOrder()
|
||||
if (inUseRes && inUseRes.code === 200 && inUseRes.data) {
|
||||
const order = inUseRes.data
|
||||
// 如果有正在进行的订单,跳转到归还页面,带上设备ID
|
||||
uni.redirectTo({
|
||||
url: `/pages/device/return?deviceId=${deviceId.value}`
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (result.hasOrder) {
|
||||
const order = result.order // 假设后端返回 order 对象
|
||||
|
||||
// 检查订单状态
|
||||
if (order.status === 'waiting_for_payment') {
|
||||
// 跳转支付页面,带上订单ID
|
||||
uni.redirectTo({
|
||||
url: `/pages/order/payment?orderId=${order.orderId}&deviceId=${deviceId.value}`
|
||||
})
|
||||
} else if (order.status === 'in_used') {
|
||||
// 如果有正在进行的订单,跳转到归还页面,带上设备ID
|
||||
uni.redirectTo({
|
||||
url: `/pages/device/return?deviceId=${deviceId.value}`
|
||||
})
|
||||
}
|
||||
// 检查是否有待支付的订单
|
||||
const unpaidRes = await getUnpaidOrder()
|
||||
if (unpaidRes && unpaidRes.code === 200 && unpaidRes.data) {
|
||||
const order = unpaidRes.data
|
||||
// 跳转支付页面,带上订单ID
|
||||
uni.redirectTo({
|
||||
url: `/pages/order/payment?orderId=${order.orderId}&deviceId=${deviceId.value}`
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('检查订单状态失败:', error)
|
||||
uni.showToast({
|
||||
title: $t('order.getOrderStatusFailed'),
|
||||
title: t('order.getOrderStatusFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
@@ -427,7 +431,7 @@
|
||||
return '30分钟'
|
||||
}
|
||||
// 按小时计费(默认)
|
||||
return $t('time.hour')
|
||||
return t('time.hour')
|
||||
}
|
||||
|
||||
// 计算计费单位时间(分钟)
|
||||
@@ -499,7 +503,7 @@
|
||||
const submitRentOrder = async (payWay) => {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: $t('common.processing')
|
||||
title: t('common.processing')
|
||||
})
|
||||
// --- 第一步:先请求订阅消息(必须在用户点击的同步上下文中)---
|
||||
if (payWay === 'wx-score-pay') {
|
||||
@@ -532,7 +536,7 @@
|
||||
// 调用设备租借接口
|
||||
const rentResult = await rentPowerBank(deviceId.value, phoneNumber.value)
|
||||
if (rentResult.code !== 200) {
|
||||
throw new Error(rentResult.msg || $t('device.rentFailed'))
|
||||
throw new Error(rentResult.msg || t('device.rentFailed'))
|
||||
}
|
||||
|
||||
// 获取后端返回的订单信息
|
||||
@@ -587,7 +591,7 @@
|
||||
// 用户取消授权,需要取消订单
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: $t('order.cancelling')
|
||||
title: t('order.cancelling')
|
||||
});
|
||||
const cancelRes = await cancelOrder({
|
||||
orderId: order.orderNo
|
||||
@@ -596,7 +600,7 @@
|
||||
uni.hideLoading();
|
||||
|
||||
uni.showToast({
|
||||
title: $t('order.orderCancelled'),
|
||||
title: t('order.orderCancelled'),
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
@@ -611,7 +615,7 @@
|
||||
console.error('取消订单失败:', cancelError);
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: $t('order.cancelFailedContactService'),
|
||||
title: t('order.cancelFailedContactService'),
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
@@ -622,7 +626,7 @@
|
||||
// 支付分调用异常,也需要取消订单
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: $t('order.cancelling')
|
||||
title: t('order.cancelling')
|
||||
});
|
||||
const cancelRes = await cancelOrder({
|
||||
orderId: order.orderNo
|
||||
@@ -635,7 +639,7 @@
|
||||
}
|
||||
|
||||
uni.showToast({
|
||||
title: $t('device.payScoreFailedCancelled'),
|
||||
title: t('device.payScoreFailedCancelled'),
|
||||
icon: 'none'
|
||||
});
|
||||
|
||||
@@ -647,7 +651,7 @@
|
||||
}
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: res?.msg || $t('device.getPayParamsFailed'),
|
||||
title: res?.msg || t('device.getPayParamsFailed'),
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
@@ -655,7 +659,7 @@
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: error.message || $t('device.rentFailedRetry'),
|
||||
title: error.message || t('device.rentFailedRetry'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -62,11 +62,11 @@
|
||||
} from '@/config/api/expressReturn.js'
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
onMounted(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t('express.fillExpress')
|
||||
title: t('express.fillExpress')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
|
||||
if (!orderId.value) {
|
||||
uni.showToast({
|
||||
title: $t('express.orderNoMissing'),
|
||||
title: t('express.orderNoMissing'),
|
||||
icon: 'none'
|
||||
})
|
||||
setTimeout(() => {
|
||||
@@ -111,7 +111,7 @@
|
||||
const loadOrder = async () => {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: $t('common.loading')
|
||||
title: t('common.loading')
|
||||
})
|
||||
const res = await queryById(orderId.value)
|
||||
if (res?.code === 200 && res.data) {
|
||||
@@ -121,11 +121,11 @@
|
||||
// 默认联系电话可回填订单上的手机号(若有)
|
||||
if (res.data.phone && !phone.value) phone.value = res.data.phone
|
||||
} else {
|
||||
throw new Error(res?.msg || $t('order.getOrderFailed'))
|
||||
throw new Error(res?.msg || t('order.getOrderFailed'))
|
||||
}
|
||||
} catch (e) {
|
||||
uni.showToast({
|
||||
title: e.message || $t('express.loadFailed'),
|
||||
title: e.message || t('express.loadFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
@@ -136,7 +136,7 @@
|
||||
const loadRecordAndOrderByRecord = async () => {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: $t('common.loading')
|
||||
title: t('common.loading')
|
||||
})
|
||||
const res = await getExpressReturnDetail(recordId.value)
|
||||
if (res?.code === 200 && res.data) {
|
||||
@@ -146,11 +146,11 @@
|
||||
}
|
||||
if (res.data.userPhone && !phone.value) phone.value = res.data.userPhone
|
||||
} else {
|
||||
throw new Error(res?.msg || $t('express.getRecordFailed'))
|
||||
throw new Error(res?.msg || t('express.getRecordFailed'))
|
||||
}
|
||||
} catch (e) {
|
||||
uni.showToast({
|
||||
title: e.message || $t('express.loadFailed'),
|
||||
title: e.message || t('express.loadFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
@@ -166,10 +166,10 @@
|
||||
if (rec.status === 0) {
|
||||
recordId.value = rec.id
|
||||
uni.showModal({
|
||||
title: $t('common.tips'),
|
||||
content: $t('express.existingReturnNotice'),
|
||||
confirmText: $t('express.goToFill'),
|
||||
cancelText: $t('common.cancel'),
|
||||
title: t('common.tips'),
|
||||
content: t('express.existingReturnNotice'),
|
||||
confirmText: t('express.goToFill'),
|
||||
cancelText: t('common.cancel'),
|
||||
success: (r) => {
|
||||
if (r.confirm) {
|
||||
uni.redirectTo({
|
||||
@@ -181,7 +181,7 @@
|
||||
return
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: $t('express.alreadyHasRecord'),
|
||||
title: t('express.alreadyHasRecord'),
|
||||
icon: 'none'
|
||||
})
|
||||
setTimeout(() => {
|
||||
@@ -201,14 +201,14 @@
|
||||
const digits = (phone.value || '').replace(/\D/g, '')
|
||||
if (!digits || digits.length < 5) {
|
||||
uni.showToast({
|
||||
title: $t('express.pleaseEnterValidPhone'),
|
||||
title: t('express.pleaseEnterValidPhone'),
|
||||
icon: 'none'
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (isFillMode.value && !trackingNumber.value) {
|
||||
uni.showToast({
|
||||
title: $t('express.pleaseEnterTrackingNo'),
|
||||
title: t('express.pleaseEnterTrackingNo'),
|
||||
icon: 'none'
|
||||
})
|
||||
return false
|
||||
@@ -221,7 +221,7 @@
|
||||
submitting.value = true
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: isFillMode.value ? $t('common.filling') : $t('common.submitting')
|
||||
title: isFillMode.value ? t('common.filling') : t('common.submitting')
|
||||
})
|
||||
let res
|
||||
if (isFillMode.value) {
|
||||
@@ -238,18 +238,18 @@
|
||||
}
|
||||
if (res && res.code === 200) {
|
||||
uni.showToast({
|
||||
title: isFillMode.value ? '补填成功' : '提交成功',
|
||||
title: isFillMode.value ? t('express.fillSuccess') : t('express.submitSuccess'),
|
||||
icon: 'success'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 800)
|
||||
} else {
|
||||
throw new Error(res?.msg || (isFillMode.value ? '补填失败' : '提交失败'))
|
||||
throw new Error(res?.msg || (isFillMode.value ? t('express.fillFailed') : t('express.submitFailed')))
|
||||
}
|
||||
} catch (e) {
|
||||
uni.showToast({
|
||||
title: e.message || (isFillMode.value ? '补填失败' : '提交失败'),
|
||||
title: e.message || (isFillMode.value ? t('express.fillFailed') : t('express.submitFailed')),
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
|
||||
@@ -91,7 +91,7 @@ import { getExpressReturnDetail } from '@/config/api/expressReturn.js'
|
||||
import { getCustomerPhone } from '@/util/index.js'
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
// 详情数据
|
||||
const detailData = ref({
|
||||
@@ -131,21 +131,21 @@ const getStatusIcon = (status) => {
|
||||
// 获取状态文本
|
||||
const getStatusText = (status) => {
|
||||
const textMap = {
|
||||
'completed': $t('express.returnCompleted'),
|
||||
'processing': $t('express.processing'),
|
||||
'pending': $t('express.pending')
|
||||
'completed': t('express.returnCompleted'),
|
||||
'processing': t('express.processing'),
|
||||
'pending': t('express.pending')
|
||||
}
|
||||
return textMap[status] || $t('express.pending')
|
||||
return textMap[status] || t('express.pending')
|
||||
}
|
||||
|
||||
// 获取状态描述
|
||||
const getStatusDesc = (status) => {
|
||||
const descMap = {
|
||||
'completed': $t('express.returnCompletedDesc'),
|
||||
'processing': $t('express.processingDesc'),
|
||||
'pending': $t('express.pendingDesc')
|
||||
'completed': t('express.returnCompletedDesc'),
|
||||
'processing': t('express.processingDesc'),
|
||||
'pending': t('express.pendingDesc')
|
||||
}
|
||||
return descMap[status] || $t('express.pendingDesc')
|
||||
return descMap[status] || t('express.pendingDesc')
|
||||
}
|
||||
|
||||
// 复制运单号
|
||||
@@ -154,7 +154,7 @@ const handleCopyTracking = () => {
|
||||
data: detailData.value.trackingNumber,
|
||||
success: () => {
|
||||
uni.showToast({
|
||||
title: $t('express.trackingNoCopied'),
|
||||
title: t('express.trackingNoCopied'),
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
@@ -165,10 +165,10 @@ const handleCopyTracking = () => {
|
||||
const handleContactService = () => {
|
||||
const customerPhone = getCustomerPhone()
|
||||
uni.showModal({
|
||||
title: $t('user.customerService'),
|
||||
content: `${$t('help.phone')}:${customerPhone}\n${$t('help.workingHours')}:${$t('express.workingHours')}`,
|
||||
confirmText: $t('express.call'),
|
||||
cancelText: $t('common.cancel'),
|
||||
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({
|
||||
@@ -182,7 +182,7 @@ const handleContactService = () => {
|
||||
// 页面加载时获取详情数据
|
||||
onMounted(async () => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t('express.returnDetail')
|
||||
title: t('express.returnDetail')
|
||||
})
|
||||
|
||||
const pages = getCurrentPages()
|
||||
@@ -190,7 +190,7 @@ onMounted(async () => {
|
||||
const options = currentPage.options || {}
|
||||
if (!options.id) return
|
||||
try {
|
||||
uni.showLoading({ title: $t('common.loading') })
|
||||
uni.showLoading({ title: t('common.loading') })
|
||||
const res = await getExpressReturnDetail(options.id)
|
||||
if (res && res.code === 200 && res.data) {
|
||||
const r = res.data
|
||||
@@ -208,10 +208,10 @@ onMounted(async () => {
|
||||
remark: r.remark || ''
|
||||
}
|
||||
} else {
|
||||
throw new Error(res?.msg || $t('express.getDetailFailed'))
|
||||
throw new Error(res?.msg || t('express.getDetailFailed'))
|
||||
}
|
||||
} catch (e) {
|
||||
uni.showToast({ title: e.message || $t('express.loadFailed'), icon: 'none' })
|
||||
uni.showToast({ title: e.message || t('express.loadFailed'), icon: 'none' })
|
||||
} finally {
|
||||
uni.hideLoading()
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ import { ref, onMounted } from 'vue'
|
||||
import { getExpressReturnList } from '@/config/api/expressReturn.js'
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
const returnList = ref([])
|
||||
const loading = ref(false)
|
||||
@@ -62,7 +62,7 @@ const query = ref({ pageNum: 1, pageSize: 20 })
|
||||
|
||||
onMounted(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t('express.returnRecord')
|
||||
title: t('express.returnRecord')
|
||||
})
|
||||
loadList()
|
||||
})
|
||||
@@ -80,12 +80,12 @@ const loadList = async () => {
|
||||
const rows = (res.data && (res.data.rows || res.data)) || []
|
||||
returnList.value = rows.map(r => ({
|
||||
id: r.id,
|
||||
expressCompany: r.expressCompany || r.company || '待填写',
|
||||
trackingNumber: r.logisticsTrackingNumber || r.trackingNumber || '待填写',
|
||||
returnAddress: r.returnAddress || r.address || '待填写',
|
||||
returnTime: r.expressFillTime || r.createTime || r.returnTime || '待填写',
|
||||
packageType: r.packageType || '待填写',
|
||||
weight: r.weight || '待填写',
|
||||
expressCompany: r.expressCompany || r.company || t('express.toFill'),
|
||||
trackingNumber: r.logisticsTrackingNumber || r.trackingNumber || t('express.toFill'),
|
||||
returnAddress: r.returnAddress || r.address || t('express.toFill'),
|
||||
returnTime: r.expressFillTime || r.createTime || r.returnTime || t('express.toFill'),
|
||||
packageType: r.packageType || t('express.toFill'),
|
||||
weight: r.weight || t('express.toFill'),
|
||||
status: mapStatus(r.status),
|
||||
rawStatus: r.status,
|
||||
userPhone: r.userPhone,
|
||||
@@ -93,10 +93,10 @@ const loadList = async () => {
|
||||
remark: r.remark
|
||||
}))
|
||||
} else {
|
||||
throw new Error(res?.msg || $t('express.getListFailed'))
|
||||
throw new Error(res?.msg || t('express.getListFailed'))
|
||||
}
|
||||
} catch (e) {
|
||||
uni.showToast({ title: e.message || $t('express.loadFailed'), icon: 'none' })
|
||||
uni.showToast({ title: e.message || t('express.loadFailed'), icon: 'none' })
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -118,34 +118,33 @@ const getStatusClass = (status) => ({
|
||||
}[status] || 'status-pending')
|
||||
|
||||
const getStatusText = (status) => ({
|
||||
'completed': $t('express.billingPaused'),
|
||||
'processing': $t('express.billingPaused'),
|
||||
'pending': $t('express.billingPaused')
|
||||
}[status] || $t('express.billingPaused'))
|
||||
'completed': t('express.billingPaused'),
|
||||
'processing': t('express.billingPaused'),
|
||||
'pending': t('express.billingPaused')
|
||||
}[status] || t('express.billingPaused'))
|
||||
|
||||
const getStatusBadge = (status) => ({
|
||||
'completed': $t('express.completed'),
|
||||
'processing': $t('express.processing'),
|
||||
'pending': $t('express.pending')
|
||||
}[status] || $t('express.pending'))
|
||||
'completed': t('express.completed'),
|
||||
'processing': t('express.processing'),
|
||||
'pending': t('express.pending')
|
||||
}[status] || t('express.pending'))
|
||||
|
||||
// 一键复制全部信息
|
||||
const copyAllInfo = () => {
|
||||
const allInfo = `${$t('express.recipient')}:${recipientName}\n${$t('express.recipientAddressLabel')}:${recipientAddress}`
|
||||
const allInfo = `${t('express.recipient')}:${recipientName}\n${t('express.recipientAddressLabel')}:${recipientAddress}`
|
||||
uni.setClipboardData({
|
||||
data: allInfo,
|
||||
success: () => {
|
||||
uni.showToast({ title: $t('express.copySuccess'), icon: 'success' })
|
||||
uni.showToast({ title: t('express.copySuccess'), icon: 'success' })
|
||||
},
|
||||
fail: () => {
|
||||
uni.showToast({ title: $t('express.copyFailed'), icon: 'none' })
|
||||
uni.showToast({ title: t('express.copyFailed'), icon: 'none' })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 点击列表项
|
||||
const handleItemClick = (item) => {
|
||||
console.log('点击了归还记录:', item)
|
||||
// 未填写(status=0 -> mapped 'pending')时跳转到补填页,其它跳详情
|
||||
if (item && item.rawStatus === 0) {
|
||||
uni.navigateTo({ url: `/pages/expressReturn/addExpressReturn?id=${item.id}` })
|
||||
|
||||
+17
-17
@@ -98,13 +98,13 @@
|
||||
} from '@/utils/i18n.js'
|
||||
|
||||
const {
|
||||
t: $t
|
||||
t
|
||||
} = useI18n()
|
||||
|
||||
// 设置页面标题
|
||||
onMounted(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t('feedback.detail')
|
||||
title: t('feedback.detail')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
await loadDetail();
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: $t('feedback.idRequired'),
|
||||
title: t('feedback.idRequired'),
|
||||
icon: 'none'
|
||||
});
|
||||
setTimeout(() => {
|
||||
@@ -170,7 +170,7 @@
|
||||
try {
|
||||
if (shouldShowLoading) {
|
||||
uni.showLoading({
|
||||
title: $t('common.loading')
|
||||
title: t('common.loading')
|
||||
});
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
await loadMessages(res.data.messages);
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: res.msg || $t('feedback.getDetailFailed'),
|
||||
title: res.msg || t('feedback.getDetailFailed'),
|
||||
icon: 'none'
|
||||
});
|
||||
setTimeout(() => {
|
||||
@@ -190,7 +190,7 @@
|
||||
} catch (error) {
|
||||
console.error('获取投诉详情失败:', error);
|
||||
uni.showToast({
|
||||
title: $t('feedback.getDetailFailed'),
|
||||
title: t('feedback.getDetailFailed'),
|
||||
icon: 'none'
|
||||
});
|
||||
setTimeout(() => {
|
||||
@@ -207,7 +207,7 @@
|
||||
const submitReply = async () => {
|
||||
if (!replyContent.value.trim()) {
|
||||
uni.showToast({
|
||||
title: $t('feedback.pleaseEnterReply'),
|
||||
title: t('feedback.pleaseEnterReply'),
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
@@ -215,7 +215,7 @@
|
||||
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: $t('common.submitting')
|
||||
title: t('common.submitting')
|
||||
});
|
||||
|
||||
const res = await sendFeedbackMessage(feedbackId.value, {
|
||||
@@ -224,7 +224,7 @@
|
||||
|
||||
if (res.code === 200) {
|
||||
uni.showToast({
|
||||
title: $t('feedback.replySuccess'),
|
||||
title: t('feedback.replySuccess'),
|
||||
icon: 'success'
|
||||
});
|
||||
replyContent.value = '';
|
||||
@@ -234,14 +234,14 @@
|
||||
});
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: res.msg || $t('feedback.replyFailed'),
|
||||
title: res.msg || t('feedback.replyFailed'),
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提交回复失败:', error);
|
||||
uni.showToast({
|
||||
title: $t('feedback.replyFailed'),
|
||||
title: t('feedback.replyFailed'),
|
||||
icon: 'none'
|
||||
});
|
||||
} finally {
|
||||
@@ -252,11 +252,11 @@
|
||||
// 获取状态文本
|
||||
const getStatusText = (status) => {
|
||||
const statusMap = {
|
||||
'pending': $t('feedback.pending'),
|
||||
'in_progress': $t('feedback.processing'),
|
||||
'resolved': $t('feedback.completed')
|
||||
'pending': t('feedback.pending'),
|
||||
'in_progress': t('feedback.processing'),
|
||||
'resolved': t('feedback.completed')
|
||||
};
|
||||
return statusMap[status] || $t('feedback.pending');
|
||||
return statusMap[status] || t('feedback.pending');
|
||||
};
|
||||
|
||||
// 获取状态样式类
|
||||
@@ -272,8 +272,8 @@
|
||||
// 获取类型文本
|
||||
const getTypeText = (type) => {
|
||||
const typeMap = {
|
||||
'complain': $t('feedback.complain'),
|
||||
'suggestion': $t('feedback.suggestion')
|
||||
'complain': t('feedback.complain'),
|
||||
'suggestion': t('feedback.suggestion')
|
||||
};
|
||||
return typeMap[type] || type || '-';
|
||||
};
|
||||
|
||||
+10
-10
@@ -80,7 +80,7 @@
|
||||
useI18n
|
||||
} from '@/utils/i18n.js'
|
||||
const {
|
||||
t: $t
|
||||
t
|
||||
} = useI18n()
|
||||
|
||||
// 跳转到投诉记录列表
|
||||
@@ -92,7 +92,7 @@
|
||||
|
||||
onMounted(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t('feedback.title')
|
||||
title: t('feedback.title')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
const submitFeedback = async () => {
|
||||
if (selectedType.value === -1) {
|
||||
uni.showToast({
|
||||
title: $t('feedback.pleaseSelectType'),
|
||||
title: t('feedback.pleaseSelectType'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
@@ -145,7 +145,7 @@
|
||||
|
||||
if (!description.value.trim()) {
|
||||
uni.showToast({
|
||||
title: $t('feedback.pleaseDescribe'),
|
||||
title: t('feedback.pleaseDescribe'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
@@ -153,7 +153,7 @@
|
||||
|
||||
if (!contact.value) {
|
||||
uni.showToast({
|
||||
title: $t('feedback.pleaseContact'),
|
||||
title: t('feedback.pleaseContact'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
@@ -169,7 +169,7 @@
|
||||
try {
|
||||
// 显示上传进度
|
||||
uni.showLoading({
|
||||
title: $t('feedback.uploading') || '上传中...',
|
||||
title: t('feedback.uploading') || '上传中...',
|
||||
mask: true
|
||||
})
|
||||
|
||||
@@ -185,7 +185,7 @@
|
||||
console.error(`文件 ${i + 1} 上传失败:`, err)
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: $t('feedback.imageUploadFailed'),
|
||||
title: t('feedback.imageUploadFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
@@ -208,7 +208,7 @@
|
||||
// 处理响应
|
||||
if (res && (res.code === 200 || res === true || res?.success === true)) {
|
||||
uni.showToast({
|
||||
title: $t('feedback.submitSuccess'),
|
||||
title: t('feedback.submitSuccess'),
|
||||
icon: 'success'
|
||||
})
|
||||
setTimeout(() => {
|
||||
@@ -216,7 +216,7 @@
|
||||
}, 1500);
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: (res && (res.msg || res.message)) || $t('feedback.submitFailed'),
|
||||
title: (res && (res.msg || res.message)) || t('feedback.submitFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
@@ -224,7 +224,7 @@
|
||||
console.error('feedback submit failed:', err)
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: $t('error.networkError') || '网络错误,请重试',
|
||||
title: t('error.networkError') || '网络错误,请重试',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
+14
-14
@@ -78,13 +78,13 @@
|
||||
} from '@/utils/i18n.js'
|
||||
|
||||
const {
|
||||
t: $t
|
||||
t
|
||||
} = useI18n()
|
||||
|
||||
// 设置页面标题
|
||||
onMounted(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t('feedback.recordList')
|
||||
title: t('feedback.recordList')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -100,25 +100,25 @@
|
||||
// 状态标签
|
||||
const statusTabs = reactive([{
|
||||
get text() {
|
||||
return $t('common.all')
|
||||
return t('common.all')
|
||||
},
|
||||
status: ''
|
||||
},
|
||||
{
|
||||
get text() {
|
||||
return $t('feedback.pending')
|
||||
return t('feedback.pending')
|
||||
},
|
||||
status: 'pending'
|
||||
},
|
||||
{
|
||||
get text() {
|
||||
return $t('feedback.processing')
|
||||
return t('feedback.processing')
|
||||
},
|
||||
status: 'in_progress'
|
||||
},
|
||||
{
|
||||
get text() {
|
||||
return $t('feedback.completed')
|
||||
return t('feedback.completed')
|
||||
},
|
||||
status: 'resolved'
|
||||
}
|
||||
@@ -176,14 +176,14 @@
|
||||
}
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: res.msg || $t('feedback.getListFailed'),
|
||||
title: res.msg || t('feedback.getListFailed'),
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取投诉列表失败:', error);
|
||||
uni.showToast({
|
||||
title: $t('feedback.getListFailed'),
|
||||
title: t('feedback.getListFailed'),
|
||||
icon: 'none'
|
||||
});
|
||||
} finally {
|
||||
@@ -211,11 +211,11 @@
|
||||
// 获取状态文本
|
||||
const getStatusText = (status) => {
|
||||
const statusMap = {
|
||||
'pending': $t('feedback.pending'),
|
||||
'in_progress': $t('feedback.processing'),
|
||||
'resolved': $t('feedback.completed')
|
||||
'pending': t('feedback.pending'),
|
||||
'in_progress': t('feedback.processing'),
|
||||
'resolved': t('feedback.completed')
|
||||
};
|
||||
return statusMap[status] || $t('feedback.pending');
|
||||
return statusMap[status] || t('feedback.pending');
|
||||
};
|
||||
|
||||
// 获取状态样式类
|
||||
@@ -231,8 +231,8 @@
|
||||
// 获取类型文本
|
||||
const getTypeText = (type) => {
|
||||
const typeMap = {
|
||||
'complain': $t('feedback.complain'),
|
||||
'suggestion': $t('feedback.suggestion')
|
||||
'complain': t('feedback.complain'),
|
||||
'suggestion': t('feedback.suggestion')
|
||||
};
|
||||
return typeMap[type] || type || '-';
|
||||
};
|
||||
|
||||
+21
-25
@@ -26,40 +26,36 @@
|
||||
</view>
|
||||
<view class="contact-item">
|
||||
<text class="label">{{ $t('help.workingHours') }}</text>
|
||||
<text class="value">{{ HELP_CONTENT.CONTACT.SERVICE_TIME.VALUE }}</text>
|
||||
<text class="value">{{ $t('help.workingHoursValue') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { HELP_CONTENT } from '@/constants/help'
|
||||
import { getCustomerPhone } from '@/util/index.js'
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
HELP_CONTENT,
|
||||
faqList: HELP_CONTENT.FAQ_LIST,
|
||||
customerPhone: HELP_CONTENT.CONTACT.PHONE.VALUE // 默认客服电话
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
// 设置页面标题
|
||||
uni.setNavigationBarTitle({
|
||||
title: this.$t('help.title')
|
||||
})
|
||||
// 从缓存读取客服电话
|
||||
this.customerPhone = getCustomerPhone()
|
||||
},
|
||||
methods: {
|
||||
makePhoneCall() {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: this.customerPhone
|
||||
})
|
||||
}
|
||||
}
|
||||
const { t } = useI18n()
|
||||
|
||||
const faqList = ref(HELP_CONTENT.FAQ_LIST)
|
||||
const customerPhone = ref(HELP_CONTENT.CONTACT.PHONE.VALUE)
|
||||
|
||||
onLoad(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: t('help.title')
|
||||
})
|
||||
customerPhone.value = getCustomerPhone()
|
||||
})
|
||||
|
||||
const makePhoneCall = () => {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: customerPhone.value
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
+45
-87
@@ -198,10 +198,13 @@
|
||||
transformDeviceData
|
||||
} from '../../config/api/device.js'
|
||||
import {
|
||||
getPotionsDetail
|
||||
getInUseOrder,
|
||||
getUnpaidOrder
|
||||
} from '../../config/api/order.js'
|
||||
import {
|
||||
getActiveActivity
|
||||
getActiveActivity,
|
||||
getCurrentAnnouncement,
|
||||
getCurrentAdvertisement
|
||||
} from '../../config/api/system.js'
|
||||
// 导入地图工具函数
|
||||
import {
|
||||
@@ -227,7 +230,7 @@
|
||||
// #endif
|
||||
|
||||
const {
|
||||
t: $t
|
||||
t
|
||||
} = useI18n()
|
||||
|
||||
// 响应式数据
|
||||
@@ -294,39 +297,21 @@
|
||||
const bannerImages = ref([]) // 首页广告图片列表
|
||||
const bannerImageList = ref([]) // 完整的广告配置列表(包含链接信息)
|
||||
|
||||
// 将语言代码转换为后端接受的格式
|
||||
const convertLanguageCode = (lang) => {
|
||||
// zh-CN -> zh_CN (转换下划线)
|
||||
// en-US -> en_US (转换下划线)
|
||||
return lang.replace(/-/g, '_')
|
||||
}
|
||||
|
||||
// 获取公告内容(支持多语言)
|
||||
const getNoticeText = async () => {
|
||||
try {
|
||||
// 获取当前语言设置
|
||||
const currentLang = uni.getStorageSync('language') || 'zh-CN'
|
||||
const languageCode = convertLanguageCode(currentLang)
|
||||
|
||||
console.log('加载公告,语言:', currentLang, '转换后:', languageCode)
|
||||
console.log('加载公告')
|
||||
|
||||
// 调用接口获取公告内容
|
||||
const res = await uni.request({
|
||||
url: `${URL}/device/announcementConfig/current`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Content-Language': languageCode
|
||||
},
|
||||
data: {
|
||||
type: 'wx_user_type' // 微信小程序用户端
|
||||
}
|
||||
const res = await getCurrentAnnouncement({
|
||||
type: 'wx_user_type' // 微信小程序用户端
|
||||
})
|
||||
|
||||
console.log('公告响应:', res)
|
||||
|
||||
if (res.statusCode === 200 && res.data.code === 200 && res.data.data) {
|
||||
if (res && res.code === 200 && res.data) {
|
||||
// 使用后端自动解析的 announcement 字段
|
||||
const announcement = res.data.data.announcement || ''
|
||||
const announcement = res.data.announcement || ''
|
||||
noticeText.value = announcement
|
||||
|
||||
// 设置通知栏高度
|
||||
@@ -342,7 +327,7 @@
|
||||
console.warn('缓存通知内容失败:', e)
|
||||
}
|
||||
} else {
|
||||
console.warn('获取公告失败:', res.data?.msg || '未知错误')
|
||||
console.warn('获取公告失败:', res?.msg || '未知错误')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取公告失败:', error)
|
||||
@@ -352,31 +337,20 @@
|
||||
// 获取首页广告图片(支持多语言)
|
||||
const getBannerImages = async () => {
|
||||
try {
|
||||
// 获取当前语言设置
|
||||
const currentLang = uni.getStorageSync('language') || 'zh-CN'
|
||||
const languageCode = convertLanguageCode(currentLang)
|
||||
|
||||
console.log('加载首页广告,语言:', currentLang, '转换后:', languageCode)
|
||||
console.log('加载首页广告')
|
||||
|
||||
// 调用接口获取广告内容
|
||||
const res = await uni.request({
|
||||
url: `${URL}/device/advertisementConfig/current`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Content-Language': languageCode
|
||||
},
|
||||
data: {
|
||||
appPlatform: 'wechat', // 微信平台
|
||||
appType: 'user' ,// 用户端
|
||||
pictureLocation:'home_banner'
|
||||
}
|
||||
const res = await getCurrentAdvertisement({
|
||||
appPlatform: 'wechat', // 微信平台
|
||||
appType: 'user' ,// 用户端
|
||||
pictureLocation:'home_banner'
|
||||
})
|
||||
|
||||
console.log('首页广告响应:', res)
|
||||
|
||||
if (res.statusCode === 200 && res.data.code === 200 && res.data.data) {
|
||||
if (res && res.code === 200 && res.data) {
|
||||
// 使用 imageList 字段(包含图片和链接信息)
|
||||
const imageList = res.data.data.imageList || []
|
||||
const imageList = res.data.imageList || []
|
||||
if (imageList.length > 0) {
|
||||
bannerImageList.value = imageList
|
||||
// 提取图片URL用于展示
|
||||
@@ -385,7 +359,7 @@
|
||||
console.warn('未获取到广告图片')
|
||||
}
|
||||
} else {
|
||||
console.warn('获取首页广告失败:', res.data?.msg || '未知错误')
|
||||
console.warn('获取首页广告失败:', res?.msg || '未知错误')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取首页广告失败:', error)
|
||||
@@ -415,7 +389,7 @@
|
||||
fail: (err) => {
|
||||
console.error('跳转小程序失败:', err)
|
||||
uni.showToast({
|
||||
title: '跳转失败',
|
||||
title: t('common.loadFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
@@ -423,7 +397,7 @@
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
uni.showToast({
|
||||
title: '请在微信小程序中使用',
|
||||
title: t('auth.pleaseUseInWechat'),
|
||||
icon: 'none'
|
||||
})
|
||||
// #endif
|
||||
@@ -568,10 +542,10 @@
|
||||
console.warn('清理旧缓存失败:', e);
|
||||
}
|
||||
|
||||
// 开发环境测试距离计算
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
testDistanceCalculation()
|
||||
}
|
||||
// // 开发环境测试距离计算
|
||||
// if (process.env.NODE_ENV === 'development') {
|
||||
// testDistanceCalculation()
|
||||
// }
|
||||
|
||||
// 并行加载公告和广告(不依赖定位)
|
||||
await Promise.all([
|
||||
@@ -586,12 +560,12 @@
|
||||
await loadPositions()
|
||||
|
||||
// 3. 查询活动并显示弹窗
|
||||
await checkActiveActivity()
|
||||
// await checkActiveActivity()
|
||||
|
||||
} catch (error) {
|
||||
console.error('初始化失败:', error)
|
||||
uni.showToast({
|
||||
title: $t('home.getLocationFailed'),
|
||||
title: t('home.getLocationFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
@@ -787,12 +761,16 @@
|
||||
isRelocating.value = true
|
||||
|
||||
uni.showLoading({
|
||||
title: $t('home.relocating'),
|
||||
title: t('home.relocating'),
|
||||
mask: true
|
||||
})
|
||||
|
||||
// 重新获取用户真实位置(不使用缓存)
|
||||
// 重新获取用户真实位置
|
||||
const loc = await getUserLocation()
|
||||
if (!loc || !loc.longitude || !loc.latitude) {
|
||||
throw new Error('location failed')
|
||||
}
|
||||
|
||||
const newLocation = {
|
||||
longitude: Number(loc.longitude),
|
||||
latitude: Number(loc.latitude)
|
||||
@@ -827,7 +805,7 @@
|
||||
uni.hideLoading()
|
||||
|
||||
uni.showToast({
|
||||
title: $t('home.locateSuccess'),
|
||||
title: t('home.locateSuccess'),
|
||||
icon: 'success',
|
||||
duration: 1500
|
||||
})
|
||||
@@ -836,7 +814,7 @@
|
||||
uni.hideLoading()
|
||||
|
||||
uni.showToast({
|
||||
title: e.errMsg || $t('home.locateFailed'),
|
||||
title: e.errMsg || t('home.locateFailed'),
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
@@ -868,7 +846,7 @@
|
||||
|
||||
const selectPosition = (position) => {
|
||||
uni.showActionSheet({
|
||||
itemList: ['扫码使用', '导航前往'],
|
||||
itemList: [t('home.scanToUse'), t('home.navigate')],
|
||||
success: (res) => {
|
||||
switch (res.tapIndex) {
|
||||
case 0:
|
||||
@@ -948,27 +926,17 @@
|
||||
|
||||
if (!deviceNo) {
|
||||
uni.showToast({
|
||||
title: $t('home.invalidQRCode'),
|
||||
title: t('home.invalidQRCode'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否有使用中的订单
|
||||
const inUseRes = await uni.request({
|
||||
url: `${URL}/app/order/inUse`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Authorization': "Bearer " + uni.getStorageSync('token'),
|
||||
'Clientid': uni.getStorageSync('client_id')
|
||||
}
|
||||
})
|
||||
const inUseRes = await getInUseOrder()
|
||||
|
||||
if (inUseRes.statusCode === 401 || inUseRes.data?.code === 401 || inUseRes.data?.code === 40101) {
|
||||
redirectToLogin()
|
||||
return
|
||||
} else if (inUseRes.statusCode == 200 && inUseRes.data.code == 200 && inUseRes.data.data) {
|
||||
const inUseOrder = inUseRes.data.data
|
||||
if (inUseRes && inUseRes.code === 200 && inUseRes.data) {
|
||||
const inUseOrder = inUseRes.data
|
||||
uni.reLaunch({
|
||||
url: `/pages/order/detail?orderId=${inUseOrder.orderId}&deviceId=${deviceNo || inUseOrder.deviceNo}`
|
||||
})
|
||||
@@ -976,20 +944,10 @@
|
||||
}
|
||||
|
||||
// 检查是否有待支付订单
|
||||
const orderRes = await uni.request({
|
||||
url: `${URL}/app/order/unpaid`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Authorization': "Bearer " + uni.getStorageSync('token'),
|
||||
'Clientid': uni.getStorageSync('client_id')
|
||||
}
|
||||
})
|
||||
const orderRes = await getUnpaidOrder()
|
||||
|
||||
if (orderRes.statusCode === 401 || orderRes.data?.code === 401 || orderRes.data?.code === 40101) {
|
||||
redirectToLogin()
|
||||
return
|
||||
} else if (orderRes.statusCode == 200 && orderRes.data.code == 200 && orderRes.data.data) {
|
||||
const unpaidOrder = orderRes.data.data
|
||||
if (orderRes && orderRes.code === 200 && orderRes.data) {
|
||||
const unpaidOrder = orderRes.data
|
||||
uni.navigateTo({
|
||||
url: `/pages/order/payment?orderId=${unpaidOrder.orderId}`
|
||||
})
|
||||
@@ -1018,7 +976,7 @@
|
||||
}
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '获取设备信息失败',
|
||||
title: t('device.getDeviceInfoFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
uni.navigateTo({
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
} from 'vue'
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
// 外部网页地址
|
||||
const webUrl = ref('https://joininvestment.gxfs123.com/')
|
||||
@@ -27,7 +27,7 @@
|
||||
const handleError = (e) => {
|
||||
console.error('web-view 加载错误:', e)
|
||||
uni.showToast({
|
||||
title: $t('join.pageLoadFailed'),
|
||||
title: t('join.pageLoadFailed'),
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
onMounted(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t('join.title')
|
||||
title: t('join.title')
|
||||
})
|
||||
console.log('招商页面加载,外部网址:', webUrl.value)
|
||||
})
|
||||
|
||||
+13
-33
@@ -36,9 +36,9 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
import { URL } from '@/config/url.js'
|
||||
import { getCurrentAgreement } from '@/config/api/system.js'
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(true)
|
||||
@@ -50,13 +50,6 @@
|
||||
remark: ''
|
||||
})
|
||||
|
||||
// 将语言代码转换为后端接受的格式
|
||||
const convertLanguageCode = (lang) => {
|
||||
// zh-CN -> zh-CN (保持不变)
|
||||
// en-US -> en_US (转换下划线)
|
||||
return lang.replace(/-/g, '_')
|
||||
}
|
||||
|
||||
// 加载协议内容
|
||||
const loadAgreement = async () => {
|
||||
loading.value = true
|
||||
@@ -64,35 +57,22 @@
|
||||
errorMessage.value = ''
|
||||
|
||||
try {
|
||||
// 获取当前语言设置
|
||||
const currentLang = uni.getStorageSync('language') || 'zh-CN'
|
||||
const languageCode = convertLanguageCode(currentLang)
|
||||
|
||||
console.log('加载用户协议,语言:', currentLang, '转换后:', languageCode)
|
||||
console.log('加载用户协议')
|
||||
|
||||
// 调用接口获取协议内容
|
||||
const res = await uni.request({
|
||||
url: `${URL}/device/agreementConfig/current`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Content-Language': languageCode,
|
||||
'Authorization': 'Bearer ' + uni.getStorageSync('token'),
|
||||
'Clientid': uni.getStorageSync('client_id')
|
||||
},
|
||||
data: {
|
||||
agreementCode: 'USER_AGREEMENT',
|
||||
appPlatform: 'wechat',
|
||||
appType: 'user'
|
||||
}
|
||||
const res = await getCurrentAgreement({
|
||||
agreementCode: 'USER_AGREEMENT',
|
||||
appPlatform: 'wechat',
|
||||
appType: 'user'
|
||||
})
|
||||
|
||||
console.log('用户协议响应:', res)
|
||||
|
||||
if (res.statusCode === 200 && res.data.code === 200 && res.data.data) {
|
||||
if (res && res.code === 200 && res.data) {
|
||||
agreementData.value = {
|
||||
title: res.data.data.title || $t('legal.agreement'),
|
||||
content: res.data.data.content || '',
|
||||
remark: res.data.data.remark || ''
|
||||
title: res.data.title || t('legal.agreement'),
|
||||
content: res.data.content || '',
|
||||
remark: res.data.remark || ''
|
||||
}
|
||||
|
||||
// 设置页面标题
|
||||
@@ -100,12 +80,12 @@
|
||||
title: agreementData.value.title
|
||||
})
|
||||
} else {
|
||||
throw new Error(res.data.msg || $t('common.loadFailed'))
|
||||
throw new Error(res?.msg || t('common.loadFailed'))
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('加载用户协议失败:', err)
|
||||
error.value = true
|
||||
errorMessage.value = err.message || $t('common.loadFailed')
|
||||
errorMessage.value = err.message || t('common.loadFailed')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
+13
-33
@@ -36,9 +36,9 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
import { URL } from '@/config/url.js'
|
||||
import { getCurrentAgreement } from '@/config/api/system.js'
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(true)
|
||||
@@ -50,13 +50,6 @@
|
||||
remark: ''
|
||||
})
|
||||
|
||||
// 将语言代码转换为后端接受的格式
|
||||
const convertLanguageCode = (lang) => {
|
||||
// zh-CN -> zh-CN (保持不变)
|
||||
// en-US -> en_US (转换下划线)
|
||||
return lang.replace(/-/g, '_')
|
||||
}
|
||||
|
||||
// 加载协议内容
|
||||
const loadAgreement = async () => {
|
||||
loading.value = true
|
||||
@@ -64,35 +57,22 @@
|
||||
errorMessage.value = ''
|
||||
|
||||
try {
|
||||
// 获取当前语言设置
|
||||
const currentLang = uni.getStorageSync('language') || 'zh-CN'
|
||||
const languageCode = convertLanguageCode(currentLang)
|
||||
|
||||
console.log('加载隐私政策,语言:', currentLang, '转换后:', languageCode)
|
||||
console.log('加载隐私政策')
|
||||
|
||||
// 调用接口获取协议内容
|
||||
const res = await uni.request({
|
||||
url: `${URL}/device/agreementConfig/current`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Content-Language': languageCode,
|
||||
'Authorization': 'Bearer ' + uni.getStorageSync('token'),
|
||||
'Clientid': uni.getStorageSync('client_id')
|
||||
},
|
||||
data: {
|
||||
agreementCode: 'PRIVACY_POLICY',
|
||||
appPlatform: 'wechat',
|
||||
appType: 'user'
|
||||
}
|
||||
const res = await getCurrentAgreement({
|
||||
agreementCode: 'PRIVACY_POLICY',
|
||||
appPlatform: 'wechat',
|
||||
appType: 'user'
|
||||
})
|
||||
|
||||
console.log('隐私政策响应:', res)
|
||||
|
||||
if (res.statusCode === 200 && res.data.code === 200 && res.data.data) {
|
||||
if (res && res.code === 200 && res.data) {
|
||||
agreementData.value = {
|
||||
title: res.data.data.title || $t('legal.privacy'),
|
||||
content: res.data.data.content || '',
|
||||
remark: res.data.data.remark || ''
|
||||
title: res.data.title || t('legal.privacy'),
|
||||
content: res.data.content || '',
|
||||
remark: res.data.remark || ''
|
||||
}
|
||||
|
||||
// 设置页面标题
|
||||
@@ -100,12 +80,12 @@
|
||||
title: agreementData.value.title
|
||||
})
|
||||
} else {
|
||||
throw new Error(res.data.msg || $t('common.loadFailed'))
|
||||
throw new Error(res?.msg || t('common.loadFailed'))
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('加载隐私政策失败:', err)
|
||||
error.value = true
|
||||
errorMessage.value = err.message || $t('common.loadFailed')
|
||||
errorMessage.value = err.message || t('common.loadFailed')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
+28
-17
@@ -16,8 +16,10 @@
|
||||
{{ $t('auth.getPhoneNumber') }}
|
||||
</button>
|
||||
|
||||
<!-- 仅微信登录(不授权手机号时使用) -->
|
||||
<!-- <button class="btn outline" @click="onWeChatLogin">仅微信登录</button> -->
|
||||
<!-- 手机号验证码登录 -->
|
||||
<button class="btn outline" @click="goToPhoneLogin" v-if="isHTML5">
|
||||
{{ $t('auth.phoneLogin') }}
|
||||
</button>
|
||||
|
||||
<view class="agreement-box">
|
||||
<checkbox-group @change="onAgreementChange">
|
||||
@@ -41,22 +43,23 @@
|
||||
import { wxLogin, getUserPhoneNumber, getUserInfo } from '../../util/index.js'
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
// 设置页面标题
|
||||
onMounted(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t('auth.loginTitle')
|
||||
title: t('auth.loginTitle')
|
||||
})
|
||||
})
|
||||
|
||||
const isHTML5 = ref(false) // 是否是HTML5模式
|
||||
|
||||
const redirect = ref('/pages/index/index')
|
||||
const isAgreed = ref(false) // 是否同意协议
|
||||
|
||||
// 勾选协议变化
|
||||
const onAgreementChange = (e) => {
|
||||
isAgreed.value = e.detail.value.includes('agreed')
|
||||
console.log('协议勾选状态:', isAgreed.value, e.detail.value)
|
||||
}
|
||||
|
||||
// 未勾选协议时点击登录按钮
|
||||
@@ -79,10 +82,10 @@
|
||||
|
||||
// 未勾选,弹窗提示
|
||||
uni.showModal({
|
||||
title: $t('common.tips'),
|
||||
content: $t('auth.pleaseAgreeToTerms'),
|
||||
confirmText: $t('common.confirm'),
|
||||
cancelText: $t('common.cancel'),
|
||||
title: t('common.tips'),
|
||||
content: t('auth.pleaseAgreeToTerms'),
|
||||
confirmText: t('common.confirm'),
|
||||
cancelText: t('common.cancel'),
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 用户点击同意,自动勾选
|
||||
@@ -90,7 +93,7 @@
|
||||
resolve()
|
||||
} else {
|
||||
// 用户点击取消
|
||||
reject(new Error('需要同意协议才能登录'))
|
||||
reject(new Error(t('auth.pleaseAgreeToTerms')))
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -119,18 +122,18 @@
|
||||
await checkAgreement()
|
||||
|
||||
await wxLogin()
|
||||
uni.showToast({ title: $t('auth.loginSuccess'), icon: 'success' })
|
||||
uni.showToast({ title: t('auth.loginSuccess'), icon: 'success' })
|
||||
await navigateAfterLogin()
|
||||
} catch (error) {
|
||||
if (error.message !== '需要同意协议才能登录') {
|
||||
uni.showToast({ title: error.message || '登录失败', icon: 'none' })
|
||||
if (error.message !== t('auth.pleaseAgreeToTerms')) {
|
||||
uni.showToast({ title: error.message || t('auth.loginFailed'), icon: 'none' })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onGetPhoneNumber = async (e) => {
|
||||
if (!e || e.detail.errMsg !== 'getPhoneNumber:ok') {
|
||||
uni.showToast({ title: $t('auth.phoneCancelled'), icon: 'none' })
|
||||
uni.showToast({ title: t('auth.phoneCancelled'), icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
@@ -139,10 +142,10 @@
|
||||
await wxLogin()
|
||||
// 再用微信返回的临时 code 换取手机号
|
||||
await getUserPhoneNumber(e.detail.code)
|
||||
uni.showToast({ title: $t('auth.loginSuccess'), icon: 'success' })
|
||||
uni.showToast({ title: t('auth.loginSuccess'), icon: 'success' })
|
||||
await navigateAfterLogin()
|
||||
} catch (error) {
|
||||
uni.showToast({ title: error.message || $t('auth.loginFailed'), icon: 'none' })
|
||||
uni.showToast({ title: error.message || t('auth.loginFailed'), icon: 'none' })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,17 +155,25 @@
|
||||
redirect.value = decodeURIComponent(opts.redirect)
|
||||
} catch (_) {}
|
||||
}
|
||||
// #ifdef H5
|
||||
isHTML5.value = true
|
||||
// #endif
|
||||
})
|
||||
|
||||
const go = (url) => {
|
||||
uni.navigateTo({ url })
|
||||
}
|
||||
|
||||
// 跳转到手机号登录页面
|
||||
const goToPhoneLogin = () => {
|
||||
uni.navigateTo({ url: '/pages/login/phone' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.login-container {
|
||||
min-height: 100vh;
|
||||
background: #f8f8f8;
|
||||
background: linear-gradient(180deg, #C8F4D9 0%, #FFFFFF 100%);
|
||||
padding: 80rpx 40rpx 40rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -0,0 +1,394 @@
|
||||
<template>
|
||||
<view class="login-container">
|
||||
<view class="header">
|
||||
<view class="title">Hello,</view>
|
||||
<view class="subtitle">{{ $t('app.welcome') }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 国家区号选择器 -->
|
||||
<view class="form-group">
|
||||
<view class="phone-input-wrapper">
|
||||
<view class="country-code" @click="showCountryPicker">
|
||||
<text>{{ countryCode }}</text>
|
||||
<text class="arrow">▼</text>
|
||||
</view>
|
||||
<view class="divider"></view>
|
||||
<input
|
||||
class="phone-input"
|
||||
v-model="phone"
|
||||
type="number"
|
||||
maxlength="11"
|
||||
:placeholder="$t('auth.phonePlaceholder')"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 验证码输入 -->
|
||||
<view class="form-group">
|
||||
<view class="code-input-wrapper">
|
||||
<input
|
||||
class="code-input"
|
||||
v-model="verifyCode"
|
||||
type="number"
|
||||
maxlength="6"
|
||||
:placeholder="$t('auth.codePlaceholder')"
|
||||
/>
|
||||
<view class="code-btn" @click="handleSendCode" :class="{ disabled: countdown > 0 }">
|
||||
<text class="code-btn-text">{{ countdown > 0 ? `${countdown}s` : $t('auth.getCode') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 区域提示 -->
|
||||
<view class="region-notice">
|
||||
<text>{{ $t('auth.regionNotSupported') }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 登录按钮 -->
|
||||
<view class="login-btn" @click="handleLogin">
|
||||
<text class="login-btn-text">{{ $t('auth.loginBtn') }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 协议勾选 -->
|
||||
<view class="agreement-box">
|
||||
<checkbox-group @change="onAgreementChange">
|
||||
<label class="agreement-label">
|
||||
<checkbox value="agreed" :checked="isAgreed" color="#07c160" class="agreement-checkbox" />
|
||||
<text class="agreement-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>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { sendVerifyCode, loginWithCode } from '@/config/api/user.js'
|
||||
import { fetchAndCacheCustomerPhone } from '@/util/index.js'
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
// 设置页面标题
|
||||
onMounted(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: t('auth.phoneLogin')
|
||||
})
|
||||
})
|
||||
|
||||
const redirect = ref('/pages/index/index')
|
||||
const isAgreed = ref(false) // 是否同意协议
|
||||
const phone = ref('') // 手机号
|
||||
const verifyCode = ref('') // 验证码
|
||||
const countryCode = ref('+86') // 国家区号
|
||||
const countdown = ref(0) // 验证码倒计时
|
||||
let timer = null // 计时器
|
||||
|
||||
// 勾选协议变化
|
||||
const onAgreementChange = (e) => {
|
||||
isAgreed.value = e.detail.value.includes('agreed')
|
||||
}
|
||||
|
||||
// 显示国家区号选择器(暂时仅支持+86)
|
||||
const showCountryPicker = () => {
|
||||
uni.showToast({
|
||||
title: t('auth.onlyMainlandSupported'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
// 验证手机号格式
|
||||
const validatePhone = () => {
|
||||
if (!phone.value) {
|
||||
uni.showToast({ title: t('auth.phoneRequired'), icon: 'none' })
|
||||
return false
|
||||
}
|
||||
const phoneReg = /^1[3-9]\d{9}$/
|
||||
if (!phoneReg.test(phone.value)) {
|
||||
uni.showToast({ title: t('auth.phoneInvalid'), icon: 'none' })
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 发送验证码
|
||||
const handleSendCode = async () => {
|
||||
if (countdown.value > 0) return
|
||||
|
||||
if (!validatePhone()) return
|
||||
|
||||
try {
|
||||
uni.showLoading({ title: t('common.sending') })
|
||||
await sendVerifyCode(phone.value)
|
||||
uni.hideLoading()
|
||||
uni.showToast({ title: t('auth.codeSent'), icon: 'success' })
|
||||
|
||||
// 启动60秒倒计时
|
||||
countdown.value = 60
|
||||
timer = setInterval(() => {
|
||||
countdown.value--
|
||||
if (countdown.value <= 0) {
|
||||
clearInterval(timer)
|
||||
timer = null
|
||||
}
|
||||
}, 1000)
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: error.message || t('auth.sendCodeFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 登录
|
||||
const handleLogin = async () => {
|
||||
if (!validatePhone()) return
|
||||
|
||||
if (!verifyCode.value) {
|
||||
uni.showToast({ title: t('auth.codeRequired'), icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
if (!isAgreed.value) {
|
||||
uni.showToast({ title: t('auth.pleaseAgreeToTerms'), icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
uni.showLoading({ title: t('common.loggingIn') })
|
||||
const res = await loginWithCode(phone.value, verifyCode.value)
|
||||
|
||||
// 保存token和client_id
|
||||
// 兼容多种返回格式:res.data.token, res.token, res.data.access_token
|
||||
const token = res.token || (res.data && (res.data.token || res.data.access_token))
|
||||
const clientId = res.client_id || (res.data && (res.data.client_id || res.data.clientId))
|
||||
|
||||
if (token) {
|
||||
uni.setStorageSync('token', token)
|
||||
if (clientId) {
|
||||
uni.setStorageSync('client_id', clientId)
|
||||
}
|
||||
|
||||
// 登录成功后获取并缓存客服电话
|
||||
fetchAndCacheCustomerPhone().catch(err => {
|
||||
console.error(t('auth.getServicePhoneFailed'), err)
|
||||
})
|
||||
} else {
|
||||
throw new Error(t('auth.noAuthToken'))
|
||||
}
|
||||
|
||||
uni.hideLoading()
|
||||
uni.showToast({ title: t('auth.loginSuccess'), icon: 'success' })
|
||||
|
||||
// 跳转到首页
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({ url: redirect.value })
|
||||
}, 1500)
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: error.message || t('auth.loginFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载
|
||||
onLoad((opts) => {
|
||||
if (opts && opts.redirect) {
|
||||
try {
|
||||
redirect.value = decodeURIComponent(opts.redirect)
|
||||
} catch (_) {}
|
||||
}
|
||||
})
|
||||
|
||||
const go = (url) => {
|
||||
uni.navigateTo({ url })
|
||||
}
|
||||
|
||||
// 清理定时器
|
||||
onUnmounted(() => {
|
||||
if (timer) {
|
||||
clearInterval(timer)
|
||||
timer = null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.login-container {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(180deg, #C8F4D9 0%, #FFFFFF 100%);
|
||||
padding: 0 48rpx;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
|
||||
.header {
|
||||
padding-top: 120rpx;
|
||||
margin-bottom: 80rpx;
|
||||
|
||||
.title {
|
||||
font-size: 64rpx;
|
||||
font-weight: 700;
|
||||
color: #000000;
|
||||
line-height: 1.2;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 64rpx;
|
||||
font-weight: 700;
|
||||
color: #000000;
|
||||
line-height: 1.2;
|
||||
}
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 32rpx;
|
||||
|
||||
.phone-input-wrapper {
|
||||
background: #FFFFFF;
|
||||
border-radius: 48rpx;
|
||||
height: 96rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 32rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||||
|
||||
.country-code {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
padding-right: 16rpx;
|
||||
|
||||
.arrow {
|
||||
margin-left: 8rpx;
|
||||
font-size: 20rpx;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
width: 2rpx;
|
||||
height: 40rpx;
|
||||
background: #E5E5E5;
|
||||
margin: 0 16rpx;
|
||||
}
|
||||
|
||||
.phone-input {
|
||||
flex: 1;
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.code-input-wrapper {
|
||||
background: #FFFFFF;
|
||||
border-radius: 48rpx;
|
||||
height: 96rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 32rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||||
|
||||
.code-input {
|
||||
flex: 1;
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.code-btn {
|
||||
padding-left: 24rpx;
|
||||
border-left: 2rpx solid #E5E5E5;
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.code-btn-text {
|
||||
font-size: 28rpx;
|
||||
color: #07c160;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.region-notice {
|
||||
margin-bottom: 48rpx;
|
||||
padding: 0 8rpx;
|
||||
|
||||
text {
|
||||
font-size: 24rpx;
|
||||
color: #666666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
background: #07c160;
|
||||
border-radius: 60rpx;
|
||||
height: 112rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 8rpx 24rpx rgba(7, 193, 96, 0.3);
|
||||
margin-bottom: 48rpx;
|
||||
|
||||
&:active {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.login-btn-text {
|
||||
font-size: 36rpx;
|
||||
color: #FFFFFF;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.agreement-box {
|
||||
position: absolute;
|
||||
left: 48rpx;
|
||||
right: 48rpx;
|
||||
bottom: 60rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.agreement-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
.agreement-checkbox {
|
||||
flex-shrink: 0;
|
||||
transform: scale(0.75);
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
.agreement-text {
|
||||
flex: 1;
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
line-height: 1.8;
|
||||
word-break: break-all;
|
||||
|
||||
.link {
|
||||
color: #07c160;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+420
-186
@@ -2,30 +2,63 @@
|
||||
<view class="my-card-page">
|
||||
<!-- 会员卡列表 -->
|
||||
<view class="card-list" v-if="cardList.length > 0">
|
||||
<view v-for="card in cardList" :key="card.id" class="card-item" @click="viewCardDetail(card)">
|
||||
<view class="card-header">
|
||||
<text class="card-name">{{ card.name }}</text>
|
||||
<view class="card-status" :class="getStatusClass(card.status)">
|
||||
<text class="status-text">{{ getStatusText(card.status) }}</text>
|
||||
<view v-for="card in cardList" :key="card.id" style="position: relative;background-color: #f5f5f5;"
|
||||
@click="viewCardDetail(card)" :style="card.cardType==='COUNT'?'height: 240rpx;':'height: 200rpx;'">
|
||||
<view
|
||||
style="height: 120rpx;background-color: #ffffff;z-index: 999;border-radius: 25rpx;padding: 32rpx;position: absolute;top: 0;left: 0;right: 0;">
|
||||
<!-- 卡片头部:标题和日期 -->
|
||||
<view class="card-header">
|
||||
<text class="card-name">{{ card.name }}</text>
|
||||
<view class="card-date">
|
||||
<text class="date-text" v-if="card.status !== 'expired'">{{ card.endDate }}{{
|
||||
$t('myCard.expire') }}</text>
|
||||
<text class="date-text expired" v-else>{{ $t('myCard.expiredOn') }}{{ card.endDate }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 地区信息 -->
|
||||
<view class="card-region">
|
||||
<text
|
||||
class="region-text">{{ $t('myCard.onlyForRegionBefore') }}{{ card.positionName }}{{ $t('myCard.onlyForRegionAfter') }}</text>
|
||||
<!-- 状态标签 / 去使用按钮 -->
|
||||
<view v-if="card.status !== 'expired'" class="status-tag active"
|
||||
style="display: flex; align-items: center; gap: 4rpx; background-color: #FFE2B8; border-radius: 26rpx; padding: 6rpx 20rpx;"
|
||||
@click.stop="handleUseCard(card)">
|
||||
<text class="status-text" style="color: #D4A574;">{{ $t('myCard.toUse') }}</text>
|
||||
<!-- <uv-icon name="scan" size="12" color="#D4A574"></uv-icon> -->
|
||||
</view>
|
||||
<view v-else class="status-tag" :class="getStatusClass(card.status)">
|
||||
<text class="status-text">{{ getStatusText(card.status) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-info">
|
||||
<text class="info-label">{{ $t('myCard.type') }}:</text>
|
||||
<text class="info-value">{{ card.cardType === 'COUNT' ? $t('myCard.timesCard') :
|
||||
$t('myCard.durationCard') }}</text>
|
||||
</view>
|
||||
<view class="card-info" v-if="card.type === 'times'">
|
||||
<text class="info-label">{{ $t('myCard.remainingTimes') }}:</text>
|
||||
<text class="info-value highlight">{{ card.remainingTimes }}/{{ card.totalTimes }}</text>
|
||||
</view>
|
||||
<view class="card-info" v-if="card.type === 'duration'">
|
||||
<text class="info-label">{{ $t('myCard.remainingDuration') }}:</text>
|
||||
<text class="info-value highlight">{{ card.remainingDuration }}{{ $t('myCard.hours') }}</text>
|
||||
</view>
|
||||
<view class="card-info">
|
||||
<text class="info-label">{{ $t('myCard.validPeriod') }}:</text>
|
||||
<text class="info-value">{{ card.startDate }} - {{ card.endDate }}</text>
|
||||
<!-- 使用情况和操作按钮 -->
|
||||
<view style="position: absolute; bottom: -10rpx; left: 0; right: 0; padding: 20rpx;z-index:1;"
|
||||
v-if="card.cardType==='COUNT'">
|
||||
<view class="card-footer">
|
||||
<!-- 次卡信息 -->
|
||||
<view v-if="card.cardType === 'COUNT'" class="card-usage-info">
|
||||
<text class="usage-text">{{ $t('myCard.remainingTimes') }}{{ card.remainingCount }}{{
|
||||
$t('myCard.times') }}</text>
|
||||
</view>
|
||||
<!-- 时长卡信息 -->
|
||||
<view v-if="card.cardType === 'TIME'" class="card-usage-info">
|
||||
<text class="usage-text">{{ $t('myCard.durationCard') }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="card-actions">
|
||||
<!-- 续卡按钮(仅次卡显示) -->
|
||||
<view v-if="card.cardType === 'COUNT'" class="renew-btn" @click.stop="renewCard(card)">
|
||||
<text class="renew-text">{{ $t('myCard.renew') }}</text>
|
||||
<uv-icon name="arrow-right" size="14" color="#D4A574"></uv-icon>
|
||||
<!-- <text class="arrow">{{ '>' }}</text> -->
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -41,202 +74,403 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
import {
|
||||
getMemberCardsByStatus
|
||||
} from '@/config/api/member.js'
|
||||
const { t: $t } = useI18n()
|
||||
import {
|
||||
ref,
|
||||
onMounted
|
||||
} from 'vue'
|
||||
import {
|
||||
useI18n
|
||||
} from '@/utils/i18n.js'
|
||||
import {
|
||||
getMemberCardsByStatus
|
||||
} from '@/config/api/member.js'
|
||||
import {
|
||||
getQueryString
|
||||
} from '@/util/index.js'
|
||||
import {
|
||||
getDeviceInfo
|
||||
} from '@/config/api/device.js'
|
||||
import {
|
||||
getInUseOrder,
|
||||
getUnpaidOrder
|
||||
} from '@/config/api/order.js'
|
||||
const {
|
||||
t
|
||||
} = useI18n()
|
||||
|
||||
// 会员卡列表
|
||||
const cardList = ref([])
|
||||
// 会员卡列表
|
||||
const cardList = ref([])
|
||||
|
||||
// 获取会员卡列表
|
||||
const getCardList = async () => {
|
||||
try {
|
||||
const response = await getMemberCardsByStatus()
|
||||
// 处理API返回的数据,转换为模板需要的格式
|
||||
if (response.code === 200 && response.data) {
|
||||
cardList.value = response.data.map(item => ({
|
||||
id: item.id,
|
||||
name: item.cardName,
|
||||
cardType: item.cardType, // TIME -> time
|
||||
totalTimes: item.totalCount,
|
||||
remainingTimes: item.remainingCount,
|
||||
remainingDuration: item.singleLimitMinutes,
|
||||
status: item.status,
|
||||
startDate: item.startTime,
|
||||
endDate: item.endTime,
|
||||
positionName: item.positionName,
|
||||
purchasePrice: item.purchasePrice,
|
||||
// 添加可能的其他字段
|
||||
...item
|
||||
}))
|
||||
} else {
|
||||
cardList.value = []
|
||||
// 获取会员卡列表
|
||||
const getCardList = async () => {
|
||||
try {
|
||||
const response = await getMemberCardsByStatus()
|
||||
// 处理API返回的数据,转换为模板需要的格式
|
||||
if (response.code === 200 && response.data) {
|
||||
cardList.value = response.data.map(item => ({
|
||||
id: item.id,
|
||||
name: item.cardName,
|
||||
cardType: item.cardType, // TIME 或 COUNT
|
||||
// 次卡相关
|
||||
totalCount: item.totalCount,
|
||||
remainingCount: item.remainingCount,
|
||||
singleLimitMinutesForCount: item.singleLimitMinutesForCount,
|
||||
// 时长卡相关
|
||||
cycleDays: item.cycleDays,
|
||||
dailyLimitCount: item.dailyLimitCount,
|
||||
singleLimitMinutes: item.singleLimitMinutes,
|
||||
currentCycleStartTime: item.currentCycleStartTime,
|
||||
currentCycleUsedCount: item.currentCycleUsedCount,
|
||||
// 通用信息
|
||||
status: item.status,
|
||||
startDate: item.startTime?.split(' ')[0] || item.startTime,
|
||||
endDate: item.endTime?.split(' ')[0] || item.endTime,
|
||||
positionId: item.positionId,
|
||||
positionName: item.positionName,
|
||||
purchasePrice: item.purchasePrice,
|
||||
remark: item.remark
|
||||
}))
|
||||
} else {
|
||||
cardList.value = []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取会员卡列表失败:', error)
|
||||
uni.showToast({
|
||||
title: t('myCard.getListFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取会员卡列表失败:', error)
|
||||
uni.showToast({
|
||||
title: $t('myCard.getListFailed'),
|
||||
icon: 'none'
|
||||
}
|
||||
|
||||
// 计算进度条宽度
|
||||
const getProgressWidth = (used, total) => {
|
||||
if (!total || total === 0) return '0%'
|
||||
const percentage = (used / total) * 100
|
||||
return `${Math.min(percentage, 100)}%`
|
||||
}
|
||||
|
||||
// 获取状态类名
|
||||
const getStatusClass = (status) => {
|
||||
const statusMap = {
|
||||
'unused': 'active',
|
||||
'expired': 'expired',
|
||||
'used': 'used',
|
||||
'active': 'active' // 兼容原始状态
|
||||
}
|
||||
return statusMap[status] || 'active' // 默认为active
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status) => {
|
||||
const statusMap = {
|
||||
'unused': t('myCard.active'), // unused表示未使用,即活跃状态
|
||||
'expired': t('myCard.expired'),
|
||||
'used': t('myCard.used'),
|
||||
'active': t('myCard.active') // 兼容原始状态
|
||||
}
|
||||
return statusMap[status] || t('myCard.active') // 默认为active
|
||||
}
|
||||
|
||||
// 查看卡详情
|
||||
const viewCardDetail = (card) => {
|
||||
// TODO: 跳转到卡详情页面
|
||||
// uni.showToast({
|
||||
// title: t('common.functionDeveloping'),
|
||||
// icon: 'none'
|
||||
// })
|
||||
}
|
||||
|
||||
// 续卡
|
||||
const renewCard = (card) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/purchase/index?positionId=${card.positionId}`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 获取状态类名
|
||||
const getStatusClass = (status) => {
|
||||
const statusMap = {
|
||||
'unused': 'active',
|
||||
'expired': 'expired',
|
||||
'used': 'used',
|
||||
'active': 'active' // 兼容原始状态
|
||||
// 去使用会员卡
|
||||
const handleUseCard = async (card) => {
|
||||
try {
|
||||
const scanResult = await new Promise((resolve, reject) => {
|
||||
uni.scanCode({
|
||||
success: resolve,
|
||||
fail: reject
|
||||
})
|
||||
})
|
||||
|
||||
console.log('扫码结果:', scanResult);
|
||||
let deviceNo;
|
||||
// 兼容不同平台的扫码结果
|
||||
if (scanResult.scanType === 'QR_CODE' || scanResult.scanType === 'qrCode') {
|
||||
deviceNo = getQueryString(scanResult.result, 'deviceNo')
|
||||
} else if (scanResult.path) {
|
||||
deviceNo = getQueryString(scanResult.path, 'deviceNo')
|
||||
} else {
|
||||
deviceNo = scanResult.result
|
||||
}
|
||||
|
||||
if (!deviceNo) {
|
||||
uni.showToast({
|
||||
title: t('home.invalidQRCode'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
uni.showLoading({
|
||||
title: t('common.getting')
|
||||
})
|
||||
|
||||
// 检查是否有使用中的订单
|
||||
const inUseRes = await getInUseOrder()
|
||||
if (inUseRes && inUseRes.code === 200 && inUseRes.data) {
|
||||
uni.hideLoading()
|
||||
const inUseOrder = inUseRes.data
|
||||
uni.reLaunch({
|
||||
url: `/pages/order/detail?orderId=${inUseOrder.orderId}&deviceId=${deviceNo || inUseOrder.deviceNo}`
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否有待支付订单
|
||||
const orderRes = await getUnpaidOrder()
|
||||
if (orderRes && orderRes.code === 200 && orderRes.data) {
|
||||
uni.hideLoading()
|
||||
const unpaidOrder = orderRes.data
|
||||
uni.navigateTo({
|
||||
url: `/pages/order/payment?orderId=${unpaidOrder.orderId}`
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 获取设备信息并跳转详情
|
||||
const deviceInfoRes = await getDeviceInfo(deviceNo)
|
||||
uni.hideLoading()
|
||||
|
||||
if (deviceInfoRes.code === 200 && deviceInfoRes.data && deviceInfoRes.data.device) {
|
||||
const deviceInfo = deviceInfoRes.data.device
|
||||
let url = `/pages/device/detail?deviceNo=${deviceNo}`
|
||||
if (deviceInfo.feeConfig) {
|
||||
url += `&feeConfig=${encodeURIComponent(deviceInfo.feeConfig)}`
|
||||
}
|
||||
uni.navigateTo({
|
||||
url
|
||||
})
|
||||
} else {
|
||||
uni.navigateTo({
|
||||
url: `/pages/device/detail?deviceNo=${deviceNo}`
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('扫码处理失败:', error)
|
||||
if (error && error.errMsg !== 'scanCode:fail cancel') {
|
||||
uni.showToast({
|
||||
title: t('home.scanFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return statusMap[status] || 'active' // 默认为active
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status) => {
|
||||
const statusMap = {
|
||||
'unused': $t('myCard.active'), // unused表示未使用,即活跃状态
|
||||
'expired': $t('myCard.expired'),
|
||||
'used': $t('myCard.used'),
|
||||
'active': $t('myCard.active') // 兼容原始状态
|
||||
// 去购买
|
||||
const goToBuy = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/purchase/index'
|
||||
})
|
||||
}
|
||||
return statusMap[status] || $t('myCard.active') // 默认为active
|
||||
}
|
||||
|
||||
// 查看卡详情
|
||||
const viewCardDetail = (card) => {
|
||||
// TODO: 跳转到卡详情页面
|
||||
// uni.showToast({
|
||||
// title: $t('common.functionDeveloping'),
|
||||
// icon: 'none'
|
||||
// })
|
||||
}
|
||||
|
||||
// 去购买
|
||||
const goToBuy = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/purchase/index'
|
||||
onMounted(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: t('user.myCards')
|
||||
})
|
||||
getCardList()
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t('user.myCards')
|
||||
})
|
||||
getCardList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.my-card-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.card-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.card-item {
|
||||
background-color: #ffffff;
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
padding-bottom: 20rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.card-status {
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 22rpx;
|
||||
|
||||
&.active {
|
||||
background-color: #e8f5e8;
|
||||
color: #4caf50;
|
||||
.my-card-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
&.expired {
|
||||
background-color: #ffeaea;
|
||||
color: #f44336;
|
||||
.card-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
&.used {
|
||||
background-color: #f0f0f0;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
.card-item {
|
||||
// background-color: #ffffff;
|
||||
border-radius: 25rpx;
|
||||
padding: 32rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
|
||||
.card-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12rpx;
|
||||
|
||||
.info-label {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
// 卡片头部
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
&.highlight {
|
||||
color: #FF6B00;
|
||||
font-weight: 600;
|
||||
.card-name {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #333333;
|
||||
line-height: 50rpx;
|
||||
}
|
||||
|
||||
.card-date {
|
||||
.date-text {
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
line-height: 34rpx;
|
||||
|
||||
&.expired {
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 0;
|
||||
// 地区信息
|
||||
.card-region {
|
||||
margin-bottom: 24rpx;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 16rpx;
|
||||
|
||||
.empty-icon {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
margin-bottom: 40rpx;
|
||||
opacity: 0.5;
|
||||
.region-text {
|
||||
font-size: 26rpx;
|
||||
color: #666666;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
margin-bottom: 40rpx;
|
||||
// 卡片底部
|
||||
.card-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
/* padding-top: 24rpx ; */
|
||||
position: absolute;
|
||||
background: rgba(255, 244, 227, 1);
|
||||
z-index: 1;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border-radius: 0 0 25rpx 25rpx;
|
||||
padding: 20rpx;
|
||||
/* text-align: 30rpx ; */
|
||||
}
|
||||
|
||||
.buy-btn {
|
||||
padding: 20rpx 60rpx;
|
||||
background-color: #B8741A;
|
||||
border-radius: 48rpx;
|
||||
.card-usage-info {
|
||||
flex: 1;
|
||||
|
||||
.buy-text {
|
||||
font-size: 28rpx;
|
||||
color: #ffffff;
|
||||
.usage-text {
|
||||
font-size: 26rpx;
|
||||
color: #D4A574;
|
||||
font-weight: 500;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
.card-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
// 续卡按钮
|
||||
.renew-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
// background-color: #FFF9F0;
|
||||
border-radius: 8rpx;
|
||||
|
||||
.renew-text {
|
||||
font-size: 24rpx;
|
||||
color: #D4A574;
|
||||
line-height: 34rpx;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
font-size: 24rpx;
|
||||
color: #D4A574;
|
||||
}
|
||||
}
|
||||
|
||||
// 状态标签
|
||||
.status-tag {
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 8rpx;
|
||||
|
||||
.status-text {
|
||||
font-size: 24rpx;
|
||||
line-height: 34rpx;
|
||||
}
|
||||
|
||||
&.active {
|
||||
// background-color: #FFF9F0;
|
||||
|
||||
.status-text {
|
||||
color: #D4A574;
|
||||
}
|
||||
}
|
||||
|
||||
&.expired {
|
||||
// background-color: #F5F5F5;
|
||||
|
||||
.status-text {
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
&.used {
|
||||
// background-color: #F5F5F5;
|
||||
|
||||
.status-text {
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 0;
|
||||
|
||||
.empty-icon {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
margin-bottom: 40rpx;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.buy-btn {
|
||||
padding: 20rpx 60rpx;
|
||||
background-color: #B8741A;
|
||||
border-radius: 48rpx;
|
||||
|
||||
.buy-text {
|
||||
font-size: 28rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+462
-343
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<view class="my-coupon-page">
|
||||
<!-- Tab 切换 -->
|
||||
<view class="tab-container">
|
||||
<!-- <view class="tab-container">
|
||||
<view class="tab-item" :class="{ active: currentTab === 'available' }" @click="switchTab('available')">
|
||||
<text class="tab-text">{{ $t('myCoupon.available') }}</text>
|
||||
</view>
|
||||
@@ -11,25 +11,35 @@
|
||||
<view class="tab-item" :class="{ active: currentTab === 'expired' }" @click="switchTab('expired')">
|
||||
<text class="tab-text">{{ $t('myCoupon.expired') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- 优惠券列表 -->
|
||||
<view class="coupon-list" v-if="filteredCoupons.length > 0">
|
||||
<view v-for="coupon in filteredCoupons" :key="coupon.id" class="coupon-item-wrapper">
|
||||
<view class="coupon-item" :class="getCouponClass(coupon.status)">
|
||||
<!-- 左侧圆形缺口 -->
|
||||
<view class="coupon-circle-left"></view>
|
||||
<!-- 右侧圆形缺口 -->
|
||||
<view class="coupon-circle-right"></view>
|
||||
|
||||
|
||||
<!-- 虚线上下圆形缺口 -->
|
||||
<view class="coupon-circle-top"></view>
|
||||
<view class="coupon-circle-bottom"></view>
|
||||
|
||||
<view class="coupon-left">
|
||||
<text class="coupon-value">{{ coupon.type === 'discount' ? coupon.discount + '折' : '¥' + coupon.value }}</text>
|
||||
<text class="coupon-condition">{{ coupon.condition }}</text>
|
||||
<text class="coupon-validity-left">{{ coupon.validity }}</text>
|
||||
<view class="coupon-value">
|
||||
<text v-if="coupon.type === 'cash'" class="coupon-unit">¥</text>
|
||||
<text class="coupon-amount">{{ coupon.type === 'discount' ? coupon.discount : coupon.value }}</text>
|
||||
<text v-if="coupon.type === 'discount'" class="coupon-unit">折</text>
|
||||
</view>
|
||||
<view style="display: flex;flex-direction: column;">
|
||||
<text class="coupon-condition">{{ coupon.condition }}</text>
|
||||
<text class="coupon-validity-left">{{ coupon.validity }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="coupon-divider"></view>
|
||||
<view class="coupon-right">
|
||||
<text class="coupon-name">{{ coupon.name }}</text>
|
||||
<!-- <text class="coupon-name">{{ coupon.name }}</text> -->
|
||||
<!-- <text class="coupon-region" v-if="coupon.positionName"
|
||||
style="font-size: 22rpx; color: #999; margin-top: 4rpx;">
|
||||
{{ $t('myCoupon.onlyForRegionBefore') }}{{ coupon.positionName }}{{ $t('myCoupon.onlyForRegionAfter') }}
|
||||
</text> -->
|
||||
<view class="use-btn" v-if="coupon.status === 'available'" @click="useCoupon(coupon)">
|
||||
<text class="use-text">{{ $t('myCoupon.useNow') }}</text>
|
||||
</view>
|
||||
@@ -51,370 +61,479 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
import { getUserCoupons } from '@/config/api/coupon.js'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
import { getUserCoupons } from '@/config/api/coupon.js'
|
||||
import { getQueryString } from '@/util/index.js'
|
||||
import { getDeviceInfo } from '@/config/api/device.js'
|
||||
import { getInUseOrder, getUnpaidOrder } from '@/config/api/order.js'
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
// 当前选中的 Tab
|
||||
const currentTab = ref('available')
|
||||
// 当前选中的 Tab
|
||||
const currentTab = ref('available')
|
||||
|
||||
// Tab 与 API 状态的映射
|
||||
const tabToStatusMap = {
|
||||
available: 'unused',
|
||||
used: 'used',
|
||||
expired: 'expired'
|
||||
}
|
||||
// // Tab 与 API 状态的映射
|
||||
// const tabToStatusMap = {
|
||||
// available: 'unused',
|
||||
// used: 'used',
|
||||
// expired: 'expired'
|
||||
// }
|
||||
|
||||
// 优惠券列表
|
||||
const couponList = ref([])
|
||||
// 优惠券列表
|
||||
const couponList = ref([])
|
||||
|
||||
// 过滤后的优惠券
|
||||
const filteredCoupons = computed(() => {
|
||||
return couponList.value.filter(coupon => coupon.status === currentTab.value)
|
||||
})
|
||||
// 过滤后的优惠券
|
||||
const filteredCoupons = computed(() => {
|
||||
return couponList.value.filter(coupon => coupon.status === currentTab.value)
|
||||
})
|
||||
|
||||
// 获取优惠券列表
|
||||
const getCouponList = async () => {
|
||||
try {
|
||||
const apiStatus = tabToStatusMap[currentTab.value]
|
||||
const res = await getUserCoupons(apiStatus)
|
||||
|
||||
if (res.code === 200 && res.data) {
|
||||
// 将后端数据转换为前端需要的格式
|
||||
couponList.value = (res.data || []).map(item => {
|
||||
// 判断优惠券类型:discount_coupon 折扣券,deduction_coupon 抵扣券
|
||||
const isCashCoupon = item.couponType === 'deduction_coupon'
|
||||
|
||||
// 格式化使用条件
|
||||
let condition = '无门槛'
|
||||
if (item.usableCondition && item.usableCondition > 0) {
|
||||
condition = `满${item.usableCondition}可用`
|
||||
}
|
||||
|
||||
// 格式化有效期
|
||||
let validity = ''
|
||||
if (currentTab.value === 'used') {
|
||||
// 已使用显示使用时间(这里暂用开始时间,如后端有使用时间字段可替换)
|
||||
validity = item.couponStartTime ? `使用时间 ${item.couponStartTime}` : ''
|
||||
} else if (item.couponEndTime) {
|
||||
validity = `有效期至 ${item.couponEndTime}`
|
||||
}
|
||||
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.couponName || '优惠券',
|
||||
type: isCashCoupon ? 'cash' : 'discount',
|
||||
value: item.deductAmount ? parseFloat(item.deductAmount) : 0,
|
||||
discount: item.discountRate ? parseFloat(item.discountRate) * 10 : null,
|
||||
condition: condition,
|
||||
validity: validity,
|
||||
status: currentTab.value
|
||||
}
|
||||
})
|
||||
} else {
|
||||
couponList.value = []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取优惠券列表失败:', error)
|
||||
// 获取优惠券列表
|
||||
const getCouponList = async () => {
|
||||
try {
|
||||
// const apiStatus = tabToStatusMap[currentTab.value]
|
||||
const res = await getUserCoupons('')
|
||||
|
||||
if (res.code === 200 && res.data) {
|
||||
// 将后端数据转换为前端需要的格式
|
||||
couponList.value = (res.data || []).map(item => {
|
||||
// 判断优惠券类型:discount_coupon 折扣券,deduction_coupon 抵扣券
|
||||
const isCashCoupon = item.couponType === 'deduction_coupon'
|
||||
|
||||
// 格式化使用条件
|
||||
let condition = '无门槛'
|
||||
if (item.usableCondition && item.usableCondition > 0) {
|
||||
condition = `满${item.usableCondition}可用`
|
||||
}
|
||||
|
||||
// 格式化有效期
|
||||
let validity = ''
|
||||
if (currentTab.value === 'used') {
|
||||
// 已使用显示使用时间
|
||||
validity = item.couponStartTime ? `使用时间 ${item.couponStartTime.split(' ')[0]}` : ''
|
||||
} else if (item.couponEndTime) {
|
||||
validity = `于 ${item.couponEndTime.split(' ')[0]} 过期`
|
||||
}
|
||||
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.couponName || '优惠券',
|
||||
type: isCashCoupon ? 'cash' : 'discount',
|
||||
value: item.deductAmount ? parseFloat(item.deductAmount) : 0,
|
||||
discount: item.discountRate ? parseFloat(item.discountRate) * 10 : null,
|
||||
condition: condition,
|
||||
validity: validity,
|
||||
status: currentTab.value,
|
||||
positionName: item.positionName
|
||||
}
|
||||
})
|
||||
} else {
|
||||
couponList.value = []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取优惠券列表失败:', error)
|
||||
couponList.value = []
|
||||
uni.showToast({
|
||||
title: t('myCoupon.getListFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 切换 Tab
|
||||
const switchTab = (tab) => {
|
||||
currentTab.value = tab
|
||||
getCouponList()
|
||||
}
|
||||
|
||||
// 获取优惠券样式类名
|
||||
const getCouponClass = (status) => {
|
||||
return status === 'available' ? '' : 'disabled'
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status) => {
|
||||
const statusMap = {
|
||||
'used': t('myCoupon.usedStatus'),
|
||||
'expired': t('myCoupon.expiredStatus')
|
||||
}
|
||||
return statusMap[status] || ''
|
||||
}
|
||||
|
||||
// 获取空状态文本
|
||||
const getEmptyText = () => {
|
||||
const textMap = {
|
||||
'available': t('myCoupon.noAvailableCoupons'),
|
||||
'used': t('myCoupon.noUsedCoupons'),
|
||||
'expired': t('myCoupon.noExpiredCoupons')
|
||||
}
|
||||
return textMap[currentTab.value] || ''
|
||||
}
|
||||
|
||||
// 使用优惠券
|
||||
const useCoupon = async (coupon) => {
|
||||
try {
|
||||
const scanResult = await new Promise((resolve, reject) => {
|
||||
uni.scanCode({
|
||||
success: resolve,
|
||||
fail: reject
|
||||
})
|
||||
})
|
||||
|
||||
console.log('扫码结果:', scanResult);
|
||||
let deviceNo;
|
||||
// 兼容不同平台的扫码结果
|
||||
if (scanResult.scanType === 'QR_CODE' || scanResult.scanType === 'qrCode') {
|
||||
deviceNo = getQueryString(scanResult.result, 'deviceNo')
|
||||
} else if (scanResult.path) {
|
||||
deviceNo = getQueryString(scanResult.path, 'deviceNo')
|
||||
} else {
|
||||
deviceNo = scanResult.result
|
||||
}
|
||||
|
||||
if (!deviceNo) {
|
||||
uni.showToast({
|
||||
title: $t('myCoupon.getListFailed'),
|
||||
title: t('home.invalidQRCode'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
uni.showLoading({
|
||||
title: t('common.getting')
|
||||
})
|
||||
|
||||
// 检查是否有使用中的订单
|
||||
const inUseRes = await getInUseOrder()
|
||||
if (inUseRes && inUseRes.code === 200 && inUseRes.data) {
|
||||
uni.hideLoading()
|
||||
const inUseOrder = inUseRes.data
|
||||
uni.reLaunch({
|
||||
url: `/pages/order/detail?orderId=${inUseOrder.orderId}&deviceId=${deviceNo || inUseOrder.deviceNo}`
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否有待支付订单
|
||||
const orderRes = await getUnpaidOrder()
|
||||
if (orderRes && orderRes.code === 200 && orderRes.data) {
|
||||
uni.hideLoading()
|
||||
const unpaidOrder = orderRes.data
|
||||
uni.navigateTo({
|
||||
url: `/pages/order/payment?orderId=${unpaidOrder.orderId}`
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 获取设备信息并跳转详情
|
||||
const deviceInfoRes = await getDeviceInfo(deviceNo)
|
||||
uni.hideLoading()
|
||||
|
||||
if (deviceInfoRes.code === 200 && deviceInfoRes.data && deviceInfoRes.data.device) {
|
||||
const deviceInfo = deviceInfoRes.data.device
|
||||
let url = `/pages/device/detail?deviceNo=${deviceNo}`
|
||||
if (deviceInfo.feeConfig) {
|
||||
url += `&feeConfig=${encodeURIComponent(deviceInfo.feeConfig)}`
|
||||
}
|
||||
uni.navigateTo({
|
||||
url
|
||||
})
|
||||
} else {
|
||||
uni.navigateTo({
|
||||
url: `/pages/device/detail?deviceNo=${deviceNo}`
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('扫码处理失败:', error)
|
||||
if (error && error.errMsg !== 'scanCode:fail cancel') {
|
||||
uni.showToast({
|
||||
title: t('home.scanFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 切换 Tab
|
||||
const switchTab = (tab) => {
|
||||
currentTab.value = tab
|
||||
getCouponList()
|
||||
}
|
||||
|
||||
// 获取优惠券样式类名
|
||||
const getCouponClass = (status) => {
|
||||
return status === 'available' ? '' : 'disabled'
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status) => {
|
||||
const statusMap = {
|
||||
'used': $t('myCoupon.usedStatus'),
|
||||
'expired': $t('myCoupon.expiredStatus')
|
||||
}
|
||||
return statusMap[status] || ''
|
||||
}
|
||||
|
||||
// 获取空状态文本
|
||||
const getEmptyText = () => {
|
||||
const textMap = {
|
||||
'available': $t('myCoupon.noAvailableCoupons'),
|
||||
'used': $t('myCoupon.noUsedCoupons'),
|
||||
'expired': $t('myCoupon.noExpiredCoupons')
|
||||
}
|
||||
return textMap[currentTab.value] || ''
|
||||
}
|
||||
|
||||
// 使用优惠券
|
||||
const useCoupon = (coupon) => {
|
||||
// TODO: 使用优惠券逻辑
|
||||
uni.navigateTo({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
}
|
||||
|
||||
// 去购买
|
||||
const goToBuy = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/purchase/index?tab=coupon'
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t('user.myCoupons')
|
||||
})
|
||||
getCouponList()
|
||||
// 去购买
|
||||
const goToBuy = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/purchase/index?tab=coupon'
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: t('user.myCoupons')
|
||||
})
|
||||
getCouponList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.my-coupon-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
// 优惠券样式变量封装
|
||||
$coupon-theme-color: #A16300;
|
||||
$coupon-divider-color: #B8741A;
|
||||
$coupon-bg-faded: #f5f5f5;
|
||||
$coupon-active-bg-start: #FFF4E6;
|
||||
$coupon-active-bg-end: #FFE8CC;
|
||||
$coupon-divider-left: 65%;
|
||||
$coupon-circle-radius: 16rpx;
|
||||
|
||||
/* Tab 切换 */
|
||||
.tab-container {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
display: flex;
|
||||
background-color: #ffffff;
|
||||
z-index: 999;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
.my-coupon-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 20rpx 0;
|
||||
position: relative;
|
||||
/* Tab 切换 */
|
||||
.tab-container {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
display: flex;
|
||||
background-color: #ffffff;
|
||||
z-index: 999;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
|
||||
.tab-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
font-weight: 500;
|
||||
}
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 20rpx 0;
|
||||
position: relative;
|
||||
|
||||
&.active {
|
||||
.tab-text {
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 60rpx;
|
||||
height: 6rpx;
|
||||
background-color: #FFA928;
|
||||
border-radius: 3rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-list {
|
||||
padding: 20rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.coupon-item-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.coupon-item {
|
||||
background: linear-gradient(135deg, #FFF4E6 0%, #FFE8CC 100%);
|
||||
border-radius: 20rpx;
|
||||
padding: 40rpx 30rpx;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
min-height: 180rpx;
|
||||
box-sizing: border-box;
|
||||
border: 2rpx solid transparent;
|
||||
transition: all 0.3s;
|
||||
|
||||
&.selected {
|
||||
border-color: #B8741A;
|
||||
box-shadow: 0 4rpx 20rpx rgba(184, 116, 26, 0.2);
|
||||
|
||||
.coupon-circle-left,
|
||||
.coupon-circle-right {
|
||||
background-color: #FFF4E6;
|
||||
border: 2rpx solid #B8741A;
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
background: linear-gradient(135deg, #f5f5f5 0%, #e8e8e8 100%);
|
||||
opacity: 0.6;
|
||||
|
||||
.coupon-value,
|
||||
.coupon-condition,
|
||||
.coupon-name {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.coupon-circle-left,
|
||||
.coupon-circle-right {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 左侧圆形缺口 */
|
||||
.coupon-circle-left {
|
||||
position: absolute;
|
||||
left: -16rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #f5f5f5;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* 右侧圆形缺口 */
|
||||
.coupon-circle-right {
|
||||
position: absolute;
|
||||
right: -16rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #f5f5f5;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.coupon-left {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.coupon-value {
|
||||
font-size: 48rpx;
|
||||
font-weight: 700;
|
||||
color: #B8741A;
|
||||
}
|
||||
|
||||
.coupon-condition {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.coupon-validity-left {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.coupon-divider {
|
||||
width: 2rpx;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 65%;
|
||||
transform: translateX(-50%);
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
// min-height: 160rpx;
|
||||
align-self: stretch;
|
||||
background: repeating-linear-gradient(to bottom,
|
||||
#B8741A 0rpx,
|
||||
#B8741A 8rpx,
|
||||
transparent 8rpx,
|
||||
transparent 16rpx);
|
||||
margin: 0 30rpx;
|
||||
}
|
||||
|
||||
.coupon-right {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.coupon-name {
|
||||
.tab-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&.active {
|
||||
.tab-text {
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 60rpx;
|
||||
height: 6rpx;
|
||||
background-color: #FFA928;
|
||||
border-radius: 3rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-list {
|
||||
padding: 20rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.coupon-item-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.coupon-item {
|
||||
background: #FFF4E3;
|
||||
border-radius: 20rpx;
|
||||
padding: 40rpx 30rpx;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
min-height: 180rpx;
|
||||
box-sizing: border-box;
|
||||
border: 2rpx solid transparent;
|
||||
transition: all 0.3s;
|
||||
|
||||
&.selected {
|
||||
border-color: $coupon-divider-color;
|
||||
box-shadow: 0 4rpx 20rpx rgba(184, 116, 26, 0.2);
|
||||
|
||||
.coupon-circle-top,
|
||||
.coupon-circle-bottom {
|
||||
background-color: $coupon-active-bg-start;
|
||||
border: 2rpx solid $coupon-divider-color;
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
background: linear-gradient(135deg, #f5f5f5 0%, #e8e8e8 100%);
|
||||
opacity: 0.6;
|
||||
|
||||
.coupon-value,
|
||||
.coupon-condition,
|
||||
.coupon-name {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.coupon-circle-top,
|
||||
.coupon-circle-bottom {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 虚线顶部圆形缺口 */
|
||||
.coupon-circle-top {
|
||||
position: absolute;
|
||||
left: $coupon-divider-left+4%;
|
||||
top: -$coupon-circle-radius;
|
||||
transform: translateX(-50%);
|
||||
width: $coupon-circle-radius * 2;
|
||||
height: $coupon-circle-radius * 2;
|
||||
border-radius: 50%;
|
||||
background-color: $coupon-bg-faded;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* 虚线底部圆形缺口 */
|
||||
.coupon-circle-bottom {
|
||||
position: absolute;
|
||||
left: $coupon-divider-left+4%;
|
||||
bottom: -$coupon-circle-radius;
|
||||
transform: translateX(-50%);
|
||||
width: $coupon-circle-radius * 2;
|
||||
height: $coupon-circle-radius * 2;
|
||||
border-radius: 50%;
|
||||
background-color: $coupon-bg-faded;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.coupon-left {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.coupon-value {
|
||||
display: flex;
|
||||
align-items: flex-end; // 单位在脚
|
||||
color: $coupon-theme-color;
|
||||
line-height: 1;
|
||||
width:120rpx;
|
||||
}
|
||||
|
||||
.coupon-amount {
|
||||
font-size: 56rpx; // 值要大
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.coupon-unit {
|
||||
font-size: 24rpx; // 单位小
|
||||
font-weight: 500;
|
||||
margin-bottom: 6rpx; // 微调单位垂直位置,使其更贴合“脚”
|
||||
margin-left: 4rpx;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
.coupon-condition {
|
||||
font-size: 24rpx;
|
||||
color: #000;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.coupon-validity-left {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.coupon-divider {
|
||||
width: 2rpx;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: $coupon-divider-left;
|
||||
transform: translateX(-50%);
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
align-self: stretch;
|
||||
background: repeating-linear-gradient(to bottom,
|
||||
$coupon-divider-color 0rpx,
|
||||
$coupon-divider-color 8rpx,
|
||||
transparent 8rpx,
|
||||
transparent 16rpx);
|
||||
margin: 0 30rpx;
|
||||
}
|
||||
|
||||
.coupon-right {
|
||||
// flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
align-items: center;
|
||||
margin: auto 0;
|
||||
width: 160rpx;
|
||||
// align-content: center;
|
||||
// transform: translateY(50%);
|
||||
}
|
||||
|
||||
.coupon-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.coupon-validity {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.use-btn {
|
||||
// margin-top: 10rpx;
|
||||
// padding: 12rpx 28rpx;
|
||||
// background-color: #B8741A;
|
||||
// border-radius: 40rpx;
|
||||
|
||||
.use-text {
|
||||
font-size: 28rpx;
|
||||
color: #A16300;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-status {
|
||||
margin-top: 10rpx;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 0;
|
||||
|
||||
.empty-icon {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
margin-bottom: 40rpx;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.coupon-validity {
|
||||
font-size: 22rpx;
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.use-btn {
|
||||
margin-top: 10rpx;
|
||||
padding: 12rpx 28rpx;
|
||||
.buy-btn {
|
||||
padding: 20rpx 60rpx;
|
||||
background-color: #B8741A;
|
||||
border-radius: 40rpx;
|
||||
border-radius: 48rpx;
|
||||
|
||||
.use-text {
|
||||
font-size: 24rpx;
|
||||
.buy-text {
|
||||
font-size: 28rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-status {
|
||||
margin-top: 10rpx;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 0;
|
||||
|
||||
.empty-icon {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
margin-bottom: 40rpx;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.buy-btn {
|
||||
padding: 20rpx 60rpx;
|
||||
background-color: #B8741A;
|
||||
border-radius: 48rpx;
|
||||
|
||||
.buy-text {
|
||||
font-size: 28rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+35
-84
@@ -155,13 +155,12 @@
|
||||
import {
|
||||
uploadUserAvatar
|
||||
} from '../../config/api/user.js'
|
||||
import {
|
||||
URL
|
||||
} from '../../config/url.js'
|
||||
import { getCurrentAdvertisement } from '@/config/api/system.js'
|
||||
import { getInUseOrder } from '@/config/api/order.js'
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
// 设置页执行退出登录,此页不再直接调用
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
// 响应式状态
|
||||
const userInfo = ref({});
|
||||
@@ -175,48 +174,25 @@ import {
|
||||
const bannerImages = ref([]) // 广告图片列表
|
||||
const bannerImageList = ref([]) // 完整的广告配置列表(包含链接信息)
|
||||
|
||||
// 将语言代码转换为后端接受的格式
|
||||
const convertLanguageCode = (lang) => {
|
||||
// zh-CN -> zh_CN (转换下划线)
|
||||
// en-US -> en_US (转换下划线)
|
||||
return lang.replace(/-/g, '_')
|
||||
}
|
||||
|
||||
// 获取广告图片
|
||||
const getBannerImages = async () => {
|
||||
try {
|
||||
// 获取当前语言设置
|
||||
const currentLang = uni.getStorageSync('language') || 'zh-CN'
|
||||
const languageCode = convertLanguageCode(currentLang)
|
||||
|
||||
console.log('加载个人中心广告,语言:', currentLang, '转换后:', languageCode)
|
||||
|
||||
// 调用接口获取广告内容
|
||||
const res = await uni.request({
|
||||
url: `${URL}/device/advertisementConfig/current`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Content-Language': languageCode
|
||||
},
|
||||
data: {
|
||||
appPlatform: 'wechat', // 微信平台
|
||||
appType: 'user' // 用户端
|
||||
}
|
||||
const res = await getCurrentAdvertisement({
|
||||
appPlatform: 'wechat', // 微信平台
|
||||
appType: 'user' // 用户端
|
||||
})
|
||||
|
||||
console.log('个人中心广告响应:', res)
|
||||
|
||||
if (res.statusCode === 200 && res.data.code === 200 && res.data.data) {
|
||||
if (res && res.code === 200 && res.data) {
|
||||
// 使用 imageList 字段(包含图片和链接信息)
|
||||
const imageList = res.data.data.imageList || []
|
||||
const imageList = res.data.imageList || []
|
||||
if (imageList.length > 0) {
|
||||
bannerImageList.value = imageList
|
||||
// 提取图片URL用于展示
|
||||
bannerImages.value = imageList.map(item => item.imageUrl)
|
||||
console.log('个人中心广告加载成功,图片数量:', imageList.length)
|
||||
}
|
||||
} else {
|
||||
console.warn('获取个人中心广告失败:', res.data?.msg || '未知错误')
|
||||
console.warn('获取个人中心广告失败:', res?.msg || '未知错误')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取个人中心广告失败:', error)
|
||||
@@ -226,12 +202,10 @@ import {
|
||||
// 处理广告点击
|
||||
const handleBannerClick = (index) => {
|
||||
if (!bannerImageList.value || !bannerImageList.value[index]) {
|
||||
console.warn('未找到对应的广告配置')
|
||||
return
|
||||
}
|
||||
|
||||
const config = bannerImageList.value[index]
|
||||
console.log('点击广告:', index, config)
|
||||
|
||||
// 根据链接类型进行跳转
|
||||
if (config.linkType === 'miniapp' && config.appId) {
|
||||
@@ -241,12 +215,11 @@ import {
|
||||
appId: config.appId,
|
||||
path: config.linkUrl || '',
|
||||
success: () => {
|
||||
console.log('跳转小程序成功')
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('跳转小程序失败:', err)
|
||||
uni.showToast({
|
||||
title: '跳转失败',
|
||||
title: t('common.loadFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
@@ -254,7 +227,7 @@ import {
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
uni.showToast({
|
||||
title: '请在微信小程序中使用',
|
||||
title: t('auth.pleaseUseInWechat'),
|
||||
icon: 'none'
|
||||
})
|
||||
// #endif
|
||||
@@ -268,15 +241,13 @@ import {
|
||||
uni.navigateTo({
|
||||
url: config.linkUrl
|
||||
})
|
||||
} else {
|
||||
console.log('无有效的跳转配置')
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
onMounted(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t('user.personalCenter')
|
||||
title: t('user.personalCenter')
|
||||
})
|
||||
getInfo();
|
||||
initVersion();
|
||||
@@ -293,7 +264,6 @@ import {
|
||||
const getInfo = async () => {
|
||||
try {
|
||||
const res = await getUserInfo();
|
||||
console.log('User info response:', res);
|
||||
|
||||
if (res.code == 401 || res.code == 40101) {
|
||||
redirectToLogin()
|
||||
@@ -317,9 +287,8 @@ import {
|
||||
deposit.value = res.data.balanceAmount || '0.00';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取用户信息失败:', error);
|
||||
uni.showToast({
|
||||
title: $t('user.getUserInfoFailed'),
|
||||
title: t('user.getUserInfoFailed'),
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
@@ -374,43 +343,29 @@ import {
|
||||
const handleQuickReturn = async () => {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: $t('common.loading')
|
||||
title: t('common.loading')
|
||||
});
|
||||
|
||||
// 获取使用中的订单
|
||||
const res = await uni.request({
|
||||
url: `${URL}/app/order/inUse`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Authorization': "Bearer " + uni.getStorageSync('token'),
|
||||
'Clientid': uni.getStorageSync('client_id')
|
||||
}
|
||||
});
|
||||
const res = await getInUseOrder();
|
||||
|
||||
uni.hideLoading();
|
||||
|
||||
if (res.statusCode === 401 || res.data?.code === 401 || res.data?.code === 40101) {
|
||||
redirectToLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.statusCode === 200 && res.data.code === 200 && res.data.data) {
|
||||
const inUseOrder = res.data.data;
|
||||
if (res && res.code === 200 && res.data) {
|
||||
const inUseOrder = res.data;
|
||||
// 跳转到统一订单详情页面
|
||||
uni.navigateTo({
|
||||
url: `/pages/order/detail?orderId=${inUseOrder.orderId}&deviceId=${inUseOrder.deviceNo}`
|
||||
});
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: $t('order.noOrder'),
|
||||
title: t('order.noOrder'),
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
uni.hideLoading();
|
||||
console.error('获取使用中订单失败:', error);
|
||||
uni.showToast({
|
||||
title: $t('order.getOrderFailed'),
|
||||
title: t('order.getOrderFailed'),
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
@@ -433,7 +388,7 @@ import {
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
uni.showToast({
|
||||
title: $t('auth.pleaseUseInWechat'),
|
||||
title: t('auth.pleaseUseInWechat'),
|
||||
icon: 'none'
|
||||
})
|
||||
// #endif
|
||||
@@ -450,13 +405,13 @@ import {
|
||||
const avatarLocalPath = e?.detail?.avatarUrl
|
||||
if (!avatarLocalPath) {
|
||||
uni.showToast({
|
||||
title: '未选择头像',
|
||||
title: t('user.noAvatar'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
uni.showLoading({
|
||||
title: $t('common.uploading'),
|
||||
title: t('common.uploading'),
|
||||
mask: true
|
||||
})
|
||||
const uploadRes = await uploadUserAvatar(avatarLocalPath)
|
||||
@@ -469,14 +424,13 @@ import {
|
||||
uni.setStorageSync('userInfo', userInfo.value)
|
||||
}
|
||||
uni.showToast({
|
||||
title: '头像已更新',
|
||||
title: t('user.avatarUpdated'),
|
||||
icon: 'success'
|
||||
})
|
||||
await getInfo()
|
||||
} catch (err) {
|
||||
console.error('选择/上传头像失败:', err)
|
||||
uni.showToast({
|
||||
title: '头像更新失败',
|
||||
title: t('user.avatarUploadFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
@@ -494,14 +448,12 @@ import {
|
||||
|
||||
// 弹窗打开事件处理
|
||||
const onPopupOpen = () => {
|
||||
console.log('授权弹窗已打开');
|
||||
isPopupVisible.value = true;
|
||||
// 这里可以添加弹窗打开后的逻辑
|
||||
};
|
||||
|
||||
// 弹窗关闭事件处理
|
||||
const onPopupClose = () => {
|
||||
console.log('授权弹窗已关闭');
|
||||
isPopupVisible.value = false;
|
||||
// 这里可以添加弹窗关闭后的逻辑
|
||||
};
|
||||
@@ -510,7 +462,7 @@ import {
|
||||
const getUserProfile = () => {
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.showLoading({
|
||||
title: $t('common.getting'),
|
||||
title: t('common.getting'),
|
||||
mask: true
|
||||
});
|
||||
|
||||
@@ -524,7 +476,7 @@ import {
|
||||
fail: (err) => {
|
||||
console.error('获取用户信息失败:', err);
|
||||
uni.showToast({
|
||||
title: '获取用户信息失败',
|
||||
title: t('user.getUserInfoFailed'),
|
||||
icon: 'none'
|
||||
});
|
||||
},
|
||||
@@ -537,7 +489,7 @@ import {
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
uni.showToast({
|
||||
title: $t('auth.pleaseUseInWechat'),
|
||||
title: t('auth.pleaseUseInWechat'),
|
||||
icon: 'none'
|
||||
});
|
||||
closeAuthPopup();
|
||||
@@ -566,7 +518,7 @@ import {
|
||||
// });
|
||||
|
||||
uni.showToast({
|
||||
title: $t('user.updateSuccess'),
|
||||
title: t('user.updateSuccess'),
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
@@ -575,7 +527,7 @@ import {
|
||||
} catch (error) {
|
||||
console.error('更新用户信息失败:', error);
|
||||
uni.showToast({
|
||||
title: $t('user.updateFailed'),
|
||||
title: t('user.updateFailed'),
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
@@ -587,7 +539,7 @@ import {
|
||||
const avatarUrl = wxUserInfo?.avatarUrl
|
||||
if (!avatarUrl) {
|
||||
uni.showToast({
|
||||
title: '未获取到头像地址',
|
||||
title: t('user.noAvatarUrl'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
@@ -601,7 +553,7 @@ import {
|
||||
resolve(res.tempFilePath)
|
||||
return
|
||||
}
|
||||
reject(new Error('头像下载失败'))
|
||||
reject(new Error(t('user.avatarDownloadFailed')))
|
||||
},
|
||||
fail: reject
|
||||
})
|
||||
@@ -618,14 +570,13 @@ import {
|
||||
uni.setStorageSync('userInfo', userInfo.value)
|
||||
}
|
||||
uni.showToast({
|
||||
title: '头像已更新',
|
||||
title: t('user.avatarUpdated'),
|
||||
icon: 'success'
|
||||
})
|
||||
await getInfo()
|
||||
} catch (error) {
|
||||
console.error('头像上传失败:', error)
|
||||
uni.showToast({
|
||||
title: '头像上传失败',
|
||||
title: t('user.avatarUploadFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
@@ -644,7 +595,7 @@ import {
|
||||
// 关于我们
|
||||
const handleAboutUs = () => {
|
||||
uni.showToast({
|
||||
title: $t('help.functionDeveloping'),
|
||||
title: t('help.functionDeveloping'),
|
||||
icon: 'none'
|
||||
});
|
||||
};
|
||||
@@ -652,7 +603,7 @@ import {
|
||||
// 隐私政策
|
||||
const handlePrivacyPolicy = () => {
|
||||
uni.showToast({
|
||||
title: $t('help.functionDeveloping'),
|
||||
title: t('help.functionDeveloping'),
|
||||
icon: 'none'
|
||||
});
|
||||
};
|
||||
|
||||
+154
-87
@@ -4,7 +4,13 @@
|
||||
<view class="page-header">
|
||||
<view class="header-top">
|
||||
<view class="header-left">
|
||||
<view class="header-title">{{ getOrderStatusText() }}</view>
|
||||
<view class="header-title-row">
|
||||
<text class="header-title">{{ getOrderStatusText() }}</text>
|
||||
<!-- 优惠券/会员卡可用提示标签 -->
|
||||
<view v-if="orderInfo.orderStatus === 'in_used' && canUsePromotionTag" class="promotion-tag">
|
||||
{{ $t('order.canUsePromotion') }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="header-desc">{{ getStatusDesc() }}</view>
|
||||
</view>
|
||||
<view class="header-right" v-if="orderInfo.orderStatus === 'in_used'">
|
||||
@@ -78,6 +84,14 @@
|
||||
<view class="rent-label">{{ $t('order.paymentMethod') }}</view>
|
||||
<view class="rent-value">{{ getPayWayText() }}</view>
|
||||
</view>
|
||||
<!-- 优惠信息显示 - 订单完成且使用了优惠 -->
|
||||
<view class="rent-item" v-if="isOrderCompleted() && orderInfo.discountTypeName">
|
||||
<view class="rent-label">{{ $t('order.usedPromotion') }}</view>
|
||||
<view class="rent-value promotion-value">
|
||||
<image src="/static/promotion-icon.png" class="promotion-icon" mode="aspectFit"></image>
|
||||
{{ orderInfo.discountTypeName }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="rent-item" v-if="isOrderCompleted() && orderInfo.endTime">
|
||||
<view class="rent-label">{{ $t('order.returnTime') }}</view>
|
||||
<view class="rent-value">{{ orderInfo.endTime }}</view>
|
||||
@@ -94,7 +108,7 @@
|
||||
</view>
|
||||
<view class="">
|
||||
<view class="" style="font-size: 24rpx;text-align: center;">
|
||||
产品归还入仓后,订单仍未结束,请前往<text @click="contactService" style="color:#07c160 ;">客服中心</text>联系工作人员。
|
||||
{{ $t('order.returnProblemTip') }}<text @click="contactService" style="color:#07c160 ;">{{ $t('user.customerService') }}</text>{{ $t('order.contactStaff') }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -209,8 +223,12 @@
|
||||
cancelOrder,
|
||||
reportDeviceNoEject,
|
||||
convertToOwned,
|
||||
closeWithMaxFee
|
||||
closeWithMaxFee,
|
||||
getInUseOrder
|
||||
} from '@/config/api/order.js'
|
||||
import {
|
||||
withdrawDeposit
|
||||
} from '@/config/api/user.js'
|
||||
import {
|
||||
addUserFeedback
|
||||
} from '@/config/api/feedback.js'
|
||||
@@ -222,7 +240,7 @@
|
||||
} from "@/config/url.js"
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
const { t } = useI18n()
|
||||
const instance = getCurrentInstance()
|
||||
const $orderMonitor = instance?.proxy?.$orderMonitor || null
|
||||
|
||||
@@ -251,7 +269,12 @@
|
||||
isSupportExpressReturn: 'yes',
|
||||
freeRentTime: '',
|
||||
unitPrice: '',
|
||||
orderType: ''
|
||||
orderType: '',
|
||||
canUseMember: false,
|
||||
canUseCoupon: false,
|
||||
userMemberCardId: '',
|
||||
userPurchaseId: '',
|
||||
discountTypeName: ''
|
||||
})
|
||||
const timer = ref(null)
|
||||
const statusCheckTimer = ref(null)
|
||||
@@ -264,8 +287,28 @@
|
||||
const countdownRemaining = ref(0)
|
||||
const showExpressAction = ref(false)
|
||||
const countdownTimer = ref(null)
|
||||
const feeRuleText = ref('5.0元/60分钟 前15分钟内归还免费 不足60分钟按60分钟计费 封顶99元 持续计费至99元视为买断')
|
||||
const feeRuleText = ref('')
|
||||
const convertToOwnPopup = ref(null)
|
||||
|
||||
// 计算属性:是否显示优惠券/会员卡可用提示
|
||||
const canUsePromotionTag = computed(() => {
|
||||
return orderInfo.value.canUseMember === true || orderInfo.value.canUseCoupon === true
|
||||
})
|
||||
|
||||
// 计算属性:是否使用了优惠
|
||||
const hasUsedPromotion = computed(() => {
|
||||
return !!(orderInfo.value.userMemberCardId || orderInfo.value.userPurchaseId)
|
||||
})
|
||||
// 获取已使用优惠的文本
|
||||
const getUsedPromotionText = () => {
|
||||
if (orderInfo.value.userMemberCardId) {
|
||||
return t('user.myCards')
|
||||
} else if (orderInfo.value.userPurchaseId) {
|
||||
return t('user.myCoupons')
|
||||
}
|
||||
return '-'
|
||||
}
|
||||
|
||||
// 判断订单是否已完成
|
||||
const isOrderCompleted = () => {
|
||||
return orderInfo.value.orderStatus === 'used_done' ||
|
||||
@@ -275,27 +318,27 @@
|
||||
// 获取订单状态文字
|
||||
const getOrderStatusText = () => {
|
||||
const statusMap = {
|
||||
'waiting_for_payment': $t('order.waitingForPayment'),
|
||||
'payment_in_progress': $t('order.paymentInProgress'),
|
||||
'payment_successful': $t('order.paymentSuccess'),
|
||||
'in_used': $t('order.inUse'),
|
||||
'payment_failed': $t('order.paymentFailed'),
|
||||
'order_cancelled': $t('order.cancelled'),
|
||||
'used_done': $t('order.finished'),
|
||||
'used_down': $t('order.finished')
|
||||
'waiting_for_payment': t('order.waitingForPayment'),
|
||||
'payment_in_progress': t('order.paymentInProgress'),
|
||||
'payment_successful': t('order.paymentSuccess'),
|
||||
'in_used': t('order.inUse'),
|
||||
'payment_failed': t('order.paymentFailed'),
|
||||
'order_cancelled': t('order.cancelled'),
|
||||
'used_done': t('order.finished'),
|
||||
'used_down': t('order.finished')
|
||||
}
|
||||
return statusMap[orderInfo.value.orderStatus] || $t('order.orderDetail')
|
||||
return statusMap[orderInfo.value.orderStatus] || t('order.orderDetail')
|
||||
}
|
||||
|
||||
// 获取状态描述
|
||||
const getStatusDesc = () => {
|
||||
const descMap = {
|
||||
'waiting_for_payment': $t('order.pleasePaySoon'),
|
||||
'in_used': $t('order.pleaseReturnInTime'),
|
||||
'used_done': $t('order.returnedThankYou'),
|
||||
'used_down': $t('order.returnedThankYou'),
|
||||
'order_cancelled': $t('order.orderCancelled'),
|
||||
'payment_failed': $t('order.paymentFailedRetry')
|
||||
'waiting_for_payment': t('order.pleasePaySoon'),
|
||||
'in_used': t('order.pleaseReturnInTime'),
|
||||
'used_done': t('order.returnedThankYou'),
|
||||
'used_down': t('order.returnedThankYou'),
|
||||
'order_cancelled': t('order.orderCancelled'),
|
||||
'payment_failed': t('order.paymentFailedRetry')
|
||||
}
|
||||
return descMap[orderInfo.value.orderStatus] || ''
|
||||
}
|
||||
@@ -303,11 +346,11 @@
|
||||
// 获取支付方式文本
|
||||
const getPayWayText = () => {
|
||||
const payWayMap = {
|
||||
'wx_score_pay': $t('order.depositFree'),
|
||||
'wx_member_pay': $t('order.memberOrder'),
|
||||
'wx_pay': $t('order.depositPay')
|
||||
'wx_score_pay': t('order.depositFree'),
|
||||
'wx_member_pay': t('order.memberOrder'),
|
||||
'wx_pay': t('order.depositPay')
|
||||
}
|
||||
return payWayMap[orderInfo.value.payWay] || $t('order.depositFree')
|
||||
return payWayMap[orderInfo.value.payWay] || t('order.depositFree')
|
||||
}
|
||||
|
||||
// 获取免费时长显示
|
||||
@@ -321,13 +364,13 @@
|
||||
if (!orderInfo.value.unitPrice || !orderInfo.value.orderType) return '-'
|
||||
|
||||
const orderTypeMap = {
|
||||
'hours': $t('time.hours'),
|
||||
'minutes': $t('time.minutes'),
|
||||
'halfhours': $t('time.halfHours')
|
||||
'hours': t('time.hours'),
|
||||
'minutes': t('time.minutes'),
|
||||
'halfhours': t('time.halfHours')
|
||||
}
|
||||
|
||||
const orderTypeText = orderTypeMap[orderInfo.value.orderType] || orderInfo.value.orderType
|
||||
return `${orderInfo.value.unitPrice}${$t('unit.yuan')}/${orderTypeText}`
|
||||
return `${orderInfo.value.unitPrice}${t('unit.yuan')}/${orderTypeText}`
|
||||
}
|
||||
|
||||
// 格式化倒计时(显示为 HH:MM:SS 格式)
|
||||
@@ -378,21 +421,21 @@
|
||||
if (totalMinutes >= 60) {
|
||||
const hours = Math.floor(totalMinutes / 60)
|
||||
const minutes = totalMinutes % 60
|
||||
usedTime = minutes > 0 ? `${hours}小时${minutes}分钟` : `${hours}小时`
|
||||
usedTime = minutes > 0 ? `${hours}${t('time.hour')}${minutes}${t('time.minute')}` : `${hours}${t('time.hour')}`
|
||||
} else {
|
||||
usedTime = `${totalMinutes}分钟`
|
||||
usedTime = `${totalMinutes}${t('time.minute')}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果还是没有值,使用默认值
|
||||
if (!usedTime) {
|
||||
usedTime = '0分钟'
|
||||
usedTime = `0${t('time.minute')}`
|
||||
}
|
||||
|
||||
// 解析时长字符串,例如 "1小时5分钟" 或 "5分钟"
|
||||
const hourMatch = usedTime.match(/(\d+)小时/)
|
||||
const minuteMatch = usedTime.match(/(\d+)分钟/)
|
||||
const hourMatch = usedTime.match(new RegExp(`(\\d+)${t('time.hour')}`))
|
||||
const minuteMatch = usedTime.match(new RegExp(`(\\d+)${t('time.minute')}`))
|
||||
|
||||
let displayNumber = ''
|
||||
let displayUnit = ''
|
||||
@@ -400,19 +443,19 @@
|
||||
if (hourMatch && minuteMatch) {
|
||||
// 有小时也有分钟,如 "1小时5分钟"
|
||||
displayNumber = `${hourMatch[1]}`
|
||||
displayUnit = `小时${minuteMatch[1]}分钟`
|
||||
displayUnit = `${t('time.hour')}${minuteMatch[1]}${t('time.minute')}`
|
||||
} else if (hourMatch) {
|
||||
// 只有小时,如 "1小时"
|
||||
displayNumber = hourMatch[1]
|
||||
displayUnit = '小时'
|
||||
displayUnit = t('time.hour')
|
||||
} else if (minuteMatch) {
|
||||
// 只有分钟,如 "5分钟"
|
||||
displayNumber = minuteMatch[1]
|
||||
displayUnit = '分钟'
|
||||
displayUnit = t('time.minute')
|
||||
} else {
|
||||
// 默认情况
|
||||
displayNumber = '0'
|
||||
displayUnit = '分钟'
|
||||
displayUnit = t('time.minute')
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -424,7 +467,7 @@
|
||||
// 获取使用时长标签文本
|
||||
const getUsedTimeLabel = () => {
|
||||
// 使用中状态显示"已使用",已完成状态显示"使用时长"
|
||||
return orderInfo.value.orderStatus === 'in_used' ? $t('order.used') : $t('order.duration')
|
||||
return orderInfo.value.orderStatus === 'in_used' ? t('order.used') : t('order.duration')
|
||||
}
|
||||
|
||||
// 获取订单费用(不含单位)
|
||||
@@ -663,7 +706,7 @@
|
||||
if (currentStatusChecks.value >= maxStatusChecks.value) {
|
||||
clearStatusCheckTimer()
|
||||
uni.showToast({
|
||||
title: $t('order.pleaseRefreshManually'),
|
||||
title: t('order.pleaseRefreshManually'),
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
})
|
||||
@@ -709,6 +752,13 @@
|
||||
orderInfo.value.unitPrice = orderData.unitPrice || ''
|
||||
orderInfo.value.orderType = orderData.orderType || ''
|
||||
|
||||
// 保存优惠券/会员卡相关信息
|
||||
orderInfo.value.canUseMember = orderData.canUseMember === true
|
||||
orderInfo.value.canUseCoupon = orderData.canUseCoupon === true
|
||||
orderInfo.value.userMemberCardId = orderData.userMemberCardId || ''
|
||||
orderInfo.value.userPurchaseId = orderData.userPurchaseId || ''
|
||||
orderInfo.value.discountTypeName = orderData.discountTypeName || ''
|
||||
|
||||
// 保存快递归还开始时间(小时为单位)
|
||||
orderInfo.value.expressReturnStart = orderData.expressReturnStart || null
|
||||
|
||||
@@ -741,7 +791,7 @@
|
||||
// 显示订单完成提示
|
||||
if (orderInfo.value.orderStatus === 'used_done' || orderInfo.value.orderStatus === 'used_down') {
|
||||
uni.showToast({
|
||||
title: $t('order.orderCompleted'),
|
||||
title: t('order.orderCompleted'),
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
@@ -763,7 +813,7 @@
|
||||
|
||||
try {
|
||||
if (!orderInfo.value.orderId) {
|
||||
throw new Error($t('order.orderIdRequired'))
|
||||
throw new Error(t('order.orderIdRequired'))
|
||||
}
|
||||
|
||||
isLoadingOrder.value = true
|
||||
@@ -813,12 +863,12 @@
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Error(result.msg || $t('order.getOrderFailed'))
|
||||
throw new Error(result.msg || t('order.getOrderFailed'))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取订单详情错误:', error)
|
||||
uni.showToast({
|
||||
title: error.message || $t('order.getOrderFailed'),
|
||||
title: error.message || t('order.getOrderFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
setTimeout(() => {
|
||||
@@ -834,22 +884,15 @@
|
||||
const getOrderByDevice = async () => {
|
||||
try {
|
||||
if (!deviceId.value) {
|
||||
throw new Error($t('device.deviceNoRequired'))
|
||||
throw new Error(t('device.deviceNoRequired'))
|
||||
}
|
||||
|
||||
const inUseRes = await uni.request({
|
||||
url: `${URL || 'http://127.0.0.1:8080'}/app/order/inUse`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Authorization': "Bearer " + uni.getStorageSync('token'),
|
||||
'Clientid': uni.getStorageSync('client_id')
|
||||
}
|
||||
})
|
||||
const inUseRes = await getInUseOrder()
|
||||
|
||||
console.log('通过设备号查询订单结果:', JSON.stringify(inUseRes))
|
||||
|
||||
if (inUseRes.statusCode === 200 && inUseRes.data.code === 200 && inUseRes.data.data) {
|
||||
const inUseOrder = inUseRes.data.data
|
||||
if (inUseRes && inUseRes.code === 200 && inUseRes.data) {
|
||||
const inUseOrder = inUseRes.data
|
||||
console.log('使用中的订单:', inUseOrder)
|
||||
|
||||
orderInfo.value.orderId = inUseOrder.orderId
|
||||
@@ -859,12 +902,12 @@
|
||||
|
||||
getOrderDetails()
|
||||
} else {
|
||||
throw new Error($t('order.noOrderInUse'))
|
||||
throw new Error(t('order.noOrderInUse'))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('通过设备号查询订单失败:', error)
|
||||
uni.showToast({
|
||||
title: error.message || $t('order.getOrderFailed'),
|
||||
title: error.message || t('order.getOrderFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
setTimeout(() => {
|
||||
@@ -885,13 +928,13 @@
|
||||
// 取消订单
|
||||
const handleCancelOrder = () => {
|
||||
uni.showModal({
|
||||
title: $t('order.confirmCancel'),
|
||||
content: $t('order.confirmCancelContent'),
|
||||
title: t('order.confirmCancel'),
|
||||
content: t('order.confirmCancelContent'),
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: $t('common.processing')
|
||||
title: t('common.processing')
|
||||
})
|
||||
const result = await cancelOrder({
|
||||
orderId: orderInfo.value.orderId
|
||||
@@ -899,17 +942,17 @@
|
||||
if (result.code === 200) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: $t('order.cancelSuccess'),
|
||||
title: t('order.cancelSuccess'),
|
||||
icon: 'success'
|
||||
})
|
||||
await getOrderDetails()
|
||||
} else {
|
||||
throw new Error(result.msg || $t('order.cancelFailed'))
|
||||
throw new Error(result.msg || t('order.cancelFailed'))
|
||||
}
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: error.message || $t('order.cancelFailed'),
|
||||
title: error.message || t('order.cancelFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
@@ -929,22 +972,14 @@
|
||||
const handleWithdraw = async () => {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: $t('common.processing')
|
||||
title: t('common.processing')
|
||||
})
|
||||
|
||||
const res = await uni.request({
|
||||
url: `${URL || 'http://127.0.0.1:8080'}/app/withdraw/add/${orderInfo.value.orderNo}`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ' + uni.getStorageSync('token'),
|
||||
'Clientid': uni.getStorageSync('client_id')
|
||||
}
|
||||
})
|
||||
const res = await withdrawDeposit(orderInfo.value.orderNo)
|
||||
|
||||
if (res.statusCode === 200 && res.data.code === 200) {
|
||||
if (res && res.code === 200) {
|
||||
uni.showToast({
|
||||
title: $t('order.refundSuccess'),
|
||||
title: t('order.refundSuccess'),
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
@@ -955,12 +990,12 @@
|
||||
getOrderDetails()
|
||||
}, 1500)
|
||||
} else {
|
||||
throw new Error(res.data.msg || $t('order.refundFailed'))
|
||||
throw new Error(res?.msg || t('order.refundFailed'))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('退款申请错误:', error)
|
||||
console.error('退款申请失败:', error)
|
||||
uni.showToast({
|
||||
title: error.message || $t('order.refundFailed'),
|
||||
title: error.message || t('order.refundFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
@@ -1002,14 +1037,14 @@
|
||||
})
|
||||
|
||||
uni.showToast({
|
||||
title: '订阅成功',
|
||||
title: t('payment.subscriptionSuccess'),
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
} catch (error) {
|
||||
console.log('订阅消息异常', error)
|
||||
uni.showToast({
|
||||
title: '订阅失败,请稍后重试',
|
||||
title: t('payment.subscriptionFailed'),
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
@@ -1040,7 +1075,7 @@
|
||||
try {
|
||||
closeConvertToOwnPopup()
|
||||
uni.showLoading({
|
||||
title: $t('common.processing')
|
||||
title: t('common.processing')
|
||||
})
|
||||
|
||||
const result = await closeWithMaxFee(orderInfo.value.orderNo)
|
||||
@@ -1048,7 +1083,7 @@
|
||||
if (result.code === 200) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: $t('order.convertToOwnWithMaxFeeSuccess'),
|
||||
title: t('order.convertToOwnWithMaxFeeSuccess'),
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
@@ -1058,12 +1093,12 @@
|
||||
getOrderDetails()
|
||||
}, 2000)
|
||||
} else {
|
||||
throw new Error(result.msg || $t('order.convertToOwnWithMaxFeeFailed'))
|
||||
throw new Error(result.msg || t('order.convertToOwnWithMaxFeeFailed'))
|
||||
}
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: error.message || $t('order.convertToOwnWithMaxFeeFailed'),
|
||||
title: error.message || t('order.convertToOwnWithMaxFeeFailed'),
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
@@ -1076,7 +1111,7 @@
|
||||
|
||||
// 设置页面标题
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t('order.orderDetail')
|
||||
title: t('order.orderDetail')
|
||||
})
|
||||
|
||||
isPageActive.value = true
|
||||
@@ -1101,7 +1136,7 @@
|
||||
getOrderDetails()
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: $t('order.orderInfoMissing'),
|
||||
title: t('order.orderInfoMissing'),
|
||||
icon: 'none'
|
||||
})
|
||||
setTimeout(() => {
|
||||
@@ -1161,12 +1196,18 @@
|
||||
.header-left {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.header-title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 48rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.header-desc {
|
||||
@@ -1210,6 +1251,18 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.promotion-tag {
|
||||
padding: 6rpx 16rpx;
|
||||
background: linear-gradient(135deg, #FFF4E6 0%, #FFE9CC 100%);
|
||||
border-radius: 10rpx;
|
||||
border: 1rpx solid #FFB84D;
|
||||
font-size: 22rpx;
|
||||
color: #FF8C00;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
// 订单信息卡片
|
||||
.info-card {
|
||||
@@ -1355,6 +1408,20 @@
|
||||
color: #333;
|
||||
text-align: right;
|
||||
max-width: 400rpx;
|
||||
|
||||
&.promotion-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
color: #FF8C00;
|
||||
font-weight: 500;
|
||||
|
||||
.promotion-icon {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+35
-41
@@ -46,7 +46,8 @@
|
||||
getOrderList,
|
||||
queryById,
|
||||
getOrderByOrderNoScorePayStatus,
|
||||
cancelOrder
|
||||
cancelOrder,
|
||||
createWxPayment
|
||||
} from '../../config/api/order.js';
|
||||
import {
|
||||
confirmPaymentAndRent
|
||||
@@ -59,7 +60,7 @@
|
||||
} from '../../config/url.js';
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
// 初始化状态
|
||||
const currentTab = ref(0);
|
||||
@@ -68,62 +69,62 @@
|
||||
// 订单状态映射
|
||||
const orderStatusMap = reactive({
|
||||
'0': {
|
||||
get text() { return $t('order.waitingForPayment') },
|
||||
get text() { return t('order.waitingForPayment') },
|
||||
class: 'status-waiting'
|
||||
},
|
||||
'1': {
|
||||
get text() { return $t('order.inUse') },
|
||||
get text() { return t('order.inUse') },
|
||||
class: 'status-using'
|
||||
},
|
||||
'2': {
|
||||
get text() { return $t('order.finished') },
|
||||
get text() { return t('order.finished') },
|
||||
class: 'status-finished'
|
||||
},
|
||||
'3': {
|
||||
get text() { return $t('order.cancelled') },
|
||||
get text() { return t('order.cancelled') },
|
||||
class: 'status-cancelled'
|
||||
},
|
||||
'waiting_for_payment': {
|
||||
get text() { return $t('order.waitingForPayment') },
|
||||
get text() { return t('order.waitingForPayment') },
|
||||
class: 'status-waiting'
|
||||
},
|
||||
'in_used': {
|
||||
get text() { return $t('order.inUse') },
|
||||
get text() { return t('order.inUse') },
|
||||
class: 'status-using'
|
||||
},
|
||||
'used_done': {
|
||||
get text() { return $t('order.finished') },
|
||||
get text() { return t('order.finished') },
|
||||
class: 'status-finished'
|
||||
},
|
||||
'order_cancelled': {
|
||||
get text() { return $t('order.cancelled') },
|
||||
get text() { return t('order.cancelled') },
|
||||
class: 'status-cancelled'
|
||||
},
|
||||
'express_return': {
|
||||
get text() { return $t('express.title') },
|
||||
get text() { return t('express.title') },
|
||||
class: 'status-express-return'
|
||||
}
|
||||
});
|
||||
|
||||
// 订单状态标签
|
||||
const orderStatusTabs = reactive([{
|
||||
get text() { return $t('common.all') },
|
||||
get text() { return t('common.all') },
|
||||
status: []
|
||||
},
|
||||
{
|
||||
get text() { return $t('order.waitingForPayment') },
|
||||
get text() { return t('order.waitingForPayment') },
|
||||
status: ['waiting_for_payment']
|
||||
},
|
||||
{
|
||||
get text() { return $t('order.inUse') },
|
||||
get text() { return t('order.inUse') },
|
||||
status: ['in_used']
|
||||
},
|
||||
{
|
||||
get text() { return $t('order.finished') },
|
||||
get text() { return t('order.finished') },
|
||||
status: ['used_done']
|
||||
},
|
||||
{
|
||||
get text() { return $t('order.cancelled') },
|
||||
get text() { return t('order.cancelled') },
|
||||
status: ['order_cancelled']
|
||||
}
|
||||
]);
|
||||
@@ -216,7 +217,7 @@
|
||||
} catch (error) {
|
||||
console.error('获取订单列表失败:', error);
|
||||
uni.showToast({
|
||||
title: $t('order.getOrderListFailed'),
|
||||
title: t('order.getOrderListFailed'),
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
@@ -233,7 +234,7 @@
|
||||
// 设置页面标题并监听订单完成事件
|
||||
onMounted(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t('order.myOrders')
|
||||
title: t('order.myOrders')
|
||||
})
|
||||
|
||||
// 监听订单完成事件
|
||||
@@ -251,14 +252,14 @@
|
||||
const res = await getOrderByOrderNoScorePayStatus(order.orderNo);
|
||||
if (res.code === 200) {
|
||||
uni.showToast({
|
||||
title: $t('order.syncSuccess'),
|
||||
title: t('order.syncSuccess'),
|
||||
icon: 'success'
|
||||
});
|
||||
await loadOrderList(orderStatusTabs[currentTab.value].status);
|
||||
}
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: $t('order.syncFailed'),
|
||||
title: t('order.syncFailed'),
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
@@ -285,28 +286,21 @@
|
||||
const handlePayment = async (order) => {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: $t('common.processing')
|
||||
title: t('common.processing')
|
||||
});
|
||||
|
||||
// 调用后端创建微信支付订单接口
|
||||
const res = await uni.request({
|
||||
url: `${URL || 'http://127.0.0.1:8080'}/app/wx-payment/create/${order.orderNo}`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Authorization': "Bearer " + uni.getStorageSync('token'),
|
||||
'Clientid': uni.getStorageSync('client_id')
|
||||
}
|
||||
});
|
||||
const res = await createWxPayment(order.orderNo);
|
||||
|
||||
if (res.statusCode === 200 && res.data.code === 200) {
|
||||
const payParams = res.data.data;
|
||||
if (res && res.code === 200) {
|
||||
const payParams = res.data;
|
||||
|
||||
// 调用微信支付
|
||||
await uni.requestPayment({
|
||||
...payParams,
|
||||
success: async () => {
|
||||
uni.showToast({
|
||||
title: $t('payment.paymentSuccess'),
|
||||
title: t('payment.paymentSuccess'),
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
@@ -322,18 +316,18 @@
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('支付失败:', err);
|
||||
throw new Error($t('payment.paymentFailedRetry'));
|
||||
throw new Error(t('payment.paymentFailedRetry'));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw new Error(res.data.msg || '创建支付订单失败');
|
||||
throw new Error(res?.msg || '创建支付订单失败');
|
||||
}
|
||||
|
||||
uni.hideLoading();
|
||||
} catch (error) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: error.message || $t('payment.paymentFailed'),
|
||||
title: error.message || t('payment.paymentFailed'),
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
@@ -343,12 +337,12 @@
|
||||
const handleCancelOrder = async (order) => {
|
||||
try {
|
||||
uni.showModal({
|
||||
title: $t('order.confirmCancel'),
|
||||
content: $t('order.confirmCancelContent'),
|
||||
title: t('order.confirmCancel'),
|
||||
content: t('order.confirmCancelContent'),
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showLoading({
|
||||
title: $t('common.processing')
|
||||
title: t('common.processing')
|
||||
});
|
||||
|
||||
const result = await cancelOrder({
|
||||
@@ -358,14 +352,14 @@
|
||||
if (result) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: $t('order.cancelSuccess'),
|
||||
title: t('order.cancelSuccess'),
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
// 刷新订单列表
|
||||
await loadOrderList();
|
||||
} else {
|
||||
throw new Error(result.msg || $t('order.cancelFailed'));
|
||||
throw new Error(result.msg || t('order.cancelFailed'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -373,7 +367,7 @@
|
||||
} catch (error) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: error.message || $t('order.cancelFailed'),
|
||||
title: error.message || t('order.cancelFailed'),
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
|
||||
+196
-322
@@ -59,345 +59,219 @@
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { queryById, updateOrderPackage } from '@/config/api/order.js'
|
||||
<script setup>
|
||||
import { ref, computed, reactive } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { queryById, createWxPayment } from '@/config/api/order.js'
|
||||
import { getDeviceInfo } from '@/config/api/device.js'
|
||||
import { updateUserBalance } from '@/config/api/user.js'
|
||||
import {
|
||||
URL
|
||||
}from"@/config/url.js"
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
orderId: null,
|
||||
deviceNo: null,
|
||||
orderInfo: {},
|
||||
packageInfo: {
|
||||
time: '',
|
||||
price: '0.00'
|
||||
},
|
||||
deviceInfo: null,
|
||||
passedTotalAmount: null,
|
||||
passedDepositAmount: null,
|
||||
orderStatus: {
|
||||
get text() { return this.$t('payment.waitingForPayment') },
|
||||
get desc() { return this.$t('payment.pleasePayIn15Min') },
|
||||
class: 'waiting'
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
totalAmount() {
|
||||
if (this.passedTotalAmount !== null) {
|
||||
return parseFloat(this.passedTotalAmount).toFixed(2);
|
||||
}
|
||||
const deposit = parseFloat(this.orderInfo.deposit || this.passedDepositAmount || 99)
|
||||
const packagePrice = parseFloat(this.packageInfo.price || 0)
|
||||
return (deposit + packagePrice).toFixed(2)
|
||||
},
|
||||
// 计算每小时的价格
|
||||
hourlyRate() {
|
||||
const price = parseFloat(this.packageInfo.price || 0);
|
||||
let time = parseFloat(this.packageInfo.time || 1);
|
||||
|
||||
// 如果时间单位不是小时(例如分钟),需要转换
|
||||
if (this.packageInfo.time && this.packageInfo.time.includes('分钟')) {
|
||||
time = time / 60; // 将分钟转换为小时
|
||||
} else if (this.packageInfo.time && this.packageInfo.time.includes('次')) {
|
||||
// 按次计费时,暂时显示单次价格
|
||||
return price.toFixed(2);
|
||||
}
|
||||
|
||||
// 避免除以零
|
||||
if (time <= 0) time = 1;
|
||||
|
||||
// 计算每小时价格
|
||||
return (price / time).toFixed(2);
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
// 设置页面标题
|
||||
uni.setNavigationBarTitle({
|
||||
title: this.$t('payment.orderPayment')
|
||||
const { t } = useI18n()
|
||||
|
||||
const orderId = ref(null)
|
||||
const deviceNo = ref(null)
|
||||
const orderInfo = ref({})
|
||||
const packageInfo = ref({
|
||||
time: '',
|
||||
price: '0.00'
|
||||
})
|
||||
const deviceInfo = ref(null)
|
||||
const passedTotalAmount = ref(null)
|
||||
const passedDepositAmount = ref(null)
|
||||
|
||||
const orderStatus = reactive({
|
||||
get text() { return t('payment.waitingForPayment') },
|
||||
get desc() { return t('payment.pleasePayIn15Min') },
|
||||
class: 'waiting'
|
||||
})
|
||||
|
||||
const totalAmount = computed(() => {
|
||||
if (passedTotalAmount.value !== null) {
|
||||
return parseFloat(passedTotalAmount.value).toFixed(2);
|
||||
}
|
||||
const deposit = parseFloat(orderInfo.value.deposit || passedDepositAmount.value || 99)
|
||||
const packagePrice = parseFloat(packageInfo.value.price || 0)
|
||||
return (deposit + packagePrice).toFixed(2)
|
||||
})
|
||||
|
||||
// 加载订单信息
|
||||
const loadOrderInfo = async () => {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: t('common.loading')
|
||||
})
|
||||
|
||||
if (options && options.orderId) {
|
||||
this.orderId = options.orderId
|
||||
const res = await queryById(orderId.value)
|
||||
if (res.code === 200 && res.data) {
|
||||
const orderData = res.data
|
||||
|
||||
if (options.totalAmount) {
|
||||
this.passedTotalAmount = options.depositAmount;
|
||||
// 处理创建时间
|
||||
let formattedTime;
|
||||
try {
|
||||
if (orderData.createTime) {
|
||||
formattedTime = formatTime(new Date(orderData.createTime));
|
||||
} else {
|
||||
formattedTime = formatTime(new Date());
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('时间格式化错误:', e);
|
||||
formattedTime = formatTime(new Date());
|
||||
}
|
||||
|
||||
if (options.depositAmount) {
|
||||
this.passedDepositAmount = options.depositAmount;
|
||||
orderInfo.value = {
|
||||
orderNo: orderData.orderNo || orderData.orderId,
|
||||
deviceNo: orderData.deviceNo,
|
||||
createTime: formattedTime,
|
||||
phone: orderData.phone,
|
||||
deposit: passedDepositAmount.value || orderData.depositAmount || '99.00',
|
||||
}
|
||||
|
||||
// 如果URL中包含了feeConfig参数,保存它
|
||||
if (options.feeConfig) {
|
||||
try {
|
||||
console.log('从URL获取到feeConfig:', options.feeConfig)
|
||||
const feeConfigStr = decodeURIComponent(options.feeConfig)
|
||||
// 创建一个临时的deviceInfo对象保存feeConfig
|
||||
this.deviceInfo = { feeConfig: feeConfigStr }
|
||||
} catch (e) {
|
||||
console.error('解析URL中的feeConfig失败:', e)
|
||||
if (orderData.packageTime && orderData.packagePrice) {
|
||||
const timeInHours = (parseFloat(orderData.packageTime) / 60).toFixed(1);
|
||||
packageInfo.value = {
|
||||
time: timeInHours.toString(),
|
||||
price: orderData.packagePrice.toString()
|
||||
}
|
||||
}
|
||||
|
||||
this.loadOrderInfo()
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: this.$t('order.orderNotExist'),
|
||||
icon: 'none'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.redirectTo({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
}, 1500)
|
||||
deviceNo.value = orderData.deviceNo;
|
||||
await loadDeviceInfo();
|
||||
} else {
|
||||
throw new Error(t('order.getOrderFailed'))
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 加载订单信息
|
||||
async loadOrderInfo() {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: this.$t('common.loading')
|
||||
})
|
||||
|
||||
const res = await queryById(this.orderId)
|
||||
if (res.code === 200 && res.data) {
|
||||
const orderData = res.data
|
||||
|
||||
// 处理创建时间,确保显示的是格式化后的时间
|
||||
let formattedTime;
|
||||
try {
|
||||
// 如果orderData.createTime存在并且是有效的日期字符串/时间戳,则格式化它
|
||||
if (orderData.createTime) {
|
||||
formattedTime = this.formatTime(new Date(orderData.createTime));
|
||||
} else {
|
||||
// 如果createTime不存在,使用当前时间作为创建时间
|
||||
formattedTime = this.formatTime(new Date());
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('时间格式化错误:', e);
|
||||
formattedTime = this.formatTime(new Date());
|
||||
}
|
||||
|
||||
this.orderInfo = {
|
||||
orderNo: orderData.orderNo || orderData.orderId,
|
||||
deviceNo: orderData.deviceNo,
|
||||
createTime: formattedTime,
|
||||
phone: orderData.phone,
|
||||
deposit: this.passedDepositAmount || orderData.depositAmount || '99.00',
|
||||
}
|
||||
|
||||
// 直接从订单数据中获取套餐信息
|
||||
if (orderData.packageTime && orderData.packagePrice) {
|
||||
// 将分钟转换为小时
|
||||
const timeInHours = (parseFloat(orderData.packageTime) / 60).toFixed(1);
|
||||
this.packageInfo = {
|
||||
time: timeInHours.toString(),
|
||||
price: orderData.packagePrice.toString()
|
||||
}
|
||||
}
|
||||
|
||||
// 获取设备信息(但不再用于设置套餐信息)
|
||||
this.deviceNo = orderData.deviceNo;
|
||||
await this.loadDeviceInfo();
|
||||
} else {
|
||||
throw new Error('获取订单信息失败')
|
||||
}
|
||||
|
||||
uni.hideLoading()
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: error.message || '获取订单信息失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// 加载设备信息
|
||||
async loadDeviceInfo() {
|
||||
if (!this.deviceNo) return;
|
||||
|
||||
try {
|
||||
const res = await getDeviceInfo(this.deviceNo);
|
||||
if (res.code === 200 && res.data) {
|
||||
this.deviceInfo = res.data.device;
|
||||
|
||||
// 设置存款金额
|
||||
if (this.deviceInfo && this.deviceInfo.depositAmount) {
|
||||
this.orderInfo.deposit = this.deviceInfo.depositAmount;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取设备信息失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 处理支付
|
||||
async handlePayment() {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: this.$t('common.processing')
|
||||
})
|
||||
|
||||
// 调用后端创建微信支付订单接口
|
||||
const res = await uni.request({
|
||||
url: `${URL || 'http://127.0.0.1:8080'}/app/wx-payment/create/${this.orderInfo.orderNo}`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Authorization': "Bearer " + uni.getStorageSync('token'),
|
||||
'Clientid': uni.getStorageSync('client_id')
|
||||
}
|
||||
})
|
||||
|
||||
if (res.statusCode === 200 && res.data.code === 200) {
|
||||
const payParams = res.data.data
|
||||
|
||||
// 调用微信支付
|
||||
await uni.requestPayment({
|
||||
...payParams,
|
||||
success: async () => {
|
||||
uni.showToast({
|
||||
title: this.$t('payment.paymentSuccess'),
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
// 更新用户余额
|
||||
try {
|
||||
await updateUserBalance(this.orderId);
|
||||
} catch (error) {
|
||||
console.warn('更新用户余额失败:', error);
|
||||
}
|
||||
|
||||
// 支付成功后直接跳转到订单页面,不再轮询
|
||||
setTimeout(() => {
|
||||
uni.redirectTo({
|
||||
url: `/pages/order/index?orderId=${this.orderId}`
|
||||
});
|
||||
}, 1500);
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('支付失败:', err)
|
||||
throw new Error(this.$t('payment.paymentFailedRetry'))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
throw new Error(res.data.msg || '创建支付订单失败')
|
||||
}
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: error.message || '支付失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// 发送租借指令
|
||||
async sendRentCommand() {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: this.$t('common.processing')
|
||||
})
|
||||
|
||||
// 调用发送租借指令的接口
|
||||
const res = await this.sendRentRequest()
|
||||
|
||||
if (res.code === 200) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: this.$t('device.rentSuccess'),
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
// 跳转到订单列表页面
|
||||
setTimeout(() => {
|
||||
uni.redirectTo({
|
||||
url: `/pages/order/index?orderId=${this.orderId}`
|
||||
})
|
||||
}, 1500)
|
||||
} else {
|
||||
throw new Error(res.msg || this.$t('device.rentFailed'))
|
||||
}
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: error.message || this.$t('device.rentFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
// 发送租借请求
|
||||
sendRentRequest() {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url: `${URL || 'http://127.0.0.1:8080'}/app/device/sendRentCommand`,
|
||||
method: 'POST',
|
||||
data: {
|
||||
orderId: this.orderId
|
||||
},
|
||||
header: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': "Bearer " + uni.getStorageSync('token'),
|
||||
'Clientid': uni.getStorageSync('client_id')
|
||||
},
|
||||
success(res) {
|
||||
if (res.statusCode === 200) {
|
||||
resolve(res.data)
|
||||
} else {
|
||||
reject(new Error('请求失败'))
|
||||
}
|
||||
},
|
||||
fail(err) {
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 格式化时间
|
||||
formatTime(date) {
|
||||
const year = date.getFullYear()
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = date.getDate().toString().padStart(2, '0')
|
||||
const hour = date.getHours().toString().padStart(2, '0')
|
||||
const minute = date.getMinutes().toString().padStart(2, '0')
|
||||
|
||||
return `${year}-${month}-${day} ${hour}:${minute}`
|
||||
},
|
||||
|
||||
// 检查订单状态(单次查询,不轮询)
|
||||
async checkOrderStatus() {
|
||||
try {
|
||||
const res = await uni.request({
|
||||
url: `${URL || 'http://127.0.0.1:8080'}/app/wx-payment/status/${this.orderInfo.orderNo}`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Authorization': "Bearer " + uni.getStorageSync('token'),
|
||||
'Clientid': uni.getStorageSync('client_id')
|
||||
}
|
||||
});
|
||||
|
||||
if (res.statusCode === 200 && res.data.code === 200) {
|
||||
return res.data.data;
|
||||
} else {
|
||||
throw new Error('查询订单状态失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('查询订单状态错误:', error);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
} catch (error) {
|
||||
console.error('获取订单信息失败:', error)
|
||||
uni.showToast({
|
||||
title: error.message || t('order.getOrderFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
uni.hideLoading()
|
||||
}
|
||||
}
|
||||
|
||||
// 加载设备信息
|
||||
const loadDeviceInfo = async () => {
|
||||
if (!deviceNo.value) return;
|
||||
|
||||
try {
|
||||
const res = await getDeviceInfo(deviceNo.value);
|
||||
if (res.code === 200 && res.data) {
|
||||
deviceInfo.value = res.data.device;
|
||||
|
||||
if (deviceInfo.value && deviceInfo.value.depositAmount) {
|
||||
orderInfo.value.deposit = deviceInfo.value.depositAmount;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取设备信息失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理支付
|
||||
const handlePayment = async () => {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: t('common.processing')
|
||||
})
|
||||
|
||||
const res = await createWxPayment(orderInfo.value.orderNo)
|
||||
|
||||
if (res && res.code === 200) {
|
||||
const payParams = res.data
|
||||
|
||||
await uni.requestPayment({
|
||||
...payParams,
|
||||
success: async () => {
|
||||
uni.showToast({
|
||||
title: t('payment.paymentSuccess'),
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
try {
|
||||
await updateUserBalance(orderId.value);
|
||||
} catch (error) {
|
||||
console.warn('更新用户余额失败:', error);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
uni.redirectTo({
|
||||
url: `/pages/order/index?orderId=${orderId.value}`
|
||||
});
|
||||
}, 1500);
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('支付失败:', err)
|
||||
uni.showToast({
|
||||
title: t('payment.paymentFailedRetry'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
throw new Error(res?.msg || t('payment.createPayOrderFailed'))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('支付失败:', error)
|
||||
uni.showToast({
|
||||
title: error.message || t('payment.paymentFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
uni.hideLoading()
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (date) => {
|
||||
const year = date.getFullYear()
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = date.getDate().toString().padStart(2, '0')
|
||||
const hour = date.getHours().toString().padStart(2, '0')
|
||||
const minute = date.getMinutes().toString().padStart(2, '0')
|
||||
|
||||
return `${year}-${month}-${day} ${hour}:${minute}`
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: t('payment.orderPayment')
|
||||
})
|
||||
|
||||
if (options && options.orderId) {
|
||||
orderId.value = options.orderId
|
||||
|
||||
if (options.totalAmount) {
|
||||
passedTotalAmount.value = options.totalAmount;
|
||||
}
|
||||
|
||||
if (options.depositAmount) {
|
||||
passedDepositAmount.value = options.depositAmount;
|
||||
}
|
||||
|
||||
if (options.feeConfig) {
|
||||
try {
|
||||
const feeConfigStr = decodeURIComponent(options.feeConfig)
|
||||
deviceInfo.value = { feeConfig: feeConfigStr }
|
||||
} catch (e) {
|
||||
console.error('解析URL中的feeConfig失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
loadOrderInfo()
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: t('order.orderNotExist'),
|
||||
icon: 'none'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.redirectTo({
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
}, 1500)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -85,6 +85,7 @@
|
||||
|
||||
<script>
|
||||
import { queryById } from '@/config/api/order.js'
|
||||
import { withdrawDeposit } from '@/config/api/user.js'
|
||||
import {
|
||||
URL
|
||||
}from"@/config/url.js"
|
||||
@@ -234,17 +235,9 @@ export default {
|
||||
try {
|
||||
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}`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ' + uni.getStorageSync('token'),
|
||||
'Clientid': uni.getStorageSync('client_id')
|
||||
}
|
||||
})
|
||||
const res = await withdrawDeposit(this.orderInfo.orderNo)
|
||||
|
||||
if (res.statusCode === 200 && res.data.code === 200) {
|
||||
if (res && res.code === 200) {
|
||||
uni.showToast({
|
||||
title: this.$t('order.refundSuccess'),
|
||||
icon: 'success'
|
||||
@@ -259,7 +252,7 @@ export default {
|
||||
this.loadOrderInfo();
|
||||
}, 1500);
|
||||
} else {
|
||||
throw new Error(res.data.msg || this.$t('order.refundFailed'));
|
||||
throw new Error(res?.msg || this.$t('order.refundFailed'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('退款申请错误:', error);
|
||||
|
||||
@@ -74,7 +74,7 @@ import {
|
||||
} from '../../config/api/device.js'
|
||||
import { useI18n } from '../../utils/i18n.js'
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
const positionInfo = ref({})
|
||||
const positionId = ref('')
|
||||
@@ -112,7 +112,7 @@ const { t: $t } = useI18n()
|
||||
const loadPositionDetail = async () => {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: $t('common.loading')
|
||||
title: t('common.loading')
|
||||
})
|
||||
|
||||
// 获取用户位置用于查询附近设备
|
||||
@@ -147,7 +147,7 @@ const { t: $t } = useI18n()
|
||||
positionInfo.value = position
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: $t('location.notExist'),
|
||||
title: t('location.notExist'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
@@ -155,7 +155,7 @@ const { t: $t } = useI18n()
|
||||
} catch (e) {
|
||||
console.error('加载设备详情失败:', e)
|
||||
uni.showToast({
|
||||
title: $t('common.loadFailed'),
|
||||
title: t('common.loadFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
@@ -166,7 +166,7 @@ const { t: $t } = useI18n()
|
||||
const navigateToPosition = () => {
|
||||
if (!positionInfo.value.latitude || !positionInfo.value.longitude) {
|
||||
uni.showToast({
|
||||
title: $t('location.coordinateError'),
|
||||
title: t('location.coordinateError'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
@@ -181,7 +181,7 @@ const { t: $t } = useI18n()
|
||||
longitude < -180 || longitude > 180 ||
|
||||
(latitude === 0 && longitude === 0)) {
|
||||
uni.showToast({
|
||||
title: $t('location.coordinateError'),
|
||||
title: t('location.coordinateError'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
|
||||
+913
-704
File diff suppressed because it is too large
Load Diff
+5
-11
@@ -143,7 +143,8 @@
|
||||
<script>
|
||||
import {
|
||||
queryById,
|
||||
cancelOrder
|
||||
cancelOrder,
|
||||
getInUseOrder
|
||||
} from '@/config/api/order.js'
|
||||
import {
|
||||
getSystemConfig
|
||||
@@ -710,19 +711,12 @@
|
||||
}
|
||||
|
||||
// 这里调用API查询该设备的使用中订单
|
||||
const inUseRes = await uni.request({
|
||||
url: `${URL || 'http://127.0.0.1:8080'}/app/order/inUse`,
|
||||
method: 'GET',
|
||||
header: {
|
||||
'Authorization': "Bearer " + uni.getStorageSync('token'),
|
||||
'Clientid': uni.getStorageSync('client_id')
|
||||
}
|
||||
})
|
||||
const inUseRes = await getInUseOrder()
|
||||
|
||||
console.log('通过设备号查询订单结果:', JSON.stringify(inUseRes))
|
||||
|
||||
if (inUseRes.statusCode === 200 && inUseRes.data.code === 200 && inUseRes.data.data) {
|
||||
const inUseOrder = inUseRes.data.data
|
||||
if (inUseRes && inUseRes.code === 200 && inUseRes.data) {
|
||||
const inUseOrder = inUseRes.data
|
||||
console.log('使用中的订单:', inUseOrder)
|
||||
|
||||
// 更新订单ID
|
||||
|
||||
+88
-77
@@ -22,52 +22,52 @@
|
||||
:class="{ available: isRentable(item), invalid: !isValidCoordinate(item.latitude, item.longitude) }"
|
||||
v-for="(item, index) in filteredPositions" :key="item.positionId || index"
|
||||
@click="goToPositionDetail(item)">
|
||||
<view class="thumb">
|
||||
<image v-if="item.deviceImg" :src="item.deviceImg" class="thumb-img" mode="aspectFill">
|
||||
</image>
|
||||
<image v-else src="/static/device-info.png" class="thumb-img" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="info">
|
||||
<view class="row top">
|
||||
<view class="name">{{ item.name }}</view>
|
||||
<!-- 第一行:三列布局 -->
|
||||
<view class="card-row-first">
|
||||
<!-- 第一列:缩略图 -->
|
||||
<view class="thumb">
|
||||
<image v-if="item.deviceImg" :src="item.deviceImg" class="thumb-img" mode="aspectFill">
|
||||
</image>
|
||||
<image v-else src="/static/device-info.png" class="thumb-img" mode="aspectFit"></image>
|
||||
</view>
|
||||
|
||||
<!-- 第二列:信息 -->
|
||||
<view class="info">
|
||||
<view class="row top">
|
||||
<view class="name">{{ item.name }}</view>
|
||||
</view>
|
||||
<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">{{ $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>
|
||||
<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">{{ $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="row meta"
|
||||
v-if="item.availablePowerBankCount !== undefined && item.availablePowerBankCount !== null">
|
||||
<text class="time">可租借:{{ item.availablePowerBankCount }} 台</text>
|
||||
</view>
|
||||
<view class="row meta"
|
||||
v-if="item.availableEmptyGridCount !== undefined && item.availableEmptyGridCount !== null">
|
||||
<text class="time">可归还:{{ item.availableEmptyGridCount }} 个空位</text>
|
||||
</view>
|
||||
<view class="row meta remark-info" v-if="item.remark">
|
||||
<text class="time">💰 {{ item.remark }}</text>
|
||||
|
||||
<!-- 第三列:操作 -->
|
||||
<view class="actions">
|
||||
<view class="nav" :class="{ disabled: !isValidCoordinate(item.latitude, item.longitude) }"
|
||||
@click.stop="navigateToPosition(item)">
|
||||
<image src="/static/luxian.png" class="action-icon" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="distance"
|
||||
v-if="item.distance && isValidCoordinate(item.latitude, item.longitude)">
|
||||
{{ item.distance }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 第二行:标签 -->
|
||||
<view class="card-row-second">
|
||||
<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 class="tag" v-if="isCoupon(item)">{{CouponOrmember(item)}}</view>
|
||||
<view class="tag coupon" v-if="item.supportCouponOrMember">{{ $t('location.supportCouponOrMember') }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="actions">
|
||||
|
||||
<view class="nav" :class="{ disabled: !isValidCoordinate(item.latitude, item.longitude) }"
|
||||
@click.stop="navigateToPosition(item)">
|
||||
<image src="/static/luxian.png" class="action-icon" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view class="distance"
|
||||
v-if="item.distance && isValidCoordinate(item.latitude, item.longitude)">
|
||||
{{ item.distance }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="empty-state" v-if="!isLoading && (!positionList || positionList.length === 0)">
|
||||
@@ -101,13 +101,13 @@
|
||||
} from '@/utils/i18n.js'
|
||||
|
||||
const {
|
||||
t: $t
|
||||
t
|
||||
} = useI18n()
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t('search.title')
|
||||
title: t('search.title')
|
||||
})
|
||||
// uni.showLoading({
|
||||
// title:'11111',
|
||||
@@ -132,11 +132,6 @@
|
||||
if (typeof item?.canReturn !== 'undefined') return !!item.canReturn
|
||||
return String(item?.status || '').toLowerCase() === 'online'
|
||||
}
|
||||
|
||||
const isCoupon = (item)=>{
|
||||
if(typeof item.canCoupon!=='undefined')return !!item.canCoupon
|
||||
return String(item?.canCoupon||'').toLowerCase()==='true';
|
||||
}
|
||||
|
||||
const formatDistance = (meters) => {
|
||||
if (meters < 1000) return `${Math.round(meters)}m`
|
||||
@@ -192,11 +187,6 @@
|
||||
positionList.value.sort((a, b) => (a.distanceInMeters || 999999) - (b.distanceInMeters || 999999))
|
||||
applyFilter()
|
||||
}
|
||||
|
||||
const CouponOrmember= async(item)=>{
|
||||
|
||||
return "可使用优惠券、会员卡"
|
||||
}
|
||||
|
||||
const loadPositions = async (center) => {
|
||||
try {
|
||||
@@ -227,8 +217,8 @@
|
||||
return {
|
||||
...transformed,
|
||||
canRent: activeTab.value === 'rent' ? true : (device.availablePowerBankCount > 0),
|
||||
canReturn: activeTab.value === 'return' ? true : (device.availableEmptyGridCount >
|
||||
0)
|
||||
canReturn: activeTab.value === 'return' ? true : (device.availableEmptyGridCount > 0),
|
||||
supportCouponOrMember: device.supportCouponOrMember || false
|
||||
}
|
||||
})
|
||||
|
||||
@@ -267,7 +257,7 @@ const init = async () => {
|
||||
positionList.value = []
|
||||
filteredPositions.value = []
|
||||
uni.showToast({
|
||||
title: $t('home.getLocationFailed'),
|
||||
title: t('home.getLocationFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
@@ -289,7 +279,7 @@ const init = async () => {
|
||||
const navigateToPosition = (position) => {
|
||||
if (!isValidCoordinate(position.latitude, position.longitude)) {
|
||||
uni.showToast({
|
||||
title: $t('search.invalidCoordinate'),
|
||||
title: t('search.invalidCoordinate'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
@@ -307,7 +297,7 @@ const init = async () => {
|
||||
const goToPositionDetail = (position) => {
|
||||
if (!position.positionId) {
|
||||
uni.showToast({
|
||||
title: $t('search.positionInfoError'),
|
||||
title: t('search.positionInfoError'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
@@ -403,9 +393,8 @@ const init = async () => {
|
||||
}
|
||||
|
||||
.card {
|
||||
display: grid;
|
||||
grid-template-columns: 120rpx 1fr 72rpx;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
padding: 20rpx;
|
||||
border-radius: 20rpx;
|
||||
@@ -424,6 +413,21 @@ const init = async () => {
|
||||
background: #FFF9F9;
|
||||
}
|
||||
|
||||
// 第一行:三列布局
|
||||
.card-row-first {
|
||||
display: grid;
|
||||
grid-template-columns: 120rpx 1fr 72rpx;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
// 第二行:标签
|
||||
.card-row-second {
|
||||
width: 100%;
|
||||
padding-top: 8rpx;
|
||||
// border-top: 1rpx solid #F0F0F0;
|
||||
}
|
||||
|
||||
.thumb {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
@@ -453,7 +457,7 @@ const init = async () => {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #2A2A2A;
|
||||
max-width: 70%;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@@ -485,28 +489,34 @@ const init = async () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tags {
|
||||
display: flex;
|
||||
gap: 10rpx;
|
||||
}
|
||||
.tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.tag {
|
||||
padding: 6rpx 14rpx;
|
||||
border-radius: 16rpx;
|
||||
font-size: 22rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
.tag {
|
||||
padding: 6rpx 14rpx;
|
||||
border-radius: 16rpx;
|
||||
font-size: 22rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tag.rent {
|
||||
background: #E8F5E8;
|
||||
color: #3EAB64;
|
||||
}
|
||||
.tag.rent {
|
||||
background: #E8F5E8;
|
||||
color: #3EAB64;
|
||||
}
|
||||
|
||||
.tag.return {
|
||||
background: #E8F2FF;
|
||||
color: #3578e5;
|
||||
}
|
||||
.tag.return {
|
||||
background: #E8F2FF;
|
||||
color: #3578e5;
|
||||
}
|
||||
|
||||
.tag.coupon {
|
||||
background: #FFF9F0;
|
||||
color: #D4A574;
|
||||
}
|
||||
|
||||
.actions {
|
||||
@@ -514,6 +524,7 @@ const init = async () => {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.nav {
|
||||
|
||||
+9
-33
@@ -33,7 +33,7 @@ import { ref, computed, onMounted, getCurrentInstance } from 'vue'
|
||||
import { userLogout } from '@/config/api/user.js'
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
// 获取全局 i18n 实例
|
||||
const instance = getCurrentInstance()
|
||||
@@ -42,7 +42,7 @@ const globalI18n = instance?.appContext?.config?.globalProperties?.$i18n
|
||||
// 设置页面标题
|
||||
onMounted(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t('settings.title')
|
||||
title: t('settings.title')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -51,7 +51,7 @@ const currentLanguage = ref(uni.getStorageSync('language') || 'zh-CN')
|
||||
|
||||
// 当前语言文本显示
|
||||
const currentLanguageText = computed(() => {
|
||||
return currentLanguage.value === 'zh-CN' ? '简体中文' : 'English'
|
||||
return currentLanguage.value === 'zh-CN' ? t('settings.chinese') : t('settings.english')
|
||||
})
|
||||
|
||||
const navigateTo = (url) => {
|
||||
@@ -61,57 +61,33 @@ const navigateTo = (url) => {
|
||||
// 显示语言选择器
|
||||
const showLanguageSelector = () => {
|
||||
uni.showActionSheet({
|
||||
itemList: ['简体中文', 'English'],
|
||||
itemList: [t('settings.chinese'), t('settings.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...',
|
||||
title: t('settings.languageSwitched'),
|
||||
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)
|
||||
}
|
||||
url: '/pages/index/index'
|
||||
})
|
||||
}, 800)
|
||||
}
|
||||
@@ -121,13 +97,13 @@ const showLanguageSelector = () => {
|
||||
|
||||
const handleLogout = async () => {
|
||||
uni.showModal({
|
||||
title: $t('common.tips'),
|
||||
content: $t('user.confirmLogout'),
|
||||
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: $t('user.logoutSuccess'), icon: 'none' })
|
||||
uni.showToast({ title: t('user.logoutSuccess'), icon: 'none' })
|
||||
setTimeout(() => {
|
||||
uni.removeStorageSync('token')
|
||||
uni.removeStorageSync('userInfo')
|
||||
|
||||
+12
-12
@@ -59,7 +59,7 @@ 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 { t } = useI18n()
|
||||
|
||||
// 响应式状态
|
||||
const userInfo = ref({
|
||||
@@ -75,7 +75,7 @@ const isEditingNickname = ref(false);
|
||||
// 页面加载时初始化
|
||||
onMounted(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t('userProfile.title')
|
||||
title: t('userProfile.title')
|
||||
})
|
||||
loadUserInfo();
|
||||
});
|
||||
@@ -102,7 +102,7 @@ const loadUserInfo = async () => {
|
||||
} catch (error) {
|
||||
console.error('获取用户信息失败:', error);
|
||||
uni.showToast({
|
||||
title: $t('user.getUserInfoFailed'),
|
||||
title: t('user.getUserInfoFailed'),
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
@@ -138,13 +138,13 @@ const onChooseAvatar = async (e) => {
|
||||
const avatarLocalPath = e?.detail?.avatarUrl;
|
||||
if (!avatarLocalPath) {
|
||||
uni.showToast({
|
||||
title: $t('user.noAvatar'),
|
||||
title: t('user.noAvatar'),
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.showLoading({
|
||||
title: $t('userProfile.uploading'),
|
||||
title: t('userProfile.uploading'),
|
||||
mask: true
|
||||
});
|
||||
const uploadRes = await uploadUserAvatar(avatarLocalPath);
|
||||
@@ -157,14 +157,14 @@ const onChooseAvatar = async (e) => {
|
||||
uni.setStorageSync('userInfo', userInfo.value);
|
||||
}
|
||||
uni.showToast({
|
||||
title: $t('user.avatarUpdated'),
|
||||
title: t('user.avatarUpdated'),
|
||||
icon: 'success'
|
||||
});
|
||||
await loadUserInfo();
|
||||
} catch (err) {
|
||||
console.error('选择/上传头像失败:', err);
|
||||
uni.showToast({
|
||||
title: $t('user.avatarUploadFailed'),
|
||||
title: t('user.avatarUploadFailed'),
|
||||
icon: 'none'
|
||||
});
|
||||
} finally {
|
||||
@@ -188,7 +188,7 @@ const cancelEditNickname = () => {
|
||||
const saveNickname = async () => {
|
||||
if (!newNickname.value || !newNickname.value.trim()) {
|
||||
uni.showToast({
|
||||
title: $t('userProfile.nicknameRequired'),
|
||||
title: t('userProfile.nicknameRequired'),
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
@@ -196,7 +196,7 @@ const saveNickname = async () => {
|
||||
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: $t('userProfile.saving'),
|
||||
title: t('userProfile.saving'),
|
||||
mask: true
|
||||
});
|
||||
|
||||
@@ -228,19 +228,19 @@ const saveNickname = async () => {
|
||||
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: $t('userProfile.nicknameUpdated'),
|
||||
title: t('userProfile.nicknameUpdated'),
|
||||
icon: 'success'
|
||||
});
|
||||
isEditingNickname.value = false;
|
||||
} else {
|
||||
throw new Error(res.message || $t('userProfile.updateFailed'));
|
||||
throw new Error(res.message || t('userProfile.updateFailed'));
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('修改昵称失败:', error);
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: error.message || $t('userProfile.updateFailed'),
|
||||
title: error.message || t('userProfile.updateFailed'),
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
import { getOrderByOrderNoScorePayStatus, cancelOrder } from '@/config/api/order.js'
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
const progress = ref(0)
|
||||
const leftRotateDeg = ref(0)
|
||||
@@ -91,7 +91,7 @@
|
||||
await cancelOrder({ orderId: orderNo.value })
|
||||
}
|
||||
} catch (e) {}
|
||||
uni.showToast({ title: $t('waiting.rentFailed'), icon: 'none' })
|
||||
uni.showToast({ title: t('waiting.rentFailed'), icon: 'none' })
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({ url: '/pages/index/index' })
|
||||
}, 800)
|
||||
@@ -129,7 +129,7 @@
|
||||
// 超时保护:例如 60 秒
|
||||
timeoutTimer = setTimeout(() => {
|
||||
stopAllTimers()
|
||||
uni.showToast({ title: $t('waiting.timeout'), icon: 'none' })
|
||||
uni.showToast({ title: t('waiting.timeout'), icon: 'none' })
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({ url: '/pages/index/index' })
|
||||
}, 800)
|
||||
@@ -138,7 +138,7 @@
|
||||
|
||||
onLoad((query) => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t('waiting.title')
|
||||
title: t('waiting.title')
|
||||
})
|
||||
if (query) {
|
||||
if (query.orderNo) {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
// 网页链接
|
||||
const webUrl = ref('')
|
||||
@@ -24,7 +24,7 @@
|
||||
console.log('加载外部链接:', webUrl.value)
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: $t('common.invalidUrl') || '无效的链接',
|
||||
title: t('common.invalidUrl') || '无效的链接',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
@@ -44,7 +44,7 @@
|
||||
const handleError = (e) => {
|
||||
console.error('网页加载错误:', e)
|
||||
uni.showToast({
|
||||
title: $t('common.loadFailed') || '加载失败',
|
||||
title: t('common.loadFailed') || '加载失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user