第一次提交:微信小程序前端最新完整代码
This commit is contained in:
44
_backup_pre_redesign/REVERT.md
Normal file
44
_backup_pre_redesign/REVERT.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# 大改备份与恢复说明
|
||||
|
||||
改造日期:2026-06-09
|
||||
|
||||
## 如何一键恢复改造前(若已手动备份)
|
||||
|
||||
将 `_backup_pre_redesign/` 下对应文件夹覆盖回项目即可(见各子目录)。
|
||||
|
||||
## 本次改造改动文件清单
|
||||
|
||||
### 新增
|
||||
- `utils/primary-role.js` — 三端固定 primaryRole
|
||||
- `utils/avatar.js` — 统一默认头像
|
||||
|
||||
### TabBar
|
||||
- `custom-tab-bar/index.js`
|
||||
- `custom-tab-bar/index.wxml`
|
||||
- `custom-tab-bar/index.wxss`
|
||||
|
||||
### 打手合一中心(主战场)
|
||||
- `pages/dashouduan/dashouduan.js`
|
||||
- `pages/dashouduan/dashouduan.wxml`
|
||||
- `pages/dashouduan/dashouduan.wxss`
|
||||
- `pages/dashouduan/dashouduan.json`
|
||||
|
||||
### 启动与 IM
|
||||
- `app.js`
|
||||
- `utils/chat-core.js`
|
||||
|
||||
### 老板端 / 认证
|
||||
- `pages/wode/wode.wxml`
|
||||
- `pages/renzheng/renzheng.js`
|
||||
- `utils/role-tab-bar.js`
|
||||
|
||||
### 废弃独立 Tab(路由保留,自动跳转打手中心)
|
||||
- `pages/guanshiduan/guanshiduan.js` — onLoad redirect
|
||||
- `pages/zuzhangduan/zuzhangduan.js` — onLoad redirect
|
||||
|
||||
## 改造要点
|
||||
1. 去掉 TabBar「切换身份」
|
||||
2. 三端固定:normal / dashou / shangjia
|
||||
3. 管事+组长功能合并进 dashouduan
|
||||
4. 刷新按钮一次调用:dashouxinxi + guanshixinxi + zuzhangxinxi
|
||||
5. IM 打手端统一 Ds 前缀连接
|
||||
417
_backup_pre_redesign/app.js
Normal file
417
_backup_pre_redesign/app.js
Normal file
@@ -0,0 +1,417 @@
|
||||
// app.js - 稳健生产版本(修复 GoEasy 初始化错误 + 配置100%加载)
|
||||
import GoEasy from './static/lib/goeasy-2.13.24.esm.min';
|
||||
const ChatCore = require('./utils/chat-core');
|
||||
import { check } from './utils/phone-auth';
|
||||
|
||||
|
||||
// ===== 角色 → 默认首页(与 custom-tab-bar 完全一致) =====
|
||||
const roleDefaultPage = {
|
||||
normal: '/pages/index/index',
|
||||
dashou: '/pages/dashouduan/dashouduan',
|
||||
shangjia: '/pages/shangjiaduan/shangjiaduan',
|
||||
guanshi: '/pages/guanshiduan/guanshiduan',
|
||||
zuzhang: '/pages/zuzhangduan/zuzhangduan',
|
||||
kaoheguan: '/pages/kaoheguan/kaoheguan'
|
||||
};
|
||||
|
||||
App({
|
||||
globalData: {
|
||||
|
||||
hasShownPopupOnColdStart: false,
|
||||
|
||||
apiBaseUrl: 'https://www.abas.asia/hqhd',
|
||||
ossImageUrl: '',
|
||||
morentouxiang: '',
|
||||
dashouguize: '',
|
||||
|
||||
shangpinliebiao: [],
|
||||
shangpinzhuanqu: [],
|
||||
shangpinleixing: [],
|
||||
shangpinlunbo: [],
|
||||
shangpingonggao: '',
|
||||
lunbozhanwei: '/images/lunbozhanwei.jpg',
|
||||
|
||||
dingdanTiaoshu: {
|
||||
daifuwu: 0,
|
||||
fuwuzhong: 0,
|
||||
yiwancheng: 0,
|
||||
yituikuan: 0
|
||||
},
|
||||
|
||||
dashouqun: '',
|
||||
dashouqunid: '',
|
||||
guanshiqun: '',
|
||||
guanshiqunid: '',
|
||||
|
||||
appId:'wx0e4be86faac4a8d1',
|
||||
|
||||
shangjiastatus: 0,
|
||||
dashoustatus: 0,
|
||||
guanshistatus: 0,
|
||||
dashouNicheng: '',
|
||||
zhanghaoStatus: null,
|
||||
dashouzhuangtai: null,
|
||||
yongjin: null,
|
||||
zonge: null,
|
||||
yajin: null,
|
||||
chenghao: '',
|
||||
jinfen: null,
|
||||
clumber: [{
|
||||
huiyuanid: '',
|
||||
huiyuanming: '',
|
||||
daoqi: ''
|
||||
}],
|
||||
chengjiaoliang: null,
|
||||
zaixianZhuangtai: null,
|
||||
|
||||
guanshi: {
|
||||
nicheng: '',
|
||||
uid: '',
|
||||
touxiang: '',
|
||||
gszhstatus: '',
|
||||
yaoqingzongshu: 0,
|
||||
fenyongzonge: '0.00',
|
||||
fenyongtixian: '0.00'
|
||||
},
|
||||
|
||||
shangjia: {
|
||||
sjzhzhuangtai: '',
|
||||
sjyue: '',
|
||||
fadanzong: null,
|
||||
tuikuanzong: null,
|
||||
riliushui: '',
|
||||
yueliushui: ''
|
||||
},
|
||||
|
||||
liaotian_liebiao: [],
|
||||
xshenfen: 1,
|
||||
|
||||
goEasyConfig: null,
|
||||
kefuConfig: {
|
||||
link: '',
|
||||
enterpriseId: ''
|
||||
},
|
||||
|
||||
cosConfig: {
|
||||
bucket: '',
|
||||
region: 'ap-shanghai',
|
||||
uploadPathPrefix: 'order/'
|
||||
},
|
||||
|
||||
goEasyConnection: {
|
||||
status: 'disconnected',
|
||||
userId: '',
|
||||
identityType: '',
|
||||
lastConnectTime: 0,
|
||||
autoReconnect: true,
|
||||
reconnectAttempts: 0,
|
||||
maxReconnectAttempts: 5,
|
||||
heartbeatInterval: null,
|
||||
cacheKeys: {
|
||||
savedConnection: 'savedGoEasyConnection',
|
||||
userId: 'goEasyUserId',
|
||||
identityType: 'currentGoEasyIdentity',
|
||||
connectTime: 'goEasyConnectTime'
|
||||
},
|
||||
config: {
|
||||
heartbeatInterval: 20000,
|
||||
reconnectDelay: 3000,
|
||||
offlineTimeout: 30000,
|
||||
cacheValidityHours: 12
|
||||
}
|
||||
},
|
||||
|
||||
messageManager: {
|
||||
unreadTotal: 0,
|
||||
unreadMap: {},
|
||||
latestMessages: [],
|
||||
notificationQueue: [],
|
||||
notificationVisible: false,
|
||||
lastNotificationTime: 0,
|
||||
notificationCooldown: 3000,
|
||||
tabBarIndex: 2,
|
||||
showTabBarBadge: true,
|
||||
customTabBar: true,
|
||||
tabBarBadgeText: 0,
|
||||
soundEnabled: true,
|
||||
vibrationEnabled: true,
|
||||
doNotDisturb: false,
|
||||
doNotDisturbStart: '22:00',
|
||||
doNotDisturbEnd: '08:00',
|
||||
notificationStyle: {
|
||||
position: 'top',
|
||||
duration: 3000,
|
||||
backgroundColor: '#00f7ff',
|
||||
textColor: '#ffffff',
|
||||
borderRadius: '16rpx',
|
||||
boxShadow: '0 10rpx 30rpx rgba(0, 247, 255, 0.3)'
|
||||
},
|
||||
currentNotification: null,
|
||||
cacheKeys: {
|
||||
messageSettings: 'messageSettings',
|
||||
unreadTotal: 'messageUnreadTotal',
|
||||
latestMessages: 'latestMessages',
|
||||
notificationMuted: 'notificationMuted'
|
||||
}
|
||||
},
|
||||
|
||||
pageState: {
|
||||
currentPage: '',
|
||||
isInChatPage: false,
|
||||
currentChatId: '',
|
||||
lastPageUpdate: 0
|
||||
},
|
||||
|
||||
eventListeners: {},
|
||||
debugMode: false,
|
||||
currentUser: null,
|
||||
currentRole: 'normal'
|
||||
},
|
||||
|
||||
// ========== 核心启动流程 ==========
|
||||
async onLaunch() {
|
||||
// ---------- ① 立即隐藏“返回首页”按钮 ----------
|
||||
wx.hideHomeButton();
|
||||
wx.onAppRoute(() => {
|
||||
wx.hideHomeButton();
|
||||
});
|
||||
|
||||
// ---------- ② 恢复角色并跳转 ----------
|
||||
const savedRole = wx.getStorageSync('currentRole') || 'normal';
|
||||
this.globalData.currentRole = savedRole;
|
||||
|
||||
const targetPage = roleDefaultPage[savedRole] || '/pages/index/index';
|
||||
if (savedRole !== 'normal' || targetPage !== '/pages/index/index') {
|
||||
setTimeout(() => {
|
||||
wx.reLaunch({ url: targetPage });
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// ---------- ③ 立刻应用本地缓存配置 ----------
|
||||
const cachedConfig = this.readConfigFromStorage();
|
||||
if (cachedConfig) {
|
||||
this.applyDynamicConfig(cachedConfig);
|
||||
}
|
||||
|
||||
// ---------- ④ 初始化基础设施 ----------
|
||||
this.initGoEasyWithConfig(); // 只有appkey有效时才真正初始化
|
||||
ChatCore.initGlobalMessageSystem(this);
|
||||
this.initCurrentUser();
|
||||
|
||||
// ---------- ⑤ 建立连接 ----------
|
||||
const saved = this.getSavedConnection();
|
||||
console.log('【启动】缓存身份:', saved ? saved.identityType : '无',
|
||||
'userId:', saved ? saved.userId : '无');
|
||||
setTimeout(() => {
|
||||
this.ensureConnection();
|
||||
}, 300);
|
||||
|
||||
// ---------- ⑥ 后台静默获取最新配置 ----------
|
||||
this.fetchConfigSafely()
|
||||
.then(latestConfig => {
|
||||
this.saveConfigToStorage(latestConfig);
|
||||
this.applyDynamicConfig(latestConfig);
|
||||
// 重新尝试初始化 GoEasy(如果之前因缺少appkey而跳过,现在会成功)
|
||||
this.initGoEasyWithConfig();
|
||||
console.log('远程配置更新完成');
|
||||
})
|
||||
.catch(err => {
|
||||
console.warn('远程配置获取失败,继续使用本地缓存', err);
|
||||
});
|
||||
|
||||
// ---------- ⑦ 手机号认证检查 ----------
|
||||
try {
|
||||
const needAuth = await check();
|
||||
if (needAuth) {
|
||||
wx.reLaunch({ url: '/pages/phone-auth/phone-auth' });
|
||||
return;
|
||||
}
|
||||
} catch (e) {}
|
||||
},
|
||||
|
||||
// ========== 连接管理方法 ==========
|
||||
getSavedConnection() {
|
||||
try {
|
||||
const saved = wx.getStorageSync(
|
||||
this.globalData.goEasyConnection.cacheKeys.savedConnection
|
||||
);
|
||||
return saved || null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
ensureConnection() {
|
||||
const conn = this.globalData.goEasyConnection;
|
||||
const userId = wx.getStorageSync(conn.cacheKeys.userId);
|
||||
if (userId && conn.status !== 'connected') {
|
||||
console.log('尝试建立GoEasy连接,userId:', userId);
|
||||
// 此处可调用实际的连接逻辑,目前仅做标记
|
||||
}
|
||||
},
|
||||
|
||||
// ========== 配置缓存读写 ==========
|
||||
CONFIG_CACHE_KEY: 'app_dynamic_config',
|
||||
|
||||
readConfigFromStorage() {
|
||||
try {
|
||||
const data = wx.getStorageSync(this.CONFIG_CACHE_KEY);
|
||||
if (data && typeof data === 'object') {
|
||||
return data;
|
||||
}
|
||||
} catch (e) {}
|
||||
return null;
|
||||
},
|
||||
|
||||
saveConfigToStorage(config) {
|
||||
try {
|
||||
if (config && typeof config === 'object') {
|
||||
wx.setStorageSync(this.CONFIG_CACHE_KEY, config);
|
||||
}
|
||||
} catch (e) {}
|
||||
},
|
||||
|
||||
// ========== 安全获取远程配置(永不报错) ==========
|
||||
fetchConfigSafely() {
|
||||
return new Promise((resolve) => {
|
||||
wx.request({
|
||||
url: this.globalData.apiBaseUrl + '/peizhi/qdpzhq',
|
||||
method: 'POST',
|
||||
header: { 'content-type': 'application/json' },
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200 && res.data && res.data.code === 0 && res.data.data) {
|
||||
resolve(res.data.data);
|
||||
} else {
|
||||
console.warn('远程配置接口返回异常,使用已有配置');
|
||||
resolve(this.readConfigFromStorage() || {});
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.warn('远程配置网络请求失败,使用已有配置', err);
|
||||
resolve(this.readConfigFromStorage() || {});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// ========== 原有方法(保留,仅增强) ==========
|
||||
fetchDynamicConfig() {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.request({
|
||||
url: this.globalData.apiBaseUrl + '/peizhi/qdpzhq',
|
||||
method: 'POST',
|
||||
header: { 'content-type': 'application/json' },
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200 && res.data && res.data.code === 0) {
|
||||
resolve(res.data.data);
|
||||
} else {
|
||||
reject(new Error(res.data?.msg || '接口返回错误'));
|
||||
}
|
||||
},
|
||||
fail: reject
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
applyDynamicConfig(config) {
|
||||
if (!config || typeof config !== 'object') return;
|
||||
|
||||
if (config.cos) {
|
||||
this.globalData.ossImageUrl = config.cos.ossImageUrl || '';
|
||||
this.globalData.cosConfig = {
|
||||
bucket: config.cos.bucket || '',
|
||||
region: config.cos.region || 'ap-shanghai',
|
||||
uploadPathPrefix: config.cos.uploadPathPrefix || 'order/'
|
||||
};
|
||||
}
|
||||
|
||||
if (config.goEasy) {
|
||||
this.globalData.goEasyConfig = {
|
||||
host: config.goEasy.host || 'hangzhou.goeasy.io',
|
||||
appkey: config.goEasy.appkey || ''
|
||||
};
|
||||
}
|
||||
|
||||
if (config.otherConfig) {
|
||||
this.globalData.morentouxiang = config.otherConfig.morentouxiang || '';
|
||||
this.globalData.dashouguize = config.otherConfig.dashouguize || '';
|
||||
}
|
||||
|
||||
if (config.kefu) {
|
||||
this.globalData.kefuConfig = {
|
||||
link: config.kefu.link || '',
|
||||
enterpriseId: config.kefu.enterpriseId || ''
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
// 🔥 关键修复:只在有效 appkey 时初始化,且避免重复初始化
|
||||
initGoEasyWithConfig() {
|
||||
const cfg = this.globalData.goEasyConfig;
|
||||
if (!cfg || !cfg.appkey) {
|
||||
console.warn('GoEasy appkey 未配置,聊天功能将在配置就绪后自动恢复');
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果已经初始化过,就不再重复创建实例
|
||||
if (wx.goEasy && wx.GoEasy) {
|
||||
console.log('GoEasy 已初始化,跳过');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
wx.goEasy = GoEasy.getInstance({
|
||||
host: cfg.host || 'hangzhou.goeasy.io',
|
||||
appkey: cfg.appkey,
|
||||
modules: ['im', 'pubsub']
|
||||
});
|
||||
wx.GoEasy = GoEasy;
|
||||
console.log('GoEasy 初始化成功');
|
||||
} catch (error) {
|
||||
console.error('GoEasy 初始化失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
initCurrentUser() {
|
||||
const uid = wx.getStorageSync('uid');
|
||||
if (uid) {
|
||||
this.globalData.currentUser = {
|
||||
id: uid,
|
||||
name: '用户' + uid.substring(0, 6),
|
||||
avatar: this.globalData.ossImageUrl + this.globalData.morentouxiang
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
emitEvent(eventName, data) {
|
||||
if (this.globalData.eventListeners[eventName]) {
|
||||
this.globalData.eventListeners[eventName].forEach(callback => {
|
||||
try { callback(data); } catch (error) { console.error(error); }
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/** 切换当前角色并通知 custom-tab-bar 立即刷新(Storage + globalData + 事件) */
|
||||
setCurrentRole(role) {
|
||||
if (!role) return;
|
||||
this.globalData.currentRole = role;
|
||||
wx.setStorageSync('currentRole', role);
|
||||
this.emitEvent('currentRoleChanged', { role });
|
||||
},
|
||||
|
||||
on(eventName, callback) {
|
||||
if (!this.globalData.eventListeners[eventName]) {
|
||||
this.globalData.eventListeners[eventName] = [];
|
||||
}
|
||||
this.globalData.eventListeners[eventName].push(callback);
|
||||
},
|
||||
|
||||
off(eventName, callback) {
|
||||
if (this.globalData.eventListeners[eventName]) {
|
||||
const index = this.globalData.eventListeners[eventName].indexOf(callback);
|
||||
if (index > -1) {
|
||||
this.globalData.eventListeners[eventName].splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
268
_backup_pre_redesign/chat-core.js
Normal file
268
_backup_pre_redesign/chat-core.js
Normal file
@@ -0,0 +1,268 @@
|
||||
// utils/chat-core.js - 最终角标稳定版
|
||||
import GoEasy from '../static/lib/goeasy-2.13.24.esm.min';
|
||||
import { jianquanxian } from './imAuth/jianquanxian';
|
||||
|
||||
let _convListener = null;
|
||||
let _privateListener = null;
|
||||
let _groupListener = null;
|
||||
let _notificationHandler = null;
|
||||
|
||||
function initGlobalMessageSystem(app) {
|
||||
app.ensureConnection = () => ensureConnection(app);
|
||||
app.loadConversations = () => loadConversations(app);
|
||||
app.disconnectGoEasy = () => disconnectGoEasy(app);
|
||||
app.switchRoleAndReconnect = (newRole) => switchRoleAndReconnect(app, newRole);
|
||||
app.updateUnreadCount = (count) => updateTabBarBadge(app, count || 0);
|
||||
|
||||
try {
|
||||
const muted = wx.getStorageSync('notificationMuted');
|
||||
if (muted !== undefined) app.globalData.messageManager.notificationMuted = muted;
|
||||
} catch(e) {}
|
||||
|
||||
initAppStateListener(app);
|
||||
initNetworkListener(app);
|
||||
ensureConnection(app);
|
||||
}
|
||||
|
||||
function getCurrentUserId() {
|
||||
return (wx.goEasy && wx.goEasy.im && wx.goEasy.im.userId) || null;
|
||||
}
|
||||
|
||||
async function ensureConnection(app) {
|
||||
const role = app.globalData.currentRole || 'normal';
|
||||
const uid = wx.getStorageSync('uid');
|
||||
if (!uid) return;
|
||||
|
||||
const prefixMap = { normal: 'Boss', dashou: 'Ds', shangjia: 'Sj', guanshi: 'Gs', zuzhang: 'Zz', kaoheguan: 'Kh' };
|
||||
const prefix = prefixMap[role] || 'Boss';
|
||||
const targetUserId = prefix + uid;
|
||||
|
||||
const status = wx.goEasy.getConnectionStatus ? wx.goEasy.getConnectionStatus() : 'disconnected';
|
||||
if (status === 'connected' || status === 'reconnected') {
|
||||
const currentUserId = getCurrentUserId();
|
||||
if (currentUserId === targetUserId) {
|
||||
registerGlobalListeners(app);
|
||||
await subscribeAllGroups(app);
|
||||
loadConversations(app);
|
||||
registerNotification(app);
|
||||
// 强制立即更新一次角标,避免切后台后不显示
|
||||
setTimeout(() => { loadConversations(app); }, 500);
|
||||
return;
|
||||
} else {
|
||||
wx.goEasy.disconnect();
|
||||
}
|
||||
}
|
||||
connectGoEasy(app, targetUserId, role);
|
||||
}
|
||||
|
||||
function connectGoEasy(app, userId, identityType) {
|
||||
const currentUser = app.globalData.currentUser || {
|
||||
id: wx.getStorageSync('uid'),
|
||||
name: '用户' + (wx.getStorageSync('uid') || '').substring(0, 6),
|
||||
avatar: app.globalData.ossImageUrl + app.globalData.morentouxiang
|
||||
};
|
||||
wx.goEasy.connect({
|
||||
id: userId,
|
||||
data: {
|
||||
name: currentUser.name,
|
||||
avatar: currentUser.avatar || (app.globalData.ossImageUrl + app.globalData.morentouxiang),
|
||||
identityType: identityType
|
||||
},
|
||||
onSuccess: async () => {
|
||||
wx.setStorageSync('savedGoEasyConnection', JSON.stringify({
|
||||
userId, identityType, lastConnectTime: Date.now(), status: 'connected'
|
||||
}));
|
||||
registerGlobalListeners(app);
|
||||
await subscribeAllGroups(app);
|
||||
loadConversations(app);
|
||||
registerNotification(app);
|
||||
},
|
||||
onFailed: (error) => {
|
||||
console.error('GoEasy连接失败', error);
|
||||
if (error.code === 408) {
|
||||
registerGlobalListeners(app);
|
||||
loadConversations(app);
|
||||
registerNotification(app);
|
||||
} else {
|
||||
wx.showToast({ title: '连接失败,请重试', icon: 'none' });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function subscribeAllGroups(app) {
|
||||
return new Promise((resolve) => {
|
||||
if (!wx.goEasy || !wx.goEasy.im) return resolve();
|
||||
wx.goEasy.im.latestConversations({
|
||||
onSuccess: (result) => {
|
||||
const conversations = result?.content?.conversations || result?.conversations || [];
|
||||
const groupIds = conversations.filter(c => c.type === 'group' && c.groupId).map(c => c.groupId);
|
||||
if (groupIds.length && wx.goEasy.im.subscribeGroup) {
|
||||
wx.goEasy.im.subscribeGroup({
|
||||
groupIds: groupIds,
|
||||
onSuccess: () => resolve(),
|
||||
onFailed: () => resolve()
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
onFailed: () => resolve()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function registerGlobalListeners(app) {
|
||||
if (!wx.goEasy || !wx.goEasy.im) return;
|
||||
if (_convListener) {
|
||||
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, _convListener);
|
||||
}
|
||||
if (_privateListener) {
|
||||
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _privateListener);
|
||||
}
|
||||
if (_groupListener) {
|
||||
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _groupListener);
|
||||
}
|
||||
|
||||
_convListener = (result) => {
|
||||
const unreadTotal = result.unreadTotal || 0;
|
||||
updateTabBarBadge(app, unreadTotal);
|
||||
app.emitEvent('conversationsUpdated', result);
|
||||
};
|
||||
wx.goEasy.im.on(wx.GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, _convListener);
|
||||
|
||||
_privateListener = () => { loadConversations(app); };
|
||||
wx.goEasy.im.on(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _privateListener);
|
||||
|
||||
_groupListener = () => { loadConversations(app); };
|
||||
wx.goEasy.im.on(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _groupListener);
|
||||
}
|
||||
|
||||
function loadConversations(app) {
|
||||
if (!getCurrentUserId()) return;
|
||||
wx.goEasy.im.latestConversations({
|
||||
onSuccess: (result) => {
|
||||
const unreadTotal = result.unreadTotal || 0;
|
||||
updateTabBarBadge(app, unreadTotal);
|
||||
app.emitEvent('conversationsUpdated', result);
|
||||
subscribeAllGroups(app);
|
||||
},
|
||||
onFailed: (err) => console.error('加载会话失败', err)
|
||||
});
|
||||
}
|
||||
|
||||
function updateTabBarBadge(app, unreadCount) {
|
||||
const badgeText = unreadCount > 0 ? (unreadCount > 99 ? '99+' : String(unreadCount)) : '';
|
||||
app.globalData.messageManager.unreadTotal = unreadCount;
|
||||
app.emitEvent('tabBarBadgeChanged', { badgeText, showRedDot: unreadCount > 0 });
|
||||
// 直接更新组件,确保角标立即显示
|
||||
try {
|
||||
const pages = getCurrentPages();
|
||||
if (pages.length) {
|
||||
const tabBar = pages[pages.length-1].selectComponent('#custom-tab-bar');
|
||||
if (tabBar && tabBar.setData) tabBar.setData({ badgeText });
|
||||
// 兼容 getTabBar 方式
|
||||
const page = pages[pages.length-1];
|
||||
if (page.getTabBar) {
|
||||
const tabBarInstance = page.getTabBar();
|
||||
if (tabBarInstance && tabBarInstance.setData) tabBarInstance.setData({ badgeText });
|
||||
}
|
||||
}
|
||||
} catch(e) {}
|
||||
wx.setStorageSync('messageUnreadTotal', unreadCount);
|
||||
}
|
||||
|
||||
function registerNotification(app) {
|
||||
if (!wx.goEasy || !wx.goEasy.im) return;
|
||||
if (_notificationHandler) {
|
||||
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _notificationHandler);
|
||||
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _notificationHandler);
|
||||
}
|
||||
_notificationHandler = (message) => {
|
||||
if (app.globalData.messageManager.notificationMuted) return;
|
||||
const pages = getCurrentPages();
|
||||
if (pages.length) {
|
||||
const curPage = pages[pages.length - 1];
|
||||
if (curPage.route === 'pages/liaotian/liaotian' || curPage.route === 'pages/qunliaotian/qunliaotian') return;
|
||||
}
|
||||
const senderName = message.senderData?.name || (message.senderId || '用户');
|
||||
let content = '';
|
||||
if (message.type === 'text') content = message.payload?.text || '[文本]';
|
||||
else if (message.type === 'image') content = '[图片]';
|
||||
else if (message.type === 'audio') content = '[语音]';
|
||||
else content = '[新消息]';
|
||||
if (app.globalData.globalNotification && app.globalData.globalNotification.show) {
|
||||
app.globalData.globalNotification.show({
|
||||
senderName, content, message,
|
||||
notificationType: message.groupId ? 'group' : 'private',
|
||||
groupId: message.groupId, orderId: message.orderId
|
||||
});
|
||||
} else {
|
||||
wx.showToast({ title: `${senderName}: ${content}`, icon: 'none', duration: 2000 });
|
||||
}
|
||||
};
|
||||
wx.goEasy.im.on(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _notificationHandler);
|
||||
wx.goEasy.im.on(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _notificationHandler);
|
||||
}
|
||||
|
||||
function disconnectGoEasy(app) {
|
||||
if (_convListener) {
|
||||
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.CONVERSATIONS_UPDATED, _convListener);
|
||||
_convListener = null;
|
||||
}
|
||||
if (_privateListener) {
|
||||
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _privateListener);
|
||||
_privateListener = null;
|
||||
}
|
||||
if (_groupListener) {
|
||||
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _groupListener);
|
||||
_groupListener = null;
|
||||
}
|
||||
if (_notificationHandler) {
|
||||
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.PRIVATE_MESSAGE_RECEIVED, _notificationHandler);
|
||||
wx.goEasy.im.off(wx.GoEasy.IM_EVENT.GROUP_MESSAGE_RECEIVED, _notificationHandler);
|
||||
_notificationHandler = null;
|
||||
}
|
||||
wx.goEasy.disconnect({ onSuccess: () => {}, onFailed: () => {} });
|
||||
wx.removeStorageSync('savedGoEasyConnection');
|
||||
}
|
||||
|
||||
async function switchRoleAndReconnect(app, newRole) {
|
||||
await disconnectGoEasy(app);
|
||||
app.globalData.currentRole = newRole;
|
||||
wx.setStorageSync('currentRole', newRole);
|
||||
ensureConnection(app);
|
||||
return true;
|
||||
}
|
||||
|
||||
function initAppStateListener(app) {
|
||||
let isActive = true;
|
||||
wx.onAppShow(() => {
|
||||
if (!isActive) return;
|
||||
const status = wx.goEasy.getConnectionStatus ? wx.goEasy.getConnectionStatus() : 'disconnected';
|
||||
if (status !== 'connected' && status !== 'reconnected') {
|
||||
ensureConnection(app);
|
||||
} else {
|
||||
// 连接存在但可能角标未更新,强制拉取并更新
|
||||
loadConversations(app);
|
||||
// 额外确保角标组件刷新
|
||||
setTimeout(() => {
|
||||
const unread = app.globalData.messageManager.unreadTotal || 0;
|
||||
updateTabBarBadge(app, unread);
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
wx.onAppHide(() => {});
|
||||
}
|
||||
|
||||
function initNetworkListener(app) {
|
||||
wx.onNetworkStatusChange((res) => {
|
||||
if (res.isConnected) {
|
||||
setTimeout(() => {
|
||||
ensureConnection(app);
|
||||
}, 1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { initGlobalMessageSystem };
|
||||
220
_backup_pre_redesign/custom-tab-bar/index.js
Normal file
220
_backup_pre_redesign/custom-tab-bar/index.js
Normal file
@@ -0,0 +1,220 @@
|
||||
// custom-tab-bar/index.js
|
||||
const app = getApp();
|
||||
|
||||
const roleCfg = {
|
||||
normal: {
|
||||
name: '点单老板',
|
||||
defaultPage: '/pages/index/index',
|
||||
tabList: [
|
||||
{ pagePath: 'pages/index/index', text: '商城', iconPath: '/images/zhuye.png', selectedIconPath: '/images/zhuye.png' },
|
||||
{ pagePath: 'pages/fenlei/fenlei', text: '分类', iconPath: '/images/fenlei.png', selectedIconPath: '/images/fenlei.png' },
|
||||
{ pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' },
|
||||
{ pagePath: 'pages/wode/wode', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' }
|
||||
]
|
||||
},
|
||||
dashou: {
|
||||
name: '打手',
|
||||
defaultPage: '/pages/jiedan/jiedan',
|
||||
tabList: [
|
||||
{ pagePath: 'pages/jiedan/jiedan', text: '接单池', iconPath: '/images/jiedanchi.png', selectedIconPath: '/images/jiedanchi.png' },
|
||||
{ pagePath: 'pages/dashoudingdan/dashoudingdan', text: '订单', iconPath: '/images/dingdan.png', selectedIconPath: '/images/dingdan.png' },
|
||||
{ pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' },
|
||||
{ pagePath: 'pages/dashouduan/dashouduan', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' }
|
||||
]
|
||||
},
|
||||
shangjia: {
|
||||
name: '商家',
|
||||
defaultPage: 'pages/sjdingdan/sjdingdan',
|
||||
tabList: [
|
||||
{ pagePath: 'pages/sjdingdan/sjdingdan', text: '订单', iconPath: '/images/dingdan.png', selectedIconPath: '/images/dingdan.png' },
|
||||
{ pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' },
|
||||
{ pagePath: 'pages/shangjiaduan/shangjiaduan', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' }
|
||||
]
|
||||
},
|
||||
guanshi: {
|
||||
name: '管事',
|
||||
defaultPage: '/pages/guanshiduan/guanshiduan',
|
||||
tabList: [
|
||||
{ pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' },
|
||||
{ pagePath: 'pages/guanshiduan/guanshiduan', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' }
|
||||
]
|
||||
},
|
||||
zuzhang: {
|
||||
name: '组长',
|
||||
defaultPage: '/pages/zuzhangduan/zuzhangduan',
|
||||
tabList: [
|
||||
{ pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' },
|
||||
{ pagePath: 'pages/zuzhangduan/zuzhangduan', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' }
|
||||
]
|
||||
},
|
||||
kaoheguan: {
|
||||
name: '考核官',
|
||||
defaultPage: '/pages/kaoheguan/kaoheguan',
|
||||
tabList: [
|
||||
{ pagePath: 'pages/xiaoxi/xiaoxi', text: '消息', iconPath: '/images/xiaoxi.png', selectedIconPath: '/images/xiaoxi.png' },
|
||||
{ pagePath: 'pages/kaoheguan/kaoheguan', text: '我的', iconPath: '/images/wode.png', selectedIconPath: '/images/wode.png' }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
Component({
|
||||
data: {
|
||||
selectedIndex: 0,
|
||||
tabList: [],
|
||||
currentRole: 'normal',
|
||||
showRolePicker: false,
|
||||
availableRoles: [],
|
||||
badgeText: ''
|
||||
},
|
||||
lifetimes: {
|
||||
attached() {
|
||||
this.refresh();
|
||||
this._badgeListener = (data) => {
|
||||
if (data.badgeText !== undefined) {
|
||||
this.setData({ badgeText: data.badgeText });
|
||||
}
|
||||
};
|
||||
this._roleListener = () => {
|
||||
this.refresh();
|
||||
};
|
||||
app.on('tabBarBadgeChanged', this._badgeListener);
|
||||
app.on('currentRoleChanged', this._roleListener);
|
||||
},
|
||||
detached() {
|
||||
if (this._badgeListener) {
|
||||
app.off('tabBarBadgeChanged', this._badgeListener);
|
||||
}
|
||||
if (this._roleListener) {
|
||||
app.off('currentRoleChanged', this._roleListener);
|
||||
}
|
||||
}
|
||||
},
|
||||
pageLifetimes: {
|
||||
show() {
|
||||
this.refresh();
|
||||
const globalBadge = app.globalData.messageManager?.tabBarBadgeText || '';
|
||||
if (this.data.badgeText !== globalBadge) {
|
||||
this.setData({ badgeText: globalBadge });
|
||||
}
|
||||
this._syncUnreadWithDebounce();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
_syncUnreadWithDebounce() {
|
||||
if (this._syncTimer) clearTimeout(this._syncTimer);
|
||||
this._syncTimer = setTimeout(() => {
|
||||
if (app && app.loadConversations) {
|
||||
app.loadConversations();
|
||||
}
|
||||
}, 300);
|
||||
},
|
||||
|
||||
refresh() {
|
||||
const token = wx.getStorageSync('token');
|
||||
// 未登录时强制点单端,避免清除缓存后 TabBar 仍显示其他身份
|
||||
let role = wx.getStorageSync('currentRole') || app.globalData.currentRole || 'normal';
|
||||
if (!token) {
|
||||
role = 'normal';
|
||||
app.globalData.currentRole = 'normal';
|
||||
wx.setStorageSync('currentRole', 'normal');
|
||||
} else {
|
||||
app.globalData.currentRole = role;
|
||||
}
|
||||
let cfg = roleCfg[role];
|
||||
if (!cfg) return;
|
||||
|
||||
// 打手身份下,如果 isJinpai=1,悄悄把“我的”页面换成金牌页面
|
||||
if (role === 'dashou') {
|
||||
const isJinpai = wx.getStorageSync('isJinpai') === 1;
|
||||
if (isJinpai) {
|
||||
cfg = {
|
||||
...cfg,
|
||||
tabList: cfg.tabList.map(item => {
|
||||
if (item.pagePath === 'pages/dashouduan/dashouduan') {
|
||||
return { ...item, pagePath: 'pages/jinpaids/jinpaids' };
|
||||
}
|
||||
return { ...item };
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const list = cfg.tabList.map((v, i) => ({ ...v, index: i }));
|
||||
const pages = getCurrentPages();
|
||||
const curRoute = pages.length ? pages[pages.length - 1].route : '';
|
||||
let idx = list.findIndex(t => t.pagePath === curRoute);
|
||||
if (idx === -1) idx = 0;
|
||||
const avail = this._getAvail();
|
||||
this.setData({ currentRole: role, tabList: list, selectedIndex: idx, availableRoles: avail });
|
||||
},
|
||||
|
||||
_getAvail() {
|
||||
const token = wx.getStorageSync('token');
|
||||
if (!token) {
|
||||
return [{ key: 'normal', name: '点单老板' }];
|
||||
}
|
||||
const isActive = (key) => Number(wx.getStorageSync(key)) === 1;
|
||||
const arr = [{ key: 'normal', name: '点单老板' }];
|
||||
if (isActive('dashoustatus')) arr.push({ key: 'dashou', name: '打手' });
|
||||
if (isActive('shangjiastatus')) arr.push({ key: 'shangjia', name: '商家' });
|
||||
if (isActive('guanshistatus')) arr.push({ key: 'guanshi', name: '管事' });
|
||||
if (isActive('zuzhangstatus')) arr.push({ key: 'zuzhang', name: '组长' });
|
||||
if (isActive('kaoheguanstatus')) arr.push({ key: 'kaoheguan', name: '考核官' });
|
||||
return arr;
|
||||
},
|
||||
|
||||
toggleRolePicker() {
|
||||
this.setData({ showRolePicker: !this.data.showRolePicker });
|
||||
},
|
||||
|
||||
async selectRole(e) {
|
||||
const newRole = e.currentTarget.dataset.role;
|
||||
if (newRole === this.data.currentRole) {
|
||||
this.setData({ showRolePicker: false });
|
||||
return;
|
||||
}
|
||||
|
||||
this.setData({ showRolePicker: false });
|
||||
|
||||
// 1. 立即更新角色并保存
|
||||
if (app.setCurrentRole) {
|
||||
app.setCurrentRole(newRole);
|
||||
} else {
|
||||
app.globalData.currentRole = newRole;
|
||||
wx.setStorageSync('currentRole', newRole);
|
||||
app.emitEvent('currentRoleChanged', { role: newRole });
|
||||
}
|
||||
|
||||
// 2. 🔥 确定跳转页面:所有角色都去“我的”页面
|
||||
let targetPage = '';
|
||||
if (newRole === 'dashou') {
|
||||
// 打手需要根据金牌状态决定跳转
|
||||
const isJinpai = wx.getStorageSync('isJinpai') === 1;
|
||||
targetPage = isJinpai ? '/pages/jinpaids/jinpaids' : '/pages/dashouduan/dashouduan';
|
||||
} else {
|
||||
// 其他角色直接取 tabList 最后一个页面
|
||||
const cfg = roleCfg[newRole];
|
||||
if (cfg && cfg.tabList.length > 0) {
|
||||
targetPage = '/' + cfg.tabList[cfg.tabList.length - 1].pagePath;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 跳转
|
||||
if (targetPage) {
|
||||
wx.reLaunch({ url: targetPage });
|
||||
}
|
||||
|
||||
// 4. 后台异步切换连接
|
||||
app.switchRoleAndReconnect(newRole).catch(err => {
|
||||
console.error('后台切换连接失败:', err);
|
||||
});
|
||||
},
|
||||
|
||||
switchTab(e) {
|
||||
const path = e.currentTarget.dataset.path;
|
||||
const idx = e.currentTarget.dataset.index;
|
||||
if (idx === this.data.selectedIndex) return;
|
||||
wx.redirectTo({ url: '/' + path });
|
||||
}
|
||||
}
|
||||
});
|
||||
4
_backup_pre_redesign/custom-tab-bar/index.json
Normal file
4
_backup_pre_redesign/custom-tab-bar/index.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
||||
42
_backup_pre_redesign/custom-tab-bar/index.wxml
Normal file
42
_backup_pre_redesign/custom-tab-bar/index.wxml
Normal file
@@ -0,0 +1,42 @@
|
||||
<view class="custom-tab-bar">
|
||||
<view class="bar-row">
|
||||
<!-- 左侧椭圆身份按钮 -->
|
||||
<view class="role-switch-btn" catchtap="toggleRolePicker">
|
||||
<image class="role-switch-icon" src="/images/role-switch.png" mode="aspectFit" />
|
||||
<text class="role-switch-text">切换身份</text>
|
||||
</view>
|
||||
|
||||
<!-- 右侧纯黑闪光胶囊 -->
|
||||
<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">
|
||||
<!-- 🔥 根据页面路径判断是否显示角标,不再写死 index -->
|
||||
<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 wx:if="{{showRolePicker}}" class="picker-mask" catchtap="toggleRolePicker"></view>
|
||||
<view wx:if="{{showRolePicker}}" class="picker-panel">
|
||||
<view class="picker-header">
|
||||
<text class="picker-title">切换身份</text>
|
||||
<text class="picker-close" catchtap="toggleRolePicker">✕</text>
|
||||
</view>
|
||||
<block wx:for="{{availableRoles}}" wx:key="key">
|
||||
<view class="role-option {{currentRole === item.key ? 'active' : ''}}"
|
||||
data-role="{{item.key}}" catchtap="selectRole">
|
||||
<text>{{item.name}}</text>
|
||||
<text wx:if="{{currentRole === item.key}}" class="check-mark">✓</text>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
165
_backup_pre_redesign/custom-tab-bar/index.wxss
Normal file
165
_backup_pre_redesign/custom-tab-bar/index.wxss
Normal file
@@ -0,0 +1,165 @@
|
||||
/* custom-tab-bar/index.wxss - 角标浮于右上角,可超出胶囊 */
|
||||
|
||||
.custom-tab-bar {
|
||||
position: fixed;
|
||||
bottom: 0; left: 0; right: 0;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-end;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.bar-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15rpx;
|
||||
padding: 0 20rpx;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 左侧椭圆身份按钮 */
|
||||
.role-switch-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 110rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 50rpx;
|
||||
background: #000000;
|
||||
border: 1px solid rgba(255,255,255,0.15);
|
||||
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.8);
|
||||
margin-right: 20rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.role-switch-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
margin-bottom: 2rpx;
|
||||
}
|
||||
|
||||
.role-switch-text {
|
||||
font-size: 22rpx;
|
||||
font-weight: 600;
|
||||
color: #00f7ff;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
/* 纯黑闪光胶囊,移除overflow保证角标可见 */
|
||||
.tab-capsule {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
height: 100rpx;
|
||||
background: #000000;
|
||||
border-radius: 50rpx;
|
||||
border: 1px solid rgba(255,255,255,0.15);
|
||||
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.8);
|
||||
padding: 0 20rpx;
|
||||
position: relative;
|
||||
}
|
||||
.tab-capsule::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0; left: -100%;
|
||||
width: 60%; height: 100%;
|
||||
background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.25) 20%, rgba(255,255,255,0.35) 50%, rgba(255,255,255,0.25) 80%, transparent 100%);
|
||||
animation: shine 3s infinite;
|
||||
z-index: 1;
|
||||
}
|
||||
@keyframes shine {
|
||||
0% { left: -100%; }
|
||||
25% { left: 150%; }
|
||||
100% { left: 150%; }
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
position: relative; /* 角标的定位参考 */
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 70rpx;
|
||||
}
|
||||
.tab-icon {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
margin-bottom: 4rpx;
|
||||
filter: brightness(0) invert(1);
|
||||
opacity: 0.9;
|
||||
}
|
||||
.tab-text {
|
||||
font-size: 20rpx;
|
||||
color: #aaa;
|
||||
z-index: 2;
|
||||
}
|
||||
.active .tab-icon {
|
||||
opacity: 1;
|
||||
filter: brightness(0) invert(1) drop-shadow(0 0 8rpx #00aaff);
|
||||
}
|
||||
.active .tab-text {
|
||||
color: #00aaff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 🔴 角标容器:绝对定位到图标右上角,允许超出 */
|
||||
.badge-container {
|
||||
position: absolute;
|
||||
top: -10rpx; /* 往上提,更接近图标右上角 */
|
||||
right: -15rpx; /* 往右伸,超出tab-item */
|
||||
z-index: 100;
|
||||
pointer-events: none; /* 不阻挡点击 */
|
||||
}
|
||||
.tab-badge {
|
||||
min-width: 36rpx;
|
||||
height: 36rpx;
|
||||
line-height: 36rpx;
|
||||
padding: 0 10rpx;
|
||||
background: #ff3b30;
|
||||
color: #fff;
|
||||
font-size: 22rpx;
|
||||
font-weight: bold;
|
||||
border-radius: 36rpx;
|
||||
text-align: center;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.3);
|
||||
border: 1rpx solid rgba(255,255,255,0.4);
|
||||
}
|
||||
|
||||
/* 弹窗部分保持不变 */
|
||||
.picker-mask {
|
||||
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,0.6);
|
||||
z-index: 10000;
|
||||
}
|
||||
.picker-panel {
|
||||
position: fixed; bottom: 0; left: 0; right: 0;
|
||||
background: #000000;
|
||||
border-radius: 30rpx 30rpx 0 0;
|
||||
z-index: 10001;
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.picker-header {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
padding: 30rpx 40rpx 20rpx;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
.picker-title { font-size: 32rpx; color: #00f7ff; }
|
||||
.picker-close { font-size: 36rpx; color: #aaa; padding: 0 10rpx; }
|
||||
.role-option {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
padding: 28rpx 40rpx;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.05);
|
||||
color: #ccc; font-size: 28rpx;
|
||||
}
|
||||
.role-option.active { color: #00f7ff; }
|
||||
.check-mark { font-size: 32rpx; color: #00f7ff; }
|
||||
|
||||
page {
|
||||
padding-bottom: 100rpx;
|
||||
}
|
||||
Reference in New Issue
Block a user