diff --git a/api/rental.js b/api/rental.js
index 2303ea6..9d36250 100644
--- a/api/rental.js
+++ b/api/rental.js
@@ -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)
}
}
diff --git a/pages.json b/pages.json
index 9532be2..d11665a 100644
--- a/pages.json
+++ b/pages.json
@@ -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": {
diff --git a/pages/billing/order-detail.vue b/pages/billing/order-detail.vue
index 8b05f04..32aafd6 100644
--- a/pages/billing/order-detail.vue
+++ b/pages/billing/order-detail.vue
@@ -72,17 +72,21 @@
费用明细
- 套餐单价
- ¥{{order.unitPrice || order.subscriptionPlan?.monthlyPrice || 0}}/月
+ 套餐价格
+ {{orderPriceText}}
购买时长
{{order.months}} 个月
- 基础费用
+ 订阅费用
¥{{order.amount}}
+
+ 计费周期
+ {{billingCycleText}}
+
优惠金额
-¥{{order.discountAmount}}
@@ -133,7 +137,7 @@
-
+
@@ -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: '我知道了'
})
},
diff --git a/pages/billing/order-list.vue b/pages/billing/order-list.vue
index 458af5f..59b237f 100644
--- a/pages/billing/order-list.vue
+++ b/pages/billing/order-list.vue
@@ -67,7 +67,7 @@
@@ -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: '我知道了'
})
},
diff --git a/pages/billing/plan-select.vue b/pages/billing/plan-select.vue
index a3aa970..f9e803a 100644
--- a/pages/billing/plan-select.vue
+++ b/pages/billing/plan-select.vue
@@ -30,6 +30,7 @@
¥{{plan.monthlyPrice}}
/月
+ ¥{{plan.yearlyPrice}}/年
@@ -76,20 +77,20 @@
费用明细
- 套餐单价
- ¥{{selectedPlan?.monthlyPrice || 0}}/月
+ 套餐价格
+ {{selectedPriceText}}
购买时长
{{selectedMonths}} 个月
- 基础费用
+ 订阅费用
¥{{baseAmount}}
-
- 优惠金额
- -¥{{discountAmount}}
+
+ 计费周期
+ {{billingCycleText}}
合计
@@ -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;
diff --git a/pages/rental-detail/rental-detail.vue b/pages/rental-detail/rental-detail.vue
index c1b7208..04d841d 100644
--- a/pages/rental-detail/rental-detail.vue
+++ b/pages/rental-detail/rental-detail.vue
@@ -134,6 +134,10 @@
办理续租
+
+
+ 办理换房
+
办理退租
@@ -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);