mirror of
https://github.com/zclzone/vue-naive-admin.git
synced 2025-12-28 12:10:20 +08:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7a61db339 | ||
|
|
39da2634d5 | ||
|
|
b569b58c9d | ||
|
|
525d379af3 | ||
|
|
59cf11be7a | ||
|
|
5eb6874754 | ||
|
|
96644f14f5 | ||
|
|
a240985ee7 | ||
|
|
741524ffac | ||
|
|
d94a7a5a05 | ||
|
|
9f2126835a |
10
README.md
10
README.md
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## 简介
|
## 简介
|
||||||
|
|
||||||
Vue Naive Admin,一个基于 Vue3.0、Vite、Naive UI 的后台管理模板,相较于其他比较流行的后台管理模板,此项目相对简洁、轻量,没有集成 TypeScript,没有集成国际化,没有集成复杂的主题配置,学习成本非常低,对新手极其友好。不过麻雀虽小五脏俱全,权限、Mock、菜单、axios 封装、pinia、项目配置、样式配置、环境配置,以及一些经常用的基础组件封装等等这些该有的都有,经过参考多个 vue3 后台管理模板后以最简洁优雅的方式实现,非常适用于中小型项目或者个人项目,当然,以此模板进行二次封装改造用于大型项目也未尝不可。
|
Vue Naive Admin,一个基于 Vue3.0、Vite、Naive UI 的轻量级后台管理模板,没有集成 TypeScript,没有集成国际化,没有集成复杂的主题配置,上手成本非常低,对新手极其友好。不过麻雀虽小五脏俱全,权限、Mock、菜单、axios 封装、pinia、项目配置、样式配置、环境配置,以及一些经常用的基础组件封装等等这些该有的都有,参考多个 vue3 后台管理模板后以最简洁优雅的方式实现,非常适用于中小型项目或者个人项目。
|
||||||
|
|
||||||
## 为什么要开发这个模板
|
## 为什么要开发这个模板
|
||||||
|
|
||||||
@@ -23,6 +23,10 @@ Vue Naive Admin,一个基于 Vue3.0、Vite、Naive UI 的后台管理模板,
|
|||||||
- 🍌 二次封装全局 Dialog、Message、LoadingBar 组件
|
- 🍌 二次封装全局 Dialog、Message、LoadingBar 组件
|
||||||
- 🍋 二次封装 localStorage 和 sessionStorage,支持设置过期时间
|
- 🍋 二次封装 localStorage 和 sessionStorage,支持设置过期时间
|
||||||
|
|
||||||
|
## 预览
|
||||||
|
|
||||||
|
[template.qszone.com](https://template.qszone.com)
|
||||||
|
|
||||||
## 文档
|
## 文档
|
||||||
|
|
||||||
[羽雀文档:Vue Naive Admin](https://www.yuque.com/qszone/vue-naive-admin)
|
[羽雀文档:Vue Naive Admin](https://www.yuque.com/qszone/vue-naive-admin)
|
||||||
@@ -40,8 +44,8 @@ git clone https://github.com/zclzone/vue-naive-admin.git
|
|||||||
# 进入项目目录
|
# 进入项目目录
|
||||||
cd vue-naive-admin
|
cd vue-naive-admin
|
||||||
|
|
||||||
# 安装依赖
|
# 安装依赖(建议使用pnpm: https://pnpm.io/zh/installation)
|
||||||
npm i
|
pnpm i # 或者 npm i
|
||||||
|
|
||||||
# 启动
|
# 启动
|
||||||
npm run dev
|
npm run dev
|
||||||
|
|||||||
@@ -1,11 +1,24 @@
|
|||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
import Components from 'unplugin-vue-components/vite'
|
||||||
|
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
|
||||||
|
|
||||||
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
|
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
|
||||||
|
|
||||||
import { unocss } from './unocss'
|
import { unocss } from './unocss'
|
||||||
import { configHtmlPlugin } from './html'
|
import { configHtmlPlugin } from './html'
|
||||||
import { configMockPlugin } from './mock'
|
import { configMockPlugin } from './mock'
|
||||||
|
|
||||||
export function createVitePlugins(viteEnv, isBuild) {
|
export function createVitePlugins(viteEnv, isBuild) {
|
||||||
const plugins = [vue(), VueSetupExtend(), unocss(), configHtmlPlugin(viteEnv, isBuild)]
|
const plugins = [
|
||||||
|
vue(),
|
||||||
|
Components({
|
||||||
|
resolvers: [NaiveUiResolver()],
|
||||||
|
}),
|
||||||
|
VueSetupExtend(),
|
||||||
|
unocss(),
|
||||||
|
configHtmlPlugin(viteEnv, isBuild),
|
||||||
|
]
|
||||||
|
|
||||||
viteEnv?.VITE_APP_USE_MOCK && plugins.push(configMockPlugin(isBuild))
|
viteEnv?.VITE_APP_USE_MOCK && plugins.push(configMockPlugin(isBuild))
|
||||||
|
|
||||||
|
|||||||
2955
package-lock.json
generated
2955
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -39,6 +39,7 @@
|
|||||||
"prettier": "^2.5.1",
|
"prettier": "^2.5.1",
|
||||||
"sass": "^1.38.1",
|
"sass": "^1.38.1",
|
||||||
"unocss": "^0.16.3",
|
"unocss": "^0.16.3",
|
||||||
|
"unplugin-vue-components": "^0.17.18",
|
||||||
"vite": "^2.7.6",
|
"vite": "^2.7.6",
|
||||||
"vite-plugin-html": "^2.1.1",
|
"vite-plugin-html": "^2.1.1",
|
||||||
"vite-plugin-mock": "^2.9.6",
|
"vite-plugin-mock": "^2.9.6",
|
||||||
|
|||||||
2429
pnpm-lock.yaml
generated
Normal file
2429
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -8,9 +8,8 @@ let loadingMessage = null
|
|||||||
class Message {
|
class Message {
|
||||||
/**
|
/**
|
||||||
* 规则:
|
* 规则:
|
||||||
* * 同一Message实例只显示一个loading message,如果需要显示多个可以创建多个Message实例
|
* * loading message只显示一个,新的message会替换正在显示的loading message
|
||||||
* * 新的message会替换正在显示的loading message
|
* * loading message不会自动清除,除非被替换成非loading message,非loading message默认2秒后自动清除
|
||||||
* * 默认已创建一个Message实例$message挂载到window,同时也将Message类挂载到了window
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
removeMessage(message, duration = 2000) {
|
removeMessage(message, duration = 2000) {
|
||||||
@@ -23,20 +22,20 @@ class Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
showMessage(type, content, option = {}) {
|
showMessage(type, content, option = {}) {
|
||||||
if (this.loadingMessage && this.loadingMessage.type === 'loading') {
|
if (loadingMessage && loadingMessage.type === 'loading') {
|
||||||
// 如果存在则替换正在显示的loading message
|
// 如果存在则替换正在显示的loading message
|
||||||
this.loadingMessage.type = type
|
loadingMessage.type = type
|
||||||
this.loadingMessage.content = content
|
loadingMessage.content = content
|
||||||
|
|
||||||
if (type !== 'loading') {
|
if (type !== 'loading') {
|
||||||
// 非loading message需设置自动清除
|
// 非loading message需设置自动清除
|
||||||
this.removeMessage(this.loadingMessage, option.duration)
|
this.removeMessage(loadingMessage, option.duration)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 不存在正在显示的loading则新建一个message,如果新建的message是loading message则将message赋值存储下来
|
// 不存在正在显示的loading则新建一个message,如果新建的message是loading message则将message赋值存储下来
|
||||||
let message = NMessage[type](content, option)
|
let message = NMessage[type](content, option)
|
||||||
if (type === 'loading') {
|
if (type === 'loading') {
|
||||||
this.loadingMessage = message
|
loadingMessage = message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { NConfigProvider, NGlobalStyle, NLoadingBarProvider, NMessageProvider, NDialogProvider } from 'naive-ui'
|
|
||||||
|
|
||||||
import MessageContent from './MessageContent.vue'
|
import MessageContent from './MessageContent.vue'
|
||||||
import DialogContent from './DialogContent.vue'
|
import DialogContent from './DialogContent.vue'
|
||||||
import LoadingBar from './LoadingBar.vue'
|
import LoadingBar from './LoadingBar.vue'
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { NBreadcrumb, NBreadcrumbItem } from 'naive-ui'
|
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { currentRoute } = router
|
const { currentRoute } = router
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { useUserStore } from '@/store/modules/user'
|
import { useUserStore } from '@/store/modules/user'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { NDropdown } from 'naive-ui'
|
import { resetRouter } from '@/router'
|
||||||
|
import { usePermissionStore } from '@/store/modules/permission'
|
||||||
|
import { NOT_FOUND_ROUTE } from '@/router/routes'
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const options = [
|
const options = [
|
||||||
|
{
|
||||||
|
label: '切换角色',
|
||||||
|
key: 'switchRole',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '退出登录',
|
label: '退出登录',
|
||||||
key: 'logout',
|
key: 'logout',
|
||||||
@@ -15,10 +21,53 @@ const options = [
|
|||||||
|
|
||||||
function handleSelect(key) {
|
function handleSelect(key) {
|
||||||
if (key === 'logout') {
|
if (key === 'logout') {
|
||||||
|
logout()
|
||||||
|
} else if (key === 'switchRole') {
|
||||||
|
switchRole()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function logout() {
|
||||||
userStore.logout()
|
userStore.logout()
|
||||||
$message.success('已退出登录')
|
$message.success('已退出登录')
|
||||||
router.push({ path: '/login' })
|
router.push({ path: '/login' })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function switchRole() {
|
||||||
|
const permissionStore = usePermissionStore()
|
||||||
|
|
||||||
|
const users = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: '大脸怪(admin)',
|
||||||
|
avatar: 'https://gitee.com/zclzone/res/raw/master/qs-zone/blob/img/lADPDiQ3QDTwsz3NAarNAaw_428_426.jpg',
|
||||||
|
email: 'Ronnie@123.com',
|
||||||
|
role: ['admin'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: '大脸怪(editor)',
|
||||||
|
avatar: 'https://gitee.com/zclzone/res/raw/master/qs-zone/blob/img/lADPDiQ3QDTwsz3NAarNAaw_428_426.jpg',
|
||||||
|
email: 'Ronnie@123.com',
|
||||||
|
role: ['editor'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: '访客(guest)',
|
||||||
|
avatar: 'https://gitee.com/zclzone/res/raw/master/qs-zone/blob/img/lADPDiQ3QDTwsz3NAarNAaw_428_426.jpg',
|
||||||
|
role: [],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const switchUser = users[+userStore.userId % users.length]
|
||||||
|
resetRouter()
|
||||||
|
userStore.setUserInfo(switchUser)
|
||||||
|
const accessRoutes = permissionStore.generateRoutes(switchUser.role)
|
||||||
|
accessRoutes.forEach((route) => {
|
||||||
|
!router.hasRoute(route.name) && router.addRoute(route)
|
||||||
|
})
|
||||||
|
router.addRoute(NOT_FOUND_ROUTE)
|
||||||
|
$message.success(`${switchUser.name}`)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { NGradientText, NIcon } from 'naive-ui'
|
|
||||||
import { LastfmSquare } from '@vicons/fa'
|
import { LastfmSquare } from '@vicons/fa'
|
||||||
const title = import.meta.env.VITE_APP_TITLE
|
const title = import.meta.env.VITE_APP_TITLE
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { NMenu } from 'naive-ui'
|
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { usePermissionStore } from '@/store/modules/permission'
|
import { usePermissionStore } from '@/store/modules/permission'
|
||||||
|
|
||||||
|
import { isExternal } from '@/utils/is'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const permissionStore = usePermissionStore()
|
const permissionStore = usePermissionStore()
|
||||||
|
|
||||||
const { currentRoute } = router
|
const { currentRoute } = router
|
||||||
const routes = permissionStore.routes
|
|
||||||
|
|
||||||
const menuOptions = computed(() => {
|
const menuOptions = computed(() => {
|
||||||
return generateOptions(routes, '')
|
return generateOptions(permissionStore.routes, '')
|
||||||
})
|
})
|
||||||
|
|
||||||
function resolvePath(...pathes) {
|
function resolvePath(basePath, path) {
|
||||||
|
if (isExternal(path)) return path
|
||||||
return (
|
return (
|
||||||
'/' +
|
'/' +
|
||||||
pathes
|
[basePath, path]
|
||||||
.filter((path) => !!path && path !== '/')
|
.filter((path) => !!path && path !== '/')
|
||||||
.map((path) => path.replace(/(^\/)|(\/$)/g, ''))
|
.map((path) => path.replace(/(^\/)|(\/$)/g, ''))
|
||||||
.join('/')
|
.join('/')
|
||||||
@@ -49,7 +50,11 @@ function generateOptions(routes, basePath) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleMenuSelect(key, item) {
|
function handleMenuSelect(key, item) {
|
||||||
|
if (isExternal(item.path)) {
|
||||||
|
window.open(item.path)
|
||||||
|
} else {
|
||||||
router.push(item.path)
|
router.push(item.path)
|
||||||
|
}
|
||||||
|
|
||||||
// 通过path重定向
|
// 通过path重定向
|
||||||
// router.push({
|
// router.push({
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { NLayout, NLayoutHeader, NLayoutSider } from 'naive-ui'
|
|
||||||
|
|
||||||
import AppHeader from './components/header/index.vue'
|
import AppHeader from './components/header/index.vue'
|
||||||
import SideMenu from './components/sidebar/index.vue'
|
import SideMenu from './components/sidebar/index.vue'
|
||||||
import AppMain from './components/AppMain.vue'
|
import AppMain from './components/AppMain.vue'
|
||||||
|
|||||||
@@ -11,9 +11,10 @@ export const router = createRouter({
|
|||||||
export function resetRouter() {
|
export function resetRouter() {
|
||||||
router.getRoutes().forEach((route) => {
|
router.getRoutes().forEach((route) => {
|
||||||
const { name } = route
|
const { name } = route
|
||||||
if (name && !WHITE_NAME_LIST.includes(name)) {
|
|
||||||
router.hasRoute(name) && router.removeRoute(name)
|
router.hasRoute(name) && router.removeRoute(name)
|
||||||
}
|
})
|
||||||
|
basicRoutes.forEach((route) => {
|
||||||
|
!router.hasRoute(route.name) && router.addRoute(route)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,6 @@ export const basicRoutes = [
|
|||||||
component: () => import('@/views/error-page/404.vue'),
|
component: () => import('@/views/error-page/404.vue'),
|
||||||
isHidden: true,
|
isHidden: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: '401',
|
|
||||||
path: '/401',
|
|
||||||
component: () => import('@/views/error-page/401.vue'),
|
|
||||||
isHidden: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'REDIRECT',
|
name: 'REDIRECT',
|
||||||
path: '/redirect',
|
path: '/redirect',
|
||||||
@@ -27,7 +21,6 @@ export const basicRoutes = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: 'LOGIN',
|
name: 'LOGIN',
|
||||||
path: '/login',
|
path: '/login',
|
||||||
@@ -93,6 +86,38 @@ export const basicRoutes = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'EXTERNAL-LINK',
|
||||||
|
path: '/external-link',
|
||||||
|
component: Layout,
|
||||||
|
meta: {
|
||||||
|
title: '外链',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'LINK-GITHUB-SRC',
|
||||||
|
path: 'https://github.com/zclzone/vue-naive-admin',
|
||||||
|
meta: {
|
||||||
|
title: '源码 - github',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LINK-GITEE-SRC',
|
||||||
|
path: 'https://gitee.com/zclzone/vue-naive-admin',
|
||||||
|
meta: {
|
||||||
|
title: '源码 - gitee',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LINK-DOCS',
|
||||||
|
path: 'https://www.yuque.com/qszone/vue-naive-admin',
|
||||||
|
meta: {
|
||||||
|
title: '文档 - 语雀',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export const NOT_FOUND_ROUTE = {
|
export const NOT_FOUND_ROUTE = {
|
||||||
|
|||||||
@@ -42,5 +42,8 @@ export const useUserStore = defineStore('user', {
|
|||||||
removeToken()
|
removeToken()
|
||||||
this.userInfo = {}
|
this.userInfo = {}
|
||||||
},
|
},
|
||||||
|
setUserInfo(userInfo = {}) {
|
||||||
|
this.userInfo = { ...this.userInfo, ...userInfo }
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -97,6 +97,14 @@ export function isUrl(path) {
|
|||||||
return reg.test(path)
|
return reg.test(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} path
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
export function isExternal(path) {
|
||||||
|
return /^(https?:|mailto:|tel:)/.test(path)
|
||||||
|
}
|
||||||
|
|
||||||
export const isServer = typeof window === 'undefined'
|
export const isServer = typeof window === 'undefined'
|
||||||
|
|
||||||
export const isClient = !isServer
|
export const isClient = !isServer
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
<template>
|
|
||||||
<h1>401</h1>
|
|
||||||
</template>
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup name="TestDialog">
|
<script setup name="TestDialog">
|
||||||
import { NButton } from 'naive-ui'
|
|
||||||
const handleDelete = function () {
|
const handleDelete = function () {
|
||||||
$dialog.confirm({
|
$dialog.confirm({
|
||||||
content: '确认删除?',
|
content: '确认删除?',
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { NButton } from 'naive-ui'
|
|
||||||
|
|
||||||
function handleLogin() {
|
function handleLogin() {
|
||||||
$message.loading('登陆中...')
|
$message.loading('登陆中...')
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user