181 lines
4.1 KiB
Vue
181 lines
4.1 KiB
Vue
<template>
|
|
<div class="main-layout">
|
|
<app-nav-bar @height-change="onChangeNavBarHeight" />
|
|
<div class="router-view">
|
|
<page-transition />
|
|
</div>
|
|
<t-tab-bar
|
|
v-model="tabVal"
|
|
:class="{ 'is-hidden': !tabBarStore.isShowing }"
|
|
class="tab-bar glass-white"
|
|
shape="round"
|
|
theme="tag"
|
|
:split="false"
|
|
:z-index="999"
|
|
:disabled="!tabBarStore.isShowing"
|
|
@change="onChangeTab"
|
|
>
|
|
<t-tab-bar-item v-for="item in list" :key="item.value" class="bg-transparent" :value="item.value">
|
|
<span v-text="item.text"></span>
|
|
<template #icon>
|
|
<t-icon :name="item.icon" />
|
|
</template>
|
|
</t-tab-bar-item>
|
|
</t-tab-bar>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import AppNavBar from "@/components/navbar/index.vue";
|
|
import PageTransition from "@/components/PageTransition.vue";
|
|
import { useTabBarStore } from "@/store/tabBarStore";
|
|
import { gaoRoleCodes, permissionCodes } from "@/utils/permission";
|
|
import { useUserStore } from "timi-web-api";
|
|
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
const tabBarStore = useTabBarStore();
|
|
const userStore = useUserStore();
|
|
|
|
// ---------- 导航边距 ----------
|
|
|
|
const navBarHeight = ref("0px");
|
|
|
|
function onChangeNavBarHeight(value: string): void {
|
|
navBarHeight.value = value;
|
|
}
|
|
|
|
// ---------- Tab 切换 ----------
|
|
|
|
type TabItem = {
|
|
value: string;
|
|
icon: string;
|
|
text: string;
|
|
permission?: string | string[];
|
|
permissionMode?: "and" | "or";
|
|
role?: string | string[];
|
|
roleMode?: "and" | "or";
|
|
};
|
|
|
|
const tabVal = ref(route.path);
|
|
const tabList: TabItem[] = [
|
|
{
|
|
value: "/customers",
|
|
icon: "usergroup",
|
|
text: "客户",
|
|
permission: [permissionCodes.customer.create, permissionCodes.eventRecord.create]
|
|
},
|
|
{ value: "/product", icon: "layers", text: "产品", role: gaoRoleCodes.storeManager },
|
|
{ value: "/store", icon: "shop-3", text: "门店", permission: permissionCodes.stat.read },
|
|
{ value: "/management", icon: "setting-1", text: "管理" }
|
|
];
|
|
const list = computed(() => tabList.filter(item => (
|
|
(!item.permission || userStore.hasPermissions(item.permission, item.permissionMode))
|
|
&& (!item.role || userStore.hasRoles(item.role, item.roleMode))
|
|
)));
|
|
|
|
watch(() => route.path, (newPath: string) => {
|
|
tabVal.value = resolveTabValue(newPath);
|
|
}, { immediate: true });
|
|
|
|
async function syncVisibleTab(): Promise<void> {
|
|
const visiblePath = resolveVisibleTabValue(route.path);
|
|
if (tabVal.value !== visiblePath) {
|
|
tabVal.value = visiblePath;
|
|
}
|
|
|
|
if (route.path === "/customers" && visiblePath !== "/customers") {
|
|
await router.replace(visiblePath);
|
|
}
|
|
}
|
|
|
|
watch(list, syncVisibleTab, { immediate: true });
|
|
|
|
function resolveTabValue(path: string): string {
|
|
if (path.startsWith("/customers")) {
|
|
return "/customers";
|
|
}
|
|
|
|
if (path.startsWith("/store")) {
|
|
return "/store";
|
|
}
|
|
|
|
if (path.startsWith("/product")) {
|
|
return "/product";
|
|
}
|
|
|
|
if (path.startsWith("/management")) {
|
|
return "/management";
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
function resolveVisibleTabValue(path: string): string {
|
|
const tabPath = resolveTabValue(path);
|
|
if (list.value.some(item => item.value === tabPath)) {
|
|
return tabPath;
|
|
}
|
|
|
|
return list.value[0]?.value || tabPath;
|
|
}
|
|
|
|
function onChangeTab(value: string | number): void {
|
|
const path = String(value);
|
|
if (resolveTabValue(route.path) === path) {
|
|
return;
|
|
}
|
|
|
|
router.push(path);
|
|
}
|
|
|
|
// ---------- Tab 边距 ----------
|
|
|
|
const isTabBarPaddingNeeded = computed(() => {
|
|
if (!tabBarStore.isShowing) {
|
|
return false;
|
|
}
|
|
|
|
return route.meta.tabBarPadding !== false;
|
|
});
|
|
|
|
const tabBarHeight = computed(() => {
|
|
if (isTabBarPaddingNeeded.value) {
|
|
return "6rem";
|
|
}
|
|
|
|
return "0rem";
|
|
});
|
|
</script>
|
|
|
|
<style scoped lang="less">
|
|
.main-layout {
|
|
--app-nav-height: v-bind(navBarHeight);
|
|
--app-tab-height: v-bind(tabBarHeight);
|
|
|
|
height: 100%;
|
|
transition: padding-bottom .24s ease;
|
|
|
|
.router-view {
|
|
height: 100%;
|
|
}
|
|
|
|
.tab-bar {
|
|
--td-tab-bar-height: 52px;
|
|
|
|
bottom: 1rem;
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
backdrop-filter: blur(10px);
|
|
pointer-events: auto;
|
|
transition: opacity 520ms, transform 520ms var(--tui-bezier);
|
|
|
|
&.is-hidden {
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
transform: translateY(calc(100% + 1rem));
|
|
}
|
|
}
|
|
}
|
|
</style>
|