| | |
| | | <script setup lang="ts"> |
| | | import { ref } from 'vue' |
| | | import { logoutMini } from '@/api/auth' |
| | | import { useMiniAuthStore } from '@/store/auth' |
| | | |
| | | definePage({ |
| | | name: 'mine', |
| | |
| | | }, |
| | | }) |
| | | |
| | | interface MenuItem { |
| | | title: string |
| | | icon: string |
| | | path: string |
| | | showFor: Array<'employee' | 'normal'> |
| | | } |
| | | |
| | | const router = useRouter() |
| | | const authStore = useMiniAuthStore() |
| | | const sysInfo = uni.getSystemInfoSync() |
| | | const statusBarHeight = sysInfo.statusBarHeight || 0 |
| | | const navBarHeight = 44 |
| | | |
| | | const userRole = ref('employee') // 'employee' 或 'normal' |
| | | |
| | | const menuList = ref([ |
| | | const profile = computed(() => authStore.profile) |
| | | const userRole = computed<'employee' | 'normal'>(() => profile.value?.userType === 'EMPLOYEE' ? 'employee' : 'normal') |
| | | const roleLabel = computed(() => { |
| | | if (userRole.value === 'employee') { |
| | | return profile.value?.deptLeaderFlag ? '部门领导' : '员工' |
| | | } |
| | | return '普通访客' |
| | | }) |
| | | const secondaryText = computed(() => { |
| | | if (!profile.value) { |
| | | return '' |
| | | } |
| | | if (userRole.value === 'employee') { |
| | | return profile.value.deptName || '' |
| | | } |
| | | return profile.value.companyName || '' |
| | | }) |
| | | const menuList = ref<MenuItem[]>([ |
| | | { title: '预约审核', icon: '/static/mine/review.png', path: '/pages/reviewList/index', showFor: ['employee'] }, |
| | | { title: '来访记录', icon: '/static/mine/record.png', path: '/pages/visitRecord/index', showFor: ['employee'] }, |
| | | { title: '关于我们', icon: '/static/mine/aboutUs.png', path: '/pages/aboutUs/index', showFor: ['employee', 'normal'] }, |
| | | { title: '退出登录', icon: '/static/mine/logout.png', path: '', showFor: ['employee', 'normal'] }, |
| | | ]) |
| | | |
| | | function handleMenuClick(item: any) { |
| | | if (item.path) { |
| | | uni.navigateTo({ url: item.path }) |
| | | } else if (item.title === '退出登录') { |
| | | uni.showToast({ title: '已退出登录', icon: 'none' }) |
| | | } |
| | | } |
| | | const visibleMenus = computed(() => menuList.value.filter(item => item.showFor.includes(userRole.value))) |
| | | |
| | | function handleEditProfile() { |
| | | uni.navigateTo({ url: '/pages/profile/index' }) |
| | | } |
| | | |
| | | async function handleLogout() { |
| | | try { |
| | | await logoutMini() |
| | | } |
| | | catch (error) { |
| | | console.warn('mini logout failed', error) |
| | | } |
| | | finally { |
| | | authStore.clearSession() |
| | | router.replaceAll({ name: 'login' }) |
| | | } |
| | | } |
| | | |
| | | function handleMenuClick(item: MenuItem) { |
| | | if (item.title === '退出登录') { |
| | | void handleLogout() |
| | | return |
| | | } |
| | | |
| | | if (item.path) { |
| | | uni.navigateTo({ url: item.path }) |
| | | } |
| | | } |
| | | |
| | | onShow(() => { |
| | | authStore.restoreSession() |
| | | }) |
| | | </script> |
| | | |
| | | <template> |
| | | <view class="h-screen w-full relative box-border flex flex-col overflow-hidden m-0 p-0"> |
| | | <!-- 顶部背景图 (被覆盖层) 整个页面的背景 --> |
| | | <view class="absolute left-0 top-0 z-0 h-full w-full pointer-events-none"> |
| | | <image src="/static/mine/bg.png" class="w-full h-full" mode="scaleToFill" /> |
| | | <view class="relative m-0 h-screen w-full box-border flex flex-col overflow-hidden p-0"> |
| | | <view class="pointer-events-none absolute left-0 top-0 z-0 h-full w-full"> |
| | | <image src="/static/mine/bg.png" class="h-full w-full" mode="scaleToFill" /> |
| | | </view> |
| | | |
| | | <!-- 整体内容背景图 (bgmask覆盖在上面约202rpx处) --> |
| | | <view class="absolute left-0 top-[330rpx] w-full z-0 pointer-events-none" style="height: calc(100% - 330rpx);"> |
| | | <image src="/static/mine/bgmask.png" class="w-full h-full" mode="scaleToFill" /> |
| | | <view class="pointer-events-none absolute left-0 top-[330rpx] z-0 w-full" style="height: calc(100% - 330rpx);"> |
| | | <image src="/static/mine/bgmask.png" class="h-full w-full" mode="scaleToFill" /> |
| | | </view> |
| | | |
| | | <!-- 自定义导航栏 --> |
| | | <view class="relative z-50 w-full shrink-0" :style="{ height: (statusBarHeight + navBarHeight) + 'px' }"> |
| | | <view class="fixed left-0 top-0 w-full z-50" :style="{ height: (statusBarHeight + navBarHeight) + 'px' }"> |
| | | <view class="flex items-center justify-between w-full absolute left-0 z-10" |
| | | <view class="fixed left-0 top-0 z-50 w-full" :style="{ height: (statusBarHeight + navBarHeight) + 'px' }"> |
| | | <view class="absolute left-0 z-10 flex w-full items-center justify-between" |
| | | :style="{ top: statusBarHeight + 'px', height: navBarHeight + 'px' }"> |
| | | <view class="flex h-full w-[88rpx] items-center justify-center relative z-10"> |
| | | <!-- 占位,tabbar 页面不需要返回按钮 --> |
| | | </view> |
| | | <view class="absolute left-0 w-full text-center text-[34rpx] font-medium text-white z-0"> |
| | | <view class="relative z-10 flex h-full w-[88rpx] items-center justify-center" /> |
| | | <view class="absolute left-0 z-0 w-full text-center text-[34rpx] font-medium text-white"> |
| | | 预约小程序 |
| | | </view> |
| | | <view class="w-[88rpx]"></view> |
| | | <view class="w-[88rpx]" /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 用户信息区 --> |
| | | <view class="relative z-10 px-[48rpx] mt-[32rpx] flex items-center shrink-0"> |
| | | <view class="relative w-[122rpx] h-[122rpx] rounded-full border-[2rpx] border-solid border-[#0068ff] box-border shrink-0"> |
| | | <image src="/static/mine/header.png" class="w-full h-full rounded-full" mode="aspectFill" /> |
| | | <!-- 带有 #0068FF 边框的外圈 --> |
| | | <view v-if="userRole === 'employee'" class="absolute bottom-[-4rpx] right-[4rpx] w-[32rpx] h-[32rpx] bg-white border-[2rpx] border-solid border-[#0068FF] box-border rounded-full z-10 flex items-center justify-center"> |
| | | <image src="/static/mine/change.png" class="w-[20rpx] h-[20rpx]" mode="aspectFit" /> |
| | | <view class="relative z-10 mt-[32rpx] flex items-center px-[48rpx] shrink-0"> |
| | | <view class="relative h-[122rpx] w-[122rpx] shrink-0 rounded-full border-[2rpx] border-solid border-[#0068ff] box-border"> |
| | | <image :src="profile?.avatarUrl || '/static/mine/header.png'" class="h-full w-full rounded-full" mode="aspectFill" /> |
| | | <view v-if="userRole === 'employee'" class="absolute bottom-[-4rpx] right-[4rpx] z-10 h-[32rpx] w-[32rpx] flex items-center justify-center rounded-full border-[2rpx] border-solid border-[#0068FF] bg-white box-border"> |
| | | <image src="/static/mine/change.png" class="h-[20rpx] w-[20rpx]" mode="aspectFit" /> |
| | | </view> |
| | | </view> |
| | | |
| | | |
| | | <view class="ml-[32rpx] flex flex-col justify-center"> |
| | | <view class="flex items-center mb-[12rpx]"> |
| | | <text class="text-[32rpx] text-[#333333] font-[500]">王明</text> |
| | | |
| | | <!-- 员工角色标签 --> |
| | | <view v-if="userRole === 'employee'" class="ml-[16rpx] w-[96rpx] h-[32rpx] rounded-[18rpx] flex items-center justify-center box-border border-[1rpx] border-solid border-[#FFFFFF]" style="background: linear-gradient(90deg, #68A5FF 0%, #0068FF 107%, rgba(255, 255, 255, 0) 107%);"> |
| | | <text class="text-white text-[16rpx]">部门领导</text> |
| | | <view class="mb-[12rpx] flex items-center"> |
| | | <text class="text-[32rpx] text-[#333333] font-[500]">{{ profile?.displayName || '用户' }}</text> |
| | | |
| | | <view v-if="userRole === 'employee'" class="ml-[16rpx] flex h-[32rpx] min-w-[96rpx] items-center justify-center rounded-[18rpx] border-[1rpx] border-solid border-[#FFFFFF] px-[12rpx] box-border" style="background: linear-gradient(90deg, #68A5FF 0%, #0068FF 107%, rgba(255, 255, 255, 0) 107%);"> |
| | | <text class="text-[16rpx] text-white">{{ roleLabel }}</text> |
| | | </view> |
| | | <!-- 普通用户角色标签 --> |
| | | <image v-else src="/static/mine/edit.png" class="w-[32rpx] mt-[4rpx] h-[32rpx] ml-[10rpx]" mode="aspectFit" @click="handleEditProfile" /> |
| | | <image v-else src="/static/mine/edit.png" class="ml-[10rpx] mt-[4rpx] h-[32rpx] w-[32rpx]" mode="aspectFit" @click="handleEditProfile" /> |
| | | </view> |
| | | |
| | | <text v-if="userRole === 'employee'" class="text-[28rpx] text-[#666666]">综合管理部</text> |
| | | <view v-if="userRole === 'normal'" class="flex flex-col"> |
| | | <text class="text-[28rpx] text-[#666666] mb-[8rpx]">186****4567</text> |
| | | <text class="text-[28rpx] text-[#666666]">成都xxxxx公司</text> |
| | | </view> |
| | | |
| | | <text v-if="secondaryText" class="mb-[8rpx] text-[28rpx] text-[#666666]">{{ secondaryText }}</text> |
| | | <text class="text-[28rpx] text-[#666666]">{{ profile?.mobile || '未绑定手机号' }}</text> |
| | | <text v-if="userRole === 'normal'" class="mt-[8rpx] text-[28rpx] text-[#666666]">{{ roleLabel }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 菜单选项卡片 --> |
| | | <view class="relative z-10 px-[32rpx] mt-[80rpx] shrink-0"> |
| | | <view class="bg-white rounded-[16rpx] py-[24rpx] w-full" style="box-shadow: 0px 4rpx 8rpx 0px rgba(6, 47, 108, 0.1608);"> |
| | | <template v-for="(item, index) in menuList.filter(m => m.showFor.includes(userRole))" :key="index"> |
| | | <view |
| | | <view class="relative z-10 mt-[80rpx] px-[32rpx] shrink-0"> |
| | | <view class="w-full rounded-[16rpx] bg-white py-[24rpx]" style="box-shadow: 0px 4rpx 8rpx 0px rgba(6, 47, 108, 0.1608);"> |
| | | <template v-for="(item, index) in visibleMenus" :key="item.title"> |
| | | <view |
| | | class="flex items-center justify-between px-[40rpx] py-[20rpx] active:bg-gray-50 transition-colors" |
| | | :class="index !== menuList.filter(m => m.showFor.includes(userRole)).length - 1 ? 'border-b1' : ''" |
| | | :class="index !== visibleMenus.length - 1 ? 'border-b1' : ''" |
| | | @click="handleMenuClick(item)" |
| | | > |
| | | <view class="flex items-center"> |
| | | <image :src="item.icon" class="w-[56rpx] h-[56rpx] shrink-0" mode="aspectFit" /> |
| | | <text class="ml-[16rpx] text-[#000000] text-[28rpx] font-normal">{{ item.title }}</text> |
| | | <image :src="item.icon" class="h-[56rpx] w-[56rpx] shrink-0" mode="aspectFit" /> |
| | | <text class="ml-[16rpx] text-[28rpx] text-[#000000] font-normal">{{ item.title }}</text> |
| | | </view> |
| | | <wd-icon name="arrow-right" size="28rpx" color="#000000" /> |
| | | </view> |
| | | </template> |
| | | </view> |
| | | </view> |
| | | |
| | | </view> |
| | | </template> |
| | | |
| | | <style> |
| | | /* 针对整个页面的样式重置,强制隐藏原生滚动条并固定页面 */ |
| | | page { |
| | | height: 100%; |
| | | overflow: hidden; |