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: '已关闭', } const orderActionsByStatus = { DRAFT: [{ action: 'SUBMIT', label: '提交订单' }, { action: 'CANCEL', label: '取消订单' }, { action: 'CLOSE', label: '关闭订单' }], PENDING_PAYMENT: [{ action: 'CANCEL', label: '取消订单' }, { action: 'CLOSE', label: '关闭订单' }], PAID: [{ action: 'RESERVE', label: '确认预留' }, { action: 'START', label: '开始使用' }, { action: 'CANCEL', label: '取消订单' }, { action: 'BEGIN_REFUND', label: '发起退款' }], RESERVED: [{ action: 'START', label: '开始使用' }, { action: 'CANCEL', label: '取消订单' }, { action: 'BEGIN_REFUND', label: '发起退款' }], IN_PROGRESS: [{ action: 'FINISH', label: '结束使用' }, { action: 'BEGIN_REFUND', label: '发起退款' }], FINISHED: [{ action: 'BEGIN_REFUND', label: '发起退款' }, { action: 'CLOSE', label: '关闭订单' }], CANCELLED: [{ action: 'BEGIN_REFUND', label: '发起退款' }, { action: 'CLOSE', label: '关闭订单' }], REFUNDING: [{ action: 'COMPLETE_REFUND', label: '确认退款完成' }], REFUNDED: [{ action: 'CLOSE', label: '关闭订单' }], } Page({ data: { loading: false, errorMessage: '', canWrite: false, stores: [], selectedStoreId: '', selectedStoreName: '', rooms: [], orders: [], visibleOrders: [], orderFilters: [ { value: '', label: '全部' }, { value: 'PENDING_PAYMENT', label: '待支付' }, { value: 'RESERVED', label: '待开始' }, { value: 'IN_PROGRESS', label: '进行中' }, { value: 'FINISHED', label: '已结束' }, { value: 'REFUNDING', label: '退款中' }, ], selectedOrderStatus: '', busyRoomId: '', busyOrderId: '', summary: { roomTotal: 0, available: 0, inUse: 0, attention: 0, activeOrders: 0, }, }, async onLoad() { await this.loadDashboard() }, async onShow() { if (this.data.selectedStoreId) await this.loadStoreData(this.data.selectedStoreId) }, 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 = await request('/management/stores') 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, selectedStoreId, selectedStoreName: stores.find((store) => store.id === selectedStoreId)?.name || '', }) await this.loadStoreData(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 || '', selectedOrderStatus: '', }) await this.loadStoreData(storeId) }, async loadStoreData(storeId) { if (!storeId) { this.setData({ rooms: [], orders: [], visibleOrders: [] }) this.refreshSummary() return } try { const encodedStoreId = encodeURIComponent(storeId) const [roomsResponse, ordersResponse] = await Promise.all([ request(`/management/stores/${encodedStoreId}/rooms`), request(`/orders?page=1&pageSize=50&storeId=${encodedStoreId}`), ]) const rooms = (roomsResponse.data || []).map((item) => ({ ...item, statusText: roomStatusLabels[item.operationalStatus] || item.operationalStatus, priceText: cents(item.basePriceCents), })) const orders = (ordersResponse.data?.items || []).map((item) => this.presentOrder(item)) this.setData({ rooms, orders }) this.applyOrderFilter() this.refreshSummary() } catch (error) { this.setData({ errorMessage: error.message || '门店运营数据加载失败' }) } }, selectOrderFilter(event) { this.setData({ selectedOrderStatus: event.currentTarget.dataset.status || '' }) this.applyOrderFilter() }, applyOrderFilter() { const status = this.data.selectedOrderStatus this.setData({ visibleOrders: status ? this.data.orders.filter((item) => item.status === status) : this.data.orders, }) }, 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.orders.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.loadStoreData(this.data.selectedStoreId) wx.showToast({ title: '房态已更新', icon: 'success' }) } catch (error) { this.setData({ errorMessage: error.message || '房态更新失败' }) } finally { this.setData({ busyRoomId: '' }) } }, createOrder() { if (!this.data.canWrite || !this.data.selectedStoreId) return wx.navigateTo({ url: `/pages/manager/order-create?storeId=${encodeURIComponent(this.data.selectedStoreId)}&storeName=${encodeURIComponent(this.data.selectedStoreName)}`, }) }, manageOrder(event) { if (!this.data.canWrite) return const orderId = event.currentTarget.dataset.orderId const status = event.currentTarget.dataset.status const actions = orderActionsByStatus[status] || [] if (!actions.length) { wx.showToast({ title: '当前状态无可用动作', icon: 'none' }) return } wx.showActionSheet({ itemList: actions.map((item) => item.label), success: ({ tapIndex }) => { const selected = actions[tapIndex] wx.showModal({ title: selected.label, content: `确认对订单执行“${selected.label}”吗?`, success: ({ confirm }) => { if (confirm) this.executeOrderAction(orderId, selected) }, }) }, }) }, addOrderNote(event) { if (!this.data.canWrite) return const orderId = event.currentTarget.dataset.orderId wx.showModal({ title: '添加订单备注', editable: true, placeholderText: '请输入备注内容', success: ({ confirm, content }) => { const note = String(content || '').trim() if (confirm && note) this.saveOrderNote(orderId, note) }, }) }, async executeOrderAction(orderId, selected) { if (!orderId || this.data.busyOrderId) return this.setData({ busyOrderId: orderId, errorMessage: '' }) try { await request(`/management/orders/${encodeURIComponent(orderId)}/actions`, { method: 'POST', data: { action: selected.action, reason: `管理员小程序:${selected.label}` }, }) await this.loadStoreData(this.data.selectedStoreId) wx.showToast({ title: '订单已更新', icon: 'success' }) } catch (error) { this.setData({ errorMessage: error.message || '订单操作失败' }) } finally { this.setData({ busyOrderId: '' }) } }, async saveOrderNote(orderId, note) { if (!orderId || this.data.busyOrderId) return this.setData({ busyOrderId: orderId, errorMessage: '' }) try { await request(`/management/orders/${encodeURIComponent(orderId)}/note`, { method: 'POST', data: { note }, }) wx.showToast({ title: '备注已保存', icon: 'success' }) } catch (error) { this.setData({ errorMessage: error.message || '订单备注保存失败' }) } finally { this.setData({ busyOrderId: '' }) } }, 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())}` }, })