add FileExplorerPage
This commit is contained in:
@@ -1,20 +1,18 @@
|
||||
<template>
|
||||
<div class="page-transition">
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition v-if="hasTransition" :name="transitionName">
|
||||
<div class="pages" :key="route.fullPath">
|
||||
<transition :name="transitionName" :css="hasTransition">
|
||||
<div class="pages" :key="pageKey">
|
||||
<component :is="Component" />
|
||||
</div>
|
||||
</transition>
|
||||
<div v-else class="pages" :key="route.fullPath">
|
||||
<component :is="Component" />
|
||||
</div>
|
||||
</router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { RouteLocationNormalized } from "vue-router";
|
||||
import { viewDepthKey } from "vue-router";
|
||||
import { useGlobalUIStore } from "@/store/globalUIStore";
|
||||
|
||||
defineOptions({
|
||||
@@ -24,12 +22,26 @@ defineOptions({
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const globalUIStore = useGlobalUIStore();
|
||||
const viewDepth = inject(viewDepthKey, 0);
|
||||
|
||||
const transitionName = ref("push-left");
|
||||
const hasTransition = computed(() => transitionName.value !== "");
|
||||
const transitionName = ref("");
|
||||
const hasTransition = ref(false);
|
||||
const pageBackground = computed(() => globalUIStore.bodyBackground);
|
||||
const currentDepth = computed(() => Number(unref(viewDepth)));
|
||||
const pageKey = computed(() => {
|
||||
const depth = currentDepth.value;
|
||||
const matchedRecord = route.matched[depth];
|
||||
|
||||
// ---------- 路由深度计算 ----------
|
||||
if (!matchedRecord) {
|
||||
return route.fullPath;
|
||||
}
|
||||
|
||||
if (depth < route.matched.length - 1) {
|
||||
return matchedRecord.path;
|
||||
}
|
||||
|
||||
return route.fullPath;
|
||||
});
|
||||
|
||||
const pathCache = new Map<string, number>();
|
||||
|
||||
@@ -51,17 +63,25 @@ function calcDepth(sourceRoute: RouteLocationNormalized): number {
|
||||
return depth;
|
||||
}
|
||||
|
||||
// beforeEach 返回注销函数,组件卸载时必须调用,否则守卫会一直存在。
|
||||
const unregisterGuard = router.beforeEach((to, from) => {
|
||||
if (to.meta.tabBarVisible && from.meta.tabBarVisible) {
|
||||
transitionName.value = "";
|
||||
hasTransition.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const toDepth = calcDepth(to);
|
||||
const fromDepth = calcDepth(from);
|
||||
|
||||
if (fromDepth < toDepth) {
|
||||
transitionName.value = "push-left";
|
||||
hasTransition.value = true;
|
||||
} else if (toDepth < fromDepth) {
|
||||
transitionName.value = "push-right";
|
||||
hasTransition.value = true;
|
||||
} else {
|
||||
transitionName.value = "";
|
||||
hasTransition.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -84,28 +104,35 @@ onUnmounted(() => {
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
|
||||
// 这个 wrapper 必须独立,不能把 class=pages 直接挂在 <component> 上。
|
||||
// 否则页面根节点会和动画容器变成同一个 DOM 节点,容易导致 fixed 布局溢出视口。
|
||||
.pages {
|
||||
inset: 0;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
background: v-bind(pageBackground);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
// ---------- push-left / push-right 公共过渡状态 ----------
|
||||
|
||||
.push-left-enter-active,
|
||||
.push-left-leave-active,
|
||||
.push-right-enter-active,
|
||||
.push-right-leave-active {
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
transition: transform @duration @easing, opacity .2s linear;
|
||||
backface-visibility: hidden;
|
||||
}
|
||||
|
||||
// ---------- 前进:下一页从右滑入,当前页退到左侧 ----------
|
||||
.push-left-enter-active,
|
||||
.push-right-leave-active {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.push-left-leave-active,
|
||||
.push-right-enter-active {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.push-left-enter-from {
|
||||
transform: translate3d(100%, 0, 0);
|
||||
@@ -125,10 +152,7 @@ onUnmounted(() => {
|
||||
transform: translate3d(-@page-offset, 0, 0);
|
||||
}
|
||||
|
||||
// ---------- 返回:当前页从右滑出,上一页回到原位 ----------
|
||||
|
||||
.push-right-enter-active {
|
||||
z-index: -1;
|
||||
transition: transform @duration @easing, opacity .3s;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<template>
|
||||
<route-placeholder title="文件详情页" description="这里保留为二级详情占位,用于验证从文件页进入和返回时的滑动方向。" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
463
src/pages/file/FileExplorerPage.vue
Normal file
463
src/pages/file/FileExplorerPage.vue
Normal file
@@ -0,0 +1,463 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<section class="toolbar glass-white">
|
||||
<div class="path">
|
||||
<t-button
|
||||
size="small"
|
||||
variant="text"
|
||||
theme="primary"
|
||||
:disabled="!currentSegments.length"
|
||||
@click="openRoot"
|
||||
>
|
||||
根目录
|
||||
</t-button>
|
||||
<span v-for="(segment, index) in currentSegments" :key="`${index}-${segment}`" class="crumb-wrap">
|
||||
<span class="sep">/</span>
|
||||
<t-button
|
||||
size="small"
|
||||
variant="text"
|
||||
theme="primary"
|
||||
class="crumb"
|
||||
@click="openByIndex(index)"
|
||||
>
|
||||
<span v-text="segment"></span>
|
||||
</t-button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<t-button
|
||||
size="small"
|
||||
theme="primary"
|
||||
:variant="displayMode === 'list' ? 'base' : 'outline'"
|
||||
@click="setDisplayMode('list')"
|
||||
>
|
||||
列表
|
||||
</t-button>
|
||||
<t-button
|
||||
size="small"
|
||||
theme="primary"
|
||||
:variant="displayMode === 'grid' ? 'base' : 'outline'"
|
||||
@click="setDisplayMode('grid')"
|
||||
>
|
||||
平铺
|
||||
</t-button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-if="currentSegments.length" class="go-up" @click="openParent">
|
||||
<div class="icon dir">
|
||||
<t-icon name="rollback" />
|
||||
</div>
|
||||
<div class="meta">
|
||||
<p class="name">返回上一级</p>
|
||||
<p class="desc">当前目录的父级目录</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section :class="['list', `is-${displayMode}`]">
|
||||
<article
|
||||
v-for="item in currentItems"
|
||||
:key="item.path"
|
||||
class="item glass-white"
|
||||
@click="openItem(item)"
|
||||
>
|
||||
<div :class="['icon', item.type]">
|
||||
<t-icon :name="item.type === 'dir' ? 'folder' : item.icon" />
|
||||
</div>
|
||||
<div class="meta">
|
||||
<p class="name" v-text="item.name"></p>
|
||||
<p class="desc" v-text="formatItemDesc(item)"></p>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<t-empty v-if="!currentItems.length" description="当前目录为空" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
type DisplayMode = "list" | "grid";
|
||||
type FileItemType = "dir" | "file";
|
||||
|
||||
interface FileEntry {
|
||||
name: string;
|
||||
type: FileItemType;
|
||||
size?: string;
|
||||
updatedAt: string;
|
||||
icon: string;
|
||||
children?: FileEntry[];
|
||||
}
|
||||
|
||||
interface ExplorerItem extends Omit<FileEntry, "children"> {
|
||||
path: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
mode?: DisplayMode;
|
||||
path?: string | string[];
|
||||
useRoutePath?: boolean;
|
||||
}>(), {
|
||||
mode: "list",
|
||||
path: "",
|
||||
useRoutePath: true
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
"update:path": [value: string[]];
|
||||
"open-folder": [value: string[]];
|
||||
"open-file": [value: ExplorerItem];
|
||||
"update:mode": [value: DisplayMode];
|
||||
}>();
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const fileTree: FileEntry[] = [
|
||||
{
|
||||
name: "文档",
|
||||
type: "dir",
|
||||
icon: "folder",
|
||||
updatedAt: "今天 09:24",
|
||||
children: [
|
||||
{
|
||||
name: "项目说明.md",
|
||||
type: "file",
|
||||
size: "18 KB",
|
||||
icon: "file-text",
|
||||
updatedAt: "今天 09:20"
|
||||
},
|
||||
{
|
||||
name: "设计稿",
|
||||
type: "dir",
|
||||
icon: "folder",
|
||||
updatedAt: "昨天 18:10",
|
||||
children: [
|
||||
{
|
||||
name: "home.png",
|
||||
type: "file",
|
||||
size: "1.2 MB",
|
||||
icon: "image",
|
||||
updatedAt: "昨天 18:08"
|
||||
},
|
||||
{
|
||||
name: "file.png",
|
||||
type: "file",
|
||||
size: "984 KB",
|
||||
icon: "image",
|
||||
updatedAt: "昨天 18:03"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "媒体",
|
||||
type: "dir",
|
||||
icon: "folder",
|
||||
updatedAt: "今天 08:16",
|
||||
children: [
|
||||
{
|
||||
name: "音乐",
|
||||
type: "dir",
|
||||
icon: "folder",
|
||||
updatedAt: "今天 08:11",
|
||||
children: [
|
||||
{
|
||||
name: "demo.mp3",
|
||||
type: "file",
|
||||
size: "6.8 MB",
|
||||
icon: "music",
|
||||
updatedAt: "今天 08:10"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "封面.jpg",
|
||||
type: "file",
|
||||
size: "2.4 MB",
|
||||
icon: "image",
|
||||
updatedAt: "昨天 22:40"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "系统日志.log",
|
||||
type: "file",
|
||||
size: "428 KB",
|
||||
icon: "file-text",
|
||||
updatedAt: "今天 07:55"
|
||||
},
|
||||
{
|
||||
name: "backup.zip",
|
||||
type: "file",
|
||||
size: "126 MB",
|
||||
icon: "file-zip",
|
||||
updatedAt: "昨天 23:18"
|
||||
}
|
||||
];
|
||||
|
||||
const displayMode = ref<DisplayMode>(props.mode);
|
||||
const localSegments = ref<string[]>(normalizePath(props.path));
|
||||
|
||||
const shouldUseRoutePath = computed(() => {
|
||||
if (!props.useRoutePath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return route.name === "FilePage" || route.name === "FileExplorerPage";
|
||||
});
|
||||
|
||||
const routeSegments = computed(() => normalizePath(route.params.pathMatch));
|
||||
const currentSegments = computed(() => {
|
||||
if (shouldUseRoutePath.value) {
|
||||
return routeSegments.value;
|
||||
}
|
||||
|
||||
return localSegments.value;
|
||||
});
|
||||
|
||||
const currentItems = computed<ExplorerItem[]>(() => {
|
||||
const target = resolveEntries(currentSegments.value);
|
||||
return target.map((item) => ({
|
||||
...item,
|
||||
path: buildItemPath(currentSegments.value, item.name)
|
||||
}));
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.mode,
|
||||
(value) => {
|
||||
displayMode.value = value;
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.path,
|
||||
(value) => {
|
||||
if (shouldUseRoutePath.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
localSegments.value = normalizePath(value);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
function normalizePath(source?: string | string[]): string[] {
|
||||
if (Array.isArray(source)) {
|
||||
return source.filter((segment) => !!segment);
|
||||
}
|
||||
|
||||
if (!source) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return source.split("/").filter((segment) => !!segment);
|
||||
}
|
||||
|
||||
function resolveEntries(pathSegments: string[]): FileEntry[] {
|
||||
let entries = fileTree;
|
||||
|
||||
for (const segment of pathSegments) {
|
||||
const next = entries.find((item) => item.type === "dir" && item.name === segment);
|
||||
if (!next || !next.children) {
|
||||
return [];
|
||||
}
|
||||
|
||||
entries = next.children;
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
function buildItemPath(pathSegments: string[], name: string): string {
|
||||
return [...pathSegments, name].join("/");
|
||||
}
|
||||
|
||||
function setDisplayMode(mode: DisplayMode): void {
|
||||
displayMode.value = mode;
|
||||
emit("update:mode", mode);
|
||||
}
|
||||
|
||||
async function openRoot(): Promise<void> {
|
||||
await syncPath([]);
|
||||
}
|
||||
|
||||
async function openParent(): Promise<void> {
|
||||
if (shouldUseRoutePath.value && route.name === "FileExplorerPage" && currentSegments.value.length === 1) {
|
||||
await router.back();
|
||||
return;
|
||||
}
|
||||
|
||||
await syncPath(currentSegments.value.slice(0, -1));
|
||||
}
|
||||
|
||||
async function openByIndex(index: number): Promise<void> {
|
||||
await syncPath(currentSegments.value.slice(0, index + 1));
|
||||
}
|
||||
|
||||
async function openItem(item: ExplorerItem): Promise<void> {
|
||||
if (item.type === "dir") {
|
||||
const nextSegments = [...currentSegments.value, item.name];
|
||||
emit("open-folder", nextSegments);
|
||||
await syncPath(nextSegments);
|
||||
return;
|
||||
}
|
||||
|
||||
emit("open-file", item);
|
||||
}
|
||||
|
||||
async function syncPath(nextSegments: string[]): Promise<void> {
|
||||
if (shouldUseRoutePath.value) {
|
||||
if (!nextSegments.length) {
|
||||
await router.push("/");
|
||||
return;
|
||||
}
|
||||
|
||||
await router.push({
|
||||
name: "FileExplorerPage",
|
||||
params: {
|
||||
pathMatch: nextSegments
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
localSegments.value = nextSegments;
|
||||
emit("update:path", nextSegments);
|
||||
}
|
||||
|
||||
function formatItemDesc(item: ExplorerItem): string {
|
||||
if (item.type === "dir") {
|
||||
return `文件夹 · ${item.updatedAt}`;
|
||||
}
|
||||
|
||||
return `${item.size || "--"} · ${item.updatedAt}`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.page {
|
||||
gap: 1rem;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
padding: 1rem;
|
||||
overflow: auto;
|
||||
flex-direction: column;
|
||||
|
||||
.toolbar,
|
||||
.go-up,
|
||||
.item {
|
||||
border: 1px solid var(--app-line);
|
||||
background: var(--app-card);
|
||||
box-shadow: 0 .35rem 1rem rgba(17, 32, 56, .05);
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
gap: .75rem;
|
||||
display: flex;
|
||||
padding: .875rem;
|
||||
border-radius: 1rem;
|
||||
flex-direction: column;
|
||||
|
||||
.path,
|
||||
.actions {
|
||||
gap: .25rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.crumb-wrap {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sep {
|
||||
color: var(--app-sub);
|
||||
}
|
||||
|
||||
.crumb {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.go-up,
|
||||
.item {
|
||||
gap: .875rem;
|
||||
display: flex;
|
||||
padding: .875rem 1rem;
|
||||
border-radius: 1rem;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.list {
|
||||
gap: .75rem;
|
||||
display: grid;
|
||||
|
||||
&.is-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
|
||||
.item {
|
||||
padding: 1rem .875rem;
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
|
||||
.meta {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 2.75rem;
|
||||
height: 2.75rem;
|
||||
display: flex;
|
||||
border-radius: .9rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #FFF;
|
||||
background: linear-gradient(135deg, #4D8DFF, #76A9FF);
|
||||
|
||||
&.dir {
|
||||
background: linear-gradient(135deg, #FF9C3D, #FFBF69);
|
||||
}
|
||||
}
|
||||
|
||||
.meta {
|
||||
gap: .3rem;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.name,
|
||||
.desc {
|
||||
margin: 0;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.name {
|
||||
color: var(--app-text);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.desc {
|
||||
color: var(--app-sub);
|
||||
font-size: .8125rem;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.theme-dark) .page {
|
||||
.toolbar,
|
||||
.go-up,
|
||||
.item {
|
||||
box-shadow: 0 .35rem 1rem rgba(0, 0, 0, .22);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,21 +0,0 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<route-placeholder title="LoginPage" description="登录页占位,当前仅保留独立路由入口,不引入业务表单。" />
|
||||
<t-button block theme="primary" @click="router.replace('/')">
|
||||
进入应用首页
|
||||
</t-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const router = useRouter();
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.page {
|
||||
gap: 1rem;
|
||||
display: flex;
|
||||
padding: 1.2rem;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
@@ -1,25 +1,7 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<route-placeholder title="文件页" description="这里保留为文件管理入口,用于验证首页标签、布局容器和二级详情页切换。" />
|
||||
<t-button block theme="primary" @click="openFileDetail">
|
||||
打开文件详情页
|
||||
</t-button>
|
||||
</div>
|
||||
<file-explorer-page />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const router = useRouter();
|
||||
|
||||
async function openFileDetail(): Promise<void> {
|
||||
await router.push("/files/detail/demo-file");
|
||||
}
|
||||
import FileExplorerPage from "@/pages/file/FileExplorerPage.vue";
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.page {
|
||||
gap: 1rem;
|
||||
display: flex;
|
||||
padding: 1.2rem;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<template>
|
||||
<route-placeholder title="阅读页" description="该页面暂时不在 tab 中展示,只保留占位,后续可按需恢复。" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
@@ -4,10 +4,9 @@ import MainLayout from "@/layout/MainLayout.vue";
|
||||
import tabs from "@/router/tabs";
|
||||
import { DEFAULT_BODY_BACKGROUND, useGlobalUIStore } from "@/store/globalUIStore";
|
||||
import { useSettingStore } from "@/store/settingStore";
|
||||
import LoginPage from "@/pages/system/LoginPage.vue";
|
||||
import NotFoundPage from "@/pages/system/NotFoundPage.vue";
|
||||
import ServerIndexPage from "@/pages/system/ServerIndexPage.vue";
|
||||
import FileDetailPage from "@/pages/detail/FileDetailPage.vue";
|
||||
import FileExplorerPage from "@/pages/file/FileExplorerPage.vue";
|
||||
import ServerLogPage from "@/pages/detail/ServerLogPage.vue";
|
||||
|
||||
const router = createRouter({
|
||||
@@ -28,18 +27,6 @@ const router = createRouter({
|
||||
name: "RootLayout",
|
||||
component: RootLayout,
|
||||
children: [
|
||||
{
|
||||
path: "/login",
|
||||
name: "LoginPage",
|
||||
meta: {
|
||||
depth: 1,
|
||||
ignoreConnectCheck: true,
|
||||
navBarVisible: false,
|
||||
tabBarVisible: false,
|
||||
bodyBackground: "#FFF"
|
||||
},
|
||||
component: LoginPage
|
||||
},
|
||||
{
|
||||
path: "/server-index",
|
||||
name: "ServerIndexPage",
|
||||
@@ -63,16 +50,17 @@ const router = createRouter({
|
||||
children: [
|
||||
...tabs,
|
||||
{
|
||||
path: "/files/detail/:id",
|
||||
name: "FileDetailPage",
|
||||
path: "/files/:pathMatch(.*)+",
|
||||
name: "FileExplorerPage",
|
||||
meta: {
|
||||
depth: 3,
|
||||
dynamicDepth: true,
|
||||
baseDepth: 2,
|
||||
navBarVisible: true,
|
||||
navBarCanBack: true,
|
||||
navBarTitle: "文件详情",
|
||||
navBarTitle: "文件",
|
||||
tabBarVisible: false
|
||||
},
|
||||
component: FileDetailPage
|
||||
component: FileExplorerPage
|
||||
},
|
||||
{
|
||||
path: "/server/logs",
|
||||
|
||||
@@ -12,7 +12,8 @@ const tabs: RouteRecordRaw[] = [
|
||||
navBarVisible: true,
|
||||
navBarTitle: "文件",
|
||||
tabBarVisible: true,
|
||||
tabBarPadding: true
|
||||
tabBarPadding: true,
|
||||
bodyBackground: "#FFF"
|
||||
},
|
||||
component: FilePage
|
||||
},
|
||||
@@ -24,7 +25,8 @@ const tabs: RouteRecordRaw[] = [
|
||||
navBarVisible: true,
|
||||
navBarTitle: "状态",
|
||||
tabBarVisible: true,
|
||||
tabBarPadding: true
|
||||
tabBarPadding: true,
|
||||
bodyBackground: "#FFF"
|
||||
},
|
||||
component: ServerStatusPage
|
||||
},
|
||||
@@ -37,7 +39,7 @@ const tabs: RouteRecordRaw[] = [
|
||||
navBarTitle: "设置",
|
||||
tabBarVisible: true,
|
||||
tabBarPadding: true,
|
||||
bodyBackground: "var(--app-bg)"
|
||||
bodyBackground: "#FFF"
|
||||
},
|
||||
component: SettingsPage
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user