<template>
|
|
<view class="cu-page cu-page--with-footer cu-page--with-fab" v-if="contract">
|
|
<view :class="['cu-detail-hero', contract.status === 1 ? 'cu-detail-hero--green' : '']">
|
|
<view class="cu-detail-hero__top">
|
|
<view>
|
|
<view class="cu-detail-hero__label">合同编号</view>
|
|
<view class="cu-detail-hero__code">{{ contract.code }}</view>
|
|
</view>
|
|
<text class="cu-status">{{ statusText(contract.status) }}</text>
|
|
</view>
|
|
<view class="cu-detail-hero__amount">
|
|
<text class="cu-detail-hero__amount-label">租赁面积</text>
|
|
<text class="cu-detail-hero__amount-value">{{ formatArea(contract.totalArea) }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view v-if="contract.billStatusTip" :class="['cu-bill-tip', 'cu-bill-tip--inset', billTipClass(contract.billStatusType)]">
|
|
<view class="cu-bill-tip__icon-box">
|
|
<u-icon :name="billTipIconName(contract.billStatusType)" color="#ffffff" size="18" />
|
|
</view>
|
|
<text class="cu-bill-tip__text">{{ contract.billStatusTip }}</text>
|
|
</view>
|
|
|
|
<view class="cu-segment">
|
|
<view :class="['cu-segment__item', tab === 0 ? 'cu-segment__item--active' : '']" @click="tab = 0">合同信息</view>
|
|
<view :class="['cu-segment__item', tab === 1 ? 'cu-segment__item--active' : '']" @click="tab = 1">关联账单</view>
|
|
</view>
|
|
|
|
<view v-if="tab === 0">
|
|
<view class="cu-panel">
|
|
<view class="cu-panel__head">
|
|
<view class="cu-panel__icon cu-panel__icon--room">
|
|
<u-icon name="home-fill" color="#ffffff" size="20" />
|
|
</view>
|
|
<text class="cu-panel__title-text">房源信息</text>
|
|
</view>
|
|
<view v-if="contract.roomList && contract.roomList.length" class="cu-room-list">
|
|
<view v-for="(room, idx) in contract.roomList" :key="idx" class="cu-room-item">
|
|
<text class="cu-room-item__name">{{ formatRoomLine(room) }}</text>
|
|
<text class="cu-room-item__area">{{ formatArea(room.rentArea) }}</text>
|
|
</view>
|
|
</view>
|
|
<view v-else class="cu-kv__item">
|
|
<text class="cu-kv__value cu-kv__value--muted">{{ contract.roomInfo || '暂无房间信息' }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="cu-panel">
|
|
<view class="cu-panel__head">
|
|
<view class="cu-panel__icon cu-panel__icon--info">
|
|
<u-icon name="file-text-fill" color="#ffffff" size="20" />
|
|
</view>
|
|
<text class="cu-panel__title-text">基本信息</text>
|
|
</view>
|
|
<view class="cu-kv__item">
|
|
<text class="cu-kv__label">开始日期</text>
|
|
<text class="cu-kv__value">{{ contract.startDate || '-' }}</text>
|
|
</view>
|
|
<view class="cu-kv__item">
|
|
<text class="cu-kv__label">结束日期</text>
|
|
<text class="cu-kv__value">{{ contract.endDate || '-' }}</text>
|
|
</view>
|
|
<view class="cu-kv__item">
|
|
<text class="cu-kv__label">签订日期</text>
|
|
<text class="cu-kv__value">{{ contract.signDate || '-' }}</text>
|
|
</view>
|
|
<view class="cu-kv__item">
|
|
<text class="cu-kv__label">付款方式</text>
|
|
<text class="cu-kv__value">{{ contract.payTypeText || '-' }}</text>
|
|
</view>
|
|
<view class="cu-kv__item">
|
|
<text class="cu-kv__label">免租期</text>
|
|
<text class="cu-kv__value">{{ contract.freeRentPeriod || '-' }}</text>
|
|
</view>
|
|
<view class="cu-kv__item">
|
|
<text class="cu-kv__label">租赁押金</text>
|
|
<text class="cu-kv__value">¥{{ contract.zlDeposit != null ? contract.zlDeposit : '-' }}</text>
|
|
</view>
|
|
<view class="cu-kv__item">
|
|
<text class="cu-kv__label">合同单价</text>
|
|
<text class="cu-kv__value cu-kv__value--danger">¥{{ contract.zlFirstPrice != null ? contract.zlFirstPrice : '-' }}/月</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view v-if="showZlClause" class="cu-panel cu-clause-panel cu-clause-panel--zl">
|
|
<view class="cu-clause-panel__head">
|
|
<view class="cu-clause-panel__badge">
|
|
<u-icon name="red-packet-fill" color="#ffffff" size="22" />
|
|
</view>
|
|
<text class="cu-clause-panel__title">租赁条款</text>
|
|
</view>
|
|
|
|
<view class="cu-clause-summary">
|
|
<view class="cu-clause-chip">
|
|
<text class="cu-clause-chip__label">押金金额</text>
|
|
<text class="cu-clause-chip__value">{{ formatDeposit(contract.zlDeposit) }}</text>
|
|
</view>
|
|
<view class="cu-clause-chip">
|
|
<text class="cu-clause-chip__label">付款方式</text>
|
|
<text class="cu-clause-chip__value">{{ formatPayType(contract.zlPayType) }}</text>
|
|
</view>
|
|
<view class="cu-clause-chip cu-clause-chip--wide">
|
|
<text class="cu-clause-chip__label">免租期</text>
|
|
<text class="cu-clause-chip__value">{{ formatFreePeriod(contract.zlFreeStartDate, contract.zlFreeEndDate) }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view v-if="contract.zlDetailList && contract.zlDetailList.length" class="cu-clause-detail-list">
|
|
<view v-for="(item, idx) in contract.zlDetailList" :key="item.id || idx" class="cu-clause-detail-card">
|
|
<view class="cu-clause-detail-grid">
|
|
<view class="cu-clause-detail-grid__item">
|
|
<text class="cu-clause-detail-grid__label">开始日期</text>
|
|
<text class="cu-clause-detail-grid__value">{{ formatDate(item.startDate) || '-' }}</text>
|
|
</view>
|
|
<view class="cu-clause-detail-grid__item">
|
|
<text class="cu-clause-detail-grid__label">结束日期</text>
|
|
<text class="cu-clause-detail-grid__value">{{ formatDate(item.endDate) || '-' }}</text>
|
|
</view>
|
|
<view class="cu-clause-detail-grid__item cu-clause-detail-grid__item--highlight">
|
|
<text class="cu-clause-detail-grid__label">合同单价</text>
|
|
<text class="cu-clause-detail-grid__value">{{ formatPrice(item.price, item.circleType) }}</text>
|
|
</view>
|
|
<view class="cu-clause-detail-grid__item cu-clause-detail-grid__item--highlight">
|
|
<text class="cu-clause-detail-grid__label">付款提前天数</text>
|
|
<text class="cu-clause-detail-grid__value">{{ item.advanceDays != null ? item.advanceDays + '天' : '-' }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view v-else class="cu-clause-empty">暂无条款明细</view>
|
|
</view>
|
|
|
|
<view v-if="showWyClause" class="cu-panel cu-clause-panel cu-clause-panel--wy">
|
|
<view class="cu-clause-panel__head">
|
|
<view class="cu-clause-panel__badge">
|
|
<u-icon name="tags-fill" color="#ffffff" size="22" />
|
|
</view>
|
|
<text class="cu-clause-panel__title">物业费条款</text>
|
|
</view>
|
|
|
|
<view class="cu-clause-summary">
|
|
<view class="cu-clause-chip">
|
|
<text class="cu-clause-chip__label">押金金额</text>
|
|
<text class="cu-clause-chip__value">{{ formatDeposit(contract.wyDeposit) }}</text>
|
|
</view>
|
|
<view class="cu-clause-chip">
|
|
<text class="cu-clause-chip__label">付款方式</text>
|
|
<text class="cu-clause-chip__value">{{ formatPayType(contract.wyPayType) }}</text>
|
|
</view>
|
|
<view class="cu-clause-chip cu-clause-chip--wide">
|
|
<text class="cu-clause-chip__label">免租期</text>
|
|
<text class="cu-clause-chip__value">{{ formatFreePeriod(contract.wyFreeStartDate, contract.wyFreeEndDate) }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view v-if="contract.wyDetailList && contract.wyDetailList.length" class="cu-clause-detail-list">
|
|
<view v-for="(item, idx) in contract.wyDetailList" :key="item.id || idx" class="cu-clause-detail-card">
|
|
<view class="cu-clause-detail-grid">
|
|
<view class="cu-clause-detail-grid__item">
|
|
<text class="cu-clause-detail-grid__label">开始日期</text>
|
|
<text class="cu-clause-detail-grid__value">{{ formatDate(item.startDate) || '-' }}</text>
|
|
</view>
|
|
<view class="cu-clause-detail-grid__item">
|
|
<text class="cu-clause-detail-grid__label">结束日期</text>
|
|
<text class="cu-clause-detail-grid__value">{{ formatDate(item.endDate) || '-' }}</text>
|
|
</view>
|
|
<view class="cu-clause-detail-grid__item cu-clause-detail-grid__item--highlight">
|
|
<text class="cu-clause-detail-grid__label">合同单价</text>
|
|
<text class="cu-clause-detail-grid__value">{{ formatPrice(item.price, item.circleType) }}</text>
|
|
</view>
|
|
<view class="cu-clause-detail-grid__item cu-clause-detail-grid__item--highlight">
|
|
<text class="cu-clause-detail-grid__label">付款提前天数</text>
|
|
<text class="cu-clause-detail-grid__value">{{ item.advanceDays != null ? item.advanceDays + '天' : '-' }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view v-else class="cu-clause-empty">暂无条款明细</view>
|
|
</view>
|
|
|
|
<view class="cu-panel">
|
|
<view class="cu-panel__head">
|
|
<view class="cu-panel__icon cu-panel__icon--file">
|
|
<u-icon name="folder" color="#ffffff" size="20" />
|
|
</view>
|
|
<text class="cu-panel__title-text">合同附件</text>
|
|
</view>
|
|
<view v-if="contract.fileList && contract.fileList.length">
|
|
<view
|
|
v-for="file in contract.fileList"
|
|
:key="file.id"
|
|
class="cu-file-item"
|
|
@click="openFile(file)"
|
|
>
|
|
<view class="cu-file-item__icon-box">
|
|
<u-icon name="attach" color="#ffffff" size="22" />
|
|
</view>
|
|
<view class="cu-file-item__main">
|
|
<text class="cu-file-item__name">{{ file.name || '附件' }}</text>
|
|
<text class="cu-file-item__time">{{ file.createDate || '' }}</text>
|
|
</view>
|
|
<text class="cu-file-item__action">下载</text>
|
|
</view>
|
|
</view>
|
|
<view v-else class="cu-kv__item">
|
|
<text class="cu-kv__value cu-kv__value--muted">暂无附件</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<template v-else>
|
|
<view class="cu-contract-bill-panel">
|
|
<view class="cu-contract-bill-switch">
|
|
<view
|
|
:class="['cu-contract-bill-switch__item', 'cu-contract-bill-switch__item--pay', billType === 0 ? 'cu-contract-bill-switch__item--active' : '']"
|
|
@click="switchBillType(0)"
|
|
>
|
|
<text class="cu-contract-bill-switch__icon">付</text>
|
|
<view class="cu-contract-bill-switch__text">
|
|
<text class="cu-contract-bill-switch__title">付款账单</text>
|
|
<text class="cu-contract-bill-switch__desc">需向物业缴纳</text>
|
|
</view>
|
|
</view>
|
|
<view
|
|
:class="['cu-contract-bill-switch__item', 'cu-contract-bill-switch__item--in', billType === 1 ? 'cu-contract-bill-switch__item--active' : '']"
|
|
@click="switchBillType(1)"
|
|
>
|
|
<text class="cu-contract-bill-switch__icon">收</text>
|
|
<view class="cu-contract-bill-switch__text">
|
|
<text class="cu-contract-bill-switch__title">收款账单</text>
|
|
<text class="cu-contract-bill-switch__desc">物业应退还/支付</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="cu-contract-bill-summary">
|
|
<text class="cu-contract-bill-summary__label">{{ billType === 0 ? '待缴账单' : '待收账单' }}</text>
|
|
<text class="cu-contract-bill-summary__count">{{ bills.length }} 笔</text>
|
|
</view>
|
|
|
|
<view v-if="billsLoading" class="cu-contract-bill-loading">
|
|
<text>加载中...</text>
|
|
</view>
|
|
|
|
<view v-else-if="bills.length" class="cu-contract-bill-list">
|
|
<view
|
|
v-for="b in bills"
|
|
:key="b.id"
|
|
:class="['cu-contract-bill-card', billType === 0 ? 'cu-contract-bill-card--pay' : 'cu-contract-bill-card--in']"
|
|
@click="goBillDetail(b.id)"
|
|
>
|
|
<view class="cu-contract-bill-card__top">
|
|
<view class="cu-contract-bill-card__title-wrap">
|
|
<text class="cu-contract-bill-card__type">{{ costTypeText(b.costType) }}</text>
|
|
<text class="cu-contract-bill-card__code">{{ b.code || '-' }}</text>
|
|
</view>
|
|
<text :class="['cu-contract-bill-card__status', billPayStatusClass(b.payStatus)]">{{ payStatusText(b.payStatus) }}</text>
|
|
</view>
|
|
|
|
<view class="cu-contract-bill-card__amounts">
|
|
<view class="cu-contract-bill-card__amount-item">
|
|
<text class="cu-contract-bill-card__amount-label">{{ receivableLabel }}</text>
|
|
<text class="cu-contract-bill-card__amount-value">¥{{ formatMoney(b.receivableFee) }}</text>
|
|
</view>
|
|
<view class="cu-contract-bill-card__amount-divider" />
|
|
<view class="cu-contract-bill-card__amount-item">
|
|
<text class="cu-contract-bill-card__amount-label">{{ receivedLabel }}</text>
|
|
<text class="cu-contract-bill-card__amount-value cu-contract-bill-card__amount-value--muted">¥{{ formatMoney(b.actReceivableFee) }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view v-if="b.isOverdue === 1" class="cu-contract-bill-card__overdue">已逾期</view>
|
|
|
|
<view class="cu-contract-bill-card__meta">
|
|
<view class="cu-contract-bill-card__meta-row">
|
|
<text class="cu-contract-bill-card__meta-label">账单金额</text>
|
|
<text class="cu-contract-bill-card__meta-value">¥{{ formatMoney(b.totleFee) }}</text>
|
|
</view>
|
|
<view class="cu-contract-bill-card__meta-row">
|
|
<text class="cu-contract-bill-card__meta-label">计费周期</text>
|
|
<text class="cu-contract-bill-card__meta-value">{{ formatDate(b.startDate) || '-' }} ~ {{ formatDate(b.endDate) || '-' }}</text>
|
|
</view>
|
|
<view class="cu-contract-bill-card__meta-row">
|
|
<text class="cu-contract-bill-card__meta-label">{{ dueDateLabel }}</text>
|
|
<text class="cu-contract-bill-card__meta-value">{{ formatDate(b.planPayDate) || '-' }}</text>
|
|
</view>
|
|
<view class="cu-contract-bill-card__meta-row">
|
|
<text class="cu-contract-bill-card__meta-label">是否逾期</text>
|
|
<text :class="['cu-contract-bill-card__meta-value', b.isOverdue === 1 ? 'cu-contract-bill-card__meta-value--danger' : '']">
|
|
{{ overdueText(b) }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="cu-contract-bill-card__foot">
|
|
<text>查看详情</text>
|
|
<text class="cu-contract-bill-card__arrow">→</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<u-empty v-else :text="billType === 0 ? '暂无付款账单' : '暂无收款账单'" margin-top="60" />
|
|
</view>
|
|
</template>
|
|
<cu-workbench-fab />
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import { customerContractDetail } from '@/api'
|
import { baseUrl } from '@/utils/config.js'
|
|
const COST_TYPE_MAP = {
|
0: '租赁费',
|
1: '物业费',
|
2: '租赁押金',
|
3: '物业押金',
|
4: '水电费',
|
5: '杂项费',
|
6: '其他',
|
7: '保证金'
|
}
|
|
export default {
|
|
data () { return { id: null, contract: null, bills: [], tab: 0, billType: 0, billsLoading: false } },
|
|
computed: {
|
receivableLabel () { return this.billType === 0 ? '应付金额' : '应收金额' },
|
receivedLabel () { return this.billType === 0 ? '实付金额' : '实收金额' },
|
dueDateLabel () { return this.billType === 0 ? '应付日期' : '应收日期' },
|
showZlClause () {
|
if (!this.contract) return false
|
const t = this.contract.type
|
return t === 0 || t === 2
|
},
|
showWyClause () {
|
if (!this.contract) return false
|
const t = this.contract.type
|
return t === 0 || t === 1
|
}
|
},
|
|
onLoad (q) { this.id = q.id; this.load() },
|
|
methods: {
|
|
load (reloadContract = true) {
|
|
if (!reloadContract) this.billsLoading = true
|
|
customerContractDetail(this.id, this.billType).then(res => {
|
|
if (reloadContract) this.contract = res.data.contract
|
|
this.bills = this.sortBillsByPlanPayDate(res.data.bills || [])
|
|
}).finally(() => {
|
|
this.billsLoading = false
|
|
})
|
|
},
|
|
switchBillType (type) {
|
|
if (this.billType === type) return
|
|
this.billType = type
|
|
this.bills = []
|
|
this.load(false)
|
|
},
|
|
sortBillsByPlanPayDate (list) {
|
|
return [...list].sort((a, b) => {
|
|
const pa = this.formatDate(a.planPayDate) || ''
|
|
const pb = this.formatDate(b.planPayDate) || ''
|
|
if (pb !== pa) return pb.localeCompare(pa)
|
|
return (b.id || 0) - (a.id || 0)
|
|
})
|
|
},
|
|
costTypeText (type) { return COST_TYPE_MAP[type] || '账单' },
|
|
formatMoney (val) {
|
|
if (val === null || val === undefined || val === '') return '0.00'
|
|
return Number(val).toFixed(2)
|
|
},
|
|
formatDate (val) {
|
|
if (!val) return ''
|
|
return String(val).replace('T', ' ').substring(0, 10)
|
|
},
|
|
formatDeposit (val) {
|
|
if (val === null || val === undefined || val === '') return '-'
|
|
return `${val}元`
|
|
},
|
|
formatPayType (payType) {
|
|
if (payType === 1) return '每三个月一付'
|
|
if (payType === 2) return '六个月一付'
|
|
if (payType === 3) return '一年一付'
|
|
if (payType === 0) return '一次性付款'
|
|
return '-'
|
|
},
|
|
formatFreePeriod (start, end) {
|
|
const s = this.formatDate(start)
|
|
const e = this.formatDate(end)
|
|
if (!s && !e) return '-'
|
|
return `${s || '-'} ~ ${e || '-'}`
|
|
},
|
|
circleTypeUnit (type) {
|
|
const map = {
|
|
0: '元/m²·天',
|
|
1: '元/m²·月',
|
|
2: '元/m²·年',
|
|
3: '元/天',
|
|
4: '元/月',
|
|
5: '元/年',
|
|
6: '元/场'
|
|
}
|
|
return map[type] || ''
|
|
},
|
|
formatPrice (price, circleType) {
|
|
if (price === null || price === undefined || price === '') return '-'
|
|
const unit = this.circleTypeUnit(circleType)
|
|
return unit ? `${price} ${unit}` : String(price)
|
|
},
|
|
formatArea (area) {
|
|
if (area === null || area === undefined || area === '') return '-'
|
|
return `${area}㎡`
|
|
},
|
|
formatRoomLine (room) {
|
|
if (!room) return '-'
|
|
const parts = [room.projectName, room.buildingName, room.floorName, room.roomNum].filter(Boolean)
|
|
return parts.length ? parts.join('/') : '-'
|
|
},
|
|
statusText (s) { return { 0: '待执行', 1: '执行中', 2: '已到期', 3: '退租中', 4: '已退租' }[s] || '-' },
|
|
payStatusText (s) {
|
|
const common = { 1: '已结清', 2: '部分结清', 5: '已关闭' }
|
|
if (common[s]) return common[s]
|
|
if (this.billType === 0) {
|
|
return { 0: '待付款', 3: '待付款', 4: '待退款' }[s] || '-'
|
|
}
|
|
return { 0: '待收款', 3: '待收款', 4: '待退款' }[s] || '-'
|
|
},
|
|
billPayStatusClass (s) {
|
|
if (s === 1) return 'cu-contract-bill-card__status--ok'
|
|
if (s === 0 || s === 3) return 'cu-contract-bill-card__status--warn'
|
|
if (s === 4) return 'cu-contract-bill-card__status--bad'
|
|
return 'cu-contract-bill-card__status--muted'
|
|
},
|
|
overdueText (item) {
|
|
if (!item) return '-'
|
|
return item.isOverdue === 1 ? '是' : '否'
|
|
},
|
|
goBillDetail (id) { uni.navigateTo({ url: `/pages/customer/bill/detail?id=${id}` }) },
|
|
billTipClass (type) {
|
|
if (type === 'danger') return 'cu-bill-tip--danger'
|
|
if (type === 'warn') return 'cu-bill-tip--warn'
|
|
return 'cu-bill-tip--ok'
|
|
},
|
|
billTipIconName (type) {
|
|
if (type === 'danger') return 'close-circle-fill'
|
|
if (type === 'warn') return 'clock-fill'
|
|
return 'checkmark-circle-fill'
|
|
},
|
|
resolveFileUrl (raw) {
|
|
if (!raw) return ''
|
|
const url = String(raw).trim()
|
|
if (/^https?:\/\//i.test(url)) return url
|
|
if (typeof window === 'undefined') return url
|
|
if (url.startsWith('//')) return `${window.location.protocol}${url}`
|
|
if (url.startsWith('/')) return `${window.location.origin}${url}`
|
|
try {
|
|
const origin = new URL(baseUrl, window.location.href).origin
|
|
return `${origin}/${url.replace(/^\/+/, '')}`
|
|
} catch (e) {
|
|
return `${window.location.origin}/${url.replace(/^\/+/, '')}`
|
|
}
|
|
},
|
|
openFile (file) {
|
|
const url = this.resolveFileUrl(file.fileurlFull || file.fileurl)
|
|
if (!url) {
|
|
uni.showToast({ title: '附件地址无效', icon: 'none' })
|
|
return
|
|
}
|
|
if (typeof window !== 'undefined' && window.document) {
|
|
const link = document.createElement('a')
|
|
link.style.display = 'none'
|
|
link.href = url
|
|
link.target = '_blank'
|
|
link.rel = 'noopener noreferrer'
|
|
if (file.name) link.setAttribute('download', file.name)
|
|
document.body.appendChild(link)
|
|
link.click()
|
|
document.body.removeChild(link)
|
|
return
|
|
}
|
|
uni.showLoading({ title: '下载中' })
|
|
uni.downloadFile({
|
|
url,
|
|
success: (res) => {
|
|
if (res.statusCode === 200) {
|
|
uni.openDocument({ filePath: res.tempFilePath, showMenu: true })
|
|
} else {
|
|
uni.showToast({ title: '下载失败', icon: 'none' })
|
|
}
|
|
},
|
|
fail: () => uni.showToast({ title: '下载失败', icon: 'none' }),
|
|
complete: () => uni.hideLoading()
|
|
})
|
|
}
|
|
}
|
|
}
|
|
</script>
|