精华内容
下载资源
问答
  • ThinkPHP Ant Design Pro Vue ThinkPHP 6.0与Ant Design Pro Vue基础权限系统基于Ant Design Vue Pro 预览地址: 开发文档: : Ant Design Vue Pro文档: : 初步: : 服务器提供: 预览账号 超级管理员admin, ...
  • beego Ant Design Pro Vue RBAC beego与Ant Design Pro Vue基础权限系统 初步: ://beego.me ORM:gorm Ant Design Pro Vue文档: : 预览: 账号 超级管理员admin, 1234 普通管理员test, 1234 前端部署 安装 cd...
  • Ant Design Pro Vue使用心得 目录结构 ├── public │ └── logo.png # LOGO | └── index.html # Vue 入口模板 ├── src │ ├── api # Api ajax 等 │ ├── assets # 本地静态资源 │ ...

    Ant Design Pro Vue使用心得

    目录结构

     

    ├── 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.js
      permission.js
      goods.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 } = this
                getUser(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.imgUrl
                  this.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 Configuration
    
    const 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 record
                    next({ ...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 = false
        for (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 = routers
          state.routers = constantRouterMap.concat(routers)
        }
      },
      actions: {
        GenerateRoutes ({ commit }, data) {
          return new Promise(resolve => {
            const { roles } = data
            const 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 Configuration
    
    router.beforeEach((to, from, next) => {
      NProgress.start() // start progress bar
      to.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不完全开发手册

    千次阅读 2021-01-21 10:08:03
    Ant Design Pro Vue不完全开发手册前言准备工作:1、安装npm2、可选安装3、安装 vue-cli 工具:4、参考文档基础开发学习:1、git项目拉取2、安装项目3、启动项目4、文件解析vue.config.jsrouter.config.jsviews(1)...

    前言

    此文档是我为公司写的前端框架开发手册(应该没人看,哈哈),这里删除公司敏感信息后,作为自己的总结文档记录一下。

    准备工作:

    1、安装npm

    npm更换阿里镜像

    npm config set registry https://registry.npm.taobao.org/ 
    

    查看是否成功

    npm config get registry
    

    2、可选安装

    cnpm(同npm,淘宝镜像速度快)、yarn(https://blog.csdn.net/csdn_yudong/article/details/82015885

    3、安装 vue-cli 工具:

    安装命令npm install -g @vue/cli

    4、参考文档

    ant design vue :http://vue.ant-design.cn/docs/vue/introduce-cn/
    ant design vue pro :https://pro.loacg.com/docs/getting-started

    基础开发学习:

    1、git项目拉取

    2、安装项目

    cnpm install 或者 npm install

    3、启动项目

    npm run serve
    或者
    yarn serve

    注:相关命令存放于package.json

    启动成功信息
    在这里插入图片描述

    4、文件解析

    vue.config.js

    本地开发需要在

    vue.config.js

    配置代理对象
    在这里插入图片描述

    router.config.js

    路由用于菜单配置
    在这里插入图片描述

    注意:
    (1)admin账户直接读取的是router.config.js路由配置,未和数据库做交互,为全权限。
    (2)其他账户经过admin-service服务读取表sys_permission获取权限,其权限为部分权限,故添加新页面时必须要向该表添加对应数据,sys_permission.title = router.config.js.title
    sys_permission.name = router.config.js.permission,其他无要求。
    (3)相关权限控制代码位于
    在这里插入图片描述

    views

    在这里插入图片描述

    视图——》业务逻辑开发位置
    业务开发推荐目录结构(add和update其实可以复用,为了降低开发难度,才做此拆分)
    在这里插入图片描述

    (1)api.js

    ——》对外接口,主要用于发送请求
    使用组件为 axios

    (1)get请求写法

    export function getAirportInfo (parameter) {
      return axios({
        url: '...',
        method: 'get',
        params: parameter // 此处为params
      })
    }
    

    (2)post请求写法

    export function page (parameter) {
      return axios({
        url: '...',
        method: 'post',
        data: parameter // 此处为data
      })
    }
    

    (2)vue开发浅谈

    在这里插入图片描述

    1、引入组件(按需引用)

    import { STable } from '@/components'
    

    2、注册组件
    在这里插入图片描述

    3、数据定义

    data () {
      return {...}
    }
    

    4、生命周期定义

    mounted () {...}
    

    拓展学习https://cn.vuejs.org/v2/guide/instance.html#生命周期图示
    5、方法定义

    methods: {...}
    

    6、监听
    多用于更新页面数据回显

    watch: {...}
    

    7、mock
    ——》模拟数据存放位置,脱离后端进行调试

    问题与解惑

    1、如何把父组件的值传到子组件

    父组件中
    在这里插入图片描述

    子组件中
    在这里插入图片描述

    父组件直接传递即可
    在这里插入图片描述

    2、父组件调如何用子组件方法

    在这里插入图片描述

    如何取别名:ref=“model”

    【this.$refs.{别名}】

    这个的意思是获取对应vue的对象
    this.$refs.{别名}.{方法}

    3、子组件如何调用父组件方法

    调用$emit方法,其中“o”为父组件中,子组件的方法

    this.$emit(o)

    在这里插入图片描述

    4、列表属性

    在这里插入图片描述

    5、导入了组件,却发现使用不了

    为了前端的渲染速度,我们采用的是按需引用
    在这里插入图片描述

    框架没有引用的,我们需要手动引用组件

    (1)方式一:引用组件相关所有文件

    // 折叠框,直接使用会报错,在此手动引用
    import ACollapse from 'ant-design-vue/es/collapse/Collapse'
    import ACollapsePanel from 'ant-design-vue/es/collapse/CollapsePanel'
    import 'ant-design-vue/lib/collapse/style'
    

    (2)方式二(推荐)

    import { Button } from 'ant-design-vue';
    Vue.use(Button);
    

    6、如何进行权限控制(按钮级)

    <span v-if="$auth('luggageOrder.luggageManualRefund')"></span>
    

    注意:luggageOrder.luggageManualRefund权限表的关联性(别忘了router.config.js路由哟)

    拓展学习
    https://pro.antdv.com/docs/authority-management

    7、更新页面如何重置数据

    在父页面Lsit,将data设置为空
    在这里插入图片描述

    8、新增页面如何重置数据

    在新增页面,调用关闭方法时,重置form表单

    this.$nextTick(() => {
      this.form.setFieldsValue({ ...formdata })
    })
    

    9、数据类型怎么定义

    对象 - queryParam : {}
    数组 - queryParam : []
    字符串 - queryParam : ''
    

    10、下拉框有数据,但是为空白,不显示

    多半是下拉框数据类型不匹配,请检查number还是字符串

    11、时间框的对象用什么

    import moment from 'moment'
    

    拓展学习
    http://momentjs.cn/

    12、列表字段实现toptip效果

    ellipsis
    

    在这里插入图片描述

    13、使用watch监听时,第一次加载页面会报错。(20210220新增)

    增加数据校验

            if (!n || !n.id) {
              return
            }
    

    结语

    为什么这个手册叫不完全开发手册呢?因为我也是一个前端小白,从来没有系统学习过vue,都是从0开始,撸起袖子就干了。以上内容都是从经验出发,希望大家少走弯路,快速开发,所以描述啥的可能会不准确,如果有错误,同学们可以直接改正,或者联系【黎晓东】。这里引用晓东大神的一句话:“虽然我们是后端开发,但是如果连一个后台管理系统的前端页面都写下不下来,真的说不过去”。

    附录

    ant design vue :http://vue.ant-design.cn/docs/vue/introduce-cn/
    ant design vue pro :https://pro.loacg.com/docs/getting-started
    vue:https://cn.vuejs.org/
    moment:http://momentjs.cn/
    axios:http://www.axios-js.com/

    展开全文
  • Ant Design Pro Vue 填坑记

    2020-07-02 09:33:26
    Ant Design Pro Vue 填坑记Issus Issus We’re sorry but blog doesn’t work properly without JavaScript enable Fixed issue 了很多问答,其实问题并不是出在router文件夹的index.js文件中的mode 其实是在于你的...

    Ant Design Pro Vue 填坑记

    Issus

    1. We’re sorry but blog doesn’t work properly without JavaScript enable
      Fixed issue
      了很多问答,其实问题并不是出在router文件夹的index.js文件中的mode
      其实是在于你的组件并没有正确的引入进来。
      文件路径 src\router\generator-routers.js中的export const generator = (routerMap, parent) 方法
      component: (constantRouterComponents[item.component || item.key]) || (() => import(@/views/${cc})),
      需要debug这些问题
      看看到底导入了没有。

    未完待续

    展开全文
  • ThinkPHP 6.0与Ant Design Pro Vue基础前后分离权限系统 预览地址: 前端 蚂蚁设计Vue Pro 初步 没安装Composer?请在最新版本链接下找到TAnt_Full.zip下载 安装 拉取代码 git clone ...
  • 蚂蚁设计专业Vue模板 根据改造后的开箱即用的后台管理系统模板,优化了很多内容,更轻量,更简洁,可以在此模板上快速,便捷地去建造自己的后台项目。预览:| 优化:项目结构和文件夹命名,项目结构更加合理 优化:...
  • Ant Design Pro Vue使用心得

    万次阅读 多人点赞 2019-06-22 13:12:21
    目录结构 ├── public │ └── logo.png # LOGO | └── index.html # Vue 入口模板 ├── src │ ├── api # Api ajax 等 │ ├── assets # 本地静态资源 │ ├── config ...

    目录结构

    ├── 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.js
      permission.js
      goods.js
      ...
    

    其中,@/src/utils/request.js 是基于 axios 的封装,便于统一处理 POSTGET 等请求参数,请求头,以及错误提示信息等。具体可以参看 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 } = this
                getUser(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.imgUrl
                  this.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 Configuration
    
    const 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 record
                    next({ ...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 = false
        for (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 = routers
          state.routers = constantRouterMap.concat(routers)
        }
      },
      actions: {
        GenerateRoutes ({ commit }, data) {
          return new Promise(resolve => {
            const { roles } = data
            const 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 Configuration
    
    router.beforeEach((to, from, next) => {
      NProgress.start() // start progress bar
      to.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是蚂蚁金服体验技术部打磨出一个服务于企业级产品的设计体系,通过模块化解决方案降低生产成本。...ant pro vue 是一套基于Ant Design Vue的中后台管理控制台的脚手架。 直接从GitHub仓库中直接安装最...
  • ant design pro vue 踩坑

    2020-06-04 22:08:45
    在跟着官方文档一步步做的时候发现了一个问题,当使用babel-plugin-import按需... 遂照着网上的解决方案来做,但是发现很多方法都没用比如添加vue.config.js之类的,最终发现是less版本的问题,将less引入之后,它的版
  • 目录结构├── public│ └── logo.png # LOGO| └── index.html # Vue 入口模板├── src│ ├── api # Api ajax 等│ ├── assets # 本地静态资源│ ├── config ...
  • 关于Ant-Design-Pro-Vue项目打包后无法预览问题 打包后的dist文件夹 打开index.html 一直处于加载状态 由于官方没有明确说明打包后的配置和部署,对于初学者来说以为打开index.html就可以预览了 预览打包后的项目...
  • 我是vue-antd-pro 3.0版本,设置全局国际化包括日期控件,话不多说直接上手操作:打开src/App.vue,修改scriptimport { domTitle, setDocumentTitle } from '@/utils/domUtil'import { i18nRender } from '@/locales...
  • Ant Design Pro Vue 全局国际化-设置中文

    千次阅读 2020-09-25 09:54:45
    我是vue-antd-pro 3.0版本,设置全局国际化包括日期控件,话不多说直接上手操作: 打开src/App.vue,修改script <script> import { domTitle, setDocumentTitle } from '@/utils/domUtil' import { i18...
  • ant design pro vue 设置访问开发服务器api

    千次阅读 热门讨论 2019-06-11 23:15:11
    1.首先需要将src/mock下对应的接口注释...2.在 vue.config.js配置中增加如下内容 devServer: { // development server port 8888 port: 8000, proxy: { '/api': { // 匹配模式,所有api开始的连接都匹配到下面...
  • const auth = Vue.directive('auth', { inserted: function (el, binding) { if (!Vue.prototype.$_has(binding.value)) { el.parentNode.removeChild(el) // el.setAttribute('disabled', 'disabled') // .
  • (我这里就是上线炸了,滑稽) Vue模块: 创建一个组件 {{$t('Map.MapInputDesc')}}: 引用模块 唤起地图 import addressSelection from '@/views/account/settings/page/AddressSelection' export default { name:...
  • 问题:无法使用tree组件 提示 Unknown custom element:<a-tree> - did you register the component correctly? For recursive components, make sure to provide the “name” option. 原因 按需加载默认未...
  • 问题1:Viser在响应式布局中,会导致图表超出的问题,或者跟随导航栏伸缩导致图表过窄或过长。 可能出现的原因!1.由于图表是Canvas画布,在他加载时只确定栅格布局的宽度,当导航栏再次伸缩,就出超出,溢出。...
  • 基于 Ant Design of Vue 实现的 Ant Design Pro
  • ref="ruleForm":model="formDataAll":rules="rules":label-col="labelCol":wrapper-col="wrapperCol">v-model="formDataAll.name"@blur="()=>{$refs.name.onFieldBlur();}"/>上海北京v-model="formDataAll....
  • 基于 Ant Design of Vue 实现的 Ant Design Pro 源码 安装依赖 npm install 开发模式运行 npm run serve 编译项目 npm run build

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 437
精华内容 174
热门标签
关键字:

antdesignprovue

vue 订阅