30 changed files with 9183 additions and 0 deletions
@ -0,0 +1,68 @@ |
|||||
|
module.exports = { |
||||
|
parser: "vue-eslint-parser", |
||||
|
"env": { |
||||
|
"browser": true, |
||||
|
"es2021": true |
||||
|
}, |
||||
|
"extends": [ |
||||
|
"plugin:@typescript-eslint/recommended", |
||||
|
"plugin:vue/vue3-recommended" |
||||
|
], |
||||
|
"parserOptions": { |
||||
|
"parser": "@typescript-eslint/parser", |
||||
|
"sourceType": "module", |
||||
|
ecmaFeature: { |
||||
|
jsx: true, |
||||
|
tsx: true |
||||
|
} |
||||
|
}, |
||||
|
"plugins": [ |
||||
|
"@typescript-eslint" |
||||
|
], |
||||
|
"rules": { |
||||
|
'vue/max-attributes-per-line': ['error', { |
||||
|
singleline: 10, |
||||
|
multiline: { |
||||
|
max: 1, |
||||
|
allowFirstLine: false |
||||
|
} |
||||
|
}], |
||||
|
'vue/singleline-html-element-content-newline': 'off', |
||||
|
'vue/multiline-html-element-content-newline': 'off', |
||||
|
'vue/html-indent': ['error', 4], |
||||
|
indent: ['error', 4], // 4行缩进
|
||||
|
'vue/script-indent': ['error', 4], |
||||
|
quotes: ['error', 'single'], // 单引号
|
||||
|
// 'vue/html-quotes': ['error', 'single'],
|
||||
|
semi: ['error', 'never'], // 禁止使用分号
|
||||
|
'space-infix-ops': ['error', { int32Hint: false }], // 要求操作符周围有空格
|
||||
|
'no-multi-spaces': 'error', // 禁止多个空格
|
||||
|
'no-whitespace-before-property': 'error', // 禁止在属性前使用空格
|
||||
|
'space-before-blocks': 'error', // 在块之前强制保持一致的间距
|
||||
|
'space-before-function-paren': ['error', 'never'], // 在“ function”定义打开括号之前强制不加空格
|
||||
|
'space-in-parens': ['error', 'never'], // 强制括号左右的不加空格
|
||||
|
'spaced-comment': ['error', 'always'], // 注释间隔
|
||||
|
'template-tag-spacing': ['error', 'always'], // 在模板标签及其文字之间需要空格
|
||||
|
'no-var': 'error', |
||||
|
'prefer-destructuring': ['error', { // 优先使用数组和对象解构
|
||||
|
array: true, |
||||
|
object: true |
||||
|
}, { |
||||
|
enforceForRenamedProperties: false |
||||
|
}], |
||||
|
'comma-dangle': ['error', 'never'], // 最后一个属性不允许有逗号
|
||||
|
'arrow-spacing': 'error', // 箭头函数空格
|
||||
|
'prefer-template': 'error', |
||||
|
'template-curly-spacing': 'error', |
||||
|
'quote-props': ['error', 'as-needed'], // 对象字面量属性名称使用引号
|
||||
|
'object-curly-spacing': ['error', 'always'], // 强制在花括号中使用一致的空格
|
||||
|
'no-unneeded-ternary': 'error', // 禁止可以表达为更简单结构的三元操作符
|
||||
|
'no-restricted-syntax': ['error', 'WithStatement', 'BinaryExpression[operator="in"]'], // 禁止with/in语句
|
||||
|
'no-lonely-if': 'error', // 禁止 if 语句作为唯一语句出现在 else 语句块中
|
||||
|
'newline-per-chained-call': ['error', { ignoreChainWithDepth: 2 }], // 要求方法链中每个调用都有一个换行符
|
||||
|
// 路径别名设置
|
||||
|
'no-submodule-imports': ['off', '/@'], |
||||
|
'no-implicit-dependencies': ['off', ['/@']], |
||||
|
'@typescript-eslint/no-explicit-any': 'off' // 类型可以使用any
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
# Logs |
||||
|
logs |
||||
|
*.log |
||||
|
npm-debug.log* |
||||
|
yarn-debug.log* |
||||
|
yarn-error.log* |
||||
|
pnpm-debug.log* |
||||
|
lerna-debug.log* |
||||
|
|
||||
|
node_modules |
||||
|
dist |
||||
|
dist-ssr |
||||
|
*.local |
||||
|
|
||||
|
# Editor directories and files |
||||
|
.vscode/* |
||||
|
!.vscode/extensions.json |
||||
|
.idea |
||||
|
.DS_Store |
||||
|
*.suo |
||||
|
*.ntvs* |
||||
|
*.njsproj |
||||
|
*.sln |
||||
|
*.sw? |
||||
@ -0,0 +1 @@ |
|||||
|
# DTM-Admin |
||||
@ -0,0 +1,13 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="en"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8" /> |
||||
|
<link rel="icon" href="/favicon.ico" /> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||
|
<title>Vite App</title> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div id="app"></div> |
||||
|
<script type="module" src="/src/main.ts"></script> |
||||
|
</body> |
||||
|
</html> |
||||
File diff suppressed because it is too large
@ -0,0 +1,36 @@ |
|||||
|
{ |
||||
|
"name": "dtm-admin", |
||||
|
"private": true, |
||||
|
"version": "0.0.0", |
||||
|
"scripts": { |
||||
|
"dev": "vite", |
||||
|
"build": "vite build", |
||||
|
"preview": "vite preview" |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"ant-design-vue": "^3.1.1", |
||||
|
"vue": "^3.2.25" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"@types/node": "^17.0.23", |
||||
|
"@types/nprogress": "^0.2.0", |
||||
|
"@typescript-eslint/eslint-plugin": "^5.18.0", |
||||
|
"@typescript-eslint/parser": "^5.18.0", |
||||
|
"@vitejs/plugin-vue": "^2.3.0", |
||||
|
"autoprefixer": "^10.4.4", |
||||
|
"axios": "^0.26.1", |
||||
|
"eslint": "^8.13.0", |
||||
|
"eslint-plugin-vue": "^8.6.0", |
||||
|
"nprogress": "^0.2.0", |
||||
|
"pinia": "^2.0.0-rc.10", |
||||
|
"postcss": "^8.4.12", |
||||
|
"postcss-import": "^14.1.0", |
||||
|
"postcss-nested": "^5.0.6", |
||||
|
"postcss-simple-vars": "^6.0.3", |
||||
|
"typescript": "^4.5.4", |
||||
|
"vite": "^2.9.1", |
||||
|
"vite-plugin-svg-icons": "^1.1.0", |
||||
|
"vue-router": "^4.0.13", |
||||
|
"vue-tsc": "^0.29.8" |
||||
|
} |
||||
|
} |
||||
|
After Width: | Height: | Size: 4.2 KiB |
@ -0,0 +1,6 @@ |
|||||
|
<template> |
||||
|
<router-view /> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"></script> |
||||
|
|
||||
|
After Width: | Height: | Size: 6.7 KiB |
@ -0,0 +1,42 @@ |
|||||
|
<template> |
||||
|
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" /> |
||||
|
<svg v-else :class="svgClass" aria-hidden="true"> |
||||
|
<use :xlink:href="iconName" /> |
||||
|
</svg> |
||||
|
</template> |
||||
|
<script setup lang='ts'> |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
iconClass: { |
||||
|
type: String, |
||||
|
required: true |
||||
|
}, |
||||
|
className: { |
||||
|
type: String, |
||||
|
default: '' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const isExternal = /^(https?:|mailto:|tel:)/.test(props.iconClass) |
||||
|
const iconName = `#icon-${props.iconClass}` |
||||
|
const svgClass = props.className ? `svg-icon ${props.className}` : 'svg-icon ' |
||||
|
const styleExternalIcon = () => { |
||||
|
return { |
||||
|
mask: `url(${props.iconClass}) no-repeat 50% 50%`, |
||||
|
'-webkit-mask': `url(${props.iconClass}) no-repeat 50% 50%` |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="postcss" scoped> |
||||
|
.svg-icon { |
||||
|
vertical-align: -0.15em; |
||||
|
fill: currentColor; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
.svg-external-icon { |
||||
|
background-color: currentColor; |
||||
|
mask-size: cover !important; |
||||
|
display: inline-block; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,63 @@ |
|||||
|
<template> |
||||
|
<a-layout style="height: 730px"> |
||||
|
<a-layout-sider width="200" style="background: #fff"> |
||||
|
<Sidebar /> |
||||
|
</a-layout-sider> |
||||
|
<a-layout style="padding: 0 24px 24px"> |
||||
|
<a-breadcrumb style="margin: 16px 0"> |
||||
|
<a-breadcrumb-item>{{ mainNav }}</a-breadcrumb-item> |
||||
|
<a-breadcrumb-item>{{ subNav }}</a-breadcrumb-item> |
||||
|
<a-breadcrumb-item>{{ page }}</a-breadcrumb-item> |
||||
|
</a-breadcrumb> |
||||
|
<a-layout-content |
||||
|
:style="{ background: '#fff', padding: '24px', margin: 0, minHeight: '280px' }" |
||||
|
> |
||||
|
<Content /> |
||||
|
</a-layout-content> |
||||
|
</a-layout> |
||||
|
</a-layout> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang='ts'> |
||||
|
import Sidebar from './components/sidebar.vue' |
||||
|
import Content from './components/content.vue' |
||||
|
import { useRoute } from 'vue-router' |
||||
|
import { useLayoutStore } from '../store/modules/layout' |
||||
|
import { IMenubarList } from '../type/store/layout' |
||||
|
import { findCurrentMenubar } from '../utils/util' |
||||
|
import { computed, onMounted, ref } from 'vue' |
||||
|
|
||||
|
const route = useRoute() |
||||
|
const { getMenubar } = useLayoutStore() |
||||
|
|
||||
|
const mainNav = computed(() => { |
||||
|
const currentMenubar = findCurrentMenubar(getMenubar.menuList, true) |
||||
|
return currentMenubar?.meta.title |
||||
|
}) |
||||
|
|
||||
|
const subNav = computed(() => { |
||||
|
let subNav = '' |
||||
|
const currentMenubar = findCurrentMenubar(getMenubar.menuList, true) |
||||
|
currentMenubar.children?.forEach(v => { |
||||
|
if (route.path.indexOf(v.path) !== -1) { |
||||
|
subNav = v.meta.title |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
return subNav |
||||
|
}) |
||||
|
|
||||
|
const page = computed(() => { |
||||
|
let page = '' |
||||
|
const currentMenubar = findCurrentMenubar(getMenubar.menuList, true) |
||||
|
currentMenubar.children?.forEach(v => { |
||||
|
v.children?.forEach(vv => { |
||||
|
if (route.path == vv.path) { |
||||
|
page = vv.meta.title |
||||
|
} |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
|
return page |
||||
|
}) |
||||
|
</script> |
||||
@ -0,0 +1,17 @@ |
|||||
|
<template> |
||||
|
<router-view v-slot="{Component}"> |
||||
|
<transition name="fade-transform" mode="out-in"> |
||||
|
<keep-alive> |
||||
|
<component :is="Component" :key="key" /> |
||||
|
</keep-alive> |
||||
|
</transition> |
||||
|
</router-view> |
||||
|
</template> |
||||
|
<script setup lang='ts'> |
||||
|
import { computed } from 'vue' |
||||
|
import { useRoute } from 'vue-router' |
||||
|
|
||||
|
const route = useRoute() |
||||
|
|
||||
|
const key = computed(() => route.path) |
||||
|
</script> |
||||
@ -0,0 +1,39 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<a-layout-header class="header"> |
||||
|
<svg-icon class="logo" style="width: 36px; height: 36px;" icon-class="svg-dtm" /> |
||||
|
<a-menu |
||||
|
v-model:selectedKeys="activeMenu" |
||||
|
theme="dark" |
||||
|
mode="horizontal" |
||||
|
:style="{ lineHeight: '64px' }" |
||||
|
@select="onOpenChange" |
||||
|
> |
||||
|
<a-menu-item v-for="v in getMenubar.menuList" :key="v.path">{{ v.meta.title }}</a-menu-item> |
||||
|
</a-menu> |
||||
|
</a-layout-header> |
||||
|
</div> |
||||
|
</template> |
||||
|
<script setup lang='ts'> |
||||
|
import { computed, ref } from 'vue'; |
||||
|
import { useRoute, useRouter } from 'vue-router'; |
||||
|
import { useLayoutStore } from '/@/store/modules/layout' |
||||
|
|
||||
|
const route = useRoute() |
||||
|
const router = useRouter() |
||||
|
const { getMenubar } = useLayoutStore() |
||||
|
const firstRedirectPath = '/dashboard' |
||||
|
|
||||
|
const activeMenu = ref([route.meta.activeMenu !== firstRedirectPath ? route.meta.activeMenu : '/']) |
||||
|
|
||||
|
const onOpenChange = (d:any) => { |
||||
|
router.push({ path: d.key }) |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.logo { |
||||
|
float: left; |
||||
|
margin: 16px 24px 16px 0; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,57 @@ |
|||||
|
<template> |
||||
|
<a-menu |
||||
|
v-model:selectedKeys="activeMenu" |
||||
|
v-model:openKeys="openKeys" |
||||
|
mode="inline" |
||||
|
:style="{ height: '100%', borderRight: 0 }" |
||||
|
@select="onOpenChange" |
||||
|
> |
||||
|
<a-sub-menu v-for="v in filterSubMenubarData" :key="v.path"> |
||||
|
<template #title> |
||||
|
<span> |
||||
|
{{ v.meta.title }} |
||||
|
</span> |
||||
|
</template> |
||||
|
<a-menu-item v-for="vv in v.children" :key="vv.path">{{ vv.meta.title }}</a-menu-item> |
||||
|
</a-sub-menu> |
||||
|
</a-menu> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang='ts'> |
||||
|
import { ref, computed } from 'vue' |
||||
|
import { useRoute, useRouter } from 'vue-router' |
||||
|
import { useLayoutStore } from '/@/store/modules/layout' |
||||
|
import { IMenubarList } from '/@/type/store/layout' |
||||
|
import { findCurrentMenubar } from '/@/utils/util' |
||||
|
|
||||
|
const route = useRoute() |
||||
|
const router = useRouter() |
||||
|
const { getMenubar } = useLayoutStore() |
||||
|
|
||||
|
const filterSubMenubarData = computed(() => { |
||||
|
return findCurrentMenubar(getMenubar.menuList) as IMenubarList[] |
||||
|
}) |
||||
|
|
||||
|
const activeMenu = computed({ |
||||
|
get: () => { |
||||
|
return [route.path] |
||||
|
}, |
||||
|
set: (val) => { |
||||
|
// do nothing, just for eliminate warn |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const openKeys = computed({ |
||||
|
get: () => { |
||||
|
const pos = route.path.lastIndexOf('/') |
||||
|
return [route.path.substring(0, pos)] |
||||
|
}, |
||||
|
set: (val) => { |
||||
|
// do onthing, just for eliminate warn |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const onOpenChange = (d:any) => { |
||||
|
router.push({ path: d.key }) |
||||
|
} |
||||
|
</script> |
||||
@ -0,0 +1,12 @@ |
|||||
|
<script setup lang="ts"> |
||||
|
import Header from './components/header.vue' |
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<a-layout> |
||||
|
<a-layout-header class="header"> |
||||
|
<Header /> |
||||
|
</a-layout-header> |
||||
|
<router-view /> |
||||
|
</a-layout> |
||||
|
</template> |
||||
@ -0,0 +1,17 @@ |
|||||
|
import { createApp } from 'vue' |
||||
|
import App from './App.vue' |
||||
|
import router from '/@/router/index' |
||||
|
import { pinia } from '/@/store' |
||||
|
import '/@/permission' |
||||
|
|
||||
|
import Antd from 'ant-design-vue' |
||||
|
import 'ant-design-vue/dist/antd.css' |
||||
|
import 'virtual:svg-icons-register' |
||||
|
import SvgIcon from '/@/components/SvgIcon/index.vue' |
||||
|
|
||||
|
const app = createApp(App) |
||||
|
app.use(Antd) |
||||
|
app.use(router) |
||||
|
app.use(pinia) |
||||
|
app.component('SvgIcon', SvgIcon) |
||||
|
app.mount('#app') |
||||
@ -0,0 +1,23 @@ |
|||||
|
import router from '/@/router' |
||||
|
import { configure, start, done } from 'nprogress' |
||||
|
import { useLayoutStore } from './store/modules/layout' |
||||
|
|
||||
|
configure({ showSpinner: false }) |
||||
|
|
||||
|
const defaultRoutePath = '/' |
||||
|
|
||||
|
router.beforeEach((to) => { |
||||
|
start() |
||||
|
|
||||
|
const { getMenubar, concatAllowRoutes } = useLayoutStore() |
||||
|
|
||||
|
if (getMenubar.menuList.length === 0) { |
||||
|
concatAllowRoutes() |
||||
|
|
||||
|
return to.fullPath |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
router.afterEach(() => { |
||||
|
done() |
||||
|
}) |
||||
@ -0,0 +1,18 @@ |
|||||
|
const modules = import.meta.glob('../views/**/**.vue') |
||||
|
const components: IObject<() => Promise<typeof import('*.vue')>> = { |
||||
|
LayoutHeader: (() => import('/@/layout/index.vue')) as unknown as () => Promise<typeof import('*.vue')> |
||||
|
} |
||||
|
|
||||
|
Object.keys(modules).forEach(key => { |
||||
|
const nameMatch = key.match(/^\.\.\/views\/(.+)\.vue/) |
||||
|
if (!nameMatch) return |
||||
|
if (nameMatch[1].includes('_Components')) return |
||||
|
const indexMatch = nameMatch[1].match(/(.*)\/Index$/i) |
||||
|
let name = indexMatch ? indexMatch[1] : nameMatch[1]; |
||||
|
[name] = name.split('/').splice(-1) |
||||
|
components[name] = modules[key] as () => Promise<typeof import('*.vue')> |
||||
|
}) |
||||
|
|
||||
|
export { |
||||
|
components |
||||
|
} |
||||
@ -0,0 +1,109 @@ |
|||||
|
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' |
||||
|
import { IMenubarList } from '../type/store/layout'; |
||||
|
import { components } from './asyncRouter'; |
||||
|
|
||||
|
const Components: IObject<() => Promise<typeof import('*.vue')>> = Object.assign({}, components, { |
||||
|
LayoutHeader: (() => import('/@/layout/index.vue')) as unknown as () => Promise<typeof import('*.vue')>, |
||||
|
LayoutMain: (() => import('/@/layout/aside.vue')) as unknown as () => Promise<typeof import('*.vue')> |
||||
|
}) |
||||
|
|
||||
|
export const allowRouter: Array<IMenubarList> = [ |
||||
|
{ |
||||
|
name: 'Dashboard', |
||||
|
path: '/', |
||||
|
redirect: '/dashboard/sub1/1', |
||||
|
component: Components['LayoutHeader'], |
||||
|
meta: { title: '首页', activeMenu: '/dashboard' }, |
||||
|
children: [ |
||||
|
{ |
||||
|
name: 'SubNav1', |
||||
|
path: '/dashboard/sub1', |
||||
|
component: Components['LayoutMain'], |
||||
|
meta: { title: '子导航1' }, |
||||
|
children: [ |
||||
|
{ |
||||
|
name: 'Page1', |
||||
|
path: '/dashboard/sub1/1', |
||||
|
component: Components['DashboardPage1'], |
||||
|
meta: { title: '子页面1' }, |
||||
|
}, { |
||||
|
name: 'Page2', |
||||
|
path: '/dashboard/sub1/2', |
||||
|
component: Components['DashboardPage2'], |
||||
|
meta: { title: '子页面2' }, |
||||
|
} |
||||
|
] |
||||
|
}, { |
||||
|
name: 'SubNav2', |
||||
|
path: '/dashboard/sub2', |
||||
|
component: Components['LayoutMain'], |
||||
|
meta: { title: '子导航2' }, |
||||
|
children: [ |
||||
|
{ |
||||
|
name: 'Page21', |
||||
|
path: '/dashboard/sub2/1', |
||||
|
component: Components['DashboardPage1'], |
||||
|
meta: { title: '子页面21' }, |
||||
|
}, { |
||||
|
name: 'Page22', |
||||
|
path: '/dashboard/sub2/2', |
||||
|
component: Components['DashboardPage2'], |
||||
|
meta: { title: '子页面22' }, |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
] |
||||
|
}, { |
||||
|
name: 'Nav2', |
||||
|
path: '/nav2', |
||||
|
redirect: '/nav2/sub2/1', |
||||
|
component: Components['LayoutHeader'], |
||||
|
meta: { title: '导航2', activeMenu: '/nav2' }, |
||||
|
children: [ |
||||
|
{ |
||||
|
name: 'Nav2Sub1', |
||||
|
path: '/nav2/sub1', |
||||
|
component: Components['LayoutMain'], |
||||
|
meta: { title: '子导航1' }, |
||||
|
children: [ |
||||
|
{ |
||||
|
name: 'Nav2Page1', |
||||
|
path: '/nav2/sub1/1', |
||||
|
component: Components['DashboardPage1'], |
||||
|
meta: { title: '子页面1' }, |
||||
|
}, { |
||||
|
name: 'Nav2Page2', |
||||
|
path: '/nav2/sub1/2', |
||||
|
component: Components['DashboardPage2'], |
||||
|
meta: { title: '子页面2' }, |
||||
|
} |
||||
|
] |
||||
|
}, { |
||||
|
name: 'Nav2Sub2', |
||||
|
path: '/nav2/sub2', |
||||
|
component: Components['LayoutMain'], |
||||
|
meta: { title: '子导航2' }, |
||||
|
children: [ |
||||
|
{ |
||||
|
name: 'Nav2Page21', |
||||
|
path: '/nav2/sub2/1', |
||||
|
component: Components['DashboardPage1'], |
||||
|
meta: { title: '子页面21' }, |
||||
|
}, { |
||||
|
name: 'Nav2Page22', |
||||
|
path: '/nav2/sub2/2', |
||||
|
component: Components['DashboardPage2'], |
||||
|
meta: { title: '子页面22' }, |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
] |
||||
|
|
||||
|
const router = createRouter({ |
||||
|
history: createWebHistory(), |
||||
|
routes: allowRouter as RouteRecordRaw[] |
||||
|
}) |
||||
|
|
||||
|
export default router |
||||
@ -0,0 +1,2 @@ |
|||||
|
import { createPinia } from 'pinia' |
||||
|
export const pinia = createPinia() |
||||
@ -0,0 +1,32 @@ |
|||||
|
import { defineStore } from 'pinia'; |
||||
|
import { allowRouter } from '/@/router'; |
||||
|
import { ILayout, IMenubar, IMenubarList, IStatus } from '/@/type/store/layout'; |
||||
|
import { findCurrentMenubar } from '/@/utils/util'; |
||||
|
|
||||
|
export const useLayoutStore = defineStore({ |
||||
|
id: 'layout', |
||||
|
state: ():ILayout => ({ |
||||
|
menubar: { |
||||
|
menuList: [] |
||||
|
}, |
||||
|
status: { |
||||
|
isLoading: false |
||||
|
}, |
||||
|
}), |
||||
|
getters: { |
||||
|
getMenubar(): IMenubar { |
||||
|
return this.menubar |
||||
|
}, |
||||
|
getStatus(): IStatus { |
||||
|
return this.status |
||||
|
}, |
||||
|
}, |
||||
|
actions: { |
||||
|
setRoutes(data: Array<IMenubarList>): void { |
||||
|
this.menubar.menuList = data |
||||
|
}, |
||||
|
concatAllowRoutes(): void { |
||||
|
allowRouter.reverse().forEach(v => this.menubar.menuList.unshift(v)) |
||||
|
}, |
||||
|
} |
||||
|
}) |
||||
@ -0,0 +1,11 @@ |
|||||
|
export {} |
||||
|
declare global { |
||||
|
interface IObject<T> { |
||||
|
[index: string]: T |
||||
|
} |
||||
|
interface ImportMetaEnv { |
||||
|
VITE_APP_TITLE: string |
||||
|
VITE_PORT: number |
||||
|
VITE_PROXY: string |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,5 @@ |
|||||
|
declare module '*.vue' { |
||||
|
import { defineComponent } from 'vue'; |
||||
|
const Component: ReturnType<typeof defineComponent> |
||||
|
export default Component |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
export interface IMenubar { |
||||
|
menuList: Array<IMenubarList> |
||||
|
} |
||||
|
|
||||
|
export interface ILayout { |
||||
|
menubar: IMenubar |
||||
|
status: IStatus |
||||
|
} |
||||
|
|
||||
|
export interface IStatus { |
||||
|
isLoading: boolean |
||||
|
} |
||||
|
|
||||
|
export interface IMenubarList { |
||||
|
parentId?: number | string |
||||
|
id?: number | string |
||||
|
name: string |
||||
|
path: string |
||||
|
redirect?: string |
||||
|
meta: { |
||||
|
icon?: string |
||||
|
title: string |
||||
|
permission?: string[] |
||||
|
activeMenu?: string |
||||
|
hidden?: boolean |
||||
|
alwaysShow?: boolean |
||||
|
} |
||||
|
component: (() => Promise<typeof import('*.vue')>) | string |
||||
|
children?: Array<IMenubarList> |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
import { useRoute } from 'vue-router'; |
||||
|
import { IMenubarList } from '../type/store/layout'; |
||||
|
|
||||
|
export const findCurrentMenubar = (menuList: IMenubarList[], root?:boolean) => { |
||||
|
const route = useRoute() |
||||
|
let arr:IMenubarList[] | IMenubarList = [] |
||||
|
for (let i = 0; i < menuList.length; i++) { |
||||
|
const v = menuList[i]; |
||||
|
const usePath = v.meta.activeMenu || v.redirect || v.path; |
||||
|
const pos = usePath.lastIndexOf('/') |
||||
|
const rootPath = pos == 0 ? usePath : usePath.substring(0, pos) |
||||
|
if (route.path.indexOf(rootPath) !== -1) { |
||||
|
if (!root) { |
||||
|
arr = v.children as IMenubarList[] |
||||
|
} else { |
||||
|
arr = v |
||||
|
} |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return arr |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
<script setup lang='ts'> |
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<div> |
||||
|
<h1>Page1</h1> |
||||
|
</div> |
||||
|
</template> |
||||
@ -0,0 +1,9 @@ |
|||||
|
<script setup lang='ts'> |
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<div> |
||||
|
<h1>Page2</h1> |
||||
|
</div> |
||||
|
</template> |
||||
@ -0,0 +1,21 @@ |
|||||
|
{ |
||||
|
"compilerOptions": { |
||||
|
"target": "esnext", |
||||
|
"useDefineForClassFields": true, |
||||
|
"module": "esnext", |
||||
|
"moduleResolution": "node", |
||||
|
"strict": true, |
||||
|
"jsx": "preserve", |
||||
|
"sourceMap": true, |
||||
|
"resolveJsonModule": true, |
||||
|
"esModuleInterop": true, |
||||
|
"baseUrl": ".", |
||||
|
"paths": { |
||||
|
"/@/*": ["src/*"], |
||||
|
}, |
||||
|
"lib": ["esnext", "dom"], |
||||
|
"types": ["vite/client", "node"] |
||||
|
}, |
||||
|
"include": ["**/*.ts", "**/*.d.ts", "**/*.tsx", "**/*.vue"], |
||||
|
"exclude": ["node_modules"] |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
import { ConfigEnv, UserConfigExport } from 'vite'; |
||||
|
import path from 'path'; |
||||
|
import vue from '@vitejs/plugin-vue'; |
||||
|
import viteSvgIcons from 'vite-plugin-svg-icons'; |
||||
|
|
||||
|
const setAlias = (alias: [string, string][]) => alias.map((v) => { |
||||
|
return { find: v[0], replacement: path.resolve(__dirname, v[1]) }; |
||||
|
}); |
||||
|
|
||||
|
export default ({}: ConfigEnv): UserConfigExport => { |
||||
|
return { |
||||
|
resolve: { |
||||
|
alias: setAlias([['/@', 'src']]), |
||||
|
}, |
||||
|
plugins: [ |
||||
|
vue(), |
||||
|
viteSvgIcons({ |
||||
|
iconDirs: [path.resolve(process.cwd(), 'src/icons')], |
||||
|
symbolId: 'icon-[dir]-[name]' |
||||
|
}) |
||||
|
], |
||||
|
css: { |
||||
|
postcss: { |
||||
|
plugins: [ |
||||
|
require('autoprefixer'), |
||||
|
require('postcss-simple-vars'), |
||||
|
require('postcss-import') |
||||
|
] |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
}; |
||||
File diff suppressed because it is too large
Loading…
Reference in new issue