498 lines
17 KiB
Vue
498 lines
17 KiB
Vue
<template>
|
||
<view class="tabbar">
|
||
<!-- 调试信息:显示当前可见的tabs -->
|
||
<view style="display: none;">
|
||
DEBUG: visibleTabs length: {{ visibleTabs.length }},
|
||
tabs: {{ visibleTabs.map(t => t.text).join(', ') }},
|
||
furnitureTabs length: {{ furnitureTabs.length }},
|
||
furnitureTabs: {{ furnitureTabs.map(t => t.text).join(', ') }},
|
||
roles: {{ roles.join(', ') }}
|
||
</view>
|
||
|
||
<!-- 强制显示所有家具角色的tab -->
|
||
<view
|
||
v-for="item in furnitureTabs"
|
||
:key="item.key"
|
||
class="tabbar-item"
|
||
:class="{ active: currentPath === item.pagePath }"
|
||
@click="switchTab(item)"
|
||
>
|
||
<image class="tabbar-icon" :src="currentPath === item.pagePath ? item.selectedIconPath : item.iconPath" />
|
||
<text class="tabbar-text">{{ item.text }}</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
// 直接在全局作用域添加调试代码,确保能执行
|
||
console.log('CUSTOM TABBAR FILE LOADED - THIS MUST SHOW');
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
roleName: '',
|
||
roles: [],
|
||
currentPath: '',
|
||
allTabs: [
|
||
{
|
||
key: 'furniture_reception',
|
||
text: '接待',
|
||
pagePath: '/pages/furniture_reception/furniture_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: 'ucenter',
|
||
text: '我的',
|
||
pagePath: 'pages/ucenter/ucenter',
|
||
iconPath: '/static/tabbar/me.png',
|
||
selectedIconPath: '/static/tabbar/me_active.png',
|
||
},
|
||
// 已从导航中移除,但保留代码
|
||
// {
|
||
// key: 'champion',
|
||
// text: '销冠',
|
||
// pagePath: '/pages/champion/champion',
|
||
// iconPath: '/static/tabbar/insight.png',
|
||
// selectedIconPath: '/static/tabbar/insight_active.png',
|
||
// },
|
||
// {
|
||
// key: 'workspace',
|
||
// text: '工作台',
|
||
// pagePath: '/pages/workspace/workspace',
|
||
// iconPath: '/static/tabbar/workspace.png',
|
||
// selectedIconPath: '/static/tabbar/workspace_active.png',
|
||
// },
|
||
{
|
||
key: 'ucenter',
|
||
text: '我的',
|
||
pagePath: '/pages/ucenter/ucenter',
|
||
iconPath: '/static/tabbar/me.png',
|
||
selectedIconPath: '/static/tabbar/me_active.png',
|
||
},
|
||
{
|
||
key: 'ai_analysis',
|
||
text: 'AI分析',
|
||
pagePath: '/pages/ai_analysis/ai_analysis',
|
||
iconPath: '/static/tabbar/ai_analysis.png',
|
||
selectedIconPath: '/static/tabbar/ai_analysis_active.png',
|
||
},
|
||
{
|
||
key: 'meeting_summary',
|
||
text: '总结和统计',
|
||
pagePath: '/pages-subpackage/meeting_summary/meeting_summary',
|
||
iconPath: '/static/tabbar/insight.png',
|
||
selectedIconPath: '/static/tabbar/insight_active.png',
|
||
},
|
||
],
|
||
};
|
||
},
|
||
computed: {
|
||
furnitureTabs() {
|
||
// 强制返回所有家具角色的tab,使用与pages.json相同的路径格式
|
||
return [
|
||
{
|
||
key: 'furniture_reception',
|
||
text: '接待',
|
||
pagePath: 'pages/furniture_reception/furniture_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: 'ai_analysis',
|
||
text: 'AI分析',
|
||
pagePath: 'pages/ai_analysis/ai_analysis',
|
||
iconPath: '/static/tabbar/ai_analysis.png',
|
||
selectedIconPath: '/static/tabbar/ai_analysis_active.png',
|
||
},
|
||
{
|
||
key: 'ucenter',
|
||
text: '我的',
|
||
pagePath: 'pages/ucenter/ucenter', // 移除开头的/
|
||
iconPath: '/static/tabbar/me.png',
|
||
selectedIconPath: '/static/tabbar/me_active.png',
|
||
}
|
||
];
|
||
},
|
||
|
||
visibleTabs() {
|
||
console.log('=== CUSTOM TABBAR VISIBLE TABS CALCULATED ===');
|
||
console.log('VISIBLE TABS CALC - THIS MUST SHOW IN H5');
|
||
|
||
// 简化逻辑:直接返回家具相关的tabs,跳过权限检查
|
||
const furnitureKeys = ['furniture_reception', 'furniture_customer', 'ai_analysis', 'ucenter'];
|
||
const result = furnitureKeys.map(key => {
|
||
return this.allTabs.find(tab => tab.key === key);
|
||
}).filter(Boolean);
|
||
|
||
console.log('[TabBar][visibleTabs] 直接返回家具tabs:', result.map(t => t.key), 'texts:', result.map(t => t.text));
|
||
return result;
|
||
},
|
||
},
|
||
created() {
|
||
console.log('=== CUSTOM TABBAR COMPONENT CREATED ===');
|
||
console.log('CUSTOM TABBAR CREATED - THIS MUST SHOW IN H5');
|
||
this.refreshRole();
|
||
this.updateActivePath();
|
||
},
|
||
mounted() {
|
||
console.log('=== CUSTOM TABBAR COMPONENT MOUNTED ===');
|
||
console.log('CUSTOM TABBAR MOUNTED - THIS MUST SHOW IN CONSOLE');
|
||
|
||
// 强制执行,不依赖条件编译
|
||
this.forceH5TabBarSetup();
|
||
|
||
// 监听外部刷新事件(登录后、页面切换时可触发)
|
||
uni.$on('tabbar:refresh', this.handleExternalRefresh);
|
||
// 初始化时同步一次
|
||
this.handleExternalRefresh();
|
||
|
||
// H5平台:额外处理页面刷新后的显示问题
|
||
// #ifdef H5
|
||
// 监听页面显示事件,确保刷新后tabBar正确显示
|
||
uni.on('onShow', () => {
|
||
setTimeout(() => {
|
||
console.log('H5: 页面显示事件触发,刷新tabBar');
|
||
this.handleExternalRefresh();
|
||
this.forceH5TabBarSetup();
|
||
}, 300);
|
||
});
|
||
|
||
// 监听浏览器刷新事件
|
||
if (typeof window !== 'undefined') {
|
||
window.addEventListener('load', () => {
|
||
setTimeout(() => {
|
||
console.log('H5: 页面加载完成,刷新tabBar');
|
||
this.handleExternalRefresh();
|
||
this.forceH5TabBarSetup();
|
||
}, 500);
|
||
});
|
||
}
|
||
// #endif
|
||
},
|
||
beforeDestroy() {
|
||
uni.$off('tabbar:refresh', this.handleExternalRefresh);
|
||
},
|
||
methods: {
|
||
refreshRole() {
|
||
try {
|
||
// 角色来源优先级:单独存储的 roleName > 登录响应里的 roleName
|
||
const storedRole = uni.getStorageSync('backend-role-name') || '';
|
||
const loginResp = uni.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');
|
||
this.roles = roles;
|
||
this.roleName = hasEdu ? 'speaking_training_teacher' : hasStudent ? 'speaking_training_students' : (roles[0] || '');
|
||
|
||
console.log('[TabBar][role] storedRole:', storedRole, 'respRole:', respRole, 'normalized:', roles, 'hasEdu:', hasEdu, 'hasStudent:', hasStudent, 'finalRoleName:', this.roleName);
|
||
// #ifdef H5
|
||
// H5 平台自定义 tabBar 无法生效,仍使用原生 DOM;这里额外做一次 DOM 显隐
|
||
console.log('[TabBar][H5] refreshRole completed, will call toggleNativeTabs');
|
||
// 注意:不在refreshRole中直接调用,而是在setupH5TabBar中处理
|
||
// #endif
|
||
} catch (e) {
|
||
console.warn('读取角色失败:', e);
|
||
}
|
||
},
|
||
updateActivePath() {
|
||
const pages = getCurrentPages();
|
||
const currentPage = pages[pages.length - 1];
|
||
// currentPage.route 不带前导斜杠
|
||
this.currentPath = currentPage && currentPage.route ? `/${currentPage.route}` : '';
|
||
},
|
||
switchTab(item) {
|
||
// 切换前刷新一次角色,防止角色变更后仍看到教务/作业等
|
||
this.refreshRole();
|
||
const allowedKeys = this.getAllowedKeys();
|
||
if (!allowedKeys.includes(item.key)) {
|
||
uni.showToast({ title: '当前账号无此模块权限', icon: 'none', duration: 1200 });
|
||
console.warn('[TabBar][block-switch]', 'key:', item.key, 'path:', item.pagePath, 'allowed:', allowedKeys, 'roles:', this.roles);
|
||
return;
|
||
}
|
||
if (this.currentPath === item.pagePath) return;
|
||
|
||
// uni.switchTab 需要相对路径(去掉前导斜杠)
|
||
const url = item.pagePath.startsWith('/') ? item.pagePath.substring(1) : item.pagePath;
|
||
|
||
uni.switchTab({
|
||
url: url,
|
||
success: () => {
|
||
console.log('[TabBar][switchTab] success:', url);
|
||
this.updateActivePath();
|
||
},
|
||
fail: (err) => {
|
||
console.error('[TabBar][switchTab] fail:', err, 'url:', url);
|
||
// 如果 switchTab 失败,尝试使用 reLaunch(适用于 tabBar 页面)
|
||
uni.reLaunch({
|
||
url: url,
|
||
fail: (err2) => {
|
||
console.error('[TabBar][reLaunch] fail:', err2, 'url:', url);
|
||
uni.showToast({
|
||
title: '页面跳转失败,请检查页面配置',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
},
|
||
success: () => {
|
||
this.updateActivePath();
|
||
}
|
||
});
|
||
},
|
||
complete: () => {
|
||
// 延迟更新,确保页面切换完成
|
||
setTimeout(() => {
|
||
this.updateActivePath();
|
||
}, 100);
|
||
},
|
||
});
|
||
},
|
||
handleExternalRefresh() {
|
||
// 每次收到事件时重新读取角色并刷新选中态
|
||
this.refreshRole();
|
||
this.updateActivePath();
|
||
|
||
console.log('[TabBar][refresh] currentPath:', this.currentPath, 'roleName:', this.roleName, 'visibleKeys:', this.visibleTabs.map(t => t.key), 'roles:', this.roles);
|
||
},
|
||
getAllowedKeys() {
|
||
console.log('=== CUSTOM TABBAR GET ALLOWED KEYS CALLED ===');
|
||
console.log('GET ALLOWED KEYS - 简化版本,总是返回家具相关tabs');
|
||
|
||
// 简化逻辑:总是返回家具相关的tabs
|
||
const furnitureTabs = ['furniture_reception', 'furniture_customer', 'ai_analysis', 'ucenter'];
|
||
console.log('[TabBar][allowed] 简化逻辑 ->', furnitureTabs);
|
||
return furnitureTabs;
|
||
},
|
||
// #ifdef H5
|
||
toggleNativeTabs() {
|
||
// H5 平台自定义 tabbar 不生效,直接根据角色显隐原生 tabbar 项
|
||
// 当前位置顺序(根据 pages.json 中的 tabBar.list 顺序):接待、客户、AI分析、我的、团队、总结和统计…
|
||
const ORDER = ['furniture_reception', 'furniture_customer', 'ai_analysis', 'ucenter', 'reception', 'customer', 'team', 'meeting_summary', 'common_begin_reception'];
|
||
const allowed = this.getAllowedKeys();
|
||
// 限制重试次数,防止渲染时机未就绪
|
||
this._h5TabRetries = (this._h5TabRetries || 0) + 1;
|
||
try {
|
||
const items = document.querySelectorAll('.uni-tabbar .uni-tabbar__item');
|
||
if (!items || !items.length) {
|
||
// 原生 tabbar 可能尚未渲染,稍后重试一次
|
||
if (this._h5TabRetries <= 10) {
|
||
setTimeout(() => this.toggleNativeTabs(), 150);
|
||
} else {
|
||
console.warn('[TabBar][H5 DOM] retry limit reached, allowed:', allowed.join(','));
|
||
}
|
||
return;
|
||
}
|
||
ORDER.forEach((key, idx) => {
|
||
const node = items[idx];
|
||
if (!node) return;
|
||
node.style.display = allowed.includes(key) ? '' : 'none';
|
||
});
|
||
console.log('[TabBar][H5 DOM] allowed:', allowed.join(','), 'retries:', this._h5TabRetries);
|
||
this._h5TabRetries = 0;
|
||
} catch (e) {
|
||
console.warn('[TabBar][H5 DOM] toggle tabs failed:', e);
|
||
}
|
||
},
|
||
isInTabBar(pagePath) {
|
||
// 检查页面路径是否在原生tabBar配置中
|
||
const tabBarList = [
|
||
'pages/furniture_reception/furniture_reception',
|
||
'pages-subpackage/furniture_customer/furniture_customer',
|
||
'pages/ai_analysis/ai_analysis',
|
||
'pages/ucenter/ucenter'
|
||
];
|
||
const normalizedPath = pagePath.startsWith('/') ? pagePath.substring(1) : pagePath;
|
||
const result = tabBarList.includes(normalizedPath);
|
||
console.log('[TabBar][isInTabBar]', { pagePath, normalizedPath, tabBarList, result });
|
||
return result;
|
||
},
|
||
forceH5TabBarSetup() {
|
||
console.log('=== FORCE H5 TABBAR SETUP CALLED ===');
|
||
|
||
// 立即执行toggleNativeTabs
|
||
setTimeout(() => {
|
||
console.log('FORCE: Executing toggleNativeTabs immediately');
|
||
this.forceToggleNativeTabs();
|
||
}, 100);
|
||
|
||
// 多次延迟执行
|
||
[500, 1000, 2000, 3000, 5000].forEach((delay, index) => {
|
||
setTimeout(() => {
|
||
console.log(`FORCE: Delayed execution ${index + 1} after ${delay}ms`);
|
||
this.forceToggleNativeTabs();
|
||
}, delay);
|
||
});
|
||
},
|
||
|
||
forceToggleNativeTabs() {
|
||
console.log('=== FORCE TOGGLE NATIVE TABS START ===');
|
||
|
||
try {
|
||
// 获取允许的tab
|
||
const allowed = this.getAllowedKeys();
|
||
console.log('FORCE: Current roles:', this.roles);
|
||
console.log('FORCE: Allowed keys:', allowed);
|
||
|
||
// 简化DOM查找 - 直接查找所有可能的tabBar元素
|
||
const allElements = document.querySelectorAll('*');
|
||
const tabElements = Array.from(allElements).filter(el =>
|
||
el.textContent && (
|
||
el.textContent.includes('接待') ||
|
||
el.textContent.includes('客户') ||
|
||
el.textContent.includes('AI分析') ||
|
||
el.textContent.includes('我的')
|
||
)
|
||
);
|
||
|
||
console.log('FORCE: Found tab elements with text:', tabElements.length);
|
||
tabElements.forEach((el, idx) => {
|
||
console.log(`FORCE: Element ${idx}:`, {
|
||
tag: el.tagName,
|
||
class: el.className,
|
||
text: el.textContent,
|
||
currentDisplay: el.style.display,
|
||
computedDisplay: window.getComputedStyle(el).display
|
||
});
|
||
});
|
||
|
||
if (tabElements.length === 0) {
|
||
console.warn('FORCE: No tab elements found! Looking for containers...');
|
||
|
||
// 查找可能的容器
|
||
const containers = document.querySelectorAll('div, nav, section');
|
||
const possibleContainers = Array.from(containers).filter(el =>
|
||
el.children && el.children.length >= 4
|
||
);
|
||
|
||
console.log('FORCE: Possible containers:', possibleContainers.length);
|
||
possibleContainers.forEach((container, idx) => {
|
||
console.log(`FORCE: Container ${idx}:`, {
|
||
tag: container.tagName,
|
||
class: container.className,
|
||
children: container.children.length
|
||
});
|
||
});
|
||
|
||
return;
|
||
}
|
||
|
||
// 按文本内容排序:接待、客户、AI分析、我的
|
||
const sortedElements = tabElements.sort((a, b) => {
|
||
const order = ['接待', '客户', 'AI分析', '我的'];
|
||
const aIndex = order.findIndex(text => a.textContent.includes(text));
|
||
const bIndex = order.findIndex(text => b.textContent.includes(text));
|
||
return aIndex - bIndex;
|
||
});
|
||
|
||
console.log('FORCE: Processing sorted elements...');
|
||
|
||
// 处理每个元素
|
||
sortedElements.forEach((element, index) => {
|
||
const keyMap = ['furniture_reception', 'furniture_customer', 'ai_analysis', 'ucenter'];
|
||
const key = keyMap[index];
|
||
const shouldShow = allowed.includes(key);
|
||
|
||
console.log(`FORCE: Processing ${key} (${element.textContent}): shouldShow=${shouldShow}`);
|
||
|
||
if (shouldShow) {
|
||
element.style.display = '';
|
||
element.style.visibility = 'visible';
|
||
// 确保父元素也可见
|
||
let parent = element.parentElement;
|
||
while (parent) {
|
||
parent.style.display = '';
|
||
parent.style.visibility = 'visible';
|
||
parent = parent.parentElement;
|
||
}
|
||
console.log(`FORCE: SHOWED ${key}`);
|
||
} else {
|
||
element.style.display = 'none';
|
||
element.style.visibility = 'hidden';
|
||
console.log(`FORCE: HID ${key}`);
|
||
}
|
||
});
|
||
|
||
console.log('=== FORCE TOGGLE NATIVE TABS END ===');
|
||
|
||
} catch (e) {
|
||
console.error('FORCE: Error in forceToggleNativeTabs:', e);
|
||
}
|
||
},
|
||
|
||
// #endif
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style>
|
||
.tabbar {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
height: 52px;
|
||
background: #ffffff;
|
||
border-top: 1px solid #e5e7eb;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-around;
|
||
padding-bottom: env(safe-area-inset-bottom);
|
||
}
|
||
|
||
.tabbar-item {
|
||
flex: 1;
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #7a7e83;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.tabbar-item.active {
|
||
color: #007aff;
|
||
}
|
||
|
||
.tabbar-icon {
|
||
width: 24px;
|
||
height: 24px;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.tabbar-text {
|
||
line-height: 16px;
|
||
}
|
||
</style>
|
||
|