Files
gao-customer-h5/src/pages/tabs/ManagementTab.vue
T
2026-08-05 22:50:22 +08:00

232 lines
5.3 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>
<div class="management-tab">
<t-cell-group class="user-card" theme="card">
<t-cell
class="cell"
:title="currentUserName"
arrow
hover
@click="go('/management/profile')"
>
<template #description>
<telephone-link v-if="currentUser?.phoneNo" :value="currentUser.phoneNo" />
<span v-else v-text="currentUserDesc" />
</template>
<template #leftIcon>
<t-image
class="avatar"
:src="currentUserAvatar"
:alt="currentUserName"
fit="cover"
/>
</template>
</t-cell>
</t-cell-group>
<t-cell-group v-for="group in visibleMenuGroupList" :key="group.title" :title="group.title" theme="card">
<t-cell
v-for="item in group.items"
:key="item.path"
class="item"
:title="item.title"
arrow
hover
@click="go(item.path)"
>
<template #leftIcon>
<t-icon :name="item.icon" size="1.75rem" />
</template>
</t-cell>
</t-cell-group>
<footer class="footer">
<p>版本 1.0.0</p>
<p>技术支持:夜雨</p>
<p>Copyright © 2026 imyeyu.com</p>
</footer>
</div>
</template>
<script setup lang="ts">
import { UserAPI, useUserStore } from "timi-web";
import { corePermissionCodes, coreRoleCodes, permissionCodes } from "@/utils/permission";
type UserAvatarSource = Parameters<typeof UserAPI.getAvatarURL>[0];
type MenuItem = {
icon: string;
path: string;
title: string;
visible: boolean;
};
type MenuGroup = {
items: MenuItem[];
title: string;
};
defineOptions({
name: "ManagementTab"
});
const router = useRouter();
const userStore = useUserStore();
const currentUser = computed(() => userStore.loginUser.user);
const currentUserName = computed(() => currentUser.value?.nick || currentUser.value?.name || "未登录用户");
const currentUserDesc = computed(() => currentUser.value?.phoneNo || currentUser.value?.email || "暂无联系方式");
const currentUserAvatar = computed(() => buildUserAvatar(currentUser.value));
const canManagePermission = computed(() => (
userStore.hasRoles(coreRoleCodes.admin) || userStore.hasRoles(coreRoleCodes.userRoleAdmin)
) && userStore.hasPermissions([
corePermissionCodes.role.read,
corePermissionCodes.permission.read
], "and"));
const visibleMenuGroupList = computed<MenuGroup[]>(() => menuGroupList.value
.map(group => ({
...group,
items: group.items.filter(item => item.visible)
}))
.filter(group => group.items.length));
const menuGroupList = computed<MenuGroup[]>(() => [
{
title: "客户",
items: [
{
icon: "usergroup",
path: "/management/customers",
title: "客户管理",
visible: userStore.hasPermissions(permissionCodes.customer.read)
},
{
icon: "task",
path: "/management/registration-events",
title: "登记事件",
visible: userStore.hasPermissions([permissionCodes.event.read, permissionCodes.user.read], "and")
},
{
icon: "history",
path: "/management/registration-event-records",
title: "登记事件记录",
visible: userStore.hasPermissions(permissionCodes.eventRecord.read)
},
{
icon: "usergroup",
path: "/management/customer-data-charts",
title: "客户统计数据",
visible: userStore.hasPermissions(permissionCodes.stat.read)
},
{
icon: "chart",
path: "/management/data-charts",
title: "全局统计数据",
visible: userStore.hasPermissions(permissionCodes.stat.read)
}
]
},
{
title: "系统",
items: [
{
icon: "setting",
path: "/management/settings",
title: "应用设置",
visible: true
},
{
icon: "secured",
path: "/management/permissions",
title: "权限设置",
visible: canManagePermission.value
},
{
icon: "root-list",
path: "/management/users",
title: "用户管理",
visible: userStore.hasPermissions(permissionCodes.user.read)
},
{
icon: "qrcode",
path: "/management/qr-code",
title: "生成客户二维码",
visible: userStore.hasPermissions(permissionCodes.qrCode.create)
}
]
}
]);
function buildFallbackAvatar(name: string): string {
const text = encodeURIComponent((name.trim() || "用户").slice(0, 2));
return [
"data:image/svg+xml;charset=UTF-8,",
"%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120' viewBox='0 0 120 120'%3E",
"%3Crect width='120' height='120' rx='24' fill='%230f766e'/%3E",
"%3Ctext x='50%25' y='54%25' fill='white' font-size='36' font-family='Arial,sans-serif' text-anchor='middle' dominant-baseline='middle'%3E",
text,
"%3C/text%3E%3C/svg%3E"
].join("");
}
function buildUserAvatar(user?: UserAvatarSource): string {
return UserAPI.getAvatarURL(user) || buildFallbackAvatar(currentUserName.value);
}
async function go(path: string): Promise<void> {
await router.push(path);
}
</script>
<style scoped lang="less">
.management-tab {
gap: .5rem;
display: flex;
padding: calc(var(--app-nav-height) + 1rem) 0 5rem 0;
flex-direction: column;
.user-card {
.cell {
padding: 10px;
.avatar {
width: 4rem;
height: 4rem;
border-radius: .875rem;
}
.title {
gap: .5rem;
display: flex;
}
}
}
.footer {
padding: 1.25rem 1rem calc(1rem + env(safe-area-inset-bottom, 0));
text-align: center;
p {
margin: 0;
color: #52607a;
font-size: .8125rem;
line-height: 1.8;
}
}
}
:global(html.theme-dark) {
.management-tab {
.office {
color: #94a3b8;
}
.footer p {
color: #94a3b8;
}
}
}
</style>