目录结构

├── public
│   └── logo.png             # LOGO
|   └── index.html           # Vue 入口模板
├── src
│   ├── api                  # Api ajax 等
│   ├── assets               # 本地静态资源
│   ├── config               # 项目基础配置,包含路由,全局设置
│   ├── components           # 业务通用组件
│   ├── core                 # 项目引导, 全局配置初始化,依赖包引入等
│   ├── router               # Vue-Router
│   ├── store                # Vuex
│   ├── utils                # 工具库
│   ├── locales              # 国际化资源
│   ├── views                # 业务页面入口和常用模板
│   ├── App.vue              # Vue 模板入口
│   └── main.js              # Vue 入口 JS
│   └── permission.js        # 路由守卫(路由权限控制)
├── tests                    # 测试工具
├── README.md
└── package.json

路由和菜单

基本结构

路由和菜单是组织起一个应用的关键骨架,pro 中的路由为了方便管理,使用了中心化的方式,在 router.config.js 统一配置和管理。

  • 路由管理 通过约定的语法根据在router.config.js中配置路由。
  • 菜单生成 根据路由配置来生成菜单。菜单项名称,嵌套路径与路由高度耦合。
  • 面包屑 组件 PageHeader 中内置的面包屑也可由脚手架提供的配置信息自动生成。
路由

目前脚手架中所有的路由都通过 router.config.js 来统一管理,在 vue-router 的配置中我们增加了一些参数,如 hideChildrenInMenu,meta.title,meta.icon,meta.permission,来辅助生成菜单。其中:

  • hideChildrenInMenu 用于隐藏不需要在菜单中展示的子路由。用法可以查看 分步表单 的配置。
  • hidden 可以在菜单中不展示这个路由,包括子路由。效果可以查看 other 下的路由配置。
  • meta.title 和 meta.icon分别代表生成菜单项的文本和图标。
  • meta.permission 用来配置这个路由的权限,如果配置了将会验证当前用户的权限,并决定是否展示 *(默认情况下)。
  • meta.hidden 可以强制子菜单不显示在菜单上(和父级 hideChildrenInMenu 配合)
  • meta.hiddenHeaderContent 可以强制当前页面不显示 PageHeader 组件中的页面带的 面包屑和页面标题栏
路由配置项
/*** 路由配置说明:* 建议:sider menu 请不要超过三级菜单,若超过三级菜单,则应该设计为顶部主菜单 配合左侧次级菜单***/{redirect: noredirect,  //重定向name: 'router-name',   //路由名称hidden: true,          //可以在菜单中不展示这个路由,包括子路由。效果可以查看 other 下的路由配置。meta: {title: 'title',      //菜单项名称icon: 'a-icon',      //菜单项图标keepAlive: true,     //缓存页面permission:[string]   //用来配置这个路由的权限,如果配置了将会验证当前用户的权限,并决定是否展示 *(默认情况下)hiddenHeaderContent: true,   //可以强制当前页面不显示 PageHeader 组件中的页面带的 面包屑和页面标题栏}
}

具体请参考 https://pro.loacg.com/docs/router-and-nav

菜单

菜单根据 router.config.js 生成,具体逻辑在 src/store/modules/permission.js 中的 actions.GenerateRoutes 方法实现。

Ant Design Pro 的布局

在 Ant Design Pro 中,我们抽离了使用过程中的通用布局,都放在 /components/layouts 目录中,分别为:

  • BasicLayout:基础页面布局,包含了头部导航,侧边栏和通知栏:
  • UserLayout:抽离出用于登陆注册页面的通用布局
  • PageView:基础布局,包含了面包屑,和中间内容区 (slot)
  • RouterView:空布局,专门为了二级菜单内容区自定义
  • BlankLayout:空白的布局

定义全局样式

/* 定义全局样式 */
:global(.text) {font-size: 16px;
}/* 定义多个全局样式 */
:global {.footer {color: #ccc;}.sider {background: #ebebeb;}
}
//覆盖组件样式
// 使用 css 时可以用 >>> 进行样式穿透
.test-wrapper >>> .ant-select {font-size: 16px;
}// 使用 scss, less 时,可以用 /deep/ 进行样式穿透
.test-wrapper /deep/ .ant-select {font-size: 16px;
}// less CSS modules 时亦可用 :global 进行覆盖
.test-wrapper {:global {.ant-select {font-size: 16px;}}
}

与服务器交互

在 Ant Design Pro 中,一个完整的前端 UI 交互到服务端处理流程是这样的:

  1. UI 组件交互操作;
  2. 调用统一管理的 api service 请求函数;
  3. 使用封装的 request.js 发送请求;
  4. 获取服务端返回;
  5. 更新 data。

从上面的流程可以看出,为了方便管理维护,统一的请求处理都放在 @/src/api 文件夹中,并且一般按照 model 纬度进行拆分文件,如:

api/user.jspermission.jsgoods.js...

其中,@/src/utils/request.js 是基于 axios 的封装,便于统一处理 POST,GET 等请求参数,请求头,以及错误提示信息等。具体可以参看 request.js。 它封装了全局 request 拦截器、response 拦截器、统一的错误处理、baseURL 设置等。

例如在 api 中的一个请求用户信息的例子:

// api/user.js
import { axios } fromm '@/utils/request'const api = {info: '/user',list: '/users'
}// 根据用户 id 获取用户信息
export function getUser (id) {return axios({url: `${api.user}/${id}`,method: 'get'})
}// 增加用户
export function addUser (parameter) {return axios({url: api.user,method: 'post',data: parameter})
}// 更新用户 // or (id, parameter)
export function updateUser (parameter) {return axios({url: `${api.user}/${parameter.id}`, // or `${api.user}/${id}`method: 'put',data: parameter})
}// 删除用户
export function deleteUser (id) {return axios({url: `${api.user}/${id}`,method: 'delete',data: parameter})
}// 获取用户列表 parameter: { pageSize: 10, pageNo: 1 }
export function getUsers (parameter) {return axios({url: api.list,method: 'get',params: parameter})
}
<template><div><a-button @click="queryUser"></a-button><a-table :dataSource="list"></a-table></div>
</template><script>
import { getUser, getUsers } from '@/api/user'export default {data () {return {id: 0,queryParam: {pageSize: 10,pageNo: 1,username: ''},info: {},list: []}},methods: {queryUser () {const { $message } = thisgetUser(this.id).then(res => {this.info = res.data}).catch(err => {$message.error(`load user err: ${err.message}`)})},queryUsers () {getUsers(this.queryParam).then(res => {this.list = res})}}
}
</script>
*** 获取裁剪后的图片*/cropImage () {this.form.cropimg = this.$refs.cropper.getCroppedCanvas().toDataURL();},/*** 确认裁剪*/sureCrop () {this.dialogVisible = false},/*** 上传裁剪后的图片到服务器*/upCropImg () {//判断是否是新增还是编辑if (this.$route.query.id && this.$route.query.id != '') {//如果是编辑的就直接提交this.onSubmit()} else {//否则先上传裁剪图片,将64位图片转换为二进制数据流var formdata1 = new FormData();// 创建form对象formdata1.append('file', convertBase64UrlToBlob(this.form.cropimg), 'aaa.png');//this.$ajax.post(this.$api + "/upload/singleUploadImg", formdata1, { headers: { 'Content-Type': 'multipart/form-data' } }).then(response => {if (response.data.msg == "success" && response.data.code == 1) {this.form.imgUrl = response.data.data.imgUrlthis.onSubmit()} else {console.log(response)this.$message.error(response.data.msg);}}).catch(function (error) {console.log(error);});}},

引入外部模块

$ npm install '组件名字' --save
使用
//全局引入
import Vue from 'vue'
import VueQuillEditor from 'vue-quill-editor'// require styles
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'Vue.use(VueQuillEditor, /* { default global options } */)
<template><div><quill-editor ref="myTextEditor"v-model="content":config="editorOption"@blur="onEditorBlur($event)"@focus="onEditorFocus($event)"@ready="onEditorReady($event)"></quill-editor></div>
</template><script>
//按需加载
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
import { quillEditor } from 'vue-quill-editor'export default {components: {quillEditor},data () {return {content: '<h2>I am Example</h2>',editorOption: {// something config}}},// 如果需要手动控制数据同步,父组件需要显式地处理changed事件methods: {onEditorBlur(editor) {console.log('editor blur!', editor)},onEditorFocus(editor) {console.log('editor focus!', editor)},onEditorReady(editor) {console.log('editor ready!', editor)},onEditorChange({ editor, html, text }) {// console.log('editor change!', editor, html, text)this.content = html}},// 如果你需要得到当前的editor对象来做一些事情,你可以像下面这样定义一个方法属性来获取当前的editor对象,实际上这里的$refs对应的是当前组件内所有关联了ref属性的组件元素对象computed: {editor() {return this.$refs.myTextEditor.quillEditor}},mounted() {// you can use current editor object to do something(editor methods)console.log('this is my editor', this.editor)// this.editor to do something...}
}
</script>

引入业务图标

参考:https://pro.loacg.com/docs/biz-icon、

国际化

参考:https://pro.loacg.com/docs/i18n

权限管理

参考:https://pro.loacg.com/docs/authority-management

自定义使用规则

  • 修改网站icon的文件地址在 public文件夹中把logo.png换成自定义的,也可在public/index.html自定义
<!DOCTYPE html>
<html lang="zh-cmn-Hans"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1.0"><link rel="icon" href="<%= BASE_URL %>logo.png"><title>共享云店</title><style>#loading-mask{position:fixed;left:0;top:0;height:100%;width:100%;background:#fff;user-select:none;z-index:9999;overflow:hidden}.loading-wrapper{position:absolute;top:50%;left:50%;transform:translate(-50%,-100%)}.loading-dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:64px;width:64px;height:64px;box-sizing:border-box}.loading-dot i{width:22px;height:22px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.loading-dot i:nth-child(1){top:0;left:0}.loading-dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.loading-dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.loading-dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}</style></head><body><noscript><strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"><div id="loading-mask"><div class="loading-wrapper"><span class="loading-dot loading-dot-spin"><i></i><i></i><i></i><i></i></span></div></div></div><!-- built files will be auto injected --></body>
</html>

-更换logo在src\components\tools\Logo.vue中更换

<template><div class="logo"><router-link :to="{name:'dashboard'}"><LogoSvg alt="logo" />   //这是logo<h1 v-if="showTitle">{{ title }}</h1>   //这是网站标题</router-link></div>
</template><script>
import LogoSvg from '@/assets/logo.svg?inline';
export default {name: 'Logo',components: {LogoSvg},props: {title: {type: String,default: 'Admin For Ok',    //网站默认标题required: false},showTitle: {                  //是否显示网站标题,默认不显示type: Boolean,default: true,required: false}}
}
</script>

pro权限管理和路由控制思路分析(粗略分析)

  • 主要是通过三个文件实现,src\mock\services\user.js文件通过登陆的角色获取对应的鉴权规则,具体可查看该文件下的源码
  • src\config\router.config.js文件为路由配置文件,可增加路由取消路由等,变量asyncRouterMap为主要路由数组集合,可配置鉴权权限,变量constantRouterMap为基础路由,不参与鉴权
  • src\permission.js文件为动态配置路由的主要逻辑,代码如下
import Vue from 'vue'
import router from './router'
import store from './store'import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import notification from 'ant-design-vue/es/notification'
import { setDocumentTitle, domTitle } from '@/utils/domUtil'
import { ACCESS_TOKEN } from '@/store/mutation-types'NProgress.configure({ showSpinner: false }) // NProgress Configurationconst whiteList = ['login', 'register', 'registerResult'] // no redirect whitelist配置白名单router.beforeEach((to, from, next) => {NProgress.start() // start progress bar//生成动态网站标题to.meta && (typeof to.meta.title !== 'undefined' && setDocumentTitle(`${to.meta.title} - ${domTitle}`))if (Vue.ls.get(ACCESS_TOKEN)) {/* has token 如果有token并且是从登录页来的就直接跳到工作空间*/if (to.path === '/user/login') {next({ path: '/dashboard/workplace' })NProgress.done()} else {//否则检测是不是没有检测到规则,请求获取用户信息,获取用户权限if (store.getters.roles.length === 0) {//请求mock模拟数据获取用户权限store.dispatch('GetInfo').then(res => {const roles = res.result && res.result.role//调用src\store\modules\permission.js里面的GenerateRoutes方法,处理数据store.dispatch('GenerateRoutes', { roles }).then(() => {// 根据roles权限生成可访问的路由表// 动态添加可访问路由表router.addRoutes(store.getters.addRouters)const redirect = decodeURIComponent(from.query.redirect || to.path)if (to.path === redirect) {// hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history recordnext({ ...to, replace: true })} else {// 跳转到目的路由next({ path: redirect })}})}).catch(() => {notification.error({message: '错误',description: '请求用户信息失败,请重试'})store.dispatch('Logout').then(() => {next({ path: '/user/login', query: { redirect: to.fullPath } })})})} else {next()}}} else {if (whiteList.includes(to.name)) {// 在免登录白名单,直接进入next()} else {next({ path: '/user/login', query: { redirect: to.fullPath } })NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it}}
})router.afterEach(() => {NProgress.done() // finish progress bar
})
  • src\store\modules\permission.js文件为路由数据的详细处理逻辑,配合src\permission.js文件使用,代码如下:
import { asyncRouterMap, constantRouterMap } from '@/config/router.config'/*** 过滤账户是否拥有某一个权限,并将菜单从加载列表移除** @param permission* @param route* @returns {boolean}*/
function hasPermission (permission, route) {if (route.meta && route.meta.permission) {let flag = falsefor (let i = 0, len = permission.length; i < len; i++) {flag = route.meta.permission.includes(permission[i])if (flag) {return true}}return false}return true
}/*** 单账户多角色时,使用该方法可过滤角色不存在的菜单** @param roles* @param route* @returns {*}*/
// eslint-disable-next-line
function hasRole(roles, route) {if (route.meta && route.meta.roles) {return route.meta.roles.includes(roles.id)} else {return true}
}function filterAsyncRouter (routerMap, roles) {const accessedRouters = routerMap.filter(route => {if (hasPermission(roles.permissionList, route)) {if (route.children && route.children.length) {route.children = filterAsyncRouter(route.children, roles)}return true}return false})return accessedRouters
}const permission = {state: {routers: constantRouterMap,addRouters: []},mutations: {SET_ROUTERS: (state, routers) => {state.addRouters = routersstate.routers = constantRouterMap.concat(routers)}},actions: {GenerateRoutes ({ commit }, data) {return new Promise(resolve => {const { roles } = dataconst accessedRouters = filterAsyncRouter(asyncRouterMap, roles)commit('SET_ROUTERS', accessedRouters)resolve()})}}
}export default permission

跨域请求设置

在vue.config.js文件中修改

 // 配置跨域devServer: {// development server port 8000// port: 8000,proxy: {'/apis': {// target: 'https://mock.ihx.me/mock/5baf3052f7da7e07e04a5116/antd-pro',target: 'http://192.168.1.73:8092/okcloud/',// target: 'http://39.107.78.120:8083/okcloud/',ws: false,changeOrigin: true,  //是否允许跨域pathRewrite: {'^/apis': ''}}}

axios拦截器

在文件src\utils\request.js中设置

// request interceptor
service.interceptors.request.use(config => {const token = Vue.ls.get(ACCESS_TOKEN)if (token) {config.headers['okcloud_token'] = token // 让每个请求携带自定义 token 请根据实际情况自行修改}return config
}, err)// response interceptor
service.interceptors.response.use((response) => {if (response.data.code === 10000) {notification.warning({message: '提示',description: response.data.message})} else {return response.data}
}, err)

自定义角色的菜单权限

  • 在src\main.js文件中注释掉"// import ‘./permission’ // permission control 权限控制"
  • 自定义路由权限文件src\routeGuard.js,代码如下
import Vue from 'vue'
import router from './router'
// import store from './store'import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { setDocumentTitle, domTitle } from '@/utils/domUtil'
import { ACCESS_TOKEN } from '@/store/mutation-types'NProgress.configure({ showSpinner: false }) // NProgress Configurationrouter.beforeEach((to, from, next) => {NProgress.start() // start progress barto.meta && (typeof to.meta.title !== 'undefined' && setDocumentTitle(`${to.meta.title} - ${domTitle}`))if (to.path === '/user/login' && Vue.ls.get(ACCESS_TOKEN)) {next({ path: '/dashboard/workplace' })NProgress.done()} else if (to.path !== '/user/login' && !Vue.ls.get(ACCESS_TOKEN)) {next({ path: '/user/login' })NProgress.done()} else {next()NProgress.done()}
})router.afterEach(() => {NProgress.done() // finish progress bar
})
  • 在main.js中引入import ‘./routeGuard’
  • 对src\components\Menu\menu.js文件进行自定义菜单改造
  • 在src\store\modules\app.js文件中store加入menu,在actions中进行获取菜单的异步操作,获取菜单信息,然后进行全局变量动态获取
  • 在src\layouts\BasicLayout.vue中进行全局变量的引用
    ...mapState({// 动态主路由menus: state => state.app.menu}),

动态方法的引用

...mapActions(['setSidebar', 'setMenu']),

调用获取动态方法

this.setMenu()

Ant Design Pro Vue使用心得相关推荐

  1. ant design pro vue左侧菜单自定义图标

    做ant design pro vue项目的时候突然想修改左侧菜单的图标,在修改途中发现源码中提供的方法方法居然不生效(如下所示) 在src/assets/icons文件夹中下载svg图标 在src/ ...

  2. ant design pro vue 动态路由 流程详解

    ant design pro vue 动态路由 流程详解 前言 流程图 流程1 src/permission.js 流程2 src/store/modules/user.js 流程3 src/perm ...

  3. Ant Design Pro Vue 登录后userInfo存在vuex问题

    问题描述: 作者发现使用Ant Design Pro中存在一个问题,页面刷新后vuex数据丢失,导致UserInfo找不到影响页面的问题,故而写此文,望能助有需之人. 原因分析: 提示:Vuex的机制 ...

  4. 使用Ant Design 和Vue,React中后台开发套餐

    2019独角兽企业重金招聘Python工程师标准>>> 前言 目前Ant Design 提供 React 和 Vue 两种整合开发框架,开箱即用的中台前端/设计解决方案,可适合中小公 ...

  5. 同志们,免费版的Ant Design Pro Vue3 来啦

    Vue3也出了一段时间了,于是照着ant pro vue搭个免费的vue3版 希望大家喜欢 基于 Vite2 Vue3 Ant-Design-of-Vue2 TS实现的 Ant Design Pro ...

  6. ant design pro v5 总结

    Ant Design Pro v5 使用心得 相比V4,V5到底强在哪? 如何更改 title图标和加载时的页面? defaultSettings中菜单的几种模式及主题等配置 国际化 request ...

  7. 搭建Vue版Ant Design Pro后台管理系统

    搭建Vue版Ant Design Pro后台管理系统 此文章通过基于Vue实现的Ant DesignPro脚手架快速构建一个后台管理系统的前端 相关文档链接 1.[Ant Design Pro of ...

  8. Ant Design Pro of Vue方案一权限路由配置(一步步完成,包括所有后端数据库代码,萌新向)

    Ant Design Pro of Vue方案一权限路由配置(一步步完成,包括所有后端数据库代码,萌新向) 参考了许多csdn的文章,感谢大家为社区做的贡献,我也来汇总一波,谢谢

  9. Ant Design Pro of Vue——蚂蚁金服中后台系统框架搭建

    Ant Design Pro of Vue--蚂蚁金服中后台系统框架搭建 Ant Design Pro of Vue下载安装启动步骤: 一.Ant Design Pro of Vue 的介绍 Ant ...

最新文章

  1. HTML5新特性总结
  2. 二叉搜索树的简明实现(ES5 ES6)
  3. Windows 操作系统的安全设置
  4. html运行代码出现问号乱码_Java 0基础入门(初识Html)
  5. JavaScript基本语法(续)
  6. 职场心理:12个建议或许能改变你的一生(图)
  7. 从Excel文件中找出在TXT文件中没有出现的 行之_代码片段
  8. ASP如何获取客户端真实IP地址
  9. JavaScript数据类型之数据类型之间的转换(6)
  10. linux top 网络,Linux Top 详解
  11. DXperience-7.1.1 Sources 源代下载
  12. 计算机基础考试题及答案多选,2016年计算机一级考试PS及基础多选模拟试题及答案...
  13. NLP学习(七)使用stanford实现句法分析-Python3实现
  14. vue 使用qrcode生成二维码功能
  15. linux看门狗使用
  16. 一文读懂串口及各种电平信号含义
  17. Mac上Chrome安装 Tampermonkey插件的时候提示CRX_HEADER_INVALID
  18. html中如何把一个div放到页面底部,html – 如何将DIV锚定到页面的底部?
  19. 2008春天在玉渊潭看到传说中的榆钱树
  20. 当你改变不了环境的时候试着改变自己

热门文章

  1. 基于C++实现的股票大数据的统计分析与可视化
  2. jquery 执行完动画后再执行别的操作
  3. Java高级--->多线程的学习
  4. 深度学习-----数据预处理
  5. Ubuntu22.04TLS插入3.5mm耳机没有声音
  6. 抖音、今日头条、西瓜视频将打包上市
  7. Xftp的安装与使用
  8. aircrack-ng 添加Mac OS X 支持 airodump-ng和aireplay-ng都可以用了
  9. 显卡、GPU和CUDA
  10. 2021年广东专插本计算机专业学校,2021年广东省专插本学校名单和专业,广东专插本有哪些学校和那些专业...