commit.
parent
e49e21f841
commit
6d9fb30bbd
|
@ -12,4 +12,7 @@ module.exports = {
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaVersion: 'latest',
|
ecmaVersion: 'latest',
|
||||||
},
|
},
|
||||||
|
rules: {
|
||||||
|
'vue/multi-word-component-names': 'off',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
export const useCounterStore = defineStore('counter', () => {
|
export const useAccountStore = defineStore('account', () => {
|
||||||
const count = ref(0)
|
const count = ref(0)
|
||||||
const doubleCount = computed(() => count.value * 2)
|
const doubleCount = computed(() => count.value * 2)
|
||||||
function increment() {
|
function increment() {
|
|
@ -0,0 +1,5 @@
|
||||||
|
<template>
|
||||||
|
<h1>404</h1>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts"></script>
|
|
@ -1,12 +1,20 @@
|
||||||
<template>
|
<template>
|
||||||
<n-layout position="absolute" style="top: 0; right: 0; bottom: 0; left: 0">
|
<n-layout position="absolute" style="top: 0; right: 0; bottom: 0; left: 0">
|
||||||
<n-layout-header position="absolute" style="z-index: 9999">
|
<n-layout-header position="absolute" :inverted="dark" :style="invertedStyle" :bordered="!dark">
|
||||||
<LogoComponent :collapsed="collapsed" style="width: 224px" />
|
<Logo :collapsed="false" style="width: 224px" />
|
||||||
<div class="header">颐和园路</div>
|
<div class="header">
|
||||||
|
<n-button type="primary" @click="dark = !dark">{{ dark ? '亮色' : '暗色' }}</n-button>
|
||||||
|
颐和园路
|
||||||
|
</div>
|
||||||
</n-layout-header>
|
</n-layout-header>
|
||||||
<n-layout has-sider position="absolute" style="top: 72px; right: 0; bottom: 0; left: 0">
|
<n-layout
|
||||||
|
has-sider
|
||||||
|
position="absolute"
|
||||||
|
style="right: 0; bottom: 0; left: 0"
|
||||||
|
:style="dark ? { top: '72px' } : { top: '73px' }"
|
||||||
|
>
|
||||||
<n-layout-sider
|
<n-layout-sider
|
||||||
bordered
|
:bordered="!dark"
|
||||||
:collapsed-width="64"
|
:collapsed-width="64"
|
||||||
:width="256"
|
:width="256"
|
||||||
show-trigger="arrow-circle"
|
show-trigger="arrow-circle"
|
||||||
|
@ -15,16 +23,10 @@
|
||||||
@collapse="collapsed = true"
|
@collapse="collapsed = true"
|
||||||
@expand="collapsed = false"
|
@expand="collapsed = false"
|
||||||
:native-scrollbar="false"
|
:native-scrollbar="false"
|
||||||
inverted
|
:inverted="dark"
|
||||||
|
:style="invertedStyle"
|
||||||
>
|
>
|
||||||
<n-menu
|
<SiderMenu :collapsed="collapsed" invertedBackgroundColor="#282c34" :inverted="dark" />
|
||||||
inverted
|
|
||||||
v-model:value="activeKey"
|
|
||||||
:collapsed="collapsed"
|
|
||||||
:collapsed-width="64"
|
|
||||||
:collapsed-icon-size="22"
|
|
||||||
:options="menuOptions"
|
|
||||||
/>
|
|
||||||
</n-layout-sider>
|
</n-layout-sider>
|
||||||
<n-layout-content content-style="padding: 24px;" :native-scrollbar="false">
|
<n-layout-content content-style="padding: 24px;" :native-scrollbar="false">
|
||||||
平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />
|
平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />平山道<br />
|
||||||
|
@ -37,133 +39,48 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, h, ref, type Component } from 'vue'
|
import { defineComponent, h, ref, computed, type Component } from 'vue'
|
||||||
|
|
||||||
import { NIcon } from 'naive-ui'
|
import Logo from './components/Logo.vue'
|
||||||
import type { MenuOption } from 'naive-ui'
|
import SiderMenu from './components/SiderMenu.vue'
|
||||||
import {
|
|
||||||
BookOpen20Filled as BookIcon,
|
|
||||||
Person20Regular as PersonIcon,
|
|
||||||
DrinkWine20Regular as WineIcon,
|
|
||||||
} from '@vicons/fluent'
|
|
||||||
|
|
||||||
import LogoComponent from './components/LogoComponent.vue'
|
|
||||||
|
|
||||||
function renderIcon(icon: Component) {
|
|
||||||
return () => h(NIcon, null, { default: () => h(icon) })
|
|
||||||
}
|
|
||||||
|
|
||||||
const menuOptions: MenuOption[] = [
|
|
||||||
{
|
|
||||||
label: '且听风吟',
|
|
||||||
key: 'hear-the-wind-sing',
|
|
||||||
icon: renderIcon(BookIcon),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '1973年的弹珠玩具',
|
|
||||||
key: 'pinball-1973',
|
|
||||||
icon: renderIcon(BookIcon),
|
|
||||||
disabled: true,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: '鼠',
|
|
||||||
key: 'rat',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '寻羊冒险记',
|
|
||||||
key: 'a-wild-sheep-chase',
|
|
||||||
disabled: true,
|
|
||||||
icon: renderIcon(BookIcon),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '舞,舞,舞',
|
|
||||||
key: 'dance-dance-dance',
|
|
||||||
icon: renderIcon(BookIcon),
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'group',
|
|
||||||
label: '人物',
|
|
||||||
key: 'people',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: '叙事者',
|
|
||||||
key: 'narrator',
|
|
||||||
icon: renderIcon(PersonIcon),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '羊男',
|
|
||||||
key: 'sheep-man',
|
|
||||||
icon: renderIcon(PersonIcon),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '饮品',
|
|
||||||
key: 'beverage',
|
|
||||||
icon: renderIcon(WineIcon),
|
|
||||||
children: [
|
|
||||||
{ label: '威士忌', key: 'whisky' },
|
|
||||||
{ label: '威士忌', key: 'whisky' },
|
|
||||||
{ label: '威士忌', key: 'whisky' },
|
|
||||||
{ label: '威士忌', key: 'whisky' },
|
|
||||||
{ label: '威士忌', key: 'whisky' },
|
|
||||||
{ label: '威士忌', key: 'whisky' },
|
|
||||||
{ label: '威士忌', key: 'whisky' },
|
|
||||||
{ label: '威士忌', key: 'whisky' },
|
|
||||||
{ label: '威士忌', key: 'whisky' },
|
|
||||||
{ label: '威士忌', key: 'whisky' },
|
|
||||||
{ label: '威士忌', key: 'whisky' },
|
|
||||||
{ label: '威士忌', key: 'whisky' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '食物',
|
|
||||||
key: 'food',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
label: '三明治',
|
|
||||||
key: 'sandwich',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '过去增多,未来减少',
|
|
||||||
key: 'the-past-increases-the-future-recedes',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'HomeView',
|
name: 'HomeView',
|
||||||
components: {
|
components: {
|
||||||
LogoComponent,
|
Logo,
|
||||||
|
SiderMenu,
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const collapsed = ref(false)
|
const collapsed = ref(false)
|
||||||
|
const dark = ref(true)
|
||||||
|
const invertedStyle = computed(() => {
|
||||||
|
return dark.value ? { '--n-color': '#282c34' } : { '--n-color': '#ffffff' }
|
||||||
|
})
|
||||||
return {
|
return {
|
||||||
activeKey: ref<string | null>(null),
|
activeKey: ref<string | null>(null),
|
||||||
collapsed,
|
collapsed,
|
||||||
menuOptions,
|
dark,
|
||||||
|
invertedStyle,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
:root {
|
||||||
|
--sider-width: 256px;
|
||||||
|
}
|
||||||
|
|
||||||
.n-layout-header {
|
.n-layout-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
.header {
|
|
||||||
padding: 24px;
|
|
||||||
border-bottom: solid 1px rgb(239, 239, 245);
|
|
||||||
}
|
|
||||||
|
|
||||||
.n-scrollbar > .n-scrollbar-rail.n-scrollbar-rail--vertical {
|
.n-layout-header > .header {
|
||||||
--n-scrollbar-color: rgba(255, 255, 255, 0.25);
|
padding: 24px;
|
||||||
--n-scrollbar-color-hover: rgba(255, 255, 255, 0.4);
|
position: absolute;
|
||||||
width: 25px;
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 256px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="logo">
|
<div class="logo" :style="style">
|
||||||
<img alt="logo" src="@/assets/logo.svg" />
|
<img alt="logo" src="@/assets/logo.svg" />
|
||||||
<span class="title" v-if="!collapsed">Plusone Admin</span>
|
<span class="title" v-if="!collapsed">Plusone Admin</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
collapsed: Boolean,
|
collapsed: Boolean,
|
||||||
|
inverted: Boolean,
|
||||||
|
})
|
||||||
|
|
||||||
|
const style = computed(() => {
|
||||||
|
return props.inverted ? { color: '#ffffff' } : { color: 'unset' }
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -15,8 +22,9 @@ const props = defineProps({
|
||||||
.logo {
|
.logo {
|
||||||
padding: 20px 16px;
|
padding: 20px 16px;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
background-color: rgb(0, 20, 40);
|
background-color: var(--n-color);
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
|
height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo > img {
|
.logo > img {
|
|
@ -0,0 +1,113 @@
|
||||||
|
import type { MenuOption } from 'naive-ui'
|
||||||
|
import { type RouteRecordRaw, RouterLink } from 'vue-router'
|
||||||
|
import { NIcon } from 'naive-ui'
|
||||||
|
import * as iconComponents from '@vicons/fluent'
|
||||||
|
import { type Component, h } from 'vue'
|
||||||
|
|
||||||
|
export interface MenuVO {
|
||||||
|
type: number
|
||||||
|
typeName: string
|
||||||
|
id: number
|
||||||
|
parentId: number
|
||||||
|
name: string
|
||||||
|
// 若 type 为 MENU_ITEM 且 path 以 http:// 或 https:// 开头则被识别为外链
|
||||||
|
path: string
|
||||||
|
title: string
|
||||||
|
icon: string
|
||||||
|
hidden: boolean
|
||||||
|
orderNumber: number
|
||||||
|
status: number
|
||||||
|
remarks: string
|
||||||
|
|
||||||
|
// MENU_ITEM
|
||||||
|
component?: string
|
||||||
|
cache?: boolean
|
||||||
|
resource?: string
|
||||||
|
actions?: ActionVO[]
|
||||||
|
|
||||||
|
// MENU_LIST
|
||||||
|
children?: MenuVO[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ActionVO {
|
||||||
|
id: number
|
||||||
|
identifier: string
|
||||||
|
label: string
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum MenuType {
|
||||||
|
MENU_LIST,
|
||||||
|
MENU_ITEM,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertToRoutes(menuVOTree: MenuVO[]): RouteRecordRaw[] {
|
||||||
|
const routes: RouteRecordRaw[] = menuVOTree.map((menuVO) => {
|
||||||
|
if (menuVO.type == MenuType.MENU_LIST) {
|
||||||
|
return {
|
||||||
|
path: menuVO.path,
|
||||||
|
name: menuVO.name,
|
||||||
|
component:
|
||||||
|
menuVO.component !== undefined ? () => import('@/views' + menuVO.component) : undefined,
|
||||||
|
children: convertToRoutes(menuVO.children ? menuVO.children : []),
|
||||||
|
meta: {
|
||||||
|
title: menuVO.title,
|
||||||
|
icon: menuVO.icon,
|
||||||
|
hidden: menuVO.hidden,
|
||||||
|
order: menuVO.orderNumber,
|
||||||
|
status: menuVO.status,
|
||||||
|
remarks: menuVO.remarks,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
path: menuVO.path,
|
||||||
|
name: menuVO.name,
|
||||||
|
component: import('@/views' + menuVO.component),
|
||||||
|
meta: {
|
||||||
|
title: menuVO.title,
|
||||||
|
icon: menuVO.icon,
|
||||||
|
hidden: menuVO.hidden,
|
||||||
|
order: menuVO.orderNumber,
|
||||||
|
status: menuVO.status,
|
||||||
|
remarks: menuVO.remarks,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return routes
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertToMenuOptions(menuVOTree: MenuVO[]): MenuOption[] {
|
||||||
|
const menuOptions: MenuOption[] = menuVOTree.map((menuVO) => {
|
||||||
|
if (menuVO.type == MenuType.MENU_ITEM) {
|
||||||
|
return {
|
||||||
|
key: menuVO.name,
|
||||||
|
icon: renderIcon(menuVO.icon),
|
||||||
|
label:
|
||||||
|
menuVO.path.startsWith('http://') || menuVO.path.startsWith('https://')
|
||||||
|
? () => h('a', { href: menuVO.path, target: '_blank' }, menuVO.title)
|
||||||
|
: () => h(RouterLink, { to: menuVO.path }, menuVO.title),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
key: menuVO.name,
|
||||||
|
icon: renderIcon(menuVO.icon),
|
||||||
|
label: menuVO.title,
|
||||||
|
children: convertToMenuOptions(menuVO.children ? menuVO.children : []),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return menuOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
const icons: Map<string, Component> = new Map(Object.entries(iconComponents))
|
||||||
|
export function renderIcon(icon: string) {
|
||||||
|
const iconComponent = icons.get(icon)
|
||||||
|
return () =>
|
||||||
|
h(NIcon, null, {
|
||||||
|
default: () => h(iconComponent ? iconComponent : iconComponents.QuestionCircle20Regular),
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
<template>
|
||||||
|
<n-menu
|
||||||
|
:inverted="inverted"
|
||||||
|
:style="style"
|
||||||
|
v-model:value="activeKey"
|
||||||
|
:collapsed="collapsed"
|
||||||
|
:collapsed-width="64"
|
||||||
|
:collapsed-icon-size="22"
|
||||||
|
:options="menuOptions"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { h, computed, type Component } from 'vue'
|
||||||
|
import { RouterLink, useRoute } from 'vue-router'
|
||||||
|
|
||||||
|
import { NIcon } from 'naive-ui'
|
||||||
|
import type { MenuOption } from 'naive-ui'
|
||||||
|
|
||||||
|
import * as iconComponents from '@vicons/fluent'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
inverted: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
invertedBackgroundColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#282c34',
|
||||||
|
},
|
||||||
|
collapsed: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const style = computed(() => {
|
||||||
|
return props.inverted ? { '--n-color': props.invertedBackgroundColor } : { '--n-color': '#ffffff' }
|
||||||
|
})
|
||||||
|
|
||||||
|
// MenuOptions
|
||||||
|
const icons: Map<string, Component> = new Map(Object.entries(iconComponents))
|
||||||
|
|
||||||
|
function renderIcon(icon: string) {
|
||||||
|
const iconComponent = icons.get(icon)
|
||||||
|
return () =>
|
||||||
|
h(NIcon, null, {
|
||||||
|
default: () => h(iconComponent ? iconComponent : iconComponents.QuestionCircle20Regular),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const menuOptions: MenuOption[] = [
|
||||||
|
{
|
||||||
|
label: () =>
|
||||||
|
h(
|
||||||
|
RouterLink,
|
||||||
|
{
|
||||||
|
to: {
|
||||||
|
name: 'home',
|
||||||
|
params: {
|
||||||
|
lang: 'zh-CN',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ default: () => '回家' }
|
||||||
|
),
|
||||||
|
key: '/',
|
||||||
|
icon: renderIcon('Home20Regular'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: () =>
|
||||||
|
h(
|
||||||
|
RouterLink,
|
||||||
|
{
|
||||||
|
to: '/login',
|
||||||
|
},
|
||||||
|
{ default: () => '上班' }
|
||||||
|
),
|
||||||
|
key: '/login',
|
||||||
|
icon: renderIcon('Desktop20Regular'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: () =>
|
||||||
|
h(
|
||||||
|
'a',
|
||||||
|
{
|
||||||
|
href: 'http://zhouxy.xyz',
|
||||||
|
target: '_blank',
|
||||||
|
rel: 'noopenner noreferrer',
|
||||||
|
},
|
||||||
|
'ZhouXY'
|
||||||
|
),
|
||||||
|
key: 'hear-the-wind-sing',
|
||||||
|
icon: renderIcon('BookOpen20Filled'),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
// ActiveKey
|
||||||
|
const route = useRoute()
|
||||||
|
const activeKey = computed(() => {
|
||||||
|
return route.path
|
||||||
|
})
|
||||||
|
</script>
|
|
@ -2,8 +2,11 @@
|
||||||
"extends": "@vue/tsconfig/tsconfig.web.json",
|
"extends": "@vue/tsconfig/tsconfig.web.json",
|
||||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"lib": ["ES2017"],
|
||||||
|
"target": "ES2015",
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
|
"@": ["src"],
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,4 +11,7 @@ export default defineConfig({
|
||||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
server: {
|
||||||
|
port: 8888,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue