rentease-app/pages/bill-detail/bill-detail.vue

195 lines
8.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="bill-detail-page">
<view class="custom-nav safe-area-top">
<view class="nav-content">
<view class="nav-back" @click="goBack">
<uni-icons type="left" size="20" color="#1E293B"></uni-icons>
</view>
<text class="nav-title">账单详情</text>
<view class="nav-actions">
<view class="nav-btn" @click="deleteBill">
<uni-icons type="trash" size="20" color="#EF4444"></uni-icons>
</view>
</view>
</view>
</view>
<scroll-view scroll-y class="page-content" v-if="bill">
<!-- 金额展示 -->
<view class="amount-section">
<view class="amount-icon" :class="bill.type">
<uni-icons :type="bill.type === 'income' ? 'wallet-filled' : 'cart-filled'" size="48" color="#FFFFFF"></uni-icons>
</view>
<text class="amount-value" :class="bill.type">{{bill.type === 'income' ? '+' : '-'}}¥{{bill.receivableAmount}}</text>
<view class="amount-status" :class="bill.status">
<text>{{getStatusText(bill.status)}}</text>
</view>
</view>
<!-- 详细信息 -->
<view class="info-section">
<view class="info-card">
<view class="info-item">
<text class="item-label">账单编号</text>
<text class="item-value">{{bill.billNo || '-'}}</text>
</view>
<view class="info-item">
<text class="item-label">收支类型</text>
<text class="item-value">{{bill.type === 'income' ? '收入' : '支出'}}</text>
</view>
<view class="info-item">
<text class="item-label">收支类目</text>
<text class="item-value">{{getCategoryText(bill.category)}}</text>
</view>
<view class="info-item">
<text class="item-label">关联房间</text>
<text class="item-value">{{bill.roomNumber || '-'}}</text>
</view>
<view class="info-item">
<text class="item-label">租客</text>
<text class="item-value">{{bill.renterName || '-'}}</text>
</view>
<view class="info-item">
<text class="item-label">账单日期</text>
<text class="item-value">{{bill.billDate}}</text>
</view>
<view class="info-item">
<text class="item-label">应收金额</text>
<text class="item-value">¥{{bill.receivableAmount}}</text>
</view>
<view class="info-item">
<text class="item-label">实收金额</text>
<text class="item-value">¥{{bill.receivedAmount || '0.00'}}</text>
</view>
<view class="info-item" v-if="bill.remark">
<text class="item-label">备注</text>
<text class="item-value">{{bill.remark}}</text>
</view>
</view>
</view>
<!-- 收款按钮 -->
<view v-if="bill.status !== 'paid'" class="action-section">
<button class="action-btn primary" @click="receivePayment">收款</button>
</view>
<view class="safe-area-bottom" style="height: 40rpx;"></view>
</scroll-view>
</view>
</template>
<script>
import { billApi } from '../../api/index.js'
export default {
data() {
return {
billId: null,
bill: null
}
},
onLoad(options) {
if (options.id) {
this.billId = options.id
this.loadData()
}
},
methods: {
async loadData() {
try {
uni.showLoading({ title: '加载中...' })
const res = await billApi.getDetail(this.billId)
this.bill = res.data || {}
} catch (error) {
uni.showToast({ title: '加载失败', icon: 'none' })
} finally {
uni.hideLoading()
}
},
receivePayment() {
uni.showModal({
title: '确认收款',
content: `应收金额: ¥${this.bill.receivableAmount}\n确认已收到款项吗`,
success: async (res) => {
if (res.confirm) {
try {
uni.showLoading({ title: '处理中...' })
await billApi.update(this.billId, {
receivedAmount: this.bill.receivableAmount,
status: 'paid'
})
uni.showToast({ title: '收款成功', icon: 'success' })
this.loadData()
} catch (error) {
uni.showToast({ title: '操作失败', icon: 'none' })
} finally {
uni.hideLoading()
}
}
}
})
},
deleteBill() {
uni.showModal({
title: '确认删除',
content: '删除后无法恢复,确定要删除该账单吗?',
confirmColor: '#EF4444',
success: async (res) => {
if (res.confirm) {
try {
uni.showLoading({ title: '删除中...' })
await billApi.delete(this.billId)
uni.showToast({ title: '删除成功', icon: 'success' })
setTimeout(() => { uni.navigateBack() }, 1500)
} catch (error) {
uni.showToast({ title: '删除失败', icon: 'none' })
} finally {
uni.hideLoading()
}
}
}
})
},
goBack() { uni.navigateBack() },
getStatusText(status) {
const texts = { 'paid': '已付清', 'partial': '部分付款', 'unpaid': '未付款' }
return texts[status] || status
},
getCategoryText(category) {
const texts = { 'rent': '租金', 'deposit': '押金', 'water': '水费', 'electricity': '电费', 'maintenance': '维修费', 'property': '物业费', 'other': '其他' }
return texts[category] || category || '其他'
}
}
}
</script>
<style scoped>
.bill-detail-page { min-height: 100vh; background: #F8FAFC; display: flex; flex-direction: column; }
.custom-nav { background: #FFFFFF; border-bottom: 2rpx solid #F1F5F9; }
.nav-content { display: flex; align-items: center; justify-content: space-between; padding: 20rpx 32rpx; }
.nav-back { width: 60rpx; height: 60rpx; display: flex; align-items: center; justify-content: center; }
.nav-title { font-size: 36rpx; font-weight: 700; color: #1E293B; }
.nav-actions { width: 60rpx; height: 60rpx; display: flex; align-items: center; justify-content: center; }
.page-content { flex: 1; padding: 24rpx 32rpx; }
.amount-section { display: flex; flex-direction: column; align-items: center; padding: 48rpx 0; }
.amount-icon { width: 120rpx; height: 120rpx; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-bottom: 24rpx; }
.amount-icon.income { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
.amount-icon.expense { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); }
.amount-value { font-size: 56rpx; font-weight: 700; margin-bottom: 16rpx; }
.amount-value.income { color: #667eea; }
.amount-value.expense { color: #f5576c; }
.amount-status { padding: 8rpx 24rpx; border-radius: 8rpx; font-size: 26rpx; }
.amount-status.paid { background: #DCFCE7; color: #16A34A; }
.amount-status.partial { background: #FEF3C7; color: #D97706; }
.amount-status.unpaid { background: #FEE2E2; color: #DC2626; }
.info-section { margin-bottom: 32rpx; }
.info-card { background: #FFFFFF; border-radius: 24rpx; padding: 0 32rpx; box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04); }
.info-item { display: flex; justify-content: space-between; align-items: center; padding: 28rpx 0; border-bottom: 2rpx solid #F8FAFC; }
.info-item:last-child { border-bottom: none; }
.item-label { font-size: 30rpx; color: #64748B; }
.item-value { font-size: 30rpx; color: #1E293B; font-weight: 500; }
.action-section { margin-top: 48rpx; }
.action-btn { width: 100%; height: 96rpx; background: #F1F5F9; color: #64748B; font-size: 32rpx; font-weight: 600; border-radius: 16rpx; border: none; display: flex; align-items: center; justify-content: center; }
.action-btn.primary { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #FFFFFF; }
</style>