const { request, ensureLogin, cents } = require('../../utils/api.js') const managerRoles = ['STAFF', 'STORE_ADMIN', 'TENANT_ADMIN', 'PLATFORM_ADMIN'] const roomStatusLabels = { AVAILABLE: '空闲', MAINTENANCE: '维护中', RESERVED: '已预订', IN_USE: '使用中', CLEANING_REQUIRED: '待清洁', } const orderStatusLabels = { DRAFT: '草稿', PENDING_PAYMENT: '待支付', PAID: '已支付', RESERVED: '待开始', IN_PROGRESS: '进行中', FINISHED: '已结束', CANCELLED: '已取消', REFUNDING: '退款中', REFUNDED: '已退款', CLOSED: '已关闭', } Page({ data: { loading: false, errorMessage: '', canWrite: false, stores: [], selectedStoreId: '', selectedStoreName: '', rooms: [], orders: [], visibleOrders: [], busyRoomId: '', summary: { roomTotal: 0, available: 0, inUse: 0, attention: 0, activeOrders: 0, }, }, async onLoad() { await this.loadDashboard() }, async onPullDownRefresh() { await this.loadDashboard(this.data.selectedStoreId) wx.stopPullDownRefresh() }, async loadDashboard(preferredStoreId = '') { this.setData({ loading: true, errorMessage: '' }) try { await ensureLogin() const me = await request('/auth/me') const access = me.data?.access || { roles: [], capabilities: [], storeIds: [] } if (!access.roles.some((role) => managerRoles.includes(role))) { throw new Error('当前账号没有门店运营权限') } const [storesResponse, ordersResponse] = await Promise.all([ request('/management/stores'), request('/orders?page=1&pageSize=50'), ]) const stores = storesResponse.data || [] const selectedStoreId = stores.some((store) => store.id === preferredStoreId) ? preferredStoreId : (stores[0]?.id || '') this.setData({ canWrite: access.capabilities.includes('store.operation.write') || access.capabilities.includes('tenant.manage') || access.roles.includes('PLATFORM_ADMIN'), stores, orders: (ordersResponse.data?.items || []).map((item) => this.presentOrder(item)), selectedStoreId, selectedStoreName: stores.find((store) => store.id === selectedStoreId)?.name || '', }) await this.loadRooms(selectedStoreId) } catch (error) { this.setData({ errorMessage: error.message || '门店运营数据加载失败' }) } finally { this.setData({ loading: false }) } }, async selectStore(event) { const storeId = event.currentTarget.dataset.storeId if (!storeId || storeId === this.data.selectedStoreId) return const store = this.data.stores.find((item) => item.id === storeId) this.setData({ selectedStoreId: storeId, selectedStoreName: store?.name || '' }) await this.loadRooms(storeId) }, async loadRooms(storeId) { if (!storeId) { this.setData({ rooms: [], visibleOrders: [] }) this.refreshSummary() return } try { const response = await request(`/management/stores/${encodeURIComponent(storeId)}/rooms`) const rooms = (response.data || []).map((item) => ({ ...item, statusText: roomStatusLabels[item.operationalStatus] || item.operationalStatus, priceText: cents(item.basePriceCents), })) this.setData({ rooms, visibleOrders: this.data.orders.filter((item) => item.storeId === storeId), }) this.refreshSummary() } catch (error) { this.setData({ errorMessage: error.message || '房态加载失败' }) } }, refreshSummary() { const rooms = this.data.rooms const activeStatuses = ['PAID', 'RESERVED', 'IN_PROGRESS'] this.setData({ summary: { roomTotal: rooms.length, available: rooms.filter((item) => item.configurationStatus === 'ENABLED' && item.operationalStatus === 'AVAILABLE').length, inUse: rooms.filter((item) => ['RESERVED', 'IN_USE'].includes(item.operationalStatus)).length, attention: rooms.filter((item) => item.configurationStatus === 'DISABLED' || ['MAINTENANCE', 'CLEANING_REQUIRED'].includes(item.operationalStatus)).length, activeOrders: this.data.visibleOrders.filter((item) => activeStatuses.includes(item.status)).length, }, }) }, changeRoomStatus(event) { if (!this.data.canWrite) return const roomId = event.currentTarget.dataset.roomId wx.showActionSheet({ itemList: ['设为空闲', '设为维护中', '设为待清洁'], success: ({ tapIndex }) => { const statuses = ['AVAILABLE', 'MAINTENANCE', 'CLEANING_REQUIRED'] this.updateRoomStatus(roomId, { operationalStatus: statuses[tapIndex] }, '管理员小程序调整房态') }, }) }, toggleRoomConfiguration(event) { if (!this.data.canWrite) return const roomId = event.currentTarget.dataset.roomId const current = event.currentTarget.dataset.status const configurationStatus = current === 'ENABLED' ? 'DISABLED' : 'ENABLED' this.updateRoomStatus(roomId, { configurationStatus }, configurationStatus === 'ENABLED' ? '管理员小程序启用房间' : '管理员小程序停用房间') }, async updateRoomStatus(roomId, status, reason) { if (!roomId || this.data.busyRoomId) return this.setData({ busyRoomId: roomId, errorMessage: '' }) try { await request(`/management/rooms/${encodeURIComponent(roomId)}/status`, { method: 'PATCH', data: { storeId: this.data.selectedStoreId, ...status, reason }, }) await this.loadRooms(this.data.selectedStoreId) wx.showToast({ title: '房态已更新', icon: 'success' }) } catch (error) { this.setData({ errorMessage: error.message || '房态更新失败' }) } finally { this.setData({ busyRoomId: '' }) } }, openOrder(event) { const orderId = event.currentTarget.dataset.orderId if (!orderId) return wx.navigateTo({ url: `/pages/orders/detail?orderId=${encodeURIComponent(orderId)}` }) }, presentOrder(item) { return { ...item, statusText: orderStatusLabels[item.status] || item.status, amountText: cents(item.paidAmountCents || item.totalAmountCents), timeText: `${this.formatTime(item.startAt)} - ${this.formatTime(item.endAt)}`, } }, formatTime(value) { const date = new Date(value) if (Number.isNaN(date.getTime())) return String(value || '') const pad = (part) => String(part).padStart(2, '0') return `${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}` }, })