Files
gao-customer-h5/src/pages/tabs/SettingTab.vue
T
2026-08-23 21:51:07 +08:00

346 lines
8.0 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
v-if="currentUser"
class="info"
arrow
hover
@click="go('/management/profile')"
>
<template #description>
<div v-if="currentUser?.phoneNo" v-text="currentUser.phoneNo" />
</template>
<template #title>
<div class="header">
<div class="name" v-text="currentUser.name" />
<gender-icon :value="currentUser.gender" />
<div class="roles">
<t-tag
v-for="role in currentUserRoleList"
:key="getRoleKey(role)"
size="small"
theme="success"
variant="outline"
:content="getRoleName(role)"
/>
<span v-if="!currentUserRoleList.length && !isRoleLoading" class="muted" v-text="'未配置角色'" />
</div>
</div>
</template>
<template #leftIcon>
<t-image
class="avatar"
:src="currentUserAvatar"
:alt="currentUser.name"
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)"
>
<template #leftIcon>
<t-icon :name="item.icon" size="1.75rem" />
</template>
</t-cell>
</t-cell-group>
<footer class="footer">
<p>技术支持:夜雨</p>
<p>Copyright © 2026 imyeyu.com</p>
</footer>
</div>
</template>
<script lang="ts">
let settingTabScrollTop: number | undefined;
</script>
<script setup lang="ts">
import { onBeforeRouteLeave } from "vue-router";
import { type Role as UserRole, RoleAPI, UserAPI, useUserStore } from "timi-web";
import { buildFallbackAvatar } from "@/utils/avatar";
import { moduleCode } from "@/store/permissionSettingsStore";
import { corePermissionCodes, gaoRoleCodes, 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: "SettingTab"
});
const router = useRouter();
const route = useRoute();
const userStore = useUserStore();
function getSettingTabPage(): HTMLElement | undefined {
return Array.from(document.querySelectorAll<HTMLElement>(".page-transition .pages"))
.find(element => !!element.querySelector(".management-tab"));
}
const currentUser = computed(() => userStore.loginUser.user);
const currentUserAvatar = computed(() => buildUserAvatar(currentUser.value));
const currentUserRoleList = ref<UserRole[]>([]);
const isRoleLoading = ref(false);
const canManagePermission = computed(() => userStore.hasPermissions([
corePermissionCodes.role.read,
corePermissionCodes.permission.read
], "and"));
const canManageUser = computed(() => userStore.hasRoles([gaoRoleCodes.globalManager, gaoRoleCodes.storeManager], "or"));
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: "chart-combo",
path: "/management/customer-data-charts",
title: "客户统计数据",
visible: userStore.hasPermissions(permissionCodes.stat.read)
},
{
icon: "tag",
path: "/management/tags",
title: "标签管理",
visible: userStore.hasPermissions(corePermissionCodes.tag.read)
}
]
},
{
title: "登记",
items: [
{
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: "order-list",
path: "/management/registration-event-record-rank",
title: "登记记录排行",
visible: userStore.hasPermissions(permissionCodes.stat.read)
}
]
},
{
title: "积分",
items: [
{
icon: "git-merge",
path: "/management/points/rules",
title: "积分规则",
visible: userStore.hasPermissions(permissionCodes.pointRule.read)
},
{
icon: "wallet",
path: "/management/points/account",
title: "积分账户",
visible: userStore.hasPermissions(permissionCodes.point.accountRead)
},
{
icon: "bill",
path: "/management/points/ledger",
title: "积分流水",
visible: userStore.hasPermissions(permissionCodes.point.ledgerRead)
},
{
icon: "user-marked",
path: "/management/points/adjust",
title: "人工调整积分",
visible: userStore.hasPermissions(permissionCodes.point.adjust)
}
]
},
{
title: "系统",
items: [
{
icon: "setting",
path: "/management/settings",
title: "应用设置",
visible: true
},
{
icon: "secured",
path: "/management/permissions",
title: "权限设置",
visible: canManagePermission.value
},
{
icon: "usergroup",
path: "/management/users",
title: "用户管理",
visible: canManageUser.value && userStore.hasPermissions(permissionCodes.user.read)
},
{
icon: "notification",
path: "/management/notifications",
title: "通知管理",
visible: true
},
{
icon: "qrcode",
path: "/management/qr-code",
title: "生成客户二维码",
visible: userStore.hasPermissions(permissionCodes.qrCode.create)
}
]
}
]);
function getRoleKey(role: UserRole): string {
return role.id || `${role.moduleCode}:${role.code}`;
}
function getRoleName(role: UserRole): string {
return role.name || role.code || "未配置角色";
}
async function loadCurrentRoleList(): Promise<void> {
if (!currentUser.value) {
return;
}
isRoleLoading.value = true;
try {
currentUserRoleList.value = await RoleAPI.currentDetailList(moduleCode);
} finally {
isRoleLoading.value = false;
}
}
function buildUserAvatar(user?: UserAvatarSource): string {
return UserAPI.getAvatarURL(user) || buildFallbackAvatar(currentUser.value?.name, "#0f766e");
}
async function go(item: MenuItem | string): Promise<void> {
if (typeof item === "string") {
await router.push(item);
return;
}
await router.push(item.path);
}
function restoreScrollPosition(): void {
if (settingTabScrollTop === undefined || route.path !== "/management") {
return;
}
const scrollTop = settingTabScrollTop;
const settingTabPage = getSettingTabPage();
if (!settingTabPage) {
return;
}
settingTabPage.scrollTop = scrollTop;
settingTabScrollTop = undefined;
}
onMounted(async () => {
restoreScrollPosition();
await loadCurrentRoleList();
});
onBeforeRouteLeave(() => {
const settingTabPage = getSettingTabPage();
const scrollElement = document.scrollingElement;
settingTabScrollTop = settingTabPage?.scrollTop ?? scrollElement?.scrollTop ?? window.scrollY;
});
</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 {
.info {
padding: 10px;
.avatar {
width: 4rem;
height: 4rem;
border-radius: .875rem;
}
.header {
gap: .5rem;
display: flex;
align-items: center;
.roles {
gap: .25rem;
display: flex;
flex-wrap: wrap;
}
}
}
}
.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>