feat: 新增工单中心、房间巡查、失物招领等业务模块
- 新增工单中心(ticket_center)与工单工作台(ticket_workbench)页面 - 新增房间巡查(room_inspection)与失物招领发布(lost_found_publish)页面 - 预约看样流程新增房型/楼栋/楼层选择器组件 - 新增工具模块: booking-entry、privacy、reservation-flow - api/index.js 补充楼层列表、看样提交等接口 - locales/messages.js 补充多语言文案 - manifest.json 开启微信小程序隐私检查 - 完善登录、注册、申请详情、账单等相关页面交互
This commit is contained in:
@@ -114,14 +114,28 @@
|
||||
v-if="canCancelOrder"
|
||||
class="danger-btn"
|
||||
:class="{ 'danger-btn--loading': canceling }"
|
||||
@tap.stop="handleCancelOrder"
|
||||
@click.stop="handleCancelOrder"
|
||||
>
|
||||
<text class="tn-text-bold">{{ canceling ? t('cancelOrderLoading') : t('cancelOrder') }}</text>
|
||||
</view>
|
||||
<view
|
||||
v-if="canViewBill"
|
||||
v-if="currentStepActionText"
|
||||
class="action-btn"
|
||||
@tap.stop="goToBillPage"
|
||||
@click.stop="handleCurrentStepAction"
|
||||
>
|
||||
<text class="tn-text-bold">{{ currentStepActionText }}</text>
|
||||
</view>
|
||||
<view
|
||||
v-else-if="isReservationCheckinProcessing()"
|
||||
class="action-btn action-btn--ghost"
|
||||
@click.stop="refreshCurrentPage"
|
||||
>
|
||||
<text class="tn-text-bold">{{ t('refreshAction') }}</text>
|
||||
</view>
|
||||
<view
|
||||
v-else-if="canViewBill"
|
||||
class="action-btn"
|
||||
@click.stop="goToBillPage"
|
||||
>
|
||||
<text class="tn-text-bold">{{ t('viewBill') }}</text>
|
||||
</view>
|
||||
@@ -131,8 +145,48 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { userDetailCancel } from '@/api/index.js'
|
||||
import { contractDetail, orderSearchedReservationFlow, roomInspectionDetail, userDetailCancel } from '@/api/index.js'
|
||||
import { getMyApplicationList, saveActiveApplicationCode, saveMyApplicationList } from '@/utils/app-state'
|
||||
import {
|
||||
RESERVATION_FLOW_STEP,
|
||||
attachReservationFlow,
|
||||
buildReservationFlowMap,
|
||||
getReservationCurrentActionText,
|
||||
getReservationCurrentStepText,
|
||||
getReservationOrderNo,
|
||||
parseReservationFlowResponse
|
||||
} from '@/utils/reservation-flow'
|
||||
|
||||
const VIEWING_APPLICATION_PATTERN = /预约看房申请|看房时间[::]|意向房型[::]|意向区域[::]|viewing request|viewing time[::]|preferred room type[::]|preferred area[::]/i
|
||||
|
||||
function isViewingApplicationRecord(item = {}) {
|
||||
if (!item || typeof item !== 'object') {
|
||||
return false
|
||||
}
|
||||
|
||||
const typeText = [
|
||||
item.businessType,
|
||||
item.applicationType,
|
||||
item.applicationTypeDesc,
|
||||
item.formType,
|
||||
item.bizType,
|
||||
item.type,
|
||||
item.typeDesc
|
||||
]
|
||||
.filter(value => value !== null && typeof value !== 'undefined' && String(value).trim())
|
||||
.join(' ')
|
||||
|
||||
if (/viewing|看房|带看/i.test(typeText)) {
|
||||
return true
|
||||
}
|
||||
|
||||
if ([item.appointmentViewingTime, item.viewingTime, item.bookViewingTime].some(value => String(value || '').trim())) {
|
||||
return true
|
||||
}
|
||||
|
||||
const remark = item && item.remark ? String(item.remark) : ''
|
||||
return VIEWING_APPLICATION_PATTERN.test(remark)
|
||||
}
|
||||
|
||||
function normalizeApplicationList(list = []) {
|
||||
if (!Array.isArray(list)) {
|
||||
@@ -141,15 +195,31 @@ function normalizeApplicationList(list = []) {
|
||||
return list
|
||||
.filter(item => item && typeof item === 'object')
|
||||
.map((item) => {
|
||||
const remark = item && item.remark ? String(item.remark) : ''
|
||||
const isViewingApplication = /预约看房申请|看房时间[::]|意向房型[::]|意向区域[::]|viewing request|viewing time[::]|preferred room type[::]|preferred area[::]/i.test(remark)
|
||||
return {
|
||||
...item,
|
||||
isViewingApplication
|
||||
isViewingApplication: isViewingApplicationRecord(item)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function parseJsonStringMaybe(value) {
|
||||
if (typeof value !== 'string') {
|
||||
return value
|
||||
}
|
||||
const text = value.trim()
|
||||
if (!text) {
|
||||
return value
|
||||
}
|
||||
if (!((text.startsWith('{') && text.endsWith('}')) || (text.startsWith('[') && text.endsWith(']')))) {
|
||||
return value
|
||||
}
|
||||
try {
|
||||
return JSON.parse(text)
|
||||
} catch (error) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
zh: {
|
||||
@@ -186,6 +256,9 @@ export default {
|
||||
emptyRemark: '暂无补充说明',
|
||||
codeCopied: '申请编号已复制',
|
||||
missingBillCode: '申请单编号缺失',
|
||||
missingContractNo: '合同编号缺失',
|
||||
checkinPendingHint: '宿管正在办理入住,请耐心等待',
|
||||
refreshAction: '刷新',
|
||||
cancelOrder: '取消订单',
|
||||
cancelOrderLoading: '取消中...',
|
||||
cancelOrderTitle: '取消订单',
|
||||
@@ -235,6 +308,9 @@ export default {
|
||||
emptyRemark: 'No additional remarks',
|
||||
codeCopied: 'Application number copied',
|
||||
missingBillCode: 'Missing application bill number',
|
||||
missingContractNo: 'Missing contract number',
|
||||
checkinPendingHint: 'Move-in is being processed, please wait',
|
||||
refreshAction: 'Refresh',
|
||||
cancelOrder: 'Cancel Order',
|
||||
cancelOrderLoading: 'Canceling...',
|
||||
cancelOrderTitle: 'Cancel Order',
|
||||
@@ -255,7 +331,14 @@ export default {
|
||||
return {
|
||||
detailCode: '',
|
||||
detail: {},
|
||||
canceling: false
|
||||
canceling: false,
|
||||
reservationContractDetail: null,
|
||||
reservationInspectionDetail: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
language() {
|
||||
this.loadReservationFlow()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -323,7 +406,35 @@ export default {
|
||||
}
|
||||
return this.parseViewingRemark()
|
||||
},
|
||||
currentStepActionText() {
|
||||
if (this.isViewingApplication) {
|
||||
return ''
|
||||
}
|
||||
if (this.isReservationInspectionPendingReview()) {
|
||||
return ''
|
||||
}
|
||||
if (this.isReservationContractAwaitingReview()) {
|
||||
return ''
|
||||
}
|
||||
if (this.isReservationCheckinProcessing()) {
|
||||
return ''
|
||||
}
|
||||
return getReservationCurrentActionText(this.detail, this.language)
|
||||
},
|
||||
statusReminderText() {
|
||||
if (this.isReservationInspectionPendingReview()) {
|
||||
return this.getReservationInspectionStatusText()
|
||||
}
|
||||
if (this.isReservationContractAwaitingReview()) {
|
||||
return this.getReservationContractStatusText()
|
||||
}
|
||||
if (this.isReservationCheckinProcessing()) {
|
||||
return this.t('checkinPendingHint')
|
||||
}
|
||||
const reservationStepText = getReservationCurrentStepText(this.detail, this.language)
|
||||
if (reservationStepText) {
|
||||
return reservationStepText
|
||||
}
|
||||
if (this.shouldPayDeposit(this.detail)) {
|
||||
return this.t('statusPendingPayment')
|
||||
}
|
||||
@@ -351,6 +462,9 @@ export default {
|
||||
this.detailCode = options.code || ''
|
||||
this.restoreDetail(options.payload || '')
|
||||
},
|
||||
onShow() {
|
||||
this.loadReservationFlow()
|
||||
},
|
||||
methods: {
|
||||
t(key, params) {
|
||||
return this.$tm('pkgService.application_detail.application_detail', key, params)
|
||||
@@ -368,6 +482,106 @@ export default {
|
||||
&& Number(item.status) === 0
|
||||
&& Number(item.payStatus) === 10
|
||||
},
|
||||
getReservationCurrentStepValue() {
|
||||
return Number(this.detail && this.detail.reservationCurrentNode ? this.detail.reservationCurrentNode.step : 0)
|
||||
},
|
||||
isReservationCheckinProcessing() {
|
||||
return this.getReservationCurrentStepValue() === RESERVATION_FLOW_STEP.CHECKIN
|
||||
},
|
||||
getReservationInspectionNo() {
|
||||
const currentNode = this.detail && this.detail.reservationCurrentNode ? this.detail.reservationCurrentNode : {}
|
||||
if (Number(currentNode.step || 0) !== RESERVATION_FLOW_STEP.INSPECTION) {
|
||||
return ''
|
||||
}
|
||||
return String(currentNode.refNo || '').trim()
|
||||
},
|
||||
getReservationContractNo() {
|
||||
const currentNode = this.detail && this.detail.reservationCurrentNode ? this.detail.reservationCurrentNode : {}
|
||||
if (Number(currentNode.step || 0) !== RESERVATION_FLOW_STEP.CONTRACT) {
|
||||
return ''
|
||||
}
|
||||
return String(currentNode.refNo || '').trim()
|
||||
},
|
||||
normalizeInspectionDetail(response) {
|
||||
const payload = parseJsonStringMaybe(response && response.data ? response.data : response)
|
||||
if (payload && typeof payload === 'object' && !Array.isArray(payload)) {
|
||||
return payload
|
||||
}
|
||||
if (payload && payload.data && typeof payload.data === 'object' && !Array.isArray(payload.data)) {
|
||||
return payload.data
|
||||
}
|
||||
return {}
|
||||
},
|
||||
normalizeContractDetail(response) {
|
||||
const payload = parseJsonStringMaybe(response && response.data ? response.data : response)
|
||||
if (payload && typeof payload === 'object' && !Array.isArray(payload)) {
|
||||
return payload
|
||||
}
|
||||
if (payload && payload.data && typeof payload.data === 'object' && !Array.isArray(payload.data)) {
|
||||
return payload.data
|
||||
}
|
||||
return {}
|
||||
},
|
||||
isReservationInspectionPendingReview() {
|
||||
if (this.getReservationCurrentStepValue() !== RESERVATION_FLOW_STEP.INSPECTION) {
|
||||
return false
|
||||
}
|
||||
const detail = this.reservationInspectionDetail || {}
|
||||
return Number(detail.status) === 2
|
||||
},
|
||||
getReservationInspectionStatusText() {
|
||||
return this.t('inspectionPendingReviewHint')
|
||||
},
|
||||
isReservationContractAwaitingReview() {
|
||||
if (this.getReservationCurrentStepValue() !== RESERVATION_FLOW_STEP.CONTRACT) {
|
||||
return false
|
||||
}
|
||||
const detail = this.reservationContractDetail || {}
|
||||
const queryStatus = Number(detail.queryStatus)
|
||||
const approvalStatus = Number(detail.approvalStatus)
|
||||
const hasSigned = !!String(detail.signTime || detail.signImageUrl || '').trim()
|
||||
return queryStatus === 1 || (hasSigned && approvalStatus === 0)
|
||||
},
|
||||
getReservationContractStatusText() {
|
||||
const detail = this.reservationContractDetail || {}
|
||||
return detail.queryStatusDesc || detail.approvalStatusDesc || detail.statusDesc || ''
|
||||
},
|
||||
async ensureReservationInspectionDetail() {
|
||||
const inspectionNo = this.getReservationInspectionNo()
|
||||
if (!inspectionNo) {
|
||||
this.reservationInspectionDetail = null
|
||||
return null
|
||||
}
|
||||
try {
|
||||
const detail = this.normalizeInspectionDetail(await roomInspectionDetail({
|
||||
inspectionNo
|
||||
}, this.language))
|
||||
this.reservationInspectionDetail = detail && Object.keys(detail).length ? detail : null
|
||||
return this.reservationInspectionDetail
|
||||
} catch (error) {
|
||||
console.error('load inspection detail failed', error)
|
||||
this.reservationInspectionDetail = null
|
||||
return null
|
||||
}
|
||||
},
|
||||
async ensureReservationContractDetail() {
|
||||
const contractNo = this.getReservationContractNo()
|
||||
if (!contractNo) {
|
||||
this.reservationContractDetail = null
|
||||
return null
|
||||
}
|
||||
try {
|
||||
const detail = this.normalizeContractDetail(await contractDetail({
|
||||
contractNo
|
||||
}, this.language))
|
||||
this.reservationContractDetail = detail && Object.keys(detail).length ? detail : null
|
||||
return this.reservationContractDetail
|
||||
} catch (error) {
|
||||
console.error('load contract detail failed', error)
|
||||
this.reservationContractDetail = null
|
||||
return null
|
||||
}
|
||||
},
|
||||
restoreDetail(payload = '') {
|
||||
if (payload) {
|
||||
try {
|
||||
@@ -386,6 +600,46 @@ export default {
|
||||
isViewingApplication: false
|
||||
}
|
||||
},
|
||||
async loadReservationFlow() {
|
||||
if (!this.detail || this.detail.isViewingApplication) {
|
||||
return
|
||||
}
|
||||
|
||||
const orderNo = getReservationOrderNo(this.detail)
|
||||
if (!orderNo) {
|
||||
this.detail = attachReservationFlow(this.detail, {})
|
||||
this.reservationInspectionDetail = null
|
||||
this.reservationContractDetail = null
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await orderSearchedReservationFlow({
|
||||
orderNos: [orderNo]
|
||||
}, this.language)
|
||||
const flowMap = buildReservationFlowMap(parseReservationFlowResponse(response))
|
||||
this.detail = attachReservationFlow(this.detail, flowMap)
|
||||
await Promise.all([
|
||||
this.ensureReservationInspectionDetail(),
|
||||
this.ensureReservationContractDetail()
|
||||
])
|
||||
|
||||
const list = normalizeApplicationList(getMyApplicationList())
|
||||
if (list.length) {
|
||||
saveMyApplicationList(list.map((item) => {
|
||||
if (String(item.detailCode || '') !== String(this.detail.detailCode || '')) {
|
||||
return item
|
||||
}
|
||||
return this.detail
|
||||
}))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('load reservation flow failed', error)
|
||||
this.detail = attachReservationFlow(this.detail, {})
|
||||
this.reservationInspectionDetail = null
|
||||
this.reservationContractDetail = null
|
||||
}
|
||||
},
|
||||
parseViewingRemark() {
|
||||
const lines = this.remarkLines
|
||||
const targets = [
|
||||
@@ -537,6 +791,54 @@ export default {
|
||||
uni.navigateTo({
|
||||
url: `/pkgService/utility_recharge/utility_recharge?referenceTypeNo=${encodeURIComponent(this.detail.detailCode)}`
|
||||
})
|
||||
},
|
||||
refreshCurrentPage() {
|
||||
this.loadReservationFlow()
|
||||
},
|
||||
handleCurrentStepAction() {
|
||||
const step = this.getReservationCurrentStepValue()
|
||||
|
||||
if (step === RESERVATION_FLOW_STEP.BILL) {
|
||||
this.goToBillPage()
|
||||
return
|
||||
}
|
||||
|
||||
if (step === RESERVATION_FLOW_STEP.CONTRACT) {
|
||||
if (this.isReservationContractAwaitingReview()) {
|
||||
return
|
||||
}
|
||||
const contractNo = String(this.detail && this.detail.reservationCurrentNode && this.detail.reservationCurrentNode.refNo || '').trim()
|
||||
if (!contractNo) {
|
||||
uni.showToast({
|
||||
title: this.t('missingContractNo'),
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pkgService/contract_sign/contract_sign?no=${encodeURIComponent(contractNo)}`
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (step === RESERVATION_FLOW_STEP.CHECKIN) {
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index?pageId=stay-service'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (step === RESERVATION_FLOW_STEP.INSPECTION) {
|
||||
if (this.isReservationInspectionPendingReview()) {
|
||||
return
|
||||
}
|
||||
const orderNo = getReservationOrderNo(this.detail)
|
||||
uni.navigateTo({
|
||||
url: orderNo
|
||||
? `/pkgService/room_inspection/room_inspection?orderNo=${encodeURIComponent(orderNo)}`
|
||||
: '/pkgService/room_inspection/room_inspection'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -784,4 +1086,11 @@ export default {
|
||||
color: #FFFFFF;
|
||||
box-shadow: 0 12rpx 32rpx rgba($brand-primary, 0.25);
|
||||
}
|
||||
|
||||
.action-btn--ghost {
|
||||
background: rgba($brand-primary, 0.08);
|
||||
color: $brand-primary;
|
||||
border: 1rpx solid rgba($brand-primary, 0.18);
|
||||
box-shadow: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user