// pages/merchant-orders/merchant-orders.js const app = getApp(); import { createPage, request } from '../../utils/base-page.js'; import { reconnectForRole } from '../../utils/role-tab-bar.js'; import { getOrderStatusText } from '../../utils/api-helper.js'; import { STAFF_API, isStaffMode, refreshStaffContext, syncStaffUi } from '../../utils/staff-api.js'; import { ensurePhoneAuth } from '../../utils/phone-auth.js'; import { fetchMerchantOrderStats } from '../../utils/merchant-order-stats.js'; const EMPTY_TAB = { list: [], page: 1, hasMore: true, isLoading: false }; function buildEmptyStatusData() { return { all: { ...EMPTY_TAB }, daifuwu: { ...EMPTY_TAB }, zhidingzhong: { ...EMPTY_TAB }, jinxingzhong: { ...EMPTY_TAB }, tuikuanzhong: { ...EMPTY_TAB }, yituikuan: { ...EMPTY_TAB }, tuikuanshibai: { ...EMPTY_TAB }, yiwancheng: { ...EMPTY_TAB }, jiesuanzhong: { ...EMPTY_TAB }, }; } const STATUS_TAG_STYLE = { 1: { color: '#E65100', bg: '#FFF3E0' }, 2: { color: '#1565C0', bg: '#E3F2FD' }, 3: { color: '#2E7D32', bg: '#E8F5E9' }, 4: { color: '#EF6C00', bg: '#FFF8E1' }, 5: { color: '#616161', bg: '#F5F5F5' }, 6: { color: '#C62828', bg: '#FFEBEE' }, 7: { color: '#BF360C', bg: '#FFE0B2' }, 8: { color: '#D84315', bg: '#FBE9E7' }, }; Page(createPage({ data: { shangpinleixing: [], xuanzhongLeixingId: null, searchKeyword: '', timeStart: '', timeEnd: '', statusList: [ { name: '全部', key: 'all', zhuangtaiList: [1, 2, 3, 4, 5, 6, 7, 8], color: '#333333' }, { name: '待服务', key: 'daifuwu', zhuangtaiList: [1], color: '#E65100' }, { name: '指定中', key: 'zhidingzhong', zhuangtaiList: [7], color: '#BF360C' }, { name: '进行中', key: 'jinxingzhong', zhuangtaiList: [2], color: '#2196F3' }, { name: '退款中', key: 'tuikuanzhong', zhuangtaiList: [4], color: '#FF9800' }, { name: '已退款', key: 'yituikuan', zhuangtaiList: [5], color: '#9E9E9E' }, { name: '退款失败', key: 'tuikuanshibai', zhuangtaiList: [6], color: '#F44336' }, { name: '已完成', key: 'yiwancheng', zhuangtaiList: [3], color: '#4CAF50' }, { name: '结算中', key: 'jiesuanzhong', zhuangtaiList: [8], color: '#FF5722' }, ], currentStatusKey: 'all', sjDingdanShuju: buildEmptyStatusData(), currentList: [], hasMore: true, isLoading: false, isLoadingMore: false, scrollViewRefreshing: false, listScrollTop: 0, defaultImg: '/images/default-order.png', ossImageUrl: app.globalData.ossImageUrl || '', pendingCount: 0, staffList: [], selectedStaffMemberId: '', selectedStaffLabel: '全部客服', canViewFinance: true, orderStats: { pending_count: 0, pending_amount: '0.00', completed_count: 0, completed_amount: '0.00', refund_count: 0, refund_amount: '0.00', dispatch_count: 0, dispatch_amount: '0.00', }, }, onLoad(options) { this._listInitialized = false; this._savedScrollTop = 0; this._skipShowRefresh = false; wx.setNavigationBarTitle({ title: isStaffMode() ? '客服派单' : '我的派单' }); syncStaffUi(this); if (isStaffMode()) { refreshStaffContext(request).then(() => syncStaffUi(this)); } this.loadShangpinLeixing(); this.registerNotificationComponent(); if (options && options.staff_member_id) { this.setData({ selectedStaffMemberId: String(options.staff_member_id), selectedStaffLabel: options.staff_label || '指定客服', }); } if (!isStaffMode()) { this.loadStaffList(); } }, async onShow() { const phoneOk = await ensurePhoneAuth({ redirect: true, role: 'shangjia' }); if (!phoneOk) return; this.registerNotificationComponent(); this.loadOrderStats(); if (wx.getStorageSync('uid')) { reconnectForRole('shangjia'); if (app.startImWhenReady) app.startImWhenReady(); } if (this._skipShowRefresh) { this._skipShowRefresh = false; this.restoreListScroll(); return; } // 从详情返回:保持列表与滚动位置,不整页重载 if (this._listInitialized) { this.restoreListScroll(); return; } }, restoreListScroll() { const top = this._savedScrollTop || 0; if (top <= 0) return; this.setData({ listScrollTop: 0 }); wx.nextTick(() => { this.setData({ listScrollTop: top }); }); }, onListScroll(e) { this._savedScrollTop = e.detail.scrollTop || 0; }, buildListParams(key, page) { const statusItem = this.data.statusList.find((s) => s.key === key); const params = { zhuangtai_list: statusItem.zhuangtaiList, page, page_size: 5, leixing_id: this.data.xuanzhongLeixingId, keyword: this.data.searchKeyword || undefined, }; const { timeStart, timeEnd } = this.data; if (timeStart) params.start_time = `${timeStart} 00:00:00`; if (timeEnd) params.end_time = `${timeEnd} 23:59:59`; if (this.data.selectedStaffMemberId) { params.staff_member_id = this.data.selectedStaffMemberId; } return params; }, async loadStaffList() { try { const res = await request({ url: STAFF_API.memberList, method: 'POST' }); if (res.data && res.data.code === 0) { const list = (res.data.data && res.data.data.list) || []; this.setData({ staffList: list }); } } catch (e) { /* 非老板无权限时静默 */ } }, onStaffFilterChange(e) { const idx = Number(e.detail.value); const list = this.data.staffList || []; const row = list[idx]; if (!row) return; this._savedScrollTop = 0; this.setData({ selectedStaffMemberId: String(row.id), selectedStaffLabel: row.nickname || row.uid, listScrollTop: 0, }); this.resetAllStatusData(); this.loadCurrentStatusOrders(true); this.loadOrderStats(); }, clearStaffFilter() { this._savedScrollTop = 0; this.setData({ selectedStaffMemberId: '', selectedStaffLabel: '全部客服', listScrollTop: 0, }); this.resetAllStatusData(); this.loadCurrentStatusOrders(true); this.loadOrderStats(); }, async loadOrderStats() { try { const params = {}; const { timeStart, timeEnd, selectedStaffMemberId, xuanzhongLeixingId } = this.data; if (timeStart) params.start_time = `${timeStart} 00:00:00`; if (timeEnd) params.end_time = `${timeEnd} 23:59:59`; if (selectedStaffMemberId) params.staff_member_id = selectedStaffMemberId; if (xuanzhongLeixingId) params.leixing_id = xuanzhongLeixingId; const data = await fetchMerchantOrderStats(request, params); const order = data.order || {}; this.setData({ orderStats: { pending_count: order.pending_count || 0, pending_amount: order.pending_amount || '0.00', completed_count: order.completed_count || 0, completed_amount: order.completed_amount || '0.00', refund_count: order.refund_count || 0, refund_amount: order.refund_amount || '0.00', dispatch_count: order.dispatch_count || 0, dispatch_amount: order.dispatch_amount || '0.00', }, pendingCount: order.pending_count || 0, canViewFinance: data.can_view_finance !== false, }); } catch (e) { console.warn('订单统计失败', e); } }, async loadShangpinLeixing() { try { const res = await request({ url: '/dingdan/dsqdhqddlx', method: 'POST', header: { 'content-type': 'application/json' }, }); if (res.data && (res.data.code === 200 || res.data.code === 0)) { let list = res.data.data.list || res.data.data || []; if (!Array.isArray(list)) list = []; const oss = this.data.ossImageUrl; const processed = list.map((item) => ({ ...item, full_tupian_url: item.tupian_url && !item.tupian_url.startsWith('http') ? oss + item.tupian_url : (item.tupian_url || '/images/default-type.png'), })); this.setData({ shangpinleixing: processed, xuanzhongLeixingId: processed[0]?.id || null, }); this.loadCurrentStatusOrders(true); } } catch (e) { console.error('加载商品类型失败', e); } }, selectLeixing(e) { const id = e.currentTarget.dataset.id; if (id === this.data.xuanzhongLeixingId) return; this._savedScrollTop = 0; this.setData({ xuanzhongLeixingId: id, listScrollTop: 0 }); this.resetAllStatusData(); this.loadCurrentStatusOrders(true); this.loadOrderStats(); }, onSearchInput(e) { this.setData({ searchKeyword: e.detail.value }); if (this._searchTimer) clearTimeout(this._searchTimer); this._searchTimer = setTimeout(() => { this.onSearchConfirm(); }, 500); }, onSearchConfirm() { this._savedScrollTop = 0; this.setData({ listScrollTop: 0 }); this.resetAllStatusData(); this.loadCurrentStatusOrders(true); this.loadOrderStats(); }, clearSearch() { this._savedScrollTop = 0; this.setData({ searchKeyword: '', listScrollTop: 0 }); this.resetAllStatusData(); this.loadCurrentStatusOrders(true); }, onTimeStartChange(e) { this.setData({ timeStart: e.detail.value }, () => this.triggerTimeFilterReload()); }, onTimeEndChange(e) { this.setData({ timeEnd: e.detail.value }, () => this.triggerTimeFilterReload()); }, triggerTimeFilterReload() { this._savedScrollTop = 0; this.setData({ listScrollTop: 0 }); this.resetAllStatusData(); this.loadCurrentStatusOrders(true); }, clearTimeFilter() { this._savedScrollTop = 0; this.setData({ timeStart: '', timeEnd: '', listScrollTop: 0 }); this.resetAllStatusData(); this.loadCurrentStatusOrders(true); this.loadOrderStats(); }, pickQuickRange(e) { const days = Number(e.currentTarget.dataset.days); if (!days) { this.clearTimeFilter(); return; } const end = new Date(); const start = new Date(); start.setDate(end.getDate() - (days - 1)); const fmt = (d) => { const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, '0'); const day = String(d.getDate()).padStart(2, '0'); return `${y}-${m}-${day}`; }; this._savedScrollTop = 0; this.setData({ timeStart: fmt(start), timeEnd: fmt(end), listScrollTop: 0, }); this.resetAllStatusData(); this.loadCurrentStatusOrders(true); this.loadOrderStats(); }, switchStatus(e) { const key = e.currentTarget.dataset.key; if (key === this.data.currentStatusKey) return; this._savedScrollTop = 0; this.setData({ currentStatusKey: key, listScrollTop: 0 }); const tabData = this.data.sjDingdanShuju[key]; if (tabData.list.length === 0 && tabData.hasMore && !tabData.isLoading) { this.loadCurrentStatusOrders(true); } else { this.refreshCurrentListView(); } }, async loadCurrentStatusOrders(isRefresh = false, pageOverride = null) { const key = this.data.currentStatusKey; const tabData = this.data.sjDingdanShuju[key]; if (tabData.isLoading) return; const page = isRefresh ? 1 : (pageOverride || tabData.page + 1); if (!isRefresh && !tabData.hasMore) return; this.setData({ [`sjDingdanShuju.${key}.isLoading`]: true, isLoading: isRefresh && tabData.list.length === 0, isLoadingMore: !isRefresh, }); try { const statusItem = this.data.statusList.find((s) => s.key === key); const params = this.buildListParams(key, page); const res = await request({ url: isStaffMode() ? STAFF_API.orderList : '/dingdan/sjdingdanhq', method: 'POST', data: params, header: { 'content-type': 'application/json' }, }); const code = res.data.code; if (code === 200 || code === 0) { const newList = res.data.data.list || []; const total = Number(res.data.data.total); const hasMore = typeof res.data.data.has_more === 'boolean' ? res.data.data.has_more : (Number.isFinite(total) ? page * params.page_size < total : newList.length >= params.page_size); const processed = newList.map((item) => this.processOrderItem(item, statusItem.color)); const prevList = isRefresh ? [] : tabData.list; const updatedList = [...prevList, ...processed]; // 按订单 ID 去重,避免分页边界重复 const seen = new Set(); const deduped = updatedList.filter((row) => { const id = row.dingdan_id; if (!id || seen.has(id)) return false; seen.add(id); return true; }); this.setData({ [`sjDingdanShuju.${key}.list`]: deduped, [`sjDingdanShuju.${key}.page`]: page, [`sjDingdanShuju.${key}.hasMore`]: hasMore, [`sjDingdanShuju.${key}.isLoading`]: false, isLoading: false, isLoadingMore: false, scrollViewRefreshing: false, }); this._listInitialized = true; this.refreshCurrentListView(); } else { throw new Error(res.data.msg || '加载失败'); } } catch (err) { console.error('加载订单失败', err); wx.showToast({ title: err.message || '加载失败', icon: 'none' }); this.setData({ [`sjDingdanShuju.${key}.isLoading`]: false, isLoading: false, isLoadingMore: false, scrollViewRefreshing: false, }); this.refreshCurrentListView(); } }, refreshCurrentListView() { const key = this.data.currentStatusKey; const tabData = this.data.sjDingdanShuju[key]; this.setData({ currentList: tabData.list, hasMore: tabData.hasMore, isLoading: tabData.isLoading, }); }, resetAllStatusData() { const patch = {}; Object.keys(this.data.sjDingdanShuju).forEach((k) => { patch[`sjDingdanShuju.${k}`] = { ...EMPTY_TAB }; }); this.setData(patch); this.refreshCurrentListView(); }, resetCurrentStatusData() { const key = this.data.currentStatusKey; this.setData({ [`sjDingdanShuju.${key}`]: { ...EMPTY_TAB }, }); this.refreshCurrentListView(); }, async loadPendingCount() { return this.loadOrderStats(); }, onPullDownRefresh() { if (this.data.isLoading || this.data.isLoadingMore) { this.setData({ scrollViewRefreshing: false }); return; } this._savedScrollTop = 0; this.setData({ scrollViewRefreshing: true, listScrollTop: 0 }); this.resetCurrentStatusData(); this.loadCurrentStatusOrders(true); }, onReachBottom() { const key = this.data.currentStatusKey; const tabData = this.data.sjDingdanShuju[key]; if (tabData.isLoading || !tabData.hasMore) return; this.loadCurrentStatusOrders(false); }, onLoadMoreTap() { const key = this.data.currentStatusKey; const tabData = this.data.sjDingdanShuju[key]; if (tabData.isLoading || !tabData.hasMore) return; this.loadCurrentStatusOrders(false); }, processOrderItem(item, zhuangtaiColor) { const st = Number(item.zhuangtai); const tagStyle = STATUS_TAG_STYLE[st] || { color: zhuangtaiColor || '#333', bg: '#F5F5F5' }; return { ...item, zhuangtaiZh: getOrderStatusText(st), zhuangtaiColor: tagStyle.color, zhuangtaiBg: tagStyle.bg, creat_time: item.creat_time || item.create_time || '', }; }, goToSjDingdanXiangqing(e) { const item = e.currentTarget.dataset.item; if (!item) return; this._skipShowRefresh = true; const dataStr = encodeURIComponent(JSON.stringify(item)); wx.navigateTo({ url: `/pages/merchant-order-detail/merchant-order-detail?dingdanData=${dataStr}`, }); }, onImageError() {}, }));