support iOS 26 navbar
This commit is contained in:
@@ -0,0 +1,152 @@
|
|||||||
|
<template>
|
||||||
|
<t-navbar
|
||||||
|
ref="navBarRef"
|
||||||
|
class="nav-bar"
|
||||||
|
:left-arrow="!!navBarStore.canBack"
|
||||||
|
:z-index="20"
|
||||||
|
@left-click="emit('back')"
|
||||||
|
>
|
||||||
|
<template #title>
|
||||||
|
<div id="app-nav-title" class="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 class="nav-right-slot">
|
||||||
|
<div v-if="navBarStore.customRightVisible" id="app-nav-right" />
|
||||||
|
<t-badge
|
||||||
|
v-else-if="navBarStore.rightIcon && navBarStore.rightBadge !== undefined"
|
||||||
|
class="badge"
|
||||||
|
:count="navBarStore.rightBadge"
|
||||||
|
:max-count="99"
|
||||||
|
:show-zero="false"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="button"
|
||||||
|
type="button"
|
||||||
|
:disabled="navBarStore.rightDisabled"
|
||||||
|
@click="emit('rightAction')"
|
||||||
|
>
|
||||||
|
<t-icon :name="navBarStore.rightIcon" size="1.25rem" />
|
||||||
|
</button>
|
||||||
|
</t-badge>
|
||||||
|
<button
|
||||||
|
v-else-if="navBarStore.rightIcon"
|
||||||
|
class="button"
|
||||||
|
type="button"
|
||||||
|
:disabled="navBarStore.rightDisabled"
|
||||||
|
@click="emit('rightAction')"
|
||||||
|
>
|
||||||
|
<t-icon :name="navBarStore.rightIcon" size="1.25rem" />
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
v-text="navBarStore.rightText"
|
||||||
|
class="nav-text"
|
||||||
|
:style="{ color: navBarStore.rightDisabled ? 'var(--td-text-color-disabled)' : navBarStore.rightTextColor ? navBarStore.rightTextColor : 'var(--td-text-color-primary, #333)' }"
|
||||||
|
:class="{ clickable: !!navBarStore.rightAction && !navBarStore.rightDisabled, disabled: navBarStore.rightDisabled }"
|
||||||
|
@click="emit('rightAction')"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</t-navbar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useNavBarStore } from "@/store/navBarStore";
|
||||||
|
|
||||||
|
type TNavBarRef = { $el?: HTMLElement };
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
back: [];
|
||||||
|
rightAction: [];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const navBarStore = useNavBarStore();
|
||||||
|
const navBarRef = ref<TNavBarRef>();
|
||||||
|
|
||||||
|
function getElement(): HTMLElement | undefined {
|
||||||
|
return navBarRef.value?.$el?.querySelector(".t-navbar__content") || undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
getElement
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.nav-bar {
|
||||||
|
|
||||||
|
: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);
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
|
||||||
|
:deep(.t-badge__content) {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--td-text-color-primary, #333);
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
color: var(--td-text-color-disabled);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
|
||||||
|
:deep(.t-badge) {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-text {
|
||||||
|
cursor: default;
|
||||||
|
|
||||||
|
&.clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.t-navbar__right),
|
||||||
|
:deep(.t-navbar__right-content) {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
<template>
|
||||||
|
<div ref="navBarRef" class="ios26-nav-bar">
|
||||||
|
<div class="left">
|
||||||
|
<button
|
||||||
|
v-if="navBarStore.canBack"
|
||||||
|
class="pill back"
|
||||||
|
type="button"
|
||||||
|
@click="emit('back')"
|
||||||
|
>
|
||||||
|
<t-icon name="chevron-left" size="1.35rem" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="title" class="pill title">
|
||||||
|
<span
|
||||||
|
v-if="!navBarStore.customTitleVisible"
|
||||||
|
class="text"
|
||||||
|
v-text="navBarStore.title"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<div
|
||||||
|
v-if="navBarStore.customRightVisible || navBarStore.rightIcon || navBarStore.rightText"
|
||||||
|
class="pill slot"
|
||||||
|
>
|
||||||
|
<div v-if="navBarStore.customRightVisible" id="app-nav-right" />
|
||||||
|
<t-badge
|
||||||
|
v-else-if="navBarStore.rightIcon && navBarStore.rightBadge !== undefined"
|
||||||
|
class="badge"
|
||||||
|
:count="navBarStore.rightBadge"
|
||||||
|
:max-count="99"
|
||||||
|
:show-zero="false"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="button"
|
||||||
|
type="button"
|
||||||
|
:disabled="navBarStore.rightDisabled"
|
||||||
|
@click="emit('rightAction')"
|
||||||
|
>
|
||||||
|
<t-icon :name="navBarStore.rightIcon" size="1.25rem" />
|
||||||
|
</button>
|
||||||
|
</t-badge>
|
||||||
|
<button
|
||||||
|
v-else-if="navBarStore.rightIcon"
|
||||||
|
class="button"
|
||||||
|
type="button"
|
||||||
|
:disabled="navBarStore.rightDisabled"
|
||||||
|
@click="emit('rightAction')"
|
||||||
|
>
|
||||||
|
<t-icon :name="navBarStore.rightIcon" size="1.25rem" />
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
v-text="navBarStore.rightText"
|
||||||
|
class="text"
|
||||||
|
:style="{ color: navBarStore.rightDisabled ? 'var(--td-text-color-disabled)' : navBarStore.rightTextColor ? navBarStore.rightTextColor : 'var(--td-text-color-primary, #333)' }"
|
||||||
|
:class="{ clickable: !!navBarStore.rightAction && !navBarStore.rightDisabled, disabled: navBarStore.rightDisabled }"
|
||||||
|
@click="emit('rightAction')"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useNavBarStore } from "@/store/navBarStore";
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
back: [];
|
||||||
|
rightAction: [];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const navBarStore = useNavBarStore();
|
||||||
|
const navBarRef = ref<HTMLElement>();
|
||||||
|
|
||||||
|
function getElement(): HTMLElement | undefined {
|
||||||
|
return navBarRef.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
getElement
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.ios26-nav-bar {
|
||||||
|
top: calc(env(safe-area-inset-top, 0px) + 2rem);
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 20;
|
||||||
|
height: 2.5rem;
|
||||||
|
padding: 0 .75rem;
|
||||||
|
position: fixed;
|
||||||
|
|
||||||
|
.pill {
|
||||||
|
height: 2.5rem;
|
||||||
|
display: flex;
|
||||||
|
min-width: 0;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(250, 250, 250, .72);
|
||||||
|
box-shadow: 0 .25rem .875rem rgba(0, 0, 0, .1);
|
||||||
|
backdrop-filter: blur(18px) saturate(180%);
|
||||||
|
-webkit-backdrop-filter: blur(18px) saturate(180%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.left,
|
||||||
|
.right,
|
||||||
|
#title {
|
||||||
|
top: 0;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left {
|
||||||
|
left: .75rem;
|
||||||
|
|
||||||
|
.back {
|
||||||
|
width: 2.5rem;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
left: 50%;
|
||||||
|
padding: 0 1rem;
|
||||||
|
min-width: 8rem;
|
||||||
|
max-width: calc(100vw - 4rem);
|
||||||
|
transform: translateX(-50%);
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.text {
|
||||||
|
overflow: hidden;
|
||||||
|
color: var(--td-text-color-primary, #333);
|
||||||
|
display: block;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
right: .75rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slot {
|
||||||
|
gap: .5rem;
|
||||||
|
max-width: 8rem;
|
||||||
|
padding: 0 .75rem;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
:deep(.t-badge) {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
|
||||||
|
:deep(.t-badge__content) {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: default;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
&.clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.back,
|
||||||
|
.right .button {
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
color: var(--td-text-color-primary, #333);
|
||||||
|
background: rgba(250, 250, 250, .72);
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
color: var(--td-text-color-disabled);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
|
||||||
|
.pill,
|
||||||
|
.back,
|
||||||
|
.button {
|
||||||
|
background: rgba(36, 36, 36, .72);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
<template>
|
||||||
|
<ios26-nav-bar
|
||||||
|
v-if="navBarStore.isShowing && isIos26Standalone"
|
||||||
|
ref="navBarRef"
|
||||||
|
@back="doBack"
|
||||||
|
@right-action="doRightAction"
|
||||||
|
/>
|
||||||
|
<default-nav-bar
|
||||||
|
v-else-if="navBarStore.isShowing"
|
||||||
|
ref="navBarRef"
|
||||||
|
@back="doBack"
|
||||||
|
@right-action="doRightAction"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import DefaultNavBar from "@/components/navbar/DefaultNavBar.vue";
|
||||||
|
import Ios26NavBar from "@/components/navbar/iOS26NavBar.vue";
|
||||||
|
import { useNavBarStore } from "@/store/navBarStore";
|
||||||
|
|
||||||
|
type NavBarExpose = {
|
||||||
|
getElement: () => HTMLElement | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
heightChange: [value: string];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const navBarStore = useNavBarStore();
|
||||||
|
|
||||||
|
// ---------- 导航高度 ----------
|
||||||
|
|
||||||
|
const navBarRef = ref<NavBarExpose>();
|
||||||
|
const isIos26Standalone = ref(false);
|
||||||
|
let navBarResizeObs: ResizeObserver | undefined;
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
isIos26Standalone.value = isIos26StandaloneMode();
|
||||||
|
await syncNavBarHeight();
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => navBarStore.isShowing, async () => {
|
||||||
|
await syncNavBarHeight();
|
||||||
|
}, { flush: "post" });
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (navBarResizeObs) {
|
||||||
|
navBarResizeObs.disconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function syncNavBarHeight(): Promise<void> {
|
||||||
|
if (navBarResizeObs) {
|
||||||
|
navBarResizeObs.disconnect();
|
||||||
|
navBarResizeObs = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
emitLayoutHeight();
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
const navBarElement = navBarRef.value?.getElement();
|
||||||
|
if (!navBarElement) {
|
||||||
|
navBarStore.setHeight(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
navBarResizeObs = new ResizeObserver((entries) => {
|
||||||
|
for (const entry of entries) {
|
||||||
|
navBarStore.setHeight(entry.contentRect.height);
|
||||||
|
emitLayoutHeight();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
navBarResizeObs.observe(navBarElement);
|
||||||
|
navBarStore.setHeight(navBarElement.getBoundingClientRect().height);
|
||||||
|
emitLayoutHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
function emitLayoutHeight(): void {
|
||||||
|
if (!navBarStore.isShowing) {
|
||||||
|
emit("heightChange", "0px");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isIos26Standalone.value) {
|
||||||
|
emit("heightChange", "calc(env(safe-area-inset-top, 0px) + 5.25rem)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit("heightChange", "48px");
|
||||||
|
}
|
||||||
|
|
||||||
|
function isIos26StandaloneMode(): boolean {
|
||||||
|
const nav = window.navigator as Navigator & { standalone?: boolean };
|
||||||
|
const isStandalone = nav.standalone === true || window.matchMedia("(display-mode: standalone)").matches;
|
||||||
|
if (!isStandalone) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 26 <= getIosEffectiveMajorVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iOS 26 standalone 的 UA 可能仍带 iPhone OS 18_x,但 Version 主版本会暴露新 WebApp 行为。
|
||||||
|
*/
|
||||||
|
function getIosEffectiveMajorVersion(): number {
|
||||||
|
const nav = window.navigator;
|
||||||
|
const userAgent = nav.userAgent;
|
||||||
|
const isIosLike = /iP(hone|od|ad)/.test(userAgent) || (nav.platform === "MacIntel" && 1 < nav.maxTouchPoints);
|
||||||
|
if (!isIosLike) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const osMajor = getUserAgentMajorVersion(userAgent, /OS (\d+)[._]/);
|
||||||
|
const versionMajor = getUserAgentMajorVersion(userAgent, /Version\/(\d+)[._]/);
|
||||||
|
return Math.max(osMajor, versionMajor);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUserAgentMajorVersion(userAgent: string, pattern: RegExp): number {
|
||||||
|
const match = userAgent.match(pattern);
|
||||||
|
if (!match) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Number(match[1]) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- 导航返回 ----------
|
||||||
|
|
||||||
|
function doBack(): void {
|
||||||
|
if (navBarStore.backTo) {
|
||||||
|
router.push(navBarStore.backTo);
|
||||||
|
navBarStore.setBackTo(undefined);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- 右侧操作 ----------
|
||||||
|
|
||||||
|
async function doRightAction(): Promise<void> {
|
||||||
|
if (navBarStore.rightDisabled || !navBarStore.rightAction) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await navBarStore.rightAction();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
+7
-173
@@ -1,57 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="main-layout">
|
<div class="main-layout">
|
||||||
<t-navbar
|
<app-nav-bar @height-change="onChangeNavBarHeight" />
|
||||||
ref="navBarRef"
|
|
||||||
v-if="navBarStore.isShowing"
|
|
||||||
class="nav-bar"
|
|
||||||
:left-arrow="!!navBarStore.canBack"
|
|
||||||
:z-index="20"
|
|
||||||
@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 class="nav-right-slot">
|
|
||||||
<div v-if="navBarStore.customRightVisible" id="app-nav-right" />
|
|
||||||
<t-badge
|
|
||||||
v-else-if="navBarStore.rightIcon && navBarStore.rightBadge !== undefined"
|
|
||||||
class="badge"
|
|
||||||
:count="navBarStore.rightBadge"
|
|
||||||
:max-count="99"
|
|
||||||
:show-zero="false"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
class="nav-btn"
|
|
||||||
type="button"
|
|
||||||
:disabled="navBarStore.rightDisabled"
|
|
||||||
@click="!navBarStore.rightDisabled && navBarStore.rightAction?.()"
|
|
||||||
>
|
|
||||||
<t-icon :name="navBarStore.rightIcon" size="1.25rem" />
|
|
||||||
</button>
|
|
||||||
</t-badge>
|
|
||||||
<button
|
|
||||||
v-else-if="navBarStore.rightIcon"
|
|
||||||
class="nav-btn"
|
|
||||||
type="button"
|
|
||||||
:disabled="navBarStore.rightDisabled"
|
|
||||||
@click="!navBarStore.rightDisabled && navBarStore.rightAction?.()"
|
|
||||||
>
|
|
||||||
<t-icon :name="navBarStore.rightIcon" size="1.25rem" />
|
|
||||||
</button>
|
|
||||||
<span
|
|
||||||
v-else
|
|
||||||
v-text="navBarStore.rightText"
|
|
||||||
class="nav-text"
|
|
||||||
:style="{ color: navBarStore.rightDisabled ? 'var(--td-text-color-disabled)' : navBarStore.rightTextColor ? navBarStore.rightTextColor : '#333' }"
|
|
||||||
:class="{ clickable: !!navBarStore.rightAction && !navBarStore.rightDisabled, disabled: navBarStore.rightDisabled }"
|
|
||||||
@click="!navBarStore.rightDisabled && navBarStore.rightAction?.()"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</t-navbar>
|
|
||||||
<div class="router-view">
|
<div class="router-view">
|
||||||
<page-transition />
|
<page-transition />
|
||||||
</div>
|
</div>
|
||||||
@@ -77,63 +26,24 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import AppNavBar from "@/components/navbar/index.vue";
|
||||||
import PageTransition from "@/components/PageTransition.vue";
|
import PageTransition from "@/components/PageTransition.vue";
|
||||||
import {useNavBarStore} from "@/store/navBarStore";
|
|
||||||
import {useTabBarStore} from "@/store/tabBarStore";
|
import {useTabBarStore} from "@/store/tabBarStore";
|
||||||
import { permissionCodes } from "@/utils/permission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
import { useUserStore } from "timi-web-api";
|
import { useUserStore } from "timi-web-api";
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const navBarStore = useNavBarStore();
|
|
||||||
const tabBarStore = useTabBarStore();
|
const tabBarStore = useTabBarStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
||||||
// ---------- 导航高度 ----------
|
|
||||||
|
|
||||||
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(() => {
|
const navBarHeight = ref("0px");
|
||||||
if (navBarStore.isShowing) {
|
|
||||||
return "48px";
|
function onChangeNavBarHeight(value: string): void {
|
||||||
}
|
navBarHeight.value = value;
|
||||||
return "0px";
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// ---------- Tab 切换 ----------
|
// ---------- Tab 切换 ----------
|
||||||
|
|
||||||
@@ -236,82 +146,6 @@ const tabBarHeight = computed(() => {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
transition: padding-bottom .24s ease;
|
transition: padding-bottom .24s ease;
|
||||||
|
|
||||||
.nav-bar {
|
|
||||||
|
|
||||||
: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);
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge {
|
|
||||||
|
|
||||||
:deep(.t-badge__content) {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-btn {
|
|
||||||
padding: 0;
|
|
||||||
border: none;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: var(--td-text-color-primary, #333);
|
|
||||||
background: transparent;
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
color: var(--td-text-color-disabled);
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
|
|
||||||
:deep(.t-badge) {
|
|
||||||
display: inline-flex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-text {
|
|
||||||
cursor: default;
|
|
||||||
|
|
||||||
&.clickable {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.t-navbar__right),
|
|
||||||
:deep(.t-navbar__right-content) {
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.router-view {
|
.router-view {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user