Compare commits

..

11 Commits

Author SHA1 Message Date
chenghongxing 17e41ce1fc fix: Fixed the display problem of breadcrumbs on dynamic route page; 🐛
修复:修复动态路由页面的面包屑显示问题;
2023-04-11 10:28:01 +08:00
chenghongxing baae56f715 add: support parameterized route multi-tab title setting; #339 🐛
新增:支持带参路由多页签标题设置;
2023-04-10 23:27:37 +08:00
iczer 9a7493c99c
Merge pull request #250 from matrix-zyh/patch-1
Fix: document errors;
修复:文档错误;
2023-03-29 10:53:27 +08:00
iczer cc60ff1a27
Merge pull request #186 from fqucuo/patch-1
Optimization: Add beforeEnter parsing to asynchronous routing;
优化:异步路由中增加对beforeEnter的解析;
2023-03-29 10:51:14 +08:00
iczer c0e5f2ccec
Update README.md 2023-03-27 12:19:23 +08:00
iczer 3fa8995690
Update README.md 2023-03-27 12:14:59 +08:00
chenghongxing a4d4b28ce3 fix: fixed the issue that the router permission configuration does not support arrays
; 🐛
修复:修复路由权限配置不支持数组的问题;
2023-03-15 14:50:41 +08:00
chenghongxing d7a530db46 fix: fixed the color problem in night mode; 🐛
修复:修复黑夜模式下颜色问题;
2023-03-12 17:24:48 +08:00
iczer 7f5fbb7426 fix: fixed border color consistent with vue3 version; 🐛
修复:修正边框颜色与 vue3 版本一致;
2023-03-12 14:40:07 +08:00
Matrix baf063f9ea
onFulfilled -> onRejected
响应拦截器  - onRejected 那部分,单词写错了,大大你把写成了 onRejected 写成了 onFulfilled
2021-08-06 14:31:23 +08:00
fqucuo f0d60a8242
异步路由中增加对beforeEnter的解析
异步路由中增加对beforeEnter的解析
2021-01-24 00:19:22 +08:00
10 changed files with 56 additions and 39 deletions

View File

@ -1,3 +1,5 @@
简体中文 | [English](./README.en-US.md)
<h1 align="center">Vue Antd Admin</h1>
@ -6,6 +8,12 @@
[Ant Design Pro](https://github.com/ant-design/ant-design-pro) 的 Vue 实现版本
开箱即用的中后台前端/设计解决方案
:star::star::star:
vue3 版本现已推出,更名为
[stepin-template](https://github.com/stepui/stepin-template),欢迎体验,
[立即前往](https://github.com/stepui/stepin-template)
--
[![MIT](https://img.shields.io/github/license/iczer/vue-antd-admin)](https://github.com/iczer/vue-antd-admin/blob/master/LICENSE)
[![Dependence](https://img.shields.io/david/iczer/vue-antd-admin)](https://david-dm.org/iczer/vue-antd-admin)
[![DevDependencies](https://img.shields.io/david/dev/iczer/vue-antd-admin)](https://david-dm.org/iczer/vue-antd-admin?type=dev)

View File

@ -44,7 +44,7 @@ const tokenCheck = {
* `options: Object`: 应用配置,包含: {router, i18n, store, message},可根据需要扩展。
### onRejected
我们会为 onFulfilled 钩子函数注入 error 和 options 两个参数:
我们会为 onRejected 钩子函数注入 error 和 options 两个参数:
* `error: Error`: axios 请求错误对象
* `options: Object`: 应用配置,包含: {router, i18n, store, message},可根据需要扩展。
@ -128,4 +128,4 @@ export default {
response: [resp401, resp403] // 响应拦截
}
```
:::
:::

View File

@ -22,10 +22,11 @@ const ANTD = {
'component-background': '#fff',
'heading-color': 'rgba(0, 0, 0, 0.85)',
'text-color': 'rgba(0, 0, 0, 0.65)',
'text-color-inverse': '#fff',
'text-color-inverse': '#fefefe',
'text-color-secondary': 'rgba(0, 0, 0, 0.45)',
'shadow-color': 'rgba(0, 0, 0, 0.15)',
'border-color-split': '#f0f0f0',
'border-color-base': '#d9d9d9',
'background-color-light': '#fafafa',
'background-color-base': '#f5f5f5',
'table-selected-row-bg': '#fafafa',
@ -34,8 +35,9 @@ const ANTD = {
'disabled-color': 'rgba(0, 0, 0, 0.25)',
'menu-dark-color': 'rgba(254, 254, 254, 0.65)',
'menu-dark-highlight-color': '#fefefe',
'menu-dark-selected-item-icon-color': '#fefefe',
'menu-dark-arrow-color': '#fefefe',
'btn-primary-color': '#fff',
'btn-primary-color': '#fefefe',
},
light: {
'layout-body-background': '#f0f2f5',
@ -43,10 +45,11 @@ const ANTD = {
'component-background': '#fff',
'heading-color': 'rgba(0, 0, 0, 0.85)',
'text-color': 'rgba(0, 0, 0, 0.65)',
'text-color-inverse': '#fff',
'text-color-inverse': '#fefefe',
'text-color-secondary': 'rgba(0, 0, 0, 0.45)',
'shadow-color': 'rgba(0, 0, 0, 0.15)',
'border-color-split': '#f0f0f0',
'border-color-base': '#d9d9d9',
'background-color-light': '#fafafa',
'background-color-base': '#f5f5f5',
'table-selected-row-bg': '#fafafa',
@ -55,8 +58,9 @@ const ANTD = {
'disabled-color': 'rgba(0, 0, 0, 0.25)',
'menu-dark-color': 'rgba(1, 1, 1, 0.65)',
'menu-dark-highlight-color': '#fefefe',
'menu-dark-selected-item-icon-color': '#fefefe',
'menu-dark-arrow-color': '#fefefe',
'btn-primary-color': '#fff',
'btn-primary-color': '#fefefe',
},
night: {
'layout-body-background': '#000',
@ -64,10 +68,11 @@ const ANTD = {
'component-background': '#141414',
'heading-color': 'rgba(255, 255, 255, 0.85)',
'text-color': 'rgba(255, 255, 255, 0.85)',
'text-color-inverse': '#141414',
'text-color-inverse': '#fefefe',
'text-color-secondary': 'rgba(255, 255, 255, 0.45)',
'shadow-color': 'rgba(255, 255, 255, 0.15)',
'border-color-split': '#303030',
'border-color-base': '#282828',
'background-color-light': '#ffffff0a',
'background-color-base': '#2a2a2a',
'table-selected-row-bg': '#ffffff0a',
@ -76,8 +81,9 @@ const ANTD = {
'disabled-color': 'rgba(255, 255, 255, 0.25)',
'menu-dark-color': 'rgba(254, 254, 254, 0.65)',
'menu-dark-highlight-color': '#fefefe',
'menu-dark-selected-item-icon-color': '#fefefe',
'menu-dark-arrow-color': '#fefefe',
'btn-primary-color': '#141414',
'btn-primary-color': '#fefefe',
}
}
}

View File

@ -92,7 +92,7 @@ export default {
let routes = this.$route.matched
const path = this.$route.path
let breadcrumb = []
routes.filter(item => path.includes(item.path))
routes.filter(item => path.includes(item.path) || item.regex.test(path))
.forEach(route => {
const path = route.path.length === 0 ? '/home' : route.path
breadcrumb.push(this.$t(getI18nKey(path)))

View File

@ -95,8 +95,7 @@
this.$emit('contextmenu', pageKey, e)
},
pageName(page) {
const pagePath = page.fullPath.split('?')[0]
const custom = this.customTitles.find(item => item.path === pagePath)
const custom = this.customTitles.find(item => item.path === page.path)
return (custom && custom.title) || page.title || this.$t(getI18nKey(page.keyPath))
}
}

View File

@ -13,7 +13,7 @@
<div :class="['tabs-view-content', layout, pageWidth]" :style="`margin-top: ${multiPage ? -24 : 0}px`">
<page-toggle-transition :disabled="animate.disabled" :animate="animate.name" :direction="animate.direction">
<a-keep-alive :exclude-keys="excludeKeys" v-if="multiPage && cachePage" v-model="clearCaches">
<router-view v-if="!refreshing" ref="tabContent" :key="$route.path" />
<router-view v-if="!refreshing" ref="tabContent" :key="$route.fullPath" />
</a-keep-alive>
<router-view ref="tabContent" v-else-if="!refreshing" />
</page-toggle-transition>
@ -62,10 +62,10 @@ export default {
this.loadCacheConfig(this.$router?.options?.routes)
this.loadCachedTabs()
const route = this.$route
if (this.pageList.findIndex(item => item.path === route.path) === -1) {
if (this.pageList.findIndex(item => item.path === route.fullPath) === -1) {
this.pageList.push(this.createPage(route))
}
this.activePage = route.path
this.activePage = route.fullPath
if (this.multiPage) {
this.$nextTick(() => {
this.setCachedKey(route)
@ -86,8 +86,8 @@ export default {
this.loadCacheConfig(val)
},
'$route': function (newRoute) {
this.activePage = newRoute.path
const page = this.pageList.find(item => item.path === newRoute.path)
this.activePage = newRoute.fullPath
const page = this.pageList.find(item => item.path === newRoute.fullPath)
if (!this.multiPage) {
this.pageList = [this.createPage(newRoute)]
} else if (page) {
@ -261,7 +261,7 @@ export default {
return {
keyPath: route.matched[route.matched.length - 1].path,
fullPath: route.fullPath, loading: false,
path: route.path,
path: route.fullPath,
title: route.meta && route.meta.page && route.meta.page.title,
unclose: route.meta && route.meta.page && (route.meta.page.closable === false),
}
@ -271,7 +271,7 @@ export default {
* @param route 页面对应的路由
*/
setCachedKey(route) {
const page = this.pageList.find(item => item.path === route.path)
const page = this.pageList.find(item => item.path === route.fullPath)
page.unclose = route.meta && route.meta.page && (route.meta.page.closable === false)
if (!page._init_) {
const vnode = this.$refs.tabContent.$vnode
@ -301,7 +301,7 @@ export default {
routes.forEach(item => {
const cacheAble = item.meta?.page?.cacheAble ?? pCache ?? true
if (!cacheAble) {
this.excludeKeys.push(new RegExp(`${item.path.replace(/:[^/]*/g, '[^/]*')}\\d*$`))
this.excludeKeys.push(new RegExp(`${item.path.replace(/:[^/]*/g, '[^/]*')}(\\?.*)?\\d*$`))
}
if (item.children) {
this.loadCacheConfig(item.children, cacheAble)

View File

@ -17,8 +17,9 @@ const TabsPagePlugin = {
},
$setPageTitle(route, title) {
if (title) {
let path = typeof route === 'object' ? route.path : route
path = path && path.split('?')[0]
// let path = typeof route === 'object' ? route.path : route
// path = path && path.split('?')[0]
let path = typeof route === 'object' ? this.$router.resolve(route).route.fullPath : route
this.$store.commit('setting/setCustomTitle', {path, title})
}
}
@ -26,8 +27,8 @@ const TabsPagePlugin = {
computed: {
customTitle() {
const customTitles = this.$store.state.setting.customTitles
const path = this.$route.path.split('?')[0]
const custom = customTitles.find(item => item.path === path)
// const path = this.$route.path.split('?')[0]
const custom = customTitles.find(item => item.path === this.$route.fullPath)
return custom && custom.title
}
}

View File

@ -8,10 +8,12 @@ function hasPermission(authority, permissions) {
let required = '*'
if (typeof authority === 'string') {
required = authority
} else if (Array.isArray(authority)) {
required = authority
} else if (typeof authority === 'object') {
required = authority.permission
}
return required === '*' || (permissions && permissions.findIndex(item => item === required || item.id === required) !== -1)
return required === '*' || hasAnyItem(required, permissions, (r, t) => !!(r === t || r === t.id))
}
/**
@ -24,25 +26,23 @@ function hasRole(authority, roles) {
if (typeof authority === 'object') {
required = authority.role
}
return authority === '*' || hasAnyRole(required, roles)
return authority === '*' || hasAnyItem(required, roles, (r, t) => !!(r === t || r === t.id))
}
/**
* 判断是否有需要的任意一个角色
* @param required {String | Array[String]} 需要的角色可以是单个角色或者一个角色数组
* @param roles 拥有的角色
* 判断目标数组是否有所需元素
* @param {String | String[]}required 所需元素数组或单个元素
* @param {String[]|Object[]} source 目标数组
* @param {Function} filter 匹配条件
* (r: String, s: String|Object) => boolean
* @returns {boolean}
*/
function hasAnyRole(required, roles) {
function hasAnyItem(required, source, filter) {
if (!required) {
return false
} else if(Array.isArray(required)) {
return roles.findIndex(role => {
return required.findIndex(item => item === role || item === role.id) !== -1
}) !== -1
} else {
return roles.findIndex(role => role === required || role.id === required) !== -1
}
let checkedList = Array.isArray(required) ? required : [required]
return !!source.find(s => checkedList.find(r => filter(r, s)))
}
/**

View File

@ -75,6 +75,9 @@ function parseRoutes(routesConfig, routerMap) {
redirect: routeCfg.redirect || router.redirect,
meta: {...meta, authority: meta.authority || '*'}
}
if (router.beforeEnter) {
route.beforeEnter = router.beforeEnter
}
if (routeCfg.invisible || router.invisible) {
route.meta.invisible = true
}
@ -212,7 +215,7 @@ function formatAuthority(routes, pAuthorities = []) {
let authority = {}
if (!meta.authority) {
authority = defaultAuthority
}else if (typeof meta.authority === 'string') {
}else if (typeof meta.authority === 'string' || Array.isArray(meta.authority)) {
authority.permission = meta.authority
} else if (typeof meta.authority === 'object') {
authority = meta.authority

View File

@ -5422,9 +5422,9 @@ htmlparser2@^3.3.0:
readable-stream "^3.1.1"
http-cache-semantics@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
version "4.1.0"
resolved "https://registry.npm.taobao.org/http-cache-semantics/download/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
integrity sha1-SekcXL82yblLz81xwj1SSex045A=
http-deceiver@^1.2.7:
version "1.2.7"