feat(components): 新建网盘页面布局

This commit is contained in:
黄磊 2025-02-28 03:32:18 -05:00
parent 2953fa8676
commit f331b33b0a
18 changed files with 406 additions and 4 deletions

View File

@ -0,0 +1,11 @@
<svg t="1642407370336" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6354"
width="200" height="200">
<path
d="M952.888889 281.6V910.222222c0 62.862222-50.915556 113.777778-113.777778 113.777778H156.444444c-62.862222 0-113.777778-50.915556-113.777777-113.777778V113.777778c0-62.862222 50.915556-113.777778 113.777777-113.777778h514.844445L952.888889 281.6z"
fill="#85BCFF" p-id="6355"></path>
<path d="M676.664889 167.822222V0l281.6 281.6h-167.822222c-62.862222 0-113.777778-50.915556-113.777778-113.777778"
fill="#529EE0" p-id="6356"></path>
<path
d="M685.824 363.804444a53.76 53.76 0 0 1 53.731556 53.731556v307.029333a53.76 53.76 0 0 1-53.731556 53.731556H309.76a53.731556 53.731556 0 0 1-53.731556-53.76V417.564444c0-29.667556 24.035556-53.731556 53.731556-53.731555H685.795556z m-72.903111 149.674667l-138.183111 146.545778-80.583111-62.805333-92.131556 94.208v31.402666c0 11.548444 10.325333 20.906667 23.04 20.906667h345.400889c12.714667 0 23.04-9.386667 23.04-20.906667v-125.610666l-80.583111-83.740445z m-227.896889-85.532444a32.085333 32.085333 0 1 0 0 64.142222 32.085333 32.085333 0 0 0 0-64.142222z"
fill="#FFFFFF" p-id="6357"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,11 @@
<svg t="1642407502942" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7293"
width="200" height="200">
<path
d="M967.111111 281.6V910.222222c0 62.862222-50.915556 113.777778-113.777778 113.777778H170.666667c-62.862222 0-113.777778-50.915556-113.777778-113.777778V113.777778c0-62.862222 50.915556-113.777778 113.777778-113.777778h514.844444L967.111111 281.6z"
fill="#A15FDE" p-id="7294"></path>
<path d="M685.511111 196.266667V0L967.111111 281.6H770.844444a85.333333 85.333333 0 0 1-85.333333-85.333333"
fill="#C386F0" p-id="7295"></path>
<path
d="M669.980444 426.268444v236.999112c0 26.254222-31.857778 47.587556-71.082666 47.587555-39.253333 0-70.741333-21.333333-70.741334-47.587555 0-26.282667 31.516444-47.587556 70.741334-47.587556 14.848 0 28.728889 3.100444 40.163555 8.334222v-165.916444l-205.767111 48.497778v211.057777c0 26.254222-32.142222 47.559111-71.992889 47.559111-39.850667 0-72.305778-21.333333-72.305777-47.559111 0-26.282667 32.426667-47.587556 72.305777-47.587555a96.711111 96.711111 0 0 1 41.102223 8.647111V474.168889c0-14.222222 9.870222-26.88 23.779555-29.980445l205.795556-47.900444a30.862222 30.862222 0 0 1 38.001777 29.980444"
fill="#FFFFFF" p-id="7296"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,11 @@
<svg t="1642408119178" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="53519" width="200" height="200">
<path
d="M967.111111 281.6V910.222222c0 62.862222-50.915556 113.777778-113.777778 113.777778H170.666667c-62.862222 0-113.777778-50.915556-113.777778-113.777778V113.777778c0-62.862222 50.915556-113.777778 113.777778-113.777778h514.844444L967.111111 281.6z"
fill="#BABABA" p-id="53520"></path>
<path d="M685.511111 167.822222V0L967.111111 281.6H799.288889c-62.862222 0-113.777778-50.915556-113.777778-113.777778"
fill="#979797" p-id="53521"></path>
<path
d="M733.667556 632.689778a111.104 111.104 0 0 1-110.819556 110.819555h-221.667556a111.132444 111.132444 0 0 1-110.848-110.819555 111.047111 111.047111 0 0 1 99.754667-110.279111A122.197333 122.197333 0 0 1 512 407.694222a122.197333 122.197333 0 0 1 121.912889 114.716445 111.160889 111.160889 0 0 1 99.754667 110.279111"
fill="#FFFFFF" p-id="53522"></path>
</svg>

After

Width:  |  Height:  |  Size: 1002 B

View File

@ -0,0 +1,11 @@
<svg t="1642407315436" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5906"
width="200" height="200">
<path
d="M967.111111 281.6V910.222222c0 62.862222-50.915556 113.777778-113.777778 113.777778H170.666667c-62.862222 0-113.777778-50.915556-113.777778-113.777778V113.777778c0-62.862222 50.915556-113.777778 113.777778-113.777778h514.844444L967.111111 281.6z"
fill="#6D9FE5" p-id="5907"></path>
<path d="M685.511111 167.822222V0L967.111111 281.6H799.288889c-62.862222 0-113.777778-50.915556-113.777778-113.777778"
fill="#4B80CB" p-id="5908"></path>
<path
d="M344.177778 485.575111h312.888889V426.666667h-312.888889zM471.153778 770.019556h58.908444v-284.444445h-58.908444z"
fill="#FFFFFF" p-id="5909"></path>
</svg>

After

Width:  |  Height:  |  Size: 795 B

View File

@ -0,0 +1,14 @@
<svg t="1642407389455" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6501"
width="200" height="200">
<path
d="M967.111111 281.6V910.222222c0 62.862222-50.915556 113.777778-113.777778 113.777778H170.666667c-62.862222 0-113.777778-50.915556-113.777778-113.777778V113.777778c0-62.862222 50.915556-113.777778 113.777778-113.777778h514.844444L967.111111 281.6z"
fill="#C386F0" p-id="6502"></path>
<path
d="M284.444444 398.222222m42.666667 0l298.666667 0q42.666667 0 42.666666 42.666667l0 234.666667q0 42.666667-42.666666 42.666666l-298.666667 0q-42.666667 0-42.666667-42.666666l0-234.666667q0-42.666667 42.666667-42.666667Z"
fill="#FFFFFF" p-id="6503"></path>
<path
d="M738.417778 457.841778a31.971556 31.971556 0 0 1 48.014222 27.676444v154.538667c0 24.632889-26.652444 40.021333-47.985778 27.704889L684.430222 636.586667V488.96z"
fill="#FFFFFF" p-id="6504"></path>
<path d="M685.511111 167.822222V0L967.111111 281.6H799.288889c-62.862222 0-113.777778-50.915556-113.777778-113.777778"
fill="#A15FDE" p-id="6505"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1 @@
<svg t="1684652847211" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2238" width="200" height="200"><path d="M0 0m372.363636 0l279.272728 0q372.363636 0 372.363636 372.363636l0 279.272728q0 372.363636-372.363636 372.363636l-279.272728 0q-372.363636 0-372.363636-372.363636l0-279.272728q0-372.363636 372.363636-372.363636Z" fill="#F7A647" p-id="2239"></path><path d="M232.727273 303.872C232.727273 290.327273 243.781818 279.272727 257.326545 279.272727h148.631273c5.620364 0 10.984727 2.292364 14.848 6.283637l50.897455 52.805818c1.186909 1.233455 2.210909 2.594909 3.037091 4.049454h259.874909c31.138909 0 56.657455 25.460364 56.657454 56.610909V688.058182c0 31.150545-25.518545 56.669091-56.657454 56.669091H289.396364C258.245818 744.727273 232.727273 719.208727 232.727273 688.058182V303.872zM726.702545 556.218182h-239.825454a10.216727 10.216727 0 0 0-10.205091 10.205091v25.390545c0 5.620364 4.584727 10.205091 10.205091 10.205091h239.825454a10.216727 10.216727 0 0 0 10.205091-10.205091v-25.390545a10.216727 10.216727 0 0 0-10.205091-10.205091z m-0.058181-87.691637h-239.825455a10.216727 10.216727 0 0 0-10.205091 10.205091h-0.069818v25.390546c0 5.632 4.573091 10.216727 10.205091 10.216727h239.895273a10.216727 10.216727 0 0 0 10.205091-10.216727V478.72a10.216727 10.216727 0 0 0-10.205091-10.205091z" fill="#FFFFFF" p-id="2240"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1740719602812" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3072" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M462.266 167.466L134.168 451.847l328.098 284.359V544.827s454.799-147.683 362.705 311.707c0 0 293.586-557.828-368.422-519.544l5.719-169.527z" fill="#2C2C2C" p-id="3073"></path></svg>

After

Width:  |  Height:  |  Size: 514 B

View File

@ -3,5 +3,6 @@ export enum SetupStoreId {
Theme = 'theme-store',
Auth = 'auth-store',
Route = 'route-store',
Tab = 'tab-store'
Tab = 'tab-store',
Pan = 'pan-store'
}

View File

@ -26,7 +26,7 @@ withDefaults(defineProps<Props>(), {
<RouterLink to="/" class="w-full flex-center nowrap-hidden">
<SystemLogo class="text-32px text-primary" />
<h2 v-show="showTitle" class="pl-8px text-16px text-primary font-bold transition duration-300 ease-in-out">
{{ $t(title) }}
{{ title }}
</h2>
</RouterLink>
</template>

View File

@ -10,6 +10,7 @@ import { getRouteName } from '@/router/elegant/transform';
import { useAuthStore } from '@/store/modules/auth';
import { useRouteStore } from '@/store/modules/route';
import { localStg } from '@/utils/storage';
import { usePanStore } from '@/store/modules/pan';
/**
* create route guard
@ -19,10 +20,17 @@ import { localStg } from '@/utils/storage';
export function createRouteGuard(router: Router) {
router.beforeEach(async (to, from, next) => {
const routeStore = useRouteStore();
const panStore = usePanStore();
// 每次路由变化时更新页面类型
routeStore.setPageType(to);
const location = await initRoute(to);
if (to.name === 'pan') {
// 从路由参数中初始化路径
const pathParams = to.query.path as string;
panStore.initPathFromRoute(pathParams);
}
if (location) {
next(location);
return;

View File

@ -0,0 +1,58 @@
import { defineStore } from 'pinia';
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { SetupStoreId } from '@/enum';
import { localStg } from '@/utils/storage';
export const usePanStore = defineStore(SetupStoreId.Pan, () => {
const fileShowMode = ref<UnionKey.FileListMode>(localStg.get('fileShowMode') || 'grid');
const currentPath = ref<string[]>([]);
// 切换文件列表视图的方法
const toggleFileShowMode = (mode: UnionKey.FileListMode) => {
fileShowMode.value = mode;
localStg.set('fileShowMode', mode);
};
// 初始化路径
const initPathFromRoute = (pathStr?: string) => {
if (!pathStr) {
currentPath.value = [];
return;
}
const pathArr = pathStr.split('/');
pathArr.shift();
currentPath.value = pathArr;
};
// 带路由更新的导航方法
const navigateTo = async (path: string[]) => {
const router = useRouter();
currentPath.value = path;
// 更新路由(使用 replace 避免产生多余历史记录)
await router.replace({ query: { path: path.length ? `/${path.join('/')}` : undefined } });
};
// 进入文件夹方法
const enterFolder = async (folderName: string) => {
await navigateTo([...currentPath.value, folderName]);
};
// 返回上级方法
const backToParent = async () => {
if (currentPath.value.length > 0) {
await navigateTo(currentPath.value.slice(0, -1));
}
};
return {
currentPath,
initPathFromRoute,
navigateTo,
enterFolder,
backToParent,
fileShowMode,
toggleFileShowMode
};
});

View File

@ -17,8 +17,11 @@ declare module 'vue' {
IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
IconAntDesignReloadOutlined: typeof import('~icons/ant-design/reload-outlined')['default']
IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default']
'IconCuida:swapVerticalArrowsOutline': typeof import('~icons/cuida/swap-vertical-arrows-outline')['default']
'IconFluent:multiselect20Filled': typeof import('~icons/fluent/multiselect20-filled')['default']
IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
'IconIc:outlineFileUpload': typeof import('~icons/ic/outline-file-upload')['default']
'IconIc:roundPlus': typeof import('~icons/ic/round-plus')['default']
IconIcRoundDelete: typeof import('~icons/ic/round-delete')['default']
IconIcRoundPlus: typeof import('~icons/ic/round-plus')['default']
@ -30,12 +33,16 @@ declare module 'vue' {
IconLocalLogo: typeof import('~icons/local/logo')['default']
IconLocalWechat: typeof import('~icons/local/wechat')['default']
IconLocalWecom: typeof import('~icons/local/wecom')['default']
'IconMaterialSymbols:apps': typeof import('~icons/material-symbols/apps')['default']
'IconMdi:formatListBulleted': typeof import('~icons/mdi/format-list-bulleted')['default']
IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
IconMdiDrag: typeof import('~icons/mdi/drag')['default']
IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default']
IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
IconMdiRefresh: typeof import('~icons/mdi/refresh')['default']
'IconMeteorIcons:trashCan': typeof import('~icons/meteor-icons/trash-can')['default']
'IconTabler:filter': typeof import('~icons/tabler/filter')['default']
IconUilSearch: typeof import('~icons/uil/search')['default']
LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
@ -44,6 +51,7 @@ declare module 'vue' {
NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
NButton: typeof import('naive-ui')['NButton']
NButtonGroup: typeof import('naive-ui')['NButtonGroup']
NCard: typeof import('naive-ui')['NCard']
NCheckbox: typeof import('naive-ui')['NCheckbox']
NColorPicker: typeof import('naive-ui')['NColorPicker']
@ -62,6 +70,7 @@ declare module 'vue' {
NFormItemGi: typeof import('naive-ui')['NFormItemGi']
NGi: typeof import('naive-ui')['NGi']
NGrid: typeof import('naive-ui')['NGrid']
NGridItem: typeof import('naive-ui')['NGridItem']
NInput: typeof import('naive-ui')['NInput']
NInputGroup: typeof import('naive-ui')['NInputGroup']
NInputNumber: typeof import('naive-ui')['NInputNumber']
@ -74,11 +83,13 @@ declare module 'vue' {
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
NPopconfirm: typeof import('naive-ui')['NPopconfirm']
NPopover: typeof import('naive-ui')['NPopover']
NProgress: typeof import('naive-ui')['NProgress']
NRadio: typeof import('naive-ui')['NRadio']
NRadioGroup: typeof import('naive-ui')['NRadioGroup']
NScrollbar: typeof import('naive-ui')['NScrollbar']
NSelect: typeof import('naive-ui')['NSelect']
NSpace: typeof import('naive-ui')['NSpace']
NSpin: typeof import('naive-ui')['NSpin']
NStatistic: typeof import('naive-ui')['NStatistic']
NSwitch: typeof import('naive-ui')['NSwitch']
NTab: typeof import('naive-ui')['NTab']

View File

@ -37,5 +37,6 @@ declare namespace StorageType {
layout: UnionKey.ThemeLayoutMode;
siderCollapse: boolean;
};
fileShowMode: UnionKey.FileListMode;
}
}

View File

@ -53,6 +53,8 @@ declare namespace UnionKey {
type SystemPageType = 'pan' | 'admin' | null;
type FileListMode = 'grid' | 'list';
/** Unocss animate key */
type UnoCssAnimateKey =
| 'pulse'

View File

@ -0,0 +1,18 @@
<script setup lang="ts">
defineOptions({ name: 'FilePath' });
</script>
<template>
<div class="flex-y-center gap-8px">
<SvgIcon local-icon="return" class="text-size-icon-large" />
<NBreadcrumb>
<NBreadcrumbItem separator=">">
根目录
<template #separator></template>
</NBreadcrumbItem>
<NBreadcrumbItem separator=">">新建文件夹</NBreadcrumbItem>
</NBreadcrumb>
</div>
</template>
<style scoped></style>

View File

@ -1,7 +1,27 @@
<script setup lang="ts"></script>
<script setup lang="ts">
import { computed } from 'vue';
import { useAppStore } from '@/store/modules/app';
import FileAside from './modules/file-aside.vue';
import FileMain from './modules/file-main.vue';
const appStore = useAppStore();
const gap = computed(() => (appStore.isMobile ? 0 : 16));
</script>
<template>
<div>网盘页面</div>
<div class="h-full flex-col">
<div class="h-full flex pb[-28px]">
<NGrid :x-gap="gap" responsive="screen" item-responsive>
<NGridItem span="0 s:8 m:6 l:5 xl:4">
<FileAside></FileAside>
</NGridItem>
<NGridItem span="24 s:16 m:18 l:19 xl:20">
<FileMain />
</NGridItem>
</NGrid>
</div>
</div>
</template>
<style scoped></style>

View File

@ -0,0 +1,86 @@
<script setup lang="ts">
import { ref } from 'vue';
import { useSvgIcon } from '@/hooks/common/icon';
defineOptions({
name: 'FileAside'
});
const { SvgIconVNode } = useSvgIcon();
const selectedKey = ref('2');
const showPersent = ref(false);
const fileMenuOptions = [
{
key: '1',
label: '文件类型',
icon: SvgIconVNode({ icon: 'uim:apps' }),
children: [
{
key: '2',
label: '全部',
icon: SvgIconVNode({ localIcon: 'menu-file', fontSize: 28 })
},
{
key: '3',
label: '图片',
icon: SvgIconVNode({ localIcon: 'file-image', fontSize: 28 })
},
{
key: '4',
label: '文档',
icon: SvgIconVNode({ localIcon: 'file-txt', fontSize: 28 })
},
{
key: '5',
label: '视频',
icon: SvgIconVNode({ localIcon: 'file-video', fontSize: 28 })
},
{
key: '6',
label: '音频',
icon: SvgIconVNode({ localIcon: 'file-music', fontSize: 28 })
},
{
key: '7',
label: '其他',
icon: SvgIconVNode({ localIcon: 'file-other', fontSize: 28 })
}
]
}
];
</script>
<template>
<div class="h-full flex-col">
<NCard
title="文件列表"
:bordered="false"
size="small"
:content-style="{ padding: 0, display: 'flex', flexDirection: 'column' }"
class="h-full flex-1"
>
<template #header-extra>
<NTooltip trigger="hover">
<template #trigger>
<NSwitch v-model:value="showPersent" :round="false" size="small" />
</template>
显示容量
</NTooltip>
</template>
<NMenu v-model:value="selectedKey" :options="fileMenuOptions"></NMenu>
</NCard>
<div v-show="showPersent" class="mt-10px box-border">
<NCard :bordered="false">
<NSpace justify="center">
<NProgress type="circle" :percentage="30" :stroke-width="5" />
<NStatistic label="剩余容量 / 总容量" value="512GB / 1T"></NStatistic>
</NSpace>
</NCard>
</div>
</div>
</template>
<style scoped></style>

View File

@ -0,0 +1,137 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
import { useSvgIcon } from '@/hooks/common/icon';
import { usePanStore } from '@/store/modules/pan';
import FilePath from '../components/file-path.vue';
defineOptions({
name: 'FileMain'
});
const { SvgIconVNode } = useSvgIcon();
const panStore = usePanStore();
//
const isBatchMode = ref<boolean>(false);
//
const show = ref(false);
//
const currentMode = computed(() => panStore.fileShowMode);
//
const toggleMode = () => {
const newMode = currentMode.value === 'grid' ? 'list' : 'grid';
panStore.toggleFileShowMode(newMode);
};
const uploadOptions = [
{
key: 'file',
label: '上传文件',
icon: SvgIconVNode({ localIcon: 'upload-file', fontSize: 20 }),
props: {
onClick: () => {
// handleFileUpload();
window.$message?.info('上传文件功能');
}
}
},
{
key: 'folder',
label: '上传文件夹',
icon: SvgIconVNode({ localIcon: 'upload-folder', fontSize: 20 }),
props: {
onClick: () => {
// handleFolderUpload();
window.$message?.info('上传文件夹功能');
}
}
}
];
</script>
<template>
<div class="h-full flex-col overflow-hidden">
<NCard class="h-full">
<FilePath />
<NSpace justify="space-between" class="mt-10px">
<NSpace>
<!--上传操作按钮-->
<NDropdown trigger="click" :options="uploadOptions">
<NButton type="primary" round>
<template #icon>
<icon-ic:outline-file-upload />
</template>
上传
</NButton>
</NDropdown>
<!--搜索框-->
<NInputGroup>
<NSelect class="w-150px"></NSelect>
<NInput placeholder="请输入关键词..."></NInput>
<NButton type="primary">
<template #icon>
<icon-uil-search />
</template>
搜索
</NButton>
</NInputGroup>
</NSpace>
<!--右侧按钮及功能-->
<NSpace>
<NButton v-if="isBatchMode" type="error">
<template #icon>
<icon-meteor-icons:trash-can />
</template>
</NButton>
<NButton type="primary" @click="isBatchMode = !isBatchMode">
<template #icon>
<icon-fluent:multiselect-20-filled />
</template>
<template #default>{{ isBatchMode ? '取消批量' : '批量操作' }}</template>
</NButton>
<NButtonGroup>
<NTooltip placement="bottom" trigger="hover">
<template #trigger>
<NButton>
<template #icon>
<icon-cuida:swap-vertical-arrows-outline />
</template>
</NButton>
</template>
<span>传输列表</span>
</NTooltip>
<NTooltip placement="bottom" trigger="hover">
<template #trigger>
<NButton>
<template #icon>
<icon-tabler:filter />
</template>
</NButton>
</template>
<span>排序</span>
</NTooltip>
<NTooltip placement="bottom" trigger="hover">
<template #trigger>
<NButton @click="toggleMode">
<template #icon>
<icon-material-symbols:apps v-if="currentMode === 'grid'" />
<icon-mdi:format-list-bulleted v-else />
</template>
</NButton>
</template>
<span>视图</span>
</NTooltip>
</NButtonGroup>
</NSpace>
</NSpace>
<!--文件列表--宫格模式-->
<NSpin :show="show" class="box-border flex-col px-0 py-16px">
<FileGrid />
</NSpin>
</NCard>
</div>
</template>
<style scoped></style>