From 3b169467ac174d97022df7ef8811e78c563e8678 Mon Sep 17 00:00:00 2001 From: zhonghua1 Date: Sun, 11 Jan 2026 12:11:01 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E5=89=8D=E7=AB=AF=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=9E=B6=E6=9E=84=EF=BC=8C=20=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E6=88=91=E7=9A=84=E5=86=85=E5=AE=B9=E5=8C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.vue | 29 +- custom-tab-bar/index.vue | 91 ++- pages.json | 31 +- .../components/DeviceTab.vue | 458 ++-------------- .../speaking_training_edu.vue | 22 +- pages/ucenter/components/DeviceBindPopup.vue | 187 +++++++ pages/ucenter/components/DeviceListPopup.vue | 519 ++++++++++++++++++ pages/ucenter/ucenter.vue | 171 +++++- 8 files changed, 989 insertions(+), 519 deletions(-) create mode 100644 pages/ucenter/components/DeviceBindPopup.vue create mode 100644 pages/ucenter/components/DeviceListPopup.vue diff --git a/App.vue b/App.vue index b3681a8..74adac4 100644 --- a/App.vue +++ b/App.vue @@ -98,6 +98,10 @@ const isStudent = roles.includes('speaking_training_students'); const isAdmin = roles.includes('admin'); const isMeetingAdmin = roles.includes('meeting_admin'); + const isAdminFurniture = roles.includes('admin_furniture'); + + // 检查是否有任何角色名等于 "furniture" 字符串(精确匹配) + const hasFurnitureRole = roles.includes('furniture'); // 会议相关的tab标签 const meetingTabLabels = ['总结和统计']; @@ -107,23 +111,36 @@ const otherHidden = ['作业', '教务', '语音', '表达']; // tabBar 顺序(根据 pages.json 中的 tabBar.list 顺序) - // 0: 接待, 1: 客户, 2: 团队, 3: 销冠, 4: 工作台, 5: 我的(ucenter) - // 6: 总结和统计 - const meetingTabIndices = [6]; // 会议相关tab的索引位置 + // 0: 接待, 1: 客户, 2: 团队 + // 3: 总结和统计, 4: 接待(furniture_reception), 5: 开始接待(common_begin_reception), 6: 客户(furniture_customer), 7: 销冠(furniture_top_sales), 8: 我的(ucenter) + const meetingTabIndices = [3]; // 会议相关tab的索引位置 + const furnitureTabIndices = [4, 6, 7]; // 家具相关tab的索引位置(接待、客户、销冠) + const ucenterIndex = 8; // "我的"(ucenter)的索引位置 const shouldShow = (label, index) => { - // 任何角色都应该能看到"我的"(ucenter) + // 任何角色都应该能看到"我的"(ucenter,索引8) if (label === '我的') { return true; } + // admin_furniture 和 furniture 角色:显示 furniture_reception, furniture_customer, furniture_top_sales 和 ucenter + if (isAdminFurniture || hasFurnitureRole) { + // 显示家具相关的tab(索引4, 6, 7)和"我的"(索引8) + return furnitureTabIndices.includes(index) || index === ucenterIndex; + } + + // 隐藏"接待"和"客户"这两个tab(索引0和1) + if (label === '接待' || label === '客户') { + return false; + } + // meeting_admin 角色:显示会议相关的tab和"我的" if (isMeetingAdmin) { return meetingTabIndices.includes(index); } - // 非 meeting_admin 角色:隐藏所有会议相关的tab - if (meetingTabIndices.includes(index)) { + // 非 meeting_admin 角色:隐藏所有会议相关的tab和家具相关的tab + if (meetingTabIndices.includes(index) || furnitureTabIndices.includes(index)) { return false; } diff --git a/custom-tab-bar/index.vue b/custom-tab-bar/index.vue index 17864d3..3b1b281 100644 --- a/custom-tab-bar/index.vue +++ b/custom-tab-bar/index.vue @@ -42,20 +42,21 @@ export default { iconPath: '/static/tabbar/team.png', selectedIconPath: '/static/tabbar/team_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: '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: '我的', @@ -77,6 +78,13 @@ export default { iconPath: '/static/tabbar/reception.png', selectedIconPath: '/static/tabbar/reception_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: '客户', @@ -97,7 +105,37 @@ export default { computed: { visibleTabs() { const allowedKeys = this.getAllowedKeys(); - return this.allTabs.filter((t) => allowedKeys.includes(t.key)); + console.log('[TabBar][visibleTabs] STEP1 - getAllowedKeys returned:', JSON.stringify(allowedKeys)); + + // 创建 key 到 tab 的映射 + const tabMap = {}; + this.allTabs.forEach(tab => { + tabMap[tab.key] = tab; + }); + + // 强制将 ucenter 移到最后一个位置:先收集所有非 ucenter 的 keys,然后添加 ucenter + const otherKeys = []; + let hasUcenter = false; + allowedKeys.forEach(key => { + if (key !== 'ucenter') { + otherKeys.push(key); + } else { + hasUcenter = true; + } + }); + console.log('[TabBar][visibleTabs] STEP2 - after filtering, otherKeys:', JSON.stringify(otherKeys), 'hasUcenter:', hasUcenter); + + // 如果有 ucenter,将其添加到最后一个位置 + const finalKeys = hasUcenter ? [...otherKeys, 'ucenter'] : otherKeys; + console.log('[TabBar][visibleTabs] STEP3 - finalKeys after moving ucenter to end:', JSON.stringify(finalKeys)); + + // 按照排序后的 keys 顺序返回 tabs + const result = finalKeys + .filter(key => tabMap[key]) + .map(key => tabMap[key]); + + console.log('[TabBar][visibleTabs] STEP4 - final result keys:', result.map(t => t.key), 'texts:', result.map(t => t.text)); + return result; }, }, created() { @@ -219,11 +257,16 @@ export default { // 会议相关的tab key列表 const meetingTabKeys = ['meeting_summary']; + // 已从导航中移除的tab key列表(代码保留但不显示) + const removedTabKeys = ['champion', 'workspace']; - // admin_furniture 角色:只显示家具相关的tab和ucenter - if (isAdminFurniture) { + // 检查是否有任何角色名等于 "furniture" 字符串(精确匹配) + const hasFurnitureRole = roles.includes('furniture'); + + // admin_furniture 和 furniture 角色:显示 furniture_reception, furniture_customer, furniture_top_sales 和 ucenter(相同顺序) + if (isAdminFurniture || hasFurnitureRole) { const furnitureTabs = ['furniture_reception', 'furniture_customer', 'furniture_top_sales', 'ucenter']; - console.log('[TabBar][allowed] admin_furniture ->', furnitureTabs); + console.log('[TabBar][allowed] furniture/admin_furniture role ->', furnitureTabs, 'roles:', roles); return furnitureTabs; } @@ -236,10 +279,10 @@ export default { // 非 meeting_admin 角色:排除所有会议相关的tab if (isAdmin) { - // admin 角色:显示所有tab(包括 ucenter),但排除会议相关的tab和家具相关的tab + // admin 角色:显示所有tab(包括 ucenter),但排除会议相关的tab、家具相关的tab、以及reception和customer const all = this.allTabs .map((t) => t.key) - .filter((key) => !meetingTabKeys.includes(key) && !['furniture_reception', 'furniture_customer', 'furniture_top_sales'].includes(key)); + .filter((key) => !meetingTabKeys.includes(key) && !['furniture_reception', 'common_begin_reception', 'furniture_customer', 'furniture_top_sales', 'reception', 'customer', ...removedTabKeys].includes(key)); // 确保 ucenter 被包含(用于"我的"页面) console.log('[TabBar][allowed] admin ->', all, 'includes ucenter:', all.includes('ucenter')); return all; @@ -256,10 +299,10 @@ export default { return studentTabs; } - // 其他角色:隐藏作业/教务/语音/表达,同时排除会议相关的tab和家具相关的tab,但确保包含ucenter + // 其他角色:隐藏作业/教务/语音/表达,同时排除会议相关的tab、家具相关的tab、以及reception和customer,但确保包含ucenter const otherTabs = this.allTabs .map((t) => t.key) - .filter((key) => !['homework', 'edu', 'voice', 'expression', 'furniture_reception', 'furniture_customer', 'furniture_top_sales', ...meetingTabKeys].includes(key)); + .filter((key) => !['homework', 'edu', 'voice', 'expression', 'furniture_reception', 'common_begin_reception', 'furniture_customer', 'furniture_top_sales', 'reception', 'customer', ...meetingTabKeys, ...removedTabKeys].includes(key)); // 确保 ucenter 被包含(任何角色都应该能看到"我的") if (!otherTabs.includes('ucenter')) { otherTabs.push('ucenter'); @@ -270,8 +313,8 @@ export default { // #ifdef H5 toggleNativeTabs() { // H5 平台自定义 tabbar 不生效,直接根据角色显隐原生 tabbar 项 - // 当前位置顺序:接待、客户、团队、销冠、工作台、我的、总结和统计、家具接待、家具客户、家具销冠 - const ORDER = ['reception', 'customer', 'team', 'champion', 'workspace', 'ucenter', 'meeting_summary', 'furniture_reception', 'furniture_customer', 'furniture_top_sales']; + // 当前位置顺序(根据 pages.json 中的 tabBar.list 顺序):接待、客户、团队、总结和统计、家具接待、开始接待、家具客户、家具销冠、我的 + const ORDER = ['reception', 'customer', 'team', 'meeting_summary', 'furniture_reception', 'common_begin_reception', 'furniture_customer', 'furniture_top_sales', 'ucenter']; const allowed = this.getAllowedKeys(); // 限制重试次数,防止渲染时机未就绪 this._h5TabRetries = (this._h5TabRetries || 0) + 1; diff --git a/pages.json b/pages.json index d49264a..41a6584 100644 --- a/pages.json +++ b/pages.json @@ -124,6 +124,12 @@ "navigationStyle": "custom" } }, + { + "path": "pages/furniture_reception/common_begin_reception", + "style": { + "navigationStyle": "custom" + } + }, { "path": "pages/furniture_customer/furniture_customer", "style": { @@ -379,21 +385,6 @@ "iconPath": "static/tabbar/team.png", "selectedIconPath": "static/tabbar/team_active.png", "text": "团队" - }, { - "pagePath": "pages/champion/champion", - "iconPath": "static/tabbar/insight.png", - "selectedIconPath": "static/tabbar/insight_active.png", - "text": "销冠" - }, { - "pagePath": "pages/workspace/workspace", - "iconPath": "static/tabbar/workspace.png", - "selectedIconPath": "static/tabbar/workspace_active.png", - "text": "工作台" - }, { - "pagePath": "pages/ucenter/ucenter", - "iconPath": "static/tabbar/me.png", - "selectedIconPath": "static/tabbar/me_active.png", - "text": "我的" }, { "pagePath": "pages/meeting_summary/meeting_summary", "iconPath": "static/tabbar/insight.png", @@ -404,6 +395,11 @@ "iconPath": "static/tabbar/reception.png", "selectedIconPath": "static/tabbar/reception_active.png", "text": "接待" + }, { + "pagePath": "pages/furniture_reception/common_begin_reception", + "iconPath": "static/tabbar/reception.png", + "selectedIconPath": "static/tabbar/reception_active.png", + "text": "开始接待" }, { "pagePath": "pages/furniture_customer/furniture_customer", "iconPath": "static/tabbar/customer.png", @@ -414,6 +410,11 @@ "iconPath": "static/tabbar/insight.png", "selectedIconPath": "static/tabbar/insight_active.png", "text": "销冠" + }, { + "pagePath": "pages/ucenter/ucenter", + "iconPath": "static/tabbar/me.png", + "selectedIconPath": "static/tabbar/me_active.png", + "text": "我的" }] }, "uniIdRouter": { diff --git a/pages/speaking_training_edu/components/DeviceTab.vue b/pages/speaking_training_edu/components/DeviceTab.vue index 307dbbb..45a0d41 100644 --- a/pages/speaking_training_edu/components/DeviceTab.vue +++ b/pages/speaking_training_edu/components/DeviceTab.vue @@ -1,463 +1,71 @@ diff --git a/pages/speaking_training_edu/speaking_training_edu.vue b/pages/speaking_training_edu/speaking_training_edu.vue index cf01666..c821967 100644 --- a/pages/speaking_training_edu/speaking_training_edu.vue +++ b/pages/speaking_training_edu/speaking_training_edu.vue @@ -48,27 +48,7 @@ export default { HomeworkTab, }, onShow() { - // 非教务老师角色,禁止停留在教务页,自动跳回接待 - try { - // 从登录成功时缓存的后端响应中读取角色名称 - const roleName = uni.getStorageSync('backend-role-name') || ''; - const hasEduRole = roleName === 'speaking_training_teacher'; - - if (!hasEduRole) { - uni.showToast({ - title: '当前账号无教务权限', - icon: 'none', - duration: 1500, - }); - setTimeout(() => { - uni.switchTab({ - url: '/pages/reception/reception', - }); - }, 800); - } - } catch (e) { - console.warn('检查教务权限失败:', e); - } + // 所有登录人都可以访问,已移除权限限制 }, data() { return { diff --git a/pages/ucenter/components/DeviceBindPopup.vue b/pages/ucenter/components/DeviceBindPopup.vue new file mode 100644 index 0000000..ced51dd --- /dev/null +++ b/pages/ucenter/components/DeviceBindPopup.vue @@ -0,0 +1,187 @@ + + + + + diff --git a/pages/ucenter/components/DeviceListPopup.vue b/pages/ucenter/components/DeviceListPopup.vue new file mode 100644 index 0000000..46fba63 --- /dev/null +++ b/pages/ucenter/components/DeviceListPopup.vue @@ -0,0 +1,519 @@ + + + + + + diff --git a/pages/ucenter/ucenter.vue b/pages/ucenter/ucenter.vue index 3eccc06..89f12dc 100644 --- a/pages/ucenter/ucenter.vue +++ b/pages/ucenter/ucenter.vue @@ -35,7 +35,19 @@ - {{item.text}} + + 设备总数 + {{totalDevices}} + + + 人员总数 + {{totalSales}} + + + 项目总数 + {{totalProjects}} + + {{item.text}} @@ -51,6 +63,12 @@ + + + + + + @@ -93,8 +111,14 @@ store, mutations } from '@/uni_modules/uni-id-pages/common/store.js' - import { put } from '@/common/request.js' + import { put, get } from '@/common/request.js' + import DeviceBindPopup from './components/DeviceBindPopup.vue' + import DeviceListPopup from './components/DeviceListPopup.vue' export default { + components: { + DeviceBindPopup, + DeviceListPopup + }, // #ifdef APP onBackPress({from}) { if(from=='backbutton'){ @@ -109,17 +133,20 @@ return { tenantId: '', loginUserInfo: {}, + totalDevices: 0, + totalSales: 0, + totalProjects: 0, gridList: [{ - "text": this.$t('mine.showText'), - "icon": "chat" + "text": "设备总数", + "icon": "gear" }, { - "text": this.$t('mine.showText'), - "icon": "cloud-upload" + "text": "人员总数", + "icon": "person-filled" }, { - "text": this.$t('mine.showText'), - "icon": "contact" + "text": "项目总数", + "icon": "list" }, { "text": '修改密码', @@ -142,25 +169,13 @@ }, // #endif { - "title": this.$t('mine.signIn'), - "to": '/pages/reception/reception', + "title": "设备绑定", + "event": 'openDeviceBind', "icon": "compose" }, - // #ifdef APP-PLUS { - "title": this.$t('mine.toEvaluate'), - "event": 'gotoMarket', - "icon": "star" - }, - //#endif - { - "title":this.$t('mine.readArticles'), - "to": '/pages/customer/customer', - "icon": "flag" - }, - { - "title": this.$t('mine.myScore'), - "to": '/pages/team/team', + "title": "设备列表", + "event": 'openDeviceList', "icon": "paperplane" } // #ifdef APP @@ -172,10 +187,6 @@ // #endif ], [{ - "title": this.$t('mine.feedback'), - "to": '/pages/champion/champion', - "icon": "help" - }, { "title": this.$t('mine.settings'), "to": '/pages/ucenter/settings/settings', "icon": "gear" @@ -216,6 +227,12 @@ onShow() { // 每次显示页面时更新租户信息和登录人信息 this.loadUserInfo() + // 加载设备统计信息 + this.loadDeviceStatistics() + // 加载销售(人员)统计信息 + this.loadSalesStatistics() + // 加载项目统计信息 + this.loadProjectStatistics() }, computed: { userInfo() { @@ -254,6 +271,60 @@ console.error('加载用户信息失败:', e) } }, + /** + * 加载设备统计信息 + */ + async loadDeviceStatistics() { + try { + const res = await get('/api/deviceManagement/statistics') + + if (res.statusCode === 200 && res.data && res.data.success) { + this.totalDevices = res.data.data?.totalDevices || 0 + } else { + console.error('获取设备统计信息失败:', res.data?.message || '未知错误') + this.totalDevices = 0 + } + } catch (e) { + console.error('获取设备统计信息异常:', e) + this.totalDevices = 0 + } + }, + /** + * 加载销售(人员)统计信息 + */ + async loadSalesStatistics() { + try { + const res = await get('/api/salesManagement/statistics') + + if (res.statusCode === 200 && res.data && res.data.success) { + this.totalSales = res.data.data?.totalSales || 0 + } else { + console.error('获取销售统计信息失败:', res.data?.message || '未知错误') + this.totalSales = 0 + } + } catch (e) { + console.error('获取销售统计信息异常:', e) + this.totalSales = 0 + } + }, + /** + * 加载项目统计信息 + */ + async loadProjectStatistics() { + try { + const res = await get('/api/projectManagement/statistics') + + if (res.statusCode === 200 && res.data && res.data.success) { + this.totalProjects = res.data.data?.totalProjects || 0 + } else { + console.error('获取项目统计信息失败:', res.data?.message || '未知错误') + this.totalProjects = 0 + } + } catch (e) { + console.error('获取项目统计信息异常:', e) + this.totalProjects = 0 + } + }, toSettings() { uni.navigateTo({ url: "/pages/ucenter/settings/settings" @@ -447,6 +518,29 @@ }) // #endif }, + /** + * 打开设备绑定弹窗 + */ + openDeviceBind() { + if (this.$refs.deviceBindPopup) { + this.$refs.deviceBindPopup.open() + } + }, + /** + * 设备绑定成功回调 + */ + onDeviceBindSuccess() { + // 刷新设备统计信息 + this.loadDeviceStatistics() + }, + /** + * 打开设备列表弹窗 + */ + openDeviceList() { + if (this.$refs.deviceListPopup) { + this.$refs.deviceListPopup.open() + } + }, /** * 打开修改密码弹窗 */ @@ -678,6 +772,27 @@ align-items: center; } + .device-text-wrapper { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + + .device-label { + font-size: 14px; + color: #817f82; + line-height: 20px; + margin-bottom: 2px; + } + + .device-count { + font-size: 18px; + font-weight: bold; + color: #007AFF; + line-height: 22px; + } + /*修改边线粗细示例*/ /* #ifndef APP-NVUE */