Files
gao-customer-h5/src/pages/tabs/HomeTab.vue
T
2026-09-12 16:21:05 +08:00

231 lines
4.9 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="entry-page">
<section
v-for="group in visibleEntryGroupList"
:key="group.title"
class="entry-group"
>
<h2 v-text="group.title" />
<div class="entry-list" :class="group.layout">
<div
v-entrance-transition="index * 70"
v-for="(item, index) in group.entryList"
: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>
</div>
</div>
</section>
<div class="tips gray" v-entrance-transition="visibleEntryCount * 70">微信内打开本站可能会经常在扫码时需要授权,建议使用浏览器打开(右上角菜单,使用默认浏览器打开),并添加快捷入口到桌面。</div>
</div>
</template>
<script setup lang="ts">
import { NotifyAPI, useUserStore } from "timi-web-api";
import { useNavBarStore } from "@/store/navBarStore";
import { corePermissionCodes, permissionCodes } from "@/utils/permission";
defineOptions({
name: "HomeTab"
});
const router = useRouter();
const navBarStore = useNavBarStore();
const userStore = useUserStore();
const unreadNotifyCount = ref(0);
let isPageActive = true;
interface HomeEntry {
path: string;
permission: string;
icon: string;
title: string;
}
interface HomeEntryGroup {
title: string;
layout?: "customer";
entryList: HomeEntry[];
}
const entryGroupList: HomeEntryGroup[] = [
{
title: "客户管理",
layout: "customer",
entryList: [
{
path: "/management/customers/create?redirect=/customers",
permission: permissionCodes.customer.create,
icon: "edit-1",
title: "录入"
},
{
path: "/customer/registration",
permission: permissionCodes.eventRecord.create,
icon: "task-checked",
title: "登记"
},
{
path: "/customer/quick-sign-in",
permission: permissionCodes.eventRecord.create,
icon: "scan",
title: "快速扫码登记"
}
]
},
{
title: "订单管理",
entryList: [
{
path: "/management/sales-orders",
permission: permissionCodes.salesOrder.read,
icon: "task",
title: "订单列表"
},
{
path: "/management/sales-orders/create",
permission: permissionCodes.salesOrder.create,
icon: "task-add",
title: "创建订单"
},
{
path: "/management/customer-packages/create",
permission: permissionCodes.customerPackage.create,
icon: "layers",
title: "创建套餐订单"
}
]
}
];
const visibleEntryGroupList = computed(() => entryGroupList
.map(group => ({
...group,
entryList: group.entryList.filter(item => userStore.hasPermissions(item.permission))
}))
.filter(group => group.entryList.length));
const visibleEntryCount = computed(() => visibleEntryGroupList.value.reduce((count, group) => count + group.entryList.length, 0));
const canReadNotify = computed(() => userStore.hasPermissions(corePermissionCodes.notify.read));
async function go(path: string): Promise<void> {
await router.push(path);
}
async function goNotifyList(): Promise<void> {
await router.push("/notify");
}
async function loadUnreadNotifyCount(): Promise<void> {
try {
unreadNotifyCount.value = Math.max(0, await NotifyAPI.countUnreadInternal());
} catch (error) {
unreadNotifyCount.value = 0;
console.warn("未读通知数量加载失败", error);
}
if (isPageActive) {
navBarStore.setRightBadge(unreadNotifyCount.value);
}
}
onMounted(async () => {
navBarStore.clearRight();
if (canReadNotify.value) {
navBarStore.setRightIcon("notification", goNotifyList);
navBarStore.setRightBadge(0);
await loadUnreadNotifyCount();
}
});
onUnmounted(() => {
isPageActive = false;
navBarStore.clearRight();
});
</script>
<style scoped lang="less">
.entry-page {
gap: 2rem;
display: flex;
padding: calc(var(--app-nav-height) + 1rem) 1rem 1rem 1rem;
flex-direction: column;
.entry-group {
& + & {
margin-top: 1.5rem;
}
> h2 {
color: #52607a;
margin: 0 0 .75rem 0;
font-size: .875rem;
font-weight: 700;
}
.entry-list {
display: grid;
gap: .875rem;
grid-template-columns: repeat(3, 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: .75rem;
line-height: 1.5;
}
}
}
}
.tips {
font-size: .875rem;
margin-top: 1rem;
text-indent: 2em;
}
}
:global(html.theme-dark) {
.entry-page {
.entry-group > h2 {
color: #94a3b8;
}
.item {
background: rgba(15, 23, 42, .88);
box-shadow: 0 .75rem 2rem rgba(2, 6, 23, .32);
}
}
}
</style>