From 445bd850f48ade06e2396ba6acc7885fdd8d3846 Mon Sep 17 00:00:00 2001
From: liangjian <1394459087@qq.com>
Date: Fri, 24 Jul 2026 09:35:46 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=B7=A5=E5=8D=95?=
=?UTF-8?q?=E4=B8=AD=E5=BF=83=E3=80=81=E6=88=BF=E9=97=B4=E5=B7=A1=E6=9F=A5?=
=?UTF-8?q?=E3=80=81=E5=A4=B1=E7=89=A9=E6=8B=9B=E9=A2=86=E7=AD=89=E4=B8=9A?=
=?UTF-8?q?=E5=8A=A1=E6=A8=A1=E5=9D=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增工单中心(ticket_center)与工单工作台(ticket_workbench)页面
- 新增房间巡查(room_inspection)与失物招领发布(lost_found_publish)页面
- 预约看样流程新增房型/楼栋/楼层选择器组件
- 新增工具模块: booking-entry、privacy、reservation-flow
- api/index.js 补充楼层列表、看样提交等接口
- locales/messages.js 补充多语言文案
- manifest.json 开启微信小程序隐私检查
- 完善登录、注册、申请详情、账单等相关页面交互
---
api/index.js | 269 ++
config.js | 2 +-
locales/messages.js | 220 +-
manifest.json | 8 +-
pages.json | 24 +
pages/index/component/MyApplicationTab.vue | 1376 +++++++++-
pages/index/component/ProfileCenterTab.vue | 9 +-
pages/index/component/StayServiceTab.vue | 336 ++-
pages/index/index.vue | 319 ++-
pkgAccount/login/login.vue | 19 +-
pkgAccount/register/register.vue | 434 +++-
pkgBooking/book/book.vue | 33 +
pkgBooking/book_viewing/book_viewing.vue | 201 +-
.../components/RoomTypeSelector.vue | 363 +++
.../store_detail/components/RoomList.vue | 19 +-
.../store_detail/components/StoreInfo.vue | 56 +-
.../application_detail/application_detail.vue | 325 ++-
.../CurrentApplicationBillPanel.vue | 33 +-
pkgService/contract_sign/contract_sign.vue | 5 +-
.../lost_found_publish/lost_found_publish.vue | 554 ++++
pkgService/my_application/my_application.vue | 88 +-
.../room_inspection/room_inspection.vue | 1130 +++++++++
.../components/BuildingSelector.vue | 314 +++
.../components/FloorSelector.vue | 308 +++
.../components/RoomTypeSelector.vue | 52 +-
pkgService/submit_request/submit_request.vue | 123 +-
pkgService/ticket_center/ticket_center.vue | 211 ++
.../ticket_workbench/ticket_workbench.vue | 2252 +++++++++++++++++
store/index.js | 43 +-
utils/booking-entry.js | 36 +
utils/enum-store.js | 115 +-
utils/privacy.js | 86 +
utils/request.js | 30 +-
utils/reservation-flow.js | 176 ++
utils/select-room-normalizers.js | 5 +
utils/session-sync.js | 47 +-
36 files changed, 9281 insertions(+), 340 deletions(-)
create mode 100644 pkgBooking/book_viewing/components/RoomTypeSelector.vue
create mode 100644 pkgService/lost_found_publish/lost_found_publish.vue
create mode 100644 pkgService/room_inspection/room_inspection.vue
create mode 100644 pkgService/submit_request/components/BuildingSelector.vue
create mode 100644 pkgService/submit_request/components/FloorSelector.vue
create mode 100644 pkgService/ticket_center/ticket_center.vue
create mode 100644 pkgService/ticket_workbench/ticket_workbench.vue
create mode 100644 utils/booking-entry.js
create mode 100644 utils/privacy.js
create mode 100644 utils/reservation-flow.js
diff --git a/api/index.js b/api/index.js
index 32514c5..5741b71 100644
--- a/api/index.js
+++ b/api/index.js
@@ -67,6 +67,18 @@ function buildRequestCacheKey(prefix, params = {}, language) {
return `${prefix}:${language}:${JSON.stringify(normalizeRequestCacheValue(params || {}))}`
}
+function createJsonPatchRequest(url, data = {}, language) {
+ return request({
+ url,
+ method: 'POST',
+ data,
+ header: {
+ 'Content-Type': 'application/json-patch+json',
+ 'X-Requested-Language': resolveLanguageHeader(language)
+ }
+ })
+}
+
const notificationPageRequestCache = new Map()
const notificationPageResponseCache = new Map()
const NOTIFICATION_PAGE_CACHE_TTL = 1200
@@ -121,6 +133,17 @@ export function getEnums(params = {}, language) {
})
}
+export function getFloorList(params = {}, language) {
+ return request({
+ url: '/floor/list',
+ method: 'GET',
+ params,
+ header: {
+ 'X-Requested-Language': resolveLanguageHeader(language)
+ }
+ })
+}
+
// --- 用户与登录 ---
export function usersLogin(data = {}, language) {
return request({
@@ -169,6 +192,18 @@ export function submitApplicationForm(data = {}, language) {
})
}
+export function submitViewingApplicationForm(data = {}, language) {
+ return request({
+ url: '/applicationform/viewing/submit',
+ method: 'POST',
+ data,
+ header: {
+ 'Content-Type': 'application/json-patch+json',
+ 'X-Requested-Language': resolveLanguageHeader(language)
+ }
+ })
+}
+
export function registrationApplicationMePage(params = {}, language) {
return request({
url: '/applicationform/registration/me/page',
@@ -202,6 +237,110 @@ export function applicationFormMePage(params = {}, language) {
})
}
+// --- 工单 ---
+export function ticketOrderPage(params = {}, language) {
+ return request({
+ url: '/ticketorder/page',
+ method: 'GET',
+ params,
+ header: {
+ 'X-Requested-Language': resolveLanguageHeader(language)
+ }
+ })
+}
+
+export function ticketOrderMyPage(params = {}, language) {
+ return request({
+ url: '/ticketorder/my/page',
+ method: 'GET',
+ params,
+ header: {
+ 'X-Requested-Language': resolveLanguageHeader(language)
+ }
+ })
+}
+
+export function ticketOrderDetail(flowNo, language) {
+ return request({
+ url: `/ticketorder/${flowNo}/flows`,
+ method: 'GET',
+ header: {
+ 'X-Requested-Language': resolveLanguageHeader(language)
+ }
+ })
+}
+
+export function ticketOrderFlows(orderNo, language) {
+ return request({
+ url: `/ticketorder/${orderNo}/flows`,
+ method: 'GET',
+ header: {
+ 'X-Requested-Language': resolveLanguageHeader(language)
+ }
+ })
+}
+
+export function ticketOrderTransitionOptions(flowNo, language) {
+ return request({
+ url: `/ticketorder/${flowNo}/transition-options`,
+ method: 'GET',
+ header: {
+ 'X-Requested-Language': resolveLanguageHeader(language)
+ }
+ })
+}
+
+export function ticketFlowTransitionStepRuleSelectTypeItems(params = {}, language) {
+ return request({
+ url: '/ticketflowtransitionsteprule/selecttypeitems',
+ method: 'GET',
+ params,
+ header: {
+ 'X-Requested-Language': resolveLanguageHeader(language)
+ }
+ })
+}
+
+export function ticketOrderFixonhold(data = {}, language) {
+ return createJsonPatchRequest('/ticketorder/fixonhold', data, language)
+}
+
+export function ticketOrderTransit(data = {}, language) {
+ return createJsonPatchRequest('/ticketorder/transit', data, language)
+}
+
+export function ticketOrderSubmitChangeroom(data = {}, language) {
+ return createJsonPatchRequest('/ticketordersubmit/changeroom', data, language)
+}
+
+export function ticketOrderSubmitCheckout(data = {}, language) {
+ return createJsonPatchRequest('/ticketordersubmit/checkout', data, language)
+}
+
+export function ticketOrderSubmitRepair(data = {}, language) {
+ return createJsonPatchRequest('/ticketordersubmit/repair', data, language)
+}
+
+export function ticketOrderSubmitSuggestion(data = {}, language) {
+ return createJsonPatchRequest('/ticketordersubmit/suggestion', data, language)
+}
+
+export function ticketOrderSubmitRenewal(data = {}, language) {
+ return createJsonPatchRequest('/ticketordersubmit/renewal', data, language)
+}
+
+export function ticketOrderSubmitWaterdelivery(data = {}, language) {
+ return createJsonPatchRequest('/ticketordersubmit/waterdelivery', data, language)
+}
+
+export function ticketOrderSubmitCleaning(data = {}, language) {
+ return createJsonPatchRequest('/ticketordersubmit/cleaning', data, language)
+}
+
+export function lostAndFoundPublish(data = {}, language) {
+ return createJsonPatchRequest('/lostandfound/publish', data, language)
+}
+
// --- 订单与入住 ---
export function orderSearchedHistoryPage(params = {}, language) {
return request({
@@ -225,6 +364,17 @@ export function orderSearchedMePage(params = {}, language) {
})
}
+export function orderSearchedMyPage(params = {}, language) {
+ return request({
+ url: '/order/searched/me/page',
+ method: 'GET',
+ params: params || {},
+ header: {
+ 'X-Requested-Language': resolveLanguageHeader(language)
+ }
+ })
+}
+
export function orderSearchedMeHistoryPage(params = {}, language) {
return request({
url: '/order/searched/me/history/page',
@@ -268,6 +418,64 @@ export function orderSearchedReservationPage(params = {}, language) {
})
}
+export function orderSearchedReservationFlow(data = {}, language) {
+ return request({
+ url: '/order/searched/reservation/flow',
+ method: 'POST',
+ data,
+ header: {
+ 'Content-Type': 'application/json-patch+json',
+ 'X-Requested-Language': resolveLanguageHeader(language)
+ }
+ })
+}
+
+export function roomInspectionMyPage(params = {}, language) {
+ return request({
+ url: '/roominspection/my/page',
+ method: 'GET',
+ params: params || {},
+ header: {
+ 'X-Requested-Language': resolveLanguageHeader(language)
+ }
+ })
+}
+
+export function roomInspectionDetail(params = {}, language) {
+ return request({
+ url: '/roominspection/detail',
+ method: 'GET',
+ params: params || {},
+ header: {
+ 'X-Requested-Language': resolveLanguageHeader(language)
+ }
+ })
+}
+
+export function roomInspectionUpdateItems(data = {}, language) {
+ return request({
+ url: '/roominspection/update/items',
+ method: 'POST',
+ data,
+ header: {
+ 'Content-Type': 'application/json-patch+json',
+ 'X-Requested-Language': resolveLanguageHeader(language)
+ }
+ })
+}
+
+export function roomInspectionSubmit(data = {}, language) {
+ return request({
+ url: '/roominspection/submit',
+ method: 'POST',
+ data,
+ header: {
+ 'Content-Type': 'application/json-patch+json',
+ 'X-Requested-Language': resolveLanguageHeader(language)
+ }
+ })
+}
+
export function checkRecordByChecked(params = {}, language) {
const query = params || {}
const orderNo = String(query.orderNo || '').trim()
@@ -300,6 +508,24 @@ export function accountQueryBills(params = {}, language) {
})
}
+export function accountQueryBill(params = {}, language) {
+ const query = params || {}
+ const billNo = String(query.billNo || query.BillNo || '').trim()
+ if (!billNo) {
+ return Promise.reject(new Error('账单号不能为空'))
+ }
+ return request({
+ url: '/accountquery/bill',
+ method: 'GET',
+ params: {
+ billNo
+ },
+ header: {
+ 'X-Requested-Language': resolveLanguageHeader(language)
+ }
+ })
+}
+
export function paymentBillPay(data = {}, language) {
const payload = data || {}
return request({
@@ -385,6 +611,17 @@ export function getMyEffectiveContracts(params = {}, language) {
})
}
+export function contractDetail(params = {}, language) {
+ return request({
+ url: '/contract/detail',
+ method: 'GET',
+ params,
+ header: {
+ 'X-Requested-Language': resolveLanguageHeader(language)
+ }
+ })
+}
+
export function contractPreview(data = {}, language) {
return request({
url: '/contract/preview',
@@ -484,6 +721,38 @@ export function facilityManagers(params = {}, language) {
})
}
+// --- 宿管与管理员联系人 ---
+export function myDormitoryManagers(language) {
+ return request({
+ url: '/usercampus/my-dormitory-managers',
+ method: 'GET',
+ header: {
+ 'X-Requested-Language': resolveLanguageHeader(language)
+ }
+ })
+}
+
+export function myCounselor(language) {
+ return request({
+ url: '/usercampus/my-counselor',
+ method: 'GET',
+ header: {
+ 'X-Requested-Language': resolveLanguageHeader(language)
+ }
+ })
+}
+
+export function buildingPagination(params = {}, language) {
+ return request({
+ url: '/building/pagination',
+ method: 'GET',
+ params,
+ header: {
+ 'X-Requested-Language': resolveLanguageHeader(language)
+ }
+ })
+}
+
export function roomtypeList(params = {}, language) {
return request({
url: '/roomtype/list',
diff --git a/config.js b/config.js
index d788dc3..17bc204 100644
--- a/config.js
+++ b/config.js
@@ -1,6 +1,6 @@
export const config = {
// 根据不同环境维护接口基准地址
- // baseURL: 'http://192.168.199.115:5003/api',
+ // baseURL: 'http://192.168.199.182:5003/api',
// baseURL: 'http://192.168.199.124:5111/admin/api',
// baseURL: 'https://api.xhct.yaens.com/admin/api',
baseURL: 'https://apartment.lvjuwuye.cn/admin/api',
diff --git a/locales/messages.js b/locales/messages.js
index 9ed18a6..43e6e02 100644
--- a/locales/messages.js
+++ b/locales/messages.js
@@ -25,8 +25,12 @@ const messages = {
"pkgService/message_detail/message_detail": "Message Detail",
"pkgService/my_application/my_application": "My Applications",
"pkgService/my_contract/my_contract": "My Contracts",
+ "pkgService/room_inspection/room_inspection": "Inspection",
"pkgService/service_stub/service_stub": "Service Center",
"pkgService/smart_control/smart_control": "Smart Control",
+ "pkgService/ticket_center/ticket_center": "Work Orders",
+ "pkgService/ticket_workbench/ticket_workbench": "Work Order",
+ "pkgService/lost_found_publish/lost_found_publish": "Lost and Found",
"pkgService/submit_request/submit_request": "Submit Request",
"pkgService/utility_recharge/utility_recharge": "Utility Recharge"
}
@@ -92,6 +96,7 @@ const messages = {
cancelOrderSuccess: "Order canceled",
cancelOrderTitle: "Cancel Order",
continuePay: "Continue Pay",
+ currentStepTag: "Current",
dateRangeJoiner: " to ",
depositNoticeTitle: "Rental Deposit Notice",
emptyDesc: "You have not submitted any application yet. Go select a room to start a stay or viewing application.",
@@ -102,11 +107,21 @@ const messages = {
loadingDesc: "Syncing your application records. Please wait.",
missingApplicationCode: "Missing application number",
missingBillCode: "Missing application bill number",
+ missingContractNo: "Missing contract number",
+ noPendingDepositBill: "No pending deposit bill available",
noMore: "No more applications",
occupantLabel: "Resident",
pageDesc: "Your stay and viewing application records",
pageTitle: "My Applications",
payDeposit: "Pay Deposit",
+ fetchPaymentParamsFailed: "Failed to get payment parameters",
+ fetchWechatLoginCodeFailed: "Failed to get WeChat login code",
+ loadingBill: "Loading bill...",
+ loadingPayment: "Opening payment...",
+ depositPaymentDesc: "Deposit Payment",
+ paymentSuccess: "Payment succeeded",
+ paymentCanceled: "Payment canceled",
+ paymentFailed: "Payment failed",
pendingPaymentAmountLabel: "Order Amount",
pendingPaymentDescLabel: "Description",
pendingPaymentExpireLabel: "Time Left",
@@ -118,8 +133,20 @@ const messages = {
refreshEmptySuccess: "No application found. Reservation records synced.",
refreshSuccess: "Application status refreshed",
refreshing: "Refreshing...",
+ refreshAction: "Refresh",
remarkLabel: "Remarks",
+ reservationFlowDesc: "Progress of the current reservation order",
+ reservationFlowTitle: "Reservation Flow",
+ reservationOrderFallback: "Reservation",
+ reservationOrderNoLabel: "Order No.",
+ roomNoLabel: "Room No.",
+ reservationRoomLabel: "Room Type",
+ reservationStatusLabel: "Order Status",
+ contractPendingReviewHint: "Contract signed, waiting for review",
+ inspectionPendingReviewHint: "Inspection under review",
+ checkinPendingHint: "Move-in is being processed, please wait",
scrollToRead: "Scroll to bottom",
+ refreshFlowAction: "Refresh Flow",
statusApproved: "Approved",
statusFinished: "Closed",
statusPending: "Pending",
@@ -249,9 +276,14 @@ const messages = {
loadingManagerInfo: "Loading butler info",
loadingShort: "Syncing",
managerContactSummary: "{count} butlers are available in this area",
+ managementContacts: "Dorm Managers & Admins",
+ dormitoryManagerTag: "Dorm Mgr",
+ counselorTag: "Admin",
+ noManagerContacts: "No management contacts",
managerFallback: "Community Butler",
managerInitialFallback: "B",
managerWechatCopied: "Butler Wechat copied",
+ mealDeliveryJumpFailed: "Failed to open the ordering mini-program",
multiButlerName: "{name} +{count}",
myBill: "Monthly Bill",
noButlerInfo: "No butler information yet",
@@ -268,7 +300,7 @@ const messages = {
pendingContractFound: "Pending contract found",
pendingSync: "Pending",
primaryButlerTag: "Lead",
- publicService: "Facility Booking",
+ publicService: "Premium Services",
refreshFailed: "Refresh failed, please try again later",
refreshStatus: "Refresh Status",
refreshing: "Refreshing...",
@@ -366,6 +398,7 @@ const messages = {
},
register: {
register: {
+ admissionNoticeAttachmentLabel: "Admission Notice (Employment Certificate)",
accountLabel: "Account",
accountPlaceholder: "Enter account name",
accountSection: "Account Information",
@@ -436,6 +469,7 @@ const messages = {
submitRegister: "Submit Registration",
submitting: "Submitting...",
uploadFailed: "Upload failed, please retry",
+ uploadAdmissionNotice: "Upload admission notice or employment certificate",
uploadIdBack: "Upload ID card back",
uploadIdFront: "Upload ID card front",
uploadImageOption: "Upload Image",
@@ -521,6 +555,7 @@ const messages = {
duration5: "Over 1 year",
durationLabel: "Expected Stay Duration",
navTitle: "On-site Viewing",
+ roomTypeLabel: "Preferred Room Type",
remarkArea: "Preferred Area: {area}",
remarkBudget: "Budget: {budget} CNY/month",
remarkDuration: "Expected Stay: {duration}",
@@ -795,6 +830,10 @@ const messages = {
leaseDurationKey: "Expected Stay Duration",
leaseTermLabel: "Lease Term",
missingBillCode: "Missing application bill number",
+ missingContractNo: "Missing contract number",
+ inspectionPendingReviewHint: "Inspection under review",
+ checkinPendingHint: "Move-in is being processed, please wait",
+ refreshAction: "Refresh",
navTitle: "Application Detail",
paymentMethodKey: "Payment Method",
preferredAreaKey: "Preferred Area",
@@ -1147,8 +1186,62 @@ const messages = {
unsupportedSwitch: "This device does not support quick switching"
}
},
+ lost_found_publish: {
+ lost_found_publish: {
+ navTitle: "Lost and Found",
+ heroBadge: "Community Service",
+ heroTitle: "Publish lost or found item details",
+ heroDesc: "Share lost or found information with the community so that follow-up and matching can happen faster.",
+ formTitle: "Item Information",
+ formDesc: "Please complete the key details below. Required fields help others contact you faster.",
+ typeLabel: "Publish Type",
+ typePlaceholder: "Select lost or found",
+ typeLost: "Lost Item",
+ typeFound: "Found Item",
+ itemTypeLabel: "Item Category",
+ itemTypePlaceholder: "For example: documents, keys, electronics",
+ itemNameLabel: "Item Name",
+ itemNamePlaceholder: "Please enter the item name",
+ placeLabel: "Location",
+ placePlaceholder: "Please enter where it was lost or found",
+ whenLabel: "Time",
+ selectDate: "Select date",
+ selectTime: "Select time",
+ whenTip: "Optional. Fill in the approximate time if available.",
+ contractLabel: "Contact Person",
+ contractPlaceholder: "Please enter the contact person",
+ contractPhoneLabel: "Contact Phone",
+ contractPhonePlaceholder: "Please enter the contact phone",
+ remarkLabel: "Remarks",
+ remarkPlaceholder: "Add more details such as appearance, claim notes, or identifying features",
+ imagesLabel: "Images",
+ uploadImage: "Upload Image",
+ imagesTip: "Optional. Upload on-site or item photos to help identify the item.",
+ submitTip: "Please verify the information before publishing. We will submit it with the current language.",
+ submitting: "Submitting...",
+ submitButton: "Publish Now",
+ uploadFailed: "Image upload failed",
+ uploadOversize: "Each image must be within 5 MB",
+ uploadExceed: "You can upload up to 6 images",
+ validateItemType: "Please enter the item category",
+ validateItemName: "Please enter the item name",
+ validatePlace: "Please enter the location",
+ validateContract: "Please enter the contact person",
+ validateContractPhone: "Please enter the contact phone",
+ submitSuccess: "Published successfully",
+ submitFailed: "Failed to publish"
+ }
+ },
submit_request: {
components: {
+ BuildingSelector: {
+ emptyText: "No buildings available",
+ loadFailed: "Failed to load buildings",
+ loadingText: "Loading buildings...",
+ popupTitle: "Select Building",
+ selectAreaFirst: "Please select an area first",
+ selectBuilding: "Select building"
+ },
DateRangeSelector: {
dateDivider: "to",
endLabel: "Expected Check-out",
@@ -1156,6 +1249,14 @@ const messages = {
startLabel: "Expected Check-in",
startPlaceholder: "Select check-in date"
},
+ FloorSelector: {
+ clearSelection: "Not specified",
+ emptyText: "No floors available",
+ loadFailed: "Failed to load floor list",
+ loadingText: "Loading floors...",
+ popupTitle: "Select Floor",
+ selectFloor: "Select floor"
+ },
TagSelector: {
emptyText: "No tags available",
loadingText: "Loading tags..."
@@ -1167,12 +1268,14 @@ const messages = {
applyRemarkPlaceholder: "Enter your additional notes",
applyRoomType: "Requested Room Type",
areaLabel: "Area",
+ buildingLabel: "Preferred Building",
currentRoom: "Current Room",
dateCannotBefore: "{endLabel} cannot be earlier than {startLabel}",
emptyHobbyTags: "No hobby tags",
emptyLifestyleTags: "No lifestyle tags",
expectedCheckin: "Expected Check-in",
expectedCheckout: "Expected Checkout",
+ floorLabel: "Preferred Floor",
hobbyLabel: "Hobbies",
leaseRange: "Lease Period",
lifestyleLabel: "Lifestyle Habits",
@@ -1184,6 +1287,10 @@ const messages = {
roomTypeApplyBlockTitle: "Application Details",
roomTypeApplyDesc: "Confirm the room type, area, preference tags, and lease period before submitting.",
roomTypeApplyTitle: "Room Type Application",
+ switchRoomType: "Switch",
+ summaryTitle: "Application Summary",
+ expandSummary: "Expand",
+ collapseSummary: "Collapse",
selectCheckinDate: "Select check-in date",
selectCheckoutDate: "Select checkout date",
selectDateEnd: "Please select {label}",
@@ -1231,8 +1338,12 @@ const messages = {
"pkgService/message_detail/message_detail": "消息详情",
"pkgService/my_application/my_application": "我的申请",
"pkgService/my_contract/my_contract": "我的合同",
+ "pkgService/room_inspection/room_inspection": "验房",
"pkgService/service_stub/service_stub": "业务办理",
"pkgService/smart_control/smart_control": "智能控制",
+ "pkgService/ticket_center/ticket_center": "工单中心",
+ "pkgService/ticket_workbench/ticket_workbench": "工单处理",
+ "pkgService/lost_found_publish/lost_found_publish": "失物拾物",
"pkgService/submit_request/submit_request": "我要提交",
"pkgService/utility_recharge/utility_recharge": "水电充值"
}
@@ -1298,6 +1409,7 @@ const messages = {
cancelOrderSuccess: "订单已取消",
cancelOrderTitle: "取消订单",
continuePay: "继续支付",
+ currentStepTag: "当前",
dateRangeJoiner: " 至 ",
depositNoticeTitle: "租房定金须知",
emptyDesc: "您还没有提交任何申请,去选房后可发起入住申请或预约看房。",
@@ -1308,11 +1420,21 @@ const messages = {
loadingDesc: "正在同步您的入住申请记录,请稍候。",
missingApplicationCode: "申请编号缺失",
missingBillCode: "申请单编号缺失",
+ missingContractNo: "合同编号缺失",
+ noPendingDepositBill: "暂无待支付定金账单",
noMore: "没有更多申请了",
occupantLabel: "入住人",
pageDesc: "您的入住和预约看房申请记录",
pageTitle: "我的申请",
payDeposit: "支付定金",
+ fetchPaymentParamsFailed: "获取支付参数失败",
+ fetchWechatLoginCodeFailed: "获取微信登录凭证失败",
+ loadingBill: "正在获取账单...",
+ loadingPayment: "正在拉起支付...",
+ depositPaymentDesc: "支付定金",
+ paymentSuccess: "支付成功",
+ paymentCanceled: "已取消支付",
+ paymentFailed: "支付失败",
pendingPaymentAmountLabel: "订单金额",
pendingPaymentDescLabel: "支付说明",
pendingPaymentExpireLabel: "剩余支付时间",
@@ -1324,8 +1446,20 @@ const messages = {
refreshEmptySuccess: "未查到申请单,已同步预订单",
refreshSuccess: "申请状态已刷新",
refreshing: "刷新中...",
+ refreshAction: "刷新",
remarkLabel: "补充说明",
+ reservationFlowDesc: "当前预定单的流程进度",
+ reservationFlowTitle: "预定流程",
+ reservationOrderFallback: "预定单",
+ reservationOrderNoLabel: "预定单号",
+ roomNoLabel: "房间号",
+ reservationRoomLabel: "房型",
+ reservationStatusLabel: "订单状态",
+ contractPendingReviewHint: "合同已签,等待宿管审核",
+ inspectionPendingReviewHint: "验房审批中",
+ checkinPendingHint: "宿管正在办理入住,请耐心等待",
scrollToRead: "请滑动阅读至底部",
+ refreshFlowAction: "刷新流程",
statusApproved: "已通过",
statusFinished: "已结束",
statusPending: "待审核",
@@ -1455,9 +1589,14 @@ const messages = {
loadingManagerInfo: "正在加载管家信息",
loadingShort: "同步中",
managerContactSummary: "当前区域共有 {count} 位管家可联系",
+ managementContacts: "宿管与管理员",
+ dormitoryManagerTag: "宿管",
+ counselorTag: "管理员",
+ noManagerContacts: "暂无管理联系人",
managerFallback: "社区管家",
managerInitialFallback: "管",
managerWechatCopied: "管家微信已复制",
+ mealDeliveryJumpFailed: "打开点餐小程序失败",
multiButlerName: "{name} 等{count}位管家",
myBill: "本月账单",
noButlerInfo: "暂未获取到管家信息",
@@ -1474,7 +1613,7 @@ const messages = {
pendingContractFound: "发现待签合同",
pendingSync: "待同步",
primaryButlerTag: "主责",
- publicService: "社区设施预约",
+ publicService: "公寓精选服务",
refreshFailed: "刷新失败,请稍后重试",
refreshStatus: "刷新状态",
refreshing: "刷新中...",
@@ -1572,6 +1711,7 @@ const messages = {
},
register: {
register: {
+ admissionNoticeAttachmentLabel: "入学通知书(在职证明)附件",
accountLabel: "登录账号",
accountPlaceholder: "请输入登录账号",
accountSection: "账号信息",
@@ -1642,6 +1782,7 @@ const messages = {
submitRegister: "提交注册申请",
submitting: "提交中...",
uploadFailed: "上传失败,请重试",
+ uploadAdmissionNotice: "点击上传入学通知书/在职证明",
uploadIdBack: "点击上传身份证反面",
uploadIdFront: "点击上传身份证正面",
uploadImageOption: "上传图片",
@@ -1727,6 +1868,7 @@ const messages = {
duration5: "1年以上",
durationLabel: "预计租住时长",
navTitle: "预约现场带看",
+ roomTypeLabel: "意向房型",
remarkArea: "意向区域:{area}",
remarkBudget: "租金预算:{budget} 元/月",
remarkDuration: "预计租住时长:{duration}",
@@ -1745,7 +1887,7 @@ const messages = {
selectViewingTimeDesc: "请选择您方便的时间段",
submitBooking: "提交预约",
submitFailed: "预约提交失败",
- submitSuccessContent: "您的预约看房申请已提交。\n系统会自动转发给对应区域有房间权限的宿管,宿管确认带看后可直接完结订单,或选定房型房号后转为预定单发送给租客支付。",
+ submitSuccessContent: "您的预约看房申请已提交,请等待管家联系",
submitSuccessTitle: "预约成功",
submitting: "提交中...",
today: "今天",
@@ -2001,6 +2143,10 @@ const messages = {
leaseDurationKey: "预计租住时长",
leaseTermLabel: "预计租期",
missingBillCode: "申请单编号缺失",
+ missingContractNo: "合同编号缺失",
+ inspectionPendingReviewHint: "验房审批中",
+ checkinPendingHint: "宿管正在办理入住,请耐心等待",
+ refreshAction: "刷新",
navTitle: "申请详情",
paymentMethodKey: "支付方式",
preferredAreaKey: "意向区域",
@@ -2353,8 +2499,62 @@ const messages = {
unsupportedSwitch: "该设备不支持快捷开关"
}
},
+ lost_found_publish: {
+ lost_found_publish: {
+ navTitle: "失物拾物",
+ heroBadge: "社区服务",
+ heroTitle: "发布失物或招领信息",
+ heroDesc: "将失物或拾获信息同步给社区,方便后续核实与快速匹配。",
+ formTitle: "物品信息",
+ formDesc: "请补充关键字段,必填项越完整,越方便他人联系与认领。",
+ typeLabel: "发布类型",
+ typePlaceholder: "请选择失物或招领",
+ typeLost: "失物",
+ typeFound: "招领",
+ itemTypeLabel: "物品类型",
+ itemTypePlaceholder: "例如证件、钥匙、电子设备",
+ itemNameLabel: "物品名称",
+ itemNamePlaceholder: "请输入物品名称",
+ placeLabel: "地点",
+ placePlaceholder: "请输入丢失或拾取地点",
+ whenLabel: "时间",
+ selectDate: "选择日期",
+ selectTime: "选择时间",
+ whenTip: "可选,若已知大致时间可一并填写。",
+ contractLabel: "联系人",
+ contractPlaceholder: "请输入联系人",
+ contractPhoneLabel: "联系电话",
+ contractPhonePlaceholder: "请输入联系电话",
+ remarkLabel: "备注",
+ remarkPlaceholder: "可补充外观特征、认领说明或其他辅助信息",
+ imagesLabel: "现场图片",
+ uploadImage: "上传图片",
+ imagesTip: "可选,上传现场图或物品图可提升识别效率。",
+ submitTip: "发布前请确认信息准确,系统会按当前语言提交接口。",
+ submitting: "发布中...",
+ submitButton: "立即发布",
+ uploadFailed: "图片上传失败",
+ uploadOversize: "单张图片不能超过 5 MB",
+ uploadExceed: "最多上传 6 张图片",
+ validateItemType: "请输入物品类型",
+ validateItemName: "请输入物品名称",
+ validatePlace: "请输入地点",
+ validateContract: "请输入联系人",
+ validateContractPhone: "请输入联系电话",
+ submitSuccess: "发布成功",
+ submitFailed: "发布失败"
+ }
+ },
submit_request: {
components: {
+ BuildingSelector: {
+ emptyText: "暂无可选楼栋",
+ loadFailed: "楼栋列表加载失败",
+ loadingText: "楼栋加载中...",
+ popupTitle: "选择意向楼栋",
+ selectAreaFirst: "请先选择区域",
+ selectBuilding: "请选择楼栋"
+ },
DateRangeSelector: {
dateDivider: "至",
endLabel: "预计离开",
@@ -2362,6 +2562,14 @@ const messages = {
startLabel: "预计入住",
startPlaceholder: "请选择入住日期"
},
+ FloorSelector: {
+ clearSelection: "不选择",
+ emptyText: "暂无可选楼层",
+ loadFailed: "楼层列表加载失败",
+ loadingText: "楼层加载中...",
+ popupTitle: "选择意向楼层",
+ selectFloor: "请选择楼层"
+ },
TagSelector: {
emptyText: "暂无标签",
loadingText: "标签加载中..."
@@ -2373,12 +2581,14 @@ const messages = {
applyRemarkPlaceholder: "请输入您的申请补充说明",
applyRoomType: "申请房型",
areaLabel: "所属区域",
+ buildingLabel: "意向楼栋",
currentRoom: "当前房间",
dateCannotBefore: "{endLabel}不能早于{startLabel}",
emptyHobbyTags: "暂无兴趣爱好标签",
emptyLifestyleTags: "暂无生活习惯标签",
expectedCheckin: "预计入住",
expectedCheckout: "预计离开",
+ floorLabel: "意向楼层",
hobbyLabel: "兴趣爱好",
leaseRange: "租期时间",
lifestyleLabel: "生活习惯",
@@ -2390,6 +2600,10 @@ const messages = {
roomTypeApplyBlockTitle: "房型申请信息",
roomTypeApplyDesc: "请确认房型、区域、偏好标签和租期信息后提交申请。",
roomTypeApplyTitle: "房型申请",
+ switchRoomType: "切换",
+ summaryTitle: "申请摘要",
+ expandSummary: "展开",
+ collapseSummary: "收起",
selectCheckinDate: "请选择入住日期",
selectCheckoutDate: "请选择离开日期",
selectDateEnd: "请选择{label}",
diff --git a/manifest.json b/manifest.json
index 7d9638b..78f58d2 100644
--- a/manifest.json
+++ b/manifest.json
@@ -51,11 +51,15 @@
/* 小程序特有相关 */
"mp-weixin" : {
"appid" : "wx92fd9a1cd56f1185",
+ "__usePrivacyCheck__" : true,
"setting" : {
- "urlCheck" : false
+ "urlCheck" : false,
+ "minified" : true,
+ "postcss" : true
},
"usingComponents" : true,
- "requiredPrivateInfos" : [ "chooseAddress" ]
+ "requiredPrivateInfos" : [ "chooseAddress" ],
+ "permission" : {}
},
"mp-alipay" : {
"usingComponents" : true
diff --git a/pages.json b/pages.json
index d51d408..23230bb 100644
--- a/pages.json
+++ b/pages.json
@@ -96,6 +96,12 @@
"navigationBarTitleText": "My Contracts"
}
},
+ {
+ "path": "room_inspection/room_inspection",
+ "style": {
+ "navigationBarTitleText": "Inspection"
+ }
+ },
{
"path": "contract_detail/contract_detail",
"style": {
@@ -114,6 +120,24 @@
"navigationBarTitleText": "Message Detail"
}
},
+ {
+ "path": "ticket_center/ticket_center",
+ "style": {
+ "navigationBarTitleText": "Work Orders"
+ }
+ },
+ {
+ "path": "ticket_workbench/ticket_workbench",
+ "style": {
+ "navigationBarTitleText": "Work Order"
+ }
+ },
+ {
+ "path": "lost_found_publish/lost_found_publish",
+ "style": {
+ "navigationBarTitleText": "Lost and Found"
+ }
+ },
{
"path": "submit_request/submit_request",
"style": {
diff --git a/pages/index/component/MyApplicationTab.vue b/pages/index/component/MyApplicationTab.vue
index 94da39f..e1a7719 100644
--- a/pages/index/component/MyApplicationTab.vue
+++ b/pages/index/component/MyApplicationTab.vue
@@ -5,7 +5,111 @@
{{ t('pageDesc') }}
-
+
+
+
+
+ {{ t('reservationFlowTitle') }}
+ {{ t('reservationFlowDesc') }}
+
+
+ {{ t('refreshFlowAction') }}
+
+
+
+
+
+
+ {{ item.roomTypeText || item.roomTypeName || t('reservationOrderFallback') }}
+
+
+
+
+
+
+ {{ t('apartmentLabel') }}
+ {{ activeReservationOrder.areaText || activeReservationOrder.areaName || '--' }}
+
+
+ {{ t('reservationRoomLabel') }}
+ {{ activeReservationOrder.roomTypeText || activeReservationOrder.roomTypeName || '--' }}
+
+
+ {{ t('roomNoLabel') }}
+ {{ activeReservationOrder.roomNoText || activeReservationOrder.roomNo || '--' }}
+
+
+ {{ t('reservationStatusLabel') }}
+ {{ activeReservationOrder.orderStatusDesc || activeReservationOrder.statusText || '--' }}
+
+
+
+
+
+
+ {{ node.sort || node.step }}
+
+
+
+
+
+
+
+ {{ t('pendingPaymentDescLabel') }}
+ {{ getReservationBillDisplayTitle(getActiveReservationBillDetail()) }}
+
+
+ {{ t('pendingPaymentAmountLabel') }}
+ ¥{{ formatMoney(getReservationBillDisplayAmount(getActiveReservationBillDetail())) }}
+
+
+
+
+ {{ getReservationCurrentStepHint(activeReservationOrder) }}
+
+
+
+
+ {{ t('refreshAction') }}
+
+
+
+
+
+ {{ getCurrentStepActionText(activeReservationOrder) }}
+
+
+
+
+
+
+
-
- {{ t('statusReminderTitle') }}
+
+
+ {{ t('statusReminderTitle') }}
+
+ {{ t('refreshAction') }}
+
+
{{ t('statusReminderTitle') }}
{{ getStatusReminderText(item) }}
@@ -90,7 +199,18 @@
+ {{ getCurrentStepActionText(item) }}
+
+
+
+
diff --git a/pkgExplore/store_detail/components/RoomList.vue b/pkgExplore/store_detail/components/RoomList.vue
index 7e7bf37..50b1d23 100644
--- a/pkgExplore/store_detail/components/RoomList.vue
+++ b/pkgExplore/store_detail/components/RoomList.vue
@@ -45,6 +45,14 @@
+
+
diff --git a/pkgService/my_application/my_application.vue b/pkgService/my_application/my_application.vue
index 70b968c..96b5c15 100644
--- a/pkgService/my_application/my_application.vue
+++ b/pkgService/my_application/my_application.vue
@@ -109,14 +109,51 @@
+
+
diff --git a/pkgService/submit_request/components/BuildingSelector.vue b/pkgService/submit_request/components/BuildingSelector.vue
new file mode 100644
index 0000000..57331af
--- /dev/null
+++ b/pkgService/submit_request/components/BuildingSelector.vue
@@ -0,0 +1,314 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pkgService/submit_request/components/FloorSelector.vue b/pkgService/submit_request/components/FloorSelector.vue
new file mode 100644
index 0000000..2f36032
--- /dev/null
+++ b/pkgService/submit_request/components/FloorSelector.vue
@@ -0,0 +1,308 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pkgService/submit_request/components/RoomTypeSelector.vue b/pkgService/submit_request/components/RoomTypeSelector.vue
index 0fd8735..57a2fb3 100644
--- a/pkgService/submit_request/components/RoomTypeSelector.vue
+++ b/pkgService/submit_request/components/RoomTypeSelector.vue
@@ -1,6 +1,6 @@
-
@@ -98,6 +108,7 @@ import { getStoredUser } from '../../utils/flow'
import TagSelector from './components/TagSelector.vue'
import DateRangeSelector from './components/DateRangeSelector.vue'
import RoomTypeSelector from './components/RoomTypeSelector.vue'
+import FloorSelector from './components/FloorSelector.vue'
function createDefaultForm() {
const now = new Date()
@@ -107,6 +118,7 @@ function createDefaultForm() {
return {
intendedAreaCode: '',
intendedRoomTypeCode: '',
+ intendedFloorLevel: '',
roomSlotHeight: 1,
hobbiesAndInterestsTagKeys: [],
lifestyleHabitsTagKeys: [],
@@ -564,7 +576,8 @@ export default {
components: {
TagSelector,
DateRangeSelector,
- RoomTypeSelector
+ RoomTypeSelector,
+ FloorSelector
},
data() {
return {
@@ -743,12 +756,27 @@ export default {
roomTypeLabel() {
return this.getBusinessText('roomTypeLabel') || this.t('applyRoomType')
},
+ pageBottomPadding() {
+ return this.showFloatingSubmitBtn ? '220rpx' : '60rpx'
+ },
+ showFloatingSubmitBtn() {
+ return this.isRoomTypeApply
+ },
+ floorLabel() {
+ return this.getBusinessText('floorLabel') || this.t('floorLabel')
+ },
showRoomTypeSelector() {
if (this.isRoomTypeApply) {
return true
}
return this.businessConfig ? this.businessConfig.showRoomTypeSelector !== false : true
},
+ showFloorSelector() {
+ if (this.isRoomTypeApply) {
+ return true
+ }
+ return this.businessConfig ? this.businessConfig.showFloorSelector !== false : true
+ },
showPreferenceSelectors() {
if (this.isRoomTypeApply) {
return true
@@ -775,41 +803,6 @@ export default {
},
dateRangeEndPlaceholder() {
return this.getBusinessText('dateRangeEndPlaceholder') || this.t('selectCheckoutDate')
- },
- sourceSummaryList() {
- const payload = this.sourcePayload || {}
- if (!Object.keys(payload).length) {
- return []
- }
- const roomTypeName = this.language === 'en'
- ? (payload.roomTypeNameEn || payload.roomTypeName || '')
- : (payload.roomTypeName || '')
- const roomLabel = this.language === 'en'
- ? (payload.roomLabelEn || payload.currentRoomLabelEn || payload.roomLabel || payload.currentRoomLabel || '')
- : (payload.roomLabel || payload.currentRoomLabel || '')
- const areaName = this.language === 'en'
- ? (payload.areaLabelEn || payload.areaNameEn || payload.areaLabel || payload.areaName || '')
- : (payload.areaLabel || payload.areaName || '')
- const list = []
- if (roomTypeName) {
- list.push({ label: this.t('applyRoomType'), value: roomTypeName })
- }
- if (roomLabel) {
- list.push({ label: this.t('currentRoom'), value: roomLabel })
- }
- if (areaName) {
- list.push({ label: this.t('areaLabel'), value: areaName })
- }
- if (payload.price) {
- list.push({ label: this.t('referencePrice'), value: payload.price })
- }
- if (payload.payMethod) {
- list.push({ label: this.t('paymentMethod'), value: localizePayMethodText(payload.payMethod, this.language) })
- }
- if (this.pageTitle) {
- list.push({ label: this.t('applicationType'), value: this.pageTitle })
- }
- return list
}
},
watch: {
@@ -943,6 +936,7 @@ export default {
...defaultForm,
intendedAreaCode: this.sourcePayload.areaCode || '',
intendedRoomTypeCode: this.sourcePayload.roomTypeCode || '',
+ intendedFloorLevel: this.sourcePayload.intendedFloorLevel || this.sourcePayload.IntendedFloorLevel || '',
remark: '',
start: this.sourcePayload.start || defaultForm.start,
end: this.sourcePayload.end || ''
@@ -967,18 +961,31 @@ export default {
this.sourcePayload.roomTypeCode = roomType.code
this.sourcePayload.roomTypeName = roomType.name
this.sourcePayload.roomTypeNameEn = roomType.nameEn || ''
+ this.sourcePayload.selectedRoomTypeBuildingCode = roomType.buildingCode || ''
+ this.sourcePayload.selectedRoomTypeBuildingSectionCode = roomType.buildingSectionCode || ''
+ this.sourcePayload.selectedRoomTypeBuildingName = roomType.buildingName || ''
+ this.sourcePayload.selectedRoomTypeBuildingSectionName = roomType.buildingSectionName || ''
if (area) {
this.sourcePayload.areaCode = area.code
this.sourcePayload.areaName = area.name
this.sourcePayload.areaNameEn = area.nameEn || ''
}
-
+
if (roomType.price) {
this.sourcePayload.price = roomType.price
}
}
},
+ handleFloorChange({ floor }) {
+ if (!this.sourcePayload) {
+ this.sourcePayload = {}
+ }
+ this.sourcePayload.intendedFloorLevel = floor && floor.value ? floor.value : ''
+ this.sourcePayload.intendedFloorName = floor
+ ? (this.language === 'en' ? (floor.nameEn || floor.name || '') : (floor.name || floor.nameEn || ''))
+ : ''
+ },
loadDictOptions() {
this.dictLoading = true
return Promise.all([
@@ -1052,6 +1059,7 @@ export default {
return {
intendedAreaCode: this.form.intendedAreaCode || null,
intendedRoomTypeCode: this.form.intendedRoomTypeCode || null,
+ IntendedFloorLevel: this.form.intendedFloorLevel || null,
roomSlotHeight: Number(this.form.roomSlotHeight),
hobbiesAndInterestsTagKeys: this.showPreferenceSelectors && this.form.hobbiesAndInterestsTagKeys.length ? this.form.hobbiesAndInterestsTagKeys : null,
lifestyleHabitsTagKeys: this.showPreferenceSelectors && this.form.lifestyleHabitsTagKeys.length ? this.form.lifestyleHabitsTagKeys : null,
@@ -1276,6 +1284,22 @@ export default {
pointer-events: none;
}
+.floating-submit-bar {
+ position: fixed;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 30;
+ padding: 20rpx 24rpx calc(20rpx + env(safe-area-inset-bottom));
+ background: linear-gradient(180deg, rgba(248, 250, 255, 0) 0%, rgba(248, 250, 255, 0.92) 24%, #F8FAFF 100%);
+ backdrop-filter: blur(16rpx);
+ -webkit-backdrop-filter: blur(16rpx);
+}
+
+.floating-submit-bar__inner {
+ max-width: 100%;
+}
+
.source-row {
padding: 24rpx 0;
border-bottom: 1px dashed rgba($brand-primary, 0.15);
@@ -1286,6 +1310,10 @@ export default {
padding-bottom: 8rpx;
}
+.source-row--interactive:active {
+ opacity: 0.8;
+}
+
.source-row__label {
color: #6A7A99;
font-size: 26rpx;
@@ -1301,4 +1329,5 @@ export default {
margin-left: 24rpx;
word-break: break-word;
}
+
diff --git a/pkgService/ticket_center/ticket_center.vue b/pkgService/ticket_center/ticket_center.vue
new file mode 100644
index 0000000..8436704
--- /dev/null
+++ b/pkgService/ticket_center/ticket_center.vue
@@ -0,0 +1,211 @@
+
+
+
+ {{ t('navTitle') }}
+
+
+
+
+
+
+ {{ t('heroBadge') }}
+
+ {{ t('heroTitle') }}
+ {{ t('heroDesc') }}
+
+
+
+
+
+ {{ getItemText(item, 'title', 'titleEn') }}
+ {{ getItemText(item, 'desc', 'descEn') }}
+
+
+
+
+
+
+
+
+
diff --git a/pkgService/ticket_workbench/ticket_workbench.vue b/pkgService/ticket_workbench/ticket_workbench.vue
new file mode 100644
index 0000000..4fbb757
--- /dev/null
+++ b/pkgService/ticket_workbench/ticket_workbench.vue
@@ -0,0 +1,2252 @@
+
+
+
+ {{ pageTitle }}
+
+
+
+
+
+
+
+
+ {{ t('submitTab') }}
+
+
+ {{ t('historyTab') }}
+
+
+
+
+ {{ t('currentStayInfo') }}
+ {{ currentOrderStatusText }}
+
+
+ {{ t('roomLabel') }}
+ {{ currentRoomText }}
+
+
+ {{ t('areaLabel') }}
+ {{ currentAreaText }}
+
+
+ {{ t('leaseLabel') }}
+ {{ currentLeaseText }}
+
+
+ {{ t('roomTypeLabel') }}
+ {{ currentRoomTypeText }}
+
+
+
+
+
+ {{ modeBannerTitle }}
+ {{ modeBannerText }}
+ {{ t('cancelSupplement') }}
+ {{ t('cancelProcess') }}
+
+
+
+ {{ submitSectionTitle }}
+ {{ submitSectionDesc }}
+
+
+ {{ t('changeRoomTypeLabel') }}
+
+
+
+
+ {{ t('checkoutTimeLabel') }}
+
+
+ {{ form.checkOutDate || t('selectDate') }}
+
+
+ {{ form.checkOutTimePart || t('selectTime') }}
+
+
+
+
+
+ {{ t('renewalTimeLabel') }}
+
+
+ {{ form.renewalDate || t('selectDate') }}
+
+
+ {{ form.renewalTimePart || t('selectTime') }}
+
+
+
+
+
+ {{ t('cleaningTimeLabel') }}
+
+
+ {{ form.cleaningDate || t('selectDate') }}
+
+
+ {{ form.cleaningTimePart || t('selectTime') }}
+
+
+
+
+
+ {{ contentFieldLabel }}
+
+ {{ (form.content || '').length }}/500
+
+
+
+ {{ t('remarkLabel') }}
+
+
+
+
+ {{ t('assigneeLabel') }}
+
+
+ {{ transitSelectedAssigneeName || t('selectAssignee') }}
+
+
+
+
+
+ {{ t('supplementLabel') }}
+
+ {{ item.title }}
+
+
+
+
+
+
+ {{ t('attachmentsLabel') }} *
+
+
+
+
+ {{ t('uploadImage') }}
+
+
+
+ {{ uploadTipText }}
+
+
+
+
+
+
+
+
+
+
+ {{ historyLoading ? t('refreshing') : t('refresh') }}
+
+
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+
+
+
+ {{ getHistoryTitle(item) }}
+ {{ item.no || '--' }}
+
+
+ {{ getHistoryStatusText(item) }}
+
+
+
+
+ {{ t('submittedAtLabel') }}
+ {{ formatDateTime(item.createTime) }}
+
+
+ {{ t('contentLabel') }}
+ {{ item.content || item.remark || '--' }}
+
+
+
+
+
+ {{ t('loadingMore') }}
+ {{ t('noMore') }}
+ {{ t('loadMore') }}
+
+
+
+
+ {{ t('emptyHistoryTitle') }}
+ {{ t('emptyHistoryDesc') }}
+
+
+
+
+
+
+
+ {{ submitFooterTip }}
+
+ {{ submitting ? t('submitting') : submitButtonText }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/store/index.js b/store/index.js
index 1dc1fd7..2412b1d 100644
--- a/store/index.js
+++ b/store/index.js
@@ -2,6 +2,7 @@ import Vue from 'vue'
import Vuex from 'vuex'
import { getEnums, getMyEffectiveContracts, getMyPendingSignContracts, orderSearchedMePage } from '@/api/index.js'
import { normalizeBookingList, parseOrderListResponse, pickCurrentBooking, pickCurrentStayOrder } from '@/utils/booking-helper'
+import { getBuiltInEnums } from '@/utils/enum-store'
Vue.use(Vuex)
@@ -62,6 +63,21 @@ function logRequestError(name, error) {
return []
}
+function resolveOrderNo(order = {}) {
+ const candidates = [order.orderNo, order.no, order.code, order.OrderNo]
+ for (let index = 0; index < candidates.length; index += 1) {
+ const value = candidates[index]
+ if (value === undefined || value === null) {
+ continue
+ }
+ const text = String(value).trim()
+ if (text) {
+ return text
+ }
+ }
+ return ''
+}
+
let lastFetchStatusTime = 0
let fetchUserGlobalStatusPromise = null
let globalEnumsPromise = null
@@ -89,7 +105,7 @@ const store = new Vuex.Store({
vuex_effective_contracts: [],
vuex_valid_orders: [],
vuex_current_order: lifeData.vuex_current_order ? lifeData.vuex_current_order : null,
- vuex_enums: {}
+ vuex_enums: getBuiltInEnums(lifeData.vuex_locale || 'zh-Hans')
},
mutations: {
$tStore(state, payload) {
@@ -148,14 +164,25 @@ const store = new Vuex.Store({
lastFetchStatusTime = now
fetchUserGlobalStatusPromise = Promise.all([
- getMyPendingSignContracts({ UserCode: userCode }, language).catch((error) => logRequestError('getMyPendingSignContracts', error)),
- getMyEffectiveContracts({ UserCode: userCode }, language).catch((error) => logRequestError('getMyEffectiveContracts', error)),
orderSearchedMePage({ PageIndex: 1, PageSize: 100, IsExport: false }, language).catch((error) => logRequestError('orderSearchedMePage', error))
- ]).then(([pendingContractsResponse, effectiveContractsResponse, orderResponse]) => {
- const pendingContracts = parseContractList(pendingContractsResponse)
- const effectiveContracts = parseContractList(effectiveContractsResponse)
+ ]).then(([orderResponse]) => {
const validOrders = normalizeBookingList(parseOrderListResponse(orderResponse), language)
const currentOrder = pickCurrentStayOrder(validOrders)
+ const currentOrderNo = resolveOrderNo(currentOrder)
+
+ return Promise.all([
+ Promise.resolve(validOrders),
+ Promise.resolve(currentOrder),
+ currentOrderNo
+ ? getMyPendingSignContracts({ OrderNo: currentOrderNo, ProviderType: 'WechatMiniProgram' }, language).catch((error) => logRequestError('getMyPendingSignContracts', error))
+ : Promise.resolve([]),
+ currentOrderNo
+ ? getMyEffectiveContracts({ OrderNo: currentOrderNo }, language).catch((error) => logRequestError('getMyEffectiveContracts', error))
+ : Promise.resolve([])
+ ])
+ }).then(([validOrders, currentOrder, pendingContractsResponse, effectiveContractsResponse]) => {
+ const pendingContracts = parseContractList(pendingContractsResponse)
+ const effectiveContracts = parseContractList(effectiveContractsResponse)
const currentBooking = pickCurrentBooking(validOrders)
const nextUser = {
...currentUser,
@@ -208,7 +235,7 @@ const store = new Vuex.Store({
globalEnumsPromise = getEnums({}, language)
.then((res) => {
const data = res && res.data ? res.data : res
- const enumsMap = {}
+ const enumsMap = getBuiltInEnums(language)
if (Array.isArray(data)) {
data.forEach(item => {
enumsMap[item.code] = item.data || []
@@ -222,7 +249,7 @@ const store = new Vuex.Store({
return enumsMap
})
.catch(() => {
- return hasEnums ? state.vuex_enums : {}
+ return hasEnums ? state.vuex_enums : getBuiltInEnums(language)
})
.finally(() => {
globalEnumsPromise = null
diff --git a/utils/booking-entry.js b/utils/booking-entry.js
new file mode 100644
index 0000000..9872188
--- /dev/null
+++ b/utils/booking-entry.js
@@ -0,0 +1,36 @@
+function normalizeUrl(url = '') {
+ return String(url || '').trim()
+}
+
+export function isBookingEntryUrl(url = '') {
+ return /^\/pkgBooking\/(book_viewing\/book_viewing|book\/book)(?:\?|$)/.test(normalizeUrl(url))
+}
+
+export function resolveExistingBookingNavigationTarget(stayState = {}) {
+ const source = stayState || {}
+ const currentBooking = source.currentBooking || null
+ const hasCheckedIn = source.hasCheckedIn === true
+
+ if (currentBooking && currentBooking.orderNo) {
+ return `/pkgBooking/booking_detail/booking_detail?orderNo=${encodeURIComponent(currentBooking.orderNo)}`
+ }
+
+ if (currentBooking) {
+ return '/pkgBooking/booking_detail/booking_detail'
+ }
+
+ if (hasCheckedIn) {
+ return '/pages/index/index?pageId=stay-service'
+ }
+
+ return ''
+}
+
+export function resolveBookingRedirectAfterLogin(targetUrl = '', stayState = {}) {
+ const normalizedTargetUrl = normalizeUrl(targetUrl)
+ if (!isBookingEntryUrl(normalizedTargetUrl)) {
+ return normalizedTargetUrl
+ }
+
+ return resolveExistingBookingNavigationTarget(stayState) || normalizedTargetUrl
+}
diff --git a/utils/enum-store.js b/utils/enum-store.js
index e696897..b28e3a3 100644
--- a/utils/enum-store.js
+++ b/utils/enum-store.js
@@ -2,15 +2,110 @@ import { getEnums } from '@/api/index.js'
import { getCurrentLanguage } from '@/utils/i18n'
const ENUMS_CACHE_KEY = 'mini_enums_cache_v1'
+const BUILTIN_ENUMS = {
+ zh: {
+ IdCardTypeEnum: [
+ { key: 3, value: '港澳通行证', valueEn: 'Mainland Travel Permit for Hong Kong and Macao Residents' },
+ { key: 2, value: '护照', valueEn: 'Passport' },
+ { key: 99, value: '其他', valueEn: 'Other' },
+ { key: 1, value: '身份证', valueEn: 'ID Card' },
+ { key: 4, value: '台湾通行证', valueEn: 'Mainland Travel Permit for Taiwan Residents' }
+ ]
+ },
+ en: {
+ IdCardTypeEnum: [
+ { key: 3, value: 'Mainland Travel Permit for Hong Kong and Macao Residents', valueZh: '港澳通行证' },
+ { key: 2, value: 'Passport', valueZh: '护照' },
+ { key: 99, value: 'Other', valueZh: '其他' },
+ { key: 1, value: 'ID Card', valueZh: '身份证' },
+ { key: 4, value: 'Mainland Travel Permit for Taiwan Residents', valueZh: '台湾通行证' }
+ ]
+ }
+}
let enumsMap = {}
let enumsLanguage = ''
let fetchPromise = null
+function hasRemoteEnumData(map = {}, language) {
+ const currentLanguage = getNormalizedLanguage(language)
+ const builtinKeys = Object.keys(BUILTIN_ENUMS[currentLanguage] || {})
+ const sourceMap = map && typeof map === 'object' ? map : {}
+ return Object.keys(sourceMap).some(code => !builtinKeys.includes(code))
+}
+
+function mergeEnumOptionList(builtinList = [], customList = []) {
+ const nextList = []
+ const customMap = new Map()
+
+ ;(Array.isArray(customList) ? customList : []).forEach((item) => {
+ if (!item || typeof item !== 'object') return
+ customMap.set(String(item.key), item)
+ })
+
+ ;(Array.isArray(builtinList) ? builtinList : []).forEach((item) => {
+ const key = String(item && item.key)
+ const customItem = customMap.get(key)
+ if (customItem) {
+ nextList.push({
+ ...item,
+ ...customItem
+ })
+ customMap.delete(key)
+ return
+ }
+ nextList.push(item)
+ })
+
+ customMap.forEach((item) => {
+ nextList.push(item)
+ })
+
+ return nextList
+}
+
+function mergeBuiltinEnums(map = {}, language) {
+ const currentLanguage = getNormalizedLanguage(language)
+ const builtinMap = BUILTIN_ENUMS[currentLanguage] || {}
+ const mergedMap = {
+ ...builtinMap,
+ ...(map || {})
+ }
+
+ Object.keys(builtinMap).forEach((code) => {
+ if (!Array.isArray(mergedMap[code])) {
+ mergedMap[code] = builtinMap[code]
+ return
+ }
+ mergedMap[code] = mergeEnumOptionList(builtinMap[code], mergedMap[code])
+ })
+
+ return mergedMap
+}
+
+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
+ }
+}
+
function normalizeEnumMap(data) {
+ const normalizedData = parseJsonStringMaybe(data)
const map = {}
- if (Array.isArray(data)) {
- data.forEach((item) => {
+ if (Array.isArray(normalizedData)) {
+ normalizedData.forEach((item) => {
if (item && item.code) {
map[item.code] = Array.isArray(item.data) ? item.data : []
}
@@ -76,26 +171,30 @@ function writeEnumsCache(map, language) {
export function initEnumsFromCache(language) {
const currentLanguage = getNormalizedLanguage(language)
if (Object.keys(enumsMap).length && enumsLanguage === currentLanguage) return enumsMap
- enumsMap = readEnumsCache(currentLanguage)
+ enumsMap = mergeBuiltinEnums(readEnumsCache(currentLanguage), currentLanguage)
enumsLanguage = currentLanguage
return enumsMap
}
export async function fetchEnums(force = false, language) {
const currentLanguage = getNormalizedLanguage(language)
- if (!force && Object.keys(enumsMap).length && enumsLanguage === currentLanguage) return enumsMap
+ if (!force && Object.keys(enumsMap).length && enumsLanguage === currentLanguage && hasRemoteEnumData(enumsMap, currentLanguage)) {
+ return enumsMap
+ }
if (fetchPromise) return fetchPromise
fetchPromise = (async () => {
if (!force && (!Object.keys(enumsMap).length || enumsLanguage !== currentLanguage)) {
initEnumsFromCache(currentLanguage)
- if (Object.keys(enumsMap).length) return enumsMap
+ if (Object.keys(enumsMap).length && hasRemoteEnumData(enumsMap, currentLanguage)) {
+ return enumsMap
+ }
}
try {
const res = await getEnums({}, currentLanguage)
const data = res && res.data ? res.data : res
- const map = normalizeEnumMap(data)
+ const map = mergeBuiltinEnums(normalizeEnumMap(data), currentLanguage)
if (Object.keys(map).length) {
enumsMap = map
enumsLanguage = currentLanguage
@@ -118,6 +217,10 @@ export function getEnumOptions(code, language) {
return enumsMap[code] || []
}
+export function getBuiltInEnums(language) {
+ return mergeBuiltinEnums({}, language)
+}
+
export function getEnumLabel(code, key, fallback = '', language) {
const options = getEnumOptions(code, language)
const matched = options.find(item => String(item.key) === String(key))
diff --git a/utils/privacy.js b/utils/privacy.js
new file mode 100644
index 0000000..f5aa13b
--- /dev/null
+++ b/utils/privacy.js
@@ -0,0 +1,86 @@
+const privacyAuthorizationHandlers = []
+let hasBoundPrivacyAuthorizationListener = false
+
+function getActivePrivacyAuthorizationHandler() {
+ return privacyAuthorizationHandlers.length
+ ? privacyAuthorizationHandlers[privacyAuthorizationHandlers.length - 1]
+ : null
+}
+
+export function bindPrivacyAuthorizationListener(handler) {
+ if (typeof handler !== 'function') {
+ return
+ }
+
+ const existedIndex = privacyAuthorizationHandlers.indexOf(handler)
+ if (existedIndex !== -1) {
+ privacyAuthorizationHandlers.splice(existedIndex, 1)
+ }
+ privacyAuthorizationHandlers.push(handler)
+
+ // #ifdef MP-WEIXIN
+ if (hasBoundPrivacyAuthorizationListener || typeof wx === 'undefined' || !wx.onNeedPrivacyAuthorization) {
+ return
+ }
+
+ wx.onNeedPrivacyAuthorization((resolve) => {
+ const activeHandler = getActivePrivacyAuthorizationHandler()
+ if (typeof activeHandler === 'function') {
+ activeHandler(resolve)
+ }
+ })
+ hasBoundPrivacyAuthorizationListener = true
+ // #endif
+}
+
+export function unbindPrivacyAuthorizationListener(handler) {
+ const existedIndex = privacyAuthorizationHandlers.lastIndexOf(handler)
+ if (existedIndex !== -1) {
+ privacyAuthorizationHandlers.splice(existedIndex, 1)
+ }
+}
+
+export function getPrivacyAuthorizationStatus() {
+ // #ifdef MP-WEIXIN
+ return new Promise((resolve) => {
+ if (typeof wx === 'undefined' || !wx.getPrivacySetting) {
+ resolve({ needAuthorization: false })
+ return
+ }
+
+ wx.getPrivacySetting({
+ success: (res) => {
+ resolve({
+ ...res,
+ needAuthorization: !!(res && res.needAuthorization)
+ })
+ },
+ fail: () => {
+ resolve({ needAuthorization: false })
+ }
+ })
+ })
+ // #endif
+ // #ifndef MP-WEIXIN
+ return Promise.resolve({ needAuthorization: false })
+ // #endif
+}
+
+export function openWechatPrivacyContract() {
+ // #ifdef MP-WEIXIN
+ return new Promise((resolve, reject) => {
+ if (typeof wx === 'undefined' || !wx.openPrivacyContract) {
+ resolve()
+ return
+ }
+
+ wx.openPrivacyContract({
+ success: resolve,
+ fail: reject
+ })
+ })
+ // #endif
+ // #ifndef MP-WEIXIN
+ return Promise.resolve()
+ // #endif
+}
diff --git a/utils/request.js b/utils/request.js
index 783e2ac..d5ef388 100644
--- a/utils/request.js
+++ b/utils/request.js
@@ -84,6 +84,29 @@ function shouldSkipUnauthorizedRedirect() {
return route === 'pkgAccount/login/login' || route === 'pages/login/index' || route === 'pages/login/login'
}
+function normalizeRequestedLanguageHeaderValue(language) {
+ return String(language || getCurrentLanguage() || 'zh').trim().toLowerCase() === 'en' ? 'en' : 'zh'
+}
+
+function buildRequestHeader(header = {}, token = '') {
+ const normalizedHeader = { ...(header || {}) }
+ const requestedLanguageHeaderKey = Object.keys(normalizedHeader).find(key => String(key || '').toLowerCase() === 'x-requested-language')
+
+ if (requestedLanguageHeaderKey && requestedLanguageHeaderKey !== 'X-Requested-Language') {
+ delete normalizedHeader[requestedLanguageHeaderKey]
+ }
+
+ normalizedHeader['X-Requested-Language'] = normalizeRequestedLanguageHeaderValue(
+ requestedLanguageHeaderKey ? normalizedHeader[requestedLanguageHeaderKey] : normalizedHeader['X-Requested-Language']
+ )
+
+ return {
+ 'Content-Type': 'application/json',
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
+ ...normalizedHeader
+ }
+}
+
function handleUnauthorized(responseData) {
clearLoginSession()
@@ -126,12 +149,7 @@ export function request(options = {}) {
data: uppercaseMethod === 'GET'
? (params !== undefined ? normalizedParams : normalizedData)
: normalizedData,
- header: {
- 'Content-Type': 'application/json',
- 'X-Requested-Language': getCurrentLanguage(),
- ...(token ? { Authorization: `Bearer ${token}` } : {}),
- ...header
- },
+ header: buildRequestHeader(header, token),
success: (res) => {
const { statusCode, data: responseData } = res
if ((statusCode === 401) || (responseData && Number(responseData.code) === 401)) {
diff --git a/utils/reservation-flow.js b/utils/reservation-flow.js
new file mode 100644
index 0000000..4c672bf
--- /dev/null
+++ b/utils/reservation-flow.js
@@ -0,0 +1,176 @@
+const RESERVATION_FLOW_STEP = {
+ INSPECTION: 10,
+ BILL: 20,
+ CONTRACT: 30,
+ CHECKIN: 40
+}
+
+function getLanguage(language) {
+ return language === 'en' ? 'en' : 'zh'
+}
+
+function parseJsonStringMaybe(value) {
+ if (typeof value !== 'string') {
+ return value
+ }
+
+ const text = value.trim()
+ if (!text) {
+ return value
+ }
+
+ const isJsonLike = (
+ (text.startsWith('{') && text.endsWith('}'))
+ || (text.startsWith('[') && text.endsWith(']'))
+ )
+
+ if (!isJsonLike) {
+ return value
+ }
+
+ try {
+ return JSON.parse(text)
+ } catch (error) {
+ return value
+ }
+}
+
+function normalizeArray(value) {
+ if (Array.isArray(value)) {
+ return value
+ }
+ return []
+}
+
+export function parseReservationFlowResponse(response) {
+ const rawPayload = response && response.data ? response.data : response
+ const payload = parseJsonStringMaybe(rawPayload)
+
+ if (Array.isArray(payload)) {
+ return payload
+ }
+
+ if (payload && Array.isArray(payload.data)) {
+ return payload.data
+ }
+
+ if (payload && payload.data && Array.isArray(payload.data.data)) {
+ return payload.data.data
+ }
+
+ return []
+}
+
+export function getReservationOrderNo(source = {}) {
+ const candidates = [
+ source.orderNo,
+ source.OrderNo,
+ source.reservationOrderNo,
+ source.searchedOrderNo,
+ source.bookingOrderNo
+ ]
+
+ for (let index = 0; index < candidates.length; index += 1) {
+ const value = String(candidates[index] || '').trim()
+ if (value) {
+ return value
+ }
+ }
+
+ return ''
+}
+
+export function getReservationCurrentNode(flow = {}) {
+ const nodes = normalizeArray(flow && flow.flowNodes)
+ .filter(item => item && typeof item === 'object')
+ .sort((a, b) => Number(a.sort || 0) - Number(b.sort || 0))
+
+ if (!nodes.length) {
+ return null
+ }
+
+ return nodes.find(item => item.isCurrent) || nodes[nodes.length - 1]
+}
+
+export function buildReservationFlowMap(flowList = []) {
+ return normalizeArray(flowList)
+ .filter(item => item && typeof item === 'object')
+ .reduce((result, item) => {
+ const orderNo = getReservationOrderNo(item)
+ if (!orderNo) {
+ return result
+ }
+
+ result[orderNo] = {
+ ...item,
+ orderNo,
+ currentNode: getReservationCurrentNode(item)
+ }
+ return result
+ }, {})
+}
+
+export function attachReservationFlow(item = {}, flowMap = {}) {
+ const orderNo = getReservationOrderNo(item)
+ const matchedFlow = orderNo ? flowMap[orderNo] : null
+
+ return {
+ ...item,
+ reservationOrderNo: orderNo,
+ reservationFlow: matchedFlow || null,
+ reservationCurrentNode: matchedFlow && matchedFlow.currentNode ? matchedFlow.currentNode : null
+ }
+}
+
+export function getReservationStepMeta(step, language = 'zh') {
+ const currentLanguage = getLanguage(language)
+ const map = {
+ [RESERVATION_FLOW_STEP.INSPECTION]: {
+ label: currentLanguage === 'en' ? 'Inspection' : '验房',
+ actionText: currentLanguage === 'en' ? 'View Inspection' : '查看验房'
+ },
+ [RESERVATION_FLOW_STEP.BILL]: {
+ label: currentLanguage === 'en' ? 'Bill' : '账单',
+ actionText: currentLanguage === 'en' ? 'Go to Bill' : '去缴费'
+ },
+ [RESERVATION_FLOW_STEP.CONTRACT]: {
+ label: currentLanguage === 'en' ? 'Contract' : '合同',
+ actionText: currentLanguage === 'en' ? 'Go to Contract' : '去签约'
+ },
+ [RESERVATION_FLOW_STEP.CHECKIN]: {
+ label: currentLanguage === 'en' ? 'Check-in' : '入住',
+ actionText: currentLanguage === 'en' ? 'Go to Check-in' : '去入住'
+ }
+ }
+
+ return map[Number(step)] || {
+ label: currentLanguage === 'en' ? 'Current Step' : '当前步骤',
+ actionText: currentLanguage === 'en' ? 'View Current Step' : '查看当前步骤'
+ }
+}
+
+export function getReservationCurrentStepText(source = {}, language = 'zh') {
+ const node = source && source.reservationCurrentNode
+ ? source.reservationCurrentNode
+ : getReservationCurrentNode(source)
+
+ if (!node) {
+ return ''
+ }
+
+ return String(node.stepDesc || '').trim() || getReservationStepMeta(node.step, language).label
+}
+
+export function getReservationCurrentActionText(source = {}, language = 'zh') {
+ const node = source && source.reservationCurrentNode
+ ? source.reservationCurrentNode
+ : getReservationCurrentNode(source)
+
+ if (!node) {
+ return ''
+ }
+
+ return getReservationStepMeta(node.step, language).actionText
+}
+
+export { RESERVATION_FLOW_STEP }
diff --git a/utils/select-room-normalizers.js b/utils/select-room-normalizers.js
index e43006f..92b2ad8 100644
--- a/utils/select-room-normalizers.js
+++ b/utils/select-room-normalizers.js
@@ -364,6 +364,11 @@ export function normalizeAreaRoomType(item = {}) {
return {
code: item.code || '',
+ areaCode: item.areaCode || '',
+ buildingCode: item.buildingCode || item.buildingNo || '',
+ buildingName: item.buildingName || '',
+ buildingSectionCode: item.buildingSectionCode || item.unitCode || '',
+ buildingSectionName: item.buildingSectionName || item.unitName || '',
name: item.name || '',
nameEn: item.nameEn || '',
nameEnDisplay: compactEnglishText(item.nameEn || '', 22),
diff --git a/utils/session-sync.js b/utils/session-sync.js
index a261f7f..4fb6103 100644
--- a/utils/session-sync.js
+++ b/utils/session-sync.js
@@ -112,18 +112,25 @@ function pickFirstValue(...values) {
return ''
}
-function buildPendingContractQuery() {
- const user = getCurrentUser() || {}
- const userCode = pickFirstValue(user.userCode, user.code, user.UserCode)
- const query = {
- ProviderType: 'WechatMiniProgram'
+function buildPendingContractQuery(order = {}) {
+ const orderNo = pickFirstValue(order.orderNo, order.no, order.code, order.OrderNo)
+ if (!orderNo) {
+ return null
}
-
- if (userCode) {
- query.UserCode = userCode
+ return {
+ ProviderType: 'WechatMiniProgram',
+ OrderNo: orderNo
}
+}
- return query
+function buildEffectiveContractQuery(order = {}) {
+ const orderNo = pickFirstValue(order.orderNo, order.no, order.code, order.OrderNo)
+ if (!orderNo) {
+ return null
+ }
+ return {
+ OrderNo: orderNo
+ }
}
function enrichBookingLanguageFields(booking = {}) {
@@ -233,15 +240,23 @@ function requestStayState(language = 'zh') {
IsExport: false
}, language)
.then((orderResponse) => {
- return Promise.all([
- Promise.resolve(orderResponse),
- getMyPendingSignContracts(buildPendingContractQuery(), language).catch(() => []),
- getMyEffectiveContracts({}, language).catch(() => [])
- ])
- })
- .then(([orderResponse, pendingContractsResponse, effectiveContractsResponse]) => {
const orderList = normalizeBookingList(parseOrderListResponse(orderResponse), language)
const currentOrder = pickCurrentStayOrder(orderList)
+ const pendingContractQuery = buildPendingContractQuery(currentOrder)
+ const effectiveContractQuery = buildEffectiveContractQuery(currentOrder)
+
+ return Promise.all([
+ Promise.resolve(orderList),
+ Promise.resolve(currentOrder),
+ pendingContractQuery
+ ? getMyPendingSignContracts(pendingContractQuery, language).catch(() => [])
+ : Promise.resolve([]),
+ effectiveContractQuery
+ ? getMyEffectiveContracts(effectiveContractQuery, language).catch(() => [])
+ : Promise.resolve([])
+ ])
+ })
+ .then(([orderList, currentOrder, pendingContractsResponse, effectiveContractsResponse]) => {
const pendingContracts = parseContractList(pendingContractsResponse)
const effectiveContracts = parseContractList(effectiveContractsResponse)
const currentBooking = enrichBookingLanguageFields(pickCurrentBooking(orderList) || buildContractStepBooking(pendingContracts))