feat: 优化首页服务入口与租住业务流程
补齐首页待办跳转、押金支付引导和多类租住服务页面能力,统一租客端关键业务流程体验。
This commit is contained in:
@@ -23,16 +23,28 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-if="showPaymentStatusPanel"
|
||||
class="payment-status-panel tn-margin-bottom-lg"
|
||||
:class="`payment-status-panel--${paymentStatusType}`"
|
||||
>
|
||||
<view class="payment-status-panel__label">{{ $tr('支付状态') }}</view>
|
||||
<view class="payment-status-panel__main">
|
||||
<view class="payment-status-panel__value">{{ paymentStatusText }}</view>
|
||||
<view v-if="canViewBill" class="payment-status-panel__hint">{{ $tr('请尽快完成定金支付') }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-if="statusReminderText"
|
||||
class="status-reminder-panel tn-margin-bottom-lg"
|
||||
:class="{
|
||||
'status-reminder-panel--warning': shouldPayDeposit(detail),
|
||||
'status-reminder-panel--warning': shouldPayDeposit(detail) && !isWaitingButlerRoomAssignment(detail),
|
||||
'status-reminder-panel--info': isWaitingRoomAssignment(detail)
|
||||
}"
|
||||
>
|
||||
<view class="status-reminder-panel__icon">
|
||||
<text :class="shouldPayDeposit(detail) ? 'tn-icon-warning-fill' : 'tn-icon-time-fill'"></text>
|
||||
<text :class="shouldPayDeposit(detail) && !isWaitingButlerRoomAssignment(detail) ? 'tn-icon-warning-fill' : 'tn-icon-time-fill'"></text>
|
||||
</view>
|
||||
<view class="status-reminder-panel__content">
|
||||
<view class="status-reminder-panel__title">{{ $tr('当前进度提醒') }}</view>
|
||||
@@ -145,7 +157,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { contractDetail, orderSearchedReservationFlow, roomInspectionDetail, userDetailCancel } from '@/api/index.js'
|
||||
import { accountQueryBills, contractDetail, orderSearchedReservationFlow, roomInspectionDetail, userDetailCancel } from '@/api/index.js'
|
||||
import { getMyApplicationList, saveActiveApplicationCode, saveMyApplicationList } from '@/utils/app-state'
|
||||
import {
|
||||
RESERVATION_FLOW_STEP,
|
||||
@@ -202,6 +214,43 @@ function normalizeApplicationList(list = []) {
|
||||
})
|
||||
}
|
||||
|
||||
function resolveApplicationPaymentStatusInfo(item = {}) {
|
||||
if (!item || item.isViewingApplication) {
|
||||
return {
|
||||
key: 'exempt',
|
||||
desc: ''
|
||||
}
|
||||
}
|
||||
|
||||
const desc = String(item.payStatusDesc || item.paymentStatusDesc || '').trim()
|
||||
const status = Number(
|
||||
item.payStatus !== undefined && item.payStatus !== null
|
||||
? item.payStatus
|
||||
: item.paymentStatus
|
||||
)
|
||||
|
||||
if (status === 10 || /已支付|paid/i.test(desc)) {
|
||||
return { key: 'paid', desc }
|
||||
}
|
||||
if (/退款|refunded?/i.test(desc)) {
|
||||
return { key: 'refunded', desc }
|
||||
}
|
||||
if (/失败|failed?/i.test(desc)) {
|
||||
return { key: 'failed', desc }
|
||||
}
|
||||
if (/处理中|processing/i.test(desc)) {
|
||||
return { key: 'processing', desc }
|
||||
}
|
||||
if (status === 0 || /待支付|未支付|unpaid|pending/i.test(desc)) {
|
||||
return { key: 'pending', desc }
|
||||
}
|
||||
|
||||
return {
|
||||
key: desc ? 'processing' : 'pending',
|
||||
desc
|
||||
}
|
||||
}
|
||||
|
||||
function parseJsonStringMaybe(value) {
|
||||
if (typeof value !== 'string') {
|
||||
return value
|
||||
@@ -233,6 +282,7 @@ export default {
|
||||
watch: {
|
||||
language() {
|
||||
this.loadReservationFlow()
|
||||
this.loadPendingDepositBillState()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -278,6 +328,42 @@ export default {
|
||||
heroBadgeClass() {
|
||||
return this.isViewingApplication ? 'badge-viewing' : 'badge-stay'
|
||||
},
|
||||
showPaymentStatusPanel() {
|
||||
return !this.isViewingApplication
|
||||
},
|
||||
paymentStatusText() {
|
||||
if (this.isWaitingButlerRoomAssignment(this.detail)) {
|
||||
return this.$tr('等待管家排房')
|
||||
}
|
||||
const statusInfo = resolveApplicationPaymentStatusInfo(this.detail)
|
||||
if (this.$getLanguage() !== 'en' && statusInfo.desc) {
|
||||
return statusInfo.desc
|
||||
}
|
||||
const textMap = {
|
||||
exempt: this.$tr('无需支付'),
|
||||
paid: this.$tr('已支付'),
|
||||
refunded: this.$tr('已退款'),
|
||||
failed: this.$tr('支付失败'),
|
||||
processing: this.$tr('处理中'),
|
||||
pending: this.$tr('待支付')
|
||||
}
|
||||
return textMap[statusInfo.key] || statusInfo.desc || this.$tr('待支付')
|
||||
},
|
||||
paymentStatusType() {
|
||||
if (this.isWaitingButlerRoomAssignment(this.detail)) {
|
||||
return 'info'
|
||||
}
|
||||
const statusInfo = resolveApplicationPaymentStatusInfo(this.detail)
|
||||
const typeMap = {
|
||||
exempt: 'neutral',
|
||||
paid: 'success',
|
||||
refunded: 'neutral',
|
||||
failed: 'danger',
|
||||
processing: 'info',
|
||||
pending: 'warning'
|
||||
}
|
||||
return typeMap[statusInfo.key] || 'warning'
|
||||
},
|
||||
displayDateRange() {
|
||||
if (!this.detail.start && !this.detail.end) {
|
||||
return '--'
|
||||
@@ -329,26 +415,36 @@ export default {
|
||||
if (reservationStepText) {
|
||||
return reservationStepText
|
||||
}
|
||||
if (this.isWaitingButlerRoomAssignment(this.detail)) {
|
||||
return this.$tr('等待管家排房')
|
||||
}
|
||||
if (this.shouldPayDeposit(this.detail)) {
|
||||
return this.$tr('请支付定金')
|
||||
}
|
||||
if (this.isWaitingRoomAssignment(this.detail)) {
|
||||
return this.$tr('等待预订中心分配房间')
|
||||
return this.$tr('等待管家排房')
|
||||
}
|
||||
return ''
|
||||
},
|
||||
canViewBill() {
|
||||
return this.shouldPayDeposit(this.detail)
|
||||
&& this.hasPendingDepositBill(this.detail)
|
||||
&& !this.isViewingApplication
|
||||
&& this.detail
|
||||
&& this.detail.detailCode
|
||||
&& this.statusType === 'pending'
|
||||
},
|
||||
canCancelApplication() {
|
||||
return !!(this.detail
|
||||
&& this.detail.detailCode
|
||||
&& Number(this.detail.status) === 0
|
||||
&& (this.isViewingApplication || Number(this.detail.payStatus) !== 10))
|
||||
if (!(this.detail && this.detail.detailCode && Number(this.detail.status) === 0)) {
|
||||
return false
|
||||
}
|
||||
if (this.isViewingApplication) {
|
||||
return true
|
||||
}
|
||||
if (Number(this.detail.payStatus) === 10) {
|
||||
return false
|
||||
}
|
||||
return this.hasPendingDepositBill(this.detail)
|
||||
},
|
||||
canCancelOrder() {
|
||||
return this.canCancelApplication && !this.canceling
|
||||
@@ -360,11 +456,39 @@ export default {
|
||||
},
|
||||
onShow() {
|
||||
this.loadReservationFlow()
|
||||
this.loadPendingDepositBillState()
|
||||
},
|
||||
methods: {
|
||||
getBillNo(item = {}) {
|
||||
return item.no || item.billNo || ''
|
||||
},
|
||||
normalizeBillList(response) {
|
||||
const payload = parseJsonStringMaybe(response && response.data ? response.data : response)
|
||||
if (Array.isArray(payload)) {
|
||||
return payload.filter(item => item && typeof item === 'object')
|
||||
}
|
||||
if (payload && Array.isArray(payload.data)) {
|
||||
return payload.data.filter(item => item && typeof item === 'object')
|
||||
}
|
||||
return []
|
||||
},
|
||||
isPaidApplication(item = {}) {
|
||||
return !item.isViewingApplication && Number(item.payStatus) === 10
|
||||
},
|
||||
shouldQueryPendingDepositBill(item = {}) {
|
||||
return !item.isViewingApplication
|
||||
&& Number(item.status) === 0
|
||||
&& Number(item.payStatus) !== 10
|
||||
&& !!String(item.detailCode || this.detailCode || '').trim()
|
||||
},
|
||||
hasPendingDepositBill(item = {}) {
|
||||
return !!String(this.getBillNo(item && item.pendingDepositBill ? item.pendingDepositBill : {}) || '').trim()
|
||||
},
|
||||
isWaitingButlerRoomAssignment(item = {}) {
|
||||
return this.shouldQueryPendingDepositBill(item)
|
||||
&& !!(item && item.pendingDepositBillQueried)
|
||||
&& !this.hasPendingDepositBill(item)
|
||||
},
|
||||
shouldPayDeposit(item = {}) {
|
||||
return !item.isViewingApplication
|
||||
&& Number(item.status) === 0
|
||||
@@ -373,7 +497,7 @@ export default {
|
||||
isWaitingRoomAssignment(item = {}) {
|
||||
return !item.isViewingApplication
|
||||
&& Number(item.status) === 0
|
||||
&& Number(item.payStatus) === 10
|
||||
&& (Number(item.payStatus) === 10 || this.isWaitingButlerRoomAssignment(item))
|
||||
},
|
||||
getReservationCurrentStepValue() {
|
||||
return Number(this.detail && this.detail.reservationCurrentNode ? this.detail.reservationCurrentNode.step : 0)
|
||||
@@ -493,6 +617,50 @@ export default {
|
||||
isViewingApplication: false
|
||||
}
|
||||
},
|
||||
syncDetailToCache(detail = this.detail) {
|
||||
const list = normalizeApplicationList(getMyApplicationList())
|
||||
if (!list.length || !detail || !detail.detailCode) {
|
||||
return
|
||||
}
|
||||
saveMyApplicationList(list.map((item) => {
|
||||
if (String(item.detailCode || '') !== String(detail.detailCode || '')) {
|
||||
return item
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
...detail
|
||||
}
|
||||
}))
|
||||
},
|
||||
async loadPendingDepositBillState() {
|
||||
const currentDetail = this.detail && typeof this.detail === 'object' ? this.detail : {}
|
||||
const referenceTypeNo = String(currentDetail.detailCode || this.detailCode || '').trim()
|
||||
if (!this.shouldQueryPendingDepositBill(currentDetail) || !referenceTypeNo) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await accountQueryBills({
|
||||
referenceTypeNo,
|
||||
status: 10,
|
||||
paymentStatus: 0,
|
||||
ProviderType: 'WechatMiniProgram'
|
||||
}, this.language)
|
||||
if (referenceTypeNo !== String(this.detail && this.detail.detailCode || this.detailCode || '').trim()) {
|
||||
return
|
||||
}
|
||||
const billList = this.normalizeBillList(response)
|
||||
const nextDetail = {
|
||||
...this.detail,
|
||||
pendingDepositBill: Array.isArray(billList) && billList.length ? billList[0] : null,
|
||||
pendingDepositBillQueried: true
|
||||
}
|
||||
this.detail = nextDetail
|
||||
this.syncDetailToCache(nextDetail)
|
||||
} catch (error) {
|
||||
console.error('load pending deposit bill failed', error)
|
||||
}
|
||||
},
|
||||
async loadReservationFlow() {
|
||||
if (!this.detail || this.detail.isViewingApplication) {
|
||||
return
|
||||
@@ -782,6 +950,85 @@ export default {
|
||||
border: 1px solid rgba(59, 130, 246, 0.14);
|
||||
}
|
||||
|
||||
.payment-status-panel {
|
||||
padding: 24rpx 28rpx;
|
||||
border-radius: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 24rpx;
|
||||
border: 1rpx solid transparent;
|
||||
}
|
||||
|
||||
.payment-status-panel__label {
|
||||
flex-shrink: 0;
|
||||
font-size: 24rpx;
|
||||
color: #64748B;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.payment-status-panel__main {
|
||||
min-width: 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.payment-status-panel__value {
|
||||
font-size: 34rpx;
|
||||
line-height: 1.2;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.payment-status-panel__hint {
|
||||
margin-top: 8rpx;
|
||||
font-size: 22rpx;
|
||||
color: #9A3412;
|
||||
}
|
||||
|
||||
.payment-status-panel--warning {
|
||||
background: linear-gradient(180deg, rgba(255, 247, 237, 0.96) 0%, rgba(255, 251, 245, 0.98) 100%);
|
||||
border-color: rgba(249, 115, 22, 0.16);
|
||||
}
|
||||
|
||||
.payment-status-panel--warning .payment-status-panel__value {
|
||||
color: #C2410C;
|
||||
}
|
||||
|
||||
.payment-status-panel--success {
|
||||
background: linear-gradient(180deg, rgba(236, 253, 245, 0.96) 0%, rgba(240, 253, 250, 0.98) 100%);
|
||||
border-color: rgba(16, 185, 129, 0.16);
|
||||
}
|
||||
|
||||
.payment-status-panel--success .payment-status-panel__value {
|
||||
color: #047857;
|
||||
}
|
||||
|
||||
.payment-status-panel--info {
|
||||
background: linear-gradient(180deg, rgba(239, 246, 255, 0.96) 0%, rgba(248, 250, 252, 0.98) 100%);
|
||||
border-color: rgba(59, 130, 246, 0.14);
|
||||
}
|
||||
|
||||
.payment-status-panel--info .payment-status-panel__value {
|
||||
color: #1D4ED8;
|
||||
}
|
||||
|
||||
.payment-status-panel--danger {
|
||||
background: linear-gradient(180deg, rgba(254, 242, 242, 0.96) 0%, rgba(255, 245, 245, 0.98) 100%);
|
||||
border-color: rgba(239, 68, 68, 0.16);
|
||||
}
|
||||
|
||||
.payment-status-panel--danger .payment-status-panel__value {
|
||||
color: #B91C1C;
|
||||
}
|
||||
|
||||
.payment-status-panel--neutral {
|
||||
background: linear-gradient(180deg, rgba(241, 245, 249, 0.96) 0%, rgba(248, 250, 252, 0.98) 100%);
|
||||
border-color: rgba(148, 163, 184, 0.16);
|
||||
}
|
||||
|
||||
.payment-status-panel--neutral .payment-status-panel__value {
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.status-reminder-panel__icon {
|
||||
width: 68rpx;
|
||||
height: 68rpx;
|
||||
|
||||
Reference in New Issue
Block a user