From c03d22776fe8bcfe18b684cd98073c29d33d207c Mon Sep 17 00:00:00 2001 From: XingQue Date: Mon, 29 Jun 2026 01:59:42 +0800 Subject: [PATCH] =?UTF-8?q?backup:=20=E7=B4=AB=E8=89=B2UI=E6=8D=A2?= =?UTF-8?q?=E8=82=A4=E5=89=8D=E5=AE=8C=E6=95=B4=E5=A4=87=E4=BB=BD=EF=BC=88?= =?UTF-8?q?=E5=BD=93=E5=89=8D=E6=A9=99=E8=89=B2=E9=80=8D=E9=81=A5=E6=A2=A6?= =?UTF-8?q?=E4=B8=BB=E9=A2=98=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 保留改造前全部页面样式与功能代码,便于回滚。 Co-authored-by: Cursor --- app.js | 839 +++---- app.json | 3 + pages/accept-order/accept-order.js | 6 +- pages/express-order/express-order.js | 1282 ++++++----- pages/express-order/express-order.wxml | 6 +- pages/fighter-recharge/fighter-recharge.js | 1988 +++++++++-------- pages/fighter-recharge/fighter-recharge.wxml | 22 +- pages/fighter-recharge/fighter-recharge.wxss | 27 +- pages/fighter/fighter.js | 22 +- pages/merchant-dispatch/merchant-dispatch.js | 109 +- .../merchant-dispatch/merchant-dispatch.wxml | 4 +- pages/merchant-home/merchant-home.js | 50 +- pages/merchant-home/merchant-home.wxml | 10 +- pages/merchant-home/merchant-home.wxss | 13 + pages/merchant-kefu-key/merchant-kefu-key.js | 122 +- .../merchant-kefu-key/merchant-kefu-key.wxml | 11 +- .../merchant-kefu-list/merchant-kefu-list.js | 411 +++- .../merchant-kefu-list.wxml | 137 +- .../merchant-kefu-list.wxss | 48 + .../merchant-order-detail.js | 1705 +++++++------- .../merchant-order-detail.wxml | 725 +++--- pages/merchant-orders/merchant-orders.js | 17 +- pages/merchant-recharge/merchant-recharge.js | 5 + .../merchant-staff-audit.js | 99 + .../merchant-staff-audit.json | 5 + .../merchant-staff-audit.wxml | 39 + .../merchant-staff-audit.wxss | 25 + .../merchant-staff-role.js | 236 ++ .../merchant-staff-role.json | 4 + .../merchant-staff-role.wxml | 75 + .../merchant-staff-role.wxss | 221 ++ pages/merchant/merchant.js | 163 +- pages/merchant/merchant.wxml | 53 +- pages/merchant/merchant.wxss | 14 + pages/mine/mine.js | 49 +- pages/mine/mine.wxml | 7 + pages/phone-auth/phone-auth.js | 7 +- pages/staff-join/staff-join.js | 55 + pages/staff-join/staff-join.json | 4 + pages/staff-join/staff-join.wxml | 13 + pages/staff-join/staff-join.wxss | 8 + pages/verify/verify.js | 14 +- utils/base-page.js | 6 +- utils/login.js | 2 + utils/phone-auth.js | 209 +- utils/primary-role.js | 20 +- utils/request.js | 145 +- utils/role-tab-bar.js | 521 ++--- utils/staff-api.js | 253 +++ 49 files changed, 6025 insertions(+), 3784 deletions(-) create mode 100644 pages/merchant-staff-audit/merchant-staff-audit.js create mode 100644 pages/merchant-staff-audit/merchant-staff-audit.json create mode 100644 pages/merchant-staff-audit/merchant-staff-audit.wxml create mode 100644 pages/merchant-staff-audit/merchant-staff-audit.wxss create mode 100644 pages/merchant-staff-role/merchant-staff-role.js create mode 100644 pages/merchant-staff-role/merchant-staff-role.json create mode 100644 pages/merchant-staff-role/merchant-staff-role.wxml create mode 100644 pages/merchant-staff-role/merchant-staff-role.wxss create mode 100644 pages/staff-join/staff-join.js create mode 100644 pages/staff-join/staff-join.json create mode 100644 pages/staff-join/staff-join.wxml create mode 100644 pages/staff-join/staff-join.wxss create mode 100644 utils/staff-api.js 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]}} - - - 撤销退款申请 - 修改退款理由 - - - - - - 服务接单员 - - - - - - {{dashouInfo.nicheng || '未知昵称'}} - - 用户ID{{dashouInfo.yonghuid}}📋 - - - - - - - 服务详情 - - - 交付图片 - - - - - - - - - - - - - - 接单员留言 - {{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 ? '★' : '☆'}} - - - - -