Files
smartDriveEEUniApp/custom-tab-bar/index.js
2026-04-17 09:41:37 +08:00

555 lines
21 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Component({
data: {
roleName: '',
roles: [],
currentPath: '',
visibleTabs: [],
allTabs: [
{
key: 'reception',
text: '接待',
pagePath: 'pages-subpackage/reception/reception', // 使用与 pages.json 一致的格式(不带前导斜杠)
iconPath: '/static/tabbar/reception.png',
selectedIconPath: '/static/tabbar/reception_active.png',
},
{
key: 'customer',
text: '客户',
pagePath: 'pages-subpackage/customer/customer',
iconPath: '/static/tabbar/customer.png',
selectedIconPath: '/static/tabbar/customer_active.png',
},
{
key: 'team',
text: '团队',
pagePath: 'pages/team/team',
iconPath: '/static/tabbar/team.png',
selectedIconPath: '/static/tabbar/team_active.png',
},
{
key: 'ucenter',
text: '我的',
pagePath: 'pages/ucenter/ucenter',
iconPath: '/static/tabbar/me.png',
selectedIconPath: '/static/tabbar/me_active.png',
},
{
key: 'meeting_summary',
text: '总结和统计',
pagePath: 'pages-subpackage/meeting_summary/meeting_summary',
iconPath: '/static/tabbar/insight.png',
selectedIconPath: '/static/tabbar/insight_active.png',
},
{
key: 'common_begin_reception',
text: '开始接待',
pagePath: 'pages/furniture_reception/common_begin_reception',
iconPath: '/static/tabbar/reception.png',
selectedIconPath: '/static/tabbar/reception_active.png',
},
{
key: 'furniture_customer',
text: '客户',
pagePath: 'pages-subpackage/furniture_customer/furniture_customer',
iconPath: '/static/tabbar/customer.png',
selectedIconPath: '/static/tabbar/customer_active.png',
},
{
key: 'workbench',
text: '工作台',
pagePath: 'pages/workbench/workbench',
iconPath: '/static/tabbar/team.png',
selectedIconPath: '/static/tabbar/team_active.png',
},
{
key: 'furniture_top_sales',
text: '销冠',
pagePath: 'pages/furniture_top_sales/furniture_top_sales',
iconPath: '/static/tabbar/insight.png',
selectedIconPath: '/static/tabbar/insight_active.png',
},
],
},
lifetimes: {
attached() {
console.log('[TabBar][lifetimes] attached, 组件已挂载');
this.refreshRole();
this.updateActivePath();
// 监听全局事件用于登录后刷新tabBar
// 注意微信小程序不支持uni.$on需要通过其他方式实现
// 这里通过监听storage变化来实现
try {
// 使用定时器定期检查角色变化(登录后)
this._refreshTimer = setInterval(() => {
const storedRole = wx.getStorageSync('backend-role-name') || '';
if (storedRole !== this.data.roleName) {
this.refreshRole();
this.updateActivePath();
}
}, 1000);
} catch (e) {
console.warn('设置tabBar刷新监听失败:', e);
}
// 记录组件挂载时的页面信息
try {
const pages = getCurrentPages();
console.log('[TabBar][lifetimes] 组件挂载时的页面栈:', {
pagesLength: pages.length,
currentRoute: pages.length > 0 ? pages[pages.length - 1].route : 'unknown',
allRoutes: pages.map(p => p.route)
});
} catch (e) {
console.warn('[TabBar][lifetimes] 获取页面栈失败:', e);
}
},
detached() {
console.log('[TabBar][lifetimes] detached, 组件已卸载');
// 清理定时器
if (this._refreshTimer) {
clearInterval(this._refreshTimer);
this._refreshTimer = null;
}
},
},
pageLifetimes: {
show() {
this.refreshRole();
this.updateActivePath();
},
},
methods: {
refreshRole() {
try {
const storedRole = wx.getStorageSync('backend-role-name') || '';
const loginResp = wx.getStorageSync('backend-login-response') || {};
const respRole = loginResp.roleName || '';
const normalize = (val) =>
typeof val === 'string'
? val
.split(',')
.map((s) => s.trim().toLowerCase())
.filter(Boolean)
: [];
const roles = [
...normalize(storedRole),
...normalize(respRole),
];
const hasEdu = roles.includes('speaking_training_teacher');
const hasStudent = roles.includes('speaking_training_students');
const roleName = hasEdu ? 'speaking_training_teacher' : hasStudent ? 'speaking_training_students' : (roles[0] || '');
this.setData({
roles: roles,
roleName: roleName,
});
// 更新可见的tabs
this.updateVisibleTabs();
console.log('[TabBar][role] storedRole:', storedRole, 'respRole:', respRole, 'normalized:', roles);
} catch (e) {
console.warn('读取角色失败:', e);
}
},
updateActivePath() {
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
// currentPage.route 格式为 pages/xxx/xxx不带前导斜杠
// 为了匹配 allTabs 中的 pagePath我们也使用不带前导斜杠的格式
const currentPath = currentPage && currentPage.route ? currentPage.route : '';
this.setData({
currentPath: currentPath,
});
// 更新可见的tabs
this.updateVisibleTabs();
},
updateVisibleTabs() {
const allowedKeys = this.getAllowedKeys();
console.log('[TabBar][visibleTabs] getAllowedKeys returned:', JSON.stringify(allowedKeys));
const tabMap = {};
this.data.allTabs.forEach(tab => {
tabMap[tab.key] = tab;
});
const otherKeys = [];
let hasUcenter = false;
allowedKeys.forEach(key => {
if (key !== 'ucenter') {
otherKeys.push(key);
} else {
hasUcenter = true;
}
});
const finalKeys = hasUcenter ? [...otherKeys, 'ucenter'] : otherKeys;
const result = finalKeys
.filter(key => tabMap[key])
.map(key => tabMap[key]);
console.log('[TabBar][visibleTabs] final result keys:', result.map(t => t.key));
this.setData({
visibleTabs: result,
});
},
// 外部调用刷新tabBar登录成功后调用
refresh() {
this.refreshRole();
this.updateActivePath();
console.log('[TabBar][refresh] currentPath:', this.data.currentPath, 'roleName:', this.data.roleName, 'visibleKeys:', this.data.visibleTabs.map(t => t.key), 'roles:', this.data.roles);
},
switchTab(e) {
// 只从 dataset 获取 key避免路径传递问题
const dataset = e.currentTarget.dataset || {};
const key = dataset.key;
if (!key) {
console.error('[TabBar][switchTab] 缺少必要参数 key:', dataset);
return;
}
// 根据key找到对应的item
const item = this.data.allTabs.find(tab => tab.key === key);
if (!item) {
console.error('[TabBar][switchTab] 未找到对应的tab项:', key);
return;
}
// 直接使用 item 中的 pagePath确保路径正确
// 重要:创建一个全新的字符串,避免任何引用或编码问题
let pagePath = String(item.pagePath);
// 移除所有前导和尾部空白字符、斜杠
pagePath = pagePath.trim().replace(/^\/+/, '').replace(/\/+$/, '');
console.log('[TabBar][switchTab] 准备跳转:', {
key: key,
pagePath: pagePath,
currentPath: this.data.currentPath,
itemPagePath: item.pagePath
});
this.refreshRole();
const allowedKeys = this.getAllowedKeys();
if (!allowedKeys.includes(key)) {
wx.showToast({
title: '当前账号无此模块权限',
icon: 'none',
duration: 1200
});
console.warn('[TabBar][block-switch]', 'key:', key, 'path:', pagePath, 'allowed:', allowedKeys);
return;
}
// 检查是否已经是当前页面
const normalizedCurrent = (this.data.currentPath || '').trim().replace(/^\/+/, '').replace(/\/+$/, '');
const normalizedTarget = pagePath.trim().replace(/^\/+/, '').replace(/\/+$/, '');
if (normalizedCurrent === normalizedTarget) {
console.log('[TabBar][switchTab] 已经是当前页面,跳过跳转');
return;
}
// switchTab 的 url 必须与 pages.json 中 tabBar.list 的 pagePath 完全一致
// 格式pages/xxx/xxx不带前导斜杠
// 重要:使用字符串字面量重新构建路径,确保是全新的字符串
// 根据 key 直接构建路径,避免使用 item.pagePath 可能存在的引用问题
const pathMap = {
'reception': 'pages-subpackage/reception/reception',
'customer': 'pages-subpackage/customer/customer',
'team': 'pages/team/team',
'ucenter': 'pages/ucenter/ucenter',
'meeting_summary': 'pages-subpackage/meeting_summary/meeting_summary',
'common_begin_reception': 'pages/furniture_reception/common_begin_reception',
'workbench': 'pages/workbench/workbench',
'furniture_customer': 'pages-subpackage/furniture_customer/furniture_customer',
'furniture_top_sales': 'pages/furniture_top_sales/furniture_top_sales'
};
// 直接从 pathMap 获取路径,确保路径是全新的字符串字面量
let switchTabUrl = pathMap[key];
if (!switchTabUrl) {
console.error('[TabBar][switchTab] 未找到对应的路径映射:', key);
wx.showToast({
title: '页面路径不存在',
icon: 'none',
duration: 2000
});
return;
}
// 获取当前页面信息,用于调试
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
const currentRoute = currentPage ? currentPage.route : 'unknown';
const currentOptions = currentPage ? currentPage.options : {};
// 验证路径格式
if (!switchTabUrl.match(/^pages\/[^\/]+\/[^\/]+$/)) {
console.error('[TabBar][switchTab] 路径格式错误:', switchTabUrl);
return;
}
// 创建全新的字符串,确保没有任何引用或隐藏字符
// 使用 JSON 序列化再反序列化,确保是全新的字符串
switchTabUrl = JSON.parse(JSON.stringify(switchTabUrl));
// 再次验证
if (!switchTabUrl.match(/^pages\/[^\/]+\/[^\/]+$/)) {
console.error('[TabBar][switchTab] JSON处理后路径格式错误:', switchTabUrl);
return;
}
// 详细日志:记录所有相关信息
console.log('[TabBar][switchTab] ========== 详细调试信息 ==========');
console.log('[TabBar][switchTab] 1. 基本信息:', {
key: key,
switchTabUrl: switchTabUrl,
switchTabUrlType: typeof switchTabUrl,
switchTabUrlLength: switchTabUrl.length,
switchTabUrlCharCodes: switchTabUrl.split('').map(c => c.charCodeAt(0)),
switchTabUrlJSON: JSON.stringify(switchTabUrl)
});
console.log('[TabBar][switchTab] 2. 当前页面信息:', {
currentPath: this.data.currentPath,
currentRoute: currentRoute,
currentOptions: currentOptions,
pagesLength: pages.length,
allRoutes: pages.map(p => p.route)
});
console.log('[TabBar][switchTab] 3. 路径对比:', {
switchTabUrl: switchTabUrl,
itemPagePath: item.pagePath,
pathMapValue: pathMap[key],
areEqual: switchTabUrl === pathMap[key],
startsWithPages: switchTabUrl.startsWith('pages/'),
endsWithSlash: switchTabUrl.endsWith('/')
});
console.log('[TabBar][switchTab] 4. 准备调用 wx.switchTab');
console.log('[TabBar][switchTab] ====================================');
// 尝试使用 getApp() 获取全局配置,检查是否有路径配置
try {
const app = getApp({ allowDefault: true });
console.log('[TabBar][switchTab] 5. App全局信息:', {
hasApp: !!app,
globalData: app && app.globalData ? Object.keys(app.globalData) : []
});
} catch (e) {
console.warn('[TabBar][switchTab] 获取App实例失败:', e);
}
// 尝试获取 tabBar 配置
try {
const tabBarConfig = wx.getTabBar ? wx.getTabBar() : null;
console.log('[TabBar][switchTab] 6. TabBar配置:', {
hasTabBarConfig: !!tabBarConfig,
tabBarType: typeof tabBarConfig
});
} catch (e) {
console.warn('[TabBar][switchTab] 获取TabBar配置失败:', e);
}
// 根本解决方案:根据微信小程序文档和错误分析
// 问题:在自定义 tabBar 组件中直接调用 wx.switchTab 时,微信小程序会错误地将当前页面路径拼接
// 解决方案:通过页面实例调用,或者使用事件机制通知页面调用
// 方法:通过页面实例的 selectComponent 或直接调用页面方法
// 但更好的方法是:使用 wx.navigateTo 先跳转到一个中间页面,然后立即 switchTab
// 或者:直接使用 wx.reLaunch虽然文档说 tabBar 不支持,但实际可以)
console.log('[TabBar][switchTab] ========== 尝试解决路径拼接问题 ==========');
console.log('[TabBar][switchTab] 目标路径:', switchTabUrl);
console.log('[TabBar][switchTab] 当前页面:', this.data.currentPath);
// 关键发现:根据错误信息,微信小程序会将当前页面路径错误拼接
// 解决方案:使用 wx.reLaunch 替代 wx.switchTab
// reLaunch 会关闭所有页面,然后打开指定页面,不会进行路径拼接
console.log('[TabBar][switchTab] 使用 wx.reLaunch 替代 wx.switchTab避免路径拼接');
wx.reLaunch({
url: '/' + switchTabUrl, // reLaunch 需要带前导斜杠
success: () => {
console.log('[TabBar][switchTab] reLaunch 成功:', '/' + switchTabUrl);
setTimeout(() => {
this.updateActivePath();
}, 100);
},
fail: (err) => {
console.error('[TabBar][switchTab] reLaunch 失败:', err);
// 如果 reLaunch 失败,尝试使用 switchTab带前导斜杠
console.log('[TabBar][switchTab] reLaunch 失败,尝试 switchTab带前导斜杠');
wx.switchTab({
url: '/' + switchTabUrl,
success: () => {
console.log('[TabBar][switchTab] switchTab带前导斜杠成功');
setTimeout(() => {
this.updateActivePath();
}, 100);
},
fail: (err2) => {
console.error('[TabBar][switchTab] switchTab带前导斜杠也失败:', err2);
// 最后尝试:不带前导斜杠的 switchTab
console.log('[TabBar][switchTab] 最后尝试switchTab不带前导斜杠');
this.callWxSwitchTab(switchTabUrl, key, pagePath);
}
});
}
});
},
// 单独的方法调用 wx.switchTab便于调试和错误处理
callWxSwitchTab(switchTabUrl, key, pagePath) {
console.log('[TabBar][callWxSwitchTab] 调用 wx.switchTab:', {
url: switchTabUrl,
key: key,
urlType: typeof switchTabUrl,
urlLength: switchTabUrl.length
});
wx.switchTab({
url: switchTabUrl,
success: () => {
console.log('[TabBar][callWxSwitchTab] success:', switchTabUrl);
setTimeout(() => {
this.updateActivePath();
}, 100);
},
fail: (err) => {
console.error('[TabBar][callWxSwitchTab] ========== 失败详情 ==========');
console.error('[TabBar][callWxSwitchTab] 错误对象:', err);
console.error('[TabBar][callWxSwitchTab] 传递的url:', {
url: switchTabUrl,
urlType: typeof switchTabUrl,
urlLength: switchTabUrl.length,
urlCharCodes: switchTabUrl.split('').map(c => c.charCodeAt(0)),
urlJSON: JSON.stringify(switchTabUrl),
urlString: String(switchTabUrl),
urlSlice: switchTabUrl.slice(0),
urlSubstring: switchTabUrl.substring(0)
});
console.error('[TabBar][callWxSwitchTab] 错误消息:', err.errMsg);
console.error('[TabBar][callWxSwitchTab] 当前页面:', {
currentPath: this.data.currentPath,
currentRoute: getCurrentPages().length > 0 ? getCurrentPages()[getCurrentPages().length - 1].route : 'unknown',
allRoutes: getCurrentPages().map(p => p.route)
});
// 解析错误消息中的路径
const errorPathMatch = err.errMsg ? err.errMsg.match(/page\s+"([^"]+)"/) : null;
const actualSearchedPath = errorPathMatch ? errorPathMatch[1] : null;
console.error('[TabBar][callWxSwitchTab] 路径分析:', {
passedUrl: switchTabUrl,
errorPathInMsg: actualSearchedPath,
isPathConcatenated: actualSearchedPath && (actualSearchedPath.includes('pages/reception/pages/') || actualSearchedPath.includes('pages-subpackage/reception/pages/')),
expectedPath: switchTabUrl,
actualSearchedPath: actualSearchedPath,
pathDifference: actualSearchedPath ? actualSearchedPath.replace(switchTabUrl, '') : null,
currentPagePrefix: this.data.currentPath ? this.data.currentPath.split('/').slice(0, -1).join('/') : null
});
// 检查是否是路径拼接问题
if (actualSearchedPath && (actualSearchedPath.includes('pages/reception/pages/') || actualSearchedPath.includes('pages-subpackage/reception/pages/'))) {
console.error('[TabBar][callWxSwitchTab] 确认是路径拼接问题!');
console.error('[TabBar][callWxSwitchTab] 当前页面路径被错误地拼接到了目标路径前面');
console.error('[TabBar][callWxSwitchTab] 这可能是因为微信小程序在处理自定义 tabBar 的 switchTab 时的 bug');
}
console.error('[TabBar][callWxSwitchTab] ====================================');
// 如果 switchTab 失败,尝试检查是否是路径问题
// 注意:不能使用 reLaunch 替代 switchTab因为 reLaunch 不支持 tabBar 页面
wx.showToast({
title: '页面跳转失败,请检查页面配置',
icon: 'none',
duration: 2000
});
},
complete: () => {
// 延迟更新,确保页面切换完成
setTimeout(() => {
this.updateActivePath();
}, 200);
},
});
},
getAllowedKeys() {
const roles = this.data.roles || [];
const isTeacher = roles.includes('speaking_training_teacher');
const isStudent = roles.includes('speaking_training_students');
const isAdmin = roles.includes('admin');
const isMeetingAdmin = roles.includes('meeting_admin');
const isAdminFurniture = roles.includes('admin_furniture');
const meetingTabKeys = ['meeting_summary'];
const removedTabKeys = ['champion', 'workspace'];
const hasFurnitureRole = roles.includes('furniture');
if (isAdminFurniture || hasFurnitureRole) {
const furnitureTabs = ['workbench', 'ucenter'];
console.log('[TabBar][allowed] furniture/admin_furniture role ->', furnitureTabs);
return furnitureTabs;
}
if (isMeetingAdmin) {
const meetingAdminTabs = [...meetingTabKeys, 'ucenter'];
console.log('[TabBar][allowed] meeting_admin ->', meetingAdminTabs);
return meetingAdminTabs;
}
if (isAdmin) {
const all = this.data.allTabs
.map((t) => t.key)
.filter((key) => !meetingTabKeys.includes(key) && !['furniture_reception', 'common_begin_reception', 'furniture_customer', 'ai_qa', 'ai_analysis', 'furniture_top_sales', 'reception', 'customer', ...removedTabKeys].includes(key));
console.log('[TabBar][allowed] admin ->', all);
return all;
}
if (isTeacher && !isAdmin) {
const teacherTabs = ['ucenter'];
console.log('[TabBar][allowed] teacher ->', teacherTabs);
return teacherTabs;
}
if (isStudent && !isAdmin) {
const studentTabs = ['ucenter'];
console.log('[TabBar][allowed] student ->', studentTabs);
return studentTabs;
}
const otherTabs = this.data.allTabs
.map((t) => t.key)
.filter((key) => !['homework', 'edu', 'voice', 'expression', 'furniture_reception', 'common_begin_reception', 'furniture_customer', 'ai_qa', 'ai_analysis', 'furniture_top_sales', 'reception', 'customer', ...meetingTabKeys, ...removedTabKeys].includes(key));
if (!otherTabs.includes('ucenter')) {
otherTabs.push('ucenter');
}
console.log('[TabBar][allowed] other ->', otherTabs);
return otherTabs;
},
},
observers: {
'roles, currentPath': function() {
this.updateVisibleTabs();
},
},
});