1
0
mirror of https://github.com/zclzone/vue-naive-admin.git synced 2025-12-28 04:00:22 +08:00

11 Commits

Author SHA1 Message Date
zhangchuanlong
d7a61db339 feat: 集成unplugin-vue-components插件,NaiveUI组件无需再手动引入 2022-02-21 11:25:06 +08:00
zhangchuanlong
39da2634d5 mod: update readme 2022-02-18 15:28:15 +08:00
zhangchuanlong
b569b58c9d mod: update readme 2022-02-18 09:21:53 +08:00
zhangchuanlong
525d379af3 fix:修复切换角色后路由未正确添加问题 2022-02-18 09:21:33 +08:00
zhangchuanlong
59cf11be7a feat:增加切换角色支持 2022-02-17 19:02:05 +08:00
zhangchuanlong
5eb6874754 mod: 修改基础组件Message写法,并修改对应的规则描述 2022-02-16 10:01:55 +08:00
zhangchuanlong
96644f14f5 feat: 增加语雀文档外链 2022-02-16 09:08:05 +08:00
zhangchuanlong
a240985ee7 feat: 侧边菜单栏增加外链支持 2022-02-15 14:29:06 +08:00
zhangchuanlong
741524ffac mod: update readme 2022-02-14 14:05:40 +08:00
zhangchuanlong
d94a7a5a05 feat:安装方式替换为pnpm 2022-02-11 12:05:45 +08:00
zhangchuanlong
9f2126835a mod: 删除多余路由及组件 2022-02-10 17:42:16 +08:00
19 changed files with 2569 additions and 2999 deletions

View File

@@ -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 组件
- 🍋 二次封装 localStorage 和 sessionStorage支持设置过期时间
## 预览
[template.qszone.com](https://template.qszone.com)
## 文档
[羽雀文档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
# 安装依赖
npm i
# 安装依赖(建议使用pnpm: https://pnpm.io/zh/installation)
pnpm i # 或者 npm i
# 启动
npm run dev

View File

@@ -1,11 +1,24 @@
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 { unocss } from './unocss'
import { configHtmlPlugin } from './html'
import { configMockPlugin } from './mock'
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))

2955
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -39,6 +39,7 @@
"prettier": "^2.5.1",
"sass": "^1.38.1",
"unocss": "^0.16.3",
"unplugin-vue-components": "^0.17.18",
"vite": "^2.7.6",
"vite-plugin-html": "^2.1.1",
"vite-plugin-mock": "^2.9.6",

2429
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -8,9 +8,8 @@ let loadingMessage = null
class Message {
/**
* 规则:
* * 同一Message实例只显示一个loading message如果需要显示多个可以创建多个Message实例
* * 新的message会替换正在显示的loading message
* * 默认已创建一个Message实例$message挂载到window同时也将Message类挂载到了window
* * loading message只显示一个新的message会替换正在显示的loading message
* * loading message不会自动清除除非被替换成非loading message非loading message默认2秒后自动清除
*/
removeMessage(message, duration = 2000) {
@@ -23,20 +22,20 @@ class Message {
}
showMessage(type, content, option = {}) {
if (this.loadingMessage && this.loadingMessage.type === 'loading') {
if (loadingMessage && loadingMessage.type === 'loading') {
// 如果存在则替换正在显示的loading message
this.loadingMessage.type = type
this.loadingMessage.content = content
loadingMessage.type = type
loadingMessage.content = content
if (type !== 'loading') {
// 非loading message需设置自动清除
this.removeMessage(this.loadingMessage, option.duration)
this.removeMessage(loadingMessage, option.duration)
}
} else {
// 不存在正在显示的loading则新建一个message,如果新建的message是loading message则将message赋值存储下来
let message = NMessage[type](content, option)
if (type === 'loading') {
this.loadingMessage = message
loadingMessage = message
}
}
}

View File

@@ -1,6 +1,4 @@
<script setup>
import { NConfigProvider, NGlobalStyle, NLoadingBarProvider, NMessageProvider, NDialogProvider } from 'naive-ui'
import MessageContent from './MessageContent.vue'
import DialogContent from './DialogContent.vue'
import LoadingBar from './LoadingBar.vue'

View File

@@ -1,5 +1,4 @@
<script setup>
import { NBreadcrumb, NBreadcrumbItem } from 'naive-ui'
import { useRouter } from 'vue-router'
const router = useRouter()
const { currentRoute } = router

View File

@@ -1,12 +1,18 @@
<script setup>
import { useUserStore } from '@/store/modules/user'
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 router = useRouter()
const options = [
{
label: '切换角色',
key: 'switchRole',
},
{
label: '退出登录',
key: 'logout',
@@ -15,11 +21,54 @@ const options = [
function handleSelect(key) {
if (key === 'logout') {
userStore.logout()
$message.success('已退出登录')
router.push({ path: '/login' })
logout()
} else if (key === 'switchRole') {
switchRole()
}
}
function logout() {
userStore.logout()
$message.success('已退出登录')
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>
<template>

View File

@@ -1,5 +1,4 @@
<script setup>
import { NGradientText, NIcon } from 'naive-ui'
import { LastfmSquare } from '@vicons/fa'
const title = import.meta.env.VITE_APP_TITLE
</script>

View File

@@ -1,23 +1,24 @@
<script setup>
import { NMenu } from 'naive-ui'
import { useRouter } from 'vue-router'
import { computed } from 'vue'
import { usePermissionStore } from '@/store/modules/permission'
import { isExternal } from '@/utils/is'
const router = useRouter()
const permissionStore = usePermissionStore()
const { currentRoute } = router
const routes = permissionStore.routes
const menuOptions = computed(() => {
return generateOptions(routes, '')
return generateOptions(permissionStore.routes, '')
})
function resolvePath(...pathes) {
function resolvePath(basePath, path) {
if (isExternal(path)) return path
return (
'/' +
pathes
[basePath, path]
.filter((path) => !!path && path !== '/')
.map((path) => path.replace(/(^\/)|(\/$)/g, ''))
.join('/')
@@ -49,7 +50,11 @@ function generateOptions(routes, basePath) {
}
function handleMenuSelect(key, item) {
router.push(item.path)
if (isExternal(item.path)) {
window.open(item.path)
} else {
router.push(item.path)
}
// 通过path重定向
// router.push({

View File

@@ -1,6 +1,4 @@
<script setup>
import { NLayout, NLayoutHeader, NLayoutSider } from 'naive-ui'
import AppHeader from './components/header/index.vue'
import SideMenu from './components/sidebar/index.vue'
import AppMain from './components/AppMain.vue'

View File

@@ -11,9 +11,10 @@ export const router = createRouter({
export function resetRouter() {
router.getRoutes().forEach((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)
})
}

View File

@@ -8,12 +8,6 @@ export const basicRoutes = [
component: () => import('@/views/error-page/404.vue'),
isHidden: true,
},
{
name: '401',
path: '/401',
component: () => import('@/views/error-page/401.vue'),
isHidden: true,
},
{
name: 'REDIRECT',
path: '/redirect',
@@ -27,7 +21,6 @@ export const basicRoutes = [
},
],
},
{
name: '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 = {

View File

@@ -42,5 +42,8 @@ export const useUserStore = defineStore('user', {
removeToken()
this.userInfo = {}
},
setUserInfo(userInfo = {}) {
this.userInfo = { ...this.userInfo, ...userInfo }
},
},
})

View File

@@ -97,6 +97,14 @@ export function isUrl(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 isClient = !isServer

View File

@@ -1,3 +0,0 @@
<template>
<h1>401</h1>
</template>

View File

@@ -1,5 +1,4 @@
<script setup name="TestDialog">
import { NButton } from 'naive-ui'
const handleDelete = function () {
$dialog.confirm({
content: '确认删除?',

View File

@@ -1,6 +1,4 @@
<script setup>
import { NButton } from 'naive-ui'
function handleLogin() {
$message.loading('登陆中...')
setTimeout(() => {