diff --git a/app.js b/app.js
index 811ef86..f9d7d0a 100644
--- a/app.js
+++ b/app.js
@@ -1,414 +1,427 @@
-// app.js
-import GoEasy from './static/lib/goeasy-2.13.24.esm.min';
-const ChatCore = require('./utils/chat-core');
-import { check } from './utils/phone-auth';
-import { getPrimaryRole, lockPrimaryRole, migrateLegacyCenterRole, PRIMARY_DEFAULT_PAGES } from './utils/primary-role';
-
-// 三端固定首页
-const roleDefaultPage = PRIMARY_DEFAULT_PAGES;
-
-App({
- globalData: {
-
- hasShownPopupOnColdStart: false,
-
- apiBaseUrl: 'https://www.abas.asia/hqhd',
- ossImageUrl: '',
- morentouxiang: '',
- dashouguize: '',
-
- shangpinliebiao: [],
- shangpinzhuanqu: [],
- shangpinleixing: [],
- shangpinlunbo: [],
- shangpingonggao: '',
- lunbozhanwei: '/images/lunbozhanwei.jpg',
-
- dingdanTiaoshu: {
- daifuwu: 0,
- fuwuzhong: 0,
- yiwancheng: 0,
- yituikuan: 0
- },
-
- dashouqun: '',
- dashouqunid: '',
- guanshiqun: '',
- guanshiqunid: '',
-
- appId:'wx0e4be86faac4a8d1',
-
- shangjiastatus: 0,
- dashoustatus: 0,
- guanshistatus: 0,
- dashouNicheng: '',
- zhanghaoStatus: null,
- dashouzhuangtai: null,
- yongjin: null,
- zonge: null,
- yajin: null,
- chenghao: '',
- jinfen: null,
- clumber: [{
- huiyuanid: '',
- huiyuanming: '',
- daoqi: ''
- }],
- chengjiaoliang: null,
- zaixianZhuangtai: null,
-
- guanshi: {
- nicheng: '',
- uid: '',
- touxiang: '',
- gszhstatus: '',
- yaoqingzongshu: 0,
- fenyongzonge: '0.00',
- fenyongtixian: '0.00'
- },
-
- shangjia: {
- sjzhzhuangtai: '',
- sjyue: '',
- fadanzong: null,
- tuikuanzong: null,
- riliushui: '',
- yueliushui: ''
- },
-
- liaotian_liebiao: [],
- xshenfen: 1,
-
- goEasyConfig: null,
- kefuConfig: {
- link: '',
- enterpriseId: ''
- },
-
- cosConfig: {
- bucket: '',
- region: 'ap-shanghai',
- uploadPathPrefix: 'order/'
- },
-
- goEasyConnection: {
- status: 'disconnected',
- userId: '',
- identityType: '',
- lastConnectTime: 0,
- autoReconnect: true,
- reconnectAttempts: 0,
- maxReconnectAttempts: 5,
- heartbeatInterval: null,
- cacheKeys: {
- savedConnection: 'savedGoEasyConnection',
- userId: 'goEasyUserId',
- identityType: 'currentGoEasyIdentity',
- connectTime: 'goEasyConnectTime'
- },
- config: {
- heartbeatInterval: 20000,
- reconnectDelay: 3000,
- offlineTimeout: 30000,
- cacheValidityHours: 12
- }
- },
-
- messageManager: {
- unreadTotal: 0,
- unreadMap: {},
- latestMessages: [],
- notificationQueue: [],
- notificationVisible: false,
- lastNotificationTime: 0,
- notificationCooldown: 3000,
- tabBarIndex: 2,
- showTabBarBadge: true,
- customTabBar: true,
- tabBarBadgeText: 0,
- soundEnabled: true,
- vibrationEnabled: true,
- doNotDisturb: false,
- doNotDisturbStart: '22:00',
- doNotDisturbEnd: '08:00',
- notificationStyle: {
- position: 'top',
- duration: 3000,
- backgroundColor: '#00f7ff',
- textColor: '#ffffff',
- borderRadius: '16rpx',
- boxShadow: '0 10rpx 30rpx rgba(0, 247, 255, 0.3)'
- },
- currentNotification: null,
- cacheKeys: {
- messageSettings: 'messageSettings',
- unreadTotal: 'messageUnreadTotal',
- latestMessages: 'latestMessages',
- notificationMuted: 'notificationMuted'
- }
- },
-
- pageState: {
- currentPage: '',
- isInChatPage: false,
- currentChatId: '',
- lastPageUpdate: 0
- },
-
- eventListeners: {},
- debugMode: false,
- currentUser: null,
- currentRole: 'normal',
- primaryRole: 'normal'
- },
-
- // 核心启动流程
- async onLaunch() {
- // ① 隐藏返回首页按钮
- wx.hideHomeButton();
- wx.onAppRoute((res) => {
- wx.hideHomeButton();
- const path = (res && res.path) || '';
- if (path === 'pages/manager/manager' || path === 'pages/leader/leader') {
- migrateLegacyCenterRole(this);
- lockPrimaryRole('dashou', this);
- }
- });
-
- // ② 恢复主端并跳转
- migrateLegacyCenterRole(this);
- const savedRole = getPrimaryRole(this);
- lockPrimaryRole(savedRole, this);
-
- const targetPage = roleDefaultPage[savedRole] || '/pages/index/index';
- if (savedRole !== 'normal') {
- setTimeout(() => {
- wx.reLaunch({ url: targetPage });
- }, 0);
- }
-
- // ③ 应用本地缓存配置
- const cachedConfig = this.readConfigFromStorage();
- if (cachedConfig) {
- this.applyDynamicConfig(cachedConfig);
- }
-
- // ④ 初始化
- this.initGoEasyWithConfig(); // 只有appkey有效时才真正初始化
- ChatCore.initGlobalMessageSystem(this);
- this.initCurrentUser();
-
- // ⑤ 建立连接
- const saved = this.getSavedConnection();
- console.log('【启动】缓存身份:', saved ? saved.identityType : '无',
- 'userId:', saved ? saved.userId : '无');
- setTimeout(() => {
- if (this.startImWhenReady) this.startImWhenReady();
- else if (this.ensureConnection) this.ensureConnection();
- }, 300);
-
- // ⑥ 获取远程配置
- this.fetchConfigSafely()
- .then(latestConfig => {
- this.saveConfigToStorage(latestConfig);
- this.applyDynamicConfig(latestConfig);
- this.initGoEasyWithConfig();
- this.initCurrentUser();
- if (this.startImWhenReady) this.startImWhenReady();
- else if (this.connectForCurrentRole) this.connectForCurrentRole();
- console.log('远程配置更新完成');
- })
- .catch(err => {
- console.warn('远程配置获取失败,继续使用本地缓存', err);
- });
-
- // ⑦ 手机号认证检查
- try {
- const needAuth = await check();
- if (needAuth) {
- wx.reLaunch({ url: '/pages/phone-auth/phone-auth' });
- return;
- }
- } catch (e) {}
- },
-
- // 连接管理方法
- getSavedConnection() {
- try {
- const saved = wx.getStorageSync(
- this.globalData.goEasyConnection.cacheKeys.savedConnection
- );
- return saved || null;
- } catch (e) {
- return null;
- }
- },
-
- // 配置缓存读写
- CONFIG_CACHE_KEY: 'app_dynamic_config',
-
- readConfigFromStorage() {
- try {
- const data = wx.getStorageSync(this.CONFIG_CACHE_KEY);
- if (data && typeof data === 'object') {
- return data;
- }
- } catch (e) {}
- return null;
- },
-
- saveConfigToStorage(config) {
- try {
- if (config && typeof config === 'object') {
- wx.setStorageSync(this.CONFIG_CACHE_KEY, config);
- }
- } catch (e) {}
- },
-
- // 获取远程配置
- fetchConfigSafely() {
- return new Promise((resolve) => {
- wx.request({
- url: this.globalData.apiBaseUrl + '/peizhi/qdpzhq',
- method: 'POST',
- header: { 'content-type': 'application/json' },
- success: (res) => {
- if (res.statusCode === 200 && res.data && res.data.code === 0 && res.data.data) {
- resolve(res.data.data);
- } else {
- console.warn('远程配置接口返回异常,使用已有配置');
- resolve(this.readConfigFromStorage() || {});
- }
- },
- fail: (err) => {
- console.warn('远程配置网络请求失败,使用已有配置', err);
- resolve(this.readConfigFromStorage() || {});
- }
- });
- });
- },
-
- // 获取远程配置
- fetchDynamicConfig() {
- return new Promise((resolve, reject) => {
- wx.request({
- url: this.globalData.apiBaseUrl + '/peizhi/qdpzhq',
- method: 'POST',
- header: { 'content-type': 'application/json' },
- success: (res) => {
- if (res.statusCode === 200 && res.data && res.data.code === 0) {
- resolve(res.data.data);
- } else {
- reject(new Error(res.data?.msg || '接口返回错误'));
- }
- },
- fail: reject
- });
- });
- },
-
- applyDynamicConfig(config) {
- if (!config || typeof config !== 'object') return;
-
- if (config.cos) {
- this.globalData.ossImageUrl = config.cos.ossImageUrl || '';
- this.globalData.cosConfig = {
- bucket: config.cos.bucket || '',
- region: config.cos.region || 'ap-shanghai',
- uploadPathPrefix: config.cos.uploadPathPrefix || 'order/'
- };
- }
-
- if (config.goEasy) {
- this.globalData.goEasyConfig = {
- host: config.goEasy.host || 'hangzhou.goeasy.io',
- appkey: config.goEasy.appkey || ''
- };
- }
-
- if (config.otherConfig) {
- this.globalData.morentouxiang = config.otherConfig.morentouxiang || '';
- this.globalData.dashouguize = config.otherConfig.dashouguize || '';
- }
-
- if (config.kefu) {
- this.globalData.kefuConfig = {
- link: config.kefu.link || '',
- enterpriseId: config.kefu.enterpriseId || ''
- };
- }
- },
-
- // 仅在有效 appkey 时初始化
- initGoEasyWithConfig() {
- const cfg = this.globalData.goEasyConfig;
- if (!cfg || !cfg.appkey) {
- console.warn('GoEasy appkey 未配置,聊天功能将在配置就绪后自动恢复');
- return;
- }
-
- // 避免重复初始化
- if (wx.goEasy && wx.GoEasy) {
- console.log('GoEasy 已初始化,跳过');
- return;
- }
-
- try {
- wx.goEasy = GoEasy.getInstance({
- host: cfg.host || 'hangzhou.goeasy.io',
- appkey: cfg.appkey,
- modules: ['im', 'pubsub']
- });
- wx.GoEasy = GoEasy;
- console.log('GoEasy 初始化成功');
- if (this.startImWhenReady && wx.getStorageSync('uid')) {
- setTimeout(() => this.startImWhenReady(), 100);
- }
- } catch (error) {
- console.error('GoEasy 初始化失败:', error);
- }
- },
-
- initCurrentUser() {
- const uid = wx.getStorageSync('uid');
- if (uid) {
- this.globalData.currentUser = {
- id: uid,
- name: '用户' + uid.substring(0, 6),
- avatar: this.globalData.ossImageUrl + this.globalData.morentouxiang
- };
- }
- },
-
- emitEvent(eventName, data) {
- if (this.globalData.eventListeners[eventName]) {
- this.globalData.eventListeners[eventName].forEach(callback => {
- try { callback(data); } catch (error) { console.error(error); }
- });
- }
- },
-
- /** 切换角色并通知刷新 */
- setCurrentRole(role) {
- if (!role) return;
- this.globalData.currentRole = role;
- wx.setStorageSync('currentRole', role);
- this.emitEvent('currentRoleChanged', { role });
- },
-
- on(eventName, callback) {
- if (!this.globalData.eventListeners[eventName]) {
- this.globalData.eventListeners[eventName] = [];
- }
- this.globalData.eventListeners[eventName].push(callback);
- },
-
- off(eventName, callback) {
- if (this.globalData.eventListeners[eventName]) {
- const index = this.globalData.eventListeners[eventName].indexOf(callback);
- if (index > -1) {
- this.globalData.eventListeners[eventName].splice(index, 1);
- }
- }
- }
+// app.js
+import GoEasy from './static/lib/goeasy-2.13.24.esm.min';
+const ChatCore = require('./utils/chat-core');
+import { ensurePhoneAuth } from './utils/phone-auth';
+import { getPrimaryRole, lockPrimaryRole, migrateLegacyCenterRole, PRIMARY_DEFAULT_PAGES } from './utils/primary-role';
+
+// 三端固定首页
+const roleDefaultPage = PRIMARY_DEFAULT_PAGES;
+
+App({
+ globalData: {
+
+ hasShownPopupOnColdStart: false,
+
+ apiBaseUrl: 'https://www.abas.asia/hqhd',
+ ossImageUrl: '',
+ morentouxiang: '',
+ dashouguize: '',
+
+ shangpinliebiao: [],
+ shangpinzhuanqu: [],
+ shangpinleixing: [],
+ shangpinlunbo: [],
+ shangpingonggao: '',
+ lunbozhanwei: '/images/lunbozhanwei.jpg',
+
+ dingdanTiaoshu: {
+ daifuwu: 0,
+ fuwuzhong: 0,
+ yiwancheng: 0,
+ yituikuan: 0
+ },
+
+ dashouqun: '',
+ dashouqunid: '',
+ guanshiqun: '',
+ guanshiqunid: '',
+
+ appId:'wx0e4be86faac4a8d1',
+
+ shangjiastatus: 0,
+ dashoustatus: 0,
+ guanshistatus: 0,
+ dashouNicheng: '',
+ zhanghaoStatus: null,
+ dashouzhuangtai: null,
+ yongjin: null,
+ zonge: null,
+ yajin: null,
+ chenghao: '',
+ jinfen: null,
+ clumber: [{
+ huiyuanid: '',
+ huiyuanming: '',
+ daoqi: ''
+ }],
+ chengjiaoliang: null,
+ zaixianZhuangtai: null,
+
+ guanshi: {
+ nicheng: '',
+ uid: '',
+ touxiang: '',
+ gszhstatus: '',
+ yaoqingzongshu: 0,
+ fenyongzonge: '0.00',
+ fenyongtixian: '0.00'
+ },
+
+ shangjia: {
+ sjzhzhuangtai: '',
+ sjyue: '',
+ fadanzong: null,
+ tuikuanzong: null,
+ riliushui: '',
+ yueliushui: ''
+ },
+
+ liaotian_liebiao: [],
+ xshenfen: 1,
+
+ goEasyConfig: null,
+ kefuConfig: {
+ link: '',
+ enterpriseId: ''
+ },
+
+ cosConfig: {
+ bucket: '',
+ region: 'ap-shanghai',
+ uploadPathPrefix: 'order/'
+ },
+
+ goEasyConnection: {
+ status: 'disconnected',
+ userId: '',
+ identityType: '',
+ lastConnectTime: 0,
+ autoReconnect: true,
+ reconnectAttempts: 0,
+ maxReconnectAttempts: 5,
+ heartbeatInterval: null,
+ cacheKeys: {
+ savedConnection: 'savedGoEasyConnection',
+ userId: 'goEasyUserId',
+ identityType: 'currentGoEasyIdentity',
+ connectTime: 'goEasyConnectTime'
+ },
+ config: {
+ heartbeatInterval: 20000,
+ reconnectDelay: 3000,
+ offlineTimeout: 30000,
+ cacheValidityHours: 12
+ }
+ },
+
+ messageManager: {
+ unreadTotal: 0,
+ unreadMap: {},
+ latestMessages: [],
+ notificationQueue: [],
+ notificationVisible: false,
+ lastNotificationTime: 0,
+ notificationCooldown: 3000,
+ tabBarIndex: 2,
+ showTabBarBadge: true,
+ customTabBar: true,
+ tabBarBadgeText: 0,
+ soundEnabled: true,
+ vibrationEnabled: true,
+ doNotDisturb: false,
+ doNotDisturbStart: '22:00',
+ doNotDisturbEnd: '08:00',
+ notificationStyle: {
+ position: 'top',
+ duration: 3000,
+ backgroundColor: '#00f7ff',
+ textColor: '#ffffff',
+ borderRadius: '16rpx',
+ boxShadow: '0 10rpx 30rpx rgba(0, 247, 255, 0.3)'
+ },
+ currentNotification: null,
+ cacheKeys: {
+ messageSettings: 'messageSettings',
+ unreadTotal: 'messageUnreadTotal',
+ latestMessages: 'latestMessages',
+ notificationMuted: 'notificationMuted'
+ }
+ },
+
+ pageState: {
+ currentPage: '',
+ isInChatPage: false,
+ currentChatId: '',
+ lastPageUpdate: 0
+ },
+
+ eventListeners: {},
+ debugMode: false,
+ currentUser: null,
+ currentRole: 'normal',
+ primaryRole: 'normal'
+ },
+
+ // 核心启动流程
+ async onLaunch() {
+ // ① 隐藏返回首页按钮
+ wx.hideHomeButton();
+ wx.onAppRoute((res) => {
+ wx.hideHomeButton();
+ const path = (res && res.path) || '';
+ if (path === 'pages/manager/manager' || path === 'pages/leader/leader') {
+ migrateLegacyCenterRole(this);
+ lockPrimaryRole('dashou', this);
+ }
+ });
+
+ // ② 已登录时先恢复子客服身份,再决定主端 Tab(避免客服被当成未入驻)
+ if (wx.getStorageSync('token')) {
+ try {
+ const { restoreStaffContextAfterAuth } = require('./utils/staff-api.js');
+ await restoreStaffContextAfterAuth();
+ } catch (e) {}
+ }
+
+ migrateLegacyCenterRole(this);
+ const savedRole = getPrimaryRole(this);
+ lockPrimaryRole(savedRole, this);
+
+ // ②b 冷启动:有 token 时先问后端是否需要手机号认证
+ if (wx.getStorageSync('token')) {
+ const phoneOk = await ensurePhoneAuth({ redirect: true });
+ if (!phoneOk) return;
+ }
+
+ const targetPage = roleDefaultPage[savedRole] || '/pages/index/index';
+ if (savedRole !== 'normal') {
+ setTimeout(() => {
+ wx.reLaunch({ url: targetPage });
+ }, 0);
+ }
+
+ // ③ 应用本地缓存配置
+ const cachedConfig = this.readConfigFromStorage();
+ if (cachedConfig) {
+ this.applyDynamicConfig(cachedConfig);
+ }
+
+ // ④ 初始化
+ this.initGoEasyWithConfig(); // 只有appkey有效时才真正初始化
+ ChatCore.initGlobalMessageSystem(this);
+ this.initCurrentUser();
+ if (wx.getStorageSync('token') && this.emitEvent) {
+ this.emitEvent('staffContextChanged', {});
+ }
+
+ // ⑤ 建立连接
+ const saved = this.getSavedConnection();
+ console.log('【启动】缓存身份:', saved ? saved.identityType : '无',
+ 'userId:', saved ? saved.userId : '无');
+ setTimeout(() => {
+ if (this.startImWhenReady) this.startImWhenReady();
+ else if (this.ensureConnection) this.ensureConnection();
+ }, 300);
+
+ // ⑥ 获取远程配置
+ this.fetchConfigSafely()
+ .then(latestConfig => {
+ this.saveConfigToStorage(latestConfig);
+ this.applyDynamicConfig(latestConfig);
+ this.initGoEasyWithConfig();
+ this.initCurrentUser();
+ if (this.startImWhenReady) this.startImWhenReady();
+ else if (this.connectForCurrentRole) this.connectForCurrentRole();
+ console.log('远程配置更新完成');
+ })
+ .catch(err => {
+ console.warn('远程配置获取失败,继续使用本地缓存', err);
+ });
+ },
+
+ /** 从后台切回前台时再次向后端校验 */
+ async onShow() {
+ if (!wx.getStorageSync('token')) return;
+ await ensurePhoneAuth({ redirect: true });
+ },
+
+ // 连接管理方法
+ getSavedConnection() {
+ try {
+ const saved = wx.getStorageSync(
+ this.globalData.goEasyConnection.cacheKeys.savedConnection
+ );
+ return saved || null;
+ } catch (e) {
+ return null;
+ }
+ },
+
+ // 配置缓存读写
+ CONFIG_CACHE_KEY: 'app_dynamic_config',
+
+ readConfigFromStorage() {
+ try {
+ const data = wx.getStorageSync(this.CONFIG_CACHE_KEY);
+ if (data && typeof data === 'object') {
+ return data;
+ }
+ } catch (e) {}
+ return null;
+ },
+
+ saveConfigToStorage(config) {
+ try {
+ if (config && typeof config === 'object') {
+ wx.setStorageSync(this.CONFIG_CACHE_KEY, config);
+ }
+ } catch (e) {}
+ },
+
+ // 获取远程配置
+ fetchConfigSafely() {
+ return new Promise((resolve) => {
+ wx.request({
+ url: this.globalData.apiBaseUrl + '/peizhi/qdpzhq',
+ method: 'POST',
+ header: { 'content-type': 'application/json' },
+ success: (res) => {
+ if (res.statusCode === 200 && res.data && res.data.code === 0 && res.data.data) {
+ resolve(res.data.data);
+ } else {
+ console.warn('远程配置接口返回异常,使用已有配置');
+ resolve(this.readConfigFromStorage() || {});
+ }
+ },
+ fail: (err) => {
+ console.warn('远程配置网络请求失败,使用已有配置', err);
+ resolve(this.readConfigFromStorage() || {});
+ }
+ });
+ });
+ },
+
+ // 获取远程配置
+ fetchDynamicConfig() {
+ return new Promise((resolve, reject) => {
+ wx.request({
+ url: this.globalData.apiBaseUrl + '/peizhi/qdpzhq',
+ method: 'POST',
+ header: { 'content-type': 'application/json' },
+ success: (res) => {
+ if (res.statusCode === 200 && res.data && res.data.code === 0) {
+ resolve(res.data.data);
+ } else {
+ reject(new Error(res.data?.msg || '接口返回错误'));
+ }
+ },
+ fail: reject
+ });
+ });
+ },
+
+ applyDynamicConfig(config) {
+ if (!config || typeof config !== 'object') return;
+
+ if (config.cos) {
+ this.globalData.ossImageUrl = config.cos.ossImageUrl || '';
+ this.globalData.cosConfig = {
+ bucket: config.cos.bucket || '',
+ region: config.cos.region || 'ap-shanghai',
+ uploadPathPrefix: config.cos.uploadPathPrefix || 'order/'
+ };
+ }
+
+ if (config.goEasy) {
+ this.globalData.goEasyConfig = {
+ host: config.goEasy.host || 'hangzhou.goeasy.io',
+ appkey: config.goEasy.appkey || ''
+ };
+ }
+
+ if (config.otherConfig) {
+ this.globalData.morentouxiang = config.otherConfig.morentouxiang || '';
+ this.globalData.dashouguize = config.otherConfig.dashouguize || '';
+ }
+
+ if (config.kefu) {
+ this.globalData.kefuConfig = {
+ link: config.kefu.link || '',
+ enterpriseId: config.kefu.enterpriseId || ''
+ };
+ }
+ },
+
+ // 仅在有效 appkey 时初始化
+ initGoEasyWithConfig() {
+ const cfg = this.globalData.goEasyConfig;
+ if (!cfg || !cfg.appkey) {
+ console.warn('GoEasy appkey 未配置,聊天功能将在配置就绪后自动恢复');
+ return;
+ }
+
+ // 避免重复初始化
+ if (wx.goEasy && wx.GoEasy) {
+ console.log('GoEasy 已初始化,跳过');
+ return;
+ }
+
+ try {
+ wx.goEasy = GoEasy.getInstance({
+ host: cfg.host || 'hangzhou.goeasy.io',
+ appkey: cfg.appkey,
+ modules: ['im', 'pubsub']
+ });
+ wx.GoEasy = GoEasy;
+ console.log('GoEasy 初始化成功');
+ if (this.startImWhenReady && wx.getStorageSync('uid')) {
+ setTimeout(() => this.startImWhenReady(), 100);
+ }
+ } catch (error) {
+ console.error('GoEasy 初始化失败:', error);
+ }
+ },
+
+ initCurrentUser() {
+ const uid = wx.getStorageSync('uid');
+ if (uid) {
+ this.globalData.currentUser = {
+ id: uid,
+ name: '用户' + uid.substring(0, 6),
+ avatar: this.globalData.ossImageUrl + this.globalData.morentouxiang
+ };
+ }
+ },
+
+ emitEvent(eventName, data) {
+ if (this.globalData.eventListeners[eventName]) {
+ this.globalData.eventListeners[eventName].forEach(callback => {
+ try { callback(data); } catch (error) { console.error(error); }
+ });
+ }
+ },
+
+ /** 切换角色并通知刷新 */
+ setCurrentRole(role) {
+ if (!role) return;
+ this.globalData.currentRole = role;
+ wx.setStorageSync('currentRole', role);
+ this.emitEvent('currentRoleChanged', { role });
+ },
+
+ on(eventName, callback) {
+ if (!this.globalData.eventListeners[eventName]) {
+ this.globalData.eventListeners[eventName] = [];
+ }
+ this.globalData.eventListeners[eventName].push(callback);
+ },
+
+ off(eventName, callback) {
+ if (this.globalData.eventListeners[eventName]) {
+ const index = this.globalData.eventListeners[eventName].indexOf(callback);
+ if (index > -1) {
+ this.globalData.eventListeners[eventName].splice(index, 1);
+ }
+ }
+ }
});
\ No newline at end of file
diff --git a/app.json b/app.json
index 1ed971e..7dba254 100644
--- a/app.json
+++ b/app.json
@@ -13,6 +13,9 @@
"pages/merchant-home/merchant-home",
"pages/merchant-kefu-key/merchant-kefu-key",
"pages/merchant-kefu-list/merchant-kefu-list",
+ "pages/staff-join/staff-join",
+ "pages/merchant-staff-audit/merchant-staff-audit",
+ "pages/merchant-staff-role/merchant-staff-role",
"pages/manager/manager",
"pages/orders/orders",
"pages/order-detail/order-detail",
diff --git a/pages/accept-order/accept-order.js b/pages/accept-order/accept-order.js
index 749dfe7..e47549e 100644
--- a/pages/accept-order/accept-order.js
+++ b/pages/accept-order/accept-order.js
@@ -3,6 +3,7 @@ const app = getApp();
import request from '../../utils/request.js';
import PopupService from '../../services/popupService.js';
import { reconnectForRole } from '../../utils/role-tab-bar.js';
+import { ensurePhoneAuth } from '../../utils/phone-auth.js';
Page({
data: {
@@ -62,7 +63,10 @@ Page({
}
},
- onShow() {
+ async onShow() {
+ const phoneOk = await ensurePhoneAuth({ redirect: true, role: 'dashou' });
+ if (!phoneOk) return;
+
this.syncShopBannerFromGlobal();
this.registerNotificationComponent();
this.loadGlobalStatus();
diff --git a/pages/express-order/express-order.js b/pages/express-order/express-order.js
index 5c14b45..c682217 100644
--- a/pages/express-order/express-order.js
+++ b/pages/express-order/express-order.js
@@ -1,618 +1,666 @@
-// pages/express-order/express-order.js
-import request from '../../utils/request.js'
-
-const PAGE_SIZE = 50
-const LINK_PAGE_SIZE = 5
-
-Page({
- data: {
- sjyue: '0.00',
- shangpinList: [], // 最终展示的商品类型列表(已倒序)
- selectedTypeId: null,
- selectedHuiyuanId: null,
- selectedYaoqiuleixing: null,
- currentTypeChenghaoList: [],
-
- searchParams: { keyword: '', minPrice: '', labelId: '' },
- searchLabelName: '',
- isSearchMode: false,
-
- templateList: [],
- currentPage: 1,
- hasMoreData: true,
- isLoadingMore: false,
-
- linkMap: {},
-
- showDetailModal: false,
- showAddModal: false,
- showDeleteConfirm: false,
- showNewLinkConfirm: false,
-
- currentTemplate: null,
- currentTemplateIndex: -1,
-
- isEditing: false,
- editJieshao: '',
- editJiage: '',
- editLabelId: '',
- editLabelName: '',
- editCommissionEnabled: false,
- editCommissionValue: '',
-
- addJieshao: '',
- addJiage: '',
- addLabelId: '',
- addLabelName: '',
- addCommissionEnabled: false,
- addCommissionValue: '',
-
- detailLinkFilter: 0,
- detailLinkList: [],
- detailLinkPage: 1,
- detailLinkHasMore: true,
- isLoadingLinks: false,
-
- isLoading: false
- },
-
- onLoad() {
- this.loadShangjiaYue()
- this.loadShangpinTypes()
- },
-
- onShow() {
- this.registerNotificationComponent()
- this.loadShangjiaYue()
- },
-
- registerNotificationComponent() {
- const app = getApp()
- const comp = this.selectComponent('#global-notification')
- if (comp && comp.showNotification) {
- app.globalData.globalNotification = {
- show: data => comp.showNotification(data),
- hide: () => comp.hideNotification()
- }
- }
- },
-
- loadShangjiaYue() {
- const app = getApp()
- const data = app.globalData.shangjia || {}
- this.setData({ sjyue: data.sjyue || '0.00' })
- },
-
- async loadShangpinTypes() {
- this.setData({ isLoading: true })
- try {
- const res = await request({ url: '/dingdan/sjspleixing', method: 'POST' })
- if (res && res.data.code === 200) {
- let list = res.data.data || []
- // 强制倒序排列
- list.reverse()
- this.setData({ shangpinList: list, isLoading: false })
- if (list.length > 0) {
- this.setSelectedType(list[0])
- }
- }
- } catch (e) {
- console.error(e)
- this.setData({ isLoading: false })
- wx.showToast({ title: '加载类型失败', icon: 'error' })
- }
- },
-
- setSelectedType(item) {
- this.setData({
- selectedTypeId: item.id,
- selectedHuiyuanId: item.huiyuan_id,
- selectedYaoqiuleixing: item.yaoqiuleixing,
- currentTypeChenghaoList: item.chenghaoList || []
- }, () => {
- this.resetAndLoadTemplates()
- })
- },
-
- resetAndLoadTemplates() {
- this.setData({
- templateList: [],
- currentPage: 1,
- hasMoreData: true,
- isSearchMode: false,
- searchParams: { keyword: '', minPrice: '', labelId: '' },
- searchLabelName: ''
- })
- this.loadTemplates()
- },
-
- async loadTemplates() {
- const { selectedTypeId, currentPage, isSearchMode, searchParams, linkMap } = this.data
- if (!selectedTypeId) {
- wx.showToast({ title: '请先选择订单类型', icon: 'none' })
- return
- }
- if (currentPage === 1) this.setData({ isLoading: true })
- else this.setData({ isLoadingMore: true })
-
- try {
- const postData = {
- shangpinTypeId: selectedTypeId,
- page: currentPage,
- pageSize: PAGE_SIZE,
- getType: isSearchMode ? 1 : 2
- }
- if (isSearchMode) {
- if (searchParams.keyword) postData.keyword = searchParams.keyword.trim()
- if (searchParams.minPrice) postData.minPrice = searchParams.minPrice
- if (searchParams.labelId) postData.labelId = searchParams.labelId
- }
-
- const res = await request({ url: '/peizhi/sjddmblb', method: 'POST', data: postData })
- if (res && res.data.code === 200) {
- const data = res.data.data || {}
- const newTemplates = data.list || []
- const existingIds = new Set(this.data.templateList.map(item => item.mobanId))
- const uniqueNew = newTemplates.filter(item => !existingIds.has(item.mobanId))
-
- const processed = uniqueNew.map(item => {
- const mobanId = item.mobanId || item.id
- const cached = linkMap[mobanId] || {}
- return {
- ...item,
- hasGenerated: cached.hasGenerated || !!item.linkUrl,
- isCopied: cached.isCopied || false,
- linkUrl: cached.linkUrl || item.linkUrl || '',
- mobanId: mobanId,
- jieshao: item.jieshao || item.moban_jieshao,
- jiage: item.jiage || item.price,
- fabushuliang: item.fabushuliang || item.fabu_shuliang || 0,
- labelId: item.labelId,
- labelName: item.labelName || '',
- commissionEnabled: item.commissionEnabled,
- commissionValue: item.commissionValue
- }
- })
-
- this.setData({
- templateList: currentPage === 1 ? processed : [...this.data.templateList, ...processed],
- hasMoreData: data.hasMore !== false && processed.length === PAGE_SIZE,
- isLoading: false,
- isLoadingMore: false
- })
- } else {
- this.setData({ isLoading: false, isLoadingMore: false, hasMoreData: false })
- }
- } catch (e) {
- console.error(e)
- this.setData({ isLoading: false, isLoadingMore: false })
- wx.showToast({ title: '加载模板失败', icon: 'error' })
- }
- },
-
- // ---------- 搜索相关 ----------
- onSearchKeywordInput(e) { this.setData({ 'searchParams.keyword': e.detail.value }) },
- onSearchMinPriceInput(e) { this.setData({ 'searchParams.minPrice': e.detail.value }) },
- onSearchLabelChange(e) {
- const idx = e.detail.value
- const label = this.data.currentTypeChenghaoList[idx]
- this.setData({
- 'searchParams.labelId': label ? label.id : '',
- searchLabelName: label ? label.mingcheng : ''
- })
- },
- onSearchConfirm() {
- const { searchParams } = this.data
- if (!searchParams.keyword && !searchParams.minPrice && !searchParams.labelId) {
- this.resetAndLoadTemplates()
- return
- }
- this.setData({
- isSearchMode: true,
- currentPage: 1,
- templateList: [],
- hasMoreData: true
- }, () => this.loadTemplates())
- },
- onClearSearch() { this.resetAndLoadTemplates() },
-
- // ---------- 卡片生成链接 ----------
- async onGenerateLink(e) {
- const { id } = e.currentTarget.dataset
- const idx = this.data.templateList.findIndex(item => item.mobanId === id)
- if (idx === -1) return
- await this.generateLinkForTemplate(this.data.templateList[idx], idx)
- },
-
- onCopyLink(e) {
- const { id } = e.currentTarget.dataset
- const idx = this.data.templateList.findIndex(item => item.mobanId === id)
- if (idx === -1) return
- const template = this.data.templateList[idx]
- if (!template.linkUrl) {
- wx.showToast({ title: '请先生成链接', icon: 'none' })
- return
- }
- wx.setClipboardData({
- data: template.linkUrl,
- success: () => {
- const { linkMap } = this.data
- const mobanId = template.mobanId
- const updatedLinkMap = { ...linkMap }
- if (updatedLinkMap[mobanId]) updatedLinkMap[mobanId].isCopied = true
- const updatedList = [...this.data.templateList]
- updatedList[idx].isCopied = true
- this.setData({ linkMap: updatedLinkMap, templateList: updatedList })
- wx.showToast({ title: '链接已复制', icon: 'success' })
- }
- })
- },
-
- async generateLinkForTemplate(template, templateIndex) {
- const { selectedTypeId, linkMap } = this.data
- if (!selectedTypeId) {
- wx.showToast({ title: '请选择订单类型', icon: 'none' })
- return
- }
- wx.showLoading({ title: '生成中...', mask: true })
- try {
- const res = await request({
- url: '/peizhi/sjljhq',
- method: 'POST',
- data: { mobanId: template.mobanId, shangpinTypeId: selectedTypeId }
- })
- if (res && res.data.code === 200) {
- const data = res.data.data || {}
- const mobanId = template.mobanId
- const updatedLinkMap = { ...linkMap }
- updatedLinkMap[mobanId] = {
- hasGenerated: true,
- linkUrl: data.linkUrl || '',
- isCopied: false,
- newBalance: data.newBalance
- }
- const updatedList = [...this.data.templateList]
- updatedList[templateIndex] = {
- ...updatedList[templateIndex],
- hasGenerated: true,
- linkUrl: data.linkUrl || '',
- isCopied: false,
- newBalance: data.newBalance
- }
- if (data.newBalance) {
- const app = getApp()
- if (app.globalData.shangjia) app.globalData.shangjia.sjyue = data.newBalance
- this.setData({ sjyue: data.newBalance })
- }
- this.setData({ linkMap: updatedLinkMap, templateList: updatedList })
- wx.showToast({ title: '链接生成成功', icon: 'success' })
- } else {
- wx.showToast({ title: res.data.msg || '生成失败', icon: 'error' })
- }
- } catch (e) {
- wx.showToast({ title: '生成失败', icon: 'error' })
- } finally {
- wx.hideLoading()
- }
- },
-
- onCardTap(e) {
- const { target } = e
- if (target.className && (target.className.includes('btn') || target.className.includes('action'))) return
- const { item } = e.currentTarget.dataset
- if (!item) return
- const idx = this.data.templateList.findIndex(t => t.mobanId === item.mobanId)
- this.setData({
- currentTemplate: item,
- currentTemplateIndex: idx,
- showDetailModal: true,
- isEditing: false,
- editJieshao: item.jieshao || '',
- editJiage: item.jiage || '',
- editLabelId: item.labelId || '',
- editLabelName: item.labelName || '',
- editCommissionEnabled: item.commissionEnabled || false,
- editCommissionValue: item.commissionValue || '',
- detailLinkFilter: 0,
- detailLinkList: [],
- detailLinkPage: 1,
- detailLinkHasMore: true
- })
- this.loadDetailLinks()
- },
-
- hideDetailModal() { this.setData({ showDetailModal: false, isEditing: false }) },
- showAddModal() {
- this.setData({
- showAddModal: true,
- addJieshao: '', addJiage: '', addLabelId: '', addLabelName: '',
- addCommissionEnabled: false, addCommissionValue: ''
- })
- },
- hideAddModal() { this.setData({ showAddModal: false }) },
- hideDeleteConfirm() { this.setData({ showDeleteConfirm: false }) },
- hideNewLinkConfirm() { this.setData({ showNewLinkConfirm: false }) },
-
- // ---------- 链接筛选与加载 ----------
- onToggleLinkFilter() {
- const newFilter = this.data.detailLinkFilter === 0 ? 1 : 0
- this.setData({
- detailLinkFilter: newFilter,
- detailLinkList: [],
- detailLinkPage: 1,
- detailLinkHasMore: true
- }, () => this.loadDetailLinks())
- },
-
- async loadDetailLinks() {
- const { currentTemplate, detailLinkFilter, detailLinkPage, isLoadingLinks } = this.data
- if (!currentTemplate || isLoadingLinks) return
- this.setData({ isLoadingLinks: true })
- try {
- const res = await request({
- url: '/peizhi/hqsjlslj',
- method: 'POST',
- data: {
- mobanId: currentTemplate.mobanId,
- used: detailLinkFilter,
- page: detailLinkPage,
- pageSize: LINK_PAGE_SIZE
- }
- })
- if (res && res.data.code === 200) {
- const data = res.data.data || {}
- const newLinks = data.list || []
- const merged = detailLinkPage === 1 ? newLinks : [...this.data.detailLinkList, ...newLinks]
- this.setData({
- detailLinkList: merged,
- detailLinkHasMore: newLinks.length === LINK_PAGE_SIZE,
- isLoadingLinks: false
- })
- } else {
- this.setData({ isLoadingLinks: false })
- }
- } catch (e) {
- this.setData({ isLoadingLinks: false })
- wx.showToast({ title: '加载链接失败', icon: 'error' })
- }
- },
-
- onLoadMoreLinks() {
- if (!this.data.detailLinkHasMore || this.data.isLoadingLinks) return
- this.setData({ detailLinkPage: this.data.detailLinkPage + 1 }, () => this.loadDetailLinks())
- },
-
- onCopyLinkItem(e) {
- const url = e.currentTarget.dataset.url
- if (!url) return
- wx.setClipboardData({ data: url, success: () => wx.showToast({ title: '已复制', icon: 'success' }) })
- },
-
- // ---------- 弹窗内操作 ----------
- onGenerateLinkModal() {
- const { currentTemplate, currentTemplateIndex } = this.data
- if (!currentTemplate) return
- if (currentTemplate.hasGenerated && !currentTemplate.isCopied) {
- this.setData({ showNewLinkConfirm: true })
- return
- }
- this.generateLinkForTemplate(currentTemplate, currentTemplateIndex)
- },
-
- onCopyLinkModal() {
- const { currentTemplate, linkMap } = this.data
- if (!currentTemplate || !currentTemplate.linkUrl) {
- wx.showToast({ title: '请先生成链接', icon: 'none' })
- return
- }
- wx.setClipboardData({
- data: currentTemplate.linkUrl,
- success: () => {
- const mobanId = currentTemplate.mobanId
- const updatedLinkMap = { ...linkMap }
- if (updatedLinkMap[mobanId]) updatedLinkMap[mobanId].isCopied = true
- const updatedList = [...this.data.templateList]
- if (this.data.currentTemplateIndex !== -1) {
- updatedList[this.data.currentTemplateIndex].isCopied = true
- }
- const updatedCurrent = { ...currentTemplate, isCopied: true }
- this.setData({ linkMap: updatedLinkMap, templateList: updatedList, currentTemplate: updatedCurrent })
- wx.showToast({ title: '链接已复制', icon: 'success' })
- }
- })
- },
-
- // ---------- 编辑 ----------
- onEditTemplate() {
- if (!this.data.isEditing) this.setData({ isEditing: true })
- else this.saveTemplateEdit()
- },
- onEditJieshaoInput(e) { this.setData({ editJieshao: e.detail.value }) },
- onEditJiageInput(e) { this.setData({ editJiage: e.detail.value }) },
- onEditLabelChange(e) {
- const idx = e.detail.value
- const label = this.data.currentTypeChenghaoList[idx]
- this.setData({ editLabelId: label ? label.id : '', editLabelName: label ? label.mingcheng : '' })
- },
- onEditCommissionToggle() { this.setData({ editCommissionEnabled: !this.data.editCommissionEnabled }) },
- onEditCommissionValueInput(e) { this.setData({ editCommissionValue: e.detail.value }) },
-
- async saveTemplateEdit() {
- const { currentTemplate, selectedTypeId, editJieshao, editJiage, editLabelId, editCommissionEnabled, editCommissionValue } = this.data
- if (!currentTemplate || !selectedTypeId) return
- if (editCommissionEnabled && (!editCommissionValue || isNaN(parseFloat(editCommissionValue)))) {
- wx.showToast({ title: '请输入有效佣金金额', icon: 'none' })
- return
- }
- wx.showLoading({ title: '保存中...', mask: true })
- try {
- const res = await request({
- url: '/peizhi/sjddmbgx',
- method: 'POST',
- data: {
- mobanId: currentTemplate.mobanId,
- shangpinTypeId: selectedTypeId,
- jieshao: editJieshao,
- jiage: editJiage,
- labelId: editLabelId || '',
- commissionEnabled: editCommissionEnabled,
- commissionValue: editCommissionValue
- }
- })
- if (res && res.data.code === 200) {
- const updatedList = [...this.data.templateList]
- const idx = this.data.currentTemplateIndex
- if (idx !== -1) {
- updatedList[idx] = {
- ...updatedList[idx],
- jieshao: editJieshao,
- jiage: editJiage,
- labelId: editLabelId,
- labelName: this.getLabelNameById(editLabelId),
- commissionEnabled: editCommissionEnabled,
- commissionValue: editCommissionValue
- }
- this.setData({ templateList: updatedList, currentTemplate: updatedList[idx], isEditing: false })
- }
- wx.showToast({ title: '修改成功', icon: 'success' })
- } else {
- wx.showToast({ title: res.data.msg || '修改失败', icon: 'error' })
- }
- } catch (e) {
- wx.showToast({ title: '修改失败', icon: 'error' })
- } finally { wx.hideLoading() }
- },
-
- onDeleteTemplate() { this.setData({ showDeleteConfirm: true }) },
-
- async confirmDeleteTemplate() {
- const { currentTemplate, selectedTypeId, linkMap } = this.data
- if (!currentTemplate || !selectedTypeId) return
- wx.showLoading({ title: '删除中...', mask: true })
- try {
- const res = await request({
- url: '/peizhi/sjddmbsc',
- method: 'POST',
- data: { mobanId: currentTemplate.mobanId, shangpinTypeId: selectedTypeId }
- })
- if (res && res.data.code === 200) {
- const updatedLinkMap = { ...linkMap }
- delete updatedLinkMap[currentTemplate.mobanId]
- const updatedList = [...this.data.templateList]
- updatedList.splice(this.data.currentTemplateIndex, 1)
- this.setData({
- linkMap: updatedLinkMap,
- templateList: updatedList,
- showDetailModal: false,
- showDeleteConfirm: false,
- currentTemplate: null,
- currentTemplateIndex: -1
- })
- wx.showToast({ title: '删除成功', icon: 'success' })
- } else {
- wx.showToast({ title: res.data.msg || '删除失败', icon: 'error' })
- }
- } catch (e) {
- wx.showToast({ title: '删除失败', icon: 'error' })
- } finally { wx.hideLoading() }
- },
-
- async confirmNewLink() {
- const { currentTemplate, currentTemplateIndex } = this.data
- if (!currentTemplate) return
- this.setData({ showNewLinkConfirm: false })
- await this.generateLinkForTemplate(currentTemplate, currentTemplateIndex)
- },
-
- // ---------- 添加模板 ----------
- onAddJieshaoInput(e) { this.setData({ addJieshao: e.detail.value }) },
- onAddJiageInput(e) { this.setData({ addJiage: e.detail.value }) },
- onAddLabelChange(e) {
- const idx = e.detail.value
- const label = this.data.currentTypeChenghaoList[idx]
- this.setData({ addLabelId: label ? label.id : '', addLabelName: label ? label.mingcheng : '' })
- },
- onAddCommissionToggle() { this.setData({ addCommissionEnabled: !this.data.addCommissionEnabled }) },
- onAddCommissionValueInput(e) { this.setData({ addCommissionValue: e.detail.value }) },
-
- async onAddTemplate() {
- const { selectedTypeId, addJieshao, addJiage, addLabelId, addCommissionEnabled, addCommissionValue } = this.data
- if (!selectedTypeId) {
- wx.showToast({ title: '请选择订单类型', icon: 'none' })
- return
- }
- if (!addJieshao.trim()) {
- wx.showToast({ title: '请输入订单介绍', icon: 'none' })
- return
- }
- if (!addJiage || isNaN(parseFloat(addJiage)) || parseFloat(addJiage) <= 0) {
- wx.showToast({ title: '请输入有效的价格', icon: 'none' })
- return
- }
- if (addCommissionEnabled && (!addCommissionValue || isNaN(parseFloat(addCommissionValue)))) {
- wx.showToast({ title: '请输入有效的佣金金额', icon: 'none' })
- return
- }
-
- wx.showLoading({ title: '添加中...', mask: true })
- try {
- const res = await request({
- url: '/peizhi/sjtjddmb',
- method: 'POST',
- data: {
- shangpinTypeId: selectedTypeId,
- jieshao: addJieshao.trim(),
- jiage: addJiage,
- labelId: addLabelId || '',
- commissionEnabled: addCommissionEnabled,
- commissionValue: addCommissionValue
- }
- })
- if (res && res.data.code === 200) {
- const data = res.data.data || {}
- const newTemplate = {
- mobanId: data.mobanId,
- jieshao: addJieshao,
- jiage: addJiage,
- fabushuliang: 0,
- hasGenerated: false,
- isCopied: false,
- linkUrl: '',
- labelId: addLabelId,
- labelName: this.getLabelNameById(addLabelId),
- commissionEnabled: addCommissionEnabled,
- commissionValue: addCommissionValue
- }
- this.setData({
- templateList: [newTemplate, ...this.data.templateList],
- showAddModal: false,
- addJieshao: '', addJiage: '', addLabelId: '', addLabelName: '',
- addCommissionEnabled: false, addCommissionValue: ''
- })
- wx.showToast({ title: '添加成功', icon: 'success' })
- } else {
- wx.showToast({ title: res.data.msg || '添加失败', icon: 'error' })
- }
- } catch (e) {
- wx.showToast({ title: '添加失败', icon: 'error' })
- } finally { wx.hideLoading() }
- },
-
- getLabelNameById(labelId) {
- if (!labelId) return ''
- const list = this.data.currentTypeChenghaoList
- const found = list.find(item => item.id == labelId)
- return found ? found.mingcheng : ''
- },
-
- onScrollToLower() {
- const { hasMoreData, isLoadingMore, isSearchMode } = this.data
- if (!hasMoreData || isLoadingMore || isSearchMode) return
- this.setData({ currentPage: this.data.currentPage + 1 }, () => this.loadTemplates())
- },
-
- onSelectType(e) {
- const { id, item } = e.currentTarget.dataset
- this.setSelectedType(item)
- }
+// pages/express-order/express-order.js
+import request from '../../utils/request.js'
+import { STAFF_API, isStaffMode, getStaffContext, setStaffContext, syncStaffUi, staffOrOwner, refreshStaffContext, restoreStaffContextAfterAuth, isMerchantPortalUser } from '../../utils/staff-api.js'
+
+const PAGE_SIZE = 50
+const LINK_PAGE_SIZE = 5
+
+function apiOk(res) {
+ const code = res && res.data && res.data.code
+ return code === 200 || code === 0
+}
+
+Page({
+ data: {
+ sjyue: '0.00',
+ shangpinList: [], // 最终展示的商品类型列表(已倒序)
+ selectedTypeId: null,
+ selectedHuiyuanId: null,
+ selectedYaoqiuleixing: null,
+ currentTypeChenghaoList: [],
+
+ searchParams: { keyword: '', minPrice: '', labelId: '' },
+ searchLabelName: '',
+ isSearchMode: false,
+
+ templateList: [],
+ currentPage: 1,
+ hasMoreData: true,
+ isLoadingMore: false,
+
+ linkMap: {},
+
+ showDetailModal: false,
+ showAddModal: false,
+ showDeleteConfirm: false,
+ showNewLinkConfirm: false,
+
+ currentTemplate: null,
+ currentTemplateIndex: -1,
+
+ isEditing: false,
+ editJieshao: '',
+ editJiage: '',
+ editLabelId: '',
+ editLabelName: '',
+ editCommissionEnabled: false,
+ editCommissionValue: '',
+
+ addJieshao: '',
+ addJiage: '',
+ addLabelId: '',
+ addLabelName: '',
+ addCommissionEnabled: false,
+ addCommissionValue: '',
+
+ detailLinkFilter: 0,
+ detailLinkList: [],
+ detailLinkPage: 1,
+ detailLinkHasMore: true,
+ isLoadingLinks: false,
+
+ isLoading: false,
+ canTemplate: true,
+ isStaffMode: false,
+ staffRoleName: '',
+ },
+
+ async onLoad() {
+ if (wx.getStorageSync('token')) {
+ await restoreStaffContextAfterAuth()
+ }
+ if (!isMerchantPortalUser()) {
+ wx.showToast({ title: '请先成为商家或绑定客服', icon: 'none' })
+ setTimeout(() => wx.navigateBack(), 1500)
+ return
+ }
+ syncStaffUi(this)
+ this.loadShangjiaYue()
+ this.loadShangpinTypes()
+ },
+
+ onShow() {
+ this.registerNotificationComponent()
+ syncStaffUi(this)
+ this.loadShangjiaYue()
+ },
+
+ registerNotificationComponent() {
+ const app = getApp()
+ const comp = this.selectComponent('#global-notification')
+ if (comp && comp.showNotification) {
+ app.globalData.globalNotification = {
+ show: data => comp.showNotification(data),
+ hide: () => comp.hideNotification()
+ }
+ }
+ },
+
+ loadShangjiaYue() {
+ if (isStaffMode()) {
+ refreshStaffContext(request).then((ctx) => {
+ const wallet = (ctx && ctx.wallet) || (getStaffContext() || {}).wallet || {};
+ this.setData({ sjyue: wallet.quota_available || '0.00' });
+ }).catch(() => {
+ const ctx = getStaffContext() || {};
+ const wallet = ctx.wallet || {};
+ this.setData({ sjyue: wallet.quota_available || '0.00' });
+ });
+ return;
+ }
+ const app = getApp()
+ const data = app.globalData.shangjia || {}
+ this.setData({ sjyue: data.sjyue || '0.00' })
+ },
+
+ async loadShangpinTypes() {
+ this.setData({ isLoading: true })
+ const staff = isStaffMode()
+ try {
+ const res = await request({
+ url: staff ? STAFF_API.productTypes : '/dingdan/sjspleixing',
+ method: 'POST',
+ })
+ const code = res && res.data && res.data.code
+ if (code === 200 || code === 0) {
+ let list = res.data.data || []
+ if (!Array.isArray(list) && list.list) list = list.list
+ // 强制倒序排列
+ list.reverse()
+ this.setData({ shangpinList: list, isLoading: false })
+ if (list.length > 0) {
+ this.setSelectedType(list[0])
+ }
+ } else {
+ throw new Error((res.data && res.data.msg) || '加载失败')
+ }
+ } catch (e) {
+ console.error(e)
+ this.setData({ isLoading: false })
+ wx.showToast({ title: e.message || '加载类型失败', icon: 'none' })
+ }
+ },
+
+ setSelectedType(item) {
+ this.setData({
+ selectedTypeId: item.id,
+ selectedHuiyuanId: item.huiyuan_id,
+ selectedYaoqiuleixing: item.yaoqiuleixing,
+ currentTypeChenghaoList: item.chenghaoList || []
+ }, () => {
+ this.resetAndLoadTemplates()
+ })
+ },
+
+ resetAndLoadTemplates() {
+ this.setData({
+ templateList: [],
+ currentPage: 1,
+ hasMoreData: true,
+ isSearchMode: false,
+ searchParams: { keyword: '', minPrice: '', labelId: '' },
+ searchLabelName: ''
+ })
+ this.loadTemplates()
+ },
+
+ async loadTemplates() {
+ const { selectedTypeId, currentPage, isSearchMode, searchParams, linkMap } = this.data
+ if (!selectedTypeId) {
+ wx.showToast({ title: '请先选择订单类型', icon: 'none' })
+ return
+ }
+ if (currentPage === 1) this.setData({ isLoading: true })
+ else this.setData({ isLoadingMore: true })
+
+ try {
+ const postData = {
+ shangpinTypeId: selectedTypeId,
+ page: currentPage,
+ pageSize: PAGE_SIZE,
+ getType: isSearchMode ? 1 : 2
+ }
+ if (isSearchMode) {
+ if (searchParams.keyword) postData.keyword = searchParams.keyword.trim()
+ if (searchParams.minPrice) postData.minPrice = searchParams.minPrice
+ if (searchParams.labelId) postData.labelId = searchParams.labelId
+ }
+
+ const res = await request({ url: staffOrOwner(STAFF_API.templateList, '/peizhi/sjddmblb'), method: 'POST', data: postData })
+ if (res && apiOk(res)) {
+ const data = res.data.data || {}
+ const newTemplates = data.list || []
+ const existingIds = new Set(this.data.templateList.map(item => item.mobanId))
+ const uniqueNew = newTemplates.filter(item => !existingIds.has(item.mobanId))
+
+ const processed = uniqueNew.map(item => {
+ const mobanId = item.mobanId || item.id
+ const cached = linkMap[mobanId] || {}
+ return {
+ ...item,
+ hasGenerated: cached.hasGenerated || !!item.linkUrl,
+ isCopied: cached.isCopied || false,
+ linkUrl: cached.linkUrl || item.linkUrl || '',
+ mobanId: mobanId,
+ jieshao: item.jieshao || item.moban_jieshao,
+ jiage: item.jiage || item.price,
+ fabushuliang: item.fabushuliang || item.fabu_shuliang || 0,
+ labelId: item.labelId,
+ labelName: item.labelName || '',
+ commissionEnabled: item.commissionEnabled,
+ commissionValue: item.commissionValue
+ }
+ })
+
+ this.setData({
+ templateList: currentPage === 1 ? processed : [...this.data.templateList, ...processed],
+ hasMoreData: data.hasMore !== false && processed.length === PAGE_SIZE,
+ isLoading: false,
+ isLoadingMore: false
+ })
+ } else {
+ this.setData({ isLoading: false, isLoadingMore: false, hasMoreData: false })
+ }
+ } catch (e) {
+ console.error(e)
+ this.setData({ isLoading: false, isLoadingMore: false })
+ wx.showToast({ title: '加载模板失败', icon: 'error' })
+ }
+ },
+
+ // ---------- 搜索相关 ----------
+ onSearchKeywordInput(e) { this.setData({ 'searchParams.keyword': e.detail.value }) },
+ onSearchMinPriceInput(e) { this.setData({ 'searchParams.minPrice': e.detail.value }) },
+ onSearchLabelChange(e) {
+ const idx = e.detail.value
+ const label = this.data.currentTypeChenghaoList[idx]
+ this.setData({
+ 'searchParams.labelId': label ? label.id : '',
+ searchLabelName: label ? label.mingcheng : ''
+ })
+ },
+ onSearchConfirm() {
+ const { searchParams } = this.data
+ if (!searchParams.keyword && !searchParams.minPrice && !searchParams.labelId) {
+ this.resetAndLoadTemplates()
+ return
+ }
+ this.setData({
+ isSearchMode: true,
+ currentPage: 1,
+ templateList: [],
+ hasMoreData: true
+ }, () => this.loadTemplates())
+ },
+ onClearSearch() { this.resetAndLoadTemplates() },
+
+ // ---------- 卡片生成链接 ----------
+ async onGenerateLink(e) {
+ const { id } = e.currentTarget.dataset
+ const idx = this.data.templateList.findIndex(item => item.mobanId === id)
+ if (idx === -1) return
+ await this.generateLinkForTemplate(this.data.templateList[idx], idx)
+ },
+
+ onCopyLink(e) {
+ const { id } = e.currentTarget.dataset
+ const idx = this.data.templateList.findIndex(item => item.mobanId === id)
+ if (idx === -1) return
+ const template = this.data.templateList[idx]
+ if (!template.linkUrl) {
+ wx.showToast({ title: '请先生成链接', icon: 'none' })
+ return
+ }
+ wx.setClipboardData({
+ data: template.linkUrl,
+ success: () => {
+ const { linkMap } = this.data
+ const mobanId = template.mobanId
+ const updatedLinkMap = { ...linkMap }
+ if (updatedLinkMap[mobanId]) updatedLinkMap[mobanId].isCopied = true
+ const updatedList = [...this.data.templateList]
+ updatedList[idx].isCopied = true
+ this.setData({ linkMap: updatedLinkMap, templateList: updatedList })
+ wx.showToast({ title: '链接已复制', icon: 'success' })
+ }
+ })
+ },
+
+ async generateLinkForTemplate(template, templateIndex) {
+ const { selectedTypeId, linkMap } = this.data
+ if (!selectedTypeId) {
+ wx.showToast({ title: '请选择订单类型', icon: 'none' })
+ return
+ }
+ wx.showLoading({ title: '生成中...', mask: true })
+ try {
+ const res = await request({
+ url: staffOrOwner(STAFF_API.linkGenerate, '/peizhi/sjljhq'),
+ method: 'POST',
+ data: { mobanId: template.mobanId, shangpinTypeId: selectedTypeId }
+ })
+ if (res && apiOk(res)) {
+ const data = res.data.data || {}
+ const mobanId = template.mobanId
+ const updatedLinkMap = { ...linkMap }
+ updatedLinkMap[mobanId] = {
+ hasGenerated: true,
+ linkUrl: data.linkUrl || '',
+ isCopied: false,
+ newBalance: data.newBalance
+ }
+ const updatedList = [...this.data.templateList]
+ updatedList[templateIndex] = {
+ ...updatedList[templateIndex],
+ hasGenerated: true,
+ linkUrl: data.linkUrl || '',
+ isCopied: false,
+ newBalance: data.newBalance
+ }
+ if (data.newBalance || data.quotaAvailable) {
+ const balance = data.quotaAvailable || data.newBalance
+ if (isStaffMode()) {
+ const ctx = getStaffContext() || {}
+ if (ctx.wallet) {
+ ctx.wallet.quota_available = balance
+ setStaffContext(ctx)
+ }
+ this.setData({ sjyue: balance })
+ } else {
+ const app = getApp()
+ if (app.globalData.shangjia) app.globalData.shangjia.sjyue = balance
+ this.setData({ sjyue: balance })
+ }
+ }
+ this.setData({ linkMap: updatedLinkMap, templateList: updatedList })
+ wx.showToast({ title: '链接生成成功', icon: 'success' })
+ } else {
+ wx.showToast({ title: res.data.msg || '生成失败', icon: 'error' })
+ }
+ } catch (e) {
+ wx.showToast({ title: '生成失败', icon: 'error' })
+ } finally {
+ wx.hideLoading()
+ }
+ },
+
+ onCardTap(e) {
+ const { target } = e
+ if (target.className && (target.className.includes('btn') || target.className.includes('action'))) return
+ const { item } = e.currentTarget.dataset
+ if (!item) return
+ const idx = this.data.templateList.findIndex(t => t.mobanId === item.mobanId)
+ this.setData({
+ currentTemplate: item,
+ currentTemplateIndex: idx,
+ showDetailModal: true,
+ isEditing: false,
+ editJieshao: item.jieshao || '',
+ editJiage: item.jiage || '',
+ editLabelId: item.labelId || '',
+ editLabelName: item.labelName || '',
+ editCommissionEnabled: item.commissionEnabled || false,
+ editCommissionValue: item.commissionValue || '',
+ detailLinkFilter: 0,
+ detailLinkList: [],
+ detailLinkPage: 1,
+ detailLinkHasMore: true
+ })
+ this.loadDetailLinks()
+ },
+
+ hideDetailModal() { this.setData({ showDetailModal: false, isEditing: false }) },
+ showAddModal() {
+ this.setData({
+ showAddModal: true,
+ addJieshao: '', addJiage: '', addLabelId: '', addLabelName: '',
+ addCommissionEnabled: false, addCommissionValue: ''
+ })
+ },
+ hideAddModal() { this.setData({ showAddModal: false }) },
+ hideDeleteConfirm() { this.setData({ showDeleteConfirm: false }) },
+ hideNewLinkConfirm() { this.setData({ showNewLinkConfirm: false }) },
+
+ // ---------- 链接筛选与加载 ----------
+ onToggleLinkFilter() {
+ const newFilter = this.data.detailLinkFilter === 0 ? 1 : 0
+ this.setData({
+ detailLinkFilter: newFilter,
+ detailLinkList: [],
+ detailLinkPage: 1,
+ detailLinkHasMore: true
+ }, () => this.loadDetailLinks())
+ },
+
+ async loadDetailLinks() {
+ const { currentTemplate, detailLinkFilter, detailLinkPage, isLoadingLinks } = this.data
+ if (!currentTemplate || isLoadingLinks) return
+ this.setData({ isLoadingLinks: true })
+ try {
+ const res = await request({
+ url: staffOrOwner(STAFF_API.linkList, '/peizhi/hqsjlslj'),
+ method: 'POST',
+ data: {
+ mobanId: currentTemplate.mobanId,
+ used: detailLinkFilter,
+ page: detailLinkPage,
+ pageSize: LINK_PAGE_SIZE
+ }
+ })
+ if (res && apiOk(res)) {
+ const data = res.data.data || {}
+ const newLinks = data.list || []
+ const merged = detailLinkPage === 1 ? newLinks : [...this.data.detailLinkList, ...newLinks]
+ this.setData({
+ detailLinkList: merged,
+ detailLinkHasMore: newLinks.length === LINK_PAGE_SIZE,
+ isLoadingLinks: false
+ })
+ } else {
+ this.setData({ isLoadingLinks: false })
+ }
+ } catch (e) {
+ this.setData({ isLoadingLinks: false })
+ wx.showToast({ title: '加载链接失败', icon: 'error' })
+ }
+ },
+
+ onLoadMoreLinks() {
+ if (!this.data.detailLinkHasMore || this.data.isLoadingLinks) return
+ this.setData({ detailLinkPage: this.data.detailLinkPage + 1 }, () => this.loadDetailLinks())
+ },
+
+ onCopyLinkItem(e) {
+ const url = e.currentTarget.dataset.url
+ if (!url) return
+ wx.setClipboardData({ data: url, success: () => wx.showToast({ title: '已复制', icon: 'success' }) })
+ },
+
+ // ---------- 弹窗内操作 ----------
+ onGenerateLinkModal() {
+ const { currentTemplate, currentTemplateIndex } = this.data
+ if (!currentTemplate) return
+ if (currentTemplate.hasGenerated && !currentTemplate.isCopied) {
+ this.setData({ showNewLinkConfirm: true })
+ return
+ }
+ this.generateLinkForTemplate(currentTemplate, currentTemplateIndex)
+ },
+
+ onCopyLinkModal() {
+ const { currentTemplate, linkMap } = this.data
+ if (!currentTemplate || !currentTemplate.linkUrl) {
+ wx.showToast({ title: '请先生成链接', icon: 'none' })
+ return
+ }
+ wx.setClipboardData({
+ data: currentTemplate.linkUrl,
+ success: () => {
+ const mobanId = currentTemplate.mobanId
+ const updatedLinkMap = { ...linkMap }
+ if (updatedLinkMap[mobanId]) updatedLinkMap[mobanId].isCopied = true
+ const updatedList = [...this.data.templateList]
+ if (this.data.currentTemplateIndex !== -1) {
+ updatedList[this.data.currentTemplateIndex].isCopied = true
+ }
+ const updatedCurrent = { ...currentTemplate, isCopied: true }
+ this.setData({ linkMap: updatedLinkMap, templateList: updatedList, currentTemplate: updatedCurrent })
+ wx.showToast({ title: '链接已复制', icon: 'success' })
+ }
+ })
+ },
+
+ // ---------- 编辑 ----------
+ onEditTemplate() {
+ if (!this.data.isEditing) this.setData({ isEditing: true })
+ else this.saveTemplateEdit()
+ },
+ onEditJieshaoInput(e) { this.setData({ editJieshao: e.detail.value }) },
+ onEditJiageInput(e) { this.setData({ editJiage: e.detail.value }) },
+ onEditLabelChange(e) {
+ const idx = e.detail.value
+ const label = this.data.currentTypeChenghaoList[idx]
+ this.setData({ editLabelId: label ? label.id : '', editLabelName: label ? label.mingcheng : '' })
+ },
+ onEditCommissionToggle() { this.setData({ editCommissionEnabled: !this.data.editCommissionEnabled }) },
+ onEditCommissionValueInput(e) { this.setData({ editCommissionValue: e.detail.value }) },
+
+ async saveTemplateEdit() {
+ const { currentTemplate, selectedTypeId, editJieshao, editJiage, editLabelId, editCommissionEnabled, editCommissionValue } = this.data
+ if (!currentTemplate || !selectedTypeId) return
+ if (editCommissionEnabled && (!editCommissionValue || isNaN(parseFloat(editCommissionValue)))) {
+ wx.showToast({ title: '请输入有效佣金金额', icon: 'none' })
+ return
+ }
+ wx.showLoading({ title: '保存中...', mask: true })
+ try {
+ const res = await request({
+ url: staffOrOwner(STAFF_API.templateUpdate, '/peizhi/sjddmbgx'),
+ method: 'POST',
+ data: {
+ mobanId: currentTemplate.mobanId,
+ shangpinTypeId: selectedTypeId,
+ jieshao: editJieshao,
+ jiage: editJiage,
+ labelId: editLabelId || '',
+ commissionEnabled: editCommissionEnabled,
+ commissionValue: editCommissionValue
+ }
+ })
+ if (res && apiOk(res)) {
+ const updatedList = [...this.data.templateList]
+ const idx = this.data.currentTemplateIndex
+ if (idx !== -1) {
+ updatedList[idx] = {
+ ...updatedList[idx],
+ jieshao: editJieshao,
+ jiage: editJiage,
+ labelId: editLabelId,
+ labelName: this.getLabelNameById(editLabelId),
+ commissionEnabled: editCommissionEnabled,
+ commissionValue: editCommissionValue
+ }
+ this.setData({ templateList: updatedList, currentTemplate: updatedList[idx], isEditing: false })
+ }
+ wx.showToast({ title: '修改成功', icon: 'success' })
+ } else {
+ wx.showToast({ title: res.data.msg || '修改失败', icon: 'error' })
+ }
+ } catch (e) {
+ wx.showToast({ title: '修改失败', icon: 'error' })
+ } finally { wx.hideLoading() }
+ },
+
+ onDeleteTemplate() { this.setData({ showDeleteConfirm: true }) },
+
+ async confirmDeleteTemplate() {
+ const { currentTemplate, selectedTypeId, linkMap } = this.data
+ if (!currentTemplate || !selectedTypeId) return
+ wx.showLoading({ title: '删除中...', mask: true })
+ try {
+ const res = await request({
+ url: staffOrOwner(STAFF_API.templateDelete, '/peizhi/sjddmbsc'),
+ method: 'POST',
+ data: { mobanId: currentTemplate.mobanId, shangpinTypeId: selectedTypeId }
+ })
+ if (res && apiOk(res)) {
+ const updatedLinkMap = { ...linkMap }
+ delete updatedLinkMap[currentTemplate.mobanId]
+ const updatedList = [...this.data.templateList]
+ updatedList.splice(this.data.currentTemplateIndex, 1)
+ this.setData({
+ linkMap: updatedLinkMap,
+ templateList: updatedList,
+ showDetailModal: false,
+ showDeleteConfirm: false,
+ currentTemplate: null,
+ currentTemplateIndex: -1
+ })
+ wx.showToast({ title: '删除成功', icon: 'success' })
+ } else {
+ wx.showToast({ title: res.data.msg || '删除失败', icon: 'error' })
+ }
+ } catch (e) {
+ wx.showToast({ title: '删除失败', icon: 'error' })
+ } finally { wx.hideLoading() }
+ },
+
+ async confirmNewLink() {
+ const { currentTemplate, currentTemplateIndex } = this.data
+ if (!currentTemplate) return
+ this.setData({ showNewLinkConfirm: false })
+ await this.generateLinkForTemplate(currentTemplate, currentTemplateIndex)
+ },
+
+ // ---------- 添加模板 ----------
+ onAddJieshaoInput(e) { this.setData({ addJieshao: e.detail.value }) },
+ onAddJiageInput(e) { this.setData({ addJiage: e.detail.value }) },
+ onAddLabelChange(e) {
+ const idx = e.detail.value
+ const label = this.data.currentTypeChenghaoList[idx]
+ this.setData({ addLabelId: label ? label.id : '', addLabelName: label ? label.mingcheng : '' })
+ },
+ onAddCommissionToggle() { this.setData({ addCommissionEnabled: !this.data.addCommissionEnabled }) },
+ onAddCommissionValueInput(e) { this.setData({ addCommissionValue: e.detail.value }) },
+
+ async onAddTemplate() {
+ const { selectedTypeId, addJieshao, addJiage, addLabelId, addCommissionEnabled, addCommissionValue } = this.data
+ if (!selectedTypeId) {
+ wx.showToast({ title: '请选择订单类型', icon: 'none' })
+ return
+ }
+ if (!addJieshao.trim()) {
+ wx.showToast({ title: '请输入订单介绍', icon: 'none' })
+ return
+ }
+ if (!addJiage || isNaN(parseFloat(addJiage)) || parseFloat(addJiage) <= 0) {
+ wx.showToast({ title: '请输入有效的价格', icon: 'none' })
+ return
+ }
+ if (addCommissionEnabled && (!addCommissionValue || isNaN(parseFloat(addCommissionValue)))) {
+ wx.showToast({ title: '请输入有效的佣金金额', icon: 'none' })
+ return
+ }
+
+ wx.showLoading({ title: '添加中...', mask: true })
+ try {
+ const res = await request({
+ url: staffOrOwner(STAFF_API.templateCreate, '/peizhi/sjtjddmb'),
+ method: 'POST',
+ data: {
+ shangpinTypeId: selectedTypeId,
+ jieshao: addJieshao.trim(),
+ jiage: addJiage,
+ labelId: addLabelId || '',
+ commissionEnabled: addCommissionEnabled,
+ commissionValue: addCommissionValue
+ }
+ })
+ if (res && apiOk(res)) {
+ const data = res.data.data || {}
+ const newTemplate = {
+ mobanId: data.mobanId,
+ jieshao: addJieshao,
+ jiage: addJiage,
+ fabushuliang: 0,
+ hasGenerated: false,
+ isCopied: false,
+ linkUrl: '',
+ labelId: addLabelId,
+ labelName: this.getLabelNameById(addLabelId),
+ commissionEnabled: addCommissionEnabled,
+ commissionValue: addCommissionValue
+ }
+ this.setData({
+ templateList: [newTemplate, ...this.data.templateList],
+ showAddModal: false,
+ addJieshao: '', addJiage: '', addLabelId: '', addLabelName: '',
+ addCommissionEnabled: false, addCommissionValue: ''
+ })
+ wx.showToast({ title: '添加成功', icon: 'success' })
+ } else {
+ wx.showToast({ title: res.data.msg || '添加失败', icon: 'error' })
+ }
+ } catch (e) {
+ wx.showToast({ title: '添加失败', icon: 'error' })
+ } finally { wx.hideLoading() }
+ },
+
+ getLabelNameById(labelId) {
+ if (!labelId) return ''
+ const list = this.data.currentTypeChenghaoList
+ const found = list.find(item => item.id == labelId)
+ return found ? found.mingcheng : ''
+ },
+
+ onScrollToLower() {
+ const { hasMoreData, isLoadingMore, isSearchMode } = this.data
+ if (!hasMoreData || isLoadingMore || isSearchMode) return
+ this.setData({ currentPage: this.data.currentPage + 1 }, () => this.loadTemplates())
+ },
+
+ onSelectType(e) {
+ const { id, item } = e.currentTarget.dataset
+ this.setSelectedType(item)
+ }
})
\ No newline at end of file
diff --git a/pages/express-order/express-order.wxml b/pages/express-order/express-order.wxml
index 64cfa0a..7a941d7 100644
--- a/pages/express-order/express-order.wxml
+++ b/pages/express-order/express-order.wxml
@@ -2,12 +2,13 @@
- 可用余额
+ {{isStaffMode ? '可用额度' : '可用余额'}}
{{sjyue}}
元
- 生成链接将从此余额扣除
+ 当前身份:{{staffRoleName || '客服'}} · 生成链接从此额度扣除
+ 生成链接将从此余额扣除
@@ -126,6 +127,7 @@
{{item.lianjie}}
+ {{item.roleName || item.displayName}}
{{item.is_used ? '已使用' : '未使用'}}
复制
diff --git a/pages/fighter-recharge/fighter-recharge.js b/pages/fighter-recharge/fighter-recharge.js
index ebb1da2..f2fa86e 100644
--- a/pages/fighter-recharge/fighter-recharge.js
+++ b/pages/fighter-recharge/fighter-recharge.js
@@ -1,975 +1,1015 @@
-// pages/dashou-chongzhi/index.js
-import request from '../../utils/request';
-// 引入弹窗服务(路径根据实际调整)
-import PopupService from '../../services/popupService.js';
-
-Page({
- data: {
- // ========== 全局数据 ==========
- clubmber: [], // 会员列表(注意:全局变量中是 clumber,这里保持原样)
- yajin: 0, // 押金
- jifen: 0, // 积分(注意:全局变量中是 jinfen)
- jifenProgress: 0, // 积分进度条角度
-
- // ========== 会员商品列表 ==========
- huiyuanList: [], // 从后端获取的会员列表
- currentHuiyuanIndex: 0, // 当前选中的会员索引
- currentHuiyuan: {}, // 当前选中的会员详情
-
- // ========== 押金充值相关 ==========
- showYajinModal: false, // 显示押金弹窗
- yajinAmount: '100', // 押金金额
-
- // ========== 支付相关 ==========
- payLoading: false, // 支付加载状态
- loadingText: '支付中...',
-
- // ========== 订单ID记录 ==========
- currentDingdanid: '', // 当前操作的订单ID
-
- // ========== 计算高度 ==========
- huiyuanListHeight: 120,
-
- // ========== 会员详情规则 ==========
- detailRules: [
- '会员有效期为30天,从购买当天开始计算',
- '会员期间享受优先接单特权',
- '可享受专属客服服务',
- '会员到期前3天会有提醒',
- '支持随时续费,续费天数叠加'
- ],
-
- // ========== 新增:跳转参数处理 ==========
- // 用于接收从其他页面跳转过来的参数,决定是否自动滚动
- jumpParams: {},
-
- // ========== 新增:支付方式选择 ==========
- showPayMethodModal: false, // 支付方式选择弹窗
- currentBuyType: null, // 当前购买类型:1会员 2押金 3积分
- currentHuiyuanId: null, // 当前选中的会员ID(用于会员购买)
- currentYajinAmount: null, // 当前押金金额(用于押金购买)
-
- // ========== 新增:余额抵扣身份选择 ==========
- showBalanceModal: false, // 余额抵扣身份选择弹窗
- balanceOptions: [], // 后端返回的身份列表
- selectedBalanceId: null, // 选中的身份ID
-
- // ========== 新增:余额抵扣确认弹窗 ==========
- showConfirmModal: false, // 余额抵扣确认弹窗
- selectedBalanceInfo: null, // 选中的身份详情(用于确认弹窗)
-
- // ========== 新增:会员详情弹窗 ==========
- showMemberModal: false,
- currentMemberDetail: null,
- },
-
- // ========== 生命周期函数 ==========
- onLoad(options) {
- //console.log('页面加载,跳转参数:', options);
-
- // ✅ 新增:保存跳转参数
- this.setData({
- jumpParams: options || {}
- });
-
- this.initPage();
- },
-
- // pages/submit/submit.js 中添加 onHide 方法(如果已有则合并内容)
-onHide() {
- const popupComp = this.selectComponent('#popupNotice');
- if (popupComp && popupComp.cleanup) {
- popupComp.cleanup();
- }
- },
-
- onShow() {
- // 每次显示页面都刷新数据
- this.registerNotificationComponent();
- this.loadFromGlobalData();
- this.checkAndRefreshData();
-
- // 调用统一弹窗组件检查并展示公告
- PopupService.checkAndShow(this, 'dashouchongzhi');
- },
-
- // 注册通知组件(原有,保持不变)
- registerNotificationComponent() {
- const app = getApp();
- const notificationComp = this.selectComponent('#global-notification');
-
- if (notificationComp && notificationComp.showNotification) {
-
- app.globalData.globalNotification = {
- show: (data) => notificationComp.showNotification(data),
- hide: () => notificationComp.hideNotification()
- };
- }
- },
-
- onReady() {
- // ✅ 新增:页面渲染完成后处理自动滚动
- this.handleAutoScroll();
- },
-
- // ========== 初始化页面 ==========
- async initPage() {
- // 从全局数据加载
- this.loadFromGlobalData();
-
- // 获取会员商品列表
- await this.fetchHuiyuanList();
-
- // 计算会员列表高度
- this.calculateHuiyuanHeight();
- },
-
- // ========== 从全局变量加载数据 ==========
- loadFromGlobalData() {
- const app = getApp();
- const globalData = app.globalData || {};
-
- // ✅ 重要:保持字段对应关系
- // 全局变量中是 clumber,页面中是 clubmber
- // 全局变量中是 jinfen,页面中是 jifen
- this.setData({
- clubmber: globalData.clumber || [],
- yajin: globalData.yajin || 0,
- jifen: globalData.jinfen || 0 // ✅ 注意:这里从 jinfen 读取
- });
-
- // 计算积分进度条
- this.calculateJifenProgress();
- },
-
- // ========== 计算积分进度条角度 ==========
- calculateJifenProgress() {
- const jifen = this.data.jifen || 0;
- const progress = (jifen / 10) * 360; // 满分10分
- this.setData({
- jifenProgress: progress
- });
- },
-
- // ========== 计算会员列表高度 ==========
- calculateHuiyuanHeight() {
- const clubmber = this.data.clubmber || [];
-
- const itemHeight = 100; // 每个会员标签高度
- const maxHeight = 300; // 最大高度(3个会员)
-
- let height = clubmber.length * itemHeight;
- height = Math.min(height, maxHeight);
-
- this.setData({
- huiyuanListHeight: height
- });
- },
-
- // ========== 获取会员商品列表 ==========
- async fetchHuiyuanList() {
- try {
- const res = await request({
- url: '/shangpin/dshyhq',
- method: 'POST',
- data: {}
- });
-
- if (res.data.code === 200) {
- const huiyuanList = res.data.data || [];
-
- // 处理数据,标记已购买的会员
- const processedList = this.processHuiyuanList(huiyuanList);
-
- this.setData({
- huiyuanList: processedList,
- currentHuiyuan: processedList[0] || {}
- });
- } else {
- wx.showToast({
- title: res.data.message || '获取会员列表失败',
- icon: 'none'
- });
- }
- } catch (error) {
- console.error('获取会员列表失败:', error);
- wx.showToast({
- title: '网络错误,请重试',
- icon: 'none'
- });
- }
- },
-
- // ========== 处理会员列表,标记已购买的会员 ==========
- processHuiyuanList(huiyuanList) {
- const clubmber = this.data.clubmber || [];
-
- return huiyuanList.map(item => {
- // 检查是否已购买(注意:会员ID字段是 id)
- const boughtItem = clubmber.find(c => c.huiyuanid === item.id);
-
- return {
- ...item,
- isBought: !!boughtItem,
- buyInfo: boughtItem || null
- };
- });
- },
-
- // ========== Swiper切换事件 ==========
- onSwiperChange(e) {
- const current = e.detail.current;
- const huiyuanList = this.data.huiyuanList || [];
-
- this.setData({
- currentHuiyuanIndex: current,
- currentHuiyuan: huiyuanList[current] || {}
- });
- },
-
- // ========== 手动选择会员 ==========
- selectHuiyuan(e) {
- const index = e.currentTarget.dataset.index;
- const huiyuanList = this.data.huiyuanList || [];
-
- this.setData({
- currentHuiyuanIndex: index,
- currentHuiyuan: huiyuanList[index] || {}
- });
- },
-
- // ========== 格式化到期时间 ==========
- formatDaoqiTime(daoqi) {
- if (!daoqi) return '';
-
- try {
- // 如果是标准日期格式,格式化为 YYYY-MM-DD
- if (daoqi.includes(' ')) {
- const dateStr = daoqi.split(' ')[0];
- const dateParts = dateStr.split('-');
- if (dateParts.length === 3) {
- return `${dateParts[0]}-${dateParts[1]}-${dateParts[2]}`;
- }
- }
- return daoqi;
- } catch (error) {
- console.error('格式化到期时间错误:', error);
- return daoqi;
- }
- },
-
- // ========== 计算剩余时间 ==========
- formatRemainTime(daoqi) {
- if (!daoqi) return '';
-
- try {
- const now = new Date();
- const end = new Date(daoqi);
- const diff = end - now;
-
- if (diff <= 0) return '已过期';
-
- const days = Math.floor(diff / (1000 * 60 * 60 * 24));
- if (days > 0) {
- return `${days}天`;
- }
-
- const hours = Math.floor(diff / (1000 * 60 * 60));
- if (hours > 0) {
- return `${hours}小时`;
- }
-
- return '即将到期';
- } catch (error) {
- console.error('计算剩余时间错误:', error);
- return '时间错误';
- }
- },
-
- // ========== 处理自动滚动 ==========
- handleAutoScroll() {
- const { jumpParams } = this.data;
-
- if (!jumpParams || !jumpParams.needScroll) return;
-
- setTimeout(() => {
- let scrollTop = 0;
-
- if (jumpParams.scrollTo === 'bottom') {
- scrollTop = 2000;
- } else if (jumpParams.scrollTo === 'member') {
- return;
- }
-
- if (scrollTop > 0) {
- wx.pageScrollTo({
- scrollTop: scrollTop,
- duration: 300
- });
- }
- }, 800);
- },
-
- // ========== 押金充值相关 ==========
- showYajinModal() {
- this.setData({
- showYajinModal: true,
- yajinAmount: '100'
- });
- },
-
- hideYajinModal() {
- this.setData({
- showYajinModal: false
- });
- },
-
- onYajinInput(e) {
- let value = e.detail.value;
- value = value.replace(/[^\d]/g, '');
-
- if (value) {
- const num = parseInt(value);
- if (num < 1) value = '1';
- if (num > 10000) value = '10000';
- }
-
- this.setData({
- yajinAmount: value
- });
- },
-
- setYajinAmount(e) {
- const amount = e.currentTarget.dataset.amount;
- this.setData({
- yajinAmount: amount
- });
- },
-
- async handleYajinPay() {
- const amount = this.data.yajinAmount;
-
- if (!amount || amount < 1 || amount > 10000) {
- wx.showToast({
- title: '请输入1-10000元的金额',
- icon: 'none'
- });
- return;
- }
-
- this.showLoading('发起支付中...');
-
- try {
- const res = await request({
- url: '/shangpin/yajingoumai',
- method: 'POST',
- data: {
- jine: parseInt(amount)
- }
- });
-
- if (res.data.code === 200) {
- const payParams = res.data.payParams;
- const dingdanid = res.data.dingdanid;
-
- this.setData({
- currentDingdanid: dingdanid
- });
-
- await this.wechatPay(payParams, 'yajin');
- } else {
- wx.showToast({
- title: res.data.message || '支付发起失败',
- icon: 'none'
- });
- }
- } catch (error) {
- console.error('押金支付失败:', error);
- wx.showToast({
- title: '支付失败,请重试',
- icon: 'none'
- });
- } finally {
- this.hideLoading();
- }
- },
-
- // ========== 积分充值相关 ==========
- // 🔥 修改点1:增加积分已满的硬拦截,不弹任何提示,直接返回
- onJifenClick(e) {
- // 优先判断积分是否已满(10分为满分)
- if (this.data.jifen >= 10) {
- // 积分已满,不弹窗,不提示,静默返回
- return;
- }
-
- // 检查是否有会员
- const clubmber = this.data.clubmber || [];
- if (clubmber.length === 0) {
- wx.showToast({
- title: '请先购买会员才能充值积分',
- icon: 'none',
- duration: 3000
- });
- return;
- }
-
- // 积分未满,正常打开支付方式选择
- this.setData({
- currentBuyType: 3,
- showPayMethodModal: true
- });
- },
-
- // ========== 会员购买相关 ==========
- async handleHuiyuanBuy(e) {
- const huiyuanId = e.currentTarget.dataset.id;
-
- if (!huiyuanId) {
- wx.showToast({
- title: '会员信息错误',
- icon: 'none'
- });
- return;
- }
-
- this.showLoading('发起会员支付中...');
-
- try {
- const res = await request({
- url: '/shangpin/huiyuangm',
- method: 'POST',
- data: {
- huiyuanid: huiyuanId
- }
- });
-
- if (res.data.code === 200) {
- const payParams = res.data.payParams;
- const dingdanid = res.data.dingdanid;
-
- this.setData({
- currentDingdanid: dingdanid
- });
-
- await this.wechatPay(payParams, 'huiyuan', huiyuanId);
- } else {
- wx.showToast({
- title: res.data.message || '会员支付发起失败',
- icon: 'none'
- });
- }
- } catch (error) {
- console.error('会员支付失败:', error);
- wx.showToast({
- title: '支付失败,请重试',
- icon: 'none'
- });
- } finally {
- this.hideLoading();
- }
- },
-
- // ========== 积分购买(微信支付)—— 已替换为正确的后端接口 ==========
- async handleJifenBuy(e) {
- const disabled = e.currentTarget.dataset.disabled;
-
- // 检查积分是否已满
- if (disabled === 'true') {
- wx.showToast({ title: '积分已满,无需补充', icon: 'none' });
- return;
- }
-
- // 检查是否有会员
- const clubmber = this.data.clubmber || [];
- if (clubmber.length === 0) {
- wx.showToast({ title: '请先购买会员才能充值积分', icon: 'none', duration: 3000 });
- return;
- }
-
- this.showLoading('发起积分支付中...');
-
- try {
- const res = await request({
- url: '/shangpin/jifenbc', // 正确的积分下单接口
- method: 'POST',
- data: {}
- });
-
- if (res.data.code === 200) {
- const payParams = res.data.payParams;
- const dingdanid = res.data.dingdanid;
-
- this.setData({ currentDingdanid: dingdanid });
- await this.wechatPay(payParams, 'jifen');
- } else {
- wx.showToast({ title: res.data.message || '积分支付发起失败', icon: 'none' });
- }
- } catch (error) {
- wx.showToast({ title: '支付失败,请重试', icon: 'none' });
- } finally {
- this.hideLoading();
- }
- },
-
- // ========== 微信支付封装 ==========
- async wechatPay(payParams, payType, extraData = null) {
- return new Promise((resolve, reject) => {
- if (!payParams || !payParams.timeStamp || !payParams.paySign) {
- wx.showToast({
- title: '支付参数错误',
- icon: 'none'
- });
- reject(new Error('支付参数错误'));
- return;
- }
-
- this.showLoading('调起支付中...');
-
- wx.requestPayment({
- timeStamp: payParams.timeStamp,
- nonceStr: payParams.nonceStr,
- package: payParams.package,
- signType: payParams.signType || 'MD5',
- paySign: payParams.paySign,
- success: async () => {
- this.hideLoading();
-
- wx.showToast({
- title: '支付成功!确认中...',
- icon: 'none',
- duration: 2000
- });
-
- try {
- await this.startPolling(payType, extraData);
- resolve();
- } catch (error) {
- reject(error);
- }
- },
- fail: async (res) => {
- this.hideLoading();
-
- let errorMsg = '支付失败';
- if (res.errMsg.includes('cancel')) {
- errorMsg = '您取消了支付';
- } else if (res.errMsg.includes('fail')) {
- errorMsg = '支付失败,请重试';
- }
-
- wx.showToast({
- title: errorMsg,
- icon: 'none'
- });
-
- try {
- await this.handlePayFailure(payType, extraData);
- } catch (error) {
- console.error('支付失败通知失败:', error);
- }
-
- reject(new Error(res.errMsg));
- }
- });
- });
- },
-
- // ========== 开始轮询支付结果 ==========
- async startPolling(payType, extraData = null) {
- const maxRetries = 10;
- const interval = 2000;
- const dingdanid = this.data.currentDingdanid;
-
- if (!dingdanid) {
- wx.showToast({
- title: '订单号缺失',
- icon: 'none'
- });
- return;
- }
-
- this.showLoading('确认支付结果中...');
-
- for (let i = 0; i < maxRetries; i++) {
- try {
- const pollResult = await this.checkPayStatus(payType, dingdanid, extraData);
-
- if (pollResult.success) {
- this.hideLoading();
- await this.updateAfterPayment(payType, extraData, pollResult.data);
-
- wx.showToast({
- title: this.getSuccessMessage(payType),
- icon: 'success',
- duration: 2000
- });
-
- return;
- }
-
- await this.sleep(interval);
- } catch (error) {
- console.error(`轮询失败第${i + 1}次:`, error);
- }
- }
-
- this.hideLoading();
- wx.showToast({
- title: '支付确认超时,请联系客服',
- icon: 'none'
- });
- },
-
- // ========== 检查支付状态 ==========
- async checkPayStatus(payType, dingdanid, extraData = null) {
- let url = '';
- let data = { dingdanid: dingdanid };
-
- switch (payType) {
- case 'yajin':
- url = '/shangpin/yajinlunxun';
- break;
- case 'jifen':
- url = '/shangpin/jifenlunxun';
- break;
- case 'huiyuan':
- url = '/shangpin/huiyuanlx';
- data.huiyuanid = extraData;
- break;
- default:
- throw new Error('未知的支付类型');
- }
-
- try {
- const res = await request({
- url,
- method: 'POST',
- data
- });
-
- return {
- success: res.data.code === 200,
- data: res.data
- };
- } catch (error) {
- console.error('检查支付状态失败:', error);
- return {
- success: false,
- data: null
- };
- }
- },
-
- // ========== 支付失败处理 ==========
- async handlePayFailure(payType, extraData = null) {
- let url = '';
- let data = { dingdanid: this.data.currentDingdanid };
-
- switch (payType) {
- case 'yajin':
- url = '/shangpin/yajinshibai';
- break;
- case 'jifen':
- url = '/shangpin/jifenshibai';
- break;
- case 'huiyuan':
- url = '/shangpin/huiyuanshibai';
- data.huiyuanid = extraData;
- break;
- default:
- return;
- }
-
- try {
- await request({
- url,
- method: 'POST',
- data
- });
- } catch (error) {
- console.error('支付失败通知失败:', error);
- }
- },
-
- // ========== 支付成功后更新数据 ==========
- async updateAfterPayment(payType, extraData = null, responseData = null) {
- const app = getApp();
-
- switch (payType) {
- case 'yajin':
- if (responseData && responseData.yajin !== undefined) {
- app.globalData.yajin = responseData.yajin;
- }
- await this.refreshYajinData();
- break;
-
- case 'jifen':
- if (responseData && responseData.jifen !== undefined) {
- app.globalData.jinfen = responseData.jifen;
- }
- await this.refreshJifenData();
- break;
-
- case 'huiyuan':
- if (responseData && responseData.huiyuan) {
- app.globalData.jinfen = responseData.jifen;
- await this.updateClubmberData(responseData.huiyuan);
- }
- await this.refreshHuiyuanData(extraData);
- break;
- }
-
- this.loadFromGlobalData();
- },
-
- // ========== 刷新押金数据 ==========
- async refreshYajinData() {
- this.loadFromGlobalData();
- },
-
- // ========== 刷新积分数据 ==========
- async refreshJifenData() {
- this.loadFromGlobalData();
- },
-
- // ========== 刷新会员数据 ==========
- async refreshHuiyuanData(huiyuanId) {
- this.loadFromGlobalData();
- await this.fetchHuiyuanList();
- },
-
- // ========== 更新会员数据到全局变量 ==========
- async updateClubmberData(huiyuanData) {
- const app = getApp();
- const globalClubmber = app.globalData.clumber || [];
-
- const { huiyuanid, huiyuanming, daoqi } = huiyuanData;
-
- const index = globalClubmber.findIndex(item => item.huiyuanid === huiyuanid);
-
- if (index >= 0) {
- globalClubmber[index].daoqi = daoqi;
- } else {
- globalClubmber.push({
- huiyuanid: huiyuanid,
- huiyuanming: huiyuanming,
- daoqi: daoqi
- });
- }
-
- app.globalData.clumber = globalClubmber;
-
- this.setData({
- clubmber: [...globalClubmber]
- });
- },
-
- // ========== 检查并刷新数据 ==========
- async checkAndRefreshData() {
- this.loadFromGlobalData();
- },
-
- // ========== 工具方法 ==========
- getSuccessMessage(payType) {
- switch (payType) {
- case 'yajin': return '押金充值成功';
- case 'jifen': return '积分补充成功';
- case 'huiyuan': return '会员购买成功';
- default: return '支付成功';
- }
- },
-
- showLoading(text = '加载中...') {
- this.setData({
- payLoading: true,
- loadingText: text
- });
- },
-
- hideLoading() {
- this.setData({
- payLoading: false
- });
- },
-
- sleep(ms) {
- return new Promise(resolve => setTimeout(resolve, ms));
- },
-
- // ========== 新增功能(余额抵扣等) ==========
- onBuyHuiyuanClick(e) {
- const huiyuanId = e.currentTarget.dataset.id;
- this.setData({
- currentBuyType: 1,
- currentHuiyuanId: huiyuanId,
- showPayMethodModal: true
- });
- },
-
- onYajinClick() {
- this.showYajinModal();
- },
-
- onYajinConfirm() {
- const amount = this.data.yajinAmount;
- if (!amount || amount < 1 || amount > 10000) {
- wx.showToast({ title: '请输入1-10000元的金额', icon: 'none' });
- return;
- }
- this.hideYajinModal();
- this.setData({
- currentBuyType: 2,
- currentYajinAmount: amount,
- showPayMethodModal: true
- });
- },
-
- hidePayMethodModal() {
- this.setData({ showPayMethodModal: false });
- },
-
- // 🔥 修改点2:选择支付方式时再次校验积分状态(防止异步过程中积分变化)
- onPayMethodSelect(e) {
- const method = e.currentTarget.dataset.method;
- const { currentBuyType, currentHuiyuanId, currentYajinAmount, jifen } = this.data;
- this.hidePayMethodModal();
-
- // 如果是积分购买,且积分已满,直接拒绝
- if (currentBuyType === 3 && jifen >= 10) {
- wx.showToast({
- title: '积分已满,无需补充',
- icon: 'none'
- });
- return;
- }
-
- if (method === 'wx') {
- if (currentBuyType === 1) {
- this.handleHuiyuanBuy({ currentTarget: { dataset: { id: currentHuiyuanId } } });
- } else if (currentBuyType === 2) {
- this.setData({ yajinAmount: currentYajinAmount }, () => {
- this.handleYajinPay();
- });
- } else if (currentBuyType === 3) {
- // ✅ 调用已修正的积分支付函数
- this.handleJifenBuy({ currentTarget: { dataset: { disabled: 'false' } } });
- }
- } else if (method === 'balance') {
- this.fetchBalanceOptions();
- }
- },
-
- // 🔥 修改点3:请求余额抵扣选项前校验积分状态
- async fetchBalanceOptions() {
- const { currentBuyType, currentHuiyuanId, currentYajinAmount, jifen } = this.data;
-
- // 如果是积分购买,且积分已满,拒绝发起余额抵扣
- if (currentBuyType === 3 && jifen >= 10) {
- wx.showToast({
- title: '积分已满,无需补充',
- icon: 'none'
- });
- return;
- }
-
- this.showLoading('获取抵扣信息...');
-
- try {
- const res = await request({
- url: '/shangpin/czhqdy',
- method: 'POST',
- data: {
- leixing: currentBuyType,
- huiyuan_id: currentBuyType === 1 ? currentHuiyuanId : undefined,
- yajin_jine: currentBuyType === 2 ? parseInt(currentYajinAmount) : undefined,
- }
- });
- if (res.data.code === 200) {
- const options = res.data.data || [];
- this.setData({
- balanceOptions: options,
- showBalanceModal: true
- });
- } else {
- wx.showToast({ title: res.data.message || '获取失败', icon: 'none' });
- }
- } catch (error) {
- console.error('获取抵扣身份失败:', error);
- wx.showToast({ title: '网络错误', icon: 'none' });
- } finally {
- this.hideLoading();
- }
- },
-
- hideBalanceModal() {
- this.setData({ showBalanceModal: false });
- },
-
- onBalanceSelect(e) {
- const identityId = e.currentTarget.dataset.id;
- const selected = this.data.balanceOptions.find(opt => opt.id === identityId);
- if (!selected) return;
-
- this.setData({
- selectedBalanceId: identityId,
- selectedBalanceInfo: selected,
- showConfirmModal: true,
- showBalanceModal: false
- });
- },
-
- hideConfirmModal() {
- this.setData({ showConfirmModal: false });
- },
-
- async confirmBalancePay() {
- const { selectedBalanceId, currentBuyType, currentHuiyuanId, currentYajinAmount } = this.data;
- if (!selectedBalanceId) return;
-
- this.hideConfirmModal();
- this.showLoading('抵扣支付中...');
-
- try {
- const res = await request({
- url: '/shangpin/dsqrgmdh',
- method: 'POST',
- data: {
- leixing: currentBuyType,
- shenfen_id: selectedBalanceId,
- huiyuan_id: currentBuyType === 1 ? currentHuiyuanId : undefined,
- yajin_jine: currentBuyType === 2 ? parseInt(currentYajinAmount) : undefined,
- }
- });
-
- if (res.data.code === 200) {
- const responseData = res.data.data || {};
- await this.updateAfterPayment(
- currentBuyType === 1 ? 'huiyuan' : (currentBuyType === 2 ? 'yajin' : 'jifen'),
- currentBuyType === 1 ? currentHuiyuanId : null,
- responseData
- );
- wx.showToast({ title: '购买成功', icon: 'success' });
- } else {
- wx.showToast({ title: res.data.message || '购买失败', icon: 'none' });
- }
- } catch (error) {
- console.error('抵扣支付失败:', error);
- wx.showToast({ title: '网络错误', icon: 'none' });
- } finally {
- this.hideLoading();
- }
- },
-
- showMemberDetail(e) {
- const item = e.currentTarget.dataset.item;
- this.setData({
- currentMemberDetail: item,
- showMemberModal: true
- });
- },
-
- hideMemberModal() {
- this.setData({ showMemberModal: false });
- },
+// pages/dashou-chongzhi/index.js
+import request from '../../utils/request';
+import PopupService from '../../services/popupService.js';
+import { ensurePhoneAuth } from '../../utils/phone-auth.js';
+
+Page({
+ data: {
+ // ========== 全局数据 ==========
+ clubmber: [], // 会员列表(注意:全局变量中是 clumber,这里保持原样)
+ yajin: 0, // 押金
+ jifen: 0, // 积分(注意:全局变量中是 jinfen)
+ jifenProgress: 0, // 积分进度条角度
+
+ // ========== 会员商品列表 ==========
+ huiyuanList: [], // 从后端获取的会员列表
+ currentHuiyuanIndex: 0, // 当前选中的会员索引
+ currentHuiyuan: {}, // 当前选中的会员详情
+
+ // ========== 押金充值相关 ==========
+ showYajinModal: false, // 显示押金弹窗
+ yajinAmount: '100', // 押金金额
+
+ // ========== 支付相关 ==========
+ payLoading: false, // 支付加载状态
+ loadingText: '支付中...',
+
+ // ========== 订单ID记录 ==========
+ currentDingdanid: '', // 当前操作的订单ID
+
+ // ========== 计算高度 ==========
+ huiyuanListHeight: 120,
+
+ // ========== 会员详情规则 ==========
+ detailRules: [],
+
+ // ========== 新增:跳转参数处理 ==========
+ // 用于接收从其他页面跳转过来的参数,决定是否自动滚动
+ jumpParams: {},
+
+ // ========== 新增:支付方式选择 ==========
+ showPayMethodModal: false, // 支付方式选择弹窗
+ currentBuyType: null, // 当前购买类型:1会员 2押金 3积分
+ currentHuiyuanId: null, // 当前选中的会员ID(用于会员购买)
+ currentBuyIsTrial: false, // 正式/体验:仅体验禁余额抵扣
+ currentYajinAmount: null, // 当前押金金额(用于押金购买)
+
+ // ========== 新增:余额抵扣身份选择 ==========
+ showBalanceModal: false, // 余额抵扣身份选择弹窗
+ balanceOptions: [], // 后端返回的身份列表
+ selectedBalanceId: null, // 选中的身份ID
+
+ // ========== 新增:余额抵扣确认弹窗 ==========
+ showConfirmModal: false, // 余额抵扣确认弹窗
+ selectedBalanceInfo: null, // 选中的身份详情(用于确认弹窗)
+
+ // ========== 新增:会员详情弹窗 ==========
+ showMemberModal: false,
+ currentMemberDetail: null,
+ },
+
+ // ========== 生命周期函数 ==========
+ onLoad(options) {
+ //console.log('页面加载,跳转参数:', options);
+
+ // ✅ 新增:保存跳转参数
+ this.setData({
+ jumpParams: options || {}
+ });
+
+ this.initPage();
+ },
+
+ // pages/submit/submit.js 中添加 onHide 方法(如果已有则合并内容)
+onHide() {
+ const popupComp = this.selectComponent('#popupNotice');
+ if (popupComp && popupComp.cleanup) {
+ popupComp.cleanup();
+ }
+ },
+
+ async onShow() {
+ const phoneOk = await ensurePhoneAuth({ redirect: true, role: 'dashou' });
+ if (!phoneOk) return;
+
+ // 每次显示页面都刷新数据
+ this.registerNotificationComponent();
+ this.loadFromGlobalData();
+ this.checkAndRefreshData();
+
+ // 调用统一弹窗组件检查并展示公告
+ PopupService.checkAndShow(this, 'dashouchongzhi');
+ },
+
+ // 注册通知组件(原有,保持不变)
+ registerNotificationComponent() {
+ const app = getApp();
+ const notificationComp = this.selectComponent('#global-notification');
+
+ if (notificationComp && notificationComp.showNotification) {
+
+ app.globalData.globalNotification = {
+ show: (data) => notificationComp.showNotification(data),
+ hide: () => notificationComp.hideNotification()
+ };
+ }
+ },
+
+ onReady() {
+ // ✅ 新增:页面渲染完成后处理自动滚动
+ this.handleAutoScroll();
+ },
+
+ // ========== 初始化页面 ==========
+ async initPage() {
+ // 从全局数据加载
+ this.loadFromGlobalData();
+
+ // 获取会员商品列表
+ await this.fetchHuiyuanList();
+
+ // 计算会员列表高度
+ this.calculateHuiyuanHeight();
+ },
+
+ // ========== 从全局变量加载数据 ==========
+ loadFromGlobalData() {
+ const app = getApp();
+ const globalData = app.globalData || {};
+
+ // ✅ 重要:保持字段对应关系
+ // 全局变量中是 clumber,页面中是 clubmber
+ // 全局变量中是 jinfen,页面中是 jifen
+ this.setData({
+ clubmber: globalData.clumber || [],
+ yajin: globalData.yajin || 0,
+ jifen: globalData.jinfen || 0 // ✅ 注意:这里从 jinfen 读取
+ });
+
+ // 计算积分进度条
+ this.calculateJifenProgress();
+ },
+
+ // ========== 计算积分进度条角度 ==========
+ calculateJifenProgress() {
+ const jifen = this.data.jifen || 0;
+ const progress = (jifen / 10) * 360; // 满分10分
+ this.setData({
+ jifenProgress: progress
+ });
+ },
+
+ // ========== 计算会员列表高度 ==========
+ calculateHuiyuanHeight() {
+ const clubmber = this.data.clubmber || [];
+
+ const itemHeight = 100; // 每个会员标签高度
+ const maxHeight = 300; // 最大高度(3个会员)
+
+ let height = clubmber.length * itemHeight;
+ height = Math.min(height, maxHeight);
+
+ this.setData({
+ huiyuanListHeight: height
+ });
+ },
+
+ // ========== 获取会员商品列表 ==========
+ async fetchHuiyuanList() {
+ try {
+ const res = await request({
+ url: '/shangpin/dshyhq',
+ method: 'POST',
+ data: {}
+ });
+
+ if (res.data.code === 200) {
+ const huiyuanList = res.data.data || [];
+
+ // 处理数据,标记已购买的会员
+ const processedList = this.processHuiyuanList(huiyuanList);
+
+ const currentHuiyuan = processedList[0] || {};
+ this.setData({
+ huiyuanList: processedList,
+ currentHuiyuan,
+ detailRules: this.buildDetailRules(currentHuiyuan)
+ });
+ } else {
+ wx.showToast({
+ title: res.data.message || '获取会员列表失败',
+ icon: 'none'
+ });
+ }
+ } catch (error) {
+ console.error('获取会员列表失败:', error);
+ wx.showToast({
+ title: '网络错误,请重试',
+ icon: 'none'
+ });
+ }
+ },
+
+ // ========== 处理会员列表,标记已购买的会员 ==========
+ processHuiyuanList(huiyuanList) {
+ const clubmber = this.data.clubmber || [];
+
+ return huiyuanList.map(item => {
+ // 检查是否已购买(注意:会员ID字段是 id)
+ const boughtItem = clubmber.find(c => c.huiyuanid === item.id);
+
+ return {
+ ...item,
+ isBought: !!boughtItem,
+ buyInfo: boughtItem || null
+ };
+ });
+ },
+
+ // ========== Swiper切换事件 ==========
+ onSwiperChange(e) {
+ const current = e.detail.current;
+ const huiyuanList = this.data.huiyuanList || [];
+ const currentHuiyuan = huiyuanList[current] || {};
+
+ this.setData({
+ currentHuiyuanIndex: current,
+ currentHuiyuan,
+ detailRules: this.buildDetailRules(currentHuiyuan)
+ });
+ },
+
+ // ========== 手动选择会员 ==========
+ selectHuiyuan(e) {
+ const index = e.currentTarget.dataset.index;
+ const huiyuanList = this.data.huiyuanList || [];
+
+ const currentHuiyuan = huiyuanList[index] || {};
+ this.setData({
+ currentHuiyuanIndex: index,
+ currentHuiyuan,
+ detailRules: this.buildDetailRules(currentHuiyuan)
+ });
+ },
+
+ buildDetailRules(huiyuan = {}) {
+ const formalDays = huiyuan.formal_days || 30;
+ const rules = [
+ `正式会员有效期为${formalDays}天,从购买当天开始计算`,
+ '会员期间享受优先接单特权',
+ '支持随时续费,未过期续费天数叠加,已过期从今天重新计算'
+ ];
+ if (huiyuan.can_buy_trial) {
+ rules.push(`体验会员终身仅可购买1次(${huiyuan.trial_days || 0}天),体验期仅限制佣金提现`);
+ }
+ if (huiyuan.trial_enabled && !huiyuan.can_buy_trial) {
+ rules.push('您已使用过该会员的体验资格');
+ }
+ rules.push('体验会员仅支持微信支付;正式会员可使用佣金、分红或押金抵扣');
+ return rules;
+ },
+
+ // ========== 格式化到期时间 ==========
+ formatDaoqiTime(daoqi) {
+ if (!daoqi) return '';
+
+ try {
+ // 如果是标准日期格式,格式化为 YYYY-MM-DD
+ if (daoqi.includes(' ')) {
+ const dateStr = daoqi.split(' ')[0];
+ const dateParts = dateStr.split('-');
+ if (dateParts.length === 3) {
+ return `${dateParts[0]}-${dateParts[1]}-${dateParts[2]}`;
+ }
+ }
+ return daoqi;
+ } catch (error) {
+ console.error('格式化到期时间错误:', error);
+ return daoqi;
+ }
+ },
+
+ // ========== 计算剩余时间 ==========
+ formatRemainTime(daoqi) {
+ if (!daoqi) return '';
+
+ try {
+ const now = new Date();
+ const end = new Date(daoqi);
+ const diff = end - now;
+
+ if (diff <= 0) return '已过期';
+
+ const days = Math.floor(diff / (1000 * 60 * 60 * 24));
+ if (days > 0) {
+ return `${days}天`;
+ }
+
+ const hours = Math.floor(diff / (1000 * 60 * 60));
+ if (hours > 0) {
+ return `${hours}小时`;
+ }
+
+ return '即将到期';
+ } catch (error) {
+ console.error('计算剩余时间错误:', error);
+ return '时间错误';
+ }
+ },
+
+ // ========== 处理自动滚动 ==========
+ handleAutoScroll() {
+ const { jumpParams } = this.data;
+
+ if (!jumpParams || !jumpParams.needScroll) return;
+
+ setTimeout(() => {
+ let scrollTop = 0;
+
+ if (jumpParams.scrollTo === 'bottom') {
+ scrollTop = 2000;
+ } else if (jumpParams.scrollTo === 'member') {
+ return;
+ }
+
+ if (scrollTop > 0) {
+ wx.pageScrollTo({
+ scrollTop: scrollTop,
+ duration: 300
+ });
+ }
+ }, 800);
+ },
+
+ // ========== 押金充值相关 ==========
+ showYajinModal() {
+ this.setData({
+ showYajinModal: true,
+ yajinAmount: '100'
+ });
+ },
+
+ hideYajinModal() {
+ this.setData({
+ showYajinModal: false
+ });
+ },
+
+ onYajinInput(e) {
+ let value = e.detail.value;
+ value = value.replace(/[^\d]/g, '');
+
+ if (value) {
+ const num = parseInt(value);
+ if (num < 1) value = '1';
+ if (num > 10000) value = '10000';
+ }
+
+ this.setData({
+ yajinAmount: value
+ });
+ },
+
+ setYajinAmount(e) {
+ const amount = e.currentTarget.dataset.amount;
+ this.setData({
+ yajinAmount: amount
+ });
+ },
+
+ async handleYajinPay() {
+ const amount = this.data.yajinAmount;
+
+ if (!amount || amount < 1 || amount > 10000) {
+ wx.showToast({
+ title: '请输入1-10000元的金额',
+ icon: 'none'
+ });
+ return;
+ }
+
+ this.showLoading('发起支付中...');
+
+ try {
+ const res = await request({
+ url: '/shangpin/yajingoumai',
+ method: 'POST',
+ data: {
+ jine: parseInt(amount)
+ }
+ });
+
+ if (res.data.code === 200) {
+ const payParams = res.data.payParams;
+ const dingdanid = res.data.dingdanid;
+
+ this.setData({
+ currentDingdanid: dingdanid
+ });
+
+ await this.wechatPay(payParams, 'yajin');
+ } else {
+ wx.showToast({
+ title: res.data.message || '支付发起失败',
+ icon: 'none'
+ });
+ }
+ } catch (error) {
+ console.error('押金支付失败:', error);
+ wx.showToast({
+ title: '支付失败,请重试',
+ icon: 'none'
+ });
+ } finally {
+ this.hideLoading();
+ }
+ },
+
+ // ========== 积分充值相关 ==========
+ // 🔥 修改点1:增加积分已满的硬拦截,不弹任何提示,直接返回
+ onJifenClick(e) {
+ // 优先判断积分是否已满(10分为满分)
+ if (this.data.jifen >= 10) {
+ // 积分已满,不弹窗,不提示,静默返回
+ return;
+ }
+
+ // 检查是否有会员
+ const clubmber = this.data.clubmber || [];
+ if (clubmber.length === 0) {
+ wx.showToast({
+ title: '请先购买会员才能充值积分',
+ icon: 'none',
+ duration: 3000
+ });
+ return;
+ }
+
+ // 积分未满,正常打开支付方式选择
+ this.setData({
+ currentBuyType: 3,
+ showPayMethodModal: true
+ });
+ },
+
+ // ========== 会员购买相关 ==========
+ async handleHuiyuanBuy(e) {
+ const huiyuanId = e.currentTarget.dataset.id;
+ const isTrial = e.currentTarget.dataset.trial === true || e.currentTarget.dataset.trial === 'true';
+
+ if (!huiyuanId) {
+ wx.showToast({
+ title: '会员信息错误',
+ icon: 'none'
+ });
+ return;
+ }
+
+ this.showLoading('发起会员支付中...');
+
+ try {
+ const res = await request({
+ url: '/shangpin/huiyuangm',
+ method: 'POST',
+ data: {
+ huiyuanid: huiyuanId,
+ is_trial: isTrial
+ }
+ });
+
+ if (res.data.code === 200) {
+ const payParams = res.data.payParams;
+ const dingdanid = res.data.dingdanid;
+
+ this.setData({
+ currentDingdanid: dingdanid
+ });
+
+ await this.wechatPay(payParams, 'huiyuan', huiyuanId);
+ } else {
+ wx.showToast({
+ title: res.data.message || '会员支付发起失败',
+ icon: 'none'
+ });
+ }
+ } catch (error) {
+ console.error('会员支付失败:', error);
+ wx.showToast({
+ title: '支付失败,请重试',
+ icon: 'none'
+ });
+ } finally {
+ this.hideLoading();
+ }
+ },
+
+ // ========== 积分购买(微信支付)—— 已替换为正确的后端接口 ==========
+ async handleJifenBuy(e) {
+ const disabled = e.currentTarget.dataset.disabled;
+
+ // 检查积分是否已满
+ if (disabled === 'true') {
+ wx.showToast({ title: '积分已满,无需补充', icon: 'none' });
+ return;
+ }
+
+ // 检查是否有会员
+ const clubmber = this.data.clubmber || [];
+ if (clubmber.length === 0) {
+ wx.showToast({ title: '请先购买会员才能充值积分', icon: 'none', duration: 3000 });
+ return;
+ }
+
+ this.showLoading('发起积分支付中...');
+
+ try {
+ const res = await request({
+ url: '/shangpin/jifenbc', // 正确的积分下单接口
+ method: 'POST',
+ data: {}
+ });
+
+ if (res.data.code === 200) {
+ const payParams = res.data.payParams;
+ const dingdanid = res.data.dingdanid;
+
+ this.setData({ currentDingdanid: dingdanid });
+ await this.wechatPay(payParams, 'jifen');
+ } else {
+ wx.showToast({ title: res.data.message || '积分支付发起失败', icon: 'none' });
+ }
+ } catch (error) {
+ wx.showToast({ title: '支付失败,请重试', icon: 'none' });
+ } finally {
+ this.hideLoading();
+ }
+ },
+
+ // ========== 微信支付封装 ==========
+ async wechatPay(payParams, payType, extraData = null) {
+ return new Promise((resolve, reject) => {
+ if (!payParams || !payParams.timeStamp || !payParams.paySign) {
+ wx.showToast({
+ title: '支付参数错误',
+ icon: 'none'
+ });
+ reject(new Error('支付参数错误'));
+ return;
+ }
+
+ this.showLoading('调起支付中...');
+
+ wx.requestPayment({
+ timeStamp: payParams.timeStamp,
+ nonceStr: payParams.nonceStr,
+ package: payParams.package,
+ signType: payParams.signType || 'MD5',
+ paySign: payParams.paySign,
+ success: async () => {
+ this.hideLoading();
+
+ wx.showToast({
+ title: '支付成功!确认中...',
+ icon: 'none',
+ duration: 2000
+ });
+
+ try {
+ await this.startPolling(payType, extraData);
+ resolve();
+ } catch (error) {
+ reject(error);
+ }
+ },
+ fail: async (res) => {
+ this.hideLoading();
+
+ let errorMsg = '支付失败';
+ if (res.errMsg.includes('cancel')) {
+ errorMsg = '您取消了支付';
+ } else if (res.errMsg.includes('fail')) {
+ errorMsg = '支付失败,请重试';
+ }
+
+ wx.showToast({
+ title: errorMsg,
+ icon: 'none'
+ });
+
+ try {
+ await this.handlePayFailure(payType, extraData);
+ } catch (error) {
+ console.error('支付失败通知失败:', error);
+ }
+
+ reject(new Error(res.errMsg));
+ }
+ });
+ });
+ },
+
+ // ========== 开始轮询支付结果 ==========
+ async startPolling(payType, extraData = null) {
+ const maxRetries = 10;
+ const interval = 2000;
+ const dingdanid = this.data.currentDingdanid;
+
+ if (!dingdanid) {
+ wx.showToast({
+ title: '订单号缺失',
+ icon: 'none'
+ });
+ return;
+ }
+
+ this.showLoading('确认支付结果中...');
+
+ for (let i = 0; i < maxRetries; i++) {
+ try {
+ const pollResult = await this.checkPayStatus(payType, dingdanid, extraData);
+
+ if (pollResult.success) {
+ this.hideLoading();
+ await this.updateAfterPayment(payType, extraData, pollResult.data);
+
+ wx.showToast({
+ title: this.getSuccessMessage(payType),
+ icon: 'success',
+ duration: 2000
+ });
+
+ return;
+ }
+
+ await this.sleep(interval);
+ } catch (error) {
+ console.error(`轮询失败第${i + 1}次:`, error);
+ }
+ }
+
+ this.hideLoading();
+ wx.showToast({
+ title: '支付确认超时,请联系客服',
+ icon: 'none'
+ });
+ },
+
+ // ========== 检查支付状态 ==========
+ async checkPayStatus(payType, dingdanid, extraData = null) {
+ let url = '';
+ let data = { dingdanid: dingdanid };
+
+ switch (payType) {
+ case 'yajin':
+ url = '/shangpin/yajinlunxun';
+ break;
+ case 'jifen':
+ url = '/shangpin/jifenlunxun';
+ break;
+ case 'huiyuan':
+ url = '/shangpin/huiyuanlx';
+ data.huiyuanid = extraData;
+ break;
+ default:
+ throw new Error('未知的支付类型');
+ }
+
+ try {
+ const res = await request({
+ url,
+ method: 'POST',
+ data
+ });
+
+ return {
+ success: res.data.code === 200,
+ data: res.data
+ };
+ } catch (error) {
+ console.error('检查支付状态失败:', error);
+ return {
+ success: false,
+ data: null
+ };
+ }
+ },
+
+ // ========== 支付失败处理 ==========
+ async handlePayFailure(payType, extraData = null) {
+ let url = '';
+ let data = { dingdanid: this.data.currentDingdanid };
+
+ switch (payType) {
+ case 'yajin':
+ url = '/shangpin/yajinshibai';
+ break;
+ case 'jifen':
+ url = '/shangpin/jifenshibai';
+ break;
+ case 'huiyuan':
+ url = '/shangpin/huiyuanshibai';
+ data.huiyuanid = extraData;
+ break;
+ default:
+ return;
+ }
+
+ try {
+ await request({
+ url,
+ method: 'POST',
+ data
+ });
+ } catch (error) {
+ console.error('支付失败通知失败:', error);
+ }
+ },
+
+ // ========== 支付成功后更新数据 ==========
+ async updateAfterPayment(payType, extraData = null, responseData = null) {
+ const app = getApp();
+
+ switch (payType) {
+ case 'yajin':
+ if (responseData && responseData.yajin !== undefined) {
+ app.globalData.yajin = responseData.yajin;
+ }
+ await this.refreshYajinData();
+ break;
+
+ case 'jifen':
+ if (responseData && responseData.jifen !== undefined) {
+ app.globalData.jinfen = responseData.jifen;
+ }
+ await this.refreshJifenData();
+ break;
+
+ case 'huiyuan':
+ if (responseData && responseData.huiyuan) {
+ app.globalData.jinfen = responseData.jifen;
+ await this.updateClubmberData(responseData.huiyuan);
+ }
+ await this.refreshHuiyuanData(extraData);
+ break;
+ }
+
+ this.loadFromGlobalData();
+ },
+
+ // ========== 刷新押金数据 ==========
+ async refreshYajinData() {
+ this.loadFromGlobalData();
+ },
+
+ // ========== 刷新积分数据 ==========
+ async refreshJifenData() {
+ this.loadFromGlobalData();
+ },
+
+ // ========== 刷新会员数据 ==========
+ async refreshHuiyuanData(huiyuanId) {
+ this.loadFromGlobalData();
+ await this.fetchHuiyuanList();
+ },
+
+ // ========== 更新会员数据到全局变量 ==========
+ async updateClubmberData(huiyuanData) {
+ const app = getApp();
+ const globalClubmber = app.globalData.clumber || [];
+
+ const { huiyuanid, huiyuanming, daoqi } = huiyuanData;
+
+ const index = globalClubmber.findIndex(item => item.huiyuanid === huiyuanid);
+
+ if (index >= 0) {
+ globalClubmber[index].daoqi = daoqi;
+ } else {
+ globalClubmber.push({
+ huiyuanid: huiyuanid,
+ huiyuanming: huiyuanming,
+ daoqi: daoqi
+ });
+ }
+
+ app.globalData.clumber = globalClubmber;
+
+ this.setData({
+ clubmber: [...globalClubmber]
+ });
+ },
+
+ // ========== 检查并刷新数据 ==========
+ async checkAndRefreshData() {
+ this.loadFromGlobalData();
+ },
+
+ // ========== 工具方法 ==========
+ getSuccessMessage(payType) {
+ switch (payType) {
+ case 'yajin': return '押金充值成功';
+ case 'jifen': return '积分补充成功';
+ case 'huiyuan': return '会员购买成功';
+ default: return '支付成功';
+ }
+ },
+
+ showLoading(text = '加载中...') {
+ this.setData({
+ payLoading: true,
+ loadingText: text
+ });
+ },
+
+ hideLoading() {
+ this.setData({
+ payLoading: false
+ });
+ },
+
+ sleep(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+ },
+
+ // ========== 新增功能(余额抵扣等) ==========
+ onBuyHuiyuanClick(e) {
+ const huiyuanId = e.currentTarget.dataset.id;
+ const isTrial = e.currentTarget.dataset.trial === true || e.currentTarget.dataset.trial === 'true';
+
+ if (isTrial) {
+ this.handleHuiyuanBuy(e);
+ return;
+ }
+
+ this.setData({
+ currentBuyType: 1,
+ currentHuiyuanId: huiyuanId,
+ currentBuyIsTrial: false,
+ showPayMethodModal: true
+ });
+ },
+
+ onYajinClick() {
+ this.showYajinModal();
+ },
+
+ onYajinConfirm() {
+ const amount = this.data.yajinAmount;
+ if (!amount || amount < 1 || amount > 10000) {
+ wx.showToast({ title: '请输入1-10000元的金额', icon: 'none' });
+ return;
+ }
+ this.hideYajinModal();
+ this.setData({
+ currentBuyType: 2,
+ currentYajinAmount: amount,
+ showPayMethodModal: true
+ });
+ },
+
+ hidePayMethodModal() {
+ this.setData({ showPayMethodModal: false });
+ },
+
+ // 🔥 修改点2:选择支付方式时再次校验积分状态(防止异步过程中积分变化)
+ onPayMethodSelect(e) {
+ const method = e.currentTarget.dataset.method;
+ const { currentBuyType, currentHuiyuanId, currentYajinAmount, jifen } = this.data;
+ this.hidePayMethodModal();
+
+ // 如果是积分购买,且积分已满,直接拒绝
+ if (currentBuyType === 3 && jifen >= 10) {
+ wx.showToast({
+ title: '积分已满,无需补充',
+ icon: 'none'
+ });
+ return;
+ }
+
+ if (method === 'wx') {
+ if (currentBuyType === 1) {
+ this.handleHuiyuanBuy({
+ currentTarget: { dataset: { id: currentHuiyuanId, trial: 'false' } }
+ });
+ } else if (currentBuyType === 2) {
+ this.setData({ yajinAmount: currentYajinAmount }, () => {
+ this.handleYajinPay();
+ });
+ } else if (currentBuyType === 3) {
+ // ✅ 调用已修正的积分支付函数
+ this.handleJifenBuy({ currentTarget: { dataset: { disabled: 'false' } } });
+ }
+ } else if (method === 'balance') {
+ this.fetchBalanceOptions();
+ }
+ },
+
+ // 🔥 修改点3:请求余额抵扣选项前校验积分状态
+ async fetchBalanceOptions() {
+ const { currentBuyType, currentHuiyuanId, currentYajinAmount, currentBuyIsTrial, jifen } = this.data;
+
+ if (currentBuyType === 1 && currentBuyIsTrial) {
+ wx.showToast({ title: '体验会员仅支持微信支付', icon: 'none' });
+ return;
+ }
+
+ // 如果是积分购买,且积分已满,拒绝发起余额抵扣
+ if (currentBuyType === 3 && jifen >= 10) {
+ wx.showToast({
+ title: '积分已满,无需补充',
+ icon: 'none'
+ });
+ return;
+ }
+
+ this.showLoading('获取抵扣信息...');
+
+ try {
+ const res = await request({
+ url: '/shangpin/czhqdy',
+ method: 'POST',
+ data: {
+ leixing: currentBuyType,
+ huiyuan_id: currentBuyType === 1 ? currentHuiyuanId : undefined,
+ is_trial: currentBuyType === 1 ? false : undefined,
+ yajin_jine: currentBuyType === 2 ? parseInt(currentYajinAmount) : undefined,
+ }
+ });
+ if (res.data.code === 200) {
+ const options = res.data.data || [];
+ this.setData({
+ balanceOptions: options,
+ showBalanceModal: true
+ });
+ } else {
+ wx.showToast({ title: res.data.message || '获取失败', icon: 'none' });
+ }
+ } catch (error) {
+ console.error('获取抵扣身份失败:', error);
+ wx.showToast({ title: '网络错误', icon: 'none' });
+ } finally {
+ this.hideLoading();
+ }
+ },
+
+ hideBalanceModal() {
+ this.setData({ showBalanceModal: false });
+ },
+
+ onBalanceSelect(e) {
+ const identityId = e.currentTarget.dataset.id;
+ const selected = this.data.balanceOptions.find(opt => opt.id === identityId);
+ if (!selected) return;
+
+ this.setData({
+ selectedBalanceId: identityId,
+ selectedBalanceInfo: selected,
+ showConfirmModal: true,
+ showBalanceModal: false
+ });
+ },
+
+ hideConfirmModal() {
+ this.setData({ showConfirmModal: false });
+ },
+
+ async confirmBalancePay() {
+ const { selectedBalanceId, currentBuyType, currentHuiyuanId, currentYajinAmount, currentBuyIsTrial } = this.data;
+ if (!selectedBalanceId) return;
+
+ this.hideConfirmModal();
+ this.showLoading('抵扣支付中...');
+
+ try {
+ const res = await request({
+ url: '/shangpin/dsqrgmdh',
+ method: 'POST',
+ data: {
+ leixing: currentBuyType,
+ shenfen_id: selectedBalanceId,
+ huiyuan_id: currentBuyType === 1 ? currentHuiyuanId : undefined,
+ is_trial: currentBuyType === 1 ? currentBuyIsTrial : undefined,
+ yajin_jine: currentBuyType === 2 ? parseInt(currentYajinAmount) : undefined,
+ }
+ });
+
+ if (res.data.code === 200) {
+ const responseData = res.data.data || {};
+ await this.updateAfterPayment(
+ currentBuyType === 1 ? 'huiyuan' : (currentBuyType === 2 ? 'yajin' : 'jifen'),
+ currentBuyType === 1 ? currentHuiyuanId : null,
+ responseData
+ );
+ wx.showToast({ title: '购买成功', icon: 'success' });
+ } else {
+ wx.showToast({ title: res.data.message || '购买失败', icon: 'none' });
+ }
+ } catch (error) {
+ console.error('抵扣支付失败:', error);
+ wx.showToast({ title: '网络错误', icon: 'none' });
+ } finally {
+ this.hideLoading();
+ }
+ },
+
+ showMemberDetail(e) {
+ const item = e.currentTarget.dataset.item;
+ this.setData({
+ currentMemberDetail: item,
+ showMemberModal: true
+ });
+ },
+
+ hideMemberModal() {
+ this.setData({ showMemberModal: false });
+ },
});
\ No newline at end of file
diff --git a/pages/fighter-recharge/fighter-recharge.wxml b/pages/fighter-recharge/fighter-recharge.wxml
index a5187df..d3a4682 100644
--- a/pages/fighter-recharge/fighter-recharge.wxml
+++ b/pages/fighter-recharge/fighter-recharge.wxml
@@ -56,7 +56,7 @@
¥
{{item.jiage}}
- /30天
+ /{{item.formal_days || 30}}天
@@ -107,20 +107,24 @@
价格:
¥{{currentHuiyuan.jiage}}
+ /{{currentHuiyuan.formal_days || 30}}天
已激活,{{currentHuiyuan.buyInfo.daoqi}}后到期
- 激活会员特权
+ 正式会员支持微信或余额抵扣
+
+ 体验版仅微信支付 ¥{{currentHuiyuan.trial_price}}/{{currentHuiyuan.trial_days}}天,终身1次
+
+ bindtap="onBuyHuiyuanClick" data-id="{{currentHuiyuan.id}}" data-trial="false">
- {{currentHuiyuan.isBought ? '立即续费' : '立即激活'}}
+ {{currentHuiyuan.isBought ? '正式续费' : '购买正式版'}}
@@ -128,6 +132,16 @@
+
+
+ 购买体验版
+
diff --git a/pages/fighter-recharge/fighter-recharge.wxss b/pages/fighter-recharge/fighter-recharge.wxss
index 2f808eb..68e9ef8 100644
--- a/pages/fighter-recharge/fighter-recharge.wxss
+++ b/pages/fighter-recharge/fighter-recharge.wxss
@@ -855,11 +855,29 @@
/* 购买区域 */
.buy-section {
display: flex;
- align-items: center;
- justify-content: space-between;
+ flex-direction: column;
+ align-items: stretch;
+ gap: 20rpx;
padding-top: 25rpx;
border-top: 1px solid rgba(64, 156, 255, 0.2);
}
+
+ .buy-action {
+ display: flex;
+ flex-direction: column;
+ gap: 16rpx;
+ align-items: flex-end;
+ }
+
+ .price-days {
+ font-size: 22rpx;
+ color: #a0c8ff;
+ }
+
+ .trial-hint {
+ margin-top: 6rpx;
+ color: #ffb347;
+ }
.buy-info {
display: flex;
@@ -928,6 +946,11 @@
background: linear-gradient(135deg, #1e4b8f, #409cff);
}
+ .tech-buy-btn.trial-buy-btn .buy-btn-bg {
+ background: linear-gradient(135deg, rgba(255, 179, 71, 0.35), rgba(255, 120, 80, 0.45));
+ border: 1px solid rgba(255, 179, 71, 0.6);
+ }
+
.tech-buy-btn.renew .buy-btn-bg {
background: linear-gradient(135deg, #0f2c5c, #36cfc9);
}
diff --git a/pages/fighter/fighter.js b/pages/fighter/fighter.js
index 99a60b9..e6b1e34 100644
--- a/pages/fighter/fighter.js
+++ b/pages/fighter/fighter.js
@@ -2,6 +2,8 @@
import { createPage, request, isRoleStatusActive, isCenterPageActive, syncRoleStatuses, syncProfileFields, syncGroupFields, ensureRoleOnCenterPage, clearCacheAndEnterNormal, lockPrimaryRole, migrateLegacyCenterRole, enterLockedRole, parseSceneOptions } from '../../utils/base-page.js'
import { resolveAvatarUrl } from '../../utils/avatar.js'
import { openPrivateChat, buildInviterPeerId } from '../../utils/im-user.js'
+import { isStaffMode, getStaffContext } from '../../utils/staff-api.js'
+import { ensurePhoneAuth } from '../../utils/phone-auth.js'
const PLATFORM_INVITE_CODE = '0000008RffVgKHMj7kQC'
@@ -179,7 +181,10 @@ Page(createPage({
this.checkColdStartPopup('dashouduan');
},
- onShow() {
+ async onShow() {
+ const phoneOk = await ensurePhoneAuth({ redirect: true, role: 'dashou' });
+ if (!phoneOk) return;
+
migrateLegacyCenterRole(getApp());
lockPrimaryRole('dashou', getApp());
wx.setStorageSync('isJinpai', 0);
@@ -277,6 +282,13 @@ Page(createPage({
tag: d.shangjiaCertified ? '进入商家端' : '去认证',
done: d.shangjiaCertified,
});
+ list.push({
+ type: 'staff',
+ name: '商家客服',
+ icon: img.iconKefu || img.iconShangjia,
+ tag: isStaffMode() ? '进入工作台' : '去入驻',
+ done: isStaffMode(),
+ });
if (!d.isGuanshi) {
list.push({ type: 'guanshi', name: '管事', icon: img.iconGuanshiAuth, tag: '去认证' });
}
@@ -1015,6 +1027,14 @@ Page(createPage({
onTapAuthItem(e) {
const type = e.currentTarget.dataset.type;
+ if (type === 'staff') {
+ if (isStaffMode()) {
+ enterLockedRole('shangjia', getApp());
+ } else {
+ wx.navigateTo({ url: '/pages/staff-join/staff-join' });
+ }
+ return;
+ }
if (type === 'shangjia') {
this.onTapShangjiaAuth();
return;
diff --git a/pages/merchant-dispatch/merchant-dispatch.js b/pages/merchant-dispatch/merchant-dispatch.js
index e28da8e..d2f15d6 100644
--- a/pages/merchant-dispatch/merchant-dispatch.js
+++ b/pages/merchant-dispatch/merchant-dispatch.js
@@ -1,11 +1,15 @@
// pages/merchant-dispatch/merchant-dispatch.js
import { createPage, request } from '../../utils/base-page.js'
import PopupService from '../../services/popupService.js' // 引入弹窗服务
+import { STAFF_API, isStaffMode, refreshStaffContext, getStaffContext, restoreStaffContextAfterAuth, isMerchantPortalUser } from '../../utils/staff-api.js'
Page(createPage({
data: {
- // 商家余额 (从全局变量获取)
+ // 可用余额 / 客服额度
sjyue: '0.00',
+ isStaffMode: false,
+ balanceLabel: '可用余额',
+ balanceTip: '派单将从此余额扣除',
// 商品类型列表
shangpinList: [],
@@ -42,7 +46,15 @@ Page(createPage({
bgImageUrl: ''
},
- onLoad() {
+ async onLoad() {
+ if (wx.getStorageSync('token')) {
+ await restoreStaffContextAfterAuth()
+ }
+ if (!isMerchantPortalUser()) {
+ wx.showToast({ title: '请先成为商家或绑定客服', icon: 'none' })
+ setTimeout(() => wx.navigateBack(), 1500)
+ return
+ }
this.loadBgImage() // 拼接背景图
this.loadShangjiaYue() // 从全局变量获取商家余额
this.loadShangpinTypes() // 加载商品类型
@@ -50,15 +62,9 @@ Page(createPage({
},
onShow() {
- // 每次显示页面时刷新余额(可能从其他页面充值后返回)
this.loadShangjiaYue()
- // 重新拼接背景图(确保最新)
this.loadBgImage()
-
- // 注册通知组件(用于全局消息提示)
this.registerNotificationComponent()
-
- // 重置按钮缩进定时器(重新开始5秒倒计时)
this.startTutorialBtnTimer()
},
@@ -87,6 +93,22 @@ Page(createPage({
// ==================== 基础数据加载 ====================
loadShangjiaYue() {
+ const staff = isStaffMode()
+ this.setData({
+ isStaffMode: staff,
+ balanceLabel: staff ? '可用额度' : '可用余额',
+ balanceTip: staff ? '派单将从您的客服额度扣除(需老板先划额度)' : '派单将从此余额扣除',
+ })
+ if (staff) {
+ const ctx = getStaffContext() || {}
+ const wallet = ctx.wallet || {}
+ this.setData({ sjyue: wallet.quota_available || '0.00' })
+ refreshStaffContext(request).then((fresh) => {
+ const w = (fresh && fresh.wallet) || wallet
+ this.setData({ sjyue: w.quota_available || '0.00' })
+ }).catch(() => {})
+ return
+ }
const app = getApp()
const shangjiaData = app.globalData.shangjia || {}
this.setData({
@@ -97,13 +119,16 @@ Page(createPage({
// 加载商品类型(与极速派单完全一致)
async loadShangpinTypes() {
this.setData({ isLoading: true })
+ const staff = isStaffMode()
try {
const res = await request({
- url: '/dingdan/sjspleixing',
+ url: staff ? STAFF_API.productTypes : '/dingdan/sjspleixing',
method: 'POST'
})
- if (res && res.data.code === 200) {
- let list = res.data.data || []
+ const code = res && res.data && res.data.code
+ if (code === 200 || code === 0) {
+ let list = (res.data.data || [])
+ if (!Array.isArray(list) && list.list) list = list.list
// 强制倒序排列(与极速派单保持一致)
list.reverse()
this.setData({
@@ -115,14 +140,14 @@ Page(createPage({
this.setSelectedType(list[0])
}
} else {
- throw new Error(res.data.msg || '加载失败')
+ throw new Error((res.data && res.data.msg) || '加载失败')
}
} catch (error) {
console.error('加载商品类型失败:', error)
this.setData({ isLoading: false })
wx.showToast({
- title: '加载类型失败',
- icon: 'error',
+ title: error.message || '加载类型失败',
+ icon: 'none',
duration: 2000
})
}
@@ -282,21 +307,29 @@ Page(createPage({
}
}
- // 4. 验证余额是否足够
+ // 4. 验证余额/额度是否足够
const balance = parseFloat(sjyue)
if (price > balance) {
- wx.showModal({
- title: '余额不足',
- content: `当前余额${sjyue}元,需要${jiage}元。请先充值。`,
- confirmText: '去充值',
- success: (res) => {
- if (res.confirm) {
- wx.navigateTo({
- url: '/pages/merchant-recharge/merchant-recharge'
- })
+ if (this.data.isStaffMode) {
+ wx.showModal({
+ title: '额度不足',
+ content: `当前可用额度${sjyue}元,需要${jiage}元。请联系商家老板划额度。`,
+ showCancel: false,
+ })
+ } else {
+ wx.showModal({
+ title: '余额不足',
+ content: `当前余额${sjyue}元,需要${jiage}元。请先充值。`,
+ confirmText: '去充值',
+ success: (res) => {
+ if (res.confirm) {
+ wx.navigateTo({
+ url: '/pages/merchant-recharge/merchant-recharge'
+ })
+ }
}
- }
- })
+ })
+ }
return false
}
@@ -349,29 +382,31 @@ Page(createPage({
try {
const res = await request({
- url: '/dingdan/sjpaifa',
+ url: isStaffMode() ? STAFF_API.orderDispatch : '/dingdan/sjpaifa',
method: 'POST',
data: postData
})
if (res && res.data.code === 200) {
- // 派单成功
wx.showToast({
title: '派单成功',
icon: 'success',
duration: 2000
})
- // 更新全局变量中的商家信息(余额、发单量、流水)
const orderAmount = parseFloat(this.data.jiage)
- const app = getApp()
- if (app.globalData.shangjia) {
- const newBalance = parseFloat(app.globalData.shangjia.sjyue || '0') - orderAmount
- app.globalData.shangjia.sjyue = newBalance.toFixed(2)
- app.globalData.shangjia.fadanzong = (parseInt(app.globalData.shangjia.fadanzong || '0') + 1).toString()
- app.globalData.shangjia.riliushui = (parseFloat(app.globalData.shangjia.riliushui || '0') + orderAmount).toFixed(2)
- app.globalData.shangjia.yueliushui = (parseFloat(app.globalData.shangjia.yueliushui || '0') + orderAmount).toFixed(2)
- this.setData({ sjyue: app.globalData.shangjia.sjyue })
+ if (this.data.isStaffMode) {
+ this.loadShangjiaYue()
+ } else {
+ const app = getApp()
+ if (app.globalData.shangjia) {
+ const newBalance = parseFloat(app.globalData.shangjia.sjyue || '0') - orderAmount
+ app.globalData.shangjia.sjyue = newBalance.toFixed(2)
+ app.globalData.shangjia.fadanzong = (parseInt(app.globalData.shangjia.fadanzong || '0') + 1).toString()
+ app.globalData.shangjia.riliushui = (parseFloat(app.globalData.shangjia.riliushui || '0') + orderAmount).toFixed(2)
+ app.globalData.shangjia.yueliushui = (parseFloat(app.globalData.shangjia.yueliushui || '0') + orderAmount).toFixed(2)
+ this.setData({ sjyue: app.globalData.shangjia.sjyue })
+ }
}
// 延迟重置表单,避免用户连续提交
diff --git a/pages/merchant-dispatch/merchant-dispatch.wxml b/pages/merchant-dispatch/merchant-dispatch.wxml
index 947aba1..2e6e6c0 100644
--- a/pages/merchant-dispatch/merchant-dispatch.wxml
+++ b/pages/merchant-dispatch/merchant-dispatch.wxml
@@ -2,12 +2,12 @@
- 可用余额
+ {{balanceLabel}}
{{sjyue}}
元
- 派单将从此余额扣除
+ {{balanceTip}}
diff --git a/pages/merchant-home/merchant-home.js b/pages/merchant-home/merchant-home.js
index f82958d..662f178 100644
--- a/pages/merchant-home/merchant-home.js
+++ b/pages/merchant-home/merchant-home.js
@@ -1,5 +1,10 @@
import { createPage, request, ensureRoleOnCenterPage } from '../../utils/base-page.js';
import { getOrderStatusText } from '../../utils/api-helper.js';
+import {
+ STAFF_API, isStaffMode, isMerchantPortalUser, refreshStaffContext, syncStaffUi,
+ restoreStaffContextAfterAuth, applyStaffStatsToPage, getStaffContext,
+} from '../../utils/staff-api.js';
+import { ensurePhoneAuth } from '../../utils/phone-auth.js';
const app = getApp();
@@ -12,6 +17,11 @@ Page(createPage({
statusBar: 20,
navBar: 44,
isShangjia: false,
+ isStaffMode: false,
+ staffRoleName: '',
+ quotaAvailable: '0.00',
+ canDispatch: true,
+ canStaffManage: true,
lunboList: [],
lunbozhanwei: '/images/lunbozhanwei.jpg',
gonggao: '',
@@ -32,21 +42,42 @@ Page(createPage({
recentLoading: false,
},
- onLoad() {
+ async onLoad() {
const sys = wx.getSystemInfoSync();
this.setData({
statusBar: sys.statusBarHeight || 20,
navBar: (sys.statusBarHeight || 20) + 44,
lunbozhanwei: app.globalData.lunbozhanwei || '/images/lunbozhanwei.jpg',
});
+ if (wx.getStorageSync('token')) {
+ await restoreStaffContextAfterAuth();
+ }
this.checkRole();
+ syncStaffUi(this);
this.waitForConfigAndLoadBanner();
this.checkColdStartPopup('shangjiaduan');
},
- onShow() {
+ async onShow() {
+ const phoneOk = await ensurePhoneAuth({ redirect: true, role: 'shangjia' });
+ if (!phoneOk) return;
+
this.registerNotificationComponent();
+ // 先用本地缓存渲染,再向服务端同步子客服身份
this.checkRole();
+ syncStaffUi(this);
+ const token = wx.getStorageSync('token');
+ const isOwner = Number(wx.getStorageSync('shangjiastatus')) === 1;
+ if (token && !isOwner) {
+ await restoreStaffContextAfterAuth();
+ this.checkRole();
+ syncStaffUi(this);
+ } else if (isStaffMode()) {
+ await refreshStaffContext(request);
+ this.checkRole();
+ syncStaffUi(this);
+ applyStaffStatsToPage(this, getStaffContext());
+ }
if (this.data.isShangjia) {
ensureRoleOnCenterPage(this, 'shangjia');
this.loadDashboard();
@@ -78,8 +109,7 @@ Page(createPage({
},
checkRole() {
- const ok = Number(wx.getStorageSync('shangjiastatus')) === 1;
- this.setData({ isShangjia: ok });
+ this.setData({ isShangjia: isMerchantPortalUser() });
},
loadBannerData(forceFetch) {
@@ -132,6 +162,12 @@ Page(createPage({
},
async loadShangjiaBrief() {
+ if (isStaffMode()) {
+ const ctx = await refreshStaffContext(request);
+ syncStaffUi(this);
+ applyStaffStatsToPage(this, ctx);
+ return;
+ }
try {
const res = await request({
url: '/yonghu/shangjiaxinxi',
@@ -156,7 +192,7 @@ Page(createPage({
async loadPendingStats() {
try {
const res = await request({
- url: '/dingdan/sjdingdanhq',
+ url: isStaffMode() ? STAFF_API.orderList : '/dingdan/sjdingdanhq',
method: 'POST',
data: { zhuangtai_list: [8], page: 1, page_size: 20 },
});
@@ -187,7 +223,7 @@ Page(createPage({
this.setData({ recentLoading: true });
try {
const res = await request({
- url: '/dingdan/sjdingdanhq',
+ url: isStaffMode() ? STAFF_API.orderList : '/dingdan/sjdingdanhq',
method: 'POST',
data: {
zhuangtai_list: ALL_ORDER_ZHUANGTAI,
@@ -206,7 +242,7 @@ Page(createPage({
return {
dingdan_id: item.dingdan_id,
title: item.jieshao || '暂无描述',
- time: item.create_time || '',
+ time: item.create_time || item.CreateTime || '',
status: getOrderStatusText(item.zhuangtai),
money: item.jine || '0.00',
raw,
diff --git a/pages/merchant-home/merchant-home.wxml b/pages/merchant-home/merchant-home.wxml
index 354e576..86debc7 100644
--- a/pages/merchant-home/merchant-home.wxml
+++ b/pages/merchant-home/merchant-home.wxml
@@ -7,7 +7,7 @@
-
+
商家入驻
完成入驻后可发单、管理订单
@@ -39,7 +39,11 @@
{{gonggao}}
-
+
+ 商家客服 · {{staffRoleName}} · 可用额度 ¥{{quotaAvailable}}
+
+
+
@@ -73,7 +77,7 @@
{{stats.todayRefund}}
-
+
diff --git a/pages/merchant-home/merchant-home.wxss b/pages/merchant-home/merchant-home.wxss
index 1945741..f38340a 100644
--- a/pages/merchant-home/merchant-home.wxss
+++ b/pages/merchant-home/merchant-home.wxss
@@ -307,3 +307,16 @@ page {
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
+
+.staff-banner {
+ margin-top: 16rpx;
+ padding: 16rpx 24rpx;
+ background: #fff8e8;
+ border-radius: 12rpx;
+ border: 1rpx solid #f0d89a;
+}
+
+.staff-banner-txt {
+ font-size: 24rpx;
+ color: #8a6d2b;
+}
diff --git a/pages/merchant-kefu-key/merchant-kefu-key.js b/pages/merchant-kefu-key/merchant-kefu-key.js
index 07716c2..778c87e 100644
--- a/pages/merchant-kefu-key/merchant-kefu-key.js
+++ b/pages/merchant-kefu-key/merchant-kefu-key.js
@@ -1,60 +1,128 @@
-/** 客服邀请码/秘钥 - UI 预览,后端接入前使用 mock 数据 */
+/** 客服邀请码管理 — 对接 merchant-staff API */
+import request from '../../utils/request.js';
+import { STAFF_API } from '../../utils/staff-api.js';
+
Page({
data: {
statusBar: 20,
gudingCode: 'XQKF2026',
- kefuTips: '客服秘钥用于绑定子客服账号,后端接口就绪后将自动替换为真实数据。',
- sublist: [{ name: '全部' }, { name: '未使用' }, { name: '已使用' }],
+ kefuTips: '一次性邀请码绑定子客服,固定码仅展示不可绑定。',
+ sublist: [{ name: '全部', status: null }, { name: '未使用', status: 0 }, { name: '已使用', status: 1 }],
subCurrent: 0,
- list: [
- { id: 1, typeName: '财务', code: 'CW-8X2K9P', status: 0, adddate: '2026-06-10' },
- { id: 2, typeName: '总管', code: 'ZG-3M7N1Q', status: 1, adddate: '2026-06-08', usedate: '2026-06-12', user: { nickname: '客服小张', avatar: '/images/default-avatar.png' } },
- ],
+ list: [],
+ roles: [],
showQrcodeTips: false,
+ loading: false,
},
onLoad() {
const sys = wx.getSystemInfoSync();
this.setData({ statusBar: sys.statusBarHeight || 20 });
+ this.loadRoles();
+ this.loadList();
},
- goBack() {
- wx.navigateBack();
+ onShow() {
+ this.loadList();
},
+ async loadRoles() {
+ try {
+ const res = await request({ url: STAFF_API.roleList, method: 'POST' });
+ if (res.data && res.data.code === 0) {
+ this.setData({ roles: res.data.data.roles || [] });
+ }
+ } catch (e) {}
+ },
+
+ async loadList() {
+ const tab = this.data.sublist[this.data.subCurrent];
+ try {
+ const res = await request({
+ url: STAFF_API.inviteList,
+ method: 'POST',
+ data: tab.status === null ? {} : { status: tab.status },
+ });
+ if (res.data && res.data.code === 0) {
+ const d = res.data.data || {};
+ this.setData({
+ list: d.list || [],
+ gudingCode: d.gudingCode || 'XQKF2026',
+ });
+ }
+ } catch (e) {
+ wx.showToast({ title: '加载失败', icon: 'none' });
+ }
+ },
+
+ goBack() { wx.navigateBack(); },
+
copyFixed() {
wx.setClipboardData({ data: this.data.gudingCode });
},
switchSub(e) {
- this.setData({ subCurrent: Number(e.currentTarget.dataset.idx) });
+ this.setData({ subCurrent: Number(e.currentTarget.dataset.idx) }, () => this.loadList());
},
- addKey() {
- wx.showToast({ title: '后端接入后可添加', icon: 'none' });
+ async addKey() {
+ const roles = this.data.roles;
+ if (!roles.length) {
+ wx.showToast({ title: '暂无角色,请稍后重试', icon: 'none' });
+ return;
+ }
+ const names = roles.map(r => r.role_name);
+ wx.showActionSheet({
+ itemList: names,
+ success: async (r) => {
+ const role = roles[r.tapIndex];
+ wx.showLoading({ title: '生成中' });
+ try {
+ const res = await request({
+ url: STAFF_API.inviteCreate,
+ method: 'POST',
+ data: { role_id: role.id },
+ });
+ if (res.data && res.data.code === 0) {
+ wx.showToast({ title: '已生成', icon: 'success' });
+ this.loadList();
+ } else {
+ wx.showToast({ title: res.data.msg || '失败', icon: 'none' });
+ }
+ } finally {
+ wx.hideLoading();
+ }
+ },
+ });
},
copyCode(e) {
wx.setClipboardData({ data: e.currentTarget.dataset.code || '' });
},
- delKey() {
- wx.showToast({ title: '预览数据不可删除', icon: 'none' });
- },
-
- showTips() {
- this.setData({ showQrcodeTips: true });
- },
-
- closeTips() {
- this.setData({ showQrcodeTips: false });
- },
-
- previewQr() {
- wx.previewImage({
- urls: ['https://xy-1314784552.cos.ap-guangzhou.myqcloud.com/uploads/images/ewm.png'],
+ async delKey(e) {
+ const id = e.currentTarget.dataset.id;
+ if (!id) return;
+ const res = await request({
+ url: STAFF_API.inviteRevoke,
+ method: 'POST',
+ data: { id },
});
+ if (res.data && res.data.code === 0) {
+ wx.showToast({ title: '已作废' });
+ this.loadList();
+ } else {
+ wx.showToast({ title: res.data.msg || '失败', icon: 'none' });
+ }
},
+ showTips() { this.setData({ showQrcodeTips: true }); },
+ closeTips() { this.setData({ showQrcodeTips: false }); },
+ goAudit() {
+ wx.navigateTo({ url: '/pages/merchant-staff-audit/merchant-staff-audit' });
+ },
+ goToRoleManage() {
+ wx.navigateTo({ url: '/pages/merchant-staff-role/merchant-staff-role' });
+ },
noop() {},
});
diff --git a/pages/merchant-kefu-key/merchant-kefu-key.wxml b/pages/merchant-kefu-key/merchant-kefu-key.wxml
index 76ea3a5..1696a56 100644
--- a/pages/merchant-kefu-key/merchant-kefu-key.wxml
+++ b/pages/merchant-kefu-key/merchant-kefu-key.wxml
@@ -26,6 +26,16 @@
添加
+
+ 角色与权限管理(资金池/划额度在此配置角色)
+ ›
+
+
+
+ 操作日志与客服排行
+ ›
+
+
秘钥使用方法
›
@@ -50,7 +60,6 @@
暂无卡密
- 预览 · 接口就绪后对接真实数据
diff --git a/pages/merchant-kefu-list/merchant-kefu-list.js b/pages/merchant-kefu-list/merchant-kefu-list.js
index 20e28fe..5ca8c31 100644
--- a/pages/merchant-kefu-list/merchant-kefu-list.js
+++ b/pages/merchant-kefu-list/merchant-kefu-list.js
@@ -1,47 +1,398 @@
-/** 客服列表 - UI 预览,mock 数据 */
+/** 客服列表 — 派单量 / 剩余额度 / 封禁解封 */
+
+import request from '../../utils/request.js';
+
+import { STAFF_API, isStaffMode } from '../../utils/staff-api.js';
+
+
+
+const app = getApp();
+
+
+
+function avatarUrl(path) {
+
+ if (!path) return '/images/default-avatar.png';
+
+ if (path.startsWith('http')) return path;
+
+ const oss = app.globalData.ossImageUrl || '';
+
+ const p = path.startsWith('/') ? path.slice(1) : path;
+
+ return oss ? oss + p : '/images/default-avatar.png';
+
+}
+
+
+
Page({
+
data: {
+
statusBar: 20,
- list: [
- {
- id: 1,
- nickname: '客服总管-阿星',
- avatar: '/images/default-avatar.png',
- uid: '1008601',
- isChief: true,
- isFinance: false,
- orderCount: 128,
- settleCount: 96,
- settleAmount: '12800.00',
- },
- {
- id: 2,
- nickname: '财务-小李',
- avatar: '/images/default-avatar.png',
- uid: '1008602',
- isChief: false,
- isFinance: true,
- orderCount: 56,
- settleCount: 52,
- settleAmount: '6800.00',
- },
- ],
+
+ list: [],
+
+ isOwner: false,
+
},
+
+
onLoad() {
+
const sys = wx.getSystemInfoSync();
- this.setData({ statusBar: sys.statusBarHeight || 20 });
+
+ this.setData({ statusBar: sys.statusBarHeight || 20, isOwner: !isStaffMode() });
+
+ this.loadList();
+
},
- goBack() {
- wx.navigateBack();
+
+
+ onShow() {
+
+ this.loadList();
+
},
+
+
+ formatList(raw) {
+
+ return (raw || []).map((item) => ({
+
+ ...item,
+
+ avatar: avatarUrl(item.avatar),
+
+ todayDispatchCount: item.todayDispatchCount != null ? item.todayDispatchCount : 0,
+
+ todayDispatchAmount: item.todayDispatchAmount || '0.00',
+
+ dispatchCount: item.dispatchCount != null ? item.dispatchCount : (item.orderCount || 0),
+
+ dispatchAmount: item.dispatchAmount || '0.00',
+
+ settleCount: item.settleCount != null ? item.settleCount : 0,
+
+ settleAmount: item.settleAmount || '0.00',
+
+ refundCount: item.refundCount != null ? item.refundCount : 0,
+
+ refundAmount: item.refundAmount || '0.00',
+
+ quotaAvailable: item.quota_available || '0.00',
+
+ quotaRevokable: item.quota_available || '0.00',
+
+ isSelf: !!item.isSelf,
+
+ }));
+
+ },
+
+
+
+ rejectSelf(id) {
+
+ const row = (this.data.list || []).find((x) => String(x.id) === String(id));
+
+ if (row && row.isSelf) {
+
+ wx.showToast({ title: '不能对本人操作', icon: 'none' });
+
+ return true;
+
+ }
+
+ return false;
+
+ },
+
+
+
+ async loadList() {
+
+ try {
+
+ const res = await request({ url: STAFF_API.memberList, method: 'POST' });
+
+ if (res.data && res.data.code === 0) {
+
+ const raw = (res.data.data && res.data.data.list) || [];
+
+ this.setData({ list: this.formatList(raw) });
+
+ }
+
+ } catch (e) {
+
+ wx.showToast({ title: '加载失败', icon: 'none' });
+
+ }
+
+ },
+
+
+
+ goBack() { wx.navigateBack(); },
+
+
+
copyUid(e) {
+
wx.setClipboardData({ data: e.currentTarget.dataset.uid || '' });
+
},
- onAction(e) {
- wx.showToast({ title: '后端接入后生效', icon: 'none' });
+
+
+ async onAllocate(e) {
+
+ const id = e.currentTarget.dataset.id;
+
+ if (this.rejectSelf(id)) return;
+
+ wx.showModal({
+
+ title: '划额度',
+
+ editable: true,
+
+ placeholderText: '输入金额',
+
+ success: async (r) => {
+
+ if (!r.confirm || !r.content) return;
+
+ const res = await request({
+
+ url: STAFF_API.walletAllocate,
+
+ method: 'POST',
+
+ data: { member_id: id, amount: r.content },
+
+ });
+
+ wx.showToast({
+
+ title: (res.data && res.data.code === 0) ? '成功' : (res.data.msg || '失败'),
+
+ icon: 'none',
+
+ });
+
+ this.loadList();
+
+ },
+
+ });
+
},
+
+
+
+ async onRevoke(e) {
+
+ const id = e.currentTarget.dataset.id;
+
+ if (this.rejectSelf(id)) return;
+
+ const available = e.currentTarget.dataset.available || '0';
+
+ wx.showModal({
+
+ title: '收回额度',
+
+ editable: true,
+
+ placeholderText: `最多可收回 ${available} 元`,
+
+ success: async (r) => {
+
+ if (!r.confirm || !r.content) return;
+
+ const res = await request({
+
+ url: STAFF_API.walletRevoke,
+
+ method: 'POST',
+
+ data: { member_id: id, amount: r.content },
+
+ });
+
+ wx.showToast({
+
+ title: (res.data && res.data.code === 0) ? '已收回' : (res.data.msg || '失败'),
+
+ icon: 'none',
+
+ });
+
+ if (res.data && res.data.code === 0) this.loadList();
+
+ },
+
+ });
+
+ },
+
+
+
+ async onToggleDisable(e) {
+
+ const id = e.currentTarget.dataset.id;
+
+ if (this.rejectSelf(id)) return;
+
+ const disabled = e.currentTarget.dataset.disabled;
+
+ const action = disabled ? '解封' : '封禁';
+
+ wx.showModal({
+
+ title: `${action}客服`,
+
+ content: disabled ? '解封后该客服可继续登录操作' : '封禁后该客服无法使用商家端功能',
+
+ success: async (r) => {
+
+ if (!r.confirm) return;
+
+ const res = await request({
+
+ url: STAFF_API.memberDisable,
+
+ method: 'POST',
+
+ data: { member_id: id, disable: !disabled },
+
+ });
+
+ wx.showToast({ title: (res.data && res.data.msg) || action, icon: 'none' });
+
+ this.loadList();
+
+ },
+
+ });
+
+ },
+
+
+
+ async onRemove(e) {
+
+ const id = e.currentTarget.dataset.id;
+
+ wx.showModal({
+
+ title: '移除客服',
+
+ content: '移除后该用户可认证商家或绑定其他商家',
+
+ success: async (r) => {
+
+ if (!r.confirm) return;
+
+ const res = await request({
+
+ url: STAFF_API.memberRemove,
+
+ method: 'POST',
+
+ data: { member_id: id },
+
+ });
+
+ wx.showToast({ title: res.data.msg || '完成', icon: 'none' });
+
+ this.loadList();
+
+ },
+
+ });
+
+ },
+
+
+
+ async onAssignRole(e) {
+
+ const memberId = e.currentTarget.dataset.id;
+
+ if (this.rejectSelf(memberId)) return;
+
+ const rolesRes = await request({ url: STAFF_API.roleList, method: 'POST' });
+
+ const roles = (rolesRes.data && rolesRes.data.data && rolesRes.data.data.roles) || [];
+
+ if (!roles.length) {
+
+ wx.showToast({ title: '请先创建角色', icon: 'none' });
+
+ return;
+
+ }
+
+ wx.showActionSheet({
+
+ itemList: roles.map((r) => r.role_name),
+
+ success: async (r) => {
+
+ const role = roles[r.tapIndex];
+
+ const res = await request({
+
+ url: STAFF_API.memberUpdateRole,
+
+ method: 'POST',
+
+ data: { member_id: memberId, role_id: role.id },
+
+ });
+
+ const ok = res.data && res.data.code === 0;
+
+ const roleName = (res.data && res.data.data && res.data.data.role_name) || role.role_name;
+
+ wx.showToast({
+
+ title: ok ? `已设为${roleName}` : (res.data.msg || '失败'),
+
+ icon: 'none',
+
+ });
+
+ if (ok) this.loadList();
+
+ },
+
+ });
+
+ },
+
+
+
+ goRank() {
+
+ wx.navigateTo({ url: '/pages/merchant-staff-audit/merchant-staff-audit?tab=rank' });
+
+ },
+
+
+
+ goAudit() {
+
+ wx.navigateTo({ url: '/pages/merchant-staff-audit/merchant-staff-audit' });
+
+ },
+
});
+
diff --git a/pages/merchant-kefu-list/merchant-kefu-list.wxml b/pages/merchant-kefu-list/merchant-kefu-list.wxml
index db7b934..78166a7 100644
--- a/pages/merchant-kefu-list/merchant-kefu-list.wxml
+++ b/pages/merchant-kefu-list/merchant-kefu-list.wxml
@@ -1,47 +1,162 @@
+
+
+
‹
+
客服列表
+
+
+
+
-
+
+
+
+
+
+
+
{{item.nickname}}
+
+ {{item.role_name || '客服'}}
+
总管
+
+ 本人
+
财务
+
+ 已封禁
+
+
+
ID: {{item.uid}}
+
复制
+
+
+
+
+
+
+ 剩余额度
+
+ ¥{{item.quotaAvailable}}
+
+
+
+
- {{item.orderCount}}
- 接单数
+
+ {{item.todayDispatchCount}}
+
+ 今日派单
+
+
+
+ {{item.dispatchCount}}
+
+ 累计派单
+
+
+
+
+
{{item.settleCount}}
- 结算数
+
+ 结单数
+
+
- ¥{{item.settleAmount}}
- 结算金额
+
+ {{item.refundCount}}
+
+ 退款数
+
+
-
- 查看订单
- 权限管理
- 删除
+
+
+
+
+
+ ¥{{item.todayDispatchAmount}}
+
+ 今日派单额
+
+
+
+
+
+ ¥{{item.dispatchAmount}}
+
+ 派单金额
+
+
+
+
+
+ ¥{{item.settleAmount}}
+
+ 结算金额
+
+
+
+
+
+ ¥{{item.refundAmount}}
+
+ 退款金额
+
+
+
+
+
+
+ 划额度
+
+ 收回额度
+
+ 换角色
+
+ {{item.isDisabled ? '解封' : '封禁'}}
+
+ 移除
+
+
+
+ 本人账号不可在此操作,如需调整请联系商家老板
+
- 预览 · 接口就绪后对接真实数据
+
+ 暂无客服成员
+
+ 操作日志 ›
+
+ 客服排行 ›
+
+
+
diff --git a/pages/merchant-kefu-list/merchant-kefu-list.wxss b/pages/merchant-kefu-list/merchant-kefu-list.wxss
index effefea..12a8fce 100644
--- a/pages/merchant-kefu-list/merchant-kefu-list.wxss
+++ b/pages/merchant-kefu-list/merchant-kefu-list.wxss
@@ -31,6 +31,34 @@
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
}
+.kefu-card.disabled-card { opacity: 0.72; }
+
+.badge-role {
+ font-size: 20rpx;
+ padding: 2rpx 10rpx;
+ background: #f0f0f0;
+ color: #666;
+ border-radius: 6rpx;
+ margin-left: 8rpx;
+}
+
+.badge-off {
+ background: #ffebee;
+ color: #c62828;
+}
+
+.quota-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 12rpx 0 8rpx;
+ border-bottom: 1rpx solid #f0f0f0;
+ margin-bottom: 12rpx;
+}
+
+.quota-label { font-size: 24rpx; color: #888; }
+.quota-val { font-size: 30rpx; color: #C9A962; font-weight: 600; }
+
.kefu-av {
width: 88rpx;
height: 88rpx;
@@ -55,6 +83,15 @@
.badge-chief { background: #ffb508; }
.badge-finance { background: #1b6ef3; }
+.badge-self { background: #9e9e9e; }
+
+.self-tip {
+ margin-top: 16rpx;
+ padding: 16rpx 0 4rpx;
+ font-size: 24rpx;
+ color: #999;
+ text-align: center;
+}
.kefu-uid {
font-size: 24rpx;
@@ -75,12 +112,23 @@
border-bottom: 1rpx solid #f5f5f5;
}
+.amount-stats {
+ margin-top: 0;
+ border-top: none;
+ padding-top: 0;
+}
+
.stat-n {
font-size: 28rpx;
font-weight: 700;
color: #e67e22;
}
+.stat-amt {
+ font-size: 24rpx;
+ color: #c0392b;
+}
+
.stat-l {
font-size: 22rpx;
color: #999;
diff --git a/pages/merchant-order-detail/merchant-order-detail.js b/pages/merchant-order-detail/merchant-order-detail.js
index 25b5660..f8e8b08 100644
--- a/pages/merchant-order-detail/merchant-order-detail.js
+++ b/pages/merchant-order-detail/merchant-order-detail.js
@@ -1,835 +1,872 @@
-// pages/merchant-order-detail/merchant-order-detail.js - 商家订单详情(完整版,已添加罚款/退款操作及教程按钮)
-const app = getApp()
-import request from '../../utils/request.js'
-import PopupService from '../../services/popupService.js' // 新增:导入弹窗服务
-
-Page({
- data: {
- jibenShuju: {
- dingdan_id: '', tupian: '', jieshao: '', create_time: '',
- jine: '', nicheng: '', beizhu: '', zhuangtai: 0, fadanpingtai: 0
- },
- dashouInfo: { yonghuid: '', nicheng: '', avatar: '' },
- xiangxiShuju: {
- dashou_jiaofu: [], dashou_liuyan: '', tuikuan_liyou: '', shangjia_liuyan: ''
- },
- fadanData: null,
- fenhongLilv: 0,
- xinDingdanId: '',
- zhuangtaiYanseMap: {
- 1: '#E8F5E9', 2: '#E3F2FD', 3: '#F3E5F5', 4: '#FFF3E0',
- 5: '#FFEBEE', 6: '#EFEBE9', 7: '#E0F7FA', 8: '#FFF8E1'
- },
- zhuangtaiWenziMap: {
- 1: '已下单', 2: '进行中', 3: '已完成', 4: '退款中',
- 5: '已退款', 6: '退款失败', 7: '指定中', 8: '结算中'
- },
- isLoading: true,
- showChexiaoModal: false, chexiaoLiyou: '', isChexiaoLoading: false,
- showTuikuanModal: false, tuikuanLiyou: '', isTuikuanLoading: false,
- showFakuanModal: false, fakuanLiyou: '', fakuanJine: '', yingxiangQiangdan: true, isFakuanLoading: false,
- showGhDashouModal: false, isGhDashouLoading: false,
- showJiesuanConfirm: false, isJiesuanLoading: false,
- jiesuanPingfen: 0, jiesuanLiuyan: '',
- xingxingData: [
- { id: 1, active: false }, { id: 2, active: false }, { id: 3, active: false },
- { id: 4, active: false }, { id: 5, active: false }
- ],
- currentIMUserId: '',
-
- // ========== 新增:罚单/订单操作弹窗控制 ==========
- showModifyFakuanModal: false, // 修改罚款弹窗
- modifyFakuanLiyou: '', // 修改后的罚款理由
- modifyFakuanJine: '', // 修改后的罚款金额
- showCancelFakuanModal: false, // 撤销罚款弹窗
- cancelFakuanLiyou: '', // 撤销驳回理由
- showResubmitFakuanModal: false, // 再次申请罚款弹窗
- resubmitFakuanLiyou: '', // 再次申请的理由
- resubmitFakuanJine: '', // 再次申请的金额
- showCancelTuikuanModal: false, // 撤销退款申请弹窗
- cancelTuikuanLiyou: '', // 撤销退款理由
- showModifyTuikuanModal: false, // 修改退款理由弹窗
- modifyTuikuanLiyou: '', // 修改后的退款理由
- // 教程按钮状态
- tutorialBtnHidden: false
- },
-
- onLoad(options) {
- wx.setNavigationBarTitle({ title: '订单详情' })
- this.ensureIMConnection()
- this.jiexiTiaozhuanCanshu(options)
- // 新增:启动教程按钮定时器
- this.startTutorialBtnTimer()
- },
-
- onShow() {
- this.registerNotificationComponent()
- this.ensureIMConnection()
- },
-
- onHide() {
- // 页面隐藏时重置弹窗服务状态,避免影响其他页面
- PopupService.reset()
- },
-
- onUnload() {
- // 清除缩进定时器,避免内存泄漏
- if (this.tutorialTimer) {
- clearTimeout(this.tutorialTimer)
- }
- // 页面卸载时重置弹窗服务
- PopupService.reset()
- },
-
- registerNotificationComponent() {
- const notificationComp = this.selectComponent('#global-notification')
- if (notificationComp && notificationComp.showNotification) {
- app.globalData.globalNotification = {
- show: (data) => notificationComp.showNotification(data),
- hide: () => notificationComp.hideNotification()
- }
- }
- },
-
- ensureIMConnection() {
- const uid = wx.getStorageSync('uid')
- if (!uid) return
- const targetUserId = 'Sj' + uid
- if (wx.goEasy && wx.goEasy.getConnectionStatus) {
- const status = wx.goEasy.getConnectionStatus()
- if (status === 'connected' || status === 'reconnected') {
- const currentUserId = wx.goEasy.im ? wx.goEasy.im.userId : null
- if (currentUserId === targetUserId) {
- this.setData({ currentIMUserId: targetUserId })
- return
- } else {
- wx.goEasy.disconnect()
- }
- }
- }
- this.connectGoEasy(targetUserId)
- },
-
- connectGoEasy(userId) {
- let currentUser = app.globalData.currentUser
- if (!currentUser) {
- const uid = wx.getStorageSync('uid')
- currentUser = {
- id: uid,
- name: '用户' + (uid ? uid.substring(0, 6) : ''),
- avatar: app.globalData.ossImageUrl + app.globalData.morentouxiang
- }
- }
- wx.goEasy.connect({
- id: userId,
- data: {
- name: currentUser.name,
- avatar: currentUser.avatar || (app.globalData.ossImageUrl + app.globalData.morentouxiang)
- },
- onSuccess: () => {
- this.setData({ currentIMUserId: userId })
- try {
- wx.setStorageSync('savedGoEasyConnection', JSON.stringify({
- userId, identityType: 'shangjia', lastConnectTime: Date.now()
- }))
- wx.setStorageSync('goEasyUserId', userId)
- } catch (e) {}
- },
- onFailed: (error) => {
- if (error.code === 408) {
- this.setData({ currentIMUserId: userId })
- } else {
- console.error('IM连接失败', error)
- }
- }
- })
- },
-
- jiexiTiaozhuanCanshu(options) {
- try {
- const dingdanDataStr = options.dingdanData || ''
- if (!dingdanDataStr) {
- wx.showToast({ title: '订单数据错误', icon: 'none' })
- setTimeout(() => wx.navigateBack(), 1500)
- return
- }
- const dingdanData = JSON.parse(decodeURIComponent(dingdanDataStr))
- let tupianUrl = dingdanData.tupian || ''
- if (!tupianUrl) tupianUrl = this.getTupianUrl()
- let jine = parseFloat(dingdanData.jine || 0)
- if (isNaN(jine)) jine = 0
- this.setData({
- 'jibenShuju.dingdan_id': dingdanData.dingdan_id || '',
- 'jibenShuju.tupian': tupianUrl,
- 'jibenShuju.jieshao': dingdanData.jieshao || '',
- 'jibenShuju.create_time': dingdanData.create_time || '',
- 'jibenShuju.jine': jine.toFixed(2),
- 'jibenShuju.nicheng': dingdanData.nicheng || '',
- 'jibenShuju.beizhu': dingdanData.beizhu || '',
- 'jibenShuju.zhuangtai': dingdanData.zhuangtai || 0,
- 'jibenShuju.fadanpingtai': dingdanData.fadanpingtai || 0,
- isLoading: false
- })
- this.jiazaiXiangxiShuju(dingdanData.dingdan_id)
- } catch (error) {
- console.error('解析订单数据失败', error)
- wx.showToast({ title: '数据解析失败', icon: 'none' })
- setTimeout(() => wx.navigateBack(), 1500)
- }
- },
-
- getTupianUrl() {
- const cacheTouxiang = wx.getStorageSync('touxiang') || ''
- let tupianPath = cacheTouxiang || app.globalData.morentouxiang || ''
- const ossImageUrl = app.globalData.ossImageUrl || ''
- if (tupianPath && ossImageUrl) {
- if (tupianPath.startsWith('/')) tupianPath = tupianPath.substring(1)
- return ossImageUrl + tupianPath
- }
- return ''
- },
-
- async jiazaiXiangxiShuju(dingdanId) {
- if (!dingdanId) return
- try {
- const res = await request({
- url: '/dingdan/sjddxq',
- method: 'POST',
- data: { dingdan_id: dingdanId },
- header: { 'content-type': 'application/json' }
- })
- if (res && res.data.code === 0 && res.data.data) {
- const d = res.data.data
- let jiaofuList = d.dashou_jiaofu || []
- jiaofuList = jiaofuList.map(url => {
- if (url && !url.startsWith('http')) {
- const oss = app.globalData.ossImageUrl || ''
- return oss + url
- }
- return url
- })
- let dashouAvatar = d.dashou_avatar || ''
- if (dashouAvatar && !dashouAvatar.startsWith('http')) {
- dashouAvatar = (app.globalData.ossImageUrl || '') + dashouAvatar
- }
- this.setData({
- 'dashouInfo.yonghuid': d.dashou_yonghuid || '',
- 'dashouInfo.nicheng': d.dashou_nicheng || '',
- 'dashouInfo.avatar': dashouAvatar,
- 'xiangxiShuju.dashou_jiaofu': jiaofuList,
- 'xiangxiShuju.dashou_liuyan': d.dashou_liuyan || '',
- 'xiangxiShuju.tuikuan_liyou': d.tuikuan_liyou || '',
- 'xiangxiShuju.shangjia_liuyan': d.shangjia_liuyan || '',
- fadanData: d.fadan || null,
- fenhongLilv: parseFloat(d.fenhong_lilv) || 0,
- xinDingdanId: d.xin_dingdan_id || '',
- 'jibenShuju.zhuangtai': d.zhuangtai || this.data.jibenShuju.zhuangtai
- })
- } else {
- wx.showToast({ title: res?.data?.msg || '获取详情失败', icon: 'none' })
- }
- } catch (err) {
- console.error('加载详情失败', err)
- wx.showToast({ title: '网络错误', icon: 'none' })
- }
- },
-
- fuzhiWenben(e) {
- const text = e.currentTarget.dataset.text
- if (!text) return
- wx.setClipboardData({ data: text })
- wx.showToast({ title: '已复制', icon: 'success' })
- },
-
- yulanTupian(e) {
- const current = e.currentTarget.dataset.url
- const urls = e.currentTarget.dataset.urls || [current]
- wx.previewImage({ current, urls })
- },
-
- goToKefu() {
- const kefuLink = app.globalData.kefuConfig?.link || ''
- const corpId = app.globalData.kefuConfig?.enterpriseId || ''
- if (!kefuLink || !corpId) {
- wx.showToast({ title: '客服配置未加载', icon: 'none' })
- return
- }
- wx.openCustomerServiceChat({
- extInfo: { url: kefuLink },
- corpId: corpId,
- success: (res) => console.log('跳转客服成功', res),
- fail: (err) => {
- console.error('跳转客服失败', err)
- wx.showToast({ title: '暂时无法连接客服', icon: 'none' })
- }
- })
- },
-
- // ===== 联系打手(替打手订阅 + 发机器人消息 + 跳转) =====
- goToChatWithDashou() {
- const uid = wx.getStorageSync('uid')
- if (!uid) return
-
- const orderId = this.data.jibenShuju.dingdan_id
- if (!orderId) {
- wx.showToast({ title: '订单ID缺失', icon: 'none' })
- return
- }
-
- const targetUserId = 'Sj' + uid
- if (this.data.currentIMUserId !== targetUserId) {
- this.ensureIMConnection()
- wx.showToast({ title: '连接中,请稍后再试', icon: 'none' })
- return
- }
-
- const that = this
- const groupName = this.data.jibenShuju.nicheng + '的订单群'
- const isCross = this.data.jibenShuju.fadanpingtai || 0
- const dashouUid = this.data.dashouInfo.yonghuid
-
- // 第一步:商家自己订阅群组
- wx.goEasy.im.subscribeGroup({
- groupIds: [orderId],
- onSuccess: () => {
- // 如果有打手,则需要帮打手订阅并发送消息
- if (dashouUid) {
- that.bangDashouDingyue(orderId, dashouUid)
- .then(() => {
- that.tiaozhuanQunLiao(orderId, groupName, isCross)
- })
- .catch((err) => {
- console.error('替打手订阅失败,仍可跳转群聊', err)
- wx.showToast({ title: '通知接单员可能失败', icon: 'none' })
- that.tiaozhuanQunLiao(orderId, groupName, isCross)
- })
- } else {
- // 无打手,直接跳转
- that.tiaozhuanQunLiao(orderId, groupName, isCross)
- }
- },
- onFailed: (error) => {
- console.error('订阅群组失败', error)
- wx.showToast({ title: '连接群聊失败', icon: 'none' })
- }
- })
- },
-
- // 帮打手订阅群组 + 发送机器人消息
- bangDashouDingyue(orderId, dashouUid) {
- return new Promise((resolve, reject) => {
- const dashouImId = 'Ds' + dashouUid
- const appkey = 'BC-32e2991c69bc47e1b538e73aaed37fc1' // 你的GoEasy AppKey
- const restHost = 'https://rest-hz.goeasy.io'
-
- // 1. 通过 REST API 为打手订阅群组
- wx.request({
- url: restHost + '/v2/im/subscribe-groups',
- method: 'POST',
- header: { 'content-type': 'application/json' },
- data: {
- appkey: appkey,
- userIds: [dashouImId],
- groupIds: [orderId]
- },
- success: (res) => {
- if (res.data.code === 200) {
- // 2. 订阅成功后,以打手身份发送一条系统消息
- wx.request({
- url: restHost + '/v2/im/message',
- method: 'POST',
- header: { 'content-type': 'application/json' },
- data: {
- appkey: appkey,
- senderId: dashouImId,
- senderData: { name: '系统通知', avatar: '' },
- to: {
- type: 'group',
- id: orderId,
- data: { name: '订单群', avatar: '' }
- },
- type: 'text',
- payload: '此消息为系统自动发送,接单员看到后会尽快回复您。'
- },
- success: (msgRes) => {
- if (msgRes.data.code === 200) {
- resolve()
- } else {
- reject(new Error('发送消息失败'))
- }
- },
- fail: reject
- })
- } else {
- reject(new Error('订阅打手失败'))
- }
- },
- fail: reject
- })
- })
- },
-
- // 跳转群聊页面
- tiaozhuanQunLiao(orderId, groupName, isCross) {
- const param = {
- groupId: orderId,
- orderId: orderId,
- groupName: groupName,
- groupAvatar: '',
- isCross: isCross
- }
- wx.navigateTo({
- url: '/pages/group-chat/group-chat?data=' + encodeURIComponent(JSON.stringify(param))
- })
- },
-
- // ========== 以下所有业务功能完全保持不变 ==========
- showGhDashouConfirm() { this.setData({ showGhDashouModal: true }) },
- closeGhDashouModal() { this.setData({ showGhDashouModal: false }) },
- async tijiaoGhDashou() {
- if (this.data.isGhDashouLoading) return
- this.setData({ isGhDashouLoading: true })
- wx.showLoading({ title: '更换中...', mask: true })
- try {
- const res = await request({
- url: '/dingdan/zxsjghds',
- method: 'POST',
- data: { dingdan_id: this.data.jibenShuju.dingdan_id },
- header: { 'content-type': 'application/json' }
- })
- wx.hideLoading()
- this.setData({ isGhDashouLoading: false, showGhDashouModal: false })
- if (res && res.data.code === 0) {
- this.setData({ xinDingdanId: res.data.data?.xin_dingdan_id || '' })
- wx.showToast({ title: '更换成功', icon: 'success' })
- this.jiazaiXiangxiShuju(this.data.jibenShuju.dingdan_id)
- } else {
- wx.showToast({ title: res?.data?.msg || '更换失败', icon: 'none' })
- }
- } catch (err) {
- wx.hideLoading()
- this.setData({ isGhDashouLoading: false, showGhDashouModal: false })
- wx.showToast({ title: '网络错误', icon: 'none' })
- }
- },
-
- showFakuanModal() { this.setData({ showFakuanModal: true, fakuanLiyou: '', fakuanJine: '', yingxiangQiangdan: true }) },
- closeFakuanModal() { this.setData({ showFakuanModal: false }) },
- inputFakuanLiyou(e) { this.setData({ fakuanLiyou: e.detail.value.slice(0, 50) }) },
- inputFakuanJine(e) { this.setData({ fakuanJine: e.detail.value }) },
- toggleQiangdan() { this.setData({ yingxiangQiangdan: !this.data.yingxiangQiangdan }) },
- jisuanFenhong() {
- const jine = parseFloat(this.data.fakuanJine)
- if (isNaN(jine) || jine <= 0) return '0.00'
- return (jine * this.data.fenhongLilv).toFixed(2)
- },
- async tijiaoFakuan() {
- const { fakuanLiyou, fakuanJine, jibenShuju, dashouInfo } = this.data
- if (!fakuanLiyou.trim()) { wx.showToast({ title: '请输入罚款原因', icon: 'none' }); return }
- const jineNum = parseFloat(fakuanJine)
- if (isNaN(jineNum) || jineNum <= 0) { wx.showToast({ title: '请输入正确的罚款金额', icon: 'none' }); return }
- if (this.data.isFakuanLoading) return
- this.setData({ isFakuanLoading: true })
- wx.showLoading({ title: '提交中...', mask: true })
- try {
- const res = await request({
- url: '/dingdan/sjfksq',
- method: 'POST',
- data: {
- dingdan_id: jibenShuju.dingdan_id,
- chufa_liyou: fakuanLiyou,
- fakuanjine: jineNum.toFixed(2),
- yingxiang_qiangdan: this.data.yingxiangQiangdan ? 1 : 0,
- dashou_id: dashouInfo.yonghuid
- },
- header: { 'content-type': 'application/json' }
- })
- wx.hideLoading()
- this.setData({ isFakuanLoading: false })
- if (res && res.data.code === 0) {
- wx.showToast({ title: '罚款申请已提交', icon: 'success' })
- this.setData({ showFakuanModal: false })
- this.jiazaiXiangxiShuju(jibenShuju.dingdan_id)
- } else {
- wx.showToast({ title: res?.data?.msg || '提交失败', icon: 'none' })
- }
- } catch (err) {
- wx.hideLoading()
- this.setData({ isFakuanLoading: false })
- wx.showToast({ title: '网络错误', icon: 'none' })
- }
- },
-
- showChexiaoBtn() { this.setData({ showChexiaoModal: true, chexiaoLiyou: '' }) },
- closeChexiaoModal() { this.setData({ showChexiaoModal: false }) },
- shuruChexiaoLiyou(e) { this.setData({ chexiaoLiyou: e.detail.value.slice(0, 50) }) },
- async tijiaoChexiao() {
- if (this.data.isChexiaoLoading) return
- const { chexiaoLiyou, jibenShuju } = this.data
- if (!chexiaoLiyou.trim()) { wx.showToast({ title: '请输入撤销原因', icon: 'none' }); return }
- this.setData({ isChexiaoLoading: true })
- wx.showLoading({ title: '撤销中...', mask: true })
- try {
- const res = await request({
- url: '/dingdan/sjchexiao',
- method: 'POST',
- data: { dingdan_id: jibenShuju.dingdan_id, chexiao_liyou: chexiaoLiyou },
- header: { 'content-type': 'application/json' }
- })
- wx.hideLoading()
- this.setData({ isChexiaoLoading: false })
- if (res && res.data.code === 0) {
- this.setData({ 'jibenShuju.zhuangtai': 5, showChexiaoModal: false })
- wx.showToast({ title: '撤销成功', icon: 'success' })
- } else {
- wx.showToast({ title: res?.data?.msg || '撤销失败', icon: 'none' })
- }
- } catch (err) {
- wx.hideLoading()
- this.setData({ isChexiaoLoading: false })
- wx.showToast({ title: '网络错误', icon: 'none' })
- }
- },
-
- showTuikuanBtn() { this.setData({ showTuikuanModal: true, tuikuanLiyou: '' }) },
- closeTuikuanModal() { this.setData({ showTuikuanModal: false }) },
- shuruTuikuanLiyou(e) { this.setData({ tuikuanLiyou: e.detail.value.slice(0, 50) }) },
- async tijiaoTuikuan() {
- if (this.data.isTuikuanLoading) return
- const { tuikuanLiyou, jibenShuju } = this.data
- if (!tuikuanLiyou.trim()) { wx.showToast({ title: '请输入退款原因', icon: 'none' }); return }
- this.setData({ isTuikuanLoading: true })
- wx.showLoading({ title: '提交中...', mask: true })
- try {
- const res = await request({
- url: '/dingdan/sjtkshenqing',
- method: 'POST',
- data: { dingdan_id: jibenShuju.dingdan_id, tuikuan_liyou: tuikuanLiyou },
- header: { 'content-type': 'application/json' }
- })
- wx.hideLoading()
- this.setData({ isTuikuanLoading: false })
- if (res && res.data.code === 0) {
- this.setData({ 'jibenShuju.zhuangtai': 4, showTuikuanModal: false })
- wx.showToast({ title: '退款申请已提交', icon: 'success' })
- } else {
- wx.showToast({ title: res?.data?.msg || '失败', icon: 'none' })
- }
- } catch (err) {
- wx.hideLoading()
- this.setData({ isTuikuanLoading: false })
- wx.showToast({ title: '网络错误', icon: 'none' })
- }
- },
-
- dianjiXingxing(e) {
- const pf = e.currentTarget.dataset.index
- const newStars = this.data.xingxingData.map((item, idx) => ({ ...item, active: idx < pf }))
- this.setData({ xingxingData: newStars, jiesuanPingfen: pf })
- },
- shuruLiuyan(e) { this.setData({ jiesuanLiuyan: e.detail.value.slice(0, 50) }) },
- showJiesuanConfirm() { this.setData({ showJiesuanConfirm: true }) },
- closeJiesuanConfirm() { this.setData({ showJiesuanConfirm: false }) },
- async tijiaoJiesuan() {
- if (this.data.isJiesuanLoading) return
- this.setData({ isJiesuanLoading: true })
- wx.showLoading({ title: '结算中...', mask: true })
- try {
- const res = await request({
- url: '/dingdan/sjjiesuan',
- method: 'POST',
- data: {
- dingdan_id: this.data.jibenShuju.dingdan_id,
- pingfen: this.data.jiesuanPingfen,
- liuyan: this.data.jiesuanLiuyan
- },
- header: { 'content-type': 'application/json' }
- })
- wx.hideLoading()
- this.setData({ isJiesuanLoading: false, showJiesuanConfirm: false })
- if (res && res.data.code === 0) {
- this.setData({ 'jibenShuju.zhuangtai': 3 })
- wx.showToast({ title: '结算成功', icon: 'success' })
- } else {
- wx.showToast({ title: res?.data?.msg || '结算失败', icon: 'none' })
- }
- } catch (err) {
- wx.hideLoading()
- this.setData({ isJiesuanLoading: false })
- wx.showToast({ title: '网络错误', icon: 'none' })
- }
- },
-
- // ========== 新增:罚款操作(修改/撤销/再次申请) ==========
- // 显示修改罚款弹窗(仅当状态为待缴纳 或 申诉中 时可用)
- showModifyFakuan() {
- const status = this.data.fadanData?.zhuangtai;
- if (status !== 1 && status !== 3) {
- wx.showToast({ title: '当前状态不可修改罚款', icon: 'none' });
- return;
- }
- this.setData({
- showModifyFakuanModal: true,
- modifyFakuanLiyou: this.data.fadanData?.chufaliyou || '',
- modifyFakuanJine: this.data.fadanData?.fakuanjine || ''
- });
- },
- closeModifyFakuanModal() {
- this.setData({ showModifyFakuanModal: false });
- },
- inputModifyFakuanLiyou(e) {
- this.setData({ modifyFakuanLiyou: e.detail.value.slice(0, 100) });
- },
- inputModifyFakuanJine(e) {
- this.setData({ modifyFakuanJine: e.detail.value });
- },
- async submitModifyFakuan() {
- const { modifyFakuanLiyou, modifyFakuanJine, fadanData, jibenShuju } = this.data;
- if (!modifyFakuanLiyou.trim()) {
- wx.showToast({ title: '请输入罚款理由', icon: 'none' }); return;
- }
- const jine = parseFloat(modifyFakuanJine);
- if (isNaN(jine) || jine <= 0) {
- wx.showToast({ title: '请输入正确的罚款金额', icon: 'none' }); return;
- }
- wx.showLoading({ title: '提交中...', mask: true });
- try {
- const res = await request({
- url: '/dingdan/sjfkxgycx',
- method: 'POST',
- data: {
- operation: 'modify_penalty',
- fadan_id: fadanData.id,
- dingdan_id: jibenShuju.dingdan_id,
- liyou: modifyFakuanLiyou,
- jine: jine.toFixed(2)
- }
- });
- wx.hideLoading();
- if (res && res.data.code === 0) {
- wx.showToast({ title: '修改成功', icon: 'success' });
- this.setData({ showModifyFakuanModal: false });
- this.jiazaiXiangxiShuju(jibenShuju.dingdan_id); // 刷新数据
- } else {
- wx.showToast({ title: res?.data?.msg || '修改失败', icon: 'none' });
- }
- } catch (err) {
- wx.hideLoading();
- wx.showToast({ title: '网络错误', icon: 'none' });
- }
- },
-
- // 显示撤销罚款弹窗(待缴纳/申诉中 可撤销,变为已驳回)
- showCancelFakuan() {
- const status = this.data.fadanData?.zhuangtai;
- if (status !== 1 && status !== 3) {
- wx.showToast({ title: '当前状态不可撤销罚款', icon: 'none' });
- return;
- }
- this.setData({ showCancelFakuanModal: true, cancelFakuanLiyou: '' });
- },
- closeCancelFakuanModal() {
- this.setData({ showCancelFakuanModal: false });
- },
- inputCancelFakuanLiyou(e) {
- this.setData({ cancelFakuanLiyou: e.detail.value.slice(0, 100) });
- },
- async submitCancelFakuan() {
- const { cancelFakuanLiyou, fadanData, jibenShuju } = this.data;
- if (!cancelFakuanLiyou.trim()) {
- wx.showToast({ title: '请输入驳回理由', icon: 'none' }); return;
- }
- wx.showLoading({ title: '提交中...', mask: true });
- try {
- const res = await request({
- url: '/dingdan/sjfkxgycx',
- method: 'POST',
- data: {
- operation: 'cancel_penalty',
- fadan_id: fadanData.id,
- dingdan_id: jibenShuju.dingdan_id,
- liyou: cancelFakuanLiyou
- }
- });
- wx.hideLoading();
- if (res && res.data.code === 0) {
- wx.showToast({ title: '已撤销', icon: 'success' });
- this.setData({ showCancelFakuanModal: false });
- this.jiazaiXiangxiShuju(jibenShuju.dingdan_id);
- } else {
- wx.showToast({ title: res?.data?.msg || '撤销失败', icon: 'none' });
- }
- } catch (err) {
- wx.hideLoading();
- wx.showToast({ title: '网络错误', icon: 'none' });
- }
- },
-
- // 显示再次申请罚款弹窗(仅当状态为已驳回时可用)
- showResubmitFakuan() {
- if (this.data.fadanData?.zhuangtai !== 4) {
- wx.showToast({ title: '当前状态不可再次申请', icon: 'none' });
- return;
- }
- this.setData({
- showResubmitFakuanModal: true,
- resubmitFakuanLiyou: '',
- resubmitFakuanJine: ''
- });
- },
- closeResubmitFakuanModal() {
- this.setData({ showResubmitFakuanModal: false });
- },
- inputResubmitFakuanLiyou(e) {
- this.setData({ resubmitFakuanLiyou: e.detail.value.slice(0, 100) });
- },
- inputResubmitFakuanJine(e) {
- this.setData({ resubmitFakuanJine: e.detail.value });
- },
- async submitResubmitFakuan() {
- const { resubmitFakuanLiyou, resubmitFakuanJine, fadanData, jibenShuju } = this.data;
- if (!resubmitFakuanLiyou.trim()) {
- wx.showToast({ title: '请输入罚款理由', icon: 'none' }); return;
- }
- const jine = parseFloat(resubmitFakuanJine);
- if (isNaN(jine) || jine <= 0) {
- wx.showToast({ title: '请输入正确的罚款金额', icon: 'none' }); return;
- }
- wx.showLoading({ title: '提交中...', mask: true });
- try {
- const res = await request({
- url: '/dingdan/sjfkxgycx',
- method: 'POST',
- data: {
- operation: 'resubmit_penalty',
- fadan_id: fadanData.id,
- dingdan_id: jibenShuju.dingdan_id,
- liyou: resubmitFakuanLiyou,
- jine: jine.toFixed(2)
- }
- });
- wx.hideLoading();
- if (res && res.data.code === 0) {
- wx.showToast({ title: '申请成功', icon: 'success' });
- this.setData({ showResubmitFakuanModal: false });
- this.jiazaiXiangxiShuju(jibenShuju.dingdan_id);
- } else {
- wx.showToast({ title: res?.data?.msg || '申请失败', icon: 'none' });
- }
- } catch (err) {
- wx.hideLoading();
- wx.showToast({ title: '网络错误', icon: 'none' });
- }
- },
-
- // ========== 新增:订单退款操作(撤销/修改理由) ==========
- showCancelTuikuan() {
- if (this.data.jibenShuju.zhuangtai !== 4) {
- wx.showToast({ title: '只有退款中的订单可撤销', icon: 'none' });
- return;
- }
- this.setData({ showCancelTuikuanModal: true, cancelTuikuanLiyou: '' });
- },
- closeCancelTuikuanModal() {
- this.setData({ showCancelTuikuanModal: false });
- },
- inputCancelTuikuanLiyou(e) {
- this.setData({ cancelTuikuanLiyou: e.detail.value.slice(0, 100) });
- },
- async submitCancelTuikuan() {
- const { cancelTuikuanLiyou, jibenShuju } = this.data;
- if (!cancelTuikuanLiyou.trim()) {
- wx.showToast({ title: '请输入撤销理由', icon: 'none' }); return;
- }
- wx.showLoading({ title: '撤销中...', mask: true });
- try {
- const res = await request({
- url: '/dingdan/sjfkxgycx',
- method: 'POST',
- data: {
- operation: 'cancel_refund',
- dingdan_id: jibenShuju.dingdan_id,
- liyou: cancelTuikuanLiyou
- }
- });
- wx.hideLoading();
- if (res && res.data.code === 0) {
- wx.showToast({ title: '已撤销退款申请', icon: 'success' });
- this.setData({ showCancelTuikuanModal: false });
- this.jiazaiXiangxiShuju(jibenShuju.dingdan_id);
- } else {
- wx.showToast({ title: res?.data?.msg || '撤销失败', icon: 'none' });
- }
- } catch (err) {
- wx.hideLoading();
- wx.showToast({ title: '网络错误', icon: 'none' });
- }
- },
-
- showModifyTuikuan() {
- if (this.data.jibenShuju.zhuangtai !== 4) {
- wx.showToast({ title: '只有退款中的订单可修改理由', icon: 'none' });
- return;
- }
- this.setData({
- showModifyTuikuanModal: true,
- modifyTuikuanLiyou: this.data.xiangxiShuju.tuikuan_liyou || ''
- });
- },
- closeModifyTuikuanModal() {
- this.setData({ showModifyTuikuanModal: false });
- },
- inputModifyTuikuanLiyou(e) {
- this.setData({ modifyTuikuanLiyou: e.detail.value.slice(0, 100) });
- },
- async submitModifyTuikuan() {
- const { modifyTuikuanLiyou, jibenShuju } = this.data;
- if (!modifyTuikuanLiyou.trim()) {
- wx.showToast({ title: '请输入退款理由', icon: 'none' }); return;
- }
- wx.showLoading({ title: '修改中...', mask: true });
- try {
- const res = await request({
- url: '/dingdan/sjfkxgycx',
- method: 'POST',
- data: {
- operation: 'modify_refund_reason',
- dingdan_id: jibenShuju.dingdan_id,
- liyou: modifyTuikuanLiyou
- }
- });
- wx.hideLoading();
- if (res && res.data.code === 0) {
- wx.showToast({ title: '修改成功', icon: 'success' });
- this.setData({ showModifyTuikuanModal: false });
- this.jiazaiXiangxiShuju(jibenShuju.dingdan_id);
- } else {
- wx.showToast({ title: res?.data?.msg || '修改失败', icon: 'none' });
- }
- } catch (err) {
- wx.hideLoading();
- wx.showToast({ title: '网络错误', icon: 'none' });
- }
- },
-
- // ========== 新增:教程按钮 ==========
- startTutorialBtnTimer() {
- if (this.tutorialTimer) clearTimeout(this.tutorialTimer);
- this.tutorialTimer = setTimeout(() => {
- this.setData({ tutorialBtnHidden: true });
- }, 5000);
- },
- resetTutorialBtn() {
- if (this.data.tutorialBtnHidden) {
- this.setData({ tutorialBtnHidden: false });
- }
- if (this.tutorialTimer) clearTimeout(this.tutorialTimer);
- this.tutorialTimer = setTimeout(() => {
- this.setData({ tutorialBtnHidden: true });
- }, 5000);
- },
- onTutorialBtnTap() {
- this.resetTutorialBtn();
- PopupService.checkAndShow(this, 'sjddxq1');
- }
+// pages/merchant-order-detail/merchant-order-detail.js - 商家订单详情(完整版,已添加罚款/退款操作及教程按钮)
+const app = getApp()
+import request from '../../utils/request.js'
+import PopupService from '../../services/popupService.js' // 新增:导入弹窗服务
+import { STAFF_API, isStaffMode, syncStaffUi, getStaffContext, staffCan } from '../../utils/staff-api.js'
+
+Page({
+ data: {
+ jibenShuju: {
+ dingdan_id: '', tupian: '', jieshao: '', create_time: '',
+ jine: '', nicheng: '', beizhu: '', zhuangtai: 0, fadanpingtai: 0
+ },
+ dashouInfo: { yonghuid: '', nicheng: '', avatar: '' },
+ dispatchStaff: null,
+ dispatchStaffAvatar: '',
+ xiangxiShuju: {
+ dashou_jiaofu: [], dashou_liuyan: '', tuikuan_liyou: '', shangjia_liuyan: ''
+ },
+ fadanData: null,
+ fenhongLilv: 0,
+ xinDingdanId: '',
+ zhuangtaiYanseMap: {
+ 1: '#E8F5E9', 2: '#E3F2FD', 3: '#F3E5F5', 4: '#FFF3E0',
+ 5: '#FFEBEE', 6: '#EFEBE9', 7: '#E0F7FA', 8: '#FFF8E1'
+ },
+ zhuangtaiWenziMap: {
+ 1: '已下单', 2: '进行中', 3: '已完成', 4: '退款中',
+ 5: '已退款', 6: '退款失败', 7: '指定中', 8: '结算中'
+ },
+ isLoading: true,
+ showChexiaoModal: false, chexiaoLiyou: '', isChexiaoLoading: false,
+ showTuikuanModal: false, tuikuanLiyou: '', isTuikuanLoading: false,
+ showFakuanModal: false, fakuanLiyou: '', fakuanJine: '', yingxiangQiangdan: true, isFakuanLoading: false,
+ showGhDashouModal: false, isGhDashouLoading: false,
+ showJiesuanConfirm: false, isJiesuanLoading: false,
+ jiesuanPingfen: 0, jiesuanLiuyan: '',
+ xingxingData: [
+ { id: 1, active: false }, { id: 2, active: false }, { id: 3, active: false },
+ { id: 4, active: false }, { id: 5, active: false }
+ ],
+ currentIMUserId: '',
+
+ // ========== 新增:罚单/订单操作弹窗控制 ==========
+ showModifyFakuanModal: false, // 修改罚款弹窗
+ modifyFakuanLiyou: '', // 修改后的罚款理由
+ modifyFakuanJine: '', // 修改后的罚款金额
+ showCancelFakuanModal: false, // 撤销罚款弹窗
+ cancelFakuanLiyou: '', // 撤销驳回理由
+ showResubmitFakuanModal: false, // 再次申请罚款弹窗
+ resubmitFakuanLiyou: '', // 再次申请的理由
+ resubmitFakuanJine: '', // 再次申请的金额
+ showCancelTuikuanModal: false, // 撤销退款申请弹窗
+ cancelTuikuanLiyou: '', // 撤销退款理由
+ showModifyTuikuanModal: false, // 修改退款理由弹窗
+ modifyTuikuanLiyou: '', // 修改后的退款理由
+ // 教程按钮状态
+ tutorialBtnHidden: false,
+ canCancel: true,
+ canRefund: true,
+ canSettle: true,
+ canPenalty: true,
+ canPenaltyManage: true,
+ },
+
+ onLoad(options) {
+ wx.setNavigationBarTitle({ title: '订单详情' })
+ syncStaffUi(this)
+ this.ensureIMConnection()
+ this.jiexiTiaozhuanCanshu(options)
+ // 新增:启动教程按钮定时器
+ this.startTutorialBtnTimer()
+ },
+
+ onShow() {
+ this.registerNotificationComponent()
+ syncStaffUi(this)
+ this.ensureIMConnection()
+ },
+
+ onHide() {
+ // 页面隐藏时重置弹窗服务状态,避免影响其他页面
+ PopupService.reset()
+ },
+
+ onUnload() {
+ // 清除缩进定时器,避免内存泄漏
+ if (this.tutorialTimer) {
+ clearTimeout(this.tutorialTimer)
+ }
+ // 页面卸载时重置弹窗服务
+ PopupService.reset()
+ },
+
+ registerNotificationComponent() {
+ const notificationComp = this.selectComponent('#global-notification')
+ if (notificationComp && notificationComp.showNotification) {
+ app.globalData.globalNotification = {
+ show: (data) => notificationComp.showNotification(data),
+ hide: () => notificationComp.hideNotification()
+ }
+ }
+ },
+
+ ensureIMConnection() {
+ const uid = wx.getStorageSync('uid')
+ if (!uid) return
+ const targetUserId = 'Sj' + uid
+ if (wx.goEasy && wx.goEasy.getConnectionStatus) {
+ const status = wx.goEasy.getConnectionStatus()
+ if (status === 'connected' || status === 'reconnected') {
+ const currentUserId = wx.goEasy.im ? wx.goEasy.im.userId : null
+ if (currentUserId === targetUserId) {
+ this.setData({ currentIMUserId: targetUserId })
+ return
+ } else {
+ wx.goEasy.disconnect()
+ }
+ }
+ }
+ this.connectGoEasy(targetUserId)
+ },
+
+ connectGoEasy(userId) {
+ let currentUser = app.globalData.currentUser
+ if (!currentUser) {
+ const uid = wx.getStorageSync('uid')
+ currentUser = {
+ id: uid,
+ name: '用户' + (uid ? uid.substring(0, 6) : ''),
+ avatar: app.globalData.ossImageUrl + app.globalData.morentouxiang
+ }
+ }
+ const staffCtx = isStaffMode() ? getStaffContext() : null
+ let imName = currentUser.name || wx.getStorageSync('nicheng') || '商家'
+ if (staffCtx) {
+ const roleLabel = staffCtx.role_name || '客服'
+ const nick = staffCtx.display_name || wx.getStorageSync('nicheng') || ''
+ imName = nick ? `${nick}(${roleLabel})` : roleLabel
+ }
+ wx.goEasy.connect({
+ id: userId,
+ data: {
+ name: imName,
+ avatar: currentUser.avatar || (app.globalData.ossImageUrl + app.globalData.morentouxiang)
+ },
+ onSuccess: () => {
+ this.setData({ currentIMUserId: userId })
+ try {
+ wx.setStorageSync('savedGoEasyConnection', JSON.stringify({
+ userId, identityType: 'shangjia', lastConnectTime: Date.now()
+ }))
+ wx.setStorageSync('goEasyUserId', userId)
+ } catch (e) {}
+ },
+ onFailed: (error) => {
+ if (error.code === 408) {
+ this.setData({ currentIMUserId: userId })
+ } else {
+ console.error('IM连接失败', error)
+ }
+ }
+ })
+ },
+
+ jiexiTiaozhuanCanshu(options) {
+ const directId = options.dingdan_id || options.dingdanId || ''
+ if (directId) {
+ this.setData({
+ 'jibenShuju.dingdan_id': directId,
+ isLoading: false,
+ })
+ this.jiazaiXiangxiShuju(directId)
+ return
+ }
+ try {
+ const dingdanDataStr = options.dingdanData || ''
+ if (!dingdanDataStr) {
+ wx.showToast({ title: '订单数据错误', icon: 'none' })
+ setTimeout(() => wx.navigateBack(), 1500)
+ return
+ }
+ const dingdanData = JSON.parse(decodeURIComponent(dingdanDataStr))
+ let tupianUrl = dingdanData.tupian || ''
+ if (!tupianUrl) tupianUrl = this.getTupianUrl()
+ let jine = parseFloat(dingdanData.jine || 0)
+ if (isNaN(jine)) jine = 0
+ this.setData({
+ 'jibenShuju.dingdan_id': dingdanData.dingdan_id || '',
+ 'jibenShuju.tupian': tupianUrl,
+ 'jibenShuju.jieshao': dingdanData.jieshao || '',
+ 'jibenShuju.create_time': dingdanData.create_time || '',
+ 'jibenShuju.jine': jine.toFixed(2),
+ 'jibenShuju.nicheng': dingdanData.nicheng || '',
+ 'jibenShuju.beizhu': dingdanData.beizhu || '',
+ 'jibenShuju.zhuangtai': dingdanData.zhuangtai || 0,
+ 'jibenShuju.fadanpingtai': dingdanData.fadanpingtai || 0,
+ isLoading: false
+ })
+ this.jiazaiXiangxiShuju(dingdanData.dingdan_id)
+ } catch (error) {
+ console.error('解析订单数据失败', error)
+ wx.showToast({ title: '数据解析失败', icon: 'none' })
+ setTimeout(() => wx.navigateBack(), 1500)
+ }
+ },
+
+ getTupianUrl() {
+ const cacheTouxiang = wx.getStorageSync('touxiang') || ''
+ let tupianPath = cacheTouxiang || app.globalData.morentouxiang || ''
+ const ossImageUrl = app.globalData.ossImageUrl || ''
+ if (tupianPath && ossImageUrl) {
+ if (tupianPath.startsWith('/')) tupianPath = tupianPath.substring(1)
+ return ossImageUrl + tupianPath
+ }
+ return ''
+ },
+
+ async jiazaiXiangxiShuju(dingdanId) {
+ if (!dingdanId) return
+ try {
+ const res = await request({
+ url: isStaffMode() ? STAFF_API.orderDetail : '/dingdan/sjddxq',
+ method: 'POST',
+ data: { dingdan_id: dingdanId },
+ header: { 'content-type': 'application/json' }
+ })
+ if (res && res.data.code === 0 && res.data.data) {
+ const d = res.data.data
+ let jiaofuList = d.dashou_jiaofu || []
+ jiaofuList = jiaofuList.map(url => {
+ if (url && !url.startsWith('http')) {
+ const oss = app.globalData.ossImageUrl || ''
+ return oss + url
+ }
+ return url
+ })
+ let dashouAvatar = d.dashou_avatar || ''
+ if (dashouAvatar && !dashouAvatar.startsWith('http')) {
+ dashouAvatar = (app.globalData.ossImageUrl || '') + dashouAvatar
+ }
+ let dispatchStaffAvatar = (d.dispatch_staff && d.dispatch_staff.dispatch_staff_avatar) || ''
+ if (dispatchStaffAvatar && !dispatchStaffAvatar.startsWith('http')) {
+ const oss = app.globalData.ossImageUrl || ''
+ dispatchStaffAvatar = oss + (dispatchStaffAvatar.startsWith('/') ? dispatchStaffAvatar.slice(1) : dispatchStaffAvatar)
+ }
+ this.setData({
+ 'dashouInfo.yonghuid': d.dashou_yonghuid || '',
+ 'dashouInfo.nicheng': d.dashou_nicheng || '',
+ 'dashouInfo.avatar': dashouAvatar,
+ dispatchStaff: d.dispatch_staff || null,
+ dispatchStaffAvatar: dispatchStaffAvatar || '/images/default-avatar.png',
+ 'xiangxiShuju.dashou_jiaofu': jiaofuList,
+ 'xiangxiShuju.dashou_liuyan': d.dashou_liuyan || '',
+ 'xiangxiShuju.tuikuan_liyou': d.tuikuan_liyou || '',
+ 'xiangxiShuju.shangjia_liuyan': d.shangjia_liuyan || '',
+ fadanData: d.fadan || null,
+ fenhongLilv: parseFloat(d.fenhong_lilv) || 0,
+ xinDingdanId: d.xin_dingdan_id || '',
+ 'jibenShuju.zhuangtai': d.zhuangtai || this.data.jibenShuju.zhuangtai
+ })
+ } else {
+ wx.showToast({ title: res?.data?.msg || '获取详情失败', icon: 'none' })
+ }
+ } catch (err) {
+ console.error('加载详情失败', err)
+ wx.showToast({ title: '网络错误', icon: 'none' })
+ }
+ },
+
+ fuzhiWenben(e) {
+ const text = e.currentTarget.dataset.text
+ if (!text) return
+ wx.setClipboardData({ data: text })
+ wx.showToast({ title: '已复制', icon: 'success' })
+ },
+
+ yulanTupian(e) {
+ const current = e.currentTarget.dataset.url
+ const urls = e.currentTarget.dataset.urls || [current]
+ wx.previewImage({ current, urls })
+ },
+
+ goToKefu() {
+ const kefuLink = app.globalData.kefuConfig?.link || ''
+ const corpId = app.globalData.kefuConfig?.enterpriseId || ''
+ if (!kefuLink || !corpId) {
+ wx.showToast({ title: '客服配置未加载', icon: 'none' })
+ return
+ }
+ wx.openCustomerServiceChat({
+ extInfo: { url: kefuLink },
+ corpId: corpId,
+ success: (res) => console.log('跳转客服成功', res),
+ fail: (err) => {
+ console.error('跳转客服失败', err)
+ wx.showToast({ title: '暂时无法连接客服', icon: 'none' })
+ }
+ })
+ },
+
+ // ===== 联系打手(替打手订阅 + 发机器人消息 + 跳转) =====
+ goToChatWithDashou() {
+ if (isStaffMode() && !staffCan('imChat')) {
+ wx.showToast({ title: '当前角色无订单聊天权限', icon: 'none' })
+ return
+ }
+ const uid = wx.getStorageSync('uid')
+ if (!uid) return
+
+ const orderId = this.data.jibenShuju.dingdan_id
+ if (!orderId) {
+ wx.showToast({ title: '订单ID缺失', icon: 'none' })
+ return
+ }
+
+ const targetUserId = 'Sj' + uid
+ if (this.data.currentIMUserId !== targetUserId) {
+ this.ensureIMConnection()
+ wx.showToast({ title: '连接中,请稍后再试', icon: 'none' })
+ return
+ }
+
+ const that = this
+ const groupName = this.data.jibenShuju.nicheng + '的订单群'
+ const isCross = this.data.jibenShuju.fadanpingtai || 0
+ const dashouUid = this.data.dashouInfo.yonghuid
+
+ // 第一步:商家自己订阅群组
+ wx.goEasy.im.subscribeGroup({
+ groupIds: [orderId],
+ onSuccess: () => {
+ // 如果有打手,则需要帮打手订阅并发送消息
+ if (dashouUid) {
+ that.bangDashouDingyue(orderId, dashouUid)
+ .then(() => {
+ that.tiaozhuanQunLiao(orderId, groupName, isCross)
+ })
+ .catch((err) => {
+ console.error('替打手订阅失败,仍可跳转群聊', err)
+ wx.showToast({ title: '通知接单员可能失败', icon: 'none' })
+ that.tiaozhuanQunLiao(orderId, groupName, isCross)
+ })
+ } else {
+ // 无打手,直接跳转
+ that.tiaozhuanQunLiao(orderId, groupName, isCross)
+ }
+ },
+ onFailed: (error) => {
+ console.error('订阅群组失败', error)
+ wx.showToast({ title: '连接群聊失败', icon: 'none' })
+ }
+ })
+ },
+
+ // 帮打手订阅群组 + 发送机器人消息
+ bangDashouDingyue(orderId, dashouUid) {
+ return new Promise((resolve, reject) => {
+ const dashouImId = 'Ds' + dashouUid
+ const appkey = 'BC-32e2991c69bc47e1b538e73aaed37fc1' // 你的GoEasy AppKey
+ const restHost = 'https://rest-hz.goeasy.io'
+
+ // 1. 通过 REST API 为打手订阅群组
+ wx.request({
+ url: restHost + '/v2/im/subscribe-groups',
+ method: 'POST',
+ header: { 'content-type': 'application/json' },
+ data: {
+ appkey: appkey,
+ userIds: [dashouImId],
+ groupIds: [orderId]
+ },
+ success: (res) => {
+ if (res.data.code === 200) {
+ // 2. 订阅成功后,以打手身份发送一条系统消息
+ wx.request({
+ url: restHost + '/v2/im/message',
+ method: 'POST',
+ header: { 'content-type': 'application/json' },
+ data: {
+ appkey: appkey,
+ senderId: dashouImId,
+ senderData: { name: '系统通知', avatar: '' },
+ to: {
+ type: 'group',
+ id: orderId,
+ data: { name: '订单群', avatar: '' }
+ },
+ type: 'text',
+ payload: '此消息为系统自动发送,接单员看到后会尽快回复您。'
+ },
+ success: (msgRes) => {
+ if (msgRes.data.code === 200) {
+ resolve()
+ } else {
+ reject(new Error('发送消息失败'))
+ }
+ },
+ fail: reject
+ })
+ } else {
+ reject(new Error('订阅打手失败'))
+ }
+ },
+ fail: reject
+ })
+ })
+ },
+
+ // 跳转群聊页面
+ tiaozhuanQunLiao(orderId, groupName, isCross) {
+ const param = {
+ groupId: orderId,
+ orderId: orderId,
+ groupName: groupName,
+ groupAvatar: '',
+ isCross: isCross
+ }
+ wx.navigateTo({
+ url: '/pages/group-chat/group-chat?data=' + encodeURIComponent(JSON.stringify(param))
+ })
+ },
+
+ // ========== 以下所有业务功能完全保持不变 ==========
+ showGhDashouConfirm() { this.setData({ showGhDashouModal: true }) },
+ closeGhDashouModal() { this.setData({ showGhDashouModal: false }) },
+ async tijiaoGhDashou() {
+ if (this.data.isGhDashouLoading) return
+ this.setData({ isGhDashouLoading: true })
+ wx.showLoading({ title: '更换中...', mask: true })
+ try {
+ const res = await request({
+ url: isStaffMode() ? STAFF_API.changePlayer : '/dingdan/zxsjghds',
+ method: 'POST',
+ data: { dingdan_id: this.data.jibenShuju.dingdan_id },
+ header: { 'content-type': 'application/json' }
+ })
+ wx.hideLoading()
+ this.setData({ isGhDashouLoading: false, showGhDashouModal: false })
+ if (res && res.data.code === 0) {
+ this.setData({ xinDingdanId: res.data.data?.xin_dingdan_id || '' })
+ wx.showToast({ title: '更换成功', icon: 'success' })
+ this.jiazaiXiangxiShuju(this.data.jibenShuju.dingdan_id)
+ } else {
+ wx.showToast({ title: res?.data?.msg || '更换失败', icon: 'none' })
+ }
+ } catch (err) {
+ wx.hideLoading()
+ this.setData({ isGhDashouLoading: false, showGhDashouModal: false })
+ wx.showToast({ title: '网络错误', icon: 'none' })
+ }
+ },
+
+ showFakuanModal() { this.setData({ showFakuanModal: true, fakuanLiyou: '', fakuanJine: '', yingxiangQiangdan: true }) },
+ closeFakuanModal() { this.setData({ showFakuanModal: false }) },
+ inputFakuanLiyou(e) { this.setData({ fakuanLiyou: e.detail.value.slice(0, 50) }) },
+ inputFakuanJine(e) { this.setData({ fakuanJine: e.detail.value }) },
+ toggleQiangdan() { this.setData({ yingxiangQiangdan: !this.data.yingxiangQiangdan }) },
+ jisuanFenhong() {
+ const jine = parseFloat(this.data.fakuanJine)
+ if (isNaN(jine) || jine <= 0) return '0.00'
+ return (jine * this.data.fenhongLilv).toFixed(2)
+ },
+ async tijiaoFakuan() {
+ const { fakuanLiyou, fakuanJine, jibenShuju, dashouInfo } = this.data
+ if (!fakuanLiyou.trim()) { wx.showToast({ title: '请输入罚款原因', icon: 'none' }); return }
+ const jineNum = parseFloat(fakuanJine)
+ if (isNaN(jineNum) || jineNum <= 0) { wx.showToast({ title: '请输入正确的罚款金额', icon: 'none' }); return }
+ if (this.data.isFakuanLoading) return
+ this.setData({ isFakuanLoading: true })
+ wx.showLoading({ title: '提交中...', mask: true })
+ try {
+ const res = await request({
+ url: isStaffMode() ? STAFF_API.penaltyApply : '/dingdan/sjfksq',
+ method: 'POST',
+ data: {
+ dingdan_id: jibenShuju.dingdan_id,
+ chufa_liyou: fakuanLiyou,
+ fakuanjine: jineNum.toFixed(2),
+ yingxiang_qiangdan: this.data.yingxiangQiangdan ? 1 : 0,
+ dashou_id: dashouInfo.yonghuid
+ },
+ header: { 'content-type': 'application/json' }
+ })
+ wx.hideLoading()
+ this.setData({ isFakuanLoading: false })
+ if (res && res.data.code === 0) {
+ wx.showToast({ title: '罚款申请已提交', icon: 'success' })
+ this.setData({ showFakuanModal: false })
+ this.jiazaiXiangxiShuju(jibenShuju.dingdan_id)
+ } else {
+ wx.showToast({ title: res?.data?.msg || '提交失败', icon: 'none' })
+ }
+ } catch (err) {
+ wx.hideLoading()
+ this.setData({ isFakuanLoading: false })
+ wx.showToast({ title: '网络错误', icon: 'none' })
+ }
+ },
+
+ showChexiaoBtn() { this.setData({ showChexiaoModal: true, chexiaoLiyou: '' }) },
+ closeChexiaoModal() { this.setData({ showChexiaoModal: false }) },
+ shuruChexiaoLiyou(e) { this.setData({ chexiaoLiyou: e.detail.value.slice(0, 50) }) },
+ async tijiaoChexiao() {
+ if (this.data.isChexiaoLoading) return
+ const { chexiaoLiyou, jibenShuju } = this.data
+ if (!chexiaoLiyou.trim()) { wx.showToast({ title: '请输入撤销原因', icon: 'none' }); return }
+ this.setData({ isChexiaoLoading: true })
+ wx.showLoading({ title: '撤销中...', mask: true })
+ try {
+ const res = await request({
+ url: isStaffMode() ? STAFF_API.orderCancel : '/dingdan/sjchexiao',
+ method: 'POST',
+ data: { dingdan_id: jibenShuju.dingdan_id, chexiao_liyou: chexiaoLiyou },
+ header: { 'content-type': 'application/json' }
+ })
+ wx.hideLoading()
+ this.setData({ isChexiaoLoading: false })
+ if (res && res.data.code === 0) {
+ this.setData({ 'jibenShuju.zhuangtai': 5, showChexiaoModal: false })
+ wx.showToast({ title: '撤销成功', icon: 'success' })
+ } else {
+ wx.showToast({ title: res?.data?.msg || '撤销失败', icon: 'none' })
+ }
+ } catch (err) {
+ wx.hideLoading()
+ this.setData({ isChexiaoLoading: false })
+ wx.showToast({ title: '网络错误', icon: 'none' })
+ }
+ },
+
+ showTuikuanBtn() { this.setData({ showTuikuanModal: true, tuikuanLiyou: '' }) },
+ closeTuikuanModal() { this.setData({ showTuikuanModal: false }) },
+ shuruTuikuanLiyou(e) { this.setData({ tuikuanLiyou: e.detail.value.slice(0, 50) }) },
+ async tijiaoTuikuan() {
+ if (this.data.isTuikuanLoading) return
+ const { tuikuanLiyou, jibenShuju } = this.data
+ if (!tuikuanLiyou.trim()) { wx.showToast({ title: '请输入退款原因', icon: 'none' }); return }
+ this.setData({ isTuikuanLoading: true })
+ wx.showLoading({ title: '提交中...', mask: true })
+ try {
+ const res = await request({
+ url: isStaffMode() ? STAFF_API.orderRefund : '/dingdan/sjtkshenqing',
+ method: 'POST',
+ data: { dingdan_id: jibenShuju.dingdan_id, tuikuan_liyou: tuikuanLiyou },
+ header: { 'content-type': 'application/json' }
+ })
+ wx.hideLoading()
+ this.setData({ isTuikuanLoading: false })
+ if (res && res.data.code === 0) {
+ this.setData({ 'jibenShuju.zhuangtai': 4, showTuikuanModal: false })
+ wx.showToast({ title: '退款申请已提交', icon: 'success' })
+ } else {
+ wx.showToast({ title: res?.data?.msg || '失败', icon: 'none' })
+ }
+ } catch (err) {
+ wx.hideLoading()
+ this.setData({ isTuikuanLoading: false })
+ wx.showToast({ title: '网络错误', icon: 'none' })
+ }
+ },
+
+ dianjiXingxing(e) {
+ const pf = e.currentTarget.dataset.index
+ const newStars = this.data.xingxingData.map((item, idx) => ({ ...item, active: idx < pf }))
+ this.setData({ xingxingData: newStars, jiesuanPingfen: pf })
+ },
+ shuruLiuyan(e) { this.setData({ jiesuanLiuyan: e.detail.value.slice(0, 50) }) },
+ showJiesuanConfirm() { this.setData({ showJiesuanConfirm: true }) },
+ closeJiesuanConfirm() { this.setData({ showJiesuanConfirm: false }) },
+ async tijiaoJiesuan() {
+ if (this.data.isJiesuanLoading) return
+ this.setData({ isJiesuanLoading: true })
+ wx.showLoading({ title: '结算中...', mask: true })
+ try {
+ const res = await request({
+ url: isStaffMode() ? STAFF_API.orderSettle : '/dingdan/sjjiesuan',
+ method: 'POST',
+ data: {
+ dingdan_id: this.data.jibenShuju.dingdan_id,
+ pingfen: this.data.jiesuanPingfen,
+ liuyan: this.data.jiesuanLiuyan
+ },
+ header: { 'content-type': 'application/json' }
+ })
+ wx.hideLoading()
+ this.setData({ isJiesuanLoading: false, showJiesuanConfirm: false })
+ if (res && res.data.code === 0) {
+ this.setData({ 'jibenShuju.zhuangtai': 3 })
+ wx.showToast({ title: '结算成功', icon: 'success' })
+ } else {
+ wx.showToast({ title: res?.data?.msg || '结算失败', icon: 'none' })
+ }
+ } catch (err) {
+ wx.hideLoading()
+ this.setData({ isJiesuanLoading: false })
+ wx.showToast({ title: '网络错误', icon: 'none' })
+ }
+ },
+
+ // ========== 新增:罚款操作(修改/撤销/再次申请) ==========
+ // 显示修改罚款弹窗(仅当状态为待缴纳 或 申诉中 时可用)
+ showModifyFakuan() {
+ const status = this.data.fadanData?.zhuangtai;
+ if (status !== 1 && status !== 3) {
+ wx.showToast({ title: '当前状态不可修改罚款', icon: 'none' });
+ return;
+ }
+ this.setData({
+ showModifyFakuanModal: true,
+ modifyFakuanLiyou: this.data.fadanData?.chufaliyou || '',
+ modifyFakuanJine: this.data.fadanData?.fakuanjine || ''
+ });
+ },
+ closeModifyFakuanModal() {
+ this.setData({ showModifyFakuanModal: false });
+ },
+ inputModifyFakuanLiyou(e) {
+ this.setData({ modifyFakuanLiyou: e.detail.value.slice(0, 100) });
+ },
+ inputModifyFakuanJine(e) {
+ this.setData({ modifyFakuanJine: e.detail.value });
+ },
+ async submitModifyFakuan() {
+ const { modifyFakuanLiyou, modifyFakuanJine, fadanData, jibenShuju } = this.data;
+ if (!modifyFakuanLiyou.trim()) {
+ wx.showToast({ title: '请输入罚款理由', icon: 'none' }); return;
+ }
+ const jine = parseFloat(modifyFakuanJine);
+ if (isNaN(jine) || jine <= 0) {
+ wx.showToast({ title: '请输入正确的罚款金额', icon: 'none' }); return;
+ }
+ wx.showLoading({ title: '提交中...', mask: true });
+ try {
+ const res = await request({
+ url: isStaffMode() ? STAFF_API.penaltyManage : '/dingdan/sjfkxgycx',
+ method: 'POST',
+ data: {
+ operation: 'modify_penalty',
+ fadan_id: fadanData.id,
+ dingdan_id: jibenShuju.dingdan_id,
+ liyou: modifyFakuanLiyou,
+ jine: jine.toFixed(2)
+ }
+ });
+ wx.hideLoading();
+ if (res && res.data.code === 0) {
+ wx.showToast({ title: '修改成功', icon: 'success' });
+ this.setData({ showModifyFakuanModal: false });
+ this.jiazaiXiangxiShuju(jibenShuju.dingdan_id); // 刷新数据
+ } else {
+ wx.showToast({ title: res?.data?.msg || '修改失败', icon: 'none' });
+ }
+ } catch (err) {
+ wx.hideLoading();
+ wx.showToast({ title: '网络错误', icon: 'none' });
+ }
+ },
+
+ // 显示撤销罚款弹窗(待缴纳/申诉中 可撤销,变为已驳回)
+ showCancelFakuan() {
+ const status = this.data.fadanData?.zhuangtai;
+ if (status !== 1 && status !== 3) {
+ wx.showToast({ title: '当前状态不可撤销罚款', icon: 'none' });
+ return;
+ }
+ this.setData({ showCancelFakuanModal: true, cancelFakuanLiyou: '' });
+ },
+ closeCancelFakuanModal() {
+ this.setData({ showCancelFakuanModal: false });
+ },
+ inputCancelFakuanLiyou(e) {
+ this.setData({ cancelFakuanLiyou: e.detail.value.slice(0, 100) });
+ },
+ async submitCancelFakuan() {
+ const { cancelFakuanLiyou, fadanData, jibenShuju } = this.data;
+ if (!cancelFakuanLiyou.trim()) {
+ wx.showToast({ title: '请输入驳回理由', icon: 'none' }); return;
+ }
+ wx.showLoading({ title: '提交中...', mask: true });
+ try {
+ const res = await request({
+ url: isStaffMode() ? STAFF_API.penaltyManage : '/dingdan/sjfkxgycx',
+ method: 'POST',
+ data: {
+ operation: 'cancel_penalty',
+ fadan_id: fadanData.id,
+ dingdan_id: jibenShuju.dingdan_id,
+ liyou: cancelFakuanLiyou
+ }
+ });
+ wx.hideLoading();
+ if (res && res.data.code === 0) {
+ wx.showToast({ title: '已撤销', icon: 'success' });
+ this.setData({ showCancelFakuanModal: false });
+ this.jiazaiXiangxiShuju(jibenShuju.dingdan_id);
+ } else {
+ wx.showToast({ title: res?.data?.msg || '撤销失败', icon: 'none' });
+ }
+ } catch (err) {
+ wx.hideLoading();
+ wx.showToast({ title: '网络错误', icon: 'none' });
+ }
+ },
+
+ // 显示再次申请罚款弹窗(仅当状态为已驳回时可用)
+ showResubmitFakuan() {
+ if (this.data.fadanData?.zhuangtai !== 4) {
+ wx.showToast({ title: '当前状态不可再次申请', icon: 'none' });
+ return;
+ }
+ this.setData({
+ showResubmitFakuanModal: true,
+ resubmitFakuanLiyou: '',
+ resubmitFakuanJine: ''
+ });
+ },
+ closeResubmitFakuanModal() {
+ this.setData({ showResubmitFakuanModal: false });
+ },
+ inputResubmitFakuanLiyou(e) {
+ this.setData({ resubmitFakuanLiyou: e.detail.value.slice(0, 100) });
+ },
+ inputResubmitFakuanJine(e) {
+ this.setData({ resubmitFakuanJine: e.detail.value });
+ },
+ async submitResubmitFakuan() {
+ const { resubmitFakuanLiyou, resubmitFakuanJine, fadanData, jibenShuju } = this.data;
+ if (!resubmitFakuanLiyou.trim()) {
+ wx.showToast({ title: '请输入罚款理由', icon: 'none' }); return;
+ }
+ const jine = parseFloat(resubmitFakuanJine);
+ if (isNaN(jine) || jine <= 0) {
+ wx.showToast({ title: '请输入正确的罚款金额', icon: 'none' }); return;
+ }
+ wx.showLoading({ title: '提交中...', mask: true });
+ try {
+ const res = await request({
+ url: isStaffMode() ? STAFF_API.penaltyManage : '/dingdan/sjfkxgycx',
+ method: 'POST',
+ data: {
+ operation: 'resubmit_penalty',
+ fadan_id: fadanData.id,
+ dingdan_id: jibenShuju.dingdan_id,
+ liyou: resubmitFakuanLiyou,
+ jine: jine.toFixed(2)
+ }
+ });
+ wx.hideLoading();
+ if (res && res.data.code === 0) {
+ wx.showToast({ title: '申请成功', icon: 'success' });
+ this.setData({ showResubmitFakuanModal: false });
+ this.jiazaiXiangxiShuju(jibenShuju.dingdan_id);
+ } else {
+ wx.showToast({ title: res?.data?.msg || '申请失败', icon: 'none' });
+ }
+ } catch (err) {
+ wx.hideLoading();
+ wx.showToast({ title: '网络错误', icon: 'none' });
+ }
+ },
+
+ // ========== 新增:订单退款操作(撤销/修改理由) ==========
+ showCancelTuikuan() {
+ if (this.data.jibenShuju.zhuangtai !== 4) {
+ wx.showToast({ title: '只有退款中的订单可撤销', icon: 'none' });
+ return;
+ }
+ this.setData({ showCancelTuikuanModal: true, cancelTuikuanLiyou: '' });
+ },
+ closeCancelTuikuanModal() {
+ this.setData({ showCancelTuikuanModal: false });
+ },
+ inputCancelTuikuanLiyou(e) {
+ this.setData({ cancelTuikuanLiyou: e.detail.value.slice(0, 100) });
+ },
+ async submitCancelTuikuan() {
+ const { cancelTuikuanLiyou, jibenShuju } = this.data;
+ if (!cancelTuikuanLiyou.trim()) {
+ wx.showToast({ title: '请输入撤销理由', icon: 'none' }); return;
+ }
+ wx.showLoading({ title: '撤销中...', mask: true });
+ try {
+ const res = await request({
+ url: isStaffMode() ? STAFF_API.penaltyManage : '/dingdan/sjfkxgycx',
+ method: 'POST',
+ data: {
+ operation: 'cancel_refund',
+ dingdan_id: jibenShuju.dingdan_id,
+ liyou: cancelTuikuanLiyou
+ }
+ });
+ wx.hideLoading();
+ if (res && res.data.code === 0) {
+ wx.showToast({ title: '已撤销退款申请', icon: 'success' });
+ this.setData({ showCancelTuikuanModal: false });
+ this.jiazaiXiangxiShuju(jibenShuju.dingdan_id);
+ } else {
+ wx.showToast({ title: res?.data?.msg || '撤销失败', icon: 'none' });
+ }
+ } catch (err) {
+ wx.hideLoading();
+ wx.showToast({ title: '网络错误', icon: 'none' });
+ }
+ },
+
+ showModifyTuikuan() {
+ if (this.data.jibenShuju.zhuangtai !== 4) {
+ wx.showToast({ title: '只有退款中的订单可修改理由', icon: 'none' });
+ return;
+ }
+ this.setData({
+ showModifyTuikuanModal: true,
+ modifyTuikuanLiyou: this.data.xiangxiShuju.tuikuan_liyou || ''
+ });
+ },
+ closeModifyTuikuanModal() {
+ this.setData({ showModifyTuikuanModal: false });
+ },
+ inputModifyTuikuanLiyou(e) {
+ this.setData({ modifyTuikuanLiyou: e.detail.value.slice(0, 100) });
+ },
+ async submitModifyTuikuan() {
+ const { modifyTuikuanLiyou, jibenShuju } = this.data;
+ if (!modifyTuikuanLiyou.trim()) {
+ wx.showToast({ title: '请输入退款理由', icon: 'none' }); return;
+ }
+ wx.showLoading({ title: '修改中...', mask: true });
+ try {
+ const res = await request({
+ url: isStaffMode() ? STAFF_API.penaltyManage : '/dingdan/sjfkxgycx',
+ method: 'POST',
+ data: {
+ operation: 'modify_refund_reason',
+ dingdan_id: jibenShuju.dingdan_id,
+ liyou: modifyTuikuanLiyou
+ }
+ });
+ wx.hideLoading();
+ if (res && res.data.code === 0) {
+ wx.showToast({ title: '修改成功', icon: 'success' });
+ this.setData({ showModifyTuikuanModal: false });
+ this.jiazaiXiangxiShuju(jibenShuju.dingdan_id);
+ } else {
+ wx.showToast({ title: res?.data?.msg || '修改失败', icon: 'none' });
+ }
+ } catch (err) {
+ wx.hideLoading();
+ wx.showToast({ title: '网络错误', icon: 'none' });
+ }
+ },
+
+ // ========== 新增:教程按钮 ==========
+ startTutorialBtnTimer() {
+ if (this.tutorialTimer) clearTimeout(this.tutorialTimer);
+ this.tutorialTimer = setTimeout(() => {
+ this.setData({ tutorialBtnHidden: true });
+ }, 5000);
+ },
+ resetTutorialBtn() {
+ if (this.data.tutorialBtnHidden) {
+ this.setData({ tutorialBtnHidden: false });
+ }
+ if (this.tutorialTimer) clearTimeout(this.tutorialTimer);
+ this.tutorialTimer = setTimeout(() => {
+ this.setData({ tutorialBtnHidden: true });
+ }, 5000);
+ },
+ onTutorialBtnTap() {
+ this.resetTutorialBtn();
+ PopupService.checkAndShow(this, 'sjddxq1');
+ }
})
\ No newline at end of file
diff --git a/pages/merchant-order-detail/merchant-order-detail.wxml b/pages/merchant-order-detail/merchant-order-detail.wxml
index 5301cd6..c9b5e5e 100644
--- a/pages/merchant-order-detail/merchant-order-detail.wxml
+++ b/pages/merchant-order-detail/merchant-order-detail.wxml
@@ -1,354 +1,373 @@
-
-
-
-
-
-
-
-
- 加载中...
-
-
-
-
-
-
-
-
-
-
-
- {{jibenShuju.jieshao || '无描述'}}
-
-
-
-
-
- 订单信息
-
- 游戏昵称{{jibenShuju.nicheng}}
-
- 备注{{jibenShuju.beizhu}}
-
- 订单ID{{jibenShuju.dingdan_id}}📋
-
- 创建时间{{jibenShuju.create_time}}
-
- 订单金额¥{{jibenShuju.jine}}
-
- 状态{{zhuangtaiWenziMap[jibenShuju.zhuangtai]}}
-
-
- 撤销退款申请
- 修改退款理由
-
-
-
-
-
- 服务接单员
-
-
-
-
-
- 服务详情
-
-
- 交付图片
-
-
-
-
-
-
-
-
-
-
-
-
-
- 接单员留言
- {{xiangxiShuju.dashou_liuyan}}
-
-
-
- 退款理由
- {{xiangxiShuju.tuikuan_liyou}}
-
-
-
- 商家留言
- {{xiangxiShuju.shangjia_liuyan}}
-
-
-
-
-
- 罚款信息
-
- 罚款金额¥{{fadanData.fakuanjine || '0.00'}}
-
- 罚款理由{{fadanData.chufaliyou || '-'}}
-
- 是否影响抢单{{fadanData.yingxiang_qiangdan == 1 ? '是' : '否'}}
-
- 罚单状态{{fadanData.zhuangtai == 1 ? '待缴纳' : fadanData.zhuangtai == 2 ? '已缴纳' : fadanData.zhuangtai == 3 ? '申诉中' : '已驳回'}}
-
- 申诉理由{{fadanData.shensuliyou}}
-
- 同意处罚理由{{fadanData.tongyi_chufaliyou}}
-
- 驳回理由{{fadanData.bohuiliyou}}
-
- 创建时间{{fadanData.create_time}}
-
- 更新时间{{fadanData.update_time}}
-
-
-
-
- 修改罚款
- 撤销罚款
-
-
-
- 再次申请罚款
-
-
-
-
-
-
- 更换接单员成功
- 新订单ID{{xinDingdanId}}📋
-
-
-
-
- 订单结算
-
- 打星评分
-
-
-
-
- {{item.active ? '★' : '☆'}}
-
-
-
-
-
-
- 立即结算
-
-
-
-
-
-
-
- 联系客服
- 联系接单员
-
-
-
-
- 撤销订单
-
-
-
- 申请退款
-
-
-
-
-
-
-
- 撤销订单×
-
-
- 取消
- {{isChexiaoLoading ? '处理中' : '确认撤销'}}
-
-
-
-
-
-
-
-
- 申请退款×
-
-
- 取消
- {{isTuikuanLoading ? '处理中' : '确认申请'}}
-
-
-
-
-
-
-
-
- 更换接单员×
-
- 确认更换接单员吗?当前订单将被撤销,并生成一条相同内容的新订单(接单员清空)。
-
-
- 取消
- {{isGhDashouLoading ? '处理中' : '确认更换'}}
-
-
-
-
-
-
-
-
- 申请罚款×
-
- 罚款原因
-
- 罚款金额
-
-
- 是否影响抢单
-
-
-
-
- 此次罚款应得分红:¥{{jisuanFenhong()}}(利率 {{fenhongLilv*100}}%)
-
-
- 取消
- {{isFakuanLoading ? '处理中' : '确认申请'}}
-
-
-
-
-
-
-
-
- 确认结算×
-
- 确认结算该订单吗?结算后状态变为“已完成”。
-
-
- 取消
- {{isJiesuanLoading ? '处理中' : '确认结算'}}
-
-
-
-
-
-
-
-
- 修改罚款×
-
- 罚款理由
-
- 罚款金额
-
-
-
- 取消
- 确认修改
-
-
-
-
-
-
-
-
- 撤销罚款×
-
- 驳回理由
-
-
-
- 取消
- 确认撤销
-
-
-
-
-
-
-
-
- 再次申请罚款×
-
- 罚款理由
-
- 罚款金额
-
-
-
- 取消
- 确认申请
-
-
-
-
-
-
-
-
- 撤销退款申请×
-
- 撤销理由
-
-
-
- 取消
- 确认撤销
-
-
-
-
-
-
-
-
- 修改退款理由×
-
- 退款理由
-
-
-
- 取消
- 确认修改
-
-
-
-
-
-
-
-
-
- 教程
-
-
-
-
+
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+
+
+
+
+
+
+ {{jibenShuju.jieshao || '无描述'}}
+
+
+
+
+
+ 订单信息
+
+ 游戏昵称{{jibenShuju.nicheng}}
+
+ 备注{{jibenShuju.beizhu}}
+
+ 订单ID{{jibenShuju.dingdan_id}}📋
+
+ 创建时间{{jibenShuju.create_time}}
+
+ 订单金额¥{{jibenShuju.jine}}
+
+ 状态{{zhuangtaiWenziMap[jibenShuju.zhuangtai]}}
+
+
+ 派单客服
+
+
+
+
+ 撤销退款申请
+ 修改退款理由
+
+
+
+
+
+ 服务接单员
+
+
+
+
+
+ 服务详情
+
+
+ 交付图片
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 接单员留言
+ {{xiangxiShuju.dashou_liuyan}}
+
+
+
+ 退款理由
+ {{xiangxiShuju.tuikuan_liyou}}
+
+
+
+ 商家留言
+ {{xiangxiShuju.shangjia_liuyan}}
+
+
+
+
+
+ 罚款信息
+
+ 罚款金额¥{{fadanData.fakuanjine || '0.00'}}
+
+ 罚款理由{{fadanData.chufaliyou || '-'}}
+
+ 是否影响抢单{{fadanData.yingxiang_qiangdan == 1 ? '是' : '否'}}
+
+ 罚单状态{{fadanData.zhuangtai == 1 ? '待缴纳' : fadanData.zhuangtai == 2 ? '已缴纳' : fadanData.zhuangtai == 3 ? '申诉中' : '已驳回'}}
+
+ 申诉理由{{fadanData.shensuliyou}}
+
+ 同意处罚理由{{fadanData.tongyi_chufaliyou}}
+
+ 驳回理由{{fadanData.bohuiliyou}}
+
+ 创建时间{{fadanData.create_time}}
+
+ 更新时间{{fadanData.update_time}}
+
+
+
+
+ 修改罚款
+ 撤销罚款
+
+
+
+ 再次申请罚款
+
+
+
+
+
+
+ 更换接单员成功
+ 新订单ID{{xinDingdanId}}📋
+
+
+
+
+ 订单结算
+
+ 打星评分
+
+
+
+
+ {{item.active ? '★' : '☆'}}
+
+
+
+
+
+
+ 立即结算
+
+
+
+
+
+
+
+ 联系客服
+ 联系接单员
+
+
+
+
+ 撤销订单
+
+
+
+ 申请退款
+
+
+
+
+
+
+
+ 撤销订单×
+
+
+ 取消
+ {{isChexiaoLoading ? '处理中' : '确认撤销'}}
+
+
+
+
+
+
+
+
+ 申请退款×
+
+
+ 取消
+ {{isTuikuanLoading ? '处理中' : '确认申请'}}
+
+
+
+
+
+
+
+
+ 更换接单员×
+
+ 确认更换接单员吗?当前订单将被撤销,并生成一条相同内容的新订单(接单员清空)。
+
+
+ 取消
+ {{isGhDashouLoading ? '处理中' : '确认更换'}}
+
+
+
+
+
+
+
+
+ 申请罚款×
+
+ 罚款原因
+
+ 罚款金额
+
+
+ 是否影响抢单
+
+
+
+
+ 此次罚款应得分红:¥{{jisuanFenhong()}}(利率 {{fenhongLilv*100}}%)
+
+
+ 取消
+ {{isFakuanLoading ? '处理中' : '确认申请'}}
+
+
+
+
+
+
+
+
+ 确认结算×
+
+ 确认结算该订单吗?结算后状态变为“已完成”。
+
+
+ 取消
+ {{isJiesuanLoading ? '处理中' : '确认结算'}}
+
+
+
+
+
+
+
+
+ 修改罚款×
+
+ 罚款理由
+
+ 罚款金额
+
+
+
+ 取消
+ 确认修改
+
+
+
+
+
+
+
+
+ 撤销罚款×
+
+ 驳回理由
+
+
+
+ 取消
+ 确认撤销
+
+
+
+
+
+
+
+
+ 再次申请罚款×
+
+ 罚款理由
+
+ 罚款金额
+
+
+
+ 取消
+ 确认申请
+
+
+
+
+
+
+
+
+ 撤销退款申请×
+
+ 撤销理由
+
+
+
+ 取消
+ 确认撤销
+
+
+
+
+
+
+
+
+ 修改退款理由×
+
+ 退款理由
+
+
+
+ 取消
+ 确认修改
+
+
+
+
+
+
+
+
+
+ 教程
+
+
+
+
\ No newline at end of file
diff --git a/pages/merchant-orders/merchant-orders.js b/pages/merchant-orders/merchant-orders.js
index 8ae4cc6..c1ec171 100644
--- a/pages/merchant-orders/merchant-orders.js
+++ b/pages/merchant-orders/merchant-orders.js
@@ -3,6 +3,8 @@ 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';
Page(createPage({
data: {
@@ -49,12 +51,19 @@ Page(createPage({
},
onLoad() {
- wx.setNavigationBarTitle({ title: '我的派单' });
+ wx.setNavigationBarTitle({ title: isStaffMode() ? '客服派单' : '我的派单' });
+ syncStaffUi(this);
+ if (isStaffMode()) {
+ refreshStaffContext(request).then(() => syncStaffUi(this));
+ }
this.loadShangpinLeixing();
this.registerNotificationComponent();
},
- onShow() {
+ async onShow() {
+ const phoneOk = await ensurePhoneAuth({ redirect: true, role: 'shangjia' });
+ if (!phoneOk) return;
+
this.registerNotificationComponent();
this.loadPendingCount();
if (wx.getStorageSync('uid')) {
@@ -169,7 +178,7 @@ Page(createPage({
};
const res = await request({
- url: '/dingdan/sjdingdanhq', // 商家订单接口(和原来一样)
+ url: isStaffMode() ? STAFF_API.orderList : '/dingdan/sjdingdanhq',
method: 'POST',
data: params,
header: { 'content-type': 'application/json' }
@@ -247,7 +256,7 @@ Page(createPage({
async loadPendingCount() {
try {
const res = await request({
- url: '/dingdan/sjdingdanhq',
+ url: isStaffMode() ? STAFF_API.orderList : '/dingdan/sjdingdanhq',
method: 'POST',
data: {
zhuangtai_list: [8],
diff --git a/pages/merchant-recharge/merchant-recharge.js b/pages/merchant-recharge/merchant-recharge.js
index 889cfee..518da2f 100644
--- a/pages/merchant-recharge/merchant-recharge.js
+++ b/pages/merchant-recharge/merchant-recharge.js
@@ -1,5 +1,6 @@
// pages/shangjia-chongzhi/index.js
import request from '../../utils/request';
+import { guardMerchantOwnerAction } from '../../utils/staff-api.js';
Page({
data: {
@@ -37,6 +38,10 @@ Page({
},
onLoad() {
+ if (!guardMerchantOwnerAction('仅商家老板可充值')) {
+ setTimeout(() => wx.navigateBack(), 1200);
+ return;
+ }
this.initPage();
},
diff --git a/pages/merchant-staff-audit/merchant-staff-audit.js b/pages/merchant-staff-audit/merchant-staff-audit.js
new file mode 100644
index 0000000..9b665d3
--- /dev/null
+++ b/pages/merchant-staff-audit/merchant-staff-audit.js
@@ -0,0 +1,99 @@
+import request from '../../utils/request.js';
+import { STAFF_API } from '../../utils/staff-api.js';
+
+const ORDER_AUDIT_ACTIONS = [
+ 'DISPATCH', 'LINK_GENERATE', 'SETTLE', 'CANCEL', 'REFUND_APPLY',
+ 'CHANGE_PLAYER', 'PENALTY_APPLY', 'PENALTY_MANAGE',
+];
+
+Page({
+ data: {
+ tab: 'audit',
+ auditList: [],
+ rankList: [],
+ page: 1,
+ hasMore: true,
+ loading: false,
+ total: 0,
+ },
+
+ onLoad(options) {
+ if (options.tab === 'rank') this.setData({ tab: 'rank' });
+ wx.setNavigationBarTitle({
+ title: options.tab === 'rank' ? '客服排行' : '操作日志',
+ });
+ this.loadData(true);
+ },
+
+ onPullDownRefresh() {
+ this.loadData(true).finally(() => wx.stopPullDownRefresh());
+ },
+
+ onReachBottom() {
+ if (this.data.tab === 'audit' && this.data.hasMore && !this.data.loading) {
+ this.loadAudit(false);
+ }
+ },
+
+ switchTab(e) {
+ const tab = e.currentTarget.dataset.tab;
+ wx.setNavigationBarTitle({ title: tab === 'rank' ? '客服排行' : '操作日志' });
+ this.setData({ tab, page: 1, hasMore: true }, () => this.loadData(true));
+ },
+
+ loadData(reset) {
+ if (this.data.tab === 'audit') return this.loadAudit(reset);
+ return this.loadRank();
+ },
+
+ async loadAudit(reset) {
+ if (this.data.loading) return;
+ const page = reset ? 1 : this.data.page;
+ this.setData({ loading: true });
+ try {
+ const res = await request({
+ url: STAFF_API.auditList,
+ method: 'POST',
+ data: { page, page_size: 20 },
+ });
+ if (res.data && res.data.code === 0) {
+ const d = res.data.data || {};
+ const list = (d.list || []).map((item) => ({
+ ...item,
+ canJumpOrder: ORDER_AUDIT_ACTIONS.indexOf(item.action) >= 0 && !!item.resource_id,
+ }));
+ this.setData({
+ auditList: reset ? list : [...this.data.auditList, ...list],
+ page: page + 1,
+ hasMore: !!d.has_more,
+ total: d.total || 0,
+ loading: false,
+ });
+ } else {
+ this.setData({ loading: false });
+ wx.showToast({ title: res.data?.msg || '加载失败', icon: 'none' });
+ }
+ } catch (e) {
+ this.setData({ loading: false });
+ }
+ },
+
+ async loadRank() {
+ const res = await request({
+ url: STAFF_API.rank,
+ method: 'POST',
+ data: { period: 'month', sort_by: 'dispatch_count' },
+ });
+ if (res.data && res.data.code === 0) {
+ this.setData({ rankList: (res.data.data && res.data.data.list) || [] });
+ }
+ },
+
+ goOrderDetail(e) {
+ const item = e.currentTarget.dataset.item;
+ if (!item || !item.canJumpOrder || !item.resource_id) return;
+ wx.navigateTo({
+ url: `/pages/merchant-order-detail/merchant-order-detail?dingdan_id=${encodeURIComponent(item.resource_id)}`,
+ });
+ },
+});
diff --git a/pages/merchant-staff-audit/merchant-staff-audit.json b/pages/merchant-staff-audit/merchant-staff-audit.json
new file mode 100644
index 0000000..97f600a
--- /dev/null
+++ b/pages/merchant-staff-audit/merchant-staff-audit.json
@@ -0,0 +1,5 @@
+{
+ "navigationBarTitleText": "操作日志",
+ "enablePullDownRefresh": true,
+ "usingComponents": {}
+}
\ No newline at end of file
diff --git a/pages/merchant-staff-audit/merchant-staff-audit.wxml b/pages/merchant-staff-audit/merchant-staff-audit.wxml
new file mode 100644
index 0000000..ecc1393
--- /dev/null
+++ b/pages/merchant-staff-audit/merchant-staff-audit.wxml
@@ -0,0 +1,39 @@
+
+ 仅记录派单、退款、模板等重要操作,不含普通查看
+
+ 操作日志
+ 客服排行
+
+
+ 共 {{total}} 条重要操作
+
+
+ {{item.action_label || item.action}}
+ ¥{{item.amount}}
+
+
+
+ {{item.role_name || (item.operator_type === 'STAFF' ? '客服' : '老板')}}
+
+ {{item.operator_name}}
+ ID:{{item.operator_user_id}}
+
+ {{item.time}}
+ 订单:{{item.resource_id}}
+ 点击查看订单详情 ›
+
+
+ 加载中...
+ 上拉加载更多
+ 暂无操作日志
+
+
+
+ #{{item.rank}} {{item.display_name}}
+ 派单 {{item.dispatch_count}} 单 · ¥{{item.dispatch_amount}}
+ 结单 {{item.settle_count}} 单 · ¥{{item.settle_amount}}
+
+ 暂无排行数据
+
+
diff --git a/pages/merchant-staff-audit/merchant-staff-audit.wxss b/pages/merchant-staff-audit/merchant-staff-audit.wxss
new file mode 100644
index 0000000..3edb7b4
--- /dev/null
+++ b/pages/merchant-staff-audit/merchant-staff-audit.wxss
@@ -0,0 +1,25 @@
+.page { padding: 24rpx; background: #f5f5f5; min-height: 100vh; box-sizing: border-box; }
+.head-tip { font-size: 24rpx; color: #888; margin-bottom: 16rpx; line-height: 1.5; }
+.tabs { display: flex; margin-bottom: 24rpx; }
+.tab { flex: 1; text-align: center; padding: 20rpx; background: #fff; margin-right: 8rpx; border-radius: 8rpx; font-size: 28rpx; }
+.tab.on { background: #C9A962; color: #fff; }
+.summary { font-size: 24rpx; color: #666; margin-bottom: 16rpx; }
+.item { background: #fff; padding: 24rpx; margin-bottom: 16rpx; border-radius: 12rpx; }
+.item-hd { margin-bottom: 12rpx; }
+.act { font-size: 30rpx; color: #333; font-weight: 600; }
+.amount { font-size: 28rpx; color: #C9A962; }
+.operator { display: flex; align-items: center; flex-wrap: wrap; gap: 12rpx; margin-bottom: 8rpx; }
+.tag { font-size: 22rpx; padding: 4rpx 12rpx; border-radius: 6rpx; }
+.tag-staff { background: #e8f4ff; color: #1976d2; }
+.tag-owner { background: #fff3e0; color: #e65100; }
+.name { font-size: 26rpx; color: #333; }
+.uid { font-size: 22rpx; color: #999; }
+.meta { display: block; font-size: 24rpx; color: #999; margin-top: 6rpx; }
+.res { display: block; font-size: 24rpx; color: #666; margin-top: 8rpx; word-break: break-all; }
+.remark { display: block; font-size: 24rpx; color: #888; margin-top: 6rpx; }
+.rank { font-weight: 600; font-size: 28rpx; display: block; }
+.load-tip { text-align: center; color: #999; padding: 24rpx; font-size: 24rpx; }
+.empty { text-align: center; color: #999; padding: 80rpx; }
+.item-link { border-left: 6rpx solid #C9A962; }
+.jump-tip { display: block; font-size: 24rpx; color: #C9A962; margin-top: 8rpx; }
+.flexb { display: flex; justify-content: space-between; align-items: center; }
diff --git a/pages/merchant-staff-role/merchant-staff-role.js b/pages/merchant-staff-role/merchant-staff-role.js
new file mode 100644
index 0000000..9ba3950
--- /dev/null
+++ b/pages/merchant-staff-role/merchant-staff-role.js
@@ -0,0 +1,236 @@
+/** 商家老板:角色与权限管理 */
+import request from '../../utils/request.js';
+import { STAFF_API, isStaffMode, isMerchantOwnerOnly } from '../../utils/staff-api.js';
+
+/** 与后端 constants.PERMISSION_DEFINITIONS 一致,接口为空时前端兜底 */
+const FALLBACK_PERMISSIONS = [
+ { perm_code: 'staff_manage', perm_name: '子客服管理' },
+ { perm_code: 'role_manage', perm_name: '角色管理' },
+ { perm_code: 'wallet_allocate', perm_name: '划额度' },
+ { perm_code: 'wallet_revoke', perm_name: '收回额度' },
+ { perm_code: 'wallet_view', perm_name: '查看额度' },
+ { perm_code: 'finance_view', perm_name: '查看商家余额' },
+ { perm_code: 'order_dispatch', perm_name: '派单发单' },
+ { perm_code: 'order_view_all', perm_name: '查看全部订单' },
+ { perm_code: 'order_view_self', perm_name: '只看自己派的单' },
+ { perm_code: 'order_detail', perm_name: '订单详情' },
+ { perm_code: 'order_settle', perm_name: '结单' },
+ { perm_code: 'order_refund_apply', perm_name: '申请退款' },
+ { perm_code: 'order_cancel', perm_name: '撤销订单' },
+ { perm_code: 'order_change_player', perm_name: '换打手' },
+ { perm_code: 'penalty_apply', perm_name: '申请罚单' },
+ { perm_code: 'penalty_manage', perm_name: '改撤罚单' },
+ { perm_code: 'penalty_view', perm_name: '查看罚单' },
+ { perm_code: 'staff_disable', perm_name: '封禁解封客服' },
+ { perm_code: 'template_manage', perm_name: '派单模板' },
+ { perm_code: 'audit_view', perm_name: '操作日志' },
+ { perm_code: 'rank_view', perm_name: '客服排行榜' },
+ { perm_code: 'im_chat', perm_name: '订单IM' },
+ { perm_code: 'stats_view', perm_name: '统计数据' },
+];
+
+Page({
+ data: {
+ statusBar: 20,
+ permScrollHeight: 360,
+ loading: false,
+ roles: [],
+ rawPermissions: [],
+ permissionOptions: [],
+ selectedPerms: [],
+ showEditPanel: false,
+ showCreatePanel: false,
+ editingRoleId: null,
+ editingRoleName: '',
+ newRoleName: '',
+ },
+
+ onLoad() {
+ const sys = wx.getSystemInfoSync();
+ const h = sys.windowHeight || 667;
+ this.setData({
+ statusBar: sys.statusBarHeight || 20,
+ permScrollHeight: Math.floor(h * 0.45),
+ });
+ if (!this.ensureOwnerAccess()) return;
+ this.loadRoles();
+ },
+
+ onShow() {
+ if (!isMerchantOwnerOnly()) return;
+ if (!this.data.showEditPanel && !this.data.showCreatePanel) {
+ this.loadRoles();
+ }
+ },
+
+ ensureOwnerAccess() {
+ if (isStaffMode() || Number(wx.getStorageSync('shangjiastatus')) !== 1) {
+ wx.showToast({ title: '仅商家老板可管理角色权限', icon: 'none' });
+ setTimeout(() => wx.navigateBack(), 1200);
+ return false;
+ }
+ return true;
+ },
+
+ /** allPerms: [{perm_code, perm_name}] selectedCodes: ['order_dispatch', ...] */
+ buildPermOptions(allPerms, selectedCodes) {
+ const list = (allPerms && allPerms.length) ? allPerms : FALLBACK_PERMISSIONS;
+ const set = new Set(selectedCodes || []);
+ return list.map((p) => {
+ const code = p.perm_code || p;
+ const name = p.perm_name || code;
+ return {
+ perm_code: code,
+ perm_name: name,
+ checked: set.has(code),
+ };
+ });
+ },
+
+ normalizeRoles(list) {
+ return (list || []).map((r) => ({
+ ...r,
+ permCount: (r.permissions && r.permissions.length) || 0,
+ }));
+ },
+
+ resolveAllPermissions(apiList) {
+ if (apiList && apiList.length) return apiList;
+ return FALLBACK_PERMISSIONS;
+ },
+
+ async loadRoles() {
+ this.setData({ loading: true });
+ try {
+ const res = await request({ url: STAFF_API.roleList, method: 'POST' });
+ if (res.data && res.data.code === 0) {
+ const d = res.data.data || {};
+ const rawPermissions = this.resolveAllPermissions(d.all_permissions);
+ this.setData({
+ roles: this.normalizeRoles(d.roles),
+ rawPermissions,
+ });
+ return rawPermissions;
+ }
+ wx.showToast({ title: (res.data && res.data.msg) || '加载失败', icon: 'none' });
+ } catch (e) {
+ wx.showToast({ title: '加载失败', icon: 'none' });
+ } finally {
+ this.setData({ loading: false });
+ }
+ const fallback = FALLBACK_PERMISSIONS;
+ this.setData({ rawPermissions: fallback });
+ return fallback;
+ },
+
+ goBack() { wx.navigateBack(); },
+
+ openCreate() {
+ const raw = this.data.rawPermissions.length
+ ? this.data.rawPermissions
+ : FALLBACK_PERMISSIONS;
+ this.setData({
+ showCreatePanel: true,
+ showEditPanel: false,
+ newRoleName: '',
+ selectedPerms: [],
+ permissionOptions: this.buildPermOptions(raw, []),
+ });
+ },
+
+ closeCreate() {
+ this.setData({ showCreatePanel: false, permissionOptions: [], selectedPerms: [] });
+ },
+
+ onNameInput(e) {
+ this.setData({ newRoleName: e.detail.value });
+ },
+
+ togglePermItem(e) {
+ const code = e.currentTarget.dataset.code;
+ if (!code) return;
+ const opts = (this.data.permissionOptions || []).map((p) => (
+ p.perm_code === code ? { ...p, checked: !p.checked } : p
+ ));
+ this.setData({
+ permissionOptions: opts,
+ selectedPerms: opts.filter((x) => x.checked).map((x) => x.perm_code),
+ });
+ },
+
+ async editRole(e) {
+ const id = e.currentTarget.dataset.id;
+ let role = this.data.roles.find((r) => String(r.id) === String(id));
+ if (!role) return;
+
+ let rawPermissions = this.data.rawPermissions;
+ if (!rawPermissions.length) {
+ rawPermissions = await this.loadRoles();
+ role = this.data.roles.find((r) => String(r.id) === String(id));
+ if (!role) return;
+ }
+
+ const perms = role.permissions || [];
+ this.setData({
+ showEditPanel: true,
+ showCreatePanel: false,
+ editingRoleId: role.id,
+ editingRoleName: role.role_name || '',
+ selectedPerms: [...perms],
+ permissionOptions: this.buildPermOptions(rawPermissions, perms),
+ });
+ },
+
+ closeEdit() {
+ this.setData({
+ showEditPanel: false,
+ editingRoleId: null,
+ editingRoleName: '',
+ selectedPerms: [],
+ permissionOptions: [],
+ });
+ },
+
+ async saveRolePerms() {
+ const { editingRoleId, selectedPerms } = this.data;
+ if (!editingRoleId) return;
+ wx.showLoading({ title: '保存中' });
+ try {
+ const res = await request({
+ url: STAFF_API.roleSetPerms,
+ method: 'POST',
+ data: { role_id: editingRoleId, permissions: selectedPerms },
+ });
+ wx.showToast({ title: (res.data && res.data.msg) || '已保存', icon: 'none' });
+ if (res.data && res.data.code === 0) {
+ this.closeEdit();
+ this.loadRoles();
+ }
+ } finally {
+ wx.hideLoading();
+ }
+ },
+
+ async createRole() {
+ const name = (this.data.newRoleName || '').trim();
+ if (!name) {
+ wx.showToast({ title: '请输入角色名', icon: 'none' });
+ return;
+ }
+ wx.showLoading({ title: '创建中' });
+ try {
+ const res = await request({
+ url: STAFF_API.roleCreate,
+ method: 'POST',
+ data: { role_name: name, permissions: this.data.selectedPerms },
+ });
+ wx.showToast({ title: (res.data && res.data.msg) || '完成', icon: 'none' });
+ if (res.data && res.data.code === 0) {
+ this.closeCreate();
+ this.loadRoles();
+ }
+ } finally {
+ wx.hideLoading();
+ }
+ },
+});
diff --git a/pages/merchant-staff-role/merchant-staff-role.json b/pages/merchant-staff-role/merchant-staff-role.json
new file mode 100644
index 0000000..5089993
--- /dev/null
+++ b/pages/merchant-staff-role/merchant-staff-role.json
@@ -0,0 +1,4 @@
+{
+ "navigationBarTitleText": "角色与权限",
+ "navigationStyle": "custom"
+}
diff --git a/pages/merchant-staff-role/merchant-staff-role.wxml b/pages/merchant-staff-role/merchant-staff-role.wxml
new file mode 100644
index 0000000..e79c771
--- /dev/null
+++ b/pages/merchant-staff-role/merchant-staff-role.wxml
@@ -0,0 +1,75 @@
+
+
+
+ ‹
+ 角色与权限
+ 新建
+
+
+
+ 可自由创建角色并勾选权限;预置角色也可改权限。
+ 暂无角色,请先新建或联系管理员同步权限
+
+
+ {{item.role_name}}
+ {{item.is_system ? '系统' : '自定义'}}
+
+ {{item.role_hint || ''}}
+ 已授权 {{item.permCount}} 项 · 点击编辑
+
+
+
+
+
+
+
+ 编辑角色权限
+ {{editingRoleName}}
+
+
+
+ {{item.perm_name}}
+ {{item.perm_code}}
+
+ {{item.checked ? '✓' : ''}}
+
+
+
+
+
+
+
+
+
+
+
+
+ 新建角色
+
+
+
+
+ {{item.perm_name}}
+ {{item.perm_code}}
+
+ {{item.checked ? '✓' : ''}}
+
+
+
+
+
+
+
+
diff --git a/pages/merchant-staff-role/merchant-staff-role.wxss b/pages/merchant-staff-role/merchant-staff-role.wxss
new file mode 100644
index 0000000..b606080
--- /dev/null
+++ b/pages/merchant-staff-role/merchant-staff-role.wxss
@@ -0,0 +1,221 @@
+@import '../../styles/shangjia-xym-common.wxss';
+
+.role-page {
+ min-height: 100vh;
+ background: linear-gradient(180deg, #f7dc51 0%, #fff 28%, #fff8e1 100%);
+ box-sizing: border-box;
+}
+
+.role-nav {
+ padding: 0 24rpx 16rpx;
+}
+
+.nav-back {
+ width: 60rpx;
+ font-size: 48rpx;
+ color: #333;
+}
+
+.nav-title {
+ font-size: 34rpx;
+ font-weight: 700;
+ color: #343434;
+}
+
+.nav-link {
+ width: 60rpx;
+ text-align: right;
+ font-size: 28rpx;
+ color: #c9a962;
+}
+
+.role-scroll {
+ height: calc(100vh - 120rpx);
+}
+
+.role-tip {
+ font-size: 24rpx;
+ color: #888;
+ margin: 20rpx 24rpx;
+}
+
+.role-empty {
+ text-align: center;
+ padding: 80rpx 24rpx;
+ font-size: 26rpx;
+ color: #999;
+}
+
+.role-card {
+ background: #fff;
+ border-radius: 16rpx;
+ padding: 24rpx;
+ margin: 0 24rpx 20rpx;
+ box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
+}
+
+.role-name {
+ font-size: 30rpx;
+ font-weight: 600;
+ color: #333;
+}
+
+.role-tag {
+ font-size: 22rpx;
+ color: #999;
+}
+
+.role-sub {
+ display: block;
+ margin-top: 8rpx;
+ font-size: 24rpx;
+ color: #666;
+}
+
+/* 弹层:独立根节点,全屏 fixed */
+.role-mask {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 1000;
+}
+
+.role-mask-bg {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.5);
+}
+
+.role-panel {
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: #fff;
+ border-radius: 24rpx 24rpx 0 0;
+ padding: 32rpx 24rpx calc(24rpx + env(safe-area-inset-bottom));
+ z-index: 1001;
+ max-height: 85vh;
+ box-sizing: border-box;
+}
+
+.panel-title {
+ font-size: 32rpx;
+ font-weight: 600;
+ color: #333;
+}
+
+.panel-role-name {
+ display: block;
+ margin-top: 8rpx;
+ margin-bottom: 16rpx;
+ font-size: 26rpx;
+ color: #888;
+}
+
+.perm-scroll {
+ width: 100%;
+}
+
+.perm-row {
+ padding: 20rpx 8rpx;
+ border-bottom: 1rpx solid #f0f0f0;
+ min-height: 88rpx;
+ box-sizing: border-box;
+}
+
+.perm-row.perm-on {
+ background: #fffbf0;
+}
+
+.perm-text {
+ flex: 1;
+ padding-right: 16rpx;
+}
+
+.perm-name {
+ display: block;
+ font-size: 28rpx;
+ color: #333;
+ line-height: 1.4;
+}
+
+.perm-code {
+ display: block;
+ margin-top: 4rpx;
+ font-size: 22rpx;
+ color: #999;
+}
+
+.perm-check {
+ width: 48rpx;
+ height: 48rpx;
+ line-height: 48rpx;
+ text-align: center;
+ font-size: 32rpx;
+ color: #c9a962;
+ font-weight: 700;
+ border: 2rpx solid #ddd;
+ border-radius: 8rpx;
+ flex-shrink: 0;
+}
+
+.perm-on .perm-check {
+ border-color: #c9a962;
+ background: #c9a962;
+ color: #fff;
+}
+
+.perm-empty {
+ padding: 40rpx 0;
+ text-align: center;
+ font-size: 26rpx;
+ color: #999;
+}
+
+.name-input {
+ border: 1rpx solid #eee;
+ padding: 20rpx;
+ margin: 16rpx 0;
+ border-radius: 8rpx;
+ font-size: 28rpx;
+}
+
+.panel-btns {
+ margin-top: 24rpx;
+ gap: 16rpx;
+}
+
+.panel-btn {
+ flex: 1;
+ margin: 0;
+ padding: 0;
+ height: 88rpx;
+ line-height: 88rpx;
+ font-size: 30rpx;
+ border-radius: 12rpx;
+ border: none;
+}
+
+.panel-btn::after {
+ border: none;
+}
+
+.panel-btn.cancel {
+ background: #f5f5f5;
+ color: #666;
+}
+
+.panel-btn.ok {
+ background: #c9a962;
+ color: #fff;
+}
+
+.btn-hover {
+ opacity: 0.85;
+}
diff --git a/pages/merchant/merchant.js b/pages/merchant/merchant.js
index 2d5a1bb..5f291e3 100644
--- a/pages/merchant/merchant.js
+++ b/pages/merchant/merchant.js
@@ -2,6 +2,11 @@
import { createPage, ensureRoleOnCenterPage, lockPrimaryRole, parseSceneOptions, request } from '../../utils/base-page.js';
import { ensureLogin } from '../../utils/login.js';
+import {
+ STAFF_API, isStaffMode, isMerchantPortalUser, refreshStaffContext, syncStaffUi, getStaffContext,
+ restoreStaffContextAfterAuth, applyStaffStatsToPage, guardMerchantOwnerAction, clearStaffSession,
+} from '../../utils/staff-api.js';
+import { clearCacheAndEnterNormal } from '../../utils/base-page.js';
const app = getApp();
@@ -32,7 +37,15 @@ Page(createPage({
// 商家状态
isShangjia: false,
+ isStaffMode: false,
+ staffRoleName: '',
+ quotaAvailable: '0.00',
+ canDispatch: true,
+ canStaffManage: true,
+ canAudit: true,
+ showRechargeWithdraw: true,
isAutoRegistering: false,
+ staffBooting: false,
// 用户基础信息
uid: '',
@@ -57,7 +70,7 @@ Page(createPage({
navBar: 44,
},
- onLoad(options) {
+ async onLoad(options) {
const sys = wx.getSystemInfoSync();
this.setData({
statusBar: sys.statusBarHeight || 20,
@@ -65,6 +78,9 @@ Page(createPage({
});
const parsed = parseSceneOptions(options);
this.setupImageUrls();
+ if (wx.getStorageSync('token')) {
+ await restoreStaffContextAfterAuth();
+ }
this.checkRoleStatus();
const inviteCode = parsed.inviteCode;
@@ -97,13 +113,33 @@ Page(createPage({
this.checkColdStartPopup('shangjiaduan');
},
- onShow() {
+ async onShow() {
this.registerNotificationComponent();
- if (this.data.isShangjia) {
- ensureRoleOnCenterPage(this, 'shangjia');
- this.refreshShangjiaInfo();
- this.loadPendingCount();
- this.loadPunishPending();
+ try {
+ const token = wx.getStorageSync('token');
+ const isOwner = Number(wx.getStorageSync('shangjiastatus')) === 1;
+ if (token && !isOwner) {
+ await restoreStaffContextAfterAuth();
+ } else if (isStaffMode()) {
+ await refreshStaffContext(request);
+ }
+ this.checkRoleStatus();
+ syncStaffUi(this);
+ if (this.data.isShangjia) {
+ ensureRoleOnCenterPage(this, 'shangjia');
+ if (isStaffMode()) {
+ await this.loadStaffProfile();
+ } else {
+ this.refreshShangjiaInfo();
+ }
+ this.loadPendingCount();
+ if (!isStaffMode()) {
+ this.loadPunishPending();
+ }
+ }
+ } catch (err) {
+ console.error('商家个人中心 onShow', err);
+ this.setData({ isLoading: false, staffBooting: false });
}
},
@@ -137,27 +173,102 @@ Page(createPage({
});
},
- // 商家状态检查(覆盖基类 checkRoleStatus,增加 getShangjiaInfo 调用)
+ // 商家状态检查(子客服与老板均可进入;子客服绝不走商家注册接口)
checkRoleStatus() {
try {
- const shangjiastatus = wx.getStorageSync('shangjiastatus');
const uid = wx.getStorageSync('uid');
- if (shangjiastatus) {
- this.setData({ isShangjia: true, uid: uid || '' });
- this.loadAvatar();
- this.getShangjiaInfo();
+ const portal = isMerchantPortalUser();
+ const staffFlag = Number(wx.getStorageSync('staffstatus')) === 1;
+
+ if (portal) {
+ this.setData({ isShangjia: true, uid: uid || '', staffBooting: false, isLoading: false });
+ syncStaffUi(this);
+ if (isStaffMode()) {
+ this.loadStaffProfile();
+ } else if (Number(wx.getStorageSync('shangjiastatus')) === 1) {
+ this.loadAvatar();
+ this.getShangjiaInfo();
+ } else if (staffFlag) {
+ this.bootStaffPortal(uid);
+ }
ensureRoleOnCenterPage(this, 'shangjia');
- } else {
- this.setData({ isShangjia: false });
+ return;
}
+
+ if (staffFlag) {
+ this.bootStaffPortal(uid);
+ return;
+ }
+
+ this.setData({ isShangjia: false, staffBooting: false, isLoading: false });
+ syncStaffUi(this);
} catch (error) {
console.error('读取商家状态失败', error);
- this.setData({ isShangjia: false });
+ this.setData({ isShangjia: false, staffBooting: false, isLoading: false });
+ syncStaffUi(this);
}
},
- // 获取商家信息
+ /** 已绑定客服、尚未拉到 context 时恢复身份(不展示商家邀请码) */
+ async bootStaffPortal(uid) {
+ this.setData({ staffBooting: true, isLoading: true, uid: uid || '' });
+ syncStaffUi(this);
+ await restoreStaffContextAfterAuth();
+ if (isMerchantPortalUser()) {
+ this.setData({ isShangjia: true, staffBooting: false, isLoading: false });
+ syncStaffUi(this);
+ if (isStaffMode()) {
+ await this.loadStaffProfile();
+ }
+ ensureRoleOnCenterPage(this, 'shangjia');
+ return;
+ }
+ this.setData({ staffBooting: false, isLoading: false, isShangjia: false });
+ syncStaffUi(this);
+ wx.showToast({ title: '客服身份已失效,请联系商家老板', icon: 'none' });
+ },
+
+ onInviteCodeInput(e) {
+ this.setData({ inviteCode: (e.detail.value || '').trim() });
+ },
+
+ goToStaffJoin() {
+ wx.navigateTo({ url: '/pages/staff-join/staff-join' });
+ },
+
+ clearCache() {
+ wx.showModal({
+ title: '清除缓存',
+ content: '将清除所有登录与身份数据,并回到点单端,确定继续?',
+ confirmText: '确定',
+ cancelText: '取消',
+ success: (r) => {
+ if (!r.confirm) return;
+ clearStaffSession();
+ clearCacheAndEnterNormal(this);
+ },
+ });
+ },
+
+ async loadStaffProfile() {
+ const ctx = await refreshStaffContext(request);
+ const profile = wx.getStorageSync('nicheng') || (ctx && ctx.display_name) || '商家客服';
+ this.loadAvatar();
+ this.setData({
+ nicheng: (ctx && ctx.display_name) || profile,
+ sjyue: (ctx && ctx.wallet && ctx.wallet.quota_available) || '0.00',
+ chenghaoList: [],
+ isLoading: false,
+ });
+ syncStaffUi(this);
+ applyStaffStatsToPage(this, ctx);
+ },
+
+ // 获取商家信息(仅商家老板;子客服禁止调用)
async getShangjiaInfo() {
+ if (isStaffMode() || Number(wx.getStorageSync('staffstatus')) === 1) {
+ return this.loadStaffProfile();
+ }
await this.loadData({
url: '/yonghu/shangjiaxinxi',
method: 'POST',
@@ -192,13 +303,17 @@ Page(createPage({
},
refreshShangjiaInfo() {
+ if (isStaffMode()) {
+ this.throttledRefresh(() => this.loadStaffProfile());
+ return;
+ }
this.throttledRefresh(() => this.getShangjiaInfo());
},
async loadPendingCount() {
try {
const res = await request({
- url: '/dingdan/sjdingdanhq',
+ url: isStaffMode() ? STAFF_API.orderList : '/dingdan/sjdingdanhq',
method: 'POST',
data: { zhuangtai_list: [8], page: 1, page_size: 1 },
});
@@ -273,8 +388,16 @@ Page(createPage({
goToHome() { wx.redirectTo({ url: '/pages/merchant-home/merchant-home' }); },
goToKefuKey() { wx.navigateTo({ url: '/pages/merchant-kefu-key/merchant-kefu-key' }); },
goToKefuList() { wx.navigateTo({ url: '/pages/merchant-kefu-list/merchant-kefu-list' }); },
- goToRecharge() { wx.navigateTo({ url: '/pages/merchant-recharge/merchant-recharge' }); },
- goToWithdraw() { wx.navigateTo({ url: '/pages/withdraw/withdraw' }); },
+ goToAuditLog() { wx.navigateTo({ url: '/pages/merchant-staff-audit/merchant-staff-audit' }); },
+ goToStaffRank() { wx.navigateTo({ url: '/pages/merchant-staff-audit/merchant-staff-audit?tab=rank' }); },
+ goToRecharge() {
+ if (!guardMerchantOwnerAction('子客服无法充值,请联系商家老板')) return;
+ wx.navigateTo({ url: '/pages/merchant-recharge/merchant-recharge' });
+ },
+ goToWithdraw() {
+ if (!guardMerchantOwnerAction('子客服无法提现,请联系商家老板')) return;
+ wx.navigateTo({ url: '/pages/withdraw/withdraw' });
+ },
goToRanking() { wx.navigateTo({ url: '/pages/fighter-rank/fighter-rank?type=shangjia' }); },
goToMessages() { wx.navigateTo({ url: '/pages/merchant-msg/merchant-msg' }); },
}, {
diff --git a/pages/merchant/merchant.wxml b/pages/merchant/merchant.wxml
index 308544e..e09c8f9 100644
--- a/pages/merchant/merchant.wxml
+++ b/pages/merchant/merchant.wxml
@@ -1,5 +1,21 @@
-
+
+
+
+
+
+ 客服身份加载中
+ 请稍候,正在同步您的派单员权限…
+
+
+
+
+
+
+