feat: add page components

This commit is contained in:
张传龙 2022-08-28 19:37:23 +08:00
parent 841bab0d63
commit 079761b6fd
23 changed files with 272 additions and 221 deletions

View File

@ -44,7 +44,7 @@
"fs-extra": "^10.0.1",
"husky": "^8.0.1",
"lint-staged": "^13.0.3",
"naive-ui": "^2.32.1",
"naive-ui": "^2.32.2",
"prettier": "^2.6.1",
"rollup-plugin-visualizer": "^5.6.0",
"sass": "^1.49.10",

60
pnpm-lock.yaml generated
View File

@ -26,7 +26,7 @@ specifiers:
lodash-es: ^4.17.21
md-editor-v3: ^1.11.4
mockjs: ^1.1.0
naive-ui: ^2.32.1
naive-ui: ^2.32.2
pinia: ^2.0.13
prettier: ^2.6.1
rollup-plugin-visualizer: ^5.6.0
@ -75,7 +75,7 @@ devDependencies:
fs-extra: 10.0.1
husky: 8.0.1
lint-staged: 13.0.3
naive-ui: 2.32.1_vue@3.2.31
naive-ui: 2.32.2_vue@3.2.31
prettier: 2.6.1
rollup-plugin-visualizer: 5.6.0
sass: 1.49.10
@ -314,16 +314,16 @@ packages:
'@jridgewell/trace-mapping': 0.3.9
dev: true
/@css-render/plugin-bem/0.15.10_css-render@0.15.10:
resolution: {integrity: sha512-V7b08sM2PWJlXI7BJiVIa0Sg30H3u/jHay4AclNXfF2yRFwwb4ZJjggsMfzwj3WSihAdNf2WTqvOU5qsOD80Dg==}
/@css-render/plugin-bem/0.15.11_css-render@0.15.11:
resolution: {integrity: sha512-Bn8qadYPIz5DhZ4obTGHOJzeziQH6kY0+Fk5AEvwuuy378SLwwvXuuoechLjBHcgKkPCM03Oo4dDSGP/6NMdyw==}
peerDependencies:
css-render: ~0.15.10
css-render: ~0.15.11
dependencies:
css-render: 0.15.10
css-render: 0.15.11
dev: true
/@css-render/vue3-ssr/0.15.10_vue@3.2.31:
resolution: {integrity: sha512-keGKnkB2nyVGoA8GezMKNsmvTGXEzgLOGGlgshwOTSEzd1dsROyZ2m/khJ9jV5zbzDM4rWeAWbWF0zwHemsJcw==}
/@css-render/vue3-ssr/0.15.11_vue@3.2.31:
resolution: {integrity: sha512-n+SuqLPbY30FUTM8slX75OaEG+c8XlTOFrAklekX2XQGvBbz9XdBE6hTEgGlV5kPcTMqTJeCG7Vzhs9/29VC7w==}
peerDependencies:
vue: ^3.0.11
dependencies:
@ -436,8 +436,8 @@ packages:
'@jridgewell/sourcemap-codec': 1.4.14
dev: true
/@juggle/resize-observer/3.3.1:
resolution: {integrity: sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw==}
/@juggle/resize-observer/3.4.0:
resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==}
dev: true
/@nodelib/fs.scandir/2.1.5:
@ -534,11 +534,11 @@ packages:
/@types/lodash-es/4.17.6:
resolution: {integrity: sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg==}
dependencies:
'@types/lodash': 4.14.183
'@types/lodash': 4.14.184
dev: true
/@types/lodash/4.14.183:
resolution: {integrity: sha512-UXavyuxzXKMqJPEpFPri6Ku5F9af6ZJXUneHhvQJxavrEjuHkFp2YnDWHcxJiG7hk8ZkWqjcyNeW1s/smZv5cw==}
/@types/lodash/4.14.184:
resolution: {integrity: sha512-RoZphVtHbxPZizt4IcILciSWiC6dcn+eZ8oX9IWEYfDMcocdd42f7NPI6fQj+6zI8y4E0L7gu2pcZKLGTRaV9Q==}
dev: true
/@types/minimist/1.2.2:
@ -1488,8 +1488,8 @@ packages:
which: 2.0.2
dev: true
/css-render/0.15.10:
resolution: {integrity: sha512-6j5acvm81sXTHJiF47FNNICtDpF74YoWk1xEK3qQvdqgW6vc+OXrPqflL6m8f5GE6XuFYrbACNEd17kraCSBAQ==}
/css-render/0.15.11:
resolution: {integrity: sha512-hnLrHPUndVUTF5nmNPRey6hpixK02IPUGdEsm2xRjvJuewToyrVFx9Nmai8rgfVzhTFo5SJVh2PHAtzaIV8JKw==}
dependencies:
'@emotion/hash': 0.8.0
'@types/node': 17.0.23
@ -1592,16 +1592,16 @@ packages:
engines: {node: '>=8'}
dev: true
/date-fns-tz/1.3.6_date-fns@2.29.1:
/date-fns-tz/1.3.6_date-fns@2.29.2:
resolution: {integrity: sha512-C8q7mErvG4INw1ZwAFmPlGjEo5Sv4udjKVbTc03zpP9cu6cp5AemFzKhz0V68LGcWEtX5mJudzzg3G04emIxLA==}
peerDependencies:
date-fns: '>=2.0.0'
dependencies:
date-fns: 2.29.1
date-fns: 2.29.2
dev: true
/date-fns/2.29.1:
resolution: {integrity: sha512-dlLD5rKaKxpFdnjrs+5azHDFOPEu4ANy/LTh04A1DTzMM7qoajmKCBc8pkKRFT41CNzw+4gQh79X5C+Jq27HAw==}
/date-fns/2.29.2:
resolution: {integrity: sha512-0VNbwmWJDS/G3ySwFSJA3ayhbURMTJLtwM2DTxf9CWondCnh6DTNlO9JgRSq6ibf4eD0lfMJNBxUdEAHHix+bA==}
engines: {node: '>=0.11'}
dev: true
@ -3767,19 +3767,19 @@ packages:
resolution: {integrity: sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=}
dev: true
/naive-ui/2.32.1_vue@3.2.31:
resolution: {integrity: sha512-4zRHAn9d273qKWHs2ZQ+9xZmGTfxhL/CI1tj8bJkE313zjwyGRaiPbCeh3hQgrNdZWiRXdqmA442EVKHizGWeg==}
/naive-ui/2.32.2_vue@3.2.31:
resolution: {integrity: sha512-vTNuZ8LBlfo/cdiv4S8o6Cg5g7p9V9cR5rK+Fag2cplOnng5twTILD3sBaCqw3k/BV1331Xdk26ml8Me8QJ7iA==}
peerDependencies:
vue: ^3.0.0
dependencies:
'@css-render/plugin-bem': 0.15.10_css-render@0.15.10
'@css-render/vue3-ssr': 0.15.10_vue@3.2.31
'@types/lodash': 4.14.183
'@css-render/plugin-bem': 0.15.11_css-render@0.15.11
'@css-render/vue3-ssr': 0.15.11_vue@3.2.31
'@types/lodash': 4.14.184
'@types/lodash-es': 4.17.6
async-validator: 4.2.5
css-render: 0.15.10
date-fns: 2.29.1
date-fns-tz: 1.3.6_date-fns@2.29.1
css-render: 0.15.11
date-fns: 2.29.2
date-fns-tz: 1.3.6_date-fns@2.29.2
evtd: 0.2.4
highlight.js: 11.6.0
lodash: 4.17.21
@ -5604,9 +5604,9 @@ packages:
peerDependencies:
vue: ^3.0.11
dependencies:
'@css-render/vue3-ssr': 0.15.10_vue@3.2.31
'@juggle/resize-observer': 3.3.1
css-render: 0.15.10
'@css-render/vue3-ssr': 0.15.11_vue@3.2.31
'@juggle/resize-observer': 3.4.0
css-render: 0.15.11
evtd: 0.2.4
seemly: 0.3.6
vdirs: 0.1.8_vue@3.2.31

View File

@ -1,8 +1,7 @@
<script setup>
import { renderCustomIcon } from '@/utils/icon'
/** 自定义图标 */
const props = defineProps({
/** 图标名称(图片的文件名) */
/** 图标名称(assets/svg下的文件名) */
icon: {
type: String,
required: true,
@ -16,10 +15,8 @@ const props = defineProps({
default: undefined,
},
})
const iconCom = computed(() => renderCustomIcon(props.icon, props))
</script>
<template>
<component :is="iconCom" />
<TheIcon type="custom" v-bind="props" />
</template>

View File

@ -1,4 +1,4 @@
<script setup name="SvgIcon">
<script setup>
const props = defineProps({
icon: {
type: String,

View File

@ -0,0 +1,33 @@
<script setup>
import { renderIcon, renderCustomIcon } from '@/utils/icon'
const props = defineProps({
icon: {
type: String,
required: true,
},
size: {
type: Number,
default: 14,
},
color: {
type: String,
default: undefined,
},
/** iconify | custom */
type: {
type: String,
default: 'iconify',
},
})
const iconCom = computed(() =>
props.type === 'iconify'
? renderIcon(props.icon, { size: props.size, color: props.color })
: renderCustomIcon(props.icon, { size: props.size, color: props.color })
)
</script>
<template>
<component :is="iconCom" />
</template>

View File

@ -0,0 +1,17 @@
<template>
<transition name="fade-slide" mode="out-in" appear>
<section class="cus-scroll-y wh-full p-15 flex-col bg-[#f5f6fb]">
<slot />
<AppFooter v-if="showFooter" mt-15 />
</section>
</transition>
</template>
<script setup>
defineProps({
showFooter: {
type: Boolean,
default: false,
},
})
</script>

View File

@ -0,0 +1,28 @@
<template>
<AppPage :show-footer="showFooter">
<n-card rounded-10 flex-1>
<slot v-if="showHeader" name="header">
<h2 mb-15 color="#333">{{ title || route.meta?.title }}</h2>
</slot>
<slot />
</n-card>
</AppPage>
</template>
<script setup>
defineProps({
showFooter: {
type: Boolean,
default: false,
},
showHeader: {
type: Boolean,
default: true,
},
title: {
type: String,
default: undefined,
},
})
const route = useRoute()
</script>

View File

@ -1,10 +1,6 @@
<template>
<router-view v-slot="{ Component, route }">
<transition name="fade-slide" mode="out-in" appear>
<keep-alive :include="keepAliveRouteNames">
<component :is="Component" v-if="appStore.reloadFlag" :key="route.path" />
</keep-alive>
</transition>
<router-view v-slot="{ Component }">
<component :is="Component" v-if="appStore.reloadFlag" />
</router-view>
</template>
@ -12,9 +8,4 @@
import { useAppStore } from '@/store/modules/app'
const appStore = useAppStore()
const router = useRouter()
const allRoutes = router.getRoutes()
const keepAliveRouteNames = computed(() => {
return allRoutes.filter((route) => route.meta?.keepAlive).map((route) => route.name)
})
</script>

View File

@ -1,15 +1,13 @@
<template>
<header px-15 h-full flex items-center>
<div flex items-center>
<MenuCollapse />
<BreadCrumb ml-15 />
</div>
<div ml-auto flex items-center>
<GithubSite />
<FullScreen />
<UserAvatar />
</div>
</header>
<div flex items-center>
<MenuCollapse />
<BreadCrumb ml-15 />
</div>
<div ml-auto flex items-center>
<GithubSite />
<FullScreen />
<UserAvatar />
</div>
</template>
<script setup>

View File

@ -106,7 +106,7 @@ function handleMenuSelect(key, item) {
&.n-menu-item-content--selected,
&:hover {
&::before {
border-left: 4px solid var(--primaryColor);
border-left: 4px solid var(--primary-color);
}
}
}

View File

@ -1,5 +1,5 @@
<template>
<n-layout has-sider h-full>
<n-layout has-sider wh-full>
<n-layout-sider
bordered
collapse-mode="width"
@ -10,19 +10,18 @@
>
<SideBar />
</n-layout-sider>
<n-layout>
<n-layout-header bg-white border-b bc-eee :style="`height: ${header.height ?? 60}px`">
<AppHeader />
</n-layout-header>
<n-layout bg="#f5f6fb" :style="`height: calc(100% - ${header.height ?? 60}px)`">
<AppTags v-if="tags.visible" :style="`height: ${tags.height ?? 50}px`" />
<AppMain
class="cus-scroll border-t bc-eee overflow-auto"
:style="{ height: `calc(100% - ${tags.visible ? tags.height ?? 50 : 0}px)` }"
/>
</n-layout>
</n-layout>
<article flex-1 flex-col overflow-hidden>
<header bg-white px-15 border-b bc-eee flex items-center :style="`height: ${header.height}px`">
<AppHeader />
</header>
<section v-if="tags.visible" border-b bc-eee>
<AppTags :style="{ height: `${tags.height}px` }" />
</section>
<section flex-1 overflow-hidden>
<AppMain />
</section>
</article>
</n-layout>
</template>

View File

@ -32,25 +32,42 @@ body {
transform: translateX(30px);
}
/* 自定义滚动条样式 */
/* 自定义滚动条样式 */
.cus-scroll {
overflow: auto;
&::-webkit-scrollbar {
width: 8px;
height: 8px;
}
}
.cus-scroll-x {
overflow-x: auto;
&::-webkit-scrollbar {
width: 0;
height: 8px;
}
}
.cus-scroll-y {
overflow-y: auto;
&::-webkit-scrollbar {
width: 8px;
height: 0;
}
}
.cus-scroll,
.cus-scroll-x,
.cus-scroll-y {
&::-webkit-scrollbar-thumb {
background-color: transparent;
border-radius: 3px;
}
&::-webkit-scrollbar-corner {
background: #f6f6f6;
border-radius: 4px;
}
&:hover {
&::-webkit-scrollbar-thumb {
background: #bfbfbf;
}
&::-webkit-scrollbar-thumb:hover {
background: #999999;
background: var(--primary-color);
}
}
}

View File

@ -1,7 +1,7 @@
import { h } from 'vue'
import { Icon } from '@iconify/vue'
import { NIcon } from 'naive-ui'
import SvgIcon from '@/components/custom/SvgIcon.vue'
import SvgIcon from '@/components/icon/SvgIcon.vue'
export function renderIcon(icon, props = { size: 12 }) {
return () => h(NIcon, props, { default: () => h(Icon, { icon }) })

View File

@ -1,5 +1,5 @@
<template>
<div h-full flex>
<AppPage>
<n-result m-auto status="404" description="抱歉,您访问的页面不存在。">
<template #icon>
<img src="@/assets/images/404.webp" width="500" />
@ -8,7 +8,7 @@
<n-button @click="replace('/')">返回首页</n-button>
</template>
</n-result>
</div>
</AppPage>
</template>
<script setup>

View File

@ -1,5 +1,5 @@
<template>
<div p-24>
<CommonPage :show-header="false" title="32323">
<div h-60 pl-20 pr-20 flex items-center bg-white>
<input
v-model="post.title"
@ -7,10 +7,12 @@
type="text"
placeholder="输入文章标题..."
/>
<n-button type="primary" style="width: 80px" :loading="btnLoading" @click="handleSavePost">保存</n-button>
<n-button type="primary" style="width: 80px" :loading="btnLoading" @click="handleSavePost">
<TheIcon v-if="!btnLoading" icon="line-md:confirm-circle" class="mr-5" :size="18" /> 保存
</n-button>
</div>
<MdEditor v-model="post.content" style="height: calc(100vh - 220px)" />
</div>
<MdEditor v-model="post.content" style="height: calc(100vh - 250px)" />
</CommonPage>
</template>
<script setup>

View File

@ -1,8 +1,5 @@
<template>
<div p-24>
<div flex>
<n-button size="small" type="primary" @click="handleCreate">新建文章</n-button>
</div>
<CommonPage show-footer title="文章">
<n-data-table
mt-20
:loading="loading"
@ -13,7 +10,7 @@
:row-key="(row) => row.id"
@update:checked-row-keys="handleCheck"
/>
</div>
</CommonPage>
</template>
<script setup>

View File

@ -1,12 +1,15 @@
<template>
<div class="cus-scroll h-full py-15 flex-col overflow-auto bg-cover" :style="{ backgroundImage: `url(${bgImg})` }">
<div m-auto p-15 f-c-c min-w-345 rounded-10 card-shadow bg-white dark:bg-dark bg-opacity-60>
<AppPage :show-footer="true" bg-cover :style="{ backgroundImage: `url(${bgImg})` }">
<div
style="transform: translateY(25px)"
class="m-auto p-15 f-c-c min-w-345 max-w-700 rounded-10 card-shadow bg-white bg-opacity-60"
>
<div w-380 hidden md:block px-20 py-35>
<img src="@/assets/images/login_banner.webp" w-full alt="login_banner" />
</div>
<div w-320 flex-col px-20 py-35>
<h5 f-c-c text-24 font-normal color="#6a6a6a"><icon-custom-logo mr-30 text-50 color-primary />{{ title }}</h5>
<h5 f-c-c text-24 font-normal color="#6a6a6a"><icon-custom-logo mr-10 text-50 color-primary />{{ title }}</h5>
<div mt-30>
<n-input
v-model:value="loginInfo.name"
@ -39,8 +42,7 @@
</div>
</div>
</div>
<AppFooter />
</div>
</AppPage>
</template>
<script setup>

View File

@ -1,7 +1,7 @@
<template>
<div p24>
<CommonPage :show-header="false">
<n-button type="error" @click="handleDelete"> <icon-mi:delete mr-5 />删除</n-button>
</div>
</CommonPage>
</template>
<script setup name="TestDialog">

View File

@ -1,20 +0,0 @@
<template>
<div p24>
<n-gradient-text gradient="linear-gradient(90deg, red 0%, green 50%, blue 100%)"> 注意查看提示语 </n-gradient-text>
</div>
</template>
<!--使用keep-alive须设置name注意请与对应的路由的name保持一致方便统一处理-->
<script setup name="TestKeepAlive">
onMounted(() => {
$message.success('触发onMounted')
})
onActivated(() => {
$message.success('触发onActivated')
})
onDeactivated(() => {
$message.success('触发onDeactivated')
})
</script>

View File

@ -1,10 +1,10 @@
<template>
<div p24>
<CommonPage title="我是自定义Title">
<n-button type="primary" @click="handleLogin">
<icon-mdi:login mr-5 />
登陆
</n-button>
</div>
</CommonPage>
</template>
<script setup>

View File

@ -38,15 +38,5 @@ export default {
icon: 'material-symbols:auto-awesome-outline-rounded',
},
},
{
name: 'TestKeepAlive',
path: 'keep-alive',
component: () => import('./keep-alive/index.vue'),
meta: {
title: '测试Keep-Alive',
keepAlive: true,
icon: 'material-symbols:auto-awesome-outline-rounded',
},
},
],
}

View File

@ -1,5 +1,5 @@
<template>
<div p-24>
<CommonPage show-footer>
<p>
文档<a hover-decoration-underline c-blue href="https://uno.antfu.me/" target="_blank">https://uno.antfu.me/</a>
</p>
@ -10,59 +10,57 @@
</a>
</p>
<div flex mt-20>
<div flex p-20 rounded-5 bg="#fff">
<div text-20 font-600>Flex布局</div>
<div flex w-360 flex-wrap justify-around ml-15 p-10>
<div w-50 h-50 b-1 rounded-5 f-c-c p-10 m-20>
<div f-c-c mt-20 w-350 rounded-10 b-1 bc-ccc>
<div flex w-360 flex-wrap justify-around p-10>
<div w-50 h-50 b-1 rounded-5 f-c-c p-10 m-20>
<span w-6 h-6 rounded-3 bg-black></span>
</div>
<div w-50 h-50 b-1 rounded-5 flex justify-between p-10 m-20>
<span w-6 h-6 rounded-3 bg-black></span>
<span w-6 h-6 rounded-3 bg-black self-end></span>
</div>
<div w-50 h-50 b-1 rounded-5 flex justify-between p-10 m-20>
<span w-6 h-6 rounded-3 bg-black></span>
<span w-6 h-6 rounded-3 bg-black self-center></span>
<span w-6 h-6 rounded-3 bg-black self-end></span>
</div>
<div w-50 h-50 b-1 rounded-5 flex justify-between p-10 m-20>
<div flex-col justify-between>
<span w-6 h-6 rounded-3 bg-black></span>
<span w-6 h-6 rounded-3 bg-black></span>
</div>
<div w-50 h-50 b-1 rounded-5 flex justify-between p-10 m-20>
<div flex-col justify-between>
<span w-6 h-6 rounded-3 bg-black></span>
<span w-6 h-6 rounded-3 bg-black self-end></span>
</div>
<div w-50 h-50 b-1 rounded-5 flex justify-between p-10 m-20>
<span w-6 h-6 rounded-3 bg-black></span>
<span w-6 h-6 rounded-3 bg-black self-center></span>
<span w-6 h-6 rounded-3 bg-black self-end></span>
</div>
<div w-50 h-50 b-1 rounded-5 flex justify-between p-10 m-20>
<div flex-col justify-between>
<span w-6 h-6 rounded-3 bg-black></span>
<span w-6 h-6 rounded-3 bg-black></span>
</div>
<div flex-col justify-between>
<span w-6 h-6 rounded-3 bg-black></span>
<span w-6 h-6 rounded-3 bg-black></span>
</div>
</div>
<div w-50 h-50 b-1 rounded-5 f-c-c flex-col p-10 m-20>
<div flex w-full justify-between>
<span w-6 h-6 rounded-3 bg-black></span>
<span w-6 h-6 rounded-3 bg-black></span>
</div>
</div>
<div w-50 h-50 b-1 rounded-5 f-c-c flex-col p-10 m-20>
<div flex w-full justify-between>
<span w-6 h-6 rounded-3 bg-black></span>
<span w-6 h-6 rounded-3 bg-black></span>
<div flex w-full justify-between>
<span w-6 h-6 rounded-3 bg-black></span>
<span w-6 h-6 rounded-3 bg-black></span>
</div>
</div>
<div w-50 h-50 b-1 rounded-5 flex-col justify-between p-10 m-20>
<div flex w-full justify-between>
<span w-6 h-6 rounded-3 bg-black></span>
<span w-6 h-6 rounded-3 bg-black></span>
</div>
<div flex w-full justify-between>
<span w-6 h-6 rounded-3 bg-black></span>
<span w-6 h-6 rounded-3 bg-black></span>
</div>
<div flex w-full justify-between>
<span w-6 h-6 rounded-3 bg-black></span>
<span w-6 h-6 rounded-3 bg-black></span>
</div>
<span w-6 h-6 rounded-3 bg-black></span>
<div flex w-full justify-between>
<span w-6 h-6 rounded-3 bg-black></span>
<span w-6 h-6 rounded-3 bg-black></span>
</div>
</div>
<div w-50 h-50 b-1 rounded-5 flex-col justify-between p-10 m-20>
<div flex w-full justify-between>
<span w-6 h-6 rounded-3 bg-black></span>
<span w-6 h-6 rounded-3 bg-black></span>
</div>
<div flex w-full justify-between>
<span w-6 h-6 rounded-3 bg-black></span>
<span w-6 h-6 rounded-3 bg-black></span>
</div>
<div flex w-full justify-between>
<span w-6 h-6 rounded-3 bg-black></span>
<span w-6 h-6 rounded-3 bg-black></span>
</div>
</div>
</div>
</div>
</div>
<h2 font-normal text-18 text-center w-350 mt-10 color-gray-400>Flex 骰子</h2>
</CommonPage>
</template>

View File

@ -1,52 +1,54 @@
<template>
<div p-15>
<n-card rounded-10>
<div flex items-center>
<img rounded-full width="60" :src="userStore.avatar" />
<div ml-20>
<p text-16>Hello, {{ userStore.name }}</p>
<p mt-5 text-12 op-60>今天又是元气满满的一天</p>
<AppPage :show-footer="true">
<div flex-1>
<n-card rounded-10>
<div flex items-center>
<img rounded-full width="60" :src="userStore.avatar" />
<div ml-20>
<p text-16>Hello, {{ userStore.name }}</p>
<p mt-5 text-12 op-60>今天又是元气满满的一天</p>
</div>
<div ml-auto flex items-center>
<n-statistic label="待办" :value="4">
<template #suffix> / 10 </template>
</n-statistic>
<n-statistic label="Stars" w-100 ml-80>
<a href="https://github.com/zclzone/vue-naive-admin">
<img allt="stars" src="https://badgen.net/github/stars/zclzone/vue-naive-admin" />
</a>
</n-statistic>
<n-statistic label="Forks" w-100 ml-80>
<a href="https://github.com/zclzone/vue-naive-admin">
<img allt="forks" src="https://badgen.net/github/forks/zclzone/vue-naive-admin" />
</a>
</n-statistic>
</div>
</div>
<div ml-auto flex items-center>
<n-statistic label="待办" :value="4">
<template #suffix> / 10 </template>
</n-statistic>
<n-statistic label="Stars" w-100 ml-80>
<a href="https://github.com/zclzone/vue-naive-admin">
<img allt="stars" src="https://badgen.net/github/stars/zclzone/vue-naive-admin" />
</a>
</n-statistic>
<n-statistic label="Forks" w-100 ml-80>
<a href="https://github.com/zclzone/vue-naive-admin">
<img allt="forks" src="https://badgen.net/github/forks/zclzone/vue-naive-admin" />
</a>
</n-statistic>
</div>
</div>
</n-card>
</n-card>
<n-card title="项目" size="small" :segmented="true" mt-15 rounded-10>
<template #header-extra>
<n-button text type="primary">更多</n-button>
</template>
<div flex flex-wrap justify-between>
<n-card
v-for="i in 10"
:key="i"
class="w-300 flex-shrink-0 mt-10 mb-10 cursor-pointer"
hover:card-shadow
title="Vue Naive Admin"
size="small"
>
<p op-60>一个基于 Vue3.0ViteNaive UI 的轻量级后台管理模板</p>
</n-card>
<div w-300 h-0></div>
<div w-300 h-0></div>
<div w-300 h-0></div>
<div w-300 h-0></div>
</div>
</n-card>
</div>
<n-card title="项目" size="small" :segmented="true" mt-15 rounded-10>
<template #header-extra>
<n-button text type="primary">更多</n-button>
</template>
<div flex flex-wrap justify-between>
<n-card
v-for="i in 10"
:key="i"
class="w-300 flex-shrink-0 mt-10 mb-10 cursor-pointer"
hover:card-shadow
title="Vue Naive Admin"
size="small"
>
<p op-60>一个基于 Vue3.0ViteNaive UI 的轻量级后台管理模板</p>
</n-card>
<div w-300 h-0></div>
<div w-300 h-0></div>
<div w-300 h-0></div>
<div w-300 h-0></div>
</div>
</n-card>
</div>
</AppPage>
</template>
<script setup>