修正了 pages 名为拼音的问题

This commit is contained in:
2026-06-13 10:44:02 +08:00
parent 76cc4ac55e
commit 296e41a8a7
249 changed files with 29660 additions and 29505 deletions

827
app.js
View File

@@ -1,415 +1,414 @@
// 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);
}
}
}
// app.js
import GoEasy from './static/lib/goeasy-2.13.24.esm.min';
const ChatCore = require('./utils/chat-core');
import { check } from './utils/phone-auth';
import { getPrimaryRole, lockPrimaryRole, migrateLegacyCenterRole, PRIMARY_DEFAULT_PAGES } from './utils/primary-role';
// 三端固定首页
const roleDefaultPage = PRIMARY_DEFAULT_PAGES;
App({
globalData: {
hasShownPopupOnColdStart: false,
apiBaseUrl: 'https://www.abas.asia/hqhd',
ossImageUrl: '',
morentouxiang: '',
dashouguize: '',
shangpinliebiao: [],
shangpinzhuanqu: [],
shangpinleixing: [],
shangpinlunbo: [],
shangpingonggao: '',
lunbozhanwei: '/images/lunbozhanwei.jpg',
dingdanTiaoshu: {
daifuwu: 0,
fuwuzhong: 0,
yiwancheng: 0,
yituikuan: 0
},
dashouqun: '',
dashouqunid: '',
guanshiqun: '',
guanshiqunid: '',
appId:'wx0e4be86faac4a8d1',
shangjiastatus: 0,
dashoustatus: 0,
guanshistatus: 0,
dashouNicheng: '',
zhanghaoStatus: null,
dashouzhuangtai: null,
yongjin: null,
zonge: null,
yajin: null,
chenghao: '',
jinfen: null,
clumber: [{
huiyuanid: '',
huiyuanming: '',
daoqi: ''
}],
chengjiaoliang: null,
zaixianZhuangtai: null,
guanshi: {
nicheng: '',
uid: '',
touxiang: '',
gszhstatus: '',
yaoqingzongshu: 0,
fenyongzonge: '0.00',
fenyongtixian: '0.00'
},
shangjia: {
sjzhzhuangtai: '',
sjyue: '',
fadanzong: null,
tuikuanzong: null,
riliushui: '',
yueliushui: ''
},
liaotian_liebiao: [],
xshenfen: 1,
goEasyConfig: null,
kefuConfig: {
link: '',
enterpriseId: ''
},
cosConfig: {
bucket: '',
region: 'ap-shanghai',
uploadPathPrefix: 'order/'
},
goEasyConnection: {
status: 'disconnected',
userId: '',
identityType: '',
lastConnectTime: 0,
autoReconnect: true,
reconnectAttempts: 0,
maxReconnectAttempts: 5,
heartbeatInterval: null,
cacheKeys: {
savedConnection: 'savedGoEasyConnection',
userId: 'goEasyUserId',
identityType: 'currentGoEasyIdentity',
connectTime: 'goEasyConnectTime'
},
config: {
heartbeatInterval: 20000,
reconnectDelay: 3000,
offlineTimeout: 30000,
cacheValidityHours: 12
}
},
messageManager: {
unreadTotal: 0,
unreadMap: {},
latestMessages: [],
notificationQueue: [],
notificationVisible: false,
lastNotificationTime: 0,
notificationCooldown: 3000,
tabBarIndex: 2,
showTabBarBadge: true,
customTabBar: true,
tabBarBadgeText: 0,
soundEnabled: true,
vibrationEnabled: true,
doNotDisturb: false,
doNotDisturbStart: '22:00',
doNotDisturbEnd: '08:00',
notificationStyle: {
position: 'top',
duration: 3000,
backgroundColor: '#00f7ff',
textColor: '#ffffff',
borderRadius: '16rpx',
boxShadow: '0 10rpx 30rpx rgba(0, 247, 255, 0.3)'
},
currentNotification: null,
cacheKeys: {
messageSettings: 'messageSettings',
unreadTotal: 'messageUnreadTotal',
latestMessages: 'latestMessages',
notificationMuted: 'notificationMuted'
}
},
pageState: {
currentPage: '',
isInChatPage: false,
currentChatId: '',
lastPageUpdate: 0
},
eventListeners: {},
debugMode: false,
currentUser: null,
currentRole: 'normal',
primaryRole: 'normal'
},
// 核心启动流程
async onLaunch() {
// ① 隐藏返回首页按钮
wx.hideHomeButton();
wx.onAppRoute((res) => {
wx.hideHomeButton();
const path = (res && res.path) || '';
if (path === 'pages/manager/manager' || path === 'pages/leader/leader') {
migrateLegacyCenterRole(this);
lockPrimaryRole('dashou', this);
}
});
// ② 恢复主端并跳转
migrateLegacyCenterRole(this);
const savedRole = getPrimaryRole(this);
lockPrimaryRole(savedRole, this);
const targetPage = roleDefaultPage[savedRole] || '/pages/index/index';
if (savedRole !== 'normal') {
setTimeout(() => {
wx.reLaunch({ url: targetPage });
}, 0);
}
// ③ 应用本地缓存配置
const cachedConfig = this.readConfigFromStorage();
if (cachedConfig) {
this.applyDynamicConfig(cachedConfig);
}
// ④ 初始化
this.initGoEasyWithConfig(); // 只有appkey有效时才真正初始化
ChatCore.initGlobalMessageSystem(this);
this.initCurrentUser();
// ⑤ 建立连接
const saved = this.getSavedConnection();
console.log('【启动】缓存身份:', saved ? saved.identityType : '无',
'userId:', saved ? saved.userId : '无');
setTimeout(() => {
if (this.startImWhenReady) this.startImWhenReady();
else if (this.ensureConnection) this.ensureConnection();
}, 300);
// ⑥ 获取远程配置
this.fetchConfigSafely()
.then(latestConfig => {
this.saveConfigToStorage(latestConfig);
this.applyDynamicConfig(latestConfig);
this.initGoEasyWithConfig();
this.initCurrentUser();
if (this.startImWhenReady) this.startImWhenReady();
else if (this.connectForCurrentRole) this.connectForCurrentRole();
console.log('远程配置更新完成');
})
.catch(err => {
console.warn('远程配置获取失败,继续使用本地缓存', err);
});
// ⑦ 手机号认证检查
try {
const needAuth = await check();
if (needAuth) {
wx.reLaunch({ url: '/pages/phone-auth/phone-auth' });
return;
}
} catch (e) {}
},
// 连接管理方法
getSavedConnection() {
try {
const saved = wx.getStorageSync(
this.globalData.goEasyConnection.cacheKeys.savedConnection
);
return saved || null;
} catch (e) {
return null;
}
},
// 配置缓存读写
CONFIG_CACHE_KEY: 'app_dynamic_config',
readConfigFromStorage() {
try {
const data = wx.getStorageSync(this.CONFIG_CACHE_KEY);
if (data && typeof data === 'object') {
return data;
}
} catch (e) {}
return null;
},
saveConfigToStorage(config) {
try {
if (config && typeof config === 'object') {
wx.setStorageSync(this.CONFIG_CACHE_KEY, config);
}
} catch (e) {}
},
// 获取远程配置
fetchConfigSafely() {
return new Promise((resolve) => {
wx.request({
url: this.globalData.apiBaseUrl + '/peizhi/qdpzhq',
method: 'POST',
header: { 'content-type': 'application/json' },
success: (res) => {
if (res.statusCode === 200 && res.data && res.data.code === 0 && res.data.data) {
resolve(res.data.data);
} else {
console.warn('远程配置接口返回异常,使用已有配置');
resolve(this.readConfigFromStorage() || {});
}
},
fail: (err) => {
console.warn('远程配置网络请求失败,使用已有配置', err);
resolve(this.readConfigFromStorage() || {});
}
});
});
},
// 获取远程配置
fetchDynamicConfig() {
return new Promise((resolve, reject) => {
wx.request({
url: this.globalData.apiBaseUrl + '/peizhi/qdpzhq',
method: 'POST',
header: { 'content-type': 'application/json' },
success: (res) => {
if (res.statusCode === 200 && res.data && res.data.code === 0) {
resolve(res.data.data);
} else {
reject(new Error(res.data?.msg || '接口返回错误'));
}
},
fail: reject
});
});
},
applyDynamicConfig(config) {
if (!config || typeof config !== 'object') return;
if (config.cos) {
this.globalData.ossImageUrl = config.cos.ossImageUrl || '';
this.globalData.cosConfig = {
bucket: config.cos.bucket || '',
region: config.cos.region || 'ap-shanghai',
uploadPathPrefix: config.cos.uploadPathPrefix || 'order/'
};
}
if (config.goEasy) {
this.globalData.goEasyConfig = {
host: config.goEasy.host || 'hangzhou.goeasy.io',
appkey: config.goEasy.appkey || ''
};
}
if (config.otherConfig) {
this.globalData.morentouxiang = config.otherConfig.morentouxiang || '';
this.globalData.dashouguize = config.otherConfig.dashouguize || '';
}
if (config.kefu) {
this.globalData.kefuConfig = {
link: config.kefu.link || '',
enterpriseId: config.kefu.enterpriseId || ''
};
}
},
// 仅在有效 appkey 时初始化
initGoEasyWithConfig() {
const cfg = this.globalData.goEasyConfig;
if (!cfg || !cfg.appkey) {
console.warn('GoEasy appkey 未配置,聊天功能将在配置就绪后自动恢复');
return;
}
// 避免重复初始化
if (wx.goEasy && wx.GoEasy) {
console.log('GoEasy 已初始化,跳过');
return;
}
try {
wx.goEasy = GoEasy.getInstance({
host: cfg.host || 'hangzhou.goeasy.io',
appkey: cfg.appkey,
modules: ['im', 'pubsub']
});
wx.GoEasy = GoEasy;
console.log('GoEasy 初始化成功');
if (this.startImWhenReady && wx.getStorageSync('uid')) {
setTimeout(() => this.startImWhenReady(), 100);
}
} catch (error) {
console.error('GoEasy 初始化失败:', error);
}
},
initCurrentUser() {
const uid = wx.getStorageSync('uid');
if (uid) {
this.globalData.currentUser = {
id: uid,
name: '用户' + uid.substring(0, 6),
avatar: this.globalData.ossImageUrl + this.globalData.morentouxiang
};
}
},
emitEvent(eventName, data) {
if (this.globalData.eventListeners[eventName]) {
this.globalData.eventListeners[eventName].forEach(callback => {
try { callback(data); } catch (error) { console.error(error); }
});
}
},
/** 切换角色并通知刷新 */
setCurrentRole(role) {
if (!role) return;
this.globalData.currentRole = role;
wx.setStorageSync('currentRole', role);
this.emitEvent('currentRoleChanged', { role });
},
on(eventName, callback) {
if (!this.globalData.eventListeners[eventName]) {
this.globalData.eventListeners[eventName] = [];
}
this.globalData.eventListeners[eventName].push(callback);
},
off(eventName, callback) {
if (this.globalData.eventListeners[eventName]) {
const index = this.globalData.eventListeners[eventName].indexOf(callback);
if (index > -1) {
this.globalData.eventListeners[eventName].splice(index, 1);
}
}
}
});

212
app.json
View File

@@ -1,107 +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"
{
"pages": [
"pages/index/index",
"pages/category/category",
"pages/pick-user/pick-user",
"pages/messages/messages",
"pages/mine/mine",
"pages/product-detail/product-detail",
"pages/submit/submit",
"pages/edit/edit",
"pages/fighter/fighter",
"pages/merchant/merchant",
"pages/manager/manager",
"pages/orders/orders",
"pages/order-detail/order-detail",
"pages/fighter-rules/fighter-rules",
"pages/fighter-edit/fighter-edit",
"pages/fighter-rank/fighter-rank",
"pages/fighter-recharge/fighter-recharge",
"pages/fighter-msg/fighter-msg",
"pages/fighter-orders/fighter-orders",
"pages/accept-order/accept-order",
"pages/withdraw/withdraw",
"pages/invite-fighter/invite-fighter",
"pages/my-fighter/my-fighter",
"pages/recharge-log/recharge-log",
"pages/manager-rank/manager-rank",
"pages/merchant-dispatch/merchant-dispatch",
"pages/merchant-orders/merchant-orders",
"pages/merchant-msg/merchant-msg",
"pages/merchant-rank/merchant-rank",
"pages/merchant-recharge/merchant-recharge",
"pages/merchant-order-detail/merchant-order-detail",
"pages/fighter-order-detail/fighter-order-detail",
"pages/chat/chat",
"pages/express-order/express-order",
"pages/order-pool/order-pool",
"pages/poster/poster",
"pages/gold-fighter/gold-fighter",
"pages/leader/leader",
"pages/manager-assign/manager-assign",
"pages/leader-bonus-log/leader-bonus-log",
"pages/invite-manager/invite-manager",
"pages/order-pool2/order-pool2",
"pages/group-chat/group-chat",
"pages/cs-chat/cs-chat",
"pages/verify/verify",
"components/global-notification/global-notification",
"components/popup-notice/popup-notice",
"components/order-card/order-card",
"components/order-sender/order-sender",
"pages/penalty/penalty/penalty",
"pages/penalty/components/fakuan-list/fakuan-list",
"pages/penalty/components/jifen-list/jifen-list",
"pages/penalty/components/fakuan-pay/fakuan-pay",
"pages/phone-auth/phone-auth",
"components/chenghao-tag/chenghao-tag",
"pages/assessor/assessor",
"pages/assess-score/assess-score",
"pages/assess-log/assess-log",
"pages/assess-center/assess-center",
"pages/assess-gold/assess-gold",
"pages/escort-orders/escort-orders",
"pages/withdraw/components/mode1/mode1",
"pages/withdraw/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/order-pool/order-pool",
"text": "接单池",
"iconPath": "/images/jiedanchi.png",
"selectedIconPath": "/images/jiedanchi.png"
},
{
"pagePath": "pages/order-pool2/order-pool2",
"text": "接单池",
"iconPath": "/images/jiedanchi.png",
"selectedIconPath": "/images/jiedanchi.png"
}
]
},
"style": "v2",
"sitemapLocation": "sitemap.json",
"lazyCodeLoading": "requiredComponents"
}

889
app.wxss
View File

@@ -1,10 +1,889 @@
/**app.wxss**/
.container {
height: 100%;
/* app.wxss — 全局共享样式 */
/* ========== CSS 变量 ========== */
page {
--color-price: #ff4444;
--color-text-main: #333;
--color-text-gray: #999;
--color-border-light: #f0f0f0;
--bg-white: #ffffff;
--bg-gray-hover: #f5f5f5;
--radius-lg: 30rpx;
--radius-normal: 25rpx;
--radius-full: 50%;
--padding-base: 40rpx;
--shadow-modal: 0 20rpx 60rpx rgba(0, 0, 0, 0.2);
box-sizing: border-box;
}
/* ========== 动画 ========== */
@keyframes spin {
to { transform: rotate(360deg); }
}
@keyframes pulse {
0%, 100% { opacity: 0.8; transform: scale(1); }
50% { opacity: 1; transform: scale(1.2); }
}
@keyframes modalShow {
from { opacity: 0; transform: scale(0.9); }
to { opacity: 1; transform: scale(1); }
}
@keyframes marquee {
0% { transform: translateX(100%); }
100% { transform: translateX(-100%); }
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes slideUp {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
@keyframes slideDown {
from { transform: translateY(0); }
to { transform: translateY(100%); }
}
/* ========== 加载提示 ========== */
.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: var(--radius-normal);
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: #fff;
border-radius: var(--radius-full);
animation: spin 1s linear infinite;
margin-bottom: 20rpx;
}
.loading-text {
font-size: 26rpx;
color: #fff;
}
/* ========== 价格组件 ========== */
.price-wrap {
color: var(--color-price);
font-weight: bold;
}
.price-icon {
font-size: 24rpx;
margin-right: 2rpx;
}
.price-integer {
font-size: 32rpx;
}
.price-decimal {
font-size: 24rpx;
}
/* ========== 弹窗基础 ========== */
.modal-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(5px);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
}
.modal-content {
width: 600rpx;
background: var(--bg-white);
border-radius: var(--radius-lg);
overflow: hidden;
box-shadow: var(--shadow-modal);
z-index: 1001;
animation: modalShow 0.3s ease-out;
}
.modal-header {
padding: var(--padding-base);
border-bottom: 2rpx solid var(--color-border-light);
display: flex;
justify-content: space-between;
padding: 200rpx 0;
align-items: center;
}
.modal-title {
font-size: 32rpx;
font-weight: bold;
color: var(--color-text-main);
}
.modal-close {
font-size: 40rpx;
color: var(--color-text-gray);
width: 60rpx;
height: 60rpx;
display: flex;
justify-content: center;
align-items: center;
border-radius: var(--radius-full);
flex-shrink: 0;
}
.modal-close:active {
background: var(--bg-gray-hover);
}
.modal-body {
max-height: 600rpx;
padding: var(--padding-base);
overflow-y: auto;
}
.modal-text {
font-size: 28rpx;
color: var(--color-text-main);
line-height: 1.6;
white-space: normal;
word-break: break-word;
}
/* ========== Flex 工具类 ========== */
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
.flex-between {
display: flex;
align-items: center;
justify-content: space-between;
}
.flex-column {
display: flex;
flex-direction: column;
}
.flex-column-center {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.flex-1 {
flex: 1;
min-width: 0;
}
.flex-shrink-0 {
flex-shrink: 0;
}
.flex-wrap {
flex-wrap: wrap;
}
/* ========== 文本工具类 ========== */
.text-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.text-ellipsis-2 {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
}
.text-ellipsis-3 {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
text-overflow: ellipsis;
}
.text-center {
text-align: center;
}
.text-right {
text-align: right;
}
.text-bold {
font-weight: bold;
}
.text-gray {
color: var(--color-text-gray);
}
.text-main {
color: var(--color-text-main);
}
/* ========== 安全区域 ========== */
.safe-bottom {
padding-bottom: env(safe-area-inset-bottom);
}
.safe-bottom-fixed {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding-bottom: env(safe-area-inset-bottom);
}
/* ========== 通用卡片 ========== */
.card {
background: var(--bg-white);
border-radius: var(--radius-lg);
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
overflow: hidden;
}
.card-padding {
padding: var(--padding-base);
}
/* ========== 毛玻璃 ========== */
.glass {
background: rgba(255, 255, 255, 0.72);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
}
.glass-dark {
background: rgba(0, 0, 0, 0.55);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
}
/* ========== 圆形头像 ========== */
.avatar-circle {
border-radius: var(--radius-full);
border: 2rpx solid #fff;
object-fit: cover;
flex-shrink: 0;
}
.avatar-sm {
width: 64rpx;
height: 64rpx;
}
.avatar-md {
width: 88rpx;
height: 88rpx;
}
.avatar-lg {
width: 120rpx;
height: 120rpx;
}
/* ========== 红点/角标 ========== */
.badge-dot {
position: absolute;
top: -6rpx;
right: -6rpx;
width: 16rpx;
height: 16rpx;
background: #fa5151;
border-radius: var(--radius-full);
border: 2rpx solid #fff;
}
.badge-num {
position: absolute;
top: -10rpx;
right: -10rpx;
min-width: 32rpx;
height: 32rpx;
line-height: 32rpx;
padding: 0 8rpx;
background: #fa5151;
color: #fff;
font-size: 20rpx;
font-weight: 600;
border-radius: 16rpx;
text-align: center;
border: 2rpx solid #fff;
}
/* ========== 空状态 ========== */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 40rpx;
}
.empty-icon {
width: 200rpx;
height: 200rpx;
margin-bottom: 30rpx;
opacity: 0.5;
}
.empty-text {
font-size: 30rpx;
color: var(--color-text-gray);
margin-bottom: 10rpx;
}
.empty-subtext {
font-size: 26rpx;
color: #ccc;
}
/* ========== 分割线 ========== */
.divider {
height: 2rpx;
background: var(--color-border-light);
margin: 20rpx 0;
}
.divider-light {
height: 1rpx;
background: rgba(0, 0, 0, 0.04);
margin: 16rpx 0;
}
.divider-gradient {
height: 2rpx;
background: linear-gradient(90deg, transparent, rgba(0, 0, 0, 0.1), transparent);
margin: 20rpx 0;
}
/* ========== 固定底部按钮区 ========== */
.fixed-bottom {
position: fixed;
left: 0;
right: 0;
bottom: 0;
padding: 20rpx 30rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
background: linear-gradient(transparent, rgba(255, 255, 255, 0.95) 30%);
z-index: 100;
}
/* ========== 列表项 ========== */
.list-item {
display: flex;
align-items: center;
padding: 24rpx 28rpx;
border-bottom: 1rpx solid rgba(0, 0, 0, 0.04);
}
.list-item:last-child {
border-bottom: none;
}
.list-icon {
width: 36rpx;
height: 36rpx;
margin-right: 16rpx;
flex-shrink: 0;
}
.list-text {
flex: 1;
font-size: 28rpx;
color: var(--color-text-main);
}
.list-arrow {
width: 20rpx;
height: 20rpx;
opacity: 0.4;
flex-shrink: 0;
}
/* ========== 标签 ========== */
.tag {
display: inline-flex;
align-items: center;
padding: 4rpx 14rpx;
border-radius: 999rpx;
font-size: 20rpx;
font-weight: 600;
line-height: 1.4;
}
.tag-primary {
background: rgba(64, 156, 255, 0.1);
color: #409cff;
}
.tag-success {
background: rgba(82, 196, 26, 0.1);
color: #52c41a;
}
.tag-warning {
background: rgba(250, 173, 20, 0.1);
color: #faad14;
}
.tag-danger {
background: rgba(255, 77, 79, 0.1);
color: #ff4d4f;
}
.tag-gold {
background: rgba(201, 169, 98, 0.1);
color: #C9A962;
}
/* ========== 页面容器 ========== */
.page-container {
min-height: 100vh;
position: relative;
}
.page-bg {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
pointer-events: none;
}
/* ========== 按钮 ========== */
.btn {
display: flex;
align-items: center;
justify-content: center;
border-radius: 48rpx;
font-weight: 600;
text-align: center;
border: none;
position: relative;
overflow: hidden;
transition: all 0.2s ease;
}
.btn:active {
transform: scale(0.98);
}
/* 主按钮(全宽) */
.btn-primary {
width: 100%;
height: 96rpx;
line-height: 96rpx;
font-size: 34rpx;
color: #fff;
}
.btn-primary--gold {
background: linear-gradient(135deg, #C9A962, #D4B56A);
box-shadow: 0 8rpx 20rpx rgba(201, 169, 98, 0.3);
}
.btn-primary--green {
background: linear-gradient(135deg, #52c41a, #389e0d);
box-shadow: 0 8rpx 20rpx rgba(82, 196, 26, 0.3);
}
.btn-primary--blue {
background: linear-gradient(135deg, #409cff, #3080e0);
box-shadow: 0 8rpx 20rpx rgba(64, 156, 255, 0.3);
}
.btn-primary--orange {
background: linear-gradient(135deg, #ffb347, #ff8c1a);
box-shadow: 0 8rpx 20rpx rgba(255, 140, 26, 0.3);
}
.btn-primary--purple {
background: linear-gradient(135deg, #6a11cb, #2575fc);
box-shadow: 0 8rpx 20rpx rgba(106, 17, 203, 0.3);
}
.btn-primary--red {
background: linear-gradient(135deg, #ff4d4f, #cf1322);
box-shadow: 0 8rpx 20rpx rgba(255, 77, 79, 0.3);
}
/* 次要按钮 */
.btn-secondary {
background: rgba(0, 0, 0, 0.04);
color: var(--color-text-main);
font-size: 28rpx;
padding: 16rpx 32rpx;
}
.btn-secondary:active {
background: rgba(0, 0, 0, 0.08);
}
/* 小型按钮 */
.btn-sm {
padding: 14rpx 32rpx;
font-size: 26rpx;
color: #fff;
border-radius: 40rpx;
}
.btn-sm--gold {
background: linear-gradient(135deg, #ffcc00, #ffaa00);
box-shadow: 0 6rpx 16rpx rgba(255, 170, 0, 0.3);
}
.btn-sm--green {
background: linear-gradient(135deg, #52c41a, #389e0d);
box-shadow: 0 6rpx 16rpx rgba(82, 196, 26, 0.3);
}
.btn-sm--blue {
background: linear-gradient(135deg, #409cff, #3080e0);
box-shadow: 0 6rpx 16rpx rgba(64, 156, 255, 0.3);
}
.btn-sm--red {
background: linear-gradient(135deg, #ff4d4f, #cf1322);
box-shadow: 0 6rpx 16rpx rgba(255, 77, 79, 0.3);
}
.btn-sm--dark {
background: linear-gradient(135deg, #3d4f66, #2c3e50);
box-shadow: 0 6rpx 16rpx rgba(44, 62, 80, 0.3);
}
/* 幽灵按钮(描边) */
.btn-ghost {
background: transparent;
border: 2rpx solid currentColor;
padding: 14rpx 32rpx;
font-size: 26rpx;
font-weight: 600;
}
.btn-ghost--primary {
color: #409cff;
border-color: #409cff;
}
.btn-ghost--gold {
color: #C9A962;
border-color: #C9A962;
}
/* 禁用状态 */
.btn[disabled],
.btn-disabled {
opacity: 0.5;
pointer-events: none;
}
/* ========== 输入框 ========== */
.input-rounded {
width: 100%;
height: 88rpx;
background: #fff;
border-radius: 44rpx;
padding: 0 32rpx;
font-size: 28rpx;
color: var(--color-text-main);
box-sizing: border-box;
}
border: none;
}
.input-rounded--dark {
background: rgba(255, 255, 255, 0.15);
color: #fff;
border: 1rpx solid rgba(255, 255, 255, 0.3);
}
.input-rounded--dark::placeholder {
color: rgba(255, 255, 255, 0.5);
}
.input-inline {
flex: 1;
font-size: 32rpx;
color: var(--color-text-main);
height: 50rpx;
line-height: 50rpx;
border: none;
background: transparent;
}
.input-box {
background: #F7F3ED;
border-radius: 24rpx;
padding: 0 24rpx;
}
/* ========== 搜索框 ========== */
.search-box {
display: flex;
align-items: center;
background: #f0f0f0;
border-radius: 40rpx;
padding: 0 20rpx;
height: 68rpx;
}
.search-box--glass {
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(20px);
border: 1rpx solid rgba(255, 255, 255, 0.3);
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
height: 84rpx;
border-radius: 50rpx;
padding: 0 28rpx;
}
.search-input {
flex: 1;
height: 100%;
font-size: 28rpx;
margin-left: 10rpx;
}
.search-icon {
width: 32rpx;
height: 32rpx;
opacity: 0.5;
flex-shrink: 0;
}
/* ========== Tab 标签栏 ========== */
.tab-bar {
display: flex;
background: #fff;
border-bottom: 1rpx solid #f0f0f0;
}
.tab-item {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
padding: 24rpx 0;
position: relative;
color: #666;
font-size: 28rpx;
transition: color 0.3s;
}
.tab-item--active {
color: var(--color-price);
font-weight: 600;
}
.tab-indicator {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 60rpx;
height: 6rpx;
background: var(--color-price);
border-radius: 3rpx;
}
/* 胶囊式 Tab */
.tab-capsule {
display: inline-flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 60rpx;
padding: 0 30rpx;
margin-right: 20rpx;
border-radius: 30rpx;
background: #f5f5f5;
color: #666;
font-size: 28rpx;
flex-shrink: 0;
transition: all 0.3s ease;
}
.tab-capsule--active {
background: #e8f5e9;
color: #2e7d32;
font-weight: 500;
box-shadow: 0 4rpx 12rpx rgba(46, 125, 50, 0.1);
}
/* ========== 加载遮罩 ========== */
.loading-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
}
.loading-mask--dark {
background: rgba(0, 0, 0, 0.7);
backdrop-filter: blur(10px);
}
.loading-mask--light {
background: rgba(255, 255, 255, 0.85);
}
/* 旋转器尺寸变体 */
.spinner {
border-radius: var(--radius-full);
animation: spin 0.8s linear infinite;
}
.spinner--sm {
width: 40rpx;
height: 40rpx;
border: 3rpx solid rgba(0, 0, 0, 0.08);
border-top-color: var(--color-text-main);
}
.spinner--md {
width: 60rpx;
height: 60rpx;
border: 5rpx solid rgba(0, 0, 0, 0.08);
border-top-color: var(--color-text-main);
}
.spinner--lg {
width: 80rpx;
height: 80rpx;
border: 6rpx solid rgba(0, 0, 0, 0.08);
border-top-color: var(--color-text-main);
}
.spinner--white {
border-color: rgba(255, 255, 255, 0.3);
border-top-color: #fff;
}
/* ========== 刷新按钮 ========== */
.refresh-btn {
position: fixed;
top: 36rpx;
right: 32rpx;
width: 56rpx;
height: 56rpx;
border-radius: var(--radius-full);
display: flex;
align-items: center;
justify-content: center;
z-index: 20;
}
.refresh-btn--light {
background: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(12rpx);
border: 1rpx solid rgba(255, 255, 255, 0.6);
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.05);
}
.refresh-btn--dark {
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(10rpx);
}
.refresh-icon {
width: 28rpx;
height: 28rpx;
}
/* ========== 状态标签 ========== */
.status-tag {
display: inline-flex;
align-items: center;
padding: 6rpx 16rpx;
border-radius: 20rpx;
font-size: 24rpx;
font-weight: 500;
white-space: nowrap;
}
.status-tag--danger {
background: #fff5f5;
color: #ff6b6b;
}
.status-tag--success {
background: #f0fff0;
color: #52c41a;
}
.status-tag--warning {
background: #fffbe6;
color: #faad14;
}
.status-tag--info {
background: #e6f7ff;
color: #1890ff;
}
.status-tag--default {
background: #f5f5f5;
color: #999;
}
/* ========== 加载更多 ========== */
.load-more {
display: flex;
align-items: center;
justify-content: center;
padding: 40rpx 0;
}
.load-more-text {
font-size: 26rpx;
color: var(--color-text-gray);
}
.no-more {
text-align: center;
padding: 40rpx 0;
font-size: 26rpx;
color: #ccc;
}

View File

@@ -1,368 +1,363 @@
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' });
}
}
}
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;
if (data.notificationType === 'cs' || msg.teamId) {
const teamId = data.teamId || msg.teamId;
path = '/pages/cs-chat/cs-chat';
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/group-chat/group-chat';
queryParam = {
groupId,
orderId,
groupName,
groupAvatar,
isCross
};
} else {
const toUserId = data.senderId || msg.senderId;
const toName = data.senderName || '用户';
const toAvatar = data.avatar || '';
path = '/pages/chat/chat';
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/messages/messages' });
}
});
} else {
wx.switchTab({ url: '/pages/messages/messages' });
}
}
}
});

View File

@@ -18,7 +18,6 @@ Component({
isFullscreen: false,
timer: null,
// 悬浮窗相关数据
isMinimized: false,
floatX: 20,
floatY: 200,
@@ -163,7 +162,7 @@ Component({
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);
@@ -174,7 +173,6 @@ Component({
});
},
// 🆕 关闭自定义预览
onCloseCustomPreview() {
this.setData({
showCustomPreview: false,
@@ -225,7 +223,7 @@ Component({
if (app.globalData.morentouxiang) {
avatarUrl = app.globalData.morentouxiang;
} else {
// 使用内嵌的默认头像 base64灰色圆形确保绝对显示不依赖外部图片
// 默认头像
avatarUrl = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48Y2lyY2xlIGN4PSI1MCIgY3k9IjUwIiByPSI1MCIgZmlsbD0iI2NjY2NjYyIvPjxwYXRoIGQ9Ik01MCAyNWMtMTMuOCAwLTI1IDExLjItMjUgMjUgMCAzLjIgMS4yIDYuMSAzLjIgOC4zQzM0LjQgNzAuNyA0Mi4xIDc1IDUwIDc1czE1LjYtNC4zIDIxLjgtMTEuN0M3My44IDU2LjEgNzUgNTMuMiA3NSA1MGMwLTEzLjgtMTEuMi0yNS0yNS0yNXoiIGZpbGw9IiNmZmZmZmYiLz48L3N2Zz4=';
}
@@ -319,7 +317,7 @@ Component({
return false;
},
// 🆕 阻止事件冒泡(用于预览遮罩内部点击不关闭)
// 阻止事件冒泡
stopPropagation() {}
}
});

View File

@@ -1,6 +1,5 @@
<!-- components/popup-notice/popup-notice.wxml -->
<!-- 全屏弹窗遮罩层(完全不变) -->
<view class="popup-mask" wx:if="{{visible}}" catchtouchmove="preventMove">
<view class="popup-container {{isFullscreen ? 'fullscreen' : ''}}">
<!-- 右上角操作按钮组 -->
@@ -83,7 +82,7 @@
</view>
<!-- 🆕 自定义图片预览遮罩(全屏,不触发页面生命周期) -->
<view
<view
class="custom-preview-mask"
wx:if="{{showCustomPreview}}"
catchtouchmove="preventMove"

View File

@@ -229,7 +229,7 @@
transform: translateZ(0);
}
/* 以下原有 movable-view 相关样式已弃用,但保留以维持代码完整(用户要求不删代码) */
/* 弃用样式 */
.floating-area {
display: none;
}

View File

@@ -1,109 +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 });
},
},
});
// 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/category/category', text: '分类', iconPath: '/images/fenlei.png', selectedIconPath: '/images/fenlei.png' },
{ pagePath: 'pages/messages/messages', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' },
{ pagePath: 'pages/mine/mine', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' },
],
},
dashou: {
name: '打手',
tabList: [
{ pagePath: 'pages/accept-order/accept-order', text: '接单池', iconPath: '/images/jiedanchi.png', selectedIconPath: '/images/jiedanchi.png' },
{ pagePath: 'pages/fighter-orders/fighter-orders', text: '订单', iconPath: '/images/dingdan.png', selectedIconPath: '/images/dingdan.png' },
{ pagePath: 'pages/messages/messages', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' },
{ pagePath: 'pages/fighter/fighter', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' },
],
},
shangjia: {
name: '商家',
tabList: [
{ pagePath: 'pages/merchant-orders/merchant-orders', text: '订单', iconPath: '/images/dingdan.png', selectedIconPath: '/images/dingdan.png' },
{ pagePath: 'pages/messages/messages', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' },
{ pagePath: 'pages/merchant/merchant', 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 });
},
},
});

View File

@@ -1,24 +1,24 @@
<view class="custom-tab-bar">
<view class="bar-row">
<view class="tab-capsule">
<block wx:for="{{tabList}}" wx:key="index">
<view
class="tab-item {{selectedIndex === index ? 'active' : ''}}"
data-path="{{item.pagePath}}"
data-index="{{index}}"
catchtap="switchTab"
>
<view class="badge-container" wx:if="{{item.pagePath === 'pages/xiaoxi/xiaoxi' && badgeText}}">
<view class="tab-badge">{{badgeText}}</view>
</view>
<image
class="tab-icon"
src="{{selectedIndex === index ? item.selectedIconPath : item.iconPath}}"
mode="aspectFit"
/>
<text class="tab-text">{{item.text}}</text>
</view>
</block>
</view>
</view>
</view>
<view class="custom-tab-bar">
<view class="bar-row">
<view class="tab-capsule">
<block wx:for="{{tabList}}" wx:key="index">
<view
class="tab-item {{selectedIndex === index ? 'active' : ''}}"
data-path="{{item.pagePath}}"
data-index="{{index}}"
catchtap="switchTab"
>
<view class="badge-container" wx:if="{{item.pagePath === 'pages/messages/messages' && badgeText}}">
<view class="tab-badge">{{badgeText}}</view>
</view>
<image
class="tab-icon"
src="{{selectedIndex === index ? item.selectedIconPath : item.iconPath}}"
mode="aspectFit"
/>
<text class="tab-text">{{item.text}}</text>
</view>
</block>
</view>
</view>
</view>

File diff suppressed because it is too large Load Diff

View File

@@ -1,240 +1,240 @@
// pages/shenhedating/shenhedating.js
const app = getApp();
import request from '../../utils/request.js';
Page({
data: {
// 模块列表 [{bankuai_id, mingcheng, count, is_related}]
moduleList: [],
xuanzhongBankuaiId: 0, // 0代表全部
// 审核记录列表
recordList: [],
page: 1,
pageSize: 5,
hasMore: true,
isLoading: false,
isLoadingMore: false,
scrollViewRefreshing: false,
lastRefreshTime: 0,
refreshCooldown: 2000,
// 弹窗控制
showRuleModal: false,
currentRuleContent: '',
showBeizhuModal: false,
currentBeizhuContent: '',
// 加载状态
loadingModules: false
},
onLoad() {
this.loadModules();
},
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()
};
}
},
// 加载板块模块
async loadModules() {
this.setData({ loadingModules: true });
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' });
} finally {
this.setData({ loadingModules: false });
}
},
// 选择模块(含全部)
selectModule(e) {
const bankuaiId = e.currentTarget.dataset.id;
if (bankuaiId === this.data.xuanzhongBankuaiId) return;
this.setData({
xuanzhongBankuaiId: bankuaiId,
recordList: [],
page: 1,
hasMore: true
});
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/hqshsj',
method: 'POST',
data: {
bankuai_id: this.data.xuanzhongBankuaiId || undefined,
page: page,
page_size: this.data.pageSize
}
});
this.setData({ isLoading: false, isLoadingMore: false, scrollViewRefreshing: false });
if (res && res.data.code === 0) {
const list = res.data.data.list || [];
const hasMore = res.data.data.has_more;
const ossUrl = app.globalData.ossImageUrl || '';
const processedList = list.map(item => {
let avatar = item.shenqingren_avatar || '';
if (avatar && !avatar.startsWith('http')) {
avatar = ossUrl + avatar;
}
return { ...item, shenqingren_avatar: avatar };
});
const newList = isRefresh ? processedList : [...this.data.recordList, ...processedList];
this.setData({ recordList: newList, page: page + 1, hasMore: hasMore });
if (isRefresh) this.setData({ lastRefreshTime: Date.now() });
} else {
wx.showToast({ title: '加载记录失败', icon: 'none' });
}
} catch (e) {
this.setData({ isLoading: false, isLoadingMore: false, scrollViewRefreshing: false });
wx.showToast({ title: '网络错误', icon: 'none' });
}
},
// 上拉加载更多
onReachBottom() {
const now = Date.now();
if (now - this.data.lastRefreshTime < this.data.refreshCooldown ||
this.data.isLoading || this.data.isLoadingMore) return;
if (this.data.hasMore) {
this.loadRecords(false);
}
},
// 下拉刷新
onPullDownRefresh() {
const now = Date.now();
if (now - this.data.lastRefreshTime < this.data.refreshCooldown || this.data.isLoading) {
this.setData({ scrollViewRefreshing: false });
return;
}
this.setData({ scrollViewRefreshing: true, lastRefreshTime: now, page: 1, hasMore: true });
this.loadRecords(true);
},
// 查看规则详情
showRuleDetail(e) {
const content = e.currentTarget.dataset.content;
this.setData({ showRuleModal: true, currentRuleContent: content });
},
hideRuleDetail() {
this.setData({ showRuleModal: false, currentRuleContent: '' });
},
// 查看备注详情
showBeizhuDetail(e) {
const content = e.currentTarget.dataset.content;
this.setData({ showBeizhuModal: true, currentBeizhuContent: content || '无' });
},
hideBeizhuDetail() {
this.setData({ showBeizhuModal: false, currentBeizhuContent: '' });
},
// 抢单
async onShouShen(e) {
const jiluId = e.currentTarget.dataset.jiluid;
const bankuaiId = e.currentTarget.dataset.bankuaiid;
if (!jiluId) {
wx.showToast({ title: '记录ID异常无法接单', icon: 'none' });
return;
}
const that = this;
wx.showModal({
title: '确认审核',
content: '确定要审核此用户吗?审核后请前往游戏进行审核,并严格评分。',
confirmColor: '#c9a558',
success: async (res) => {
if (res.confirm) {
wx.showLoading({ title: '接单中...', mask: true });
try {
const qdRes = await request({
url: '/dengji/shqd',
method: 'POST',
data: { jilu_id: jiluId }
});
if (qdRes && qdRes.data.code === 0) {
// 抢单成功,弹出联系打手弹窗
wx.showModal({
title: '接待成功',
content: '请认真考核,点击联系他查看打手信息并打分。',
showCancel: false,
confirmText: '联系他',
confirmColor: '#c9a558',
success: (modalRes) => {
if (modalRes.confirm) {
wx.navigateTo({
url: '/pages/kaohe_dafen/kaohe_dafen?jilu_id=' + jiluId
});
}
}
});
const newList = that.data.recordList.filter(r => r.jilu_id !== jiluId);
that.setData({ recordList: newList });
// 对应板块待审核数减1
const bkId = qdRes.data.data ? qdRes.data.data.bankuai_id : bankuaiId;
if (bkId) {
const newModules = that.data.moduleList.map(m => {
if (m.bankuai_id === bkId && m.count > 0) {
return { ...m, count: m.count - 1 };
}
return m;
});
that.setData({ moduleList: newModules });
}
} else {
const errMsg = qdRes?.data?.msg || '接单失败,请重试';
wx.showModal({
title: '接单失败',
content: errMsg,
showCancel: false,
confirmText: '知道了',
confirmColor: '#c9a558'
});
}
} catch (e) {
wx.showModal({
title: '提示',
content: '网络错误,请重试',
showCancel: false,
confirmText: '知道了'
});
} finally {
wx.hideLoading();
}
}
}
});
},
noop() {}
// pages/shenhedating/shenhedating.js
const app = getApp();
import request from '../../utils/request.js';
Page({
data: {
// 模块列表 [{bankuai_id, mingcheng, count, is_related}]
moduleList: [],
xuanzhongBankuaiId: 0, // 0代表全部
// 审核记录列表
recordList: [],
page: 1,
pageSize: 5,
hasMore: true,
isLoading: false,
isLoadingMore: false,
scrollViewRefreshing: false,
lastRefreshTime: 0,
refreshCooldown: 2000,
// 弹窗控制
showRuleModal: false,
currentRuleContent: '',
showBeizhuModal: false,
currentBeizhuContent: '',
// 加载状态
loadingModules: false
},
onLoad() {
this.loadModules();
},
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()
};
}
},
// 加载板块模块
async loadModules() {
this.setData({ loadingModules: true });
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' });
} finally {
this.setData({ loadingModules: false });
}
},
// 选择模块(含全部)
selectModule(e) {
const bankuaiId = e.currentTarget.dataset.id;
if (bankuaiId === this.data.xuanzhongBankuaiId) return;
this.setData({
xuanzhongBankuaiId: bankuaiId,
recordList: [],
page: 1,
hasMore: true
});
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/hqshsj',
method: 'POST',
data: {
bankuai_id: this.data.xuanzhongBankuaiId || undefined,
page: page,
page_size: this.data.pageSize
}
});
this.setData({ isLoading: false, isLoadingMore: false, scrollViewRefreshing: false });
if (res && res.data.code === 0) {
const list = res.data.data.list || [];
const hasMore = res.data.data.has_more;
const ossUrl = app.globalData.ossImageUrl || '';
const processedList = list.map(item => {
let avatar = item.shenqingren_avatar || '';
if (avatar && !avatar.startsWith('http')) {
avatar = ossUrl + avatar;
}
return { ...item, shenqingren_avatar: avatar };
});
const newList = isRefresh ? processedList : [...this.data.recordList, ...processedList];
this.setData({ recordList: newList, page: page + 1, hasMore: hasMore });
if (isRefresh) this.setData({ lastRefreshTime: Date.now() });
} else {
wx.showToast({ title: '加载记录失败', icon: 'none' });
}
} catch (e) {
this.setData({ isLoading: false, isLoadingMore: false, scrollViewRefreshing: false });
wx.showToast({ title: '网络错误', icon: 'none' });
}
},
// 上拉加载更多
onReachBottom() {
const now = Date.now();
if (now - this.data.lastRefreshTime < this.data.refreshCooldown ||
this.data.isLoading || this.data.isLoadingMore) return;
if (this.data.hasMore) {
this.loadRecords(false);
}
},
// 下拉刷新
onPullDownRefresh() {
const now = Date.now();
if (now - this.data.lastRefreshTime < this.data.refreshCooldown || this.data.isLoading) {
this.setData({ scrollViewRefreshing: false });
return;
}
this.setData({ scrollViewRefreshing: true, lastRefreshTime: now, page: 1, hasMore: true });
this.loadRecords(true);
},
// 查看规则详情
showRuleDetail(e) {
const content = e.currentTarget.dataset.content;
this.setData({ showRuleModal: true, currentRuleContent: content });
},
hideRuleDetail() {
this.setData({ showRuleModal: false, currentRuleContent: '' });
},
// 查看备注详情
showBeizhuDetail(e) {
const content = e.currentTarget.dataset.content;
this.setData({ showBeizhuModal: true, currentBeizhuContent: content || '无' });
},
hideBeizhuDetail() {
this.setData({ showBeizhuModal: false, currentBeizhuContent: '' });
},
// 抢单
async onShouShen(e) {
const jiluId = e.currentTarget.dataset.jiluid;
const bankuaiId = e.currentTarget.dataset.bankuaiid;
if (!jiluId) {
wx.showToast({ title: '记录ID异常无法接单', icon: 'none' });
return;
}
const that = this;
wx.showModal({
title: '确认审核',
content: '确定要审核此用户吗?审核后请前往游戏进行审核,并严格评分。',
confirmColor: '#c9a558',
success: async (res) => {
if (res.confirm) {
wx.showLoading({ title: '接单中...', mask: true });
try {
const qdRes = await request({
url: '/dengji/shqd',
method: 'POST',
data: { jilu_id: jiluId }
});
if (qdRes && qdRes.data.code === 0) {
// 抢单成功,弹出联系打手弹窗
wx.showModal({
title: '接待成功',
content: '请认真考核,点击联系他查看打手信息并打分。',
showCancel: false,
confirmText: '联系他',
confirmColor: '#c9a558',
success: (modalRes) => {
if (modalRes.confirm) {
wx.navigateTo({
url: '/pages/assess-score/assess-score?jilu_id=' + jiluId
});
}
}
});
const newList = that.data.recordList.filter(r => r.jilu_id !== jiluId);
that.setData({ recordList: newList });
// 对应板块待审核数减1
const bkId = qdRes.data.data ? qdRes.data.data.bankuai_id : bankuaiId;
if (bkId) {
const newModules = that.data.moduleList.map(m => {
if (m.bankuai_id === bkId && m.count > 0) {
return { ...m, count: m.count - 1 };
}
return m;
});
that.setData({ moduleList: newModules });
}
} else {
const errMsg = qdRes?.data?.msg || '接单失败,请重试';
wx.showModal({
title: '接单失败',
content: errMsg,
showCancel: false,
confirmText: '知道了',
confirmColor: '#c9a558'
});
}
} catch (e) {
wx.showModal({
title: '提示',
content: '网络错误,请重试',
showCancel: false,
confirmText: '知道了'
});
} finally {
wx.hideLoading();
}
}
}
});
},
noop() {}
});

View File

@@ -245,8 +245,7 @@
animation: spin 0.8s linear infinite;
margin-bottom: 20rpx;
}
@keyframes spin { to { transform: rotate(360deg); } }
/* 弹窗 */
.modal-mask {
position: fixed;

View File

@@ -1,461 +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;
},
// pages/assess-gold/assess-gold.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/chat/chat?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;
},
});

View File

@@ -1,135 +1,135 @@
<!-- pages/kaohe_jinpai/kaohe_jinpai.wxml -->
<view class="page-root">
<!-- 顶部Tab切换 -->
<view class="tab-bar">
<view class="tab-item {{activeTab === 'all_tags' ? 'active' : ''}}" data-tab="all_tags" bindtap="switchTab">考核标签</view>
<view class="tab-item {{activeTab === 'my_records' ? 'active' : ''}}" data-tab="my_records" bindtap="switchTab">我的考核</view>
</view>
<!-- 考核标签面板 -->
<view class="content-area" wx:if="{{activeTab === 'all_tags'}}">
<scroll-view scroll-y="true" class="scroll-view">
<view wx:for="{{plateList}}" wx:key="bankuai_id" class="plate-group">
<view class="plate-title">{{item.mingcheng}}</view>
<view wx:for="{{item.tags}}" wx:key="id" class="tag-card" bindtap="showTagDetail" data-tag="{{item}}">
<view class="tag-header">
<chenghao-tag mingcheng="{{item.name}}" texiaoJson="{{item.texiao_json}}" />
<text wx:if="{{item.jinpai}}" class="jinpai-badge">金牌</text>
</view>
<view class="fee-line">
<text wx:for="{{item.fee_list}}" wx:key="cishu" class="fee-item">{{item.cishu}}次 ¥{{item.feiyong}}</text>
<text wx:if="{{item.fee_list.length > 0}}" class="fee-item">≥{{item.fee_list.length}}次 ¥{{item.fee_list[item.fee_list.length-1].feiyong}}</text>
</view>
<view wx:if="{{!myTags.includes(item.name)}}" class="apply-btn" data-tag="{{item}}" bindtap="openApplyModal">申请考核</view>
<view wx:else class="owned-tip">已拥有</view>
</view>
</view>
</scroll-view>
</view>
<!-- 我的考核面板 -->
<view class="content-area" wx:if="{{activeTab === 'my_records'}}">
<view class="filter-row">
<view class="filter-tag {{recordFilter === 'pending' ? 'active' : ''}}" data-filter="pending" bindtap="filterRecord">进行中/未通过</view>
<view class="filter-tag {{recordFilter === 'passed' ? 'active' : ''}}" data-filter="passed" bindtap="filterRecord">已通过</view>
</view>
<scroll-view scroll-y="true" class="scroll-view" bindscrolltolower="onReachBottom">
<view wx:for="{{recordList}}" wx:key="jilu_id" class="record-card">
<view class="card-top">
<text class="tag-name">{{item.chenghao_name}}</text>
<text class="status-text" style="color: {{item.statusColor}}">{{item.statusText}}</text>
</view>
<view class="card-info">
<text>板块:{{item.bankuai_name}}</text>
<text>考核官:{{item.shenheguan_id || '待分配'}}</text>
<!-- 联系考核官:纯文字蓝色下划线 -->
<view wx:if="{{item.shenheguan_id}}" class="contact-text-link" data-item="{{item}}" bindtap="contactKaoheguan">联系考核官</view>
<text>游戏ID{{item.dashou_youxi_id}}</text>
<text>备注:{{item.dashou_beizhu}}</text>
<text>第{{item.cishu}}次考核 · 共缴 ¥{{item.total_fee}}</text>
<!-- 进行中/未通过时显示本次及预计下次费用 -->
<view wx:if="{{item.zhuangtai !== 1}}" class="fee-predict">
<text>本次考核费用:¥{{item.current_fee}}</text>
<text>预计下次费用:¥{{item.next_fee}}</text>
</view>
<text wx:if="{{item.last_fail_reason}}">上次拒绝原因:{{item.last_fail_reason}}</text>
</view>
<!-- 未通过时显示“再次考核”按钮,传递 tag_obj 以便计算费用 -->
<view wx:if="{{item.zhuangtai === 2}}" class="reapply-btn" data-tag="{{item.tag_obj}}" data-jiluid="{{item.jilu_id}}" data-reapply="{{true}}" bindtap="openApplyModal">再次考核</view>
</view>
<view wx:if="{{recordLoading}}" class="loading-more">加载中...</view>
<view wx:if="{{!recordHasMore && recordList.length > 0}}" class="no-more">—— 没有更多了 ——</view>
</scroll-view>
</view>
<!-- 申请考核弹窗 -->
<view wx:if="{{showApplyModal}}" class="modal-mask" bindtap="closeApplyModal">
<view class="modal-dialog" catchtap="noop">
<view class="modal-header">申请考核 - {{selectedTag.name}}</view>
<view class="form-group">
<text class="form-label">游戏昵称</text>
<input class="form-input" value="{{applyGameNick}}" bindinput="onNickInput" placeholder="请输入游戏昵称" />
</view>
<view class="form-group">
<text class="form-label">备注</text>
<textarea class="form-textarea" value="{{applyRemark}}" bindinput="onRemarkInput" placeholder="区服、时间段等(请勿填写无关内容,违者封号)" maxlength="100" />
</view>
<!-- 指定考核官区域 -->
<view class="form-group">
<text class="form-label">指定考核官(可选)</text>
<view wx:if="{{!canSpecify}}" class="kg-disabled-tip">
<text>已有考核官:{{originalShenheguanId}},不可重新指定</text>
</view>
<view wx:else>
<view wx:if="{{!selectedKaoheguanInfo}}" class="select-kg-btn" bindtap="openKaoheguanModal">选择考核官</view>
<view wx:else class="selected-kg-card">
<image class="kg-avatar" src="{{selectedKaoheguanInfo.avatar}}" mode="aspectFill" />
<view class="kg-info">
<text class="kg-uid">UID {{selectedKaoheguanInfo.yonghuid}}</text>
<text class="kg-nick" wx:if="{{selectedKaoheguanInfo.dashou_nick}}">{{selectedKaoheguanInfo.dashou_nick}}</text>
<text class="kg-status {{selectedKaoheguanInfo.is_renzheng ? 'renzheng' : 'not-renzheng'}}">{{selectedKaoheguanInfo.is_renzheng ? '已认证' : '未认证'}}</text>
</view>
<view class="clear-kg-btn" bindtap="clearSelectedKaoheguan">取消</view>
</view>
</view>
</view>
<view wx:if="{{applyFee > 0}}" class="fee-info">本次需缴纳:<text class="fee-amount">¥{{applyFee}}</text></view>
<view class="modal-actions">
<view class="btn-cancel" bindtap="closeApplyModal">取消</view>
<view class="btn-confirm" bindtap="submitApply">确认申请{{applyFee > 0 ? '并支付' : ''}}</view>
</view>
</view>
</view>
<!-- 考核官选择弹窗 -->
<view wx:if="{{showKaoheguanModal}}" class="modal-mask" bindtap="closeKaoheguanModal">
<view class="modal-dialog" catchtap="noop">
<view class="modal-header">选择考核官</view>
<view class="search-box">
<input class="search-input" value="{{searchKeyword}}" bindinput="onSearchInput" placeholder="搜索用户ID或昵称" />
</view>
<scroll-view scroll-y="true" class="kg-scroll">
<view wx:for="{{filteredKaoheguanList}}" wx:key="yonghuid" class="kg-item" data-kg="{{item}}" bindtap="selectKaoheguan">
<image class="kg-avatar" src="{{item.avatar}}" mode="aspectFill" />
<view class="kg-info">
<text class="kg-uid">UID {{item.yonghuid}}</text>
<text wx:if="{{item.dashou_nick}}" class="kg-nick">{{item.dashou_nick}}</text>
<text class="kg-status {{item.is_renzheng ? 'renzheng' : 'not-renzheng'}}">{{item.is_renzheng ? '已认证' : '未认证'}}</text>
<text class="kg-state {{item.shenhe_zhuangtai === 0 ? 'free' : 'busy'}}">{{item.shenhe_zhuangtai === 0 ? '空闲' : '忙碌'}}</text>
</view>
</view>
<view wx:if="{{filteredKaoheguanList.length === 0}}" class="empty-tip">暂无符合条件的考核官</view>
</scroll-view>
<view class="modal-actions">
<view class="btn-cancel" bindtap="closeKaoheguanModal">取消</view>
</view>
</view>
</view>
<global-notification id="global-notification" />
<!-- pages/assess-gold/assess-gold.wxml -->
<view class="page-root">
<!-- 顶部Tab切换 -->
<view class="tab-bar">
<view class="tab-item {{activeTab === 'all_tags' ? 'active' : ''}}" data-tab="all_tags" bindtap="switchTab">考核标签</view>
<view class="tab-item {{activeTab === 'my_records' ? 'active' : ''}}" data-tab="my_records" bindtap="switchTab">我的考核</view>
</view>
<!-- 考核标签面板 -->
<view class="content-area" wx:if="{{activeTab === 'all_tags'}}">
<scroll-view scroll-y="true" class="scroll-view">
<view wx:for="{{plateList}}" wx:key="bankuai_id" class="plate-group">
<view class="plate-title">{{item.mingcheng}}</view>
<view wx:for="{{item.tags}}" wx:key="id" class="tag-card" bindtap="showTagDetail" data-tag="{{item}}">
<view class="tag-header">
<chenghao-tag mingcheng="{{item.name}}" texiaoJson="{{item.texiao_json}}" />
<text wx:if="{{item.jinpai}}" class="jinpai-badge">金牌</text>
</view>
<view class="fee-line">
<text wx:for="{{item.fee_list}}" wx:key="cishu" class="fee-item">{{item.cishu}}次 ¥{{item.feiyong}}</text>
<text wx:if="{{item.fee_list.length > 0}}" class="fee-item">≥{{item.fee_list.length}}次 ¥{{item.fee_list[item.fee_list.length-1].feiyong}}</text>
</view>
<view wx:if="{{!myTags.includes(item.name)}}" class="apply-btn" data-tag="{{item}}" bindtap="openApplyModal">申请考核</view>
<view wx:else class="owned-tip">已拥有</view>
</view>
</view>
</scroll-view>
</view>
<!-- 我的考核面板 -->
<view class="content-area" wx:if="{{activeTab === 'my_records'}}">
<view class="filter-row">
<view class="filter-tag {{recordFilter === 'pending' ? 'active' : ''}}" data-filter="pending" bindtap="filterRecord">进行中/未通过</view>
<view class="filter-tag {{recordFilter === 'passed' ? 'active' : ''}}" data-filter="passed" bindtap="filterRecord">已通过</view>
</view>
<scroll-view scroll-y="true" class="scroll-view" bindscrolltolower="onReachBottom">
<view wx:for="{{recordList}}" wx:key="jilu_id" class="record-card">
<view class="card-top">
<text class="tag-name">{{item.chenghao_name}}</text>
<text class="status-text" style="color: {{item.statusColor}}">{{item.statusText}}</text>
</view>
<view class="card-info">
<text>板块:{{item.bankuai_name}}</text>
<text>考核官:{{item.shenheguan_id || '待分配'}}</text>
<!-- 联系考核官:纯文字蓝色下划线 -->
<view wx:if="{{item.shenheguan_id}}" class="contact-text-link" data-item="{{item}}" bindtap="contactKaoheguan">联系考核官</view>
<text>游戏ID{{item.dashou_youxi_id}}</text>
<text>备注:{{item.dashou_beizhu}}</text>
<text>第{{item.cishu}}次考核 · 共缴 ¥{{item.total_fee}}</text>
<!-- 进行中/未通过时显示本次及预计下次费用 -->
<view wx:if="{{item.zhuangtai !== 1}}" class="fee-predict">
<text>本次考核费用:¥{{item.current_fee}}</text>
<text>预计下次费用:¥{{item.next_fee}}</text>
</view>
<text wx:if="{{item.last_fail_reason}}">上次拒绝原因:{{item.last_fail_reason}}</text>
</view>
<!-- 未通过时显示“再次考核”按钮,传递 tag_obj 以便计算费用 -->
<view wx:if="{{item.zhuangtai === 2}}" class="reapply-btn" data-tag="{{item.tag_obj}}" data-jiluid="{{item.jilu_id}}" data-reapply="{{true}}" bindtap="openApplyModal">再次考核</view>
</view>
<view wx:if="{{recordLoading}}" class="loading-more">加载中...</view>
<view wx:if="{{!recordHasMore && recordList.length > 0}}" class="no-more">—— 没有更多了 ——</view>
</scroll-view>
</view>
<!-- 申请考核弹窗 -->
<view wx:if="{{showApplyModal}}" class="modal-mask" bindtap="closeApplyModal">
<view class="modal-dialog" catchtap="noop">
<view class="modal-header">申请考核 - {{selectedTag.name}}</view>
<view class="form-group">
<text class="form-label">游戏昵称</text>
<input class="form-input" value="{{applyGameNick}}" bindinput="onNickInput" placeholder="请输入游戏昵称" />
</view>
<view class="form-group">
<text class="form-label">备注</text>
<textarea class="form-textarea" value="{{applyRemark}}" bindinput="onRemarkInput" placeholder="区服、时间段等(请勿填写无关内容,违者封号)" maxlength="100" />
</view>
<!-- 指定考核官区域 -->
<view class="form-group">
<text class="form-label">指定考核官(可选)</text>
<view wx:if="{{!canSpecify}}" class="kg-disabled-tip">
<text>已有考核官:{{originalShenheguanId}},不可重新指定</text>
</view>
<view wx:else>
<view wx:if="{{!selectedKaoheguanInfo}}" class="select-kg-btn" bindtap="openKaoheguanModal">选择考核官</view>
<view wx:else class="selected-kg-card">
<image class="kg-avatar" src="{{selectedKaoheguanInfo.avatar}}" mode="aspectFill" />
<view class="kg-info">
<text class="kg-uid">UID {{selectedKaoheguanInfo.yonghuid}}</text>
<text class="kg-nick" wx:if="{{selectedKaoheguanInfo.dashou_nick}}">{{selectedKaoheguanInfo.dashou_nick}}</text>
<text class="kg-status {{selectedKaoheguanInfo.is_renzheng ? 'renzheng' : 'not-renzheng'}}">{{selectedKaoheguanInfo.is_renzheng ? '已认证' : '未认证'}}</text>
</view>
<view class="clear-kg-btn" bindtap="clearSelectedKaoheguan">取消</view>
</view>
</view>
</view>
<view wx:if="{{applyFee > 0}}" class="fee-info">本次需缴纳:<text class="fee-amount">¥{{applyFee}}</text></view>
<view class="modal-actions">
<view class="btn-cancel" bindtap="closeApplyModal">取消</view>
<view class="btn-confirm" bindtap="submitApply">确认申请{{applyFee > 0 ? '并支付' : ''}}</view>
</view>
</view>
</view>
<!-- 考核官选择弹窗 -->
<view wx:if="{{showKaoheguanModal}}" class="modal-mask" bindtap="closeKaoheguanModal">
<view class="modal-dialog" catchtap="noop">
<view class="modal-header">选择考核官</view>
<view class="search-box">
<input class="search-input" value="{{searchKeyword}}" bindinput="onSearchInput" placeholder="搜索用户ID或昵称" />
</view>
<scroll-view scroll-y="true" class="kg-scroll">
<view wx:for="{{filteredKaoheguanList}}" wx:key="yonghuid" class="kg-item" data-kg="{{item}}" bindtap="selectKaoheguan">
<image class="kg-avatar" src="{{item.avatar}}" mode="aspectFill" />
<view class="kg-info">
<text class="kg-uid">UID {{item.yonghuid}}</text>
<text wx:if="{{item.dashou_nick}}" class="kg-nick">{{item.dashou_nick}}</text>
<text class="kg-status {{item.is_renzheng ? 'renzheng' : 'not-renzheng'}}">{{item.is_renzheng ? '已认证' : '未认证'}}</text>
<text class="kg-state {{item.shenhe_zhuangtai === 0 ? 'free' : 'busy'}}">{{item.shenhe_zhuangtai === 0 ? '空闲' : '忙碌'}}</text>
</view>
</view>
<view wx:if="{{filteredKaoheguanList.length === 0}}" class="empty-tip">暂无符合条件的考核官</view>
</scroll-view>
<view class="modal-actions">
<view class="btn-cancel" bindtap="closeKaoheguanModal">取消</view>
</view>
</view>
</view>
<global-notification id="global-notification" />
</view>

View File

@@ -1,385 +1,385 @@
/* pages/kaohe_jinpai/kaohe_jinpai.wxss - 晨雾微光风格 */
.page-root {
min-height: 100vh;
background: linear-gradient(135deg, #F5F7FA 0%, #E4E8F0 100%);
display: flex;
flex-direction: column;
overflow: hidden;
}
.tab-bar {
display: flex;
padding: 30rpx 50rpx;
gap: 60rpx;
background: rgba(255, 255, 255, 0.65);
backdrop-filter: blur(15px);
border-bottom: 1rpx solid rgba(0, 0, 0, 0.04);
}
.tab-item {
font-size: 32rpx;
color: #6B7280;
padding-bottom: 10rpx;
border-bottom: 4rpx solid transparent;
transition: all 0.3s;
}
.tab-item.active {
color: #4C5BF0;
border-bottom-color: #4C5BF0;
font-weight: 600;
}
.content-area {
flex: 1;
overflow: hidden;
}
.scroll-view {
height: 100%;
padding: 30rpx 0;
}
.plate-group {
margin-bottom: 50rpx;
}
.plate-title {
font-size: 34rpx;
font-weight: 700;
color: #1F2937;
margin-bottom: 20rpx;
padding-left: 30rpx;
border-left: 6rpx solid #4C5BF0;
margin-left: 30rpx;
margin-right: 30rpx;
}
.tag-card {
background: #FFFFFF;
border-radius: 28rpx;
padding: 30rpx;
margin: 0 30rpx 20rpx 30rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.03);
border: 1rpx solid rgba(0, 0, 0, 0.04);
}
.tag-header {
display: flex;
align-items: center;
gap: 16rpx;
margin-bottom: 18rpx;
}
.jinpai-badge {
font-size: 22rpx;
color: #F59E0B;
background: rgba(245, 158, 11, 0.1);
padding: 4rpx 14rpx;
border-radius: 20rpx;
font-weight: 600;
}
.fee-line {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
margin-bottom: 20rpx;
}
.fee-item {
font-size: 24rpx;
color: #4B5563;
background: #F3F4F6;
padding: 6rpx 16rpx;
border-radius: 20rpx;
}
.apply-btn {
display: flex;
align-items: center;
justify-content: center;
height: 80rpx;
background: linear-gradient(135deg, #4C5BF0, #6D7AF5);
color: #FFFFFF;
border-radius: 34rpx;
font-size: 28rpx;
font-weight: 600;
letter-spacing: 2rpx;
transition: all 0.2s;
}
.apply-btn:active {
opacity: 0.85;
transform: scale(0.98);
}
.owned-tip {
display: flex;
align-items: center;
justify-content: center;
height: 80rpx;
color: #10B981;
font-size: 28rpx;
font-weight: 600;
}
/* 记录卡片 */
.record-card {
background: #FFFFFF;
border-radius: 28rpx;
padding: 30rpx;
margin: 0 30rpx 20rpx 30rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.03);
border: 1rpx solid rgba(0, 0, 0, 0.04);
}
.card-top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.tag-name { font-size: 32rpx; font-weight: 600; color: #1F2937; }
.status-text { font-size: 26rpx; }
.card-info {
display: flex;
flex-direction: column;
gap: 10rpx;
font-size: 26rpx;
color: #4B5563;
}
/* 联系考核官:蓝色下划线 */
.contact-text-link {
color: #4C5BF0;
text-decoration: underline;
font-size: 26rpx;
}
.fee-predict {
background: #F3F4F6;
padding: 12rpx 16rpx;
border-radius: 12rpx;
margin-top: 10rpx;
display: flex;
flex-direction: column;
gap: 6rpx;
font-size: 24rpx;
color: #4B5563;
}
.reapply-btn {
display: flex;
align-items: center;
justify-content: center;
height: 80rpx;
margin-top: 20rpx;
background: #F59E0B;
color: #FFFFFF;
border-radius: 34rpx;
font-size: 28rpx;
font-weight: 600;
}
.reapply-btn:active {
opacity: 0.85;
transform: scale(0.98);
}
.filter-row {
display: flex;
padding: 20rpx 30rpx;
gap: 40rpx;
}
.filter-tag {
font-size: 28rpx;
color: #6B7280;
padding-bottom: 8rpx;
border-bottom: 3rpx solid transparent;
}
.filter-tag.active {
color: #4C5BF0;
border-bottom-color: #4C5BF0;
}
/* 弹窗 */
.modal-mask {
position: fixed; top:0; left:0; width:100%; height:100%;
background: rgba(0, 0, 0, 0.4);
display: flex; align-items: center; justify-content: center;
z-index: 1000;
}
.modal-dialog {
width: 85%;
background: #FFFFFF;
border-radius: 36rpx;
padding: 40rpx;
box-shadow: 0 20rpx 40rpx rgba(0, 0, 0, 0.15);
}
.modal-header { font-size: 34rpx; font-weight: 700; color: #1F2937; margin-bottom: 30rpx; }
.form-group { margin-bottom: 25rpx; }
.form-label { font-size: 28rpx; color: #4B5563; margin-bottom: 10rpx; display: block; }
.form-input, .form-textarea {
width: 100%;
background: #F9FAFB;
border-radius: 14rpx;
padding: 16rpx;
font-size: 28rpx;
color: #1F2937;
border: 1rpx solid #E5E7EB;
}
.form-textarea { height: 150rpx; }
.fee-info { text-align: center; font-size: 30rpx; margin: 20rpx 0; color: #F59E0B; }
.fee-amount { font-weight: 700; }
.modal-actions { display: flex; gap: 20rpx; margin-top: 30rpx; }
.btn-cancel, .btn-confirm {
flex: 1;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 40rpx;
font-size: 30rpx;
font-weight: 600;
transition: all 0.2s;
}
.btn-cancel { background: #F3F4F6; color: #4B5563; }
.btn-cancel:active { background: #E5E7EB; }
.btn-confirm { background: linear-gradient(135deg, #4C5BF0, #6D7AF5); color: #FFFFFF; }
.btn-confirm:active { opacity: 0.85; transform: scale(0.98); }
.loading-more, .no-more { text-align: center; padding: 30rpx; color: #9CA3AF; font-size: 26rpx; }
.kg-avatar {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
margin-right: 8rpx;
vertical-align: middle;
}
/* 原有样式保留,以下是新增样式 */
/* 指定考核官选择按钮 */
.select-kg-btn {
display: flex;
align-items: center;
justify-content: center;
height: 80rpx;
background: #F3F4F6;
color: #4B5563;
border-radius: 34rpx;
font-size: 28rpx;
margin-top: 10rpx;
}
.select-kg-btn:active {
background: #E5E7EB;
}
/* 已选择的考核官卡片 */
.selected-kg-card {
display: flex;
align-items: center;
background: #F9FAFB;
border-radius: 20rpx;
padding: 20rpx;
margin-top: 10rpx;
border: 1rpx solid #E5E7EB;
}
.kg-avatar {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
margin-right: 16rpx;
}
.kg-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 4rpx;
}
.kg-uid {
font-size: 28rpx;
font-weight: 600;
color: #1F2937;
}
.kg-nick {
font-size: 24rpx;
color: #6B7280;
}
.kg-status {
font-size: 22rpx;
padding: 2rpx 12rpx;
border-radius: 20rpx;
display: inline-block;
width: fit-content;
}
.kg-status.renzheng {
background: rgba(16, 185, 129, 0.1);
color: #10B981;
}
.kg-status.not-renzheng {
background: rgba(245, 158, 11, 0.1);
color: #F59E0B;
}
.clear-kg-btn {
color: #EF4444;
font-size: 26rpx;
padding: 0 10rpx;
}
.clear-kg-btn:active {
opacity: 0.7;
}
/* 禁用指定提示 */
.kg-disabled-tip {
background: #FEF3C7;
padding: 16rpx;
border-radius: 12rpx;
font-size: 24rpx;
color: #92400E;
margin-top: 10rpx;
}
/* 考核官选择弹窗内搜索 */
.search-box {
margin-bottom: 20rpx;
}
.search-input {
width: 100%;
background: #F9FAFB;
border-radius: 14rpx;
padding: 16rpx;
font-size: 28rpx;
border: 1rpx solid #E5E7EB;
}
/* 考核官滚动列表 */
.kg-scroll {
max-height: 500rpx;
margin-bottom: 20rpx;
}
/* 单个考核官项 */
.kg-item {
display: flex;
align-items: center;
padding: 20rpx;
background: #FFFFFF;
border-radius: 16rpx;
margin-bottom: 12rpx;
border: 1rpx solid #F3F4F6;
}
.kg-item:active {
background: #F9FAFB;
}
.kg-item .kg-state {
font-size: 22rpx;
margin-top: 4rpx;
}
.kg-state.free {
color: #10B981;
}
.kg-state.busy {
color: #EF4444;
}
.empty-tip {
text-align: center;
padding: 30rpx;
color: #9CA3AF;
font-size: 26rpx;
/* pages/assess-gold/assess-gold.wxss - 晨雾微光风格 */
.page-root {
min-height: 100vh;
background: linear-gradient(135deg, #F5F7FA 0%, #E4E8F0 100%);
display: flex;
flex-direction: column;
overflow: hidden;
}
.tab-bar {
display: flex;
padding: 30rpx 50rpx;
gap: 60rpx;
background: rgba(255, 255, 255, 0.65);
backdrop-filter: blur(15px);
border-bottom: 1rpx solid rgba(0, 0, 0, 0.04);
}
.tab-item {
font-size: 32rpx;
color: #6B7280;
padding-bottom: 10rpx;
border-bottom: 4rpx solid transparent;
transition: all 0.3s;
}
.tab-item.active {
color: #4C5BF0;
border-bottom-color: #4C5BF0;
font-weight: 600;
}
.content-area {
flex: 1;
overflow: hidden;
}
.scroll-view {
height: 100%;
padding: 30rpx 0;
}
.plate-group {
margin-bottom: 50rpx;
}
.plate-title {
font-size: 34rpx;
font-weight: 700;
color: #1F2937;
margin-bottom: 20rpx;
padding-left: 30rpx;
border-left: 6rpx solid #4C5BF0;
margin-left: 30rpx;
margin-right: 30rpx;
}
.tag-card {
background: #FFFFFF;
border-radius: 28rpx;
padding: 30rpx;
margin: 0 30rpx 20rpx 30rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.03);
border: 1rpx solid rgba(0, 0, 0, 0.04);
}
.tag-header {
display: flex;
align-items: center;
gap: 16rpx;
margin-bottom: 18rpx;
}
.jinpai-badge {
font-size: 22rpx;
color: #F59E0B;
background: rgba(245, 158, 11, 0.1);
padding: 4rpx 14rpx;
border-radius: 20rpx;
font-weight: 600;
}
.fee-line {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
margin-bottom: 20rpx;
}
.fee-item {
font-size: 24rpx;
color: #4B5563;
background: #F3F4F6;
padding: 6rpx 16rpx;
border-radius: 20rpx;
}
.apply-btn {
display: flex;
align-items: center;
justify-content: center;
height: 80rpx;
background: linear-gradient(135deg, #4C5BF0, #6D7AF5);
color: #FFFFFF;
border-radius: 34rpx;
font-size: 28rpx;
font-weight: 600;
letter-spacing: 2rpx;
transition: all 0.2s;
}
.apply-btn:active {
opacity: 0.85;
transform: scale(0.98);
}
.owned-tip {
display: flex;
align-items: center;
justify-content: center;
height: 80rpx;
color: #10B981;
font-size: 28rpx;
font-weight: 600;
}
/* 记录卡片 */
.record-card {
background: #FFFFFF;
border-radius: 28rpx;
padding: 30rpx;
margin: 0 30rpx 20rpx 30rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.03);
border: 1rpx solid rgba(0, 0, 0, 0.04);
}
.card-top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.tag-name { font-size: 32rpx; font-weight: 600; color: #1F2937; }
.status-text { font-size: 26rpx; }
.card-info {
display: flex;
flex-direction: column;
gap: 10rpx;
font-size: 26rpx;
color: #4B5563;
}
/* 联系考核官:蓝色下划线 */
.contact-text-link {
color: #4C5BF0;
text-decoration: underline;
font-size: 26rpx;
}
.fee-predict {
background: #F3F4F6;
padding: 12rpx 16rpx;
border-radius: 12rpx;
margin-top: 10rpx;
display: flex;
flex-direction: column;
gap: 6rpx;
font-size: 24rpx;
color: #4B5563;
}
.reapply-btn {
display: flex;
align-items: center;
justify-content: center;
height: 80rpx;
margin-top: 20rpx;
background: #F59E0B;
color: #FFFFFF;
border-radius: 34rpx;
font-size: 28rpx;
font-weight: 600;
}
.reapply-btn:active {
opacity: 0.85;
transform: scale(0.98);
}
.filter-row {
display: flex;
padding: 20rpx 30rpx;
gap: 40rpx;
}
.filter-tag {
font-size: 28rpx;
color: #6B7280;
padding-bottom: 8rpx;
border-bottom: 3rpx solid transparent;
}
.filter-tag.active {
color: #4C5BF0;
border-bottom-color: #4C5BF0;
}
/* 弹窗 */
.modal-mask {
position: fixed; top:0; left:0; width:100%; height:100%;
background: rgba(0, 0, 0, 0.4);
display: flex; align-items: center; justify-content: center;
z-index: 1000;
}
.modal-dialog {
width: 85%;
background: #FFFFFF;
border-radius: 36rpx;
padding: 40rpx;
box-shadow: 0 20rpx 40rpx rgba(0, 0, 0, 0.15);
}
.modal-header { font-size: 34rpx; font-weight: 700; color: #1F2937; margin-bottom: 30rpx; }
.form-group { margin-bottom: 25rpx; }
.form-label { font-size: 28rpx; color: #4B5563; margin-bottom: 10rpx; display: block; }
.form-input, .form-textarea {
width: 100%;
background: #F9FAFB;
border-radius: 14rpx;
padding: 16rpx;
font-size: 28rpx;
color: #1F2937;
border: 1rpx solid #E5E7EB;
}
.form-textarea { height: 150rpx; }
.fee-info { text-align: center; font-size: 30rpx; margin: 20rpx 0; color: #F59E0B; }
.fee-amount { font-weight: 700; }
.modal-actions { display: flex; gap: 20rpx; margin-top: 30rpx; }
.btn-cancel, .btn-confirm {
flex: 1;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 40rpx;
font-size: 30rpx;
font-weight: 600;
transition: all 0.2s;
}
.btn-cancel { background: #F3F4F6; color: #4B5563; }
.btn-cancel:active { background: #E5E7EB; }
.btn-confirm { background: linear-gradient(135deg, #4C5BF0, #6D7AF5); color: #FFFFFF; }
.btn-confirm:active { opacity: 0.85; transform: scale(0.98); }
.loading-more, .no-more { text-align: center; padding: 30rpx; color: #9CA3AF; font-size: 26rpx; }
.kg-avatar {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
margin-right: 8rpx;
vertical-align: middle;
}
/* 原有样式保留,以下是新增样式 */
/* 指定考核官选择按钮 */
.select-kg-btn {
display: flex;
align-items: center;
justify-content: center;
height: 80rpx;
background: #F3F4F6;
color: #4B5563;
border-radius: 34rpx;
font-size: 28rpx;
margin-top: 10rpx;
}
.select-kg-btn:active {
background: #E5E7EB;
}
/* 已选择的考核官卡片 */
.selected-kg-card {
display: flex;
align-items: center;
background: #F9FAFB;
border-radius: 20rpx;
padding: 20rpx;
margin-top: 10rpx;
border: 1rpx solid #E5E7EB;
}
.kg-avatar {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
margin-right: 16rpx;
}
.kg-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 4rpx;
}
.kg-uid {
font-size: 28rpx;
font-weight: 600;
color: #1F2937;
}
.kg-nick {
font-size: 24rpx;
color: #6B7280;
}
.kg-status {
font-size: 22rpx;
padding: 2rpx 12rpx;
border-radius: 20rpx;
display: inline-block;
width: fit-content;
}
.kg-status.renzheng {
background: rgba(16, 185, 129, 0.1);
color: #10B981;
}
.kg-status.not-renzheng {
background: rgba(245, 158, 11, 0.1);
color: #F59E0B;
}
.clear-kg-btn {
color: #EF4444;
font-size: 26rpx;
padding: 0 10rpx;
}
.clear-kg-btn:active {
opacity: 0.7;
}
/* 禁用指定提示 */
.kg-disabled-tip {
background: #FEF3C7;
padding: 16rpx;
border-radius: 12rpx;
font-size: 24rpx;
color: #92400E;
margin-top: 10rpx;
}
/* 考核官选择弹窗内搜索 */
.search-box {
margin-bottom: 20rpx;
}
.search-input {
width: 100%;
background: #F9FAFB;
border-radius: 14rpx;
padding: 16rpx;
font-size: 28rpx;
border: 1rpx solid #E5E7EB;
}
/* 考核官滚动列表 */
.kg-scroll {
max-height: 500rpx;
margin-bottom: 20rpx;
}
/* 单个考核官项 */
.kg-item {
display: flex;
align-items: center;
padding: 20rpx;
background: #FFFFFF;
border-radius: 16rpx;
margin-bottom: 12rpx;
border: 1rpx solid #F3F4F6;
}
.kg-item:active {
background: #F9FAFB;
}
.kg-item .kg-state {
font-size: 22rpx;
margin-top: 4rpx;
}
.kg-state.free {
color: #10B981;
}
.kg-state.busy {
color: #EF4444;
}
.empty-tip {
text-align: center;
padding: 30rpx;
color: #9CA3AF;
font-size: 26rpx;
}

View File

@@ -1,149 +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() {}
// pages/assess-log/assess-log.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/assess-score/assess-score?jilu_id=' + item.jilu_id });
},
// 阻止冒泡
noop() {}
});

View File

@@ -1,130 +1,130 @@
<!-- pages/kaohe_jilu/kaohe_jilu.wxml -->
<view class="page-root">
<!-- 顶部筛选区 -->
<view class="filter-bar">
<!-- 板块筛选 -->
<scroll-view class="module-scroll" scroll-x enhanced show-scrollbar="{{false}}">
<view class="module-container">
<view class="filter-item {{xuanzhongBankuaiId === 0 ? 'active' : ''}}"
data-id="{{0}}" bindtap="selectModule">
<text>全部板块</text>
</view>
<block wx:for="{{moduleList}}" wx:key="bankuai_id">
<view class="filter-item {{xuanzhongBankuaiId === item.bankuai_id ? 'active' : ''}}"
data-id="{{item.bankuai_id}}" bindtap="selectModule">
<text>{{item.mingcheng}}</text>
</view>
</block>
</view>
</scroll-view>
<!-- 状态筛选 -->
<view class="status-row">
<view class="status-item {{statusFilter === 0 ? 'active' : ''}}" data-status="{{0}}" bindtap="selectStatus">全部</view>
<view class="status-item {{statusFilter === 1 ? 'active' : ''}}" data-status="{{1}}" bindtap="selectStatus">审核中</view>
<view class="status-item {{statusFilter === 2 ? 'active' : ''}}" data-status="{{2}}" bindtap="selectStatus">已通过</view>
<view class="status-item {{statusFilter === 3 ? 'active' : ''}}" data-status="{{3}}" bindtap="selectStatus">未通过</view>
</view>
</view>
<!-- 列表区域 -->
<view class="list-container">
<scroll-view class="list-scroll" scroll-y enhanced show-scrollbar="{{false}}"
bindscrolltolower="onReachBottom">
<view class="record-list">
<view wx:if="{{!isLoading && recordList.length === 0}}" class="empty-tip">暂无考核记录</view>
<block wx:for="{{recordList}}" wx:key="jilu_id">
<view class="card">
<!-- 头部信息 -->
<view class="card-header">
<image class="avatar" src="{{item.shenqingren_avatar}}" mode="aspectFill" />
<view class="user-info">
<text class="nickname">{{item.shenqingren_nicheng || '用户'}}</text>
<text class="uid">UID {{item.shenqingren_id}}</text>
</view>
<view class="status-tag status-{{item.zhuangtai}}">
{{item.zhuangtai == 0 ? '审核中' : (item.zhuangtai == 1 ? '已通过' : (item.zhuangtai == 2 ? '未通过' : '待审核'))}}
</view>
</view>
<!-- 摘要信息 -->
<view class="card-summary">
<view class="summary-item">
<text class="label">板块</text>
<text class="value">{{item.bankuai_mingcheng}}</text>
</view>
<view class="summary-item">
<text class="label">标签</text>
<chenghao-tag mingcheng="{{item.chenghao_mingcheng}}" texiaoJson="{{item.chenghao_texiao_json}}" />
</view>
<view class="summary-item">
<text class="label">次数</text>
<text class="value">{{item.cishu}}次</text>
</view>
<view class="summary-item">
<text class="label">总费用</text>
<text class="value">¥{{item.total_fee}}</text>
</view>
</view>
<!-- 操作行 -->
<view class="card-actions">
<view class="expand-btn" data-id="{{item.jilu_id}}" bindtap="toggleExpand">
{{expandedIds.indexOf(item.jilu_id) > -1 ? '收起详情' : '展开详情'}}
</view>
<view wx:if="{{item.zhuangtai == 0}}" class="judge-btn" data-item="{{item}}" bindtap="goJudge">
去评判
</view>
</view>
<!-- 展开的详细内容 -->
<view class="card-detail" wx:if="{{expandedIds.indexOf(item.jilu_id) > -1}}">
<view class="detail-section" wx:if="{{item.guize_neirong}}">
<text class="detail-title">考核规则</text>
<text class="detail-text">{{item.guize_neirong}}</text>
</view>
<view class="detail-section">
<text class="detail-title">缴纳记录</text>
<view wx:for="{{item.fee_list}}" wx:key="cishu" class="fee-row">
<text>第{{item.cishu}}次:¥{{item.jine}}</text>
</view>
<text wx:if="{{!item.fee_list || item.fee_list.length === 0}}" class="detail-empty">暂无缴纳记录</text>
</view>
<view class="detail-section">
<text class="detail-title">拒绝记录</text>
<view wx:for="{{item.reject_list}}" wx:key="cishu" class="reject-row">
<view class="reject-item">
<text class="reject-time">{{item.create_time}}</text>
<text class="reject-reason">第{{item.cishu}}次拒绝:{{item.yuanyin}}</text>
</view>
</view>
<text wx:if="{{!item.reject_list || item.reject_list.length === 0}}" class="detail-empty">暂无拒绝记录</text>
</view>
<view class="detail-section">
<text class="detail-title">其他信息</text>
<view class="info-grid">
<text>打手游戏ID{{item.dashou_youxi_id || '无'}}</text>
<text>打手备注:{{item.dashou_beizhu || '无'}}</text>
<text>审核官游戏ID{{item.shenheguan_youxi_id || '无'}}</text>
<text>创建时间:{{item.create_time}}</text>
<text>更新时间:{{item.update_time}}</text>
</view>
</view>
</view>
</view>
</block>
<view wx:if="{{isLoadingMore}}" class="loading-more">加载更多...</view>
<view wx:if="{{!hasMore && recordList.length > 0}}" class="no-more">—— 没有更多了 ——</view>
</view>
</scroll-view>
</view>
<!-- 加载遮罩 -->
<view wx:if="{{isLoading && recordList.length === 0}}" class="loading-mask">
<view class="loading-spinner"></view>
<text>加载中...</text>
</view>
</view>
<!-- pages/assess-log/assess-log.wxml -->
<view class="page-root">
<!-- 顶部筛选区 -->
<view class="filter-bar">
<!-- 板块筛选 -->
<scroll-view class="module-scroll" scroll-x enhanced show-scrollbar="{{false}}">
<view class="module-container">
<view class="filter-item {{xuanzhongBankuaiId === 0 ? 'active' : ''}}"
data-id="{{0}}" bindtap="selectModule">
<text>全部板块</text>
</view>
<block wx:for="{{moduleList}}" wx:key="bankuai_id">
<view class="filter-item {{xuanzhongBankuaiId === item.bankuai_id ? 'active' : ''}}"
data-id="{{item.bankuai_id}}" bindtap="selectModule">
<text>{{item.mingcheng}}</text>
</view>
</block>
</view>
</scroll-view>
<!-- 状态筛选 -->
<view class="status-row">
<view class="status-item {{statusFilter === 0 ? 'active' : ''}}" data-status="{{0}}" bindtap="selectStatus">全部</view>
<view class="status-item {{statusFilter === 1 ? 'active' : ''}}" data-status="{{1}}" bindtap="selectStatus">审核中</view>
<view class="status-item {{statusFilter === 2 ? 'active' : ''}}" data-status="{{2}}" bindtap="selectStatus">已通过</view>
<view class="status-item {{statusFilter === 3 ? 'active' : ''}}" data-status="{{3}}" bindtap="selectStatus">未通过</view>
</view>
</view>
<!-- 列表区域 -->
<view class="list-container">
<scroll-view class="list-scroll" scroll-y enhanced show-scrollbar="{{false}}"
bindscrolltolower="onReachBottom">
<view class="record-list">
<view wx:if="{{!isLoading && recordList.length === 0}}" class="empty-tip">暂无考核记录</view>
<block wx:for="{{recordList}}" wx:key="jilu_id">
<view class="card">
<!-- 头部信息 -->
<view class="card-header">
<image class="avatar" src="{{item.shenqingren_avatar}}" mode="aspectFill" />
<view class="user-info">
<text class="nickname">{{item.shenqingren_nicheng || '用户'}}</text>
<text class="uid">UID {{item.shenqingren_id}}</text>
</view>
<view class="status-tag status-{{item.zhuangtai}}">
{{item.zhuangtai == 0 ? '审核中' : (item.zhuangtai == 1 ? '已通过' : (item.zhuangtai == 2 ? '未通过' : '待审核'))}}
</view>
</view>
<!-- 摘要信息 -->
<view class="card-summary">
<view class="summary-item">
<text class="label">板块</text>
<text class="value">{{item.bankuai_mingcheng}}</text>
</view>
<view class="summary-item">
<text class="label">标签</text>
<chenghao-tag mingcheng="{{item.chenghao_mingcheng}}" texiaoJson="{{item.chenghao_texiao_json}}" />
</view>
<view class="summary-item">
<text class="label">次数</text>
<text class="value">{{item.cishu}}次</text>
</view>
<view class="summary-item">
<text class="label">总费用</text>
<text class="value">¥{{item.total_fee}}</text>
</view>
</view>
<!-- 操作行 -->
<view class="card-actions">
<view class="expand-btn" data-id="{{item.jilu_id}}" bindtap="toggleExpand">
{{expandedIds.indexOf(item.jilu_id) > -1 ? '收起详情' : '展开详情'}}
</view>
<view wx:if="{{item.zhuangtai == 0}}" class="judge-btn" data-item="{{item}}" bindtap="goJudge">
去评判
</view>
</view>
<!-- 展开的详细内容 -->
<view class="card-detail" wx:if="{{expandedIds.indexOf(item.jilu_id) > -1}}">
<view class="detail-section" wx:if="{{item.guize_neirong}}">
<text class="detail-title">考核规则</text>
<text class="detail-text">{{item.guize_neirong}}</text>
</view>
<view class="detail-section">
<text class="detail-title">缴纳记录</text>
<view wx:for="{{item.fee_list}}" wx:key="cishu" class="fee-row">
<text>第{{item.cishu}}次:¥{{item.jine}}</text>
</view>
<text wx:if="{{!item.fee_list || item.fee_list.length === 0}}" class="detail-empty">暂无缴纳记录</text>
</view>
<view class="detail-section">
<text class="detail-title">拒绝记录</text>
<view wx:for="{{item.reject_list}}" wx:key="cishu" class="reject-row">
<view class="reject-item">
<text class="reject-time">{{item.create_time}}</text>
<text class="reject-reason">第{{item.cishu}}次拒绝:{{item.yuanyin}}</text>
</view>
</view>
<text wx:if="{{!item.reject_list || item.reject_list.length === 0}}" class="detail-empty">暂无拒绝记录</text>
</view>
<view class="detail-section">
<text class="detail-title">其他信息</text>
<view class="info-grid">
<text>打手游戏ID{{item.dashou_youxi_id || '无'}}</text>
<text>打手备注:{{item.dashou_beizhu || '无'}}</text>
<text>审核官游戏ID{{item.shenheguan_youxi_id || '无'}}</text>
<text>创建时间:{{item.create_time}}</text>
<text>更新时间:{{item.update_time}}</text>
</view>
</view>
</view>
</view>
</block>
<view wx:if="{{isLoadingMore}}" class="loading-more">加载更多...</view>
<view wx:if="{{!hasMore && recordList.length > 0}}" class="no-more">—— 没有更多了 ——</view>
</view>
</scroll-view>
</view>
<!-- 加载遮罩 -->
<view wx:if="{{isLoading && recordList.length === 0}}" class="loading-mask">
<view class="loading-spinner"></view>
<text>加载中...</text>
</view>
</view>
<global-notification id="global-notification" />

View File

@@ -1,263 +1,262 @@
/* 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); } }
/* pages/assess-log/assess-log.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;
}

View File

@@ -1,208 +1,208 @@
// pages/kaohe_dafen/kaohe_dafen.js
const app = getApp();
import request from '../../utils/request.js';
import { openPrivateChat, buildDashouPeerId } from '../../utils/im-user.js';
import { resolveAvatarUrl } from '../../utils/avatar.js';
Page({
data: {
recordList: [],
page: 1,
pageSize: 10,
hasMore: false,
isLoading: false,
ossImageUrl: app.globalData.ossImageUrl || '',
// 操作弹窗
showAgreeModal: false,
showRejectModal: false,
showTransferModal: false,
currentItem: null,
rejectReason: '',
// 倒计时
countdown: 2,
counting: false,
countdownTimer: null,
},
onLoad() {
this.loadRecords(true);
},
// 加载考核记录
async loadRecords(isRefresh = false) {
if (this.data.isLoading) return;
const page = isRefresh ? 1 : this.data.page;
this.setData({ isLoading: true });
try {
const res = await request({
url: '/dengji/hqkhxq',
method: 'POST',
data: { page, page_size: this.data.pageSize }
});
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 processed = list.map(item => ({
...item,
shenqingren_avatar: item.shenqingren_avatar && !item.shenqingren_avatar.startsWith('http')
? oss + item.shenqingren_avatar
: item.shenqingren_avatar || '/images/default-avatar.png'
}));
const newList = isRefresh ? processed : [...this.data.recordList, ...processed];
this.setData({ recordList: newList, page: page + 1, hasMore, isLoading: false });
} else {
wx.showToast({ title: '加载失败', icon: 'none' });
this.setData({ isLoading: false });
}
} catch (e) {
wx.showToast({ title: '网络错误', icon: 'none' });
this.setData({ isLoading: false });
}
},
onReachBottom() {
if (this.data.hasMore && !this.data.isLoading) {
this.loadRecords(false);
}
},
// 联系打手(私聊)—— 照搬金牌页面逻辑
contactDashou(e) {
const item = e.currentTarget.dataset.item;
if (!item || !item.shenqingren_id) {
wx.showToast({ title: '无法获取打手ID', icon: 'none' });
return;
}
openPrivateChat(this, {
toUserId: buildDashouPeerId(item.shenqingren_id),
toName: '打手' + item.shenqingren_id,
toAvatar: resolveAvatarUrl(item.shenqingren_avatar),
});
},
// 修改游戏昵称
async modifyNick(e) {
const { id } = e.currentTarget.dataset;
const item = this.data.recordList.find(r => r.jilu_id === id);
if (!item) return;
const newNick = item.shenheguan_youxi_id || '';
try {
const res = await request({
url: '/dengji/khgxgkh',
method: 'POST',
data: { jilu_id: id, caozuo: 'modify_nick', youxi_id: newNick }
});
if (res && res.data.code === 0) {
wx.showToast({ title: '修改成功', icon: 'success' });
} else {
wx.showToast({ title: res.data?.msg || '修改失败', icon: 'none' });
}
} catch (e) {
wx.showToast({ title: '网络错误', icon: 'none' });
}
},
onNickInput(e) {
const id = e.currentTarget.dataset.id;
const value = e.detail.value;
const list = this.data.recordList.map(item => {
if (item.jilu_id === id) {
return { ...item, shenheguan_youxi_id: value };
}
return item;
});
this.setData({ recordList: list });
},
// 🆕 实时更新拒绝原因
onRejectInput(e) {
this.setData({ rejectReason: e.detail.value });
},
openAgreeModal(e) {
const item = e.currentTarget.dataset.item;
this.setData({ showAgreeModal: true, currentItem: item });
this.startCountdown();
},
openRejectModal(e) {
const item = e.currentTarget.dataset.item;
this.setData({ showRejectModal: true, currentItem: item, rejectReason: '' });
this.startCountdown();
},
openTransferModal(e) {
const item = e.currentTarget.dataset.item;
this.setData({ showTransferModal: true, currentItem: item });
this.startCountdown();
},
closeAllModals() {
this.stopCountdown();
this.setData({
showAgreeModal: false,
showRejectModal: false,
showTransferModal: false,
currentItem: null,
rejectReason: ''
});
},
startCountdown() {
this.stopCountdown();
this.setData({ counting: true, countdown: 2 });
this.data.countdownTimer = setInterval(() => {
if (this.data.countdown > 1) {
this.setData({ countdown: this.data.countdown - 1 });
} else {
this.stopCountdown();
this.setData({ counting: false, countdown: 0 });
}
}, 1000);
},
stopCountdown() {
if (this.data.countdownTimer) {
clearInterval(this.data.countdownTimer);
this.data.countdownTimer = null;
}
},
// 执行操作(从事件对象中获取操作类型)
async executeAction(e) {
const actionType = e.currentTarget.dataset.action;
if (!actionType) {
wx.showToast({ title: '操作类型异常', icon: 'none' });
return;
}
const item = this.data.currentItem;
if (!item) return;
const params = { jilu_id: item.jilu_id, caozuo: actionType };
if (actionType === 'reject') {
if (!this.data.rejectReason.trim()) {
wx.showToast({ title: '请填写拒绝原因', icon: 'none' });
return;
}
params.yuanyin = this.data.rejectReason;
}
try {
const res = await request({ url: '/dengji/khgxgkh', method: 'POST', data: params });
if (res && res.data.code === 0) {
wx.showToast({ title: '操作成功', icon: 'success' });
this.closeAllModals();
const newList = this.data.recordList.filter(r => r.jilu_id !== item.jilu_id);
this.setData({ recordList: newList });
} else {
wx.showToast({ title: res.data?.msg || '操作失败', icon: 'none' });
this.closeAllModals();
}
} catch (e) {
wx.showToast({ title: '网络错误', icon: 'none' });
this.closeAllModals();
}
},
// pages/assess-score/assess-score.js
const app = getApp();
import request from '../../utils/request.js';
import { openPrivateChat, buildDashouPeerId } from '../../utils/im-user.js';
import { resolveAvatarUrl } from '../../utils/avatar.js';
Page({
data: {
recordList: [],
page: 1,
pageSize: 10,
hasMore: false,
isLoading: false,
ossImageUrl: app.globalData.ossImageUrl || '',
// 操作弹窗
showAgreeModal: false,
showRejectModal: false,
showTransferModal: false,
currentItem: null,
rejectReason: '',
// 倒计时
countdown: 2,
counting: false,
countdownTimer: null,
},
onLoad() {
this.loadRecords(true);
},
// 加载考核记录
async loadRecords(isRefresh = false) {
if (this.data.isLoading) return;
const page = isRefresh ? 1 : this.data.page;
this.setData({ isLoading: true });
try {
const res = await request({
url: '/dengji/hqkhxq',
method: 'POST',
data: { page, page_size: this.data.pageSize }
});
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 processed = list.map(item => ({
...item,
shenqingren_avatar: item.shenqingren_avatar && !item.shenqingren_avatar.startsWith('http')
? oss + item.shenqingren_avatar
: item.shenqingren_avatar || '/images/default-avatar.png'
}));
const newList = isRefresh ? processed : [...this.data.recordList, ...processed];
this.setData({ recordList: newList, page: page + 1, hasMore, isLoading: false });
} else {
wx.showToast({ title: '加载失败', icon: 'none' });
this.setData({ isLoading: false });
}
} catch (e) {
wx.showToast({ title: '网络错误', icon: 'none' });
this.setData({ isLoading: false });
}
},
onReachBottom() {
if (this.data.hasMore && !this.data.isLoading) {
this.loadRecords(false);
}
},
// 联系打手(私聊)—— 照搬金牌页面逻辑
contactDashou(e) {
const item = e.currentTarget.dataset.item;
if (!item || !item.shenqingren_id) {
wx.showToast({ title: '无法获取打手ID', icon: 'none' });
return;
}
openPrivateChat(this, {
toUserId: buildDashouPeerId(item.shenqingren_id),
toName: '打手' + item.shenqingren_id,
toAvatar: resolveAvatarUrl(item.shenqingren_avatar),
});
},
// 修改游戏昵称
async modifyNick(e) {
const { id } = e.currentTarget.dataset;
const item = this.data.recordList.find(r => r.jilu_id === id);
if (!item) return;
const newNick = item.shenheguan_youxi_id || '';
try {
const res = await request({
url: '/dengji/khgxgkh',
method: 'POST',
data: { jilu_id: id, caozuo: 'modify_nick', youxi_id: newNick }
});
if (res && res.data.code === 0) {
wx.showToast({ title: '修改成功', icon: 'success' });
} else {
wx.showToast({ title: res.data?.msg || '修改失败', icon: 'none' });
}
} catch (e) {
wx.showToast({ title: '网络错误', icon: 'none' });
}
},
onNickInput(e) {
const id = e.currentTarget.dataset.id;
const value = e.detail.value;
const list = this.data.recordList.map(item => {
if (item.jilu_id === id) {
return { ...item, shenheguan_youxi_id: value };
}
return item;
});
this.setData({ recordList: list });
},
// 🆕 实时更新拒绝原因
onRejectInput(e) {
this.setData({ rejectReason: e.detail.value });
},
openAgreeModal(e) {
const item = e.currentTarget.dataset.item;
this.setData({ showAgreeModal: true, currentItem: item });
this.startCountdown();
},
openRejectModal(e) {
const item = e.currentTarget.dataset.item;
this.setData({ showRejectModal: true, currentItem: item, rejectReason: '' });
this.startCountdown();
},
openTransferModal(e) {
const item = e.currentTarget.dataset.item;
this.setData({ showTransferModal: true, currentItem: item });
this.startCountdown();
},
closeAllModals() {
this.stopCountdown();
this.setData({
showAgreeModal: false,
showRejectModal: false,
showTransferModal: false,
currentItem: null,
rejectReason: ''
});
},
startCountdown() {
this.stopCountdown();
this.setData({ counting: true, countdown: 2 });
this.data.countdownTimer = setInterval(() => {
if (this.data.countdown > 1) {
this.setData({ countdown: this.data.countdown - 1 });
} else {
this.stopCountdown();
this.setData({ counting: false, countdown: 0 });
}
}, 1000);
},
stopCountdown() {
if (this.data.countdownTimer) {
clearInterval(this.data.countdownTimer);
this.data.countdownTimer = null;
}
},
// 执行操作(从事件对象中获取操作类型)
async executeAction(e) {
const actionType = e.currentTarget.dataset.action;
if (!actionType) {
wx.showToast({ title: '操作类型异常', icon: 'none' });
return;
}
const item = this.data.currentItem;
if (!item) return;
const params = { jilu_id: item.jilu_id, caozuo: actionType };
if (actionType === 'reject') {
if (!this.data.rejectReason.trim()) {
wx.showToast({ title: '请填写拒绝原因', icon: 'none' });
return;
}
params.yuanyin = this.data.rejectReason;
}
try {
const res = await request({ url: '/dengji/khgxgkh', method: 'POST', data: params });
if (res && res.data.code === 0) {
wx.showToast({ title: '操作成功', icon: 'success' });
this.closeAllModals();
const newList = this.data.recordList.filter(r => r.jilu_id !== item.jilu_id);
this.setData({ recordList: newList });
} else {
wx.showToast({ title: res.data?.msg || '操作失败', icon: 'none' });
this.closeAllModals();
}
} catch (e) {
wx.showToast({ title: '网络错误', icon: 'none' });
this.closeAllModals();
}
},
});

View File

@@ -1,118 +1,118 @@
<!-- pages/kaohe_dafen/kaohe_dafen.wxml -->
<view class="root-page">
<view class="scroll-container" scroll-y="true" bindscrolltolower="onReachBottom">
<view class="list">
<block wx:for="{{recordList}}" wx:key="jilu_id">
<view class="card">
<!-- 用户信息行 -->
<view class="user-row">
<image class="avatar" src="{{item.shenqingren_avatar}}" mode="aspectFill" />
<view class="info">
<text class="uid">UID {{item.shenqingren_id}}</text>
<text class="time">{{item.create_time}}</text>
</view>
<view class="contact-btn" bindtap="contactDashou" data-item="{{item}}">联系</view>
</view>
<!-- 详情区 -->
<view class="detail-grid">
<view class="detail-item">
<text class="label">板块</text>
<text class="value">{{item.bankuai_mingcheng}}</text>
</view>
<view class="detail-item">
<text class="label">标签</text>
<chenghao-tag mingcheng="{{item.chenghao_mingcheng}}" texiaoJson="{{item.chenghao_texiao_json}}" />
<text wx:if="{{item.is_jinpai}}" class="jinpai-note">(开启金牌)</text>
</view>
<view class="detail-item">
<text class="label">规则</text>
<text class="value rule-content">{{item.guize_neirong}}</text>
</view>
<!-- 🆕 添加被考核人游戏昵称 -->
<view class="detail-item" wx:if="{{item.dashou_youxi_id}}">
<text class="label">打手游戏昵称</text>
<text class="value">{{item.dashou_youxi_id}}</text>
</view>
<view class="detail-item" wx:if="{{item.dashou_beizhu}}">
<text class="label">备注</text>
<text class="value">{{item.dashou_beizhu}}</text>
</view>
<view class="detail-item">
<text class="label">次数</text>
<text class="value">{{item.cishu}}</text>
</view>
<view class="detail-item">
<text class="label">待收考核费</text>
<text class="value amount">¥{{item.daishou_fei}}</text>
</view>
</view>
<!-- 上次失败原因 -->
<view wx:if="{{item.last_fail_reason}}" class="fail-box">
<text class="fail-label">上次拒绝原因:</text>
<text class="fail-text">{{item.last_fail_reason}}</text>
</view>
<!-- 游戏昵称修改 -->
<view class="nick-row">
<text class="label">我的游戏昵称</text>
<input class="nick-input" data-id="{{item.jilu_id}}" value="{{item.shenheguan_youxi_id}}" bindinput="onNickInput" placeholder="输入昵称" />
<view class="save-nick-btn" data-id="{{item.jilu_id}}" bindtap="modifyNick">保存</view>
</view>
<!-- 操作按钮 -->
<view class="action-row">
<view class="action-btn agree" data-item="{{item}}" bindtap="openAgreeModal">合格</view>
<view class="action-btn reject" data-item="{{item}}" bindtap="openRejectModal">不合格</view>
<view class="action-btn transfer" data-item="{{item}}" bindtap="openTransferModal">转移</view>
</view>
</view>
</block>
<view wx:if="{{isLoading}}" class="loading">加载中...</view>
<view wx:if="{{!hasMore && recordList.length > 0}}" class="no-more">—— 已加载全部 ——</view>
<view wx:if="{{!isLoading && recordList.length === 0}}" class="empty">暂无待考核记录</view>
</view>
</view>
</view>
<!-- 通过确认弹窗 -->
<view wx:if="{{showAgreeModal}}" class="modal-mask" bindtap="closeAllModals">
<view class="modal-box" catchtap="noop">
<view class="modal-header">确认通过考核</view>
<view class="modal-body">通过后,该用户将获得对应标签,请认真审核后操作。</view>
<view class="countdown-text" wx:if="{{counting}}">{{countdown}}秒后可确认</view>
<view class="modal-footer">
<view class="btn cancel" bindtap="closeAllModals">取消</view>
<view class="btn confirm {{counting ? 'disabled' : ''}}" bindtap="{{counting ? '' : 'executeAction'}}" data-action="agree">确认通过</view>
</view>
</view>
</view>
<!-- 拒绝弹窗 -->
<view wx:if="{{showRejectModal}}" class="modal-mask" bindtap="closeAllModals">
<view class="modal-box" catchtap="noop">
<view class="modal-header">拒绝通过</view>
<textarea class="reason-input" value="{{rejectReason}}" bindinput="onRejectInput" placeholder="请填写拒绝原因" maxlength="200"></textarea>
<view class="countdown-text" wx:if="{{counting}}">{{countdown}}秒后可确认</view>
<view class="modal-footer">
<view class="btn cancel" bindtap="closeAllModals">取消</view>
<view class="btn confirm {{counting ? 'disabled' : ''}}" bindtap="{{counting ? '' : 'executeAction'}}" data-action="reject">确认拒绝</view>
</view>
</view>
</view>
<!-- 转移弹窗 -->
<view wx:if="{{showTransferModal}}" class="modal-mask" bindtap="closeAllModals">
<view class="modal-box" catchtap="noop">
<view class="modal-header">确认转移考核</view>
<view class="modal-body">转移后,您将无法继续考核此订单,可在考核中心重新接待。</view>
<view class="countdown-text" wx:if="{{counting}}">{{countdown}}秒后可确认</view>
<view class="modal-footer">
<view class="btn cancel" bindtap="closeAllModals">取消</view>
<view class="btn confirm {{counting ? 'disabled' : ''}}" bindtap="{{counting ? '' : 'executeAction'}}" data-action="transfer">确认转移</view>
</view>
</view>
</view>
<!-- pages/assess-score/assess-score.wxml -->
<view class="root-page">
<view class="scroll-container" scroll-y="true" bindscrolltolower="onReachBottom">
<view class="list">
<block wx:for="{{recordList}}" wx:key="jilu_id">
<view class="card">
<!-- 用户信息行 -->
<view class="user-row">
<image class="avatar" src="{{item.shenqingren_avatar}}" mode="aspectFill" />
<view class="info">
<text class="uid">UID {{item.shenqingren_id}}</text>
<text class="time">{{item.create_time}}</text>
</view>
<view class="contact-btn" bindtap="contactDashou" data-item="{{item}}">联系</view>
</view>
<!-- 详情区 -->
<view class="detail-grid">
<view class="detail-item">
<text class="label">板块</text>
<text class="value">{{item.bankuai_mingcheng}}</text>
</view>
<view class="detail-item">
<text class="label">标签</text>
<chenghao-tag mingcheng="{{item.chenghao_mingcheng}}" texiaoJson="{{item.chenghao_texiao_json}}" />
<text wx:if="{{item.is_jinpai}}" class="jinpai-note">(开启金牌)</text>
</view>
<view class="detail-item">
<text class="label">规则</text>
<text class="value rule-content">{{item.guize_neirong}}</text>
</view>
<!-- 🆕 添加被考核人游戏昵称 -->
<view class="detail-item" wx:if="{{item.dashou_youxi_id}}">
<text class="label">打手游戏昵称</text>
<text class="value">{{item.dashou_youxi_id}}</text>
</view>
<view class="detail-item" wx:if="{{item.dashou_beizhu}}">
<text class="label">备注</text>
<text class="value">{{item.dashou_beizhu}}</text>
</view>
<view class="detail-item">
<text class="label">次数</text>
<text class="value">{{item.cishu}}</text>
</view>
<view class="detail-item">
<text class="label">待收考核费</text>
<text class="value amount">¥{{item.daishou_fei}}</text>
</view>
</view>
<!-- 上次失败原因 -->
<view wx:if="{{item.last_fail_reason}}" class="fail-box">
<text class="fail-label">上次拒绝原因:</text>
<text class="fail-text">{{item.last_fail_reason}}</text>
</view>
<!-- 游戏昵称修改 -->
<view class="nick-row">
<text class="label">我的游戏昵称</text>
<input class="nick-input" data-id="{{item.jilu_id}}" value="{{item.shenheguan_youxi_id}}" bindinput="onNickInput" placeholder="输入昵称" />
<view class="save-nick-btn" data-id="{{item.jilu_id}}" bindtap="modifyNick">保存</view>
</view>
<!-- 操作按钮 -->
<view class="action-row">
<view class="action-btn agree" data-item="{{item}}" bindtap="openAgreeModal">合格</view>
<view class="action-btn reject" data-item="{{item}}" bindtap="openRejectModal">不合格</view>
<view class="action-btn transfer" data-item="{{item}}" bindtap="openTransferModal">转移</view>
</view>
</view>
</block>
<view wx:if="{{isLoading}}" class="loading">加载中...</view>
<view wx:if="{{!hasMore && recordList.length > 0}}" class="no-more">—— 已加载全部 ——</view>
<view wx:if="{{!isLoading && recordList.length === 0}}" class="empty">暂无待考核记录</view>
</view>
</view>
</view>
<!-- 通过确认弹窗 -->
<view wx:if="{{showAgreeModal}}" class="modal-mask" bindtap="closeAllModals">
<view class="modal-box" catchtap="noop">
<view class="modal-header">确认通过考核</view>
<view class="modal-body">通过后,该用户将获得对应标签,请认真审核后操作。</view>
<view class="countdown-text" wx:if="{{counting}}">{{countdown}}秒后可确认</view>
<view class="modal-footer">
<view class="btn cancel" bindtap="closeAllModals">取消</view>
<view class="btn confirm {{counting ? 'disabled' : ''}}" bindtap="{{counting ? '' : 'executeAction'}}" data-action="agree">确认通过</view>
</view>
</view>
</view>
<!-- 拒绝弹窗 -->
<view wx:if="{{showRejectModal}}" class="modal-mask" bindtap="closeAllModals">
<view class="modal-box" catchtap="noop">
<view class="modal-header">拒绝通过</view>
<textarea class="reason-input" value="{{rejectReason}}" bindinput="onRejectInput" placeholder="请填写拒绝原因" maxlength="200"></textarea>
<view class="countdown-text" wx:if="{{counting}}">{{countdown}}秒后可确认</view>
<view class="modal-footer">
<view class="btn cancel" bindtap="closeAllModals">取消</view>
<view class="btn confirm {{counting ? 'disabled' : ''}}" bindtap="{{counting ? '' : 'executeAction'}}" data-action="reject">确认拒绝</view>
</view>
</view>
</view>
<!-- 转移弹窗 -->
<view wx:if="{{showTransferModal}}" class="modal-mask" bindtap="closeAllModals">
<view class="modal-box" catchtap="noop">
<view class="modal-header">确认转移考核</view>
<view class="modal-body">转移后,您将无法继续考核此订单,可在考核中心重新接待。</view>
<view class="countdown-text" wx:if="{{counting}}">{{countdown}}秒后可确认</view>
<view class="modal-footer">
<view class="btn cancel" bindtap="closeAllModals">取消</view>
<view class="btn confirm {{counting ? 'disabled' : ''}}" bindtap="{{counting ? '' : 'executeAction'}}" data-action="transfer">确认转移</view>
</view>
</view>
</view>
<global-notification id="global-notification" />

View File

@@ -1,218 +1,218 @@
// pages/kaoheguan/kaoheguan.js
// 只新增了两个字段的接收,其余所有代码未变
import request from '../../utils/request.js';
import {
isRoleStatusActive,
isCenterPageActive,
syncRoleStatuses,
ensureRoleOnCenterPage,
} from '../../utils/role-tab-bar.js';
const app = getApp();
Page({
data: {
isKaoheguan: false,
isLoading: false,
inviteCode: '',
statusText: '用户尚未注册或账号已被封禁',
uid: '',
avatarUrl: '',
icons: {
refresh: '',
tixian: '',
daofen: '',
jilu: '',
zhongxin: '',
bgImage: ''
},
kaoheData: {
shenheZongshu: 0,
tongguoZongshu: 0,
yue: '0.00',
zonge: '0.00',
shenheZhuangtai: 0,
bankuaiList: [],
isRenzheng: false, // 🆕 是否认证
pendingShenheCount: 0 // 🆕 审核中数量
},
lastRefreshTime: 0,
canRefresh: true,
planetPaused: {
top: false,
bottom: false,
left: false,
right: false
}
},
_planetTimers: {},
onLoad() {
wx.redirectTo({ url: '/pages/dashouduan/dashouduan' });
return;
const ossUrl = app.globalData.ossImageUrl || '';
const iconBase = 'beijing/kaohe/';
this.setData({
'icons.refresh': ossUrl + iconBase + 'shuaxin.png',
'icons.tixian': ossUrl + iconBase + 'tixian.png',
'icons.daofen': ossUrl + iconBase + 'daofen.png',
'icons.jilu': ossUrl + iconBase + 'jilu.png',
'icons.zhongxin': ossUrl + iconBase + 'zhongxin.png',
'icons.bgImage': ossUrl + iconBase + 'top_bg.jpg'
});
const kaoheguanstatus = wx.getStorageSync('kaoheguanstatus');
if (isRoleStatusActive(kaoheguanstatus)) {
this.loadUserInfo();
this.setData({ isKaoheguan: true });
this.getKaoheguanInfo();
ensureRoleOnCenterPage(this, 'kaoheguan');
} else {
this.setData({ isKaoheguan: false });
}
},
onReady() {
if (isCenterPageActive(this, 'isKaoheguan', 'kaoheguanstatus')) {
ensureRoleOnCenterPage(this, 'kaoheguan');
}
},
onShow() {
if (isCenterPageActive(this, 'isKaoheguan', 'kaoheguanstatus')) {
if (!this.data.isKaoheguan) this.setData({ isKaoheguan: true });
ensureRoleOnCenterPage(this, 'kaoheguan');
this.refreshKaoheInfo();
}
},
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);
}
this.setData({ uid, avatarUrl });
},
async getKaoheguanInfo() {
this.setData({ isLoading: true });
try {
const res = await request({ url: '/dengji/khgsx', method: 'POST' });
if (res && res.data.code === 200) {
const data = res.data.data;
if (data.zhuangtai === 0 || data.zhuangtai === 2) {
wx.setStorageSync('kaoheguanstatus', 0);
this.setData({ isKaoheguan: false, isLoading: false, statusText: '用户尚未注册或账号已被封禁' });
return;
}
wx.setStorageSync('kaoheguanstatus', 1);
this.setData({
isKaoheguan: true,
isLoading: false,
kaoheData: {
shenheZongshu: data.shenhe_zongshu || 0,
tongguoZongshu: data.tongguo_zongshu || 0,
yue: data.yue || '0.00',
zonge: data.zonge || '0.00',
shenheZhuangtai: data.shenhe_zhuangtai || 0,
bankuaiList: data.bankuai_list || [],
isRenzheng: data.is_renzheng || false, // 🆕
pendingShenheCount: data.pending_shenhe_count || 0 // 🆕
}
});
ensureRoleOnCenterPage(this, 'kaoheguan');
} 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 });
}
},
onInviteCodeInput(e) {
this.setData({ inviteCode: e.detail.value.trim() });
},
async onRegister() {
const { inviteCode } = this.data;
if (!inviteCode) {
wx.showToast({ title: '请输入邀请码', icon: 'none', duration: 2000 });
return;
}
if (inviteCode.length > 100) {
wx.showToast({ title: '邀请码不能超过100位', icon: 'none', duration: 2000 });
return;
}
this.setData({ isLoading: true });
try {
const res = await request({
url: '/dengji/khgzc',
method: 'POST',
data: { inviteCode: inviteCode }
});
if (res && res.data.code === 200) {
const data = res.data.data || {};
syncRoleStatuses(data);
if (!Object.prototype.hasOwnProperty.call(data, 'kaoheguanstatus')) {
wx.setStorageSync('kaoheguanstatus', 1);
}
wx.showToast({ title: '注册成功', icon: 'success', duration: 2000 });
this.loadUserInfo();
await this.getKaoheguanInfo();
ensureRoleOnCenterPage(this, 'kaoheguan');
} else {
wx.showToast({ title: res?.data?.msg || '注册失败', icon: 'none', duration: 2000 });
this.setData({ isLoading: false });
}
} catch (error) {
console.error('注册失败:', error);
wx.showToast({ title: '网络错误', icon: 'none' });
this.setData({ isLoading: false });
}
},
refreshKaoheInfo() {
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.getKaoheguanInfo();
setTimeout(() => this.setData({ canRefresh: true }), 3000);
},
goToWithdraw() {
const yue = this.data.kaoheData.yue || 0;
wx.navigateTo({
url: `/pages/tixian/tixian?amount=${yue}&from=kaoheguan`
});
},
goToDafen() { wx.navigateTo({ url: '/pages/kaohe_dafen/kaohe_dafen' }); },
goToJilu() { wx.navigateTo({ url: '/pages/kaohe_jilu/kaohe_jilu' }); },
goToZhongxin() { wx.navigateTo({ url: '/pages/kaohe_zhongxin/kaohe_zhongxin' }); },
onPlanetTouch(e) {
const id = e.currentTarget.dataset.id;
if (this._planetTimers[id]) {
clearTimeout(this._planetTimers[id]);
this._planetTimers[id] = null;
}
const paused = `planetPaused.${id}`;
this.setData({ [paused]: true });
},
onPlanetLeave(e) {
const id = e.currentTarget.dataset.id;
this._planetTimers[id] = setTimeout(() => {
const paused = `planetPaused.${id}`;
this.setData({ [paused]: false });
}, 5000);
}
// pages/assessor/assessor.js
// 只新增了两个字段的接收,其余所有代码未变
import request from '../../utils/request.js';
import {
isRoleStatusActive,
isCenterPageActive,
syncRoleStatuses,
ensureRoleOnCenterPage,
} from '../../utils/role-tab-bar.js';
const app = getApp();
Page({
data: {
isKaoheguan: false,
isLoading: false,
inviteCode: '',
statusText: '用户尚未注册或账号已被封禁',
uid: '',
avatarUrl: '',
icons: {
refresh: '',
tixian: '',
daofen: '',
jilu: '',
zhongxin: '',
bgImage: ''
},
kaoheData: {
shenheZongshu: 0,
tongguoZongshu: 0,
yue: '0.00',
zonge: '0.00',
shenheZhuangtai: 0,
bankuaiList: [],
isRenzheng: false, // 🆕 是否认证
pendingShenheCount: 0 // 🆕 审核中数量
},
lastRefreshTime: 0,
canRefresh: true,
planetPaused: {
top: false,
bottom: false,
left: false,
right: false
}
},
_planetTimers: {},
onLoad() {
wx.redirectTo({ url: '/pages/fighter/fighter' });
return;
const ossUrl = app.globalData.ossImageUrl || '';
const iconBase = 'beijing/kaohe/';
this.setData({
'icons.refresh': ossUrl + iconBase + 'shuaxin.png',
'icons.tixian': ossUrl + iconBase + 'tixian.png',
'icons.daofen': ossUrl + iconBase + 'daofen.png',
'icons.jilu': ossUrl + iconBase + 'jilu.png',
'icons.zhongxin': ossUrl + iconBase + 'zhongxin.png',
'icons.bgImage': ossUrl + iconBase + 'top_bg.jpg'
});
const kaoheguanstatus = wx.getStorageSync('kaoheguanstatus');
if (isRoleStatusActive(kaoheguanstatus)) {
this.loadUserInfo();
this.setData({ isKaoheguan: true });
this.getKaoheguanInfo();
ensureRoleOnCenterPage(this, 'kaoheguan');
} else {
this.setData({ isKaoheguan: false });
}
},
onReady() {
if (isCenterPageActive(this, 'isKaoheguan', 'kaoheguanstatus')) {
ensureRoleOnCenterPage(this, 'kaoheguan');
}
},
onShow() {
if (isCenterPageActive(this, 'isKaoheguan', 'kaoheguanstatus')) {
if (!this.data.isKaoheguan) this.setData({ isKaoheguan: true });
ensureRoleOnCenterPage(this, 'kaoheguan');
this.refreshKaoheInfo();
}
},
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);
}
this.setData({ uid, avatarUrl });
},
async getKaoheguanInfo() {
this.setData({ isLoading: true });
try {
const res = await request({ url: '/dengji/khgsx', method: 'POST' });
if (res && res.data.code === 200) {
const data = res.data.data;
if (data.zhuangtai === 0 || data.zhuangtai === 2) {
wx.setStorageSync('kaoheguanstatus', 0);
this.setData({ isKaoheguan: false, isLoading: false, statusText: '用户尚未注册或账号已被封禁' });
return;
}
wx.setStorageSync('kaoheguanstatus', 1);
this.setData({
isKaoheguan: true,
isLoading: false,
kaoheData: {
shenheZongshu: data.shenhe_zongshu || 0,
tongguoZongshu: data.tongguo_zongshu || 0,
yue: data.yue || '0.00',
zonge: data.zonge || '0.00',
shenheZhuangtai: data.shenhe_zhuangtai || 0,
bankuaiList: data.bankuai_list || [],
isRenzheng: data.is_renzheng || false, // 🆕
pendingShenheCount: data.pending_shenhe_count || 0 // 🆕
}
});
ensureRoleOnCenterPage(this, 'kaoheguan');
} 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 });
}
},
onInviteCodeInput(e) {
this.setData({ inviteCode: e.detail.value.trim() });
},
async onRegister() {
const { inviteCode } = this.data;
if (!inviteCode) {
wx.showToast({ title: '请输入邀请码', icon: 'none', duration: 2000 });
return;
}
if (inviteCode.length > 100) {
wx.showToast({ title: '邀请码不能超过100位', icon: 'none', duration: 2000 });
return;
}
this.setData({ isLoading: true });
try {
const res = await request({
url: '/dengji/khgzc',
method: 'POST',
data: { inviteCode: inviteCode }
});
if (res && res.data.code === 200) {
const data = res.data.data || {};
syncRoleStatuses(data);
if (!Object.prototype.hasOwnProperty.call(data, 'kaoheguanstatus')) {
wx.setStorageSync('kaoheguanstatus', 1);
}
wx.showToast({ title: '注册成功', icon: 'success', duration: 2000 });
this.loadUserInfo();
await this.getKaoheguanInfo();
ensureRoleOnCenterPage(this, 'kaoheguan');
} else {
wx.showToast({ title: res?.data?.msg || '注册失败', icon: 'none', duration: 2000 });
this.setData({ isLoading: false });
}
} catch (error) {
console.error('注册失败:', error);
wx.showToast({ title: '网络错误', icon: 'none' });
this.setData({ isLoading: false });
}
},
refreshKaoheInfo() {
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.getKaoheguanInfo();
setTimeout(() => this.setData({ canRefresh: true }), 3000);
},
goToWithdraw() {
const yue = this.data.kaoheData.yue || 0;
wx.navigateTo({
url: `/pages/withdraw/withdraw?amount=${yue}&from=kaoheguan`
});
},
goToDafen() { wx.navigateTo({ url: '/pages/assess-score/assess-score' }); },
goToJilu() { wx.navigateTo({ url: '/pages/assess-log/assess-log' }); },
goToZhongxin() { wx.navigateTo({ url: '/pages/assess-center/assess-center' }); },
onPlanetTouch(e) {
const id = e.currentTarget.dataset.id;
if (this._planetTimers[id]) {
clearTimeout(this._planetTimers[id]);
this._planetTimers[id] = null;
}
const paused = `planetPaused.${id}`;
this.setData({ [paused]: true });
},
onPlanetLeave(e) {
const id = e.currentTarget.dataset.id;
this._planetTimers[id] = setTimeout(() => {
const paused = `planetPaused.${id}`;
this.setData({ [paused]: false });
}, 5000);
}
});

View File

@@ -1,128 +1,128 @@
<!-- pages/kaoheguan/kaoheguan.wxml -->
<view class="universe-root">
<!-- 全屏弱化背景图 -->
<image class="full-bg" src="{{icons.bgImage}}" mode="aspectFill" />
<!-- 动态星空与极光 -->
<canvas type="2d" id="starfield" class="starfield-canvas"></canvas>
<view class="aurora-ribbon"></view>
<!-- 未注册状态 -->
<view class="unreg-floating" wx:if="{{!isKaoheguan}}">
<view class="unreg-core">
<image class="unreg-icon" src="/images/kaoheguan.png" mode="aspectFit" />
<text class="unreg-title">考核官</text>
<text class="unreg-desc">{{statusText}}</text>
<input class="unreg-input" type="text" placeholder="输入邀请码" value="{{inviteCode}}" bindinput="onInviteCodeInput" placeholder-class="unreg-placeholder" maxlength="100" />
<view class="unreg-btn" bindtap="onRegister">
<text class="unreg-btn-text">即刻入驻</text>
</view>
</view>
</view>
<!-- 已注册状态 · 行星系统 -->
<view class="orbit-zone" wx:else>
<!-- 身份条 -->
<view class="top-identity">
<image class="identity-avatar" src="{{avatarUrl}}" mode="aspectFill" />
<view class="identity-info">
<text class="identity-name">考核官</text>
<view class="identity-uid">
<text class="uid-txt">{{uid}}</text>
<view class="status-indicator {{kaoheData.shenheZhuangtai == 1 ? 'active' : ''}}"></view>
</view>
<!-- 🆕 添加认证状态,这是唯一新增的行 -->
<text class="renzheng-tag {{kaoheData.isRenzheng ? 'authed' : 'unauth'}}">
{{kaoheData.isRenzheng ? '已认证' : '未认证'}}
</text>
</view>
<view class="refresh-touch" bindtap="refreshKaoheInfo">
<image class="refresh-icon" src="{{icons.refresh}}" mode="aspectFit" />
</view>
</view>
<!-- 板块标签云 -->
<view class="plate-cloud" wx:if="{{kaoheData.bankuaiList.length > 0}}">
<scroll-view class="plate-cloud-scroll" scroll-x="true" enhanced show-scrollbar="{{false}}">
<view class="plate-chip" wx:for="{{kaoheData.bankuaiList}}" wx:key="bankuai_id">{{item.mingcheng}}</view>
</scroll-view>
</view>
<!-- 中央余额星球 -->
<view class="balance-planet">
<view class="planet-ring"></view>
<view class="planet-body">
<text class="planet-num">{{kaoheData.yue}}元</text>
<text class="planet-label">可提现余额</text>
</view>
<view class="planet-glow"></view>
</view>
<!-- 流星数据条 -->
<view class="meteor-stats">
<view class="meteor-item">
<text class="meteor-num">{{kaoheData.shenheZongshu}}</text>
<text class="meteor-label">审核总数</text>
</view>
<view class="meteor-divider"></view>
<view class="meteor-item">
<text class="meteor-num">{{kaoheData.tongguoZongshu}}</text>
<text class="meteor-label">通过总数</text>
</view>
<view class="meteor-divider"></view>
<view class="meteor-item">
<text class="meteor-num">{{kaoheData.zonge}}元</text>
<text class="meteor-label">累计赚取</text>
</view>
</view>
<!-- 十字星阵功能区 -->
<view class="star-cross">
<!-- 上方:考核中心 -->
<view class="cross-item cross-top {{planetPaused.top ? 'pause-spin' : ''}}"
bindtap="goToZhongxin"
bindtouchstart="onPlanetTouch" bindtouchend="onPlanetLeave" data-id="top">
<view class="orbit-ring"></view>
<image class="cross-icon" src="{{icons.zhongxin}}" mode="aspectFit" />
<text class="cross-text">考核中心</text>
</view>
<!-- 下方:考核打分 -->
<view class="cross-item cross-bottom {{planetPaused.bottom ? 'pause-spin' : ''}}"
bindtap="goToDafen"
bindtouchstart="onPlanetTouch" bindtouchend="onPlanetLeave" data-id="bottom">
<view class="orbit-ring"></view>
<image class="cross-icon" src="{{icons.daofen}}" mode="aspectFit" />
<text class="cross-text">考核打分</text>
<!-- 🆕 添加红色角标,这是唯一新增的行 -->
<view class="badge" wx:if="{{kaoheData.pendingShenheCount > 0}}">{{kaoheData.pendingShenheCount}}</view>
</view>
<!-- 左侧:考核记录 -->
<view class="cross-item cross-left {{planetPaused.left ? 'pause-spin' : ''}}"
bindtap="goToJilu"
bindtouchstart="onPlanetTouch" bindtouchend="onPlanetLeave" data-id="left">
<view class="orbit-ring"></view>
<image class="cross-icon" src="{{icons.jilu}}" mode="aspectFit" />
<text class="cross-text">考核记录</text>
</view>
<!-- 右侧:立即提现 -->
<view class="cross-item cross-right {{planetPaused.right ? 'pause-spin' : ''}}"
bindtap="goToWithdraw"
bindtouchstart="onPlanetTouch" bindtouchend="onPlanetLeave" data-id="right">
<view class="orbit-ring"></view>
<image class="cross-icon" src="{{icons.tixian}}" mode="aspectFit" />
<text class="cross-text">立即提现</text>
</view>
<!-- 中央五角星 -->
<view class="pentagram"></view>
</view>
</view>
<!-- 加载星门 -->
<view class="loading-portal" wx:if="{{isLoading}}">
<view class="portal-ring"></view>
<text class="portal-text">加载中...</text>
</view>
</view>
<global-notification id="global-notification" />
<!-- pages/assessor/assessor.wxml -->
<view class="universe-root">
<!-- 全屏弱化背景图 -->
<image class="full-bg" src="{{icons.bgImage}}" mode="aspectFill" />
<!-- 动态星空与极光 -->
<canvas type="2d" id="starfield" class="starfield-canvas"></canvas>
<view class="aurora-ribbon"></view>
<!-- 未注册状态 -->
<view class="unreg-floating" wx:if="{{!isKaoheguan}}">
<view class="unreg-core">
<image class="unreg-icon" src="/images/kaoheguan.png" mode="aspectFit" />
<text class="unreg-title">考核官</text>
<text class="unreg-desc">{{statusText}}</text>
<input class="unreg-input" type="text" placeholder="输入邀请码" value="{{inviteCode}}" bindinput="onInviteCodeInput" placeholder-class="unreg-placeholder" maxlength="100" />
<view class="unreg-btn" bindtap="onRegister">
<text class="unreg-btn-text">即刻入驻</text>
</view>
</view>
</view>
<!-- 已注册状态 · 行星系统 -->
<view class="orbit-zone" wx:else>
<!-- 身份条 -->
<view class="top-identity">
<image class="identity-avatar" src="{{avatarUrl}}" mode="aspectFill" />
<view class="identity-info">
<text class="identity-name">考核官</text>
<view class="identity-uid">
<text class="uid-txt">{{uid}}</text>
<view class="status-indicator {{kaoheData.shenheZhuangtai == 1 ? 'active' : ''}}"></view>
</view>
<!-- 🆕 添加认证状态,这是唯一新增的行 -->
<text class="renzheng-tag {{kaoheData.isRenzheng ? 'authed' : 'unauth'}}">
{{kaoheData.isRenzheng ? '已认证' : '未认证'}}
</text>
</view>
<view class="refresh-touch" bindtap="refreshKaoheInfo">
<image class="refresh-icon" src="{{icons.refresh}}" mode="aspectFit" />
</view>
</view>
<!-- 板块标签云 -->
<view class="plate-cloud" wx:if="{{kaoheData.bankuaiList.length > 0}}">
<scroll-view class="plate-cloud-scroll" scroll-x="true" enhanced show-scrollbar="{{false}}">
<view class="plate-chip" wx:for="{{kaoheData.bankuaiList}}" wx:key="bankuai_id">{{item.mingcheng}}</view>
</scroll-view>
</view>
<!-- 中央余额星球 -->
<view class="balance-planet">
<view class="planet-ring"></view>
<view class="planet-body">
<text class="planet-num">{{kaoheData.yue}}元</text>
<text class="planet-label">可提现余额</text>
</view>
<view class="planet-glow"></view>
</view>
<!-- 流星数据条 -->
<view class="meteor-stats">
<view class="meteor-item">
<text class="meteor-num">{{kaoheData.shenheZongshu}}</text>
<text class="meteor-label">审核总数</text>
</view>
<view class="meteor-divider"></view>
<view class="meteor-item">
<text class="meteor-num">{{kaoheData.tongguoZongshu}}</text>
<text class="meteor-label">通过总数</text>
</view>
<view class="meteor-divider"></view>
<view class="meteor-item">
<text class="meteor-num">{{kaoheData.zonge}}元</text>
<text class="meteor-label">累计赚取</text>
</view>
</view>
<!-- 十字星阵功能区 -->
<view class="star-cross">
<!-- 上方:考核中心 -->
<view class="cross-item cross-top {{planetPaused.top ? 'pause-spin' : ''}}"
bindtap="goToZhongxin"
bindtouchstart="onPlanetTouch" bindtouchend="onPlanetLeave" data-id="top">
<view class="orbit-ring"></view>
<image class="cross-icon" src="{{icons.zhongxin}}" mode="aspectFit" />
<text class="cross-text">考核中心</text>
</view>
<!-- 下方:考核打分 -->
<view class="cross-item cross-bottom {{planetPaused.bottom ? 'pause-spin' : ''}}"
bindtap="goToDafen"
bindtouchstart="onPlanetTouch" bindtouchend="onPlanetLeave" data-id="bottom">
<view class="orbit-ring"></view>
<image class="cross-icon" src="{{icons.daofen}}" mode="aspectFit" />
<text class="cross-text">考核打分</text>
<!-- 🆕 添加红色角标,这是唯一新增的行 -->
<view class="badge" wx:if="{{kaoheData.pendingShenheCount > 0}}">{{kaoheData.pendingShenheCount}}</view>
</view>
<!-- 左侧:考核记录 -->
<view class="cross-item cross-left {{planetPaused.left ? 'pause-spin' : ''}}"
bindtap="goToJilu"
bindtouchstart="onPlanetTouch" bindtouchend="onPlanetLeave" data-id="left">
<view class="orbit-ring"></view>
<image class="cross-icon" src="{{icons.jilu}}" mode="aspectFit" />
<text class="cross-text">考核记录</text>
</view>
<!-- 右侧:立即提现 -->
<view class="cross-item cross-right {{planetPaused.right ? 'pause-spin' : ''}}"
bindtap="goToWithdraw"
bindtouchstart="onPlanetTouch" bindtouchend="onPlanetLeave" data-id="right">
<view class="orbit-ring"></view>
<image class="cross-icon" src="{{icons.tixian}}" mode="aspectFit" />
<text class="cross-text">立即提现</text>
</view>
<!-- 中央五角星 -->
<view class="pentagram"></view>
</view>
</view>
<!-- 加载星门 -->
<view class="loading-portal" wx:if="{{isLoading}}">
<view class="portal-ring"></view>
<text class="portal-text">加载中...</text>
</view>
</view>
<global-notification id="global-notification" />
<custom-tab-bar />

File diff suppressed because it is too large Load Diff

View File

@@ -1,452 +1,331 @@
// 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'
}
}
})
// pages/category/category.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: {},
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) {
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 = {}
zhuanquList.forEach(zhuanqu => {
const leixingId = zhuanqu.leixing_id
if (!zhuanquByLeixing[leixingId]) {
zhuanquByLeixing[leixingId] = []
}
zhuanquByLeixing[leixingId].push(zhuanqu)
})
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
const filteredZhuanquList = zhuanquByLeixing[selectedLeixingId] || []
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: []
})
},
getLeixingName(leixingId) {
const leixingList = this.data.shangpinleixing
for (const item of leixingList) {
if (item.id === leixingId) {
return item.jieshao
}
}
return ''
},
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/product-detail/product-detail?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/category/category'
}
}
})

View File

@@ -1,217 +1,217 @@
<!-- pages/fenlei/fenlei.wxml -->
<view class="fenlei-page">
<!-- 1. 毛玻璃搜索框区域(固定) -->
<view class="search-container">
<view class="search-box">
<image src="/images/sousuo.png" class="search-icon" />
<input
type="text"
class="search-input"
placeholder="输入商品名称搜索"
placeholder-class="search-placeholder"
value="{{searchText}}"
bindinput="onSearchInput"
bindconfirm="onSearchConfirm"
confirm-type="search"
/>
<!-- 清空按钮 -->
<view wx:if="{{searchText}}" class="search-clear" bindtap="onClearSearch">
<image src="/images/clear.png" class="clear-icon" />
</view>
<!-- 搜索按钮 -->
<view class="search-btn" bindtap="onSearchTap">
<text class="search-btn-text">搜索</text>
</view>
</view>
</view>
<!-- 2. 商品类型选择区域 -->
<view class="leixing-container" wx:if="{{!showSearchResult}}">
<scroll-view
class="leixing-scroll"
scroll-x
scroll-with-animation
show-scrollbar="{{false}}"
>
<view class="leixing-list">
<block wx:for="{{shangpinleixing}}" wx:key="id" wx:for-index="idx">
<view
class="leixing-item {{selectedLeixingId === item.id ? 'leixing-active' : ''}}"
bindtap="selectLeixing"
data-id="{{item.id}}"
data-index="{{idx}}"
>
<view class="leixing-image-box">
<image
src="{{ossImageUrl}}{{item.tupian_url}}"
mode="aspectFill"
class="leixing-image"
/>
<view class="leixing-glow {{selectedLeixingId === item.id ? 'leixing-glow-active' : ''}}"></view>
</view>
<text class="leixing-text">{{item.jieshao}}</text>
<view class="leixing-active-bar {{selectedLeixingId === item.id ? 'leixing-active-bar-show' : ''}}"></view>
</view>
</block>
</view>
</scroll-view>
</view>
<!-- 3. 左右分栏布局 -->
<view class="main-container" wx:if="{{!showSearchResult}}">
<!-- 左边:商品专区列表 -->
<scroll-view
class="zhuanqu-left"
scroll-y
enhanced="{{true}}"
show-scrollbar="{{false}}"
scroll-into-view="{{'zhuanqu-' + selectedZhuanquId}}"
>
<view class="zhuanqu-list">
<block wx:for="{{filteredZhuanquList}}" wx:key="id">
<view
id="zhuanqu-{{item.id}}"
class="zhuanqu-item {{selectedZhuanquId === item.id ? 'zhuanqu-active' : ''}}"
bindtap="selectZhuanqu"
data-id="{{item.id}}"
>
<view class="zhuanqu-item-content">
<text class="zhuanqu-name">{{item.mingzi}}</text>
<text class="zhuanqu-count">({{zhuanquGoodsCount[item.id] || 0}})</text>
</view>
<view class="zhuanqu-active-indicator {{selectedZhuanquId === item.id ? 'zhuanqu-indicator-glow' : ''}}"></view>
</view>
</block>
<view wx:if="{{filteredZhuanquList.length === 0}}" class="zhuanqu-empty">
<image src="/images/empty-box.png" class="zhuanqu-empty-icon" />
<text class="zhuanqu-empty-text">暂无专区</text>
</view>
</view>
</scroll-view>
<!-- 右边:商品列表 -->
<scroll-view
class="shangpin-right"
scroll-y
enhanced="{{true}}"
show-scrollbar="{{false}}"
>
<view class="shangpin-list">
<block wx:for="{{filteredShangpinList}}" wx:key="id">
<view
class="shangpin-item"
bindtap="goToDetail"
data-id="{{item.id}}"
>
<view class="shangpin-image-box">
<image
src="{{ossImageUrl}}{{item.tupian_url}}"
mode="aspectFill"
class="shangpin-image"
/>
</view>
<view class="shangpin-info">
<view class="shangpin-title">
<text class="shangpin-title-text">{{item.biaoqian}}</text>
</view>
<view class="shangpin-footer">
<view class="shangpin-price">
<text class="price-icon">¥</text>
<text class="price-integer">{{item.priceInteger}}</text>
<text class="price-decimal">.{{item.priceDecimal}}</text>
</view>
<view class="detail-btn">
<text class="detail-btn-text">了解详情</text>
</view>
</view>
</view>
<view class="item-divider" wx:if="{{index !== filteredShangpinList.length - 1}}"></view>
</view>
</block>
<view wx:if="{{filteredShangpinList.length === 0 && !isLoading}}" class="shangpin-empty">
<image src="/images/empty-goods.png" class="shangpin-empty-icon" />
<text class="shangpin-empty-text">该专区暂无商品</text>
</view>
</view>
<view class="load-more" wx:if="{{filteredShangpinList.length > 0}}">
<text class="load-more-text">— 已加载全部商品 —</text>
</view>
</scroll-view>
</view>
<!-- 搜索结果弹窗 -->
<view class="search-results-container" wx:if="{{showSearchResult}}">
<view class="search-mask" bindtap="onClearSearch"></view>
<view class="search-results-content">
<view class="search-results-header">
<view class="search-header-left">
<text class="search-results-title">搜索结果</text>
<text class="search-results-count">({{searchResults.length}}个)</text>
</view>
<view class="search-close" bindtap="onClearSearch">
<image src="/images/close.png" class="close-icon" />
</view>
</view>
<scroll-view class="search-results-list" scroll-y>
<block wx:for="{{searchResults}}" wx:key="id">
<view class="search-result-item" bindtap="goToDetail" data-id="{{item.id}}">
<view class="search-result-left">
<image
src="{{ossImageUrl}}{{item.tupian_url}}"
mode="aspectFill"
class="search-result-image"
/>
</view>
<view class="search-result-right">
<view class="search-result-title">{{item.biaoqian}}</view>
<view class="search-result-info">
<text class="search-result-category">{{item.leixing_name}}</text>
<text class="search-result-separator">·</text>
<text class="search-result-zhuanqu">{{item.zhuanqu_name}}</text>
</view>
<view class="search-result-price">
<text class="price-icon">¥</text>
<text class="price-integer">{{item.priceInteger}}</text>
<text class="price-decimal">.{{item.priceDecimal}}</text>
</view>
</view>
</view>
</block>
<view wx:if="{{searchResults.length === 0 && !isSearching}}" class="search-empty">
<image src="/images/search-empty.png" class="search-empty-icon" />
<text class="search-empty-text">未找到相关商品</text>
</view>
<view wx:if="{{isSearching}}" class="search-loading">
<view class="search-spinner">
<view class="spinner-circle"></view>
</view>
<text class="search-loading-text">搜索中...</text>
</view>
</scroll-view>
</view>
</view>
<!-- 全局加载状态 -->
<view class="loading-toast" wx:if="{{isLoading}}">
<view class="loading-content">
<view class="loading-spinner">
<view class="spinner-ring"></view>
<view class="spinner-dot"></view>
</view>
<text class="loading-text">加载中...</text>
</view>
</view>
</view>
<!-- 底部自定义TabBar -->
<custom-tab-bar />
<!-- pages/category/category.wxml -->
<view class="fenlei-page">
<!-- 1. 毛玻璃搜索框区域(固定) -->
<view class="search-container">
<view class="search-box search-box--glass">
<image src="/images/sousuo.png" class="search-icon" />
<input
type="text"
class="search-input"
placeholder="输入商品名称搜索"
placeholder-class="search-placeholder"
value="{{searchText}}"
bindinput="onSearchInput"
bindconfirm="onSearchConfirm"
confirm-type="search"
/>
<!-- 清空按钮 -->
<view wx:if="{{searchText}}" class="search-clear" bindtap="onClearSearch">
<image src="/images/clear.png" class="clear-icon" />
</view>
<!-- 搜索按钮 -->
<view class="search-btn btn btn-sm btn-sm--gold" bindtap="onSearchTap">
<text class="search-btn-text">搜索</text>
</view>
</view>
</view>
<!-- 2. 商品类型选择区域 -->
<view class="leixing-container" wx:if="{{!showSearchResult}}">
<scroll-view
class="leixing-scroll"
scroll-x
scroll-with-animation
show-scrollbar="{{false}}"
>
<view class="leixing-list">
<block wx:for="{{shangpinleixing}}" wx:key="id" wx:for-index="idx">
<view
class="leixing-item {{selectedLeixingId === item.id ? 'leixing-active' : ''}}"
bindtap="selectLeixing"
data-id="{{item.id}}"
data-index="{{idx}}"
>
<view class="leixing-image-box">
<image
src="{{ossImageUrl}}{{item.tupian_url}}"
mode="aspectFill"
class="leixing-image"
/>
<view class="leixing-glow {{selectedLeixingId === item.id ? 'leixing-glow-active' : ''}}"></view>
</view>
<text class="leixing-text">{{item.jieshao}}</text>
<view class="leixing-active-bar {{selectedLeixingId === item.id ? 'leixing-active-bar-show' : ''}}"></view>
</view>
</block>
</view>
</scroll-view>
</view>
<!-- 3. 左右分栏布局 -->
<view class="main-container" wx:if="{{!showSearchResult}}">
<!-- 左边:商品专区列表 -->
<scroll-view
class="zhuanqu-left"
scroll-y
enhanced="{{true}}"
show-scrollbar="{{false}}"
scroll-into-view="{{'zhuanqu-' + selectedZhuanquId}}"
>
<view class="zhuanqu-list">
<block wx:for="{{filteredZhuanquList}}" wx:key="id">
<view
id="zhuanqu-{{item.id}}"
class="zhuanqu-item {{selectedZhuanquId === item.id ? 'zhuanqu-active' : ''}}"
bindtap="selectZhuanqu"
data-id="{{item.id}}"
>
<view class="zhuanqu-item-content">
<text class="zhuanqu-name">{{item.mingzi}}</text>
<text class="zhuanqu-count">({{zhuanquGoodsCount[item.id] || 0}})</text>
</view>
<view class="zhuanqu-active-indicator {{selectedZhuanquId === item.id ? 'zhuanqu-indicator-glow' : ''}}"></view>
</view>
</block>
<view wx:if="{{filteredZhuanquList.length === 0}}" class="zhuanqu-empty">
<image src="/images/empty-box.png" class="zhuanqu-empty-icon" />
<text class="zhuanqu-empty-text">暂无专区</text>
</view>
</view>
</scroll-view>
<!-- 右边:商品列表 -->
<scroll-view
class="shangpin-right"
scroll-y
enhanced="{{true}}"
show-scrollbar="{{false}}"
>
<view class="shangpin-list">
<block wx:for="{{filteredShangpinList}}" wx:key="id">
<view
class="shangpin-item"
bindtap="goToDetail"
data-id="{{item.id}}"
>
<view class="shangpin-image-box">
<image
src="{{ossImageUrl}}{{item.tupian_url}}"
mode="aspectFill"
class="shangpin-image"
/>
</view>
<view class="shangpin-info">
<view class="shangpin-title">
<text class="shangpin-title-text">{{item.biaoqian}}</text>
</view>
<view class="shangpin-footer">
<view class="shangpin-price">
<text class="price-icon">¥</text>
<text class="price-integer">{{item.priceInteger}}</text>
<text class="price-decimal">.{{item.priceDecimal}}</text>
</view>
<view class="detail-btn">
<text class="detail-btn-text">了解详情</text>
</view>
</view>
</view>
<view class="item-divider" wx:if="{{index !== filteredShangpinList.length - 1}}"></view>
</view>
</block>
<view wx:if="{{filteredShangpinList.length === 0 && !isLoading}}" class="shangpin-empty">
<image src="/images/empty-goods.png" class="shangpin-empty-icon" />
<text class="shangpin-empty-text">该专区暂无商品</text>
</view>
</view>
<view class="load-more" wx:if="{{filteredShangpinList.length > 0}}">
<text class="load-more-text">— 已加载全部商品 —</text>
</view>
</scroll-view>
</view>
<!-- 搜索结果弹窗 -->
<view class="search-results-container" wx:if="{{showSearchResult}}">
<view class="search-mask" bindtap="onClearSearch"></view>
<view class="search-results-content">
<view class="search-results-header">
<view class="search-header-left">
<text class="search-results-title">搜索结果</text>
<text class="search-results-count">({{searchResults.length}}个)</text>
</view>
<view class="search-close" bindtap="onClearSearch">
<image src="/images/close.png" class="close-icon" />
</view>
</view>
<scroll-view class="search-results-list" scroll-y>
<block wx:for="{{searchResults}}" wx:key="id">
<view class="search-result-item" bindtap="goToDetail" data-id="{{item.id}}">
<view class="search-result-left">
<image
src="{{ossImageUrl}}{{item.tupian_url}}"
mode="aspectFill"
class="search-result-image"
/>
</view>
<view class="search-result-right">
<view class="search-result-title">{{item.biaoqian}}</view>
<view class="search-result-info">
<text class="search-result-category">{{item.leixing_name}}</text>
<text class="search-result-separator">·</text>
<text class="search-result-zhuanqu">{{item.zhuanqu_name}}</text>
</view>
<view class="search-result-price">
<text class="price-icon">¥</text>
<text class="price-integer">{{item.priceInteger}}</text>
<text class="price-decimal">.{{item.priceDecimal}}</text>
</view>
</view>
</view>
</block>
<view wx:if="{{searchResults.length === 0 && !isSearching}}" class="search-empty">
<image src="/images/search-empty.png" class="search-empty-icon" />
<text class="search-empty-text">未找到相关商品</text>
</view>
<view wx:if="{{isSearching}}" class="search-loading">
<view class="search-spinner">
<view class="spinner-circle"></view>
</view>
<text class="search-loading-text">搜索中...</text>
</view>
</scroll-view>
</view>
</view>
<!-- 全局加载状态 -->
<view class="loading-toast loading-mask loading-mask--dark" wx:if="{{isLoading}}">
<view class="loading-content">
<view class="loading-spinner">
<view class="spinner-ring"></view>
<view class="spinner-dot"></view>
</view>
<text class="loading-text">加载中...</text>
</view>
</view>
</view>
<!-- 底部自定义TabBar -->
<custom-tab-bar />
<global-notification id="global-notification" />

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +0,0 @@
{
"component": true,
"usingComponents": {
"fakuan-pay": "/pages/cfss/components/fakuan-pay/fakuan-pay"
}
}

View File

@@ -41,10 +41,8 @@ Page({
teamName = p.teamName || '客服';
teamAvatar = p.teamAvatar || '';
} catch (e) {
console.error('解析客服页面参数失败', e);
}
}
// 默认头像使用全局配置
if (!teamAvatar) {
teamAvatar = app.globalData.ossImageUrl + app.globalData.morentouxiang;
}
@@ -171,7 +169,7 @@ Page({
this.setData({ messages: msgs });
},
// ---------- 历史消息 + 永久欢迎语 ----------
// 历史消息
loadHistory(refresh) {
if (!refresh && !this.data.hasMore) return Promise.resolve();
return new Promise((resolve) => {
@@ -209,7 +207,7 @@ Page({
const unique = [];
for (const m of final) { if (!ids.has(m.messageId)) { ids.add(m.messageId); unique.push(m); } }
// 强制在第一行插入欢迎语(如果不存在)
// 插入欢迎语
const welcomeId = 'welcome-cs';
if (!unique.some(m => m.messageId === welcomeId)) {
const welcome = this.createWelcomeMessage();
@@ -249,7 +247,7 @@ Page({
};
},
// ---------- 接收新客服消息 ----------
// 接收新客服消息
listenNewMsg() {
if (this._msgHandler) wx.goEasy.im.off(wx.GoEasy.IM_EVENT.CS_MESSAGE_RECEIVED, this._msgHandler);
this._msgHandler = (msg) => {
@@ -310,7 +308,7 @@ Page({
wx.goEasy.im.on(wx.GoEasy.IM_EVENT.MESSAGE_DELETED, this._deleteHandler);
},
// ---------- 面板控制 ----------
// 面板控制
togglePlusPanel() { this.setData({ showPlusPanel: !this.data.showPlusPanel, showEmojiPanel: false }); },
closePlusPanel() { this.setData({ showPlusPanel: false }); },
openEmojiFromPlus() { this.setData({ showPlusPanel: false, showEmojiPanel: true }); },
@@ -318,7 +316,7 @@ Page({
insertEmoji(e) { this.setData({ inputText: this.data.inputText + e.currentTarget.dataset.emoji }); },
closeEmojiPanel() { this.setData({ showEmojiPanel: false }); },
// ---------- 发送消息 ----------
// 发送消息
onInput(e) { this.setData({ inputText: e.detail.value }); },
chooseImage() {
const that = this;
@@ -409,7 +407,7 @@ Page({
});
},
// ---------- 订单发送 ----------
// 订单发送
openOrderSender() { this.setData({ showOrderSender: true, showPlusPanel: false }); },
closeOrderSender() { this.setData({ showOrderSender: false }); },
onSendOrder(e) {
@@ -445,7 +443,7 @@ Page({
updateMsg(id, status) { this.setData({ messages: this.data.messages.map(m => m.messageId === id ? { ...m, status } : m) }); },
needShow(ts) { const ms = this.data.messages; if (!ms.length) return true; const last = ms[ms.length-1]; return (ts - last.timestamp) / 60000 > 5; },
// ---------- 消息操作 ----------
// 消息操作
onBubbleTap(e) {
const msgId = e.currentTarget.dataset.messageid;
const text = e.currentTarget.dataset.text;

View File

@@ -1,228 +1,227 @@
// pages/xiugai/xiugai.js
const app = getApp()
import upload from '../../utils/upload.js'
Page({
data: {
avatarUrl: '',
uid: '',
nicheng: '',
phone: '',
originalAvatar: '',
originalNicheng: '',
isSaving: false,
hasChanges: false,
newAvatarFile: null,
isLoading: true,
isAvatarActive: false,
nicknameError: ''
},
onLoad() {
wx.setNavigationBarTitle({ title: '编辑资料' })
this.loadUserData()
},
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()
};
}
},
// 加载用户数据(手机号只读,不参与修改逻辑)
loadUserData() {
try {
const uid = wx.getStorageSync('uid') || ''
const nicheng = wx.getStorageSync('nicheng') || ''
const phone = wx.getStorageSync('phone') || ''
const touxiang = wx.getStorageSync('touxiang') || ''
const ossImageUrl = app.globalData.ossImageUrl || ''
const defaultAvatar = app.globalData.morentouxiang || ''
let avatarUrl = ''
if (touxiang) {
avatarUrl = touxiang.startsWith('http') ? touxiang : `${ossImageUrl}${touxiang}`
} else if (defaultAvatar) {
avatarUrl = defaultAvatar.startsWith('http') ? defaultAvatar : `${ossImageUrl}${defaultAvatar}`
}
this.setData({
avatarUrl,
uid,
nicheng,
phone, // 仅展示
originalAvatar: touxiang,
originalNicheng: nicheng,
isLoading: false
})
} catch (error) {
console.error('加载用户数据失败:', error)
this.setData({ isLoading: false })
wx.showToast({ title: '加载数据失败', icon: 'none' })
}
},
// 微信头像选择(未改动)
onChooseAvatar(e) {
const avatarTempPath = e.detail.avatarUrl
this.setData({
avatarUrl: avatarTempPath,
newAvatarFile: avatarTempPath
}, () => {
this.checkChanges()
})
},
chooseAvatar() {
// 仅保留动画效果(透明按钮已触发 onChooseAvatar
this.setData({ isAvatarActive: true })
setTimeout(() => {
this.setData({ isAvatarActive: false })
}, 500)
},
// 昵称输入(未改动)
onNicknameInput(e) {
const value = e.detail.value.trim()
this.setData({
nicheng: value,
nicknameError: ''
}, () => {
this.checkChanges()
})
},
// 检查变化(只比较昵称和头像)
checkChanges() {
const { originalNicheng, nicheng, newAvatarFile } = this.data;
const hasChanges = (nicheng !== originalNicheng) || (newAvatarFile !== null);
this.setData({ hasChanges });
},
// 保存(不包含手机号逻辑)
async onSave() {
if (!this.data.hasChanges) {
wx.showToast({ title: '没有需要保存的更改', icon: 'none' });
return;
}
if (this.data.isSaving) return;
// 验证数据(昵称)
if (!this.validateData()) return;
this.setData({ isSaving: true });
wx.showLoading({ title: '保存中...', mask: true });
try {
const formData = {};
if (this.data.nicheng !== this.data.originalNicheng) {
formData.nicheng = this.data.nicheng;
}
const res = await upload({
url: '/yonghu/xiugai',
formData,
filePath: this.data.newAvatarFile,
fileName: 'avatar'
});
if (res.statusCode === 200 && res.data) {
const response = res.data;
if (response.code === 0) {
await this.handleUpdateSuccess(response.data);
} else {
throw new Error(response.msg || '保存失败');
}
} else {
throw new Error('网络错误');
}
} catch (error) {
console.error('保存失败:', error);
if (error.message.includes('认证失败') || error.message.includes('401')) {
wx.showModal({
title: '提示',
content: '登录已过期,请重新登录',
complete: (res) => {
if (res.confirm) {
wx.removeStorageSync('token');
wx.removeStorageSync('userInfo');
wx.switchTab({ url: '/pages/wode/wode' });
}
}
});
} else {
wx.showToast({ title: error.message || '保存失败', icon: 'none' });
}
} finally {
wx.hideLoading();
this.setData({ isSaving: false });
}
},
// 验证数据(只验证昵称)
validateData() {
const { nicheng } = this.data;
let hasError = false;
this.setData({ nicknameError: '' });
if (nicheng && nicheng.length > 20) {
this.setData({ nicknameError: '昵称不能超过20个字符' });
hasError = true;
}
if (nicheng !== this.data.originalNicheng && nicheng.trim() === '') {
this.setData({ nicknameError: '昵称不能为空' });
hasError = true;
}
if (hasError) {
if (this.data.nicknameError) {
wx.showToast({ title: this.data.nicknameError, icon: 'none' });
}
return false;
}
return true;
},
// 更新成功处理(不再处理手机号)
async handleUpdateSuccess(updateData) {
if (updateData.touxiang !== undefined && updateData.touxiang !== null) {
wx.setStorageSync('touxiang', updateData.touxiang);
}
if (updateData.nicheng !== undefined && updateData.nicheng !== null) {
wx.setStorageSync('nicheng', updateData.nicheng);
}
if (updateData.nicheng) {
app.globalData.userNicheng = updateData.nicheng;
}
const ossImageUrl = app.globalData.ossImageUrl || '';
let avatarUrl = this.data.avatarUrl;
if (updateData.touxiang) {
avatarUrl = updateData.touxiang.startsWith('http') ?
updateData.touxiang : `${ossImageUrl}${updateData.touxiang}`;
}
this.setData({
avatarUrl,
originalAvatar: updateData.touxiang || this.data.originalAvatar,
originalNicheng: updateData.nicheng || this.data.originalNicheng,
newAvatarFile: null,
hasChanges: false
});
wx.showToast({
title: '保存成功',
icon: 'success',
duration: 2000
});
}
// pages/edit/edit.js
const app = getApp()
import upload from '../../utils/upload.js'
Page({
data: {
avatarUrl: '',
uid: '',
nicheng: '',
phone: '',
originalAvatar: '',
originalNicheng: '',
isSaving: false,
hasChanges: false,
newAvatarFile: null,
isLoading: true,
isAvatarActive: false,
nicknameError: ''
},
onLoad() {
wx.setNavigationBarTitle({ title: '编辑资料' })
this.loadUserData()
},
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()
};
}
},
// 加载用户数据
loadUserData() {
try {
const uid = wx.getStorageSync('uid') || ''
const nicheng = wx.getStorageSync('nicheng') || ''
const phone = wx.getStorageSync('phone') || ''
const touxiang = wx.getStorageSync('touxiang') || ''
const ossImageUrl = app.globalData.ossImageUrl || ''
const defaultAvatar = app.globalData.morentouxiang || ''
let avatarUrl = ''
if (touxiang) {
avatarUrl = touxiang.startsWith('http') ? touxiang : `${ossImageUrl}${touxiang}`
} else if (defaultAvatar) {
avatarUrl = defaultAvatar.startsWith('http') ? defaultAvatar : `${ossImageUrl}${defaultAvatar}`
}
this.setData({
avatarUrl,
uid,
nicheng,
phone,
originalAvatar: touxiang,
originalNicheng: nicheng,
isLoading: false
})
} catch (error) {
console.error('加载用户数据失败:', error)
this.setData({ isLoading: false })
wx.showToast({ title: '加载数据失败', icon: 'none' })
}
},
// 微信头像选择
onChooseAvatar(e) {
const avatarTempPath = e.detail.avatarUrl
this.setData({
avatarUrl: avatarTempPath,
newAvatarFile: avatarTempPath
}, () => {
this.checkChanges()
})
},
chooseAvatar() {
this.setData({ isAvatarActive: true })
setTimeout(() => {
this.setData({ isAvatarActive: false })
}, 500)
},
// 昵称输入
onNicknameInput(e) {
const value = e.detail.value.trim()
this.setData({
nicheng: value,
nicknameError: ''
}, () => {
this.checkChanges()
})
},
// 检查变化
checkChanges() {
const { originalNicheng, nicheng, newAvatarFile } = this.data;
const hasChanges = (nicheng !== originalNicheng) || (newAvatarFile !== null);
this.setData({ hasChanges });
},
// 保存
async onSave() {
if (!this.data.hasChanges) {
wx.showToast({ title: '没有需要保存的更改', icon: 'none' });
return;
}
if (this.data.isSaving) return;
// 验证数据
if (!this.validateData()) return;
this.setData({ isSaving: true });
wx.showLoading({ title: '保存中...', mask: true });
try {
const formData = {};
if (this.data.nicheng !== this.data.originalNicheng) {
formData.nicheng = this.data.nicheng;
}
const res = await upload({
url: '/yonghu/xiugai',
formData,
filePath: this.data.newAvatarFile,
fileName: 'avatar'
});
if (res.statusCode === 200 && res.data) {
const response = res.data;
if (response.code === 0) {
await this.handleUpdateSuccess(response.data);
} else {
throw new Error(response.msg || '保存失败');
}
} else {
throw new Error('网络错误');
}
} catch (error) {
console.error('保存失败:', error);
if (error.message.includes('认证失败') || error.message.includes('401')) {
wx.showModal({
title: '提示',
content: '登录已过期,请重新登录',
complete: (res) => {
if (res.confirm) {
wx.removeStorageSync('token');
wx.removeStorageSync('userInfo');
wx.switchTab({ url: '/pages/mine/mine' });
}
}
});
} else {
wx.showToast({ title: error.message || '保存失败', icon: 'none' });
}
} finally {
wx.hideLoading();
this.setData({ isSaving: false });
}
},
// 验证数据
validateData() {
const { nicheng } = this.data;
let hasError = false;
this.setData({ nicknameError: '' });
if (nicheng && nicheng.length > 20) {
this.setData({ nicknameError: '昵称不能超过20个字符' });
hasError = true;
}
if (nicheng !== this.data.originalNicheng && nicheng.trim() === '') {
this.setData({ nicknameError: '昵称不能为空' });
hasError = true;
}
if (hasError) {
if (this.data.nicknameError) {
wx.showToast({ title: this.data.nicknameError, icon: 'none' });
}
return false;
}
return true;
},
// 更新成功处理
async handleUpdateSuccess(updateData) {
if (updateData.touxiang !== undefined && updateData.touxiang !== null) {
wx.setStorageSync('touxiang', updateData.touxiang);
}
if (updateData.nicheng !== undefined && updateData.nicheng !== null) {
wx.setStorageSync('nicheng', updateData.nicheng);
}
if (updateData.nicheng) {
app.globalData.userNicheng = updateData.nicheng;
}
const ossImageUrl = app.globalData.ossImageUrl || '';
let avatarUrl = this.data.avatarUrl;
if (updateData.touxiang) {
avatarUrl = updateData.touxiang.startsWith('http') ?
updateData.touxiang : `${ossImageUrl}${updateData.touxiang}`;
}
this.setData({
avatarUrl,
originalAvatar: updateData.touxiang || this.data.originalAvatar,
originalNicheng: updateData.nicheng || this.data.originalNicheng,
newAvatarFile: null,
hasChanges: false
});
wx.showToast({
title: '保存成功',
icon: 'success',
duration: 2000
});
}
});

View File

@@ -1,69 +1,69 @@
<!-- pages/xiugai/xiugai.wxml -->
<view class="xiugai-page">
<!-- 头像区域(未改动) -->
<view class="avatar-section">
<view class="avatar-container {{isAvatarActive ? 'active' : ''}}">
<!-- 新增:头像包装器,把头像+光晕包在一起,和提示文字隔离 -->
<view class="avatar-image-wrapper">
<image class="avatar-image" src="{{avatarUrl}}" mode="aspectFill"></image>
<view class="avatar-halo"></view>
</view>
<view class="avatar-hint">点击更换头像</view>
</view>
<button
class="avatar-trigger-button"
open-type="chooseAvatar"
bindchooseavatar="onChooseAvatar"
hover-class="none"
></button>
</view>
<!-- 表单区域 -->
<view class="form-section">
<!-- UID只读 -->
<view class="form-item">
<view class="item-label">UID</view>
<view class="item-value readonly">{{uid || '••••••'}}</view>
</view>
<!-- 昵称(可编辑) -->
<view class="form-item">
<view class="item-label">昵称</view>
<view class="input-container">
<input
class="item-input {{nicknameError ? 'error' : ''}}"
type="text"
placeholder="请输入昵称"
placeholder-class="placeholder"
maxlength="20"
value="{{nicheng}}"
bindinput="onNicknameInput"
/>
<view class="error-hint" wx:if="{{nicknameError}}">{{nicknameError}}</view>
</view>
</view>
<!-- 手机号(只读展示,不可修改) -->
<view class="form-item">
<view class="item-label">手机号</view>
<view class="item-value readonly">{{phone || '未绑定'}}</view>
</view>
</view>
<!-- 保存按钮(未改动) -->
<button
class="save-btn"
bindtap="onSave"
hover-class="btn-hover"
disabled="{{isSaving || !hasChanges}}"
>
<view class="btn-content">
{{isSaving ? '保存中...' : '确认保存'}}
<view class="btn-sparkle" wx:if="{{!isSaving}}"></view>
</view>
</button>
<view class="safe-area"></view>
<global-notification id="global-notification" />
<!-- pages/edit/edit.wxml -->
<view class="xiugai-page">
<!-- 头像区域(未改动) -->
<view class="avatar-section">
<view class="avatar-container {{isAvatarActive ? 'active' : ''}}">
<!-- 新增:头像包装器,把头像+光晕包在一起,和提示文字隔离 -->
<view class="avatar-image-wrapper">
<image class="avatar-image" src="{{avatarUrl}}" mode="aspectFill"></image>
<view class="avatar-halo"></view>
</view>
<view class="avatar-hint">点击更换头像</view>
</view>
<button
class="avatar-trigger-button"
open-type="chooseAvatar"
bindchooseavatar="onChooseAvatar"
hover-class="none"
></button>
</view>
<!-- 表单区域 -->
<view class="form-section">
<!-- UID只读 -->
<view class="form-item">
<view class="item-label">UID</view>
<view class="item-value readonly">{{uid || '••••••'}}</view>
</view>
<!-- 昵称(可编辑) -->
<view class="form-item">
<view class="item-label">昵称</view>
<view class="input-container">
<input
class="item-input {{nicknameError ? 'error' : ''}}"
type="text"
placeholder="请输入昵称"
placeholder-class="placeholder"
maxlength="20"
value="{{nicheng}}"
bindinput="onNicknameInput"
/>
<view class="error-hint" wx:if="{{nicknameError}}">{{nicknameError}}</view>
</view>
</view>
<!-- 手机号(只读展示,不可修改) -->
<view class="form-item">
<view class="item-label">手机号</view>
<view class="item-value readonly">{{phone || '未绑定'}}</view>
</view>
</view>
<!-- 保存按钮(未改动) -->
<button
class="save-btn"
bindtap="onSave"
hover-class="btn-hover"
disabled="{{isSaving || !hasChanges}}"
>
<view class="btn-content">
{{isSaving ? '保存中...' : '确认保存'}}
<view class="btn-sparkle" wx:if="{{!isSaving}}"></view>
</view>
</button>
<view class="safe-area"></view>
<global-notification id="global-notification" />
</view>

View File

@@ -1,401 +1,380 @@
/* pages/xiugai/xiugai.wxss */
page {
background: linear-gradient(135deg, #fffaf0 0%, #f0f8ff 100%);
min-height: 100vh;
}
.xiugai-page {
padding: 30rpx 40rpx 100rpx;
}
/* ============ 头像区域样式 ============ */
.avatar-section {
position: relative;
display: flex;
justify-content: center;
margin-bottom: 60rpx;
padding-top: 30rpx;
}
.avatar-container {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
}
/* 头像包装器:精确居中,包含头像+光晕 */
.avatar-image-wrapper {
position: relative;
width: 200rpx;
height: 200rpx;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
}
/* 头像图片 */
.avatar-image {
width: 200rpx;
height: 200rpx;
border-radius: 50%;
border: 6rpx solid #fff;
background-color: #f8f9fa;
z-index: 2;
position: relative;
animation: gentleBreath 4s ease-in-out infinite;
box-shadow:
0 0 0 1rpx rgba(0, 200, 83, 0.3),
0 15rpx 40rpx rgba(0, 200, 83, 0.15);
box-sizing: border-box; /* 关键:边框向内计算,不占宽度 */
}
/* ✅ 光晕精确居中:已计算头像 6rpx 边框的偏移量 */
.avatar-halo {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%); /* 强制居中,无视任何偏差 */
width: 228rpx; /* 精确计算200 + 6*2(边框) + 8*2(外延) = 228 */
height: 228rpx;
border-radius: 50%;
background: transparent;
border: 3rpx solid transparent;
animation: gentleGlow 2s ease-in-out infinite, colorShift 3s ease-in-out infinite;
opacity: 0;
z-index: 1;
pointer-events: none;
}
/* 像素点环绕也强制居中 */
.avatar-image-wrapper::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 230rpx;
height: 230rpx;
border-radius: 50%;
background:
radial-gradient(circle at 30% 30%, rgba(0, 200, 83, 0.1) 0%, transparent 50%),
radial-gradient(circle at 70% 70%, rgba(100, 221, 23, 0.1) 0%, transparent 50%);
animation: pixelOrbit 4s linear infinite;
z-index: 0;
pointer-events: none;
}
/* 温和的呼吸动画 */
@keyframes gentleBreath {
0%, 100% {
transform: scale(1);
box-shadow:
0 0 0 2rpx rgba(0, 200, 83, 0.4),
0 15rpx 40rpx rgba(0, 200, 83, 0.2);
}
50% {
transform: scale(1.03);
box-shadow:
0 0 0 5rpx rgba(0, 200, 83, 0.6),
0 20rpx 50rpx rgba(0, 200, 83, 0.3);
}
}
/* 光晕动画 */
@keyframes gentleGlow {
0%, 100% {
opacity: 0;
transform: translate(-50%, -50%) scale(0.98);
}
50% {
opacity: 1;
transform: translate(-50%, -50%) scale(1.05);
}
}
@keyframes colorShift {
0% {
border-color: rgba(0, 200, 83, 0.5);
}
33% {
border-color: rgba(100, 221, 23, 0.5);
}
66% {
border-color: rgba(0, 255, 153, 0.5);
}
100% {
border-color: rgba(0, 200, 83, 0.5);
}
}
@keyframes pixelOrbit {
0% {
transform: translate(-50%, -50%) rotate(0deg);
}
100% {
transform: translate(-50%, -50%) rotate(360deg);
}
}
/* 更换提示 */
.avatar-hint {
margin-top: 25rpx;
font-size: 28rpx;
color: #666;
padding: 12rpx 30rpx;
background: rgba(255, 255, 255, 0.9);
border-radius: 25rpx;
border: 1rpx solid #eee;
box-shadow: 0 5rpx 15rpx rgba(0, 0, 0, 0.05);
position: relative;
overflow: hidden;
}
.avatar-hint::before {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 2rpx;
background: linear-gradient(90deg, transparent, #00C853, transparent);
animation: hintGlow 2s ease-in-out infinite;
}
@keyframes hintGlow {
0%, 100% {
opacity: 0.3;
}
50% {
opacity: 1;
}
}
/* 点击特效 */
.avatar-container.active .avatar-image {
animation: clickScale 0.5s ease;
}
@keyframes clickScale {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(0.95);
}
}
/* 下载状态 */
.avatar-container.downloading .avatar-image {
opacity: 0.7;
filter: grayscale(30%);
}
/* 透明功能按钮 */
.avatar-trigger-button {
position: absolute;
top: 30rpx;
left: 50%;
transform: translateX(-50%);
width: 200rpx;
height: 200rpx;
border-radius: 50%;
z-index: 100;
background: transparent;
border: none;
padding: 0;
margin: 0;
opacity: 0;
line-height: 1;
font-size: 0;
}
.avatar-trigger-button::after {
border: none;
}
/* ============ 表单区域 ============ */
.form-section {
background: white;
border-radius: 25rpx;
overflow: hidden;
margin-bottom: 50rpx;
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.08);
border: 1rpx solid #f0f0f0;
}
.form-item {
display: flex;
align-items: center;
padding-left: 40rpx;
border-bottom: 1rpx solid #f0f0f0;
min-height: 110rpx;
}
.form-item:last-child {
border-bottom: none;
}
.item-label {
width: 150rpx;
font-size: 32rpx;
color: #333;
font-weight: 500;
flex-shrink: 0;
}
.item-value.readonly {
flex: 1;
font-size: 32rpx;
color: #666;
}
.item-input {
flex: 1;
font-size: 32rpx;
color: #333;
height: 50rpx;
line-height: 50rpx;
}
.placeholder {
color: #ccc;
font-size: 30rpx;
}
.input-container {
flex: 1;
display: flex;
flex-direction: column;
}
.item-input.error {
border-bottom: 2rpx solid #ff4d4f;
animation: shake 0.5s;
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-2rpx); }
20%, 40%, 60%, 80% { transform: translateX(2rpx); }
}
.error-hint {
font-size: 24rpx;
color: #ff4d4f;
margin-top: 8rpx;
padding-left: 10rpx;
animation: fadeIn 0.3s;
}
.phone-hint {
font-size: 24rpx;
color: #00C853;
margin-top: 8rpx;
padding-left: 10rpx;
animation: fadeIn 0.3s;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-10rpx); }
to { opacity: 1; transform: translateY(0); }
}
/* ============ 保存按钮 ============ */
.save-btn {
width: 100%;
height: 100rpx;
line-height: 100rpx;
border-radius: 50rpx;
background: linear-gradient(135deg, #00C853 0%, #64DD17 100%);
color: white;
font-size: 34rpx;
font-weight: 500;
border: none;
position: relative;
overflow: hidden;
box-shadow: 0 15rpx 40rpx rgba(0, 200, 83, 0.25);
margin-top: 30rpx;
}
.save-btn[disabled] {
background: linear-gradient(135deg, #cccccc 0%, #aaaaaa 100%);
color: #999;
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.1);
}
.btn-content {
position: relative;
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.btn-sparkle {
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
transparent 0%,
rgba(255, 255, 255, 0.2) 50%,
transparent 100%
);
animation: sparkleMove 2.5s ease-in-out infinite;
z-index: 1;
}
@keyframes sparkleMove {
0% { left: -100%; }
100% { left: 100%; }
}
.btn-hover {
opacity: 0.9;
transform: scale(0.98);
}
/* ============ 手机号获取按钮 ============ */
.phone-row {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
}
.phone-value {
font-size: 32rpx;
color: #333;
flex: 1;
}
.get-phone-btn {
flex-shrink: 0;
margin-left: 20rpx;
padding: 10rpx 24rpx;
background: #4a6cf7;
color: #fff;
font-size: 26rpx;
border-radius: 40rpx;
border: none;
line-height: 1.4;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
.phone-btn-hover {
opacity: 0.8;
}
.safe-area {
height: 100rpx;
width: 100%;
/* pages/edit/edit.wxss */
page {
background: linear-gradient(135deg, #fffaf0 0%, #f0f8ff 100%);
min-height: 100vh;
}
.xiugai-page {
padding: 30rpx 40rpx 100rpx;
}
/* 头像区域样式 */
.avatar-section {
position: relative;
display: flex;
justify-content: center;
margin-bottom: 60rpx;
padding-top: 30rpx;
}
.avatar-container {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
}
/* 头像包装器 */
.avatar-image-wrapper {
position: relative;
width: 200rpx;
height: 200rpx;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
}
/* 头像图片 */
.avatar-image {
width: 200rpx;
height: 200rpx;
border-radius: 50%;
border: 6rpx solid #fff;
background-color: #f8f9fa;
z-index: 2;
position: relative;
animation: gentleBreath 4s ease-in-out infinite;
box-shadow:
0 0 0 1rpx rgba(0, 200, 83, 0.3),
0 15rpx 40rpx rgba(0, 200, 83, 0.15);
box-sizing: border-box;
}
/* 光晕 */
.avatar-halo {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 228rpx;
height: 228rpx;
border-radius: 50%;
background: transparent;
border: 3rpx solid transparent;
animation: gentleGlow 2s ease-in-out infinite, colorShift 3s ease-in-out infinite;
opacity: 0;
z-index: 1;
pointer-events: none;
}
/* 像素点环绕 */
.avatar-image-wrapper::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 230rpx;
height: 230rpx;
border-radius: 50%;
background:
radial-gradient(circle at 30% 30%, rgba(0, 200, 83, 0.1) 0%, transparent 50%),
radial-gradient(circle at 70% 70%, rgba(100, 221, 23, 0.1) 0%, transparent 50%);
animation: pixelOrbit 4s linear infinite;
z-index: 0;
pointer-events: none;
}
/* 温和的呼吸动画 */
@keyframes gentleBreath {
0%, 100% {
transform: scale(1);
box-shadow:
0 0 0 2rpx rgba(0, 200, 83, 0.4),
0 15rpx 40rpx rgba(0, 200, 83, 0.2);
}
50% {
transform: scale(1.03);
box-shadow:
0 0 0 5rpx rgba(0, 200, 83, 0.6),
0 20rpx 50rpx rgba(0, 200, 83, 0.3);
}
}
/* 光晕动画 */
@keyframes gentleGlow {
0%, 100% {
opacity: 0;
transform: translate(-50%, -50%) scale(0.98);
}
50% {
opacity: 1;
transform: translate(-50%, -50%) scale(1.05);
}
}
@keyframes colorShift {
0% {
border-color: rgba(0, 200, 83, 0.5);
}
33% {
border-color: rgba(100, 221, 23, 0.5);
}
66% {
border-color: rgba(0, 255, 153, 0.5);
}
100% {
border-color: rgba(0, 200, 83, 0.5);
}
}
@keyframes pixelOrbit {
0% {
transform: translate(-50%, -50%) rotate(0deg);
}
100% {
transform: translate(-50%, -50%) rotate(360deg);
}
}
/* 更换提示 */
.avatar-hint {
margin-top: 25rpx;
font-size: 28rpx;
color: #666;
padding: 12rpx 30rpx;
background: rgba(255, 255, 255, 0.9);
border-radius: 25rpx;
border: 1rpx solid #eee;
box-shadow: 0 5rpx 15rpx rgba(0, 0, 0, 0.05);
position: relative;
overflow: hidden;
}
.avatar-hint::before {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 2rpx;
background: linear-gradient(90deg, transparent, #00C853, transparent);
animation: hintGlow 2s ease-in-out infinite;
}
@keyframes hintGlow {
0%, 100% {
opacity: 0.3;
}
50% {
opacity: 1;
}
}
/* 点击特效 */
.avatar-container.active .avatar-image {
animation: clickScale 0.5s ease;
}
@keyframes clickScale {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(0.95);
}
}
/* 下载状态 */
.avatar-container.downloading .avatar-image {
opacity: 0.7;
filter: grayscale(30%);
}
/* 透明功能按钮 */
.avatar-trigger-button {
position: absolute;
top: 30rpx;
left: 50%;
transform: translateX(-50%);
width: 200rpx;
height: 200rpx;
border-radius: 50%;
z-index: 100;
background: transparent;
border: none;
padding: 0;
margin: 0;
opacity: 0;
line-height: 1;
font-size: 0;
}
.avatar-trigger-button::after {
border: none;
}
/* 表单区域 */
.form-section {
border-radius: 25rpx;
margin-bottom: 50rpx;
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.08);
border: 1rpx solid #f0f0f0;
}
.form-item {
display: flex;
align-items: center;
padding-left: 40rpx;
border-bottom: 1rpx solid #f0f0f0;
min-height: 110rpx;
}
.form-item:last-child {
border-bottom: none;
}
.item-label {
width: 150rpx;
font-size: 32rpx;
color: #333;
font-weight: 500;
flex-shrink: 0;
}
.item-value.readonly {
flex: 1;
font-size: 32rpx;
color: #666;
}
/* .item-input — 与全局 .input-inline 完全相同,已移除 */
.placeholder {
color: #ccc;
font-size: 30rpx;
}
.input-container {
flex: 1;
display: flex;
flex-direction: column;
}
.item-input.error {
border-bottom: 2rpx solid #ff4d4f;
animation: shake 0.5s;
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-2rpx); }
20%, 40%, 60%, 80% { transform: translateX(2rpx); }
}
.error-hint {
font-size: 24rpx;
color: #ff4d4f;
margin-top: 8rpx;
padding-left: 10rpx;
animation: fadeIn 0.3s;
}
.phone-hint {
font-size: 24rpx;
color: #00C853;
margin-top: 8rpx;
padding-left: 10rpx;
animation: fadeIn 0.3s;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-10rpx); }
to { opacity: 1; transform: translateY(0); }
}
/* 保存按钮 */
.save-btn {
height: 100rpx;
line-height: 100rpx;
border-radius: 50rpx;
background: linear-gradient(135deg, #00C853 0%, #64DD17 100%);
font-weight: 500;
box-shadow: 0 15rpx 40rpx rgba(0, 200, 83, 0.25);
margin-top: 30rpx;
}
.save-btn[disabled] {
background: linear-gradient(135deg, #cccccc 0%, #aaaaaa 100%);
color: #999;
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.1);
}
.btn-content {
position: relative;
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.btn-sparkle {
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
transparent 0%,
rgba(255, 255, 255, 0.2) 50%,
transparent 100%
);
animation: sparkleMove 2.5s ease-in-out infinite;
z-index: 1;
}
@keyframes sparkleMove {
0% { left: -100%; }
100% { left: 100%; }
}
.btn-hover {
opacity: 0.9;
transform: scale(0.98);
}
/* 手机号获取按钮 */
.phone-row {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
}
.phone-value {
font-size: 32rpx;
color: #333;
flex: 1;
}
.get-phone-btn {
flex-shrink: 0;
margin-left: 20rpx;
padding: 10rpx 24rpx;
background: #4a6cf7;
line-height: 1.4;
height: 60rpx;
}
.phone-btn-hover {
opacity: 0.8;
}
.safe-area {
height: 100rpx;
width: 100%;
}

View File

@@ -1,66 +1,66 @@
// pages/peihuDingdan/peihuDingdan.js
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
// pages/escort-orders/escort-orders.js
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})

View File

@@ -0,0 +1,2 @@
<!--pages/escort-orders/escort-orders.wxml-->
<text>pages/escort-orders/escort-orders.wxml</text>

View File

@@ -0,0 +1 @@
/* pages/escort-orders/escort-orders.wxss */

File diff suppressed because it is too large Load Diff

View File

@@ -1,230 +1,230 @@
<!-- pages/jisufd/jisufd.wxml -->
<view class="page-container">
<!-- 余额卡片 -->
<view class="balance-card">
<text class="balance-label">可用余额</text>
<view class="balance-amount">
<text class="amount-value">{{sjyue}}</text>
<text class="amount-unit">元</text>
</view>
<text class="balance-tip">生成链接将从此余额扣除</text>
</view>
<!-- 商品类型选择 -->
<view class="section">
<text class="section-title">选择订单类型</text>
<scroll-view class="type-scroll" scroll-x="true" enable-flex>
<block wx:for="{{shangpinList}}" wx:key="id">
<view class="type-item {{selectedTypeId === item.id ? 'type-active' : ''}}"
data-id="{{item.id}}" data-item="{{item}}" bindtap="onSelectType">
<text class="type-text">{{item.jieshao}}</text>
</view>
</block>
</scroll-view>
</view>
<!-- 搜索区域 -->
<view class="search-section">
<view class="search-box">
<text class="search-icon">🔍</text>
<input class="search-input" placeholder="输入模板ID/介绍..." value="{{searchParams.keyword}}"
bindinput="onSearchKeywordInput" bindconfirm="onSearchConfirm"/>
<view class="search-btn" bindtap="onSearchConfirm">
<text class="search-btn-text">搜索</text>
</view>
</view>
<view class="filter-row">
<view class="filter-item">
<text class="filter-label">金额≥</text>
<input class="filter-input" type="digit" placeholder="元" value="{{searchParams.minPrice}}" bindinput="onSearchMinPriceInput"/>
</view>
<view class="filter-item">
<text class="filter-label">标签</text>
<picker mode="selector" range="{{currentTypeChenghaoList}}" range-key="mingcheng" bindchange="onSearchLabelChange">
<view class="picker-view">
<text>{{searchLabelName || '全部'}}</text>
</view>
</picker>
</view>
<view class="clear-search" bindtap="onClearSearch"><text>清空</text></view>
</view>
</view>
<!-- 模板卡片网格 -->
<view class="template-section">
<scroll-view class="template-scroll" scroll-y="true" scroll-with-animation="true" bindscrolltolower="onScrollToLower">
<view class="template-grid">
<block wx:for="{{templateList}}" wx:key="mobanId">
<view class="template-card" bindtap="onCardTap" data-item="{{item}}">
<view class="template-intro">
<text class="intro-text">{{item.jieshao || '无介绍'}}</text>
</view>
<view class="template-label" wx:if="{{item.labelName}}">
<chenghao-tag mingcheng="{{item.labelName}}" texiaoJson="{{''}}"/>
</view>
<view class="template-price-row">
<text class="template-price-label">价格:</text>
<text class="template-price-value">{{item.jiage || '0.00'}}元</text>
</view>
<view class="template-actions-grid">
<view class="action-grid-btn generate-grid-btn {{item.hasGenerated ? 'generated' : ''}}"
data-id="{{item.mobanId}}" catchtap="onGenerateLink">
{{item.hasGenerated ? '获取新链接' : '生成链接'}}
</view>
<view class="action-grid-btn copy-grid-btn {{item.isCopied ? 'copied' : ''}}"
data-id="{{item.mobanId}}" catchtap="onCopyLink">
{{item.isCopied ? '已复制' : '一键复制'}}
</view>
</view>
</view>
</block>
<view class="template-card add-card" bindtap="showAddModal">
<view class="add-icon"></view>
<text class="add-text">添加模板</text>
<text class="add-subtext">点击创建新模板</text>
</view>
</view>
<view class="load-more" wx:if="{{isLoadingMore}}">...</view>
<view class="no-more" wx:if="{{!hasMoreData && templateList.length > 0}}">没有更多模板了</view>
<view class="empty-state" wx:if="{{templateList.length === 0 && !isLoading}}">暂无模板</view>
</scroll-view>
</view>
</view>
<!-- 详情弹窗 -->
<view class="modal-overlay" wx:if="{{showDetailModal}}" bindtap="hideDetailModal">
<view class="modal-content detail-modal" catchtap="stopPropagation">
<view class="modal-close" bindtap="hideDetailModal">×</view>
<view class="modal-header"><text class="modal-title">模板详情</text></view>
<view class="detail-info" wx:if="{{currentTemplate}}">
<view class="detail-item">
<text class="detail-label">订单介绍</text>
<view class="detail-value intro-value"><text>{{currentTemplate.jieshao || '无介绍'}}</text></view>
</view>
<view class="detail-item" wx:if="{{currentTemplate.labelName}}">
<text class="detail-label">关联标签</text>
<chenghao-tag mingcheng="{{currentTemplate.labelName}}" texiaoJson="{{currentTemplate.texiaoJson}}"/>
</view>
<view class="detail-meta-row">
<view class="meta-item"><text class="meta-label">价格</text><text class="meta-value price-meta">{{currentTemplate.jiage || '0.00'}}元</text></view>
<view class="meta-divider">|</view>
<view class="meta-item"><text class="meta-label">发布数量</text><text class="meta-value count-meta">{{currentTemplate.fabushuliang || 0}}次</text></view>
<view class="meta-divider">|</view>
<view class="meta-item"><text class="meta-label">模板ID</text><text class="meta-value id-meta">{{currentTemplate.mobanId || 'N/A'}}</text></view>
</view>
<view class="detail-item" wx:if="{{currentTemplate.commissionEnabled}}">
<text class="detail-label">佣金要求</text>
<text class="detail-value">{{currentTemplate.commissionValue}}元建议≤50</text>
</view>
<!-- 链接筛选 -->
<view class="link-filter-bar">
<text class="filter-label">链接状态:</text>
<view class="filter-switch {{detailLinkFilter === 0 ? 'active' : ''}}" bindtap="onToggleLinkFilter">未使用</view>
<view class="filter-switch {{detailLinkFilter === 1 ? 'active' : ''}}" bindtap="onToggleLinkFilter">已使用</view>
</view>
<scroll-view class="link-list" scroll-y="true" style="max-height: 300rpx;">
<block wx:for="{{detailLinkList}}" wx:key="id">
<view class="link-item">
<text class="link-url" selectable="true">{{item.lianjie}}</text>
<text class="link-status">{{item.is_used ? '已使用' : '未使用'}}</text>
<view wx:if="{{!item.is_used}}" class="copy-link-btn" bindtap="onCopyLinkItem" data-url="{{item.lianjie}}">复制</view>
</view>
</block>
<view class="load-more-links" wx:if="{{detailLinkHasMore}}">
<view class="load-more-btn" bindtap="onLoadMoreLinks">获取更多链接</view>
</view>
<view wx:if="{{isLoadingLinks}}" class="link-loading">加载中...</view>
<view wx:if="{{detailLinkList.length === 0 && !isLoadingLinks}}" class="link-empty">暂无链接</view>
</scroll-view>
</view>
<view class="modal-actions-grid">
<view class="modal-grid-btn generate-modal-grid-btn" bindtap="onGenerateLinkModal">{{currentTemplate.hasGenerated ? '获取新链接' : '生成链接'}}</view>
<view class="modal-grid-btn copy-modal-grid-btn {{currentTemplate.isCopied ? 'copied' : ''}}" bindtap="onCopyLinkModal">{{currentTemplate.isCopied ? '已复制' : '复制链接'}}</view>
<view class="modal-grid-btn edit-modal-grid-btn" bindtap="onEditTemplate">{{isEditing ? '保存修改' : '立即修改'}}</view>
<view class="modal-grid-btn delete-modal-grid-btn" bindtap="onDeleteTemplate">删除模板</view>
</view>
<!-- 编辑区 -->
<view class="edit-section" wx:if="{{isEditing}}">
<view class="edit-item">
<text class="edit-label">订单介绍</text>
<textarea class="edit-input intro-edit" value="{{editJieshao}}" maxlength="100" placeholder="请输入订单介绍100字以内" bindinput="onEditJieshaoInput" auto-height/>
<text class="word-count">{{editJieshao.length}}/100</text>
</view>
<view class="edit-item">
<text class="edit-label">价格(元)</text>
<input class="edit-input price-edit" type="digit" value="{{editJiage}}" placeholder="0.1 - 10000" bindinput="onEditJiageInput"/>
</view>
<view class="edit-item">
<text class="edit-label">标签</text>
<picker mode="selector" range="{{currentTypeChenghaoList}}" range-key="mingcheng" bindchange="onEditLabelChange">
<view class="picker-view"><text>{{editLabelName || '无'}}</text></view>
</picker>
</view>
<view class="edit-item commission-item">
<text class="edit-label">佣金要求建议≤50</text>
<switch checked="{{editCommissionEnabled}}" bindchange="onEditCommissionToggle"/>
<input wx:if="{{editCommissionEnabled}}" class="commission-input" type="digit" placeholder="佣金金额" value="{{editCommissionValue}}" bindinput="onEditCommissionValueInput"/>
</view>
</view>
</view>
</view>
<!-- 添加模板弹窗 -->
<view class="modal-overlay" wx:if="{{showAddModal}}" bindtap="hideAddModal">
<view class="modal-content add-modal" catchtap="stopPropagation">
<view class="modal-close" bindtap="hideAddModal">×</view>
<view class="modal-header"><text class="modal-title">添加模板</text></view>
<view class="add-form">
<view class="form-item">
<text class="form-label">订单介绍</text>
<textarea class="form-input intro-input" value="{{addJieshao}}" maxlength="100" placeholder="请输入订单介绍(必填)" bindinput="onAddJieshaoInput" auto-height/>
<text class="word-count">{{addJieshao.length}}/100</text>
</view>
<view class="form-item">
<text class="form-label">价格(元)</text>
<input class="form-input price-input" type="digit" value="{{addJiage}}" placeholder="0.1 - 10000" bindinput="onAddJiageInput"/>
</view>
<view class="form-item">
<text class="form-label">标签</text>
<picker mode="selector" range="{{currentTypeChenghaoList}}" range-key="mingcheng" bindchange="onAddLabelChange">
<view class="picker-view"><text>{{addLabelName || '可选'}}</text></view>
</picker>
</view>
<view class="form-item commission-item">
<text class="form-label">佣金要求建议≤50</text>
<switch checked="{{addCommissionEnabled}}" bindchange="onAddCommissionToggle"/>
<input wx:if="{{addCommissionEnabled}}" class="commission-input" type="digit" placeholder="佣金金额" value="{{addCommissionValue}}" bindinput="onAddCommissionValueInput"/>
</view>
</view>
<view class="modal-actions-grid add-actions-grid">
<view class="modal-grid-btn cancel-grid-btn" bindtap="hideAddModal">取消</view>
<view class="modal-grid-btn confirm-add-grid-btn" bindtap="onAddTemplate">确认添加</view>
</view>
</view>
</view>
<!-- 删除确认弹窗 -->
<view class="modal-overlay confirm-overlay" wx:if="{{showDeleteConfirm}}" bindtap="hideDeleteConfirm">
<view class="confirm-content" catchtap="stopPropagation">
<text class="confirm-title">确认删除</text>
<text class="confirm-text">确定要删除这个模板吗?此操作不可恢复。</text>
<view class="confirm-actions-grid">
<view class="confirm-grid-btn cancel-confirm-grid-btn" bindtap="hideDeleteConfirm">取消</view>
<view class="confirm-grid-btn delete-confirm-grid-btn" bindtap="confirmDeleteTemplate">确定删除</view>
</view>
</view>
</view>
<!-- 获取新链接确认弹窗 -->
<view class="modal-overlay confirm-overlay" wx:if="{{showNewLinkConfirm}}" bindtap="hideNewLinkConfirm">
<view class="confirm-content" catchtap="stopPropagation">
<text class="confirm-title">获取新链接</text>
<text class="confirm-text">{{currentTemplate.isCopied ? '确定要获取新链接吗?旧链接将失效。' : '您还没有复制当前链接,确定要获取新链接吗?'}}</text>
<view class="confirm-actions-grid">
<view class="confirm-grid-btn cancel-confirm-grid-btn" bindtap="hideNewLinkConfirm">取消</view>
<view class="confirm-grid-btn confirm-new-link-grid-btn" bindtap="confirmNewLink">确定</view>
</view>
</view>
</view>
<!-- pages/express-order/express-order.wxml -->
<view class="page-container">
<!-- 余额卡片 -->
<view class="balance-card">
<text class="balance-label">可用余额</text>
<view class="balance-amount">
<text class="amount-value">{{sjyue}}</text>
<text class="amount-unit">元</text>
</view>
<text class="balance-tip">生成链接将从此余额扣除</text>
</view>
<!-- 商品类型选择 -->
<view class="section">
<text class="section-title">选择订单类型</text>
<scroll-view class="type-scroll" scroll-x="true" enable-flex>
<block wx:for="{{shangpinList}}" wx:key="id">
<view class="type-item {{selectedTypeId === item.id ? 'type-active' : ''}}"
data-id="{{item.id}}" data-item="{{item}}" bindtap="onSelectType">
<text class="type-text">{{item.jieshao}}</text>
</view>
</block>
</scroll-view>
</view>
<!-- 搜索区域 -->
<view class="search-section">
<view class="search-box">
<text class="search-icon">🔍</text>
<input class="search-input" placeholder="输入模板ID/介绍..." value="{{searchParams.keyword}}"
bindinput="onSearchKeywordInput" bindconfirm="onSearchConfirm"/>
<view class="search-btn" bindtap="onSearchConfirm">
<text class="search-btn-text">搜索</text>
</view>
</view>
<view class="filter-row">
<view class="filter-item">
<text class="filter-label">金额≥</text>
<input class="filter-input" type="digit" placeholder="元" value="{{searchParams.minPrice}}" bindinput="onSearchMinPriceInput"/>
</view>
<view class="filter-item">
<text class="filter-label">标签</text>
<picker mode="selector" range="{{currentTypeChenghaoList}}" range-key="mingcheng" bindchange="onSearchLabelChange">
<view class="picker-view">
<text>{{searchLabelName || '全部'}}</text>
</view>
</picker>
</view>
<view class="clear-search" bindtap="onClearSearch"><text>清空</text></view>
</view>
</view>
<!-- 模板卡片网格 -->
<view class="template-section">
<scroll-view class="template-scroll" scroll-y="true" scroll-with-animation="true" bindscrolltolower="onScrollToLower">
<view class="template-grid">
<block wx:for="{{templateList}}" wx:key="mobanId">
<view class="template-card" bindtap="onCardTap" data-item="{{item}}">
<view class="template-intro">
<text class="intro-text">{{item.jieshao || '无介绍'}}</text>
</view>
<view class="template-label" wx:if="{{item.labelName}}">
<chenghao-tag mingcheng="{{item.labelName}}" texiaoJson="{{''}}"/>
</view>
<view class="template-price-row">
<text class="template-price-label">价格:</text>
<text class="template-price-value">{{item.jiage || '0.00'}}元</text>
</view>
<view class="template-actions-grid">
<view class="action-grid-btn generate-grid-btn {{item.hasGenerated ? 'generated' : ''}}"
data-id="{{item.mobanId}}" catchtap="onGenerateLink">
{{item.hasGenerated ? '获取新链接' : '生成链接'}}
</view>
<view class="action-grid-btn copy-grid-btn {{item.isCopied ? 'copied' : ''}}"
data-id="{{item.mobanId}}" catchtap="onCopyLink">
{{item.isCopied ? '已复制' : '一键复制'}}
</view>
</view>
</view>
</block>
<view class="template-card add-card" bindtap="showAddModal">
<view class="add-icon"></view>
<text class="add-text">添加模板</text>
<text class="add-subtext">点击创建新模板</text>
</view>
</view>
<view class="load-more" wx:if="{{isLoadingMore}}">...</view>
<view class="no-more" wx:if="{{!hasMoreData && templateList.length > 0}}">没有更多模板了</view>
<view class="empty-state" wx:if="{{templateList.length === 0 && !isLoading}}">暂无模板</view>
</scroll-view>
</view>
</view>
<!-- 详情弹窗 -->
<view class="modal-overlay" wx:if="{{showDetailModal}}" bindtap="hideDetailModal">
<view class="modal-content detail-modal" catchtap="stopPropagation">
<view class="modal-close" bindtap="hideDetailModal">×</view>
<view class="modal-header"><text class="modal-title">模板详情</text></view>
<view class="detail-info" wx:if="{{currentTemplate}}">
<view class="detail-item">
<text class="detail-label">订单介绍</text>
<view class="detail-value intro-value"><text>{{currentTemplate.jieshao || '无介绍'}}</text></view>
</view>
<view class="detail-item" wx:if="{{currentTemplate.labelName}}">
<text class="detail-label">关联标签</text>
<chenghao-tag mingcheng="{{currentTemplate.labelName}}" texiaoJson="{{currentTemplate.texiaoJson}}"/>
</view>
<view class="detail-meta-row">
<view class="meta-item"><text class="meta-label">价格</text><text class="meta-value price-meta">{{currentTemplate.jiage || '0.00'}}元</text></view>
<view class="meta-divider">|</view>
<view class="meta-item"><text class="meta-label">发布数量</text><text class="meta-value count-meta">{{currentTemplate.fabushuliang || 0}}次</text></view>
<view class="meta-divider">|</view>
<view class="meta-item"><text class="meta-label">模板ID</text><text class="meta-value id-meta">{{currentTemplate.mobanId || 'N/A'}}</text></view>
</view>
<view class="detail-item" wx:if="{{currentTemplate.commissionEnabled}}">
<text class="detail-label">佣金要求</text>
<text class="detail-value">{{currentTemplate.commissionValue}}元建议≤50</text>
</view>
<!-- 链接筛选 -->
<view class="link-filter-bar">
<text class="filter-label">链接状态:</text>
<view class="filter-switch {{detailLinkFilter === 0 ? 'active' : ''}}" bindtap="onToggleLinkFilter">未使用</view>
<view class="filter-switch {{detailLinkFilter === 1 ? 'active' : ''}}" bindtap="onToggleLinkFilter">已使用</view>
</view>
<scroll-view class="link-list" scroll-y="true" style="max-height: 300rpx;">
<block wx:for="{{detailLinkList}}" wx:key="id">
<view class="link-item">
<text class="link-url" selectable="true">{{item.lianjie}}</text>
<text class="link-status">{{item.is_used ? '已使用' : '未使用'}}</text>
<view wx:if="{{!item.is_used}}" class="copy-link-btn" bindtap="onCopyLinkItem" data-url="{{item.lianjie}}">复制</view>
</view>
</block>
<view class="load-more-links" wx:if="{{detailLinkHasMore}}">
<view class="load-more-btn" bindtap="onLoadMoreLinks">获取更多链接</view>
</view>
<view wx:if="{{isLoadingLinks}}" class="link-loading">加载中...</view>
<view wx:if="{{detailLinkList.length === 0 && !isLoadingLinks}}" class="link-empty">暂无链接</view>
</scroll-view>
</view>
<view class="modal-actions-grid">
<view class="modal-grid-btn generate-modal-grid-btn" bindtap="onGenerateLinkModal">{{currentTemplate.hasGenerated ? '获取新链接' : '生成链接'}}</view>
<view class="modal-grid-btn copy-modal-grid-btn {{currentTemplate.isCopied ? 'copied' : ''}}" bindtap="onCopyLinkModal">{{currentTemplate.isCopied ? '已复制' : '复制链接'}}</view>
<view class="modal-grid-btn edit-modal-grid-btn" bindtap="onEditTemplate">{{isEditing ? '保存修改' : '立即修改'}}</view>
<view class="modal-grid-btn delete-modal-grid-btn" bindtap="onDeleteTemplate">删除模板</view>
</view>
<!-- 编辑区 -->
<view class="edit-section" wx:if="{{isEditing}}">
<view class="edit-item">
<text class="edit-label">订单介绍</text>
<textarea class="edit-input intro-edit" value="{{editJieshao}}" maxlength="100" placeholder="请输入订单介绍100字以内" bindinput="onEditJieshaoInput" auto-height/>
<text class="word-count">{{editJieshao.length}}/100</text>
</view>
<view class="edit-item">
<text class="edit-label">价格(元)</text>
<input class="edit-input price-edit" type="digit" value="{{editJiage}}" placeholder="0.1 - 10000" bindinput="onEditJiageInput"/>
</view>
<view class="edit-item">
<text class="edit-label">标签</text>
<picker mode="selector" range="{{currentTypeChenghaoList}}" range-key="mingcheng" bindchange="onEditLabelChange">
<view class="picker-view"><text>{{editLabelName || '无'}}</text></view>
</picker>
</view>
<view class="edit-item commission-item">
<text class="edit-label">佣金要求建议≤50</text>
<switch checked="{{editCommissionEnabled}}" bindchange="onEditCommissionToggle"/>
<input wx:if="{{editCommissionEnabled}}" class="commission-input" type="digit" placeholder="佣金金额" value="{{editCommissionValue}}" bindinput="onEditCommissionValueInput"/>
</view>
</view>
</view>
</view>
<!-- 添加模板弹窗 -->
<view class="modal-overlay" wx:if="{{showAddModal}}" bindtap="hideAddModal">
<view class="modal-content add-modal" catchtap="stopPropagation">
<view class="modal-close" bindtap="hideAddModal">×</view>
<view class="modal-header"><text class="modal-title">添加模板</text></view>
<view class="add-form">
<view class="form-item">
<text class="form-label">订单介绍</text>
<textarea class="form-input intro-input" value="{{addJieshao}}" maxlength="100" placeholder="请输入订单介绍(必填)" bindinput="onAddJieshaoInput" auto-height/>
<text class="word-count">{{addJieshao.length}}/100</text>
</view>
<view class="form-item">
<text class="form-label">价格(元)</text>
<input class="form-input price-input" type="digit" value="{{addJiage}}" placeholder="0.1 - 10000" bindinput="onAddJiageInput"/>
</view>
<view class="form-item">
<text class="form-label">标签</text>
<picker mode="selector" range="{{currentTypeChenghaoList}}" range-key="mingcheng" bindchange="onAddLabelChange">
<view class="picker-view"><text>{{addLabelName || '可选'}}</text></view>
</picker>
</view>
<view class="form-item commission-item">
<text class="form-label">佣金要求建议≤50</text>
<switch checked="{{addCommissionEnabled}}" bindchange="onAddCommissionToggle"/>
<input wx:if="{{addCommissionEnabled}}" class="commission-input" type="digit" placeholder="佣金金额" value="{{addCommissionValue}}" bindinput="onAddCommissionValueInput"/>
</view>
</view>
<view class="modal-actions-grid add-actions-grid">
<view class="modal-grid-btn cancel-grid-btn" bindtap="hideAddModal">取消</view>
<view class="modal-grid-btn confirm-add-grid-btn" bindtap="onAddTemplate">确认添加</view>
</view>
</view>
</view>
<!-- 删除确认弹窗 -->
<view class="modal-overlay confirm-overlay" wx:if="{{showDeleteConfirm}}" bindtap="hideDeleteConfirm">
<view class="confirm-content" catchtap="stopPropagation">
<text class="confirm-title">确认删除</text>
<text class="confirm-text">确定要删除这个模板吗?此操作不可恢复。</text>
<view class="confirm-actions-grid">
<view class="confirm-grid-btn cancel-confirm-grid-btn" bindtap="hideDeleteConfirm">取消</view>
<view class="confirm-grid-btn delete-confirm-grid-btn" bindtap="confirmDeleteTemplate">确定删除</view>
</view>
</view>
</view>
<!-- 获取新链接确认弹窗 -->
<view class="modal-overlay confirm-overlay" wx:if="{{showNewLinkConfirm}}" bindtap="hideNewLinkConfirm">
<view class="confirm-content" catchtap="stopPropagation">
<text class="confirm-title">获取新链接</text>
<text class="confirm-text">{{currentTemplate.isCopied ? '确定要获取新链接吗?旧链接将失效。' : '您还没有复制当前链接,确定要获取新链接吗?'}}</text>
<view class="confirm-actions-grid">
<view class="confirm-grid-btn cancel-confirm-grid-btn" bindtap="hideNewLinkConfirm">取消</view>
<view class="confirm-grid-btn confirm-new-link-grid-btn" bindtap="confirmNewLink">确定</view>
</view>
</view>
</view>
<global-notification id="global-notification" />

View File

@@ -1,222 +1,222 @@
/* pages/jisufd/jisufd.wxss - 增强版,包含标签展示 */
.page-container {
min-height: 100vh;
background: linear-gradient(135deg, #0a0a0f 0%, #1a1a2e 50%, #16213e 100%);
padding: 30rpx;
box-sizing: border-box;
padding-bottom: 40rpx;
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
}
.balance-card {
background: linear-gradient(135deg, rgba(20,20,30,0.9), rgba(30,30,50,0.9));
border-radius: 24rpx;
padding: 36rpx;
margin-bottom: 40rpx;
color: #fff;
box-shadow: 0 15rpx 35rpx rgba(0,0,0,0.4), 0 0 40rpx rgba(0,150,255,0.15);
text-align: center;
border: 1.5rpx solid rgba(0,180,255,0.2);
backdrop-filter: blur(10rpx);
position: relative;
overflow: hidden;
}
.balance-card::before {
content: '';
position: absolute;
top: -50%; left: -50%;
width: 200%; height: 200%;
background: linear-gradient(45deg, transparent 30%, rgba(0,180,255,0.1) 50%, transparent 70%);
animation: cardShine 6s infinite linear;
z-index: 1;
}
@keyframes cardShine {
0% { transform: translateX(-100%) translateY(-100%) rotate(45deg); }
100% { transform: translateX(100%) translateY(100%) rotate(45deg); }
}
.balance-label { font-size: 28rpx; color: rgba(255,255,255,0.8); margin-bottom: 18rpx; font-weight: 500; position: relative; z-index: 2; }
.balance-amount { display: flex; align-items: baseline; justify-content: center; margin-bottom: 15rpx; position: relative; z-index: 2; }
.amount-value { font-size: 64rpx; font-weight: bold; color: #00B4FF; text-shadow: 0 0 20rpx rgba(0,180,255,0.7), 0 0 40rpx rgba(0,180,255,0.4); animation: amountGlow 3s infinite alternate; }
@keyframes amountGlow {
0% { color: #00B4FF; text-shadow: 0 0 15rpx rgba(0,180,255,0.6); }
100% { color: #00E0FF; text-shadow: 0 0 25rpx rgba(0,180,255,0.8), 0 0 50rpx rgba(0,180,255,0.5); }
}
.amount-unit { font-size: 36rpx; color: #00B4FF; margin-left: 12rpx; font-weight: 500; z-index: 2; }
.balance-tip { font-size: 24rpx; color: rgba(255,255,255,0.6); z-index: 2; }
.section { margin-bottom: 35rpx; }
.section-title { font-size: 32rpx; font-weight: 700; color: #FFFFFF; margin-bottom: 25rpx; padding-left: 12rpx; border-left: 6rpx solid #9C27B0; text-shadow: 0 2rpx 4rpx rgba(0,0,0,0.3); }
.type-scroll { white-space: nowrap; display: flex; height: 90rpx; }
.type-item {
display: inline-flex; flex-shrink: 0; align-items: center; justify-content: center;
height: 85rpx; padding: 0 40rpx; margin-right: 20rpx;
background: rgba(255,255,255,0.08); border-radius: 45rpx; border: 2rpx solid rgba(255,255,255,0.15);
font-size: 28rpx; color: rgba(255,255,255,0.8);
transition: all 0.3s cubic-bezier(0.175,0.885,0.32,1.275);
backdrop-filter: blur(10rpx);
}
.type-active {
background: linear-gradient(135deg, #9C27B0 0%, #673AB7 100%);
border-color: #9C27B0; color: white; transform: scale(1.05);
box-shadow: 0 10rpx 25rpx rgba(156,39,176,0.4), 0 0 30rpx rgba(156,39,176,0.3);
}
.type-text { max-width: 220rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
/* 搜索区域 */
.search-section { margin-bottom: 35rpx; }
.search-box {
display: flex; align-items: center;
background: rgba(255,255,255,0.1); border-radius: 50rpx;
padding: 20rpx 30rpx; border: 2rpx solid rgba(255,255,255,0.2);
backdrop-filter: blur(10rpx); transition: all 0.3s ease;
}
.search-box:focus-within { border-color: #9C27B0; background: rgba(255,255,255,0.15); box-shadow: 0 0 25rpx rgba(156,39,176,0.3); }
.search-icon { font-size: 32rpx; color: rgba(255,255,255,0.7); margin-right: 20rpx; flex-shrink: 0; }
.search-input { flex: 1; font-size: 28rpx; color: #FFFFFF; background: transparent; border: none; outline: none; min-height: 40rpx; }
.search-input::placeholder { color: rgba(255,255,255,0.4); }
.search-btn { background: linear-gradient(135deg, #9C27B0 0%, #673AB7 100%); border-radius: 40rpx; padding: 15rpx 30rpx; margin-left: 20rpx; flex-shrink: 0; box-shadow: 0 8rpx 20rpx rgba(156,39,176,0.3); }
.search-btn:active { transform: scale(0.95); }
.search-btn-text { font-size: 26rpx; color: white; font-weight: 600; }
.filter-row { display: flex; align-items: center; margin-top: 20rpx; gap: 15rpx; }
.filter-item { display: flex; align-items: center; background: rgba(255,255,255,0.08); border-radius: 30rpx; padding: 10rpx 20rpx; border: 1rpx solid rgba(255,255,255,0.15); }
.filter-label { font-size: 24rpx; color: rgba(255,255,255,0.7); margin-right: 10rpx; }
.filter-input { width: 120rpx; height: 50rpx; background: transparent; color: #fff; font-size: 26rpx; }
.picker-view { padding: 10rpx 0; color: rgba(255,255,255,0.9); font-size: 26rpx; max-width: 160rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.clear-search { margin-left: auto; padding: 10rpx 20rpx; background: rgba(255,255,255,0.1); border-radius: 20rpx; color: rgba(255,255,255,0.7); font-size: 24rpx; }
/* 模板区域 */
.template-section { height: calc(100vh - 600rpx); position: relative; }
.template-scroll { height: 100%; }
.template-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 25rpx; padding-bottom: 30rpx; }
.template-card {
background: linear-gradient(145deg, rgba(30,30,45,0.9), rgba(40,40,60,0.9));
border-radius: 20rpx; padding: 25rpx;
border: 1.5rpx solid rgba(255,255,255,0.1);
box-shadow: 0 10rpx 30rpx rgba(0,0,0,0.3), 0 0 25rpx rgba(0,180,255,0.1);
transition: all 0.3s ease; backdrop-filter: blur(10rpx);
display: flex; flex-direction: column; min-height: 280rpx; position: relative; overflow: hidden;
}
.template-card::before { content: ''; position: absolute; top:0; left:0; right:0; height:4rpx; background: linear-gradient(90deg, #9C27B0, #00B4FF, #9C27B0); opacity:0.6; }
.template-intro { flex:1; margin-bottom:10rpx; min-height:70rpx; overflow:hidden; }
.intro-text { font-size:24rpx; line-height:1.4; color:rgba(255,255,255,0.9); display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3; overflow:hidden; font-weight:500; }
.template-label { margin-bottom: 10rpx; }
.template-price-row { display:flex; align-items:center; margin-bottom:20rpx; padding:10rpx 0; border-bottom:1rpx solid rgba(255,255,255,0.1); }
.template-price-label { font-size:22rpx; color:rgba(255,255,255,0.7); margin-right:10rpx; }
.template-price-value { font-size:26rpx; font-weight:bold; color:#FFD700; text-shadow:0 0 10rpx rgba(255,215,0,0.3); flex:1; }
.template-actions-grid { display:flex; flex-direction:column; gap:12rpx; margin-top:10rpx; }
.action-grid-btn {
height:65rpx; border-radius:32rpx; font-size:24rpx; font-weight:600; transition:all 0.3s ease;
border:none; padding:0 20rpx; box-shadow:0 4rpx 12rpx rgba(0,0,0,0.2), inset 0 1rpx 0 rgba(255,255,255,0.1);
display:flex !important; align-items:center !important; justify-content:center !important; text-align:center !important;
}
.generate-grid-btn { background:linear-gradient(135deg, #00B4FF, #0091EA); color:white; }
.generate-grid-btn.generated { background:linear-gradient(135deg, #4CAF50, #2E7D32); }
.copy-grid-btn { background:linear-gradient(135deg, #9C27B0, #673AB7); color:white; }
.copy-grid-btn.copied { background:linear-gradient(135deg, #FF9800, #F57C00); }
.add-card { background: linear-gradient(145deg, rgba(40,40,60,0.7), rgba(60,60,80,0.7)); border:2rpx dashed rgba(255,255,255,0.3); display:flex; flex-direction:column; align-items:center; justify-content:center; min-height:280rpx; }
.add-icon { font-size:60rpx; color:rgba(255,255,255,0.6); margin-bottom:20rpx; }
.add-text { font-size:30rpx; color:rgba(255,255,255,0.8); font-weight:600; margin-bottom:8rpx; }
.add-subtext { font-size:24rpx; color:rgba(255,255,255,0.5); }
/* 弹窗通用 */
.modal-overlay { position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.75); display:flex; align-items:center; justify-content:center; z-index:9999; padding:30rpx; backdrop-filter:blur(10rpx); animation:fadeIn 0.3s ease; }
@keyframes fadeIn { from{opacity:0;} to{opacity:1;} }
.modal-content {
background:linear-gradient(145deg, rgba(30,30,45,0.95), rgba(40,40,60,0.95));
border-radius:30rpx; width:100%; max-width:700rpx; max-height:85vh; overflow-y:auto;
border:1.5rpx solid rgba(255,255,255,0.15);
box-shadow:0 25rpx 80rpx rgba(0,0,0,0.6), 0 0 60rpx rgba(0,180,255,0.2);
animation:slideUp 0.4s cubic-bezier(0.175,0.885,0.32,1.275);
position:relative;
}
@keyframes slideUp { from{transform:translateY(100rpx) scale(0.9); opacity:0;} to{transform:translateY(0) scale(1); opacity:1;} }
.modal-close { position:absolute; top:25rpx; right:25rpx; width:60rpx; height:60rpx; border-radius:50%; background:rgba(255,255,255,0.1); display:flex; align-items:center; justify-content:center; font-size:40rpx; color:rgba(255,255,255,0.7); z-index:10; }
.modal-header { padding:40rpx 40rpx 20rpx; text-align:center; }
.modal-title { font-size:36rpx; font-weight:bold; color:#FFFFFF; }
.detail-info { padding:30rpx 40rpx; }
.detail-item { margin-bottom:30rpx; }
.detail-label { font-size:26rpx; color:rgba(255,255,255,0.7); margin-bottom:10rpx; font-weight:500; }
.detail-value { font-size:28rpx; color:#FFFFFF; word-break:break-word; }
.intro-value { background:rgba(255,255,255,0.05); border-radius:15rpx; padding:20rpx; border:1rpx solid rgba(255,255,255,0.1); max-height:200rpx; overflow-y:auto; }
.detail-meta-row { display:flex; align-items:center; justify-content:space-between; padding:25rpx 0; margin:20rpx 0; border-top:1rpx solid rgba(255,255,255,0.1); border-bottom:1rpx solid rgba(255,255,255,0.1); }
.meta-item { display:flex; flex-direction:column; align-items:center; flex:1; }
.meta-label { font-size:22rpx; color:rgba(255,255,255,0.6); margin-bottom:8rpx; }
.meta-value { font-size:26rpx; font-weight:600; }
.price-meta { color:#FFD700; text-shadow:0 0 10rpx rgba(255,215,0,0.3); }
.count-meta { color:#00B4FF; }
.id-meta { color:#4CAF50; font-family:'Courier New',monospace; font-size:24rpx; }
.meta-divider { color:rgba(255,255,255,0.2); font-size:24rpx; margin:0 15rpx; }
.link-filter-bar { display:flex; align-items:center; margin-top:30rpx; margin-bottom:20rpx; }
.filter-switch {
padding:10rpx 25rpx; margin-left:10rpx; border-radius:20rpx; background:rgba(255,255,255,0.1);
font-size:24rpx; color:rgba(255,255,255,0.7); transition:all 0.3s;
}
.filter-switch.active { background:linear-gradient(135deg, #00B4FF, #0091EA); color:white; }
.link-list { background:rgba(0,0,0,0.2); border-radius:15rpx; padding:15rpx; }
.link-item { display:flex; align-items:center; justify-content:space-between; padding:15rpx 0; border-bottom:1rpx solid rgba(255,255,255,0.1); }
.link-url { flex:1; font-size:22rpx; color:rgba(255,255,255,0.8); word-break:break-all; margin-right:15rpx; }
.link-status { font-size:20rpx; color:rgba(255,255,255,0.5); margin-right:15rpx; }
.copy-link-btn { padding:8rpx 20rpx; background:#00B4FF; color:white; border-radius:15rpx; font-size:22rpx; }
.load-more-links { text-align:center; padding:20rpx 0; }
.load-more-btn { display:inline-block; padding:15rpx 40rpx; background:linear-gradient(135deg, #9C27B0, #673AB7); border-radius:30rpx; color:white; font-size:26rpx; }
.link-loading, .link-empty { text-align:center; padding:30rpx; color:rgba(255,255,255,0.5); font-size:24rpx; }
/* 弹窗按钮 */
.modal-actions-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:18rpx; padding:30rpx 40rpx 40rpx; border-top:1rpx solid rgba(255,255,255,0.1); }
.modal-grid-btn {
height:85rpx; border-radius:42rpx; font-size:28rpx; font-weight:600; transition:all 0.3s;
border:none; padding:0 20rpx; box-shadow:0 8rpx 25rpx rgba(0,0,0,0.3), inset 0 1rpx 0 rgba(255,255,255,0.2);
display:flex !important; align-items:center !important; justify-content:center !important;
}
.generate-modal-grid-btn { background:linear-gradient(145deg, #00B4FF, #0091EA); color:white; }
.copy-modal-grid-btn { background:linear-gradient(145deg, #9C27B0, #673AB7); color:white; }
.copy-modal-grid-btn.copied { background:linear-gradient(145deg, #FF9800, #F57C00); }
.edit-modal-grid-btn { background:linear-gradient(145deg, #4CAF50, #2E7D32); color:white; }
.delete-modal-grid-btn { background:linear-gradient(145deg, #FF4444, #CC0000); color:white; }
/* 编辑区 */
.edit-section { padding:30rpx 40rpx 20rpx; border-top:1rpx solid rgba(255,255,255,0.1); margin-top:20rpx; }
.edit-item { margin-bottom:30rpx; }
.edit-label { font-size:26rpx; color:rgba(255,255,255,0.8); margin-bottom:15rpx; font-weight:600; }
.edit-input { width:100%; background:rgba(255,255,255,0.08); border:1.5rpx solid rgba(255,255,255,0.2); border-radius:15rpx; padding:25rpx; font-size:28rpx; color:#FFFFFF; }
.intro-edit { min-height:150rpx; }
.price-edit { text-align:center; font-size:32rpx; font-weight:600; height:90rpx; line-height:90rpx; }
.picker-view { padding:25rpx; background:rgba(255,255,255,0.08); border-radius:15rpx; border:1.5rpx solid rgba(255,255,255,0.2); color:#fff; }
.commission-item { display:flex; align-items:center; gap:20rpx; }
.commission-input { flex:1; background:rgba(255,255,255,0.08); border-radius:15rpx; padding:20rpx; color:#fff; }
/* 添加表单 */
.add-form { padding:30rpx 40rpx 20rpx; }
.form-item { margin-bottom:35rpx; }
.form-label { font-size:28rpx; color:rgba(255,255,255,0.8); margin-bottom:18rpx; font-weight:600; }
.intro-input, .price-input { width:100%; background:rgba(255,255,255,0.08); border:1.5rpx solid rgba(255,255,255,0.2); border-radius:15rpx; padding:25rpx; font-size:28rpx; color:#FFFFFF; }
.intro-input { min-height:180rpx; }
.price-input { height:90rpx; line-height:90rpx; text-align:center; font-weight:600; }
.add-actions-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:25rpx; padding:30rpx 40rpx 40rpx; border-top:1rpx solid rgba(255,255,255,0.1); }
.cancel-grid-btn { background:rgba(255,255,255,0.1); color:rgba(255,255,255,0.8); border:1.5rpx solid rgba(255,255,255,0.2); }
.confirm-add-grid-btn { background:linear-gradient(145deg, #9C27B0, #673AB7); color:white; }
/* 确认弹窗 */
.confirm-content {
background:linear-gradient(145deg, rgba(40,40,60,0.95), rgba(50,50,70,0.95));
border-radius:25rpx; padding:50rpx 40rpx 40rpx; width:85%; max-width:550rpx;
border:1.5rpx solid rgba(255,255,255,0.15); box-shadow:0 30rpx 90rpx rgba(0,0,0,0.7); text-align:center;
animation:scaleIn 0.3s ease;
}
@keyframes scaleIn { from{transform:scale(0.8); opacity:0;} to{transform:scale(1); opacity:1;} }
.confirm-title { font-size:34rpx; font-weight:bold; color:#FFFFFF; margin-bottom:25rpx; }
.confirm-text { font-size:28rpx; color:rgba(255,255,255,0.8); margin-bottom:40rpx; }
.confirm-actions-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:25rpx; }
.confirm-grid-btn { height:85rpx; border-radius:42rpx; font-size:28rpx; font-weight:600; display:flex; align-items:center; justify-content:center; }
.cancel-confirm-grid-btn { background:rgba(255,255,255,0.1); color:rgba(255,255,255,0.8); border:1.5rpx solid rgba(255,255,255,0.2); }
.delete-confirm-grid-btn { background:linear-gradient(145deg, #FF4444, #CC0000); color:white; }
.confirm-new-link-grid-btn { background:linear-gradient(145deg, #00B4FF, #0091EA); color:white; }
.loading-mask { position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(18,18,24,0.95); display:flex; align-items:center; justify-content:center; z-index:10000; }
.loading-content { background:rgba(40,40,60,0.95); padding:60rpx 80rpx; border-radius:30rpx; display:flex; flex-direction:column; align-items:center; box-shadow:0 25rpx 70rpx rgba(0,0,0,0.6); }
.loading-spinner { width:90rpx; height:90rpx; border:8rpx solid rgba(0,180,255,0.2); border-top:8rpx solid #00B4FF; border-radius:50%; animation:spin 1.2s linear infinite; margin-bottom:35rpx; }
.loading-text { font-size:30rpx; color:#00B4FF; font-weight:600; }
/* pages/express-order/express-order.wxss - 增强版,包含标签展示 */
.page-container {
min-height: 100vh;
background: linear-gradient(135deg, #0a0a0f 0%, #1a1a2e 50%, #16213e 100%);
padding: 30rpx;
box-sizing: border-box;
padding-bottom: 40rpx;
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
}
.balance-card {
background: linear-gradient(135deg, rgba(20,20,30,0.9), rgba(30,30,50,0.9));
border-radius: 24rpx;
padding: 36rpx;
margin-bottom: 40rpx;
color: #fff;
box-shadow: 0 15rpx 35rpx rgba(0,0,0,0.4), 0 0 40rpx rgba(0,150,255,0.15);
text-align: center;
border: 1.5rpx solid rgba(0,180,255,0.2);
backdrop-filter: blur(10rpx);
position: relative;
overflow: hidden;
}
.balance-card::before {
content: '';
position: absolute;
top: -50%; left: -50%;
width: 200%; height: 200%;
background: linear-gradient(45deg, transparent 30%, rgba(0,180,255,0.1) 50%, transparent 70%);
animation: cardShine 6s infinite linear;
z-index: 1;
}
@keyframes cardShine {
0% { transform: translateX(-100%) translateY(-100%) rotate(45deg); }
100% { transform: translateX(100%) translateY(100%) rotate(45deg); }
}
.balance-label { font-size: 28rpx; color: rgba(255,255,255,0.8); margin-bottom: 18rpx; font-weight: 500; position: relative; z-index: 2; }
.balance-amount { display: flex; align-items: baseline; justify-content: center; margin-bottom: 15rpx; position: relative; z-index: 2; }
.amount-value { font-size: 64rpx; font-weight: bold; color: #00B4FF; text-shadow: 0 0 20rpx rgba(0,180,255,0.7), 0 0 40rpx rgba(0,180,255,0.4); animation: amountGlow 3s infinite alternate; }
@keyframes amountGlow {
0% { color: #00B4FF; text-shadow: 0 0 15rpx rgba(0,180,255,0.6); }
100% { color: #00E0FF; text-shadow: 0 0 25rpx rgba(0,180,255,0.8), 0 0 50rpx rgba(0,180,255,0.5); }
}
.amount-unit { font-size: 36rpx; color: #00B4FF; margin-left: 12rpx; font-weight: 500; z-index: 2; }
.balance-tip { font-size: 24rpx; color: rgba(255,255,255,0.6); z-index: 2; }
.section { margin-bottom: 35rpx; }
.section-title { font-size: 32rpx; font-weight: 700; color: #FFFFFF; margin-bottom: 25rpx; padding-left: 12rpx; border-left: 6rpx solid #9C27B0; text-shadow: 0 2rpx 4rpx rgba(0,0,0,0.3); }
.type-scroll { white-space: nowrap; display: flex; height: 90rpx; }
.type-item {
display: inline-flex; flex-shrink: 0; align-items: center; justify-content: center;
height: 85rpx; padding: 0 40rpx; margin-right: 20rpx;
background: rgba(255,255,255,0.08); border-radius: 45rpx; border: 2rpx solid rgba(255,255,255,0.15);
font-size: 28rpx; color: rgba(255,255,255,0.8);
transition: all 0.3s cubic-bezier(0.175,0.885,0.32,1.275);
backdrop-filter: blur(10rpx);
}
.type-active {
background: linear-gradient(135deg, #9C27B0 0%, #673AB7 100%);
border-color: #9C27B0; color: white; transform: scale(1.05);
box-shadow: 0 10rpx 25rpx rgba(156,39,176,0.4), 0 0 30rpx rgba(156,39,176,0.3);
}
.type-text { max-width: 220rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
/* 搜索区域 */
.search-section { margin-bottom: 35rpx; }
.search-box {
display: flex; align-items: center;
background: rgba(255,255,255,0.1); border-radius: 50rpx;
padding: 20rpx 30rpx; border: 2rpx solid rgba(255,255,255,0.2);
backdrop-filter: blur(10rpx); transition: all 0.3s ease;
}
.search-box:focus-within { border-color: #9C27B0; background: rgba(255,255,255,0.15); box-shadow: 0 0 25rpx rgba(156,39,176,0.3); }
.search-icon { font-size: 32rpx; color: rgba(255,255,255,0.7); margin-right: 20rpx; flex-shrink: 0; }
.search-input { flex: 1; font-size: 28rpx; color: #FFFFFF; background: transparent; border: none; outline: none; min-height: 40rpx; }
.search-input::placeholder { color: rgba(255,255,255,0.4); }
.search-btn { background: linear-gradient(135deg, #9C27B0 0%, #673AB7 100%); border-radius: 40rpx; padding: 15rpx 30rpx; margin-left: 20rpx; flex-shrink: 0; box-shadow: 0 8rpx 20rpx rgba(156,39,176,0.3); }
.search-btn:active { transform: scale(0.95); }
.search-btn-text { font-size: 26rpx; color: white; font-weight: 600; }
.filter-row { display: flex; align-items: center; margin-top: 20rpx; gap: 15rpx; }
.filter-item { display: flex; align-items: center; background: rgba(255,255,255,0.08); border-radius: 30rpx; padding: 10rpx 20rpx; border: 1rpx solid rgba(255,255,255,0.15); }
.filter-label { font-size: 24rpx; color: rgba(255,255,255,0.7); margin-right: 10rpx; }
.filter-input { width: 120rpx; height: 50rpx; background: transparent; color: #fff; font-size: 26rpx; }
.picker-view { padding: 10rpx 0; color: rgba(255,255,255,0.9); font-size: 26rpx; max-width: 160rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.clear-search { margin-left: auto; padding: 10rpx 20rpx; background: rgba(255,255,255,0.1); border-radius: 20rpx; color: rgba(255,255,255,0.7); font-size: 24rpx; }
/* 模板区域 */
.template-section { height: calc(100vh - 600rpx); position: relative; }
.template-scroll { height: 100%; }
.template-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 25rpx; padding-bottom: 30rpx; }
.template-card {
background: linear-gradient(145deg, rgba(30,30,45,0.9), rgba(40,40,60,0.9));
border-radius: 20rpx; padding: 25rpx;
border: 1.5rpx solid rgba(255,255,255,0.1);
box-shadow: 0 10rpx 30rpx rgba(0,0,0,0.3), 0 0 25rpx rgba(0,180,255,0.1);
transition: all 0.3s ease; backdrop-filter: blur(10rpx);
display: flex; flex-direction: column; min-height: 280rpx; position: relative; overflow: hidden;
}
.template-card::before { content: ''; position: absolute; top:0; left:0; right:0; height:4rpx; background: linear-gradient(90deg, #9C27B0, #00B4FF, #9C27B0); opacity:0.6; }
.template-intro { flex:1; margin-bottom:10rpx; min-height:70rpx; overflow:hidden; }
.intro-text { font-size:24rpx; line-height:1.4; color:rgba(255,255,255,0.9); display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3; overflow:hidden; font-weight:500; }
.template-label { margin-bottom: 10rpx; }
.template-price-row { display:flex; align-items:center; margin-bottom:20rpx; padding:10rpx 0; border-bottom:1rpx solid rgba(255,255,255,0.1); }
.template-price-label { font-size:22rpx; color:rgba(255,255,255,0.7); margin-right:10rpx; }
.template-price-value { font-size:26rpx; font-weight:bold; color:#FFD700; text-shadow:0 0 10rpx rgba(255,215,0,0.3); flex:1; }
.template-actions-grid { display:flex; flex-direction:column; gap:12rpx; margin-top:10rpx; }
.action-grid-btn {
height:65rpx; border-radius:32rpx; font-size:24rpx; font-weight:600; transition:all 0.3s ease;
border:none; padding:0 20rpx; box-shadow:0 4rpx 12rpx rgba(0,0,0,0.2), inset 0 1rpx 0 rgba(255,255,255,0.1);
display:flex !important; align-items:center !important; justify-content:center !important; text-align:center !important;
}
.generate-grid-btn { background:linear-gradient(135deg, #00B4FF, #0091EA); color:white; }
.generate-grid-btn.generated { background:linear-gradient(135deg, #4CAF50, #2E7D32); }
.copy-grid-btn { background:linear-gradient(135deg, #9C27B0, #673AB7); color:white; }
.copy-grid-btn.copied { background:linear-gradient(135deg, #FF9800, #F57C00); }
.add-card { background: linear-gradient(145deg, rgba(40,40,60,0.7), rgba(60,60,80,0.7)); border:2rpx dashed rgba(255,255,255,0.3); display:flex; flex-direction:column; align-items:center; justify-content:center; min-height:280rpx; }
.add-icon { font-size:60rpx; color:rgba(255,255,255,0.6); margin-bottom:20rpx; }
.add-text { font-size:30rpx; color:rgba(255,255,255,0.8); font-weight:600; margin-bottom:8rpx; }
.add-subtext { font-size:24rpx; color:rgba(255,255,255,0.5); }
/* 弹窗通用 */
.modal-overlay { position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.75); display:flex; align-items:center; justify-content:center; z-index:9999; padding:30rpx; backdrop-filter:blur(10rpx); animation:fadeIn 0.3s ease; }
@keyframes fadeIn { from{opacity:0;} to{opacity:1;} }
.modal-content {
background:linear-gradient(145deg, rgba(30,30,45,0.95), rgba(40,40,60,0.95));
border-radius:30rpx; width:100%; max-width:700rpx; max-height:85vh; overflow-y:auto;
border:1.5rpx solid rgba(255,255,255,0.15);
box-shadow:0 25rpx 80rpx rgba(0,0,0,0.6), 0 0 60rpx rgba(0,180,255,0.2);
animation:slideUp 0.4s cubic-bezier(0.175,0.885,0.32,1.275);
position:relative;
}
@keyframes slideUp { from{transform:translateY(100rpx) scale(0.9); opacity:0;} to{transform:translateY(0) scale(1); opacity:1;} }
.modal-close { position:absolute; top:25rpx; right:25rpx; width:60rpx; height:60rpx; border-radius:50%; background:rgba(255,255,255,0.1); display:flex; align-items:center; justify-content:center; font-size:40rpx; color:rgba(255,255,255,0.7); z-index:10; }
.modal-header { padding:40rpx 40rpx 20rpx; text-align:center; }
.modal-title { font-size:36rpx; font-weight:bold; color:#FFFFFF; }
.detail-info { padding:30rpx 40rpx; }
.detail-item { margin-bottom:30rpx; }
.detail-label { font-size:26rpx; color:rgba(255,255,255,0.7); margin-bottom:10rpx; font-weight:500; }
.detail-value { font-size:28rpx; color:#FFFFFF; word-break:break-word; }
.intro-value { background:rgba(255,255,255,0.05); border-radius:15rpx; padding:20rpx; border:1rpx solid rgba(255,255,255,0.1); max-height:200rpx; overflow-y:auto; }
.detail-meta-row { display:flex; align-items:center; justify-content:space-between; padding:25rpx 0; margin:20rpx 0; border-top:1rpx solid rgba(255,255,255,0.1); border-bottom:1rpx solid rgba(255,255,255,0.1); }
.meta-item { display:flex; flex-direction:column; align-items:center; flex:1; }
.meta-label { font-size:22rpx; color:rgba(255,255,255,0.6); margin-bottom:8rpx; }
.meta-value { font-size:26rpx; font-weight:600; }
.price-meta { color:#FFD700; text-shadow:0 0 10rpx rgba(255,215,0,0.3); }
.count-meta { color:#00B4FF; }
.id-meta { color:#4CAF50; font-family:'Courier New',monospace; font-size:24rpx; }
.meta-divider { color:rgba(255,255,255,0.2); font-size:24rpx; margin:0 15rpx; }
.link-filter-bar { display:flex; align-items:center; margin-top:30rpx; margin-bottom:20rpx; }
.filter-switch {
padding:10rpx 25rpx; margin-left:10rpx; border-radius:20rpx; background:rgba(255,255,255,0.1);
font-size:24rpx; color:rgba(255,255,255,0.7); transition:all 0.3s;
}
.filter-switch.active { background:linear-gradient(135deg, #00B4FF, #0091EA); color:white; }
.link-list { background:rgba(0,0,0,0.2); border-radius:15rpx; padding:15rpx; }
.link-item { display:flex; align-items:center; justify-content:space-between; padding:15rpx 0; border-bottom:1rpx solid rgba(255,255,255,0.1); }
.link-url { flex:1; font-size:22rpx; color:rgba(255,255,255,0.8); word-break:break-all; margin-right:15rpx; }
.link-status { font-size:20rpx; color:rgba(255,255,255,0.5); margin-right:15rpx; }
.copy-link-btn { padding:8rpx 20rpx; background:#00B4FF; color:white; border-radius:15rpx; font-size:22rpx; }
.load-more-links { text-align:center; padding:20rpx 0; }
.load-more-btn { display:inline-block; padding:15rpx 40rpx; background:linear-gradient(135deg, #9C27B0, #673AB7); border-radius:30rpx; color:white; font-size:26rpx; }
.link-loading, .link-empty { text-align:center; padding:30rpx; color:rgba(255,255,255,0.5); font-size:24rpx; }
/* 弹窗按钮 */
.modal-actions-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:18rpx; padding:30rpx 40rpx 40rpx; border-top:1rpx solid rgba(255,255,255,0.1); }
.modal-grid-btn {
height:85rpx; border-radius:42rpx; font-size:28rpx; font-weight:600; transition:all 0.3s;
border:none; padding:0 20rpx; box-shadow:0 8rpx 25rpx rgba(0,0,0,0.3), inset 0 1rpx 0 rgba(255,255,255,0.2);
display:flex !important; align-items:center !important; justify-content:center !important;
}
.generate-modal-grid-btn { background:linear-gradient(145deg, #00B4FF, #0091EA); color:white; }
.copy-modal-grid-btn { background:linear-gradient(145deg, #9C27B0, #673AB7); color:white; }
.copy-modal-grid-btn.copied { background:linear-gradient(145deg, #FF9800, #F57C00); }
.edit-modal-grid-btn { background:linear-gradient(145deg, #4CAF50, #2E7D32); color:white; }
.delete-modal-grid-btn { background:linear-gradient(145deg, #FF4444, #CC0000); color:white; }
/* 编辑区 */
.edit-section { padding:30rpx 40rpx 20rpx; border-top:1rpx solid rgba(255,255,255,0.1); margin-top:20rpx; }
.edit-item { margin-bottom:30rpx; }
.edit-label { font-size:26rpx; color:rgba(255,255,255,0.8); margin-bottom:15rpx; font-weight:600; }
.edit-input { width:100%; background:rgba(255,255,255,0.08); border:1.5rpx solid rgba(255,255,255,0.2); border-radius:15rpx; padding:25rpx; font-size:28rpx; color:#FFFFFF; }
.intro-edit { min-height:150rpx; }
.price-edit { text-align:center; font-size:32rpx; font-weight:600; height:90rpx; line-height:90rpx; }
.picker-view { padding:25rpx; background:rgba(255,255,255,0.08); border-radius:15rpx; border:1.5rpx solid rgba(255,255,255,0.2); color:#fff; }
.commission-item { display:flex; align-items:center; gap:20rpx; }
.commission-input { flex:1; background:rgba(255,255,255,0.08); border-radius:15rpx; padding:20rpx; color:#fff; }
/* 添加表单 */
.add-form { padding:30rpx 40rpx 20rpx; }
.form-item { margin-bottom:35rpx; }
.form-label { font-size:28rpx; color:rgba(255,255,255,0.8); margin-bottom:18rpx; font-weight:600; }
.intro-input, .price-input { width:100%; background:rgba(255,255,255,0.08); border:1.5rpx solid rgba(255,255,255,0.2); border-radius:15rpx; padding:25rpx; font-size:28rpx; color:#FFFFFF; }
.intro-input { min-height:180rpx; }
.price-input { height:90rpx; line-height:90rpx; text-align:center; font-weight:600; }
.add-actions-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:25rpx; padding:30rpx 40rpx 40rpx; border-top:1rpx solid rgba(255,255,255,0.1); }
.cancel-grid-btn { background:rgba(255,255,255,0.1); color:rgba(255,255,255,0.8); border:1.5rpx solid rgba(255,255,255,0.2); }
.confirm-add-grid-btn { background:linear-gradient(145deg, #9C27B0, #673AB7); color:white; }
/* 确认弹窗 */
.confirm-content {
background:linear-gradient(145deg, rgba(40,40,60,0.95), rgba(50,50,70,0.95));
border-radius:25rpx; padding:50rpx 40rpx 40rpx; width:85%; max-width:550rpx;
border:1.5rpx solid rgba(255,255,255,0.15); box-shadow:0 30rpx 90rpx rgba(0,0,0,0.7); text-align:center;
animation:scaleIn 0.3s ease;
}
@keyframes scaleIn { from{transform:scale(0.8); opacity:0;} to{transform:scale(1); opacity:1;} }
.confirm-title { font-size:34rpx; font-weight:bold; color:#FFFFFF; margin-bottom:25rpx; }
.confirm-text { font-size:28rpx; color:rgba(255,255,255,0.8); margin-bottom:40rpx; }
.confirm-actions-grid { display:grid; grid-template-columns:repeat(2,1fr); gap:25rpx; }
.confirm-grid-btn { height:85rpx; border-radius:42rpx; font-size:28rpx; font-weight:600; display:flex; align-items:center; justify-content:center; }
.cancel-confirm-grid-btn { background:rgba(255,255,255,0.1); color:rgba(255,255,255,0.8); border:1.5rpx solid rgba(255,255,255,0.2); }
.delete-confirm-grid-btn { background:linear-gradient(145deg, #FF4444, #CC0000); color:white; }
.confirm-new-link-grid-btn { background:linear-gradient(145deg, #00B4FF, #0091EA); color:white; }
.loading-mask { position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(18,18,24,0.95); display:flex; align-items:center; justify-content:center; z-index:10000; }
.loading-content { background:rgba(40,40,60,0.95); padding:60rpx 80rpx; border-radius:30rpx; display:flex; flex-direction:column; align-items:center; box-shadow:0 25rpx 70rpx rgba(0,0,0,0.6); }
.loading-spinner { width:90rpx; height:90rpx; border:8rpx solid rgba(0,180,255,0.2); border-top:8rpx solid #00B4FF; border-radius:50%; animation:spin 1.2s linear infinite; margin-bottom:35rpx; }
.loading-text { font-size:30rpx; color:#00B4FF; font-weight:600; }
@keyframes spin { 0%{transform:rotate(0deg);} 100%{transform:rotate(360deg);} }

View File

@@ -1,316 +1,316 @@
<!-- pages/cfss/cfss.wxml -->
<view class="cfss-container">
<!-- 顶部统计卡片 -->
<view class="tongji-card">
<view class="tongji-title">处罚统计</view>
<view class="tongji-content">
<view class="tongji-item">
<text class="tongji-label">总记录</text>
<text class="tongji-value">{{ zongshu }}</text>
</view>
<view class="tongji-item">
<text class="tongji-label">待处理</text>
<text class="tongji-value daichuli-value">{{ daichuli }}</text>
</view>
<view class="tongji-item">
<text class="tongji-label">已处理</text>
<text class="tongji-value yichuli-value">{{ yichuli }}</text>
</view>
</view>
</view>
<!-- 类型切换 -->
<view class="leixing-qiehuan">
<view
class="leixing-item {{ shenfen === 0 ? 'leixing-active' : '' }}"
bindtap="qiehuanShenfen"
data-type="0"
>
<text class="leixing-text">待处理</text>
<text class="leixing-count">{{ daichuli }}</text>
</view>
<view
class="leixing-item {{ shenfen === 1 ? 'leixing-active' : '' }}"
bindtap="qiehuanShenfen"
data-type="1"
>
<text class="leixing-text">已处理</text>
<text class="leixing-count">{{ yichuli }}</text>
</view>
</view>
<!-- 处罚记录列表 -->
<scroll-view
class="chufa-list"
scroll-y
enable-back-to-top
scroll-with-animation
style="height: {{ scrollHeight }}px"
bindscrolltolower="shanglaShuaxin"
>
<!-- 加载中 -->
<view wx:if="{{ jiazhaozhong && chufaList.length === 0 }}" class="jiazai-zhong">
<view class="loading-animation">
<view class="loading-dot"></view>
<view class="loading-dot"></view>
<view class="loading-dot"></view>
</view>
<text>加载中...</text>
</view>
<!-- 🔴【关键修改】处罚记录卡片列表 - 直接使用处理好的字段 -->
<block wx:for="{{ chufaList }}" wx:key="index">
<view
class="chufa-card"
bindtap="chakanXiangqing"
data-item="{{ item }}"
>
<!-- 卡片顶部:时间和状态 -->
<view class="card-top">
<!-- 🔴 直接使用display_time -->
<text class="shijian">{{ item.display_time }}</text>
<!-- 🔴 直接使用display_status和status_class -->
<view class="zhuangtai-badge {{ item.status_class }}">
{{ item.display_status }}
</view>
</view>
<!-- 申请人信息 -->
<view class="qingqiu-row">
<text class="qingqiu-label">申请人:</text>
<text class="qingqiu-value">{{ item.qingqiuid || '--' }}</text>
</view>
<!-- 处罚理由 -->
<view class="liyou-row">
<text class="liyou-label">处罚理由:</text>
<text class="liyou-value">{{ item.cfliyou || '无' }}</text>
</view>
<!-- 订单编号 -->
<view class="dingdan-row">
<text class="dingdan-label">订单编号:</text>
<text class="dingdan-value">{{ item.dingdan_id || '--' }}</text>
</view>
<!-- 卡片边框效果 -->
<view class="card-border"></view>
<view class="card-glow"></view>
</view>
</block>
<!-- 加载更多 -->
<view wx:if="{{ jiazhaigengduo }}" class="jiazai-gengduo">
<view class="scan-line"></view>
<text>加载更多...</text>
</view>
<!-- 没有更多数据 -->
<view wx:if="{{ !haiyougengduo && chufaList.length > 0 }}" class="meiyou-gengduo">
<text>—— 没有更多记录了 ——</text>
</view>
<!-- 空状态 -->
<view wx:if="{{ !jiazhaozhong && chufaList.length === 0 }}" class="kong-zhuangtai">
<view class="empty-icon">⚡</view>
<text class="empty-text">暂无处罚记录</text>
<text class="empty-subtext">所有事务已处理完毕</text>
</view>
</scroll-view>
<!-- 处罚详情弹窗 -->
<view wx:if="{{ showXiangqing }}" class="xiangqing-modal">
<view class="modal-mask" bindtap="guanbiXiangqing"></view>
<view class="modal-content">
<!-- 弹窗头部 -->
<view class="modal-header">
<text class="modal-title">处罚详情</text>
<view class="modal-close" bindtap="guanbiXiangqing">
<text class="close-icon">×</text>
</view>
</view>
<!-- 🔴【弹窗主体 - 确保可以滚动】 -->
<scroll-view class="modal-body" scroll-y style="max-height: 1000rpx;">
<!-- 基本信息 -->
<view class="detail-section">
<view class="section-title">基本信息</view>
<view class="detail-row">
<text class="detail-label">订单编号:</text>
<view class="detail-value-container">
<text class="detail-value">{{ xuanzhongChufa.dingdan_id || '--' }}</text>
<text class="fuzhi-btn" bindtap="fuzhiWenben" data-text="{{ xuanzhongChufa.dingdan_id }}">复制</text>
</view>
</view>
<view class="detail-row">
<text class="detail-label">申请人ID</text>
<view class="detail-value-container">
<text class="detail-value">{{ xuanzhongChufa.qingqiuid || '--' }}</text>
<text class="fuzhi-btn" bindtap="fuzhiWenben" data-text="{{ xuanzhongChufa.qingqiuid }}">复制</text>
</view>
</view>
<view class="detail-row">
<!-- 🔴 直接使用display_time -->
<text class="detail-label">创建时间:</text>
<text class="detail-value">{{ xuanzhongChufa.display_time || '--' }}</text>
</view>
</view>
<!-- 处罚信息 -->
<view class="detail-section">
<view class="section-title">处罚信息</view>
<view class="detail-row">
<text class="detail-label">处罚状态:</text>
<!-- 🔴 直接使用display_status和status_class -->
<view class="zhuangtai-badge-inline {{ xuanzhongChufa.status_class }}">
{{ xuanzhongChufa.display_status }}
</view>
</view>
<view class="detail-row full-row">
<text class="detail-label">处罚理由:</text>
<text class="detail-value liyou-full">{{ xuanzhongChufa.cfliyou || '无' }}</text>
</view>
<!-- 🔴 证据图片展示 - 直接使用full_zhengju_tupian -->
<view wx:if="{{ xuanzhongChufa.full_zhengju_tupian && xuanzhongChufa.full_zhengju_tupian.length > 0 }}" class="detail-row full-row">
<text class="detail-label">证据图片:</text>
<view class="tupian-grid">
<block wx:for="{{ xuanzhongChufa.full_zhengju_tupian }}" wx:key="index">
<view class="tupian-item" bindtap="yulanTupian" data-url="{{ item }}" data-urls="{{ xuanzhongChufa.full_zhengju_tupian }}">
<image
src="{{ item }}"
class="tupian-image"
mode="aspectFill"
/>
</view>
</block>
</view>
</view>
</view>
<!-- 申诉信息 -->
<view wx:if="{{ xuanzhongChufa.ssliyou || (xuanzhongChufa.full_shensu_tupian && xuanzhongChufa.full_shensu_tupian.length > 0) }}" class="detail-section">
<view class="section-title">申诉信息</view>
<view wx:if="{{ xuanzhongChufa.ssliyou }}" class="detail-row full-row">
<text class="detail-label">申诉理由:</text>
<text class="detail-value liyou-full">{{ xuanzhongChufa.ssliyou }}</text>
</view>
<!-- 🔴 申诉图片展示 - 直接使用full_shensu_tupian -->
<view wx:if="{{ xuanzhongChufa.full_shensu_tupian && xuanzhongChufa.full_shensu_tupian.length > 0 }}" class="detail-row full-row">
<text class="detail-label">申诉图片:</text>
<view class="tupian-grid">
<block wx:for="{{ xuanzhongChufa.full_shensu_tupian }}" wx:key="index">
<view class="tupian-item" bindtap="yulanTupian" data-url="{{ item }}" data-urls="{{ xuanzhongChufa.full_shensu_tupian }}">
<image
src="{{ item }}"
class="tupian-image"
mode="aspectFill"
/>
</view>
</block>
</view>
</view>
</view>
</scroll-view>
<!-- 弹窗底部按钮 -->
<view class="modal-footer">
<view class="btn-group">
<!-- 联系客服按钮(始终显示) -->
<view class="btn btn-kefu" bindtap="lianxiKefu">
<text>联系客服</text>
</view>
<!-- 申诉按钮(只有待处罚状态可点击) -->
<view
class="btn {{ xuanzhongChufa.sqzhuangtai === 0 ? 'btn-shensu' : 'btn-shensu-disabled' }}"
bindtap="{{ xuanzhongChufa.sqzhuangtai === 0 ? 'openShensuModal' : '' }}"
>
<text>申诉</text>
</view>
</view>
</view>
</view>
</view>
<!-- 申诉弹窗 -->
<view wx:if="{{ showShensuModal }}" class="shensu-modal">
<view class="modal-mask" bindtap="closeShensuModal"></view>
<view class="modal-content small-modal">
<view class="modal-header">
<text class="modal-title">提交申诉</text>
</view>
<view class="modal-body">
<!-- 申诉理由输入 -->
<view class="input-group">
<view class="input-label">申诉理由</view>
<textarea
class="shensu-textarea"
value="{{ shensuLiyou }}"
bindinput="onShensuLiyouInput"
placeholder="请输入申诉理由最多500字..."
maxlength="500"
auto-height
/>
<view class="word-count">{{ shensuLiyou.length }}/500</view>
</view>
<!-- 申诉图片上传 -->
<view class="input-group">
<view class="input-label">申诉图片</view>
<view class="upload-tip">最多可上传9张图片</view>
<!-- 图片网格 -->
<view class="tupian-grid-upload">
<!-- 已上传图片 -->
<block wx:for="{{ shensuTupian }}" wx:key="index">
<view class="tupian-item-upload">
<image
src="{{ item }}"
class="tupian-image-upload"
mode="aspectFill"
bindtap="yulanShensuTupian"
data-index="{{ index }}"
/>
<view class="tupian-delete" bindtap="deleteShensuTupian" data-index="{{ index }}">
<text>×</text>
</view>
</view>
</block>
<!-- 添加按钮 -->
<view
wx:if="{{ shensuTupian.length < 9 }}"
class="tupian-add-btn"
bindtap="chooseShensuTupian"
>
<view class="add-icon">+</view>
<text class="add-text">添加图片</text>
</view>
</view>
</view>
<!-- 上传进度 -->
<view wx:if="{{ shangchuanZongshu > 0 }}" class="upload-progress">
<view class="progress-title">上传进度</view>
<view class="progress-bar">
<view class="progress-inner" style="width: {{ jinduWidth }}"></view>
</view>
<view class="progress-text">{{ shangchuanJindu }}/{{ shangchuanZongshu }}</view>
</view>
</view>
<view class="modal-footer">
<view class="btn-group">
<view class="btn btn-quxiao" bindtap="closeShensuModal">
<text>取消</text>
</view>
<view class="btn btn-queren" bindtap="submitShensu">
<text>提交申诉</text>
</view>
</view>
</view>
</view>
</view>
<!-- 全局通知组件 -->
<global-notification id="global-notification" />
<!-- pages/penalty/penalty.wxml -->
<view class="cfss-container">
<!-- 顶部统计卡片 -->
<view class="tongji-card">
<view class="tongji-title">处罚统计</view>
<view class="tongji-content">
<view class="tongji-item">
<text class="tongji-label">总记录</text>
<text class="tongji-value">{{ zongshu }}</text>
</view>
<view class="tongji-item">
<text class="tongji-label">待处理</text>
<text class="tongji-value daichuli-value">{{ daichuli }}</text>
</view>
<view class="tongji-item">
<text class="tongji-label">已处理</text>
<text class="tongji-value yichuli-value">{{ yichuli }}</text>
</view>
</view>
</view>
<!-- 类型切换 -->
<view class="leixing-qiehuan">
<view
class="leixing-item {{ shenfen === 0 ? 'leixing-active' : '' }}"
bindtap="qiehuanShenfen"
data-type="0"
>
<text class="leixing-text">待处理</text>
<text class="leixing-count">{{ daichuli }}</text>
</view>
<view
class="leixing-item {{ shenfen === 1 ? 'leixing-active' : '' }}"
bindtap="qiehuanShenfen"
data-type="1"
>
<text class="leixing-text">已处理</text>
<text class="leixing-count">{{ yichuli }}</text>
</view>
</view>
<!-- 处罚记录列表 -->
<scroll-view
class="chufa-list"
scroll-y
enable-back-to-top
scroll-with-animation
style="height: {{ scrollHeight }}px"
bindscrolltolower="shanglaShuaxin"
>
<!-- 加载中 -->
<view wx:if="{{ jiazhaozhong && chufaList.length === 0 }}" class="jiazai-zhong">
<view class="loading-animation">
<view class="loading-dot"></view>
<view class="loading-dot"></view>
<view class="loading-dot"></view>
</view>
<text>加载中...</text>
</view>
<!-- 🔴【关键修改】处罚记录卡片列表 - 直接使用处理好的字段 -->
<block wx:for="{{ chufaList }}" wx:key="index">
<view
class="chufa-card"
bindtap="chakanXiangqing"
data-item="{{ item }}"
>
<!-- 卡片顶部:时间和状态 -->
<view class="card-top">
<!-- 🔴 直接使用display_time -->
<text class="shijian">{{ item.display_time }}</text>
<!-- 🔴 直接使用display_status和status_class -->
<view class="zhuangtai-badge {{ item.status_class }}">
{{ item.display_status }}
</view>
</view>
<!-- 申请人信息 -->
<view class="qingqiu-row">
<text class="qingqiu-label">申请人:</text>
<text class="qingqiu-value">{{ item.qingqiuid || '--' }}</text>
</view>
<!-- 处罚理由 -->
<view class="liyou-row">
<text class="liyou-label">处罚理由:</text>
<text class="liyou-value">{{ item.cfliyou || '无' }}</text>
</view>
<!-- 订单编号 -->
<view class="dingdan-row">
<text class="dingdan-label">订单编号:</text>
<text class="dingdan-value">{{ item.dingdan_id || '--' }}</text>
</view>
<!-- 卡片边框效果 -->
<view class="card-border"></view>
<view class="card-glow"></view>
</view>
</block>
<!-- 加载更多 -->
<view wx:if="{{ jiazhaigengduo }}" class="jiazai-gengduo">
<view class="scan-line"></view>
<text>加载更多...</text>
</view>
<!-- 没有更多数据 -->
<view wx:if="{{ !haiyougengduo && chufaList.length > 0 }}" class="meiyou-gengduo">
<text>—— 没有更多记录了 ——</text>
</view>
<!-- 空状态 -->
<view wx:if="{{ !jiazhaozhong && chufaList.length === 0 }}" class="kong-zhuangtai">
<view class="empty-icon">⚡</view>
<text class="empty-text">暂无处罚记录</text>
<text class="empty-subtext">所有事务已处理完毕</text>
</view>
</scroll-view>
<!-- 处罚详情弹窗 -->
<view wx:if="{{ showXiangqing }}" class="xiangqing-modal">
<view class="modal-mask" bindtap="guanbiXiangqing"></view>
<view class="modal-content">
<!-- 弹窗头部 -->
<view class="modal-header">
<text class="modal-title">处罚详情</text>
<view class="modal-close" bindtap="guanbiXiangqing">
<text class="close-icon">×</text>
</view>
</view>
<!-- 🔴【弹窗主体 - 确保可以滚动】 -->
<scroll-view class="modal-body" scroll-y style="max-height: 1000rpx;">
<!-- 基本信息 -->
<view class="detail-section">
<view class="section-title">基本信息</view>
<view class="detail-row">
<text class="detail-label">订单编号:</text>
<view class="detail-value-container">
<text class="detail-value">{{ xuanzhongChufa.dingdan_id || '--' }}</text>
<text class="fuzhi-btn" bindtap="fuzhiWenben" data-text="{{ xuanzhongChufa.dingdan_id }}">复制</text>
</view>
</view>
<view class="detail-row">
<text class="detail-label">申请人ID</text>
<view class="detail-value-container">
<text class="detail-value">{{ xuanzhongChufa.qingqiuid || '--' }}</text>
<text class="fuzhi-btn" bindtap="fuzhiWenben" data-text="{{ xuanzhongChufa.qingqiuid }}">复制</text>
</view>
</view>
<view class="detail-row">
<!-- 🔴 直接使用display_time -->
<text class="detail-label">创建时间:</text>
<text class="detail-value">{{ xuanzhongChufa.display_time || '--' }}</text>
</view>
</view>
<!-- 处罚信息 -->
<view class="detail-section">
<view class="section-title">处罚信息</view>
<view class="detail-row">
<text class="detail-label">处罚状态:</text>
<!-- 🔴 直接使用display_status和status_class -->
<view class="zhuangtai-badge-inline {{ xuanzhongChufa.status_class }}">
{{ xuanzhongChufa.display_status }}
</view>
</view>
<view class="detail-row full-row">
<text class="detail-label">处罚理由:</text>
<text class="detail-value liyou-full">{{ xuanzhongChufa.cfliyou || '无' }}</text>
</view>
<!-- 🔴 证据图片展示 - 直接使用full_zhengju_tupian -->
<view wx:if="{{ xuanzhongChufa.full_zhengju_tupian && xuanzhongChufa.full_zhengju_tupian.length > 0 }}" class="detail-row full-row">
<text class="detail-label">证据图片:</text>
<view class="tupian-grid">
<block wx:for="{{ xuanzhongChufa.full_zhengju_tupian }}" wx:key="index">
<view class="tupian-item" bindtap="yulanTupian" data-url="{{ item }}" data-urls="{{ xuanzhongChufa.full_zhengju_tupian }}">
<image
src="{{ item }}"
class="tupian-image"
mode="aspectFill"
/>
</view>
</block>
</view>
</view>
</view>
<!-- 申诉信息 -->
<view wx:if="{{ xuanzhongChufa.ssliyou || (xuanzhongChufa.full_shensu_tupian && xuanzhongChufa.full_shensu_tupian.length > 0) }}" class="detail-section">
<view class="section-title">申诉信息</view>
<view wx:if="{{ xuanzhongChufa.ssliyou }}" class="detail-row full-row">
<text class="detail-label">申诉理由:</text>
<text class="detail-value liyou-full">{{ xuanzhongChufa.ssliyou }}</text>
</view>
<!-- 🔴 申诉图片展示 - 直接使用full_shensu_tupian -->
<view wx:if="{{ xuanzhongChufa.full_shensu_tupian && xuanzhongChufa.full_shensu_tupian.length > 0 }}" class="detail-row full-row">
<text class="detail-label">申诉图片:</text>
<view class="tupian-grid">
<block wx:for="{{ xuanzhongChufa.full_shensu_tupian }}" wx:key="index">
<view class="tupian-item" bindtap="yulanTupian" data-url="{{ item }}" data-urls="{{ xuanzhongChufa.full_shensu_tupian }}">
<image
src="{{ item }}"
class="tupian-image"
mode="aspectFill"
/>
</view>
</block>
</view>
</view>
</view>
</scroll-view>
<!-- 弹窗底部按钮 -->
<view class="modal-footer">
<view class="btn-group">
<!-- 联系客服按钮(始终显示) -->
<view class="btn btn-kefu" bindtap="lianxiKefu">
<text>联系客服</text>
</view>
<!-- 申诉按钮(只有待处罚状态可点击) -->
<view
class="btn {{ xuanzhongChufa.sqzhuangtai === 0 ? 'btn-shensu' : 'btn-shensu-disabled' }}"
bindtap="{{ xuanzhongChufa.sqzhuangtai === 0 ? 'openShensuModal' : '' }}"
>
<text>申诉</text>
</view>
</view>
</view>
</view>
</view>
<!-- 申诉弹窗 -->
<view wx:if="{{ showShensuModal }}" class="shensu-modal">
<view class="modal-mask" bindtap="closeShensuModal"></view>
<view class="modal-content small-modal">
<view class="modal-header">
<text class="modal-title">提交申诉</text>
</view>
<view class="modal-body">
<!-- 申诉理由输入 -->
<view class="input-group">
<view class="input-label">申诉理由</view>
<textarea
class="shensu-textarea"
value="{{ shensuLiyou }}"
bindinput="onShensuLiyouInput"
placeholder="请输入申诉理由最多500字..."
maxlength="500"
auto-height
/>
<view class="word-count">{{ shensuLiyou.length }}/500</view>
</view>
<!-- 申诉图片上传 -->
<view class="input-group">
<view class="input-label">申诉图片</view>
<view class="upload-tip">最多可上传9张图片</view>
<!-- 图片网格 -->
<view class="tupian-grid-upload">
<!-- 已上传图片 -->
<block wx:for="{{ shensuTupian }}" wx:key="index">
<view class="tupian-item-upload">
<image
src="{{ item }}"
class="tupian-image-upload"
mode="aspectFill"
bindtap="yulanShensuTupian"
data-index="{{ index }}"
/>
<view class="tupian-delete" bindtap="deleteShensuTupian" data-index="{{ index }}">
<text>×</text>
</view>
</view>
</block>
<!-- 添加按钮 -->
<view
wx:if="{{ shensuTupian.length < 9 }}"
class="tupian-add-btn"
bindtap="chooseShensuTupian"
>
<view class="add-icon">+</view>
<text class="add-text">添加图片</text>
</view>
</view>
</view>
<!-- 上传进度 -->
<view wx:if="{{ shangchuanZongshu > 0 }}" class="upload-progress">
<view class="progress-title">上传进度</view>
<view class="progress-bar">
<view class="progress-inner" style="width: {{ jinduWidth }}"></view>
</view>
<view class="progress-text">{{ shangchuanJindu }}/{{ shangchuanZongshu }}</view>
</view>
</view>
<view class="modal-footer">
<view class="btn-group">
<view class="btn btn-quxiao" bindtap="closeShensuModal">
<text>取消</text>
</view>
<view class="btn btn-queren" bindtap="submitShensu">
<text>提交申诉</text>
</view>
</view>
</view>
</view>
</view>
<!-- 全局通知组件 -->
<global-notification id="global-notification" />
</view>

View File

@@ -1,307 +1,307 @@
const app = getApp();
import request from '../../utils/request.js';
import { reconnectForRole } from '../../utils/role-tab-bar.js';
Page({
data: {
// 商品类型
shangpinleixing: [],
xuanzhongLeixingId: null,
// 订单类型: normal 普通, peihu 陪护
orderType: 'normal',
// 搜索关键字
searchKeyword: '',
// 状态列表(增加全部)
statusList: [
{ name: '全部', key: 'all', zhuangtaiList: [], color: '#333333' },
{ name: '进行中', key: 'jinxingzhong', zhuangtaiList: [2], color: '#2196F3' },
{ name: '退款中', key: 'tuikuanzhong', zhuangtaiList: [4], color: '#FF9800' },
{ name: '已退款', key: 'yituikuan', zhuangtaiList: [5], color: '#9E9E9E' },
{ name: '退款失败', key: 'tuikuanshibai', zhuangtaiList: [6], color: '#F44336' },
{ name: '已完成', key: 'yiwancheng', zhuangtaiList: [3], color: '#4CAF50' },
{ name: '结算中', key: 'jiesuanzhong', zhuangtaiList: [8], color: '#FF5722' }
],
currentStatusKey: 'all',
// 每个状态独立的数据集
dsDingdanShuju: {
all: { list: [], page: 1, hasMore: true, isLoading: false },
jinxingzhong: { list: [], page: 1, hasMore: true, isLoading: false },
tuikuanzhong: { list: [], page: 1, hasMore: true, isLoading: false },
yituikuan: { list: [], page: 1, hasMore: true, isLoading: false },
tuikuanshibai: { list: [], page: 1, hasMore: true, isLoading: false },
yiwancheng: { list: [], page: 1, hasMore: true, isLoading: false },
jiesuanzhong: { list: [], page: 1, hasMore: true, isLoading: false }
},
currentList: [],
hasMore: true,
isLoading: false,
isLoadingMore: false,
scrollViewRefreshing: false,
defaultImg: '/images/default-order.png',
ossImageUrl: app.globalData.ossImageUrl || '',
},
onLoad() {
wx.setNavigationBarTitle({ title: '我的接单' });
this.loadShangpinLeixing();
this.registerNotificationComponent();
},
onShow() {
this.registerNotificationComponent();
if (wx.getStorageSync('uid')) {
reconnectForRole('dashou');
if (app.startImWhenReady) app.startImWhenReady();
}
if (this.data.currentStatusKey && this.data.xuanzhongLeixingId) {
this.loadCurrentStatusOrders(true);
}
},
registerNotificationComponent() {
const notificationComp = this.selectComponent('#global-notification');
if (notificationComp && notificationComp.showNotification) {
app.globalData.globalNotification = {
show: (data) => notificationComp.showNotification(data),
hide: () => notificationComp.hideNotification()
};
}
},
// 加载商品类型(图片拼接)
async loadShangpinLeixing() {
try {
const res = await request({
url: '/dingdan/dsqdhqddlx',
method: 'POST',
header: { 'content-type': 'application/json' }
});
if (res.data && (res.data.code === 200 || res.data.code === 0)) {
let list = res.data.data.list || res.data.data || [];
if (!Array.isArray(list)) list = [];
const oss = this.data.ossImageUrl;
const processed = list.map(item => ({
...item,
full_tupian_url: item.tupian_url && !item.tupian_url.startsWith('http')
? oss + item.tupian_url
: (item.tupian_url || '/images/default-type.png')
}));
this.setData({
shangpinleixing: processed,
xuanzhongLeixingId: processed[0]?.id || null
});
this.loadCurrentStatusOrders(true);
}
} catch (e) {
console.error('加载商品类型失败', e);
}
},
selectLeixing(e) {
const id = e.currentTarget.dataset.id;
if (id === this.data.xuanzhongLeixingId) return;
this.setData({ xuanzhongLeixingId: id });
this.resetCurrentStatusData();
this.loadCurrentStatusOrders(true);
},
switchOrderType(e) {
const type = e.currentTarget.dataset.type;
if (type === this.data.orderType) return;
this.setData({ orderType: type });
this.resetCurrentStatusData();
this.loadCurrentStatusOrders(true);
},
onSearchInput(e) {
this.setData({ searchKeyword: e.detail.value });
if (this._searchTimer) clearTimeout(this._searchTimer);
this._searchTimer = setTimeout(() => {
this.onSearchConfirm();
}, 500);
},
onSearchConfirm() {
this.resetCurrentStatusData();
this.loadCurrentStatusOrders(true);
},
clearSearch() {
this.setData({ searchKeyword: '' });
this.resetCurrentStatusData();
this.loadCurrentStatusOrders(true);
},
switchStatus(e) {
const key = e.currentTarget.dataset.key;
if (key === this.data.currentStatusKey) return;
this.setData({ currentStatusKey: key });
const tabData = this.data.dsDingdanShuju[key];
if (tabData.list.length === 0 && tabData.hasMore && !tabData.isLoading) {
this.loadCurrentStatusOrders(true);
} else {
this.refreshCurrentListView();
}
},
// 加载订单(核心)
async loadCurrentStatusOrders(isRefresh = false) {
const key = this.data.currentStatusKey;
const tabData = this.data.dsDingdanShuju[key];
if (tabData.isLoading) return;
const page = isRefresh ? 1 : tabData.page;
this.setData({
[`dsDingdanShuju.${key}.isLoading`]: true,
isLoading: true,
isLoadingMore: !isRefresh
});
try {
const apiUrl = this.data.orderType === 'peihu'
? '/dingdan/phddlbhq'
: '/dingdan/dshqdingdan';
const statusItem = this.data.statusList.find(s => s.key === key);
const zhuangtaiList = statusItem.zhuangtaiList.length > 0 ? statusItem.zhuangtaiList : undefined;
const params = {
zhuangtai_list: zhuangtaiList,
page: page,
page_size: 5,
leixing_id: this.data.xuanzhongLeixingId,
keyword: this.data.searchKeyword || undefined
};
const res = await request({
url: apiUrl,
method: 'POST',
data: params,
header: { 'content-type': 'application/json' }
});
const code = res.data.code;
if (code === 200 || code === 0) {
const newList = res.data.data.list || [];
const hasMore = res.data.data.has_more || false;
// ✅ 图片URL拼接核心
const processed = newList.map(item => ({
...item,
zhuangtaiZh: this.getZhuangtaiZh(item.zhuangtai),
zhuangtaiColor: statusItem.color,
tupian: item.tupian
? (item.tupian.startsWith('http') ? item.tupian : this.data.ossImageUrl + item.tupian)
: this.data.defaultImg
}));
const currentList = this.data.dsDingdanShuju[key].list;
const updatedList = isRefresh ? processed : [...currentList, ...processed];
this.setData({
[`dsDingdanShuju.${key}.list`]: updatedList,
[`dsDingdanShuju.${key}.page`]: page,
[`dsDingdanShuju.${key}.hasMore`]: hasMore,
[`dsDingdanShuju.${key}.isLoading`]: false,
isLoading: false,
isLoadingMore: false,
scrollViewRefreshing: false
});
this.refreshCurrentListView();
} else {
throw new Error(res.data.msg || '加载失败');
}
} catch (err) {
console.error('加载订单失败', err);
wx.showToast({ title: err.message || '加载失败', icon: 'none' });
this.setData({
[`dsDingdanShuju.${key}.isLoading`]: false,
isLoading: false,
isLoadingMore: false,
scrollViewRefreshing: false
});
this.refreshCurrentListView();
}
},
refreshCurrentListView() {
const key = this.data.currentStatusKey;
const tabData = this.data.dsDingdanShuju[key];
this.setData({
currentList: tabData.list,
hasMore: tabData.hasMore,
isLoading: tabData.isLoading
});
},
resetCurrentStatusData() {
const key = this.data.currentStatusKey;
this.setData({
[`dsDingdanShuju.${key}.list`]: [],
[`dsDingdanShuju.${key}.page`]: 1,
[`dsDingdanShuju.${key}.hasMore`]: true,
[`dsDingdanShuju.${key}.isLoading`]: false
});
this.refreshCurrentListView();
},
// 下拉刷新(由 scroll-view 触发)
onPullDownRefresh() {
if (this.data.isLoading) {
this.setData({ scrollViewRefreshing: false });
return;
}
this.setData({ scrollViewRefreshing: true });
this.resetCurrentStatusData();
this.loadCurrentStatusOrders(true);
},
// 上拉加载更多(由 scroll-view 触发)
onReachBottom() {
const key = this.data.currentStatusKey;
const tabData = this.data.dsDingdanShuju[key];
if (tabData.isLoading || !tabData.hasMore) return;
this.setData({ [`dsDingdanShuju.${key}.page`]: tabData.page + 1 });
this.loadCurrentStatusOrders(false);
},
// ✅ 手动点击“加载更多”按钮
onLoadMoreTap() {
const key = this.data.currentStatusKey;
const tabData = this.data.dsDingdanShuju[key];
if (tabData.isLoading || !tabData.hasMore) return;
this.setData({ [`dsDingdanShuju.${key}.page`]: tabData.page + 1 });
this.loadCurrentStatusOrders(false);
},
getZhuangtaiZh(zhuangtai) {
const map = {
1: '已下单', 2: '进行中', 3: '已完成',
4: '退款中', 5: '已退款', 6: '退款失败',
7: '指定中', 8: '结算中'
};
return map[zhuangtai] || '未知状态';
},
goToDsDingdanXiangqing(e) {
const item = e.currentTarget.dataset.item;
if (!item) return;
const dataStr = encodeURIComponent(JSON.stringify(item));
if (this.data.orderType === 'peihu') {
wx.navigateTo({
url: `/pages/peihuDingdan/peihuDingdan?dingdanData=${dataStr}`
});
} else {
wx.navigateTo({
url: `/pages/dsddxq/dsddxq?dingdanData=${dataStr}`
});
}
},
onImageError() {}
const app = getApp();
import request from '../../utils/request.js';
import { reconnectForRole } from '../../utils/role-tab-bar.js';
Page({
data: {
// 商品类型
shangpinleixing: [],
xuanzhongLeixingId: null,
// 订单类型: normal 普通, peihu 陪护
orderType: 'normal',
// 搜索关键字
searchKeyword: '',
// 状态列表(增加全部)
statusList: [
{ name: '全部', key: 'all', zhuangtaiList: [], color: '#333333' },
{ name: '进行中', key: 'jinxingzhong', zhuangtaiList: [2], color: '#2196F3' },
{ name: '退款中', key: 'tuikuanzhong', zhuangtaiList: [4], color: '#FF9800' },
{ name: '已退款', key: 'yituikuan', zhuangtaiList: [5], color: '#9E9E9E' },
{ name: '退款失败', key: 'tuikuanshibai', zhuangtaiList: [6], color: '#F44336' },
{ name: '已完成', key: 'yiwancheng', zhuangtaiList: [3], color: '#4CAF50' },
{ name: '结算中', key: 'jiesuanzhong', zhuangtaiList: [8], color: '#FF5722' }
],
currentStatusKey: 'all',
// 每个状态独立的数据集
dsDingdanShuju: {
all: { list: [], page: 1, hasMore: true, isLoading: false },
jinxingzhong: { list: [], page: 1, hasMore: true, isLoading: false },
tuikuanzhong: { list: [], page: 1, hasMore: true, isLoading: false },
yituikuan: { list: [], page: 1, hasMore: true, isLoading: false },
tuikuanshibai: { list: [], page: 1, hasMore: true, isLoading: false },
yiwancheng: { list: [], page: 1, hasMore: true, isLoading: false },
jiesuanzhong: { list: [], page: 1, hasMore: true, isLoading: false }
},
currentList: [],
hasMore: true,
isLoading: false,
isLoadingMore: false,
scrollViewRefreshing: false,
defaultImg: '/images/default-order.png',
ossImageUrl: app.globalData.ossImageUrl || '',
},
onLoad() {
wx.setNavigationBarTitle({ title: '我的接单' });
this.loadShangpinLeixing();
this.registerNotificationComponent();
},
onShow() {
this.registerNotificationComponent();
if (wx.getStorageSync('uid')) {
reconnectForRole('dashou');
if (app.startImWhenReady) app.startImWhenReady();
}
if (this.data.currentStatusKey && this.data.xuanzhongLeixingId) {
this.loadCurrentStatusOrders(true);
}
},
registerNotificationComponent() {
const notificationComp = this.selectComponent('#global-notification');
if (notificationComp && notificationComp.showNotification) {
app.globalData.globalNotification = {
show: (data) => notificationComp.showNotification(data),
hide: () => notificationComp.hideNotification()
};
}
},
// 加载商品类型(图片拼接)
async loadShangpinLeixing() {
try {
const res = await request({
url: '/dingdan/dsqdhqddlx',
method: 'POST',
header: { 'content-type': 'application/json' }
});
if (res.data && (res.data.code === 200 || res.data.code === 0)) {
let list = res.data.data.list || res.data.data || [];
if (!Array.isArray(list)) list = [];
const oss = this.data.ossImageUrl;
const processed = list.map(item => ({
...item,
full_tupian_url: item.tupian_url && !item.tupian_url.startsWith('http')
? oss + item.tupian_url
: (item.tupian_url || '/images/default-type.png')
}));
this.setData({
shangpinleixing: processed,
xuanzhongLeixingId: processed[0]?.id || null
});
this.loadCurrentStatusOrders(true);
}
} catch (e) {
console.error('加载商品类型失败', e);
}
},
selectLeixing(e) {
const id = e.currentTarget.dataset.id;
if (id === this.data.xuanzhongLeixingId) return;
this.setData({ xuanzhongLeixingId: id });
this.resetCurrentStatusData();
this.loadCurrentStatusOrders(true);
},
switchOrderType(e) {
const type = e.currentTarget.dataset.type;
if (type === this.data.orderType) return;
this.setData({ orderType: type });
this.resetCurrentStatusData();
this.loadCurrentStatusOrders(true);
},
onSearchInput(e) {
this.setData({ searchKeyword: e.detail.value });
if (this._searchTimer) clearTimeout(this._searchTimer);
this._searchTimer = setTimeout(() => {
this.onSearchConfirm();
}, 500);
},
onSearchConfirm() {
this.resetCurrentStatusData();
this.loadCurrentStatusOrders(true);
},
clearSearch() {
this.setData({ searchKeyword: '' });
this.resetCurrentStatusData();
this.loadCurrentStatusOrders(true);
},
switchStatus(e) {
const key = e.currentTarget.dataset.key;
if (key === this.data.currentStatusKey) return;
this.setData({ currentStatusKey: key });
const tabData = this.data.dsDingdanShuju[key];
if (tabData.list.length === 0 && tabData.hasMore && !tabData.isLoading) {
this.loadCurrentStatusOrders(true);
} else {
this.refreshCurrentListView();
}
},
// 加载订单(核心)
async loadCurrentStatusOrders(isRefresh = false) {
const key = this.data.currentStatusKey;
const tabData = this.data.dsDingdanShuju[key];
if (tabData.isLoading) return;
const page = isRefresh ? 1 : tabData.page;
this.setData({
[`dsDingdanShuju.${key}.isLoading`]: true,
isLoading: true,
isLoadingMore: !isRefresh
});
try {
const apiUrl = this.data.orderType === 'peihu'
? '/dingdan/phddlbhq'
: '/dingdan/dshqdingdan';
const statusItem = this.data.statusList.find(s => s.key === key);
const zhuangtaiList = statusItem.zhuangtaiList.length > 0 ? statusItem.zhuangtaiList : undefined;
const params = {
zhuangtai_list: zhuangtaiList,
page: page,
page_size: 5,
leixing_id: this.data.xuanzhongLeixingId,
keyword: this.data.searchKeyword || undefined
};
const res = await request({
url: apiUrl,
method: 'POST',
data: params,
header: { 'content-type': 'application/json' }
});
const code = res.data.code;
if (code === 200 || code === 0) {
const newList = res.data.data.list || [];
const hasMore = res.data.data.has_more || false;
// ✅ 图片URL拼接核心
const processed = newList.map(item => ({
...item,
zhuangtaiZh: this.getZhuangtaiZh(item.zhuangtai),
zhuangtaiColor: statusItem.color,
tupian: item.tupian
? (item.tupian.startsWith('http') ? item.tupian : this.data.ossImageUrl + item.tupian)
: this.data.defaultImg
}));
const currentList = this.data.dsDingdanShuju[key].list;
const updatedList = isRefresh ? processed : [...currentList, ...processed];
this.setData({
[`dsDingdanShuju.${key}.list`]: updatedList,
[`dsDingdanShuju.${key}.page`]: page,
[`dsDingdanShuju.${key}.hasMore`]: hasMore,
[`dsDingdanShuju.${key}.isLoading`]: false,
isLoading: false,
isLoadingMore: false,
scrollViewRefreshing: false
});
this.refreshCurrentListView();
} else {
throw new Error(res.data.msg || '加载失败');
}
} catch (err) {
console.error('加载订单失败', err);
wx.showToast({ title: err.message || '加载失败', icon: 'none' });
this.setData({
[`dsDingdanShuju.${key}.isLoading`]: false,
isLoading: false,
isLoadingMore: false,
scrollViewRefreshing: false
});
this.refreshCurrentListView();
}
},
refreshCurrentListView() {
const key = this.data.currentStatusKey;
const tabData = this.data.dsDingdanShuju[key];
this.setData({
currentList: tabData.list,
hasMore: tabData.hasMore,
isLoading: tabData.isLoading
});
},
resetCurrentStatusData() {
const key = this.data.currentStatusKey;
this.setData({
[`dsDingdanShuju.${key}.list`]: [],
[`dsDingdanShuju.${key}.page`]: 1,
[`dsDingdanShuju.${key}.hasMore`]: true,
[`dsDingdanShuju.${key}.isLoading`]: false
});
this.refreshCurrentListView();
},
// 下拉刷新(由 scroll-view 触发)
onPullDownRefresh() {
if (this.data.isLoading) {
this.setData({ scrollViewRefreshing: false });
return;
}
this.setData({ scrollViewRefreshing: true });
this.resetCurrentStatusData();
this.loadCurrentStatusOrders(true);
},
// 上拉加载更多(由 scroll-view 触发)
onReachBottom() {
const key = this.data.currentStatusKey;
const tabData = this.data.dsDingdanShuju[key];
if (tabData.isLoading || !tabData.hasMore) return;
this.setData({ [`dsDingdanShuju.${key}.page`]: tabData.page + 1 });
this.loadCurrentStatusOrders(false);
},
// ✅ 手动点击“加载更多”按钮
onLoadMoreTap() {
const key = this.data.currentStatusKey;
const tabData = this.data.dsDingdanShuju[key];
if (tabData.isLoading || !tabData.hasMore) return;
this.setData({ [`dsDingdanShuju.${key}.page`]: tabData.page + 1 });
this.loadCurrentStatusOrders(false);
},
getZhuangtaiZh(zhuangtai) {
const map = {
1: '已下单', 2: '进行中', 3: '已完成',
4: '退款中', 5: '已退款', 6: '退款失败',
7: '指定中', 8: '结算中'
};
return map[zhuangtai] || '未知状态';
},
goToDsDingdanXiangqing(e) {
const item = e.currentTarget.dataset.item;
if (!item) return;
const dataStr = encodeURIComponent(JSON.stringify(item));
if (this.data.orderType === 'peihu') {
wx.navigateTo({
url: `/pages/escort-orders/escort-orders?dingdanData=${dataStr}`
});
} else {
wx.navigateTo({
url: `/pages/fighter-order-detail/fighter-order-detail?dingdanData=${dataStr}`
});
}
},
onImageError() {}
});

View File

@@ -281,9 +281,7 @@ page {
animation: spin 0.8s linear infinite;
margin-bottom: 24rpx;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.empty-img {
width: 200rpx;
height: 200rpx;

View File

@@ -132,8 +132,6 @@ page {
margin-bottom: 20rpx;
}
@keyframes spin { to { transform: rotate(360deg); } }
.state-img {
width: 160rpx; height: 160rpx;
margin-bottom: 20rpx;

File diff suppressed because it is too large Load Diff

View File

@@ -4,15 +4,9 @@ page {
color: #1A1A1A;
}
/* .page-bg 与全局相同,仅保留差异 */
.page-bg {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0.42;
z-index: 0;
pointer-events: none;
}
.unreg-area {
@@ -50,31 +44,26 @@ page {
color: #444;
}
/* .unreg-input 与 .input-rounded 类似,仅保留差异 */
.unreg-input {
width: 100%;
height: 80rpx;
background: #FFFFFF;
border-radius: 40rpx;
padding: 0 32rpx;
font-size: 28rpx;
box-sizing: border-box;
margin-bottom: 16rpx;
}
.ph { color: #999; }
/* .unreg-btn 与 .btn .btn-secondary 类似,仅保留差异 */
.unreg-btn {
width: 100%;
height: 80rpx;
background: rgba(255, 255, 255, 0.65);
border-radius: 40rpx;
display: flex;
align-items: center;
justify-content: center;
border: 1rpx solid rgba(0, 0, 0, 0.06);
margin-bottom: 16rpx;
}
/* .unreg-btn-secondary 与 .btn-secondary 类似,仅保留差异 */
.unreg-btn-secondary {
margin-bottom: 0;
background: rgba(255, 255, 255, 0.45);
@@ -112,27 +101,9 @@ page {
padding: 0 24rpx 150rpx;
}
.refresh-btn {
position: fixed;
top: 36rpx;
right: 32rpx;
width: 56rpx;
height: 56rpx;
background: rgba(255, 255, 255, 0.8);
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 4rpx 16rpx rgba(0, 0, 0, 0.05);
}
/* .refresh-btn 与全局 .refresh-btn .refresh-btn--light 完全相同,删除 */
.refresh-icon {
width: 28rpx;
height: 28rpx;
}
/* .refresh-icon 与全局 .refresh-icon 完全相同,删除 */
.user-card {
position: relative;
@@ -163,13 +134,9 @@ page {
backdrop-filter: blur(12rpx);
}
/* .user-avatar 与 .avatar-circle .avatar-md 类似,仅保留差异 */
.user-avatar {
width: 88rpx;
height: 88rpx;
border-radius: 50%;
border: 2rpx solid #FFFFFF;
margin-right: 20rpx;
flex-shrink: 0;
}
.user-info { flex: 1; min-width: 0; }
@@ -254,7 +221,7 @@ page {
font-weight: 600;
}
/* ===== 黑卡 VIP ===== */
/* 黑卡 VIP */
.vip-black-card {
position: relative;
height: 152rpx;
@@ -329,7 +296,7 @@ page {
color: #f3ead8;
}
/* ===== 资产面板 ===== */
/* 资产面板 */
.asset-panel {
position: relative;
border-radius: 28rpx;
@@ -447,7 +414,7 @@ page {
margin-top: 4rpx;
}
/* ===== 排行榜(无大框,梯形格) ===== */
/* 排行榜 */
.rank-section {
margin-bottom: 22rpx;
padding: 0 2rpx;
@@ -613,7 +580,7 @@ page {
text-align: right;
}
/* ===== 功能面板 ===== */
/* 功能面板 */
.func-panel {
position: relative;
border-radius: 28rpx;
@@ -720,10 +687,10 @@ page {
font-weight: 600;
}
/* .list-card 与 .card 类似,仅保留差异 */
.list-card {
position: relative;
border-radius: 28rpx;
overflow: hidden;
box-shadow: 0 6rpx 24rpx rgba(0, 0, 0, 0.04);
}
@@ -743,23 +710,22 @@ page {
backdrop-filter: blur(12rpx);
}
/* .list-item 与全局 .list-item 类似,仅保留差异 */
.list-item {
display: flex;
align-items: center;
padding: 22rpx 20rpx;
border-bottom: 1rpx solid rgba(0, 0, 0, 0.04);
}
.list-item:last-child { border-bottom: none; }
/* .list-icon 与全局 .list-icon 类似,仅保留差异(缺少 flex-shrink:0 */
.list-icon {
width: 36rpx;
height: 36rpx;
margin-right: 16rpx;
}
/* .list-text 与全局 .list-text 类似,仅保留差异 */
.list-text {
flex: 1;
font-size: 26rpx;
}
@@ -769,13 +735,12 @@ page {
margin-right: 8rpx;
}
/* .list-arrow 与全局 .list-arrow 类似,仅保留差异 */
.list-arrow {
width: 20rpx;
height: 20rpx;
opacity: 0.45;
}
/* ===== 客服入口 ===== */
/* 客服入口 */
.kefu-banner {
position: relative;
margin-bottom: 18rpx;
@@ -851,7 +816,7 @@ page {
margin-left: 16rpx;
}
/* ===== 总资产 ===== */
/* 总资产 */
.total-asset-row {
position: relative;
border-radius: 24rpx;
@@ -937,6 +902,7 @@ page {
font-weight: 500;
}
/* .withdraw-btn 与 .btn-sm--dark 类似,仅保留差异 */
.withdraw-btn {
flex-shrink: 0;
min-width: 120rpx;
@@ -944,10 +910,7 @@ page {
line-height: 72rpx;
text-align: center;
padding: 0 28rpx;
font-size: 26rpx;
font-weight: 700;
color: #fff;
background: linear-gradient(135deg, #3d4f66 0%, #2c3e50 100%);
border-radius: 999rpx;
box-shadow: 0 6rpx 20rpx rgba(44, 62, 80, 0.22);
}
@@ -965,7 +928,7 @@ page {
box-sizing: border-box;
}
/* ===== 身份认证 ===== */
/* 身份认证 */
.auth-grid {
position: relative;
z-index: 1;
@@ -1008,28 +971,12 @@ page {
background: rgba(90, 107, 87, 0.1);
}
.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-mask 与全局 .loading-mask .loading-mask--light 完全相同,删除 */
/* .loading-spinner 与 .spinner--md 类似,仅保留差异 */
.loading-spinner {
width: 52rpx;
height: 52rpx;
border: 3rpx solid rgba(0, 0, 0, 0.08);
border-top-color: #1A1A1A;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}

View File

@@ -1,313 +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);
}
},
// pages/gold-fighter/gold-fighter.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/fighter/fighter' });
},
onShow() {
wx.redirectTo({ url: '/pages/fighter/fighter' });
},
_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/fighter/fighter' })
} 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/chat/chat?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/withdraw/withdraw' }) },
goToReceiveOrder() { wx.navigateTo({ url: '/pages/accept-order/accept-order' }) },
goToRecharge() { wx.navigateTo({ url: '/pages/fighter-recharge/fighter-recharge' }) },
goToMyOrders() { wx.navigateTo({ url: '/pages/fighter-orders/fighter-orders' }) },
goToMyPunishment() { wx.navigateTo({ url: '/pages/penalty/penalty/penalty' }) },
goToRanking() { wx.navigateTo({ url: '/pages/fighter-rank/fighter-rank?type=dashou' }) },
goToModifyInfo() { wx.navigateTo({ url: '/pages/fighter-edit/fighter-edit' }) },
goToRules() { wx.navigateTo({ url: '/pages/fighter-rules/fighter-rules' }) },
// 跳转到考核金牌
goToKaohe() {
wx.navigateTo({ url: '/pages/assess-gold/assess-gold' });
},
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);
}
},
})

View File

@@ -49,7 +49,6 @@ Page({
isCross: p.isCross
});
wx.setNavigationBarTitle({ title: this.data.groupName });
// 如果有外部传入的 currentUser 信息,直接用,不再依赖全局状态
if (p.currentUserId) {
this.setData({
currentUser: {
@@ -111,22 +110,6 @@ Page({
});
this.setData({ messages: msgs });
},
/*onLoad(options) {
if (options.data) {
try {
const p = JSON.parse(decodeURIComponent(options.data));
this.setData({
groupId: p.groupId || '',
groupName: p.groupName || '订单群聊',
groupAvatar: p.groupAvatar || '',
orderId: p.orderId || '',
isCross: p.isCross || 0
});
} catch (e) {}
}
wx.setNavigationBarTitle({ title: this.data.groupName });
this.initCurrentUser();
},*/
onShow() {
this.autoConnect();
@@ -189,8 +172,8 @@ Page({
if (!groupId) return;
wx.goEasy.im.subscribeGroup({
groupIds: [groupId],
onSuccess: () => console.log(`[群聊页] 订阅成功: ${groupId}`),
onFailed: (err) => console.error(`[群聊页] 订阅失败: ${groupId}`, err)
onSuccess: () => {},
onFailed: () => {}
});
},
@@ -279,7 +262,7 @@ Page({
this.markGroupMessageAsRead();
};
wx.goEasy.im.on(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, this._msgHandler);
this.markGroupMessageAsRead(); // ← 收到新消息就标记已读
this.markGroupMessageAsRead();
},
markGroupMessageAsRead() {

View File

@@ -1,400 +1,391 @@
// 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 });
}
// 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';
// 扫码场景参数名
const SCENE_PARAM = 'scene';
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) {
// 解析二维码参数
if (options[SCENE_PARAM]) {
try {
const scene = decodeURIComponent(options[SCENE_PARAM]);
this.shopId = scene;
} catch (e) {
console.error('scene解码失败', e);
}
}
// 等待全局配置加载完成
this.waitForConfigAndInit();
// 已登录时才检查弹窗
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. 扫码进入时绑定店铺
if (this.shopId) {
await bindShop(this.shopId);
}
// 3. 并行加载公告轮播图和商品数据
const [gonggaoResult, shopData] = await Promise.all([
this.loadGonggaoAndLunbo(),
fetchShopGoods()
]);
// 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 });
}
},
/**
* 加载公告和轮播图
*/
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
});
},
/**
* 刷新所有数据
* 只刷新商品数据
*/
async refreshAllData() {
if (this.data.isRefreshing) return;
this.setData({ isRefreshing: true });
wx.showLoading({ title: '刷新中...', mask: true });
try {
// 重新获取商品数据
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/product-detail/product-detail?id=${shangpinId}`
});
},
onReachBottom() {},
onShareAppMessage() {
return {
title: '阿龙电竞',
path: '/pages/shangpin/shangpin'
};
},
onUnload() {
this.setData({ gonggaoAnim: false });
}
});

View File

@@ -113,7 +113,6 @@
class="shangpin-image"
lazy-load="{{true}}"
/>
<!-- 移除已售标签 -->
</view>
<!-- 商品标题 -->

Some files were not shown because too many files have changed in this diff Show More