petstore-frontend/src/pages/mine/Mine.vue
2026-04-19 14:54:38 +08:00

279 lines
8.5 KiB
Vue
Raw 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.

<template>
<view class="mine-page">
<!-- 用户卡片 -->
<view class="user-card" :style="userCardSafeStyle" @click="openProfile">
<view class="user-row">
<view class="user-avatar" :style="avatarBg">
<image v-if="userInfo.avatar" :src="userInfo.avatar" class="avatar-img" mode="aspectFill" />
<text v-else class="avatar-letter">{{ initials }}</text>
</view>
<view class="user-meta">
<text class="user-name">{{ userInfo.name || '未设置' }}</text>
<text class="user-phone">{{ userInfo.phone || '' }}</text>
</view>
<view class="role-tag">{{ roleText }}</view>
</view>
</view>
<!-- 店铺管理(老板):分组见 P0 B4 -->
<view v-if="userInfo.role === 'boss'" class="menu-group">
<text class="menu-label module-title">店铺与经营</text>
<view class="menu-card">
<view class="menu-item" @click="goServiceTypePage">
<view class="menu-icon-wrap"><AppIcon name="service" :size="16" color="#666660" /></view>
<text class="menu-text">服务类型</text>
<text class="menu-arrow"></text>
</view>
<view class="menu-item" @click="goStorePage">
<view class="menu-icon-wrap"><AppIcon name="store" :size="16" color="#666660" /></view>
<text class="menu-text">店铺设置</text>
<text class="menu-arrow"></text>
</view>
<view class="menu-item" @click="goSchedulePage">
<view class="menu-icon-wrap"><AppIcon name="calendar" :size="16" color="#666660" /></view>
<text class="menu-text">门店日程</text>
<text class="menu-arrow"></text>
</view>
<view class="menu-item" @click="goLeadsPage">
<view class="menu-icon-wrap"><AppIcon name="orders" :size="16" color="#666660" /></view>
<text class="menu-text">回访池</text>
<text class="menu-arrow"></text>
</view>
</view>
</view>
<view v-if="userInfo.role === 'boss'" class="menu-group">
<text class="menu-label module-title">团队与账号</text>
<view class="menu-card">
<view class="menu-item" @click="goStaffPage">
<view class="menu-icon-wrap"><AppIcon name="staff" :size="16" color="#666660" /></view>
<text class="menu-text">员工管理</text>
<text class="menu-arrow"></text>
</view>
</view>
</view>
<!-- 员工:仅门店设置(含营业时间) -->
<view v-else-if="userInfo.role === 'staff'" class="menu-group">
<text class="menu-label">门店</text>
<view class="menu-card">
<view class="menu-item" @click="goStorePage">
<view class="menu-icon-wrap"><AppIcon name="store" :size="16" color="#666660" /></view>
<text class="menu-text">店铺设置</text>
<text class="menu-arrow"></text>
</view>
<view class="menu-item" @click="goSchedulePage">
<view class="menu-icon-wrap"><AppIcon name="calendar" :size="16" color="#666660" /></view>
<text class="menu-text">门店日程</text>
<text class="menu-arrow"></text>
</view>
<view class="menu-item" @click="goLeadsPage">
<view class="menu-icon-wrap"><AppIcon name="orders" :size="16" color="#666660" /></view>
<text class="menu-text">回访池</text>
<text class="menu-arrow"></text>
</view>
</view>
</view>
<!-- 个人 -->
<view class="menu-group">
<text class="menu-label">个人中心</text>
<view class="menu-card">
<view class="menu-item" @click="openProfile">
<view class="menu-icon-wrap"><AppIcon name="profile" :size="16" color="#666660" /></view>
<text class="menu-text">个人信息</text>
<text class="menu-arrow"></text>
</view>
<view class="menu-item" @click="goMyPetsPage">
<view class="menu-icon-wrap"><AppIcon name="service" :size="16" color="#666660" /></view>
<text class="menu-text">我的宠物</text>
<text class="menu-arrow"></text>
</view>
<view class="menu-item" @click="goMyOrdersPage">
<view class="menu-icon-wrap"><AppIcon name="orders" :size="16" color="#666660" /></view>
<text class="menu-text">我的订单</text>
<text class="menu-arrow"></text>
</view>
</view>
</view>
<view class="logout-wrap">
<button class="app-btn-main app-btn-danger-outline" @click="logout">退出登录</button>
</view>
<TabBar current-page="mine" @change="goPage" />
</view>
</template>
<script setup>
import { computed } from 'vue'
import TabBar from '../../components/TabBar.vue'
import AppIcon from '../../components/AppIcon.vue'
import { clearSession, getUserSession } from '../../utils/session.js'
import { useNavigator } from '../../composables/useNavigator.js'
const userInfo = getUserSession()
const { goPage, openAppPage } = useNavigator()
const goStaffPage = () => uni.navigateTo({ url: '/pages/mine/Staff' })
const goServiceTypePage = () => uni.navigateTo({ url: '/pages/mine/ServiceType' })
const goStorePage = () => uni.navigateTo({ url: '/pages/mine/Store' })
const goSchedulePage = () => uni.navigateTo({ url: '/pages/mine/Schedule' })
const goLeadsPage = () => uni.navigateTo({ url: '/pages/mine/Leads' })
const openProfile = () => uni.navigateTo({ url: '/pages/mine/Profile' })
const goMyPetsPage = () => uni.navigateTo({ url: '/pages/mine/MyPets' })
const goMyOrdersPage = () => uni.navigateTo({ url: '/pages/mine/MyOrders' })
const userCardSafeStyle = (() => {
const statusBarHeight = uni.getSystemInfoSync?.().statusBarHeight || 20
let safeTop = statusBarHeight + 12
const menuRect = uni.getMenuButtonBoundingClientRect?.()
if (menuRect && menuRect.top && menuRect.height) {
safeTop = menuRect.top + menuRect.height + 8
}
return `padding-top:${safeTop}px;`
})()
const initials = computed(() => {
if (!userInfo.name) return '?'
return userInfo.name.slice(0, 1).toUpperCase()
})
const avatarBg = computed(() => {
if (userInfo.avatar) return {}
const colors = ['#f5913e', 'var(--c-brand)', '#5090d0', '#e06050', '#8b6914']
const idx = (userInfo.name?.charCodeAt(0) || 0) % colors.length
return { background: colors[idx] }
})
const roleText = computed(() => {
if (userInfo.role === 'boss') return '店长'
if (userInfo.role === 'customer') return '客户'
return '员工'
})
const logout = () => {
uni.showModal({
title: '提示',
content: '确定退出登录?',
success: (res) => {
if (res.confirm) {
clearSession()
openAppPage('login')
}
}
})
}
</script>
<style scoped>
.mine-page {
padding-bottom: calc(76px + env(safe-area-inset-bottom));
background: #fafaf8;
min-height: 100vh;
}
/* 用户卡片 */
.user-card {
padding: 20px 20px 24px;
background: #fff;
border-radius: 0 0 20px 20px;
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
}
.user-row {
display: flex;
align-items: center;
gap: 14px;
}
.user-avatar {
width: 52px; height: 52px;
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
overflow: hidden;
}
.avatar-img { width: 100%; height: 100%; }
.avatar-letter {
color: #fff;
font-size: 22px;
font-weight: 700;
}
.user-meta { flex: 1; min-width: 0; }
.user-name {
font-size: 18px;
font-weight: 700;
color: #1a1a1a;
}
.user-phone {
font-size: 13px;
color: var(--c-text-3);
margin-top: 2px;
}
.role-tag {
padding: 4px 12px;
border-radius: 8px;
font-size: 12px;
font-weight: 700;
background: var(--c-brand-light);
color: var(--c-brand);
flex-shrink: 0;
}
/* 菜单组 */
.menu-group {
padding: 0 16px;
margin-top: 20px;
}
.menu-label {
display: block;
font-size: 12px;
font-weight: 700;
color: #bbbbb5;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 8px;
padding-left: 4px;
}
.menu-card {
background: #fff;
border-radius: 14px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0,0,0,0.03);
}
.menu-item {
display: flex;
align-items: center;
padding: 14px 16px;
gap: 12px;
}
.menu-item + .menu-item {
border-top: 1px solid #f5f5f2;
}
.menu-icon-wrap {
width: 32px; height: 32px;
border-radius: 10px;
background: #f5f5f2;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.menu-text {
flex: 1;
font-size: 15px;
font-weight: 600;
color: #1a1a1a;
}
.menu-arrow {
color: #ddddd8;
font-size: 18px;
font-weight: 300;
}
/* 退出 */
.logout-wrap {
padding: 24px 16px;
}
</style>