commit 84be8489b4a2142971f2287e0dff18324ce88d92 Author: XingQue Date: Fri Jun 12 21:55:06 2026 +0800 第一次提交:微信小程序前端最新完整代码 diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..115cc02 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,31 @@ +/* + * Eslint config file + * Documentation: https://eslint.org/docs/user-guide/configuring/ + * Install the Eslint extension before using this feature. + */ +module.exports = { + env: { + es6: true, + browser: true, + node: true, + }, + ecmaFeatures: { + modules: true, + }, + parserOptions: { + ecmaVersion: 2018, + sourceType: 'module', + }, + globals: { + wx: true, + App: true, + Page: true, + getCurrentPages: true, + getApp: true, + Component: true, + requirePlugin: true, + requireMiniProgram: true, + }, + // extends: 'eslint:recommended', + rules: {}, +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e173d96 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +miniprogram_npm/ +project.private.config.json +.DS_Store +*.log diff --git a/.vs/ProjectSettings.json b/.vs/ProjectSettings.json new file mode 100644 index 0000000..f8b4888 --- /dev/null +++ b/.vs/ProjectSettings.json @@ -0,0 +1,3 @@ +{ + "CurrentProjectSetting": null +} \ No newline at end of file diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json new file mode 100644 index 0000000..f5d0a94 --- /dev/null +++ b/.vs/VSWorkspaceState.json @@ -0,0 +1,7 @@ +{ + "ExpandedNodes": [ + "" + ], + "SelectedNode": "\\app.js", + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/.vs/a_long_dianjing/FileContentIndex/adbed671-f010-4e25-b61e-f17273811071.vsidx b/.vs/a_long_dianjing/FileContentIndex/adbed671-f010-4e25-b61e-f17273811071.vsidx new file mode 100644 index 0000000..89c2332 Binary files /dev/null and b/.vs/a_long_dianjing/FileContentIndex/adbed671-f010-4e25-b61e-f17273811071.vsidx differ diff --git a/.vs/a_long_dianjing/v17/.wsuo b/.vs/a_long_dianjing/v17/.wsuo new file mode 100644 index 0000000..3263000 Binary files /dev/null and b/.vs/a_long_dianjing/v17/.wsuo differ diff --git a/.vs/a_long_dianjing/v17/DocumentLayout.json b/.vs/a_long_dianjing/v17/DocumentLayout.json new file mode 100644 index 0000000..c80c276 --- /dev/null +++ b/.vs/a_long_dianjing/v17/DocumentLayout.json @@ -0,0 +1,37 @@ +{ + "Version": 1, + "WorkspaceRootPath": "C:\\\u5FAE\u4FE1\u5F00\u53D1\u5DE5\u5177\\\u4EE3\u7801\\a_long_dianjing\\", + "Documents": [ + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\\u5FAE\u4FE1\u5F00\u53D1\u5DE5\u5177\\\u4EE3\u7801\\a_long_dianjing\\app.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:app.js||{14D17961-FE51-464D-9111-C4AF11D7D99A}" + } + ], + "DocumentGroupContainers": [ + { + "Orientation": 0, + "VerticalTabListWidth": 256, + "DocumentGroups": [ + { + "DockedWidth": 200, + "SelectedChildIndex": 0, + "Children": [ + { + "$type": "Document", + "DocumentIndex": 0, + "Title": "app.js", + "DocumentMoniker": "C:\\\u5FAE\u4FE1\u5F00\u53D1\u5DE5\u5177\\\u4EE3\u7801\\a_long_dianjing\\app.js", + "RelativeDocumentMoniker": "app.js", + "ToolTip": "C:\\\u5FAE\u4FE1\u5F00\u53D1\u5DE5\u5177\\\u4EE3\u7801\\a_long_dianjing\\app.js", + "RelativeToolTip": "app.js", + "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001646|", + "WhenOpened": "2026-01-30T05:12:55.655Z", + "EditorCaption": "" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite new file mode 100644 index 0000000..1ff7040 Binary files /dev/null and b/.vs/slnx.sqlite differ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9eb715c --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 GoEasy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/_backup_pre_redesign/REVERT.md b/_backup_pre_redesign/REVERT.md new file mode 100644 index 0000000..3b98c19 --- /dev/null +++ b/_backup_pre_redesign/REVERT.md @@ -0,0 +1,44 @@ +# 大改备份与恢复说明 + +改造日期:2026-06-09 + +## 如何一键恢复改造前(若已手动备份) + +将 `_backup_pre_redesign/` 下对应文件夹覆盖回项目即可(见各子目录)。 + +## 本次改造改动文件清单 + +### 新增 +- `utils/primary-role.js` — 三端固定 primaryRole +- `utils/avatar.js` — 统一默认头像 + +### TabBar +- `custom-tab-bar/index.js` +- `custom-tab-bar/index.wxml` +- `custom-tab-bar/index.wxss` + +### 打手合一中心(主战场) +- `pages/dashouduan/dashouduan.js` +- `pages/dashouduan/dashouduan.wxml` +- `pages/dashouduan/dashouduan.wxss` +- `pages/dashouduan/dashouduan.json` + +### 启动与 IM +- `app.js` +- `utils/chat-core.js` + +### 老板端 / 认证 +- `pages/wode/wode.wxml` +- `pages/renzheng/renzheng.js` +- `utils/role-tab-bar.js` + +### 废弃独立 Tab(路由保留,自动跳转打手中心) +- `pages/guanshiduan/guanshiduan.js` — onLoad redirect +- `pages/zuzhangduan/zuzhangduan.js` — onLoad redirect + +## 改造要点 +1. 去掉 TabBar「切换身份」 +2. 三端固定:normal / dashou / shangjia +3. 管事+组长功能合并进 dashouduan +4. 刷新按钮一次调用:dashouxinxi + guanshixinxi + zuzhangxinxi +5. IM 打手端统一 Ds 前缀连接 diff --git a/_backup_pre_redesign/app.js b/_backup_pre_redesign/app.js new file mode 100644 index 0000000..c1dcd95 --- /dev/null +++ b/_backup_pre_redesign/app.js @@ -0,0 +1,417 @@ +// app.js - 稳健生产版本(修复 GoEasy 初始化错误 + 配置100%加载) +import GoEasy from './static/lib/goeasy-2.13.24.esm.min'; +const ChatCore = require('./utils/chat-core'); +import { check } from './utils/phone-auth'; + + +// ===== 角色 → 默认首页(与 custom-tab-bar 完全一致) ===== +const roleDefaultPage = { + normal: '/pages/index/index', + dashou: '/pages/dashouduan/dashouduan', + shangjia: '/pages/shangjiaduan/shangjiaduan', + guanshi: '/pages/guanshiduan/guanshiduan', + zuzhang: '/pages/zuzhangduan/zuzhangduan', + kaoheguan: '/pages/kaoheguan/kaoheguan' +}; + +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' + }, + + // ========== 核心启动流程 ========== + async onLaunch() { + // ---------- ① 立即隐藏“返回首页”按钮 ---------- + wx.hideHomeButton(); + wx.onAppRoute(() => { + wx.hideHomeButton(); + }); + + // ---------- ② 恢复角色并跳转 ---------- + const savedRole = wx.getStorageSync('currentRole') || 'normal'; + this.globalData.currentRole = savedRole; + + const targetPage = roleDefaultPage[savedRole] || '/pages/index/index'; + if (savedRole !== 'normal' || targetPage !== '/pages/index/index') { + 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(() => { + this.ensureConnection(); + }, 300); + + // ---------- ⑥ 后台静默获取最新配置 ---------- + this.fetchConfigSafely() + .then(latestConfig => { + this.saveConfigToStorage(latestConfig); + this.applyDynamicConfig(latestConfig); + // 重新尝试初始化 GoEasy(如果之前因缺少appkey而跳过,现在会成功) + this.initGoEasyWithConfig(); + 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; + } + }, + + ensureConnection() { + const conn = this.globalData.goEasyConnection; + const userId = wx.getStorageSync(conn.cacheKeys.userId); + if (userId && conn.status !== 'connected') { + console.log('尝试建立GoEasy连接,userId:', userId); + // 此处可调用实际的连接逻辑,目前仅做标记 + } + }, + + // ========== 配置缓存读写 ========== + 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 初始化成功'); + } 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); } + }); + } + }, + + /** 切换当前角色并通知 custom-tab-bar 立即刷新(Storage + globalData + 事件) */ + 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/_backup_pre_redesign/chat-core.js b/_backup_pre_redesign/chat-core.js new file mode 100644 index 0000000..3527e42 --- /dev/null +++ b/_backup_pre_redesign/chat-core.js @@ -0,0 +1,268 @@ +// utils/chat-core.js - 最终角标稳定版 +import GoEasy from '../static/lib/goeasy-2.13.24.esm.min'; +import { jianquanxian } from './imAuth/jianquanxian'; + +let _convListener = null; +let _privateListener = null; +let _groupListener = null; +let _notificationHandler = null; + +function initGlobalMessageSystem(app) { + app.ensureConnection = () => ensureConnection(app); + app.loadConversations = () => loadConversations(app); + app.disconnectGoEasy = () => disconnectGoEasy(app); + app.switchRoleAndReconnect = (newRole) => switchRoleAndReconnect(app, newRole); + app.updateUnreadCount = (count) => updateTabBarBadge(app, count || 0); + + try { + const muted = wx.getStorageSync('notificationMuted'); + if (muted !== undefined) app.globalData.messageManager.notificationMuted = muted; + } catch(e) {} + + initAppStateListener(app); + initNetworkListener(app); + ensureConnection(app); +} + +function getCurrentUserId() { + return (wx.goEasy && wx.goEasy.im && wx.goEasy.im.userId) || null; +} + +async function ensureConnection(app) { + const role = app.globalData.currentRole || 'normal'; + const uid = wx.getStorageSync('uid'); + if (!uid) return; + + const prefixMap = { normal: 'Boss', dashou: 'Ds', shangjia: 'Sj', guanshi: 'Gs', zuzhang: 'Zz', kaoheguan: 'Kh' }; + const prefix = prefixMap[role] || 'Boss'; + const targetUserId = prefix + uid; + + const status = wx.goEasy.getConnectionStatus ? wx.goEasy.getConnectionStatus() : 'disconnected'; + if (status === 'connected' || status === 'reconnected') { + const currentUserId = getCurrentUserId(); + if (currentUserId === targetUserId) { + registerGlobalListeners(app); + await subscribeAllGroups(app); + loadConversations(app); + registerNotification(app); + // 强制立即更新一次角标,避免切后台后不显示 + setTimeout(() => { loadConversations(app); }, 500); + return; + } else { + wx.goEasy.disconnect(); + } + } + connectGoEasy(app, targetUserId, role); +} + +function connectGoEasy(app, userId, identityType) { + const currentUser = app.globalData.currentUser || { + id: wx.getStorageSync('uid'), + name: '用户' + (wx.getStorageSync('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), + identityType: identityType + }, + onSuccess: async () => { + wx.setStorageSync('savedGoEasyConnection', JSON.stringify({ + userId, identityType, lastConnectTime: Date.now(), status: 'connected' + })); + registerGlobalListeners(app); + await subscribeAllGroups(app); + loadConversations(app); + registerNotification(app); + }, + onFailed: (error) => { + console.error('GoEasy连接失败', error); + if (error.code === 408) { + registerGlobalListeners(app); + loadConversations(app); + registerNotification(app); + } else { + wx.showToast({ title: '连接失败,请重试', icon: 'none' }); + } + } + }); +} + +function subscribeAllGroups(app) { + return new Promise((resolve) => { + if (!wx.goEasy || !wx.goEasy.im) return resolve(); + wx.goEasy.im.latestConversations({ + onSuccess: (result) => { + const conversations = result?.content?.conversations || result?.conversations || []; + const groupIds = conversations.filter(c => c.type === 'group' && c.groupId).map(c => c.groupId); + if (groupIds.length && wx.goEasy.im.subscribeGroup) { + wx.goEasy.im.subscribeGroup({ + groupIds: groupIds, + onSuccess: () => resolve(), + onFailed: () => resolve() + }); + } else { + resolve(); + } + }, + onFailed: () => resolve() + }); + }); +} + +function registerGlobalListeners(app) { + if (!wx.goEasy || !wx.goEasy.im) return; + if (_convListener) { + wx.goEasy.im.off(wx.GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, _convListener); + } + if (_privateListener) { + wx.goEasy.im.off(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _privateListener); + } + if (_groupListener) { + wx.goEasy.im.off(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _groupListener); + } + + _convListener = (result) => { + const unreadTotal = result.unreadTotal || 0; + updateTabBarBadge(app, unreadTotal); + app.emitEvent('conversationsUpdated', result); + }; + wx.goEasy.im.on(wx.GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, _convListener); + + _privateListener = () => { loadConversations(app); }; + wx.goEasy.im.on(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _privateListener); + + _groupListener = () => { loadConversations(app); }; + wx.goEasy.im.on(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _groupListener); +} + +function loadConversations(app) { + if (!getCurrentUserId()) return; + wx.goEasy.im.latestConversations({ + onSuccess: (result) => { + const unreadTotal = result.unreadTotal || 0; + updateTabBarBadge(app, unreadTotal); + app.emitEvent('conversationsUpdated', result); + subscribeAllGroups(app); + }, + onFailed: (err) => console.error('加载会话失败', err) + }); +} + +function updateTabBarBadge(app, unreadCount) { + const badgeText = unreadCount > 0 ? (unreadCount > 99 ? '99+' : String(unreadCount)) : ''; + app.globalData.messageManager.unreadTotal = unreadCount; + app.emitEvent('tabBarBadgeChanged', { badgeText, showRedDot: unreadCount > 0 }); + // 直接更新组件,确保角标立即显示 + try { + const pages = getCurrentPages(); + if (pages.length) { + const tabBar = pages[pages.length-1].selectComponent('#custom-tab-bar'); + if (tabBar && tabBar.setData) tabBar.setData({ badgeText }); + // 兼容 getTabBar 方式 + const page = pages[pages.length-1]; + if (page.getTabBar) { + const tabBarInstance = page.getTabBar(); + if (tabBarInstance && tabBarInstance.setData) tabBarInstance.setData({ badgeText }); + } + } + } catch(e) {} + wx.setStorageSync('messageUnreadTotal', unreadCount); +} + +function registerNotification(app) { + if (!wx.goEasy || !wx.goEasy.im) return; + if (_notificationHandler) { + wx.goEasy.im.off(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _notificationHandler); + wx.goEasy.im.off(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _notificationHandler); + } + _notificationHandler = (message) => { + if (app.globalData.messageManager.notificationMuted) return; + const pages = getCurrentPages(); + if (pages.length) { + const curPage = pages[pages.length - 1]; + if (curPage.route === 'pages/liaotian/liaotian' || curPage.route === 'pages/qunliaotian/qunliaotian') return; + } + const senderName = message.senderData?.name || (message.senderId || '用户'); + let content = ''; + if (message.type === 'text') content = message.payload?.text || '[文本]'; + else if (message.type === 'image') content = '[图片]'; + else if (message.type === 'audio') content = '[语音]'; + else content = '[新消息]'; + if (app.globalData.globalNotification && app.globalData.globalNotification.show) { + app.globalData.globalNotification.show({ + senderName, content, message, + notificationType: message.groupId ? 'group' : 'private', + groupId: message.groupId, orderId: message.orderId + }); + } else { + wx.showToast({ title: `${senderName}: ${content}`, icon: 'none', duration: 2000 }); + } + }; + wx.goEasy.im.on(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _notificationHandler); + wx.goEasy.im.on(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _notificationHandler); +} + +function disconnectGoEasy(app) { + if (_convListener) { + wx.goEasy.im.off(wx.GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, _convListener); + _convListener = null; + } + if (_privateListener) { + wx.goEasy.im.off(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _privateListener); + _privateListener = null; + } + if (_groupListener) { + wx.goEasy.im.off(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _groupListener); + _groupListener = null; + } + if (_notificationHandler) { + wx.goEasy.im.off(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _notificationHandler); + wx.goEasy.im.off(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _notificationHandler); + _notificationHandler = null; + } + wx.goEasy.disconnect({ onSuccess: () => {}, onFailed: () => {} }); + wx.removeStorageSync('savedGoEasyConnection'); +} + +async function switchRoleAndReconnect(app, newRole) { + await disconnectGoEasy(app); + app.globalData.currentRole = newRole; + wx.setStorageSync('currentRole', newRole); + ensureConnection(app); + return true; +} + +function initAppStateListener(app) { + let isActive = true; + wx.onAppShow(() => { + if (!isActive) return; + const status = wx.goEasy.getConnectionStatus ? wx.goEasy.getConnectionStatus() : 'disconnected'; + if (status !== 'connected' && status !== 'reconnected') { + ensureConnection(app); + } else { + // 连接存在但可能角标未更新,强制拉取并更新 + loadConversations(app); + // 额外确保角标组件刷新 + setTimeout(() => { + const unread = app.globalData.messageManager.unreadTotal || 0; + updateTabBarBadge(app, unread); + }, 500); + } + }); + wx.onAppHide(() => {}); +} + +function initNetworkListener(app) { + wx.onNetworkStatusChange((res) => { + if (res.isConnected) { + setTimeout(() => { + ensureConnection(app); + }, 1000); + } + }); +} + +module.exports = { initGlobalMessageSystem }; \ No newline at end of file diff --git a/_backup_pre_redesign/custom-tab-bar/index.js b/_backup_pre_redesign/custom-tab-bar/index.js new file mode 100644 index 0000000..e8b0ed8 --- /dev/null +++ b/_backup_pre_redesign/custom-tab-bar/index.js @@ -0,0 +1,220 @@ +// custom-tab-bar/index.js +const app = getApp(); + +const roleCfg = { + normal: { + name: '点单老板', + defaultPage: '/pages/index/index', + tabList: [ + { pagePath: 'pages/index/index', text: '商城', iconPath: '/images/zhuye.png', selectedIconPath: '/images/zhuye.png' }, + { pagePath: 'pages/fenlei/fenlei', text: '分类', iconPath: '/images/fenlei.png', selectedIconPath: '/images/fenlei.png' }, + { pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' }, + { pagePath: 'pages/wode/wode', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' } + ] + }, + dashou: { + name: '打手', + defaultPage: '/pages/jiedan/jiedan', + tabList: [ + { pagePath: 'pages/jiedan/jiedan', text: '接单池', iconPath: '/images/jiedanchi.png', selectedIconPath: '/images/jiedanchi.png' }, + { pagePath: 'pages/dashoudingdan/dashoudingdan', text: '订单', iconPath: '/images/dingdan.png', selectedIconPath: '/images/dingdan.png' }, + { pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' }, + { pagePath: 'pages/dashouduan/dashouduan', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' } + ] + }, + shangjia: { + name: '商家', + defaultPage: 'pages/sjdingdan/sjdingdan', + tabList: [ + { pagePath: 'pages/sjdingdan/sjdingdan', text: '订单', iconPath: '/images/dingdan.png', selectedIconPath: '/images/dingdan.png' }, + { pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' }, + { pagePath: 'pages/shangjiaduan/shangjiaduan', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' } + ] + }, + guanshi: { + name: '管事', + defaultPage: '/pages/guanshiduan/guanshiduan', + tabList: [ + { pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' }, + { pagePath: 'pages/guanshiduan/guanshiduan', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' } + ] + }, + zuzhang: { + name: '组长', + defaultPage: '/pages/zuzhangduan/zuzhangduan', + tabList: [ + { pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' }, + { pagePath: 'pages/zuzhangduan/zuzhangduan', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' } + ] + }, + kaoheguan: { + name: '考核官', + defaultPage: '/pages/kaoheguan/kaoheguan', + tabList: [ + { pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' }, + { pagePath: 'pages/kaoheguan/kaoheguan', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' } + ] + } +}; + +Component({ + data: { + selectedIndex: 0, + tabList: [], + currentRole: 'normal', + showRolePicker: false, + availableRoles: [], + badgeText: '' + }, + lifetimes: { + attached() { + this.refresh(); + this._badgeListener = (data) => { + if (data.badgeText !== undefined) { + this.setData({ badgeText: data.badgeText }); + } + }; + this._roleListener = () => { + this.refresh(); + }; + app.on('tabBarBadgeChanged', this._badgeListener); + app.on('currentRoleChanged', this._roleListener); + }, + detached() { + if (this._badgeListener) { + app.off('tabBarBadgeChanged', this._badgeListener); + } + if (this._roleListener) { + app.off('currentRoleChanged', this._roleListener); + } + } + }, + pageLifetimes: { + show() { + this.refresh(); + const globalBadge = app.globalData.messageManager?.tabBarBadgeText || ''; + if (this.data.badgeText !== globalBadge) { + this.setData({ badgeText: globalBadge }); + } + this._syncUnreadWithDebounce(); + } + }, + methods: { + _syncUnreadWithDebounce() { + if (this._syncTimer) clearTimeout(this._syncTimer); + this._syncTimer = setTimeout(() => { + if (app && app.loadConversations) { + app.loadConversations(); + } + }, 300); + }, + + refresh() { + const token = wx.getStorageSync('token'); + // 未登录时强制点单端,避免清除缓存后 TabBar 仍显示其他身份 + let role = wx.getStorageSync('currentRole') || app.globalData.currentRole || 'normal'; + if (!token) { + role = 'normal'; + app.globalData.currentRole = 'normal'; + wx.setStorageSync('currentRole', 'normal'); + } else { + app.globalData.currentRole = role; + } + let cfg = roleCfg[role]; + if (!cfg) return; + + // 打手身份下,如果 isJinpai=1,悄悄把“我的”页面换成金牌页面 + if (role === 'dashou') { + const isJinpai = wx.getStorageSync('isJinpai') === 1; + if (isJinpai) { + cfg = { + ...cfg, + tabList: cfg.tabList.map(item => { + if (item.pagePath === 'pages/dashouduan/dashouduan') { + return { ...item, pagePath: 'pages/jinpaids/jinpaids' }; + } + return { ...item }; + }) + }; + } + } + + const list = cfg.tabList.map((v, i) => ({ ...v, index: i })); + const pages = getCurrentPages(); + const curRoute = pages.length ? pages[pages.length - 1].route : ''; + let idx = list.findIndex(t => t.pagePath === curRoute); + if (idx === -1) idx = 0; + const avail = this._getAvail(); + this.setData({ currentRole: role, tabList: list, selectedIndex: idx, availableRoles: avail }); + }, + + _getAvail() { + const token = wx.getStorageSync('token'); + if (!token) { + return [{ key: 'normal', name: '点单老板' }]; + } + const isActive = (key) => Number(wx.getStorageSync(key)) === 1; + const arr = [{ key: 'normal', name: '点单老板' }]; + if (isActive('dashoustatus')) arr.push({ key: 'dashou', name: '打手' }); + if (isActive('shangjiastatus')) arr.push({ key: 'shangjia', name: '商家' }); + if (isActive('guanshistatus')) arr.push({ key: 'guanshi', name: '管事' }); + if (isActive('zuzhangstatus')) arr.push({ key: 'zuzhang', name: '组长' }); + if (isActive('kaoheguanstatus')) arr.push({ key: 'kaoheguan', name: '考核官' }); + return arr; + }, + + toggleRolePicker() { + this.setData({ showRolePicker: !this.data.showRolePicker }); + }, + + async selectRole(e) { + const newRole = e.currentTarget.dataset.role; + if (newRole === this.data.currentRole) { + this.setData({ showRolePicker: false }); + return; + } + + this.setData({ showRolePicker: false }); + + // 1. 立即更新角色并保存 + if (app.setCurrentRole) { + app.setCurrentRole(newRole); + } else { + app.globalData.currentRole = newRole; + wx.setStorageSync('currentRole', newRole); + app.emitEvent('currentRoleChanged', { role: newRole }); + } + + // 2. 🔥 确定跳转页面:所有角色都去“我的”页面 + let targetPage = ''; + if (newRole === 'dashou') { + // 打手需要根据金牌状态决定跳转 + const isJinpai = wx.getStorageSync('isJinpai') === 1; + targetPage = isJinpai ? '/pages/jinpaids/jinpaids' : '/pages/dashouduan/dashouduan'; + } else { + // 其他角色直接取 tabList 最后一个页面 + const cfg = roleCfg[newRole]; + if (cfg && cfg.tabList.length > 0) { + targetPage = '/' + cfg.tabList[cfg.tabList.length - 1].pagePath; + } + } + + // 3. 跳转 + if (targetPage) { + wx.reLaunch({ url: targetPage }); + } + + // 4. 后台异步切换连接 + app.switchRoleAndReconnect(newRole).catch(err => { + console.error('后台切换连接失败:', err); + }); + }, + + switchTab(e) { + const path = e.currentTarget.dataset.path; + const idx = e.currentTarget.dataset.index; + if (idx === this.data.selectedIndex) return; + wx.redirectTo({ url: '/' + path }); + } + } +}); \ No newline at end of file diff --git a/_backup_pre_redesign/custom-tab-bar/index.json b/_backup_pre_redesign/custom-tab-bar/index.json new file mode 100644 index 0000000..62389a9 --- /dev/null +++ b/_backup_pre_redesign/custom-tab-bar/index.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} + } \ No newline at end of file diff --git a/_backup_pre_redesign/custom-tab-bar/index.wxml b/_backup_pre_redesign/custom-tab-bar/index.wxml new file mode 100644 index 0000000..75977e7 --- /dev/null +++ b/_backup_pre_redesign/custom-tab-bar/index.wxml @@ -0,0 +1,42 @@ + + + + + + 切换身份 + + + + + + + + + {{badgeText}} + + + {{item.text}} + + + + + + + + + + 切换身份 + + + + + {{item.name}} + + + + + \ No newline at end of file diff --git a/_backup_pre_redesign/custom-tab-bar/index.wxss b/_backup_pre_redesign/custom-tab-bar/index.wxss new file mode 100644 index 0000000..7a957fa --- /dev/null +++ b/_backup_pre_redesign/custom-tab-bar/index.wxss @@ -0,0 +1,165 @@ +/* custom-tab-bar/index.wxss - 角标浮于右上角,可超出胶囊 */ + +.custom-tab-bar { + position: fixed; + bottom: 0; left: 0; right: 0; + z-index: 9999; + display: flex; + justify-content: center; + align-items: flex-end; + padding-bottom: env(safe-area-inset-bottom); + background: transparent; + } + + .bar-row { + display: flex; + align-items: center; + margin-bottom: 15rpx; + padding: 0 20rpx; + width: 100%; + box-sizing: border-box; + } + + /* 左侧椭圆身份按钮 */ + .role-switch-btn { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 110rpx; + height: 100rpx; + border-radius: 50rpx; + background: #000000; + border: 1px solid rgba(255,255,255,0.15); + box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.8); + margin-right: 20rpx; + flex-shrink: 0; + } + .role-switch-icon { + width: 40rpx; + height: 40rpx; + margin-bottom: 2rpx; + } + + .role-switch-text { + font-size: 22rpx; + font-weight: 600; + color: #00f7ff; + letter-spacing: 1rpx; + } + + /* 纯黑闪光胶囊,移除overflow保证角标可见 */ + .tab-capsule { + flex: 1; + display: flex; + align-items: center; + justify-content: space-around; + height: 100rpx; + background: #000000; + border-radius: 50rpx; + border: 1px solid rgba(255,255,255,0.15); + box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.8); + padding: 0 20rpx; + position: relative; + } + .tab-capsule::before { + content: ''; + position: absolute; + top: 0; left: -100%; + width: 60%; height: 100%; + background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.25) 20%, rgba(255,255,255,0.35) 50%, rgba(255,255,255,0.25) 80%, transparent 100%); + animation: shine 3s infinite; + z-index: 1; + } + @keyframes shine { + 0% { left: -100%; } + 25% { left: 150%; } + 100% { left: 150%; } + } + + .tab-item { + position: relative; /* 角标的定位参考 */ + z-index: 2; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-width: 70rpx; + } + .tab-icon { + width: 44rpx; + height: 44rpx; + margin-bottom: 4rpx; + filter: brightness(0) invert(1); + opacity: 0.9; + } + .tab-text { + font-size: 20rpx; + color: #aaa; + z-index: 2; + } + .active .tab-icon { + opacity: 1; + filter: brightness(0) invert(1) drop-shadow(0 0 8rpx #00aaff); + } + .active .tab-text { + color: #00aaff; + font-weight: 500; + } + + /* 🔴 角标容器:绝对定位到图标右上角,允许超出 */ + .badge-container { + position: absolute; + top: -10rpx; /* 往上提,更接近图标右上角 */ + right: -15rpx; /* 往右伸,超出tab-item */ + z-index: 100; + pointer-events: none; /* 不阻挡点击 */ + } + .tab-badge { + min-width: 36rpx; + height: 36rpx; + line-height: 36rpx; + padding: 0 10rpx; + background: #ff3b30; + color: #fff; + font-size: 22rpx; + font-weight: bold; + border-radius: 36rpx; + text-align: center; + box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.3); + border: 1rpx solid rgba(255,255,255,0.4); + } + + /* 弹窗部分保持不变 */ + .picker-mask { + position: fixed; top: 0; left: 0; right: 0; bottom: 0; + background: rgba(0,0,0,0.6); + z-index: 10000; + } + .picker-panel { + position: fixed; bottom: 0; left: 0; right: 0; + background: #000000; + border-radius: 30rpx 30rpx 0 0; + z-index: 10001; + max-height: 70vh; + overflow-y: auto; + } + .picker-header { + display: flex; justify-content: space-between; align-items: center; + padding: 30rpx 40rpx 20rpx; + border-bottom: 1px solid rgba(255,255,255,0.1); + } + .picker-title { font-size: 32rpx; color: #00f7ff; } + .picker-close { font-size: 36rpx; color: #aaa; padding: 0 10rpx; } + .role-option { + display: flex; justify-content: space-between; align-items: center; + padding: 28rpx 40rpx; + border-bottom: 1px solid rgba(255,255,255,0.05); + color: #ccc; font-size: 28rpx; + } + .role-option.active { color: #00f7ff; } + .check-mark { font-size: 32rpx; color: #00f7ff; } + + page { + padding-bottom: 100rpx; + } \ No newline at end of file diff --git a/app.js b/app.js new file mode 100644 index 0000000..96aded3 --- /dev/null +++ b/app.js @@ -0,0 +1,415 @@ +// app.js - 稳健生产版本(修复 GoEasy 初始化错误 + 配置100%加载) +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'; + + +// ===== 三端固定首页(与 custom-tab-bar 一致) ===== +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/guanshiduan/guanshiduan' || path === 'pages/zuzhangduan/zuzhangduan') { + 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); } + }); + } + }, + + /** 切换当前角色并通知 custom-tab-bar 立即刷新(Storage + globalData + 事件) */ + 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 new file mode 100644 index 0000000..ee7781d --- /dev/null +++ b/app.json @@ -0,0 +1,107 @@ +{ + "pages": [ + "pages/index/index", + "pages/fenlei/fenlei", + "pages/xuanren/xuanren", + "pages/xiaoxi/xiaoxi", + "pages/wode/wode", + "pages/shangpinxiangqing/shangpinxiangqing", + "pages/tijiao/tijiao", + "pages/xiugai/xiugai", + "pages/dashouduan/dashouduan", + "pages/shangjiaduan/shangjiaduan", + "pages/guanshiduan/guanshiduan", + "pages/dingdan/dingdan", + "pages/dingdanxiangqing/dingdanxiangqing", + "pages/dashouguize/dashouguize", + "pages/dashouxiugai/dashouxiugai", + "pages/dashoupaihang/dashoupaihang", + "pages/dashouchongzhi/dashouchongzhi", + "pages/dashouxiaoxi/dashouxiaoxi", + "pages/dashoudingdan/dashoudingdan", + "pages/jiedan/jiedan", + "pages/tixian/tixian", + "pages/yaoqingdashou/yaoqingdashou", + "pages/wodedashou/wodedashou", + "pages/czjilu/czjilu", + "pages/guanshipaihang/guanshipaihang", + "pages/sjpaidan/sjpaidan", + "pages/sjdingdan/sjdingdan", + "pages/sjxiaoxi/sjxiaoxi", + "pages/sjpaihang/sjpaihang", + "pages/sjchongzhi/sjchongzhi", + "pages/sjddxq/sjddxq", + "pages/dsddxq/dsddxq", + "pages/liaotian/liaotian", + "pages/jisufd/jisufd", + "pages/jiedanchi/jiedanchi", + "pages/haibao/haibao", + "pages/jinpaids/jinpaids", + "pages/zuzhangduan/zuzhangduan", + "pages/guanzhual/guanzhual", + "pages/zzfhjilu/zzfhjilu", + "pages/yqguanshi/yqguanshi", + "pages/jiedanchi2/jiedanchi2", + "pages/qunliaotian/qunliaotian", + "pages/kefuliaotian/kefuliaotian", + "pages/renzheng/renzheng", + "components/global-notification/global-notification", + "components/popup-notice/popup-notice", + "components/order-card/order-card", + "components/order-sender/order-sender", + "pages/cfss/cfss/cfss", + "pages/cfss/components/fakuan-list/fakuan-list", + "pages/cfss/components/jifen-list/jifen-list", + "pages/cfss/components/fakuan-pay/fakuan-pay", + "pages/phone-auth/phone-auth", + "components/chenghao-tag/chenghao-tag", + "pages/kaoheguan/kaoheguan", + "pages/kaohe_dafen/kaohe_dafen", + "pages/kaohe_jilu/kaohe_jilu", + "pages/kaohe_zhongxin/kaohe_zhongxin", + "pages/kaohe_jinpai/kaohe_jinpai", + "pages/peihuDingdan/peihuDingdan", + "pages/tixian/components/mode1/mode1", + "pages/tixian/components/mode2/mode2" + + + ], + + "usingComponents": { + "popup-notice": "/components/popup-notice/popup-notice", + "custom-tab-bar": "/custom-tab-bar/index" + }, + + "window": { + "navigationBarTextStyle": "black", + "navigationStyle": "default", + "backgroundTextStyle": "light", + "navigationBarBackgroundColor": "#fff", + "navigationBarTitleText": "星阙网络" + }, + "tabBar": { + "custom": true, + "list": [ + + + { + "pagePath": "pages/jiedanchi/jiedanchi", + "text": "接单池", + "iconPath": "/images/jiedanchi.png", + "selectedIconPath": "/images/jiedanchi.png" + }, + { + "pagePath": "pages/jiedanchi2/jiedanchi2", + "text": "接单池", + "iconPath": "/images/jiedanchi.png", + "selectedIconPath": "/images/jiedanchi.png" + } + + ] + }, + + + "style": "v2", + "sitemapLocation": "sitemap.json", + "lazyCodeLoading": "requiredComponents" +} \ No newline at end of file diff --git a/app.wxss b/app.wxss new file mode 100644 index 0000000..06c6fc9 --- /dev/null +++ b/app.wxss @@ -0,0 +1,10 @@ +/**app.wxss**/ +.container { + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; + padding: 200rpx 0; + box-sizing: border-box; +} diff --git a/components/chenghao-tag/chenghao-tag.js b/components/chenghao-tag/chenghao-tag.js new file mode 100644 index 0000000..ae79133 --- /dev/null +++ b/components/chenghao-tag/chenghao-tag.js @@ -0,0 +1,73 @@ +// components/chenghao-tag/chenghao-tag.js +const app = getApp(); + +Component({ + properties: { + mingcheng: { type: String, value: '' }, + texiaoJson: { type: Object, value: {} } + }, + data: { + // 默认值:宽152,高52,六边形,无背景图,无动画,白色字 + width: 152, + height: 52, + shapeClass: 'liubianxing', // 保证至少不是矩形 + animationClass: '', + bgStyle: '', + textColor: '#FFFFFF', + textSize: 22, + imageUrl: '' + }, + lifetimes: { + attached() { + const cfg = this.properties.texiaoJson || {}; + + // 1. 尺寸(稍大一些,一行能放3~4个) + const width = cfg.width || 152; + const height = cfg.height || 52; + + // 2. 背景处理:优先背景图,否则用渐变/纯色 + let bgStyle = ''; + let imageUrl = ''; + if (cfg.image_url) { + const ossImageUrl = app.globalData.ossImageUrl || ''; + imageUrl = cfg.image_url.startsWith('http') + ? cfg.image_url + : ossImageUrl + cfg.image_url; + } else if (cfg.bg_gradient) { + bgStyle = `background: ${cfg.bg_gradient};`; + } else if (cfg.bg_color) { + bgStyle = `background: ${cfg.bg_color};`; + } else { + // 默认金橙渐变 + bgStyle = `background: linear-gradient(135deg, #FFD700, #FF8C00);`; + } + + // 3. 形状(后端传入 shape 字段,默认 liubianxing) + const shapeClass = cfg.shape || 'liubianxing'; + + // 4. 动画 + const animationClass = cfg.animation || ''; + + // 5. 文字 + const textColor = cfg.text_color || '#FFFFFF'; + const textSize = cfg.text_size || 22; + + this.setData({ + width, height, + bgStyle, imageUrl, + shapeClass, animationClass, + textColor, textSize + }); + } + }, + methods: { + // 背景图加载失败时回退为纯色背景 + onImageError() { + this.setData({ imageUrl: '' }); + // 如果 bgStyle 为空,给个默认背景 + if (!this.data.bgStyle) { + this.setData({ bgStyle: 'background: linear-gradient(135deg, #FFD700, #FF8C00);' }); + } + } + } +}); \ No newline at end of file diff --git a/components/chenghao-tag/chenghao-tag.json b/components/chenghao-tag/chenghao-tag.json new file mode 100644 index 0000000..62389a9 --- /dev/null +++ b/components/chenghao-tag/chenghao-tag.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} + } \ No newline at end of file diff --git a/components/chenghao-tag/chenghao-tag.wxml b/components/chenghao-tag/chenghao-tag.wxml new file mode 100644 index 0000000..e9c44f3 --- /dev/null +++ b/components/chenghao-tag/chenghao-tag.wxml @@ -0,0 +1,20 @@ + + + + + + + {{mingcheng}} + \ No newline at end of file diff --git a/components/chenghao-tag/chenghao-tag.wxss b/components/chenghao-tag/chenghao-tag.wxss new file mode 100644 index 0000000..d142188 --- /dev/null +++ b/components/chenghao-tag/chenghao-tag.wxss @@ -0,0 +1,90 @@ +/* components/chenghao-tag/chenghao-tag.wxss */ + +/* ========== 根容器 ========== */ +.tag-root { + display: inline-flex; + align-items: center; + justify-content: center; + position: relative; + border-radius: 4rpx; + overflow: hidden; + flex-shrink: 0; + margin: 4rpx; + box-sizing: border-box; + } + + /* ========== 背景图 ========== */ + .tag-bg-image { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 0; + border-radius: inherit; + } + + /* ========== 文字层 ========== */ + .tag-text { + position: relative; + z-index: 2; + font-weight: bold; + white-space: nowrap; + text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.5); + padding: 0 10rpx; + } + + /* ========== 多边形形状 ========== */ + .tag-root.liubianxing { + clip-path: polygon(12% 0%, 88% 0%, 100% 50%, 88% 100%, 12% 100%, 0% 50%); + } + .tag-root.lingxing { + clip-path: polygon(50% 0%, 85% 12%, 100% 50%, 85% 88%, 50% 100%, 15% 88%, 0% 50%, 15% 12%); + } + .tag-root.wujiaoxing { + clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%); + } + .tag-root.dunpai { + clip-path: polygon(18% 0%, 82% 0%, 100% 18%, 95% 50%, 100% 82%, 82% 100%, 18% 100%, 0% 82%, 5% 50%, 0% 18%); + } + .tag-root.jiantou { + clip-path: polygon(0% 0%, 85% 0%, 100% 50%, 85% 100%, 0% 100%, 12% 50%); + } + + /* ========== 动画 ========== */ + /* 流光扫描 */ + .tag-root.shine::after { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 60%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.5), transparent); + transform: skewX(-20deg); + animation: shine-anim 2s infinite; + z-index: 1; + pointer-events: none; + } + @keyframes shine-anim { + 0% { left: -100%; } + 100% { left: 200%; } + } + + /* 呼吸光晕 */ + .tag-root.pulse { + animation: pulse-anim 1.5s ease-in-out infinite; + } + @keyframes pulse-anim { + 0%, 100% { filter: brightness(1); transform: scale(1); } + 50% { filter: brightness(1.2); transform: scale(1.04); } + } + + /* 外发光闪烁 */ + .tag-root.glow { + animation: glow-anim 2s ease-in-out infinite; + } + @keyframes glow-anim { + 0%, 100% { box-shadow: 0 0 8rpx rgba(255, 215, 0, 0.4); } + 50% { box-shadow: 0 0 20rpx rgba(255, 215, 0, 0.8), 0 0 40rpx rgba(255, 215, 0, 0.4); } + } \ No newline at end of file diff --git a/components/global-notification/global-notification.js b/components/global-notification/global-notification.js new file mode 100644 index 0000000..f6a2b5a --- /dev/null +++ b/components/global-notification/global-notification.js @@ -0,0 +1,368 @@ +const { resolveAvatarUrl } = require('../../utils/avatar.js'); + +Component({ + properties: { + position: { + type: String, + value: 'top' + }, + duration: { + type: Number, + value: 3000 + }, + showProgress: { + type: Boolean, + value: true + }, + showMuteButton: { + type: Boolean, + value: true + }, + showTime: { + type: Boolean, + value: true + }, + swipeToClose: { + type: Boolean, + value: true + }, + swipeThreshold: { + type: Number, + value: 50 + } + }, + + data: { + show: false, + title: '', + message: '', + avatar: '', + notificationData: null, + progress: 100, + progressInterval: null, + positionClass: 'top', + + // 滑动相关 + touchStartY: 0, + touchStartX: 0, + currentY: 0, + currentX: 0, + isSwiping: false, + swipingClass: '', + swipeStyle: '', + swipeDistance: 0, + + // 静音状态 + isMuted: false, + + // 自动隐藏计时器 + autoHideTimeout: null + }, + + lifetimes: { + attached() { + this.loadMuteSetting(); + this.setData({ + positionClass: this.properties.position + }); + this.setupDirectEventListeners(); + }, + + detached() { + this.clearTimers(); + } + }, + + methods: { + setupDirectEventListeners() { + const app = getApp(); + if (app) { + const self = this; + const globalNotification = { + show: (data) => { + if (self && self.showNotification) { + self.showNotification(data); + } + }, + hide: () => { + if (self && self.hideNotification) { + self.hideNotification(); + } + }, + isMuted: () => { + return self.data.isMuted; + } + }; + app.globalData.globalNotification = globalNotification; + } + }, + + loadMuteSetting() { + try { + const isMuted = wx.getStorageSync('notificationMuted') || false; + this.setData({ isMuted }); + } catch (error) { + console.warn('加载静音设置失败:', error); + } + }, + + saveMuteSetting(isMuted) { + try { + wx.setStorageSync('notificationMuted', isMuted); + } catch (error) { + console.warn('保存静音设置失败:', error); + } + }, + + showNotification(data) { + if (!data) return; + if (this.data.isMuted) return; + + this.clearTimers(); + + const app = getApp(); + let avatar = resolveAvatarUrl( + data.avatar || data.message?.senderData?.avatar, + app + ); + + this.setData({ + show: true, + title: data.senderName || '新消息', + message: data.content || '[消息内容]', + avatar: avatar, + notificationData: data, + progress: 100, + swipingClass: '', + swipeStyle: '' + }); + + if (this.properties.showProgress) { + this.startProgressBar(this.properties.duration); + } + + this.data.autoHideTimeout = setTimeout(() => { + this.hideNotification(); + }, this.properties.duration); + }, + + hideNotification() { + this.clearTimers(); + this.setData({ + show: false, + progress: 100, + swipingClass: '', + swipeStyle: '' + }); + this.triggerEvent('hide'); + }, + + startProgressBar(duration) { + const interval = 100; + const steps = duration / interval; + const stepValue = 100 / steps; + + let currentProgress = 100; + this.data.progressInterval = setInterval(() => { + currentProgress -= stepValue; + if (currentProgress <= 0) { + currentProgress = 0; + this.setData({ progress: currentProgress }); + clearInterval(this.data.progressInterval); + } else { + this.setData({ progress: currentProgress }); + } + }, interval); + }, + + clearTimers() { + if (this.data.autoHideTimeout) { + clearTimeout(this.data.autoHideTimeout); + this.data.autoHideTimeout = null; + } + if (this.data.progressInterval) { + clearInterval(this.data.progressInterval); + this.data.progressInterval = null; + } + }, + + onTouchStart(e) { + if (!this.properties.swipeToClose) return; + const touch = e.touches[0]; + this.setData({ + touchStartY: touch.clientY, + touchStartX: touch.clientX, + currentY: touch.clientY, + currentX: touch.clientX, + isSwiping: false + }); + this.clearTimers(); + }, + + onTouchMove(e) { + if (!this.properties.swipeToClose) return; + const touch = e.touches[0]; + const deltaY = touch.clientY - this.data.touchStartY; + const deltaX = touch.clientX - this.data.touchStartX; + const absDeltaY = Math.abs(deltaY); + const absDeltaX = Math.abs(deltaX); + + if (absDeltaY > 10 && absDeltaY > absDeltaX) { + this.setData({ + isSwiping: true, + currentY: touch.clientY, + swipingClass: deltaY < 0 ? 'swiping-up' : 'swiping', + swipeDistance: Math.abs(deltaY), + swipeStyle: `--swipe-distance: ${Math.abs(deltaY)}rpx;` + }); + e.stopPropagation(); + } + }, + + onTouchEnd(e) { + if (!this.properties.swipeToClose || !this.data.isSwiping) { + this.restartAutoHide(); + return; + } + const deltaY = this.data.currentY - this.data.touchStartY; + const absDeltaY = Math.abs(deltaY); + if (absDeltaY > this.properties.swipeThreshold) { + this.hideNotification(); + } else { + this.setData({ + swipingClass: '', + swipeStyle: 'transition: transform 0.3s ease; transform: translateY(0);' + }); + setTimeout(() => { + this.setData({ + swipingClass: '', + swipeStyle: '' + }); + }, 300); + this.restartAutoHide(); + } + this.setData({ isSwiping: false }); + }, + + restartAutoHide() { + const remainingTime = (this.data.progress / 100) * this.properties.duration; + if (remainingTime > 0) { + this.data.autoHideTimeout = setTimeout(() => { + this.hideNotification(); + }, remainingTime); + } + }, + + onTap() { + const { notificationData } = this.data; + if (!notificationData) return; + this.triggerEvent('tap', notificationData); + this.hideNotification(); + this.navigateToChatPage(notificationData); + }, + + onClose(e) { + e.stopPropagation(); + this.triggerEvent('close'); + this.hideNotification(); + }, + + onMute(e) { + e.stopPropagation(); + const newMutedState = !this.data.isMuted; + this.setData({ isMuted: newMutedState }); + this.saveMuteSetting(newMutedState); + + const app = getApp(); + if (app && app.toggleDoNotDisturb) { + app.toggleDoNotDisturb(newMutedState); + } + this.triggerEvent('mute', { muted: newMutedState }); + wx.showToast({ + title: newMutedState ? '已开启免打扰' : '已关闭免打扰', + icon: 'success', + duration: 1500 + }); + + if (newMutedState) { + this.hideNotification(); + } + }, + + formatTime(timestamp) { + if (!timestamp) return ''; + const date = new Date(timestamp); + const now = new Date(); + const diff = now - date; + if (diff < 60 * 1000) return '刚刚'; + if (diff < 60 * 60 * 1000) return Math.floor(diff / (60 * 1000)) + '分钟前'; + if (date.toDateString() === now.toDateString()) { + return date.getHours().toString().padStart(2, '0') + ':' + date.getMinutes().toString().padStart(2, '0'); + } + const yesterday = new Date(now); + yesterday.setDate(yesterday.getDate() - 1); + if (date.toDateString() === yesterday.toDateString()) { + return '昨天 ' + date.getHours().toString().padStart(2, '0') + ':' + date.getMinutes().toString().padStart(2, '0'); + } + return (date.getMonth() + 1) + '月' + date.getDate() + '日'; + }, + + navigateToChatPage(data) { + if (!data || !data.message) { + console.warn('通知数据无效,无法跳转'); + return; + } + + const msg = data.message; + let path = ''; + let queryParam = null; + + // 根据 notificationType 或消息字段判断 + if (data.notificationType === 'cs' || msg.teamId) { + // 客服消息 + const teamId = data.teamId || msg.teamId; + path = '/pages/kefuliaotian/kefuliaotian'; + queryParam = { teamId: teamId }; + } else if (data.notificationType === 'group' || msg.groupId) { + // 群聊消息 + const groupId = data.groupId || msg.groupId; + const orderId = data.orderId || msg.orderId || ''; + const groupName = data.groupName || '订单群聊'; + const groupAvatar = data.groupAvatar || ''; + const isCross = data.isCross || msg.isCross || 0; + path = '/pages/qunliaotian/qunliaotian'; + queryParam = { + groupId, + orderId, + groupName, + groupAvatar, + isCross + }; + } else { + // 默认私聊 + const toUserId = data.senderId || msg.senderId; + const toName = data.senderName || '用户'; + const toAvatar = data.avatar || ''; + path = '/pages/liaotian/liaotian'; + queryParam = { + toUserId, + toName, + toAvatar + }; + } + + if (path && queryParam) { + const encoded = encodeURIComponent(JSON.stringify(queryParam)); + wx.navigateTo({ + url: `${path}?data=${encoded}`, + fail: (err) => { + console.error('通知跳转失败:', err); + wx.switchTab({ url: '/pages/xiaoxi/xiaoxi' }); + } + }); + } else { + wx.switchTab({ url: '/pages/xiaoxi/xiaoxi' }); + } + } + } + }); \ No newline at end of file diff --git a/components/global-notification/global-notification.json b/components/global-notification/global-notification.json new file mode 100644 index 0000000..7e37c03 --- /dev/null +++ b/components/global-notification/global-notification.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} \ No newline at end of file diff --git a/components/global-notification/global-notification.wxml b/components/global-notification/global-notification.wxml new file mode 100644 index 0000000..d76b58c --- /dev/null +++ b/components/global-notification/global-notification.wxml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + {{title}} + {{message}} + + {{formatTime(notificationData ? notificationData.timestamp : '')}} + + + + + + + + × + + + + + + {{isMuted ? '🔕' : '🔔'}} + + + + + + + + + + \ No newline at end of file diff --git a/components/global-notification/global-notification.wxss b/components/global-notification/global-notification.wxss new file mode 100644 index 0000000..fc67be6 --- /dev/null +++ b/components/global-notification/global-notification.wxss @@ -0,0 +1,215 @@ +.global-notification { + position: fixed; + left: 20rpx; + right: 20rpx; + top: 0rpx; + z-index: 99999; + opacity: 0; + transform: translateY(-120%); + transition: all 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55); + pointer-events: none; + overflow: hidden; + border-radius: 24rpx; + background: linear-gradient(135deg, + rgba(41, 47, 61, 0.98) 0%, + rgba(31, 36, 48, 0.98) 100%); + backdrop-filter: blur(30rpx) saturate(180%); + -webkit-backdrop-filter: blur(30rpx) saturate(180%); + border: 1rpx solid rgba(255, 255, 255, 0.08); + box-shadow: + 0 20rpx 60rpx rgba(0, 0, 0, 0.4), + 0 0 0 1rpx rgba(255, 255, 255, 0.03), + inset 0 1rpx 0 rgba(255, 255, 255, 0.1); + min-height: 140rpx; + touch-action: pan-y; +} + +.global-notification.show { + opacity: 1; + transform: translateY(0); + pointer-events: auto; +} + +.notification-swipe-indicator { + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4rpx; + display: flex; + justify-content: center; + align-items: center; + z-index: 10; +} + +.swipe-line { + width: 40rpx; + height: 4rpx; + background: rgba(255, 255, 255, 0.3); + border-radius: 2rpx; + transition: all 0.3s; +} + +.notification-swipe-indicator.active .swipe-line { + background: rgba(255, 255, 255, 0.6); + transform: scaleX(1.2); +} + +.notification-content { + display: flex; + align-items: center; + padding: 32rpx; + min-height: 140rpx; + position: relative; +} + +.notification-avatar { + width: 88rpx; + height: 88rpx; + border-radius: 44rpx; + margin-right: 24rpx; + flex-shrink: 0; + border: 3rpx solid rgba(212, 175, 55, 0.6); + box-shadow: + 0 8rpx 24rpx rgba(212, 175, 55, 0.2), + inset 0 1rpx 0 rgba(255, 255, 255, 0.3); + background-color: rgba(0, 0, 0, 0.2); +} + +.notification-body { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + justify-content: center; +} + +.notification-title { + font-size: 34rpx; + font-weight: 600; + margin-bottom: 8rpx; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + background: linear-gradient(135deg, #D4AF37 0%, #FFD700 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.3); +} + +.notification-message { + font-size: 28rpx; + line-height: 1.4; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + color: rgba(255, 255, 255, 0.95); + text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.5); +} + +.notification-time { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.5); + margin-top: 4rpx; + font-weight: 300; +} + +.notification-actions { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + margin-left: 20rpx; + gap: 16rpx; +} + +.notification-close, +.notification-mute { + width: 56rpx; + height: 56rpx; + display: flex; + align-items: center; + justify-content: center; + border-radius: 28rpx; + background: rgba(255, 255, 255, 0.08); + border: 1rpx solid rgba(255, 255, 255, 0.1); + transition: all 0.3s; + flex-shrink: 0; + box-shadow: + 0 4rpx 12rpx rgba(0, 0, 0, 0.2), + inset 0 1rpx 0 rgba(255, 255, 255, 0.05); +} + +.notification-close:active, +.notification-mute:active { + background: rgba(255, 255, 255, 0.15); + transform: scale(0.92); +} + +.close-icon { + font-size: 32rpx; + font-weight: 300; + color: rgba(255, 255, 255, 0.9); + line-height: 1; +} + +.mute-icon { + font-size: 28rpx; + line-height: 0; + color: rgba(255, 255, 255, 0.9); +} + +.notification-mute.muted .mute-icon { + color: #D4AF37; +} + +.notification-progress { + height: 6rpx; + background: rgba(255, 255, 255, 0.05); + overflow: hidden; + border-radius: 0 0 24rpx 24rpx; +} + +.progress-bar { + height: 100%; + width: 100%; + background: linear-gradient(90deg, + #D4AF37 0%, + #FFD700 50%, + #D4AF37 100%); + background-size: 200% 100%; + transition: width 0.1s linear; + box-shadow: 0 0 20rpx rgba(212, 175, 55, 0.4); + animation: progressShine 2s linear infinite; +} + +@keyframes progressShine { + 0% { background-position: 200% 0; } + 100% { background-position: -200% 0; } +} + +.progress-bar.warning { + background: linear-gradient(90deg, + #ff3b30 0%, + #ff6b6b 50%, + #ff3b30 100%); + animation: progressShine 1s linear infinite; +} + +.global-notification.swiping { + opacity: 0.7; + transform: translateY(calc(var(--swipe-distance, 0) * -1)); +} + +.global-notification.swiping-up { + opacity: 0.3; + transform: translateY(calc(var(--swipe-distance, 0) * -1)); +} + +.global-notification:active:not(.swiping) { + transform: scale(0.995) translateY(0); + transition: transform 0.1s; +} \ No newline at end of file diff --git a/components/navigation-bar/navigation-bar.js b/components/navigation-bar/navigation-bar.js new file mode 100644 index 0000000..eb1770e --- /dev/null +++ b/components/navigation-bar/navigation-bar.js @@ -0,0 +1,102 @@ +Component({ + options: { + multipleSlots: true // 在组件定义时的选项中启用多slot支持 + }, + /** + * 组件的属性列表 + */ + properties: { + extClass: { + type: String, + value: '' + }, + title: { + type: String, + value: '' + }, + background: { + type: String, + value: '' + }, + color: { + type: String, + value: '' + }, + back: { + type: Boolean, + value: true + }, + loading: { + type: Boolean, + value: false + }, + homeButton: { + type: Boolean, + value: false, + }, + animated: { + // 显示隐藏的时候opacity动画效果 + type: Boolean, + value: true + }, + show: { + // 显示隐藏导航,隐藏的时候navigation-bar的高度占位还在 + type: Boolean, + value: true, + observer: '_showChange' + }, + // back为true的时候,返回的页面深度 + delta: { + type: Number, + value: 1 + }, + }, + /** + * 组件的初始数据 + */ + data: { + displayStyle: '' + }, + lifetimes: { + attached() { + const rect = wx.getMenuButtonBoundingClientRect() + const platform = (wx.getDeviceInfo() || wx.getSystemInfoSync()).platform + const isAndroid = platform === 'android' + const isDevtools = platform === 'devtools' + const { windowWidth, safeArea: { top = 0, bottom = 0 } = {} } = wx.getWindowInfo() || wx.getSystemInfoSync() + this.setData({ + ios: !isAndroid, + innerPaddingRight: `padding-right: ${windowWidth - rect.left}px`, + leftWidth: `width: ${windowWidth - rect.left}px`, + safeAreaTop: isDevtools || isAndroid ? `height: calc(var(--height) + ${top}px); padding-top: ${top}px` : `` + }) + }, + }, + /** + * 组件的方法列表 + */ + methods: { + _showChange(show) { + const animated = this.data.animated + let displayStyle = '' + if (animated) { + displayStyle = `opacity: ${show ? '1' : '0' + };transition:opacity 0.5s;` + } else { + displayStyle = `display: ${show ? '' : 'none'}` + } + this.setData({ + displayStyle + }) + }, + back() { + const data = this.data + if (data.delta) { + wx.navigateBack({ + delta: data.delta + }) + } + this.triggerEvent('back', { delta: data.delta }, {}) + } + }, +}) diff --git a/components/navigation-bar/navigation-bar.json b/components/navigation-bar/navigation-bar.json new file mode 100644 index 0000000..4a20f17 --- /dev/null +++ b/components/navigation-bar/navigation-bar.json @@ -0,0 +1,5 @@ +{ + "component": true, + "styleIsolation": "apply-shared", + "usingComponents": {} +} \ No newline at end of file diff --git a/components/navigation-bar/navigation-bar.wxml b/components/navigation-bar/navigation-bar.wxml new file mode 100644 index 0000000..2bfe519 --- /dev/null +++ b/components/navigation-bar/navigation-bar.wxml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{title}} + + + + + + + + + + + + diff --git a/components/navigation-bar/navigation-bar.wxss b/components/navigation-bar/navigation-bar.wxss new file mode 100644 index 0000000..8bd379e --- /dev/null +++ b/components/navigation-bar/navigation-bar.wxss @@ -0,0 +1,96 @@ +.weui-navigation-bar { + --weui-FG-0:rgba(0,0,0,.9); + --height: 44px; + --left: 16px; +} +.weui-navigation-bar .android { + --height: 48px; +} + +.weui-navigation-bar { + overflow: hidden; + color: var(--weui-FG-0); + flex: none; +} + +.weui-navigation-bar__inner { + position: relative; + top: 0; + left: 0; + height: calc(var(--height) + env(safe-area-inset-top)); + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + padding-top: env(safe-area-inset-top); + width: 100%; + box-sizing: border-box; +} + +.weui-navigation-bar__left { + position: relative; + padding-left: var(--left); + display: flex; + flex-direction: row; + align-items: flex-start; + height: 100%; + box-sizing: border-box; +} + +.weui-navigation-bar__btn_goback_wrapper { + padding: 11px 18px 11px 16px; + margin: -11px -18px -11px -16px; +} + +.weui-navigation-bar__btn_goback_wrapper.weui-active { + opacity: 0.5; +} + +.weui-navigation-bar__btn_goback { + font-size: 12px; + width: 12px; + height: 24px; + -webkit-mask: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='24' viewBox='0 0 12 24'%3E %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M10 19.438L8.955 20.5l-7.666-7.79a1.02 1.02 0 0 1 0-1.42L8.955 3.5 10 4.563 2.682 12 10 19.438z'/%3E%3C/svg%3E") no-repeat 50% 50%; + mask: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='24' viewBox='0 0 12 24'%3E %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M10 19.438L8.955 20.5l-7.666-7.79a1.02 1.02 0 0 1 0-1.42L8.955 3.5 10 4.563 2.682 12 10 19.438z'/%3E%3C/svg%3E") no-repeat 50% 50%; + -webkit-mask-size: cover; + mask-size: cover; + background-color: var(--weui-FG-0); +} + +.weui-navigation-bar__center { + font-size: 17px; + text-align: center; + position: relative; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + font-weight: bold; + flex: 1; + height: 100%; +} + +.weui-navigation-bar__loading { + margin-right: 4px; + align-items: center; +} + +.weui-loading { + font-size: 16px; + width: 16px; + height: 16px; + display: block; + background: transparent url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='80px' height='80px' viewBox='0 0 80 80' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Ctitle%3Eloading%3C/title%3E%3Cdefs%3E%3ClinearGradient x1='94.0869141%25' y1='0%25' x2='94.0869141%25' y2='90.559082%25' id='linearGradient-1'%3E%3Cstop stop-color='%23606060' stop-opacity='0' offset='0%25'%3E%3C/stop%3E%3Cstop stop-color='%23606060' stop-opacity='0.3' offset='100%25'%3E%3C/stop%3E%3C/linearGradient%3E%3ClinearGradient x1='100%25' y1='8.67370605%25' x2='100%25' y2='90.6286621%25' id='linearGradient-2'%3E%3Cstop stop-color='%23606060' offset='0%25'%3E%3C/stop%3E%3Cstop stop-color='%23606060' stop-opacity='0.3' offset='100%25'%3E%3C/stop%3E%3C/linearGradient%3E%3C/defs%3E%3Cg stroke='none' stroke-width='1' fill='none' fill-rule='evenodd' opacity='0.9'%3E%3Cg%3E%3Cpath d='M40,0 C62.09139,0 80,17.90861 80,40 C80,62.09139 62.09139,80 40,80 L40,73 C58.2253967,73 73,58.2253967 73,40 C73,21.7746033 58.2253967,7 40,7 L40,0 Z' fill='url(%23linearGradient-1)'%3E%3C/path%3E%3Cpath d='M40,0 L40,7 C21.7746033,7 7,21.7746033 7,40 C7,58.2253967 21.7746033,73 40,73 L40,80 C17.90861,80 0,62.09139 0,40 C0,17.90861 17.90861,0 40,0 Z' fill='url(%23linearGradient-2)'%3E%3C/path%3E%3Ccircle id='Oval' fill='%23606060' cx='40.5' cy='3.5' r='3.5'%3E%3C/circle%3E%3C/g%3E%3C/g%3E%3C/svg%3E%0A") no-repeat; + background-size: 100%; + margin-left: 0; + animation: loading linear infinite 1s; +} + +@keyframes loading { + from { + transform: rotate(0); + } + to { + transform: rotate(360deg); + } +} diff --git a/components/order-card/order-card.js b/components/order-card/order-card.js new file mode 100644 index 0000000..b94bb6c --- /dev/null +++ b/components/order-card/order-card.js @@ -0,0 +1,102 @@ +const app = getApp(); + +Component({ + properties: { + orderId: { type: String, value: '' } + }, + + data: { + order: null, + loading: false, + errorMsg: '', + // 跨平台字段 + isCross: false, // 是否跨平台订单 + dispatchType: 0, // 1-我方派单,2-对方派单 + partnerClubId: '', // 对方俱乐部ID + }, + + lifetimes: { + attached() { + if (this.properties.orderId) { + this.fetchOrderDetail(); + } + } + }, + + methods: { + // 获取订单详情(含跨平台字段) + fetchOrderDetail() { + if (!this.properties.orderId) return; + this.setData({ loading: true, errorMsg: '' }); + const baseUrl = app.globalData.apiBaseUrl; + wx.request({ + url: `${baseUrl}/dingdan/order_detail/`, // 需后端返回 is_cross, dispatch_type, partner_club_id + method: 'POST', + data: { order_id: this.properties.orderId }, + header: { 'content-type': 'application/json' }, + success: (res) => { + if (res.statusCode === 200 && res.data && res.data.code === 0) { + const order = res.data.data; + // 处理图片URL + if (order.tupian && !order.tupian.startsWith('http')) { + order.tupian = app.globalData.ossImageUrl + order.tupian; + } + this.setData({ + order, + loading: false, + isCross: order.is_cross === 1, + dispatchType: order.dispatch_type || 0, + partnerClubId: order.partner_club_id || '', + }); + // 通知父页面状态 + this.triggerEvent('statusready', { + isCross: this.data.isCross, + dispatchType: this.data.dispatchType, + partnerClubId: this.data.partnerClubId + }); + } else { + this.setData({ errorMsg: res.data?.msg || '获取订单失败', loading: false }); + } + }, + fail: () => { + this.setData({ errorMsg: '网络请求失败', loading: false }); + } + }); + }, + + // 发送订单卡片消息(原有) + sendOrderMessage() { + if (!this.data.order) return; + this.triggerEvent('sendorder', { order: this.data.order }); + }, + + // 跨平台消息转发(供父页面调用) + forwardMessage(payload) { + const that = this; + const baseUrl = app.globalData.apiBaseUrl; + return new Promise((resolve, reject) => { + wx.request({ + url: `${baseUrl}/dingdan/kptxxts`, + method: 'POST', + data: { + order_id: that.properties.orderId, + message_type: payload.type || 'text', + content: payload.content || '', + image_url: payload.imageUrl || '', // 图片等 + }, + header: { 'content-type': 'application/json' }, + success: (res) => { + if (res.statusCode === 200 && res.data && res.data.code === 0) { + resolve(res.data); + } else { + reject(res.data); + } + }, + fail: (err) => { + reject(err); + } + }); + }); + } + } +}); \ No newline at end of file diff --git a/components/order-card/order-card.json b/components/order-card/order-card.json new file mode 100644 index 0000000..7e37c03 --- /dev/null +++ b/components/order-card/order-card.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} \ No newline at end of file diff --git a/components/order-card/order-card.wxml b/components/order-card/order-card.wxml new file mode 100644 index 0000000..c87ad17 --- /dev/null +++ b/components/order-card/order-card.wxml @@ -0,0 +1,20 @@ + + + 关联订单 + {{order.dingdan_id}} + + 跨平台 + + + + + {{order.jieshao || '暂无介绍'}} + ¥{{order.jine}} + + 发送 + + {{errorMsg}} + + + 加载订单信息... + \ No newline at end of file diff --git a/components/order-card/order-card.wxss b/components/order-card/order-card.wxss new file mode 100644 index 0000000..62c2737 --- /dev/null +++ b/components/order-card/order-card.wxss @@ -0,0 +1,39 @@ +.order-card { + background: #fff; + margin: 15rpx 20rpx; + border-radius: 12rpx; + overflow: hidden; + box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.05); +} +.card-header { + display: flex; + align-items: center; + padding: 16rpx 20rpx; + border-bottom: 1rpx solid #f0f0f0; +} +.card-title { font-size: 26rpx; color: #666; } +.card-id { font-size: 24rpx; color: #999; margin-left: 15rpx; } +.cross-tag { + margin-left: auto; + background: #ff9900; + color: #fff; + font-size: 20rpx; + padding: 2rpx 12rpx; + border-radius: 8rpx; +} +.card-body { + display: flex; + align-items: center; + padding: 20rpx; +} +.card-img { width: 100rpx; height: 100rpx; border-radius: 8rpx; margin-right: 15rpx; } +.card-info { flex: 1; } +.card-jieshao { font-size: 28rpx; color: #333; } +.card-jine { font-size: 26rpx; color: #e74c3c; } +.card-send-btn { + width: 80rpx; height: 50rpx; line-height: 50rpx; + text-align: center; background: #07c160; color: #fff; + border-radius: 8rpx; font-size: 24rpx; +} +.card-error { padding: 20rpx; color: #e74c3c; text-align: center; } +.loading-text { display: block; padding: 20rpx; text-align: center; color: #999; } \ No newline at end of file diff --git a/components/order-sender/order-sender.js b/components/order-sender/order-sender.js new file mode 100644 index 0000000..f4c9444 --- /dev/null +++ b/components/order-sender/order-sender.js @@ -0,0 +1,16 @@ +const app = getApp(); + +Component({ + properties: { + visible: Boolean, + showDetail: { type: Boolean, value: false } + }, + + data: {}, + + methods: { + close() { + this.triggerEvent('close'); + } + } +}); \ No newline at end of file diff --git a/components/order-sender/order-sender.json b/components/order-sender/order-sender.json new file mode 100644 index 0000000..62389a9 --- /dev/null +++ b/components/order-sender/order-sender.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} + } \ No newline at end of file diff --git a/components/order-sender/order-sender.wxml b/components/order-sender/order-sender.wxml new file mode 100644 index 0000000..ae53ed5 --- /dev/null +++ b/components/order-sender/order-sender.wxml @@ -0,0 +1,12 @@ + + + + 提示 + + + + 📋 + 此功能暂未开放 + 敬请期待 + + \ No newline at end of file diff --git a/components/order-sender/order-sender.wxss b/components/order-sender/order-sender.wxss new file mode 100644 index 0000000..c0ffc48 --- /dev/null +++ b/components/order-sender/order-sender.wxss @@ -0,0 +1,45 @@ +.order-sender-mask { + position: fixed; top: 0; left: 0; right: 0; bottom: 0; + background: rgba(0,0,0,0.5); + z-index: 2000; + } + .order-sender { + position: fixed; bottom: 0; left: 0; right: 0; + height: 360rpx; + background: #fff; + border-radius: 24rpx 24rpx 0 0; + z-index: 2001; + transform: translateY(100%); + transition: 0.25s; + display: flex; + flex-direction: column; + } + .order-sender.show { transform: translateY(0); } + + .header { + display: flex; justify-content: space-between; + padding: 24rpx 30rpx; + border-bottom: 1rpx solid #f0f0f0; + } + .title { font-size: 32rpx; font-weight: 600; } + .close { font-size: 44rpx; color: #999; padding: 0 12rpx; } + + .content { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding-bottom: 40rpx; + } + .icon { font-size: 80rpx; margin-bottom: 20rpx; } + .notice { + font-size: 34rpx; + font-weight: 600; + color: #333; + margin-bottom: 10rpx; + } + .sub { + font-size: 24rpx; + color: #999; + } \ No newline at end of file diff --git a/components/popup-notice/popup-notice.js b/components/popup-notice/popup-notice.js new file mode 100644 index 0000000..5cfc83c --- /dev/null +++ b/components/popup-notice/popup-notice.js @@ -0,0 +1,325 @@ +// components/popup-notice/popup-notice.js +import PopupService from '../../services/popupService.js'; + +Component({ + data: { + // 原有全屏弹窗数据 + visible: false, + title: '', + images: [], + processedImages: [], + hasImages: false, + hasTextOnly: false, + textContent: '', + duration: 0, + remainingSeconds: 0, + muteChecked: false, + showMuteCheckbox: false, + isFullscreen: false, + timer: null, + + // 悬浮窗相关数据 + isMinimized: false, + floatX: 20, + floatY: 200, + savedRemainingSeconds: 0, + floatAvatarUrl: '', + + // 🆕 自定义预览数据 + showCustomPreview: false, // 是否显示自定义预览遮罩 + previewUrls: [], // 当前预览的图片URL列表 + currentPreviewIndex: 0, // 当前预览图片索引 + }, + + lifetimes: { + detached() { + this.clearTimer(); + } + }, + + methods: { + // ========== 原有所有方法(完全不变,一字不漏) ========== + async show(config, onCloseCallback) { + const { title, images, duration, showMuteCheckbox, serverTime } = config; + this.onCloseCallback = onCloseCallback; + + let safeImages = JSON.parse(JSON.stringify(images || [])); + const hasImages = safeImages.length > 0; + let hasTextOnly = false, textContent = ''; + if (!hasImages) { + hasTextOnly = true; + textContent = config.text || '暂无内容'; + } + + this.setData({ + visible: true, + isMinimized: false, + title: title || '', + images: safeImages, + hasImages, + hasTextOnly, + textContent, + duration: duration || 0, + remainingSeconds: duration || 0, + muteChecked: false, + showMuteCheckbox: showMuteCheckbox || false, + isFullscreen: false, + processedImages: [], + }); + + if (duration > 0) { + this.startCountdown(); + } + + if (hasImages) { + const processed = []; + const screenWidth = wx.getSystemInfoSync().windowWidth; + const containerWidth = screenWidth * 0.9; + const imageWidth = containerWidth - 60; + for (let i = 0; i < safeImages.length; i++) { + const img = safeImages[i]; + const fullUrl = this.getFullImageUrl(img.url); + let imageHeight = 200; + try { + const res = await this.getImageInfoPromise(fullUrl); + const realHeight = (imageWidth * res.height) / res.width; + imageHeight = realHeight; + } catch (e) { + console.error('获取图片信息失败', fullUrl, e); + } + processed.push({ + url: fullUrl, + text: img.text, + height: imageHeight, + }); + } + this.setData({ processedImages: processed }); + } + }, + + getImageInfoPromise(url) { + return new Promise((resolve, reject) => { + wx.getImageInfo({ + src: url, + success: resolve, + fail: reject, + }); + }); + }, + + hide(muteToday = false) { + this.clearTimer(); + this.setData({ + visible: false, + isMinimized: false + }); + if (this.onCloseCallback) { + this.onCloseCallback({ muteToday }); + } + }, + + startCountdown() { + this.clearTimer(); + this.timer = setInterval(() => { + let sec = this.data.remainingSeconds; + if (sec <= 1) { + this.clearTimer(); + this.setData({ remainingSeconds: 0 }); + } else { + this.setData({ remainingSeconds: sec - 1 }); + } + }, 1000); + }, + + clearTimer() { + if (this.timer) { + clearInterval(this.timer); + this.timer = null; + } + }, + + onCloseTap() { + if (this.data.remainingSeconds > 0) { + wx.showToast({ + title: `请阅读${this.data.remainingSeconds}秒后再关闭`, + icon: 'none' + }); + return; + } + if (this.data.showMuteCheckbox && this.data.muteChecked) { + wx.showModal({ + title: '提示', + content: '今天将不再收到本页面的任何公告,确认吗?', + success: (res) => { + if (res.confirm) this.hide(true); + }, + }); + } else { + this.hide(false); + } + }, + + onMuteChange(e) { + this.setData({ muteChecked: e.detail.value.length > 0 }); + }, + + // 🆕 替换原有的 onPreviewImage,改用自定义预览(不触发 onShow) + onCustomPreview(e) { + const index = e.currentTarget.dataset.index; + const urls = this.data.processedImages.map(img => img.url); + this.setData({ + showCustomPreview: true, + previewUrls: urls, + currentPreviewIndex: index !== undefined ? index : 0 + }); + }, + + // 🆕 关闭自定义预览 + onCloseCustomPreview() { + this.setData({ + showCustomPreview: false, + previewUrls: [], + currentPreviewIndex: 0 + }); + }, + + // 🆕 swiper 切换事件 + onPreviewSwiperChange(e) { + this.setData({ + currentPreviewIndex: e.detail.current + }); + }, + + // 保留原 onPreviewImage 方法以防外部调用,但不再使用 + onPreviewImage(e) { + // 重定向到自定义预览 + this.onCustomPreview(e); + }, + + onImageError(e) { + console.error('图片加载失败:', e.currentTarget.dataset.url); + }, + + toggleFullscreen() { + this.setData({ isFullscreen: !this.data.isFullscreen }); + }, + + getFullImageUrl(relativeUrl) { + if (!relativeUrl) return ''; + if (relativeUrl.startsWith('http')) return relativeUrl; + const app = getApp(); + let base = app.globalData.ossImageUrl || ''; + base = base.replace(/\/+$/, ''); + const clean = relativeUrl.replace(/^\/+/, ''); + return base ? base + '/' + clean : clean; + }, + + preventMove() {}, + + onMinimize() { + const remaining = this.data.remainingSeconds; + this.clearTimer(); + + const app = getApp(); + let avatarUrl = ''; + if (app.globalData.morentouxiang) { + avatarUrl = app.globalData.morentouxiang; + } else { + // 使用内嵌的默认头像 base64(灰色圆形),确保绝对显示,不依赖外部图片 + avatarUrl = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI1MCIgZmlsbD0iI2NjY2NjYyIvPjxwYXRoIGQ9Ik01MCAyNWMtMTMuOCAwLTI1IDExLjItMjUgMjUgMCAzLjIgMS4yIDYuMSAzLjIgOC4zQzM0LjQgNzAuNyA0Mi4xIDc1IDUwIDc1czE1LjYtNC4zIDIxLjgtMTEuN0M3My44IDU2LjEgNzUgNTMuMiA3NSA1MGMwLTEzLjgtMTEuMi0yNS0yNS0yNXoiIGZpbGw9IiNmZmZmZmYiLz48L3N2Zz4='; + } + + if (avatarUrl && !avatarUrl.startsWith('http') && !avatarUrl.startsWith('data:')) { + const ossBase = app.globalData.ossImageUrl || ''; + avatarUrl = ossBase.replace(/\/+$/, '') + '/' + avatarUrl.replace(/^\/+/, ''); + } + + this.setData({ + visible: false, + isMinimized: true, + savedRemainingSeconds: remaining, + floatX: 20, + floatY: 200, + floatAvatarUrl: avatarUrl + }); + }, + + onFloatChange(e) { + this.setData({ + floatX: e.detail.x, + floatY: e.detail.y + }); + }, + + onFloatTap() { + this.setData({ + visible: true, + isMinimized: false, + remainingSeconds: this.data.savedRemainingSeconds + }); + + if (this.data.duration > 0 && this.data.savedRemainingSeconds > 0) { + this.startCountdown(); + } + }, + + onFloatAvatarError() { + this.setData({ + floatAvatarUrl: '/images/avatar-placeholder.png' + }); + }, + + cleanup() { + this.clearTimer(); + this.setData({ + visible: false, + isMinimized: false + }); + PopupService.reset(); + }, + + onFloatTouchStart(e) { + const touch = e.touches[0]; + this.touchStartX = touch.clientX; + this.touchStartY = touch.clientY; + this.startFloatX = this.data.floatX; + this.startFloatY = this.data.floatY; + this.hasMoved = false; + return false; + }, + + onFloatTouchMove(e) { + const touch = e.touches[0]; + const deltaX = touch.clientX - this.touchStartX; + const deltaY = touch.clientY - this.touchStartY; + + if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) { + this.hasMoved = true; + } + + let newX = this.startFloatX + deltaX; + let newY = this.startFloatY + deltaY; + + const systemInfo = wx.getSystemInfoSync(); + const screenWidth = systemInfo.windowWidth; + const screenHeight = systemInfo.windowHeight; + const estimatedSize = 60; + + newX = Math.max(0, Math.min(newX, screenWidth - estimatedSize)); + newY = Math.max(0, Math.min(newY, screenHeight - estimatedSize)); + this.setData({ floatX: newX, floatY: newY }); + + return false; + }, + + onFloatTouchEnd(e) { + if (!this.hasMoved) { + this.onFloatTap(); + } + return false; + }, + + // 🆕 阻止事件冒泡(用于预览遮罩内部点击不关闭) + stopPropagation() {} + } + }); \ No newline at end of file diff --git a/components/popup-notice/popup-notice.json b/components/popup-notice/popup-notice.json new file mode 100644 index 0000000..62389a9 --- /dev/null +++ b/components/popup-notice/popup-notice.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} + } \ No newline at end of file diff --git a/components/popup-notice/popup-notice.wxml b/components/popup-notice/popup-notice.wxml new file mode 100644 index 0000000..c95e3ba --- /dev/null +++ b/components/popup-notice/popup-notice.wxml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + {{isFullscreen ? '✕' : '⛶'}} + + + + + {{title}} + + + + + {{textContent}} + + + {{item.text}} + + + + + + + + + + + + 请阅读 {{remainingSeconds}} 秒后关闭 + + + + + 今日不再提示 + + + + + {{remainingSeconds > 0 ? '阅读中...' : '关闭'}} + + + + + + + + + + + + + + + {{currentPreviewIndex + 1}} / {{previewUrls.length}} + × + + + + + + + \ No newline at end of file diff --git a/components/popup-notice/popup-notice.wxss b/components/popup-notice/popup-notice.wxss new file mode 100644 index 0000000..ee4873b --- /dev/null +++ b/components/popup-notice/popup-notice.wxss @@ -0,0 +1,295 @@ +/* components/popup-notice/popup-notice.wxss */ + +/* ========== 全屏弹窗样式(原有,完全不变) ========== */ +.popup-mask { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 10000; +} + +.popup-container { + width: 90%; + height: 85vh; + background: white; + border-radius: 32rpx; + display: flex; + flex-direction: column; + overflow: hidden; + position: relative; +} + +.popup-container.fullscreen { + width: 100%; + height: 100%; + border-radius: 0; +} + +.action-buttons { + position: absolute; + top: 20rpx; + right: 20rpx; + display: flex; + gap: 16rpx; + z-index: 10; +} + +.minimize-btn, +.fullscreen-btn { + width: 60rpx; + height: 60rpx; + background: rgba(0, 0, 0, 0.4); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + backdrop-filter: blur(4rpx); + transition: all 0.2s; +} + +.minimize-btn:active, +.fullscreen-btn:active { + background: rgba(0, 0, 0, 0.6); + transform: scale(0.9); +} + +.btn-icon { + font-size: 36rpx; + color: white; + font-weight: bold; + line-height: 1; +} + +.popup-title { + font-size: 40rpx; + font-weight: bold; + color: #000; + text-align: center; + padding: 30rpx 30rpx 20rpx; + border-bottom: 2rpx solid #eee; + background: white; +} + +.scroll-wrapper { + flex: 1; + overflow: hidden; + width: 100%; +} + +.scroll-view { + height: 100%; + width: 100%; + padding: 0; +} + +.text-content { + font-size: 36rpx; + font-weight: bold; + color: #000; + line-height: 1.6; + padding: 30rpx; + text-align: left; +} + +.image-block { + display: flex; + flex-direction: column; + width: 100%; + margin: 0; + padding: 0; +} + +.block-text { + font-size: 36rpx; + font-weight: bold; + color: #000; + padding: 16rpx 30rpx 4rpx 30rpx; + text-align: left; + line-height: 1.4; +} + +.image-padding { + padding: 0 30rpx; + box-sizing: border-box; + width: 100%; +} + +.block-image { + width: 100%; + display: block; + background: #f5f5f5; + border-radius: 16rpx; +} + +.bottom-fixed { + background: white; + border-top: 1rpx solid #eee; + padding: 20rpx 0 30rpx 0; + width: 100%; + box-sizing: border-box; +} + +.countdown-tip { + text-align: center; + font-size: 28rpx; + color: #ff6600; + margin-bottom: 20rpx; +} + +.mute-row { + display: flex; + justify-content: center; + margin-bottom: 20rpx; +} + +.mute-label { + font-size: 28rpx; + color: #555; + margin-left: 12rpx; +} + +/* 🔥 关闭按钮(替换原有 button,增加扫光) */ +.close-btn { + width: 600rpx; + height: 100rpx; + background: #1a1a1a; + border-radius: 60rpx; + display: flex; + align-items: center; + justify-content: center; + position: relative; + overflow: hidden; + margin: 0 auto; +} + +.close-btn:active { + opacity: 0.85; + transform: scale(0.98); +} + +.close-btn-disabled { + opacity: 0.6; + pointer-events: none; +} + +.close-btn-text { + color: white; + font-size: 40rpx; + font-weight: bold; + position: relative; + z-index: 2; +} + +.close-btn-sweep { + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent); + transform: skewX(-20deg); + animation: sweep 2.5s infinite ease-in-out; + z-index: 1; + pointer-events: none; +} + +@keyframes sweep { + 0% { left: -100%; } + 40% { left: 100%; } + 100% { left: 100%; } +} + +/* ========== 悬浮窗样式(纯圆形,适配自定义触摸拖动) ========== */ +.floating-avatar-wrapper { + position: fixed; + width: 120rpx; + height: 120rpx; + z-index: 10001; + transform: translateZ(0); + will-change: left, top; + display: flex; + align-items: center; + justify-content: center; +} + +.floating-avatar { + width: 120rpx; + height: 120rpx; + border-radius: 50%; + box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.3); + border: 2rpx solid rgba(255, 215, 0, 0.6); + background: #f0f0f0; + display: block; + transform: translateZ(0); +} + +/* 以下原有 movable-view 相关样式已弃用,但保留以维持代码完整(用户要求不删代码) */ +.floating-area { + display: none; +} +.floating-view { + display: none; +} +.float-avatar-wrapper { + display: none; +} +.float-text { + display: none; +} + +/* ========== 自定义图片预览遮罩样式(全屏,不触发 onShow) ========== */ +.custom-preview-mask { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.95); + z-index: 100000; + display: flex; + flex-direction: column; +} + +.preview-header { + height: 100rpx; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 30rpx; + color: white; + font-size: 32rpx; + background: rgba(0, 0, 0, 0.3); +} + +.preview-index { + color: white; +} + +.preview-close { + width: 60rpx; + height: 60rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 48rpx; + color: white; + background: rgba(255, 255, 255, 0.2); + border-radius: 50%; +} + +.preview-swiper { + flex: 1; + width: 100%; + height: 100%; +} + +.preview-image { + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/custom-tab-bar/index.js b/custom-tab-bar/index.js new file mode 100644 index 0000000..f0b7cd6 --- /dev/null +++ b/custom-tab-bar/index.js @@ -0,0 +1,109 @@ +// custom-tab-bar/index.js — 三端固定,无身份切换 +const app = getApp(); +import { getTabRole } from '../utils/primary-role.js'; + +const roleCfg = { + normal: { + name: '点单老板', + tabList: [ + { pagePath: 'pages/index/index', text: '商城', iconPath: '/images/zhuye.png', selectedIconPath: '/images/zhuye.png' }, + { pagePath: 'pages/fenlei/fenlei', text: '分类', iconPath: '/images/fenlei.png', selectedIconPath: '/images/fenlei.png' }, + { pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' }, + { pagePath: 'pages/wode/wode', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' }, + ], + }, + dashou: { + name: '打手', + tabList: [ + { pagePath: 'pages/jiedan/jiedan', text: '接单池', iconPath: '/images/jiedanchi.png', selectedIconPath: '/images/jiedanchi.png' }, + { pagePath: 'pages/dashoudingdan/dashoudingdan', text: '订单', iconPath: '/images/dingdan.png', selectedIconPath: '/images/dingdan.png' }, + { pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' }, + { pagePath: 'pages/dashouduan/dashouduan', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' }, + ], + }, + shangjia: { + name: '商家', + tabList: [ + { pagePath: 'pages/sjdingdan/sjdingdan', text: '订单', iconPath: '/images/dingdan.png', selectedIconPath: '/images/dingdan.png' }, + { pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' }, + { pagePath: 'pages/shangjiaduan/shangjiaduan', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' }, + ], + }, +}; + +Component({ + data: { + selectedIndex: 0, + tabList: [], + currentRole: 'normal', + badgeText: '', + }, + + lifetimes: { + attached() { + this.refresh(); + this._badgeListener = (data) => { + if (data.badgeText !== undefined) { + this.setData({ badgeText: data.badgeText }); + } + }; + this._roleListener = () => this.refresh(); + app.on('tabBarBadgeChanged', this._badgeListener); + app.on('currentRoleChanged', this._roleListener); + }, + detached() { + if (this._badgeListener) app.off('tabBarBadgeChanged', this._badgeListener); + if (this._roleListener) app.off('currentRoleChanged', this._roleListener); + }, + }, + + pageLifetimes: { + show() { + this.refresh(); + const globalBadge = app.globalData.messageManager?.tabBarBadgeText || ''; + if (this.data.badgeText !== globalBadge) { + this.setData({ badgeText: globalBadge }); + } + this._syncUnreadWithDebounce(); + }, + }, + + methods: { + _syncUnreadWithDebounce() { + if (this._syncTimer) clearTimeout(this._syncTimer); + this._syncTimer = setTimeout(() => { + if (app && app.loadConversations) app.loadConversations(); + }, 300); + }, + + refresh() { + const token = wx.getStorageSync('token'); + let role = getTabRole(app); + if (!token) { + role = 'normal'; + } + app.globalData.currentRole = role; + wx.setStorageSync('currentRole', role); + + let cfg = roleCfg[role]; + if (!cfg) { + cfg = roleCfg.normal; + role = 'normal'; + } + + const list = cfg.tabList.map((v, i) => ({ ...v, index: i })); + const pages = getCurrentPages(); + const curRoute = pages.length ? pages[pages.length - 1].route : ''; + let idx = list.findIndex((t) => t.pagePath === curRoute); + if (idx === -1) idx = 0; + this.setData({ currentRole: role, tabList: list, selectedIndex: idx }); + }, + + switchTab(e) { + const path = e.currentTarget.dataset.path; + const idx = e.currentTarget.dataset.index; + if (idx === this.data.selectedIndex) return; + wx.redirectTo({ url: '/' + path }); + }, + }, +}); diff --git a/custom-tab-bar/index.json b/custom-tab-bar/index.json new file mode 100644 index 0000000..62389a9 --- /dev/null +++ b/custom-tab-bar/index.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} + } \ No newline at end of file diff --git a/custom-tab-bar/index.wxml b/custom-tab-bar/index.wxml new file mode 100644 index 0000000..523432e --- /dev/null +++ b/custom-tab-bar/index.wxml @@ -0,0 +1,24 @@ + + + + + + + {{badgeText}} + + + {{item.text}} + + + + + diff --git a/custom-tab-bar/index.wxss b/custom-tab-bar/index.wxss new file mode 100644 index 0000000..d8da288 --- /dev/null +++ b/custom-tab-bar/index.wxss @@ -0,0 +1,123 @@ +/* custom-tab-bar — 三端固定,全宽 Tab 胶囊 */ + +.custom-tab-bar { + position: fixed; + bottom: 0; + left: 0; + right: 0; + z-index: 9999; + display: flex; + justify-content: center; + align-items: flex-end; + padding-bottom: env(safe-area-inset-bottom); + background: transparent; +} + +.bar-row { + display: flex; + align-items: center; + margin-bottom: 15rpx; + padding: 0 24rpx; + width: 100%; + box-sizing: border-box; +} + +.tab-capsule { + flex: 1; + display: flex; + align-items: center; + justify-content: space-around; + height: 100rpx; + background: #000000; + border-radius: 50rpx; + border: 1px solid rgba(255, 255, 255, 0.15); + box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.8); + padding: 0 20rpx; + position: relative; +} + +.tab-capsule::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 60%; + height: 100%; + background: linear-gradient( + 90deg, + transparent 0%, + rgba(255, 255, 255, 0.25) 20%, + rgba(255, 255, 255, 0.35) 50%, + rgba(255, 255, 255, 0.25) 80%, + transparent 100% + ); + animation: shine 3s infinite; + z-index: 1; +} + +@keyframes shine { + 0% { left: -100%; } + 25% { left: 150%; } + 100% { left: 150%; } +} + +.tab-item { + position: relative; + z-index: 2; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-width: 70rpx; +} + +.tab-icon { + width: 44rpx; + height: 44rpx; + margin-bottom: 4rpx; + filter: brightness(0) invert(1); + opacity: 0.9; +} + +.tab-text { + font-size: 20rpx; + color: #aaa; + z-index: 2; +} + +.active .tab-icon { + opacity: 1; + filter: brightness(0) invert(1) drop-shadow(0 0 8rpx #00aaff); +} + +.active .tab-text { + color: #00aaff; + font-weight: 500; +} + +.badge-container { + position: absolute; + top: -10rpx; + right: -15rpx; + z-index: 100; + pointer-events: none; +} + +.tab-badge { + min-width: 36rpx; + height: 36rpx; + line-height: 36rpx; + padding: 0 10rpx; + background: #ff3b30; + color: #fff; + font-size: 22rpx; + font-weight: bold; + border-radius: 36rpx; + text-align: center; + box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.3); + border: 1rpx solid rgba(255, 255, 255, 0.4); +} + +page { + padding-bottom: 100rpx; +} diff --git a/images/clouds/cloud-btn-br.svg b/images/clouds/cloud-btn-br.svg new file mode 100644 index 0000000..bc4ddac --- /dev/null +++ b/images/clouds/cloud-btn-br.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/images/clouds/cloud-btn-tl.svg b/images/clouds/cloud-btn-tl.svg new file mode 100644 index 0000000..95fc3f7 --- /dev/null +++ b/images/clouds/cloud-btn-tl.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/images/clouds/cloud-card-br.svg b/images/clouds/cloud-card-br.svg new file mode 100644 index 0000000..be56459 --- /dev/null +++ b/images/clouds/cloud-card-br.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/images/clouds/cloud-card-tl.svg b/images/clouds/cloud-card-tl.svg new file mode 100644 index 0000000..d32c518 --- /dev/null +++ b/images/clouds/cloud-card-tl.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/images/clouds/cloud-top-l.svg b/images/clouds/cloud-top-l.svg new file mode 100644 index 0000000..0ac171e --- /dev/null +++ b/images/clouds/cloud-top-l.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/images/clouds/cloud-top-r.svg b/images/clouds/cloud-top-r.svg new file mode 100644 index 0000000..c5591e4 --- /dev/null +++ b/images/clouds/cloud-top-r.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/images/dingdan.png b/images/dingdan.png new file mode 100644 index 0000000..b9a3a9c Binary files /dev/null and b/images/dingdan.png differ diff --git a/images/fenlei.png b/images/fenlei.png new file mode 100644 index 0000000..4753795 Binary files /dev/null and b/images/fenlei.png differ diff --git a/images/gonggao.png b/images/gonggao.png new file mode 100644 index 0000000..19cc52c Binary files /dev/null and b/images/gonggao.png differ diff --git a/images/goumai.png b/images/goumai.png new file mode 100644 index 0000000..a2ec8ed Binary files /dev/null and b/images/goumai.png differ diff --git a/images/jiedanchi.png b/images/jiedanchi.png new file mode 100644 index 0000000..16e2b9f Binary files /dev/null and b/images/jiedanchi.png differ diff --git a/images/jieshao.png b/images/jieshao.png new file mode 100644 index 0000000..c713f53 Binary files /dev/null and b/images/jieshao.png differ diff --git a/images/jinpaidashou.png b/images/jinpaidashou.png new file mode 100644 index 0000000..b54a971 Binary files /dev/null and b/images/jinpaidashou.png differ diff --git a/images/role-switch.png b/images/role-switch.png new file mode 100644 index 0000000..bbd4afe Binary files /dev/null and b/images/role-switch.png differ diff --git a/images/shuaxin.png b/images/shuaxin.png new file mode 100644 index 0000000..15ad08d Binary files /dev/null and b/images/shuaxin.png differ diff --git a/images/sousuo.png b/images/sousuo.png new file mode 100644 index 0000000..2a8f235 Binary files /dev/null and b/images/sousuo.png differ diff --git a/images/tiaoxuan.png b/images/tiaoxuan.png new file mode 100644 index 0000000..ffcf298 Binary files /dev/null and b/images/tiaoxuan.png differ diff --git a/images/tuichu.png b/images/tuichu.png new file mode 100644 index 0000000..01553b5 Binary files /dev/null and b/images/tuichu.png differ diff --git a/images/weixinzhifu.png b/images/weixinzhifu.png new file mode 100644 index 0000000..f2551fd Binary files /dev/null and b/images/weixinzhifu.png differ diff --git a/images/wode.png b/images/wode.png new file mode 100644 index 0000000..1fec6b3 Binary files /dev/null and b/images/wode.png differ diff --git a/images/xiaoxi.png b/images/xiaoxi.png new file mode 100644 index 0000000..e6cda27 Binary files /dev/null and b/images/xiaoxi.png differ diff --git a/images/yituikuan.png b/images/yituikuan.png new file mode 100644 index 0000000..62c8fee Binary files /dev/null and b/images/yituikuan.png differ diff --git a/images/yiwancheng.png b/images/yiwancheng.png new file mode 100644 index 0000000..e1c333e Binary files /dev/null and b/images/yiwancheng.png differ diff --git a/images/zhuanqu.png b/images/zhuanqu.png new file mode 100644 index 0000000..2cb7945 Binary files /dev/null and b/images/zhuanqu.png differ diff --git a/images/zhuye.png b/images/zhuye.png new file mode 100644 index 0000000..b2fe9ae Binary files /dev/null and b/images/zhuye.png differ diff --git a/pages/cfss/cfss/cfss.js b/pages/cfss/cfss/cfss.js new file mode 100644 index 0000000..abdec80 --- /dev/null +++ b/pages/cfss/cfss/cfss.js @@ -0,0 +1,100 @@ +// pages/cfss/cfss.js +import request from '../../../utils/request.js'; // 向上4级就是根目录 +// 如果有 COS: + +const app = getApp(); + +Page({ + data: { + // 统计数字(由接口 /yonghu/cffktjhq 返回) + stats: { + fakuan: { total: 0, daijiaona: 0, shensuzhong: 0, yijiaona: 0, yibohui: 0 }, + jifen: { total: 0, daichuli: 0, yichuli: 0, daichuli_0: 0, shensuzhong_3: 0, yichufa_1: 0, yibohui_2: 0 } + }, + + // 当前选中的主标签:'fakuan' 或 'jifen' + activeTab: 'fakuan', + + // 子标签状态值 + activeFakuanStatus: 1, // 罚款默认展示“待缴纳” + activeJifenStatus: 0, // 积分默认展示“待处理” + + // 搜索关键词(订单号) + searchDingdan: '', + + // 刷新触发器,传给子组件 + trigger: { + fakuan: 0, + jifen: 0 + } + }, + + onLoad() { + this.fetchTongji(); + }, + + onShow() { + // 每次显示页面都重新拉取统计,确保数字最新 + this.fetchTongji(); + }, + + // 获取综合统计 + async fetchTongji() { + try { + const res = await request({ + url: '/yonghu/cffktjhq', + method: 'POST' + }); + if (res.statusCode === 200 && res.data.code === 0) { + this.setData({ stats: res.data.data }); + } + } catch (error) { + console.error('获取处罚统计失败', error); + } + }, + + // 切换主Tab(罚款 / 积分) + switchMainTab(e) { + const tab = e.currentTarget.dataset.tab; + if (tab === this.data.activeTab) return; + this.setData({ activeTab: tab }); + }, + + // 切换罚款状态子标签 + switchFakuanStatus(e) { + const status = Number(e.currentTarget.dataset.status); + if (status === this.data.activeFakuanStatus) return; + this.setData({ activeFakuanStatus: status }); + this.bumpTrigger('fakuan'); // 通知罚款列表组件刷新 + }, + + // 切换积分状态子标签 + switchJifenStatus(e) { + const status = Number(e.currentTarget.dataset.status); + if (status === this.data.activeJifenStatus) return; + this.setData({ activeJifenStatus: status }); + this.bumpTrigger('jifen'); // 通知积分列表组件刷新 + }, + + // 搜索输入 + onSearchInput(e) { + this.setData({ searchDingdan: e.detail.value }); + }, + + // 执行搜索(按键盘搜索按钮或点击搜索图标) + onSearchConfirm() { + this.bumpTrigger(this.data.activeTab); + }, + + // 清空搜索关键字 + clearSearch() { + this.setData({ searchDingdan: '' }); + this.bumpTrigger(this.data.activeTab); + }, + + // 递增触发器,子组件通过 observer 响应 + bumpTrigger(tab) { + const key = `trigger.${tab}`; + this.setData({ [key]: this.data.trigger[tab] + 1 }); + } +}); \ No newline at end of file diff --git a/pages/cfss/cfss/cfss.json b/pages/cfss/cfss/cfss.json new file mode 100644 index 0000000..4ca406b --- /dev/null +++ b/pages/cfss/cfss/cfss.json @@ -0,0 +1,10 @@ +{ + "usingComponents": { + "fakuan-list": "/pages/cfss/components/fakuan-list/fakuan-list", + "jifen-list": "/pages/cfss/components/jifen-list/jifen-list" + }, + "navigationBarTitleText": "处罚中心", + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black", + "backgroundColor": "#f7f8fa" + } \ No newline at end of file diff --git a/pages/cfss/cfss/cfss.wxml b/pages/cfss/cfss/cfss.wxml new file mode 100644 index 0000000..03e8517 --- /dev/null +++ b/pages/cfss/cfss/cfss.wxml @@ -0,0 +1,68 @@ + + + + + + + {{ stats.fakuan.daijiaona }} + 待缴罚款 + + + {{ stats.jifen.daichuli }} + 待处理积分 + + + + 罚款:已缴{{ stats.fakuan.yijiaona }} · 申诉中{{ stats.fakuan.shensuzhong }} · 成功{{ stats.fakuan.yibohui }} + 积分:已处理{{ stats.jifen.yichuli }} + + + + + + + 罚款 + + + 积分处罚 + + + + + + + 待缴纳 {{ stats.fakuan.daijiaona }} + 申诉中 {{ stats.fakuan.shensuzhong }} + 已缴纳 {{ stats.fakuan.yijiaona }} + 申诉成功 {{ stats.fakuan.yibohui }} + + + + + 待处理 {{ stats.jifen.daichuli }} + 已处理 {{ stats.jifen.yichuli }} + + + + + + + 搜索 + + + + + + + + + + + \ No newline at end of file diff --git a/pages/cfss/cfss/cfss.wxss b/pages/cfss/cfss/cfss.wxss new file mode 100644 index 0000000..4cd5d19 --- /dev/null +++ b/pages/cfss/cfss/cfss.wxss @@ -0,0 +1,121 @@ +/* pages/cfss/cfss.wxss */ +page { background: #f7f8fa; font-family: -apple-system, BlinkMacSystemFont, sans-serif; } + +.page-container { padding-bottom: 30rpx; } + +/* 统计卡片 */ +.stats-card { + margin: 24rpx 28rpx; + padding: 30rpx 24rpx; + background: #fff; + border-radius: 24rpx; + box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.04); +} +.stats-number-row { + display: flex; + justify-content: space-around; + margin-bottom: 20rpx; +} +.number-block { + display: flex; + flex-direction: column; + align-items: center; +} +.big-num { + font-size: 56rpx; + font-weight: 700; +} +.green { color: #2E7D32; } +.orange { color: #E67E22; } +.num-label { + font-size: 24rpx; + color: #888; + margin-top: 8rpx; +} +.stats-detail-row { + border-top: 1rpx solid #eee; + padding-top: 18rpx; +} +.stats-detail-row text { + display: block; + font-size: 22rpx; + color: #aaa; + margin-bottom: 4rpx; +} + +/* 主Tab */ +.tab-row { + display: flex; + margin: 16rpx 28rpx; + background: #fff; + border-radius: 18rpx; + overflow: hidden; + box-shadow: 0 2rpx 6rpx rgba(0,0,0,0.02); +} +.tab-item { + flex: 1; + text-align: center; + padding: 24rpx 0; + font-size: 28rpx; + color: #666; + transition: 0.2s; +} +.tab-item.active { + color: #2E7D32; + font-weight: 600; + background: #F0F9F0; +} + +/* 子状态标签 */ +.sub-tabs { + white-space: nowrap; + margin: 8rpx 28rpx 16rpx; +} +.sub-tag { + display: inline-block; + padding: 10rpx 26rpx; + margin-right: 14rpx; + background: #fff; + border-radius: 30rpx; + font-size: 24rpx; + color: #888; + box-shadow: 0 1rpx 4rpx rgba(0,0,0,0.02); +} +.sub-tag.sub-active { + background: #E8F5E9; + color: #2E7D32; + font-weight: 600; +} + +/* 搜索栏 */ +.search-bar { + display: flex; + align-items: center; + margin: 16rpx 28rpx; + background: #fff; + border-radius: 20rpx; + padding: 12rpx 22rpx; + box-shadow: 0 1rpx 6rpx rgba(0,0,0,0.02); +} +.search-input { + flex: 1; + font-size: 26rpx; + color: #333; +} +.search-btn { + margin-left: 18rpx; + padding: 12rpx 30rpx; + background: #2E7D32; + color: #fff; + border-radius: 16rpx; + font-size: 24rpx; +} +.clear-btn { + margin-left: 16rpx; + font-size: 30rpx; + color: #aaa; +} + +.list-area { + margin-top: 10rpx; +} \ No newline at end of file diff --git a/pages/cfss/components/fakuan-list/fakuan-list.js b/pages/cfss/components/fakuan-list/fakuan-list.js new file mode 100644 index 0000000..cf45977 --- /dev/null +++ b/pages/cfss/components/fakuan-list/fakuan-list.js @@ -0,0 +1,317 @@ +// components/fakuan-list/fakuan-list.js +import request from '../../../../utils/request.js'; + +const app = getApp(); +const MEIYE_TIAOSHU = 5; + +Component({ + properties: { + status: { type: Number, value: 1, observer: 'reload' }, + searchDingdan: { type: String, value: '', observer: 'reload' }, + trigger: { type: Number, value: 0, observer: 'reload' } + }, + + data: { + list: [], + page: 1, + hasMore: true, + loading: false, + loadingMore: false, + ossImageUrl: app.globalData.ossImageUrl || '', + + // 详情弹窗 + showDetail: false, + detailItem: null, + + // 申诉弹窗 + showShensu: false, + shensuLiyou: '', + shensuTupian: [], + uploading: false, + uploadProgress: { current: 0, total: 0 }, + uploadProgressWidth: '0%', + + // 支付弹窗 + showPay: false, + payFadan: null + }, + + lifetimes: { + attached() { this.reload(); } + }, + + methods: { + // ==================== 数据加载 ==================== + reload() { + this.setData({ page: 1, list: [], hasMore: true }); + this.loadData(); + }, + + async loadData(isLoadMore = false) { + // 防止重复请求 + if (this.data.loading || this.data.loadingMore) return; + + if (!isLoadMore) { + this.setData({ loading: true }); + } else { + // 加载更多时,如果后端已经告知没有更多数据,不发送请求 + if (!this.data.hasMore) { + wx.showToast({ title: '没有更多了', icon: 'none', duration: 1000 }); + return; + } + this.setData({ loadingMore: true }); + } + + try { + const res = await request({ + url: '/yonghu/dsfklbhq', + method: 'POST', + data: { + page: this.data.page, + page_size: MEIYE_TIAOSHU, + zhuangtai: this.data.status, + sousuo_dingdan_id: this.data.searchDingdan + } + }); + if (res.statusCode === 200 && res.data.code === 0) { + const data = res.data.data; + const newList = isLoadMore ? this.data.list.concat(data.list || []) : (data.list || []); + this.setData({ + list: newList, + hasMore: data.has_more || false, // 以后端返回为准 + page: isLoadMore ? this.data.page + 1 : 2, // 首次加载后页码为2 + loading: false, + loadingMore: false + }); + } else { + wx.showToast({ title: res.data?.msg || '加载失败', icon: 'none' }); + this.setData({ loading: false, loadingMore: false }); + } + } catch (e) { + wx.showToast({ title: '网络请求失败', icon: 'none' }); + this.setData({ loading: false, loadingMore: false }); + } + }, + + loadMore() { + if (this.data.list.length === 0) return; + this.loadData(true); + }, + + // ==================== 详情弹窗 ==================== + openDetail(e) { + const item = e.currentTarget.dataset.item; + this.setData({ showDetail: true, detailItem: item }); + }, + closeDetail() { + this.setData({ showDetail: false, detailItem: null }); + }, + + // ==================== 申诉逻辑 ==================== + openShensu() { + const item = this.data.detailItem; + if (item.bohuiliyou) { + wx.showToast({ title: '该罚单申诉已被驳回', icon: 'none' }); + return; + } + const existingImgs = (item.shensu_tupian || []).map(url => this.getFullUrl(url)); + this.setData({ + showShensu: true, + shensuLiyou: item.shensuliyou || '', + shensuTupian: existingImgs, + uploading: false, + uploadProgress: { current: 0, total: 0 }, + uploadProgressWidth: '0%' + }); + }, + closeShensu() { + this.setData({ showShensu: false }); + }, + onShensuInput(e) { + this.setData({ shensuLiyou: e.detail.value.slice(0, 500) }); + }, + + chooseImage() { + const remain = 9 - this.data.shensuTupian.length; + if (remain <= 0) return; + wx.chooseMedia({ + count: remain, + mediaType: ['image'], + sourceType: ['album', 'camera'], + success: (res) => { + const newImgs = [...this.data.shensuTupian, ...res.tempFiles.map(f => f.tempFilePath)]; + this.setData({ shensuTupian: newImgs.slice(0, 9) }); + } + }); + }, + deleteShensuImg(e) { + const arr = this.data.shensuTupian; + arr.splice(e.currentTarget.dataset.index, 1); + this.setData({ shensuTupian: arr }); + }, + previewImage(e) { + wx.previewImage({ current: e.currentTarget.dataset.url, urls: [e.currentTarget.dataset.url] }); + }, + previewLocalImg(e) { + const idx = e.currentTarget.dataset.index; + wx.previewImage({ current: this.data.shensuTupian[idx], urls: this.data.shensuTupian }); + }, + + // ==================== 提交流程 ==================== + async submitShensu() { + if (!this.data.shensuLiyou.trim()) { + wx.showToast({ title: '请输入申诉理由', icon: 'none' }); + return; + } + wx.showLoading({ title: '提交中...' }); + try { + const newLocalPaths = this.data.shensuTupian.filter(path => !path.startsWith('http')); + let finalUrls = []; + if (newLocalPaths.length > 0) { + finalUrls = await this.piliangShangchuanShensuTupian(newLocalPaths); + if (!finalUrls) { + wx.hideLoading(); + return; + } + } + const oldRemoteRelative = this.data.shensuTupian + .filter(path => path.startsWith('http')) + .map(url => url.replace(this.data.ossImageUrl, '').replace(/^\//, '')); + const allUrls = oldRemoteRelative.concat(finalUrls); + await this.doSubmitShensu(allUrls); + } catch (err) { + console.error('提交申诉失败', err); + wx.showToast({ title: err.message || '提交失败', icon: 'none' }); + } finally { + wx.hideLoading(); + } + }, + + async doSubmitShensu(tupianUrls) { + const res = await request({ + url: '/yonghu/fkss', + method: 'POST', + data: { + fadan_id: this.data.detailItem.id, + shensu_liyou: this.data.shensuLiyou, + shensu_tupian_urls: tupianUrls + } + }); + if (res.statusCode === 200 && res.data.code === 0) { + wx.showToast({ title: '申诉已提交', icon: 'success' }); + this.setData({ showShensu: false, showDetail: false }); + this.reload(); + } else { + throw new Error(res.data?.msg || '提交失败'); + } + }, + + // ==================== 图片上传到COS ==================== + async piliangShangchuanShensuTupian(localPaths) { + const total = localPaths.length; + if (!total) return []; + this.setData({ uploading: true, uploadProgress: { current: 0, total: total }, uploadProgressWidth: '0%' }); + + let tokenData; + try { + const credRes = await request({ + url: '/dingdan/dsscpz', + method: 'POST', + data: { + dingdan_id: this.data.detailItem.guanliandingdan_id || '', + fadan_id: this.data.detailItem.id, + yongtu: 'fakuan' + } + }); + if (credRes.data.code !== 0) throw new Error(credRes.data.msg || '凭证获取失败'); + tokenData = credRes.data.data; + } catch (e) { + wx.showToast({ title: '获取上传凭证失败', icon: 'none' }); + this.setData({ uploading: false }); + return null; + } + + const COS = require('../../../../utils/cos-wx-sdk-v5.js'); + const credentials = tokenData.credentials || tokenData; + const cos = new COS({ + SimpleUploadMethod: 'putObject', + getAuthorization: (options, callback) => { + callback({ + TmpSecretId: credentials.tmpSecretId, + TmpSecretKey: credentials.tmpSecretKey, + SecurityToken: credentials.sessionToken || '', + StartTime: tokenData.startTime, + ExpiredTime: tokenData.expiredTime + }); + } + }); + const bucket = tokenData.bucket || 'julebu-1361527063'; + const region = tokenData.region || 'ap-shanghai'; + + const yonghuid = app.globalData.userInfo?.yonghuid || wx.getStorageSync('uid') || '0000000'; + const fadanId = this.data.detailItem.id; + const keys = []; + for (let i = 0; i < total; i++) { + keys.push(`fakuan/dashoufakuan/dashoufakuanshensu/${yonghuid}_${fadanId}_${Date.now() + i}.jpg`); + } + + for (let i = 0; i < total; i++) { + try { + await new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => resolve(), 2000); + cos.uploadFile({ + Bucket: bucket, + Region: region, + Key: keys[i], + FilePath: localPaths[i], + onProgress: (info) => { + if (Math.round(info.percent * 100) === 100) { + clearTimeout(timeoutId); + resolve(); + } + } + }, (err) => { + clearTimeout(timeoutId); + if (err) reject(err); + else resolve(); + }); + }); + } catch (err) { + wx.showToast({ title: `第 ${i + 1} 张上传失败`, icon: 'none' }); + this.setData({ uploading: false }); + return null; + } + const done = i + 1; + this.setData({ + uploadProgress: { current: done, total: total }, + uploadProgressWidth: `${((done / total) * 100).toFixed(0)}%` + }); + } + this.setData({ uploading: false }); + return keys; + }, + + // ==================== 缴纳支付 ==================== + startPay() { + this.setData({ + showDetail: false, + showPay: true, + payFadan: this.data.detailItem + }); + }, + onPayClose() { + this.setData({ showPay: false, payFadan: null }); + }, + onPaySuccess() { + this.setData({ showPay: false, payFadan: null }); + this.reload(); + }, + + // ==================== 工具方法 ==================== + getFullUrl(url) { + if (!url) return ''; + if (url.startsWith('http')) return url; + return this.data.ossImageUrl + url; + } + } +}); \ No newline at end of file diff --git a/pages/cfss/components/fakuan-list/fakuan-list.json b/pages/cfss/components/fakuan-list/fakuan-list.json new file mode 100644 index 0000000..d192c45 --- /dev/null +++ b/pages/cfss/components/fakuan-list/fakuan-list.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "fakuan-pay": "/pages/cfss/components/fakuan-pay/fakuan-pay" + } +} \ No newline at end of file diff --git a/pages/cfss/components/fakuan-list/fakuan-list.wxml b/pages/cfss/components/fakuan-list/fakuan-list.wxml new file mode 100644 index 0000000..257802a --- /dev/null +++ b/pages/cfss/components/fakuan-list/fakuan-list.wxml @@ -0,0 +1,116 @@ + + + + 加载中... + + + + {{ item.create_time }} + {{ item.display_status }} + + 金额:¥{{ item.fakuanjine }} + 理由:{{ item.chufaliyou }} + 订单:{{ item.guanliandingdan_id }} + + + + + + 点击获取更多 + + 加载中... + —— 没有更多了 —— + 暂无罚款记录 + + + + + + + 罚款详情 + + + + 罚款金额¥{{ detailItem.fakuanjine }} + 当前状态{{ detailItem.display_status }} + 处罚理由{{ detailItem.chufaliyou }} + 关联订单{{ detailItem.guanliandingdan_id }} + 驳回理由{{ detailItem.bohuiliyou }} + 申诉理由{{ detailItem.shensuliyou }} + + + + 证据图片 + + + + + + + + + + + 申诉图片 + + + + + + + + + + + 申诉 + 立即缴纳 + 关闭 + + + + + + + + + 提交申诉 + + + + + 申诉理由 + + {{ dashouJieshao.length || 0 }}/200 + + + + + + 提示:已从服务器加载最新信息 + + + + + + + + + + + + 加载中... + + + + diff --git a/pages/dashouxiugai/dashouxiugai.wxss b/pages/dashouxiugai/dashouxiugai.wxss new file mode 100644 index 0000000..653e8db --- /dev/null +++ b/pages/dashouxiugai/dashouxiugai.wxss @@ -0,0 +1,197 @@ +/* 页面容器 */ +.container { + min-height: 100vh; + background: linear-gradient(135deg, #fffacd 0%, #e6f7ff 100%); + padding: 20rpx; + box-sizing: border-box; + } + + /* 头像区域 */ + .avatar-section { + display: flex; + justify-content: center; + align-items: center; + padding: 40rpx 0; + position: relative; + } + + .avatar-wrapper { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + } + + .avatar { + width: 180rpx; + height: 180rpx; + border-radius: 50%; + border: 6rpx solid #ffffff; + box-shadow: 0 0 30rpx rgba(255, 215, 0, 0.4); + z-index: 2; + position: relative; + background-color: #f5f5f5; + } + + .avatar-halo { + position: absolute; + width: 220rpx; + height: 220rpx; + border-radius: 50%; + background: radial-gradient(circle, rgba(255, 215, 0, 0.2) 0%, transparent 70%); + z-index: 1; + animation: halo-pulse 2s infinite ease-in-out; + } + + @keyframes halo-pulse { + 0%, 100% { opacity: 0.6; transform: scale(1); } + 50% { opacity: 0.8; transform: scale(1.05); } + } + + .avatar-hint { + margin-top: 20rpx; + font-size: 26rpx; + color: #666; + text-align: center; + } + + /* 信息区域 */ + .info-section { + background: rgba(255, 255, 255, 0.9); + border-radius: 20rpx; + padding: 30rpx; + margin-bottom: 40rpx; + box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.05); + } + + .info-item { + display: flex; + flex-direction: column; + margin-bottom: 40rpx; + position: relative; + padding-bottom: 30rpx; + border-bottom: 1rpx solid #f0f0f0; + } + + .info-item:last-child { + border-bottom: none; + } + + .info-label { + font-size: 28rpx; + color: #333; + font-weight: 600; + margin-bottom: 15rpx; + } + + .info-value-disabled { + font-size: 32rpx; + color: #999; + font-weight: bold; + background: #f9f9f9; + padding: 20rpx; + border-radius: 10rpx; + } + + .info-input { + font-size: 32rpx; + color: #333; + padding: 20rpx; + background: #f9f9f9; + border-radius: 10rpx; + border: 1rpx solid #e0e0e0; + } + + .info-textarea { + font-size: 30rpx; + color: #333; + padding: 20rpx; + background: #f9f9f9; + border-radius: 10rpx; + border: 1rpx solid #e0e0e0; + min-height: 200rpx; + line-height: 1.5; + } + + .info-item-textarea { + padding-bottom: 20rpx; + } + + .char-count { + position: absolute; + right: 0; + bottom: 5rpx; + font-size: 24rpx; + color: #999; + } + + /* 状态提示 */ + .status-section { + background: #e8f4fd; + border-radius: 10rpx; + padding: 20rpx; + margin-bottom: 30rpx; + border-left: 6rpx solid #1890ff; + } + + .status-text { + font-size: 26rpx; + color: #1890ff; + } + + /* 按钮区域 */ + .button-section { + padding: 20rpx 0 40rpx; + } + + .submit-btn { + background: linear-gradient(90deg, #52c41a, #73d13d); + color: white; + font-size: 32rpx; + font-weight: 600; + height: 90rpx; + line-height: 70rpx; + border-radius: 45rpx; + border: none; + box-shadow: 0 10rpx 20rpx rgba(82, 196, 26, 0.2); + } + + .submit-btn:disabled { + background: #cccccc; + box-shadow: none; + } + + .submit-btn::after { + border: none; + } + + /* 加载中 */ + .loading-wrapper { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(255, 255, 255, 0.9); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; + } + + .loading-content { + display: flex; + flex-direction: column; + align-items: center; + } + + .loading-icon { + width: 120rpx; + height: 120rpx; + margin-bottom: 30rpx; + } + + .loading-text { + font-size: 28rpx; + color: #666; + } \ No newline at end of file diff --git a/pages/dingdan/dingdan.js b/pages/dingdan/dingdan.js new file mode 100644 index 0000000..9e31b53 --- /dev/null +++ b/pages/dingdan/dingdan.js @@ -0,0 +1,287 @@ +// pages/dingdan/dingdan.js +const app = getApp() +import request from '../../utils/request.js' + +Page({ + data: { + // 订单状态tab配置 + dingdanZhuangtaiTabs: [ + { name: '全部', key: 'all', zhuangtaiList: [1, 2, 3, 4, 5, 6, 7, 8] }, + { name: '已下单', key: 'yixiadan', zhuangtaiList: [1, 7] }, + { name: '进行中', key: 'jinxingzhong', zhuangtaiList: [2] }, + { name: '争议订单', key: 'zhengyi', zhuangtaiList: [4, 5, 6] }, + { name: '已完成', key: 'yiwancheng', zhuangtaiList: [3] }, + { name: '结算中', key: 'jiesuanzhong', zhuangtaiList: [8] } + ], + + // 当前选中的tab索引 + xuanzhongTabIndex: 0, + // 当前选中的tab的key + currentTabKey: 'all', + + // 每个tab对应的订单数据 + dingdanShuju: { + all: { list: [], page: 1, hasMore: true, isLoading: false }, + yixiadan: { list: [], page: 1, hasMore: true, isLoading: false }, + jinxingzhong: { list: [], page: 1, hasMore: true, isLoading: false }, + zhengyi: { list: [], page: 1, hasMore: true, isLoading: false }, + yiwancheng: { list: [], page: 1, hasMore: true, isLoading: false }, + jiesuanzhong: { list: [], page: 1, hasMore: true, isLoading: false } + }, + + // 个人中心传递过来的type参数 + gerenzhongxinType: '', + + // 是否显示加载更多 + xianshiJiazaigengduo: false, + + // 页面加载状态 + isLoading: true + }, + + onLoad(options) { + const type = options.type || '' + + wx.setNavigationBarTitle({ + title: '我的订单' + }) + + this.qingkongGerenzhongxinJishu(type) + + const tabIndex = this.yingsheTypeToTabIndex(type) + const tabKey = this.data.dingdanZhuangtaiTabs[tabIndex].key + + this.setData({ + gerenzhongxinType: type, + xuanzhongTabIndex: tabIndex, + currentTabKey: tabKey, + isLoading: false + }) + + this.jiazaiDingdanShuju(tabIndex) + }, + onShow() { + // 原有代码... + + // 🆕 注册通知组件 + this.registerNotificationComponent(); +}, + +// 🆕 新增:注册通知组件 +registerNotificationComponent() { + const app = getApp(); + const notificationComp = this.selectComponent('#global-notification'); + + if (notificationComp && notificationComp.showNotification) { + console.log('🏪 商城页面注册通知组件'); + app.globalData.globalNotification = { + show: (data) => notificationComp.showNotification(data), + hide: () => notificationComp.hideNotification() + }; + } +}, + + + qingkongGerenzhongxinJishu(type) { + const jishuMap = { + 'daifuwu': 'daifuwu', + 'fuwuzhong': 'fuwuzhong', + 'yiwancheng': 'yiwancheng', + 'yituikuan': 'yituikuan' + } + + const jishuKey = jishuMap[type] + if (jishuKey && app.globalData.dingdanTiaoshu) { + app.globalData.dingdanTiaoshu[jishuKey] = 0 + } + }, + + yingsheTypeToTabIndex(type) { + const yingsheMap = { + 'daifuwu': 1, + 'fuwuzhong': 2, + 'yiwancheng': 4, + 'yituikuan': 3 + } + + return yingsheMap[type] || 0 + }, + + switchDingdanTab(e) { + const tabIndex = e.currentTarget.dataset.index + if (tabIndex === this.data.xuanzhongTabIndex) return + + const tabKey = this.data.dingdanZhuangtaiTabs[tabIndex].key + + this.setData({ + xuanzhongTabIndex: tabIndex, + currentTabKey: tabKey + }) + + this.jiazaiDingdanShuju(tabIndex) + }, + + async jiazaiDingdanShuju(tabIndex, isLoadMore = false) { + const tab = this.data.dingdanZhuangtaiTabs[tabIndex] + const tabKey = tab.key + + const tabData = this.data.dingdanShuju[tabKey] + + const page = isLoadMore ? tabData.page + 1 : 1 + + if (tabData.isLoading) return + + // 重置逻辑:如果不是加载更多,清空已有数据 + if (!isLoadMore) { + this.setData({ + [`dingdanShuju.${tabKey}.list`]: [], + [`dingdanShuju.${tabKey}.page`]: 1 + }) + } + + this.setData({ + [`dingdanShuju.${tabKey}.isLoading`]: true + }) + + wx.showLoading({ + title: isLoadMore ? '加载更多...' : '加载订单...', + mask: true + }) + + try { + const requestData = { + zhuangtai_list: tab.zhuangtaiList, + page: page, + page_size: 10 + } + + + + const res = await request({ + url: '/dingdan/dingdanhuoqu', + method: 'POST', + data: requestData, + header: { + 'content-type': 'application/json' + } + }) + + + + wx.hideLoading() + + // ✅ 关键修正:res是直接返回的数据对象,不是res.data + if (res && res.data.code === 0) { + // ✅ 关键修正:直接使用res.data.list + const newDingdanList = res.data.data.list || [] + const hasMore = res.data.data.has_more || false + + + + // 处理图片URL,拼接完整路径 + const processedList = newDingdanList.map(item => { + let tupianUrl = item.tupian || '' + + // 如果图片URL不是完整路径,拼接ossImageUrl + if (tupianUrl && !tupianUrl.startsWith('http')) { + const ossImageUrl = app.globalData.ossImageUrl || '' + tupianUrl = ossImageUrl + tupianUrl + } + + const zhuangtaiZh = this.getZhuangtaiZhongwen(item.zhuangtai) + + + return { + ...item, + tupian: tupianUrl, + zhuangtaiZh: zhuangtaiZh + } + }) + + const currentList = this.data.dingdanShuju[tabKey].list + const updatedList = isLoadMore + ? [...currentList, ...processedList] + : processedList + + this.setData({ + [`dingdanShuju.${tabKey}.list`]: updatedList, + [`dingdanShuju.${tabKey}.page`]: page, + [`dingdanShuju.${tabKey}.hasMore`]: hasMore, + [`dingdanShuju.${tabKey}.isLoading`]: false, + xianshiJiazaigengduo: hasMore + }) + + } else { + wx.showToast({ + title: res ? (res.msg || '加载失败') : '请求失败', + icon: 'none', + duration: 2000 + }) + + this.setData({ + [`dingdanShuju.${tabKey}.isLoading`]: false + }) + } + + } catch (error) { + console.error('加载订单数据失败:', error) + wx.hideLoading() + wx.showToast({ + title: '网络请求失败,请重试', + icon: 'none', + duration: 2000 + }) + + this.setData({ + [`dingdanShuju.${tabKey}.isLoading`]: false + }) + } + }, + + getZhuangtaiZhongwen(zhuangtai) { + const zhuangtaiMap = { + 1: '已下单', + 2: '进行中', + 3: '已完成', + 4: '退款中', + 5: '已退款', + 6: '退款失败', + 7: '指定中', + 8: '结算中' + } + + return zhuangtaiMap[zhuangtai] || '未知状态' + }, + + onReachBottom() { + const tabKey = this.data.currentTabKey + const tabData = this.data.dingdanShuju[tabKey] + + if (tabData.hasMore && !tabData.isLoading) { + const tabIndex = this.data.xuanzhongTabIndex + this.jiazaiDingdanShuju(tabIndex, true) + } + }, + + goToDingdanXiangqing(e) { + const dingdanItem = e.currentTarget.dataset.item + if (!dingdanItem) return + + const dingdanDataStr = encodeURIComponent(JSON.stringify(dingdanItem)) + + wx.navigateTo({ + url: `/pages/dingdanxiangqing/dingdanxiangqing?dingdanData=${dingdanDataStr}` + }) + }, + + onImageError(e) { + const index = e.currentTarget.dataset.index + const tabKey = this.data.currentTabKey + + const defaultImage = app.globalData.lunbozhanwei || '/images/default.jpg' + + this.setData({ + [`dingdanShuju.${tabKey}.list[${index}].tupian`]: defaultImage + }) + } +}) \ No newline at end of file diff --git a/pages/dingdan/dingdan.json b/pages/dingdan/dingdan.json new file mode 100644 index 0000000..837f016 --- /dev/null +++ b/pages/dingdan/dingdan.json @@ -0,0 +1,5 @@ +{ + "usingComponents": { + "global-notification": "/components/global-notification/global-notification" + } +} \ No newline at end of file diff --git a/pages/dingdan/dingdan.wxml b/pages/dingdan/dingdan.wxml new file mode 100644 index 0000000..9d64425 --- /dev/null +++ b/pages/dingdan/dingdan.wxml @@ -0,0 +1,111 @@ + + + + + + + + + {{item.name}} + + + + + + + + + + + + + 加载中... + + + + + + 暂无订单 + 快去下单体验服务吧 + + + + + + + + + + + + + + + {{item.jieshao || '暂无描述'}} + + + + + + + + {{item.zhuangtaiZh || '未知状态'}} + + + + + ¥ + {{item.jine || '0.00'}} + + + + + + + + + + 上拉加载更多 + + + + + + 没有更多订单了 + + + + + + diff --git a/pages/dingdan/dingdan.wxss b/pages/dingdan/dingdan.wxss new file mode 100644 index 0000000..afb2316 --- /dev/null +++ b/pages/dingdan/dingdan.wxss @@ -0,0 +1,262 @@ +/* pages/dingdan/dingdan.wxss */ + +.dingdan-page { + min-height: 100vh; + background-color: #f8f9fa; + padding-bottom: 20rpx; + } + + .dingdan-tabs-scroll { + width: 100%; + height: 90rpx; + white-space: nowrap; + background-color: #ffffff; + border-bottom: 1rpx solid #f0f0f0; + box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.03); + } + + .dingdan-tabs-container { + display: flex; + align-items: center; + height: 100%; + padding: 0 20rpx; + } + + .dingdan-tab-item { + display: inline-flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 60rpx; + padding: 0 30rpx; + margin-right: 20rpx; + border-radius: 30rpx; + background-color: #f5f5f5; + color: #666666; + font-size: 28rpx; + transition: all 0.3s ease; + position: relative; + flex-shrink: 0; + } + + .dingdan-tab-item:last-child { + margin-right: 0; + } + + .dingdan-tab-item.tab-active { + background-color: #e8f5e9; + color: #2e7d32; + font-weight: 500; + box-shadow: 0 4rpx 12rpx rgba(46, 125, 50, 0.1); + } + + .tab-active-line { + width: 40rpx; + height: 4rpx; + background-color: #4caf50; + border-radius: 2rpx; + margin-top: 6rpx; + animation: fadeIn 0.3s ease; + } + + @keyframes fadeIn { + from { opacity: 0; transform: scale(0.8); } + to { opacity: 1; transform: scale(1); } + } + + .tab-text { + font-weight: 400; + } + + .dingdan-list-container { + padding: 20rpx; + } + + .loading-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 400rpx; + } + + .loading-spinner { + width: 60rpx; + height: 60rpx; + border: 6rpx solid #e0e0e0; + border-top-color: #4caf50; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-bottom: 20rpx; + } + + @keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } + } + + .loading-text { + font-size: 28rpx; + color: #999999; + } + + .empty-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 500rpx; + padding-top: 80rpx; + } + + .empty-image { + width: 200rpx; + height: 200rpx; + margin-bottom: 30rpx; + opacity: 0.6; + } + + .empty-text { + font-size: 32rpx; + color: #999999; + margin-bottom: 10rpx; + } + + .empty-subtext { + font-size: 26rpx; + color: #cccccc; + } + + .dingdan-card { + display: flex; + align-items: center; + background-color: #ffffff; + border-radius: 16rpx; + padding: 24rpx; + margin-bottom: 20rpx; + box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05); + transition: transform 0.2s ease, box-shadow 0.2s ease; + } + + .dingdan-card:active { + transform: translateY(-2rpx); + box-shadow: 0 6rpx 24rpx rgba(0, 0, 0, 0.08); + } + + .dingdan-card-left { + flex-shrink: 0; + margin-right: 24rpx; + } + + .dingdan-image { + width: 160rpx; + height: 160rpx; + border-radius: 12rpx; + background-color: #f5f5f5; + } + + .dingdan-card-center { + flex: 1; + height: 160rpx; + display: flex; + align-items: flex-start; + } + + .dingdan-jieshao { + font-size: 28rpx; + line-height: 40rpx; + color: #333333; + font-weight: 400; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + overflow: hidden; + text-overflow: ellipsis; + } + + .dingdan-card-right { + display: flex; + flex-direction: column; + align-items: flex-end; + justify-content: space-between; + height: 160rpx; + margin-left: 20rpx; + } + + .dingdan-zhuangtai { + font-size: 24rpx; + color: #ff6b6b; + font-weight: 500; + padding: 6rpx 16rpx; + border-radius: 20rpx; + background-color: #fff5f5; + white-space: nowrap; + } + + /* ✅ 修正后的价格样式 - 简单明了 */ + .dingdan-jine { + display: flex; + align-items: center; /* 垂直居中 */ + } + + .jine-symbol { + font-size: 28rpx; + color: #333333; + font-weight: 500; + margin-right: 4rpx; /* 添加右边距,让符号和数字有间隔 */ + } + + .jine-number { + font-size: 32rpx; /* 整数和小数一样大 */ + color: #333333; + font-weight: 600; + } + + .load-more-container { + display: flex; + align-items: center; + justify-content: center; + padding: 40rpx 0; + } + + .load-more-line { + width: 80rpx; + height: 1rpx; + background-color: #e0e0e0; + margin: 0 20rpx; + } + + .load-more-text { + font-size: 26rpx; + color: #999999; + white-space: nowrap; + } + + .no-more-container { + text-align: center; + padding: 40rpx 0; + } + + .no-more-text { + font-size: 26rpx; + color: #cccccc; + } + + @media (max-width: 375px) { + .dingdan-image { + width: 140rpx; + height: 140rpx; + } + + .dingdan-card-center { + height: 140rpx; + } + + .dingdan-card-right { + height: 140rpx; + } + + .jine-number { + font-size: 30rpx; + } + } \ No newline at end of file diff --git a/pages/dingdanxiangqing/dingdanxiangqing.js b/pages/dingdanxiangqing/dingdanxiangqing.js new file mode 100644 index 0000000..d04a153 --- /dev/null +++ b/pages/dingdanxiangqing/dingdanxiangqing.js @@ -0,0 +1,310 @@ +const app = getApp() +import request from '../../utils/request.js' +import connectionManager from '../../utils/xiaoxilj.js' + +Page({ + data: { + isLoading: true, + jibenShuju: { + dingdanId: '', + tupian: '', + jieshao: '', + chuangjianshijian: '', + jine: '0.00', + nicheng: '', + beizhu: '', + zhuangtai: 0, + updateTime: '' + }, + xiangxiShuju: { + dashou: '', + dashouNicheng: '', + dashouTouxiang: '', + dashoujiaofu: [], + dashouliuyan: '', + jiedanTime: '', + tijiaoTime: '', + laobanPingjia: '', + shangxianSudu: null, + fuwuTaidu: null, + fancheCishu: null + }, + zhuangtaiWenziMap: { + 1: '已下单', 2: '进行中', 3: '已完成', 4: '退款中', + 5: '已退款', 6: '退款失败', 7: '指定中', 8: '结算中' + }, + jiesuan: { + fancheCishu: '', + speedValue: 0, + attitudeValue: 0, + liuyan: '' + }, + // 🔥 实时解释文字(必须在 data 中声明,才能用于 wxml 绑定) + speedExplain: '', + attitudeExplain: '', + speedOptions: [ + { value: 5, label: '极速', explain: '接单后秒上线,2分钟以内' }, + { value: 4, label: '快', explain: '接单后5分钟以内上线' }, + { value: 3, label: '一般', explain: '接单后10分钟以内上线' }, + { value: 2, label: '慢', explain: '接单后15分钟以内上线' }, + { value: 1, label: '极慢', explain: '接单后15分钟以后上线' } + ], + attitudeOptions: [ + { value: 5, label: '极好', explain: '主动聊天哄开心,情绪价值拉满,处处护着老板' }, + { value: 4, label: '好', explain: '主动互动,保护老板,说话语气温和' }, + { value: 3, label: '一般', explain: '正常保护与聊天,以任务为主,情绪价值一般' }, + { value: 2, label: '较差', explain: '交互少,很少说话,纯应付订单,偶尔埋怨' }, + { value: 1, label: '极差', explain: '全程无交流或骂老板,体验极差,很愤怒' } + ], + showSpeedModal: false, + showAttitudeModal: false, + showConfirm: false, + countdown: 3, + countdownTimer: null, + showMyEvaluation: false + }, + + onLoad(options) { + wx.setNavigationBarTitle({ title: '订单详情' }) + this.parseParams(options) + }, + + parseParams(options) { + try { + const dingdanDataStr = options.dingdanData || '' + if (!dingdanDataStr) { + wx.showToast({ title: '参数错误', icon: 'none' }) + setTimeout(() => wx.navigateBack(), 1500) + return + } + const data = JSON.parse(decodeURIComponent(dingdanDataStr)) + const ossImageUrl = app.globalData.ossImageUrl || '' + let tupianUrl = data.tupian || '' + if (tupianUrl && !tupianUrl.startsWith('http')) { + tupianUrl = ossImageUrl + tupianUrl + } + if (!tupianUrl) { + tupianUrl = ossImageUrl + (app.globalData.morentouxiang || '/images/default-avatar.png') + } + const jine = parseFloat(data.jine || 0).toFixed(2) + + this.setData({ + 'jibenShuju.dingdanId': data.dingdanId || '', + 'jibenShuju.tupian': tupianUrl, + 'jibenShuju.jieshao': data.jieshao || '', + 'jibenShuju.chuangjianshijian': data.chuangjianshijian || '', + 'jibenShuju.jine': jine, + 'jibenShuju.nicheng': data.nicheng || '', + 'jibenShuju.beizhu': data.beizhu || '', + 'jibenShuju.zhuangtai': data.zhuangtai || 0, + isLoading: false + }) + this.loadDetail(data.dingdanId) + } catch (e) { + console.error('参数解析失败', e) + wx.showToast({ title: '数据解析错误', icon: 'none' }) + } + }, + + async loadDetail(dingdanId) { + try { + const res = await request({ + url: '/dingdan/dingdanxiangqing2', + method: 'POST', + data: { dingdanId } + }) + if (res && res.data.code === 0 && res.data.data) { + const d = res.data.data + const oss = app.globalData.ossImageUrl || '' + let dashouTouxiang = d.dashou_touxiang || '' + if (dashouTouxiang && !dashouTouxiang.startsWith('http')) { + dashouTouxiang = oss + dashouTouxiang + } + if (!dashouTouxiang) { + dashouTouxiang = oss + (app.globalData.morentouxiang || '/images/default-avatar.png') + } + const jiaofuList = (d.dashoujiaofu || []).map(url => { + if (url && !url.startsWith('http')) return oss + url + return url + }) + const shangxian = d.shangxian_sudu + const taidu = d.fuwu_taidu + const fanche = d.fanche_cishu + const showEval = (shangxian !== null && shangxian !== undefined) || + (taidu !== null && taidu !== undefined) || + (fanche !== null && fanche !== undefined) + + this.setData({ + 'jibenShuju.zhuangtai': d.zhuangtai || 0, + 'jibenShuju.updateTime': d.update_time || '', + 'xiangxiShuju.dashou': d.dashou || '', + 'xiangxiShuju.dashouNicheng': d.dashou_nicheng || '', + 'xiangxiShuju.dashouTouxiang': dashouTouxiang, + 'xiangxiShuju.dashoujiaofu': jiaofuList, + 'xiangxiShuju.dashouliuyan': d.dashouliuyan || '', + 'xiangxiShuju.jiedanTime': d.jiedan_time || '', + 'xiangxiShuju.tijiaoTime': d.tijiao_time || '', + 'xiangxiShuju.laobanPingjia': d.laoban_pingjia || '', + 'xiangxiShuju.shangxianSudu': shangxian, + 'xiangxiShuju.fuwuTaidu': taidu, + 'xiangxiShuju.fancheCishu': fanche, + showMyEvaluation: showEval && [3,4,5,6].includes(d.zhuangtai) + }) + this.computeEvaluationText() + } else { + wx.showToast({ title: '获取详情失败', icon: 'none' }) + } + } catch (e) { + console.error('加载详情失败', e) + wx.showToast({ title: '网络错误', icon: 'none' }) + } + }, + + // 计算历史评价的文字 + computeEvaluationText() { + const { speedOptions, attitudeOptions, xiangxiShuju } = this.data + let speedText = '未评价' + let attitudeText = '未评价' + if (xiangxiShuju.shangxianSudu != null) { + const sItem = speedOptions.find(v => v.value == xiangxiShuju.shangxianSudu) + speedText = sItem ? sItem.label + ' (' + sItem.explain + ')' : '' + } + if (xiangxiShuju.fuwuTaidu != null) { + const aItem = attitudeOptions.find(v => v.value == xiangxiShuju.fuwuTaidu) + attitudeText = aItem ? aItem.label + ' (' + aItem.explain + ')' : '' + } + this.setData({ speedText, attitudeText }) + }, + + onGoodsImgError() { + const fallback = app.globalData.ossImageUrl + (app.globalData.morentouxiang || '/images/default-avatar.png') + this.setData({ 'jibenShuju.tupian': fallback }) + }, + + copyText(e) { + const text = e.currentTarget.dataset.text + if (text) { + wx.setClipboardData({ data: text, success: () => wx.showToast({ title: '已复制', icon: 'none' }) }) + } + }, + + previewImages(e) { + const current = e.currentTarget.dataset.url + const urls = e.currentTarget.dataset.urls || [current] + wx.previewImage({ current, urls }) + }, + + // 联系打手(防止角色被篡改) + contactDashou() { + const uid = wx.getStorageSync('uid') + if (!uid) { + wx.showToast({ title: '用户信息缺失', icon: 'none' }) + return + } + const dingdanId = this.data.jibenShuju.dingdanId + + // 备份全局角色 + const savedRole = app.globalData.currentRole + const savedXshenfen = app.globalData.xshenfen + + connectionManager.connectToGroupChat({ + identityType: 'normal', + userId: 'Boss' + uid, + orderId: dingdanId, + groupName: this.data.jibenShuju.nicheng + '的订单群', + isCross: 0 + }).catch(err => { + console.error('跳转群聊失败', err) + wx.showToast({ title: '跳转聊天失败,请检查网络', icon: 'none' }) + }) + + // 立即恢复角色 + app.globalData.currentRole = savedRole + app.globalData.xshenfen = savedXshenfen + wx.setStorageSync('currentRole', savedRole) + }, + + // 结算相关 + onFancheshuInput(e) { this.setData({ 'jiesuan.fancheCishu': e.detail.value }) }, + + selectSpeed(e) { + const value = parseInt(e.currentTarget.dataset.value) + const explain = this.data.speedOptions.find(v => v.value === value)?.explain || '' + this.setData({ + 'jiesuan.speedValue': value, + speedExplain: explain // 更新解释文字 + }) + }, + selectAttitude(e) { + const value = parseInt(e.currentTarget.dataset.value) + const explain = this.data.attitudeOptions.find(v => v.value === value)?.explain || '' + this.setData({ + 'jiesuan.attitudeValue': value, + attitudeExplain: explain + }) + }, + onLiuyanInput(e) { this.setData({ 'jiesuan.liuyan': e.detail.value.slice(0, 50) }) }, + + showSpeedStandard() { this.setData({ showSpeedModal: true }) }, + hideSpeedModal() { this.setData({ showSpeedModal: false }) }, + showAttitudeStandard() { this.setData({ showAttitudeModal: true }) }, + hideAttitudeModal() { this.setData({ showAttitudeModal: false }) }, + noop() {}, + + showSettleConfirm() { + const { fancheCishu } = this.data.jiesuan + if (!fancheCishu || isNaN(parseInt(fancheCishu))) { + wx.showToast({ title: '请填写翻车次数', icon: 'none' }) + return + } + if (!this.data.jiesuan.speedValue || !this.data.jiesuan.attitudeValue) { + wx.showToast({ title: '请选择上线速度和服务态度', icon: 'none' }) + return + } + this.setData({ showConfirm: true, countdown: 3 }) + this.countdownTimer = setInterval(() => { + const next = this.data.countdown - 1 + if (next <= 0) { + clearInterval(this.countdownTimer) + this.setData({ countdown: 0 }) + } else { + this.setData({ countdown: next }) + } + }, 1000) + }, + cancelConfirm() { + clearInterval(this.countdownTimer) + this.setData({ showConfirm: false, countdown: 3 }) + }, + async confirmSettle() { + if (this.data.countdown !== 0) return + clearInterval(this.countdownTimer) + this.setData({ showConfirm: false, countdown: 3 }) + + wx.showLoading({ title: '结算中' }) + const { jiesuan, jibenShuju } = this.data + try { + const res = await request({ + url: '/dingdan/jiedan2', + method: 'POST', + data: { + dingdanId: jibenShuju.dingdanId, + fanche_cishu: parseInt(jiesuan.fancheCishu), + shangxian_sudu: jiesuan.speedValue, + fuwu_taidu: jiesuan.attitudeValue, + liuyan: jiesuan.liuyan + } + }) + wx.hideLoading() + if (res && res.data.code === 0) { + wx.showToast({ title: '结算成功' }) + setTimeout(() => wx.navigateBack(), 1000) + } else { + wx.showToast({ title: res?.data?.msg || '结算失败', icon: 'none' }) + } + } catch (e) { + wx.hideLoading() + wx.showToast({ title: '网络错误', icon: 'none' }) + } + } +}) \ No newline at end of file diff --git a/pages/dingdanxiangqing/dingdanxiangqing.json b/pages/dingdanxiangqing/dingdanxiangqing.json new file mode 100644 index 0000000..837f016 --- /dev/null +++ b/pages/dingdanxiangqing/dingdanxiangqing.json @@ -0,0 +1,5 @@ +{ + "usingComponents": { + "global-notification": "/components/global-notification/global-notification" + } +} \ No newline at end of file diff --git a/pages/dingdanxiangqing/dingdanxiangqing.wxml b/pages/dingdanxiangqing/dingdanxiangqing.wxml new file mode 100644 index 0000000..c941219 --- /dev/null +++ b/pages/dingdanxiangqing/dingdanxiangqing.wxml @@ -0,0 +1,203 @@ + + + + + + 加载中... + + + + + + + + {{jibenShuju.jieshao || '暂无描述'}} + + + + + 订单信息 + + 游戏昵称 + {{jibenShuju.nicheng}} + 复制 + + + 备注 + {{jibenShuju.beizhu}} + + + 订单ID + {{jibenShuju.dingdanId}} + 复制 + + + 价格 + ¥{{jibenShuju.jine}} + + + 状态 + {{zhuangtaiWenziMap[jibenShuju.zhuangtai] || '未知'}} + + + 创建时间 + {{jibenShuju.chuangjianshijian}} + + + 更新时间 + {{jibenShuju.updateTime}} + + + + + + 服务打手 + + 头像 + + + + 昵称 + {{xiangxiShuju.dashouNicheng || '未设置'}} + + + 打手ID + {{xiangxiShuju.dashou}} + 复制 + + + 接单时间 + {{xiangxiShuju.jiedanTime}} + + + 提交时间 + {{xiangxiShuju.tijiaoTime}} + + + 联系打手 + + + + + 交付截图 + + + + + + + + 打手留言 + {{xiangxiShuju.dashouliuyan}} + + + + + 我的评价 + + 上线速度 + {{speedText}} + + + 服务态度 + {{attitudeText}} + + + 翻车次数 + {{xiangxiShuju.fancheCishu}} + + + 我的留言 + {{xiangxiShuju.laobanPingjia}} + + + + + + 订单结算 + + + + 翻车次数 + + 填写该订单翻车次数,用于评价打手实力 + + + + + + 上线速度 + 标准详情 + + + + {{item.label}} + + + + {{speedExplain}} + + + + + + 服务态度 + 标准详情 + + + + {{item.label}} + + + {{attitudeExplain}} + + + + + 留言(选填) + + {{jiesuan.liuyan.length}}/50 + + + 确认结算 + + + + + + + 上线速度标准 + + + {{item.label}}({{item.value}}分) + {{item.explain}} + + + 关闭 + + + + + + + 服务态度标准 + + + {{item.label}}({{item.value}}分) + {{item.explain}} + + + 关闭 + + + + + + + 确认结算订单? + 结算后订单将变为已完成,确认无误后再点击按钮({{countdown}}s) + 确认结算 + 取消 + + + \ No newline at end of file diff --git a/pages/dingdanxiangqing/dingdanxiangqing.wxss b/pages/dingdanxiangqing/dingdanxiangqing.wxss new file mode 100644 index 0000000..daeb265 --- /dev/null +++ b/pages/dingdanxiangqing/dingdanxiangqing.wxss @@ -0,0 +1,337 @@ +/* 页面整体 */ +.page { + min-height: 100vh; + background: #F2F2F7; + padding-bottom: 40rpx; + } + + /* 加载 */ + .loading-box { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 60vh; + } + .spinner { + width: 70rpx; height: 70rpx; + border: 6rpx solid #E0E0E0; + border-top-color: #007AFF; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-bottom: 20rpx; + } + @keyframes spin { to { transform: rotate(360deg); } } + .loading-text { font-size: 28rpx; color: #888; } + + /* 主内容 */ + .main { padding: 30rpx; } + + /* 商品区块 */ + .goods-box { + display: flex; + background: #fff; + border-radius: 20rpx; + padding: 25rpx; + margin-bottom: 25rpx; + box-shadow: 0 4rpx 15rpx rgba(0,0,0,0.03); + } + .goods-img { + width: 180rpx; height: 180rpx; + border-radius: 14rpx; + margin-right: 20rpx; + background: #EFEFEF; + } + .goods-text { + flex: 1; + font-size: 30rpx; + line-height: 44rpx; + color: #1A1A1A; + font-weight: 500; + } + + /* 通用信息块 */ + .block { + background: #FFFFFF; + border-radius: 20rpx; + padding: 30rpx 25rpx; + margin-bottom: 25rpx; + box-shadow: 0 4rpx 15rpx rgba(0,0,0,0.03); + } + .block-title { + font-size: 32rpx; + font-weight: 700; + color: #1A1A1A; + margin-bottom: 25rpx; + padding-bottom: 10rpx; + border-bottom: 1rpx solid #EFEFEF; + } + + /* 行 */ + .row { + display: flex; + align-items: center; + padding: 10rpx 0; + } + .row.vertical { + flex-direction: column; + align-items: flex-start; + } + .label { + font-size: 28rpx; + color: #666; + width: 160rpx; + flex-shrink: 0; + font-weight: 500; + } + .label-row { + display: flex; + justify-content: space-between; + width: 100%; + } + .value { + font-size: 28rpx; + color: #1A1A1A; + font-weight: 500; + flex: 1; + word-break: break-all; + } + .price { + color: #FF3B30; + font-weight: 700; + font-size: 32rpx; + } + .status-text { + font-weight: 700; + color: #007AFF; + } + .highlight { + color: #007AFF; + font-weight: 600; + } + + /* 复制按钮 */ + .copy-btn { + font-size: 24rpx; + color: #007AFF; + background: #F0F0F5; + padding: 6rpx 18rpx; + border-radius: 30rpx; + flex-shrink: 0; + margin-left: 15rpx; + } + + /* 头像 */ + .avatar { + width: 80rpx; height: 80rpx; + border-radius: 50%; + background: #EFEFEF; + } + + /* 联系打手按钮 */ + .contact-btn { + margin-top: 20rpx; + width: 100%; + height: 80rpx; + line-height: 80rpx; + text-align: center; + background: #007AFF; + color: #fff; + font-size: 30rpx; + border-radius: 40rpx; + font-weight: 600; + } + + /* 九宫格图片(正方形) */ + .grid-nine { + display: flex; + flex-wrap: wrap; + margin: -6rpx; + } + .grid-img { + width: 210rpx; + height: 210rpx; + margin: 6rpx; + border-radius: 10rpx; + background: #F2F2F2; + } + + /* 打手留言 */ + .msg-text { + background: #F9F9F9; + padding: 20rpx; + border-radius: 12rpx; + font-size: 28rpx; + color: #333; + line-height: 42rpx; + } + + /* 选项 */ + .options-grid { + display: flex; + flex-wrap: wrap; + margin: 15rpx 0 5rpx; + } + .option-card { + background: #F7F7F9; + border: 2rpx solid transparent; + padding: 12rpx 24rpx; + border-radius: 30rpx; + margin-right: 15rpx; + margin-bottom: 12rpx; + transition: all 0.2s; + } + .option-card.active { + background: #007AFF; + border-color: #007AFF; + color: #fff; + font-weight: 700; + box-shadow: 0 4rpx 10rpx rgba(0,122,255,0.3); + } + .option-label { + font-size: 26rpx; + color: inherit; + } + + /* 解释文字(大号清晰) */ + .explain-text { + font-size: 28rpx; + color: #007AFF; + font-weight: 500; + margin-top: 12rpx; + line-height: 1.6; + } + + /* 标准详情链接 */ + .standard-link { + font-size: 24rpx; + color: #007AFF; + text-decoration: underline; + } + + /* 输入框 */ + .input { + width: 100%; + height: 90rpx; + background: #F9F9F9; + border-radius: 12rpx; + border: 1rpx solid #E0E0E0; + padding: 0 18rpx; + font-size: 28rpx; + margin-top: 10rpx; + box-sizing: border-box; + } + .textarea { + width: 100%; + height: 180rpx; + background: #F9F9F9; + border-radius: 12rpx; + border: 1rpx solid #E0E0E0; + padding: 15rpx 18rpx; + font-size: 28rpx; + margin-top: 10rpx; + box-sizing: border-box; + } + .hint { + font-size: 24rpx; + color: #FF9500; + margin-top: 8rpx; + } + .count { + text-align: right; + font-size: 24rpx; + color: #999; + margin-top: 5rpx; + } + + /* 结算按钮 */ + .settle-btn { + margin-top: 40rpx; + margin-bottom: 40rpx; + width: 100%; + height: 90rpx; + line-height: 90rpx; + text-align: center; + background: linear-gradient(135deg, #007AFF, #5856D6); + color: #fff; + font-size: 32rpx; + font-weight: 700; + border-radius: 45rpx; + } + + /* 弹窗 */ + .standard-modal, .confirm-modal { + position: fixed; + top: 0; left: 0; + width: 100%; height: 100%; + background: rgba(0,0,0,0.4); + display: flex; + align-items: flex-end; + justify-content: center; + z-index: 1000; + animation: fadeIn 0.2s; + } + .modal-content { + width: 100%; + background: #fff; + border-radius: 30rpx 30rpx 0 0; + padding: 40rpx 30rpx 50rpx; + max-height: 70vh; + overflow: hidden; + } + .modal-title { + font-size: 34rpx; + font-weight: 700; + text-align: center; + margin-bottom: 30rpx; + } + .modal-body { + max-height: 50vh; + } + .standard-item { + padding: 20rpx 0; + border-bottom: 1rpx solid #F0F0F0; + } + .s-label { font-size: 28rpx; font-weight: 600; color: #1A1A1A; } + .s-desc { font-size: 26rpx; color: #666; margin-top: 8rpx; } + .modal-close { + margin-top: 25rpx; + text-align: center; + font-size: 30rpx; + color: #007AFF; + font-weight: 600; + } + + /* 结算确认弹窗 */ + .confirm-modal { + align-items: center; + } + .confirm-box { + width: 85%; + background: #fff; + border-radius: 24rpx; + padding: 50rpx 35rpx; + text-align: center; + box-shadow: 0 20rpx 40rpx rgba(0,0,0,0.1); + } + .confirm-title { font-size: 36rpx; font-weight: 700; color: #1A1A1A; margin-bottom: 20rpx; } + .confirm-desc { font-size: 26rpx; color: #666; margin-bottom: 40rpx; line-height: 1.6; } + .confirm-btn { + width: 100%; + height: 90rpx; + line-height: 90rpx; + background: #E0E0E0; + color: #999; + font-size: 32rpx; + border-radius: 45rpx; + margin-bottom: 20rpx; + } + .confirm-btn.active { + background: #007AFF; + color: #fff; + font-weight: 700; + box-shadow: 0 6rpx 15rpx rgba(0,122,255,0.3); + } + .cancel-btn { font-size: 28rpx; color: #888; } + + @keyframes fadeIn { from { opacity: 0; transform: translateY(10rpx); } to { opacity: 1; transform: translateY(0); } } \ No newline at end of file diff --git a/pages/dsddxq/dsddxq.js b/pages/dsddxq/dsddxq.js new file mode 100644 index 0000000..b81660b --- /dev/null +++ b/pages/dsddxq/dsddxq.js @@ -0,0 +1,741 @@ +// pages/dsddxq/dsddxq.js - 【最终版:提交前公告弹窗 + 修改图片至少保留一张】 +const app = getApp() +import request from '../../utils/request.js' +const COS = require('../../utils/cos-wx-sdk-v5.js') + +Page({ + data: { + jibenShuju: { + dingdan_id: '', + tupian: '', + jieshao: '', + create_time: '', + jine: '', + nicheng: '', + beizhu: '', + zhuangtai: 0, + fadanpingtai: 0 + }, + xiangxiShuju: { + shangjia_id: '', + shangjia_nicheng: '', + shangjia_liuyan: '', + tuikuan_liyou: '', + chufa_liyou: '', + chufa_zhuangtai: 0, + chufa_jieguo: '', + bohui_liyou: '', + dashou_jiaofu: [], + dashou_liuyan: '', + laoban_id: '' + }, + 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, + isSubmitting: false, + liuyan: '', + xuanzhongTupian: [], + shangchuanJindu: 0, + shangchuanZongshu: 0, + jinduWidth: '0%', + showWanzhengJieshao: false, + wanzhengJieshao: '', + showWanzhengBeizhu: false, + wanzhengBeizhu: '', + ossImageUrl: '', + morentouxiang: '', + showRefundInfo: false, + + // 修改功能所需字段 + isModifying: false, + originalDashouJiaofu: [], + originalDashouLiuyan: '', + remainingOriginals: [], + modifiedLiuyan: '', + deletedImages: [], + newImages: [], + isConfirming: false, + modifyUploadProgress: { total: 0, current: 0, width: '0%' } + }, + + onLoad(options) { + wx.setNavigationBarTitle({ title: '订单详情' }) + this.initGlobalData() + this.jiexiTiaozhuanCanshu(options) + }, + onHide() { + const popupComp = this.selectComponent('#popupNotice'); + if (popupComp && popupComp.cleanup) { + popupComp.cleanup(); + } + }, + + onShow() { + this.registerNotificationComponent(); + }, + + 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() + }; + } + }, + + initGlobalData() { + const app = getApp() + const ossImageUrl = app.globalData.ossImageUrl || 'https://your-oss-domain.com/' + const morentouxiang = app.globalData.morentouxiang || '/images/default-avatar.png' + this.setData({ ossImageUrl, morentouxiang }) + }, + + 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 + const formattedJine = jine.toFixed(2) + this.setData({ + 'jibenShuju.dingdan_id': dingdanData.dingdan_id || '', + 'jibenShuju.tupian': tupianUrl, + 'jibenShuju.jieshao': dingdanData.jieshao || '', + 'jibenShuju.create_time': dingdanData.create_time || '', + 'jibenShuju.jine': formattedJine, + '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 + wx.showLoading({ title: '加载中...', mask: true }) + try { + const res = await request({ + url: '/dingdan/dsddxq', + method: 'POST', + data: { dingdan_id: dingdanId }, + header: { 'content-type': 'application/json' } + }) + wx.hideLoading() + if (res && res.data.code === 0 && res.data.data) { + const data = res.data.data + let dashouJiaofuList = data.dashou_jiaofu || [] + dashouJiaofuList = dashouJiaofuList.map(imgUrl => { + if (imgUrl && !imgUrl.startsWith('http')) { + const ossImageUrl = app.globalData.ossImageUrl || '' + if (imgUrl.startsWith('/')) imgUrl = imgUrl.substring(1) + return ossImageUrl + imgUrl + } + return imgUrl + }) + this.setData({ + 'xiangxiShuju.shangjia_id': data.shangjia_id || '', + 'xiangxiShuju.shangjia_nicheng': data.shangjia_nicheng || '', + 'xiangxiShuju.shangjia_liuyan': data.shangjia_liuyan || '', + 'xiangxiShuju.tuikuan_liyou': data.tuikuan_liyou || '', + 'xiangxiShuju.chufa_liyou': data.chufa_liyou || '', + 'xiangxiShuju.chufa_zhuangtai': data.chufa_zhuangtai || 0, + 'xiangxiShuju.chufa_jieguo': data.chufa_jieguo || '', + 'xiangxiShuju.bohui_liyou': data.bohui_liyou || '', + 'xiangxiShuju.dashou_jiaofu': dashouJiaofuList, + 'xiangxiShuju.dashou_liuyan': data.dashou_liuyan || '', + 'xiangxiShuju.laoban_id': data.laoban_id || '', + 'jibenShuju.zhuangtai': data.zhuangtai || 0, + 'jibenShuju.beizhu': data.beizhu || '', + 'liuyan': data.dashou_liuyan || '', + 'jibenShuju.fadanpingtai': data.fadanpingtai || 0 + }) + const currentStatus = data.zhuangtai || 0 + const showRefund = [4,5,6].includes(currentStatus) + this.setData({ showRefundInfo: showRefund }) + } else { + const errorMsg = res?.data?.msg || '获取订单详情失败' + wx.showToast({ title: errorMsg, icon: 'none', duration: 2000 }) + } + } catch (error) { + console.error('加载详细数据失败:', error) + wx.hideLoading() + wx.showToast({ title: '加载数据失败,请重试', icon: 'none', duration: 2000 }) + } + }, + + // ========== 原有提交功能(部分修改) ========== + xuanzeTupian() { + if (this.data.isSubmitting) return + const shengyu = 9 - this.data.xuanzhongTupian.length + if (shengyu <= 0) { + wx.showToast({ title: '最多只能选择9张图片', icon: 'none' }) + return + } + wx.chooseImage({ + count: shengyu, + sizeType: ['compressed'], + sourceType: ['album', 'camera'], + success: (res) => { + const newTupian = [...this.data.xuanzhongTupian, ...res.tempFilePaths] + this.setData({ xuanzhongTupian: newTupian.slice(0, 9) }) + } + }) + }, + shanchuTupian(e) { + const index = e.currentTarget.dataset.index + const newTupian = [...this.data.xuanzhongTupian] + newTupian.splice(index, 1) + this.setData({ xuanzhongTupian: newTupian }) + }, + yulanTupian(e) { + const currentUrl = e.currentTarget.dataset.url + const urls = e.currentTarget.dataset.urls || [] + if (!currentUrl) return + wx.previewImage({ current: currentUrl, urls: urls.length > 0 ? urls : [currentUrl] }) + }, + getFileExtension(filePath) { + const lastDotIndex = filePath.lastIndexOf('.') + if (lastDotIndex === -1) return '.jpg' + const ext = filePath.substring(lastDotIndex).toLowerCase() + const allowedExts = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'] + return allowedExts.includes(ext) ? ext : '.jpg' + }, + shuruLiuyan(e) { + const liuyan = e.detail.value.slice(0, 50) + this.setData({ liuyan }) + }, + chakanWanzhengJieshao() { + this.setData({ showWanzhengJieshao: true, wanzhengJieshao: this.data.jibenShuju.jieshao || '无' }) + }, + guanbiWanzhengJieshao() { this.setData({ showWanzhengJieshao: false }) }, + chakanWanzhengBeizhu() { + this.setData({ showWanzhengBeizhu: true, wanzhengBeizhu: this.data.jibenShuju.beizhu || '无' }) + }, + guanbiWanzhengBeizhu() { this.setData({ showWanzhengBeizhu: false }) }, + async fuzhiWenben(e) { + const text = e.currentTarget.dataset.text + if (!text) return + try { + await wx.setClipboardData({ data: text }) + wx.showToast({ title: '复制成功', icon: 'success', duration: 1500 }) + } catch (error) { + console.error('复制失败:', error) + wx.showToast({ title: '复制失败', icon: 'none' }) + } + }, + 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' + }); + } + }); + }, + + // ===== 联系老板(订单群 groupId 即订单号,与商家端 sjddxq 一致) ===== + goToChatWithBoss() { + const uid = wx.getStorageSync('uid') + if (!uid) { + wx.showToast({ title: '用户信息缺失', icon: 'none' }) + return + } + + const orderId = this.data.jibenShuju.dingdan_id + if (!orderId) { + wx.showToast({ title: '订单ID缺失', icon: 'none' }) + return + } + + const targetUserId = 'Ds' + uid + const connected = wx.goEasy && wx.goEasy.getConnectionStatus && wx.goEasy.getConnectionStatus() === 'connected' + const currentUserId = wx.goEasy?.im?.userId + if (!connected || currentUserId !== targetUserId) { + if (app.ensureConnection) app.ensureConnection() + wx.showToast({ title: '连接中,请稍后再试', icon: 'none' }) + return + } + + const orderIdStr = String(orderId) + const groupName = (this.data.jibenShuju.nicheng || '订单') + '的订单群' + const isCross = this.data.jibenShuju.fadanpingtai || 0 + + wx.goEasy.im.subscribeGroup({ + groupIds: [orderIdStr], + onSuccess: () => { + const param = { + groupId: orderIdStr, + orderId: orderIdStr, + groupName, + groupAvatar: '', + isCross, + } + wx.navigateTo({ + url: '/pages/qunliaotian/qunliaotian?data=' + encodeURIComponent(JSON.stringify(param)), + }) + }, + onFailed: (err) => { + console.error('订阅群组失败', err) + wx.showToast({ title: '连接群聊失败', icon: 'none' }) + }, + }) + }, + + async getCOSZhengshu() { + try { + const res = await request({ + url: '/dingdan/dsscpz', + method: 'POST', + data: { dingdan_id: this.data.jibenShuju.dingdan_id } + }) + if (res && res.data.code === 0 && res.data.data) return res.data.data + else throw new Error(res?.data?.msg || '获取上传凭证失败') + } catch (error) { + console.error('获取COS凭证失败:', error) + throw error + } + }, + + initCOSClient(tokenData) { + const credentials = tokenData.credentials || tokenData + const bucket = tokenData.bucket || 'julebu-1361527063' + const region = tokenData.region || 'ap-shanghai' + const cos = new COS({ + SimpleUploadMethod: 'putObject', + getAuthorization: async (options, callback) => { + const authParams = { + TmpSecretId: credentials.tmpSecretId, + TmpSecretKey: credentials.tmpSecretKey, + SecurityToken: credentials.sessionToken || '', + StartTime: tokenData.startTime || Math.floor(Date.now() / 1000), + ExpiredTime: tokenData.expiredTime || (Math.floor(Date.now() / 1000) + 7200) + } + callback(authParams) + } + }) + return { cos, bucket, region } + }, + + async shangchuanDanZhangTupian(filePath, cosKey, index, cosInstance, bucket, region) { + return new Promise((resolve) => { + const timeoutId = setTimeout(() => resolve({ status: 'optimistic_success', key: cosKey }), 2000) + cosInstance.uploadFile({ + Bucket: bucket, + Region: region, + Key: cosKey, + FilePath: filePath, + onProgress: (progressInfo) => { + const percent = Math.round(progressInfo.percent * 100) + if (percent === 100) { + clearTimeout(timeoutId) + resolve({ status: 'optimistic_success', key: cosKey }) + } + } + }) + }) + }, + + async piliangShangchuanTupian() { + const total = this.data.xuanzhongTupian.length + this.setData({ shangchuanZongshu: total, shangchuanJindu: 0, jinduWidth: '0%' }) + wx.showLoading({ title: '正在准备...', mask: true }) + const preGeneratedUrls = [] + for (let i = 0; i < total; i++) { + const timestamp = Date.now() + i + const random = Math.floor(Math.random() * 10000) + const fileExt = this.getFileExtension(this.data.xuanzhongTupian[i]) + const fileName = `${timestamp}_${random}${fileExt}` + const cosKey = `order/${this.data.jibenShuju.dingdan_id}/${fileName}` + preGeneratedUrls.push(cosKey) + } + let cosClient, bucket, region + try { + const tokenData = await this.getCOSZhengshu() + const { cos, bucket: cosBucket, region: cosRegion } = this.initCOSClient(tokenData) + cosClient = cos + bucket = cosBucket + region = cosRegion + } catch (error) { + wx.hideLoading() + wx.showToast({ title: '上传初始化失败', icon: 'none' }) + return [] + } + const uploadTasks = [] + for (let i = 0; i < total; i++) { + const task = this.shangchuanDanZhangTupian( + this.data.xuanzhongTupian[i], + preGeneratedUrls[i], + i, + cosClient, + bucket, + region + ).then((result) => { + const currentDone = i + 1 + const jinduPercent = ((currentDone / total) * 100).toFixed(0) + this.setData({ shangchuanJindu: currentDone, jinduWidth: `${jinduPercent}%` }) + return result + }) + uploadTasks.push(task) + } + wx.showLoading({ title: `上传中...`, mask: true }) + await Promise.all(uploadTasks) + wx.hideLoading() + return preGeneratedUrls + }, + + tijiaoDingdan() { + this.showAnnouncementAndConfirm() + }, + + async showAnnouncementAndConfirm() { + try { + const res = await request({ + url: '/peizhi/tanchuang', + method: 'POST', + data: { pageKey: 'dsddxq' } + }) + if (res.statusCode !== 200 || res.data.code !== 0) { + this.showOriginalConfirmAndSubmit() + return + } + const { popups = [], serverTime } = res.data.data + if (!popups.length) { + this.showOriginalConfirmAndSubmit() + return + } + const popup = popups[0] + const popupComp = this.selectComponent('#popupNotice') + if (!popupComp) { + console.error('未找到弹窗组件 #popupNotice') + this.showOriginalConfirmAndSubmit() + return + } + const showData = { + popupId: popup.popup_id, + title: popup.title || '', + duration: popup.duration || 0, + images: popup.images || [], + showMuteCheckbox: false, + serverTime: serverTime + } + popupComp.show(showData, (result) => { + this.showOriginalConfirmAndSubmit() + }) + } catch (error) { + console.error('获取公告配置失败', error) + this.showOriginalConfirmAndSubmit() + } + }, + + showOriginalConfirmAndSubmit() { + if (this.data.isSubmitting) { + wx.showToast({ title: '正在提交中', icon: 'none' }) + return + } + if (this.data.jibenShuju.zhuangtai !== 2) { + wx.showToast({ title: '订单状态不可提交', icon: 'none' }) + return + } + if (this.data.xuanzhongTupian.length === 0) { + wx.showToast({ title: '请至少上传一张图片', icon: 'none' }) + return + } + wx.showModal({ + title: '⚠️ 提交确认', + content: '在提交订单之前,请确保您已在打手端页面认真阅读打手规则。如有漏交图片,订单被退款,概不申诉。', + confirmText: '我已阅读', + cancelText: '我再想想', + success: (res) => { + if (res.confirm) this._doSubmit() + }, + fail: (err) => { + console.error('弹窗失败', err) + wx.showToast({ title: '系统异常,请重试', icon: 'none' }) + } + }) + }, + + async _doSubmit() { + this.setData({ isSubmitting: true }) + wx.showLoading({ title: '开始提交...', mask: true }) + try { + const tupianUrls = await this.piliangShangchuanTupian() + if (!tupianUrls || tupianUrls.length === 0) throw new Error('无法生成上传路径') + wx.showLoading({ title: '提交订单数据...', mask: true }) + const res = await request({ + url: '/dingdan/dstijiao', + method: 'POST', + data: { + dingdan_id: this.data.jibenShuju.dingdan_id, + liuyan: this.data.liuyan || '', + jiaofu_tupian_urls: tupianUrls + }, + header: { 'content-type': 'application/json' } + }) + wx.hideLoading() + this.setData({ isSubmitting: false }) + if (res && res.data.code === 0) { + const ossImageUrl = app.globalData.ossImageUrl || '' + const fullUrls = tupianUrls.map(url => { + if (url && !url.startsWith('http')) { + if (url.startsWith('/')) url = url.substring(1) + return ossImageUrl + url + } + return url + }) + this.setData({ + 'jibenShuju.zhuangtai': 8, + 'xiangxiShuju.dashou_liuyan': this.data.liuyan || '', + 'xiangxiShuju.dashou_jiaofu': fullUrls, + xuanzhongTupian: [], + liuyan: '', + shangchuanJindu: 0, + shangchuanZongshu: 0, + jinduWidth: '0%', + }) + if (app.globalData.dashouzhuangtai != 1) app.globalData.dashouzhuangtai = 1 + wx.showToast({ title: '提交成功!', icon: 'success', duration: 2000 }) + setTimeout(() => wx.navigateBack(), 1500) + } else { + const errorMsg = res?.data?.msg || '提交失败' + wx.showToast({ title: errorMsg, icon: 'none' }) + } + } catch (error) { + console.error('提交失败:', error) + wx.hideLoading() + this.setData({ isSubmitting: false }) + wx.showToast({ title: error.message || '提交失败', icon: 'none' }) + } + }, + + // ========== 修改功能(增加最少一张图片校验) ========== + extractRelativePath(fullUrl) { + const ossImageUrl = this.data.ossImageUrl + if (fullUrl.startsWith(ossImageUrl)) return fullUrl.substring(ossImageUrl.length) + return fullUrl + }, + enterModify() { + const { dashou_jiaofu, dashou_liuyan } = this.data.xiangxiShuju + this.setData({ + isModifying: true, + originalDashouJiaofu: dashou_jiaofu.slice(), + remainingOriginals: dashou_jiaofu.slice(), + originalDashouLiuyan: dashou_liuyan || '', + modifiedLiuyan: dashou_liuyan || '', + deletedImages: [], + newImages: [] + }) + }, + cancelModify() { + this.setData({ + isModifying: false, + originalDashouJiaofu: [], + remainingOriginals: [], + originalDashouLiuyan: '', + modifiedLiuyan: '', + deletedImages: [], + newImages: [] + }) + }, + handleDeleteImage(e) { + const fullUrl = e.currentTarget.dataset.url + const { originalDashouJiaofu, remainingOriginals, deletedImages, newImages } = this.data + if (originalDashouJiaofu.includes(fullUrl)) { + const newRemaining = remainingOriginals.filter(url => url !== fullUrl) + this.setData({ + remainingOriginals: newRemaining, + deletedImages: [...deletedImages, fullUrl] + }) + } else { + const index = newImages.indexOf(fullUrl) + if (index > -1) { + const newImagesCopy = [...newImages] + newImagesCopy.splice(index, 1) + this.setData({ newImages: newImagesCopy }) + } + } + }, + handleAddImage() { + if (this.data.isConfirming) return + const currentCount = this.getCurrentImageCount() + const maxCount = 9 - currentCount + if (maxCount <= 0) { + wx.showToast({ title: '最多9张图片', icon: 'none' }) + return + } + wx.chooseImage({ + count: maxCount, + sizeType: ['compressed'], + sourceType: ['album', 'camera'], + success: (res) => { + const newImages = [...this.data.newImages, ...res.tempFilePaths] + this.setData({ newImages: newImages.slice(0, 9) }) + } + }) + }, + getCurrentImageCount() { + const { remainingOriginals, newImages } = this.data + return remainingOriginals.length + newImages.length + }, + onModifyLiuyanInput(e) { + this.setData({ modifiedLiuyan: e.detail.value.slice(0, 50) }) + }, + + async confirmModify() { + if (this.data.isConfirming) return + const { dingdan_id } = this.data.jibenShuju + const { remainingOriginals, deletedImages, newImages, modifiedLiuyan, originalDashouLiuyan } = this.data + const hasLiuyanChange = modifiedLiuyan !== originalDashouLiuyan + const hasDelete = deletedImages.length > 0 + const hasAdd = newImages.length > 0 + + if (!hasLiuyanChange && !hasDelete && !hasAdd) { + wx.showToast({ title: '没有修改内容', icon: 'none' }) + return + } + + const finalImageCount = remainingOriginals.length + newImages.length + if (finalImageCount === 0) { + wx.showToast({ title: '至少保留一张图片', icon: 'none' }) + return + } + + this.setData({ isConfirming: true }) + wx.showLoading({ title: '提交修改...', mask: true }) + try { + const tokenData = await this.getCOSZhengshu() + const { cos, bucket, region } = this.initCOSClient(tokenData) + + let newImageRelativePaths = [] + if (hasAdd) { + const total = newImages.length + this.setData({ 'modifyUploadProgress.total': total, 'modifyUploadProgress.current': 0, 'modifyUploadProgress.width': '0%' }) + const preGeneratedKeys = [] + for (let i = 0; i < total; i++) { + const timestamp = Date.now() + i + const random = Math.floor(Math.random() * 10000) + const fileExt = this.getFileExtension(newImages[i]) + const fileName = `${timestamp}_${random}${fileExt}` + const cosKey = `order/${this.data.jibenShuju.dingdan_id}/${fileName}` + preGeneratedKeys.push(cosKey) + } + const uploadTasks = [] + for (let i = 0; i < total; i++) { + const task = this.shangchuanDanZhangTupian( + newImages[i], + preGeneratedKeys[i], + i, + cos, + bucket, + region + ).then((result) => { + const currentDone = i + 1 + const jinduPercent = ((currentDone / total) * 100).toFixed(0) + this.setData({ + 'modifyUploadProgress.current': currentDone, + 'modifyUploadProgress.width': `${jinduPercent}%` + }) + return result.key + }) + uploadTasks.push(task) + } + wx.showLoading({ title: `上传中...`, mask: true }) + newImageRelativePaths = await Promise.all(uploadTasks) + wx.hideLoading() + this.setData({ 'modifyUploadProgress.total': 0, 'modifyUploadProgress.current': 0, 'modifyUploadProgress.width': '0%' }) + } + + const deletedRelativePaths = deletedImages.map(fullUrl => this.extractRelativePath(fullUrl)) + + const res = await request({ + url: '/dingdan/dsxiugaidd', + method: 'POST', + data: { + dingdan_id, + liuyan: modifiedLiuyan, + deleted_images: deletedRelativePaths, + new_images: newImageRelativePaths + }, + header: { 'content-type': 'application/json' } + }) + wx.hideLoading() + this.setData({ isConfirming: false }) + if (res && res.data.code === 0) { + const ossImageUrl = this.data.ossImageUrl + const newFullUrls = newImageRelativePaths.map(rel => ossImageUrl + rel) + const newDashouJiaofu = [...remainingOriginals, ...newFullUrls] + this.setData({ + 'xiangxiShuju.dashou_jiaofu': newDashouJiaofu, + 'xiangxiShuju.dashou_liuyan': modifiedLiuyan, + isModifying: false, + originalDashouJiaofu: [], + remainingOriginals: [], + originalDashouLiuyan: '', + deletedImages: [], + newImages: [], + modifiedLiuyan: '' + }) + wx.showToast({ title: '修改成功', icon: 'success' }) + } else { + const errorMsg = res?.data?.msg || '修改失败' + wx.showToast({ title: errorMsg, icon: 'none' }) + } + } catch (error) { + console.error('修改失败:', error) + wx.hideLoading() + this.setData({ isConfirming: false }) + wx.showToast({ title: error.message || '修改失败', icon: 'none' }) + } + } +}) \ No newline at end of file diff --git a/pages/dsddxq/dsddxq.json b/pages/dsddxq/dsddxq.json new file mode 100644 index 0000000..837f016 --- /dev/null +++ b/pages/dsddxq/dsddxq.json @@ -0,0 +1,5 @@ +{ + "usingComponents": { + "global-notification": "/components/global-notification/global-notification" + } +} \ No newline at end of file diff --git a/pages/dsddxq/dsddxq.wxml b/pages/dsddxq/dsddxq.wxml new file mode 100644 index 0000000..7a715d6 --- /dev/null +++ b/pages/dsddxq/dsddxq.wxml @@ -0,0 +1,478 @@ + + + + + + 加载订单详情... + + + + + + + + + + + + + {{jibenShuju.jieshao || '暂无描述'}} + + + 点击上面字体查看完整介绍 + + + + + + + 订单信息 + + + + 游戏昵称: + {{jibenShuju.nicheng}} + + 📋 + 复制 + + + + + + 备注信息: + + {{jibenShuju.beizhu.length > 20 ? jibenShuju.beizhu.substring(0, 20) + '...' : jibenShuju.beizhu}} + + 查看完整 + + + + + + + 订单ID: + + {{jibenShuju.dingdan_id}} + + 📋 + 复制 + + + + + + + 创建时间: + {{jibenShuju.create_time}} + + + + + 订单价格: + ¥{{jibenShuju.jine}} + + + + + 订单状态: + {{zhuangtaiWenziMap[jibenShuju.zhuangtai] || '未知状态'}} + + + + + 发单平台: + + {{jibenShuju.fadanpingtai == 1 ? '平台发单' : (jibenShuju.fadanpingtai == 2 ? '商家发单' : '未知')}} + + + + + + + 商家信息 + + + + 商家ID: + + {{xiangxiShuju.shangjia_id}} + + 📋 + 复制 + + + + + + + 商家昵称: + {{xiangxiShuju.shangjia_nicheng}} + + + + + 商家留言 + + {{xiangxiShuju.shangjia_liuyan}} + + + + + + + 退款信息 + + + 退款理由: + {{xiangxiShuju.tuikuan_liyou}} + + + + + + 处罚申请 + + + + 申请原因: + {{xiangxiShuju.chufa_liyou}} + + + + + 申请状态: + + {{xiangxiShuju.chufa_zhuangtai == 0 ? '审核中' : (xiangxiShuju.chufa_zhuangtai == 1 ? '处罚成功' : '处罚驳回')}} + + + + + + 处罚结果: + {{xiangxiShuju.chufa_jieguo}} + + + + + 驳回理由: + {{xiangxiShuju.bohui_liyou}} + + + + + + 我的交付 + + + + 交付图片 + + + + + + + + + + + + 我的留言 + + {{xiangxiShuju.dashou_liuyan}} + + + + + + + 修改交付 + + + + 交付图片(点击叉号删除) + + + + + + + + + × + + + + + + + + + + × + + + + + + + + + 添加图片 + ({{9 - (remainingOriginals.length + newImages.length)}}/9) + + + + + + + 留言(可选,最多50字) + + + 已输入 {{modifiedLiuyan.length}}/50 字 + + + + + + + 提交订单 + + + + 选择交付图片(最多9张) + + + + + + + + × + + + + + + + + + 添加图片 + ({{9 - xuanzhongTupian.length}}/9) + + + + + + + + + + 上传中: {{shangchuanJindu}}/{{shangchuanZongshu}} + + + + + 留言(可选,最多50字) + + + 已输入 {{liuyan.length}}/50 字 + + + + + + + + + + + + + 取消修改 + + + {{isConfirming ? '提交中...' : '确认修改'}} + + + + + + + + + + 联系客服 + + + 联系老板 + + + + + + 联系客服 + + + + + + + 修改交付 + + + + + + {{isSubmitting ? '提交中...' : '立即提交'}} + + + + + + + + + + + + 订单介绍 + × + + + + {{wanzhengJieshao}} + + + + + + + + + + + 订单备注 + × + + + + {{wanzhengBeizhu}} + + + + + + + + \ No newline at end of file diff --git a/pages/dsddxq/dsddxq.wxss b/pages/dsddxq/dsddxq.wxss new file mode 100644 index 0000000..7e9a205 --- /dev/null +++ b/pages/dsddxq/dsddxq.wxss @@ -0,0 +1,589 @@ +/* pages/dsddxq/dsddxq.wxss */ + +/* 页面基础样式 */ +.dsddxq-page { + min-height: 100vh; + background-color: #f8f9fa; + position: relative; + box-sizing: border-box; +} + +/* 加载中状态 */ +.loading-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 60vh; +} + +.loading-spinner { + width: 80rpx; + height: 80rpx; + border: 8rpx solid #e0e0e0; + border-top-color: #2196f3; + border-radius: 50%; + animation: spin 1.2s linear infinite; + margin-bottom: 30rpx; +} + +@keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +.loading-text { + font-size: 30rpx; + color: #666666; +} + +/* 内容区域 */ +.content-container { + padding: 25rpx 25rpx 220rpx; /* 减少底部内边距,避免过多空白 */ +} + +/* 商品图片和介绍区域 */ +.shangpin-area { + display: flex; + background-color: #ffffff; + border-radius: 20rpx; + padding: 25rpx; + margin-bottom: 25rpx; + box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06); +} + +.shangpin-image-container { + flex-shrink: 0; + width: 180rpx; + height: 180rpx; + margin-right: 25rpx; +} + +.shangpin-image { + width: 100%; + height: 100%; + border-radius: 12rpx; + background-color: #f5f5f5; +} + +.shangpin-jieshao { + flex: 1; + display: flex; + flex-direction: column; +} + +.jieshao-text { + font-size: 28rpx; + line-height: 42rpx; + color: #333333; + font-weight: 400; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 4; + overflow: hidden; + text-overflow: ellipsis; +} + +.chakan-wanzheng { + margin-top: 10rpx; +} + +.chakan-wanzheng text { + font-size: 24rpx; + color: #2196f3; + text-decoration: underline; +} + +/* 通用区域样式 */ +.info-title { + font-size: 32rpx; + color: #333333; + font-weight: 600; + margin-bottom: 20rpx; + padding-bottom: 12rpx; + border-bottom: 2rpx solid rgba(0, 0, 0, 0.08); +} + +.section-subtitle { + font-size: 28rpx; + color: #555555; + font-weight: 500; + display: block; + margin: 15rpx 0 10rpx; +} + +/* 订单信息区域 */ +.dingdan-info-area, +.shangjia-area, +.tuikuan-area, +.chufa-area, +.dashou-jiaofu-area, +.tijiao-area { + background-color: #ffffff; + border-radius: 20rpx; + padding: 25rpx; + margin-bottom: 25rpx; + box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06); +} + +.info-item-row { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 18rpx; + padding: 12rpx 0; +} + +.info-item-row:last-child { + margin-bottom: 0; +} + +.item-label { + font-size: 28rpx; + color: #666666; + font-weight: 400; + flex-shrink: 0; + min-width: 150rpx; +} + +/* 右侧区域容器 */ +.item-right-area { + flex: 1; + display: flex; + align-items: center; + justify-content: flex-end; +} + +.item-value { + font-size: 28rpx; + color: #333333; + font-weight: 500; + word-break: break-all; + text-align: right; + margin-right: 15rpx; +} + +/* 特殊样式 */ +.jine-green { + color: #4caf50 !important; + font-weight: 600 !important; + font-size: 32rpx !important; +} + +.zhuangtai-text { + font-weight: 600 !important; + font-size: 30rpx !important; + color: #333333 !important; +} + +/* 按钮样式(复制、查看) */ +.copy-btn, +.chakan-btn { + display: flex; + align-items: center; + justify-content: center; + font-size: 24rpx; + color: #4caf50; + background-color: #e8f5e9; + padding: 6rpx 16rpx; + border-radius: 20rpx; + border: 1rpx solid #4caf50; + transition: all 0.2s ease; + white-space: nowrap; + flex-shrink: 0; +} + +.chakan-btn { + color: #2196f3; + background-color: #e3f2fd; + border-color: #2196f3; + margin-left: 10rpx; +} + +.copy-btn-hover, +.chakan-btn:active { + background-color: #c8e6c9; + transform: scale(0.95); +} + +.copy-icon { + font-size: 20rpx; + margin-right: 6rpx; +} + +.copy-text { + font-weight: 500; +} + +/* 打手交付图片网格 */ +.jiaofu-grid { + display: flex; + flex-wrap: wrap; + margin: -5rpx; +} + +.jiaofu-image-item { + width: calc(33.333% - 10rpx); + margin: 5rpx; + aspect-ratio: 1/1; +} + +.jiaofu-image { + width: 100%; + height: 100%; + border-radius: 12rpx; + background-color: #f5f5f5; + transition: transform 0.2s ease; +} + +.jiaofu-image:active { + transform: scale(0.95); +} + +/* 留言内容样式 */ +.liuyan-content { + font-size: 26rpx; + line-height: 40rpx; + color: #555555; + padding: 15rpx; + background-color: #f9f9f9; + border-radius: 12rpx; + margin-top: 8rpx; +} + +/* 选择图片区域 */ +.yixuan-tupian-grid { + display: flex; + flex-wrap: wrap; + margin: -8rpx; +} + +.yixuan-tupian-item { + width: calc(33.333% - 16rpx); + margin: 8rpx; + aspect-ratio: 1/1; + position: relative; +} + +.yixuan-tupian { + width: 100%; + height: 100%; + border-radius: 12rpx; + background-color: #f5f5f5; +} + +.shanchu-btn { + position: absolute; + top: -10rpx; + right: -10rpx; + width: 40rpx; + height: 40rpx; + background-color: #ff4444; + color: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 24rpx; + font-weight: bold; + z-index: 10; +} + +.tianjia-btn { + width: calc(33.333% - 16rpx); + margin: 8rpx; + aspect-ratio: 1/1; + background-color: #f5f5f5; + border-radius: 12rpx; + border: 2rpx dashed #cccccc; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + color: #666666; +} + +.tianjia-icon { + font-size: 48rpx; + font-weight: 300; + margin-bottom: 10rpx; +} + +.tianjia-text { + font-size: 24rpx; + margin-bottom: 4rpx; +} + +.tianjia-tip { + font-size: 20rpx; + color: #999999; +} + +/* 上传进度 */ +.shangchuan-jindu-container { + margin-top: 20rpx; +} + +.jindu-bar { + width: 100%; + height: 20rpx; + background-color: #f0f0f0; + border-radius: 10rpx; + overflow: hidden; + margin-bottom: 10rpx; +} + +.jindu-fill { + height: 100%; + background-color: #4caf50; + border-radius: 10rpx; + transition: width 0.3s ease; +} + +.jindu-text { + font-size: 24rpx; + color: #666666; + text-align: center; + display: block; +} + +/* 留言输入 */ +.liuyan-textarea { + width: 100%; + min-height: 120rpx; + font-size: 28rpx; + color: #333333; + background-color: #f9f9f9; + border-radius: 12rpx; + padding: 20rpx; + margin-top: 10rpx; + box-sizing: border-box; +} + +.zi-shu { + display: block; + text-align: right; + font-size: 24rpx; + color: #999999; + margin-top: 8rpx; +} + +/* ==================== 底部按钮区域 ==================== */ +.bottom-btns-container { + position: fixed; + bottom: 0; + left: 0; + right: 0; + background-color: #ffffff; + padding: 20rpx 30rpx; + border-top: 1rpx solid #f0f0f0; + box-shadow: 0 -2rpx 20rpx rgba(0, 0, 0, 0.08); + z-index: 100; + display: flex; + flex-direction: column; + gap: 10rpx; /* 减小按钮间距 */ +} + +/* 🆕 新增:按钮行布局(用于并排两个按钮) */ +.btn-row { + display: flex; + justify-content: space-between; + width: 100%; +} + +/* 🆕 当有两个按钮时,各占一半(减去gap的一半) */ +.btn-row .bottom-btn { + width: calc(50% - 10rpx); +} + +/* 🆕 当只有一个按钮时,占满 */ +.btn-row .full-width { + width: 100%; +} + +/* 统一底部按钮样式 */ +.bottom-btn { + width: 100%; + height: 88rpx; /* 统一高度 */ + border-radius: 44rpx; /* 圆角 */ + display: flex; + align-items: center; + justify-content: center; + font-size: 32rpx; + font-weight: 600; + transition: all 0.3s ease; + position: relative; + overflow: hidden; + border: none; + box-sizing: border-box; +} + +.btn-hover { + opacity: 0.9; + transform: scale(0.98); +} + +/* 联系客服按钮 */ +.contact-btn { + background-color: #2196F3; + color: #ffffff; + box-shadow: 0 4rpx 15rpx rgba(33, 150, 243, 0.3); +} + +/* 🆕 新增:联系老板按钮 */ +.boss-btn { + background-color: #ff9800; + color: #ffffff; + box-shadow: 0 4rpx 15rpx rgba(255, 152, 0, 0.3); +} + +/* 🆕 新增:修改按钮(黑色) */ +.modify-btn { + background-color: #333333; + color: #ffffff; + box-shadow: 0 4rpx 15rpx rgba(51, 51, 51, 0.3); +} + +/* 🆕 新增:取消修改按钮 */ +.cancel-modify-btn { + background-color: #9e9e9e; + color: #ffffff; + box-shadow: 0 4rpx 15rpx rgba(158, 158, 158, 0.3); +} + +/* 🆕 新增:确认修改按钮 */ +.confirm-modify-btn { + background-color: #4caf50; + color: #ffffff; + box-shadow: 0 4rpx 15rpx rgba(76, 175, 80, 0.3); +} + +/* 提交按钮 */ +.submit-btn { + background: linear-gradient(135deg, #4caf50 0%, #2e7d32 100%); + color: #ffffff; + box-shadow: 0 4rpx 15rpx rgba(76, 175, 80, 0.3); +} + +/* 提交按钮闪光特效 */ +.submit-btn::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); + animation: flash 2s infinite; +} + +@keyframes flash { + 0% { left: -100%; } + 100% { left: 100%; } +} + +.btn-text { + position: relative; + z-index: 1; +} + +/* ==================== 弹窗样式 ==================== */ +.modal-container { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 1000; + display: flex; + align-items: center; + justify-content: center; +} + +.modal-mask { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); +} + +.modal-content { + position: relative; + width: 600rpx; + max-height: 70vh; + background-color: #ffffff; + border-radius: 20rpx; + overflow: hidden; + box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.15); + z-index: 1001; +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 30rpx; + border-bottom: 1rpx solid #f0f0f0; +} + +.modal-title { + font-size: 34rpx; + color: #333333; + font-weight: 600; +} + +.modal-close { + font-size: 40rpx; + color: #999999; + width: 40rpx; + height: 40rpx; + display: flex; + align-items: center; + justify-content: center; +} + +.modal-body { + padding: 30rpx; + max-height: 50vh; + overflow: hidden; +} + +.modal-scroll { + height: 100%; + max-height: 50vh; +} + +.modal-content-text { + font-size: 28rpx; + line-height: 44rpx; + color: #333333; + white-space: pre-wrap; +} + +/* 响应式调整 */ +@media (max-width: 375px) { + .shangpin-image-container { + width: 160rpx; + height: 160rpx; + } + + .bottom-btn { + height: 80rpx; + font-size: 30rpx; + } + + .item-label { + min-width: 140rpx; + font-size: 26rpx; + } + + .item-value { + font-size: 26rpx; + } + + .modal-content { + width: 560rpx; + } +} \ No newline at end of file diff --git a/pages/fenlei/fenlei.js b/pages/fenlei/fenlei.js new file mode 100644 index 0000000..9cbf998 --- /dev/null +++ b/pages/fenlei/fenlei.js @@ -0,0 +1,452 @@ +// pages/fenlei/fenlei.js +const app = getApp() + +Page({ + /** + * 页面的初始数据 + */ + data: { + // 全局变量引用 + ossImageUrl: '', + + // 搜索相关 + searchText: '', + showSearchResult: false, + searchResults: [], + isSearching: false, + + // 页面数据 + shangpinleixing: [], + shangpinzhuanqu: [], + shangpinliebiao: [], + + // 状态控制 + selectedLeixingId: null, + selectedZhuanquId: null, + filteredZhuanquList: [], // 当前类型下的专区列表 + filteredShangpinList: [], // 当前专区下的商品列表 + zhuanquByLeixing: {}, // 类型->专区映射对象 + shangpinByZhuanqu: {}, // 专区->商品映射对象 + zhuanquGoodsCount: {}, // 专区商品数量统计 + + // UI状态 + isLoading: false, + hasInitialized: false + }, + + /** + * 生命周期函数--监听页面加载 + */ + onLoad(options) { + // 获取全局变量 + this.setData({ + ossImageUrl: app.globalData.ossImageUrl + }) + + // 初始化数据 + this.initPageData() + }, + onShow() { + // 原有代码... + + // 🆕 注册通知组件 + this.registerNotificationComponent(); +}, + +// 🆕 新增:注册通知组件 +registerNotificationComponent() { + const app = getApp(); + const notificationComp = this.selectComponent('#global-notification'); + + if (notificationComp && notificationComp.showNotification) { + console.log('🏪 商城页面注册通知组件'); + app.globalData.globalNotification = { + show: (data) => notificationComp.showNotification(data), + hide: () => notificationComp.hideNotification() + }; + } +}, + + /** + * 初始化页面数据 + */ + initPageData() { + const that = this + this.setData({ isLoading: true }) + + // 检查全局变量是否已有数据 + if (app.globalData.shangpinleixing && app.globalData.shangpinleixing.length > 0) { + this.initFromGlobalData() + } else { + this.loadAllShangpinData() + } + }, + + /** + * 从全局变量初始化数据 + */ + initFromGlobalData() { + const leixingList = app.globalData.shangpinleixing || [] + const zhuanquList = app.globalData.shangpinzhuanqu || [] + const shangpinList = app.globalData.shangpinliebiao || [] + + // 构建映射关系对象 + const zhuanquByLeixing = {} + const shangpinByZhuanqu = {} + const zhuanquGoodsCount = {} + + // 1. 构建专区映射 (按类型分组) + zhuanquList.forEach(zhuanqu => { + const leixingId = zhuanqu.leixing_id + if (!zhuanquByLeixing[leixingId]) { + zhuanquByLeixing[leixingId] = [] + } + zhuanquByLeixing[leixingId].push(zhuanqu) + }) + + // 2. 构建商品映射 (按专区分组)并统计数量 + shangpinList.forEach(shangpin => { + const zhuanquId = shangpin.zhuanqu_id + if (zhuanquId) { + if (!shangpinByZhuanqu[zhuanquId]) { + shangpinByZhuanqu[zhuanquId] = [] + zhuanquGoodsCount[zhuanquId] = 0 + } + + // 处理价格显示 + const jiage = parseFloat(shangpin.jiage) || 0 + const priceInteger = Math.floor(jiage) + let priceDecimal = '00' + + const decimalPart = (jiage - priceInteger).toFixed(2) + if (decimalPart > 0) { + priceDecimal = decimalPart.toString().split('.')[1] + } + + shangpinByZhuanqu[zhuanquId].push({ + id: shangpin.id, + biaoqian: shangpin.biaoqian, + jiage: jiage, + priceInteger: priceInteger, + priceDecimal: priceDecimal, + tupian_url: shangpin.tupian_url, + duiwai_xiaoliang: shangpin.duiwai_xiaoliang || 0 + }) + + zhuanquGoodsCount[zhuanquId] += 1 + } + }) + + // 设置初始选中状态(第一个类型和第一个专区) + const firstLeixing = leixingList[0] + let firstZhuanquId = null + + if (firstLeixing && zhuanquByLeixing[firstLeixing.id]) { + firstZhuanquId = zhuanquByLeixing[firstLeixing.id][0]?.id || null + } + + this.setData({ + shangpinleixing: leixingList, + shangpinzhuanqu: zhuanquList, + shangpinliebiao: shangpinList, + zhuanquByLeixing: zhuanquByLeixing, + shangpinByZhuanqu: shangpinByZhuanqu, + zhuanquGoodsCount: zhuanquGoodsCount, + selectedLeixingId: firstLeixing?.id || null, + selectedZhuanquId: firstZhuanquId + }, () => { + // 筛选数据 + this.filterData() + this.setData({ isLoading: false, hasInitialized: true }) + }) + }, + + /** + * 加载所有商品数据 + */ + loadAllShangpinData() { + const that = this + wx.request({ + url: app.globalData.apiBaseUrl + '/shangpin/shangpinhuoqu/', + method: 'POST', + header: { + 'content-type': 'application/json' + }, + success(res) { + if (res.statusCode === 200 && res.data) { + const data = res.data + + // 存储到全局变量 + app.globalData.shangpinleixing = data.shangpinleixing || [] + app.globalData.shangpinzhuanqu = data.shangpinzhuanqu || [] + app.globalData.shangpinliebiao = data.shangpinliebiao || [] + + // 从全局变量初始化 + that.initFromGlobalData() + } else { + that.handleLoadError() + } + }, + fail(error) { + console.error('加载商品数据失败:', error) + that.handleLoadError() + } + }) + }, + + /** + * 处理加载错误 + */ + handleLoadError() { + wx.showToast({ + title: '加载失败,请重试', + icon: 'none' + }) + this.setData({ isLoading: false }) + }, + + /** + * 筛选数据 + */ + filterData() { + const { selectedLeixingId, selectedZhuanquId, zhuanquByLeixing, shangpinByZhuanqu } = this.data + + // 1. 筛选专区列表 + const filteredZhuanquList = zhuanquByLeixing[selectedLeixingId] || [] + + // 2. 筛选商品列表 + let filteredShangpinList = [] + if (selectedZhuanquId) { + filteredShangpinList = shangpinByZhuanqu[selectedZhuanquId] || [] + } else if (filteredZhuanquList.length > 0) { + // 如果没有选中的专区,默认显示第一个专区的商品 + const firstZhuanquId = filteredZhuanquList[0].id + filteredShangpinList = shangpinByZhuanqu[firstZhuanquId] || [] + this.setData({ selectedZhuanquId: firstZhuanquId }) + } + + this.setData({ + filteredZhuanquList: filteredZhuanquList, + filteredShangpinList: filteredShangpinList + }) + }, + + /** + * 选择商品类型 + */ + selectLeixing(e) { + const leixingId = e.currentTarget.dataset.id + + if (this.data.selectedLeixingId === leixingId) { + return + } + + // 获取该类型下的第一个专区 + const zhuanquList = this.data.zhuanquByLeixing[leixingId] || [] + const firstZhuanquId = zhuanquList.length > 0 ? zhuanquList[0].id : null + + this.setData({ + selectedLeixingId: leixingId, + selectedZhuanquId: firstZhuanquId + }, () => { + this.filterData() + }) + }, + + /** + * 选择商品专区 + */ + selectZhuanqu(e) { + const zhuanquId = e.currentTarget.dataset.id + + if (this.data.selectedZhuanquId === zhuanquId) { + return + } + + this.setData({ + selectedZhuanquId: zhuanquId + }, () => { + // 筛选商品列表 + const filteredShangpinList = this.data.shangpinByZhuanqu[zhuanquId] || [] + this.setData({ + filteredShangpinList: filteredShangpinList + }) + }) + }, + + /** + * 搜索框输入事件 + */ + onSearchInput(e) { + this.setData({ + searchText: e.detail.value + }) + }, + + /** + * 点击搜索按钮 + */ + onSearchTap() { + const searchText = this.data.searchText.trim() + if (!searchText) { + wx.showToast({ + title: '请输入搜索内容', + icon: 'none' + }) + return + } + + // 开始搜索 + this.startSearch(searchText) + }, + + /** + * 键盘搜索事件(回车键) + */ + onSearchConfirm(e) { + const searchText = e.detail.value.trim() + if (!searchText) { + wx.showToast({ + title: '请输入搜索内容', + icon: 'none' + }) + return + } + + this.setData({ + searchText: searchText + }) + + // 开始搜索 + this.startSearch(searchText) + }, + + /** + * 执行搜索 + */ + startSearch(searchText) { + this.setData({ + isSearching: true, + showSearchResult: true + }) + + // 本地搜索 + const allShangpin = this.data.shangpinliebiao + const results = [] + + for (const item of allShangpin) { + if (item.biaoqian && item.biaoqian.toLowerCase().includes(searchText.toLowerCase())) { + // 处理价格显示 + const jiage = parseFloat(item.jiage) || 0 + const priceInteger = Math.floor(jiage) + let priceDecimal = '00' + + const decimalPart = (jiage - priceInteger).toFixed(2) + if (decimalPart > 0) { + priceDecimal = decimalPart.toString().split('.')[1] + } + + results.push({ + id: item.id, + biaoqian: item.biaoqian, + jiage: jiage, + priceInteger: priceInteger, + priceDecimal: priceDecimal, + tupian_url: item.tupian_url, + leixing_name: this.getLeixingName(item.leixing_id), + zhuanqu_name: this.getZhuanquName(item.zhuanqu_id) + }) + } + } + + this.setData({ + searchResults: results, + isSearching: false + }) + }, + + /** + * 清空搜索 + */ + onClearSearch() { + this.setData({ + searchText: '', + showSearchResult: false, + searchResults: [] + }) + }, + + /** + * 根据类型ID获取类型名称 + */ + getLeixingName(leixingId) { + const leixingList = this.data.shangpinleixing + for (const item of leixingList) { + if (item.id === leixingId) { + return item.jieshao + } + } + return '' + }, + + /** + * 根据专区ID获取专区名称 + */ + getZhuanquName(zhuanquId) { + const zhuanquList = this.data.shangpinzhuanqu + for (const item of zhuanquList) { + if (item.id === zhuanquId) { + return item.mingzi + } + } + return '' + }, + + /** + * 跳转到商品详情页 + */ + goToDetail(e) { + const shangpinId = e.currentTarget.dataset.id + if (!shangpinId) return + + wx.navigateTo({ + url: `/pages/shangpinxiangqing/shangpinxiangqing?id=${shangpinId}` + }) + }, + + /** + * 刷新所有数据 + */ + onRefresh() { + wx.showLoading({ + title: '刷新中...', + mask: true + }) + + // 清空全局变量 + app.globalData.shangpinleixing = [] + app.globalData.shangpinzhuanqu = [] + app.globalData.shangpinliebiao = [] + + this.loadAllShangpinData() + + setTimeout(() => { + wx.hideLoading() + wx.showToast({ + title: '刷新成功', + icon: 'success', + duration: 1500 + }) + }, 1000) + }, + + /** + * 用户点击右上角分享 + */ + onShareAppMessage() { + return { + title: '阿龙电竞 - 商品分类', + path: '/pages/fenlei/fenlei' + } + } +}) \ No newline at end of file diff --git a/pages/fenlei/fenlei.json b/pages/fenlei/fenlei.json new file mode 100644 index 0000000..4af9065 --- /dev/null +++ b/pages/fenlei/fenlei.json @@ -0,0 +1,6 @@ +{ + "usingComponents": { + "global-notification": "/components/global-notification/global-notification", + "custom-tab-bar": "/custom-tab-bar/index" + } + } \ No newline at end of file diff --git a/pages/fenlei/fenlei.wxml b/pages/fenlei/fenlei.wxml new file mode 100644 index 0000000..6f015fa --- /dev/null +++ b/pages/fenlei/fenlei.wxml @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + 搜索 + + + + + + + + + + + + + + + {{item.jieshao}} + + + + + + + + + + + + + + + + {{item.mingzi}} + ({{zhuanquGoodsCount[item.id] || 0}}) + + + + + + + + 暂无专区 + + + + + + + + + + + + + + + + {{item.biaoqian}} + + + + + ¥ + {{item.priceInteger}} + .{{item.priceDecimal}} + + + 了解详情 + + + + + + + + + + + 该专区暂无商品 + + + + + — 已加载全部商品 — + + + + + + + + + + + 搜索结果 + ({{searchResults.length}}个) + + + + + + + + + + + + + + {{item.biaoqian}} + + {{item.leixing_name}} + · + {{item.zhuanqu_name}} + + + ¥ + {{item.priceInteger}} + .{{item.priceDecimal}} + + + + + + + + 未找到相关商品 + + + + + + + 搜索中... + + + + + + + + + + + + + 加载中... + + + + + + + \ No newline at end of file diff --git a/pages/fenlei/fenlei.wxss b/pages/fenlei/fenlei.wxss new file mode 100644 index 0000000..d4a3420 --- /dev/null +++ b/pages/fenlei/fenlei.wxss @@ -0,0 +1,1215 @@ +/* pages/fenlei/fenlei.wxss */ +.fenlei-page { + width: 100%; + height: 100vh; + background: linear-gradient(135deg, #f8f9ff 0%, #f0f2ff 100%); + display: flex; + flex-direction: column; + position: relative; + overflow: hidden; + } + + /* 1. 毛玻璃搜索框区域(固定) */ + .search-container { + padding: 24rpx 32rpx; + background: transparent; + flex-shrink: 0; + position: relative; + z-index: 100; + } + + .search-box { + display: flex; + align-items: center; + background: rgba(255, 255, 255, 0.85); + backdrop-filter: blur(30rpx); + -webkit-backdrop-filter: blur(30rpx); + border-radius: 50rpx; + padding: 0 28rpx; + height: 84rpx; + box-shadow: + 0 8rpx 32rpx rgba(31, 38, 135, 0.1), + inset 0 1rpx 0 rgba(255, 255, 255, 0.8); + border: 1rpx solid rgba(255, 255, 255, 0.3); + position: relative; + overflow: hidden; + } + + .search-box::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0.1)); + border-radius: 50rpx; + z-index: -1; + } + + .search-icon { + width: 34rpx; + height: 34rpx; + margin-right: 18rpx; + opacity: 0.6; + } + + .search-input { + flex: 1; + height: 100%; + font-size: 28rpx; + color: #222; + font-weight: 400; + } + + .search-placeholder { + color: #aaa; + font-size: 28rpx; + } + + .search-btn { + background: linear-gradient(135deg, #ffcc00, #ffaa00); + border-radius: 40rpx; + padding: 14rpx 32rpx; + margin-left: 20rpx; + position: relative; + overflow: hidden; + box-shadow: 0 6rpx 20rpx rgba(255, 170, 0, 0.3); + transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); + } + + .search-btn:active { + transform: scale(0.95); + box-shadow: 0 4rpx 15rpx rgba(255, 170, 0, 0.4); + } + + .search-btn-text { + font-size: 26rpx; + color: white; + font-weight: 600; + position: relative; + z-index: 1; + } + + .search-btn-glow { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent); + transform: translateX(-100%); + transition: transform 0.6s; + } + + .search-btn:active .search-btn-glow { + transform: translateX(100%); + } + + .search-clear { + width: 36rpx; + height: 36rpx; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin-left: 16rpx; + opacity: 0.6; + transition: opacity 0.3s; + } + + .search-clear:active { + opacity: 1; + } + + .clear-icon { + width: 24rpx; + height: 24rpx; + } + + /* 2. 商品类型选择区域(固定)- 恢复原大小 */ + .leixing-container { + padding: 0 32rpx 24rpx; + flex-shrink: 0; + position: relative; + } + + .leixing-bg-glow { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 300rpx; + height: 300rpx; + background: radial-gradient(circle, rgba(255, 204, 0, 0.1) 0%, transparent 70%); + z-index: -1; + } + + .leixing-scroll { + white-space: nowrap; + height: 180rpx; /* 恢复原大小 */ + } + + .leixing-list { + display: inline-flex; + padding-right: 32rpx; + } + + .leixing-item { + display: inline-flex; + flex-direction: column; + align-items: center; + margin-right: 48rpx; /* 增加间距 */ + position: relative; + padding: 12rpx; + } + + .leixing-image-box { + width: 100rpx; /* 恢复原大小 */ + height: 100rpx; /* 恢复原大小 */ + border-radius: 50%; + position: relative; + margin-bottom: 16rpx; + overflow: hidden; + background: white; + box-shadow: + 0 12rpx 30rpx rgba(0, 0, 0, 0.12), + 0 4rpx 8rpx rgba(0, 0, 0, 0.06); + transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); + } + + .leixing-active .leixing-image-box { + transform: scale(1.12); + box-shadow: + 0 18rpx 40rpx rgba(255, 204, 0, 0.3), + 0 8rpx 16rpx rgba(255, 170, 0, 0.2); + } + + .leixing-image { + width: 100%; + height: 100%; + border-radius: 50%; + object-fit: cover; + } + + .leixing-glow { + position: absolute; + top: -3rpx; + left: -3rpx; + right: -3rpx; + bottom: -3rpx; + border-radius: 50%; + background: linear-gradient(135deg, #ffcc00, #ffaa00, #ff8800); + opacity: 0; + transition: opacity 0.4s; + z-index: -1; + filter: blur(10rpx); + } + + .leixing-glow-active { + opacity: 0.6; + animation: glowPulse 2s infinite; + } + + .leixing-sparkle { + position: absolute; + top: 50%; + left: 50%; + width: 20rpx; + height: 20rpx; + background: white; + border-radius: 50%; + transform: translate(-50%, -50%) scale(0); + opacity: 0; + pointer-events: none; + } + + .sparkle-animation { + animation: sparkleFlash 1.5s infinite; + } + + .leixing-text { + font-size: 24rpx; + color: #555; /* 颜色加深 */ + font-weight: 500; + transition: all 0.3s; + white-space: nowrap; + } + + .leixing-active .leixing-text { + color: #ff9900; + font-weight: 600; + transform: translateY(-2rpx); + text-shadow: 0 2rpx 4rpx rgba(255, 153, 0, 0.2); + } + + .leixing-active-bar { + position: absolute; + bottom: -4rpx; + width: 44rpx; + height: 4rpx; + background: linear-gradient(90deg, #ffcc00, #ffaa00); + border-radius: 2rpx; + opacity: 0; + transform: scaleX(0); + transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); + box-shadow: 0 2rpx 8rpx rgba(255, 170, 0, 0.4); + } + + .leixing-active-bar-show { + opacity: 1; + transform: scaleX(1); + } + + .leixing-border-effect { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 50%; + border: 2rpx solid transparent; + opacity: 0; + pointer-events: none; + } + + .border-effect-active { + opacity: 1; + border-color: rgba(255, 204, 0, 0.5); + animation: borderGlow 2s infinite; + } + + /* 3. 左右分栏布局(可滚动区域) */ + .main-container { + flex: 1; + display: flex; + height: calc(100vh - 300rpx); + overflow: hidden; + } + + /* 左边:商品专区列表 */ + .zhuanqu-left { + width: 200rpx; + background: rgba(255, 255, 255, 0.7); + backdrop-filter: blur(10rpx); + border-right: 1rpx solid rgba(0, 0, 0, 0.08); + flex-shrink: 0; + position: relative; + } + + .zhuanqu-list { + padding: 0; + } + + .zhuanqu-item { + position: relative; + padding: 32rpx 0; + border-bottom: 1rpx solid rgba(0, 0, 0, 0.05); + transition: all 0.3s; + } + + .zhuanqu-item:active { + background: rgba(255, 204, 0, 0.05); + } + + .zhuanqu-item-content { + display: flex; + flex-direction: column; + align-items: center; + padding: 0 20rpx; + } + + .zhuanqu-name { + font-size: 26rpx; + color: #444; /* 颜色加深 */ + text-align: center; + line-height: 1.4; + margin-bottom: 8rpx; + transition: all 0.3s; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + font-weight: 500; + } + + .zhuanqu-active .zhuanqu-name { + color: #ff9900; + font-weight: 600; + text-shadow: 0 2rpx 4rpx rgba(255, 153, 0, 0.2); + } + + .zhuanqu-count { + font-size: 22rpx; + color: #888; + transition: all 0.3s; + } + + .zhuanqu-active .zhuanqu-count { + color: #ffaa00; + font-weight: 500; + } + + .zhuanqu-active-indicator { + position: absolute; + right: 0; + top: 50%; + transform: translateY(-50%); + width: 6rpx; + height: 44rpx; + background: linear-gradient(180deg, #ffcc00, #ffaa00); + border-radius: 3rpx 0 0 3rpx; + opacity: 0; + transition: all 0.3s; + overflow: hidden; + } + + .zhuanqu-indicator-glow { + opacity: 1; + animation: indicatorGlow 2s infinite; + box-shadow: 0 0 20rpx rgba(255, 204, 0, 0.5); + } + + .indicator-sparkle { + position: absolute; + top: 0; + left: 0; + right: 0; + height: 10rpx; + background: rgba(255, 255, 255, 0.6); + animation: sparkleMove 1.5s infinite; + } + + .zhuanqu-hover-glow { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(90deg, transparent, rgba(255, 204, 0, 0.05), transparent); + opacity: 0; + transition: opacity 0.3s; + pointer-events: none; + } + + .zhuanqu-item:active .zhuanqu-hover-glow { + opacity: 1; + } + + .zhuanqu-click-effect { + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; + border-radius: 50%; + background: rgba(255, 204, 0, 0.1); + transform: translate(-50%, -50%); + opacity: 0; + pointer-events: none; + } + + .zhuanqu-item:active .zhuanqu-click-effect { + animation: rippleEffect 0.6s ease-out; + } + + .zhuanqu-empty { + display: flex; + flex-direction: column; + align-items: center; + padding: 120rpx 0; + } + + .zhuanqu-empty-icon { + width: 120rpx; + height: 120rpx; + opacity: 0.2; + margin-bottom: 28rpx; + } + + .zhuanqu-empty-text { + font-size: 26rpx; + color: #999; + } + + /* 右边:商品列表(紧凑布局) */ + .shangpin-right { + flex: 1; + background: #fafafa; + } + + .shangpin-list { + padding: 0; + } + + .shangpin-item { + background: white; + padding: 28rpx 30rpx 22rpx; /* 底部距离缩小 */ + position: relative; + display: flex; + align-items: flex-start; + min-height: 140rpx; + overflow: hidden; + } + + .shangpin-item:active { + background: rgba(0, 0, 0, 0.02); + } + + .shangpin-image-box { + margin-right: 24rpx; + flex-shrink: 0; + position: relative; + } + + .shangpin-image { + width: 120rpx; + height: 120rpx; + border-radius: 14rpx; + background: #f5f5f5; + object-fit: cover; + } + + .shangpin-image-glow { + position: absolute; + top: -4rpx; + left: -4rpx; + right: -4rpx; + bottom: -4rpx; + border-radius: 18rpx; + background: linear-gradient(135deg, rgba(255, 204, 0, 0.2), transparent); + opacity: 0; + transition: opacity 0.3s; + pointer-events: none; + } + + .shangpin-item:active .shangpin-image-glow { + opacity: 0.5; + } + + .image-corner { + position: absolute; + top: 0; + right: 0; + width: 20rpx; + height: 20rpx; + background: linear-gradient(135deg, #ffcc00, transparent 70%); + border-radius: 0 14rpx 0 0; + } + + .shangpin-info { + flex: 1; + display: flex; + flex-direction: column; + height: 120rpx; + justify-content: space-between; + padding-top: 2rpx; /* 标题与图片顶部对齐 */ + } + + .shangpin-title { + margin-bottom: 0; + position: relative; + } + + .shangpin-title-text { + font-size: 30rpx; + font-weight: 500; + color: #222; /* 黑色加深 */ + line-height: 1.4; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; + overflow: hidden; + } + + .title-underline { + position: absolute; + bottom: -4rpx; + left: 0; + width: 0; + height: 2rpx; + background: linear-gradient(90deg, #ffcc00, #ffaa00); + transition: width 0.3s; + } + + .shangpin-item:active .title-underline { + width: 40rpx; + } + + .shangpin-footer { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: auto; + } + + .shangpin-price { + display: flex; + align-items: baseline; + position: relative; + } + + .price-icon { + font-size: 24rpx; + color: #ff6600; + font-weight: 500; + } + + .price-integer { + font-size: 36rpx; /* 适当调整大小 */ + color: #ff6600; + font-weight: 600; + margin-left: 2rpx; + } + + .price-decimal { + font-size: 24rpx; + color: #ff6600; + font-weight: 500; + } + + .price-glow { + position: absolute; + top: 50%; + left: 50%; + width: 80rpx; + height: 80rpx; + background: radial-gradient(circle, rgba(255, 102, 0, 0.1) 0%, transparent 70%); + transform: translate(-50%, -50%); + opacity: 0; + pointer-events: none; + } + + .shangpin-item:active .price-glow { + animation: priceGlow 0.6s; + } + + .detail-btn { + position: relative; + padding: 12rpx 28rpx; + background: linear-gradient(135deg, #ffcc00, #ffaa00); + border-radius: 40rpx; + transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); + overflow: hidden; + box-shadow: 0 6rpx 20rpx rgba(255, 170, 0, 0.2); + display: flex; + align-items: center; + justify-content: center; + min-width: 120rpx; + } + + .detail-btn:active { + transform: scale(0.95); + box-shadow: 0 4rpx 15rpx rgba(255, 170, 0, 0.4); + } + + .detail-btn-text { + font-size: 24rpx; + color: white; + font-weight: 500; + position: relative; + z-index: 2; + line-height: 1; + } + + .btn-glow-effect { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent); + transform: translateX(-100%); + transition: transform 0.6s; + } + + .detail-btn:active .btn-glow-effect { + transform: translateX(100%); + } + + .btn-halo { + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; + border-radius: 50%; + background: rgba(255, 255, 255, 0.3); + transform: translate(-50%, -50%); + opacity: 0; + pointer-events: none; + } + + .detail-btn:active .btn-halo { + animation: buttonHalo 0.6s ease-out; + } + + .btn-star { + position: absolute; + width: 6rpx; + height: 6rpx; + background: white; + border-radius: 50%; + opacity: 0; + pointer-events: none; + } + + .detail-btn:active .btn-star { + animation: starSparkle 0.8s ease-out; + } + + .item-divider { + position: absolute; + left: 30rpx; + right: 30rpx; + bottom: 0; + height: 1rpx; + background: linear-gradient(90deg, rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.08)); + } + + .item-hover-glow { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, transparent, rgba(255, 204, 0, 0.03), transparent); + opacity: 0; + transition: opacity 0.3s; + pointer-events: none; + } + + .shangpin-item:active .item-hover-glow { + opacity: 1; + } + + .item-ripple-effect { + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; + border-radius: 50%; + background: rgba(255, 204, 0, 0.1); + transform: translate(-50%, -50%); + opacity: 0; + pointer-events: none; + } + + .shangpin-item:active .item-ripple-effect { + animation: rippleEffect 0.6s ease-out; + } + + .shangpin-empty { + display: flex; + flex-direction: column; + align-items: center; + padding: 150rpx 0; + } + + .shangpin-empty-icon { + width: 180rpx; + height: 180rpx; + opacity: 0.15; + margin-bottom: 36rpx; + } + + .shangpin-empty-text { + font-size: 28rpx; + color: #999; + margin-bottom: 40rpx; + } + + .shangpin-empty-btn { + background: linear-gradient(135deg, #ffcc00, #ffaa00); + color: white; + font-size: 26rpx; + font-weight: 500; + padding: 16rpx 44rpx; + border-radius: 40rpx; + box-shadow: 0 8rpx 24rpx rgba(255, 170, 0, 0.3); + transition: all 0.3s; + } + + .shangpin-empty-btn:active { + transform: scale(0.95); + box-shadow: 0 4rpx 16rpx rgba(255, 170, 0, 0.4); + } + + .load-more { + display: flex; + align-items: center; + justify-content: center; + padding: 40rpx 0; + } + + .load-more-line { + width: 60rpx; + height: 1rpx; + background: linear-gradient(90deg, transparent, rgba(0, 0, 0, 0.1), transparent); + } + + .load-more-text { + font-size: 24rpx; + color: #bbb; + margin: 0 20rpx; + } + + /* 搜索结果弹窗 */ + .search-results-container { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 2000; + display: flex; + } + + .search-mask { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + backdrop-filter: blur(5rpx); + } + + .search-results-content { + position: absolute; + top: 100rpx; + left: 32rpx; + right: 32rpx; + bottom: 100rpx; + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(30rpx); + border-radius: 24rpx; + box-shadow: + 0 20rpx 60rpx rgba(0, 0, 0, 0.2), + inset 0 1rpx 0 rgba(255, 255, 255, 0.8); + border: 1rpx solid rgba(255, 255, 255, 0.3); + display: flex; + flex-direction: column; + overflow: hidden; + } + + .search-results-header { + padding: 32rpx; + border-bottom: 1rpx solid rgba(0, 0, 0, 0.06); + display: flex; + align-items: center; + justify-content: space-between; + flex-shrink: 0; + } + + .search-header-left { + display: flex; + align-items: baseline; + } + + .search-results-title { + font-size: 34rpx; + font-weight: 600; + color: #333; + } + + .search-results-count { + font-size: 24rpx; + color: #999; + margin-left: 12rpx; + } + + .search-close { + width: 44rpx; + height: 44rpx; + border-radius: 50%; + background: rgba(0, 0, 0, 0.05); + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s; + } + + .search-close:active { + background: rgba(0, 0, 0, 0.1); + transform: scale(0.9); + } + + .close-icon { + width: 20rpx; + height: 20rpx; + opacity: 0.6; + } + + .search-results-list { + flex: 1; + height: 100%; /* 新增:修复滚动问题的关键代码 */ + } + + .search-result-item { + display: flex; + padding: 28rpx 32rpx; + border-bottom: 1rpx solid rgba(0, 0, 0, 0.04); + position: relative; + transition: all 0.3s; + } + + .search-result-item:active { + background: rgba(0, 0, 0, 0.02); + transform: translateY(-2rpx); + } + + .search-result-left { + margin-right: 24rpx; + position: relative; + } + + .search-result-image { + width: 120rpx; + height: 120rpx; + border-radius: 16rpx; + background: #f5f5f5; + object-fit: cover; + } + + .search-image-glow { + position: absolute; + top: -4rpx; + left: -4rpx; + right: -4rpx; + bottom: -4rpx; + border-radius: 20rpx; + background: linear-gradient(135deg, rgba(255, 204, 0, 0.2), transparent); + opacity: 0; + transition: opacity 0.3s; + pointer-events: none; + } + + .search-result-item:active .search-image-glow { + opacity: 0.5; + } + + .search-result-right { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + } + + .search-result-title { + font-size: 28rpx; + font-weight: 500; + color: #333; + line-height: 1.4; + margin-bottom: 16rpx; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + } + + .search-result-info { + display: flex; + gap: 12rpx; + margin-bottom: 20rpx; + } + + .info-tag { + padding: 6rpx 12rpx; + background: rgba(255, 204, 0, 0.1); + border-radius: 6rpx; + } + + .search-result-category { + font-size: 22rpx; + color: #666; + } + + .search-result-zhuanqu { + font-size: 22rpx; + color: #888; + } + + .search-result-price { + display: flex; + align-items: baseline; + } + + .search-item-glow { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, transparent, rgba(255, 204, 0, 0.05), transparent); + opacity: 0; + transition: opacity 0.3s; + pointer-events: none; + } + + .search-result-item:active .search-item-glow { + opacity: 1; + } + + .search-empty { + display: flex; + flex-direction: column; + align-items: center; + padding: 160rpx 0; + } + + .search-empty-icon { + width: 160rpx; + height: 160rpx; + opacity: 0.2; + margin-bottom: 36rpx; + } + + .search-empty-text { + font-size: 30rpx; + color: #999; + margin-bottom: 16rpx; + } + + .search-empty-tip { + font-size: 24rpx; + color: #bbb; + } + + .search-loading { + display: flex; + flex-direction: column; + align-items: center; + padding: 160rpx 0; + } + + .search-spinner { + width: 60rpx; + height: 60rpx; + position: relative; + margin-bottom: 24rpx; + } + + .spinner-circle { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border: 4rpx solid transparent; + border-top-color: #ffcc00; + border-radius: 50%; + animation: spin 1s linear infinite; + } + + .search-loading-text { + font-size: 26rpx; + color: #999; + } + + /* 全局加载状态 */ + .loading-toast { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.7); + backdrop-filter: blur(10rpx); + display: flex; + align-items: center; + justify-content: center; + z-index: 3000; + } + + .loading-content { + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(20rpx); + padding: 60rpx 80rpx; + border-radius: 24rpx; + display: flex; + flex-direction: column; + align-items: center; + position: relative; + border: 1rpx solid rgba(255, 255, 255, 0.1); + } + + .loading-spinner { + width: 80rpx; + height: 80rpx; + position: relative; + margin-bottom: 30rpx; + } + + .spinner-ring { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + border: 4rpx solid rgba(255, 255, 255, 0.2); + border-radius: 50%; + } + + .spinner-dot { + position: absolute; + top: 0; + left: 50%; + width: 12rpx; + height: 12rpx; + background: #ffcc00; + border-radius: 50%; + transform: translateX(-50%); + animation: spin 1s linear infinite; + } + + .loading-text { + font-size: 28rpx; + color: white; + font-weight: 500; + } + + .loading-glow { + position: absolute; + top: 50%; + left: 50%; + width: 200rpx; + height: 200rpx; + background: radial-gradient(circle, rgba(255, 204, 0, 0.2) 0%, transparent 70%); + transform: translate(-50%, -50%); + z-index: -1; + } + + /* 动画定义 */ + @keyframes glowPulse { + 0%, 100% { + opacity: 0.4; + transform: scale(1); + } + 50% { + opacity: 0.8; + transform: scale(1.05); + } + } + + @keyframes sparkleFlash { + 0%, 100% { + opacity: 0; + transform: translate(-50%, -50%) scale(0); + } + 50% { + opacity: 1; + transform: translate(-50%, -50%) scale(1); + filter: blur(0); + } + } + + @keyframes borderGlow { + 0%, 100% { + border-color: rgba(255, 204, 0, 0.3); + } + 50% { + border-color: rgba(255, 204, 0, 0.8); + } + } + + @keyframes indicatorGlow { + 0%, 100% { + box-shadow: 0 0 20rpx rgba(255, 204, 0, 0.3); + } + 50% { + box-shadow: 0 0 30rpx rgba(255, 204, 0, 0.8); + } + } + + @keyframes sparkleMove { + 0% { + transform: translateY(-100%); + opacity: 0; + } + 50% { + opacity: 1; + } + 100% { + transform: translateY(400%); + opacity: 0; + } + } + + @keyframes rippleEffect { + 0% { + width: 0; + height: 0; + opacity: 0.5; + } + 100% { + width: 200rpx; + height: 200rpx; + opacity: 0; + } + } + + @keyframes priceGlow { + 0% { + opacity: 0; + transform: translate(-50%, -50%) scale(0.5); + } + 50% { + opacity: 0.6; + transform: translate(-50%, -50%) scale(1); + } + 100% { + opacity: 0; + transform: translate(-50%, -50%) scale(1.5); + } + } + + @keyframes buttonHalo { + 0% { + width: 0; + height: 0; + opacity: 0.6; + } + 100% { + width: 200rpx; + height: 200rpx; + opacity: 0; + } + } + + @keyframes starSparkle { + 0% { + opacity: 0; + transform: translate(0, 0) scale(0); + } + 20% { + opacity: 1; + transform: translate(-40rpx, -30rpx) scale(1); + } + 40% { + opacity: 1; + transform: translate(30rpx, -20rpx) scale(1); + } + 60% { + opacity: 1; + transform: translate(-20rpx, 40rpx) scale(1); + } + 80% { + opacity: 1; + transform: translate(40rpx, 20rpx) scale(1); + } + 100% { + opacity: 0; + transform: translate(0, 0) scale(0); + } + } + + @keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } + } \ No newline at end of file diff --git a/pages/guanshiduan/guanshiduan.js b/pages/guanshiduan/guanshiduan.js new file mode 100644 index 0000000..a8a06b1 --- /dev/null +++ b/pages/guanshiduan/guanshiduan.js @@ -0,0 +1,680 @@ +// pages/guanshizhongxin/guanshizhongxin.js +import request from '../../utils/request.js'; +import popupService from '../../services/popupService.js'; +import { buildInviterPeerId } from '../../utils/im-user.js'; +import { resolveAvatarUrl } from '../../utils/avatar.js'; + +Page({ + data: { + // 图片路径对象(逻辑层拼接完整URL) + imgUrls: {}, + + isGuanshi: false, + isLoading: false, + inviteCode: '', + uid: '', + nicheng: '', + avatarUrl: '', + + // 管事信息 + gszhstatus: '', + yaoqingzongshu: 0, + fenyongzonge: '0.00', + fenyongtixian: '0.00', + yichongzhiDashou: 0, + zhuanhualv: 0, + + // 称号列表 + chenghaoList: [], + + // 邀请人(组长)缓存 + inviterCache: null, + }, + + onLoad(options) { + let url = '/pages/dashouduan/dashouduan?registerType=guanshi'; + if (options.scene) { + try { + url += '&inviteCode=' + encodeURIComponent(decodeURIComponent(options.scene)); + } catch (e) { + console.error('scene解码失败', e); + } + } else if (options.inviteCode) { + url += '&inviteCode=' + encodeURIComponent(options.inviteCode); + } + wx.redirectTo({ url }); + return; + // --- 以下保留供恢复参考,不再执行 --- + // 拼接所有图片完整路径 + const app = getApp(); + const ossImageUrl = app.globalData.ossImageUrl || ''; + const imgBase = ossImageUrl + 'beijing/guanshiduan/'; + this.setData({ + imgUrls: { + pageBg: imgBase + 'page-bg.jpg', + topDecor: imgBase + 'top-decor.png', + cardBg1: imgBase + 'card-bg1.png', + cardBg2: imgBase + 'card-bg2.png', + iconRefresh: imgBase + 'icon-refresh.png', + iconCopy: imgBase + 'icon-copy.png', + avatarDefault: ossImageUrl + 'avatar/default.jpg', + iconInvite: imgBase + 'icon-invite.png', + iconSub: imgBase + 'icon-sub.png', + iconRecord: imgBase + 'icon-record.png', + iconRank: imgBase + 'icon-rank.png', + iconPoster: imgBase + 'icon-poster.png', + iconWithdraw: imgBase + 'icon-withdraw.png', + iconContact: imgBase + 'icon-contact.png', + iconArrow: imgBase + 'icon-arrow.png', + } + }); + + // 处理扫码 scene + if (options.scene) { + try { + const scene = decodeURIComponent(options.scene); + options.inviteCode = scene; + } catch (e) { + console.error('scene解码失败', e); + } + } + + this.checkGuanshiStatus(); + + const { inviteCode } = options || {}; + if (inviteCode) { + this.setData({ inviteCode }); + setTimeout(() => { + this.handleInviteCodeWithLoginCheck(inviteCode); + }, 500); + } + + if (!app.globalData.hasShownPopupOnColdStart) { + app.globalData.hasShownPopupOnColdStart = true; + setTimeout(() => { + popupService.checkAndShow(this, 'guanshiduan'); + }, 300); + } + }, + + onReady() { + if (this._isGuanshiPageActive()) { + this.ensureGuanshiRoleAndTabBar(); + } + }, + + onHide() { + const popupComp = this.selectComponent('#popupNotice'); + if (popupComp && popupComp.cleanup) { + popupComp.cleanup(); + } + }, + + onShow() { + const inviterCache = wx.getStorageSync('inviterCache') || null; + this.setData({ inviterCache }); + + this.registerNotificationComponent(); + + if (this._isGuanshiPageActive()) { + if (!this.data.isGuanshi) { + this.setData({ isGuanshi: true }); + } + this.ensureGuanshiRoleAndTabBar(); + setTimeout(() => { + this.getGuanshiInfo(); + this.fetchChenghaoList(); + }, 300); + } + + const pages = getCurrentPages(); + const currentPage = pages[pages.length - 1]; + const options = currentPage.options || {}; + if (options.inviteCode && !this._isGuanshiPageActive()) { + setTimeout(() => { + this.handleInviteCodeWithLoginCheck(options.inviteCode); + }, 300); + } + }, + + // ========== 全局通知注册 ========== + 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() + }; + } + }, + + // ========== 称号列表(新接口,区分管事) ========== + async fetchChenghaoList() { + try { + const res = await request({ + url: '/dengji/chenghao', + method: 'POST', + data: { role: 'guanshi' } + }); + if (res && res.data.code === 0 && res.data.data) { + this.setData({ chenghaoList: res.data.data.chenghao_list || [] }); + } + } catch (e) { + console.error('获取称号失败', e); + } + }, + + // ========== 联系组长(新接口) ========== + // 管事联系组长 —— 获取组长信息并跳转聊天 + // 管事联系组长 +async contactInviter() { + wx.showLoading({ title: '获取组长信息...', mask: true }); + try { + const res = await request({ + url: '/yonghu/guanshi_contact', + method: 'POST' + }); + + if (res && res.data.code === 200 && res.data.data) { + const inviter = res.data.data; + const ossImageUrl = getApp().globalData.ossImageUrl || ''; + const fullAvatar = inviter.touxiang + ? (inviter.touxiang.startsWith('http') ? inviter.touxiang : ossImageUrl + inviter.touxiang) + : ''; + + // 缓存邀请人信息 + const cacheData = { + uid: inviter.uid, + nicheng: inviter.nicheng || '', + avatar: fullAvatar + }; + wx.setStorageSync('inviterCache', cacheData); + this.setData({ inviterCache: cacheData }); + + // 联系邀请人:统一 Ds 前缀(管事/组长均走打手身份) + const param = { + toUserId: buildInviterPeerId(inviter.uid), + toName: inviter.nicheng || '邀请人', + toAvatar: fullAvatar + }; + wx.navigateTo({ + url: '/pages/liaotian/liaotian?data=' + encodeURIComponent(JSON.stringify(param)) + }); + } else { + wx.showToast({ title: '未绑定邀请人', icon: 'none' }); + } + } catch (e) { + wx.showToast({ title: '网络错误', icon: 'none' }); + } finally { + wx.hideLoading(); + } +}, + // ========== 管事状态辅助 ========== + _isApiSuccess(body) { + if (!body) return false; + const code = Number(body.code); + return code === 200 || code === 0; + }, + + _getApiMsg(body) { + if (!body) return ''; + return body.msg || body.message || ''; + }, + + _extractUserData(body) { + if (!body) return {}; + if (body.data && typeof body.data === 'object' && !Array.isArray(body.data)) { + return body.data; + } + const userData = { ...body }; + delete userData.code; + delete userData.msg; + delete userData.message; + return userData; + }, + + _isGuanshiStatusActive(status) { + return Number(status) === 1; + }, + + _isAlreadyGuanshiMsg(msg) { + if (!msg || typeof msg !== 'string') return false; + return msg.includes('已是管事') || msg.includes('已经是管事'); + }, + + _syncGuanshiCache() { + const app = getApp(); + wx.setStorageSync('guanshistatus', 1); + app.globalData.guanshistatus = 1; + }, + + // 仅同步后端响应里「明确返回」的身份 status 字段(含 0),不臆测、不补打手 + _syncRoleStatuses(data) { + if (!data || typeof data !== 'object') return; + const app = getApp(); + const roleFields = ['dashoustatus', 'guanshistatus', 'shangjiastatus', 'zuzhangstatus', 'kaoheguanstatus']; + roleFields.forEach(field => { + if (Object.prototype.hasOwnProperty.call(data, field)) { + const val = Number(data[field]); + const normalized = Number.isNaN(val) ? 0 : val; + wx.setStorageSync(field, normalized); + app.globalData[field] = normalized; + } + }); + }, + + // 后端若返回打手详情字段则写入 globalData(与 dashoustatus 是否=1 无关) + _syncDashouGlobalData(data) { + const app = getApp(); + const map = { + dashouNicheng: data.dashounicheng, + zhanghaoStatus: data.zhanghaostatus, + yongjin: data.yongjin, + zonge: data.zonge, + yajin: data.yajin, + chenghao: data.chenghao, + jinfen: data.jinfen, + clumber: data.clumber, + chengjiaoliang: data.chengjiaoliang, + zaixianZhuangtai: data.zaixianzhuangtai, + dashouzhuangtai: data.dashouzhuangtai, + }; + Object.keys(map).forEach(key => { + if (map[key] !== undefined) { + app.globalData[key] = map[key]; + } + }); + }, + + _isGuanshiPageActive() { + return this.data.isGuanshi || this._isGuanshiStatusActive(wx.getStorageSync('guanshistatus')); + }, + + // 管事页必须切到 guanshi 角色,并强制刷新 TabBar(扫码落地页不在老板 Tab 列表里) + ensureGuanshiRoleAndTabBar() { + const app = getApp(); + if (app.setCurrentRole) { + app.setCurrentRole('guanshi'); + } else { + app.globalData.currentRole = 'guanshi'; + wx.setStorageSync('currentRole', 'guanshi'); + app.emitEvent('currentRoleChanged', { role: 'guanshi' }); + } + this._refreshTabBar(); + }, + + _refreshTabBar() { + const tryRefresh = () => { + const tabBar = this.getTabBar && this.getTabBar(); + if (tabBar && tabBar.refresh) { + tabBar.refresh(); + } + }; + tryRefresh(); + [50, 150, 300, 500, 800].forEach((delay) => setTimeout(tryRefresh, delay)); + }, + + _applyRegisterSuccess(userData, toastMsg) { + const app = getApp(); + const data = userData || {}; + if (data.token) wx.setStorageSync('token', data.token); + if (data.token) app.globalData.token = data.token; + + const profileFields = ['nicheng', 'uid', 'touxiang']; + profileFields.forEach(field => { + if (data[field] !== undefined && data[field] !== null) { + wx.setStorageSync(field, data[field]); + if (field === 'nicheng') { + app.globalData.nicheng = data[field]; + } + } + }); + + // 身份列表完全以后端返回的 *status 字段为准(见 _syncRoleStatuses) + this._syncRoleStatuses(data); + // 仅在本页管事注册成功、但后端未带 guanshistatus 时兜底 + if (!Object.prototype.hasOwnProperty.call(data, 'guanshistatus') && + !this._isGuanshiStatusActive(wx.getStorageSync('guanshistatus'))) { + this._syncGuanshiCache(); + } + this._syncDashouGlobalData(data); + + const groupFields = ['dashouqun', 'dashouqunid', 'guanshiqun', 'guanshiqunid']; + groupFields.forEach(field => { + if (data[field] !== undefined) { + wx.setStorageSync(field, data[field]); + app.globalData[field] = data[field]; + } + }); + + const guanshiData = { + gszhstatus: data.gszhstatus ?? '', + yaoqingzongshu: data.yaoqingzongshu ?? 0, + fenyongzonge: data.fenyongzonge ?? '0.00', + fenyongtixian: data.fenyongtixian ?? '0.00', + yichongzhiDashou: data.yichongzhiDashou ?? 0 + }; + if (!app.globalData.guanshi) app.globalData.guanshi = {}; + Object.assign(app.globalData.guanshi, guanshiData); + + const zhuanhualv = guanshiData.yaoqingzongshu > 0 + ? (guanshiData.yichongzhiDashou / guanshiData.yaoqingzongshu * 100).toFixed(1) + : 0; + + this.setData({ + isGuanshi: true, + uid: data.uid || wx.getStorageSync('uid') || '', + nicheng: data.nicheng || app.globalData.nicheng || wx.getStorageSync('nicheng') || '', + ...guanshiData, + zhuanhualv, + isLoading: false + }); + this.loadUserInfo(); + this.switchToGuanshiRole(); + wx.hideLoading(); + if (toastMsg) { + wx.showToast({ title: toastMsg, icon: 'success', duration: 2000 }); + } + }, + + _enterRegisteredGuanshiState(toastMsg, userData) { + if (userData && typeof userData === 'object') { + this._applyRegisterSuccess(userData, toastMsg); + return; + } + this._syncGuanshiCache(); + this.setData({ isGuanshi: true, isLoading: false }); + this.loadUserInfo(); + this.switchToGuanshiRole(); + this.getGuanshiInfo(false); + this.fetchChenghaoList(); + wx.hideLoading(); + if (toastMsg) { + wx.showToast({ title: toastMsg, icon: 'success', duration: 2000 }); + } + }, + + // ========== 邀请码处理 ========== + handleInviteCodeWithLoginCheck(inviteCode) { + const token = wx.getStorageSync('token'); + const uid = wx.getStorageSync('uid'); + if (!token || !uid) { + this.loginAndRegisterWithInviteCode(inviteCode); + } else { + if (!this.data.isGuanshi) { + wx.showLoading({ title: '自动注册中...', mask: true }); + this.callRegisterApi(inviteCode); + } else { + this._enterRegisteredGuanshiState('您已是管事'); + } + } + }, + + loginAndRegisterWithInviteCode(inviteCode) { + const that = this; + wx.showLoading({ title: '登录注册中...', mask: true }); + wx.login({ + success: (loginRes) => { + if (loginRes.code) { + that.callLoginRegisterApi(loginRes.code, inviteCode); + } else { + wx.hideLoading(); + wx.showToast({ title: '微信登录失败', icon: 'none' }); + } + }, + fail: () => { + wx.hideLoading(); + wx.reLaunch({ url: '/pages/gerenzhongxin/gerenzhongxin' }); + } + }); + }, + + callLoginRegisterApi(code, inviteCode) { + const that = this; + const app = getApp(); + const apiBaseUrl = app.globalData.apiBaseUrl; + if (!apiBaseUrl) { + wx.hideLoading(); + wx.showToast({ title: '服务器配置错误', icon: 'none' }); + return; + } + wx.request({ + url: apiBaseUrl + '/yonghu/zuzhangyqmzc', + method: 'POST', + data: { code: code, inviteCode: inviteCode }, + header: { 'content-type': 'application/json' }, + success: (res) => { + const body = res.data || {}; + if (res.statusCode === 200 && that._isApiSuccess(body)) { + const userData = that._extractUserData(body); + const toastMsg = that._isAlreadyGuanshiMsg(that._getApiMsg(body)) + ? that._getApiMsg(body) + : '注册成功!'; + that._applyRegisterSuccess(userData, toastMsg); + } else if (res.statusCode === 200 && that._isAlreadyGuanshiMsg(that._getApiMsg(body))) { + that._enterRegisteredGuanshiState(that._getApiMsg(body) || '您已是管事', that._extractUserData(body)); + } else { + wx.hideLoading(); + that.setData({ isLoading: false }); + wx.showToast({ title: that._getApiMsg(body) || '注册失败', icon: 'none' }); + } + }, + fail: () => { + wx.hideLoading(); + wx.showToast({ title: '网络请求失败', icon: 'none' }); + } + }); + }, + + async callRegisterApi(inviteCode) { + try { + const res = await request({ + url: '/yonghu/guanshizhuce', + method: 'POST', + data: { inviteCode } + }); + const body = res?.data || {}; + if (res && this._isApiSuccess(body)) { + const userData = this._extractUserData(body); + const toastMsg = this._isAlreadyGuanshiMsg(this._getApiMsg(body)) + ? this._getApiMsg(body) + : '注册成功!'; + this._applyRegisterSuccess(userData, toastMsg); + } else if (this._isAlreadyGuanshiMsg(this._getApiMsg(body))) { + this._enterRegisteredGuanshiState(this._getApiMsg(body) || '您已是管事', this._extractUserData(body)); + } else { + wx.hideLoading(); + this.setData({ isLoading: false }); + wx.showToast({ title: this._getApiMsg(body) || '注册失败', icon: 'none' }); + } + } catch (e) { + console.error('注册失败:', e); + wx.hideLoading(); + this.setData({ isLoading: false }); + wx.showToast({ title: e.message || '注册失败', icon: 'error' }); + } + }, + + switchToGuanshiRole() { + const app = getApp(); + this.ensureGuanshiRoleAndTabBar(); + if (app.switchRoleAndReconnect) { + app.switchRoleAndReconnect('dashou').catch(() => {}); + } else if (app.connectForCurrentRole) { + app.connectForCurrentRole(); + } else if (app.ensureConnection) { + app.ensureConnection(); + } + }, + + checkGuanshiStatus() { + try { + const status = wx.getStorageSync('guanshistatus'); + if (this._isGuanshiStatusActive(status)) { + this.setData({ isGuanshi: true }); + this.loadUserInfo(); + this.checkGlobalData(); + this.ensureGuanshiRoleAndTabBar(); + } else { + this.setData({ isGuanshi: false }); + } + } catch (e) { + this.setData({ isGuanshi: false }); + } + }, + + loadUserInfo() { + const app = getApp(); + const globalData = app.globalData || {}; + const touxiang = wx.getStorageSync('touxiang'); + const ossImageUrl = globalData.ossImageUrl || ''; + const morentouxiang = 'avatar/default.jpg'; + let avatarUrl = ''; + if (touxiang) { + avatarUrl = ossImageUrl + (touxiang.startsWith('/') ? touxiang : '/' + touxiang); + } else { + avatarUrl = ossImageUrl + (morentouxiang.startsWith('/') ? morentouxiang : '/' + morentouxiang); + } + this.setData({ + avatarUrl, + uid: wx.getStorageSync('uid') || '', + nicheng: globalData.nicheng || wx.getStorageSync('nicheng') || '' + }); + }, + + checkGlobalData() { + const app = getApp(); + const gd = app.globalData.guanshi || {}; + const needRefresh = !gd.yaoqingzongshu && !gd.fenyongzonge && !gd.fenyongtixian; + if (needRefresh) { + this.getGuanshiInfo(); + } else { + this.setDataFromGlobalData(); + } + }, + + setDataFromGlobalData() { + const gd = getApp().globalData.guanshi || {}; + const zhuanhualv = gd.yaoqingzongshu > 0 + ? (gd.yichongzhiDashou / gd.yaoqingzongshu * 100).toFixed(1) + : 0; + this.setData({ + gszhstatus: gd.gszhstatus || '', + yaoqingzongshu: gd.yaoqingzongshu || 0, + fenyongzonge: gd.fenyongzonge || '0.00', + fenyongtixian: gd.fenyongtixian || '0.00', + yichongzhiDashou: gd.yichongzhiDashou || 0, + zhuanhualv, + isLoading: false + }); + }, + + async getGuanshiInfo(showToast = true) { + this.setData({ isLoading: true }); + try { + const res = await request({ + url: '/yonghu/guanshixinxi', + method: 'POST' + }); + if (res && this._isApiSuccess(res.data)) { + const data = this._extractUserData(res.data); + const app = getApp(); + this._syncGuanshiCache(); + if (!app.globalData.guanshi) app.globalData.guanshi = {}; + const updateData = { + gszhstatus: data.gszhstatus || '', + yaoqingzongshu: data.yaoqingzongshu || 0, + fenyongzonge: data.fenyongzonge || '0.00', + fenyongtixian: data.fenyongtixian || '0.00', + yichongzhiDashou: data.yichongzhiDashou || 0 + }; + Object.assign(app.globalData.guanshi, updateData); + const zhuanhualv = updateData.yaoqingzongshu > 0 + ? (updateData.yichongzhiDashou / updateData.yaoqingzongshu * 100).toFixed(1) + : 0; + this.setData({ + isGuanshi: true, + ...updateData, + zhuanhualv, + isLoading: false + }); + if (showToast) { + wx.showToast({ title: '信息已更新', icon: 'success', duration: 1500 }); + } + } else { + this.setData({ isLoading: false }); + if (showToast) { + wx.showToast({ title: this._getApiMsg(res?.data) || '获取失败', icon: 'none' }); + } + } + } catch (e) { + this.setData({ isLoading: false }); + if (showToast) { + wx.showToast({ title: e.message || '获取失败', icon: 'error' }); + } + } + }, + + refreshGuanshiInfo() { + this.getGuanshiInfo(); + this.fetchChenghaoList(); + }, + + onInviteCodeInput(e) { + this.setData({ inviteCode: e.detail.value.trim() }); + }, + + async onRegister() { + const { inviteCode } = this.data; + if (!inviteCode) { + wx.showToast({ title: '请输入邀请码', icon: 'none' }); + return; + } + if (inviteCode.length > 100) { + wx.showToast({ title: '邀请码过长', icon: 'none' }); + return; + } + this.setData({ isLoading: true }); + try { + const res = await request({ + url: '/yonghu/guanshizhuce', + method: 'POST', + data: { inviteCode } + }); + const body = res?.data || {}; + if (res && this._isApiSuccess(body)) { + const userData = this._extractUserData(body); + const toastMsg = this._isAlreadyGuanshiMsg(this._getApiMsg(body)) + ? this._getApiMsg(body) + : '注册成功!'; + this._applyRegisterSuccess(userData, toastMsg); + } else if (this._isAlreadyGuanshiMsg(this._getApiMsg(body))) { + this._enterRegisteredGuanshiState(this._getApiMsg(body) || '您已是管事', this._extractUserData(body)); + } else { + this.setData({ isLoading: false }); + wx.showToast({ title: this._getApiMsg(body) || '注册失败', icon: 'none' }); + } + } catch (e) { + this.setData({ isLoading: false }); + wx.showToast({ title: e.message || '注册失败', icon: 'error' }); + } + }, + + copyUid() { + const { uid } = this.data; + if (!uid) return; + wx.setClipboardData({ + data: uid, + success: () => wx.showToast({ title: '已复制', icon: 'success' }) + }); + }, + + // ========== 页面跳转 ========== + goToWithdraw() { wx.navigateTo({ url: '/pages/tixian/tixian' }); }, + goToInviteUser() { wx.navigateTo({ url: '/pages/yaoqingdashou/yaoqingdashou' }); }, + goToMySubordinate() { wx.navigateTo({ url: '/pages/wodedashou/wodedashou' }); }, + goToRechargeRecord() { wx.navigateTo({ url: '/pages/czjilu/czjilu' }); }, + goToGuanshiRanking() { wx.navigateTo({ url: '/pages/guanshipaihang/guanshipaihang' }); }, + goToPromoPoster() { wx.navigateTo({ url: '/pages/haibao/haibao' }); } +}); \ No newline at end of file diff --git a/pages/guanshiduan/guanshiduan.json b/pages/guanshiduan/guanshiduan.json new file mode 100644 index 0000000..6a1a405 --- /dev/null +++ b/pages/guanshiduan/guanshiduan.json @@ -0,0 +1,12 @@ +{ + "usingComponents": { + "global-notification": "/components/global-notification/global-notification", + "chenghao-tag": "/components/chenghao-tag/chenghao-tag", + "custom-tab-bar": "/custom-tab-bar/index" + }, + "navigationBarBackgroundColor": "#f0f4f8", + "navigationBarTextStyle": "black", + "navigationBarTitleText": "管事中心", + "backgroundColor": "#f0f4f8", + "enablePullDownRefresh": false + } \ No newline at end of file diff --git a/pages/guanshiduan/guanshiduan.wxml b/pages/guanshiduan/guanshiduan.wxml new file mode 100644 index 0000000..3ba8dbc --- /dev/null +++ b/pages/guanshiduan/guanshiduan.wxml @@ -0,0 +1,122 @@ + + + + + + + ✦ 管事认证 ✦ + + + 立即注册 + + + + + + + + + + + + + + + + + + + + + + + + + + + 可提现分成 + + {{fenyongtixian}} + + + + + + {{yaoqingzongshu}} + 邀请总数 + + + {{yichongzhiDashou}} + 已充值 + + + {{fenyongzonge}} + 分成总额 + + + {{zhuanhualv}}% + 有效率 + + + + + + 提现 + + + + + + + + 邀请用户 + + + + 我的下级 + + + + 会员记录 + + + + 管事排行 + + + + 推广海报 + + + + 联系组长 + + + + + + + + + {{inviterCache.nicheng || '联系组长'}} + ID: {{inviterCache.uid}} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pages/guanshiduan/guanshiduan.wxss b/pages/guanshiduan/guanshiduan.wxss new file mode 100644 index 0000000..3cf8113 --- /dev/null +++ b/pages/guanshiduan/guanshiduan.wxss @@ -0,0 +1,367 @@ +/* ========== 全局背景 ========== */ +page { + background: #F8FAFE; + font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', sans-serif; + color: #1A1A1A; + } + + /* 背景图清晰可见 */ + .page-bg { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0.4; + z-index: 0; + pointer-events: none; + } + + /* ========== 未注册区域 ========== */ + .unreg-area { + position: relative; + z-index: 2; + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; + padding: 0 50rpx; + } + + .unreg-card { + width: 100%; + background: rgba(255, 255, 255, 0.75); + backdrop-filter: blur(25rpx); + -webkit-backdrop-filter: blur(25rpx); + border-radius: 48rpx; + padding: 60rpx 50rpx; + text-align: center; + box-shadow: 0 20rpx 50rpx rgba(0,0,0,0.06); + border: 1rpx solid rgba(255,255,255,0.5); + } + + .unreg-title { + font-size: 40rpx; + font-weight: 700; + color: #1A1A1A; + display: block; + margin-bottom: 50rpx; + } + + .unreg-input { + width: 100%; + height: 96rpx; + background: #FFFFFF; + border-radius: 48rpx; + padding: 0 40rpx; + font-size: 30rpx; + color: #1A1A1A; + margin-bottom: 40rpx; + box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.03); + } + + .ph { + color: #999999; + } + + .unreg-btn { + width: 100%; + height: 96rpx; + background: rgba(255,255,255,0.6); + backdrop-filter: blur(15rpx); + border-radius: 48rpx; + display: flex; + align-items: center; + justify-content: center; + border: 2rpx solid rgba(0,0,0,0.08); + box-shadow: 0 12rpx 30rpx rgba(0,0,0,0.05); + } + .unreg-btn:active { opacity: 0.8; } + .unreg-btn-text { + color: #1A1A1A; + font-size: 34rpx; + font-weight: 600; + } + + /* ========== 已注册主区域 ========== */ + .main-body { + position: relative; + z-index: 2; + padding: 0 30rpx 40rpx; + } + + /* 刷新按钮 */ + .refresh-btn { + position: absolute; + top: 40rpx; + right: 40rpx; + width: 68rpx; + height: 68rpx; + background: rgba(255,255,255,0.75); + backdrop-filter: blur(12rpx); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + z-index: 20; + border: 1rpx solid rgba(255,255,255,0.6); + box-shadow: 0 8rpx 20rpx rgba(0,0,0,0.06); + } + .refresh-icon { + width: 38rpx; + height: 38rpx; + } + + /* 用户卡片 */ + .user-card { + margin-top: 60rpx; + margin-bottom: 30rpx; + background: rgba(255,255,255,0.7); + backdrop-filter: blur(20rpx); + -webkit-backdrop-filter: blur(20rpx); + border-radius: 36rpx; + padding: 28rpx 30rpx; + display: flex; + align-items: center; + border: 1rpx solid rgba(255,255,255,0.5); + box-shadow: 0 12rpx 35rpx rgba(0,0,0,0.05); + } + + .user-avatar { + width: 100rpx; + height: 100rpx; + border-radius: 50%; + border: 3rpx solid #FFFFFF; + margin-right: 24rpx; + } + + .user-name { + font-size: 38rpx; + font-weight: 700; + color: #1A1A1A; + display: block; + margin-bottom: 8rpx; + } + + .uid-row { + display: inline-flex; + align-items: center; + background: rgba(0,0,0,0.03); + border-radius: 20rpx; + padding: 6rpx 18rpx; + } + + .uid-text { + font-size: 26rpx; + color: #1A1A1A; + margin-right: 10rpx; + } + + .copy-icon { + width: 28rpx; + height: 28rpx; + } + + /* 称号区域 */ + .badge-zone { + margin-bottom: 24rpx; + } + .badge-scroll { + white-space: nowrap; + } + + /* 资产卡片(纯玻璃质感) */ + .asset-card { + background: rgba(255,255,255,0.7); + backdrop-filter: blur(20rpx); + -webkit-backdrop-filter: blur(20rpx); + border-radius: 36rpx; + padding: 28rpx 30rpx; + margin-bottom: 28rpx; + border: 1rpx solid rgba(255,255,255,0.5); + box-shadow: 0 12rpx 35rpx rgba(0,0,0,0.05); + } + + .asset-title { + font-size: 26rpx; + color: #1A1A1A; + font-weight: 500; + display: block; + } + + .asset-amount { + display: flex; + align-items: baseline; + margin: 10rpx 0 22rpx; + } + + .amount-number { + font-size: 74rpx; + font-weight: 800; + color: #1A1A1A; + margin-right: 8rpx; + } + + .amount-unit { + font-size: 32rpx; + color: #1A1A1A; + font-weight: 600; + } + + /* 四列数据统计 */ + .four-stats { + display: flex; + align-items: center; + justify-content: space-around; + padding: 20rpx 0; + border-top: 1rpx solid rgba(0,0,0,0.06); + border-bottom: 1rpx solid rgba(0,0,0,0.06); + margin-bottom: 22rpx; + } + + .stat-item { + text-align: center; + flex: 1; + } + + .stat-val { + font-size: 28rpx; + font-weight: 700; + color: #1A1A1A; + display: block; + margin-bottom: 6rpx; + } + + .stat-label { + font-size: 20rpx; + color: #1A1A1A; + font-weight: 500; + } + + /* 提现按钮 */ + .withdraw-btn { + display: flex; + align-items: center; + justify-content: center; + background: rgba(255,255,255,0.55); + backdrop-filter: blur(12rpx); + border-radius: 48rpx; + padding: 16rpx 0; + border: 2rpx solid rgba(0,0,0,0.06); + box-shadow: 0 8rpx 20rpx rgba(0,0,0,0.04); + } + + .withdraw-icon { + width: 36rpx; + height: 36rpx; + margin-right: 12rpx; + } + + .withdraw-text { + font-size: 30rpx; + font-weight: 600; + color: #1A1A1A; + } + + /* 功能入口三列布局 */ + .func-grid { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + margin-bottom: 28rpx; + } + + .func-item { + width: 31%; + aspect-ratio: 1 / 1; + background: rgba(255,255,255,0.7); + backdrop-filter: blur(16rpx); + -webkit-backdrop-filter: blur(16rpx); + border-radius: 28rpx; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + margin-bottom: 20rpx; + border: 1rpx solid rgba(255,255,255,0.5); + box-shadow: 0 8rpx 22rpx rgba(0,0,0,0.04); + } + + .func-icon { + width: 56rpx; + height: 56rpx; + margin-bottom: 12rpx; + } + + .func-label { + font-size: 26rpx; + font-weight: 500; + color: #1A1A1A; + text-align: center; + } + + /* 联系组长卡片(底部横条) */ + .contact-card { + display: flex; + align-items: center; + justify-content: space-between; + background: rgba(255,255,255,0.7); + backdrop-filter: blur(20rpx); + -webkit-backdrop-filter: blur(20rpx); + border-radius: 36rpx; + padding: 20rpx 30rpx; + margin-bottom: 40rpx; + border: 1rpx solid rgba(255,255,255,0.5); + box-shadow: 0 10rpx 25rpx rgba(0,0,0,0.04); + } + + .contact-left { + display: flex; + align-items: center; + flex: 1; + } + + .contact-avatar { + width: 60rpx; + height: 60rpx; + border-radius: 50%; + margin-right: 18rpx; + border: 2rpx solid #FFFFFF; + } + + .contact-name { + font-size: 28rpx; + font-weight: 500; + color: #1A1A1A; + } + + .contact-uid { + font-size: 22rpx; + color: #1A1A1A; + margin-top: 4rpx; + } + + .contact-arrow { + width: 24rpx; + height: 24rpx; + } + + /* 加载遮罩 */ + .loading-mask { + position: fixed; + top:0; left:0; right:0; bottom:0; + background: rgba(255,255,255,0.85); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; + } + .loading-spinner { + width: 60rpx; + height: 60rpx; + border: 4rpx solid rgba(0,0,0,0.1); + border-top-color: #1A1A1A; + border-radius: 50%; + animation: spin 0.8s linear infinite; + } + @keyframes spin { to { transform: rotate(360deg); } } \ No newline at end of file diff --git a/pages/guanshipaihang/guanshipaihang.js b/pages/guanshipaihang/guanshipaihang.js new file mode 100644 index 0000000..c3771f6 --- /dev/null +++ b/pages/guanshipaihang/guanshipaihang.js @@ -0,0 +1,393 @@ +// pages/guanshi-paihang/guanshi-paihang.js +const app = getApp() +import request from '../../utils/request.js' + +Page({ + data: { + // 页面加载状态 + jiazaiZhuangtai: true, + + // 排行榜类型:1=今日邀请,2=今月邀请 + paihangLeixing: 1, + + // 榜单数据 + paihangShuju: [], + + // 是否展示真实数据(后端控制) + xianshiZhenshiShuju: false, + + // 虚拟数据(用于当后端不允许展示时) + xuniShuju: [], + + // 按钮防抖控制 + btnFangdou: false, + + // OSS图片基础URL(从全局变量获取) + ossImageUrl: '', + + // 默认头像(相对路径,需要与OSS基础URL拼接) + morentouxiang: '', + + // 用户类型:1=打手,2=管事,3=商家(当前页面固定为管事) + yonghuLeixing: 2, + + // 前三名特殊数据 + qiansanMing: [], + + // 动画相关 + crownAnimation: null, + + // 卡片点击特效 + flashCardIndex: -1, + flashCardType: '', + flashAnimation: null, + + // 切换按钮选中状态 + switchBgPosition: 0 + }, + + onLoad() { + // 设置页面标题 + wx.setNavigationBarTitle({ + title: '管事排行榜' + }) + + // 初始化全局变量 + this.setData({ + ossImageUrl: app.globalData.ossImageUrl || '', + morentouxiang: app.globalData.morentouxiang || '' + }) + + // 初始化虚拟数据 + this.initXuniShuju() + + // 加载排行榜数据 + this.huoquPaihangShuju() + + // 开始皇冠动画 + this.startCrownAnimation() + }, + + onShow() { + // 页面显示时恢复动画 + this.registerNotificationComponent(); + this.startCrownAnimation() + }, + registerNotificationComponent() { + const app = getApp(); + const notificationComp = this.selectComponent('#global-notification'); + + if (notificationComp && notificationComp.showNotification) { + console.log('🏪 商城页面注册通知组件'); + app.globalData.globalNotification = { + show: (data) => notificationComp.showNotification(data), + hide: () => notificationComp.hideNotification() + }; + } +}, + + onHide() { + // 页面隐藏时停止动画 + this.stopCrownAnimation() + }, + + onUnload() { + // 页面卸载时停止动画 + this.stopCrownAnimation() + }, + + // 初始化虚拟数据 + initXuniShuju() { + const xuniShuju = [] + const names = ['管事大神', '组织高手', '团队领袖', '精英管理', '运营达人', + '战略专家', '人才猎手', '组织核心', '管理大师', '招募高手'] + + for (let i = 0; i < 10; i++) { + xuniShuju.push({ + mingci: i + 1, + touxiang: '', + nicheng: names[i] || `虚拟管事${i + 1}`, + uid: `G${100000 + i}`, + jine: (88 - i * 7).toString(), // 管事显示的是数量,不是金额 + jineClass: 'normal' // 金额样式 + }) + } + this.setData({ xuniShuju }) + }, + + // 开始皇冠动画 + startCrownAnimation() { + this.stopCrownAnimation() + + const animation = wx.createAnimation({ + duration: 800, + timingFunction: 'ease-in-out' + }) + + let scale = 1 + const animate = () => { + scale = scale === 1 ? 1.15 : 1 + + animation.scale(scale).step({ + duration: 800, + timingFunction: 'ease-in-out' + }) + + this.setData({ + crownAnimation: animation.export() + }) + + this.data.crownAnimationTimer = setTimeout(() => { + animate() + }, 800) + } + + animate() + }, + + // 停止皇冠动画 + stopCrownAnimation() { + if (this.data.crownAnimationTimer) { + clearTimeout(this.data.crownAnimationTimer) + this.setData({ + crownAnimationTimer: null + }) + } + }, + + // 卡片点击特效 + handleCardTap(e) { + const index = e.currentTarget.dataset.index + const type = e.currentTarget.dataset.type // 'top3' 或 'normal' + + // 创建闪光动画 + const animation = wx.createAnimation({ + duration: 300, + timingFunction: 'ease-out' + }) + + // 快速闪光效果 + animation.opacity(0.9).step({ + duration: 50, + timingFunction: 'linear' + }) + + animation.opacity(1).step({ + duration: 250, + timingFunction: 'ease-out' + }) + + this.setData({ + flashCardIndex: index, + flashCardType: type, + flashAnimation: animation.export() + }) + + // 300ms后重置闪光状态 + setTimeout(() => { + this.setData({ + flashCardIndex: -1, + flashCardType: '' + }) + }, 300) + }, + + // 获取排行榜数据 + async huoquPaihangShuju() { + this.setData({ + jiazaiZhuangtai: true + }) + + try { + // 准备请求参数 + const requestData = { + leixing: this.data.paihangLeixing, + yonghu_leixing: this.data.yonghuLeixing + } + + // 使用封装的request发送请求 + const res = await request({ + url: '/peizhi/dsph', + method: 'POST', + data: requestData, + header: { + 'content-type': 'application/json' + } + }) + + // 处理返回数据 + if (res && res.data && res.data.code === 0) { + const responseData = res.data.data + + // 检查后端是否允许展示 + if (responseData.xianshi && responseData.paihang_list && responseData.paihang_list.length > 0) { + // 处理头像URL拼接和金额样式 + const processedList = this.processData(responseData.paihang_list) + + // 提取前三名特殊展示 + const qiansanMing = processedList.slice(0, 3) + const qitaPaihang = processedList.slice(3) + + this.setData({ + paihangShuju: qitaPaihang, + qiansanMing: qiansanMing, + xianshiZhenshiShuju: true, + jiazaiZhuangtai: false + }) + } else { + // 后端不允许展示或数据不足,显示虚拟数据 + this.xianshiXuniShuju() + } + } else { + // 请求失败,显示虚拟数据 + const errorMsg = res && res.data ? (res.data.msg || '获取数据失败') : '网络请求失败' + + wx.showToast({ + title: errorMsg, + icon: 'none', + duration: 2000 + }) + + this.xianshiXuniShuju() + } + } catch (error) { + console.error('获取排行榜数据失败:', error) + + wx.showToast({ + title: '网络请求异常,请重试', + icon: 'none', + duration: 2000 + }) + + this.xianshiXuniShuju() + } + }, + + // 处理数据(头像拼接和金额样式) + processData(list) { + const { ossImageUrl, morentouxiang } = this.data + + return list.map(item => { + // 优先使用返回的头像URL + let finalTouxiang = item.touxiang + + // 如果返回的头像为空,使用默认头像 + if (!finalTouxiang && morentouxiang) { + finalTouxiang = morentouxiang + } + + // 拼接完整的URL + let fullTouxiangUrl = '' + if (finalTouxiang && ossImageUrl) { + // 确保路径不以斜杠开头 + let touxiangPath = finalTouxiang + if (touxiangPath.startsWith('/')) { + touxiangPath = touxiangPath.substring(1) + } + fullTouxiangUrl = ossImageUrl + touxiangPath + } + + // 处理数量样式(管事显示的是数量,不是金额) + const jine = parseInt(item.jine || 0) + let jineClass = 'normal' + const jineStr = jine.toString() // 管事显示整数数量 + + // 判断数量位数 + if (jineStr.length >= 5) { // 5位数及以上 + jineClass = 'very-small' + } else if (jineStr.length >= 4) { // 4位数 + jineClass = 'small' + } else if (jineStr.length >= 3) { // 3位数 + jineClass = 'medium' + } + + return { + ...item, + full_touxiang: fullTouxiangUrl, + jine: jineStr, + jineClass: jineClass + } + }) + }, + + // 显示虚拟数据 + xianshiXuniShuju() { + const processedList = this.processData(this.data.xuniShuju) + const qiansanMing = processedList.slice(0, 3) + const qitaPaihang = processedList.slice(3) + + this.setData({ + paihangShuju: qitaPaihang, + qiansanMing: qiansanMing, + xianshiZhenshiShuju: false, + jiazaiZhuangtai: false + }) + }, + + // 切换排行榜类型(今日/今月) + qiehuangPaihangLeixing(e) { + // 防抖处理:防止连续点击 + if (this.data.btnFangdou) return + + const leixing = e.currentTarget.dataset.leixing + if (leixing == this.data.paihangLeixing) return + + // 设置防抖状态 + this.setData({ + btnFangdou: true + }) + + // 更新切换按钮位置 + this.setData({ + paihangLeixing: leixing, + switchBgPosition: leixing == 1 ? 0 : 1 + }) + + // 重新加载数据 + this.huoquPaihangShuju() + + // 1秒后解除防抖 + setTimeout(() => { + this.setData({ + btnFangdou: false + }) + }, 1000) + }, + + // 用户下拉刷新 + onPullDownRefresh() { + this.huoquPaihangShuju() + wx.stopPullDownRefresh() + }, + + // 图片加载失败处理 + tupianJiazaiShibai(e) { + const index = e.currentTarget.dataset.index + const type = e.currentTarget.dataset.type // 'top3' 或 'normal' + + // 使用默认头像 + const { ossImageUrl, morentouxiang } = this.data + let defaultUrl = '' + if (morentouxiang && ossImageUrl) { + let defaultPath = morentouxiang + if (defaultPath.startsWith('/')) { + defaultPath = defaultPath.substring(1) + } + defaultUrl = ossImageUrl + defaultPath + } + + if (type === 'top3') { + // 前三名头像加载失败 + const key = `qiansanMing[${index}].full_touxiang` + this.setData({ + [key]: defaultUrl + }) + } else { + // 普通排名头像加载失败 + const key = `paihangShuju[${index}].full_touxiang` + this.setData({ + [key]: defaultUrl + }) + } + } +}) \ No newline at end of file diff --git a/pages/guanshipaihang/guanshipaihang.json b/pages/guanshipaihang/guanshipaihang.json new file mode 100644 index 0000000..f6237dd --- /dev/null +++ b/pages/guanshipaihang/guanshipaihang.json @@ -0,0 +1,12 @@ +{ + "usingComponents": { + "global-notification": "/components/global-notification/global-notification" + }, + "navigationBarTitleText": "管事排行榜", + "navigationBarBackgroundColor": "#000000", + "navigationBarTextStyle": "white", + "enablePullDownRefresh": false, + "backgroundColor": "#000000", + "backgroundTextStyle": "dark", + "onReachBottomDistance": 50 + } \ No newline at end of file diff --git a/pages/guanshipaihang/guanshipaihang.wxml b/pages/guanshipaihang/guanshipaihang.wxml new file mode 100644 index 0000000..ae0e82e --- /dev/null +++ b/pages/guanshipaihang/guanshipaihang.wxml @@ -0,0 +1,289 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + 管事排行榜 + + GUANSHI RANKING + + + + + + + 今日充值人数 + + + + 今月充值人数 + + + + + + + + + + + + + + 数据加载中 + Connecting to server... + + + + + + + + + 📡 + + 排行榜数据准备中 + 当前显示为模拟数据,实时排名即将开放 + + + + + + + + + + + + + 2 + + + + + + + + + + + + + + + 2 + + + + + + + + + + + + + + 👑 + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + 3 + + + + + + + + + + + + + + + 3 + + + + + + + + + + + + + 其他排名 + + + + + + + + {{xianshiZhenshiShuju ? index + 4 : index + 1}} + + + + + + + + + + {{item.nicheng}} + UID: {{item.uid}} + + + + {{item.jine}} + + + + + + + + + + + + 🏆 + 暂无排名数据 + 成为第一个登榜的管事吧 + + + + diff --git a/pages/guanshipaihang/guanshipaihang.wxss b/pages/guanshipaihang/guanshipaihang.wxss new file mode 100644 index 0000000..7dffd55 --- /dev/null +++ b/pages/guanshipaihang/guanshipaihang.wxss @@ -0,0 +1,1276 @@ +/* 管事排行榜和商家排行榜通用样式文件 */ +/* 保存为:pages/guanshi-paihang/guanshi-paihang.wxss 和 pages/shangjia-paihang/shangjia-paihang.wxss */ + +/* ========== 基础页面样式 ========== */ +.guanshi-paihang-page, +.shangjia-paihang-page { + min-height: 100vh; + background: #000; + color: #fff; + position: relative; + overflow-x: hidden; + padding: 40rpx 30rpx; + box-sizing: border-box; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; +} + +/* ========== 赛博朋克背景 ========== */ +.cyber-bg { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 1; + pointer-events: none; +} + +.cyber-grid { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-image: + linear-gradient(rgba(0, 255, 255, 0.05) 1px, transparent 1px), + linear-gradient(90deg, rgba(0, 255, 255, 0.05) 1px, transparent 1px); + background-size: 50rpx 50rpx; + opacity: 0.3; +} + +.cyber-scanline { + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3rpx; + background: linear-gradient(90deg, + transparent 0%, + #00ffff 30%, + #ff00ff 50%, + #00ffff 70%, + transparent 100%); + animation: scanline 3s linear infinite; + filter: blur(1rpx); + box-shadow: 0 0 20rpx #00ffff; + z-index: 2; +} + +@keyframes scanline { + 0% { top: 0; } + 100% { top: 100%; } +} + +.data-streams { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: hidden; +} + +.stream { + position: absolute; + width: 1rpx; + height: 100rpx; + background: linear-gradient(to bottom, transparent, #00ffff, transparent); + filter: blur(0.5rpx); + animation: streamFlow 8s linear infinite; +} + +.stream-1 { + left: 20%; + animation-delay: 0s; +} + +.stream-2 { + left: 50%; + animation-delay: 2s; +} + +.stream-3 { + left: 80%; + animation-delay: 4s; +} + +@keyframes streamFlow { + 0% { + top: -100rpx; + opacity: 0; + } + 10% { + opacity: 1; + } + 90% { + opacity: 1; + } + 100% { + top: 100%; + opacity: 0; + } +} + +.floating-particles { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.particle { + position: absolute; + width: 6rpx; + height: 6rpx; + background: #00ffff; + border-radius: 50%; + filter: blur(1rpx); + box-shadow: 0 0 10rpx #00ffff; + animation: particleFloat 20s infinite linear; +} + +.particle:nth-child(odd) { + background: #ff00ff; + box-shadow: 0 0 10rpx #ff00ff; +} + +@keyframes particleFloat { + 0%, 100% { + transform: translate(0, 0); + } + 25% { + transform: translate(100rpx, -100rpx); + } + 50% { + transform: translate(-50rpx, 150rpx); + } + 75% { + transform: translate(150rpx, 50rpx); + } +} + +/* ========== 页面头部 ========== */ +.page-header { + position: relative; + z-index: 10; + margin-bottom: 55rpx; + margin-top: -40rpx; +} + +.title-container { + text-align: center; + margin-bottom: 40rpx; +} + +.main-title { + display: block; + font-size: 58rpx; + font-weight: 900; + background: linear-gradient(90deg, #00ffff, #ff00ff, #ffff00); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + text-shadow: 0 0 30rpx rgba(0, 255, 255, 0.5); + letter-spacing: 4rpx; + margin-bottom: 8rpx; +} + +.title-line { + width: 220rpx; + height: 3rpx; + background: linear-gradient(90deg, transparent, #00ffff, #ff00ff, transparent); + margin: 12rpx auto; + position: relative; +} + +.title-line::before { + content: ''; + position: absolute; + top: -2rpx; + left: 0; + right: 0; + height: 7rpx; + background: linear-gradient(90deg, + transparent 0%, + rgba(0, 255, 255, 0.3) 20%, + rgba(255, 0, 255, 0.3) 80%, + transparent 100%); + filter: blur(4rpx); +} + +.sub-title { + display: block; + font-size: 22rpx; + color: rgba(255, 255, 255, 0.6); + letter-spacing: 6rpx; + text-transform: uppercase; + margin-top: 5rpx; +} + +.toggle-container { + display: flex; + background: rgba(10, 10, 30, 0.8); + border-radius: 60rpx; + padding: 6rpx; + position: relative; + border: 1rpx solid rgba(0, 255, 255, 0.2); + backdrop-filter: blur(20rpx); + max-width: 700rpx; + width: 90%; + margin: 0 auto; + box-shadow: + 0 10rpx 40rpx rgba(0, 0, 0, 0.8), + inset 0 0 20rpx rgba(0, 255, 255, 0.05); +} + +.toggle-bg { + position: absolute; + top: 6rpx; + bottom: 6rpx; + width: 50%; + background: linear-gradient(90deg, + rgba(0, 255, 255, 0.2), + rgba(255, 0, 255, 0.2)); + border-radius: 54rpx; + transition: transform 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55); + z-index: 1; +} + +.toggle-btn { + flex: 1; + text-align: center; + padding: 30rpx 0; + font-size: 32rpx; + font-weight: 600; + color: rgba(255, 255, 255, 0.7); + position: relative; + z-index: 2; + transition: all 0.3s ease; + border-radius: 54rpx; + min-width: 200rpx; +} + +.toggle-btn.active { + color: #ffffff; + text-shadow: 0 0 15rpx rgba(255, 255, 255, 0.5); +} + +.toggle-btn.disabled { + opacity: 0.5; + pointer-events: none; +} + +.toggle-text { + position: relative; + z-index: 1; +} + +.toggle-line { + position: absolute; + bottom: 15rpx; + left: 50%; + transform: translateX(-50%); + width: 40rpx; + height: 3rpx; + background: linear-gradient(90deg, #00ffff, #ff00ff); + border-radius: 2rpx; + opacity: 0; + transition: opacity 0.3s ease; +} + +.toggle-line.active { + opacity: 1; + box-shadow: 0 0 15rpx rgba(0, 255, 255, 0.5); +} + +/* ========== 加载状态 ========== */ +.loading-state { + display: flex; + align-items: center; + justify-content: center; + height: 60vh; + position: relative; + z-index: 10; +} + +.loading-content { + text-align: center; +} + +.loading-spinner { + position: relative; + width: 120rpx; + height: 120rpx; + margin: 0 auto 30rpx; +} + +.spinner-outer { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border: 8rpx solid transparent; + border-top-color: #00ffff; + border-right-color: #ff00ff; + border-radius: 50%; + animation: spinOuter 2s linear infinite; +} + +.spinner-inner { + position: absolute; + top: 15rpx; + left: 15rpx; + right: 15rpx; + bottom: 15rpx; + border: 6rpx solid transparent; + border-bottom-color: #ffff00; + border-left-color: #00ffff; + border-radius: 50%; + animation: spinInner 1.5s linear infinite reverse; +} + +.spinner-core { + position: absolute; + top: 30rpx; + left: 30rpx; + right: 30rpx; + bottom: 30rpx; + background: radial-gradient(circle, + rgba(0, 255, 255, 0.2) 0%, + rgba(255, 0, 255, 0.1) 50%, + transparent 100%); + border-radius: 50%; +} + +@keyframes spinOuter { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +@keyframes spinInner { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(-360deg); } +} + +.loading-text { + display: block; + font-size: 32rpx; + color: #00ffff; + margin-bottom: 10rpx; + text-shadow: 0 0 10rpx rgba(0, 255, 255, 0.5); +} + +.loading-sub { + display: block; + font-size: 24rpx; + color: rgba(255, 255, 255, 0.6); +} + +/* ========== 虚拟数据提示 ========== */ +.demo-notice { + background: rgba(15, 15, 35, 0.9); + border-radius: 25rpx; + padding: 30rpx; + margin-bottom: 40rpx; + position: relative; + z-index: 10; + border: 1rpx solid rgba(255, 0, 255, 0.3); + backdrop-filter: blur(20rpx); + display: flex; + align-items: center; + overflow: hidden; + box-shadow: + 0 10rpx 40rpx rgba(0, 0, 0, 0.8), + 0 0 30rpx rgba(255, 0, 255, 0.2); +} + +.notice-icon { + font-size: 60rpx; + margin-right: 30rpx; + background: linear-gradient(45deg, #ff00ff, #00ffff); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + flex-shrink: 0; +} + +.notice-content { + flex: 1; +} + +.notice-title { + display: block; + font-size: 32rpx; + font-weight: 700; + color: #ff00ff; + margin-bottom: 10rpx; + text-shadow: 0 0 15rpx rgba(255, 0, 255, 0.5); +} + +.notice-desc { + display: block; + font-size: 26rpx; + color: rgba(255, 255, 255, 0.7); + line-height: 1.4; +} + +.notice-glow { + position: absolute; + top: -100%; + left: -100%; + right: -100%; + bottom: -100%; + background: radial-gradient(circle at center, + rgba(255, 0, 255, 0.1) 0%, + transparent 70%); + z-index: -1; +} + +/* ========== 前三名区域 ========== */ +.top-three-section { + display: flex; + justify-content: center; + align-items: flex-end; + position: relative; + z-index: 10; + margin-bottom: 40rpx; + gap: 20rpx; + margin-top: 10rpx; +} + +.rank-card { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + transition: all 0.3s ease; +} + +.rank-1 { + width: 280rpx; + z-index: 30; + margin-bottom: 30rpx; +} + +.rank-2 { + width: 220rpx; + z-index: 20; + margin-bottom: 0rpx; + margin-right: -20rpx; +} + +.rank-3 { + width: 220rpx; + z-index: 20; + margin-bottom: 0rpx; + margin-left: -20rpx; +} + +.rank-card.flash { + animation: cardFlash 0.4s ease-out; +} + +@keyframes cardFlash { + 0% { + filter: brightness(1) drop-shadow(0 0 0 rgba(255, 255, 255, 0)); + } + 50% { + filter: brightness(1.4) drop-shadow(0 0 40rpx rgba(255, 255, 255, 0.8)); + } + 100% { + filter: brightness(1) drop-shadow(0 0 0 rgba(255, 255, 255, 0)); + } +} + +/* 奖台底座 */ +.podium-base { + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 60rpx; + border-radius: 30rpx 30rpx 0 0; + display: flex; + align-items: center; + justify-content: center; + z-index: 1; +} + +.rank-1 .podium-base { + height: 80rpx; +} + +.podium-gold { + background: linear-gradient(180deg, + rgba(255, 215, 0, 0.3) 0%, + rgba(255, 215, 0, 0.1) 100%); + border: 1rpx solid rgba(255, 215, 0, 0.4); +} + +.podium-silver { + background: linear-gradient(180deg, + rgba(192, 192, 192, 0.3) 0%, + rgba(192, 192, 192, 0.1) 100%); + border: 1rpx solid rgba(192, 192, 192, 0.4); +} + +.podium-bronze { + background: linear-gradient(180deg, + rgba(205, 127, 50, 0.3) 0%, + rgba(205, 127, 50, 0.1) 100%); + border: 1rpx solid rgba(205, 127, 50, 0.4); +} + +.podium-glow { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 30rpx 30rpx 0 0; + background: radial-gradient(ellipse at center, + rgba(255, 255, 255, 0.2) 0%, + transparent 70%); + z-index: -1; +} + +.podium-number { + font-size: 32rpx; + font-weight: 900; + color: rgba(255, 255, 255, 0.9); + text-shadow: 0 0 10rpx currentColor; +} + +/* ========== 皇冠容器 ========== */ +.crown-container { + position: absolute; + top: -55rpx; + left: 55%; + transform: translateX(20%) rotate(35deg)!important; + width: 110rpx; + height: 110rpx; + z-index: 40; + display: flex; + align-items: center; + justify-content: center; +} + +.crown { + position: relative; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +.crown-icon { + font-size: 85rpx; + position: relative; + z-index: 2; + filter: drop-shadow(0 0 10rpx rgba(255, 215, 0, 0.8)); + animation: crownFloat 3s ease-in-out infinite; + transform: rotate(5deg); +} + +@keyframes crownFloat { + 0%, 100% { + transform: translateY(0) rotate(5deg) scale(1); + filter: drop-shadow(0 0 10rpx rgba(255, 215, 0, 0.8)); + } + 50% { + transform: translateY(-15rpx) rotate(10deg) scale(1.1); + filter: drop-shadow(0 0 25rpx rgba(255, 215, 0, 1)); + } +} + +.crown-rays { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 180rpx; + height: 180rpx; + z-index: 1; +} + +.ray { + position: absolute; + top: 50%; + left: 50%; + width: 2rpx; + height: 80rpx; + background: linear-gradient(to top, + transparent, + rgba(255, 215, 0, 0.8), + rgba(255, 215, 0, 1), + rgba(255, 215, 0, 0.8), + transparent); + transform-origin: bottom center; + filter: blur(1rpx); +} + +.ray-1 { transform: translate(-50%, -100%) rotate(0deg); } +.ray-2 { transform: translate(-50%, -100%) rotate(60deg); } +.ray-3 { transform: translate(-50%, -100%) rotate(120deg); } +.ray-4 { transform: translate(-50%, -100%) rotate(180deg); } +.ray-5 { transform: translate(-50%, -100%) rotate(240deg); } +.ray-6 { transform: translate(-50%, -100%) rotate(300deg); } + +.crown-glow { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 200rpx; + height: 200rpx; + background: radial-gradient(circle, + rgba(255, 215, 0, 0.3) 0%, + rgba(255, 215, 0, 0.1) 40%, + transparent 70%); + z-index: 0; + animation: glowPulse 2s ease-in-out infinite; +} + +@keyframes glowPulse { + 0%, 100% { opacity: 0.5; } + 50% { opacity: 0.8; } +} + +/* ========== 排名内容 ========== */ +.rank-content { + position: relative; + width: 100%; + z-index: 2; +} + +/* 头像框架 */ +.avatar-frame { + position: relative; + width: 160rpx; + height: 160rpx; + margin: 0 auto 30rpx; + display: flex; + align-items: center; + justify-content: center; +} + +.rank-1 .avatar-frame { + width: 200rpx; + height: 200rpx; +} + +.frame-gold { + filter: drop-shadow(0 0 30rpx rgba(255, 215, 0, 0.5)); +} + +.frame-silver { + filter: drop-shadow(0 0 25rpx rgba(192, 192, 192, 0.5)); +} + +.frame-bronze { + filter: drop-shadow(0 0 25rpx rgba(205, 127, 50, 0.5)); +} + +.avatar-container { + position: relative; + width: 100%; + height: 100%; + border-radius: 50%; + overflow: hidden; + background: rgba(20, 20, 40, 0.8); + display: flex; + align-items: center; + justify-content: center; + border: 4rpx solid transparent; +} + +.rank-1 .avatar-container { + border-width: 6rpx; +} + +.frame-gold .avatar-container { + border-color: rgba(255, 215, 0, 0.3); +} + +.frame-silver .avatar-container { + border-color: rgba(192, 192, 192, 0.3); +} + +.frame-bronze .avatar-container { + border-color: rgba(205, 127, 50, 0.3); +} + +.avatar-image { + width: 100%; + height: 100%; + border-radius: 50%; + object-fit: cover; + position: relative; + z-index: 2; +} + +/* 头像光晕 */ +.avatar-halo { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + border-radius: 50%; + z-index: 1; + pointer-events: none; + animation: haloRotate 10s linear infinite; +} + +.halo-gold { + width: 220rpx; + height: 220rpx; + background: radial-gradient(circle, + rgba(255, 215, 0, 0.4) 0%, + rgba(255, 215, 0, 0.2) 30%, + transparent 70%); +} + +.rank-1 .halo-gold { + width: 260rpx; + height: 260rpx; +} + +.halo-silver { + width: 200rpx; + height: 200rpx; + background: radial-gradient(circle, + rgba(192, 192, 192, 0.4) 0%, + rgba(192, 192, 192, 0.2) 30%, + transparent 70%); +} + +.halo-bronze { + width: 200rpx; + height: 200rpx; + background: radial-gradient(circle, + rgba(205, 127, 50, 0.4) 0%, + rgba(205, 127, 50, 0.2) 30%, + transparent 70%); +} + +@keyframes haloRotate { + 0% { transform: translate(-50%, -50%) rotate(0deg); } + 100% { transform: translate(-50%, -50%) rotate(360deg); } +} + +/* 头像闪烁粒子 */ +.avatar-sparkles { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 50%; + z-index: 3; +} + +.sparkle { + position: absolute; + width: 8rpx; + height: 8rpx; + background: #fff; + border-radius: 50%; + filter: blur(1rpx); + opacity: 0; + animation: sparkleTwinkle 3s ease-in-out infinite; +} + +.rank-1 .sparkle { + width: 10rpx; + height: 10rpx; +} + +.s1 { top: 15%; left: 15%; animation-delay: 0s; } +.s2 { top: 25%; right: 25%; animation-delay: 0.5s; } +.s3 { bottom: 35%; left: 35%; animation-delay: 1s; } +.s4 { bottom: 15%; right: 15%; animation-delay: 1.5s; } + +@keyframes sparkleTwinkle { + 0%, 100% { + opacity: 0; + transform: scale(0.5); + } + 50% { + opacity: 1; + transform: scale(1); + } +} + +/* 排名徽章 */ +.rank-badge { + position: absolute; + bottom: -10rpx; + right: -10rpx; + width: 60rpx; + height: 60rpx; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 28rpx; + font-weight: 900; + color: #fff; + z-index: 10; + border: 3rpx solid rgba(255, 255, 255, 0.3); + box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.5); +} + +.rank-1 .rank-badge { + width: 70rpx; + height: 70rpx; + font-size: 32rpx; + bottom: -15rpx; + right: -15rpx; +} + +.badge-gold { + background: linear-gradient(135deg, #ffd700, #ff9800); +} + +.badge-silver { + background: linear-gradient(135deg, #c0c0c0, #808080); +} + +.badge-bronze { + background: linear-gradient(135deg, #cd7f32, #a0522d); +} + +/* 用户信息卡片 */ +.user-info-card { + background: rgba(15, 15, 35, 0.95); + border-radius: 25rpx; + padding: 30rpx 25rpx; + backdrop-filter: blur(20rpx); + border: 1rpx solid rgba(255, 255, 255, 0.1); + box-shadow: + 0 20rpx 60rpx rgba(0, 0, 0, 0.8), + inset 0 0 30rpx rgba(255, 255, 255, 0.05); + position: relative; + overflow: hidden; +} + +.rank-1 .user-info-card { + border-color: rgba(255, 215, 0, 0.3); + background: rgba(20, 15, 5, 0.95); +} + +.rank-2 .user-info-card { + border-color: rgba(192, 192, 192, 0.3); + background: rgba(25, 25, 35, 0.95); +} + +.rank-3 .user-info-card { + border-color: rgba(205, 127, 50, 0.3); + background: rgba(30, 20, 10, 0.95); +} + +.info-header { + margin-bottom: 20rpx; + text-align: center; +} + +.user-name { + font-size: 32rpx; + font-weight: 700; + color: #fff; + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.rank-1 .user-name { + color: #ffd700; + text-shadow: 0 0 15rpx rgba(255, 215, 0, 0.7); + font-size: 34rpx; +} + +.rank-2 .user-name { + color: #c0c0c0; + text-shadow: 0 0 10rpx rgba(192, 192, 192, 0.7); +} + +.rank-3 .user-name { + color: #cd7f32; + text-shadow: 0 0 10rpx rgba(205, 127, 50, 0.7); +} + +.info-body { + border-top: 1rpx solid rgba(255, 255, 255, 0.1); + padding-top: 15rpx; +} + +.uid-row, .income-row { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 12rpx; +} + +.uid-row:last-child, .income-row:last-child { + margin-bottom: 0; +} + +.uid-label, .income-label { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.6); +} + +.uid-value { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.9); + font-family: 'Courier New', monospace; +} + +.income-value { + display: flex; + align-items: baseline; +} + +.currency { + font-size: 26rpx; + color: #4ade80; + margin-right: 4rpx; +} + +.rank-1 .currency { + color: #ffd700; +} + +.rank-2 .currency { + color: #c0c0c0; +} + +.rank-3 .currency { + color: #cd7f32; +} + +.amount { + font-size: 32rpx; + font-weight: 700; + color: #4ade80; + text-shadow: 0 0 10rpx rgba(74, 222, 128, 0.5); +} + +.rank-1 .amount { + color: #ffd700; + text-shadow: 0 0 15rpx rgba(255, 215, 0, 0.7); +} + +.rank-2 .amount { + color: #c0c0c0; + text-shadow: 0 0 10rpx rgba(192, 192, 192, 0.7); +} + +.rank-3 .amount { + color: #cd7f32; + text-shadow: 0 0 10rpx rgba(205, 127, 50, 0.7); +} + +.unit { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.6); + margin-left: 4rpx; +} + +.amount.medium { + font-size: 30rpx; +} + +.amount.small { + font-size: 28rpx; +} + +.amount.very-small { + font-size: 26rpx; +} + +/* ========== 其他排名区域 ========== */ +.other-ranks-section { + position: relative; + z-index: 10; +} + +.section-title { + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 40rpx; +} + +.title-line { + flex: 1; + height: 2rpx; + background: linear-gradient(90deg, + transparent, + #00ffff, + #ff00ff, + #00ffff, + transparent); +} + +.title-text { + font-size: 32rpx; + font-weight: 700; + color: #00ffff; + margin: 0 30rpx; + text-shadow: 0 0 15rpx rgba(0, 255, 255, 0.5); +} + +.rank-list { + position: relative; +} + +.rank-item { + display: flex; + align-items: center; + background: rgba(20, 20, 40, 0.8); + border-radius: 25rpx; + padding: 25rpx 30rpx; + margin-bottom: 20rpx; + border: 1rpx solid rgba(0, 255, 255, 0.1); + backdrop-filter: blur(20rpx); + position: relative; + overflow: hidden; + transition: all 0.3s ease; + box-shadow: + 0 10rpx 40rpx rgba(0, 0, 0, 0.6), + inset 0 0 20rpx rgba(0, 255, 255, 0.05); +} + +.rank-item.flash { + animation: cardFlash 0.4s ease-out; +} + +.rank-item::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, + transparent, + rgba(0, 255, 255, 0.1), + transparent); + transition: left 0.6s ease; +} + +.rank-item:hover::before { + left: 100%; +} + +.rank-number { + width: 80rpx; + text-align: center; + position: relative; + margin-right: 30rpx; + flex-shrink: 0; +} + +.number-text { + font-size: 42rpx; + font-weight: 900; + color: rgba(255, 255, 255, 0.9); + position: relative; + z-index: 1; +} + +.number-glow { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 80rpx; + height: 80rpx; + background: radial-gradient(circle, + rgba(0, 255, 255, 0.2) 0%, + transparent 70%); + z-index: 0; +} + +.rank-avatar { + position: relative; + width: 100rpx; + height: 100rpx; + margin-right: 25rpx; + flex-shrink: 0; +} + +.rank-avatar-img { + width: 100%; + height: 100%; + border-radius: 50%; + object-fit: cover; + position: relative; + z-index: 2; + border: 3rpx solid rgba(0, 255, 255, 0.2); +} + +.rank-avatar-halo { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 120rpx; + height: 120rpx; + background: radial-gradient(circle, + rgba(0, 255, 255, 0.2) 0%, + rgba(0, 255, 255, 0.1) 30%, + transparent 70%); + z-index: 1; + border-radius: 50%; +} + +.rank-info { + flex: 1; + min-width: 0; +} + +.rank-name { + display: block; + font-size: 32rpx; + font-weight: 600; + color: #ffffff; + margin-bottom: 8rpx; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.rank-uid { + display: block; + font-size: 24rpx; + color: rgba(255, 255, 255, 0.6); + font-family: 'Courier New', monospace; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.rank-income { + position: relative; + flex-shrink: 0; + display: flex; + align-items: baseline; + margin-left: 20rpx; +} + +.income-currency { + font-size: 28rpx; + color: #00ffff; + margin-right: 4rpx; + opacity: 0.9; +} + +.income-amount { + font-size: 36rpx; + font-weight: 700; + color: #4ade80; + text-shadow: 0 0 10rpx rgba(74, 222, 128, 0.5); + white-space: nowrap; + line-height: 1; +} + +.income-unit { + font-size: 24rpx; + color: rgba(255, 255, 255, 0.6); + margin-left: 4rpx; +} + +.income-amount.medium { + font-size: 34rpx; +} + +.income-amount.small { + font-size: 32rpx; +} + +.income-amount.very-small { + font-size: 30rpx; +} + +.income-glow { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 140rpx; + height: 70rpx; + background: radial-gradient(ellipse, + rgba(74, 222, 128, 0.2) 0%, + transparent 70%); + z-index: 0; +} + +.rank-hover { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 25rpx; + border: 1rpx solid transparent; + background: linear-gradient(45deg, + rgba(0, 255, 255, 0.1), + rgba(255, 0, 255, 0.1)) border-box; + mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0); + -webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + opacity: 0; + transition: opacity 0.3s ease; + pointer-events: none; +} + +.rank-item:hover .rank-hover { + opacity: 1; +} + +/* ========== 空状态 ========== */ +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 120rpx 0; + text-align: center; + position: relative; + z-index: 10; +} + +.empty-icon { + font-size: 120rpx; + margin-bottom: 30rpx; + opacity: 0.5; + animation: iconFloat 3s ease-in-out infinite; +} + +@keyframes iconFloat { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-30rpx); } +} + +.empty-title { + font-size: 36rpx; + color: rgba(255, 255, 255, 0.8); + margin-bottom: 15rpx; +} + +.empty-desc { + font-size: 28rpx; + color: rgba(255, 255, 255, 0.5); +} + +/* ========== 调整整个内容区域的上边距 ========== */ +.ranking-content { + position: relative; + z-index: 5; + margin-top: 10rpx; +} \ No newline at end of file diff --git a/pages/guanzhual/guanzhual.js b/pages/guanzhual/guanzhual.js new file mode 100644 index 0000000..ea8dbfd --- /dev/null +++ b/pages/guanzhual/guanzhual.js @@ -0,0 +1,90 @@ +// pages/guanzhual/guanzhual.js +const app = getApp() +import request from '../../utils/request.js' + +Page({ + data: { + images: [], // 图片完整URL列表 + lastUpdate: '', // 最新更新时间 + visitCount: 0, // 浏览次数 + loading: true, // 加载中 + ossImageUrl: '' // OSS基础地址 + }, + + onLoad() { + // 获取OSS地址 + this.setData({ + ossImageUrl: app.globalData.ossImageUrl || '' + }) + this.loadData() + }, + + onShow() { + this.registerNotificationComponent() + }, + + // 注册全局通知组件 + registerNotificationComponent() { + const notificationComp = this.selectComponent('#global-notification') + if (notificationComp && notificationComp.showNotification) { + app.globalData.globalNotification = { + show: (data) => notificationComp.showNotification(data), + hide: () => notificationComp.hideNotification() + } + } + }, + + // 加载数据 + loadData() { + this.setData({ loading: true }) + + request({ + url: '/peizhi/gzal', + method: 'POST', + header: { 'content-type': 'application/json' } + }) + .then(res => { + if (res && res.data && res.data.code === 0) { + const data = res.data.data || {} + const ossImageUrl = this.data.ossImageUrl + // 拼接完整URL + const fullImages = (data.images || []).map(url => { + if (url && !url.startsWith('http')) { + return ossImageUrl + url + } + return url + }) + this.setData({ + images: fullImages, + lastUpdate: data.last_update || '', + visitCount: data.visit_count || 0, + loading: false + }) + } else { + wx.showToast({ + title: res?.data?.msg || '加载失败', + icon: 'none' + }) + this.setData({ loading: false }) + } + }) + .catch(err => { + console.error('加载关注阿龙数据失败', err) + wx.showToast({ + title: '网络错误', + icon: 'none' + }) + this.setData({ loading: false }) + }) + }, + + // 图片加载成功(可选) + onImageLoad(e) { + // 可忽略 + }, + + // 图片加载失败 + onImageError(e) { + console.warn('图片加载失败', e) + } +}) \ No newline at end of file diff --git a/pages/guanzhual/guanzhual.json b/pages/guanzhual/guanzhual.json new file mode 100644 index 0000000..03bf4df --- /dev/null +++ b/pages/guanzhual/guanzhual.json @@ -0,0 +1,8 @@ +{ + "navigationBarTitleText": "关注星阙", + "usingComponents": { + "global-notification": "/components/global-notification/global-notification" + }, + "enablePullDownRefresh": false, + "backgroundTextStyle": "dark" + } \ No newline at end of file diff --git a/pages/guanzhual/guanzhual.wxml b/pages/guanzhual/guanzhual.wxml new file mode 100644 index 0000000..5394ae8 --- /dev/null +++ b/pages/guanzhual/guanzhual.wxml @@ -0,0 +1,39 @@ + + + + + 关注快手 + {{visitCount}}次浏览 + + + 更新时间:{{lastUpdate}} + + + + + + + + + + + + 暂无内容 + + + + + + + 加载中... + + + + + \ No newline at end of file diff --git a/pages/guanzhual/guanzhual.wxss b/pages/guanzhual/guanzhual.wxss new file mode 100644 index 0000000..675b458 --- /dev/null +++ b/pages/guanzhual/guanzhual.wxss @@ -0,0 +1,95 @@ +/* pages/guanzhual/guanzhual.wxss */ +page { + background-color: #ffffff; + min-height: 100vh; + } + + .guanzhual-page { + padding-bottom: 30rpx; + box-sizing: border-box; + } + + /* 头部信息 */ + .header-info { + padding: 30rpx 20rpx 20rpx 20rpx; + border-bottom: 2rpx solid #f5f5f5; + } + + .title-section { + display: flex; + justify-content: space-between; + align-items: baseline; + margin-bottom: 12rpx; + } + + .main-title { + font-size: 40rpx; + font-weight: 600; + color: #333333; + } + + .visit-count { + font-size: 28rpx; + color: #999999; + } + + .sub-info { + display: flex; + justify-content: space-between; + font-size: 26rpx; + color: #aaaaaa; + } + + .update-time { + color: #888888; + } + + /* 图片列表 */ + .image-list { + padding: 0 8rpx; /* 非常微小的左右边距 */ + } + + .image-item { + width: 100%; + margin-bottom: 6rpx; /* 极小的上下间距 */ + background-color: #fafafa; + overflow: hidden; + border-radius: 4rpx; + } + + .image-item image { + width: 100%; + display: block; + } + + /* 空状态提示 */ + .empty-tip { + padding: 100rpx 0; + text-align: center; + color: #999999; + font-size: 28rpx; + } + + /* 加载状态 */ + .loading-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 100rpx 0; + } + + .loading-spinner { + width: 60rpx; + height: 60rpx; + border: 4rpx solid #e0e0e0; + border-top-color: #00C853; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-bottom: 20rpx; + } + + @keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } \ No newline at end of file diff --git a/pages/haibao/haibao.js b/pages/haibao/haibao.js new file mode 100644 index 0000000..3985d30 --- /dev/null +++ b/pages/haibao/haibao.js @@ -0,0 +1,281 @@ +// pages/haibao/haibao.js +import request from '../../utils/request.js'; + +const app = getApp(); + +Page({ + data: { + qrcodeUrl: '', + isLoading: false, + showPosterCanvas: false, + posterImageTemp: '', + avatarUrl: '', + uid: '', + nickName: '', + }, + + onLoad() { + this.loadQRCodeFromCache(); + this.loadUserInfo(); + }, + + onShow() { + this.registerNotificationComponent(); + }, + + registerNotificationComponent() { + const notificationComp = this.selectComponent('#global-notification'); + if (notificationComp && notificationComp.showNotification) { + app.globalData.globalNotification = { + show: (data) => notificationComp.showNotification(data), + hide: () => notificationComp.hideNotification() + }; + } + }, + + loadQRCodeFromCache() { + try { + const relativeUrl = wx.getStorageSync('haibao_url'); + if (relativeUrl) { + const fullUrl = this.getFullImageUrl(relativeUrl); + this.setData({ qrcodeUrl: fullUrl }); + } else { + this.setData({ qrcodeUrl: '' }); + } + } catch (error) { + console.error('读取缓存失败', error); + } + }, + + loadUserInfo() { + const touxiang = wx.getStorageSync('touxiang'); + const ossUrl = app.globalData.ossImageUrl || ''; + const defaultAvatar = ossUrl + (app.globalData.morentouxiang || 'avatar/default.jpg'); + let avatarUrl = defaultAvatar; + if (touxiang && typeof touxiang === 'string' && touxiang.trim() !== '') { + avatarUrl = this.getFullImageUrl(touxiang); + } + const uid = wx.getStorageSync('uid') || ''; + let nickName = app.globalData.nicheng || wx.getStorageSync('nicheng') || '管事'; + this.setData({ avatarUrl, uid, nickName }); + }, + + getFullImageUrl(url) { + if (!url) return ''; + const ossUrl = app.globalData.ossImageUrl || ''; + return url.startsWith('http') ? url : ossUrl + url; + }, + + refreshQRCode() { + if (this.data.isLoading) return; + this.setData({ isLoading: true }); + request({ + url: '/peizhi/guanshiewm', + method: 'POST', + }).then(res => { + if (res.data && res.data.code === 0) { + const relativeUrl = res.data.data.url; + if (relativeUrl) { + wx.setStorageSync('haibao_url', relativeUrl); + const fullUrl = this.getFullImageUrl(relativeUrl); + this.setData({ qrcodeUrl: fullUrl }); + wx.showToast({ title: '获取成功', icon: 'success' }); + } else { + wx.showToast({ title: '返回数据异常', icon: 'none' }); + } + } else { + wx.showToast({ title: res.data.msg || '获取失败', icon: 'none' }); + } + }).catch(err => { + console.error('获取海报失败', err); + wx.showToast({ title: '网络错误', icon: 'none' }); + }).finally(() => { + this.setData({ isLoading: false }); + }); + }, + + // 🔥【修改】保存二维码到相册(先下载网络图片) + saveToAlbum() { + if (!this.data.qrcodeUrl) { + wx.showToast({ title: '暂无二维码', icon: 'none' }); + return; + } + + wx.showLoading({ title: '保存中...', mask: true }); + wx.downloadFile({ + url: this.data.qrcodeUrl, + success: (res) => { + wx.hideLoading(); + if (res.statusCode === 200) { + // 调用已有的保存函数(传入临时路径) + this.downloadAndSave(res.tempFilePath); + } else { + wx.showToast({ title: '下载失败', icon: 'none' }); + } + }, + fail: (err) => { + wx.hideLoading(); + console.error('下载二维码失败', err); + wx.showToast({ title: '下载失败', icon: 'none' }); + } + }); + }, + + generateMyPoster() { + if (!this.data.qrcodeUrl) { + wx.showToast({ title: '请先生成二维码', icon: 'none' }); + return; + } + + wx.showLoading({ title: '生成海报中...', mask: true }); + + const ossUrl = app.globalData.ossImageUrl || ''; + const bgUrl = ossUrl + 'beijing/haibaobeijing.jpg'; + const urls = [bgUrl, this.data.qrcodeUrl, this.data.avatarUrl]; + + const promises = urls.map(url => this.loadImage(url)); + Promise.all(promises) + .then((images) => { + wx.hideLoading(); + this.setData({ showPosterCanvas: true }, () => { + setTimeout(() => { + this.drawPoster(images[0], images[1], images[2]); + }, 200); + }); + }) + .catch((err) => { + wx.hideLoading(); + wx.showToast({ title: '图片加载失败', icon: 'none' }); + console.error('图片加载失败', err); + }); + }, + + loadImage(url) { + return new Promise((resolve, reject) => { + wx.getImageInfo({ + src: url, + success: (res) => resolve(res.path), + fail: reject + }); + }); + }, + + drawPoster(bgPath, qrPath, avatarPath) { + const ctx = wx.createCanvasContext('posterCanvas', this); + const query = wx.createSelectorQuery().in(this); + query.select('.poster-canvas').boundingClientRect(rect => { + if (!rect) { + wx.showToast({ title: '画布获取失败', icon: 'none' }); + return; + } + const canvasWidth = rect.width; + const canvasHeight = rect.height; + + // 1. 绘制背景 + ctx.drawImage(bgPath, 0, 0, canvasWidth, canvasHeight); + + const qrSize = 100; + const margin = 5; + const qrX = canvasWidth - qrSize - margin; + const qrY = canvasHeight - qrSize - margin; + ctx.drawImage(qrPath, qrX, qrY, qrSize, qrSize); + + const avatarSize = 60; + const avatarX = margin + 20; + const avatarY = canvasHeight - avatarSize - margin; + ctx.save(); + ctx.beginPath(); + ctx.arc(avatarX + avatarSize/2, avatarY + avatarSize/2, avatarSize/2, 0, 2 * Math.PI); + ctx.clip(); + ctx.drawImage(avatarPath, avatarX, avatarY, avatarSize, avatarSize); + ctx.restore(); + + ctx.setFontSize(16); + ctx.setFillStyle('#333333'); + ctx.setTextAlign('left'); + + let nick = this.data.nickName || '管事'; + if (nick.length > 3) { + nick = nick.substring(0, 3) + '...'; + } + const nameText = nick; + + const textX = avatarX + avatarSize + 8; + const textY = avatarY + avatarSize/2 + 6; + ctx.fillText(nameText, textX, textY); + + ctx.setFontSize(18); + ctx.setFillStyle('#ffaa00'); + ctx.setTextAlign('left'); + const slogan = '快来加入我们 ✦'; + const sloganX = avatarX; + const sloganY = avatarY - 12; + ctx.fillText(slogan, sloganX, sloganY); + + ctx.draw(false, () => { + wx.canvasToTempFilePath({ + canvasId: 'posterCanvas', + success: (res) => { + this.setData({ posterImageTemp: res.tempFilePath }); + }, + fail: (err) => { + console.error('生成海报临时文件失败', err); + } + }, this); + }); + }).exec(); + }, + + savePoster() { + if (!this.data.posterImageTemp) { + wx.showToast({ title: '海报尚未生成', icon: 'none' }); + return; + } + this.downloadAndSave(this.data.posterImageTemp); + }, + + hidePoster() { + this.setData({ showPosterCanvas: false, posterImageTemp: '' }); + }, + + downloadAndSave(filePath) { + wx.getSetting({ + success: (res) => { + if (!res.authSetting['scope.writePhotosAlbum']) { + wx.authorize({ + scope: 'scope.writePhotosAlbum', + success: () => { + this._saveImage(filePath); + }, + fail: () => { + wx.showModal({ + title: '提示', + content: '需要您授权保存到相册', + success: (modalRes) => { + if (modalRes.confirm) { + wx.openSetting(); + } + } + }); + } + }); + } else { + this._saveImage(filePath); + } + } + }); + }, + + _saveImage(filePath) { + wx.saveImageToPhotosAlbum({ + filePath: filePath, + success: () => { + wx.showToast({ title: '保存成功', icon: 'success' }); + }, + fail: (err) => { + console.error('保存失败', err); + wx.showToast({ title: '保存失败', icon: 'none' }); + } + }); + }, +}); \ No newline at end of file diff --git a/pages/haibao/haibao.json b/pages/haibao/haibao.json new file mode 100644 index 0000000..8f6fba7 --- /dev/null +++ b/pages/haibao/haibao.json @@ -0,0 +1,8 @@ +{ + "usingComponents": { + "global-notification": "/components/global-notification/global-notification" + }, + "navigationBarTitleText": "推广海报", + "navigationBarBackgroundColor": "#0a0e17", + "navigationBarTextStyle": "white" + } \ No newline at end of file diff --git a/pages/haibao/haibao.wxml b/pages/haibao/haibao.wxml new file mode 100644 index 0000000..aa2c48d --- /dev/null +++ b/pages/haibao/haibao.wxml @@ -0,0 +1,71 @@ + + + + + + + + + + ⚡ 推广海报 ⚡ + 邀请打手扫码注册,自动填充邀请码 + + + + + + + + + + + 点击下方按钮生成您的专属二维码 + + + + + + + + 📥 + 保存二维码 + + + + {{qrcodeUrl ? '重新获取' : '获取二维码'}} + + + + 生成中... + + + + 🎨 + 生成我的专属海报 + + + + + + + + + + 📥 + 保存海报 + + + ↩️ + 返回 + + + + + + + * 打手扫码后自动进入注册页面,邀请码将自动填充 + + + + + \ No newline at end of file diff --git a/pages/haibao/haibao.wxss b/pages/haibao/haibao.wxss new file mode 100644 index 0000000..e52db1f --- /dev/null +++ b/pages/haibao/haibao.wxss @@ -0,0 +1,269 @@ +/* 机甲风格推广海报页面(含新增专属海报样式) */ + +page { + background: #0a0e17; + min-height: 100vh; + font-family: 'Avenir', 'PingFang SC', 'Helvetica Neue', sans-serif; + color: #fff; + } + + /* 背景网格 */ + .grid-bg { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: + linear-gradient(rgba(0, 160, 255, 0.05) 1px, transparent 1px), + linear-gradient(90deg, rgba(0, 160, 255, 0.05) 1px, transparent 1px); + background-size: 50rpx 50rpx; + pointer-events: none; + z-index: 0; + } + + .page-container { + position: relative; + z-index: 2; + min-height: 100vh; + padding: 30rpx 25rpx; + box-sizing: border-box; + } + + /* 机械装饰线条 */ + .mech-line { + position: absolute; + pointer-events: none; + z-index: 1; + } + .line-h { + top: 200rpx; + left: 0; + width: 100%; + height: 2rpx; + background: linear-gradient(90deg, transparent, #00a6ff, transparent); + } + .line-v { + right: 50rpx; + top: 100rpx; + width: 2rpx; + height: 300rpx; + background: linear-gradient(180deg, transparent, #00a6ff, transparent); + } + + .content { + position: relative; + z-index: 3; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 100vh; + padding: 40rpx 0; + } + + /* 标题区域 */ + .title-section { + text-align: center; + margin-bottom: 60rpx; + } + .title { + font-size: 44rpx; + font-weight: bold; + color: #fff; + text-shadow: 0 0 30rpx #00a6ff; + letter-spacing: 2rpx; + display: block; + margin-bottom: 20rpx; + } + .subtitle { + font-size: 28rpx; + color: #9cf; + opacity: 0.9; + letter-spacing: 1rpx; + } + + /* 二维码区域(方形,无斜切) */ + .qrcode-section { + width: 100%; + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 60rpx; + } + .qrcode-wrapper { + position: relative; + width: 400rpx; + height: 400rpx; + background: rgba(0, 20, 40, 0.8); + border: 4rpx solid #00a6ff; + /* 移除 clip-path,保持方形 */ + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 0 80rpx rgba(0, 166, 255, 0.4); + animation: qrcodePulse 2s infinite alternate; + } + .qrcode-img { + width: 90%; + height: 90%; + object-fit: contain; + } + .qrcode-glow { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: radial-gradient(circle at 30% 30%, rgba(0, 166, 255, 0.2), transparent 70%); + pointer-events: none; + } + .empty-qrcode { + width: 400rpx; + height: 400rpx; + background: rgba(0, 20, 40, 0.8); + border: 4rpx dashed #00a6ff; + /* 方形 */ + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + .empty-icon { + font-size: 80rpx; + color: #00a6ff; + margin-bottom: 20rpx; + opacity: 0.5; + } + .empty-text { + font-size: 26rpx; + color: #9cf; + text-align: center; + padding: 0 40rpx; + } + + /* 按钮区域(机甲斜切风格) */ + .button-section { + width: 100%; + display: flex; + justify-content: center; + margin-bottom: 40rpx; + } + .btn-group { + display: flex; + gap: 20rpx; + flex-wrap: wrap; + justify-content: center; + } + .btn { + width: 220rpx; + height: 80rpx; + background: rgba(0, 30, 60, 0.8); + border: 2rpx solid #00a6ff; + border-right: none; + border-bottom: none; + clip-path: polygon(0 0, 100% 0, 90% 100%, 0 100%); + display: flex; + align-items: center; + justify-content: center; + position: relative; + transition: 0.2s; + cursor: pointer; + } + .btn:active { + transform: scale(0.96); + box-shadow: 0 0 40rpx #00a6ff; + } + .save-btn { + border-color: #00a6ff; + } + .refresh-btn { + border-color: #ffaa00; + } + .refresh-btn .btn-icon { + color: #ffaa00; + } + /* 🔥【新增】生成海报按钮样式 */ + .poster-btn { + border-color: #ffaa00; + } + .poster-btn .btn-icon { + color: #ffaa00; + } + .loading-btn { + opacity: 0.7; + pointer-events: none; + } + .btn-icon { + font-size: 36rpx; + margin-right: 8rpx; + } + .btn-text { + font-size: 26rpx; + font-weight: 500; + color: #fff; + } + .loading-spinner-small { + width: 30rpx; + height: 30rpx; + border: 4rpx solid rgba(255,255,255,0.2); + border-top: 4rpx solid #fff; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-right: 10rpx; + } + + /* 🔥【新增】专属海报展示区 */ + .poster-section { + margin-top: 40rpx; + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + } + .poster-canvas { + width: 600rpx; + height: 900rpx; + background: #1a1f2e; /* 占位色 */ + border: 4rpx solid #00a6ff; + box-shadow: 0 0 60rpx #00a6ff; + /* 保持方形,无斜切 */ + } + .poster-actions { + display: flex; + gap: 30rpx; + margin-top: 30rpx; + justify-content: center; + } + .save-poster-btn { + background: linear-gradient(45deg, #0066cc, #00a6ff); + border-color: #00a6ff; + } + .back-btn { + background: rgba(0, 30, 60, 0.8); + border-color: #00a6ff; + } + + /* 使用说明(保留) */ + .instruction { + margin-top: 30rpx; + padding: 20rpx 40rpx; + background: rgba(0, 30, 50, 0.5); + border-left: 4rpx solid #00a6ff; + clip-path: polygon(0 0, 100% 0, 98% 100%, 0 100%); + } + .instruction-text { + font-size: 24rpx; + color: #9cf; + line-height: 1.6; + } + + /* 动画 */ + @keyframes qrcodePulse { + 0% { box-shadow: 0 0 40rpx rgba(0, 166, 255, 0.3); } + 100% { box-shadow: 0 0 100rpx rgba(0, 166, 255, 0.6); } + } + @keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } \ No newline at end of file diff --git a/pages/index/index.js b/pages/index/index.js new file mode 100644 index 0000000..bfd2000 --- /dev/null +++ b/pages/index/index.js @@ -0,0 +1,400 @@ +// pages/shangpin/shangpin.js +const app = getApp(); +import { ensureLogin } from '../../utils/login'; +import { fetchShopGoods, bindShop } from '../../utils/shop'; +import { backfillUserProfileCache, getSessionToken } from '../../utils/role-tab-bar'; +import PopupService from '../../services/popupService.js'; + +// 🔥 固定参数:扫码时从 scene 中获取店铺ID的字段名(可在此处集中修改) +const SCENE_PARAM = 'scene'; // 二维码参数统一使用 scene,解码后即为店铺ID + +Page({ + data: { + // 全局变量引用 + ossImageUrl: '', + lunbozhanwei: '', + + // 页面数据 + shangpingonggao: '', + lunboList: [], + shangpinleixing: [], + shangpinzhuanqu: [], + shangpinliebiao: [], + + // 状态控制 + selectedLeixingId: null, + filteredData: [], + showGonggaoModal: false, + gonggaoAnim: true, + isLoading: false, + isRefreshing: false, + hasInitialized: false, + + currentTime: '' + }, + + // 从二维码解析出的店铺ID(仅在扫码时存在) + shopId: null, + + onLoad(options) { + // 🔥 解析二维码参数(scene) + if (options[SCENE_PARAM]) { + try { + // 微信会对 scene 进行编码,需要解码 + const scene = decodeURIComponent(options[SCENE_PARAM]); + this.shopId = scene; // 例如:dianpu_123 + } catch (e) { + console.error('scene解码失败', e); + } + } + + // 等待全局配置加载完成 + this.waitForConfigAndInit(); + // 🆕 修改:仅在已登录时检查弹窗,避免未登录时发起请求导致 401 弹窗 + const token = getSessionToken(app); + if (token) { + PopupService.checkAndShow(this, 'index'); + } + + + }, + + // 🆕 页面隐藏时清理弹窗视图 + onHide: function () { + const popupComp = this.selectComponent('#popupNotice'); + if (popupComp && popupComp.cleanup) { + popupComp.cleanup(); + } +}, + + waitForConfigAndInit() { + if (app.globalData.ossImageUrl) { + this.setData({ + ossImageUrl: app.globalData.ossImageUrl, + lunbozhanwei: app.globalData.lunbozhanwei, + currentTime: this.getCurrentTime() + }); + this.initPageData(); + } else { + setTimeout(() => this.waitForConfigAndInit(), 100); + } + }, + + onShow() { + backfillUserProfileCache(app); + if (this.data.ossImageUrl) { + this.setData({ + gonggaoAnim: true, + currentTime: this.getCurrentTime(), + }); + } + this.registerNotificationComponent(); + + if (!getSessionToken(app)) { + this.setData({ + hasInitialized: false, + filteredData: [], + shangpinleixing: [], + shangpinzhuanqu: [], + shangpinliebiao: [], + }); + return; + } + + // 已登录时加载/刷新商品;未登录不自动登录、不跳转 + if (!this.data.isLoading) { + const needLoad = !this.data.hasInitialized + || !this.data.filteredData + || this.data.filteredData.length === 0; + if (needLoad) { + this.initPageData(); + } + } + }, + + registerNotificationComponent() { + const notificationComp = this.selectComponent('#global-notification'); + if (notificationComp && notificationComp.showNotification) { + app.globalData.globalNotification = { + show: (data) => notificationComp.showNotification(data), + hide: () => notificationComp.hideNotification() + }; + } + }, + + getCurrentTime() { + const now = new Date(); + const year = now.getFullYear(); + const month = (now.getMonth() + 1).toString().padStart(2, '0'); + const day = now.getDate().toString().padStart(2, '0'); + const hour = now.getHours().toString().padStart(2, '0'); + const minute = now.getMinutes().toString().padStart(2, '0'); + return `${year}-${month}-${day} ${hour}:${minute}`; + }, + + sortByPaixu(list) { + if (!list || !Array.isArray(list)) return []; + return [...list].sort((a, b) => { + const paixuA = a.paixu || 0; + const paixuB = b.paixu || 0; + if (paixuB !== paixuA) return paixuB - paixuA; + return a.id - b.id; + }); + }, + + /** + * 初始化页面数据(重构后) + * 流程:确保登录 → 如有扫码ID则绑定店铺 → 并行加载公告轮播图和商品数据 + */ + async initPageData() { + this.setData({ isLoading: true }); + + try { + // 1. 确保登录(静默,不跳转) + await ensureLogin({ page: this }); + + // 2. 🔥 关键:仅当通过扫码进入且携带了有效的店铺ID时,才执行绑定 + if (this.shopId) { + await bindShop(this.shopId); + } + + // 3. 并行加载公告轮播图(复用原接口,独立请求)和店铺商品(新接口) + const [gonggaoResult, shopData] = await Promise.all([ + this.loadGonggaoAndLunbo(), // 公告轮播图使用原有的 wx.request 方式,不受影响 + fetchShopGoods() // 商品数据通过封装的 request 获取 + ]); + + // 4. 处理商品数据 + const sortedLeixing = this.sortByPaixu(shopData.shangpinleixing || []); + const sortedZhuanqu = this.sortByPaixu(shopData.shangpinzhuanqu || []); + const sortedShangpin = this.sortByPaixu(shopData.shangpinliebiao || []); + + + // 更新全局缓存(可选,用于其他页面复用) + app.globalData.shangpinleixing = sortedLeixing; + app.globalData.shangpinzhuanqu = sortedZhuanqu; + app.globalData.shangpinliebiao = sortedShangpin; + + this.setData({ + shangpinleixing: sortedLeixing, + shangpinzhuanqu: sortedZhuanqu, + shangpinliebiao: sortedShangpin, + selectedLeixingId: sortedLeixing[0]?.id || null, + isLoading: false, + hasInitialized: true, + }, () => { + this.filterShangpinData(); + }); + + } catch (error) { + console.error('初始化失败:', error); + this.setData({ isLoading: false }); + // 错误提示已在各函数内部处理,此处仅做兜底 + } + }, + + /** + * 加载公告和轮播图(完全复用原逻辑,使用 wx.request,不使用封装 request) + */ + loadGonggaoAndLunbo() { + return new Promise((resolve, reject) => { + // 如果全局已有数据,直接使用 + if (app.globalData.shangpingonggao && app.globalData.shangpinlunbo.length > 0) { + this.setData({ + shangpingonggao: app.globalData.shangpingonggao, + lunboList: this.processImageUrls(app.globalData.shangpinlunbo) + }); + resolve(); + return; + } + + wx.request({ + url: app.globalData.apiBaseUrl + '/peizhi/shangpingonggao/', + method: 'POST', + header: { 'content-type': 'application/json' }, + success: (res) => { + if (res.statusCode === 200 && res.data) { + const data = res.data; + app.globalData.shangpingonggao = data.shangpingonggao || ''; + app.globalData.shangpinlunbo = data.shangpinlunbo || []; + this.setData({ + shangpingonggao: app.globalData.shangpingonggao, + lunboList: this.processImageUrls(app.globalData.shangpinlunbo) + }); + resolve(); + } else { + reject(new Error('请求失败')); + } + }, + fail: reject + }); + }); + }, + + processImageUrls(urlList) { + if (!urlList || !Array.isArray(urlList)) return []; + return urlList.map(url => url.startsWith('http') ? url : this.data.ossImageUrl + url); + }, + + /** + * 筛选商品数据(完全复用原有逻辑,不做任何改动) + */ + filterShangpinData() { + const { selectedLeixingId, shangpinzhuanqu, shangpinliebiao } = this.data; + + if (!selectedLeixingId || !shangpinzhuanqu || !shangpinliebiao) { + this.setData({ filteredData: [] }); + return; + } + + const zhuanquByLeixing = new Map(); + shangpinzhuanqu.forEach(zhuanqu => { + if (zhuanqu.leixing_id === selectedLeixingId) { + if (!zhuanquByLeixing.has(selectedLeixingId)) { + zhuanquByLeixing.set(selectedLeixingId, []); + } + zhuanquByLeixing.get(selectedLeixingId).push(zhuanqu); + } + }); + + const shangpinByZhuanqu = new Map(); + shangpinliebiao.forEach(shangpin => { + if (shangpin.leixing_id === selectedLeixingId && shangpin.zhuanqu_id) { + const zhuanquId = shangpin.zhuanqu_id; + if (!shangpinByZhuanqu.has(zhuanquId)) { + shangpinByZhuanqu.set(zhuanquId, []); + } + + const jiage = parseFloat(shangpin.jiage) || 0; + const priceInteger = Math.floor(jiage); + let priceDecimal = '00'; + const decimalPart = (jiage - priceInteger).toFixed(2); + if (decimalPart > 0) { + priceDecimal = decimalPart.toString().split('.')[1]; + } + + shangpinByZhuanqu.get(zhuanquId).push({ + id: shangpin.id, + biaoqian: shangpin.biaoqian, + jiage: jiage, + priceInteger: priceInteger, + priceDecimal: priceDecimal, + tupian_url: shangpin.tupian_url, + duiwai_xiaoliang: shangpin.duiwai_xiaoliang || 0, + paixu: shangpin.paixu || 0 + }); + } + }); + + const filteredData = []; + const currentZhuanquList = zhuanquByLeixing.get(selectedLeixingId) || []; + + currentZhuanquList.forEach(zhuanqu => { + const shangpinList = shangpinByZhuanqu.get(zhuanqu.id) || []; + if (shangpinList.length > 0) { + filteredData.push({ + zhuanqu: { + id: zhuanqu.id, + mingzi: zhuanqu.mingzi, + paixu: zhuanqu.paixu || 0 + }, + shangpinList: shangpinList + }); + } + }); + + const sortedFilteredData = filteredData.sort((a, b) => { + const paixuA = a.zhuanqu.paixu || 0; + const paixuB = b.zhuanqu.paixu || 0; + if (paixuB !== paixuA) return paixuB - paixuA; + return a.zhuanqu.id - b.zhuanqu.id; + }); + + this.setData({ filteredData: sortedFilteredData }); + }, + + selectLeixing(e) { + const leixingId = e.currentTarget.dataset.id; + if (this.data.selectedLeixingId === leixingId) return; + this.setData({ selectedLeixingId: leixingId }, () => { + this.filterShangpinData(); + }); + }, + + showGonggaoDetail() { + this.setData({ + showGonggaoModal: true, + gonggaoAnim: false, + currentTime: this.getCurrentTime() + }); + }, + + hideGonggaoDetail() { + this.setData({ + showGonggaoModal: false, + gonggaoAnim: true + }); + }, + + /** + * 刷新所有数据(点击刷新按钮) + * 只刷新商品数据,公告轮播图保持不变(若需刷新可清空全局缓存后调用 loadGonggaoAndLunbo) + */ + async refreshAllData() { + if (this.data.isRefreshing) return; + this.setData({ isRefreshing: true }); + wx.showLoading({ title: '刷新中...', mask: true }); + + try { + // 重新获取商品数据(登录状态由 fetchShopGoods 内部确保) + const shopData = await fetchShopGoods(); + + // 公告轮播图通常不需要随刷新按钮更新,保持原样 + + const sortedLeixing = this.sortByPaixu(shopData.shangpinleixing || []); + const sortedZhuanqu = this.sortByPaixu(shopData.shangpinzhuanqu || []); + const sortedShangpin = this.sortByPaixu(shopData.shangpinliebiao || []); + + app.globalData.shangpinleixing = sortedLeixing; + app.globalData.shangpinzhuanqu = sortedZhuanqu; + app.globalData.shangpinliebiao = sortedShangpin; + + this.setData({ + shangpinleixing: sortedLeixing, + shangpinzhuanqu: sortedZhuanqu, + shangpinliebiao: sortedShangpin, + selectedLeixingId: sortedLeixing[0]?.id || null, + isRefreshing: false + }, () => { + this.filterShangpinData(); + }); + + wx.hideLoading(); + wx.showToast({ title: '刷新成功', icon: 'success', duration: 1500 }); + } catch (error) { + wx.hideLoading(); + this.setData({ isRefreshing: false }); + } + }, + + goToDetail(e) { + const shangpinId = e.currentTarget.dataset.id; + if (!shangpinId) return; + wx.navigateTo({ + url: `/pages/shangpinxiangqing/shangpinxiangqing?id=${shangpinId}` + }); + }, + + onReachBottom() {}, + + onShareAppMessage() { + return { + title: '阿龙电竞', + path: '/pages/shangpin/shangpin' + }; + }, + + onUnload() { + this.setData({ gonggaoAnim: false }); + } +}); \ No newline at end of file diff --git a/pages/index/index.json b/pages/index/index.json new file mode 100644 index 0000000..06f55f5 --- /dev/null +++ b/pages/index/index.json @@ -0,0 +1,12 @@ +{ + "usingComponents": { + "global-notification": "/components/global-notification/global-notification", + "custom-tab-bar": "/custom-tab-bar/index" + }, + + "navigationBarTitleText": "商城", + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black", + + "backgroundColor": "#f5faff" + } \ No newline at end of file diff --git a/pages/index/index.wxml b/pages/index/index.wxml new file mode 100644 index 0000000..d5fff6b --- /dev/null +++ b/pages/index/index.wxml @@ -0,0 +1,172 @@ + + + + + + + + + + {{shangpingonggao || '暂无公告'}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{item.jieshao}} + + + + + + + + + + + + + + {{item.zhuanqu.mingzi}} + + + + + + + + + + + + + + + {{item.biaoqian}} + + + + + + ¥ + {{item.priceInteger}} + .{{item.priceDecimal}} + + + + + + + + + + + + + + 暂无商品 + + + + + + + + + + + 公告详情 + × + + + {{shangpingonggao || '暂无公告内容'}} + + + + + + + + 加载中... + + + + + + \ No newline at end of file diff --git a/pages/index/index.wxss b/pages/index/index.wxss new file mode 100644 index 0000000..beb4936 --- /dev/null +++ b/pages/index/index.wxss @@ -0,0 +1,585 @@ +/* ===== 1. 全局样式 ===== */ +.shangpin-page { + min-height: 100vh; + background: linear-gradient(180deg, #f5faff 0%, #f0f8ff 100%); + } + +/* ===== 2. 公告栏样式 - 物理上移方案 ===== */ +.gonggao-box { + margin: 20rpx 30rpx; + height: 80rpx; + border-radius: 40rpx; + background: linear-gradient(135deg, + rgba(220, 255, 220, 0.85) 0%, + rgba(255, 255, 200, 0.85) 100%); + backdrop-filter: blur(10px); + display: flex; + align-items: center; + padding: 0 25rpx; + box-shadow: 0 4rpx 15rpx rgba(0, 100, 0, 0.08); + border: 1rpx solid rgba(255, 255, 255, 0.3); + position: relative; + overflow: hidden; + } + + .gonggao-icon { + width: 40rpx; + height: 40rpx; + margin-right: 20rpx; + flex-shrink: 0; + display: flex; + align-items: center; + } + + .gonggao-icon image { + width: 100%; + height: 100%; + } + + .gonggao-content { + flex: 1; + overflow: hidden; + height: 100%; + position: relative; + } + + /* 关键修改:使用物理上移 */ + .gonggao-text { + font-size: 26rpx; + color: #333; + white-space: nowrap; + padding-right: 50rpx; + + /* 物理上移方法1:直接调整位置 */ + position: absolute; + top: -5rpx; /* 从顶部开始计算位置 */ + left: 0; + + /* 或者使用方法2:负margin上移 */ + /* position: relative; + top: -10rpx; */ + + /* 或者使用方法3:translate上移 */ + /* transform: translateY(-5rpx); */ + } + + /* 尝试不同的上移值,直到居中为止 */ + /* 可以尝试的值:5rpx, 8rpx, 10rpx, 12rpx, 15rpx, 18rpx, 20rpx */ + + .gonggao-anim { + animation: marquee 10s linear infinite; /* 加快速度到10秒 */ + animation-delay: 0.5s; + } + + @keyframes marquee { + 0% { + transform: translateX(100%); + } + 100% { + transform: translateX(-100%); + } + } + + /* ===== 3. 轮播图样式 ===== */ + .lunbo-container { + margin: 0 30rpx 30rpx; + height: 350rpx; + border-radius: 25rpx; + overflow: hidden; + position: relative; + box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.1); + } + + .lunbo-swiper { + width: 100%; + height: 100%; + } + + .lunbo-image { + width: 100%; + height: 100%; + } + + .lunbo-placeholder { + width: 100%; + height: 100%; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + display: flex; + justify-content: center; + align-items: center; + } + + .lunbo-placeholder image { + width: 100%; + height: 100%; + opacity: 0.8; + } + + /* 刷新按钮 */ + .refresh-btn { + position: absolute; + top: 20rpx; + right: 20rpx; + width: 80rpx; + height: 80rpx; + z-index: 10; + display: flex; + justify-content: center; + align-items: center; + } + + .refresh-icon { + width: 60rpx; + height: 60rpx; + transition: transform 0.3s; + filter: drop-shadow(0 4rpx 8rpx rgba(0, 0, 0, 0.2)); + } + + .refreshing { + animation: rotate360 1s linear infinite; + } + + @keyframes rotate360 { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } + } + + .refresh-dot { + position: absolute; + width: 12rpx; + height: 12rpx; + background: #ffcc00; + border-radius: 50%; + top: 15rpx; + right: 15rpx; + animation: pulse 2s infinite; + } + + @keyframes pulse { + 0%, 100% { + opacity: 0.8; + transform: scale(1); + } + 50% { + opacity: 1; + transform: scale(1.2); + } + } + + /* ===== 4. 商品展示区域 ===== */ + .shangpin-container { + background: rgba(255, 255, 255, 0.95); + border-radius: 40rpx 40rpx 0 0; + margin-top: -20rpx; + padding: 40rpx 30rpx 100rpx; + min-height: 800rpx; + box-shadow: 0 -10rpx 30rpx rgba(0, 0, 0, 0.05); + } + + /* 商品类型选择 - 完全重新设计,确保左对齐且间距协调 */ + .leixing-scroll { + width: 100%; + white-space: nowrap; + margin-bottom: 50rpx; + } + + .leixing-list { + display: inline-flex; + padding-left: 0; /* 完全去掉左内边距,100%左对齐 */ + padding-right: 30rpx; /* 只保留右内边距给最后一个元素一点空间 */ + padding-top: 10rpx; + padding-bottom: 10rpx; + } + + /* 关键:完全协调的间距 */ + .leixing-item { + display: inline-flex; + flex-direction: column; + align-items: center; + margin-right: 30rpx; /* 合适的间距,不会太大也不会太小 */ + transition: all 0.3s; + position: relative; + flex-shrink: 0; + width: 130rpx; /* 固定宽度,确保四个看起来协调 */ + } + + .leixing-item:last-child { + margin-right: 0; + } + + .leixing-image-box { + width: 110rpx; /* 稍微减小,让整体更紧凑 */ + height: 110rpx; + border-radius: 25rpx; + overflow: hidden; + position: relative; + margin-bottom: 12rpx; + transition: all 0.3s; + box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.1); + } + + .leixing-image { + width: 100%; + height: 100%; + } + + .leixing-glow { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 25rpx; + border: 4rpx solid transparent; + transition: all 0.3s; + } + + .leixing-active .leixing-glow { + border-color: #ffcc00; + box-shadow: 0 0 20rpx rgba(255, 204, 0, 0.5); + } + + .leixing-active .leixing-image-box { + transform: translateY(-8rpx); + } + + .leixing-text { + font-size: 24rpx; + color: #666; + white-space: nowrap; + transition: all 0.3s; + text-align: center; + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + } + + .leixing-active .leixing-text { + color: #333; + font-weight: bold; + transform: scale(1.05); + } + + /* 专区标题 */ + .zhuanqu-title { + display: flex; + align-items: center; + margin: 40rpx 0 15rpx; + padding-left: 0; + position: relative; + } + + .zhuanqu-icon { + width: 40rpx; + height: 40rpx; + margin-right: 20rpx; + position: relative; + } + + .zhuanqu-icon-image { + width: 100%; + height: 100%; + filter: drop-shadow(0 2rpx 4rpx rgba(255, 204, 0, 0.3)); + } + + .zhuanqu-sparkle { + animation: sparkle 2s infinite; + } + + @keyframes sparkle { + 0%, 100% { + transform: scale(1) rotate(0deg); + filter: drop-shadow(0 2rpx 4rpx rgba(255, 204, 0, 0.3)); + } + 50% { + transform: scale(1.1) rotate(10deg); + filter: drop-shadow(0 4rpx 8rpx rgba(255, 204, 0, 0.6)); + } + } + + .zhuanqu-text { + font-size: 32rpx; + font-weight: bold; + color: #333; + position: relative; + } + + .zhuanqu-text::after { + content: ''; + position: absolute; + left: 0; + bottom: -8rpx; + width: 60rpx; + height: 4rpx; + background: linear-gradient(90deg, #ffcc00, transparent); + border-radius: 2rpx; + } + + /* 商品网格 */ + .shangpin-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 25rpx; + margin-bottom: 40rpx; + } + + .shangpin-card { + background: #ffffff; + border-radius: 20rpx; + overflow: hidden; + box-shadow: 0 8rpx 25rpx rgba(0, 0, 0, 0.06); + transition: all 0.3s; + position: relative; + } + + .shangpin-card:active { + transform: scale(0.98); + box-shadow: 0 4rpx 15rpx rgba(0, 0, 0, 0.1); + } + + .shangpin-image-box { + width: 100%; + height: 220rpx; + position: relative; + overflow: hidden; + } + + .shangpin-image { + width: 100%; + height: 100%; + transition: transform 0.5s; + } + + /* 商品标题区域 - 协调紧凑 */ + .shangpin-title { + height: 75rpx; + padding: 6rpx 12rpx 2rpx 12rpx; + display: flex; + align-items: flex-start; + overflow: hidden; + } + + .title-text { + font-size: 26rpx; + color: #000; + line-height: 34rpx; + font-weight: 450; /* 微调粗细,更加协调 */ + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; + } + + /* 价格和购买按钮 - 协调紧凑 */ + .shangpin-footer { + height: 50rpx; + padding: 0 12rpx 8rpx 12rpx; + display: flex; + justify-content: space-between; + align-items: center; + } + + .price-box { + display: flex; + align-items: baseline; + } + + .price-icon { + font-size: 24rpx; + color: #ff4444; + font-weight: bold; + margin-right: 2rpx; + } + + .price-integer { + font-size: 32rpx; + color: #ff4444; + font-weight: bold; + } + + .price-decimal { + font-size: 24rpx; + color: #ff4444; + font-weight: bold; + } + + .buy-btn { + width: 48rpx; + height: 48rpx; + border-radius: 50%; + background: linear-gradient(135deg, #ff4444, #ff6666); + display: flex; + justify-content: center; + align-items: center; + box-shadow: 0 4rpx 12rpx rgba(255, 68, 68, 0.3); + transition: all 0.3s; + } + + .buy-btn:active { + transform: scale(0.9); + } + + .buy-icon { + width: 26rpx; + height: 26rpx; + filter: brightness(0) invert(1); + } + + /* ===== 5. 空状态 ===== */ + .empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 100rpx 0; + } + + .empty-image { + width: 200rpx; + height: 200rpx; + margin-bottom: 40rpx; + opacity: 0.7; + } + + .empty-text { + font-size: 30rpx; + color: #999; + margin-bottom: 40rpx; + } + + .empty-btn { + background: linear-gradient(135deg, #667eea, #764ba2); + color: white; + font-size: 28rpx; + padding: 20rpx 60rpx; + border-radius: 50rpx; + box-shadow: 0 8rpx 25rpx rgba(102, 126, 234, 0.3); + } + + /* ===== 6. 弹窗样式 ===== */ + .gonggao-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 1000; + display: flex; + justify-content: center; + align-items: center; + } + + .modal-mask { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + backdrop-filter: blur(5px); + } + + .modal-content { + width: 600rpx; + background: white; + border-radius: 30rpx; + overflow: hidden; + box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.2); + z-index: 1001; + animation: modalShow 0.3s; + } + + @keyframes modalShow { + from { + opacity: 0; + transform: scale(0.9); + } + to { + opacity: 1; + transform: scale(1); + } + } + + .modal-header { + padding: 40rpx; + border-bottom: 2rpx solid #f0f0f0; + display: flex; + justify-content: space-between; + align-items: center; + } + + .modal-title { + font-size: 32rpx; + font-weight: bold; + color: #333; + } + + .modal-close { + font-size: 40rpx; + color: #999; + width: 60rpx; + height: 60rpx; + display: flex; + justify-content: center; + align-items: center; + border-radius: 50%; + } + + .modal-close:active { + background: #f5f5f5; + } + + .modal-body { + max-height: 600rpx; + padding: 40rpx; + } + + .modal-text { + font-size: 28rpx; + color: #333; + line-height: 1.6; + white-space: normal; + word-break: break-word; + } + + /* ===== 7. 加载提示 ===== */ + .loading-toast { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: rgba(0, 0, 0, 0.7); + backdrop-filter: blur(10px); + padding: 30rpx 50rpx; + border-radius: 25rpx; + display: flex; + flex-direction: column; + align-items: center; + z-index: 999; + } + + .loading-spinner { + width: 60rpx; + height: 60rpx; + border: 6rpx solid rgba(255, 255, 255, 0.3); + border-top-color: white; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-bottom: 20rpx; + } + + @keyframes spin { + to { + transform: rotate(360deg); + } + } + + .loading-text { + font-size: 26rpx; + color: white; + } \ No newline at end of file diff --git a/pages/jiedan/jiedan.js b/pages/jiedan/jiedan.js new file mode 100644 index 0000000..403bf0f --- /dev/null +++ b/pages/jiedan/jiedan.js @@ -0,0 +1,577 @@ +// pages/qiangdan/qiangdan.js +const app = getApp(); +import request from '../../utils/request.js'; +import PopupService from '../../services/popupService.js'; +import { reconnectForRole } from '../../utils/role-tab-bar.js'; + +Page({ + data: { + // 1. 商品类型相关 + shangpinleixing: [], + xuanzhongLeixingId: null, + + // 2. 订单列表与分页相关 + dingdanList: [], + page: 1, + pageSize: 5, + hasMore: true, + isLoading: false, + + // 3. 全局状态字段 + dashoustatus: null, + zhuanghaoStatus: null, + dashouzhuangtai: null, + uid: null, + huiyuanList: [], + yajin: 0, + jifen: 0, + + // 4. 刷新控制字段 + scrollViewRefreshing: false, + lastRefreshTime: 0, + refreshCooldown: 2000, + isLoadingMore: false, + + // 5. 切换类型冷却 + lastSwitchTime: 0, + switchCooldown: 1500, + isSwitching: false, + + // 6. OSS地址 + ossImageUrl: app.globalData.ossImageUrl || '', + + // 🆕 7. 标签筛选相关 + bankuaiBiaoqian: [], // 当前商品类型所属板块的全部标签 + xuanzhongBiaoqianId: 0, // 0=全部 + }, + + async onLoad(options) { + this.loadGlobalStatus(); + await this.loadShangpinLeixing(); + PopupService.checkAndShow(this, 'jiedan'); + }, + + onHide: function () { + const popupComp = this.selectComponent('#popupNotice'); + if (popupComp && popupComp.cleanup) { + popupComp.cleanup(); + } + }, + + onShow() { + this.registerNotificationComponent(); + this.loadGlobalStatus(); + if (wx.getStorageSync('uid')) { + reconnectForRole('dashou'); + if (app.startImWhenReady) app.startImWhenReady(); + this.refreshDashouProfileSilent(); + } + if (this.data.xuanzhongLeixingId) { + this.loadDingdanList(true); + } + }, + + async refreshDashouProfileSilent() { + try { + const res = await request({ url: '/yonghu/dashouxinxi', method: 'POST' }); + if (!res || res.data.code != 200) return; + const data = res.data.data || {}; + const patch = { + dashouNicheng: data.dashounicheng || '', + zhanghaoStatus: data.zhanghaostatus || '', + dashouzhuangtai: data.dashouzhuangtai || '', + yajin: data.yajin || 0, + jifen: data.jifen || 0, + clumber: data.clumber || [], + }; + Object.assign(app.globalData, { + dashouNicheng: patch.dashouNicheng, + zhanghaoStatus: patch.zhanghaoStatus, + dashouzhuangtai: patch.dashouzhuangtai, + yajin: patch.yajin, + jinfen: patch.jifen, + clumber: patch.clumber, + }); + if (data.dashoustatus !== undefined) { + wx.setStorageSync('dashoustatus', data.dashoustatus); + app.globalData.dashoustatus = data.dashoustatus; + } + this.setData({ + zhuanghaoStatus: patch.zhanghaoStatus, + dashouzhuangtai: patch.dashouzhuangtai, + huiyuanList: patch.clumber, + yajin: patch.yajin, + jifen: patch.jifen, + }); + } catch (e) {} + }, + + registerNotificationComponent() { + const notificationComp = this.selectComponent('#global-notification'); + if (notificationComp && notificationComp.showNotification) { + app.globalData.globalNotification = { + show: (data) => notificationComp.showNotification(data), + hide: () => notificationComp.hideNotification() + }; + } + }, + + loadGlobalStatus() { + const globalData = app.globalData; + this.setData({ + dashoustatus: wx.getStorageSync('dashoustatus'), + zhuanghaoStatus: globalData.zhanghaoStatus, + dashouzhuangtai: globalData.dashouzhuangtai, + uid: wx.getStorageSync('uid'), + huiyuanList: globalData.clumber || [], + yajin: globalData.yajin || 0, + jifen: globalData.jinfen || 0 + }); + }, + + // 加载商品类型 + async loadShangpinLeixing() { + wx.showLoading({ title: '加载商品类型...' }); + try { + const res = await request({ + url: '/dingdan/dsqdhqddlx', + method: 'POST', + header: { 'content-type': 'application/json' } + }); + if (res.statusCode === 200 && res.data) { + const data = res.data; + let list = []; + if (data.code === 200 || data.code === 0) { + if (data.data && Array.isArray(data.data.list)) list = data.data.list; + else if (data.data && Array.isArray(data.data)) list = data.data; + else if (Array.isArray(data.list)) list = data.list; + else if (Array.isArray(data)) list = data; + } + const processedList = this.processTupianUrl(list); + this.setData({ + shangpinleixing: processedList, + xuanzhongLeixingId: processedList[0]?.id || null + }); + if (this.data.xuanzhongLeixingId) { + this.loadDingdanList(true); + // 🆕 加载板块标签 + this.loadBankuaiBiaoqian(); + } + } else { + wx.showToast({ title: res.data?.msg || '加载商品类型失败', icon: 'none' }); + } + } catch (error) { + wx.showToast({ title: '网络错误,加载失败', icon: 'none' }); + } finally { + wx.hideLoading(); + } + }, + + // 处理图片URL + processTupianUrl(list) { + const ossUrl = app.globalData.ossImageUrl || ''; + return list.map(item => ({ + ...item, + full_tupian_url: item.tupian_url && !item.tupian_url.startsWith('http') + ? ossUrl + item.tupian_url + : (item.tupian_url || '/images/default-type.png') + })); + }, + + // 🆕 加载板块标签 + async loadBankuaiBiaoqian() { + const leixingId = this.data.xuanzhongLeixingId; + if (!leixingId) return; + try { + const res = await request({ + url: '/dengji/bkqhbq', + method: 'POST', + data: { leixing_id: leixingId } + }); + if (res && res.data.code === 0) { + this.setData({ + bankuaiBiaoqian: res.data.data.biaoqian_list || [], + xuanzhongBiaoqianId: 0 + }); + } + } catch (e) { + // 标签加载失败不影响主流程 + } + }, + + // 选择商品类型 + async selectLeixing(e) { + const now = Date.now(); + const lastSwitchTime = this.data.lastSwitchTime; + const switchCooldown = this.data.switchCooldown; + + if (now - lastSwitchTime < switchCooldown) { + wx.showToast({ + title: `操作太快,请${Math.ceil((switchCooldown - (now - lastSwitchTime)) / 1000)}秒后再试`, + icon: 'none', + duration: 1500 + }); + return; + } + if (this.data.isSwitching) return; + + const leixingId = e.currentTarget.dataset.id; + if (leixingId === this.data.xuanzhongLeixingId) return; + + this.setData({ isSwitching: true, lastSwitchTime: now }); + wx.showLoading({ title: '切换中...' }); + + try { + this.setData({ + xuanzhongLeixingId: leixingId, + dingdanList: [], + page: 1, + hasMore: true, + xuanzhongBiaoqianId: 0 // 重置标签筛选 + }); + await this.loadDingdanList(true); + // 🆕 切换类型后加载新标签 + await this.loadBankuaiBiaoqian(); + } catch (error) { + console.error('切换类型失败:', error); + } finally { + this.setData({ isSwitching: false }); + wx.hideLoading(); + } + }, + + // 🆕 选择标签筛选 + async selectBiaoqian(e) { + const id = e.currentTarget.dataset.id; + if (id === this.data.xuanzhongBiaoqianId) return; + this.setData({ + xuanzhongBiaoqianId: id, + dingdanList: [], + page: 1, + hasMore: true + }); + await this.loadDingdanList(true); + }, + + // 加载订单列表 + async loadDingdanList(isRefresh = false) { + if (this.data.isLoading || !this.data.xuanzhongLeixingId) return; + + const loadPage = isRefresh ? 1 : this.data.page; + if (!isRefresh && !this.data.hasMore) return; + + this.setData({ + isLoading: true, + isLoadingMore: !isRefresh + }); + + try { + const res = await request({ + url: '/dingdan/ddhq', + method: 'POST', + data: { + leixing_id: this.data.xuanzhongLeixingId, + page: loadPage, + page_size: this.data.pageSize, + // 🆕 传递标签筛选参数(0时不传) + biaoqian_id: this.data.xuanzhongBiaoqianId || undefined + } + }); + + this.setData({ + isLoading: false, + isLoadingMore: false, + scrollViewRefreshing: false + }); + + if (res.data.code === 200 || res.data.code === 0) { + const newList = res.data.data.list || []; + const hasMore = res.data.data.has_more || false; + + // 🆕 处理每条订单,增加 hasRequiredMember 字段 + const processedList = newList.map(item => this.processDingdanItem(item)); + const updatedList = isRefresh ? processedList : [...this.data.dingdanList, ...processedList]; + updatedList.sort((a, b) => (b.creat_time || '').localeCompare(a.creat_time || '')); + + this.setData({ + dingdanList: updatedList, + page: loadPage, + hasMore: hasMore + }); + + if (isRefresh) { + this.setData({ lastRefreshTime: Date.now() }); + } + } else { + wx.showToast({ title: res.data.msg || '加载失败', icon: 'none' }); + } + } catch (error) { + console.error('加载订单失败:', error); + this.setData({ + isLoading: false, + isLoadingMore: false, + scrollViewRefreshing: false + }); + wx.showToast({ title: '网络请求失败', icon: 'none' }); + } + }, + + // 处理单条订单数据(包含标签、会员判断) + processDingdanItem(item) { + const ossUrl = app.globalData.ossImageUrl || ''; + let fullTupianUrl = ''; + + if (item.tupian) { + fullTupianUrl = !item.tupian.startsWith('http') ? ossUrl + item.tupian : item.tupian; + } else { + const leixing = this.data.shangpinleixing.find(l => l.id === item.leixing_id); + fullTupianUrl = leixing ? leixing.full_tupian_url : '/images/default-order.png'; + } + + const isZhiding = item.zhuangtai === 7; + + let zhidingAvatar = ''; + if (item.zhiding_avatar) { + zhidingAvatar = !item.zhiding_avatar.startsWith('http') + ? ossUrl + item.zhiding_avatar + : item.zhiding_avatar; + } + const zhidingNicheng = item.zhiding_nicheng || ''; + + // 🆕 判断用户是否有订单所需的会员(仅当订单要求会员类型且存在huiyuan_id) + let hasRequiredMember = true; + if (item.yaoqiuleixing == 1 && item.huiyuan_id) { + const userHasIt = this.data.huiyuanList.some(h => h.huiyuanid == item.huiyuan_id); + hasRequiredMember = userHasIt; + } + + return { + ...item, + full_tupian_url: fullTupianUrl, + isZhiding: isZhiding, + isPingtai: item.pingtai == 1, + isShangjia: item.pingtai == 2, + zhiding_avatar_full: zhidingAvatar, + zhiding_nicheng: zhidingNicheng, + // 🆕 三层标签(后端返回,直接保留) + xuqiu_biaoqian: item.xuqiu_biaoqian || [], + dashou_biaoqian: item.dashou_biaoqian || [], + shangjia_biaoqian: item.shangjia_biaoqian || [], + // 🆕 是否有查看价格的权限 + hasRequiredMember: hasRequiredMember, + }; + }, + + // 跳转到充值页面(原封不动) + goToChongzhiPage(failureType, huiyuanId = null, requiredYajin = 0) { + let url = '/pages/dashouchongzhi/dashouchongzhi'; + let params = {}; + + switch (failureType) { + case 'huiyuan': + params = { needScroll: '0', scrollTo: 'member' }; + break; + case 'yajin': + params = { needScroll: '1', scrollTo: 'bottom', requiredYajin: requiredYajin }; + break; + case 'jifen': + params = { needScroll: '1', scrollTo: 'bottom' }; + break; + } + + if (huiyuanId) params.huiyuanId = huiyuanId; + + const queryString = Object.keys(params) + .map(key => `${key}=${encodeURIComponent(params[key])}`) + .join('&'); + + wx.navigateTo({ + url: `${url}?${queryString}`, + fail: (err) => { + console.error('跳转失败:', err); + wx.showToast({ title: '跳转失败,请稍后重试', icon: 'none' }); + } + }); + }, + + // 抢单按钮(原封不动) + async onQiangdanTap(e) { + const dingdanItem = e.currentTarget.dataset.item; + if (!dingdanItem) return; + + // 校验1: 打手身份 + if (!this.data.dashoustatus || this.data.dashoustatus != 1) { + wx.showToast({ title: '请先开启打手身份', icon: 'none' }); + return; + } + // 校验2: 账号状态 + if (this.data.zhuanghaoStatus != 1) { + wx.showToast({ title: '账号异常,无法接单', icon: 'none' }); + return; + } + // 校验3: 打手接单状态 + if (this.data.dashouzhuangtai != 1) { + wx.showToast({ title: '您有订单正在接待中', icon: 'none' }); + return; + } + // 校验4: 指定订单 + if (dingdanItem.isZhiding) { + if (!this.data.uid || this.data.uid != dingdanItem.zhiding_uid) { + wx.showToast({ title: '此订单为指定单', icon: 'none' }); + return; + } + } + // 校验5: 会员要求 + if (dingdanItem.yaoqiuleixing == 1) { + const hasHuiyuan = this.data.huiyuanList.some(h => h.huiyuanid == dingdanItem.huiyuan_id); + if (!hasHuiyuan) { + wx.showToast({ + title: '未开通对应会员,无法抢单', + icon: 'none', + duration: 2000 + }); + setTimeout(() => { + this.goToChongzhiPage('huiyuan', dingdanItem.huiyuan_id); + }, 500); + return; + } + } + // 校验6: 押金要求 + if (dingdanItem.yaoqiuleixing == 2) { + if (this.data.yajin < dingdanItem.yajin) { + wx.showToast({ + title: `押金不足,需${dingdanItem.yajin}`, + icon: 'none', + duration: 2000 + }); + setTimeout(() => { + this.goToChongzhiPage('yajin', null, dingdanItem.yajin); + }, 500); + return; + } + } + // 校验7: 积分要求 + if (this.data.jifen < 5) { + wx.showToast({ + title: '积分不足,至少需要5积分', + icon: 'none', + duration: 2000 + }); + setTimeout(() => { + this.goToChongzhiPage('jifen'); + }, 500); + return; + } + + // 确认框 + const that = this; + wx.showModal({ + title: '确认抢单', + content: `确定要抢此订单吗?${dingdanItem.isZhiding ? '(指定订单)' : ''}`, + success: async function (res) { + if (res.confirm) { + wx.showLoading({ title: '抢单中...', mask: true }); + try { + const qiangdanRes = await request({ + url: '/dingdan/qiangdan', + method: 'POST', + data: { dingdan_id: dingdanItem.dingdan_id } + }); + wx.hideLoading(); + + if (qiangdanRes.data.code === 200 || qiangdanRes.data.code === 0) { + that.showQiangdanSuccessEffect(); + if (app.globalData) { + app.globalData.dashouzhuangtai = 0; + that.setData({ dashouzhuangtai: 0 }); + } + const newList = that.data.dingdanList.filter( + item => item.dingdan_id !== dingdanItem.dingdan_id + ); + that.setData({ dingdanList: newList }); + } else { + wx.showToast({ + title: qiangdanRes.data.msg || '抢单失败', + icon: 'none' + }); + } + } catch (error) { + wx.hideLoading(); + console.error('抢单请求失败:', error); + wx.showToast({ title: '网络错误,抢单失败', icon: 'none' }); + } + } + } + }); + }, + + showQiangdanSuccessEffect() { + wx.showToast({ title: '抢单成功!', icon: 'success', duration: 2000 }); + }, + + onViewDetail(e) { + const { type, content } = e.currentTarget.dataset; + wx.showModal({ + title: type === 'jieshao' ? '订单介绍' : '订单备注', + content: content, + showCancel: false, + confirmText: '知道了' + }); + }, + + onReachBottom() { + const now = Date.now(); + const lastTime = this.data.lastRefreshTime; + const cooldown = this.data.refreshCooldown; + + if (now - lastTime < cooldown) { + wx.showToast({ + title: `操作太快,请${Math.ceil((cooldown - (now - lastTime)) / 1000)}秒后再试`, + icon: 'none', + duration: 1500 + }); + return; + } + + if (this.data.isLoading || this.data.isLoadingMore) return; + + if (this.data.hasMore && !this.data.isLoading && this.data.xuanzhongLeixingId) { + this.setData({ page: this.data.page + 1 }); + this.loadDingdanList(false); + } + }, + + onPullDownRefresh() { + const now = Date.now(); + const lastTime = this.data.lastRefreshTime; + const cooldown = this.data.refreshCooldown; + + if (now - lastTime < cooldown) { + this.setData({ scrollViewRefreshing: false }); + wx.showToast({ + title: `操作太快,请${Math.ceil((cooldown - (now - lastTime)) / 1000)}秒后再试`, + icon: 'none', + duration: 1500 + }); + return; + } + + if (this.data.isLoading) { + this.setData({ scrollViewRefreshing: false }); + return; + } + + this.setData({ + scrollViewRefreshing: true, + lastRefreshTime: now, + page: 1, + hasMore: true + }); + + if (this.data.xuanzhongLeixingId) { + this.loadDingdanList(true); + } else { + this.setData({ scrollViewRefreshing: false }); + } + } +}); \ No newline at end of file diff --git a/pages/jiedan/jiedan.json b/pages/jiedan/jiedan.json new file mode 100644 index 0000000..eee9b81 --- /dev/null +++ b/pages/jiedan/jiedan.json @@ -0,0 +1,12 @@ +{ + "navigationBarTitleText": "抢单大厅", + "navigationBarBackgroundColor": "#0b0f1a", + "navigationBarTextStyle": "white", + "enablePullDownRefresh": false, + "backgroundTextStyle": "dark", + "backgroundColor": "#0b0f1a", + "usingComponents": { + "chenghao-tag": "/components/chenghao-tag/chenghao-tag", + "global-notification": "/components/global-notification/global-notification" + } + } \ No newline at end of file diff --git a/pages/jiedan/jiedan.wxml b/pages/jiedan/jiedan.wxml new file mode 100644 index 0000000..ce757cf --- /dev/null +++ b/pages/jiedan/jiedan.wxml @@ -0,0 +1,220 @@ + + + + + + + + + + + {{item.jieshao}} + + + + + + + + + + + + 全部 + + + + + + + + + + + + + + + + + ⚡️ 正在刷新... + ▼ 下拉刷新订单 + + + + + + 请先选择上方订单类型 + + + + 当前类型暂无待抢订单 + + + + + + + {{item.creat_time}} + + ⚙️ 平台订单 + + + + + 指定 + + {{item.zhiding_nicheng || '指定打手'}} + UID: {{item.zhiding_uid || '未知'}} + + + + + + + + + + 介绍: + {{item.jieshao || '暂无介绍'}} + [点击查看] + + + + + 备注: + {{item.beizhu}} + [点击查看] + + + + + 需求标签: + + + + + + + + 💰 + 分佣 + + + {{item.dashou_fencheng || 0}} + + + + 未开通会员 + + + + ⚡ 立即抢单 + + + + + + + + + + + {{item.creat_time}} + + 🏪 商家发单 + + + + + 指定 + + {{item.zhiding_nicheng || '指定打手'}} + UID: {{item.zhiding_uid || '未知'}} + + + + + + + + + {{item.sjnicheng || '未知商家'}} + + + + + + + + 订单介绍: + {{item.jieshao || '暂无介绍'}} + [点击查看] + + + + 商家备注: + {{item.beizhu}} + [点击查看] + + + + + 需求标签: + + + + + + + + 💰 + 分佣 + + + {{item.dashou_fencheng || 0}} + + + + 未开通会员 + + + + ⚡ 立即抢单 + + + + + + + + + ⚙️ 加载更多订单中... + —— 没有更多订单了 —— + + + + + + ⚡ 订单加载中... + + + + + + + \ No newline at end of file diff --git a/pages/jiedan/jiedan.wxss b/pages/jiedan/jiedan.wxss new file mode 100644 index 0000000..52ed1a3 --- /dev/null +++ b/pages/jiedan/jiedan.wxss @@ -0,0 +1,762 @@ +/* pages/qiangdan/qiangdan.wxss - 高级机甲风格重构版(对称优化) */ +.qiangdan-page { + height: 100vh; + display: flex; + flex-direction: column; + background: radial-gradient(ellipse at top, #1a1f30, #0b0f1a); + overflow: hidden; + position: relative; + } + + /* 背景动态纹理,增加层次感 */ + .qiangdan-page::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: + linear-gradient(45deg, rgba(0, 247, 255, 0.02) 0%, transparent 30%), + radial-gradient(circle at 30% 50%, rgba(255, 77, 109, 0.03) 0%, transparent 50%), + repeating-linear-gradient(45deg, rgba(255,255,255,0.01) 0px, rgba(255,255,255,0.01) 1px, transparent 1px, transparent 6px); + pointer-events: none; + z-index: 0; + } + + /* ====== 1. 商品类型区域 ====== */ + .leixing-quyu { + padding: 30rpx 0 20rpx; + background: rgba(18, 22, 38, 0.9); + backdrop-filter: blur(20rpx); + border-bottom: 1rpx solid rgba(255, 255, 255, 0.05); + position: relative; + z-index: 10; + flex-shrink: 0; + box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.5); + } + + .leixing-scroll { + width: 100%; + white-space: nowrap; + height: 180rpx; + } + + .leixing-container { + display: inline-flex; + padding: 0 30rpx; + gap: 20rpx; + } + + .leixing-item { + display: inline-flex; + flex-direction: column; + align-items: center; + width: 160rpx; + position: relative; + transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); + } + + .leixing-active { + transform: translateY(-8rpx); + } + + .guangyun-effect { + position: absolute; + top: -15rpx; + left: -15rpx; + right: -15rpx; + bottom: -15rpx; + background: radial-gradient(circle at center, rgba(0, 200, 255, 0.3) 0%, transparent 70%); + border-radius: 50rpx; + z-index: 0; + animation: guangyun-pulse 2s infinite ease-in-out; + } + + @keyframes guangyun-pulse { + 0%, 100% { opacity: 0.5; transform: scale(1); } + 50% { opacity: 1; transform: scale(1.1); } + } + + .leixing-tupian { + width: 120rpx; + height: 120rpx; + border-radius: 32rpx; + z-index: 1; + box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.6); + transition: all 0.3s ease; + border: 2rpx solid rgba(0, 200, 255, 0.4); + } + + .leixing-active .leixing-tupian { + box-shadow: 0 16rpx 40rpx rgba(0, 200, 255, 0.5); + border: 2rpx solid #00c8ff; + } + + .leixing-jieshao { + margin-top: 16rpx; + font-size: 24rpx; + color: #b0c0e0; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + text-align: center; + z-index: 1; + font-weight: 500; + } + + .leixing-active .leixing-jieshao { + color: #00c8ff; + text-shadow: 0 0 8px rgba(0,200,255,0.5); + } + + /* ====== 2. 分割线 ====== */ + .fenge-xian { + height: 2rpx; + background: linear-gradient(90deg, transparent, #00c8ff, #ff4d6d, transparent); + flex-shrink: 0; + margin: 0 30rpx; + opacity: 0.5; + } + + /* ====== 3. 订单列表容器 ====== */ + .dingdan-container { + flex: 1; + overflow: hidden; + position: relative; + z-index: 1; + padding: 0 20rpx; /* 统一左右内边距 */ + } + + .dingdan-scroll-view { + height: 100%; + -webkit-overflow-scrolling: touch; + } + + .refresher-container { + height: 100rpx; + display: flex; + align-items: center; + justify-content: center; + } + + .refreshing-text, + .pull-down-text { + font-size: 26rpx; + color: #6a9cff; + text-shadow: 0 0 8px rgba(0,200,255,0.3); + } + + /* ====== 4. 空状态提示 ====== */ + .empty-tip { + padding: 160rpx 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + + .tip-icon { + width: 180rpx; + height: 180rpx; + opacity: 0.5; + margin-bottom: 30rpx; + filter: drop-shadow(0 0 20px rgba(0,200,255,0.3)); + } + + .tip-text { + font-size: 28rpx; + color: #8a9ac0; + letter-spacing: 2rpx; + } + + /* ====== 5. 订单卡片通用样式(移除左右边框,改用顶部标识)====== */ + .dingdan-card { + margin: 20rpx 0; /* 左右由容器控制 */ + border-radius: 36rpx; + padding: 28rpx 24rpx; + position: relative; + overflow: hidden; + box-shadow: 0 20rpx 40rpx rgba(0, 0, 0, 0.6); + transition: all 0.3s ease; + border: 1rpx solid rgba(255, 255, 255, 0.03); + backdrop-filter: blur(30rpx); + } + + .dingdan-card:active { + transform: scale(0.998); + box-shadow: 0 10rpx 20rpx rgba(0, 0, 0, 0.8); + } + + /* 平台订单 - 冷色调,顶部光效 */ + .pingtai-card { + background: linear-gradient(145deg, rgba(18, 28, 48, 0.95), rgba(8, 18, 35, 0.98)); + } + + .pingtai-card::before { + content: ''; + position: absolute; + top: 0; + left: 10%; + width: 80%; + height: 4rpx; + background: linear-gradient(90deg, transparent, #00c8ff, transparent); + border-radius: 2rpx; + opacity: 0.7; + } + + /* 商家订单 - 暖色调,顶部光效 */ + .shangjia-card { + background: linear-gradient(145deg, rgba(38, 18, 48, 0.95), rgba(25, 8, 35, 0.98)); + } + + .shangjia-card::before { + content: ''; + position: absolute; + top: 0; + left: 10%; + width: 80%; + height: 4rpx; + background: linear-gradient(90deg, transparent, #ff4d6d, transparent); + border-radius: 2rpx; + opacity: 0.7; + } + + /* 卡片顶部 */ + .card-top { + display: flex; + align-items: center; + margin-bottom: 20rpx; + padding-bottom: 16rpx; + border-bottom: 1rpx dashed rgba(255, 255, 255, 0.08); + } + + .creat-time { + font-size: 26rpx; + color: #c0d0ff; + font-weight: 500; + text-shadow: 0 0 5px rgba(0,200,255,0.3); + } + + .top-line { + flex: 1; + height: 1rpx; + background: linear-gradient(to right, transparent, rgba(255, 255, 255, 0.2), transparent); + margin: 0 20rpx; + } + + .pingtai-tag { + font-size: 24rpx; + color: #aac8ff; + padding: 6rpx 18rpx; + background: rgba(0,200,255,0.08); + border-radius: 40rpx; + border: 1rpx solid rgba(0,200,255,0.3); + box-shadow: 0 0 10rpx rgba(0,200,255,0.2); + } + + /* ====== 6. 指定单标识行(新设计)====== */ + .zhiding-row { + display: flex; + align-items: center; + margin-bottom: 24rpx; + padding: 12rpx 20rpx; + background: rgba(255, 77, 109, 0.1); + border-radius: 60rpx; + border: 1rpx solid rgba(255, 77, 109, 0.3); + box-shadow: 0 0 20rpx rgba(255, 77, 109, 0.2); + backdrop-filter: blur(10rpx); + gap: 12rpx; + } + + .zhiding-icon { + font-size: 28rpx; + color: #ff4d6d; + font-weight: 700; + background: rgba(255, 77, 109, 0.2); + padding: 4rpx 16rpx; + border-radius: 40rpx; + text-shadow: 0 0 8px #ff4d6d; + letter-spacing: 2rpx; + } + + .zhiding-avatar { + width: 48rpx; + height: 48rpx; + border-radius: 50%; + border: 2rpx solid #ff4d6d; + box-shadow: 0 0 15rpx #ff4d6d; + object-fit: cover; + } + + .zhiding-nicheng { + font-size: 26rpx; + color: #ffc0d0; + font-weight: 500; + max-width: 160rpx; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .zhiding-uid { + font-size: 24rpx; + color: #ff9aab; + flex: 1; + text-align: right; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-family: monospace; + } + + /* ====== 7. 平台订单内容区 ====== */ + .card-content { + display: flex; + margin-bottom: 24rpx; + align-items: center; + } + + .neirong-tupian { + width: 140rpx; + height: 140rpx; + border-radius: 50%; + margin-right: 24rpx; + flex-shrink: 0; + box-shadow: 0 10rpx 30rpx rgba(0,200,255,0.3); + border: 2rpx solid #00c8ff; + object-fit: cover; + } + + .jieshao-box { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + min-height: 140rpx; + padding: 8rpx 0; + } + + .jieshao-label { + font-size: 26rpx; + color: #d0e0ff; + font-weight: 600; + margin-bottom: 6rpx; + text-shadow: 0 0 5px rgba(0,200,255,0.3); + } + + .jieshao-text { + font-size: 26rpx; + color: #b0c8f0; + line-height: 1.4; + word-break: break-word; + } + + .jieshao-ellipsis .jieshao-text { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + overflow: hidden; + text-overflow: ellipsis; + } + + /* ====== 8. 商家订单特有样式 ====== */ + .shangjia-info { + display: flex; + align-items: center; + margin-bottom: 20rpx; + } + + .shangjia-touxiang { + width: 90rpx; + height: 90rpx; + border-radius: 50%; + margin-right: 20rpx; + border: 2rpx solid #ff4d6d; + box-shadow: 0 6rpx 18rpx rgba(255, 77, 109, 0.5); + object-fit: cover; + } + + .shangjia-nicheng { + font-size: 30rpx; + color: #ffb3c6; + font-weight: 600; + text-shadow: 0 0 8px rgba(255, 77, 109, 0.4); + } + + .shangjia-jieshao, + .shangjia-beizhu { + margin-bottom: 16rpx; + padding: 14rpx 18rpx; + background: rgba(255, 255, 255, 0.02); + border-radius: 24rpx; + border: 1rpx solid rgba(255, 77, 109, 0.2); + } + + .shangjia-jieshao .jieshao-label, + .shangjia-beizhu .beizhu-label { + display: block; + margin-bottom: 6rpx; + font-size: 24rpx; + color: #ff9aab; + } + + .shangjia-jieshao .jieshao-text, + .shangjia-beizhu .beizhu-text { + font-size: 24rpx; + color: #e0c8ff; + line-height: 1.4; + } + + .shangjia-jieshao.jieshao-ellipsis .jieshao-text { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; + } + + .shangjia-beizhu.beizhu-ellipsis .beizhu-text { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; + } + + /* ====== 9. 通用备注区 ====== */ + .beizhu-box { + margin-bottom: 20rpx; + padding: 14rpx 18rpx; + background: rgba(255, 255, 255, 0.02); + border-radius: 24rpx; + border: 1rpx solid rgba(0,200,255,0.2); + } + + .beizhu-label { + font-size: 24rpx; + color: #80d0ff; + font-weight: 600; + margin-bottom: 6rpx; + display: block; + } + + .beizhu-text { + font-size: 24rpx; + color: #b0c8f0; + line-height: 1.4; + } + + .beizhu-ellipsis .beizhu-text { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; + } + + .view-more { + display: block; + font-size: 22rpx; + color: #00c8ff; + margin-top: 6rpx; + text-align: right; + opacity: 0.8; + text-shadow: 0 0 5px #00c8ff; + } + + /* ====== 10. 卡片底部:分佣 + 按钮 ====== */ + .card-bottom { + display: flex; + align-items: center; + justify-content: space-between; + padding-top: 20rpx; + border-top: 1rpx solid rgba(255, 255, 255, 0.06); + } + + /* 分佣容器(图标占位,用户可自定义) */ + .fenyong-box { + display: flex; + align-items: baseline; + background: rgba(0, 0, 0, 0.3); + padding: 8rpx 20rpx; + border-radius: 60rpx; + border: 1rpx solid rgba(255, 215, 0, 0.3); + } + + /* 分佣图标占位符,用户可替换为自己图标 */ + .fenyong-icon { + font-size: 32rpx; + margin-right: 10rpx; + filter: drop-shadow(0 0 8px gold); + } + + .fenyong-text { + font-size: 24rpx; + color: #c0b0a0; + margin-right: 8rpx; + } + + .fenyong-price { + font-size: 44rpx; + color: #ffd700; + font-weight: 800; + margin-right: 4rpx; + text-shadow: 0 0 20px rgba(255, 215, 0, 0.8); + } + + .fenyong-unit { + font-size: 24rpx; + color: #a0a0b0; + } + + /* ====== 11. 机甲风格抢单按钮(对称优化)====== */ + .qiangdan-btn { + position: relative; + padding: 16rpx 44rpx; + border: none; + overflow: hidden; + transition: all 0.2s ease; + cursor: pointer; + z-index: 1; + /* 多边形形状,保证上下对称 */ + clip-path: polygon(10% 0%, 90% 0%, 100% 25%, 100% 75%, 90% 100%, 10% 100%, 0% 75%, 0% 25%); + } + + /* 按钮统一基础样式,无上下不对称阴影 */ + .mecha-btn { + background: linear-gradient(145deg, #2a3050, #181e38); + box-shadow: + 0 8rpx 0 #0a0a1a, + 0 12rpx 28rpx rgba(0, 0, 0, 0.7), + inset 0 2rpx 6rpx rgba(255, 255, 255, 0.2), + inset 0 -2rpx 4rpx rgba(0, 0, 0, 0.3); + border: 1rpx solid rgba(255, 255, 255, 0.1); + } + + /* 平台按钮冷光 */ + .pingtai-btn.mecha-btn { + background: linear-gradient(145deg, #1a2a48, #0e1a30); + box-shadow: + 0 8rpx 0 #0a1525, + 0 12rpx 28rpx rgba(0, 0, 0, 0.7), + inset 0 2rpx 6rpx #00c8ff, + inset 0 -2rpx 4rpx rgba(0, 0, 0, 0.3); + } + + /* 商家按钮暖光 */ + .shangjia-btn.mecha-btn { + background: linear-gradient(145deg, #3a2048, #251030); + box-shadow: + 0 8rpx 0 #2a0a1a, + 0 12rpx 28rpx rgba(0, 0, 0, 0.7), + inset 0 2rpx 6rpx #ff4d6d, + inset 0 -2rpx 4rpx rgba(0, 0, 0, 0.3); + } + + .btn-text { + font-size: 32rpx; + color: #fff; + font-weight: 700; + letter-spacing: 3rpx; + position: relative; + z-index: 3; + text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.7), 0 0 15rpx currentColor; + } + + /* 扫光效果 */ + .btn-shine { + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: linear-gradient( + to right, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.25) 50%, + rgba(255, 255, 255, 0) 100% + ); + transform: rotate(30deg); + animation: btn-shimmer 2.8s infinite linear; + z-index: 2; + } + + /* 内发光,只在悬停/按下时出现,保证对称 */ + .btn-glow { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.5), transparent 70%); + opacity: 0; + transition: opacity 0.3s; + z-index: 1; + } + + .mecha-btn:hover .btn-glow, + .mecha-btn:active .btn-glow { + opacity: 0.3; + } + + /* 按下效果,保持对称 */ + .mecha-btn:active { + transform: translateY(4rpx) scale(0.98); + box-shadow: + 0 4rpx 0 #0a0a1a, + 0 8rpx 20rpx rgba(0, 0, 0, 0.7), + inset 0 2rpx 6rpx rgba(255, 255, 255, 0.2), + inset 0 -2rpx 4rpx rgba(0, 0, 0, 0.3); + } + + /* 机械纹理,保持视觉丰富但不过分 */ + .mecha-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-image: + repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.02) 0px, rgba(255, 255, 255, 0.02) 2px, transparent 2px, transparent 8px); + pointer-events: none; + z-index: 1; + opacity: 0.5; + } + + @keyframes btn-shimmer { + 0% { transform: translateX(-100%) rotate(30deg); } + 100% { transform: translateX(100%) rotate(30deg); } + } + + /* ====== 12. 加载状态 ====== */ + .loading-more, + .no-more { + padding: 40rpx 0; + text-align: center; + } + + .loading-text { + font-size: 26rpx; + color: #6a9cff; + text-shadow: 0 0 10px rgba(0,200,255,0.5); + animation: pulse 1.5s infinite; + } + + .no-more-text { + font-size: 26rpx; + color: #4a5a8a; + letter-spacing: 3rpx; + } + + @keyframes pulse { + 0%, 100% { opacity: 0.7; } + 50% { opacity: 1; } + } + + .loading-mask { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(5, 8, 18, 0.95); + backdrop-filter: blur(30rpx); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + z-index: 9999; + } + + .loading-spinner { + width: 90rpx; + height: 90rpx; + border: 6rpx solid rgba(0,200,255,0.2); + border-top: 6rpx solid #00c8ff; + border-right: 6rpx solid #ff4d6d; + border-radius: 50%; + animation: spin 1.2s cubic-bezier(0.68, -0.55, 0.27, 1.55) infinite; + margin-bottom: 30rpx; + box-shadow: 0 0 50rpx #00c8ff; + } + + @keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + + .loading-mask-text { + font-size: 30rpx; + color: #c0d0ff; + text-shadow: 0 0 15px rgba(0,200,255,0.7); + letter-spacing: 4rpx; + } + + + + + +/* 🆕 标签筛选区 */ +.biaoqian-quyu { + padding: 12rpx 20rpx; + background: rgba(18, 22, 38, 0.7); + backdrop-filter: blur(10rpx); + } + .biaoqian-scroll { + white-space: nowrap; + height: 80rpx; + } + .biaoqian-container { + display: inline-flex; + gap: 16rpx; + padding-right: 20rpx; + } + .biaoqian-item { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0 12rpx; + border-radius: 20rpx; + background: rgba(255,255,255,0.05); + transition: all 0.2s; + } + .biaoqian-active { + background: rgba(0,200,255,0.2); + border: 1rpx solid #00c8ff; + } + .quanbu-text { + font-size: 26rpx; + color: #ccc; + padding: 0 12rpx; + } + + /* 订单卡片内标签滚动 */ + .xuqiu-biaoqian-area { + display: flex; + align-items: center; + margin-top: 16rpx; + padding: 8rpx 0; + } + .xuqiu-label { + font-size: 24rpx; + color: #80d0ff; + margin-right: 12rpx; + flex-shrink: 0; + } + .xuqiu-biaoqian-scroll { + flex: 1; + white-space: nowrap; + } + .biaoqian-inline-scroll { + flex: 1; + white-space: nowrap; + margin-left: 10rpx; + } + /* 调整指定打手行内的标签滚动 */ + .zhiding-row .biaoqian-inline-scroll { + margin-left: auto; + max-width: 50%; + } + diff --git a/pages/jiedanchi/jiedanchi.js b/pages/jiedanchi/jiedanchi.js new file mode 100644 index 0000000..95089c4 --- /dev/null +++ b/pages/jiedanchi/jiedanchi.js @@ -0,0 +1,466 @@ +// pages/jiedanchi/jiedanchi.js +const app = getApp(); +import request from '../../utils/request.js'; +import PopupService from '../../services/popupService.js'; + +Page({ + data: { + // 1. 商品类型相关 + shangpinleixing: [], // 商品类型列表 {id, jieshao, tupian_url} + xuanzhongLeixingId: null, // 当前选中的类型ID + + // 2. 订单列表与分页相关 + dingdanList: [], // 当前展示的订单列表 + page: 1, + pageSize: 10, + hasMore: true, + isLoading: false, + + // 3. 全局状态字段 + dashoustatus: null, + zhuanghaoStatus: null, // 注意变量名:zhuanghaoStatus + dashouzhuangtai: null, + uid: null, + huiyuanList: [], // clumber 列表 + yajin: 0, + jifen: 0, + + // 4. 刷新控制字段 + scrollViewRefreshing: false, + lastRefreshTime: 0, + refreshCooldown: 2000, + isLoadingMore: false, + + // 5. 切换类型冷却时间控制 + lastSwitchTime: 0, + switchCooldown: 1500, + isSwitching: false, + + // 6. 全局OSS地址 + ossImageUrl: app.globalData.ossImageUrl || '', + + // 7. 授权状态控制 + showUnauthorized: false, + unauthorizedMsg: '您尚未开启此功能', + }, + + async onLoad(options) { + this.loadGlobalStatus(); + this.checkAuthorization(); + }, + // 🆕 页面隐藏时清理弹窗视图 + onHide: function () { + const popupComp = this.selectComponent('#popupNotice'); + if (popupComp && popupComp.cleanup) { + popupComp.cleanup(); + } +}, + + onShow() { + this.registerNotificationComponent(); + this.loadGlobalStatus(); + this.checkAuthorization(); + }, + + registerNotificationComponent() { + const notificationComp = this.selectComponent('#global-notification'); + if (notificationComp && notificationComp.showNotification) { + app.globalData.globalNotification = { + show: (data) => notificationComp.showNotification(data), + hide: () => notificationComp.hideNotification() + }; + } + }, + + loadGlobalStatus() { + const globalData = app.globalData; + // 从缓存读取,确保与抢单时使用的数据源一致 + const dashoustatusCache = wx.getStorageSync('dashoustatus'); + const dashoustatus = dashoustatusCache !== '' ? dashoustatusCache : null; + + // 从 globalData 或缓存获取 zhuanghaoStatus(注意字段名可能有差异) + let zhuanghaoStatus = globalData.zhanghaoStatus; + if (zhuanghaoStatus === undefined || zhuanghaoStatus === null) { + zhuanghaoStatus = wx.getStorageSync('zhanghaoStatus'); + } + + this.setData({ + dashoustatus: dashoustatus, + zhuanghaoStatus: zhuanghaoStatus, + dashouzhuangtai: globalData.dashouzhuangtai, + uid: wx.getStorageSync('uid'), + huiyuanList: globalData.clumber || [], + yajin: globalData.yajin || 0, + jifen: globalData.jinfen || 0 + }); + + // 打印当前值,方便调试 + //console.log('loadGlobalStatus -> dashoustatus:', dashoustatus, 'zhuanghaoStatus:', zhuanghaoStatus); + }, + + checkAuthorization() { + const { dashoustatus, zhuanghaoStatus } = this.data; + //console.log('checkAuthorization -> dashoustatus:', dashoustatus, 'zhuanghaoStatus:', zhuanghaoStatus); + + // 完全复制抢单时的判断逻辑 + const isDashouValid = (dashoustatus && dashoustatus == 1) || false; // 存在且等于1 + const isZhuanghaoValid = (zhuanghaoStatus == 1); // 宽松比较,null/undefined/0 均视为 false + + const isAuthorized = isDashouValid && isZhuanghaoValid; + + if (!isAuthorized) { + this.setData({ showUnauthorized: true }); + let msg = ''; + if (!isDashouValid) msg = '您尚未开启身份'; + //else if (!isZhuanghaoValid) msg = '您的账号状态异常'; + this.setData({ unauthorizedMsg: msg || '无法使用抢单功能' }); + } else { + this.setData({ showUnauthorized: false }); + if (!this.data.shangpinleixing.length) { + this.loadShangpinLeixing(); + } else if (this.data.xuanzhongLeixingId) { + this.loadDingdanList(true); + } + PopupService.checkAndShow(this, 'jiedanchi'); + } + }, + + async loadShangpinLeixing() { + wx.showLoading({ title: '加载商品类型...' }); + try { + const res = await request({ + url: '/dingdan/dsqdhqddlx', + method: 'POST', + header: { 'content-type': 'application/json' } + }); + + if (res.statusCode === 200 && res.data) { + const data = res.data; + let list = []; + if (data.code === 200 || data.code === 0) { + if (data.data && Array.isArray(data.data.list)) { + list = data.data.list; + } else if (data.data && Array.isArray(data.data)) { + list = data.data; + } else if (Array.isArray(data.list)) { + list = data.list; + } else if (Array.isArray(data)) { + list = data; + } + } + const processedList = this.processTupianUrl(list); + this.setData({ + shangpinleixing: processedList, + xuanzhongLeixingId: processedList[0]?.id || null + }); + if (this.data.xuanzhongLeixingId) { + this.loadDingdanList(true); + } + } else { + wx.showToast({ title: res.data?.msg || '加载商品类型失败', icon: 'none', duration: 2000 }); + } + } catch (error) { + console.error('加载商品类型失败:', error); + wx.showToast({ title: '网络错误,加载失败', icon: 'none', duration: 2000 }); + } finally { + wx.hideLoading(); + } + }, + + processTupianUrl(list) { + const ossUrl = app.globalData.ossImageUrl || ''; + return list.map(item => ({ + ...item, + full_tupian_url: item.tupian_url && !item.tupian_url.startsWith('http') ? ossUrl + item.tupian_url : (item.tupian_url || '/images/default-type.png') + })); + }, + + async selectLeixing(e) { + const now = Date.now(); + if (now - this.data.lastSwitchTime < this.data.switchCooldown) { + wx.showToast({ + title: `操作太快,请${Math.ceil((this.data.switchCooldown - (now - this.data.lastSwitchTime)) / 1000)}秒后再试`, + icon: 'none', + duration: 1500 + }); + return; + } + if (this.data.isSwitching) return; + const leixingId = e.currentTarget.dataset.id; + if (leixingId === this.data.xuanzhongLeixingId) return; + this.setData({ isSwitching: true, lastSwitchTime: now }); + wx.showLoading({ title: '切换中...' }); + try { + this.setData({ + xuanzhongLeixingId: leixingId, + dingdanList: [], + page: 1, + hasMore: true + }); + await this.loadDingdanList(true); + } catch (error) { + console.error('切换类型失败:', error); + } finally { + this.setData({ isSwitching: false }); + wx.hideLoading(); + } + }, + + async loadDingdanList(isRefresh = false) { + if (this.data.isLoading || !this.data.xuanzhongLeixingId) return; + + const loadPage = isRefresh ? 1 : this.data.page; + if (!isRefresh && !this.data.hasMore) return; + + this.setData({ + isLoading: true, + isLoadingMore: !isRefresh + }); + + try { + const res = await request({ + url: '/dingdan/ddhq', + method: 'POST', + data: { + leixing_id: this.data.xuanzhongLeixingId, + page: loadPage, + page_size: this.data.pageSize + } + }); + + this.setData({ + isLoading: false, + isLoadingMore: false, + scrollViewRefreshing: false + }); + + if (res.data.code === 200 || res.data.code === 0) { + const newList = res.data.data.list || []; + const hasMore = res.data.data.has_more || false; + + const processedList = newList.map(item => this.processDingdanItem(item)); + + const updatedList = isRefresh ? processedList : [...this.data.dingdanList, ...processedList]; + + updatedList.sort((a, b) => (b.creat_time || '').localeCompare(a.creat_time || '')); + + this.setData({ + dingdanList: updatedList, + page: loadPage, + hasMore: hasMore + }); + + if (isRefresh) { + this.setData({ lastRefreshTime: Date.now() }); + } + } else { + wx.showToast({ title: res.data.msg || '加载失败', icon: 'none' }); + } + } catch (error) { + console.error('加载订单失败:', error); + this.setData({ + isLoading: false, + isLoadingMore: false, + scrollViewRefreshing: false + }); + wx.showToast({ title: '网络请求失败', icon: 'none' }); + } + }, + + processDingdanItem(item) { + const ossUrl = app.globalData.ossImageUrl || ''; + let fullTupianUrl = ''; + if (item.tupian) { + fullTupianUrl = !item.tupian.startsWith('http') ? ossUrl + item.tupian : item.tupian; + } else { + const leixing = this.data.shangpinleixing.find(l => l.id === item.leixing_id); + fullTupianUrl = leixing ? leixing.full_tupian_url : '/images/default-order.png'; + } + + const isZhiding = item.zhuangtai === 7; + + let zhidingAvatar = ''; + if (item.zhiding_avatar) { + zhidingAvatar = !item.zhiding_avatar.startsWith('http') ? ossUrl + item.zhiding_avatar : item.zhiding_avatar; + } + const zhidingNicheng = item.zhiding_nicheng || ''; + + return { + ...item, + full_tupian_url: fullTupianUrl, + isZhiding: isZhiding, + isPingtai: item.pingtai == 1, + isShangjia: item.pingtai == 2, + zhiding_avatar_full: zhidingAvatar, + zhiding_nicheng: zhidingNicheng + }; + }, + + goToChongzhiPage(failureType, huiyuanId = null, requiredYajin = 0) { + let url = '/pages/dashouchongzhi/dashouchongzhi'; + let params = {}; + switch (failureType) { + case 'huiyuan': + params = { needScroll: '0', scrollTo: 'member' }; + break; + case 'yajin': + params = { needScroll: '1', scrollTo: 'bottom', requiredYajin: requiredYajin }; + break; + case 'jifen': + params = { needScroll: '1', scrollTo: 'bottom' }; + break; + } + if (huiyuanId) params.huiyuanId = huiyuanId; + const queryString = Object.keys(params).map(key => `${key}=${encodeURIComponent(params[key])}`).join('&'); + const fullUrl = `${url}?${queryString}`; + wx.navigateTo({ + url: fullUrl, + fail: (err) => { + console.error('跳转失败:', err); + wx.showToast({ title: '跳转失败,请稍后重试', icon: 'none' }); + } + }); + }, + + async onQiangdanTap(e) { + const dingdanItem = e.currentTarget.dataset.item; + if (!dingdanItem) return; + + if (!this.data.dashoustatus || this.data.dashoustatus != 1) { + wx.showToast({ title: '请先开启打手身份', icon: 'none' }); + return; + } + if (this.data.zhuanghaoStatus != 1) { + wx.showToast({ title: '账号异常,无法接单', icon: 'none' }); + return; + } + if (this.data.dashouzhuangtai != 1) { + wx.showToast({ title: '您有订单正在接待中', icon: 'none' }); + return; + } + if (dingdanItem.isZhiding) { + if (!this.data.uid || this.data.uid != dingdanItem.zhiding_uid) { + wx.showToast({ title: '此订单为指定单', icon: 'none' }); + return; + } + } + if (dingdanItem.yaoqiuleixing == 1) { + const hasHuiyuan = this.data.huiyuanList.some(h => h.huiyuanid == dingdanItem.huiyuan_id); + if (!hasHuiyuan) { + wx.showToast({ title: '未开通对应会员,无法抢单', icon: 'none', duration: 2000 }); + setTimeout(() => this.goToChongzhiPage('huiyuan', dingdanItem.huiyuan_id), 500); + return; + } + } + if (dingdanItem.yaoqiuleixing == 2) { + if (this.data.yajin < dingdanItem.yajin) { + wx.showToast({ title: `押金不足,需${dingdanItem.yajin}`, icon: 'none', duration: 2000 }); + setTimeout(() => this.goToChongzhiPage('yajin', null, dingdanItem.yajin), 500); + return; + } + } + if (this.data.jifen < 5) { + wx.showToast({ title: '积分不足,至少需要5积分', icon: 'none', duration: 2000 }); + setTimeout(() => this.goToChongzhiPage('jifen'), 500); + return; + } + + const that = this; + wx.showModal({ + title: '确认抢单', + content: `确定要抢此订单吗?${dingdanItem.isZhiding ? '(指定订单)' : ''}`, + success: async function (res) { + if (res.confirm) { + wx.showLoading({ title: '抢单中...', mask: true }); + try { + const qiangdanRes = await request({ + url: '/dingdan/qiangdan', + method: 'POST', + data: { dingdan_id: dingdanItem.dingdan_id } + }); + wx.hideLoading(); + if (qiangdanRes.data.code === 200 || qiangdanRes.data.code === 0) { + that.showQiangdanSuccessEffect(); + if (app.globalData) { + app.globalData.dashouzhuangtai = 0; + that.setData({ dashouzhuangtai: 0 }); + } + const newList = that.data.dingdanList.filter(item => item.dingdan_id !== dingdanItem.dingdan_id); + that.setData({ dingdanList: newList }); + } else { + wx.showToast({ title: qiangdanRes.data.msg || '抢单失败', icon: 'none' }); + } + } catch (error) { + wx.hideLoading(); + console.error('抢单请求失败:', error); + wx.showToast({ title: '网络错误,抢单失败', icon: 'none' }); + } + } + } + }); + }, + + showQiangdanSuccessEffect() { + wx.showToast({ title: '抢单成功!', icon: 'success', duration: 2000 }); + }, + + onViewDetail(e) { + const { type, content } = e.currentTarget.dataset; + wx.showModal({ + title: type === 'jieshao' ? '订单介绍' : '订单备注', + content: content, + showCancel: false, + confirmText: '知道了' + }); + }, + + onReachBottom() { + const now = Date.now(); + if (now - this.data.lastRefreshTime < this.data.refreshCooldown) { + wx.showToast({ + title: `操作太快,请${Math.ceil((this.data.refreshCooldown - (now - this.data.lastRefreshTime)) / 1000)}秒后再试`, + icon: 'none', + duration: 1500 + }); + return; + } + if (this.data.isLoading || this.data.isLoadingMore) return; + if (this.data.hasMore && !this.data.isLoading && this.data.xuanzhongLeixingId) { + this.setData({ page: this.data.page + 1 }); + this.loadDingdanList(false); + } + }, + + onPullDownRefresh() { + const now = Date.now(); + if (now - this.data.lastRefreshTime < this.data.refreshCooldown) { + this.setData({ scrollViewRefreshing: false }); + wx.showToast({ + title: `操作太快,请${Math.ceil((this.data.refreshCooldown - (now - this.data.lastRefreshTime)) / 1000)}秒后再试`, + icon: 'none', + duration: 1500 + }); + return; + } + if (this.data.isLoading) { + this.setData({ scrollViewRefreshing: false }); + return; + } + this.setData({ + scrollViewRefreshing: true, + lastRefreshTime: now + }); + if (this.data.xuanzhongLeixingId) { + this.setData({ page: 1, hasMore: true }); + this.loadDingdanList(true); + } else { + this.setData({ scrollViewRefreshing: false }); + } + }, + + goToDashouRegister() { + wx.navigateTo({ + url: '/pages/dashouduan/dashouduan' + }); + } +}); \ No newline at end of file diff --git a/pages/jiedanchi/jiedanchi.json b/pages/jiedanchi/jiedanchi.json new file mode 100644 index 0000000..ea51a5d --- /dev/null +++ b/pages/jiedanchi/jiedanchi.json @@ -0,0 +1,9 @@ +{ + "navigationBarTitleText": "接单池", + "enablePullDownRefresh": false, + "backgroundTextStyle": "dark", + "backgroundColor": "#f5f7fa", + "usingComponents": { + "global-notification": "/components/global-notification/global-notification" + } + } \ No newline at end of file diff --git a/pages/jiedanchi/jiedanchi.wxml b/pages/jiedanchi/jiedanchi.wxml new file mode 100644 index 0000000..027df01 --- /dev/null +++ b/pages/jiedanchi/jiedanchi.wxml @@ -0,0 +1,263 @@ + + + + + + + + 🔒 + 功能未开启 + {{unauthorizedMsg}} + 请先注册为服务者,即可使用抢单功能 + + 立即注册 + + + + + + + + + + + + + + + + + + {{item.jieshao}} + + + + + + + + + + + + + + + 正在刷新... + 下拉刷新订单 + + + + + + + + 请先选择上方订单类型 + + + + + 当前类型暂无待抢订单 + + + + + + + + + {{item.creat_time}} + + 平台订单 + + + + + 指定 + + {{item.zhiding_nicheng || '指定打手'}} + UID: {{item.zhiding_uid || '未知'}} + + + + + + + 介绍: + {{item.jieshao || '暂无介绍'}} + [点击查看] + + + + + + 备注: + {{item.beizhu}} + [点击查看] + + + + + + 💰 + 分佣 + {{item.dashou_fencheng || 0}} + + + + 立即抢单 + + + + + + + + + + {{item.creat_time}} + + 商家发单 + + + + + 指定 + + {{item.zhiding_nicheng || '指定打手'}} + UID: {{item.zhiding_uid || '未知'}} + + + + + + {{item.sjnicheng || '未知商家'}} + + + + + 订单介绍: + {{item.jieshao || '暂无介绍'}} + [点击查看] + + + + + 商家备注: + {{item.beizhu}} + [点击查看] + + + + + + 💰 + 分佣 + {{item.dashou_fencheng || 0}} + + + + 立即抢单 + + + + + + + + + 加载更多订单中... + + + 没有更多订单了 + + + + + + + + 订单加载中... + + + + + + + \ No newline at end of file diff --git a/pages/jiedanchi/jiedanchi.wxss b/pages/jiedanchi/jiedanchi.wxss new file mode 100644 index 0000000..5784b03 --- /dev/null +++ b/pages/jiedanchi/jiedanchi.wxss @@ -0,0 +1,655 @@ +/* pages/qiangdan/qiangdan.wxss */ +.qiangdan-page { + height: 100vh; + display: flex; + flex-direction: column; + background-color: #f5f7fa; + overflow: hidden; + position: relative; + } + + /* ====== 授权提示页(机甲风格)====== */ + .unauthorized-container { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + background: radial-gradient(ellipse at top, #0b0f1a, #03050a); + z-index: 100; + } + + .unauthorized-card { + width: 600rpx; + padding: 60rpx 40rpx; + background: linear-gradient(145deg, #1a1f30, #0f1422); + border-radius: 60rpx; + position: relative; + overflow: hidden; + border: 2rpx solid rgba(0, 247, 255, 0.3); + box-shadow: 0 30rpx 60rpx rgba(0, 0, 0, 0.8), 0 0 30rpx rgba(0, 247, 255, 0.2); + } + + .card-glow { + position: absolute; + top: -20%; + left: -20%; + width: 140%; + height: 140%; + background: radial-gradient(circle at 30% 30%, rgba(0, 247, 255, 0.2), transparent 60%); + animation: rotateGlow 10s linear infinite; + z-index: 0; + } + + @keyframes rotateGlow { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + + .card-content-unauth { + position: relative; + z-index: 2; + display: flex; + flex-direction: column; + align-items: center; + color: #ffffff; + } + + .icon-lock { + font-size: 100rpx; + margin-bottom: 30rpx; + background: rgba(255, 255, 255, 0.1); + width: 160rpx; + height: 160rpx; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + border: 4rpx solid #00f7ff; + box-shadow: 0 0 50rpx #00f7ff; + text-shadow: 0 0 20rpx #00f7ff; + } + + .title { + font-size: 48rpx; + font-weight: 700; + letter-spacing: 4rpx; + margin-bottom: 20rpx; + text-shadow: 0 0 15rpx #00f7ff; + background: linear-gradient(135deg, #ffffff, #aaddff); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } + + .message { + font-size: 32rpx; + color: #ffaa00; + margin-bottom: 16rpx; + font-weight: 600; + text-shadow: 0 0 10rpx #ffaa00; + } + + .description { + font-size: 28rpx; + color: #a0b0d0; + margin-bottom: 50rpx; + text-align: center; + } + + .btn-register { + position: relative; + padding: 24rpx 80rpx; + background: linear-gradient(145deg, #2a3050, #181e38); + border-radius: 60rpx; + overflow: hidden; + border: 2rpx solid #00f7ff; + box-shadow: 0 10rpx 30rpx rgba(0, 247, 255, 0.4); + transition: transform 0.2s; + } + + .btn-register:active { + transform: scale(0.96); + } + + .btn-text { + font-size: 32rpx; + color: #fff; + font-weight: 700; + letter-spacing: 4rpx; + position: relative; + z-index: 2; + text-shadow: 0 0 10rpx #00f7ff; + } + + .btn-shine { + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: linear-gradient( + to right, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.2) 50%, + rgba(255, 255, 255, 0) 100% + ); + transform: rotate(30deg); + animation: btn-shimmer 2.8s infinite linear; + z-index: 1; + } + + @keyframes btn-shimmer { + 0% { transform: translateX(-100%) rotate(30deg); } + 100% { transform: translateX(100%) rotate(30deg); } + } + + /* ====== 原有样式(保持不变,仅略作调整以兼容提示页)====== */ + .leixing-quyu { + padding: 30rpx 0 20rpx; + background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%); + border-bottom: 1rpx solid #eaeaea; + position: relative; + z-index: 10; + flex-shrink: 0; + } + + .leixing-scroll { + width: 100%; + white-space: nowrap; + height: 180rpx; + } + + .leixing-container { + display: inline-flex; + padding: 0 30rpx; + } + + .leixing-item { + display: inline-flex; + flex-direction: column; + align-items: center; + width: 160rpx; + margin-right: 24rpx; + position: relative; + transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); + } + + .leixing-active { + transform: translateY(-6rpx); + } + + .guangyun-effect { + position: absolute; + top: -12rpx; + left: -12rpx; + right: -12rpx; + bottom: -12rpx; + background: radial-gradient(circle at center, rgba(82, 196, 26, 0.15) 0%, transparent 70%); + border-radius: 40rpx; + z-index: 0; + animation: guangyun-pulse 2s infinite ease-in-out; + } + + @keyframes guangyun-pulse { + 0%, 100% { opacity: 0.6; transform: scale(1); } + 50% { opacity: 0.9; transform: scale(1.05); } + } + + .leixing-tupian { + width: 120rpx; + height: 120rpx; + border-radius: 28rpx; + z-index: 1; + box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08); + transition: all 0.3s ease; + } + + .leixing-active .leixing-tupian { + box-shadow: 0 16rpx 40rpx rgba(82, 196, 26, 0.3); + border: 4rpx solid #52c41a; + } + + .leixing-jieshao { + margin-top: 16rpx; + font-size: 24rpx; + color: #666; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + text-align: center; + z-index: 1; + } + + .leixing-active .leixing-jieshao { + color: #52c41a; + font-weight: 600; + } + + .fenge-xian { + height: 16rpx; + background: linear-gradient(to bottom, #f0f2f5, #f8f9fa); + flex-shrink: 0; + } + + .dingdan-container { + flex: 1; + overflow: hidden; + } + + .dingdan-scroll-view { + height: 100%; + -webkit-overflow-scrolling: touch; + } + + .refresher-container { + height: 100rpx; + display: flex; + align-items: center; + justify-content: center; + } + + .refreshing-text, + .pull-down-text { + font-size: 26rpx; + color: #999; + } + + .empty-tip { + padding: 120rpx 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + + .tip-icon { + width: 160rpx; + height: 160rpx; + opacity: 0.4; + margin-bottom: 30rpx; + } + + .tip-text { + font-size: 28rpx; + color: #aaa; + } + + .dingdan-card { + margin: 20rpx 24rpx; + border-radius: 20rpx; + padding: 28rpx; + position: relative; + overflow: hidden; + box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.06); + transition: all 0.2s ease; + border: 1rpx solid rgba(0, 0, 0, 0.08); + } + + .dingdan-card:active { + transform: scale(0.995); + } + + .pingtai-card { + background: linear-gradient(145deg, #f8fff8 0%, #f0f9f0 100%); + } + + .shangjia-card { + background: linear-gradient(145deg, #f9f0ff 0%, #f3e8ff 100%); + } + + .card-top { + display: flex; + align-items: center; + margin-bottom: 20rpx; + padding-bottom: 16rpx; + border-bottom: 1rpx dashed #e0e0e0; + } + + .creat-time { + font-size: 26rpx; + color: #555; + font-weight: 500; + } + + .top-line { + flex: 1; + height: 1rpx; + background: linear-gradient(to right, transparent, #ddd, transparent); + margin: 0 20rpx; + } + + .pingtai-tag { + font-size: 24rpx; + color: #666; + padding: 6rpx 16rpx; + background: rgba(0, 0, 0, 0.04); + border-radius: 20rpx; + } + + .zhiding-row { + display: flex; + align-items: center; + margin-bottom: 20rpx; + padding: 10rpx 16rpx; + background: rgba(255, 77, 79, 0.08); + border-radius: 12rpx; + border-left: 4rpx solid #ff4d4f; + gap: 10rpx; + } + + .zhiding-icon { + font-size: 26rpx; + margin-right: 4rpx; + } + + .zhiding-avatar { + width: 48rpx; + height: 48rpx; + border-radius: 50%; + border: 2rpx solid #ff4d4f; + object-fit: cover; + } + + .zhiding-nicheng { + font-size: 24rpx; + color: #ff4d4f; + font-weight: 500; + max-width: 120rpx; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .zhiding-uid { + font-size: 24rpx; + color: #ff4d4f; + font-weight: 400; + flex: 1; + text-align: right; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .card-content { + display: flex; + margin-bottom: 20rpx; + } + + .neirong-tupian { + width: 140rpx; + height: 140rpx; + border-radius: 50%; + margin-right: 24rpx; + flex-shrink: 0; + box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.08); + object-fit: cover; + } + + .jieshao-box { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + min-height: 140rpx; + padding: 16rpx 0; + } + + .jieshao-label { + font-size: 26rpx; + color: #333; + font-weight: 600; + margin-bottom: 8rpx; + } + + .jieshao-text { + font-size: 26rpx; + color: #666; + line-height: 1.4; + } + + .jieshao-ellipsis .jieshao-text { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + overflow: hidden; + text-overflow: ellipsis; + } + + .shangjia-info { + display: flex; + align-items: center; + margin-bottom: 20rpx; + } + + .shangjia-touxiang { + width: 90rpx; + height: 90rpx; + border-radius: 50%; + margin-right: 20rpx; + border: 2rpx solid rgba(114, 46, 209, 0.15); + box-shadow: 0 6rpx 18rpx rgba(114, 46, 209, 0.12); + object-fit: cover; + } + + .shangjia-nicheng { + font-size: 28rpx; + color: #333; + font-weight: 600; + } + + .shangjia-jieshao, + .shangjia-beizhu { + margin-bottom: 16rpx; + padding: 14rpx 18rpx; + background: rgba(255, 255, 255, 0.6); + border-radius: 12rpx; + } + + .shangjia-jieshao .jieshao-label, + .shangjia-beizhu .beizhu-label { + display: block; + margin-bottom: 6rpx; + font-size: 24rpx; + } + + .shangjia-jieshao .jieshao-text, + .shangjia-beizhu .beizhu-text { + font-size: 24rpx; + color: #555; + line-height: 1.4; + } + + .shangjia-jieshao.jieshao-ellipsis .jieshao-text { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; + } + + .shangjia-beizhu.beizhu-ellipsis .beizhu-text { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; + } + + .beizhu-box { + margin-bottom: 20rpx; + padding: 14rpx 18rpx; + background: rgba(255, 255, 255, 0.6); + border-radius: 12rpx; + } + + .beizhu-label { + font-size: 24rpx; + color: #333; + font-weight: 600; + margin-bottom: 6rpx; + display: block; + } + + .beizhu-text { + font-size: 24rpx; + color: #666; + line-height: 1.4; + } + + .beizhu-ellipsis .beizhu-text { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; + } + + .view-more { + display: block; + font-size: 22rpx; + color: #1890ff; + margin-top: 6rpx; + text-align: right; + } + + .card-bottom { + display: flex; + align-items: center; + justify-content: space-between; + padding-top: 20rpx; + border-top: 1rpx solid rgba(0, 0, 0, 0.06); + } + + .fenyong-box { + display: flex; + align-items: baseline; + } + + .fenyong-icon { + font-size: 30rpx; + margin-right: 10rpx; + } + + .fenyong-text { + font-size: 24rpx; + color: #999; + margin-right: 6rpx; + } + + .fenyong-price { + font-size: 40rpx; + color: #ff4d4f; + font-weight: 700; + margin-right: 4rpx; + } + + .fenyong-unit { + font-size: 24rpx; + color: #999; + } + + .qiangdan-btn { + position: relative; + padding: 18rpx 40rpx; + border-radius: 50rpx; + overflow: hidden; + box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.12); + } + + .pingtai-btn { + background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%); + } + + .shangjia-btn { + background: linear-gradient(135deg, #722ed1 0%, #9254de 100%); + } + + .btn-text { + font-size: 28rpx; + color: white; + font-weight: 600; + letter-spacing: 1rpx; + position: relative; + z-index: 2; + } + + .btn-shine { + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: linear-gradient( + to right, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.3) 50%, + rgba(255, 255, 255, 0) 100% + ); + transform: rotate(30deg); + animation: btn-shimmer 3s infinite linear; + z-index: 1; + } + + @keyframes btn-shimmer { + 0% { transform: translateX(-100%) rotate(30deg); } + 100% { transform: translateX(100%) rotate(30deg); } + } + + .qiangdan-btn:active { + transform: scale(0.96); + } + + .loading-more, + .no-more { + padding: 30rpx 0; + text-align: center; + } + + .loading-text { + font-size: 26rpx; + color: #999; + } + + .no-more-text { + font-size: 26rpx; + color: #ccc; + } + + .loading-mask { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(255, 255, 255, 0.9); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + z-index: 9999; + } + + .loading-spinner { + width: 70rpx; + height: 70rpx; + border: 6rpx solid #f0f0f0; + border-top: 6rpx solid #52c41a; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-bottom: 24rpx; + } + + @keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + + .loading-mask-text { + font-size: 28rpx; + color: #666; + } \ No newline at end of file diff --git a/pages/jiedanchi2/jiedanchi2.js b/pages/jiedanchi2/jiedanchi2.js new file mode 100644 index 0000000..0e33db9 --- /dev/null +++ b/pages/jiedanchi2/jiedanchi2.js @@ -0,0 +1,66 @@ +// pages/jiedanchi2/jiedanchi2.js +Page({ + + /** + * 页面的初始数据 + */ + data: { + + }, + + /** + * 生命周期函数--监听页面加载 + */ + onLoad(options) { + + }, + + /** + * 生命周期函数--监听页面初次渲染完成 + */ + onReady() { + + }, + + /** + * 生命周期函数--监听页面显示 + */ + onShow() { + + }, + + /** + * 生命周期函数--监听页面隐藏 + */ + onHide() { + + }, + + /** + * 生命周期函数--监听页面卸载 + */ + onUnload() { + + }, + + /** + * 页面相关事件处理函数--监听用户下拉动作 + */ + onPullDownRefresh() { + + }, + + /** + * 页面上拉触底事件的处理函数 + */ + onReachBottom() { + + }, + + /** + * 用户点击右上角分享 + */ + onShareAppMessage() { + + } +}) \ No newline at end of file diff --git a/pages/jiedanchi2/jiedanchi2.json b/pages/jiedanchi2/jiedanchi2.json new file mode 100644 index 0000000..3928faa --- /dev/null +++ b/pages/jiedanchi2/jiedanchi2.json @@ -0,0 +1,3 @@ +{ + "usingComponents": {} +} \ No newline at end of file diff --git a/pages/jiedanchi2/jiedanchi2.wxml b/pages/jiedanchi2/jiedanchi2.wxml new file mode 100644 index 0000000..fdd9cd8 --- /dev/null +++ b/pages/jiedanchi2/jiedanchi2.wxml @@ -0,0 +1,2 @@ + +pages/jiedanchi2/jiedanchi2.wxml \ No newline at end of file diff --git a/pages/jiedanchi2/jiedanchi2.wxss b/pages/jiedanchi2/jiedanchi2.wxss new file mode 100644 index 0000000..fae36a0 --- /dev/null +++ b/pages/jiedanchi2/jiedanchi2.wxss @@ -0,0 +1 @@ +/* pages/jiedanchi2/jiedanchi2.wxss */ \ No newline at end of file diff --git a/pages/jinpaids/jinpaids.js b/pages/jinpaids/jinpaids.js new file mode 100644 index 0000000..c8ee7b7 --- /dev/null +++ b/pages/jinpaids/jinpaids.js @@ -0,0 +1,313 @@ +// pages/jinpaids/jinpaids.js +import request from '../../utils/request.js' +import popupService from '../../services/popupService.js' +import { + isRoleStatusActive, + isCenterPageActive, + ensureRoleOnCenterPage, +} from '../../utils/role-tab-bar.js' +import { buildInviterPeerId } from '../../utils/im-user.js' + +const app = getApp() + +Page({ + data: { + // OSS地址 + ossImageUrl: '', + + // 图标路径 + icons: { + jinpai: '', + refresh: '', + copy: '', + qiangdan: '', + chongzhi: '', + dingdan: '', + chufa: '', + paihang: '', + xiugai: '', + tixian: '', + rightArrow: '' + }, + + // 用户基本信息 + uid: '', + touxiang: '', + avatarUrl: '', + dashouNicheng: '', + + // 打手核心数据 + chenghao: '', + yongjin: '0.00', + zonge: '0.00', + yajin: '0.00', + jinfen: '0', + chengjiaoliang: '0', + zaixianZhuangtai: 0, + dashouzhuangtai: '', + clumber: [], + + // 金牌专属数据 + jiedanZongliang: '0', + jinriJiedan: '0.00', + zuijinTixian: '0.00', + + // 页面状态 + isLoading: false, + lastRefreshTime: 0, + canRefresh: true, + + // 计算数据 + chengjiaoRate: '0%', + chengjiaoAngle: 0, + tixianProgress: 0, + + // 邀请人缓存(管事) + inviterCache: null, + chenghaoList: [] // 新增 + }, + + onLoad() { + wx.redirectTo({ url: '/pages/dashouduan/dashouduan' }); + }, + + onShow() { + wx.redirectTo({ url: '/pages/dashouduan/dashouduan' }); + }, + + _deprecatedOnLoad() { + const notificationComp = this.selectComponent('#global-notification') + if (notificationComp && notificationComp.showNotification) { + app.globalData.globalNotification = { + show: (data) => notificationComp.showNotification(data), + hide: () => notificationComp.hideNotification() + } + } + }, + + loadUserInfo() { + const uid = wx.getStorageSync('uid') || '' + const touxiang = wx.getStorageSync('touxiang') || '' + const ossUrl = app.globalData.ossImageUrl || '' + const defaultAvatar = ossUrl + (app.globalData.morentouxiang || 'avatar/default.jpg') + + let avatarUrl = defaultAvatar + if (touxiang) { + avatarUrl = ossUrl + (touxiang.startsWith('/') ? touxiang : '/' + touxiang) + } + + const dashouNicheng = app.globalData.dashouNicheng || wx.getStorageSync('nicheng') || '金牌选手' + + this.setData({ uid, touxiang, avatarUrl, dashouNicheng }) + }, + + async getJinpaiInfo() { + this.setData({ isLoading: true }) + + try { + const res = await request({ + url: '/yonghu/dashouxinxi', + method: 'POST' + }) + + if (res && res.data.code === 200) { + const data = res.data.data + + const updateData = { + dashouNicheng: data.dashounicheng || '', + zhanghaoStatus: data.zhanghaostatus || '', + yongjin: data.yongjin || '0.00', + zonge: data.zonge || '0.00', + yajin: data.yajin || '0.00', + chenghao: data.chenghao || '', + jinfen: data.jifen || '0', + chengjiaoliang: data.chengjiaoliang || '0', + zaixianZhuangtai: data.zaixianzhuangtai || 0, + dashouzhuangtai: data.dashouzhuangtai || '', + clumber: data.clumber || [], + jiedanZongliang: data.jiedanZongliang || '0', + jinriJiedan: data.jinriJiedan || '0.00', + zuijinTixian: data.zuijinTixian || '0.00', + } + + const jiedanTotal = parseFloat(updateData.jiedanZongliang) || 0 + const chengjiaoTotal = parseFloat(updateData.chengjiaoliang) || 0 + const rateNumber = jiedanTotal > 0 ? (chengjiaoTotal / jiedanTotal) * 100 : 0 + const chengjiaoRate = rateNumber.toFixed(1) + '%' + const chengjiaoAngle = rateNumber * 3.6 + + const zuijin = parseFloat(updateData.zuijinTixian) || 0 + const zong = parseFloat(updateData.jinriJiedan) || 0 + const tixianProgress = zong > 0 ? Math.min((zuijin / zong) * 100, 100) : 0 + + const globalUpdate = { + dashouNicheng: updateData.dashouNicheng, + zhanghaoStatus: updateData.zhanghaoStatus, + yongjin: updateData.yongjin, + zonge: updateData.zonge, + yajin: updateData.yajin, + chenghao: updateData.chenghao, + jinfen: updateData.jinfen, + chengjiaoliang: updateData.chengjiaoliang, + zaixianZhuangtai: updateData.zaixianZhuangtai, + dashouzhuangtai: updateData.dashouzhuangtai, + clumber: updateData.clumber, + } + Object.assign(app.globalData, globalUpdate) + + wx.setStorageSync('nicheng', updateData.dashouNicheng) + if (data.touxiang) { + wx.setStorageSync('touxiang', data.touxiang) + } + + this.setData({ + ...updateData, + chengjiaoRate, + chengjiaoAngle, + tixianProgress: Math.round(tixianProgress), + isLoading: false + }) + + this.checkJinpaiAndRedirect(updateData.chenghao) + + } else { + wx.showToast({ title: res?.data?.msg || '获取信息失败', icon: 'none' }) + this.setData({ isLoading: false }) + } + } catch (error) { + console.error('获取金牌信息失败:', error) + wx.showToast({ title: '网络错误', icon: 'none' }) + this.setData({ isLoading: false }) + } + }, + + refreshJinpaiInfo() { + const now = Date.now() + const lastTime = this.data.lastRefreshTime + const canRefresh = this.data.canRefresh + if (!canRefresh || (now - lastTime < 3000)) return + + + this.setData({ lastRefreshTime: now, canRefresh: false }) + this.getJinpaiInfo() + this.fetchChenghaoList() // 新增 + setTimeout(() => this.setData({ canRefresh: true }), 3000) + }, + + checkJinpaiAndRedirect(chenghao) { + if (chenghao !== '金牌选手') { + wx.setStorageSync('isJinpai', 0) + wx.redirectTo({ url: '/pages/dashouduan/dashouduan' }) + } else { + wx.setStorageSync('isJinpai', 1) + } + }, + + // 联系管事(与打手端完全一致,增加容错) + async contactInviter() { + wx.showLoading({ title: '获取管事信息...', mask: true }) + try { + const res = await request({ url: '/yonghu/hqwdyqr', method: 'POST' }) + if (res && res.data.code === 200) { + const inviter = res.data.data + const ossImageUrl = getApp().globalData.ossImageUrl || '' + const fullAvatar = inviter.touxiang + ? (inviter.touxiang.startsWith('http') ? inviter.touxiang : ossImageUrl + inviter.touxiang) + : '' + + // 缓存邀请人信息 + const cacheData = { + uid: inviter.uid, + nicheng: inviter.nicheng || '', + avatar: fullAvatar + } + wx.setStorageSync('inviterCache', cacheData) + this.setData({ inviterCache: cacheData }) + + // 确保打手身份并连接 + const app = getApp() + const myUid = wx.getStorageSync('uid') + + // 如果当前不是打手身份,则切换并重连 + if (app.globalData.currentRole !== 'dashou') { + app.globalData.currentRole = 'dashou' + wx.setStorageSync('currentRole', 'dashou') + if (app.switchRoleAndReconnect) { + await app.switchRoleAndReconnect('dashou') + } else if (app.connectWithIdentity) { + // 降级方案:直接用打手身份连接 + await app.connectWithIdentity('dashou', 'Ds' + myUid, false) + } + } else { + // 已为打手,确保连接存活 + if (app.connectForCurrentRole) { + app.connectForCurrentRole() + } else if (app.ensureConnection) { + app.ensureConnection() + } + } + + // 构造私聊参数 + const param = { + toUserId: buildInviterPeerId(inviter.uid), + toName: inviter.nicheng || '管事', + toAvatar: fullAvatar + } + wx.navigateTo({ + url: '/pages/liaotian/liaotian?data=' + encodeURIComponent(JSON.stringify(param)), + fail: (err) => { + console.error('跳转私聊失败', err) + wx.showToast({ title: '打开聊天失败', icon: 'none' }) + } + }) + } else { + wx.showToast({ title: res?.data?.msg || '获取失败', icon: 'none' }) + } + } catch (e) { + console.error('联系管事出错:', e) + wx.showToast({ title: '网络错误', icon: 'none' }) + } finally { + wx.hideLoading() + } + }, + + copyUid() { + const { uid } = this.data + if (!uid) return wx.showToast({ title: 'UID不存在', icon: 'none' }) + wx.setClipboardData({ + data: uid, + success: () => wx.showToast({ title: '已复制UID', icon: 'success' }) + }) + }, + + previewAvatar() { + if (this.data.avatarUrl) wx.previewImage({ urls: [this.data.avatarUrl] }) + }, + + goToWithdraw() { wx.navigateTo({ url: '/pages/tixian/tixian' }) }, + goToReceiveOrder() { wx.navigateTo({ url: '/pages/jiedan/jiedan' }) }, + goToRecharge() { wx.navigateTo({ url: '/pages/dashouchongzhi/dashouchongzhi' }) }, + goToMyOrders() { wx.navigateTo({ url: '/pages/dashoudingdan/dashoudingdan' }) }, + goToMyPunishment() { wx.navigateTo({ url: '/pages/cfss/cfss/cfss' }) }, + goToRanking() { wx.navigateTo({ url: '/pages/dashoupaihang/dashoupaihang?type=dashou' }) }, + goToModifyInfo() { wx.navigateTo({ url: '/pages/dashouxiugai/dashouxiugai' }) }, + goToRules() { wx.navigateTo({ url: '/pages/dashouguize/dashouguize' }) }, + // 跳转到考核金牌 +goToKaohe() { + wx.navigateTo({ url: '/pages/kaohe_jinpai/kaohe_jinpai' }); + }, + + async fetchChenghaoList() { + try { + const res = await request({ + url: '/dengji/dsbqhq', + method: 'POST' + }); + if (res && res.data.code === 0 && res.data.data) { + this.setData({ chenghaoList: res.data.data.chenghao_list || [] }); + } + } catch (e) { + console.error('获取称号标签失败', e); + } +}, +}) \ No newline at end of file diff --git a/pages/jinpaids/jinpaids.json b/pages/jinpaids/jinpaids.json new file mode 100644 index 0000000..d2be079 --- /dev/null +++ b/pages/jinpaids/jinpaids.json @@ -0,0 +1,10 @@ +{ + "usingComponents": { + "global-notification": "/components/global-notification/global-notification", + "chenghao-tag": "/components/chenghao-tag/chenghao-tag", + "custom-tab-bar": "/custom-tab-bar/index" + }, + "navigationBarTitleText": "金牌用户", + "navigationBarBackgroundColor": "#0B2A4A", + "navigationBarTextStyle": "white" + } \ No newline at end of file diff --git a/pages/jinpaids/jinpaids.wxml b/pages/jinpaids/jinpaids.wxml new file mode 100644 index 0000000..5912b19 --- /dev/null +++ b/pages/jinpaids/jinpaids.wxml @@ -0,0 +1,193 @@ + + + + + + + + + + 金牌选手 + + + + 刷新 + + + + + + + + + + + + + + + + + + + + + + + + + + + + 可提现金额 + + {{yongjin}} + + + + + + 立即提现 + + + + + + + + 总赚取 + {{zonge}}元 + + + 保证金 + {{yajin}}元 + + + 积分 + {{jinfen}} + + + 接单总量 + {{jiedanZongliang}} + + + 成交总量 + {{chengjiaoliang}} + + + 今日接单 + {{jinriJiedan}}元 + + + + + + + 成交率 + + + {{chengjiaoRate}} + + + + 最近提现占比 + + + {{tixianProgress}}% + + + 最近提现:{{zuijinTixian}}元 + 今日赚取:{{jinriJiedan}}元 + + + + + + + + + + {{inviterCache.nicheng || '我的管事'}} + ID: {{inviterCache.uid}} + + + 联系我的邀请人 + + + + + + 我的管事 + 点击联系 + + + 联系我的邀请人 + + + + + + + + 抢单大厅 + + + + 充值会员保证金 + + + + 我的订单 + + + + 我的处罚 + + + + 排行榜 + + + + 修改资料 + + + + 用户规则 + + + + 考核金牌 + + + + + + + + + + + + \ No newline at end of file diff --git a/pages/jinpaids/jinpaids.wxss b/pages/jinpaids/jinpaids.wxss new file mode 100644 index 0000000..162eae6 --- /dev/null +++ b/pages/jinpaids/jinpaids.wxss @@ -0,0 +1,610 @@ +/* pages/jinpaids/jinpaids.wxss - 金牌打手机甲风格(CSS变量版) */ + +/* ========== 全局 ========== */ +page { + background: #0B1A2F; + min-height: 100vh; + font-family: 'Avenir', 'PingFang SC', 'Helvetica Neue', sans-serif; + color: #fff; + } + + .bg-grid { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: + linear-gradient(rgba(0, 160, 255, 0.1) 1px, transparent 1px), + linear-gradient(90deg, rgba(0, 160, 255, 0.1) 1px, transparent 1px); + background-size: 50rpx 50rpx; + pointer-events: none; + z-index: 0; + } + + .bg-energy { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: radial-gradient(circle at 20% 30%, rgba(0,200,255,0.1) 0%, transparent 40%), + radial-gradient(circle at 80% 70%, rgba(0,100,255,0.1) 0%, transparent 40%); + animation: energyFlow 20s linear infinite; + pointer-events: none; + z-index: 0; + } + + @keyframes energyFlow { + 0% { opacity: 0.3; } + 50% { opacity: 0.6; } + 100% { opacity: 0.3; } + } + + .page-container { + position: relative; + z-index: 2; + min-height: 100vh; + padding: 30rpx 25rpx; + box-sizing: border-box; + } + + /* ========== 顶部栏 ========== */ + .top-bar { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 30rpx; + margin-top: 20rpx; + } + .jinpai-badge { + display: flex; + align-items: center; + } + .jinpai-icon { + width: 60rpx; + height: 60rpx; + margin-right: 15rpx; + } + /* 火苗图标燃烧动画 */ + .fire-icon { + animation: fireBurn 0.8s infinite alternate ease-in-out; + filter: drop-shadow(0 0 10rpx #ffaa00); + } + @keyframes fireBurn { + 0% { transform: scale(1) rotate(-3deg); filter: drop-shadow(0 0 10rpx #ffaa00); } + 25% { transform: scale(1.1) rotate(2deg); filter: drop-shadow(0 0 20rpx #ff5500); } + 50% { transform: scale(1.05) rotate(-2deg); filter: drop-shadow(0 0 30rpx #ffaa00); } + 75% { transform: scale(1.15) rotate(3deg); filter: drop-shadow(0 0 40rpx #ffaa00); } + 100% { transform: scale(1) rotate(-3deg); filter: drop-shadow(0 0 10rpx #ffaa00); } + } + .jinpai-title { + font-size: 40rpx; + font-weight: bold; + color: #ffaa00; + text-shadow: 0 0 30rpx #ffaa00, 0 0 60rpx #ffaa00; + letter-spacing: 2rpx; + } + .refresh-btn { + display: flex; + align-items: center; + background: rgba(0,40,70,0.8); + border: 2rpx solid #00a6ff; + clip-path: polygon(0 0, 100% 0, 90% 100%, 0 100%); + padding: 8rpx 25rpx; + } + .refresh-icon { + width: 40rpx; + height: 40rpx; + margin-right: 10rpx; + } + .refresh-text { + font-size: 26rpx; + color: #00a6ff; + } + .refresh-btn:active { + opacity: 0.8; + } + + /* ========== 用户信息 ========== */ + .user-section { + display: flex; + background: rgba(0,30,50,0.5); + backdrop-filter: blur(20px); + border: 2rpx solid #00a6ff; + clip-path: polygon(0 0, 100% 0, 95% 100%, 0 100%); + padding: 30rpx; + margin-bottom: 40rpx; + } + .avatar-wrap { + position: relative; + width: 120rpx; + height: 120rpx; + margin-right: 30rpx; + } + .avatar { + width: 100%; + height: 100%; + border-radius: 50%; + border: 4rpx solid #00a6ff; + box-shadow: 0 0 50rpx #00a6ff; + object-fit: cover; + } + .avatar-ring { + position: absolute; + top: -10rpx; + left: -10rpx; + right: -10rpx; + bottom: -10rpx; + border-radius: 50%; + border: 2rpx solid #00a6ff; + border-top-color: transparent; + border-bottom-color: transparent; + animation: ringRotate 6s linear infinite; + } + @keyframes ringRotate { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + .user-info { + flex: 1; + } + .name-row { + display: flex; + align-items: center; + margin-bottom: 10rpx; + } + .nickname { + font-size: 40rpx; + font-weight: bold; + color: #fff; + margin-right: 15rpx; + max-width: 250rpx; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .status-tag { + font-size: 22rpx; + padding: 4rpx 16rpx; + border-radius: 30rpx; + background: rgba(255,255,255,0.1); + border: 1rpx solid currentColor; + } + .status-tag.online { color: #00ff80; border-color: #00ff80; } + .status-tag.offline { color: #aaa; border-color: #aaa; } + .uid-row { + display: flex; + align-items: center; + background: rgba(0,50,80,0.6); + border-left: 4rpx solid #00a6ff; + padding: 8rpx 20rpx; + margin-bottom: 10rpx; + width: fit-content; + } + .uid-label { + font-size: 24rpx; + color: #9cf; + margin-right: 10rpx; + } + .uid-value { + font-size: 24rpx; + color: #fff; + font-family: monospace; + margin-right: 15rpx; + } + .copy-icon { + width: 30rpx; + height: 30rpx; + } + .order-status { + display: flex; + align-items: center; + } + .status-label { + font-size: 24rpx; + color: #9cf; + margin-right: 8rpx; + } + .status-value { + font-size: 24rpx; + color: #aaa; + } + .status-value.active { + color: #00ff80; + text-shadow: 0 0 10rpx #00ff80; + } + + /* ========== 核心资产卡 ========== */ + .asset-card { + display: flex; + justify-content: space-between; + align-items: center; + background: linear-gradient(135deg, #0A1F3A, #0B2A4A); + border: 2rpx solid #00a6ff; + clip-path: polygon(0 0, 100% 0, 95% 100%, 0 100%); + padding: 30rpx 30rpx; + margin-bottom: 30rpx; + box-shadow: 0 0 40rpx #00a6ff; + } + .asset-left { + flex: 1; + } + .asset-label { + font-size: 28rpx; + color: #9cf; + display: block; + margin-bottom: 10rpx; + } + .asset-value-group { + display: flex; + align-items: baseline; + } + .asset-number { + font-size: 68rpx; + font-weight: bold; + color: #fff; + text-shadow: 0 0 30rpx #00a6ff, 0 0 60rpx #00a6ff; + } + .asset-unit { + font-size: 32rpx; + color: #00a6ff; + margin-left: 10rpx; + } + .asset-right { + display: flex; + align-items: center; + background: rgba(255,255,255,0.1); + border: 2rpx solid #ffaa00; + clip-path: polygon(0 0, 100% 0, 90% 100%, 0 100%); + padding: 15rpx 30rpx; + position: relative; + overflow: hidden; + } + .withdraw-icon { + width: 50rpx; + height: 50rpx; + margin-right: 10rpx; + } + .withdraw-text { + font-size: 32rpx; + font-weight: bold; + color: #ffaa00; + } + .withdraw-glow { + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent); + animation: scan 2s infinite; + } + @keyframes scan { + 0% { left: -100%; } + 100% { left: 100%; } + } + .asset-right:active { + transform: scale(0.96); + } + + /* ========== 统计网格 ========== */ + .stats-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 20rpx; + margin-bottom: 40rpx; + } + .stat-item { + background: rgba(0,30,50,0.7); + border: 1rpx solid #00a6ff; + clip-path: polygon(0 0, 100% 0, 90% 100%, 0 100%); + padding: 20rpx 10rpx; + text-align: center; + } + .stat-label { + font-size: 24rpx; + color: #9cf; + display: block; + margin-bottom: 10rpx; + } + .stat-value { + font-size: 32rpx; + font-weight: bold; + color: #fff; + } + + /* ========== 图形化数据(使用CSS变量) ========== */ + .graphic-panel { + background: rgba(0,30,50,0.6); + backdrop-filter: blur(15px); + border: 2rpx solid #00a6ff; + clip-path: polygon(0 0, 100% 0, 98% 100%, 0 100%); + padding: 30rpx; + margin-bottom: 40rpx; + display: flex; + justify-content: space-around; + flex-wrap: wrap; + } + .chart-item { + width: 45%; + text-align: center; + } + .chart-title { + font-size: 28rpx; + color: #9cf; + margin-bottom: 15rpx; + text-transform: uppercase; + border-left: 4rpx solid #00a6ff; + padding-left: 15rpx; + text-align: left; + } + + /* 环形进度条(使用CSS变量 --progress-angle) */ + .ring-chart { + position: relative; + width: 200rpx; + height: 200rpx; + margin: 0 auto; + border-radius: 50%; + overflow: hidden; + box-shadow: 0 0 40rpx #00a6ff; + } + .ring-progress { + width: 100%; + height: 100%; + background: conic-gradient(#00a6ff 0deg, #00a6ff var(--progress-angle), #1a3a5a var(--progress-angle), #1a3a5a 360deg); + border-radius: 50%; + } + .ring-inner { + position: absolute; + top: 15rpx; + left: 15rpx; + right: 15rpx; + bottom: 15rpx; + background: #0a1a2a; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 40rpx; + font-weight: bold; + color: #00a6ff; + box-shadow: inset 0 0 20rpx rgba(0,0,0,0.5); + } + + /* 提现进度条(使用CSS变量 --progress-width) */ + .progress-container { + height: 30rpx; + background: #1a3a5a; + border-radius: 15rpx; + position: relative; + overflow: hidden; + width: 100%; + margin-top: 20rpx; + } + .progress-bar { + height: 100%; + background: linear-gradient(90deg, #00a6ff, #80d4ff); + border-radius: 15rpx; + width: var(--progress-width); + position: relative; + } + .flow-bar::after { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.6), transparent); + animation: flow 1.5s infinite; + } + @keyframes flow { + 0% { left: -100%; } + 100% { left: 100%; } + } + .progress-label { + position: absolute; + right: 20rpx; + top: -30rpx; + font-size: 24rpx; + color: #00a6ff; + } + .progress-stats { + display: flex; + justify-content: space-between; + font-size: 24rpx; + color: #9cf; + margin-top: 15rpx; + } + + /* ========== 功能按钮(图标强光效果) ========== */ + .function-panel { + margin-bottom: 40rpx; + } + .function-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 20rpx; + } + .func-item { + background: rgba(0,20,40,0.8); + border: 1rpx solid #00a6ff; + clip-path: polygon(0 0, 100% 0, 90% 100%, 0 100%); + padding: 25rpx 0; + display: flex; + flex-direction: column; + align-items: center; + transition: 0.2s; + position: relative; + overflow: hidden; + } + .func-item:active { + transform: scale(0.96); + box-shadow: 0 0 40rpx #00a6ff; + } + /* 图标超强发光 */ + .super-glow { + width: 60rpx; + height: 60rpx; + margin-bottom: 15rpx; + filter: drop-shadow(0 0 25rpx #00a6ff) drop-shadow(0 0 50rpx #00a6ff) drop-shadow(0 0 80rpx #00a6ff); + animation: superGlow 1s infinite alternate; + } + @keyframes superGlow { + 0% { filter: drop-shadow(0 0 20rpx #00a6ff) drop-shadow(0 0 40rpx #00a6ff) drop-shadow(0 0 60rpx #00a6ff); } + 100% { filter: drop-shadow(0 0 30rpx #00a6ff) drop-shadow(0 0 60rpx #00a6ff) drop-shadow(0 0 100rpx #00a6ff); } + } + .func-label { + font-size: 26rpx; + color: #fff; + z-index: 1; + } + + /* ========== 加载遮罩 ========== */ + .loading-mask { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(2,6,12,0.9); + backdrop-filter: blur(20px); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; + } + .loading-core { + width: 200rpx; + height: 200rpx; + border: 4rpx solid #00a6ff; + border-radius: 50%; + border-top-color: transparent; + animation: spin 1s linear infinite; + display: flex; + align-items: center; + justify-content: center; + } + .loading-core::after { + content: ''; + width: 100rpx; + height: 100rpx; + border: 4rpx solid #ffaa00; + border-radius: 50%; + border-bottom-color: transparent; + animation: spin 1.5s reverse infinite; + } + @keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + + + + + + /* ========== 原有样式(完全不变) ========== */ +/* ... 此处省略,切勿修改 ... */ + +/* ========== 联系管事胶囊(新增,适配机甲风格) ========== */ +.inviter-capsule { + margin: 30rpx 25rpx 30rpx; + background: rgba(0, 20, 40, 0.85); + backdrop-filter: blur(20px); + border: 2rpx solid #00a6ff; + clip-path: polygon(5% 0, 100% 0, 95% 100%, 0 100%); + padding: 20rpx 30rpx; + display: flex; + align-items: center; + justify-content: space-between; + box-shadow: 0 0 30rpx rgba(0, 166, 255, 0.4); + position: relative; + z-index: 1; + } + + .capsule-body { + display: flex; + align-items: center; + flex: 1; + } + + .capsule-avatar { + width: 80rpx; + height: 80rpx; + border-radius: 50%; + border: 3rpx solid #00a6ff; + box-shadow: 0 0 20rpx #00a6ff; + margin-right: 20rpx; + background: #0a1a2a; + } + + .capsule-info { + display: flex; + flex-direction: column; + } + + .capsule-name { + font-size: 30rpx; + font-weight: bold; + color: #fff; + text-shadow: 0 0 15rpx #00a6ff; + margin-bottom: 6rpx; + } + + .capsule-uid { + font-size: 24rpx; + color: #9cf; + } + + .capsule-contact { + background: linear-gradient(135deg, #00a6ff, #0080ff); + border-radius: 30rpx; + padding: 12rpx 30rpx; + font-size: 26rpx; + font-weight: bold; + color: #fff; + box-shadow: 0 0 20rpx #00a6ff; + letter-spacing: 2rpx; + transition: all 0.3s; + } + + .capsule-contact:active { + transform: scale(0.95); + box-shadow: 0 0 40rpx #00a6ff; + } + + + + + + + + /* ========== 🆕 称号标签展示区 ========== */ +.badge-area { + width: 100%; + margin: 20rpx 0 30rpx 0; + padding: 0 20rpx; + box-sizing: border-box; + } + + .badge-scroll { + width: 100%; + max-height: 140rpx; + overflow-y: auto; + background: transparent; + } + + .badge-grid { + display: flex; + flex-wrap: wrap; + gap: 8rpx; + padding: 6rpx 0; + align-items: center; + justify-content: flex-start; + } + + .badge-item { + flex-shrink: 0; + } \ No newline at end of file diff --git a/pages/jisufd/jisufd.js b/pages/jisufd/jisufd.js new file mode 100644 index 0000000..49ae9cb --- /dev/null +++ b/pages/jisufd/jisufd.js @@ -0,0 +1,618 @@ +// pages/jisufd/jisufd.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) + } +}) \ No newline at end of file diff --git a/pages/jisufd/jisufd.json b/pages/jisufd/jisufd.json new file mode 100644 index 0000000..e0c0695 --- /dev/null +++ b/pages/jisufd/jisufd.json @@ -0,0 +1,12 @@ +{ + "usingComponents": { + "global-notification": "/components/global-notification/global-notification", + "chenghao-tag": "/components/chenghao-tag/chenghao-tag" + }, + "backgroundTextStyle": "dark", + "navigationBarBackgroundColor": "#0a0a0f", + "navigationBarTitleText": "极速派单", + "navigationBarTextStyle": "white", + "backgroundColor": "#0a0a0f", + "enablePullDownRefresh": false +} \ No newline at end of file diff --git a/pages/jisufd/jisufd.wxml b/pages/jisufd/jisufd.wxml new file mode 100644 index 0000000..5d0fc2a --- /dev/null +++ b/pages/jisufd/jisufd.wxml @@ -0,0 +1,230 @@ + + + + + 可用余额 + + {{sjyue}} + + + 生成链接将从此余额扣除 + + + + + 选择订单类型 + + + + {{item.jieshao}} + + + + + + + + + 🔍 + + + 搜索 + + + + + 金额≥ + + + + 标签 + + + {{searchLabelName || '全部'}} + + + + 清空 + + + + + + + + + + + {{item.jieshao || '无介绍'}} + + + + + + 价格: + {{item.jiage || '0.00'}}元 + + + + {{item.hasGenerated ? '获取新链接' : '生成链接'}} + + + {{item.isCopied ? '已复制' : '一键复制'}} + + + + + + + 添加模板 + 点击创建新模板 + + + ... + 没有更多模板了 + 暂无模板 + + + + + + + + × + 模板详情 + + + 订单介绍 + {{currentTemplate.jieshao || '无介绍'}} + + + 关联标签 + + + + 价格{{currentTemplate.jiage || '0.00'}}元 + | + 发布数量{{currentTemplate.fabushuliang || 0}}次 + | + 模板ID{{currentTemplate.mobanId || 'N/A'}} + + + 佣金要求 + {{currentTemplate.commissionValue}}元(建议≤50) + + + + 链接状态: + 未使用 + 已使用 + + + + + {{item.lianjie}} + {{item.is_used ? '已使用' : '未使用'}} + 复制 + + + + 获取更多链接 + + 加载中... + 暂无链接 + + + + {{currentTemplate.hasGenerated ? '获取新链接' : '生成链接'}} + {{currentTemplate.isCopied ? '已复制' : '复制链接'}} + {{isEditing ? '保存修改' : '立即修改'}} + 删除模板 + + + + + 订单介绍 + + {{countdown}}秒后可确认 + + 取消 + 确认拒绝 + + + + + + + + 确认转移考核 + 转移后,您将无法继续考核此订单,可在考核中心重新接待。 + {{countdown}}秒后可确认 + + 取消 + 确认转移 + + + + + \ No newline at end of file diff --git a/pages/kaohe_dafen/kaohe_dafen.wxss b/pages/kaohe_dafen/kaohe_dafen.wxss new file mode 100644 index 0000000..f736716 --- /dev/null +++ b/pages/kaohe_dafen/kaohe_dafen.wxss @@ -0,0 +1,170 @@ +/* 根容器,全屏且底部固定 */ +.root-page { + width: 100vw; + height: 100vh; + background: radial-gradient(ellipse at 20% 20%, #1a1e2b 0%, #0b0c12 100%); + display: flex; + flex-direction: column; + } + + /* 滚动区域 */ + .scroll-container { + flex: 1; + overflow-y: auto; + padding: 30rpx 30rpx 0; + padding-bottom: constant(safe-area-inset-bottom); + padding-bottom: env(safe-area-inset-bottom); + } + + /* 卡片 */ + .card { + background: rgba(25, 27, 36, 0.8); + backdrop-filter: blur(20px); + border: 1rpx solid rgba(201, 169, 88, 0.15); + border-radius: 24rpx; + padding: 30rpx; + margin-bottom: 30rpx; + box-shadow: 0 8rpx 30rpx rgba(0,0,0,0.5); + } + + /* 用户信息 */ + .user-row { + display: flex; + align-items: center; + margin-bottom: 30rpx; + } + .avatar { + width: 80rpx; height: 80rpx; + border-radius: 50%; + border: 2rpx solid rgba(201,169,88,0.4); + margin-right: 20rpx; + } + .info { flex: 1; } + .uid { font-size: 30rpx; color: #f0f2ff; font-weight: 600; } + .time { font-size: 22rpx; color: #6a7090; margin-top: 4rpx; } + .contact-btn { + background: rgba(201,169,88,0.15); + border: 1rpx solid rgba(201,169,88,0.3); + border-radius: 20rpx; + padding: 8rpx 24rpx; + font-size: 24rpx; + color: #c9a558; + } + + /* 详情网格 */ + .detail-grid { + border-bottom: 1rpx solid rgba(255,255,255,0.06); + margin-bottom: 20rpx; + } + .detail-item { + display: flex; + align-items: center; + padding: 16rpx 0; + border-bottom: 1rpx solid rgba(255,255,255,0.04); + } + .detail-item:last-child { border-bottom: none; } + .label { font-size: 26rpx; color: #8a90b0; width: 160rpx; flex-shrink: 0; } + .value { font-size: 26rpx; color: #d0d4ff; font-weight: 500; } + .rule-content { white-space: pre-wrap; line-height: 1.5; } + .jinpai-note { font-size: 24rpx; color: #c9a558; font-weight: 600; margin-left: 10rpx; } + + /* 失败原因 */ + .fail-box { + background: rgba(255, 70, 70, 0.1); + border-left: 4rpx solid #ff5e5e; + padding: 16rpx; + border-radius: 8rpx; + margin-bottom: 20rpx; + } + .fail-label { font-size: 24rpx; color: #ff8a8a; } + .fail-text { font-size: 24rpx; color: #ffd0d0; } + + /* 昵称修改 */ + .nick-row { + display: flex; + align-items: center; + margin-bottom: 30rpx; + } + .nick-input { + flex: 1; + background: rgba(255,255,255,0.06); + border: 1rpx solid rgba(255,255,255,0.1); + border-radius: 12rpx; + padding: 12rpx 20rpx; + color: #fff; + font-size: 26rpx; + margin-right: 20rpx; + } + .save-nick-btn { + background: #c9a558; + color: #0b0c12; + padding: 12rpx 30rpx; + border-radius: 30rpx; + font-size: 26rpx; + font-weight: 600; + } + + /* 操作按钮 */ + .action-row { + display: flex; + gap: 20rpx; + margin-top: 20rpx; + } + .action-btn { + flex: 1; + height: 80rpx; + display: flex; + align-items: center; + justify-content: center; + border-radius: 40rpx; + font-size: 28rpx; + font-weight: 600; + color: #fff; + } + .agree { background: linear-gradient(135deg, #c9a558, #9e7d2e); } + .reject { background: linear-gradient(135deg, #a85050, #802a2a); } + .transfer { background: linear-gradient(135deg, #4a6090, #2a3a60); } + + /* 加载/空状态 */ + .loading, .no-more, .empty { text-align: center; padding: 30rpx; color: #6a7090; font-size: 28rpx; } + + /* 弹窗 */ + .modal-mask { + position: fixed; + top: 0; left: 0; right: 0; bottom: 0; + background: rgba(0,0,0,0.7); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + } + .modal-box { + width: 80%; + background: #1e2030; + border-radius: 24rpx; + padding: 30rpx; + border: 1rpx solid rgba(201,169,88,0.2); + } + .modal-header { font-size: 32rpx; color: #f0f2ff; font-weight: 600; margin-bottom: 20rpx; } + .modal-body { font-size: 26rpx; color: #b0b8d0; margin-bottom: 20rpx; } + .reason-input { + width: 100%; + height: 200rpx; + background: rgba(255,255,255,0.05); + border-radius: 12rpx; + padding: 15rpx; + color: #fff; + font-size: 26rpx; + box-sizing: border-box; + margin-bottom: 20rpx; + } + .countdown-text { text-align: center; font-size: 28rpx; color: #c9a558; margin-bottom: 20rpx; } + .modal-footer { display: flex; gap: 20rpx; } + .btn { + flex: 1; height: 80rpx; border-radius: 40rpx; + display: flex; align-items: center; justify-content: center; + font-size: 28rpx; font-weight: 600; + } + .cancel { background: #2a2f3e; color: #fff; } + .confirm { background: linear-gradient(135deg, #c9a558, #9e7d2e); color: #0b0c12; } + .confirm.disabled { opacity: 0.4; pointer-events: none; } \ No newline at end of file diff --git a/pages/kaohe_jilu/kaohe_jilu.js b/pages/kaohe_jilu/kaohe_jilu.js new file mode 100644 index 0000000..545e567 --- /dev/null +++ b/pages/kaohe_jilu/kaohe_jilu.js @@ -0,0 +1,149 @@ +// pages/kaohe_jilu/kaohe_jilu.js +const app = getApp(); +import request from '../../utils/request.js'; + +Page({ + data: { + // 板块筛选 + moduleList: [], + xuanzhongBankuaiId: 0, // 0代表全部 + // 状态筛选:0-全部 1-审核中 2-已通过 3-未通过 + statusFilter: 0, + // 记录列表 + recordList: [], + page: 1, + pageSize: 5, + hasMore: true, + isLoading: false, + isLoadingMore: false, + // 展开的记录ID集合(字符串数组) + expandedIds: [], + // OSS地址 + ossImageUrl: app.globalData.ossImageUrl || '', + }, + + onLoad() { + this.loadModules(); + }, + + // 加载板块(复用审核大厅接口) + async loadModules() { + try { + const res = await request({ url: '/dengji/shdthqmk', method: 'POST' }); + if (res && res.data.code === 0) { + const modules = res.data.data.modules || []; + this.setData({ moduleList: modules, xuanzhongBankuaiId: 0 }); + this.loadRecords(true); + } else { + wx.showToast({ title: '加载模块失败', icon: 'none' }); + } + } catch (e) { + wx.showToast({ title: '网络错误', icon: 'none' }); + } + }, + + // 选择板块 + selectModule(e) { + const bankuaiId = e.currentTarget.dataset.id; + if (bankuaiId === this.data.xuanzhongBankuaiId) return; + this.setData({ + xuanzhongBankuaiId: bankuaiId, + recordList: [], + page: 1, + hasMore: true, + expandedIds: [] + }); + this.loadRecords(true); + }, + + // 选择状态 + selectStatus(e) { + const status = e.currentTarget.dataset.status; + if (status === this.data.statusFilter) return; + this.setData({ + statusFilter: status, + recordList: [], + page: 1, + hasMore: true, + expandedIds: [] + }); + this.loadRecords(true); + }, + + // 加载历史记录 + async loadRecords(isRefresh = false) { + if (this.data.isLoading) return; + const page = isRefresh ? 1 : this.data.page; + if (!isRefresh && !this.data.hasMore) return; + + this.setData({ isLoading: true, isLoadingMore: !isRefresh }); + + try { + const res = await request({ + url: '/dengji/khghqkhjl', + method: 'POST', + data: { + bankuai_id: this.data.xuanzhongBankuaiId || undefined, + status: this.data.statusFilter || undefined, + page: page, + page_size: this.data.pageSize + } + }); + + this.setData({ isLoading: false, isLoadingMore: false }); + + if (res && res.data.code === 0) { + const list = res.data.data.list || []; + const hasMore = res.data.data.has_more; + // 拼接头像 + const oss = this.data.ossImageUrl; + const processedList = list.map(item => { + let avatar = item.shenqingren_avatar || ''; + if (avatar && !avatar.startsWith('http')) { + avatar = oss + avatar; + } + return { ...item, shenqingren_avatar: avatar }; + }); + const newList = isRefresh ? processedList : [...this.data.recordList, ...processedList]; + this.setData({ recordList: newList, page: page + 1, hasMore: hasMore }); + } else { + wx.showToast({ title: '加载记录失败', icon: 'none' }); + } + } catch (e) { + this.setData({ isLoading: false, isLoadingMore: false }); + wx.showToast({ title: '网络错误', icon: 'none' }); + } + }, + + // 上拉加载更多 + onReachBottom() { + if (this.data.hasMore && !this.data.isLoading && !this.data.isLoadingMore) { + this.loadRecords(false); + } + }, + + // 展开/收起详情 + toggleExpand(e) { + const jiluId = e.currentTarget.dataset.id; + // 确保 jiluId 是字符串 + const id = String(jiluId); + const expandedIds = this.data.expandedIds.slice(); // 拷贝 + const index = expandedIds.indexOf(id); + if (index > -1) { + expandedIds.splice(index, 1); + } else { + expandedIds.push(id); + } + this.setData({ expandedIds }); + }, + + // 去评判(跳转考核打分页) + goJudge(e) { + const item = e.currentTarget.dataset.item; + if (!item) return; + wx.navigateTo({ url: '/pages/kaohe_dafen/kaohe_dafen?jilu_id=' + item.jilu_id }); + }, + + // 阻止冒泡 + noop() {} +}); \ No newline at end of file diff --git a/pages/kaohe_jilu/kaohe_jilu.json b/pages/kaohe_jilu/kaohe_jilu.json new file mode 100644 index 0000000..f6a68c8 --- /dev/null +++ b/pages/kaohe_jilu/kaohe_jilu.json @@ -0,0 +1,9 @@ +{ + "usingComponents": { + "chenghao-tag": "/components/chenghao-tag/chenghao-tag", + "global-notification": "/components/global-notification/global-notification" + }, + "navigationBarTitleText": "考核记录", + "navigationBarBackgroundColor": "#FDFBF7", + "navigationBarTextStyle": "black" + } \ No newline at end of file diff --git a/pages/kaohe_jilu/kaohe_jilu.wxml b/pages/kaohe_jilu/kaohe_jilu.wxml new file mode 100644 index 0000000..b8ac9ff --- /dev/null +++ b/pages/kaohe_jilu/kaohe_jilu.wxml @@ -0,0 +1,130 @@ + + + + + + + + + 全部板块 + + + + {{item.mingcheng}} + + + + + + + 全部 + 审核中 + 已通过 + 未通过 + + + + + + + + 暂无考核记录 + + + + + + + + + {{item.zhuangtai == 0 ? '审核中' : (item.zhuangtai == 1 ? '已通过' : (item.zhuangtai == 2 ? '未通过' : '待审核'))}} + + + + + + + 板块 + {{item.bankuai_mingcheng}} + + + 标签 + + + + 次数 + {{item.cishu}}次 + + + 总费用 + ¥{{item.total_fee}} + + + + + + + {{expandedIds.indexOf(item.jilu_id) > -1 ? '收起详情' : '展开详情'}} + + + 去评判 + + + + + + + 考核规则 + {{item.guize_neirong}} + + + 缴纳记录 + + 第{{item.cishu}}次:¥{{item.jine}} + + 暂无缴纳记录 + + + 拒绝记录 + + + {{item.create_time}} + 第{{item.cishu}}次拒绝:{{item.yuanyin}} + + + 暂无拒绝记录 + + + 其他信息 + + 打手游戏ID:{{item.dashou_youxi_id || '无'}} + 打手备注:{{item.dashou_beizhu || '无'}} + 审核官游戏ID:{{item.shenheguan_youxi_id || '无'}} + 创建时间:{{item.create_time}} + 更新时间:{{item.update_time}} + + + + + + + 加载更多... + —— 没有更多了 —— + + + + + + + + 加载中... + + + + \ No newline at end of file diff --git a/pages/kaohe_jilu/kaohe_jilu.wxss b/pages/kaohe_jilu/kaohe_jilu.wxss new file mode 100644 index 0000000..bf37003 --- /dev/null +++ b/pages/kaohe_jilu/kaohe_jilu.wxss @@ -0,0 +1,263 @@ +/* pages/kaohe_jilu/kaohe_jilu.wxss - 晨露微光风格(左右均等边距) */ +page { + background: linear-gradient(135deg, #FDFBF7 0%, #F2EFE9 100%); + color: #4A4A4A; + font-family: 'SF Pro Display', 'PingFang SC', system-ui; + height: 100vh; + overflow: hidden; + } + + .page-root { + height: 100vh; + display: flex; + flex-direction: column; + overflow: hidden; + } + + /* 筛选栏 */ + .filter-bar { + background: rgba(255, 255, 255, 0.8); + backdrop-filter: blur(20px); + border-bottom: 1rpx solid rgba(0,0,0,0.04); + flex-shrink: 0; + padding: 20rpx 0 0; + } + + .module-scroll { + white-space: nowrap; + height: 70rpx; + padding: 0 30rpx; + } + .module-container { + display: inline-flex; + gap: 20rpx; + align-items: center; + height: 70rpx; + } + + .filter-item { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0 30rpx; + height: 56rpx; + border-radius: 28rpx; + background: #F0EFEC; + font-size: 26rpx; + color: #6B6B6B; + transition: all 0.2s; + } + .filter-item.active { + background: #C8A27A; + color: #FFFFFF; + font-weight: 600; + box-shadow: 0 4rpx 12rpx rgba(200, 162, 122, 0.3); + } + + .status-row { + display: flex; + padding: 15rpx 30rpx 20rpx; + gap: 30rpx; + } + .status-item { + font-size: 26rpx; + color: #8B8B8B; + padding-bottom: 8rpx; + border-bottom: 4rpx solid transparent; + transition: all 0.2s; + } + .status-item.active { + color: #C8A27A; + border-bottom-color: #C8A27A; + font-weight: 600; + } + + /* 列表容器 */ + .list-container { + flex: 1; + overflow: hidden; + } + .list-scroll { + height: 100%; + padding: 20rpx 0; /* 上下内边距 */ + } + + .empty-tip { + text-align: center; + color: #A0A0A0; + font-size: 28rpx; + padding-top: 200rpx; + } + + /* 卡片:统一左右边距 30rpx */ + .card { + background: #FFFFFF; + border-radius: 28rpx; + padding: 30rpx; + margin: 0 30rpx 24rpx 30rpx; /* 左右严格相等 */ + box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.04); + transition: all 0.3s; + } + + .card-header { + display: flex; + align-items: center; + margin-bottom: 24rpx; + } + .avatar { + width: 72rpx; + height: 72rpx; + border-radius: 50%; + margin-right: 18rpx; + border: 2rpx solid #F0EDE8; + background: #F7F5F2; + } + .user-info { + flex: 1; + } + .nickname { + font-size: 30rpx; + font-weight: 600; + color: #3A3A3A; + display: block; + } + .uid { + font-size: 24rpx; + color: #9A9A9A; + margin-top: 4rpx; + } + .status-tag { + font-size: 24rpx; + padding: 6rpx 18rpx; + border-radius: 20rpx; + font-weight: 500; + } + .status-0 { background: #FFF3E0; color: #EF6C00; } + .status-1 { background: #E8F5E9; color: #2E7D32; } + .status-2 { background: #FFEBEE; color: #C62828; } + .status-3 { background: #E3F2FD; color: #1565C0; } + + .card-summary { + display: flex; + flex-wrap: wrap; + gap: 16rpx 24rpx; + margin-bottom: 20rpx; + } + .summary-item { + display: flex; + align-items: center; + font-size: 26rpx; + } + .summary-item .label { + color: #8B8B8B; + margin-right: 12rpx; + } + .summary-item .value { + color: #3A3A3A; + font-weight: 500; + } + + .card-actions { + display: flex; + justify-content: space-between; + align-items: center; + padding-top: 20rpx; + border-top: 1rpx solid #F5F2ED; + } + .expand-btn { + font-size: 26rpx; + color: #C8A27A; + font-weight: 500; + } + .judge-btn { + font-size: 26rpx; + background: #C8A27A; + color: #FFFFFF; + padding: 8rpx 24rpx; + border-radius: 24rpx; + font-weight: 500; + } + + /* 展开详情(左右边距继承卡片,无需额外设置) */ + .card-detail { + margin-top: 20rpx; + padding-top: 20rpx; + border-top: 1rpx solid #F5F2ED; + } + .detail-section { + margin-bottom: 20rpx; + } + .detail-title { + font-size: 26rpx; + font-weight: 600; + color: #5A5A5A; + margin-bottom: 10rpx; + display: block; + } + .detail-text { + font-size: 24rpx; + color: #6B6B6B; + line-height: 1.6; + } + .fee-row, .reject-row { + font-size: 24rpx; + color: #6B6B6B; + padding: 6rpx 0; + } + .reject-item { + display: flex; + flex-direction: column; + background: #FFF9F5; + padding: 12rpx 16rpx; + border-radius: 12rpx; + margin-bottom: 12rpx; + } + .reject-time { + font-size: 22rpx; + color: #B0A090; + margin-bottom: 4rpx; + } + .reject-reason { + font-size: 24rpx; + color: #7A5A4A; + } + .detail-empty { + font-size: 24rpx; + color: #A0A0A0; + padding: 8rpx 0; + } + .info-grid { + display: flex; + flex-direction: column; + gap: 8rpx; + font-size: 24rpx; + color: #6B6B6B; + } + + /* 加载 */ + .loading-more, .no-more { + text-align: center; + padding: 30rpx; + font-size: 24rpx; + color: #A0A0A0; + } + .loading-mask { + position: fixed; + top: 0; left: 0; right: 0; bottom: 0; + background: rgba(253, 251, 247, 0.8); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + z-index: 999; + } + .loading-spinner { + width: 60rpx; + height: 60rpx; + border: 4rpx solid #E0DDD5; + border-top-color: #C8A27A; + border-radius: 50%; + animation: spin 0.8s linear infinite; + margin-bottom: 20rpx; + } + @keyframes spin { to { transform: rotate(360deg); } } \ No newline at end of file diff --git a/pages/kaohe_jinpai/kaohe_jinpai.js b/pages/kaohe_jinpai/kaohe_jinpai.js new file mode 100644 index 0000000..ed6fcb4 --- /dev/null +++ b/pages/kaohe_jinpai/kaohe_jinpai.js @@ -0,0 +1,461 @@ +// pages/kaohe_jinpai/kaohe_jinpai.js +const app = getApp(); +import request from '../../utils/request.js'; + +Page({ + data: { + activeTab: 'all_tags', + plateList: [], // 第一个接口返回的所有板块及标签(含 fee_list) + myTags: [], + recordList: [], + recordPage: 1, + recordHasMore: true, + recordLoading: false, + recordFilter: 'pending', + showApplyModal: false, + selectedTag: null, + applyGameNick: '', + applyRemark: '', + applyFee: 0, + applyJiluId: '', + isReapply: false, + payLoading: false, + ossImageUrl: app.globalData.ossImageUrl || '', + + // 新增:考核官相关 + kaoheguanList: [], // 所有可选的考核官 + showKaoheguanModal: false, // 考核官选择弹窗 + selectedKaoheguanId: '', // 已选择的考核官ID + selectedKaoheguanInfo: null, // 已选择的考核官信息(用于展示) + searchKeyword: '', // 搜索关键词 + filteredKaoheguanList: [], // 搜索过滤后的列表 + }, + + onLoad() { + this.fetchAllTags(); + this.fetchMyRecords(true); + }, + + switchTab(e) { + const tab = e.currentTarget.dataset.tab; + if (tab === this.data.activeTab) return; + this.setData({ activeTab: tab }); + if (tab === 'my_records') this.fetchMyRecords(true); + }, + + // 获取所有板块及标签(含费用列表),缓存到 plateList,同时获取考核官列表 + async fetchAllTags() { + try { + const res = await request({ url: '/dengji/dskhhq', method: 'POST' }); + if (res.data.code === 0) { + const { plate_list, my_tags, kaoheguan_list } = res.data.data; + // 拼接考核官头像为完整URL + const ossUrl = this.data.ossImageUrl; + const processedKgList = (kaoheguan_list || []).map(kg => ({ + ...kg, + avatar: kg.avatar && !kg.avatar.startsWith('http') ? ossUrl + kg.avatar : kg.avatar, + })); + this.setData({ + plateList: plate_list || [], + myTags: my_tags || [], + kaoheguanList: processedKgList // 使用拼接后的列表 + }); + } + } catch (e) {} + }, + + // 获取我的考核记录,并根据 plateList 计算本次/预计下次费用 + async fetchMyRecords(isRefresh = false) { + if (this.data.recordLoading) return; + const page = isRefresh ? 1 : this.data.recordPage; + this.setData({ recordLoading: true }); + try { + const res = await request({ + url: '/dengji/dskhjl', + method: 'POST', + data: { status: this.data.recordFilter, page, page_size: 5 } + }); + if (res.data.code === 0) { + const { list, has_more } = res.data.data; + + // 从 plateList 构建标签ID -> 完整标签对象的映射(用于精确匹配费用) + const tagMap = {}; + this.data.plateList.forEach(plate => { + (plate.tags || []).forEach(tag => { + tagMap[tag.id] = tag; // tag 包含 fee_list + }); + }); + + // 处理每条记录:优先使用 tag_obj.id 匹配,其次 chenghao_id + const processed = list.map(item => { + // 尝试多种方式获取标签ID + const tagId = (item.tag_obj && item.tag_obj.id) || item.chenghao_id; + const tag = tagMap[tagId] || item.tag_obj || {}; + const feeList = tag.fee_list || []; + let currentFee = 0; + let nextFee = 0; + if (feeList.length > 0) { + const cishu = item.cishu; + const cur = feeList.find(f => f.cishu === cishu); + currentFee = cur ? cur.feiyong : feeList[feeList.length - 1].feiyong; + const nextCishu = cishu + 1; + const nxt = feeList.find(f => f.cishu === nextCishu); + nextFee = nxt ? nxt.feiyong : feeList[feeList.length - 1].feiyong; + } + return { + ...item, + current_fee: currentFee, + next_fee: nextFee, + tag_obj: tag // 使用匹配到的完整标签对象,保证后续“再次考核”能拿到费用 + }; + }); + + const newList = isRefresh ? processed : [...this.data.recordList, ...processed]; + this.setData({ + recordList: newList, + recordPage: page + 1, + recordHasMore: has_more, + recordLoading: false + }); + } else { + this.setData({ recordLoading: false }); + } + } catch (e) { + this.setData({ recordLoading: false }); + } + }, + + filterRecord(e) { + const filter = e.currentTarget.dataset.filter; + if (filter === this.data.recordFilter) return; + this.setData({ recordFilter: filter, recordList: [], recordPage: 1, recordHasMore: true }); + this.fetchMyRecords(true); + }, + + onReachBottom() { + if (this.data.activeTab === 'my_records' && this.data.recordHasMore) { + this.fetchMyRecords(false); + } + }, + + // 打开申请考核弹窗,费用完全基于 plateList 中的标签数据 + openApplyModal(e) { + const { tag, jiluid, reapply } = e.currentTarget.dataset; + // 查找完整标签对象,保证 fee_list 存在 + let fullTag = tag; + if (fullTag && fullTag.fee_list === undefined) { + const found = this.findTagInPlateList(fullTag.id); + if (found) fullTag = found; + } + if (!fullTag) { + const found = this.findTagInPlateList(tag); + if (found) fullTag = found; + } + + let fee = 0; + if (fullTag) { + let nextCishu = 0; + // 再次申请时,基于原记录次数计算下一次考核次数 + if (reapply && jiluid) { + const record = this.data.recordList.find(r => r.jilu_id === jiluid); + if (record) { + nextCishu = record.cishu + 1; + } else { + nextCishu = (fullTag.my_cishu || 0) + 1; + } + } else { + nextCishu = (fullTag.my_cishu || 0) + 1; + } + const feeList = fullTag.fee_list || []; + const exact = feeList.find(f => f.cishu === nextCishu); + if (exact) { + fee = exact.feiyong; + } else if (feeList.length > 0) { + fee = feeList[feeList.length - 1].feiyong; + } + } + // 检查再次申请时原记录是否有考核官,若有则不允许指定 + let canSpecify = true; + let originalShenheguanId = ''; + if (reapply && jiluid) { + const record = this.data.recordList.find(r => r.jilu_id === jiluid); + if (record && record.shenheguan_id) { + canSpecify = false; // 已有考核官,不能指定新的 + originalShenheguanId = record.shenheguan_id; + } + } + this.setData({ + showApplyModal: true, + selectedTag: fullTag || null, + applyGameNick: '', + applyRemark: '', + applyFee: fee, + applyJiluId: jiluid || '', + isReapply: reapply || false, + selectedKaoheguanId: '', // 重置 + selectedKaoheguanInfo: null, + canSpecify: canSpecify, // 新增 + originalShenheguanId: originalShenheguanId, // 新增 + }); + }, + + // 辅助方法:从 plateList 查找指定 id 的标签 + findTagInPlateList(tagId) { + if (!tagId) return null; + for (const plate of this.data.plateList) { + for (const t of (plate.tags || [])) { + if (t.id === tagId) return t; + } + } + return null; + }, + + closeApplyModal() { + this.setData({ showApplyModal: false }); + }, + + // 实时更新游戏昵称 + onNickInput(e) { + this.setData({ applyGameNick: e.detail.value }); + }, + + // 实时更新备注 + onRemarkInput(e) { + this.setData({ applyRemark: e.detail.value }); + }, + + // 新增:打开考核官选择弹窗 + openKaoheguanModal() { + if (!this.data.canSpecify) { + wx.showToast({ title: '已有考核官,无法重新指定', icon: 'none' }); + return; + } + // 根据费用筛选可选考核官 + let all = [...this.data.kaoheguanList]; + if (this.data.applyFee > 0) { + all = all.filter(kg => kg.is_renzheng); + } + this.setData({ + showKaoheguanModal: true, + filteredKaoheguanList: all, + searchKeyword: '', + }); + }, + + // 关闭考核官选择弹窗 + closeKaoheguanModal() { + this.setData({ showKaoheguanModal: false }); + }, + + // 搜索输入 + onSearchInput(e) { + const value = e.detail.value.trim(); + this.setData({ searchKeyword: value }); + this.filterKaoheguanList(value); + }, + + // 过滤考核官列表 + filterKaoheguanList(keyword) { + let all = [...this.data.kaoheguanList]; + if (this.data.applyFee > 0) { + all = all.filter(kg => kg.is_renzheng); + } + if (keyword) { + all = all.filter(kg => + kg.yonghuid.includes(keyword) || + (kg.dashou_nick && kg.dashou_nick.includes(keyword)) + ); + } + this.setData({ filteredKaoheguanList: all }); + }, + + // 选择一个考核官 + selectKaoheguan(e) { + const kg = e.currentTarget.dataset.kg; + if (kg.shenhe_zhuangtai !== 0) { + wx.showToast({ title: '该考核官正忙,无法指定', icon: 'none' }); + return; + } + this.setData({ + selectedKaoheguanId: kg.yonghuid, + selectedKaoheguanInfo: kg, + showKaoheguanModal: false, + }); + }, + + // 取消已选择的考核官 + clearSelectedKaoheguan() { + this.setData({ + selectedKaoheguanId: '', + selectedKaoheguanInfo: null, + }); + }, + + // 提交申请 + async submitApply() { + const { applyGameNick, applyRemark, applyFee, selectedTag, isReapply, applyJiluId, selectedKaoheguanId } = this.data; + if (!applyGameNick || !applyGameNick.trim()) { + wx.showToast({ title: '请输入游戏昵称', icon: 'none' }); + return; + } + if (!applyRemark || !applyRemark.trim()) { + wx.showToast({ title: '请填写备注', icon: 'none' }); + return; + } + if (applyFee > 0) { + await this.startPayProcess(); + } else { + await this.doFreeApply(); + } + }, + + // 支付流程 + async startPayProcess() { + this.setData({ payLoading: true }); + try { + const res = await request({ + url: '/yonghu/khzf', + method: 'POST', + data: { + tag_id: this.data.selectedTag.id, + game_nick: this.data.applyGameNick, + remark: this.data.applyRemark, + reapply: this.data.isReapply, + jilu_id: this.data.applyJiluId, + shenheguan_id: this.data.selectedKaoheguanId, // 新增 + } + }); + if (res.data.code !== 200) throw new Error(res.data.message || '下单失败'); + const payParams = res.data.payParams; + const dingdanid = res.data.dingdanid; + wx.requestPayment({ + timeStamp: payParams.timeStamp, + nonceStr: payParams.nonceStr, + package: payParams.package, + signType: payParams.signType || 'MD5', + paySign: payParams.paySign, + success: async () => { + await this.pollPayStatus(dingdanid); + }, + fail: () => { + wx.showToast({ title: '支付取消或失败', icon: 'none' }); + request({ url: '/yonghu/khzfsb', method: 'POST', data: { dingdanid } }).catch(()=>{}); + } + }); + } catch (e) { + wx.showToast({ title: e.message || '支付异常', icon: 'none' }); + } finally { + this.setData({ payLoading: false }); + } + }, + + // 轮询支付结果 + async pollPayStatus(dingdanid) { + for (let i = 0; i < 10; i++) { + try { + const res = await request({ url: '/yonghu/khzffc', method: 'POST', data: { dingdanid } }); + if (res.data.code === 200) { + wx.showToast({ title: '申请成功', icon: 'success' }); + this.closeApplyModal(); + this.fetchAllTags(); + this.setData({ recordList: [], recordPage: 1 }); + this.fetchMyRecords(true); + return; + } + } catch (e) {} + await new Promise(r => setTimeout(r, 2000)); + } + wx.showToast({ title: '支付确认超时', icon: 'none' }); + }, + + // 免费申请 + async doFreeApply() { + try { + const res = await request({ + url: '/dengji/dssqkh', + method: 'POST', + data: { + tag_id: this.data.selectedTag.id, + game_nick: this.data.applyGameNick, + remark: this.data.applyRemark, + reapply: this.data.isReapply, + jilu_id: this.data.applyJiluId, + shenheguan_id: this.data.selectedKaoheguanId, // 新增 + } + }); + if (res.data.code === 0) { + wx.showToast({ title: '申请成功', icon: 'success' }); + this.closeApplyModal(); + this.fetchAllTags(); + this.setData({ recordList: [], recordPage: 1 }); + this.fetchMyRecords(true); + } else { + wx.showToast({ title: res.data.msg || '申请失败', icon: 'none' }); + } + } catch (e) { + wx.showToast({ title: '网络错误', icon: 'none' }); + } + }, + + // 联系考核官(私聊) + contactKaoheguan(e) { + const item = e.currentTarget.dataset.item; + if (!item) return; + if (!item.shenheguan_id) { + wx.showToast({ title: '暂无考核官信息', icon: 'none' }); + return; + } + const uid = wx.getStorageSync('uid'); + if (!uid) { + wx.showToast({ title: '用户信息缺失', icon: 'none' }); + return; + } + if (app.globalData.currentRole !== 'dashou') { + app.globalData.currentRole = 'dashou'; + wx.setStorageSync('currentRole', 'dashou'); + } + if (app.connectForCurrentRole) { + app.connectForCurrentRole(); + } else if (app.ensureConnection) { + app.ensureConnection(); + } + const ossImageUrl = this.data.ossImageUrl || app.globalData.ossImageUrl || ''; + let fullAvatar = item.shenheguan_avatar || ''; + if (fullAvatar && !fullAvatar.startsWith('http')) { + fullAvatar = ossImageUrl + fullAvatar; + } + if (!fullAvatar) { + fullAvatar = ossImageUrl + (app.globalData.morentouxiang || 'avatar/default.jpg'); + } + const param = { + toUserId: 'Kh' + item.shenheguan_id, + toName: '考核官' + item.shenheguan_id, + toAvatar: fullAvatar + }; + wx.navigateTo({ + url: '/pages/liaotian/liaotian?data=' + encodeURIComponent(JSON.stringify(param)), + fail: (err) => { + wx.showToast({ title: '打开聊天失败', icon: 'none' }); + } + }); + }, + + showTagDetail(e) { + const tag = e.currentTarget.dataset.tag; + const feeStr = this.formatFeeList(tag.fee_list); + wx.showModal({ + title: tag.name, + content: `规则:${tag.rule || '暂无'}\n${tag.jinpai ? '【可同时开启金牌】' : ''}\n费用:${feeStr}`, + showCancel: false, + confirmText: '知道了' + }); + }, + + formatFeeList(feeList) { + if (!feeList || feeList.length === 0) return '暂无'; + let str = feeList.map(f => `第${f.cishu}次:¥${f.feiyong}`).join(';'); + const last = feeList[feeList.length - 1]; + str += `;第${last.cishu}次及以上:¥${last.feiyong}`; + return str; + }, +}); \ No newline at end of file diff --git a/pages/kaohe_jinpai/kaohe_jinpai.json b/pages/kaohe_jinpai/kaohe_jinpai.json new file mode 100644 index 0000000..fa331e5 --- /dev/null +++ b/pages/kaohe_jinpai/kaohe_jinpai.json @@ -0,0 +1,9 @@ +{ + "usingComponents": { + "chenghao-tag": "/components/chenghao-tag/chenghao-tag", + "global-notification": "/components/global-notification/global-notification" + }, + "navigationBarTitleText": "考核金牌", + "navigationBarBackgroundColor": "#0F0A1E", + "navigationBarTextStyle": "white" + } \ No newline at end of file diff --git a/pages/kaohe_jinpai/kaohe_jinpai.wxml b/pages/kaohe_jinpai/kaohe_jinpai.wxml new file mode 100644 index 0000000..6d89494 --- /dev/null +++ b/pages/kaohe_jinpai/kaohe_jinpai.wxml @@ -0,0 +1,135 @@ + + + + + 考核标签 + 我的考核 + + + + + + + {{item.mingcheng}} + + + + 金牌 + + + {{item.cishu}}次 ¥{{item.feiyong}} + ≥{{item.fee_list.length}}次 ¥{{item.fee_list[item.fee_list.length-1].feiyong}} + + 申请考核 + 已拥有 + + + + + + + + + 进行中/未通过 + 已通过 + + + + + {{item.chenghao_name}} + {{item.statusText}} + + + 板块:{{item.bankuai_name}} + 考核官:{{item.shenheguan_id || '待分配'}} + + 联系考核官 + + 游戏ID:{{item.dashou_youxi_id}} + 备注:{{item.dashou_beizhu}} + 第{{item.cishu}}次考核 · 共缴 ¥{{item.total_fee}} + + + + 本次考核费用:¥{{item.current_fee}} + 预计下次费用:¥{{item.next_fee}} + + + 上次拒绝原因:{{item.last_fail_reason}} + + + 再次考核 + + 加载中... + —— 没有更多了 —— + + + + + + + 申请考核 - {{selectedTag.name}} + + 游戏昵称 + + + + 备注 +