Files
xingque/pages/merchant-orders/merchant-orders.js
2026-07-09 00:17:03 +08:00

513 lines
16 KiB
JavaScript

// 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() {},
}));