Commit 67af4c8eabfb3b83000026a0a78b26ba26875cbb
1 parent
48ff5b95
工作台 如果没有子节点就不渲染父节点
Showing
2 changed files
with
81 additions
and
130 deletions
pages/mine/index.vue
| @@ -8,19 +8,19 @@ | @@ -8,19 +8,19 @@ | ||
| 8 | <view class="user-info-wrap"> | 8 | <view class="user-info-wrap"> |
| 9 | <!-- 头像 --> | 9 | <!-- 头像 --> |
| 10 | <up-avatar | 10 | <up-avatar |
| 11 | - :src="'/static/imgs/default-avatar.png'" | 11 | + :src="userStore.isLogin ? userInfo.avatar : '/static/imgs/default-avatar.png'" |
| 12 | size="100rpx" | 12 | size="100rpx" |
| 13 | shape="circle" | 13 | shape="circle" |
| 14 | class="user-avatar" | 14 | class="user-avatar" |
| 15 | ></up-avatar> | 15 | ></up-avatar> |
| 16 | 16 | ||
| 17 | - <!-- 用户名+手机号+角色 --> | 17 | + <!-- 用户名+手机号 --> |
| 18 | <view class="user-info-content"> | 18 | <view class="user-info-content"> |
| 19 | - <view class="user-name">{{ userInfo.username || '未登录' }}</view> | ||
| 20 | - <view class="user-phone">{{ userInfo.nickname || '--------' }}</view> | 19 | + <view class="user-name">{{ userStore.isLogin ? userInfo.username : '未登录' }}</view> |
| 20 | + <view class="user-phone">{{ userStore.isLogin ? userInfo.nickname : '--------' }}</view> | ||
| 21 | </view> | 21 | </view> |
| 22 | 22 | ||
| 23 | - <!-- 登录/编辑入口 --> | 23 | + <!-- 登录入口 --> |
| 24 | <up-button | 24 | <up-button |
| 25 | v-if="!userStore.isLogin" | 25 | v-if="!userStore.isLogin" |
| 26 | type="primary" | 26 | type="primary" |
| @@ -32,9 +32,8 @@ | @@ -32,9 +32,8 @@ | ||
| 32 | </view> | 32 | </view> |
| 33 | </view> | 33 | </view> |
| 34 | 34 | ||
| 35 | - | ||
| 36 | <!-- 退出登录按钮 (仅登录状态显示) --> | 35 | <!-- 退出登录按钮 (仅登录状态显示) --> |
| 37 | - <view v-if="userStore.isLogin" class="logout-btn-wrap "> | 36 | + <view v-if="userStore.isLogin" class="logout-btn-wrap"> |
| 38 | <up-button | 37 | <up-button |
| 39 | type="primary" | 38 | type="primary" |
| 40 | size="large" | 39 | size="large" |
| @@ -43,77 +42,64 @@ | @@ -43,77 +42,64 @@ | ||
| 43 | 退出登录 | 42 | 退出登录 |
| 44 | </up-button> | 43 | </up-button> |
| 45 | </view> | 44 | </view> |
| 46 | - | ||
| 47 | - | ||
| 48 | </view> | 45 | </view> |
| 49 | </template> | 46 | </template> |
| 50 | 47 | ||
| 51 | <script setup lang="ts"> | 48 | <script setup lang="ts"> |
| 52 | -import { ref } from 'vue' | ||
| 53 | -import { useUserStore } from '@/pinia/user' // 匹配你的 Pinia 路径 | ||
| 54 | -import { onShow } from '@dcloudio/uni-app' | ||
| 55 | - | 49 | +import { computed } from 'vue' |
| 50 | +import { useUserStore } from '@/pinia/user' | ||
| 51 | +import { showModal, onShow } from '@dcloudio/uni-app' | ||
| 56 | // 初始化Pinia仓库 | 52 | // 初始化Pinia仓库 |
| 57 | const userStore = useUserStore() | 53 | const userStore = useUserStore() |
| 58 | -const userInfo = userStore.userInfo.user | ||
| 59 | -import { getDictList, getDictLabel, getDictSimpleList } from '@/common/utils/dict'; | ||
| 60 | -// 示例1:获取ai_image_status的完整字典列表 | ||
| 61 | -const aiImageStatusList = ref([]); | ||
| 62 | -// 示例2:获取ai_image_status中value=10的label | ||
| 63 | -const aiImageStatus10Label = ref(''); | ||
| 64 | - | ||
| 65 | -// 获取ai_image_status的完整列表(包含所有字段) | ||
| 66 | -aiImageStatusList.value = getDictList('ai_image_status'); | ||
| 67 | -// 获取ai_image_status中value=10的label | ||
| 68 | -aiImageStatus10Label.value = getDictLabel('ai_image_status', 10); | ||
| 69 | -console.log('13') | ||
| 70 | -console.log(aiImageStatusList.value) | ||
| 71 | -console.log(aiImageStatus10Label.value) | ||
| 72 | - | ||
| 73 | -// 可选:获取仅包含value和label的简化列表 | ||
| 74 | -const simpleList = getDictSimpleList('ai_image_status'); | ||
| 75 | -console.log('简化列表:', simpleList); | ||
| 76 | -console.log('24') | ||
| 77 | - | ||
| 78 | - | ||
| 79 | 54 | ||
| 55 | +// 计算属性获取用户信息(响应式) | ||
| 56 | +const userInfo = computed(() => userStore.userInfo.user || {}) | ||
| 80 | 57 | ||
| 81 | -// 页面显示时检查登录状态(同步缓存) | ||
| 82 | -onShow(() => { | ||
| 83 | - // 触发登录状态检查,确保缓存和Pinia同步 | ||
| 84 | - userStore.checkLogin() | ||
| 85 | -}) | ||
| 86 | - | ||
| 87 | -// 处理登录(跳转到登录页) | 58 | +/** |
| 59 | + * 处理登录跳转 | ||
| 60 | + */ | ||
| 88 | const handleLogin = () => { | 61 | const handleLogin = () => { |
| 89 | - navigateTo({ | ||
| 90 | - url: '/pages/login/index' // 替换为你的实际登录页路径 | 62 | + uni.navigateTo({ |
| 63 | + url: '/pages/login/index' | ||
| 91 | }) | 64 | }) |
| 92 | } | 65 | } |
| 93 | 66 | ||
| 94 | - | ||
| 95 | - | ||
| 96 | -// 确认退出登录 | 67 | +/** |
| 68 | + * 确认退出登录(带二次确认) | ||
| 69 | + */ | ||
| 97 | const confirmLogout = async () => { | 70 | const confirmLogout = async () => { |
| 71 | + console.log('13213') | ||
| 98 | try { | 72 | try { |
| 99 | - // 调用Pinia自带的logout方法(你的仓库已实现清空状态+跳转) | ||
| 100 | - await userStore.logout() | 73 | + console.log('434') |
| 74 | + const res = await uni.showModal({ | ||
| 75 | + title: '提示', | ||
| 76 | + content: '确定要退出登录吗?', | ||
| 77 | + confirmText: '退出', | ||
| 78 | + cancelText: '取消' | ||
| 79 | + }) | ||
| 101 | 80 | ||
| 81 | + if (res.confirm) { | ||
| 82 | + await userStore.logout() | ||
| 83 | + uni.showToast({ title: '退出登录成功', type: 'success' }) | ||
| 84 | + } | ||
| 102 | } catch (error) { | 85 | } catch (error) { |
| 103 | console.error('退出登录失败:', error) | 86 | console.error('退出登录失败:', error) |
| 104 | - uni.showToast({ | ||
| 105 | - title: '退出登录失败,请重试', | ||
| 106 | - type: 'error' | ||
| 107 | - }) | ||
| 108 | - | 87 | + uni.showToast({ title: '退出登录失败,请重试', type: 'error' }) |
| 109 | } | 88 | } |
| 110 | } | 89 | } |
| 111 | 90 | ||
| 91 | +// 页面显示时检查登录状态 | ||
| 92 | +onShow(() => { | ||
| 93 | + userStore.checkLogin() | ||
| 94 | +}) | ||
| 112 | </script> | 95 | </script> |
| 113 | 96 | ||
| 114 | <script lang="ts"> | 97 | <script lang="ts"> |
| 115 | export default { | 98 | export default { |
| 116 | - name: 'UserCenter' | 99 | + name: 'UserCenter', |
| 100 | + options: { | ||
| 101 | + navigationBarTitleText: '个人中心' | ||
| 102 | + } | ||
| 117 | } | 103 | } |
| 118 | </script> | 104 | </script> |
| 119 | 105 | ||
| @@ -149,6 +135,10 @@ export default { | @@ -149,6 +135,10 @@ export default { | ||
| 149 | .user-avatar { | 135 | .user-avatar { |
| 150 | border: 4rpx solid #ffffff; | 136 | border: 4rpx solid #ffffff; |
| 151 | box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1); | 137 | box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1); |
| 138 | + transition: transform 0.2s ease; | ||
| 139 | + &:active { | ||
| 140 | + transform: scale(0.95); | ||
| 141 | + } | ||
| 152 | } | 142 | } |
| 153 | 143 | ||
| 154 | .user-info-content { | 144 | .user-info-content { |
| @@ -166,25 +156,13 @@ export default { | @@ -166,25 +156,13 @@ export default { | ||
| 166 | .user-phone { | 156 | .user-phone { |
| 167 | font-size: 26rpx; | 157 | font-size: 26rpx; |
| 168 | color: rgba(255, 255, 255, 0.9); | 158 | color: rgba(255, 255, 255, 0.9); |
| 169 | - margin-bottom: 10rpx; | ||
| 170 | - } | ||
| 171 | - | ||
| 172 | - .user-role-tag { | ||
| 173 | - background-color: rgba(255, 255, 255, 0.2); | ||
| 174 | - color: #ffffff; | ||
| 175 | - border: none; | ||
| 176 | } | 159 | } |
| 177 | } | 160 | } |
| 178 | } | 161 | } |
| 179 | } | 162 | } |
| 180 | 163 | ||
| 181 | - | ||
| 182 | // 退出登录按钮 | 164 | // 退出登录按钮 |
| 183 | .logout-btn-wrap { | 165 | .logout-btn-wrap { |
| 184 | margin: 200rpx 20rpx 20rpx; | 166 | margin: 200rpx 20rpx 20rpx; |
| 185 | - padding: 20rpx; | ||
| 186 | - | ||
| 187 | - | ||
| 188 | } | 167 | } |
| 189 | - | ||
| 190 | </style> | 168 | </style> |
| 191 | \ No newline at end of file | 169 | \ No newline at end of file |
pages/workbench/index.vue
| 1 | <template> | 1 | <template> |
| 2 | - <!-- 外层容器:包含蓝色块 + 原始内容 --> | ||
| 3 | <view class="page-container"> | 2 | <view class="page-container"> |
| 4 | - <!-- 加载页:覆盖整个容器,渲染完成后隐藏 --> | 3 | + <!-- 加载页 --> |
| 5 | <up-loading-page | 4 | <up-loading-page |
| 6 | - | ||
| 7 | :loading="loading" | 5 | :loading="loading" |
| 8 | loading-text="加载中..." | 6 | loading-text="加载中..." |
| 9 | color="#577ee3" | 7 | color="#577ee3" |
| 10 | zIndex="1000" | 8 | zIndex="1000" |
| 11 | ></up-loading-page> | 9 | ></up-loading-page> |
| 12 | 10 | ||
| 13 | - <!-- 蓝色装饰块:#577ee3 --> | 11 | + <!-- 蓝色装饰块 --> |
| 14 | <view class="blue-decor-block" v-show="!loading"></view> | 12 | <view class="blue-decor-block" v-show="!loading"></view> |
| 15 | 13 | ||
| 16 | - <!-- 原始内容容器 --> | 14 | + <!-- 内容容器 --> |
| 17 | <view class="content-wrap"> | 15 | <view class="content-wrap"> |
| 18 | - <!-- uview-plus空状态组件 --> | 16 | + <!-- 空状态:过滤后无数据且非加载中时显示 --> |
| 19 | <u-empty | 17 | <u-empty |
| 20 | - v-if="!moduleList.length && !loading" | 18 | + v-if="!filteredModuleList.length && !loading" |
| 21 | mode="permission" | 19 | mode="permission" |
| 22 | ></u-empty> | 20 | ></u-empty> |
| 23 | 21 | ||
| 24 | - <!-- 菜单卡片列表(替换grid为flex布局) --> | ||
| 25 | - <view class="menu-card-wrap"> | 22 | + <!-- 菜单卡片列表:仅渲染有子节点的父模块 --> |
| 23 | + <view class="menu-card-wrap"> | ||
| 26 | <up-card | 24 | <up-card |
| 27 | :title-size="18" | 25 | :title-size="18" |
| 28 | :border="false" | 26 | :border="false" |
| 29 | :shadow="true" | 27 | :shadow="true" |
| 30 | :foot-border-top="false" | 28 | :foot-border-top="false" |
| 31 | - v-for="(parentModule, index) in moduleList" | 29 | + v-for="(parentModule, index) in filteredModuleList" |
| 32 | :key="`$parentModule.id_${index}`" | 30 | :key="`$parentModule.id_${index}`" |
| 33 | :title="parentModule.name" | 31 | :title="parentModule.name" |
| 34 | class="card-container" | 32 | class="card-container" |
| 35 | > | 33 | > |
| 36 | <template #body> | 34 | <template #body> |
| 37 | - <!-- 替换up-grid:改用flex流式布局容器 --> | ||
| 38 | <view class="card-content"> | 35 | <view class="card-content"> |
| 39 | <view | 36 | <view |
| 40 | v-for="(listItem, listIndex) in parentModule.children" | 37 | v-for="(listItem, listIndex) in parentModule.children" |
| @@ -62,8 +59,7 @@ | @@ -62,8 +59,7 @@ | ||
| 62 | </template> | 59 | </template> |
| 63 | 60 | ||
| 64 | <script setup lang="ts"> | 61 | <script setup lang="ts"> |
| 65 | -// 原始代码完全保留,无任何修改 | ||
| 66 | -import { ref, nextTick } from 'vue'; | 62 | +import { ref, computed } from 'vue'; // 新增 computed |
| 67 | import { onShow } from '@dcloudio/uni-app'; | 63 | import { onShow } from '@dcloudio/uni-app'; |
| 68 | import { useUserStore } from '@/pinia/user'; | 64 | import { useUserStore } from '@/pinia/user'; |
| 69 | import globalConfig from '@/common/config/global'; | 65 | import globalConfig from '@/common/config/global'; |
| @@ -91,60 +87,50 @@ const loading = ref(true); | @@ -91,60 +87,50 @@ const loading = ref(true); | ||
| 91 | const userStore = useUserStore(); | 87 | const userStore = useUserStore(); |
| 92 | const moduleList = ref<MenuItem[]>([]); | 88 | const moduleList = ref<MenuItem[]>([]); |
| 93 | 89 | ||
| 90 | +// 计算属性:过滤出有子节点的父模块(children 存在且长度 > 0) | ||
| 91 | +const filteredModuleList = computed(() => { | ||
| 92 | + return moduleList.value.filter(item => { | ||
| 93 | + // 确保 children 是数组且长度大于 0 | ||
| 94 | + return Array.isArray(item.children) && item.children.length > 0; | ||
| 95 | + }); | ||
| 96 | +}); | ||
| 97 | + | ||
| 94 | onShow(async () => { | 98 | onShow(async () => { |
| 95 | try { | 99 | try { |
| 96 | loading.value = true; | 100 | loading.value = true; |
| 97 | - | ||
| 98 | const rawMenuData = userStore.moduleListInfo || cache.get(globalConfig.cache.moduleListKey); | 101 | const rawMenuData = userStore.moduleListInfo || cache.get(globalConfig.cache.moduleListKey); |
| 99 | - const menuData = rawMenuData || []; | ||
| 100 | - moduleList.value = menuData; | ||
| 101 | - // | ||
| 102 | - // // await nextTick(); | ||
| 103 | - // setTimeout(() => { | ||
| 104 | - // loading.value = false; | ||
| 105 | - // }, 500); | 102 | + moduleList.value = rawMenuData || []; |
| 106 | loading.value = false; | 103 | loading.value = false; |
| 107 | - console.log('菜单数据:', moduleList.value); | 104 | + console.log('原始菜单数据:', moduleList.value); |
| 105 | + console.log('过滤后有子节点的父模块:', filteredModuleList.value); | ||
| 108 | } catch (error) { | 106 | } catch (error) { |
| 109 | console.error('获取菜单数据失败:', error); | 107 | console.error('获取菜单数据失败:', error); |
| 110 | moduleList.value = []; | 108 | moduleList.value = []; |
| 111 | - await nextTick(); | ||
| 112 | loading.value = false; | 109 | loading.value = false; |
| 113 | } | 110 | } |
| 114 | }); | 111 | }); |
| 115 | 112 | ||
| 116 | const handleMenuClick = (item: MenuItem) => { | 113 | const handleMenuClick = (item: MenuItem) => { |
| 117 | if (!item.jumpUrl) { | 114 | if (!item.jumpUrl) { |
| 118 | - uni.showToast({ | ||
| 119 | - title: '暂无跳转链接', | ||
| 120 | - icon: 'none', | ||
| 121 | - duration: 2000 | ||
| 122 | - }); | 115 | + uni.showToast({ title: '暂无跳转链接', icon: 'none', duration: 2000 }); |
| 123 | return; | 116 | return; |
| 124 | } | 117 | } |
| 125 | - console.log(item.jumpUrl); | ||
| 126 | uni.navigateTo({ | 118 | uni.navigateTo({ |
| 127 | url: item.jumpUrl, | 119 | url: item.jumpUrl, |
| 128 | fail: (err) => { | 120 | fail: (err) => { |
| 129 | console.error('页面跳转失败:', err); | 121 | console.error('页面跳转失败:', err); |
| 130 | - uni.showToast({ | ||
| 131 | - title: '页面路径错误', | ||
| 132 | - icon: 'none', | ||
| 133 | - duration: 2000 | ||
| 134 | - }); | 122 | + uni.showToast({ title: '页面路径错误', icon: 'none', duration: 2000 }); |
| 135 | } | 123 | } |
| 136 | }); | 124 | }); |
| 137 | }; | 125 | }; |
| 138 | </script> | 126 | </script> |
| 139 | 127 | ||
| 140 | <style scoped lang="scss"> | 128 | <style scoped lang="scss"> |
| 141 | -/* 原有样式保留 + 新增/调整图片居中样式 */ | ||
| 142 | .page-container { | 129 | .page-container { |
| 143 | - position: relative; // 新增:让蓝色块绝对定位生效 | ||
| 144 | - min-height: 100vh; // 新增:防止内容高度不足 | 130 | + position: relative; |
| 131 | + min-height: 100vh; | ||
| 145 | } | 132 | } |
| 146 | 133 | ||
| 147 | -/* 蓝色块样式(保留) */ | ||
| 148 | .blue-decor-block { | 134 | .blue-decor-block { |
| 149 | position: absolute; | 135 | position: absolute; |
| 150 | top: 0; | 136 | top: 0; |
| @@ -155,70 +141,57 @@ const handleMenuClick = (item: MenuItem) => { | @@ -155,70 +141,57 @@ const handleMenuClick = (item: MenuItem) => { | ||
| 155 | z-index: 1; | 141 | z-index: 1; |
| 156 | } | 142 | } |
| 157 | 143 | ||
| 158 | -/* 内容容器样式(保留 + 微调) */ | ||
| 159 | .content-wrap { | 144 | .content-wrap { |
| 160 | position: relative; | 145 | position: relative; |
| 161 | z-index: 2; | 146 | z-index: 2; |
| 162 | - padding: 120px 0 0 ; // 新增左右内边距,避免卡片贴边 | 147 | + padding: 120px 0 0; |
| 163 | display: flex; | 148 | display: flex; |
| 164 | flex-direction: column; | 149 | flex-direction: column; |
| 165 | - gap: 20rpx; // 新增:卡片之间的上下间距 | 150 | + gap: 20rpx; |
| 166 | } | 151 | } |
| 167 | 152 | ||
| 168 | -/* 菜单卡片容器(新增:统一卡片外层间距) */ | ||
| 169 | .menu-card-wrap { | 153 | .menu-card-wrap { |
| 170 | width: 100%; | 154 | width: 100%; |
| 171 | } | 155 | } |
| 172 | 156 | ||
| 173 | -/* 卡片容器:重置up-card默认样式 */ | ||
| 174 | .card-container { | 157 | .card-container { |
| 175 | - --u-card-content-padding: 0; // 取消卡片默认内边距 | ||
| 176 | - border-radius: 8rpx; // 新增:卡片圆角 | ||
| 177 | - overflow: hidden; // 新增:裁剪内部元素 | 158 | + --u-card-content-padding: 0; |
| 159 | + border-radius: 8rpx; | ||
| 160 | + overflow: hidden; | ||
| 178 | } | 161 | } |
| 179 | 162 | ||
| 180 | -/* 核心:小块容器(Flex 流式布局) */ | ||
| 181 | .card-content { | 163 | .card-content { |
| 182 | display: flex; | 164 | display: flex; |
| 183 | - flex-wrap: wrap; // 自动换行 | ||
| 184 | - gap: 20rpx; // 关键:小块之间的上下+左右间距统一 | ||
| 185 | - | 165 | + flex-wrap: wrap; |
| 166 | + gap: 20rpx; | ||
| 186 | } | 167 | } |
| 187 | 168 | ||
| 188 | -/* 小块样式:一行最多4个,宽度自适应 + 内部flex布局 */ | ||
| 189 | .content-block { | 169 | .content-block { |
| 190 | - // 宽度计算:(100% - 3*间距) / 4 (4个元素有3个间距) | ||
| 191 | width: calc((100% - 3 * 20rpx) / 4); | 170 | width: calc((100% - 3 * 20rpx) / 4); |
| 192 | - // 小块内部样式:改用flex布局,确保子元素居中 | ||
| 193 | display: flex; | 171 | display: flex; |
| 194 | flex-direction: column; | 172 | flex-direction: column; |
| 195 | - align-items: center; // 水平居中(核心:解决图片不居中问题) | ||
| 196 | - justify-content: center; // 垂直居中(可选,优化视觉) | ||
| 197 | - padding: 24rpx 0; | 173 | + align-items: center; |
| 174 | + justify-content: center; | ||
| 175 | + padding: 14rpx 0; | ||
| 198 | border-radius: 8rpx; | 176 | border-radius: 8rpx; |
| 199 | - text-align: center; | ||
| 200 | - // 点击反馈 | ||
| 201 | touch-action: manipulation; | 177 | touch-action: manipulation; |
| 202 | transition: background-color 0.2s; | 178 | transition: background-color 0.2s; |
| 203 | } | 179 | } |
| 204 | 180 | ||
| 205 | -/* 小块点击态 */ | ||
| 206 | .content-block:active { | 181 | .content-block:active { |
| 207 | background-color: #e9ecef; | 182 | background-color: #e9ecef; |
| 208 | } | 183 | } |
| 209 | 184 | ||
| 210 | -/* 新增:图片居中样式(兜底保障) */ | ||
| 211 | .block-icon { | 185 | .block-icon { |
| 212 | - display: block; // 转为块级元素 | ||
| 213 | - margin: 0 auto; // 水平居中(双重保障) | 186 | + display: block; |
| 187 | + margin: 0 auto; | ||
| 214 | } | 188 | } |
| 215 | 189 | ||
| 216 | -/* 网格文字样式(保留 + 微调) */ | ||
| 217 | .grid-text { | 190 | .grid-text { |
| 218 | font-size: 26rpx; | 191 | font-size: 26rpx; |
| 219 | color: #333; | 192 | color: #333; |
| 220 | text-align: center; | 193 | text-align: center; |
| 221 | margin-top: 10rpx; | 194 | margin-top: 10rpx; |
| 222 | - display: block; // 新增:确保文字换行 | 195 | + display: block; |
| 223 | } | 196 | } |
| 224 | </style> | 197 | </style> |
| 225 | \ No newline at end of file | 198 | \ No newline at end of file |