init project
This commit is contained in:
@@ -0,0 +1,251 @@
|
||||
<template>
|
||||
<div class="main-layout">
|
||||
<t-navbar
|
||||
ref="navBarRef"
|
||||
v-if="navBarStore.isShowing"
|
||||
class="nav-bar"
|
||||
:left-arrow="!!navBarStore.canBack"
|
||||
@left-click="doBack"
|
||||
>
|
||||
<template #title>
|
||||
<div id="app-nav-title" class="nav-title-slot">
|
||||
<span v-if="!navBarStore.customTitleVisible" class="nav-title-text" v-text="navBarStore.title" />
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="navBarStore.customRightVisible || navBarStore.rightIcon || navBarStore.rightText" #right>
|
||||
<div v-if="navBarStore.customRightVisible" id="app-nav-right" class="nav-right-slot" />
|
||||
<button
|
||||
v-else-if="navBarStore.rightIcon"
|
||||
class="nav-btn"
|
||||
type="button"
|
||||
@click="navBarStore.rightAction?.()"
|
||||
>
|
||||
<t-icon :name="navBarStore.rightIcon" size="1.25rem" />
|
||||
</button>
|
||||
<span
|
||||
v-else
|
||||
v-text="navBarStore.rightText"
|
||||
class="nav-text"
|
||||
:style="{ color: navBarStore.rightTextColor ? navBarStore.rightTextColor : '#333' }"
|
||||
:class="{ clickable: !!navBarStore.rightAction }"
|
||||
@click="navBarStore.rightAction?.()"
|
||||
></span>
|
||||
</template>
|
||||
</t-navbar>
|
||||
<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"
|
||||
: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 PageTransition from "@/components/PageTransition.vue";
|
||||
import {useNavBarStore} from "@/store/navBarStore";
|
||||
import {useTabBarStore} from "@/store/tabBarStore";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const navBarStore = useNavBarStore();
|
||||
const tabBarStore = useTabBarStore();
|
||||
|
||||
// ---------- 导航高度 ----------
|
||||
|
||||
const navBarRef = ref();
|
||||
let navBarResizeObs: ResizeObserver | undefined;
|
||||
|
||||
onMounted(() => {
|
||||
if (!navBarRef.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
navBarResizeObs = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
navBarStore.setHeight(entry.contentRect.height);
|
||||
return;
|
||||
}
|
||||
});
|
||||
navBarResizeObs.observe(navBarRef.value.$el.querySelector(".t-navbar__content"));
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (navBarResizeObs) {
|
||||
navBarResizeObs.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
// ---------- 导航返回 ----------
|
||||
|
||||
function doBack(): void {
|
||||
if (navBarStore.backTo) {
|
||||
router.push(navBarStore.backTo);
|
||||
navBarStore.setBackTo(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
router.back();
|
||||
}
|
||||
|
||||
// ---------- 导航边距 ----------
|
||||
|
||||
const navBarHeight = computed(() => {
|
||||
if (navBarStore.isShowing) {
|
||||
return "48px";
|
||||
}
|
||||
return "0px";
|
||||
});
|
||||
|
||||
// ---------- Tab 切换 ----------
|
||||
|
||||
const tabVal = ref(route.path);
|
||||
const list = [
|
||||
{ value: "/customers", icon: "usergroup", text: "客户" },
|
||||
{ value: "/management", icon: "setting-1", text: "管理" }
|
||||
];
|
||||
|
||||
watch(() => route.path, (newPath: string) => {
|
||||
tabVal.value = resolveTabValue(newPath);
|
||||
}, { immediate: true });
|
||||
|
||||
function resolveTabValue(path: string): string {
|
||||
if (path.startsWith("/customers")) {
|
||||
return "/customers";
|
||||
}
|
||||
|
||||
if (path.startsWith("/management")) {
|
||||
return "/management";
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
.nav-bar {
|
||||
z-index: 999;
|
||||
|
||||
:deep(.t-navbar__content) {
|
||||
color: var(--tui-black, #000);
|
||||
overflow: visible;
|
||||
box-shadow: 0 3px 8px rgba(0, 0, 0, .1);
|
||||
background: rgba(250, 250, 250, .8);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 0;
|
||||
border: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--td-text-color-primary, #333);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.nav-title-slot {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.nav-title-text {
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.nav-right-slot {
|
||||
width: 6rem;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.nav-text {
|
||||
cursor: default;
|
||||
|
||||
&.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
:deep(.t-navbar__right),
|
||||
:deep(.t-navbar__right-content) {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.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>
|
||||
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<div class="root-layout diselect">
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineOptions({
|
||||
name: "RootLayout"
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.root-layout {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user