init project

This commit is contained in:
Timi
2026-08-01 18:01:23 +08:00
parent 286fa1ba56
commit 11c48bf00c
107 changed files with 19350 additions and 1 deletions
+233
View File
@@ -0,0 +1,233 @@
<template>
<div class="management-tab">
<t-cell-group class="user-card" theme="card">
<t-cell
:title="currentUserName"
:description="currentUserDesc"
arrow
hover
@click="go('/management/profile')"
>
<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, gaoPermissionCodes } from "@/utils/gaoPermission";
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: "setting",
path: "/management/settings",
title: "应用设置",
visible: true
},
{
icon: "secured",
path: "/management/permissions",
title: "权限设置",
visible: canManagePermission.value
}
]
},
{
title: "用户与客户",
items: [
{
icon: "usergroup",
path: "/management/customers",
title: "客户管理",
visible: userStore.hasPermissions(gaoPermissionCodes.customer.read)
},
{
icon: "root-list",
path: "/management/users",
title: "用户管理",
visible: userStore.hasPermissions(gaoPermissionCodes.user.read)
},
{
icon: "qrcode",
path: "/management/qr-code",
title: "生成客户二维码",
visible: userStore.hasPermissions(gaoPermissionCodes.qrCode.create)
}
]
},
{
title: "登记管理",
items: [
{
icon: "task",
path: "/management/registration-events",
title: "登记事件",
visible: userStore.hasPermissions([gaoPermissionCodes.registrationEvent.read, gaoPermissionCodes.user.read], "and")
},
{
icon: "history",
path: "/management/registration-event-records",
title: "登记事件记录",
visible: userStore.hasPermissions(gaoPermissionCodes.registrationEventRecord.read)
}
]
},
{
title: "图表",
items: [
{
icon: "usergroup",
path: "/management/customer-data-charts",
title: "客户数据",
visible: userStore.hasPermissions(gaoPermissionCodes.stat.read)
},
{
icon: "chart",
path: "/management/data-charts",
title: "全局数据",
visible: userStore.hasPermissions(gaoPermissionCodes.stat.read)
}
]
}
]);
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 {
margin: 0 0 1rem 0;
.avatar {
width: 3.5rem;
height: 3.5rem;
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>