fix:修复bug
This commit is contained in:
@@ -40,7 +40,7 @@
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
const props = defineProps({
|
||||
show: { type: Boolean, default: false },
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="map-container" :class="{ 'full-width': props.fullWidth }" :style="{ '--map-height': props.customHeight || '78vh' }">
|
||||
<view class="map-container" :class="{ 'full-width': props.fullWidth }" :style="{ '--map-height': props.customHeight || '72vh' }">
|
||||
<!-- 地图容器 -->
|
||||
<view class="map-wrapper">
|
||||
<!-- 使用小程序原生地图组件 -->
|
||||
@@ -68,7 +68,7 @@
|
||||
import { useI18n } from '../utils/i18n.js'
|
||||
|
||||
// 获取 i18n 实例
|
||||
const { t: $t } = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
// 引用折叠面板组件的ref
|
||||
const collapseRef = ref(null)
|
||||
@@ -272,13 +272,19 @@
|
||||
const onMapRegionChange = (e) => {
|
||||
|
||||
// 只处理结束事件
|
||||
if (!e || e.type !== 'end') {
|
||||
if (!e || (e.type !== 'end' && e.type !== 'regionchange')) {
|
||||
return
|
||||
}
|
||||
|
||||
const causedBy = e.causedBy || e.detail?.causedBy
|
||||
|
||||
if (causedBy === 'gesture' || causedBy === 'scale' || causedBy === 'drag'||causedBy==='update') {
|
||||
// H5 环境下可能没有 causedBy,只要是 end 事件就处理
|
||||
const isH5 = false;
|
||||
// #ifdef H5
|
||||
const h5Status = true;
|
||||
// #endif
|
||||
|
||||
if (causedBy === 'gesture' || causedBy === 'scale' || causedBy === 'drag' || causedBy === 'update' || (typeof h5Status !== 'undefined' && e.type === 'end')) {
|
||||
// 清除之前的定时器
|
||||
if (regionChangeTimer) {
|
||||
clearTimeout(regionChangeTimer)
|
||||
@@ -481,22 +487,26 @@ const handleSearch = () => {
|
||||
/* 地图容器 */
|
||||
.map-container {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
// position: fixed;
|
||||
// top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 94vw;
|
||||
height: calc(100% - 20rpx); /* 减少高度,避免覆盖底部按钮 */
|
||||
height: var(--map-height, calc(100% - 20rpx)); /* 使用变量或默认高度 */
|
||||
margin: 20rpx;
|
||||
margin-bottom: 0; /* 底部不需要边距 */
|
||||
border-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.full-width {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.map-wrapper {
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
|
||||
const { t: $t } = useI18n()
|
||||
const { t } = useI18n()
|
||||
|
||||
const props = defineProps({
|
||||
order: { type: Object, required: true },
|
||||
@@ -144,7 +144,7 @@
|
||||
const isFinished = computed(() => normalizedStatus.value === 'used_done');
|
||||
const isCancelled = computed(() => normalizedStatus.value === 'order_cancelled');
|
||||
|
||||
const titleText = computed(() => $t('order.rentFan'));
|
||||
const titleText = computed(() => t('order.rentFan'));
|
||||
|
||||
// 显示金额(优先后端给定字段)
|
||||
const displayAmount = computed(() => props.order.amount || props.order.payAmount || props.order.actualDeviceAmount || props.order.currentFee || '0.00');
|
||||
@@ -158,8 +158,8 @@
|
||||
const minutes = Math.floor(diffMs / 60000);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const mins = minutes % 60;
|
||||
if (hours > 0) return `${hours}${$t('time.hour')}${mins}${$t('time.minute')}`;
|
||||
return `${mins}${$t('time.minute')}`;
|
||||
if (hours > 0) return `${hours}${t('time.hour')}${mins}${t('time.minute')}`;
|
||||
return `${mins}${t('time.minute')}`;
|
||||
});
|
||||
|
||||
function parseDate(str) {
|
||||
|
||||
@@ -79,3 +79,15 @@ export const forcefOpenEmptyGrid = (deviceNo) => {
|
||||
})
|
||||
}
|
||||
|
||||
// 发送租借指令
|
||||
export const sendRentCommand = (data) => {
|
||||
return request({
|
||||
url: '/app/device/sendRentCommand',
|
||||
method: 'post',
|
||||
data,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -104,6 +104,38 @@ export const closeWithMaxFee = (orderNo) => {
|
||||
})
|
||||
}
|
||||
|
||||
// 创建微信支付订单
|
||||
export const createWxPayment = (orderNo) => {
|
||||
return request({
|
||||
url: `/app/wx-payment/create/${orderNo}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取正在使用中的订单
|
||||
export const getInUseOrder = () => {
|
||||
return request({
|
||||
url: '/app/order/inUse',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取待支付订单
|
||||
export const getUnpaidOrder = () => {
|
||||
return request({
|
||||
url: '/app/order/unpaid',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询微信支付状态
|
||||
export const getWxPaymentStatus = (orderNo) => {
|
||||
return request({
|
||||
url: `/app/wx-payment/status/${orderNo}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 通过订单号获取支付分订单信息
|
||||
export const getOrderByOrderNoScore = (orderNo) => {
|
||||
console.log('通过订单号获取支付分订单信息', orderNo);
|
||||
|
||||
+30
-4
@@ -27,12 +27,38 @@ export const getCommonByBrand = (brandName) => {
|
||||
})
|
||||
}
|
||||
|
||||
// 查询激活的且临近关闭时间最近的活动
|
||||
export const getActiveActivity = () => {
|
||||
// 获取当前协议内容
|
||||
export const getCurrentAgreement = (data) => {
|
||||
return request({
|
||||
url: '/device/activity/agent/list',
|
||||
url: '/device/agreementConfig/current',
|
||||
method: 'get',
|
||||
hideLoading: true
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取当前广告内容
|
||||
export const getCurrentAdvertisement = (data) => {
|
||||
return request({
|
||||
url: '/device/advertisementConfig/current',
|
||||
method: 'get',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取当前公告内容
|
||||
export const getCurrentAnnouncement = (data) => {
|
||||
return request({
|
||||
url: '/device/announcementConfig/current',
|
||||
method: 'get',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const getActiveActivity = (data) => {
|
||||
return request({
|
||||
url: '/device/activeActivity/current',
|
||||
method: 'get',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+31
-2
@@ -10,6 +10,24 @@ export const login = (data) => {
|
||||
})
|
||||
}
|
||||
|
||||
// 发送验证码
|
||||
export const sendVerifyCode = (phonenumber) => {
|
||||
return request({
|
||||
url: '/app/user/sms/code',
|
||||
method: 'get',
|
||||
data: { phonenumber }
|
||||
})
|
||||
}
|
||||
|
||||
// 手机号+验证码登录
|
||||
export const loginWithCode = (phonenumber, smsCode) => {
|
||||
return request({
|
||||
url: '/app/user/sms/login',
|
||||
method: 'post',
|
||||
data: { phonenumber, smsCode }
|
||||
})
|
||||
}
|
||||
|
||||
// 用户退出登录
|
||||
export const userLogout = (data) => {
|
||||
return request({
|
||||
@@ -56,7 +74,8 @@ export const uploadUserAvatar = (filePath) => {
|
||||
header: {
|
||||
'appid': appid,
|
||||
'Authorization': 'Bearer ' + uni.getStorageSync('token'),
|
||||
'Clientid': uni.getStorageSync('client_id')
|
||||
'Clientid': uni.getStorageSync('client_id'),
|
||||
'Content-Language': (uni.getStorageSync('language') || 'zh-CN').replace(/-/g, '_')
|
||||
},
|
||||
success: (res) => {
|
||||
try {
|
||||
@@ -83,7 +102,8 @@ export const uploadOssResource = (filePath) => {
|
||||
header: {
|
||||
'appid': appid,
|
||||
'Authorization': 'Bearer ' + uni.getStorageSync('token'),
|
||||
'Clientid': uni.getStorageSync('client_id')
|
||||
'Clientid': uni.getStorageSync('client_id'),
|
||||
'Content-Language': (uni.getStorageSync('language') || 'zh-CN').replace(/-/g, '_')
|
||||
},
|
||||
success: (res) => {
|
||||
try {
|
||||
@@ -115,3 +135,12 @@ export const withdrawDeposit = (orderNo) => {
|
||||
})
|
||||
}
|
||||
|
||||
// 获取微信用户手机号
|
||||
export const getWxUserPhoneNumber = (data) => {
|
||||
return request({
|
||||
url: '/app/user/getPhoneNumber',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -33,7 +33,8 @@ const request = (option) => {
|
||||
...option.headers,
|
||||
'appid': appid,
|
||||
'Authorization': "Bearer " + uni.getStorageSync('token'),
|
||||
'Clientid': uni.getStorageSync('client_id')
|
||||
'Clientid': uni.getStorageSync('client_id'),
|
||||
'Content-Language': (uni.getStorageSync('language') || 'zh-CN').replace(/-/g, '_')
|
||||
},
|
||||
success(res) {
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
// export const URL = "https://my.gxfs123.com/api" //正式服务器-弃用
|
||||
// export const URL = "https://manager.fdzpower.com/api" //正式服务器
|
||||
export const URL = "https://fansdev.gxfs123.com/api" //测试服务器
|
||||
// export const URL = "http://192.168.5.52:8080" //本地调试
|
||||
// export const URL = "http://192.168.5.123:8080" //本地调试
|
||||
// export const URL = "http://127.0.0.1:8080" //本地调试
|
||||
|
||||
export const appid = "wx2165f0be356ae7a9" //微信小程序appid
|
||||
|
||||
+55
-7
@@ -49,6 +49,8 @@ export default {
|
||||
loginRequired: 'Please login first',
|
||||
operationSuccess: 'Operation successful',
|
||||
operationFailed: 'Operation failed',
|
||||
sending: 'Sending...',
|
||||
loggingIn: 'Logging in...',
|
||||
refresh: 'Refresh',
|
||||
pull: 'Pull to refresh',
|
||||
release: 'Release to refresh',
|
||||
@@ -114,7 +116,8 @@ export default {
|
||||
businessHours: 'Business Hours: ',
|
||||
navigateHere: 'Navigate Here',
|
||||
coordinateError: 'Invalid location coordinates',
|
||||
notExist: 'Location does not exist'
|
||||
notExist: 'Location does not exist',
|
||||
supportCouponOrMember: 'Coupons & Cards Available'
|
||||
},
|
||||
|
||||
device: {
|
||||
@@ -172,6 +175,8 @@ export default {
|
||||
deposit: 'Deposit',
|
||||
rentFee: 'Rent Fee',
|
||||
payNow: 'Pay Now',
|
||||
myCoupons:'Coupons',
|
||||
myCards:'Member Cards',
|
||||
cancelOrder: 'Cancel Order',
|
||||
quickReturn: 'Quick Return',
|
||||
returnDevice: 'Return Device',
|
||||
@@ -246,6 +251,8 @@ export default {
|
||||
perHalfHour: 'per half hour',
|
||||
deviceNoEject: 'Not Ejected',
|
||||
returnReminder: 'Return Reminder',
|
||||
canUsePromotion: 'Coupons & Cards Available',
|
||||
usedPromotion: 'Promotion Applied',
|
||||
convertToOwn: 'Don\'t want to return? Convert to own',
|
||||
convertToOwnTitle: 'Convert to Own',
|
||||
convertToOwnConfirm: 'Only ¥99 to convert to own. The power bank will be yours. Confirm?',
|
||||
@@ -261,7 +268,9 @@ export default {
|
||||
deviceNoEjectTitle: 'Device Not Ejected',
|
||||
deviceNoEjectConfirm: 'Your power bank didn\'t eject? We will handle it immediately, expected to resolve within 5 minutes.',
|
||||
deviceNoEjectSuccess: 'Feedback received, will be handled within 5 minutes',
|
||||
deviceNoEjectFailed: 'Feedback submission failed, please try again'
|
||||
deviceNoEjectFailed: 'Feedback submission failed, please try again',
|
||||
returnProblemTip: 'After returning, if the order is still active, please go to ',
|
||||
contactStaff: ' to contact staff.'
|
||||
},
|
||||
|
||||
user: {
|
||||
@@ -327,7 +336,22 @@ export default {
|
||||
phoneSuccess: 'Success',
|
||||
phoneError: 'Error',
|
||||
phoneGetFailed: 'Failed',
|
||||
authCodeFailed: 'Auth failed'
|
||||
authCodeFailed: 'Auth failed',
|
||||
phoneLogin: 'Phone Login',
|
||||
phonePlaceholder: 'Enter phone number',
|
||||
codePlaceholder: 'Enter verification code',
|
||||
getCode: 'Get Code',
|
||||
resend: 'Resend',
|
||||
loginBtn: 'Login',
|
||||
phoneRequired: 'Phone required',
|
||||
phoneInvalid: 'Invalid phone number',
|
||||
codeRequired: 'Verification code required',
|
||||
codeSent: 'Code sent',
|
||||
sendCodeFailed: 'Send code failed',
|
||||
regionNotSupported: 'Non-mainland China, Hong Kong, Macau users please login via platform phone authorization',
|
||||
onlyMainlandSupported: 'Currently only Mainland China is supported',
|
||||
getServicePhoneFailed: 'Failed to get service phone',
|
||||
noAuthToken: 'Login successful but no credentials obtained'
|
||||
},
|
||||
|
||||
permission: {
|
||||
@@ -361,7 +385,9 @@ export default {
|
||||
package: 'Package',
|
||||
total: 'Total',
|
||||
paymentFailedRetry: 'Payment failed, retry?',
|
||||
createPayOrderFailed: 'Failed'
|
||||
createPayOrderFailed: 'Failed',
|
||||
subscriptionSuccess: 'Subscription successful',
|
||||
subscriptionFailed: 'Subscription failed, please try again'
|
||||
},
|
||||
|
||||
feedback: {
|
||||
@@ -421,6 +447,7 @@ export default {
|
||||
phone: 'Phone',
|
||||
email: 'Email',
|
||||
workingHours: 'Working Hours',
|
||||
workingHoursValue: 'Mon-Sun 09:00-22:00',
|
||||
functionDeveloping: 'Feature in development',
|
||||
faq1Question: 'How to rent a fan?',
|
||||
faq1Answer: 'Click "Scan to Rent" on the homepage, scan the QR code on the device with WeChat, and complete payment as prompted.',
|
||||
@@ -440,6 +467,7 @@ export default {
|
||||
languageSetting: 'Language Setting',
|
||||
chinese: '简体中文',
|
||||
english: 'English',
|
||||
languageSwitched: 'Language switched, refreshing...',
|
||||
notification: 'Notification',
|
||||
privacy: 'Privacy',
|
||||
about: 'About',
|
||||
@@ -664,6 +692,8 @@ export default {
|
||||
withdrawNotice2: 'Withdrawal expected to arrive within 0-7 business days',
|
||||
withdrawNotice3: 'If delayed, please contact customer service',
|
||||
depositRecord: 'Deposit Record',
|
||||
payRecord: 'Payment Record',
|
||||
refundRecord: 'Refund Record',
|
||||
orderNotReturned: 'Current order not returned, please return before withdraw',
|
||||
alreadyRefunded: 'Deposit already refunded',
|
||||
refundProcessing: 'Refund processing, please wait'
|
||||
@@ -719,7 +749,7 @@ export default {
|
||||
type: 'Type',
|
||||
timesCard: 'Times Card',
|
||||
durationCard: 'Duration Card',
|
||||
remainingTimes: 'Remaining Times',
|
||||
remainingTimes: 'Remaining: ',
|
||||
remainingDuration: 'Remaining Duration',
|
||||
hours: 'Hours',
|
||||
validPeriod: 'Valid Period',
|
||||
@@ -730,7 +760,23 @@ export default {
|
||||
price: 'Purchase Price',
|
||||
noCards: 'No cards',
|
||||
buyNow: 'Buy Now',
|
||||
getListFailed: 'Failed to get card list'
|
||||
getListFailed: 'Failed to get card list',
|
||||
dailyLimit: 'Daily Limit',
|
||||
singleTimeLimit: 'Single Use Limit',
|
||||
unlimited: 'Unlimited',
|
||||
times: 'Times',
|
||||
minutes: 'Minutes',
|
||||
validWithinDays: 'days valid',
|
||||
validFromPurchase: 'Valid from purchase',
|
||||
daysValid: 'days',
|
||||
currentCycleUsed: 'Current Cycle Used',
|
||||
totalCount: 'Total Count',
|
||||
expire: 'Expire',
|
||||
expiredOn: 'Expired on ',
|
||||
renew: 'Renew',
|
||||
toUse: 'Use Now',
|
||||
onlyForRegionBefore: 'Only for ',
|
||||
onlyForRegionAfter: ''
|
||||
},
|
||||
|
||||
myCoupon: {
|
||||
@@ -744,7 +790,9 @@ export default {
|
||||
noUsedCoupons: 'No used coupons',
|
||||
noExpiredCoupons: 'No expired coupons',
|
||||
buyNow: 'Buy Now',
|
||||
getListFailed: 'Failed to get coupon list'
|
||||
getListFailed: 'Failed to get coupon list',
|
||||
onlyForRegionBefore: 'Only for ',
|
||||
onlyForRegionAfter: ''
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+57
-9
@@ -49,6 +49,8 @@ export default {
|
||||
loginRequired: '请先登录',
|
||||
operationSuccess: '操作成功',
|
||||
operationFailed: '操作失败',
|
||||
sending: '发送中...',
|
||||
loggingIn: '登录中...',
|
||||
refresh: '刷新',
|
||||
pull: '下拉刷新',
|
||||
release: '释放刷新',
|
||||
@@ -114,7 +116,8 @@ export default {
|
||||
businessHours: '营业时间:',
|
||||
navigateHere: '导航去这',
|
||||
coordinateError: '该场地坐标信息异常',
|
||||
notExist: '场地不存在'
|
||||
notExist: '场地不存在',
|
||||
supportCouponOrMember: '可使用优惠券、会员卡'
|
||||
},
|
||||
|
||||
device: {
|
||||
@@ -171,6 +174,8 @@ export default {
|
||||
payAmount: '支付金额',
|
||||
deposit: '押金',
|
||||
rentFee: '租金',
|
||||
myCards:'会员卡优惠',
|
||||
myCoupons:'优惠券优惠',
|
||||
payNow: '立即支付',
|
||||
cancelOrder: '取消订单',
|
||||
quickReturn: '快速归还',
|
||||
@@ -246,6 +251,8 @@ export default {
|
||||
perHalfHour: '每半小时',
|
||||
deviceNoEject: '宝未弹出',
|
||||
returnReminder: '归还提醒',
|
||||
canUsePromotion: '可使用优惠券、会员卡',
|
||||
usedPromotion: '已享受优惠',
|
||||
convertToOwn: '不想还了?点击转为自用',
|
||||
convertToOwnTitle: '转为自用',
|
||||
convertToOwnConfirm: '仅需花费99元,即可转为自用,充电宝将归您所有,确认操作吗?',
|
||||
@@ -261,7 +268,9 @@ export default {
|
||||
deviceNoEjectTitle: '充电宝未弹出',
|
||||
deviceNoEjectConfirm: '您的充电宝未弹出吗?我们将立即为您处理,预计5分钟内解决问题。',
|
||||
deviceNoEjectSuccess: '反馈已受理,将在5分钟内处理',
|
||||
deviceNoEjectFailed: '反馈提交失败,请稍后重试'
|
||||
deviceNoEjectFailed: '反馈提交失败,请稍后重试',
|
||||
returnProblemTip: '产品归还入仓后,订单仍未结束,请前往',
|
||||
contactStaff: '联系工作人员。'
|
||||
},
|
||||
|
||||
user: {
|
||||
@@ -327,7 +336,22 @@ export default {
|
||||
phoneSuccess: '手机号获取成功',
|
||||
phoneError: '获取手机号异常',
|
||||
phoneGetFailed: '获取手机号失败',
|
||||
authCodeFailed: '获取授权码失败'
|
||||
authCodeFailed: '获取授权码失败',
|
||||
phoneLogin: '手机号登录',
|
||||
phonePlaceholder: '请输入手机号码',
|
||||
codePlaceholder: '请输入验证码',
|
||||
getCode: '获取验证码',
|
||||
resend: '重新发送',
|
||||
loginBtn: '立即登录',
|
||||
phoneRequired: '请输入手机号',
|
||||
phoneInvalid: '请输入正确的手机号',
|
||||
codeRequired: '请输入验证码',
|
||||
codeSent: '验证码已发送',
|
||||
sendCodeFailed: '发送验证码失败',
|
||||
regionNotSupported: '非大陆、香港、澳门用户请通过平台手机号授权登录',
|
||||
onlyMainlandSupported: '当前仅支持中国大陆地区',
|
||||
getServicePhoneFailed: '获取客服电话失败',
|
||||
noAuthToken: '登录成功但未获取到授权凭证'
|
||||
},
|
||||
|
||||
permission: {
|
||||
@@ -361,7 +385,9 @@ export default {
|
||||
package: '套餐',
|
||||
total: '合计',
|
||||
paymentFailedRetry: '支付失败,请重试',
|
||||
createPayOrderFailed: '创建支付订单失败'
|
||||
createPayOrderFailed: '创建支付订单失败',
|
||||
subscriptionSuccess: '订阅成功',
|
||||
subscriptionFailed: '订阅失败,请稍后重试'
|
||||
},
|
||||
|
||||
feedback: {
|
||||
@@ -421,6 +447,7 @@ export default {
|
||||
phone: '电话',
|
||||
email: '邮箱',
|
||||
workingHours: '工作时间',
|
||||
workingHoursValue: '周一至周日 09:00-22:00',
|
||||
functionDeveloping: '功能开发中',
|
||||
faq1Question: '如何租借风扇?',
|
||||
faq1Answer: '点击首页"扫码租借"按钮,使用微信扫描设备上的二维码,按提示完成支付即可使用。',
|
||||
@@ -440,6 +467,7 @@ export default {
|
||||
languageSetting: '语言设置',
|
||||
chinese: '简体中文',
|
||||
english: 'English',
|
||||
languageSwitched: '语言已切换,正在刷新...',
|
||||
notification: '通知',
|
||||
privacy: '隐私',
|
||||
about: '关于',
|
||||
@@ -664,6 +692,8 @@ export default {
|
||||
withdrawNotice2: '提现申请提交后预计0-7个工作日到账',
|
||||
withdrawNotice3: '如超时未收到,请联系客服处理',
|
||||
depositRecord: '押金记录',
|
||||
payRecord: '支付记录',
|
||||
refundRecord: '退还记录',
|
||||
orderNotReturned: '当前订单尚未归还,请归还后再提现',
|
||||
alreadyRefunded: '押金已退还,无需重复提现',
|
||||
refundProcessing: '押金退还处理中,请耐心等待'
|
||||
@@ -719,32 +749,50 @@ export default {
|
||||
type: '类型',
|
||||
timesCard: '次卡',
|
||||
durationCard: '时长卡',
|
||||
remainingTimes: '剩余次数',
|
||||
remainingTimes: '剩余次数:',
|
||||
remainingDuration: '剩余时长',
|
||||
hours: '小时',
|
||||
validPeriod: '有效期',
|
||||
active: '使用中',
|
||||
expired: '已过期',
|
||||
expired: '已失效',
|
||||
used: '已用完',
|
||||
position: '使用地点',
|
||||
price: '购买价格',
|
||||
noCards: '暂无会员卡',
|
||||
buyNow: '立即购买',
|
||||
getListFailed: '获取会员卡列表失败'
|
||||
getListFailed: '获取会员卡列表失败',
|
||||
dailyLimit: '每日限用',
|
||||
singleTimeLimit: '单次限时',
|
||||
unlimited: '不限',
|
||||
times: '次',
|
||||
minutes: '分钟',
|
||||
validWithinDays: '天内有效',
|
||||
validFromPurchase: '从购买时间起',
|
||||
daysValid: '天有效',
|
||||
currentCycleUsed: '本周期已使用',
|
||||
totalCount: '总次数',
|
||||
expire: '到期',
|
||||
expiredOn: '失效于',
|
||||
renew: '续卡',
|
||||
toUse: '去使用',
|
||||
onlyForRegionBefore: '仅限',
|
||||
onlyForRegionAfter: '使用'
|
||||
},
|
||||
|
||||
myCoupon: {
|
||||
available: '可使用',
|
||||
used: '已使用',
|
||||
expired: '已过期',
|
||||
useNow: '立即使用',
|
||||
useNow: '去使用',
|
||||
usedStatus: '已使用',
|
||||
expiredStatus: '已过期',
|
||||
noAvailableCoupons: '暂无可用优惠券',
|
||||
noUsedCoupons: '暂无已使用优惠券',
|
||||
noExpiredCoupons: '暂无已过期优惠券',
|
||||
buyNow: '立即购买',
|
||||
getListFailed: '获取优惠券列表失败'
|
||||
getListFailed: '获取优惠券列表失败',
|
||||
onlyForRegionBefore: '仅限',
|
||||
onlyForRegionAfter: '使用'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,19 @@
|
||||
"mp-toutiao" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"h5" : {
|
||||
"sdkConfigs" : {
|
||||
"maps" : {
|
||||
"qqmap" : {
|
||||
"key" : "DJQBZ-WB53Q-WPS5B-4S6J7-53RMS-X4FJ2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"router" : {
|
||||
"mode" : "hash",
|
||||
"base" : "./"
|
||||
}
|
||||
},
|
||||
"uniStatistics" : {
|
||||
"enable" : false
|
||||
},
|
||||
|
||||
+9
-1
@@ -19,7 +19,15 @@
|
||||
"path": "pages/login/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "",
|
||||
"navigationBarBackgroundColor": "#ffffff",
|
||||
"navigationBarBackgroundColor": "#C8F4D9",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/login/phone",
|
||||
"style": {
|
||||
"navigationBarTitleText": "",
|
||||
"navigationBarBackgroundColor": "#C8F4D9",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
|
||||
+63
-81
@@ -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,145 +38,128 @@
|
||||
</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() {
|
||||
// 设置页面标题
|
||||
const { t } = useI18n()
|
||||
|
||||
const depositAmount = ref('0.00')
|
||||
const orderNo = ref('')
|
||||
const orderId = ref('')
|
||||
const records = ref([])
|
||||
|
||||
onMounted(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: this.$t('deposit.title')
|
||||
title: t('deposit.title')
|
||||
})
|
||||
// this.loadUserInfo()
|
||||
},
|
||||
onShow() {
|
||||
this.loadUserInfo()
|
||||
},
|
||||
methods: {
|
||||
async loadUserInfo() {
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
loadUserInfo()
|
||||
})
|
||||
|
||||
const loadUserInfo = async () => {
|
||||
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||''
|
||||
depositAmount.value = res.data.balanceAmount || '0.00'
|
||||
orderNo.value = res.data.latestOrderNo || ''
|
||||
orderId.value = res.data.latestOrderId || ''
|
||||
|
||||
// 如果存在余额,获取押金记录
|
||||
if (parseFloat(this.depositAmount) > 0 && this.orderNo) {
|
||||
this.records = [
|
||||
if (parseFloat(depositAmount.value) > 0 && orderNo.value) {
|
||||
records.value = [
|
||||
{
|
||||
type: '支付',
|
||||
time: this.formatDate(new Date()),
|
||||
amount: this.depositAmount
|
||||
type: 'pay',
|
||||
typeText: t('deposit.payRecord'),
|
||||
time: formatDate(new Date()),
|
||||
amount: depositAmount.value
|
||||
}
|
||||
]
|
||||
} else {
|
||||
this.records = []
|
||||
records.value = []
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取用户信息失败:', error)
|
||||
uni.showToast({
|
||||
title: this.$t('user.getUserInfoFailed'),
|
||||
title: t('user.getUserInfoFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
async handleWithdraw() {
|
||||
if (parseFloat(this.depositAmount) <= 0) {
|
||||
}
|
||||
|
||||
const handleWithdraw = async () => {
|
||||
if (parseFloat(depositAmount.value) <= 0) {
|
||||
uni.showToast({
|
||||
title: this.$t('deposit.noBalance'),
|
||||
title: 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
|
||||
// }
|
||||
|
||||
uni.showModal({
|
||||
title: this.$t('deposit.confirmWithdraw'),
|
||||
content: this.$t('deposit.withdrawDesc'),
|
||||
title: t('deposit.confirmWithdraw'),
|
||||
content: t('deposit.withdrawDesc'),
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showLoading({
|
||||
title: this.$t('deposit.withdrawing')
|
||||
title: t('deposit.withdrawing')
|
||||
})
|
||||
|
||||
try {
|
||||
console.log('发起提现请求,订单号:', this.orderNo)
|
||||
const result = await withdrawDeposit(this.orderNo)
|
||||
console.log('提现响应:', result)
|
||||
const result = await withdrawDeposit(orderNo.value)
|
||||
|
||||
if (result.code === 200) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: this.$t('deposit.withdrawSubmitted'),
|
||||
title: t('deposit.withdrawSubmitted'),
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
// 更新余额为0
|
||||
this.depositAmount = '0.00'
|
||||
this.records.push({
|
||||
type: '退还',
|
||||
time: this.formatDate(new Date()),
|
||||
amount: this.depositAmount
|
||||
// 更新记录
|
||||
records.value.push({
|
||||
type: 'refund',
|
||||
typeText: t('deposit.refundRecord'),
|
||||
time: formatDate(new Date()),
|
||||
amount: depositAmount.value
|
||||
})
|
||||
// 更新余额为0
|
||||
depositAmount.value = '0.00'
|
||||
|
||||
// 重新加载用户信息
|
||||
setTimeout(() => {
|
||||
this.loadUserInfo()
|
||||
loadUserInfo()
|
||||
}, 1500)
|
||||
} else {
|
||||
throw new Error(result.msg || this.$t('deposit.withdrawFailed'))
|
||||
throw new Error(result.msg || t('deposit.withdrawFailed'))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提现失败:', error)
|
||||
uni.hideLoading()
|
||||
|
||||
// 更详细的错误处理
|
||||
let errorMessage = this.$t('deposit.withdrawFailed');
|
||||
let errorMessage = t('deposit.withdrawFailed');
|
||||
|
||||
// 如果有具体错误信息,使用它
|
||||
if (error.message) {
|
||||
// 常见错误消息处理
|
||||
if (error.message.includes('尚未归还')) {
|
||||
errorMessage = this.$t('deposit.orderNotReturned');
|
||||
errorMessage = t('deposit.orderNotReturned');
|
||||
} else if (error.message.includes('已退还')) {
|
||||
errorMessage = this.$t('deposit.alreadyRefunded');
|
||||
errorMessage = t('deposit.alreadyRefunded');
|
||||
} else if (error.message.includes('处理中')) {
|
||||
errorMessage = this.$t('deposit.refundProcessing');
|
||||
errorMessage = t('deposit.refundProcessing');
|
||||
} else if (error.message.includes('余额为0')) {
|
||||
errorMessage = this.$t('deposit.noBalance');
|
||||
errorMessage = t('deposit.noBalance');
|
||||
} else {
|
||||
// 使用后端返回的具体错误消息
|
||||
errorMessage = error.message;
|
||||
}
|
||||
}
|
||||
|
||||
// 显示错误提示
|
||||
uni.showModal({
|
||||
title: this.$t('deposit.withdrawFailed'),
|
||||
title: t('deposit.withdrawFailed'),
|
||||
content: errorMessage,
|
||||
showCancel: false
|
||||
})
|
||||
@@ -184,8 +167,9 @@ export default {
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
formatDate(date) {
|
||||
}
|
||||
|
||||
const formatDate = (date) => {
|
||||
const year = date.getFullYear()
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = date.getDate().toString().padStart(2, '0')
|
||||
@@ -193,8 +177,6 @@ export default {
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0')
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
+46
-42
@@ -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()
|
||||
|
||||
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') {
|
||||
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
|
||||
}
|
||||
|
||||
// 检查是否有待支付的订单
|
||||
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 || '-';
|
||||
};
|
||||
|
||||
+17
-21
@@ -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() {
|
||||
// 设置页面标题
|
||||
const { t } = useI18n()
|
||||
|
||||
const faqList = ref(HELP_CONTENT.FAQ_LIST)
|
||||
const customerPhone = ref(HELP_CONTENT.CONTACT.PHONE.VALUE)
|
||||
|
||||
onLoad(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: this.$t('help.title')
|
||||
title: t('help.title')
|
||||
})
|
||||
// 从缓存读取客服电话
|
||||
this.customerPhone = getCustomerPhone()
|
||||
},
|
||||
methods: {
|
||||
makePhoneCall() {
|
||||
customerPhone.value = getCustomerPhone()
|
||||
})
|
||||
|
||||
const makePhoneCall = () => {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: this.customerPhone
|
||||
phoneNumber: customerPhone.value
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
+41
-83
@@ -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: {
|
||||
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: {
|
||||
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)
|
||||
})
|
||||
|
||||
+10
-30
@@ -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: {
|
||||
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
|
||||
}
|
||||
|
||||
+10
-30
@@ -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: {
|
||||
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>
|
||||
+344
-110
@@ -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 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-status" :class="getStatusClass(card.status)">
|
||||
<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 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 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 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 v-if="card.cardType === 'TIME'" class="card-usage-info">
|
||||
<text class="usage-text">{{ $t('myCard.durationCard') }}</text>
|
||||
</view>
|
||||
<view class="card-info">
|
||||
<text class="info-label">{{ $t('myCard.validPeriod') }}:</text>
|
||||
<text class="info-value">{{ card.startDate }} - {{ card.endDate }}</text>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<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,18 +74,35 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useI18n } from '@/utils/i18n.js'
|
||||
import {
|
||||
import {
|
||||
ref,
|
||||
onMounted
|
||||
} from 'vue'
|
||||
import {
|
||||
useI18n
|
||||
} from '@/utils/i18n.js'
|
||||
import {
|
||||
getMemberCardsByStatus
|
||||
} from '@/config/api/member.js'
|
||||
const { t: $t } = useI18n()
|
||||
} 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 () => {
|
||||
// 获取会员卡列表
|
||||
const getCardList = async () => {
|
||||
try {
|
||||
const response = await getMemberCardsByStatus()
|
||||
// 处理API返回的数据,转换为模板需要的格式
|
||||
@@ -60,17 +110,25 @@ const getCardList = async () => {
|
||||
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,
|
||||
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,
|
||||
endDate: item.endTime,
|
||||
startDate: item.startTime?.split(' ')[0] || item.startTime,
|
||||
endDate: item.endTime?.split(' ')[0] || item.endTime,
|
||||
positionId: item.positionId,
|
||||
positionName: item.positionName,
|
||||
purchasePrice: item.purchasePrice,
|
||||
// 添加可能的其他字段
|
||||
...item
|
||||
remark: item.remark
|
||||
}))
|
||||
} else {
|
||||
cardList.value = []
|
||||
@@ -78,14 +136,21 @@ const getCardList = async () => {
|
||||
} catch (error) {
|
||||
console.error('获取会员卡列表失败:', error)
|
||||
uni.showToast({
|
||||
title: $t('myCard.getListFailed'),
|
||||
title: t('myCard.getListFailed'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取状态类名
|
||||
const getStatusClass = (status) => {
|
||||
// 计算进度条宽度
|
||||
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',
|
||||
@@ -93,121 +158,290 @@ const getStatusClass = (status) => {
|
||||
'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) => {
|
||||
// 获取状态文本
|
||||
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'),
|
||||
// title: t('common.functionDeveloping'),
|
||||
// icon: 'none'
|
||||
// })
|
||||
}
|
||||
}
|
||||
|
||||
// 去购买
|
||||
const goToBuy = () => {
|
||||
// 续卡
|
||||
const renewCard = (card) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/purchase/index?positionId=${card.positionId}`
|
||||
})
|
||||
}
|
||||
|
||||
// 去使用会员卡
|
||||
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'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 去购买
|
||||
const goToBuy = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/purchase/index'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
onMounted(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t('user.myCards')
|
||||
title: t('user.myCards')
|
||||
})
|
||||
getCardList()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.my-card-page {
|
||||
.my-card-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
padding: 20rpx;
|
||||
}
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.card-list {
|
||||
.card-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.card-item {
|
||||
background-color: #ffffff;
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
.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-header {
|
||||
}
|
||||
|
||||
// 卡片头部
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 地区信息
|
||||
.card-region {
|
||||
margin-bottom: 24rpx;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 16rpx;
|
||||
|
||||
.region-text {
|
||||
font-size: 26rpx;
|
||||
color: #666666;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 卡片底部
|
||||
.card-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
padding-bottom: 20rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
/* 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 ; */
|
||||
}
|
||||
|
||||
.card-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
.card-usage-info {
|
||||
flex: 1;
|
||||
|
||||
.card-status {
|
||||
.usage-text {
|
||||
font-size: 26rpx;
|
||||
color: #D4A574;
|
||||
font-weight: 500;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.card-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
// 续卡按钮
|
||||
.renew-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 22rpx;
|
||||
// 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: #e8f5e8;
|
||||
color: #4caf50;
|
||||
// background-color: #FFF9F0;
|
||||
|
||||
.status-text {
|
||||
color: #D4A574;
|
||||
}
|
||||
}
|
||||
|
||||
&.expired {
|
||||
background-color: #ffeaea;
|
||||
color: #f44336;
|
||||
// background-color: #F5F5F5;
|
||||
|
||||
.status-text {
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
&.used {
|
||||
background-color: #f0f0f0;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
// background-color: #F5F5F5;
|
||||
|
||||
.card-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12rpx;
|
||||
|
||||
.info-label {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
|
||||
&.highlight {
|
||||
color: #FF6B00;
|
||||
font-weight: 600;
|
||||
.status-text {
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@@ -238,5 +472,5 @@ onMounted(() => {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+275
-156
@@ -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>
|
||||
<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,35 +61,38 @@
|
||||
</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(() => {
|
||||
// 过滤后的优惠券
|
||||
const filteredCoupons = computed(() => {
|
||||
return couponList.value.filter(coupon => coupon.status === currentTab.value)
|
||||
})
|
||||
})
|
||||
|
||||
// 获取优惠券列表
|
||||
const getCouponList = async () => {
|
||||
// 获取优惠券列表
|
||||
const getCouponList = async () => {
|
||||
try {
|
||||
const apiStatus = tabToStatusMap[currentTab.value]
|
||||
const res = await getUserCoupons(apiStatus)
|
||||
// const apiStatus = tabToStatusMap[currentTab.value]
|
||||
const res = await getUserCoupons('')
|
||||
|
||||
if (res.code === 200 && res.data) {
|
||||
// 将后端数据转换为前端需要的格式
|
||||
@@ -96,10 +109,10 @@
|
||||
// 格式化有效期
|
||||
let validity = ''
|
||||
if (currentTab.value === 'used') {
|
||||
// 已使用显示使用时间(这里暂用开始时间,如后端有使用时间字段可替换)
|
||||
validity = item.couponStartTime ? `使用时间 ${item.couponStartTime}` : ''
|
||||
// 已使用显示使用时间
|
||||
validity = item.couponStartTime ? `使用时间 ${item.couponStartTime.split(' ')[0]}` : ''
|
||||
} else if (item.couponEndTime) {
|
||||
validity = `有效期至 ${item.couponEndTime}`
|
||||
validity = `于 ${item.couponEndTime.split(' ')[0]} 过期`
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -110,7 +123,8 @@
|
||||
discount: item.discountRate ? parseFloat(item.discountRate) * 10 : null,
|
||||
condition: condition,
|
||||
validity: validity,
|
||||
status: currentTab.value
|
||||
status: currentTab.value,
|
||||
positionName: item.positionName
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@@ -120,82 +134,167 @@
|
||||
console.error('获取优惠券列表失败:', error)
|
||||
couponList.value = []
|
||||
uni.showToast({
|
||||
title: $t('myCoupon.getListFailed'),
|
||||
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('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 = () => {
|
||||
// 去购买
|
||||
const goToBuy = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/purchase/index?tab=coupon'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
onMounted(() => {
|
||||
uni.setNavigationBarTitle({
|
||||
title: $t('user.myCoupons')
|
||||
title: t('user.myCoupons')
|
||||
})
|
||||
getCouponList()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.my-coupon-page {
|
||||
// 优惠券样式变量封装
|
||||
$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;
|
||||
|
||||
.my-coupon-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tab 切换 */
|
||||
.tab-container {
|
||||
/* Tab 切换 */
|
||||
.tab-container {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
display: flex;
|
||||
background-color: #ffffff;
|
||||
z-index: 999;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 20rpx 0;
|
||||
@@ -225,21 +324,21 @@
|
||||
border-radius: 3rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-list {
|
||||
.coupon-list {
|
||||
padding: 20rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-item-wrapper {
|
||||
.coupon-item-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-item {
|
||||
background: linear-gradient(135deg, #FFF4E6 0%, #FFE8CC 100%);
|
||||
.coupon-item {
|
||||
background: #FFF4E3;
|
||||
border-radius: 20rpx;
|
||||
padding: 40rpx 30rpx;
|
||||
display: flex;
|
||||
@@ -252,13 +351,13 @@
|
||||
transition: all 0.3s;
|
||||
|
||||
&.selected {
|
||||
border-color: #B8741A;
|
||||
border-color: $coupon-divider-color;
|
||||
box-shadow: 0 4rpx 20rpx rgba(184, 116, 26, 0.2);
|
||||
|
||||
.coupon-circle-left,
|
||||
.coupon-circle-right {
|
||||
background-color: #FFF4E6;
|
||||
border: 2rpx solid #B8741A;
|
||||
.coupon-circle-top,
|
||||
.coupon-circle-bottom {
|
||||
background-color: $coupon-active-bg-start;
|
||||
border: 2rpx solid $coupon-divider-color;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,120 +371,140 @@
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.coupon-circle-left,
|
||||
.coupon-circle-right {
|
||||
.coupon-circle-top,
|
||||
.coupon-circle-bottom {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 左侧圆形缺口 */
|
||||
.coupon-circle-left {
|
||||
/* 虚线顶部圆形缺口 */
|
||||
.coupon-circle-top {
|
||||
position: absolute;
|
||||
left: -16rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
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: #f5f5f5;
|
||||
background-color: $coupon-bg-faded;
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
|
||||
/* 右侧圆形缺口 */
|
||||
.coupon-circle-right {
|
||||
/* 虚线底部圆形缺口 */
|
||||
.coupon-circle-bottom {
|
||||
position: absolute;
|
||||
right: -16rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
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: #f5f5f5;
|
||||
background-color: $coupon-bg-faded;
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-left {
|
||||
.coupon-left {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-value {
|
||||
font-size: 48rpx;
|
||||
.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;
|
||||
color: #B8741A;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-condition {
|
||||
.coupon-unit {
|
||||
font-size: 24rpx; // 单位小
|
||||
font-weight: 500;
|
||||
margin-bottom: 6rpx; // 微调单位垂直位置,使其更贴合“脚”
|
||||
margin-left: 4rpx;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
.coupon-condition {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
color: #000;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.coupon-validity-left {
|
||||
.coupon-validity-left {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-divider {
|
||||
.coupon-divider {
|
||||
width: 2rpx;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 65%;
|
||||
left: $coupon-divider-left;
|
||||
transform: translateX(-50%);
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
// min-height: 160rpx;
|
||||
align-self: stretch;
|
||||
background: repeating-linear-gradient(to bottom,
|
||||
#B8741A 0rpx,
|
||||
#B8741A 8rpx,
|
||||
$coupon-divider-color 0rpx,
|
||||
$coupon-divider-color 8rpx,
|
||||
transparent 8rpx,
|
||||
transparent 16rpx);
|
||||
margin: 0 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-right {
|
||||
flex: 1;
|
||||
.coupon-right {
|
||||
// flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8rpx;
|
||||
align-items: flex-end;
|
||||
}
|
||||
align-items: center;
|
||||
margin: auto 0;
|
||||
width: 160rpx;
|
||||
// align-content: center;
|
||||
// transform: translateY(50%);
|
||||
}
|
||||
|
||||
.coupon-name {
|
||||
.coupon-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-validity {
|
||||
.coupon-validity {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.use-btn {
|
||||
margin-top: 10rpx;
|
||||
padding: 12rpx 28rpx;
|
||||
background-color: #B8741A;
|
||||
border-radius: 40rpx;
|
||||
.use-btn {
|
||||
// margin-top: 10rpx;
|
||||
// padding: 12rpx 28rpx;
|
||||
// background-color: #B8741A;
|
||||
// border-radius: 40rpx;
|
||||
|
||||
.use-text {
|
||||
font-size: 24rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
}
|
||||
font-size: 28rpx;
|
||||
color: #A16300;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-status {
|
||||
.coupon-status {
|
||||
margin-top: 10rpx;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@@ -416,5 +535,5 @@
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+33
-82
@@ -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: {
|
||||
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(() => {
|
||||
@@ -1162,11 +1197,17 @@
|
||||
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 {
|
||||
@@ -1211,6 +1252,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 {
|
||||
background: #fff;
|
||||
@@ -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'
|
||||
});
|
||||
}
|
||||
|
||||
+171
-297
@@ -59,96 +59,210 @@
|
||||
</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: {
|
||||
const { t } = useI18n()
|
||||
|
||||
const orderId = ref(null)
|
||||
const deviceNo = ref(null)
|
||||
const orderInfo = ref({})
|
||||
const packageInfo = ref({
|
||||
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') },
|
||||
})
|
||||
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);
|
||||
}
|
||||
}
|
||||
},
|
||||
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)
|
||||
const deposit = parseFloat(orderInfo.value.deposit || passedDepositAmount.value || 99)
|
||||
const packagePrice = parseFloat(packageInfo.value.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);
|
||||
// 加载订单信息
|
||||
const loadOrderInfo = async () => {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: t('common.loading')
|
||||
})
|
||||
|
||||
const res = await queryById(orderId.value)
|
||||
if (res.code === 200 && res.data) {
|
||||
const orderData = res.data
|
||||
|
||||
// 处理创建时间
|
||||
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 (time <= 0) time = 1;
|
||||
|
||||
// 计算每小时价格
|
||||
return (price / time).toFixed(2);
|
||||
orderInfo.value = {
|
||||
orderNo: orderData.orderNo || orderData.orderId,
|
||||
deviceNo: orderData.deviceNo,
|
||||
createTime: formattedTime,
|
||||
phone: orderData.phone,
|
||||
deposit: passedDepositAmount.value || orderData.depositAmount || '99.00',
|
||||
}
|
||||
|
||||
if (orderData.packageTime && orderData.packagePrice) {
|
||||
const timeInHours = (parseFloat(orderData.packageTime) / 60).toFixed(1);
|
||||
packageInfo.value = {
|
||||
time: timeInHours.toString(),
|
||||
price: orderData.packagePrice.toString()
|
||||
}
|
||||
}
|
||||
|
||||
deviceNo.value = orderData.deviceNo;
|
||||
await loadDeviceInfo();
|
||||
} else {
|
||||
throw new Error(t('order.getOrderFailed'))
|
||||
}
|
||||
} 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);
|
||||
},
|
||||
onLoad(options) {
|
||||
// 设置页面标题
|
||||
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: this.$t('payment.orderPayment')
|
||||
title: t('payment.orderPayment')
|
||||
})
|
||||
|
||||
if (options && options.orderId) {
|
||||
this.orderId = options.orderId
|
||||
orderId.value = options.orderId
|
||||
|
||||
if (options.totalAmount) {
|
||||
this.passedTotalAmount = options.depositAmount;
|
||||
passedTotalAmount.value = options.totalAmount;
|
||||
}
|
||||
|
||||
if (options.depositAmount) {
|
||||
this.passedDepositAmount = options.depositAmount;
|
||||
passedDepositAmount.value = options.depositAmount;
|
||||
}
|
||||
|
||||
// 如果URL中包含了feeConfig参数,保存它
|
||||
if (options.feeConfig) {
|
||||
try {
|
||||
console.log('从URL获取到feeConfig:', options.feeConfig)
|
||||
const feeConfigStr = decodeURIComponent(options.feeConfig)
|
||||
// 创建一个临时的deviceInfo对象保存feeConfig
|
||||
this.deviceInfo = { feeConfig: feeConfigStr }
|
||||
deviceInfo.value = { feeConfig: feeConfigStr }
|
||||
} catch (e) {
|
||||
console.error('解析URL中的feeConfig失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
this.loadOrderInfo()
|
||||
loadOrderInfo()
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: this.$t('order.orderNotExist'),
|
||||
title: t('order.orderNotExist'),
|
||||
icon: 'none'
|
||||
})
|
||||
setTimeout(() => {
|
||||
@@ -157,247 +271,7 @@ export default {
|
||||
})
|
||||
}, 1500)
|
||||
}
|
||||
},
|
||||
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;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
</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
|
||||
|
||||
+374
-165
@@ -19,12 +19,60 @@
|
||||
:class="{ selected: selectedProduct?.id === item.id }" @click="selectProduct(item)">
|
||||
<view class="card-content">
|
||||
<view class="card-left">
|
||||
<view class="card-title-row">
|
||||
<text class="card-name">{{ item.name }}</text>
|
||||
<view class="card-type-tag" :class="{ 'type-time': item.type === 'TIME', 'type-count': item.type === 'COUNT' }">
|
||||
<text class="card-type-text">{{ item.type === 'TIME' ? $t('myCard.durationCard') : $t('myCard.timesCard') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="card-desc">{{ item.description }}</text>
|
||||
|
||||
<!-- 使用限制信息 -->
|
||||
<view class="card-limits">
|
||||
<!-- 时长卡:展示每日限用次数和单次限时 -->
|
||||
<view v-if="item.type === 'TIME'" class="limit-tags">
|
||||
<view class="limit-tag" v-if="item.dailyLimitCount > 0">
|
||||
<text class="limit-label">{{ $t('myCard.dailyLimit') }}:</text>
|
||||
<text class="limit-value">{{ item.dailyLimitCount }}{{ $t('myCard.times') }}</text>
|
||||
</view>
|
||||
<view class="limit-tag">
|
||||
<text class="limit-label">{{ $t('myCard.singleTimeLimit') }}:</text>
|
||||
<text class="limit-value" :class="{ unlimited: item.singleLimitMinutes === 0 }">
|
||||
{{ item.singleLimitMinutes > 0 ? item.singleLimitMinutes + $t('myCard.minutes') : $t('myCard.unlimited') }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="limit-tag validity-tag" v-if="item.cycleDays">
|
||||
<text class="limit-value">{{ item.cycleDays }}{{ $t('myCard.daysValid') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 次卡:展示总次数和单次限时 -->
|
||||
<view v-if="item.type === 'COUNT'" class="limit-tags">
|
||||
<view class="limit-tag count-tag" v-if="item.totalCount > 0">
|
||||
<text class="limit-label">{{ $t('myCard.totalCount') }}:</text>
|
||||
<text class="limit-value">{{ item.totalCount }}{{ $t('myCard.times') }}</text>
|
||||
</view>
|
||||
<view class="limit-tag">
|
||||
<text class="limit-label">{{ $t('myCard.singleTimeLimit') }}:</text>
|
||||
<text class="limit-value" :class="{ unlimited: item.singleLimitMinutesForCount === 0 }">
|
||||
{{ item.singleLimitMinutesForCount > 0 ? item.singleLimitMinutesForCount + $t('myCard.minutes') : $t('myCard.unlimited') }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="limit-tag validity-tag" v-if="item.validDays">
|
||||
<text class="limit-value">{{ item.validDays }}{{ $t('myCard.daysValid') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-right">
|
||||
<text class="card-price">¥{{ item.price }}</text>
|
||||
<text class="card-original-price" v-if="item.originalPrice">¥{{ item.originalPrice }}</text>
|
||||
<view class="card-price">
|
||||
<text class="price-currency">¥</text>
|
||||
<text class="price-value">{{ item.price }}</text>
|
||||
</view>
|
||||
<view class="card-original-price" v-if="item.originalPrice">
|
||||
<text class="original-currency">¥</text>
|
||||
<text class="original-value">{{ item.originalPrice }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -36,19 +84,24 @@
|
||||
<!-- 优惠券列表 -->
|
||||
<view v-if="currentTab === 'coupon'" class="product-list">
|
||||
<view v-for="item in coupons" :key="item.id" class="coupon-card-wrapper">
|
||||
<view class="coupon-card"
|
||||
:class="{ selected: selectedProduct?.id === item.id }" @click="selectProduct(item)">
|
||||
<view class="coupon-card" :class="{ selected: selectedProduct?.id === item.id }"
|
||||
@click="selectProduct(item)">
|
||||
<!-- 左侧圆形缺口 -->
|
||||
<view class="coupon-circle-left"></view>
|
||||
<!-- 右侧圆形缺口 -->
|
||||
<view class="coupon-circle-right"></view>
|
||||
|
||||
<view class="coupon-left">
|
||||
<text class="coupon-value">{{ item.type === 'discount' ? item.discount + '折' : '¥' + item.value
|
||||
<view class="coupon-value-wrapper">
|
||||
<text class="coupon-value-num">{{ item.type === 'discount' ? item.discount : item.value
|
||||
}}</text>
|
||||
<text class="coupon-value-unit">{{ item.type === 'discount' ? '折' : '¥' }}</text>
|
||||
</view>
|
||||
<view style="display: flex;flex-direction: column;">
|
||||
<text class="coupon-condition">{{ item.condition }}</text>
|
||||
<text class="coupon-validity">{{ item.validity }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="coupon-divider"></view>
|
||||
<view class="coupon-right">
|
||||
<view class="coupon-price">
|
||||
@@ -79,8 +132,8 @@
|
||||
<!-- 底部操作栏 -->
|
||||
<view class="bottom-bar">
|
||||
<view class="my-products-wrapper" @click="goToMyProducts">
|
||||
<view class="" style="display: flex;" >
|
||||
<view class="my-products" >
|
||||
<view class="" style="display: flex;">
|
||||
<view class="my-products">
|
||||
<image class="my-products-icon" src="/static/couponList.png" mode="aspectFit">
|
||||
</image>
|
||||
<text class="my-products-text">{{ currentTab === 'card' ? $t('purchase.myCards') :
|
||||
@@ -107,95 +160,95 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
import {
|
||||
ref,
|
||||
computed,
|
||||
onMounted
|
||||
} from 'vue'
|
||||
import {
|
||||
} from 'vue'
|
||||
import {
|
||||
onLoad
|
||||
} from '@dcloudio/uni-app'
|
||||
import {
|
||||
} from '@dcloudio/uni-app'
|
||||
import {
|
||||
useI18n
|
||||
} from '@/utils/i18n.js'
|
||||
import {
|
||||
} from '@/utils/i18n.js'
|
||||
import {
|
||||
getCouponsByPosition,
|
||||
createCouponPayment,
|
||||
cancelCouponPayment
|
||||
} from '@/config/api/coupon.js'
|
||||
// import {
|
||||
// cancelMemberCardPayment
|
||||
// } from '@/config/api/member.js'
|
||||
import {
|
||||
} from '@/config/api/coupon.js'
|
||||
// import {
|
||||
// cancelMemberCardPayment
|
||||
// } from '@/config/api/member.js'
|
||||
import {
|
||||
createMemberCardPayment,
|
||||
getMemberCardsByPosition,
|
||||
cancelMemberCardPayment
|
||||
} from '@/config/api/member.js'
|
||||
} from '@/config/api/member.js'
|
||||
|
||||
const {
|
||||
t: $t
|
||||
} = useI18n()
|
||||
const {
|
||||
t
|
||||
} = useI18n()
|
||||
|
||||
// 当前选中的 Tab
|
||||
const currentTab = ref('card') // 'card' 或 'coupon'
|
||||
// 当前选中的 Tab
|
||||
const currentTab = ref('card') // 'card' 或 'coupon'
|
||||
|
||||
// 选中的商品
|
||||
const selectedProduct = ref(null)
|
||||
// 选中的商品
|
||||
const selectedProduct = ref(null)
|
||||
|
||||
// 当前场地ID(从路由参数获取)
|
||||
const positionId = ref(null)
|
||||
// 当前场地ID(从路由参数获取)
|
||||
const positionId = ref(null)
|
||||
|
||||
// 生命周期 onLoad 钩子 - 获取路由参数
|
||||
onLoad((options) => {
|
||||
// 生命周期 onLoad 钩子 - 获取路由参数
|
||||
onLoad((options) => {
|
||||
if (options.positionId) {
|
||||
positionId.value = options.positionId
|
||||
console.log('获取到场地ID:', positionId.value)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// 会员卡列表(从后端加载)
|
||||
const memberCards = ref([])
|
||||
// 会员卡列表(从后端加载)
|
||||
const memberCards = ref([])
|
||||
|
||||
// 优惠券列表(从后端加载)
|
||||
const coupons = ref([])
|
||||
// 优惠券列表(从后端加载)
|
||||
const coupons = ref([])
|
||||
|
||||
// 说明内容
|
||||
const descriptions = computed(() => {
|
||||
// 说明内容
|
||||
const descriptions = computed(() => {
|
||||
if (currentTab.value === 'card') {
|
||||
return [
|
||||
{
|
||||
title: $t('purchase.cardUseInstruction'),
|
||||
content: $t('purchase.cardUseDescription')
|
||||
title: t('purchase.cardUseInstruction'),
|
||||
content: t('purchase.cardUseDescription')
|
||||
},
|
||||
{
|
||||
title: $t('purchase.cardValidityPeriod'),
|
||||
content: $t('purchase.cardValidityDescription')
|
||||
title: t('purchase.cardValidityPeriod'),
|
||||
content: t('purchase.cardValidityDescription')
|
||||
},
|
||||
{
|
||||
title: $t('purchase.cardRefundPolicy'),
|
||||
content: $t('purchase.cardRefundDescription')
|
||||
title: t('purchase.cardRefundPolicy'),
|
||||
content: t('purchase.cardRefundDescription')
|
||||
}
|
||||
]
|
||||
} else {
|
||||
return [
|
||||
{
|
||||
title: $t('purchase.couponUseInstruction'),
|
||||
content: $t('purchase.couponUseDescription')
|
||||
title: t('purchase.couponUseInstruction'),
|
||||
content: t('purchase.couponUseDescription')
|
||||
},
|
||||
{
|
||||
title: $t('purchase.couponValidityPeriod'),
|
||||
content: $t('purchase.couponValidityDescription')
|
||||
title: t('purchase.couponValidityPeriod'),
|
||||
content: t('purchase.couponValidityDescription')
|
||||
},
|
||||
{
|
||||
title: $t('purchase.couponUsageScope'),
|
||||
content: $t('purchase.couponUsageDescription')
|
||||
title: t('purchase.couponUsageScope'),
|
||||
content: t('purchase.couponUsageDescription')
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// 加载会员卡列表
|
||||
const loadMemberCards = async () => {
|
||||
// 加载会员卡列表
|
||||
const loadMemberCards = async () => {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: '加载中...'
|
||||
@@ -215,8 +268,9 @@
|
||||
originalPrice: null,
|
||||
availablePositions: item.positionName,
|
||||
cycleDays: item.cycleDays,
|
||||
dailyLimitCount: item.dailyLimitCount,
|
||||
singleLimitMinutes: item.singleLimitMinutes,
|
||||
dailyLimitCount: item.dailyLimitCount || 0,
|
||||
singleLimitMinutes: item.singleLimitMinutes || 0,
|
||||
singleLimitMinutesForCount: item.singleLimitMinutesForCount || 0,
|
||||
validDays: item.validDays,
|
||||
totalCount: item.totalCount,
|
||||
purchaseQuantity: item.purchaseQuantity
|
||||
@@ -230,11 +284,11 @@
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 加载优惠券列表
|
||||
const loadCoupons = async () => {
|
||||
// 加载优惠券列表
|
||||
const loadCoupons = async () => {
|
||||
try {
|
||||
uni.showLoading({
|
||||
title: '加载中...'
|
||||
@@ -249,7 +303,7 @@
|
||||
id: item.couponId,
|
||||
couponId: item.couponId,
|
||||
name: item.couponName,
|
||||
type: item.couponType === '1' ? 'discount' : 'cash',
|
||||
type: item.couponType === 'discount_coupon' ? 'discount' : 'cash',
|
||||
discount: item.discountRate ? (item.discountRate * 10).toFixed(0) : null,
|
||||
value: item.deductAmount ? item.deductAmount.toString() : null,
|
||||
condition: item.usableCondition > 0 ? `满${item.usableCondition}元可用` : '无门槛',
|
||||
@@ -268,10 +322,10 @@
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(async () => {
|
||||
// 初始化
|
||||
onMounted(async () => {
|
||||
// 加载会员卡列表
|
||||
if (positionId.value) {
|
||||
await loadMemberCards()
|
||||
@@ -286,24 +340,24 @@
|
||||
if (memberCards.value.length > 0) {
|
||||
selectedProduct.value = memberCards.value[0]
|
||||
}
|
||||
})
|
||||
const switchTab = (tab) => {
|
||||
})
|
||||
const switchTab = (tab) => {
|
||||
currentTab.value = tab
|
||||
selectedProduct.value = null
|
||||
}
|
||||
}
|
||||
|
||||
// 选择商品
|
||||
const selectProduct = (product) => {
|
||||
// 选择商品
|
||||
const selectProduct = (product) => {
|
||||
selectedProduct.value = product
|
||||
}
|
||||
}
|
||||
|
||||
const orderNo = ref('')
|
||||
const orderNo = ref('')
|
||||
|
||||
// 处理购买
|
||||
const handleBuy = async () => {
|
||||
// 处理购买
|
||||
const handleBuy = async () => {
|
||||
if (!selectedProduct.value) {
|
||||
uni.showToast({
|
||||
title: $t('purchase.pleaseSelect'),
|
||||
title: t('purchase.pleaseSelect'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
@@ -454,10 +508,10 @@
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查看我的商品
|
||||
const goToMyProducts = () => {
|
||||
// 查看我的商品
|
||||
const goToMyProducts = () => {
|
||||
if (currentTab.value === 'card') {
|
||||
uni.navigateTo({
|
||||
url: '/pages/my/card'
|
||||
@@ -467,76 +521,85 @@
|
||||
url: '/pages/my/coupon'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.purchase-page {
|
||||
.purchase-page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
padding-bottom: 180rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tab 切换 */
|
||||
.tab-container {
|
||||
top: 0;
|
||||
/* Tab 切换 */
|
||||
.tab-container {
|
||||
top: 20rpx;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
background-color: #ffffff;
|
||||
width: 500rpx;
|
||||
z-index: 999;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
padding: 5rpx 0;
|
||||
margin: 0 auto;
|
||||
border-radius: 26rpx;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 20rpx 0;
|
||||
padding: 10rpx 30rpx;
|
||||
margin: 0 10rpx;
|
||||
width: 120rpx;
|
||||
position: relative;
|
||||
|
||||
.tab-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
color: #A6A6A6;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background:#FFE2B8;
|
||||
border-radius: 26rpx;
|
||||
|
||||
|
||||
.tab-text {
|
||||
color: #333;
|
||||
color: #A16300;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 60rpx;
|
||||
height: 6rpx;
|
||||
background-color: #FFA928;
|
||||
border-radius: 3rpx;
|
||||
}
|
||||
}
|
||||
// &::after {
|
||||
// content: '';
|
||||
// position: absolute;
|
||||
// bottom: 0;
|
||||
// left: 50%;
|
||||
// transform: translateX(-50%);
|
||||
// width: 60rpx;
|
||||
// height: 6rpx;
|
||||
// background-color: #FFA928;
|
||||
// border-radius: 3rpx;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
/* 内容区域 */
|
||||
.content-area {
|
||||
/* 内容区域 */
|
||||
.content-area {
|
||||
height: calc(100vh - 180rpx);
|
||||
padding: 20rpx;
|
||||
padding-top: 120rpx;
|
||||
padding-top: 80rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
/* 商品列表 */
|
||||
.product-list {
|
||||
/* 商品列表 */
|
||||
.product-list {
|
||||
margin-bottom: 20rpx;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 会员卡卡片 */
|
||||
.product-card {
|
||||
/* 会员卡卡片 */
|
||||
.product-card {
|
||||
background-color: #ffffff;
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
@@ -567,18 +630,118 @@
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.card-title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.card-type-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 8rpx;
|
||||
flex-shrink: 0;
|
||||
|
||||
&.type-time {
|
||||
background-color: #E6F7FF;
|
||||
border: 1rpx solid #91D5FF;
|
||||
}
|
||||
|
||||
&.type-count {
|
||||
background-color: #FFF1F0;
|
||||
border: 1rpx solid #FFCCC7;
|
||||
}
|
||||
|
||||
.card-type-text {
|
||||
font-size: 20rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.type-time .card-type-text {
|
||||
color: #1890FF;
|
||||
}
|
||||
|
||||
.type-count .card-type-text {
|
||||
color: #FF4D4F;
|
||||
}
|
||||
|
||||
.card-desc {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 会员卡限制信息样式 */
|
||||
.card-limits {
|
||||
margin-top: 12rpx;
|
||||
}
|
||||
|
||||
.limit-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.limit-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4rpx;
|
||||
padding: 6rpx 12rpx;
|
||||
background-color: #FFF4E6;
|
||||
border-radius: 8rpx;
|
||||
border: 1rpx solid #FFE8CC;
|
||||
|
||||
.limit-label {
|
||||
font-size: 22rpx;
|
||||
color: #B8741A;
|
||||
// font-weight: 500;
|
||||
}
|
||||
|
||||
.limit-value {
|
||||
font-size: 22rpx;
|
||||
color: #FF6B00;
|
||||
// font-weight: 600;
|
||||
|
||||
&.unlimited {
|
||||
color: #07c160;
|
||||
}
|
||||
}
|
||||
|
||||
/* 有效期标签特殊样式 */
|
||||
&.validity-tag {
|
||||
background-color: #F0F5FF;
|
||||
border: 1rpx solid #D6E4FF;
|
||||
|
||||
.limit-value {
|
||||
color: #1890FF;
|
||||
}
|
||||
}
|
||||
|
||||
/* 总次数标签特殊样式 */
|
||||
&.count-tag {
|
||||
background-color: #FFF1F0;
|
||||
border: 1rpx solid #FFCCC7;
|
||||
|
||||
.limit-label {
|
||||
color: #CF1322;
|
||||
}
|
||||
|
||||
.limit-value {
|
||||
color: #FF4D4F;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -587,29 +750,54 @@
|
||||
}
|
||||
|
||||
.card-price {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 2rpx;
|
||||
|
||||
.price-currency {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #FF6B00;
|
||||
}
|
||||
|
||||
.price-value {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #FF6B00;
|
||||
}
|
||||
}
|
||||
|
||||
.card-original-price {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 2rpx;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.original-currency {
|
||||
font-size: 20rpx;
|
||||
font-weight: 400;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 优惠券卡片 */
|
||||
.coupon-card-wrapper {
|
||||
.original-value {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 优惠券卡片 */
|
||||
.coupon-card-wrapper {
|
||||
position: relative;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-card {
|
||||
.coupon-card {
|
||||
background: linear-gradient(135deg, #FFF4E6 0%, #FFE8CC 100%);
|
||||
border-radius: 20rpx;
|
||||
padding: 40rpx 30rpx;
|
||||
@@ -632,10 +820,10 @@
|
||||
border: 2rpx solid #B8741A;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 左侧圆形缺口 */
|
||||
.coupon-circle-left {
|
||||
/* 左侧圆形缺口 */
|
||||
.coupon-circle-left {
|
||||
position: absolute;
|
||||
left: -16rpx;
|
||||
top: 50%;
|
||||
@@ -647,10 +835,10 @@
|
||||
z-index: 10;
|
||||
transition: all 0.3s;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
/* 右侧圆形缺口 */
|
||||
.coupon-circle-right {
|
||||
/* 右侧圆形缺口 */
|
||||
.coupon-circle-right {
|
||||
position: absolute;
|
||||
right: -16rpx;
|
||||
top: 50%;
|
||||
@@ -662,32 +850,53 @@
|
||||
z-index: 10;
|
||||
transition: all 0.3s;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-left {
|
||||
.coupon-left {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-direction: row;
|
||||
gap: 8rpx;
|
||||
}
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.coupon-value {
|
||||
/* 优惠券金额显示包裹器 */
|
||||
.coupon-value-wrapper {
|
||||
width: 180rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 优惠券实际值(数字) */
|
||||
.coupon-value-num {
|
||||
font-size: 52rpx;
|
||||
font-weight: 700;
|
||||
color: #B8741A;
|
||||
}
|
||||
|
||||
/* 优惠券单位(折/¥) */
|
||||
.coupon-value-unit {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #B8741A;
|
||||
}
|
||||
|
||||
.coupon-value {
|
||||
font-size: 48rpx;
|
||||
font-weight: 700;
|
||||
color: #B8741A;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-condition {
|
||||
.coupon-condition {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-validity {
|
||||
.coupon-validity {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-divider {
|
||||
.coupon-divider {
|
||||
width: 2rpx;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
@@ -703,9 +912,9 @@
|
||||
transparent 8rpx,
|
||||
transparent 16rpx);
|
||||
margin: 0 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-right {
|
||||
.coupon-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -727,51 +936,51 @@
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 说明部分 */
|
||||
.description-section {
|
||||
/* 说明部分 */
|
||||
.description-section {
|
||||
background-color: #ffffff;
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
margin-top: 20rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.description-title {
|
||||
.description-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.description-content {
|
||||
.description-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.description-item {
|
||||
.description-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.description-subtitle {
|
||||
.description-subtitle {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.description-text {
|
||||
.description-text {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
line-height: 1.8;
|
||||
}
|
||||
}
|
||||
|
||||
/* 底部操作栏 */
|
||||
.bottom-bar {
|
||||
/* 底部操作栏 */
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
@@ -783,9 +992,9 @@
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
|
||||
z-index: 1000;
|
||||
}
|
||||
}
|
||||
|
||||
.my-products-wrapper {
|
||||
.my-products-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: row;
|
||||
@@ -793,9 +1002,9 @@
|
||||
border-radius: 40rpx;
|
||||
padding: 16rpx 32rpx;
|
||||
border-radius: 40rpx 40rpx 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.action-wrapper {
|
||||
.action-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
@@ -803,9 +1012,9 @@
|
||||
margin-left: 20rpx;
|
||||
margin-right: 20rpx;
|
||||
padding-top: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.my-products {
|
||||
.my-products {
|
||||
display: flex;
|
||||
// flex-direction: column;
|
||||
align-items: center;
|
||||
@@ -834,30 +1043,30 @@
|
||||
color: #A16300;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.price-info {
|
||||
.price-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// align-items: flex-end;
|
||||
gap: 4rpx;
|
||||
flex: 1;
|
||||
margin: 0 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.current-price {
|
||||
.current-price {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #FF6B00;
|
||||
}
|
||||
}
|
||||
|
||||
.original-price {
|
||||
.original-price {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
|
||||
.buy-button {
|
||||
.buy-button {
|
||||
padding: 24rpx 60rpx;
|
||||
background-color: #B8741A;
|
||||
border-radius: 48rpx;
|
||||
@@ -873,5 +1082,5 @@
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+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
|
||||
|
||||
+51
-40
@@ -22,15 +22,19 @@
|
||||
: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="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>
|
||||
@@ -41,32 +45,28 @@
|
||||
<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>
|
||||
<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>
|
||||
</view>
|
||||
<view class="actions">
|
||||
|
||||
<!-- 第三列:操作 -->
|
||||
<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>
|
||||
{{ 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 coupon" v-if="item.supportCouponOrMember">{{ $t('location.supportCouponOrMember') }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -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',
|
||||
@@ -133,11 +133,6 @@
|
||||
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`
|
||||
return `${(meters / 1000).toFixed(1)}km`
|
||||
@@ -193,11 +188,6 @@
|
||||
applyFilter()
|
||||
}
|
||||
|
||||
const CouponOrmember= async(item)=>{
|
||||
|
||||
return "可使用优惠券、会员卡"
|
||||
}
|
||||
|
||||
const loadPositions = async (center) => {
|
||||
try {
|
||||
isLoading.value = true
|
||||
@@ -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,9 +489,11 @@ const init = async () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
@@ -507,6 +513,10 @@ const init = async () => {
|
||||
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'
|
||||
})
|
||||
}
|
||||
|
||||
+11
-42
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
login,
|
||||
getMyIndexInfo
|
||||
getMyIndexInfo,
|
||||
getWxUserPhoneNumber
|
||||
} from "../config/api/user"
|
||||
import {
|
||||
URL,
|
||||
@@ -84,52 +85,20 @@ export const getUserInfo = () => {
|
||||
export const getUserPhoneNumber = (code) => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
// 获取登录态信息
|
||||
const token = uni.getStorageSync('token')
|
||||
|
||||
if (!token) {
|
||||
reject(new Error('用户未登录'))
|
||||
return
|
||||
}
|
||||
|
||||
console.log('开始请求获取手机号,code=', code, '请求地址=', URL + '/app/user/getPhoneNumber')
|
||||
console.log('开始请求获取手机号,code=', code)
|
||||
|
||||
// 发送请求到后端获取手机号
|
||||
uni.request({
|
||||
url: URL + '/app/user/getPhoneNumber',
|
||||
method: 'POST',
|
||||
header: {
|
||||
'Authorization': 'Bearer ' + token,
|
||||
'clientId': uni.getStorageSync('client_id'),
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
data: {
|
||||
const res = await getWxUserPhoneNumber({
|
||||
code: code, // 微信获取手机号授权后的临时code
|
||||
appid: appid
|
||||
},
|
||||
success: (res) => {
|
||||
console.log('请求获取手机号接口成功,原始响应:', JSON.stringify(res))
|
||||
|
||||
// 检查HTTP状态码
|
||||
if (res.statusCode !== 200) {
|
||||
reject(new Error(`HTTP错误: ${res.statusCode}`))
|
||||
return
|
||||
}
|
||||
|
||||
// 确保响应体存在
|
||||
if (!res.data) {
|
||||
reject(new Error('服务器返回空数据'))
|
||||
return
|
||||
}
|
||||
|
||||
// 正常返回响应数据,不做额外判断,由调用方处理业务状态码
|
||||
resolve(res.data)
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('请求获取手机号接口网络错误:', err)
|
||||
reject(new Error('请求获取手机号接口失败: ' + (err.errMsg || JSON.stringify(err))))
|
||||
}
|
||||
})
|
||||
|
||||
if (res) {
|
||||
console.log('请求获取手机号接口成功:', JSON.stringify(res))
|
||||
resolve(res)
|
||||
} else {
|
||||
reject(new Error('服务器返回空数据'))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取手机号过程中出现异常:', error)
|
||||
reject(new Error('获取手机号异常: ' + (error.message || JSON.stringify(error))))
|
||||
|
||||
+318
-12
@@ -4,16 +4,16 @@ const DEFAULT_LOCALE = 'zh-CN'
|
||||
const permissionTexts = {
|
||||
'zh-CN': {
|
||||
locationTitle: '位置信息授权',
|
||||
locationNeed: '需要获取您的位置信息以展示附近设备,请在“设置-权限管理”中开启定位权限。',
|
||||
locationDenied: '未授权定位,将无法展示附近设备。您可以稍后在“设置-权限管理”中重新开启定位权限。',
|
||||
locationNeed: '需要获取您的位置信息以展示附近设备,请在"设置-权限管理"中开启定位权限。',
|
||||
locationDenied: '未授权定位,将无法展示附近设备。您可以稍后在"设置-权限管理"中重新开启定位权限。',
|
||||
goToSettings: '去设置',
|
||||
later: '暂不',
|
||||
gotIt: '知道了'
|
||||
},
|
||||
'en-US': {
|
||||
locationTitle: 'Location Permission',
|
||||
locationNeed: 'We need your location to show nearby devices. Please enable location in “Settings > Permissions”.',
|
||||
locationDenied: 'Location access is disabled. You can re-enable it later in “Settings > Permissions”.',
|
||||
locationNeed: 'We need your location to show nearby devices. Please enable location in "Settings > Permissions".',
|
||||
locationDenied: 'Location access is disabled. You can re-enable it later in "Settings > Permissions".',
|
||||
goToSettings: 'Set',
|
||||
later: 'Skip',
|
||||
gotIt: 'OK'
|
||||
@@ -37,7 +37,14 @@ const getPermissionText = (key) => {
|
||||
}
|
||||
|
||||
// 腾讯地图Key
|
||||
const QQMAP_KEY = 'RO5BZ-ECZ63-7US3C-RT5QW-TIDZE-2FF35';
|
||||
const QQMAP_KEY =
|
||||
// #ifdef H5
|
||||
'DJQBZ-WB53Q-WPS5B-4S6J7-53RMS-X4FJ2'
|
||||
// #endif
|
||||
// #ifndef H5
|
||||
'RO5BZ-ECZ63-7US3C-RT5QW-TIDZE-2FF35'
|
||||
// #endif
|
||||
;
|
||||
|
||||
// 内联腾讯地图SDK核心代码
|
||||
const QQMapWX = (function() {
|
||||
@@ -118,7 +125,7 @@ const QQMapWX = (function() {
|
||||
}
|
||||
};
|
||||
if (!param.location) {
|
||||
wx.getLocation({
|
||||
uni.getLocation({
|
||||
type: 'gcj02',
|
||||
success: locationsuccess,
|
||||
fail: locationfail,
|
||||
@@ -280,10 +287,16 @@ const QQMapWX = (function() {
|
||||
|
||||
const locationsuccess = function (result) {
|
||||
requestParam.location = result.latitude + ',' + result.longitude;
|
||||
wx.request(Utils.buildWxRequestConfig(options, {
|
||||
// #ifdef H5
|
||||
// H5环境使用JSONP方式
|
||||
that._requestJsonp(URL_GET_GEOCODER, requestParam, options, 'reverseGeocoder');
|
||||
// #endif
|
||||
// #ifndef H5
|
||||
uni.request(Utils.buildWxRequestConfig(options, {
|
||||
url: URL_GET_GEOCODER,
|
||||
data: requestParam
|
||||
}, 'reverseGeocoder'));
|
||||
// #endif
|
||||
};
|
||||
Utils.locationProcess(options, locationsuccess);
|
||||
}
|
||||
@@ -323,10 +336,16 @@ const QQMapWX = (function() {
|
||||
|
||||
const locationsuccess = function (result) {
|
||||
requestParam.boundary = "nearby(" + result.latitude + "," + result.longitude + "," + distance + "," + auto_extend + ")";
|
||||
wx.request(Utils.buildWxRequestConfig(options, {
|
||||
// #ifdef H5
|
||||
// H5环境使用JSONP方式
|
||||
that._requestJsonp(URL_SEARCH, requestParam, options, 'search');
|
||||
// #endif
|
||||
// #ifndef H5
|
||||
uni.request(Utils.buildWxRequestConfig(options, {
|
||||
url: URL_SEARCH,
|
||||
data: requestParam
|
||||
}, 'search'));
|
||||
// #endif
|
||||
};
|
||||
|
||||
Utils.locationProcess(options, locationsuccess);
|
||||
@@ -368,17 +387,27 @@ const QQMapWX = (function() {
|
||||
if (options.location) {
|
||||
const locationsuccess = function (result) {
|
||||
requestParam.location = result.latitude + ',' + result.longitude;
|
||||
wx.request(Utils.buildWxRequestConfig(options, {
|
||||
// #ifdef H5
|
||||
that._requestJsonp(URL_SUGGESTION, requestParam, options, "suggest");
|
||||
// #endif
|
||||
// #ifndef H5
|
||||
uni.request(Utils.buildWxRequestConfig(options, {
|
||||
url: URL_SUGGESTION,
|
||||
data: requestParam
|
||||
}, "suggest"));
|
||||
// #endif
|
||||
};
|
||||
Utils.locationProcess(options, locationsuccess);
|
||||
} else {
|
||||
wx.request(Utils.buildWxRequestConfig(options, {
|
||||
// #ifdef H5
|
||||
that._requestJsonp(URL_SUGGESTION, requestParam, options, "suggest");
|
||||
// #endif
|
||||
// #ifndef H5
|
||||
uni.request(Utils.buildWxRequestConfig(options, {
|
||||
url: URL_SUGGESTION,
|
||||
data: requestParam
|
||||
}, "suggest"));
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,14 +437,64 @@ const QQMapWX = (function() {
|
||||
|
||||
const locationsuccess = function (result) {
|
||||
requestParam.from = result.latitude + ',' + result.longitude;
|
||||
wx.request(Utils.buildWxRequestConfig(options, {
|
||||
// #ifdef H5
|
||||
that._requestJsonp(URL_DISTANCE, requestParam, options, 'calculateDistance');
|
||||
// #endif
|
||||
// #ifndef H5
|
||||
uni.request(Utils.buildWxRequestConfig(options, {
|
||||
url: URL_DISTANCE,
|
||||
data: requestParam
|
||||
}, 'calculateDistance'));
|
||||
// #endif
|
||||
};
|
||||
|
||||
Utils.locationProcess(options, locationsuccess);
|
||||
}
|
||||
|
||||
// H5环境JSONP请求方法
|
||||
_requestJsonp(url, params, options, feature) {
|
||||
const callbackName = `qqmap_callback_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
// 构建URL参数
|
||||
const queryString = Object.keys(params)
|
||||
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
|
||||
.join('&');
|
||||
|
||||
const fullUrl = `${url}?${queryString}&output=jsonp&callback=${callbackName}`;
|
||||
|
||||
// 创建全局回调函数
|
||||
window[callbackName] = (data) => {
|
||||
// 清理回调函数和script标签
|
||||
delete window[callbackName];
|
||||
const script = document.getElementById(callbackName);
|
||||
if (script && script.parentNode) {
|
||||
script.parentNode.removeChild(script);
|
||||
}
|
||||
|
||||
if (data.status === 0) {
|
||||
Utils.handleData(options, data, feature);
|
||||
} else {
|
||||
options.fail(data);
|
||||
}
|
||||
options.complete(data);
|
||||
};
|
||||
|
||||
// 创建script标签进行JSONP请求
|
||||
const script = document.createElement('script');
|
||||
script.id = callbackName;
|
||||
script.src = fullUrl;
|
||||
script.onerror = () => {
|
||||
delete window[callbackName];
|
||||
const scriptEl = document.getElementById(callbackName);
|
||||
if (scriptEl && scriptEl.parentNode) {
|
||||
scriptEl.parentNode.removeChild(scriptEl);
|
||||
}
|
||||
const error = { status: 600, message: '请求失败' };
|
||||
options.fail(error);
|
||||
options.complete(error);
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
}
|
||||
|
||||
return QQMapWX;
|
||||
@@ -550,8 +629,9 @@ function getUserLocation() {
|
||||
});
|
||||
// #endif
|
||||
|
||||
// 非微信小程序平台:使用 uni.getLocation 做一个尽量兼容的兜底
|
||||
// 非微信小程序平台:根据平台使用不同的定位方式
|
||||
// #ifndef MP-WEIXIN
|
||||
// 统一使用 uni.getLocation,框架会根据环境自动选择最佳定位方式
|
||||
uni.getLocation({
|
||||
type: 'gcj02',
|
||||
success: (res) => {
|
||||
@@ -565,6 +645,16 @@ function getUserLocation() {
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('获取位置失败:', error);
|
||||
// H5 环境下的特殊错误提示
|
||||
// #ifdef H5
|
||||
if (error.errMsg && error.errMsg.indexOf('permission denied') !== -1) {
|
||||
uni.showModal({
|
||||
title: getPermissionText('locationTitle'),
|
||||
content: getPermissionText('locationNeed'),
|
||||
showCancel: false
|
||||
});
|
||||
}
|
||||
// #endif
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
@@ -575,6 +665,57 @@ function getUserLocation() {
|
||||
// 逆地理编码 - 根据经纬度获取地址信息
|
||||
function getRegeo(longitude, latitude) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// #ifdef H5
|
||||
// H5环境:使用JSONP方式调用腾讯地图API,避免跨域问题
|
||||
const callbackName = `qqmap_geocoder_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
// 创建全局回调函数
|
||||
window[callbackName] = (data) => {
|
||||
// 清理回调函数和script标签
|
||||
delete window[callbackName];
|
||||
const script = document.getElementById(callbackName);
|
||||
if (script && script.parentNode) {
|
||||
script.parentNode.removeChild(script);
|
||||
}
|
||||
|
||||
if (data.status === 0) {
|
||||
const result = data.result;
|
||||
resolve({
|
||||
success: true,
|
||||
data: {
|
||||
formatted_address: result.address,
|
||||
addressComponent: {
|
||||
city: result.address_component.city,
|
||||
district: result.address_component.district,
|
||||
province: result.address_component.province,
|
||||
street: result.address_component.street,
|
||||
street_number: result.address_component.street_number
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.error('H5逆地理编码失败:', data);
|
||||
reject({ success: false, message: data.message || '逆地理编码失败' });
|
||||
}
|
||||
};
|
||||
|
||||
// 创建script标签进行JSONP请求
|
||||
const script = document.createElement('script');
|
||||
script.id = callbackName;
|
||||
script.src = `https://apis.map.qq.com/ws/geocoder/v1/?location=${latitude},${longitude}&key=${QQMAP_KEY}&output=jsonp&callback=${callbackName}`;
|
||||
script.onerror = () => {
|
||||
delete window[callbackName];
|
||||
const scriptEl = document.getElementById(callbackName);
|
||||
if (scriptEl && scriptEl.parentNode) {
|
||||
scriptEl.parentNode.removeChild(scriptEl);
|
||||
}
|
||||
reject({ success: false, message: '逆地理编码请求失败' });
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// 小程序环境:使用 QQMapWX SDK
|
||||
const qqmap = getQQMapInstance();
|
||||
if (!qqmap) {
|
||||
reject({ success: false, message: '腾讯地图SDK未初始化' });
|
||||
@@ -608,12 +749,68 @@ function getRegeo(longitude, latitude) {
|
||||
reject({ success: false, message: error.message || '逆地理编码失败' });
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
});
|
||||
}
|
||||
|
||||
// 搜索周边POI
|
||||
function getPoiAround(longitude, latitude, keyword = '', radius = 1000) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// #ifdef H5
|
||||
// H5环境:使用JSONP方式调用腾讯地图API
|
||||
const callbackName = `qqmap_search_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
// 创建全局回调函数
|
||||
window[callbackName] = (data) => {
|
||||
// 清理回调函数和script标签
|
||||
delete window[callbackName];
|
||||
const script = document.getElementById(callbackName);
|
||||
if (script && script.parentNode) {
|
||||
script.parentNode.removeChild(script);
|
||||
}
|
||||
|
||||
if (data.status === 0) {
|
||||
const searchSimplify = data.data.map(item => ({
|
||||
id: item.id || null,
|
||||
title: item.title || null,
|
||||
latitude: item.location && item.location.lat || null,
|
||||
longitude: item.location && item.location.lng || null,
|
||||
address: item.address || null,
|
||||
category: item.category || null,
|
||||
tel: item.tel || null,
|
||||
adcode: item.ad_info && item.ad_info.adcode || null,
|
||||
city: item.ad_info && item.ad_info.city || null,
|
||||
district: item.ad_info && item.ad_info.district || null,
|
||||
province: item.ad_info && item.ad_info.province || null
|
||||
}));
|
||||
|
||||
resolve({
|
||||
success: true,
|
||||
data: searchSimplify
|
||||
});
|
||||
} else {
|
||||
console.error('H5搜索POI失败:', data);
|
||||
reject({ success: false, message: data.message || '搜索POI失败' });
|
||||
}
|
||||
};
|
||||
|
||||
// 创建script标签进行JSONP请求
|
||||
const script = document.createElement('script');
|
||||
script.id = callbackName;
|
||||
script.src = `https://apis.map.qq.com/ws/place/v1/search?boundary=nearby(${latitude},${longitude},${radius})&keyword=${encodeURIComponent(keyword)}&orderby=_distance&page_size=10&page_index=1&key=${QQMAP_KEY}&output=jsonp&callback=${callbackName}`;
|
||||
script.onerror = () => {
|
||||
delete window[callbackName];
|
||||
const scriptEl = document.getElementById(callbackName);
|
||||
if (scriptEl && scriptEl.parentNode) {
|
||||
scriptEl.parentNode.removeChild(scriptEl);
|
||||
}
|
||||
reject({ success: false, message: '搜索POI请求失败' });
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// 小程序环境:使用 QQMapWX SDK
|
||||
const qqmap = getQQMapInstance();
|
||||
if (!qqmap) {
|
||||
reject({ success: false, message: '腾讯地图SDK未初始化' });
|
||||
@@ -639,12 +836,64 @@ function getPoiAround(longitude, latitude, keyword = '', radius = 1000) {
|
||||
reject({ success: false, message: error.message || '搜索POI失败' });
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
});
|
||||
}
|
||||
|
||||
// 计算距离(异步)
|
||||
function calculateDistance(from, to) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// #ifdef H5
|
||||
// H5环境:使用JSONP方式调用腾讯地图API
|
||||
const callbackName = `qqmap_distance_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
// 创建全局回调函数
|
||||
window[callbackName] = (data) => {
|
||||
// 清理回调函数和script标签
|
||||
delete window[callbackName];
|
||||
const script = document.getElementById(callbackName);
|
||||
if (script && script.parentNode) {
|
||||
script.parentNode.removeChild(script);
|
||||
}
|
||||
|
||||
if (data.status === 0) {
|
||||
const distances = data.result.elements.map(element => element.distance);
|
||||
resolve({
|
||||
success: true,
|
||||
data: distances
|
||||
});
|
||||
} else {
|
||||
console.error('H5计算距离失败:', data);
|
||||
reject({ success: false, message: data.message || '计算距离失败' });
|
||||
}
|
||||
};
|
||||
|
||||
// 创建script标签进行JSONP请求
|
||||
const script = document.createElement('script');
|
||||
script.id = callbackName;
|
||||
// 构建to参数
|
||||
let toStr = '';
|
||||
if (Array.isArray(to)) {
|
||||
toStr = to.map(p => `${p.latitude},${p.longitude}`).join(';');
|
||||
} else {
|
||||
toStr = `${to.latitude},${to.longitude}`;
|
||||
}
|
||||
|
||||
const fromStr = `${from.latitude},${from.longitude}`;
|
||||
script.src = `https://apis.map.qq.com/ws/distance/v1/?mode=walking&from=${fromStr}&to=${toStr}&key=${QQMAP_KEY}&output=jsonp&callback=${callbackName}`;
|
||||
script.onerror = () => {
|
||||
delete window[callbackName];
|
||||
const scriptEl = document.getElementById(callbackName);
|
||||
if (scriptEl && scriptEl.parentNode) {
|
||||
scriptEl.parentNode.removeChild(scriptEl);
|
||||
}
|
||||
reject({ success: false, message: '计算距离请求失败' });
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// 小程序环境:使用 QQMapWX SDK
|
||||
const qqmap = getQQMapInstance();
|
||||
if (!qqmap) {
|
||||
reject({ success: false, message: '腾讯地图SDK未初始化' });
|
||||
@@ -667,6 +916,7 @@ function calculateDistance(from, to) {
|
||||
reject({ success: false, message: error.message || '计算距离失败' });
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
});
|
||||
}
|
||||
|
||||
@@ -685,6 +935,61 @@ function calculateDistanceSync(lat1, lng1, lat2, lng2) {
|
||||
// 关键词提示
|
||||
function getSuggestion(keyword, region = '全国') {
|
||||
return new Promise((resolve, reject) => {
|
||||
// #ifdef H5
|
||||
// H5环境:使用JSONP方式调用腾讯地图API
|
||||
const callbackName = `qqmap_suggestion_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
// 创建全局回调函数
|
||||
window[callbackName] = (data) => {
|
||||
// 清理回调函数和script标签
|
||||
delete window[callbackName];
|
||||
const script = document.getElementById(callbackName);
|
||||
if (script && script.parentNode) {
|
||||
script.parentNode.removeChild(script);
|
||||
}
|
||||
|
||||
if (data.status === 0) {
|
||||
const suggestSimplify = data.data.map(item => ({
|
||||
adcode: item.adcode || null,
|
||||
address: item.address || null,
|
||||
category: item.category || null,
|
||||
city: item.city || null,
|
||||
district: item.district || null,
|
||||
id: item.id || null,
|
||||
latitude: item.location && item.location.lat || null,
|
||||
longitude: item.location && item.location.lng || null,
|
||||
province: item.province || null,
|
||||
title: item.title || null,
|
||||
type: item.type || null
|
||||
}));
|
||||
|
||||
resolve({
|
||||
success: true,
|
||||
data: suggestSimplify
|
||||
});
|
||||
} else {
|
||||
console.error('H5关键词提示失败:', data);
|
||||
reject({ success: false, message: data.message || '关键词提示失败' });
|
||||
}
|
||||
};
|
||||
|
||||
// 创建script标签进行JSONP请求
|
||||
const script = document.createElement('script');
|
||||
script.id = callbackName;
|
||||
script.src = `https://apis.map.qq.com/ws/place/v1/suggestion?keyword=${encodeURIComponent(keyword)}®ion=${encodeURIComponent(region)}&page_size=10&page_index=1&key=${QQMAP_KEY}&output=jsonp&callback=${callbackName}`;
|
||||
script.onerror = () => {
|
||||
delete window[callbackName];
|
||||
const scriptEl = document.getElementById(callbackName);
|
||||
if (scriptEl && scriptEl.parentNode) {
|
||||
scriptEl.parentNode.removeChild(scriptEl);
|
||||
}
|
||||
reject({ success: false, message: '关键词提示请求失败' });
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// 小程序环境:使用 QQMapWX SDK
|
||||
const qqmap = getQQMapInstance();
|
||||
if (!qqmap) {
|
||||
reject({ success: false, message: '腾讯地图SDK未初始化' });
|
||||
@@ -706,6 +1011,7 @@ function getSuggestion(keyword, region = '全国') {
|
||||
reject({ success: false, message: error.message || '关键词提示失败' });
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+31
-2
@@ -249,7 +249,9 @@ class OrderMonitor {
|
||||
export const orderMonitor = new OrderMonitor()
|
||||
|
||||
// 监听页面切换事件
|
||||
uni.onAppRoute((route) => {
|
||||
// #ifdef MP-WEIXIN || APP-PLUS
|
||||
if (typeof uni.onAppRoute === 'function') {
|
||||
uni.onAppRoute((route) => {
|
||||
const pagePath = route.path || ''
|
||||
const pageSegments = pagePath.split('/')
|
||||
const pageName = pageSegments[pageSegments.length - 1]
|
||||
@@ -258,7 +260,34 @@ uni.onAppRoute((route) => {
|
||||
orderMonitor.setActivePage(pageName || null)
|
||||
|
||||
console.log('页面切换:', pagePath, '当前活跃页面:', pageName)
|
||||
})
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
// H5环境下通过路由监听实现页面切换检测
|
||||
if (typeof window !== 'undefined') {
|
||||
const updateActivePageForH5 = () => {
|
||||
const pagePath = window.location.pathname || window.location.hash
|
||||
const pageSegments = pagePath.split('/')
|
||||
const pageName = pageSegments[pageSegments.length - 1].replace(/[?#].*$/, '')
|
||||
|
||||
if (pageName) {
|
||||
orderMonitor.setActivePage(pageName)
|
||||
console.log('页面切换(H5):', pagePath, '当前活跃页面:', pageName)
|
||||
}
|
||||
}
|
||||
|
||||
// 监听 popstate 事件(浏览器前进后退)
|
||||
window.addEventListener('popstate', updateActivePageForH5)
|
||||
|
||||
// 监听 hashchange 事件(hash 模式路由)
|
||||
window.addEventListener('hashchange', updateActivePageForH5)
|
||||
|
||||
// 初始化时执行一次
|
||||
updateActivePageForH5()
|
||||
}
|
||||
// #endif
|
||||
|
||||
// 页面加载时自动恢复监控上次的活跃订单(如果有)
|
||||
const initOrderMonitor = () => {
|
||||
|
||||
Reference in New Issue
Block a user