This commit is contained in:
xiaoxian 2026-06-11 21:44:54 +08:00
parent a8de74cfd4
commit 9ad01ccce2
6 changed files with 135 additions and 84 deletions

View File

@ -79,5 +79,25 @@ export default {
*/
terminate(id, data) {
return post(`/rentals/${id}/terminate`, data)
},
/**
* 办理续租
* @param {number} id - 租赁记录ID
* @param {Object} data - 续租数据
* @returns {Promise}
*/
renew(id, data) {
return post(`/rentals/${id}/renew`, data)
},
/**
* 办理换房
* @param {number} id - 租赁记录ID
* @param {Object} data - 换房数据
* @returns {Promise}
*/
changeRoom(id, data) {
return post(`/rentals/${id}/change-room`, data)
}
}

View File

@ -108,6 +108,18 @@
"navigationStyle": "custom"
}
},
{
"path": "pages/rental-renew/rental-renew",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/rental-change-room/rental-change-room",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/rental-add/rental-add",
"style": {

View File

@ -72,17 +72,21 @@
<view class="section-title">费用明细</view>
<view class="price-list">
<view class="price-row">
<text class="row-label">套餐</text>
<text class="row-value">¥{{order.unitPrice || order.subscriptionPlan?.monthlyPrice || 0}}/</text>
<text class="row-label">套餐</text>
<text class="row-value">{{orderPriceText}}</text>
</view>
<view class="price-row">
<text class="row-label">购买时长</text>
<text class="row-value">{{order.months}} 个月</text>
</view>
<view class="price-row">
<text class="row-label">基础费用</text>
<text class="row-label">订阅费用</text>
<text class="row-value">¥{{order.amount}}</text>
</view>
<view class="price-row" v-if="order.billingCycle">
<text class="row-label">计费周期</text>
<text class="row-value">{{billingCycleText}}</text>
</view>
<view class="price-row" v-if="order.discountAmount > 0">
<text class="row-label">优惠金额</text>
<text class="row-value discount">-¥{{order.discountAmount}}</text>
@ -133,7 +137,7 @@
<!-- 底部操作栏 -->
<view class="bottom-bar safe-area-bottom" v-if="order && order.status === 'pending'">
<button class="action-btn cancel" @click="cancelOrder">取消订单</button>
<button class="action-btn pay" @click="payOrder">立即支付</button>
<button class="action-btn pay" @click="showPaymentInfo">付款说明</button>
</view>
</view>
</template>
@ -167,11 +171,26 @@
},
statusDesc() {
const map = {
pending: '请在24小时内完成支付否则订单将自动取消',
pending: '请按平台收款信息付款,管理员确认后套餐生效',
paid: '订单已支付成功,套餐已生效',
cancelled: '订单已取消,如有疑问请联系客服'
}
return map[this.order?.status] || ''
},
orderPriceText() {
if (!this.order) return '¥0/月'
if (this.order.billingCycle === 'yearly' && this.order.subscriptionPlan?.yearlyPrice) {
return `¥${this.order.subscriptionPlan.yearlyPrice}/年`
}
return `¥${this.order.unitPrice || this.order.subscriptionPlan?.monthlyPrice || 0}/月`
},
billingCycleText() {
const map = {
monthly: '月付',
yearly: '年付',
custom: '自定义周期'
}
return map[this.order?.billingCycle] || this.order?.billingCycle || '-'
}
},
onLoad(options) {
@ -246,34 +265,12 @@
})
},
payOrder() {
showPaymentInfo() {
uni.showModal({
title: '支付确认',
content: `确认支付订单 #${this.order.orderNo},金额 ¥${this.order.actualAmount}`,
success: async (res) => {
if (res.confirm) {
try {
uni.showLoading({ title: '支付中...' })
const result = await billingApi.payOrder(this.orderId)
uni.hideLoading()
if (result.code === 200) {
uni.showToast({
title: '支付成功',
icon: 'success',
duration: 2000
})
this.loadOrderDetail()
} else {
uni.showToast({ title: result.message || '支付失败', icon: 'none' })
}
} catch (error) {
uni.hideLoading()
console.error('支付失败:', error)
uni.showToast({ title: '支付失败', icon: 'none' })
}
}
}
title: '付款说明',
content: `订单 #${this.order.orderNo}\n应付金额¥${this.order.actualAmount}\n请按平台收款信息完成转账管理员确认后套餐自动生效。`,
showCancel: false,
confirmText: '我知道了'
})
},

View File

@ -67,7 +67,7 @@
<view class="order-footer" v-if="order.status === 'pending'">
<button class="action-btn cancel" @click.stop="cancelOrder(order)">取消订单</button>
<button class="action-btn pay" @click.stop="payOrder(order)">去支付</button>
<button class="action-btn pay" @click.stop="showPaymentInfo(order)">付款说明</button>
</view>
</view>
@ -223,33 +223,12 @@
})
},
async payOrder(order) {
showPaymentInfo(order) {
uni.showModal({
title: '支付确认',
content: `确认支付订单 #${order.orderNo},金额 ¥${order.actualAmount}`,
success: async (res) => {
if (res.confirm) {
uni.showLoading({ title: '支付中...' })
try {
const result = await billingApi.payOrder(order.id)
uni.hideLoading()
if (result.code === 200) {
uni.showToast({
title: '支付成功',
icon: 'success',
duration: 2000
})
this.refreshData()
} else {
uni.showToast({ title: result.message || '支付失败', icon: 'none' })
}
} catch (error) {
uni.hideLoading()
console.error('支付失败:', error)
uni.showToast({ title: '支付失败', icon: 'none' })
}
}
}
title: '付款说明',
content: `订单 #${order.orderNo}\n应付金额¥${order.actualAmount}\n请按平台收款信息完成转账管理员确认后套餐自动生效。`,
showCancel: false,
confirmText: '我知道了'
})
},

View File

@ -30,6 +30,7 @@
<view class="plan-price">
<text class="price">¥{{plan.monthlyPrice}}</text>
<text class="unit">/</text>
<text v-if="plan.yearlyPrice" class="yearly-price">¥{{plan.yearlyPrice}}/</text>
</view>
</view>
@ -76,20 +77,20 @@
<view class="section-title">费用明细</view>
<view class="price-list">
<view class="price-row">
<text class="row-label">套餐</text>
<text class="row-value">¥{{selectedPlan?.monthlyPrice || 0}}/</text>
<text class="row-label">套餐</text>
<text class="row-value">{{selectedPriceText}}</text>
</view>
<view class="price-row">
<text class="row-label">购买时长</text>
<text class="row-value">{{selectedMonths}} 个月</text>
</view>
<view class="price-row">
<text class="row-label">基础费用</text>
<text class="row-label">订阅费用</text>
<text class="row-value">¥{{baseAmount}}</text>
</view>
<view class="price-row" v-if="discountAmount > 0">
<text class="row-label">优惠金额</text>
<text class="row-value discount">-¥{{discountAmount}}</text>
<view class="price-row" v-if="billingCycle">
<text class="row-label">计费周期</text>
<text class="row-value">{{billingCycleText}}</text>
</view>
<view class="price-row total">
<text class="row-label">合计</text>
@ -165,20 +166,36 @@
selectedMonths: 1,
periods: [
{ label: '1个月', value: 1, discount: 0 },
{ label: '3个月', value: 3, discount: 5 },
{ label: '6个月', value: 6, discount: 10 },
{ label: '12个月', value: 12, discount: 15 }
{ label: '3个月', value: 3, discount: 0 },
{ label: '6个月', value: 6, discount: 0 },
{ label: '12个月', value: 12, discount: 0 }
],
paymentSettings: null,
submitting: false,
baseAmount: 0,
discountAmount: 0,
totalAmount: 0
totalAmount: 0,
billingCycle: ''
}
},
computed: {
selectedPlan() {
return this.plans.find(p => p.id === this.selectedPlanId)
},
selectedPriceText() {
if (!this.selectedPlan) return '¥0/月'
if (this.selectedMonths === 12 && Number(this.selectedPlan.yearlyPrice) > 0) {
return `¥${this.selectedPlan.yearlyPrice}/年`
}
return `¥${this.selectedPlan.monthlyPrice}/月`
},
billingCycleText() {
const map = {
monthly: '月付',
yearly: '年付',
custom: '自定义周期'
}
return map[this.billingCycle] || this.billingCycle
}
},
watch: {
@ -198,8 +215,8 @@
try {
const res = await billingApi.getPlans()
if (res.code === 200) {
// monthlyPrice <= 0
this.plans = (res.data || []).filter(p => p.status === 'active' && p.monthlyPrice > 0)
//
this.plans = (res.data || []).filter(p => p.status === 'active' && (Number(p.monthlyPrice) > 0 || Number(p.yearlyPrice) > 0))
//
const recommended = this.plans.find(p => p.isRecommended)
if (recommended) {
@ -233,25 +250,29 @@
this.selectedMonths = months
},
calculatePrice() {
async calculatePrice() {
if (!this.selectedPlan) {
this.baseAmount = 0
this.discountAmount = 0
this.totalAmount = 0
this.billingCycle = ''
return
}
const monthlyPrice = this.selectedPlan.monthlyPrice
const baseAmount = monthlyPrice * this.selectedMonths
//
const period = this.periods.find(p => p.value === this.selectedMonths)
const discountRate = period ? period.discount / 100 : 0
const discountAmount = Math.round(baseAmount * discountRate)
this.baseAmount = baseAmount
this.discountAmount = discountAmount
this.totalAmount = baseAmount - discountAmount
try {
const res = await billingApi.calculatePrice({
planId: this.selectedPlanId,
months: this.selectedMonths
})
if (res.code === 200 && res.data) {
this.baseAmount = res.data.baseAmount || 0
this.discountAmount = res.data.discountAmount || 0
this.totalAmount = res.data.totalAmount || 0
this.billingCycle = res.data.billingCycle || ''
}
} catch (error) {
console.error('计算价格失败:', error)
uni.showToast({ title: '计算价格失败', icon: 'none' })
}
},
async createOrder() {
@ -431,6 +452,13 @@
color: #94A3B8;
}
.yearly-price {
display: block;
margin-top: 6rpx;
font-size: 22rpx;
color: #64748B;
}
.plan-features {
display: flex;
flex-direction: column;

View File

@ -134,6 +134,10 @@
<uni-icons type="refresh-filled" size="20" color="#FFFFFF"></uni-icons>
<text>办理续租</text>
</view>
<view class="action-btn warning" @click="handleChangeRoom">
<uni-icons type="home-filled" size="20" color="#FFFFFF"></uni-icons>
<text>办理换房</text>
</view>
<view class="action-btn danger" @click="handleTerminate">
<uni-icons type="closeempty" size="20" color="#FFFFFF"></uni-icons>
<text>办理退租</text>
@ -366,6 +370,12 @@
url: `/pages/rental-renew/rental-renew?id=${this.rentalId}`
})
},
handleChangeRoom() {
uni.navigateTo({
url: `/pages/rental-change-room/rental-change-room?id=${this.rentalId}`
})
},
handleTerminate() {
this.terminateForm = {
@ -757,6 +767,11 @@
box-shadow: 0 8rpx 24rpx rgba(37, 99, 235, 0.3);
}
.action-btn.warning {
background: linear-gradient(135deg, #F59E0B 0%, #D97706 100%);
box-shadow: 0 8rpx 24rpx rgba(245, 158, 11, 0.28);
}
.action-btn.danger {
background: linear-gradient(135deg, #EF4444 0%, #DC2626 100%);
box-shadow: 0 8rpx 24rpx rgba(239, 68, 68, 0.3);