| | |
| | | |
| | | <view class="cu-bill-page__header"> |
| | | |
| | | <view class="cu-tabs cu-bill-tabs"> |
| | | <view class="cu-bill-page__toolbar"> |
| | | |
| | | <view |
| | | <view class="cu-tabs cu-bill-tabs cu-bill-tabs--toolbar"> |
| | | |
| | | v-for="(t, i) in tabs" |
| | | <view |
| | | |
| | | :key="i" |
| | | v-for="(t, i) in tabs" |
| | | |
| | | :class="['cu-tab', tabIdx === i ? 'cu-tab--active' : '']" |
| | | :key="i" |
| | | |
| | | @click="tabIdx = i; load()" |
| | | :class="['cu-tab', tabIdx === i ? 'cu-tab--active' : '']" |
| | | |
| | | >{{ t }}</view> |
| | | @click="switchPayTab(i)" |
| | | |
| | | >{{ t }}</view> |
| | | |
| | | </view> |
| | | |
| | | |
| | | |
| | | <picker :range="costTypeFilters" range-key="label" @change="onCostTypeChange"> |
| | | |
| | | <view :class="['cu-bill-cost-picker', costTypeIdx > 0 ? 'cu-bill-cost-picker--active' : '', costTypePickerClass]"> |
| | | |
| | | <text class="cu-bill-cost-picker__label">{{ costTypeLabel }}</text> |
| | | |
| | | <text class="cu-bill-cost-picker__arrow">▾</text> |
| | | |
| | | </view> |
| | | |
| | | </picker> |
| | | |
| | | </view> |
| | | |
| | | |
| | | |
| | | <view class="cu-bill-summary"> |
| | | |
| | | <text class="cu-bill-summary__count">{{ list.length }}</text> |
| | | <view class="cu-bill-summary__main"> |
| | | |
| | | <text class="cu-bill-summary__label">笔账单</text> |
| | | <text class="cu-bill-summary__count">{{ list.length }}</text> |
| | | |
| | | <text class="cu-bill-summary__label">笔账单</text> |
| | | |
| | | </view> |
| | | |
| | | |
| | | |
| | | <view v-if="overdueCount > 0" class="cu-bill-summary__overdue-group"> |
| | | |
| | | <text class="cu-bill-summary__chip cu-bill-summary__chip--stat">{{ overdueCount }}笔逾期</text> |
| | | |
| | | <view |
| | | |
| | | :class="['cu-bill-summary__chip', 'cu-bill-summary__chip--filter', onlyOverdue ? 'cu-bill-summary__chip--filter-on' : '']" |
| | | |
| | | @click="toggleOnlyOverdue" |
| | | |
| | | > |
| | | |
| | | <view :class="['cu-bill-summary__check', onlyOverdue ? 'cu-bill-summary__check--on' : '']"> |
| | | |
| | | <text v-if="onlyOverdue" class="cu-bill-summary__check-icon">✓</text> |
| | | |
| | | </view> |
| | | |
| | | <text class="cu-bill-summary__chip-text">仅看逾期</text> |
| | | |
| | | </view> |
| | | |
| | | </view> |
| | | |
| | | </view> |
| | | |
| | |
| | | |
| | | <view |
| | | |
| | | v-for="item in list" |
| | | v-for="item in displayList" |
| | | |
| | | :key="item.id" |
| | | |
| | |
| | | |
| | | <view class="cu-bill-card__body"> |
| | | |
| | | <view class="cu-bill-card__head"> |
| | | <view class="cu-bill-card__type-row"> |
| | | |
| | | <view class="cu-bill-card__head-main"> |
| | | <view :class="['cu-bill-type-tag', costTypeBadgeClass(item.costType)]"> |
| | | |
| | | <text class="cu-bill-card__type">{{ costTypeText(item.costType) }}</text> |
| | | |
| | | <text class="cu-bill-card__code">{{ item.code }}</text> |
| | | <text class="cu-bill-type-tag__text">{{ costTypeText(item.costType) }}</text> |
| | | |
| | | </view> |
| | | |
| | | <text :class="['cu-status', payStatusClass(item.payStatus)]">{{ payText(item.payStatus) }}</text> |
| | | |
| | | </view> |
| | | |
| | | |
| | | |
| | | <view class="cu-bill-card__code">{{ item.code }}</view> |
| | | |
| | | |
| | | |
| | |
| | | |
| | | |
| | | <view class="cu-bill-card__meta"> |
| | | |
| | | <view class="cu-bill-card__meta-item cu-bill-card__meta-item--full"> |
| | | |
| | | <text class="cu-bill-card__meta-label">计费周期</text> |
| | | |
| | | <text class="cu-bill-card__meta-value">{{ item.startDate }} ~ {{ item.endDate }}</text> |
| | | |
| | | </view> |
| | | |
| | | </view> |
| | | |
| | | |
| | |
| | | |
| | | </view> |
| | | |
| | | <u-empty v-if="!list.length" text="暂无账单" margin-top="80" /> |
| | | <u-empty v-if="!displayList.length" :text="onlyOverdue ? '暂无逾期账单' : '暂无账单'" margin-top="80" /> |
| | | |
| | | </view> |
| | | |
| | |
| | | |
| | | |
| | | |
| | | const COST_TYPE_FILTERS = [ |
| | | |
| | | { label: '费用类型', value: null }, |
| | | |
| | | { label: '租赁费', value: 0 }, |
| | | |
| | | { label: '物业费', value: 1 }, |
| | | |
| | | { label: '租赁押金', value: 2 }, |
| | | |
| | | { label: '物业押金', value: 3 }, |
| | | |
| | | { label: '水电费', value: 4 }, |
| | | |
| | | { label: '杂项费', value: 5 }, |
| | | |
| | | { label: '其他', value: 6 }, |
| | | |
| | | { label: '保证金', value: 7 } |
| | | |
| | | ] |
| | | |
| | | |
| | | |
| | | export default { |
| | | |
| | | data () { return { list: [], tabIdx: 0, tabs: ['全部', '待支付', '已支付'] } }, |
| | | data () { |
| | | |
| | | return { |
| | | |
| | | list: [], |
| | | |
| | | tabIdx: 0, |
| | | |
| | | costTypeIdx: 0, |
| | | |
| | | tabs: ['全部', '待支付', '已支付'], |
| | | |
| | | costTypeFilters: COST_TYPE_FILTERS, |
| | | |
| | | onlyOverdue: false |
| | | |
| | | } |
| | | |
| | | }, |
| | | |
| | | computed: { |
| | | |
| | | costTypeLabel () { |
| | | |
| | | return this.costTypeFilters[this.costTypeIdx].label |
| | | |
| | | }, |
| | | |
| | | costTypePickerClass () { |
| | | |
| | | const value = this.costTypeFilters[this.costTypeIdx].value |
| | | |
| | | if (value == null) return '' |
| | | |
| | | const map = { |
| | | |
| | | 0: 'cu-bill-cost-picker--rent', |
| | | |
| | | 1: 'cu-bill-cost-picker--property', |
| | | |
| | | 2: 'cu-bill-cost-picker--deposit', |
| | | |
| | | 3: 'cu-bill-cost-picker--deposit', |
| | | |
| | | 4: 'cu-bill-cost-picker--utility', |
| | | |
| | | 5: 'cu-bill-cost-picker--misc', |
| | | |
| | | 6: 'cu-bill-cost-picker--misc', |
| | | |
| | | 7: 'cu-bill-cost-picker--deposit' |
| | | |
| | | } |
| | | |
| | | return map[value] || '' |
| | | |
| | | }, |
| | | |
| | | overdueCount () { |
| | | |
| | | return this.list.filter(item => this.isOverdue(item)).length |
| | | |
| | | }, |
| | | |
| | | displayList () { |
| | | |
| | | if (!this.onlyOverdue) return this.list |
| | | |
| | | return this.list.filter(item => this.isOverdue(item)) |
| | | |
| | | } |
| | | |
| | | }, |
| | | |
| | | onShow () { this.load() }, |
| | | |
| | |
| | | |
| | | const payTab = this.tabIdx === 0 ? null : (this.tabIdx === 1 ? 0 : 1) |
| | | |
| | | customerBillPage({ page: 1, capacity: 50, model: { payTab } }) |
| | | const costType = this.costTypeFilters[this.costTypeIdx].value |
| | | |
| | | .then(res => { this.list = (res.data && res.data.records) || [] }) |
| | | const model = { payTab } |
| | | |
| | | if (costType != null) model.costType = costType |
| | | |
| | | customerBillPage({ page: 1, capacity: 50, model }) |
| | | |
| | | .then(res => { |
| | | |
| | | this.list = (res.data && res.data.records) || [] |
| | | |
| | | if (!this.list.some(item => this.isOverdue(item))) this.onlyOverdue = false |
| | | |
| | | }) |
| | | |
| | | }, |
| | | |
| | | toggleOnlyOverdue () { |
| | | |
| | | this.onlyOverdue = !this.onlyOverdue |
| | | |
| | | }, |
| | | |
| | | switchPayTab (i) { |
| | | |
| | | if (this.tabIdx === i) return |
| | | |
| | | this.tabIdx = i |
| | | |
| | | this.load() |
| | | |
| | | }, |
| | | |
| | | onCostTypeChange (e) { |
| | | |
| | | const idx = Number(e.detail.value) |
| | | |
| | | if (this.costTypeIdx === idx) return |
| | | |
| | | this.costTypeIdx = idx |
| | | |
| | | this.load() |
| | | |
| | | }, |
| | | |
| | | costTypeText (type) { return COST_TYPE_MAP[type] || '账单' }, |
| | | |
| | | costTypeBadgeClass (type) { |
| | | |
| | | const map = { |
| | | |
| | | 0: 'cu-bill-type-tag--rent', |
| | | |
| | | 1: 'cu-bill-type-tag--property', |
| | | |
| | | 2: 'cu-bill-type-tag--deposit', |
| | | |
| | | 3: 'cu-bill-type-tag--deposit', |
| | | |
| | | 4: 'cu-bill-type-tag--utility', |
| | | |
| | | 5: 'cu-bill-type-tag--misc', |
| | | |
| | | 6: 'cu-bill-type-tag--misc', |
| | | |
| | | 7: 'cu-bill-type-tag--deposit' |
| | | |
| | | } |
| | | |
| | | return map[type] || 'cu-bill-type-tag--default' |
| | | |
| | | }, |
| | | |
| | | formatMoney (val) { |
| | | |
| | |
| | | }, |
| | | |
| | | contractPeriod (item) { |
| | | |
| | | if (!item) return '-' |
| | | |
| | | const start = this.formatDate(item.contractStartDate) |
| | | |
| | | const end = this.formatDate(item.contractEndDate) |
| | | |
| | | if (!start && !end) return '-' |
| | | |
| | | return `${start || '-'} ~ ${end || '-'}` |
| | | |
| | | }, |
| | | |
| | | formatDate (val) { |
| | | |
| | | if (!val) return '' |
| | | |
| | | return String(val).replace('T', ' ').substring(0, 10) |
| | | |
| | | }, |
| | | |
| | | goDetail (id) { uni.navigateTo({ url: `/pages/customer/bill/detail?id=${id}` }) } |
| | | |
| | | } |
| | |
| | | } |
| | | |
| | | </script> |
| | | |
| | | |
| | |
| | | |
| | | <view v-if="contract.billStatusTip" :class="['cu-bill-tip', 'cu-bill-tip--inset', billTipClass(contract.billStatusType)]"> |
| | | |
| | | <text class="cu-bill-tip__icon">{{ billTipIcon(contract.billStatusType) }}</text> |
| | | <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 class="cu-panel"> |
| | | |
| | | <view class="cu-panel__title">房源信息</view> |
| | | <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 class="cu-panel"> |
| | | |
| | | <view class="cu-panel__title">基本信息</view> |
| | | <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"> |
| | | |
| | |
| | | |
| | | |
| | | |
| | | <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__title">合同附件</view> |
| | | <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"> |
| | | |
| | |
| | | |
| | | > |
| | | |
| | | <text class="cu-file-item__icon">📎</text> |
| | | <view class="cu-file-item__icon-box"> |
| | | |
| | | <u-icon name="attach" color="#ffffff" size="22" /> |
| | | |
| | | </view> |
| | | |
| | | <view class="cu-file-item__main"> |
| | | |
| | |
| | | <script> |
| | | |
| | | import { customerContractDetail } from '@/api' |
| | | import { baseUrl } from '@/utils/config.js' |
| | | |
| | | const COST_TYPE_MAP = { |
| | | 0: '租赁费', |
| | |
| | | computed: { |
| | | receivableLabel () { return this.billType === 0 ? '应付金额' : '应收金额' }, |
| | | receivedLabel () { return this.billType === 0 ? '实付金额' : '实收金额' }, |
| | | dueDateLabel () { 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() }, |
| | |
| | | |
| | | if (reloadContract) this.contract = res.data.contract |
| | | |
| | | this.bills = res.data.bills || [] |
| | | this.bills = this.sortBillsByPlanPayDate(res.data.bills || []) |
| | | |
| | | }).finally(() => { |
| | | |
| | |
| | | |
| | | }, |
| | | |
| | | 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) 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) |
| | | |
| | | }, |
| | | |
| | |
| | | |
| | | }, |
| | | |
| | | billTipIcon (type) { |
| | | billTipIconName (type) { |
| | | |
| | | if (type === 'danger') return '⚠️' |
| | | if (type === 'danger') return 'close-circle-fill' |
| | | |
| | | if (type === 'warn') return '⏰' |
| | | if (type === 'warn') return 'clock-fill' |
| | | |
| | | return '✅' |
| | | 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 = file.fileurlFull || file.fileurl |
| | | const url = this.resolveFileUrl(file.fileurlFull || file.fileurl) |
| | | |
| | | if (!url) { |
| | | |
| | |
| | | |
| | | } |
| | | |
| | | // #ifdef H5 |
| | | if (typeof window !== 'undefined' && window.document) { |
| | | |
| | | window.open(url, '_blank') |
| | | const link = document.createElement('a') |
| | | |
| | | // #endif |
| | | link.style.display = 'none' |
| | | |
| | | // #ifndef H5 |
| | | 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: '下载中' }) |
| | | |
| | |
| | | complete: () => uni.hideLoading() |
| | | |
| | | }) |
| | | |
| | | // #endif |
| | | |
| | | } |
| | | |
| | |
| | | <template>
|
| | | <view class="cu-page">
|
| | | <scroll-view scroll-x class="cu-tabs cu-tabs--scroll">
|
| | | <view
|
| | | v-for="(t, i) in tabs"
|
| | | :key="t.value"
|
| | | :class="['cu-tab', tabIdx === i ? 'cu-tab--active' : '']"
|
| | | @click="tabIdx = i; load()"
|
| | | >{{ t.label }}</view>
|
| | | </scroll-view>
|
| | |
|
| | | <view class="cu-list-header">
|
| | | <text class="cu-list-header__count">共 {{ list.length }} 份合同</text>
|
| | | </view>
|
| | |
|
| | | <view class="cu-list-wrap">
|
| | | <view v-for="item in list" :key="item.id" class="cu-list-card cu-list-card--clickable" @click="goDetail(item.id)">
|
| | | <view class="cu-list-card__head">
|
| | | <view class="cu-list-card__icon cu-list-card__icon--contract">📄</view>
|
| | | <view class="cu-list-card__main">
|
| | | <view class="cu-list-card__title-row">
|
| | | <text class="cu-list-card__title">{{ item.code }}</text>
|
| | | <text :class="['cu-status', contractStatusClass(item.status)]">{{ statusText(item.status) }}</text>
|
| | | </view>
|
| | | <text class="cu-list-card__sub">{{ item.roomInfo || '暂无房间信息' }}</text>
|
| | | <view class="cu-period-chip">{{ item.startDate }} ~ {{ item.endDate }}</view>
|
| | | </view>
|
| | | </view>
|
| | |
|
| | | <view class="cu-info-grid">
|
| | | <view class="cu-info-cell">
|
| | | <text class="cu-info-cell__label">租赁面积</text>
|
| | | <text class="cu-info-cell__value cu-info-cell__value--primary">{{ formatArea(item.totalArea) }}</text>
|
| | | </view>
|
| | | <view class="cu-info-cell">
|
| | | <text class="cu-info-cell__label">付款方式</text>
|
| | | <text class="cu-info-cell__value">{{ item.payTypeText || '-' }}</text>
|
| | | </view>
|
| | | </view>
|
| | |
|
| | | <view v-if="item.billStatusTip" :class="['cu-bill-tip', billTipClass(item.billStatusType)]">
|
| | | <text class="cu-bill-tip__icon">{{ billTipIcon(item.billStatusType) }}</text>
|
| | | <text class="cu-bill-tip__text">{{ item.billStatusTip }}</text>
|
| | | </view>
|
| | |
|
| | | <view class="cu-list-card__foot">
|
| | | <text class="cu-time">点击查看完整合同信息</text>
|
| | | <text class="cu-list-card__arrow">详情 →</text>
|
| | | </view>
|
| | | </view>
|
| | | <u-empty v-if="!list.length" text="暂无合同" margin-top="80" />
|
| | | </view>
|
| | | </view>
|
| | | </template>
|
| | |
|
| | | <script>
|
| | | import { customerContractPage } from '@/api'
|
| | | export default {
|
| | | data () {
|
| | | return {
|
| | | list: [],
|
| | | tabIdx: 0,
|
| | | tabs: [
|
| | | { label: '全部', value: null }, { label: '待执行', value: 0 }, { label: '执行中', value: 1 },
|
| | | { label: '已到期', value: 2 }, { label: '退租中', value: 3 }, { label: '已退租', value: 4 }
|
| | | ]
|
| | | }
|
| | | },
|
| | | onShow () { this.load() },
|
| | | methods: {
|
| | | load () {
|
| | | customerContractPage({ page: 1, capacity: 50, model: { status: this.tabs[this.tabIdx].value } })
|
| | | .then(res => { this.list = (res.data && res.data.records) || [] })
|
| | | },
|
| | | formatArea (area) {
|
| | | if (area === null || area === undefined || area === '') return '-'
|
| | | return `${area}㎡`
|
| | | },
|
| | | statusText (s) {
|
| | | const map = { 0: '待执行', 1: '执行中', 2: '已到期', 3: '退租中', 4: '已退租' }
|
| | | return map[s] || '-'
|
| | | },
|
| | | contractStatusClass (s) {
|
| | | if (s === 1) return 'cu-status--ok'
|
| | | if (s === 2 || s === 4) return 'cu-status--muted'
|
| | | if (s === 3) return 'cu-status--warn'
|
| | | return 'cu-status--muted'
|
| | | },
|
| | | billTipClass (type) {
|
| | | if (type === 'danger') return 'cu-bill-tip--danger'
|
| | | if (type === 'warn') return 'cu-bill-tip--warn'
|
| | | return 'cu-bill-tip--ok'
|
| | | },
|
| | | billTipIcon (type) {
|
| | | if (type === 'danger') return '⚠️'
|
| | | if (type === 'warn') return '⏰'
|
| | | return '✅'
|
| | | },
|
| | | goDetail (id) { uni.navigateTo({ url: `/pages/customer/contract/detail?id=${id}` }) }
|
| | | }
|
| | | }
|
| | | </script>
|
| | |
|
| | | <template> |
| | | <view class="cu-page"> |
| | | <scroll-view scroll-x class="cu-tabs cu-tabs--scroll"> |
| | | <view |
| | | v-for="(t, i) in tabs" |
| | | :key="t.value" |
| | | :class="['cu-tab', tabIdx === i ? 'cu-tab--active' : '']" |
| | | @click="tabIdx = i; load()" |
| | | >{{ t.label }}</view> |
| | | </scroll-view> |
| | | |
| | | <view class="cu-list-header"> |
| | | <text class="cu-list-header__count">共 {{ list.length }} 份合同</text> |
| | | </view> |
| | | |
| | | <view class="cu-list-wrap"> |
| | | <view v-for="item in list" :key="item.id" class="cu-list-card cu-list-card--clickable" @click="goDetail(item.id)"> |
| | | <view class="cu-list-card__head"> |
| | | <view class="cu-list-card__icon cu-list-card__icon--contract"> |
| | | |
| | | <u-icon name="file-text-fill" color="#40a9ff" size="22" /> |
| | | |
| | | </view> |
| | | <view class="cu-list-card__main"> |
| | | <view class="cu-list-card__title-row"> |
| | | <text class="cu-list-card__title">{{ item.code }}</text> |
| | | <text :class="['cu-status', contractStatusClass(item.status)]">{{ statusText(item.status) }}</text> |
| | | </view> |
| | | <text class="cu-list-card__sub">{{ item.roomInfo || '暂无房间信息' }}</text> |
| | | <view class="cu-period-chip">{{ item.startDate }} ~ {{ item.endDate }}</view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="cu-info-grid"> |
| | | <view class="cu-info-cell"> |
| | | <text class="cu-info-cell__label">租赁面积</text> |
| | | <text class="cu-info-cell__value cu-info-cell__value--primary">{{ formatArea(item.totalArea) }}</text> |
| | | </view> |
| | | <view class="cu-info-cell"> |
| | | <text class="cu-info-cell__label">付款方式</text> |
| | | <text class="cu-info-cell__value">{{ item.payTypeText || '-' }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-if="item.billStatusTip" :class="['cu-bill-tip', billTipClass(item.billStatusType)]"> |
| | | |
| | | <text class="cu-bill-tip__text">{{ item.billStatusTip }}</text> |
| | | |
| | | </view> |
| | | |
| | | <view class="cu-list-card__foot"> |
| | | <text class="cu-time">点击查看完整合同信息</text> |
| | | <text class="cu-list-card__arrow">详情 →</text> |
| | | </view> |
| | | </view> |
| | | <u-empty v-if="!list.length" text="暂无合同" margin-top="80" /> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { customerContractPage } from '@/api' |
| | | export default { |
| | | data () { |
| | | return { |
| | | list: [], |
| | | tabIdx: 0, |
| | | tabs: [ |
| | | { label: '全部', value: null }, { label: '待执行', value: 0 }, { label: '执行中', value: 1 }, |
| | | { label: '已到期', value: 2 }, { label: '退租中', value: 3 }, { label: '已退租', value: 4 } |
| | | ] |
| | | } |
| | | }, |
| | | onShow () { this.load() }, |
| | | methods: { |
| | | load () { |
| | | customerContractPage({ page: 1, capacity: 50, model: { status: this.tabs[this.tabIdx].value } }) |
| | | .then(res => { this.list = (res.data && res.data.records) || [] }) |
| | | }, |
| | | formatArea (area) { |
| | | if (area === null || area === undefined || area === '') return '-' |
| | | return `${area}㎡` |
| | | }, |
| | | statusText (s) { |
| | | const map = { 0: '待执行', 1: '执行中', 2: '已到期', 3: '退租中', 4: '已退租' } |
| | | return map[s] || '-' |
| | | }, |
| | | contractStatusClass (s) { |
| | | if (s === 1) return 'cu-status--ok' |
| | | if (s === 2 || s === 4) return 'cu-status--muted' |
| | | if (s === 3) return 'cu-status--warn' |
| | | return 'cu-status--muted' |
| | | }, |
| | | billTipClass (type) { |
| | | if (type === 'danger') return 'cu-bill-tip--danger' |
| | | if (type === 'warn') return 'cu-bill-tip--warn' |
| | | return 'cu-bill-tip--ok' |
| | | }, |
| | | goDetail (id) { uni.navigateTo({ url: `/pages/customer/contract/detail?id=${id}` }) } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | |
| | | <text class="cu-service-item__desc">电表 / 空调充值</text> |
| | | </view> |
| | | <view class="cu-service-item cu-service-item--contract" @click="go('/pages/customer/contract/list')"> |
| | | <view class="cu-service-item__icon">📄</view> |
| | | <view class="cu-service-item__icon"> |
| | | <u-icon name="file-text-fill" color="#40a9ff" size="24" /> |
| | | </view> |
| | | <text class="cu-service-item__label">查合同</text> |
| | | <text class="cu-service-item__desc">租赁合同查询</text> |
| | | </view> |
| | |
| | | } |
| | | |
| | | .cu-service-item--electric .cu-service-item__icon { background: #fff7e6; } |
| | | .cu-service-item--contract .cu-service-item__icon { background: #e8f7ef; } |
| | | .cu-service-item--contract .cu-service-item__icon { |
| | | background: linear-gradient(135deg, #e6f4ff 0%, #bae7ff 100%); |
| | | box-shadow: 0 6rpx 16rpx rgba(105, 192, 255, 0.28); |
| | | } |
| | | .cu-service-item--bill .cu-service-item__icon { background: #eef2ff; } |
| | | .cu-service-item--record .cu-service-item__icon { background: #fce8f3; } |
| | | |
| | |
| | | |
| | | .cu-list-card__icon--electric { background: linear-gradient(135deg, #fff7e6, #ffe8b3); } |
| | | .cu-list-card__icon--conditioner { background: linear-gradient(135deg, #e8f7ef, #c8f0dc); } |
| | | .cu-list-card__icon--contract { background: linear-gradient(135deg, #eef2ff, #d6e4ff); } |
| | | .cu-list-card__icon--contract { |
| | | background: linear-gradient(135deg, #e6f4ff 0%, #bae7ff 100%); |
| | | box-shadow: 0 6rpx 14rpx rgba(105, 192, 255, 0.28); |
| | | } |
| | | .cu-list-card__icon--bill { background: linear-gradient(135deg, #fce8f3, #f5d0e8); } |
| | | .cu-list-card__icon--record { background: linear-gradient(135deg, #e8f4ff, #cce5ff); } |
| | | |
| | |
| | | color: $cu-text; |
| | | } |
| | | |
| | | .cu-panel__head { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 16rpx; |
| | | padding: 24rpx 28rpx 16rpx; |
| | | } |
| | | |
| | | .cu-panel__title-text { |
| | | font-size: 28rpx; |
| | | font-weight: 600; |
| | | color: $cu-text; |
| | | } |
| | | |
| | | .cu-panel__icon { |
| | | width: 56rpx; |
| | | height: 56rpx; |
| | | border-radius: 16rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .cu-panel__icon--room { |
| | | background: linear-gradient(135deg, #ff9c6e 0%, #ff4d4f 100%); |
| | | box-shadow: 0 8rpx 20rpx rgba(255, 77, 79, 0.38); |
| | | } |
| | | |
| | | .cu-panel__icon--info { |
| | | background: linear-gradient(135deg, #69c0ff 0%, #2080f7 100%); |
| | | box-shadow: 0 8rpx 20rpx rgba(32, 128, 247, 0.38); |
| | | } |
| | | |
| | | .cu-panel__icon--file { |
| | | background: linear-gradient(135deg, #b37feb 0%, #722ed1 100%); |
| | | box-shadow: 0 8rpx 20rpx rgba(114, 46, 209, 0.38); |
| | | } |
| | | |
| | | .cu-kv__item { |
| | | display: flex; |
| | | align-items: flex-start; |
| | |
| | | color: #cf1322; |
| | | } |
| | | |
| | | .cu-bill-tip__icon-box { |
| | | width: 44rpx; |
| | | height: 44rpx; |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .cu-bill-tip--ok .cu-bill-tip__icon-box { |
| | | background: linear-gradient(135deg, #95de64 0%, #52c41a 100%); |
| | | box-shadow: 0 4rpx 12rpx rgba(82, 196, 26, 0.35); |
| | | } |
| | | |
| | | .cu-bill-tip--warn .cu-bill-tip__icon-box { |
| | | background: linear-gradient(135deg, #ffd666 0%, #fa8c16 100%); |
| | | box-shadow: 0 4rpx 12rpx rgba(250, 140, 16, 0.35); |
| | | } |
| | | |
| | | .cu-bill-tip--danger .cu-bill-tip__icon-box { |
| | | background: linear-gradient(135deg, #ff7875 0%, #f5222d 100%); |
| | | box-shadow: 0 4rpx 12rpx rgba(245, 34, 45, 0.35); |
| | | } |
| | | |
| | | .cu-bill-tip__icon { |
| | | flex-shrink: 0; |
| | | font-size: 28rpx; |
| | |
| | | gap: 16rpx; |
| | | padding: 22rpx 28rpx; |
| | | border-top: 1rpx solid #f5f6f8; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .cu-file-item__icon-box { |
| | | width: 64rpx; |
| | | height: 64rpx; |
| | | border-radius: 18rpx; |
| | | background: linear-gradient(135deg, #5cdbd3 0%, #13c2c2 100%); |
| | | box-shadow: 0 6rpx 16rpx rgba(19, 194, 194, 0.38); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .cu-file-item__icon { |
| | |
| | | padding: 16rpx 24rpx 8rpx; |
| | | } |
| | | |
| | | .cu-bill-page__toolbar { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | gap: 16rpx; |
| | | margin-bottom: 16rpx; |
| | | } |
| | | |
| | | .cu-bill-tabs--toolbar { |
| | | flex: 1; |
| | | min-width: 0; |
| | | padding: 0; |
| | | margin-bottom: 0; |
| | | gap: 12rpx; |
| | | background: transparent; |
| | | } |
| | | |
| | | .cu-bill-tabs--toolbar .cu-tab { |
| | | padding: 12rpx 20rpx; |
| | | font-size: 24rpx; |
| | | } |
| | | |
| | | .cu-bill-cost-picker { |
| | | flex-shrink: 0; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 6rpx; |
| | | padding: 12rpx 18rpx; |
| | | background: #fff; |
| | | border-radius: 999rpx; |
| | | box-shadow: $cu-shadow; |
| | | border: 2rpx solid transparent; |
| | | max-width: 220rpx; |
| | | } |
| | | |
| | | .cu-bill-cost-picker--active { |
| | | border-color: $cu-primary; |
| | | background: linear-gradient(135deg, #e8f2ff 0%, #fff 100%); |
| | | } |
| | | |
| | | .cu-bill-cost-picker--rent { |
| | | border-color: #40a9ff; |
| | | background: linear-gradient(135deg, #e6f4ff 0%, #fff 100%); |
| | | } |
| | | |
| | | .cu-bill-cost-picker--property { |
| | | border-color: #52c41a; |
| | | background: linear-gradient(135deg, #f0faf4 0%, #fff 100%); |
| | | } |
| | | |
| | | .cu-bill-cost-picker--deposit { |
| | | border-color: #fa8c16; |
| | | background: linear-gradient(135deg, #fff7e6 0%, #fff 100%); |
| | | } |
| | | |
| | | .cu-bill-cost-picker--utility { |
| | | border-color: #13c2c2; |
| | | background: linear-gradient(135deg, #e6fffb 0%, #fff 100%); |
| | | } |
| | | |
| | | .cu-bill-cost-picker--misc { |
| | | border-color: #9254de; |
| | | background: linear-gradient(135deg, #f9f0ff 0%, #fff 100%); |
| | | } |
| | | |
| | | .cu-bill-cost-picker__label { |
| | | font-size: 24rpx; |
| | | color: $cu-text-secondary; |
| | | max-width: 150rpx; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | line-height: 1.2; |
| | | } |
| | | |
| | | .cu-bill-cost-picker--active .cu-bill-cost-picker__label, |
| | | .cu-bill-cost-picker--rent .cu-bill-cost-picker__label, |
| | | .cu-bill-cost-picker--property .cu-bill-cost-picker__label, |
| | | .cu-bill-cost-picker--deposit .cu-bill-cost-picker__label, |
| | | .cu-bill-cost-picker--utility .cu-bill-cost-picker__label, |
| | | .cu-bill-cost-picker--misc .cu-bill-cost-picker__label { |
| | | color: $cu-text; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .cu-bill-cost-picker__arrow { |
| | | font-size: 22rpx; |
| | | color: $cu-text-muted; |
| | | line-height: 1; |
| | | transform: translateY(2rpx); |
| | | } |
| | | |
| | | .cu-bill-tabs { |
| | | margin-bottom: 20rpx; |
| | | margin-bottom: 16rpx; |
| | | } |
| | | |
| | | .cu-bill-summary { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12rpx; |
| | | padding: 0 8rpx 12rpx; |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | .cu-bill-summary__main { |
| | | display: flex; |
| | | align-items: baseline; |
| | | gap: 8rpx; |
| | | padding: 0 8rpx 12rpx; |
| | | } |
| | | |
| | | .cu-bill-summary__count { |
| | |
| | | .cu-bill-summary__label { |
| | | font-size: 26rpx; |
| | | color: $cu-text-muted; |
| | | line-height: 1.2; |
| | | } |
| | | |
| | | .cu-bill-summary__overdue-group { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 10rpx; |
| | | } |
| | | |
| | | .cu-bill-summary__chip { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 8rpx; |
| | | height: 48rpx; |
| | | padding: 0 16rpx; |
| | | border-radius: 999rpx; |
| | | box-sizing: border-box; |
| | | font-size: 24rpx; |
| | | line-height: 1; |
| | | } |
| | | |
| | | .cu-bill-summary__chip--stat { |
| | | color: $cu-danger; |
| | | font-weight: 600; |
| | | background: #fff1f0; |
| | | } |
| | | |
| | | .cu-bill-summary__chip--filter { |
| | | background: transparent; |
| | | border: none; |
| | | color: $cu-danger; |
| | | font-weight: 500; |
| | | padding: 0 4rpx; |
| | | } |
| | | |
| | | .cu-bill-summary__chip--filter-on { |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .cu-bill-summary__chip-text { |
| | | font-size: 24rpx; |
| | | line-height: 1; |
| | | color: inherit; |
| | | } |
| | | |
| | | .cu-bill-summary__check { |
| | | width: 28rpx; |
| | | height: 28rpx; |
| | | border: 2rpx solid rgba(255, 77, 79, 0.45); |
| | | border-radius: 6rpx; |
| | | background: #fff; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | box-sizing: border-box; |
| | | flex-shrink: 0; |
| | | transition: all 0.2s ease; |
| | | } |
| | | |
| | | .cu-bill-summary__check--on { |
| | | border-color: $cu-danger; |
| | | background: $cu-danger; |
| | | } |
| | | |
| | | .cu-bill-summary__check-icon { |
| | | font-size: 20rpx; |
| | | color: #fff; |
| | | line-height: 1; |
| | | font-weight: 700; |
| | | } |
| | | |
| | | .cu-bill-card { |
| | |
| | | margin-bottom: 24rpx; |
| | | } |
| | | |
| | | .cu-bill-card__head-main { |
| | | flex: 1; |
| | | min-width: 0; |
| | | .cu-bill-card__type-row { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | gap: 16rpx; |
| | | margin-bottom: 12rpx; |
| | | } |
| | | |
| | | .cu-bill-card__type { |
| | | display: inline-flex; |
| | | padding: 6rpx 14rpx; |
| | | background: #f3f6fc; |
| | | border-radius: 8rpx; |
| | | font-size: 22rpx; |
| | | color: $cu-primary; |
| | | font-weight: 500; |
| | | margin-bottom: 10rpx; |
| | | .cu-bill-type-tag { |
| | | padding: 10rpx 22rpx; |
| | | border-radius: 12rpx; |
| | | box-shadow: 0 4rpx 12rpx rgba(15, 35, 75, 0.08); |
| | | } |
| | | |
| | | .cu-bill-type-tag__text { |
| | | font-size: 28rpx; |
| | | font-weight: 700; |
| | | line-height: 1.2; |
| | | } |
| | | |
| | | .cu-bill-type-tag--rent { |
| | | background: linear-gradient(135deg, #69c0ff 0%, #40a9ff 100%); |
| | | } |
| | | |
| | | .cu-bill-type-tag--rent .cu-bill-type-tag__text { |
| | | color: #fff; |
| | | } |
| | | |
| | | .cu-bill-type-tag--property { |
| | | background: linear-gradient(135deg, #95de64 0%, #52c41a 100%); |
| | | } |
| | | |
| | | .cu-bill-type-tag--property .cu-bill-type-tag__text { |
| | | color: #fff; |
| | | } |
| | | |
| | | .cu-bill-type-tag--deposit { |
| | | background: linear-gradient(135deg, #ffc069 0%, #fa8c16 100%); |
| | | } |
| | | |
| | | .cu-bill-type-tag--deposit .cu-bill-type-tag__text { |
| | | color: #fff; |
| | | } |
| | | |
| | | .cu-bill-type-tag--utility { |
| | | background: linear-gradient(135deg, #5cdbd3 0%, #13c2c2 100%); |
| | | } |
| | | |
| | | .cu-bill-type-tag--utility .cu-bill-type-tag__text { |
| | | color: #fff; |
| | | } |
| | | |
| | | .cu-bill-type-tag--misc { |
| | | background: linear-gradient(135deg, #b37feb 0%, #9254de 100%); |
| | | } |
| | | |
| | | .cu-bill-type-tag--misc .cu-bill-type-tag__text { |
| | | color: #fff; |
| | | } |
| | | |
| | | .cu-bill-type-tag--default { |
| | | background: linear-gradient(135deg, #d9d9d9 0%, #8c8c8c 100%); |
| | | } |
| | | |
| | | .cu-bill-type-tag--default .cu-bill-type-tag__text { |
| | | color: #fff; |
| | | } |
| | | |
| | | .cu-bill-card__code { |
| | | display: block; |
| | | font-size: 30rpx; |
| | | font-weight: 600; |
| | | color: $cu-text; |
| | | font-size: 26rpx; |
| | | font-weight: 500; |
| | | color: $cu-text-secondary; |
| | | margin-bottom: 20rpx; |
| | | word-break: break-all; |
| | | line-height: 1.4; |
| | | } |
| | |
| | | } |
| | | |
| | | .cu-contract-bill-panel { |
| | | padding: 0 24rpx 32rpx; |
| | | padding: 24rpx 24rpx 32rpx; |
| | | } |
| | | |
| | | .cu-contract-bill-switch { |
| | |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .cu-clause-panel { |
| | | padding-bottom: 8rpx; |
| | | } |
| | | |
| | | .cu-clause-panel__head { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 16rpx; |
| | | padding: 24rpx 28rpx 12rpx; |
| | | } |
| | | |
| | | .cu-clause-panel__badge { |
| | | width: 56rpx; |
| | | height: 56rpx; |
| | | border-radius: 16rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .cu-clause-panel--zl .cu-clause-panel__badge { |
| | | background: linear-gradient(135deg, #69c0ff 0%, #1890ff 100%); |
| | | box-shadow: 0 8rpx 22rpx rgba(24, 144, 255, 0.42); |
| | | } |
| | | |
| | | .cu-clause-panel--wy .cu-clause-panel__badge { |
| | | background: linear-gradient(135deg, #95de64 0%, #389e0d 100%); |
| | | box-shadow: 0 8rpx 22rpx rgba(56, 158, 13, 0.42); |
| | | } |
| | | |
| | | .cu-clause-panel__title { |
| | | font-size: 30rpx; |
| | | font-weight: 600; |
| | | color: $cu-text; |
| | | } |
| | | |
| | | .cu-clause-summary { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 12rpx; |
| | | padding: 8rpx 24rpx 20rpx; |
| | | } |
| | | |
| | | .cu-clause-chip { |
| | | flex: 1; |
| | | min-width: calc(50% - 6rpx); |
| | | padding: 18rpx 20rpx; |
| | | border-radius: 16rpx; |
| | | background: #f8fafc; |
| | | } |
| | | |
| | | .cu-clause-panel--zl .cu-clause-chip { |
| | | background: linear-gradient(180deg, #f3f8ff 0%, #f8fafc 100%); |
| | | } |
| | | |
| | | .cu-clause-panel--wy .cu-clause-chip { |
| | | background: linear-gradient(180deg, #f0faf4 0%, #f8fafc 100%); |
| | | } |
| | | |
| | | .cu-clause-chip--wide { |
| | | flex: 1 1 100%; |
| | | min-width: 100%; |
| | | } |
| | | |
| | | .cu-clause-chip__label { |
| | | display: block; |
| | | font-size: 22rpx; |
| | | color: $cu-text-muted; |
| | | margin-bottom: 8rpx; |
| | | } |
| | | |
| | | .cu-clause-chip__value { |
| | | display: block; |
| | | font-size: 28rpx; |
| | | font-weight: 500; |
| | | color: $cu-text; |
| | | line-height: 1.4; |
| | | word-break: break-all; |
| | | } |
| | | |
| | | .cu-clause-detail-list { |
| | | padding: 0 24rpx 16rpx; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 16rpx; |
| | | } |
| | | |
| | | .cu-clause-detail-card { |
| | | padding: 4rpx; |
| | | border-radius: 18rpx; |
| | | background: linear-gradient(135deg, rgba(32, 128, 247, 0.12), rgba(32, 128, 247, 0.04)); |
| | | } |
| | | |
| | | .cu-clause-panel--wy .cu-clause-detail-card { |
| | | background: linear-gradient(135deg, rgba(47, 154, 79, 0.12), rgba(47, 154, 79, 0.04)); |
| | | } |
| | | |
| | | .cu-clause-detail-grid { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | padding: 20rpx; |
| | | border-radius: 16rpx; |
| | | background: #fff; |
| | | gap: 16rpx 12rpx; |
| | | } |
| | | |
| | | .cu-clause-detail-grid__item { |
| | | width: calc(50% - 6rpx); |
| | | padding: 16rpx 18rpx; |
| | | border-radius: 12rpx; |
| | | background: #f8fafc; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .cu-clause-panel--zl .cu-clause-detail-grid__item--highlight { |
| | | background: linear-gradient(135deg, #eef5ff 0%, #f8fbff 100%); |
| | | } |
| | | |
| | | .cu-clause-panel--wy .cu-clause-detail-grid__item--highlight { |
| | | background: linear-gradient(135deg, #edf9f0 0%, #f6fcf8 100%); |
| | | } |
| | | |
| | | .cu-clause-detail-grid__label { |
| | | display: block; |
| | | font-size: 22rpx; |
| | | color: $cu-text-muted; |
| | | margin-bottom: 8rpx; |
| | | } |
| | | |
| | | .cu-clause-detail-grid__value { |
| | | display: block; |
| | | font-size: 26rpx; |
| | | font-weight: 500; |
| | | color: $cu-text; |
| | | line-height: 1.45; |
| | | word-break: break-all; |
| | | } |
| | | |
| | | .cu-clause-panel--zl .cu-clause-detail-grid__item--highlight .cu-clause-detail-grid__value { |
| | | color: $cu-primary; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .cu-clause-panel--wy .cu-clause-detail-grid__item--highlight .cu-clause-detail-grid__value { |
| | | color: #2f9a4f; |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .cu-clause-empty { |
| | | padding: 32rpx 24rpx; |
| | | margin: 0 24rpx 8rpx; |
| | | text-align: center; |
| | | font-size: 24rpx; |
| | | color: $cu-text-muted; |
| | | background: #f8fafc; |
| | | border-radius: 16rpx; |
| | | } |
| | | |
| | | .cu-bill-detail-hero { |
| | | position: relative; |
| | | margin: 24rpx 24rpx 0; |
| | |
| | | @Data |
| | | public class CustomerBillQueryDTO { |
| | | private Integer payTab; |
| | | /** 费用类型:0租赁费 1物业费 2租赁押金 3物业押金 4水电费 5杂项费 6其他 7保证金 */ |
| | | private Integer costType; |
| | | } |
| | |
| | | import com.doumee.core.utils.Constants; |
| | | import com.doumee.core.utils.Utils; |
| | | import com.doumee.dao.business.YwContractBillMapper; |
| | | import com.doumee.dao.business.YwContractDetailMapper; |
| | | import com.doumee.dao.business.YwContractMapper; |
| | | import com.doumee.dao.business.YwContractRevenueMapper; |
| | | import com.doumee.dao.business.YwContractRoomMapper; |
| | |
| | | private YwElectricalBizService ywElectricalBizService; |
| | | @Autowired |
| | | private YwContractMapper ywContractMapper; |
| | | @Autowired |
| | | private YwContractDetailMapper ywContractDetailMapper; |
| | | @Autowired |
| | | private YwContractBillMapper ywContractBillMapper; |
| | | @Autowired |
| | |
| | | } else if (q.getPayTab() == 1) { |
| | | wrapper.eq(YwContractBill::getPayStatus, Constants.ONE); |
| | | } |
| | | } |
| | | if (q != null && q.getCostType() != null) { |
| | | wrapper.eq(YwContractBill::getCostType, q.getCostType()); |
| | | } |
| | | wrapper.orderByDesc(YwContractBill::getPlanPayDate); |
| | | IPage<YwContractBill> result = ywContractBillMapper.selectJoinPage(page, YwContractBill.class, wrapper); |
| | |
| | | applyRoomSummary(contract, roomMap.getOrDefault(contract.getId(), Collections.emptyList())); |
| | | contract.setPayTypeText(resolvePayTypeText(contract)); |
| | | contract.setFreeRentPeriod(resolveFreeRentPeriod(contract)); |
| | | initContractDetails(contract); |
| | | fillBillStatusTip(contract, loadContractBillMap(Collections.singletonList(contract.getId())) |
| | | .getOrDefault(contract.getId(), Collections.emptyList())); |
| | | if (withFiles) { |
| | |
| | | || Constants.equalsInteger(payStatus, Constants.FOUR); |
| | | } |
| | | |
| | | private void initContractDetails(YwContract contract) { |
| | | if (contract == null || contract.getId() == null) { |
| | | return; |
| | | } |
| | | contract.setZlDetailList(ywContractDetailMapper.selectJoinList(YwContractDetail.class, |
| | | new MPJLambdaWrapper<YwContractDetail>() |
| | | .selectAll(YwContractDetail.class) |
| | | .eq(YwContractDetail::getIsdeleted, Constants.ZERO) |
| | | .eq(YwContractDetail::getContractId, contract.getId()) |
| | | .in(YwContractDetail::getType, Constants.ZERO, Constants.TWO) |
| | | .orderByAsc(YwContractDetail::getSortnum))); |
| | | contract.setWyDetailList(ywContractDetailMapper.selectJoinList(YwContractDetail.class, |
| | | new MPJLambdaWrapper<YwContractDetail>() |
| | | .selectAll(YwContractDetail.class) |
| | | .eq(YwContractDetail::getIsdeleted, Constants.ZERO) |
| | | .eq(YwContractDetail::getContractId, contract.getId()) |
| | | .in(YwContractDetail::getType, Constants.ONE, Constants.THREE) |
| | | .orderByAsc(YwContractDetail::getSortnum))); |
| | | } |
| | | |
| | | private void initContractFiles(YwContract contract) { |
| | | List<Multifile> multifiles = multifileMapper.selectJoinList(Multifile.class, new MPJLambdaWrapper<Multifile>() |
| | | .selectAll(Multifile.class) |
| | |
| | | .eq(YwContractBill::getStatus, Constants.ZERO) |
| | | .eq(YwContractBill::getContractId, contractId) |
| | | .eq(YwContractBill::getBillType, billType != null ? billType : Constants.ZERO) |
| | | .orderByDesc(YwContractBill::getPlanPayDate) |
| | | .orderByDesc(YwContractBill::getId)); |
| | | enrichContractBillsForH5(bills); |
| | | return bills; |
| | |
| | | return false; |
| | | } |
| | | String customSmsId = "1"; |
| | | String extendedCode = "01"; |
| | | String extendedCode = "1"; |
| | | |
| | | SmsSingleRequest request = new SmsSingleRequest(mobile, content, customSmsId, extendedCode, ""); |
| | | ResultModel<SmsResponse> result = client.sendSingleSms(request); |