init project
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<div class="entry-page">
|
||||
<section class="entry-list">
|
||||
<div v-for="item in visibleEntryList" :key="item.path" class="item" @click="go(item.path)">
|
||||
<div class="body">
|
||||
<t-icon :name="item.icon" size="1.75rem" />
|
||||
<h2 v-text="item.title" />
|
||||
</div>
|
||||
<p v-text="item.desc" />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useUserStore } from "timi-web";
|
||||
import { gaoPermissionCodes } from "@/utils/gaoPermission";
|
||||
|
||||
defineOptions({
|
||||
name: "CustomerTab"
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
|
||||
const entryList = [
|
||||
{
|
||||
path: "/customer/quick-entry",
|
||||
permission: gaoPermissionCodes.registrationEventRecord.create,
|
||||
icon: "scan",
|
||||
title: "快速补录登记",
|
||||
desc: "对以往未绑定二维码的客户进行快速补录和登记"
|
||||
},
|
||||
{
|
||||
path: "/customer/quick-sign-in",
|
||||
permission: gaoPermissionCodes.registrationEventRecord.create,
|
||||
icon: "scan",
|
||||
title: "快速扫码登记",
|
||||
desc: "扫码选择客户并按配置事件登记"
|
||||
},
|
||||
{
|
||||
path: "/management/customers/create?redirect=/customers",
|
||||
permission: gaoPermissionCodes.customer.create,
|
||||
icon: "edit-1",
|
||||
title: "录入",
|
||||
desc: "新建客户档案并完善基础资料"
|
||||
},
|
||||
{
|
||||
path: "/customer/registration",
|
||||
permission: gaoPermissionCodes.registrationEventRecord.create,
|
||||
icon: "task-checked",
|
||||
title: "登记",
|
||||
desc: "按流程完成客户登记"
|
||||
}
|
||||
];
|
||||
|
||||
const visibleEntryList = computed(() => entryList.filter(item => userStore.hasPermissions(item.permission)));
|
||||
|
||||
async function go(path: string): Promise<void> {
|
||||
await router.push(path);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.entry-page {
|
||||
padding: calc(var(--app-nav-height) + 1rem) 1rem 1rem 1rem;
|
||||
|
||||
.entry-list {
|
||||
display: grid;
|
||||
gap: .875rem;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.item {
|
||||
color: #0f766e;
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
background: rgba(255, 255, 255, .92);
|
||||
box-shadow: 0 .75rem 2rem rgba(15, 23, 42, .08);
|
||||
align-items: center;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 1.4rem;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
.body {
|
||||
inset: 1rem;
|
||||
display: flex;
|
||||
gap: .5rem;
|
||||
position: absolute;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
p {
|
||||
left: 1rem;
|
||||
right: 1rem;
|
||||
bottom: 1rem;
|
||||
color: #7b889d;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
font-size: .75rem;
|
||||
line-height: 1.25;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:global(html.theme-dark) {
|
||||
.entry-page {
|
||||
.item {
|
||||
background: rgba(15, 23, 42, .88);
|
||||
box-shadow: 0 .75rem 2rem rgba(2, 6, 23, .32);
|
||||
|
||||
p {
|
||||
color: #94a3b8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user