add:新增会员、优惠券
@@ -0,0 +1,39 @@
|
|||||||
|
import request from '../http'
|
||||||
|
|
||||||
|
// 按场地查询可用优惠券
|
||||||
|
export const getCouponsByPosition = (positionId) => {
|
||||||
|
return request({
|
||||||
|
url: `/device/coupon/app/position/${positionId}`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建优惠券支付订单
|
||||||
|
export const createCouponPayment = (couponId) => {
|
||||||
|
return request({
|
||||||
|
url: '/app/coupon/pay',
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
couponId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询用户优惠券
|
||||||
|
export const getUserCoupons = (status) => {
|
||||||
|
return request({
|
||||||
|
url: '/device/userPurchase/app/my',
|
||||||
|
method: 'get',
|
||||||
|
data: {
|
||||||
|
status
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const cancelCouponPayment = (orderNo) => {
|
||||||
|
return request({
|
||||||
|
url: `/device/userPurchase/app/cancel/${orderNo}`,
|
||||||
|
method: 'post'
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import request from '../http'
|
||||||
|
|
||||||
|
// 创建会员卡支付订单
|
||||||
|
export const createMemberCardPayment = (memberCardId) => {
|
||||||
|
return request({
|
||||||
|
url: '/app/member/pay',
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
memberCardId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 根据场地ID查询可用会员卡列表
|
||||||
|
export const getMemberCardsByPosition = (positionId) => {
|
||||||
|
return request({
|
||||||
|
url: `/device/memberCard/app/position/${positionId}`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//根据状态查询个人会员卡
|
||||||
|
export const getMemberCardsByStatus = () => {
|
||||||
|
return request({
|
||||||
|
url: `/device/userMemberCard/app/my`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消会员卡支付订单
|
||||||
|
export const cancelMemberCardPayment = (orderNo) => {
|
||||||
|
return request({
|
||||||
|
url: `/device/userMemberCard/app/cancel/${orderNo}`,
|
||||||
|
method: 'post'
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// export const URL = "https://my.gxfs123.com/api" //正式服务器-弃用
|
// export const URL = "https://my.gxfs123.com/api" //正式服务器-弃用
|
||||||
export const URL = "https://manager.fdzpower.com/api" //正式服务器
|
// export const URL = "https://manager.fdzpower.com/api" //正式服务器
|
||||||
// export const URL = "https://fansdev.gxfs123.com/api" //测试服务器
|
export const URL = "https://fansdev.gxfs123.com/api" //测试服务器
|
||||||
// export const URL = "http://192.168.5.30:8080" //本地调试
|
// export const URL = "http://192.168.5.52:8080" //本地调试
|
||||||
// export const URL = "http://127.0.0.1:8080" //本地调试
|
// export const URL = "http://127.0.0.1:8080" //本地调试
|
||||||
|
|
||||||
export const appid = "wx2165f0be356ae7a9" //微信小程序appid
|
export const appid = "wx2165f0be356ae7a9" //微信小程序appid
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ export default {
|
|||||||
refresh: 'Refresh',
|
refresh: 'Refresh',
|
||||||
pull: 'Pull to refresh',
|
pull: 'Pull to refresh',
|
||||||
release: 'Release to refresh',
|
release: 'Release to refresh',
|
||||||
noMore: 'No more'
|
noMore: 'No more',
|
||||||
|
functionDeveloping: 'Function under development'
|
||||||
},
|
},
|
||||||
|
|
||||||
nav: {
|
nav: {
|
||||||
@@ -145,7 +146,9 @@ export default {
|
|||||||
rentSuccess: 'Rent successful',
|
rentSuccess: 'Rent successful',
|
||||||
rentFailedRetry: 'Rent failed, please retry',
|
rentFailedRetry: 'Rent failed, please retry',
|
||||||
getPayParamsFailed: 'Failed to get payment parameters',
|
getPayParamsFailed: 'Failed to get payment parameters',
|
||||||
payScoreFailedCancelled: 'Pay score call failed, order cancelled'
|
payScoreFailedCancelled: 'Pay score call failed, order cancelled',
|
||||||
|
canUsePromotion: 'Tips: Coupons and membership cards available',
|
||||||
|
goToBuy: 'Buy Now'
|
||||||
},
|
},
|
||||||
|
|
||||||
order: {
|
order: {
|
||||||
@@ -272,6 +275,8 @@ export default {
|
|||||||
quickReturnDesc: '(View active orders)',
|
quickReturnDesc: '(View active orders)',
|
||||||
expressReturn: 'Express Return',
|
expressReturn: 'Express Return',
|
||||||
myOrders: 'Orders',
|
myOrders: 'Orders',
|
||||||
|
myCards: 'My Cards',
|
||||||
|
myCoupons: 'My Coupons',
|
||||||
customerService: 'Support',
|
customerService: 'Support',
|
||||||
feedback: 'Feedback',
|
feedback: 'Feedback',
|
||||||
businessLicense: 'License',
|
businessLicense: 'License',
|
||||||
@@ -682,6 +687,64 @@ export default {
|
|||||||
nicknameUpdated: 'Nickname updated successfully',
|
nicknameUpdated: 'Nickname updated successfully',
|
||||||
updateFailed: 'Update failed',
|
updateFailed: 'Update failed',
|
||||||
uploading: 'Uploading...'
|
uploading: 'Uploading...'
|
||||||
|
},
|
||||||
|
|
||||||
|
purchase: {
|
||||||
|
title: 'Offers',
|
||||||
|
memberCard: 'Member Card',
|
||||||
|
coupon: 'Coupon',
|
||||||
|
buyNow: 'Buy Now',
|
||||||
|
myCards: 'My Cards',
|
||||||
|
myCoupons: 'My Coupons',
|
||||||
|
cardDescription: 'Card Description',
|
||||||
|
couponDescription: 'Coupon Description',
|
||||||
|
pleaseSelect: 'Please select a product to purchase',
|
||||||
|
noCards: 'No cards available',
|
||||||
|
noCoupons: 'No coupons available',
|
||||||
|
cardUseInstruction: 'Usage Instructions',
|
||||||
|
cardValidityPeriod: 'Validity Period',
|
||||||
|
cardRefundPolicy: 'Refund Policy',
|
||||||
|
cardUseDescription: 'Membership card takes effect immediately after purchase and can be used at designated locations. Per-use cards are charged by number of uses, time-based cards are charged by duration. Please choose the appropriate card type according to your actual needs.',
|
||||||
|
cardValidityDescription: 'Membership card takes effect from the date of purchase, with validity periods varying by card type. Per-use cards expire when all uses are consumed within the validity period, and time-based cards expire when the accumulated usage duration reaches the limit within the validity period.',
|
||||||
|
cardRefundDescription: 'Refunds are not supported after purchasing membership cards. Unused portions can continue to be used within the validity period. In case of special circumstances requiring a refund, please contact customer service for processing.',
|
||||||
|
couponUseInstruction: 'Usage Instructions',
|
||||||
|
couponValidityPeriod: 'Validity Period',
|
||||||
|
couponUsageScope: 'Usage Scope',
|
||||||
|
couponUseDescription: 'Coupons take effect immediately after purchase and can be used at checkout. Only one coupon can be used per order, and coupons cannot be stacked with other promotional offers.',
|
||||||
|
couponValidityDescription: 'Coupons take effect from the date of purchase, please use within the validity period. After expiration, coupons will automatically become invalid and cannot be extended.',
|
||||||
|
couponUsageDescription: 'Coupons can be used at designated locations. Please check the coupon details for specific available locations. Some coupons have minimum spending requirements, please pay attention to the usage conditions.'
|
||||||
|
},
|
||||||
|
|
||||||
|
myCard: {
|
||||||
|
type: 'Type',
|
||||||
|
timesCard: 'Times Card',
|
||||||
|
durationCard: 'Duration Card',
|
||||||
|
remainingTimes: 'Remaining Times',
|
||||||
|
remainingDuration: 'Remaining Duration',
|
||||||
|
hours: 'Hours',
|
||||||
|
validPeriod: 'Valid Period',
|
||||||
|
active: 'Active',
|
||||||
|
expired: 'Expired',
|
||||||
|
used: 'Used Up',
|
||||||
|
position: 'Usage Location',
|
||||||
|
price: 'Purchase Price',
|
||||||
|
noCards: 'No cards',
|
||||||
|
buyNow: 'Buy Now',
|
||||||
|
getListFailed: 'Failed to get card list'
|
||||||
|
},
|
||||||
|
|
||||||
|
myCoupon: {
|
||||||
|
available: 'Available',
|
||||||
|
used: 'Used',
|
||||||
|
expired: 'Expired',
|
||||||
|
useNow: 'Use Now',
|
||||||
|
usedStatus: 'Used',
|
||||||
|
expiredStatus: 'Expired',
|
||||||
|
noAvailableCoupons: 'No available coupons',
|
||||||
|
noUsedCoupons: 'No used coupons',
|
||||||
|
noExpiredCoupons: 'No expired coupons',
|
||||||
|
buyNow: 'Buy Now',
|
||||||
|
getListFailed: 'Failed to get coupon list'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ export default {
|
|||||||
refresh: '刷新',
|
refresh: '刷新',
|
||||||
pull: '下拉刷新',
|
pull: '下拉刷新',
|
||||||
release: '释放刷新',
|
release: '释放刷新',
|
||||||
noMore: '没有更多了'
|
noMore: '没有更多了',
|
||||||
|
functionDeveloping: '功能开发中'
|
||||||
},
|
},
|
||||||
|
|
||||||
nav: {
|
nav: {
|
||||||
@@ -145,7 +146,9 @@ export default {
|
|||||||
rentSuccess: '租借成功',
|
rentSuccess: '租借成功',
|
||||||
rentFailedRetry: '租借失败,请重试',
|
rentFailedRetry: '租借失败,请重试',
|
||||||
getPayParamsFailed: '获取支付参数失败',
|
getPayParamsFailed: '获取支付参数失败',
|
||||||
payScoreFailedCancelled: '支付分调用失败,订单已取消'
|
payScoreFailedCancelled: '支付分调用失败,订单已取消',
|
||||||
|
canUsePromotion: '提示:可使用优惠券、会员卡',
|
||||||
|
goToBuy: '去购买'
|
||||||
},
|
},
|
||||||
|
|
||||||
order: {
|
order: {
|
||||||
@@ -272,6 +275,8 @@ export default {
|
|||||||
quickReturnDesc: '(直接查看使用中的订单)',
|
quickReturnDesc: '(直接查看使用中的订单)',
|
||||||
expressReturn: '快递归还记录',
|
expressReturn: '快递归还记录',
|
||||||
myOrders: '我的订单',
|
myOrders: '我的订单',
|
||||||
|
myCards: '我的会员卡',
|
||||||
|
myCoupons: '我的优惠券',
|
||||||
customerService: '客服中心',
|
customerService: '客服中心',
|
||||||
feedback: '投诉与建议',
|
feedback: '投诉与建议',
|
||||||
businessLicense: '营业资质',
|
businessLicense: '营业资质',
|
||||||
@@ -682,6 +687,64 @@ export default {
|
|||||||
nicknameUpdated: '昵称修改成功',
|
nicknameUpdated: '昵称修改成功',
|
||||||
updateFailed: '修改失败',
|
updateFailed: '修改失败',
|
||||||
uploading: '上传中...'
|
uploading: '上传中...'
|
||||||
|
},
|
||||||
|
|
||||||
|
purchase: {
|
||||||
|
title: '优惠专区',
|
||||||
|
memberCard: '会员卡',
|
||||||
|
coupon: '优惠券',
|
||||||
|
buyNow: '立即购买',
|
||||||
|
myCards: '我的会员卡',
|
||||||
|
myCoupons: '我的优惠券',
|
||||||
|
cardDescription: '会员卡说明',
|
||||||
|
couponDescription: '优惠券说明',
|
||||||
|
pleaseSelect: '请选择要购买的商品',
|
||||||
|
noCards: '暂无可用会员卡',
|
||||||
|
noCoupons: '暂无可用优惠券',
|
||||||
|
cardUseInstruction: '使用说明',
|
||||||
|
cardValidityPeriod: '有效期限',
|
||||||
|
cardRefundPolicy: '退款说明',
|
||||||
|
cardUseDescription: '会员卡购买后即时生效,可在指定场地使用。次卡按使用次数计费,时长卡按使用时长计费,请根据您的实际需求选择合适的卡种。',
|
||||||
|
cardValidityDescription: '会员卡自购买之日起生效,有效期根据卡种不同而有所区别。次卡在有效期内使用完毕即失效,时长卡在有效期内累计使用时长达到后失效。',
|
||||||
|
cardRefundDescription: '会员卡购买后不支持退款,未使用部分可以在有效期内继续使用。如遇特殊情况需要退款,请联系客服进行处理。',
|
||||||
|
couponUseInstruction: '使用说明',
|
||||||
|
couponValidityPeriod: '有效期限',
|
||||||
|
couponUsageScope: '使用范围',
|
||||||
|
couponUseDescription: '优惠券购买后即时生效,可在订单结算时使用。每张订单仅可使用一张优惠券,优惠券不可与其他优惠活动叠加使用。',
|
||||||
|
couponValidityDescription: '优惠券自购买之日起生效,请在有效期内使用。过期后优惠券将自动失效,不可延期使用。',
|
||||||
|
couponUsageDescription: '优惠券可在指定场地使用,具体可用场地请查看优惠券详情。部分优惠券有最低消费门槛要求,请注意查看使用条件。'
|
||||||
|
},
|
||||||
|
|
||||||
|
myCard: {
|
||||||
|
type: '类型',
|
||||||
|
timesCard: '次卡',
|
||||||
|
durationCard: '时长卡',
|
||||||
|
remainingTimes: '剩余次数',
|
||||||
|
remainingDuration: '剩余时长',
|
||||||
|
hours: '小时',
|
||||||
|
validPeriod: '有效期',
|
||||||
|
active: '使用中',
|
||||||
|
expired: '已过期',
|
||||||
|
used: '已用完',
|
||||||
|
position: '使用地点',
|
||||||
|
price: '购买价格',
|
||||||
|
noCards: '暂无会员卡',
|
||||||
|
buyNow: '立即购买',
|
||||||
|
getListFailed: '获取会员卡列表失败'
|
||||||
|
},
|
||||||
|
|
||||||
|
myCoupon: {
|
||||||
|
available: '可使用',
|
||||||
|
used: '已使用',
|
||||||
|
expired: '已过期',
|
||||||
|
useNow: '立即使用',
|
||||||
|
usedStatus: '已使用',
|
||||||
|
expiredStatus: '已过期',
|
||||||
|
noAvailableCoupons: '暂无可用优惠券',
|
||||||
|
noUsedCoupons: '暂无已使用优惠券',
|
||||||
|
noExpiredCoupons: '暂无已过期优惠券',
|
||||||
|
buyNow: '立即购买',
|
||||||
|
getListFailed: '获取优惠券列表失败'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -248,6 +248,30 @@
|
|||||||
"navigationBarBackgroundColor": "#ffffff",
|
"navigationBarBackgroundColor": "#ffffff",
|
||||||
"navigationBarTextStyle": "black"
|
"navigationBarTextStyle": "black"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/purchase/index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "优惠专区",
|
||||||
|
"navigationBarBackgroundColor": "#ffffff",
|
||||||
|
"navigationBarTextStyle": "black"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/my/card",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "",
|
||||||
|
"navigationBarBackgroundColor": "#ffffff",
|
||||||
|
"navigationBarTextStyle": "black"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/my/coupon",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "",
|
||||||
|
"navigationBarBackgroundColor": "#ffffff",
|
||||||
|
"navigationBarTextStyle": "black"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"globalStyle": {
|
"globalStyle": {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<view class="card device-info-card">
|
<view class="card device-info-card">
|
||||||
<view class="device-location">
|
<view class="device-location">
|
||||||
<view class="location-left">
|
<view class="location-left">
|
||||||
<image src="/static/images/location-map.svg" mode="aspectFit" class="location-icon"></image>
|
<image src="/static/device_location.png" mode="aspectFit" class="location-icon"></image>
|
||||||
<text class="location-name">{{ deviceLocation }}</text>
|
<text class="location-name">{{ deviceLocation }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="device-status" :class="deviceStatus.class">
|
<view class="device-status" :class="deviceStatus.class">
|
||||||
@@ -23,16 +23,16 @@
|
|||||||
<text class="card-title">{{ $t('device.pricingRules') }}</text>
|
<text class="card-title">{{ $t('device.pricingRules') }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="pricing-banner">
|
<view class="pricing-banner">
|
||||||
<view class="pricing-main">
|
<view class="pricing-main">
|
||||||
<text class="price-symbol">¥</text>
|
<text class="price-symbol">¥</text>
|
||||||
<text class="price">{{ deviceFeeConfig.maxHourPrice || '5.00' }}</text>
|
<text class="price">{{ deviceFeeConfig.maxHourPrice || '5.00' }}</text>
|
||||||
<text class="unit">/{{ getPriceUnit() }}</text>
|
<text class="unit">/{{ getPriceUnit() }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="cap-badge">
|
||||||
|
<text class="cap-text">{{ deviceInfo.depositAmount || '99' }}{{ $t('device.capLimit') }}</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="cap-badge">
|
|
||||||
<text class="cap-text">{{ deviceInfo.depositAmount || '99' }}{{ $t('device.capLimit') }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="pricing-info">
|
<view class="pricing-info">
|
||||||
<view class="info-icon">
|
<view class="info-icon">
|
||||||
@@ -69,6 +69,16 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<view class="promotion-tip" @click="goToPurchase">
|
||||||
|
<view class="tip-left">
|
||||||
|
<text class="tip-text">{{ $t('device.canUsePromotion') }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="tip-right">
|
||||||
|
<text class="buy-text">{{ $t('device.goToBuy') }}</text>
|
||||||
|
<image src="/static/gotoBuy.png" mode="aspectFit" class="arrow-icon"></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 底部操作区 -->
|
<!-- 底部操作区 -->
|
||||||
<view class="footer">
|
<view class="footer">
|
||||||
<button class="rent-button" :class="{ 'return-button': hasActiveOrder }"
|
<button class="rent-button" :class="{ 'return-button': hasActiveOrder }"
|
||||||
@@ -323,14 +333,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deviceInfo.value.feeConfig) {
|
if (deviceInfo.value.feeConfig) {
|
||||||
deviceFeeConfig.value = JSON.parse(deviceInfo.value.feeConfig)[0] || {}
|
deviceFeeConfig.value = JSON.parse(deviceInfo.value.feeConfig)[0] || {}
|
||||||
console.log('deviceFeeConfig', deviceFeeConfig.value);
|
console.log('deviceFeeConfig', deviceFeeConfig.value);
|
||||||
} else {
|
} else {
|
||||||
deviceFeeConfig.value = {
|
deviceFeeConfig.value = {
|
||||||
maxHourPrice: '5.00',
|
maxHourPrice: '5.00',
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -352,6 +362,14 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 跳转到优惠专区
|
||||||
|
const goToPurchase = () => {
|
||||||
|
const positionId = positionInfo.value?.positionId || positionInfo.value?.id
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/purchase/index?positionId=${positionId}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 检查订单状态
|
// 检查订单状态
|
||||||
const checkOrderStatus = async () => {
|
const checkOrderStatus = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -405,7 +423,7 @@
|
|||||||
// 按分钟计费
|
// 按分钟计费
|
||||||
if (deviceInfo.value && deviceInfo.value.feeType === 'minute') {
|
if (deviceInfo.value && deviceInfo.value.feeType === 'minute') {
|
||||||
return '分钟'
|
return '分钟'
|
||||||
}else if(deviceInfo.value && deviceFeeConfig.value.hourPrice == '0.5'){
|
} else if (deviceInfo.value && deviceFeeConfig.value.hourPrice == '0.5') {
|
||||||
return '30分钟'
|
return '30分钟'
|
||||||
}
|
}
|
||||||
// 按小时计费(默认)
|
// 按小时计费(默认)
|
||||||
@@ -517,18 +535,18 @@
|
|||||||
throw new Error(rentResult.msg || $t('device.rentFailed'))
|
throw new Error(rentResult.msg || $t('device.rentFailed'))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取后端返回的订单信息
|
// 获取后端返回的订单信息
|
||||||
const order = rentResult.data
|
const order = rentResult.data
|
||||||
console.log('订单信息', order);
|
console.log('订单信息', order);
|
||||||
|
|
||||||
// 标记:本次是从设备详情页发起的下单流程,离开页面时不设置启动路径
|
// 标记:本次是从设备详情页发起的下单流程,离开页面时不设置启动路径
|
||||||
try {
|
try {
|
||||||
uni.setStorageSync('skipSetLaunchPathOnce', true)
|
uni.setStorageSync('skipSetLaunchPathOnce', true)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('设置 skipSetLaunchPathOnce 失败:', e)
|
console.warn('设置 skipSetLaunchPathOnce 失败:', e)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payWay == 'wx-pay') {
|
if (payWay == 'wx-pay') {
|
||||||
// 当支付方式为押金支付时
|
// 当支付方式为押金支付时
|
||||||
uni.hideLoading()
|
uni.hideLoading()
|
||||||
const res = await getOrderByOrderNo(order.orderNo);
|
const res = await getOrderByOrderNo(order.orderNo);
|
||||||
@@ -688,15 +706,15 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.location-icon {
|
.location-icon {
|
||||||
width: 40rpx;
|
width: 32rpx;
|
||||||
height: 40rpx;
|
height: 32rpx;
|
||||||
margin-right: 12rpx;
|
margin-right: 12rpx;
|
||||||
background-color: #10d673;
|
// background-color: #10d673;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.location-name {
|
.location-name {
|
||||||
font-size: 32rpx;
|
font-size: 28rpx;
|
||||||
color: #333;
|
color: #333;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
@@ -705,7 +723,7 @@
|
|||||||
.device-status {
|
.device-status {
|
||||||
padding: 8rpx 24rpx;
|
padding: 8rpx 24rpx;
|
||||||
border-radius: 30rpx;
|
border-radius: 30rpx;
|
||||||
font-size: 24rpx;
|
font-size: 22rpx;
|
||||||
|
|
||||||
&.available {
|
&.available {
|
||||||
background-color: #d4f4dd;
|
background-color: #d4f4dd;
|
||||||
@@ -734,7 +752,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.id-label {
|
.id-label {
|
||||||
font-size: 28rpx;
|
font-size: 26rpx;
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -748,7 +766,7 @@
|
|||||||
// 计费规则卡片
|
// 计费规则卡片
|
||||||
.pricing-card {
|
.pricing-card {
|
||||||
.pricing-banner {
|
.pricing-banner {
|
||||||
background: linear-gradient(135deg, #e8f5e9, #c8e6c9);
|
background: #E6F7EC;
|
||||||
border-radius: 20rpx;
|
border-radius: 20rpx;
|
||||||
padding: 40rpx 30rpx;
|
padding: 40rpx 30rpx;
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
@@ -762,21 +780,21 @@
|
|||||||
margin-bottom: 16rpx;
|
margin-bottom: 16rpx;
|
||||||
|
|
||||||
.price-symbol {
|
.price-symbol {
|
||||||
font-size: 48rpx;
|
font-size: 36rpx;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #07c160;
|
color: #07c160;
|
||||||
margin-right: 4rpx;
|
margin-right: 4rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.price {
|
.price {
|
||||||
font-size: 80rpx;
|
font-size: 64rpx;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #07c160;
|
color: #07c160;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.unit {
|
.unit {
|
||||||
font-size: 32rpx;
|
font-size: 28rpx;
|
||||||
color: #07c160;
|
color: #07c160;
|
||||||
margin-left: 8rpx;
|
margin-left: 8rpx;
|
||||||
}
|
}
|
||||||
@@ -784,11 +802,11 @@
|
|||||||
|
|
||||||
.cap-badge {
|
.cap-badge {
|
||||||
background-color: #07c160;
|
background-color: #07c160;
|
||||||
padding: 10rpx 32rpx;
|
padding: 10rpx 28rpx;
|
||||||
border-radius: 30rpx;
|
border-radius: 30rpx;
|
||||||
|
line-height: 1;
|
||||||
.cap-text {
|
.cap-text {
|
||||||
font-size: 26rpx;
|
font-size: 24rpx;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
@@ -860,6 +878,51 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 促销提示框
|
||||||
|
.promotion-tip {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 20rpx 30rpx;
|
||||||
|
background: rgba(255, 244, 227, 1);
|
||||||
|
border-radius: 22rpx;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
opacity: 0.8;
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.tip-text {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #A16300;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12rpx;
|
||||||
|
|
||||||
|
.buy-text {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #A16300;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow-icon {
|
||||||
|
width: 20rpx;
|
||||||
|
height: 20rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 底部操作区
|
// 底部操作区
|
||||||
.footer {
|
.footer {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -912,10 +975,10 @@
|
|||||||
margin-right: 8rpx;
|
margin-right: 8rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.credit-text {
|
.credit-text {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,242 @@
|
|||||||
|
<template>
|
||||||
|
<view class="my-card-page">
|
||||||
|
<!-- 会员卡列表 -->
|
||||||
|
<view class="card-list" v-if="cardList.length > 0">
|
||||||
|
<view v-for="card in cardList" :key="card.id" class="card-item" @click="viewCardDetail(card)">
|
||||||
|
<view class="card-header">
|
||||||
|
<text class="card-name">{{ card.name }}</text>
|
||||||
|
<view class="card-status" :class="getStatusClass(card.status)">
|
||||||
|
<text class="status-text">{{ getStatusText(card.status) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="card-info">
|
||||||
|
<text class="info-label">{{ $t('myCard.type') }}:</text>
|
||||||
|
<text class="info-value">{{ card.cardType === 'COUNT' ? $t('myCard.timesCard') :
|
||||||
|
$t('myCard.durationCard') }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="card-info" v-if="card.type === 'times'">
|
||||||
|
<text class="info-label">{{ $t('myCard.remainingTimes') }}:</text>
|
||||||
|
<text class="info-value highlight">{{ card.remainingTimes }}/{{ card.totalTimes }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="card-info" v-if="card.type === 'duration'">
|
||||||
|
<text class="info-label">{{ $t('myCard.remainingDuration') }}:</text>
|
||||||
|
<text class="info-value highlight">{{ card.remainingDuration }}{{ $t('myCard.hours') }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="card-info">
|
||||||
|
<text class="info-label">{{ $t('myCard.validPeriod') }}:</text>
|
||||||
|
<text class="info-value">{{ card.startDate }} - {{ card.endDate }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<view class="empty-state" v-else>
|
||||||
|
<image class="empty-icon" src="/static/empty-card.png" mode="aspectFit"></image>
|
||||||
|
<text class="empty-text">{{ $t('myCard.noCards') }}</text>
|
||||||
|
<view class="buy-btn" @click="goToBuy">
|
||||||
|
<text class="buy-text">{{ $t('myCard.buyNow') }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { useI18n } from '@/utils/i18n.js'
|
||||||
|
import {
|
||||||
|
getMemberCardsByStatus
|
||||||
|
} from '@/config/api/member.js'
|
||||||
|
const { t: $t } = useI18n()
|
||||||
|
|
||||||
|
// 会员卡列表
|
||||||
|
const cardList = ref([])
|
||||||
|
|
||||||
|
// 获取会员卡列表
|
||||||
|
const getCardList = async () => {
|
||||||
|
try {
|
||||||
|
const response = await getMemberCardsByStatus()
|
||||||
|
// 处理API返回的数据,转换为模板需要的格式
|
||||||
|
if (response.code === 200 && response.data) {
|
||||||
|
cardList.value = response.data.map(item => ({
|
||||||
|
id: item.id,
|
||||||
|
name: item.cardName,
|
||||||
|
cardType: item.cardType, // TIME -> time
|
||||||
|
totalTimes: item.totalCount,
|
||||||
|
remainingTimes: item.remainingCount,
|
||||||
|
remainingDuration: item.singleLimitMinutes,
|
||||||
|
status: item.status,
|
||||||
|
startDate: item.startTime,
|
||||||
|
endDate: item.endTime,
|
||||||
|
positionName: item.positionName,
|
||||||
|
purchasePrice: item.purchasePrice,
|
||||||
|
// 添加可能的其他字段
|
||||||
|
...item
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
cardList.value = []
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取会员卡列表失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: $t('myCard.getListFailed'),
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取状态类名
|
||||||
|
const getStatusClass = (status) => {
|
||||||
|
const statusMap = {
|
||||||
|
'unused': 'active',
|
||||||
|
'expired': 'expired',
|
||||||
|
'used': 'used',
|
||||||
|
'active': 'active' // 兼容原始状态
|
||||||
|
}
|
||||||
|
return statusMap[status] || 'active' // 默认为active
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取状态文本
|
||||||
|
const getStatusText = (status) => {
|
||||||
|
const statusMap = {
|
||||||
|
'unused': $t('myCard.active'), // unused表示未使用,即活跃状态
|
||||||
|
'expired': $t('myCard.expired'),
|
||||||
|
'used': $t('myCard.used'),
|
||||||
|
'active': $t('myCard.active') // 兼容原始状态
|
||||||
|
}
|
||||||
|
return statusMap[status] || $t('myCard.active') // 默认为active
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看卡详情
|
||||||
|
const viewCardDetail = (card) => {
|
||||||
|
// TODO: 跳转到卡详情页面
|
||||||
|
// uni.showToast({
|
||||||
|
// title: $t('common.functionDeveloping'),
|
||||||
|
// icon: 'none'
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去购买
|
||||||
|
const goToBuy = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/purchase/index'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
uni.setNavigationBarTitle({
|
||||||
|
title: $t('user.myCards')
|
||||||
|
})
|
||||||
|
getCardList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.my-card-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
padding: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-item {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 30rpx;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
padding-bottom: 20rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-name {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-status {
|
||||||
|
padding: 8rpx 16rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
font-size: 22rpx;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #e8f5e8;
|
||||||
|
color: #4caf50;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.expired {
|
||||||
|
background-color: #ffeaea;
|
||||||
|
color: #f44336;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.used {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 120rpx 0;
|
||||||
|
|
||||||
|
.empty-icon {
|
||||||
|
width: 200rpx;
|
||||||
|
height: 200rpx;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buy-btn {
|
||||||
|
padding: 20rpx 60rpx;
|
||||||
|
background-color: #B8741A;
|
||||||
|
border-radius: 48rpx;
|
||||||
|
|
||||||
|
.buy-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #ffffff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,420 @@
|
|||||||
|
<template>
|
||||||
|
<view class="my-coupon-page">
|
||||||
|
<!-- Tab 切换 -->
|
||||||
|
<view class="tab-container">
|
||||||
|
<view class="tab-item" :class="{ active: currentTab === 'available' }" @click="switchTab('available')">
|
||||||
|
<text class="tab-text">{{ $t('myCoupon.available') }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="tab-item" :class="{ active: currentTab === 'used' }" @click="switchTab('used')">
|
||||||
|
<text class="tab-text">{{ $t('myCoupon.used') }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="tab-item" :class="{ active: currentTab === 'expired' }" @click="switchTab('expired')">
|
||||||
|
<text class="tab-text">{{ $t('myCoupon.expired') }}</text>
|
||||||
|
</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-left">
|
||||||
|
<text class="coupon-value">{{ coupon.type === 'discount' ? coupon.discount + '折' : '¥' + coupon.value }}</text>
|
||||||
|
<text class="coupon-condition">{{ coupon.condition }}</text>
|
||||||
|
<text class="coupon-validity-left">{{ coupon.validity }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="coupon-divider"></view>
|
||||||
|
<view class="coupon-right">
|
||||||
|
<text class="coupon-name">{{ coupon.name }}</text>
|
||||||
|
<view class="use-btn" v-if="coupon.status === 'available'" @click="useCoupon(coupon)">
|
||||||
|
<text class="use-text">{{ $t('myCoupon.useNow') }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="coupon-status" v-else>{{ getStatusText(coupon.status) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<view class="empty-state" v-else>
|
||||||
|
<image class="empty-icon" src="/static/empty-coupon.png" mode="aspectFit"></image>
|
||||||
|
<text class="empty-text">{{ getEmptyText() }}</text>
|
||||||
|
<view class="buy-btn" @click="goToBuy" v-if="currentTab === 'available'">
|
||||||
|
<text class="buy-text">{{ $t('myCoupon.buyNow') }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, onMounted } from 'vue'
|
||||||
|
import { useI18n } from '@/utils/i18n.js'
|
||||||
|
import { getUserCoupons } from '@/config/api/coupon.js'
|
||||||
|
|
||||||
|
const { t: $t } = useI18n()
|
||||||
|
|
||||||
|
// 当前选中的 Tab
|
||||||
|
const currentTab = ref('available')
|
||||||
|
|
||||||
|
// Tab 与 API 状态的映射
|
||||||
|
const tabToStatusMap = {
|
||||||
|
available: 'unused',
|
||||||
|
used: 'used',
|
||||||
|
expired: 'expired'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 优惠券列表
|
||||||
|
const couponList = ref([])
|
||||||
|
|
||||||
|
// 过滤后的优惠券
|
||||||
|
const filteredCoupons = computed(() => {
|
||||||
|
return couponList.value.filter(coupon => coupon.status === currentTab.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取优惠券列表
|
||||||
|
const getCouponList = async () => {
|
||||||
|
try {
|
||||||
|
const apiStatus = tabToStatusMap[currentTab.value]
|
||||||
|
const res = await getUserCoupons(apiStatus)
|
||||||
|
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
// 将后端数据转换为前端需要的格式
|
||||||
|
couponList.value = (res.data || []).map(item => {
|
||||||
|
// 判断优惠券类型:discount_coupon 折扣券,deduction_coupon 抵扣券
|
||||||
|
const isCashCoupon = item.couponType === 'deduction_coupon'
|
||||||
|
|
||||||
|
// 格式化使用条件
|
||||||
|
let condition = '无门槛'
|
||||||
|
if (item.usableCondition && item.usableCondition > 0) {
|
||||||
|
condition = `满${item.usableCondition}可用`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化有效期
|
||||||
|
let validity = ''
|
||||||
|
if (currentTab.value === 'used') {
|
||||||
|
// 已使用显示使用时间(这里暂用开始时间,如后端有使用时间字段可替换)
|
||||||
|
validity = item.couponStartTime ? `使用时间 ${item.couponStartTime}` : ''
|
||||||
|
} else if (item.couponEndTime) {
|
||||||
|
validity = `有效期至 ${item.couponEndTime}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
name: item.couponName || '优惠券',
|
||||||
|
type: isCashCoupon ? 'cash' : 'discount',
|
||||||
|
value: item.deductAmount ? parseFloat(item.deductAmount) : 0,
|
||||||
|
discount: item.discountRate ? parseFloat(item.discountRate) * 10 : null,
|
||||||
|
condition: condition,
|
||||||
|
validity: validity,
|
||||||
|
status: currentTab.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
couponList.value = []
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取优惠券列表失败:', error)
|
||||||
|
couponList.value = []
|
||||||
|
uni.showToast({
|
||||||
|
title: $t('myCoupon.getListFailed'),
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换 Tab
|
||||||
|
const switchTab = (tab) => {
|
||||||
|
currentTab.value = tab
|
||||||
|
getCouponList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取优惠券样式类名
|
||||||
|
const getCouponClass = (status) => {
|
||||||
|
return status === 'available' ? '' : 'disabled'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取状态文本
|
||||||
|
const getStatusText = (status) => {
|
||||||
|
const statusMap = {
|
||||||
|
'used': $t('myCoupon.usedStatus'),
|
||||||
|
'expired': $t('myCoupon.expiredStatus')
|
||||||
|
}
|
||||||
|
return statusMap[status] || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取空状态文本
|
||||||
|
const getEmptyText = () => {
|
||||||
|
const textMap = {
|
||||||
|
'available': $t('myCoupon.noAvailableCoupons'),
|
||||||
|
'used': $t('myCoupon.noUsedCoupons'),
|
||||||
|
'expired': $t('myCoupon.noExpiredCoupons')
|
||||||
|
}
|
||||||
|
return textMap[currentTab.value] || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用优惠券
|
||||||
|
const useCoupon = (coupon) => {
|
||||||
|
// TODO: 使用优惠券逻辑
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/index/index'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去购买
|
||||||
|
const goToBuy = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/purchase/index?tab=coupon'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
uni.setNavigationBarTitle({
|
||||||
|
title: $t('user.myCoupons')
|
||||||
|
})
|
||||||
|
getCouponList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.my-coupon-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab 切换 */
|
||||||
|
.tab-container {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
display: flex;
|
||||||
|
background-color: #ffffff;
|
||||||
|
z-index: 999;
|
||||||
|
padding: 20rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
padding: 20rpx 0;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.tab-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
.tab-text {
|
||||||
|
color: #333;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 60rpx;
|
||||||
|
height: 6rpx;
|
||||||
|
background-color: #FFA928;
|
||||||
|
border-radius: 3rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-list {
|
||||||
|
padding: 20rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-item-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-item {
|
||||||
|
background: linear-gradient(135deg, #FFF4E6 0%, #FFE8CC 100%);
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 40rpx 30rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
position: relative;
|
||||||
|
overflow: visible;
|
||||||
|
min-height: 180rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 2rpx solid transparent;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
border-color: #B8741A;
|
||||||
|
box-shadow: 0 4rpx 20rpx rgba(184, 116, 26, 0.2);
|
||||||
|
|
||||||
|
.coupon-circle-left,
|
||||||
|
.coupon-circle-right {
|
||||||
|
background-color: #FFF4E6;
|
||||||
|
border: 2rpx solid #B8741A;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
background: linear-gradient(135deg, #f5f5f5 0%, #e8e8e8 100%);
|
||||||
|
opacity: 0.6;
|
||||||
|
|
||||||
|
.coupon-value,
|
||||||
|
.coupon-condition,
|
||||||
|
.coupon-name {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-circle-left,
|
||||||
|
.coupon-circle-right {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 左侧圆形缺口 */
|
||||||
|
.coupon-circle-left {
|
||||||
|
position: absolute;
|
||||||
|
left: -16rpx;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 32rpx;
|
||||||
|
height: 32rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 右侧圆形缺口 */
|
||||||
|
.coupon-circle-right {
|
||||||
|
position: absolute;
|
||||||
|
right: -16rpx;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 32rpx;
|
||||||
|
height: 32rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-left {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-value {
|
||||||
|
font-size: 48rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #B8741A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-condition {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-validity-left {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-divider {
|
||||||
|
width: 2rpx;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
left: 65%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
// min-height: 160rpx;
|
||||||
|
align-self: stretch;
|
||||||
|
background: repeating-linear-gradient(to bottom,
|
||||||
|
#B8741A 0rpx,
|
||||||
|
#B8741A 8rpx,
|
||||||
|
transparent 8rpx,
|
||||||
|
transparent 16rpx);
|
||||||
|
margin: 0 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-right {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8rpx;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-name {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-validity {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.use-btn {
|
||||||
|
margin-top: 10rpx;
|
||||||
|
padding: 12rpx 28rpx;
|
||||||
|
background-color: #B8741A;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
|
||||||
|
.use-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #ffffff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-status {
|
||||||
|
margin-top: 10rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 120rpx 0;
|
||||||
|
|
||||||
|
.empty-icon {
|
||||||
|
width: 200rpx;
|
||||||
|
height: 200rpx;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buy-btn {
|
||||||
|
padding: 20rpx 60rpx;
|
||||||
|
background-color: #B8741A;
|
||||||
|
border-radius: 48rpx;
|
||||||
|
|
||||||
|
.buy-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #ffffff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -61,6 +61,20 @@
|
|||||||
</view>
|
</view>
|
||||||
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
|
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="list-item" @click="navigateTo('/pages/my/card')">
|
||||||
|
<view class="left">
|
||||||
|
<image class="icon" src="/static/my_member.png" mode="aspectFit"></image>
|
||||||
|
<text class="title">{{ $t('user.myCards') }}</text>
|
||||||
|
</view>
|
||||||
|
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
|
||||||
|
</view>
|
||||||
|
<view class="list-item" @click="navigateTo('/pages/my/coupon')">
|
||||||
|
<view class="left">
|
||||||
|
<image class="icon" src="/static/my_coupon.png" mode="aspectFit"></image>
|
||||||
|
<text class="title">{{ $t('user.myCoupons') }}</text>
|
||||||
|
</view>
|
||||||
|
<uv-icon name="arrow-right" size="16" color="#c8c8c8"></uv-icon>
|
||||||
|
</view>
|
||||||
<view class="list-item" @click="navigateTo('/pages/help/index')">
|
<view class="list-item" @click="navigateTo('/pages/help/index')">
|
||||||
<view class="left">
|
<view class="left">
|
||||||
<image class="icon" src="/static/customer-service.png" mode="aspectFit"></image>
|
<image class="icon" src="/static/customer-service.png" mode="aspectFit"></image>
|
||||||
@@ -186,8 +200,7 @@ import {
|
|||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
appPlatform: 'wechat', // 微信平台
|
appPlatform: 'wechat', // 微信平台
|
||||||
appType: 'user' ,// 用户端
|
appType: 'user' // 用户端
|
||||||
pictureLocation:'userProfile_banner'
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,877 @@
|
|||||||
|
<template>
|
||||||
|
<view class="purchase-page">
|
||||||
|
<!-- Tab 切换 -->
|
||||||
|
<view class="tab-container">
|
||||||
|
<view class="tab-item" :class="{ active: currentTab === 'card' }" @click="switchTab('card')">
|
||||||
|
<text class="tab-text">{{ $t('purchase.memberCard') }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="tab-item" :class="{ active: currentTab === 'coupon' }" @click="switchTab('coupon')">
|
||||||
|
<text class="tab-text">{{ $t('purchase.coupon') }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<scroll-view class="content-area" scroll-y>
|
||||||
|
|
||||||
|
<!-- 会员卡列表 -->
|
||||||
|
<view v-if="currentTab === 'card'" class="product-list">
|
||||||
|
<view v-for="item in memberCards" :key="item.id" class="product-card"
|
||||||
|
:class="{ selected: selectedProduct?.id === item.id }" @click="selectProduct(item)">
|
||||||
|
<view class="card-content">
|
||||||
|
<view class="card-left">
|
||||||
|
<text class="card-name">{{ item.name }}</text>
|
||||||
|
<text class="card-desc">{{ item.description }}</text>
|
||||||
|
</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>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 空数据提示 -->
|
||||||
|
<uv-empty v-if="memberCards.length === 0" mode="car" :text="$t('purchase.noCards')"></uv-empty>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 优惠券列表 -->
|
||||||
|
<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-circle-left"></view>
|
||||||
|
<!-- 右侧圆形缺口 -->
|
||||||
|
<view class="coupon-circle-right"></view>
|
||||||
|
|
||||||
|
<view class="coupon-left">
|
||||||
|
<text class="coupon-value">{{ item.type === 'discount' ? item.discount + '折' : '¥' + item.value
|
||||||
|
}}</text>
|
||||||
|
<text class="coupon-condition">{{ item.condition }}</text>
|
||||||
|
<text class="coupon-validity">{{ item.validity }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="coupon-divider"></view>
|
||||||
|
<view class="coupon-right">
|
||||||
|
<view class="coupon-price">
|
||||||
|
<text class="price-label">¥</text>
|
||||||
|
<text class="price-value">{{ item.price }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 空数据提示 -->
|
||||||
|
<uv-empty v-if="coupons.length === 0" mode="coupon" :text="$t('purchase.noCoupons')"></uv-empty>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 说明部分 -->
|
||||||
|
<view class="description-section">
|
||||||
|
<text class="description-title">{{ currentTab === 'card' ? $t('purchase.cardDescription') :
|
||||||
|
$t('purchase.couponDescription') }}</text>
|
||||||
|
<view class="description-content">
|
||||||
|
<view v-for="(desc, index) in descriptions" :key="index" class="description-item">
|
||||||
|
<text class="description-subtitle">{{ desc.title }}</text>
|
||||||
|
<text class="description-text">{{ desc.content }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
<!-- 底部操作栏 -->
|
||||||
|
<view class="bottom-bar">
|
||||||
|
<view class="my-products-wrapper" @click="goToMyProducts">
|
||||||
|
<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') :
|
||||||
|
$t('purchase.myCoupons') }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="" style="width: 40rpx;height: 40rpx;">
|
||||||
|
<image src="/static/gotoBuy.png" mode="aspectFill" style="width: 40rpx;height: 40rpx;"></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="action-wrapper">
|
||||||
|
<view class="price-info">
|
||||||
|
<text class="current-price">¥{{ selectedProduct?.price || '0.00' }}</text>
|
||||||
|
<text class="original-price" v-if="selectedProduct?.originalPrice">¥{{
|
||||||
|
selectedProduct?.originalPrice
|
||||||
|
}}</text>
|
||||||
|
</view>
|
||||||
|
<view class="buy-button" :class="{ disabled: !selectedProduct }" @click="handleBuy">
|
||||||
|
<text class="buy-button-text">{{ $t('purchase.buyNow') }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
ref,
|
||||||
|
computed,
|
||||||
|
onMounted
|
||||||
|
} from 'vue'
|
||||||
|
import {
|
||||||
|
onLoad
|
||||||
|
} from '@dcloudio/uni-app'
|
||||||
|
import {
|
||||||
|
useI18n
|
||||||
|
} from '@/utils/i18n.js'
|
||||||
|
import {
|
||||||
|
getCouponsByPosition,
|
||||||
|
createCouponPayment,
|
||||||
|
cancelCouponPayment
|
||||||
|
} from '@/config/api/coupon.js'
|
||||||
|
// import {
|
||||||
|
// cancelMemberCardPayment
|
||||||
|
// } from '@/config/api/member.js'
|
||||||
|
import {
|
||||||
|
createMemberCardPayment,
|
||||||
|
getMemberCardsByPosition,
|
||||||
|
cancelMemberCardPayment
|
||||||
|
} from '@/config/api/member.js'
|
||||||
|
|
||||||
|
const {
|
||||||
|
t: $t
|
||||||
|
} = useI18n()
|
||||||
|
|
||||||
|
// 当前选中的 Tab
|
||||||
|
const currentTab = ref('card') // 'card' 或 'coupon'
|
||||||
|
|
||||||
|
// 选中的商品
|
||||||
|
const selectedProduct = ref(null)
|
||||||
|
|
||||||
|
// 当前场地ID(从路由参数获取)
|
||||||
|
const positionId = ref(null)
|
||||||
|
|
||||||
|
// 生命周期 onLoad 钩子 - 获取路由参数
|
||||||
|
onLoad((options) => {
|
||||||
|
if (options.positionId) {
|
||||||
|
positionId.value = options.positionId
|
||||||
|
console.log('获取到场地ID:', positionId.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 会员卡列表(从后端加载)
|
||||||
|
const memberCards = ref([])
|
||||||
|
|
||||||
|
// 优惠券列表(从后端加载)
|
||||||
|
const coupons = ref([])
|
||||||
|
|
||||||
|
// 说明内容
|
||||||
|
const descriptions = computed(() => {
|
||||||
|
if (currentTab.value === 'card') {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: $t('purchase.cardUseInstruction'),
|
||||||
|
content: $t('purchase.cardUseDescription')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: $t('purchase.cardValidityPeriod'),
|
||||||
|
content: $t('purchase.cardValidityDescription')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: $t('purchase.cardRefundPolicy'),
|
||||||
|
content: $t('purchase.cardRefundDescription')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: $t('purchase.couponUseInstruction'),
|
||||||
|
content: $t('purchase.couponUseDescription')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: $t('purchase.couponValidityPeriod'),
|
||||||
|
content: $t('purchase.couponValidityDescription')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: $t('purchase.couponUsageScope'),
|
||||||
|
content: $t('purchase.couponUsageDescription')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 加载会员卡列表
|
||||||
|
const loadMemberCards = async () => {
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '加载中...'
|
||||||
|
})
|
||||||
|
|
||||||
|
const res = await getMemberCardsByPosition(positionId.value)
|
||||||
|
uni.hideLoading()
|
||||||
|
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
// 转换为页面需要的格式
|
||||||
|
memberCards.value = res.data.map(item => ({
|
||||||
|
id: item.memberCardId,
|
||||||
|
name: item.cardName,
|
||||||
|
type: item.cardType,
|
||||||
|
description: item.positionName || '无描述',
|
||||||
|
price: item.purchasePrice ? item.purchasePrice.toString() : '0.00',
|
||||||
|
originalPrice: null,
|
||||||
|
availablePositions: item.positionName,
|
||||||
|
cycleDays: item.cycleDays,
|
||||||
|
dailyLimitCount: item.dailyLimitCount,
|
||||||
|
singleLimitMinutes: item.singleLimitMinutes,
|
||||||
|
validDays: item.validDays,
|
||||||
|
totalCount: item.totalCount,
|
||||||
|
purchaseQuantity: item.purchaseQuantity
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading()
|
||||||
|
console.error('加载会员卡失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '加载会员卡失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 加载优惠券列表
|
||||||
|
const loadCoupons = async () => {
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '加载中...'
|
||||||
|
})
|
||||||
|
|
||||||
|
const res = await getCouponsByPosition(positionId.value)
|
||||||
|
uni.hideLoading()
|
||||||
|
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
// 转换为页面需要的格式
|
||||||
|
coupons.value = res.data.map(item => ({
|
||||||
|
id: item.couponId,
|
||||||
|
couponId: item.couponId,
|
||||||
|
name: item.couponName,
|
||||||
|
type: item.couponType === '1' ? 'discount' : 'cash',
|
||||||
|
discount: item.discountRate ? (item.discountRate * 10).toFixed(0) : null,
|
||||||
|
value: item.deductAmount ? item.deductAmount.toString() : null,
|
||||||
|
condition: item.usableCondition > 0 ? `满${item.usableCondition}元可用` : '无门槛',
|
||||||
|
validity: item.validDays > 0 ? `从购买日起 有效期${item.validDays}天` : '永久有效',
|
||||||
|
price: item.purchasePrice ? item.purchasePrice.toString() : '0.00',
|
||||||
|
originalPrice: null,
|
||||||
|
usablePositions: item.usablePositionNameMap,
|
||||||
|
remark: item.remark
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading()
|
||||||
|
console.error('加载优惠券失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '加载优惠券失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(async () => {
|
||||||
|
// 加载会员卡列表
|
||||||
|
if (positionId.value) {
|
||||||
|
await loadMemberCards()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载优惠券列表
|
||||||
|
if (positionId.value) {
|
||||||
|
await loadCoupons()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认选中第一个会员卡(如果有的话)
|
||||||
|
if (memberCards.value.length > 0) {
|
||||||
|
selectedProduct.value = memberCards.value[0]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const switchTab = (tab) => {
|
||||||
|
currentTab.value = tab
|
||||||
|
selectedProduct.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择商品
|
||||||
|
const selectProduct = (product) => {
|
||||||
|
selectedProduct.value = product
|
||||||
|
}
|
||||||
|
|
||||||
|
const orderNo = ref('')
|
||||||
|
|
||||||
|
// 处理购买
|
||||||
|
const handleBuy = async () => {
|
||||||
|
if (!selectedProduct.value) {
|
||||||
|
uni.showToast({
|
||||||
|
title: $t('purchase.pleaseSelect'),
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 会员卡购买
|
||||||
|
if (currentTab.value === 'card') {
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '正在创建订单...'
|
||||||
|
})
|
||||||
|
|
||||||
|
const res = await createMemberCardPayment(selectedProduct.value.id)
|
||||||
|
uni.hideLoading()
|
||||||
|
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
|
||||||
|
orderNo.value = res.data.OutOrderNo;
|
||||||
|
// 调起微信支付
|
||||||
|
uni.requestPayment({
|
||||||
|
timeStamp: res.data.timeStamp,
|
||||||
|
nonceStr: res.data.nonceStr,
|
||||||
|
package: res.data.packageValue || res.data.package,
|
||||||
|
signType: res.data.signType || 'MD5',
|
||||||
|
paySign: res.data.paySign,
|
||||||
|
success: (payRes) => {
|
||||||
|
uni.showToast({
|
||||||
|
title: '支付成功',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
// 支付成功后,跳转到我的会员卡页面
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/my/card'
|
||||||
|
})
|
||||||
|
}, 1500)
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('支付失败:', err)
|
||||||
|
console.log('支付失败详细信息:', err.errMsg.includes('cancel'));
|
||||||
|
if (err.errMsg && err.errMsg.includes('cancel')) {
|
||||||
|
if (orderNo.value) {
|
||||||
|
cancelMemberCardPayment(orderNo.value)
|
||||||
|
.then(cancelRes => {
|
||||||
|
console.log('取消支付订单成功:', cancelRes);
|
||||||
|
})
|
||||||
|
.catch(cancelErr => {
|
||||||
|
console.error('取消支付订单失败:', cancelErr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
uni.showToast({
|
||||||
|
title: '已取消支付',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '支付失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: res.msg || '创建订单失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading()
|
||||||
|
console.error('购买失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '购买失败,请稍后重试',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 优惠券购买
|
||||||
|
if (currentTab.value === 'coupon') {
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '正在创建订单...'
|
||||||
|
})
|
||||||
|
|
||||||
|
const res = await createCouponPayment(selectedProduct.value.couponId)
|
||||||
|
uni.hideLoading()
|
||||||
|
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
// 调起微信支付
|
||||||
|
uni.requestPayment({
|
||||||
|
timeStamp: res.data.timeStamp,
|
||||||
|
nonceStr: res.data.nonceStr,
|
||||||
|
package: res.data.packageValue || res.data.package,
|
||||||
|
signType: res.data.signType || 'MD5',
|
||||||
|
paySign: res.data.paySign,
|
||||||
|
success: (payRes) => {
|
||||||
|
uni.showToast({
|
||||||
|
title: '支付成功',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
// 支付成功后,跳转到我的优惠券页面
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/my/coupon'
|
||||||
|
})
|
||||||
|
}, 1500)
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('支付失败:', err)
|
||||||
|
if (err.errMsg && err.errMsg.includes('cancel')) {
|
||||||
|
// 用户取消支付,调用取消接口
|
||||||
|
const orderNo = res.data.OutOrderNo;
|
||||||
|
if (orderNo) {
|
||||||
|
cancelCouponPayment(orderNo)
|
||||||
|
.then(cancelRes => {
|
||||||
|
console.log('取消支付订单成功:', cancelRes);
|
||||||
|
})
|
||||||
|
.catch(cancelErr => {
|
||||||
|
console.error('取消支付订单失败:', cancelErr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
uni.showToast({
|
||||||
|
title: '已取消支付',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '支付失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: res.msg || '创建订单失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading()
|
||||||
|
console.error('购买失败:', error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '购买失败,请稍后重试',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看我的商品
|
||||||
|
const goToMyProducts = () => {
|
||||||
|
if (currentTab.value === 'card') {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/my/card'
|
||||||
|
})
|
||||||
|
} else if (currentTab.value === 'coupon') {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/my/coupon'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.purchase-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
padding-bottom: 180rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab 切换 */
|
||||||
|
.tab-container {
|
||||||
|
top: 0;
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
display: flex;
|
||||||
|
background-color: #ffffff;
|
||||||
|
z-index: 999;
|
||||||
|
padding: 20rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
padding: 20rpx 0;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.tab-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
.tab-text {
|
||||||
|
color: #333;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 60rpx;
|
||||||
|
height: 6rpx;
|
||||||
|
background-color: #FFA928;
|
||||||
|
border-radius: 3rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 内容区域 */
|
||||||
|
.content-area {
|
||||||
|
height: calc(100vh - 180rpx);
|
||||||
|
padding: 20rpx;
|
||||||
|
padding-top: 120rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 商品列表 */
|
||||||
|
.product-list {
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
margin-top: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 会员卡卡片 */
|
||||||
|
.product-card {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 30rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
border: 2rpx solid transparent;
|
||||||
|
transition: all 0.3s;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
border-color: #FFA928;
|
||||||
|
box-shadow: 0 4rpx 20rpx rgba(255, 169, 40, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-left {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-name {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-desc {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-right {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-price {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #FF6B00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-original-price {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 优惠券卡片 */
|
||||||
|
.coupon-card-wrapper {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-card {
|
||||||
|
background: linear-gradient(135deg, #FFF4E6 0%, #FFE8CC 100%);
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 40rpx 30rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 2rpx solid transparent;
|
||||||
|
transition: all 0.3s;
|
||||||
|
overflow: visible;
|
||||||
|
min-height: 180rpx;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
border-color: #B8741A;
|
||||||
|
box-shadow: 0 4rpx 20rpx rgba(184, 116, 26, 0.2);
|
||||||
|
|
||||||
|
.coupon-circle-left,
|
||||||
|
.coupon-circle-right {
|
||||||
|
background-color: #FFF4E6;
|
||||||
|
border: 2rpx solid #B8741A;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 左侧圆形缺口 */
|
||||||
|
.coupon-circle-left {
|
||||||
|
position: absolute;
|
||||||
|
left: -16rpx;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 32rpx;
|
||||||
|
height: 32rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
z-index: 10;
|
||||||
|
transition: all 0.3s;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 右侧圆形缺口 */
|
||||||
|
.coupon-circle-right {
|
||||||
|
position: absolute;
|
||||||
|
right: -16rpx;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 32rpx;
|
||||||
|
height: 32rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
z-index: 10;
|
||||||
|
transition: all 0.3s;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-left {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-value {
|
||||||
|
font-size: 48rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #B8741A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-condition {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-validity {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-divider {
|
||||||
|
width: 2rpx;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
left: 65%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
// min-height: 160rpx;
|
||||||
|
align-self: stretch;
|
||||||
|
background: repeating-linear-gradient(to bottom,
|
||||||
|
#B8741A 0rpx,
|
||||||
|
#B8741A 8rpx,
|
||||||
|
transparent 8rpx,
|
||||||
|
transparent 16rpx);
|
||||||
|
margin: 0 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coupon-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.coupon-price {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 4rpx;
|
||||||
|
|
||||||
|
.price-label {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #B8741A;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-value {
|
||||||
|
font-size: 36rpx;
|
||||||
|
color: #B8741A;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 说明部分 */
|
||||||
|
.description-section {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 30rpx;
|
||||||
|
margin-top: 20rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-subtitle {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 底部操作栏 */
|
||||||
|
.bottom-bar {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: #ffffff;
|
||||||
|
padding-bottom: 20rpx;
|
||||||
|
border-radius: 40rpx 40rpx 0 0;
|
||||||
|
// padding: 20rpx 30rpx;
|
||||||
|
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 {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-direction: row;
|
||||||
|
background: #FFF4E3;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
padding: 16rpx 32rpx;
|
||||||
|
border-radius: 40rpx 40rpx 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
flex: 1;
|
||||||
|
margin-left: 20rpx;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
padding-top: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-products {
|
||||||
|
display: flex;
|
||||||
|
// flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4rpx;
|
||||||
|
min-width: 120rpx;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.full {
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 8rpx;
|
||||||
|
padding: 16rpx 32rpx;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-products-icon {
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-products-text {
|
||||||
|
margin-left: 10rpx;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #A16300;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
// align-items: flex-end;
|
||||||
|
gap: 4rpx;
|
||||||
|
flex: 1;
|
||||||
|
margin: 0 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-price {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #FF6B00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.original-price {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buy-button {
|
||||||
|
padding: 24rpx 60rpx;
|
||||||
|
background-color: #B8741A;
|
||||||
|
border-radius: 48rpx;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
background-color: #ccc;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buy-button-text {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #ffffff;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -55,6 +55,7 @@
|
|||||||
<view class="tags">
|
<view class="tags">
|
||||||
<view class="tag rent" v-if="isRentable(item)">{{ $t('location.rent') }}</view>
|
<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 return" v-if="isReturnable(item)">{{ $t('location.return') }}</view>
|
||||||
|
<view class="tag" v-if="isCoupon(item)">{{CouponOrmember(item)}}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="actions">
|
<view class="actions">
|
||||||
@@ -132,6 +133,11 @@
|
|||||||
return String(item?.status || '').toLowerCase() === 'online'
|
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) => {
|
const formatDistance = (meters) => {
|
||||||
if (meters < 1000) return `${Math.round(meters)}m`
|
if (meters < 1000) return `${Math.round(meters)}m`
|
||||||
return `${(meters / 1000).toFixed(1)}km`
|
return `${(meters / 1000).toFixed(1)}km`
|
||||||
@@ -187,6 +193,11 @@
|
|||||||
applyFilter()
|
applyFilter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CouponOrmember= async(item)=>{
|
||||||
|
|
||||||
|
return "可使用优惠券、会员卡"
|
||||||
|
}
|
||||||
|
|
||||||
const loadPositions = async (center) => {
|
const loadPositions = async (center) => {
|
||||||
try {
|
try {
|
||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
|
|||||||
|
After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 348 B |
|
Before Width: | Height: | Size: 676 KiB After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 868 B |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 1.8 KiB |