Vue开发实战


1.第一个Vue程序

  • 编写一个index.html页面如下

    
            
        
    
    
        
        
        
{{msg}}
  • 在浏览器运行index.html,在输入框中输入内容,点击“添加”按钮,把输入的内容添加到下面的

  • 标签中,如下所示

  • 程序中的Vue.component的缺点

    • 全局定义:强制要求每个component中命名不得重复

    • 字符串模板:缺乏语法高亮,在HTML有多行的时候,需要用到丑陋的\

    • 不支持CSS:意味着当HTML和JavaScript组件化时,CSS明显被遗漏

    • 没有构建步骤:限制只能使用HTML和ES5 JavaScript,而不是使用预处理,如Pug(formerly Jade)和Babel

2.初识单文件组件

  • 前期准备

    • 以管理员身份运行cmd,进入到命令行界面,

    • 全局安装脚手架,执行npm isntall -g @vue/cli,回车后报错如下

      原因:已经安装过了
      解决方法:取消安装或者覆盖原来的安装,我这里选择覆盖原来的,执行npm isntall -g @vue/cli --force命令即可,安装成功后如下所示

    • 进入到你想要存放的目录中,我这里把项目放到D:盘上,执行D:,回车,然后执行vue create my-app(说明my-app是项目名称,看自己需要起名字),回车后会让你选择语法规范以及Vue版本,我这里选择默认配置,其中Vue的版本为2

    • 执行npm run serve,运行项目,运行项目成功如下图

    • 访问http://localhost:8080/,得到如下页面

  • 修改index.html的代码

    • 使用VSCode打开my-app项目,第一次打开会让你安装Vetur插件,安装后代码会高亮显示,这里我已经安装上,如下所示

    • 打开App.vue页面的代码如下所示

      
    
      
    
      
    
    • 将index.html中

      之间的代码替换到App.vue中对应的

    • 发现组件报错,是因为还没有添加这个组件,在components目录下添加一个组件TodoItem.vue,并把index.html页面中相应的代码放到这个组件中

      
    
      
      
    
    • App.vue中引入TodoItem.vue组件,并在components中注册
      import TodoItem from './components/TodoItem.vue'
      export default {
        components: {
           TodoItem
        }
      }
    
    • index.html中data和methods的代码放到App.vue页面中export default{}
      data(){
        return {
            msg:'Hello Vue~',
            info:'',
            list:[]
        }
      },
      methods:{
          addData(){
              console.log(this.info)
              this.list.push(this.info)
              this.info=''
          }
      }
    
    • 在VSCode中的终端处执行npm run serve命令,回车,得到下图所示表示运行项目成功

    • 在浏览器访问http://localhost:8080/,发现报错,定位问题。发现此时组件爆红,如下所示,原因是少了key这个属性,加上去即可

      
    
    • 再次访问http://localhost:8080/,在输入框中输入内容,点击“添加”按钮,内容成功添加到

    • 标签中如下所示

    • 在上一个步骤中发现运行后的页面原来的不太一样,这是因为使用了App.vue页面的全局样式,把这些样式去掉即可

    • 完整的App.vueTodoItem.vue页面的代码如下

      • App.vue
        
      
        
      
        
      
      • TodoItem.vue
        
      
        
        
      
  • 使用插槽改进的代码

    • TodoItem.vue页面中

    • {{zixin}}
    • 修改为

    • App.vue页面中标签中添加一个标签来装

    • 标签中的内容

      
          {{item}}
      
    
    • 重新运行,发现效果跟之前的一样
  • 标签前面添加checkbox,并在选中时
  • 的内容显示红色,否则显示蓝色

    • 修改TodoItem.vue页面代码
      
    
      
      
    
    • 修改App.vue页面代码如下:
      
    
      
    
      
    
    • 重新访问http://localhost:8080/,可以达到效果

3. Vuex

3.1Vuex的核心概念和底层原理

  • 核心概念

    • State-----this.$store.state 取值

    • Getter----this.$store.getters.xxx 取值

    • Mutation----this.$store.commit("xxx") 赋值

    • Action----this.$store.dispatch("xxx") 赋值

    • Module

  • 底层原理

    • State:提供一个响应式数据

    • Getter:借助Vue的计算属性computed来实现缓存

    • Mutation:更改state方法

    • Action:触发mutation方法

    • Module:Vue.set动态添加state到响应式数据中

3.2Vuex实战

  • Vuex实战案例一

    • 创建一个新的项目vuex-demo,并用VSCode打开

    • 安装vuex依赖

      npm install vuex
    
    • main.js中引入vuex,然后创建一个store,并把store注册到Vue实例中,main.js的代码如下
      import Vue from 'vue'
      import App from './App.vue'
      import Vuex from 'vuex'//引入vuex
    
      Vue.use(Vuex)
      Vue.config.productionTip = false
    
      const store = new Vuex.Store({
        state:{
          count:0
        }
      })
    
    
      new Vue({
        store,//注册vuex
        render: h => h(App),
      }).$mount('#app')
    
    • App.vue中通过计算属性获取到store.state中的count,然后显示在页面中,代码如下
      
    
      
    
      
    
    • 在终端执行npm run serve,然后访问http://localhost:8080/,页面没东西显示,打开浏览器控制台,发现报错如下

    原因:vuex版本过高导致报错
    解决方法:使用管理员身份进入到cmd终端,执行npm uninstall vuex卸载原来的vuex,再输入npm install vuex@3安装低版本vuex,再重新运行

    • 在VSCode终端重新运行,发现报下面的错误

      原因:当前项目没有vuex这个依赖
      解决方法:使用管理员身份进入到cmd终端,切换到项目目录下,输入npm install vuex@3安装低版本vuex,安装成功后,问题得以解决

    • 再重新运行,访问http://localhost:8080/,可以看到页面中显示0,与预期一样

  • 对Vuex实战一进行改进,使用mutations的方式实现count++

    • App.vue中添加一个按钮,并添加一个点击事件,在点击事件中commit一个事件
      
    
    • main.js中的store中添加下面代码
      mutations:{
        increment(state){//更改state中的count的值
          state.count++
        }
      },
    
    • 重新运行,访问http://localhost:8080/,点击count++按钮,可以使count的数量增加,下面是点击两次count++后的结果

    • count++点击事件中commit一个事件的时候添加一个参数2

      
    
    • main.js中的store中的mutations的increment方法做出修改
      mutations:{
      increment(state, n){//更改state中的count的值
        state.count += n
      }
    },
    
    • 重新运行,访问http://localhost:8080/,点击count++按钮,可以使count的数量在原本数量的基础上加2,下面是点击两次count++后的结果
  • 对Vuex实战一进行改进,使用actions的方式实现count+3

    • App.vue中添加一个按钮,并添加一个点击事件,在点击事件中commit一个事件,同时传一个参数进去
      
    
    • main.js中的store中添加下面代码
      actions:{
        increment3({state}, m){
          state.count += m
        }
      },
    
    • 重新运行,访问http://localhost:8080/,点击count+3按钮,可以使count的数量在原本数量的基础上加3,下面是点击两次count+3后的结果
  • 对Vuex实战一进行改进,使用getters的方式实现count*2

    • main.js中的store中添加下面代码
      getters:{
        doubleCount(state){
          return state.count*2
        }
      }
    
    • App.vue中添加下面代码
      {{$store.getters.doubleCount}}
    

4.Vue Router

4.1Vue Router作用与使用方式

  • Vue Router的作用

    • 监听URL的变化,并在变化前后执行相应的逻辑

    • 不同的URL对应不同的组件

    • 提供多种方式改变URL的API(URL的改变不能导致浏览器刷新)

  • Vue Router使用方式

    • 提供一个路由配置表,不同的URL对应不同组件的配置

    • 初始化路由实例new VueRouter()

    • 挂在到Vue实例上

    • 提供一个路由占位,用来挂在URL匹配到的组件

4.2Vue Router实战

  • 创建一个新的项目router-demo
  vue create router-demo
  • 安装vue-router依赖
  npm install vue-router
  • 在src目录下创建一个routes.js文件,作为一个路由配置表
  import RouterDemo from './components/RouterDemo'
  import RouterChildrenDemo from './components/RouterChildrenDemo'

  const routes = [
    { path: '/foo', component: RouterDemo, name: '1'},
    { path: '/bar', component: RouterDemo, name: '2'},

    //当/user/:id匹配成功
    //RouterDemo会被渲染在RouterrrDemo的中
    {
      path: '/user/:id',
      component: RouterDemo,
      name: '3',
      props:true,
      children:[
        {
          //当/user/:id/profile匹配成功
          //RouterChildrenDemo回避渲染在RouterDemo的中
          path: 'profile',
          component: RouterChildrenDemo,
          name: '3-1'
        },
        {
          //当/user/:id/posts匹配成功
          //RouterChildrenDemo回避渲染在RouterDemo的中
          path: 'posts',
          component: RouterChildrenDemo,
        }
      ]
    },
    {path: '/a', redirect: '/bar'},
    {path: '*', component:RouterDemo, name: '404' }

  ]  

  export default routes
  • main.js文件中引入VueRouter,初始化路由实例new VueRouter(),并把router挂载到Vue实例中
    import Vue from 'vue'
    import App from './App.vue'
    import VueRouter from 'vue-router'//引入vue-router
    import routes from './routes.js'


    //在Vue中使用VueRouter
    Vue.use(VueRouter)
    Vue.config.productionTip = false

    //初始化路由实例
    const router = new VueRouter({
      routes
    }) 

    new Vue({
      router,//将router注册到Vue实例中
      render: h => h(App),
    }).$mount('#app')
  • App.vue中提供一个路由占位,用来挂在URL匹配到的组件
  

  

  
  • RouterDemo.vue组件代码
  
  
  
  • RouterChildrenDemo.vue组件代码
  
  
  
  • 运行项目,访问http://localhost:8080/,发现页面空白,打开浏览器控制台,发现如下报错

原因:vue-router版本过高导致报错

解决方法:安装低一点的版本vue-router3.2.0即可,使用管理员身份进入到cmd终端,切换到项目目录下,输入npm install vue-router3.2.0安装低版本vue-router,安装成功后,问题得以解决

  • 重新运行,访问http://localhost:8080/,可以看到页面的相关内容以及点击链接时底部内容以及路径的变化

4.3路由类型

  • Hash模式:丑,无法使用描点定位

  • History模式:需要后端配合,IE9不兼容(可使用强制刷新处理)

5.Ant Design Pro

5.1使用Cli构建项目

  • 选用自定义的方式选择配置,整个项目的配置如下图所示

  • 等待构建项目完成,使用VSCode打开项目

  • 删除HomeView.vueApp.vue中不需要的内容,删除后的代码如下所示

    • HomeView.vue
          
      
          
      
    • App.vue
        
      
        
      
  • 在终端执行npm run serve回车,发现报如下错误

    原因:与创建项目eslint设置有问题
    解决方法:把原来删除后的代码空格去掉,以及一些标签之间没内容的把它合成一行即可

  • HomeView.vueApp.vue中导致错误的空格去掉后的代码如下

    • HomeView.vue
        
        
      
    • App.vue
          
          
      
  • 安装ant-design-vue组件,执行npm install ant-design-vue --save,发现出现下面错误

原因:ant-design-vue和vue的版本冲突

解决方法:由于我安装的vue版本是2.x的,查看ant-design-vue官网,可以看到下图中的Vue2版本对应的ant-design-vue版本为1.x

点击上图中的1.x(For Vue2),就可以看到下图的信息,可知Vue2版本对应ant-design-vue的稳定版本是1.7.8

在终端中执行npm install ant-design-vue@1.7.8 --save,安装成功如下

  • main.js中引入ant-design-vue并全局注册
    import Vue from "vue";
    import App from "./App.vue";
    import router from "./router";
    import store from "./store";
    import Antd from "ant-design-vue";

    Vue.config.productionTip = false;
    Vue.use(Antd);

    new Vue({
      router,
      store,
      render: (h) => h(App),
    }).$mount("#app");
  • App.vue中添加一个按钮
  按钮
  • 重新运行,可以看到浏览器中有对应的按钮出现

  • 此时新添加的按钮还没有样式,需要在main.js中引入ant-design-vue的样式

  import "ant-design-vue/dist/antd.css";
  • 重新运行,可以看到"按钮"有样式了

5.2自定义webpack和babel配置

5.2.1自定义webpack

  • 由于后面需要自定义主题,需要的是ant-design-vue的less格式的样式而不是css,将'main.js'中原本引入的ant-design-vue样式修改为如下
    import "ant-design-vue/dist/antd.less";
  • 重新运行,发现控制台和浏览器报了一样的错误,如下所示

    原因:没有打开javascriptEnabled的配置

    解决方法:在项目目录下创建一个vue.config.js文件,并在该文件中配置信息如下所示

      module.exports = {
        css: {
          loaderOptions: {
            less: {
              lessOptions:{
                javascriptEnabled: true,
              }
            }
          }
        }
      }
    
  • 重新运行,又出现下面的错误

    原因: less在第7版本改变了原有的出阿飞运算,所以无法识别

    解决方法:移除高版本的less-loader,指定安装less-loader6版本,得以解决

5.2.2按需引入ant-design-vue的配置,自定义babel配置

  • 安装babel-plugin-import组件

  • ant-design-vue官网中找到babel的对应配置,把它复制到项目中的babel.config.js文件中

  module.exports = {
    presets: ["@vue/cli-plugin-babel/preset"],
    "plugins": [
      ["import", { "libraryName": "ant-design-vue", "libraryDirectory": "es", "style": true }] // `style: true` 会加载 less 文件
    ]
  };
  • 修改main.js文件代码为如下
  import Vue from "vue";
  import App from "./App.vue";
  import router from "./router";
  import store from "./store";
  // import Antd from "ant-design-vue";
  // import "ant-design-vue/dist/antd.less";
  import { Button } from "ant-design-vue";
  import "ant-design-vue/lib/button/style";

  Vue.config.productionTip = false;
  Vue.use(Button);

  new Vue({
    router,
    store,
    render: (h) => h(App),
  }).$mount("#app");

5.3设计整个项目的路由

5.3.1登录注册的路由

  • 在views文件夹下创建user文件夹,并在该文件夹中创建两个组件Login.vueRegister.vue

    • Login.vue
      
      
      
    
    • Register.vue
      
      
      
    
  • 在routes目录下的index.js配置路由配置表

        import Vue from "vue";
        import VueRouter from "vue-router";
        import HomeView from "../views/HomeView.vue";
    
        Vue.use(VueRouter);
    
        const routes = [
          {
            path: "/user",
            component: {render: (h) => h("router-view")},
            children:[
              {
                path:"/user/login",
                name:"login",
                component: () =>
                  import(/* webpackChunkName: "login" */ "../views/user/Login.vue"),
              },
              {
                path:"/user/register",
                name:"register",
                component: () =>
                  import(/* webpackChunkName: "register" */ "../views/user/Register.vue"),
              }
            ]
          },
          {
            path: "/",
            name: "home",
            component: HomeView,
          },
          {
            path: "/about",
            name: "about",
            // route level code-splitting
            // this generates a separate chunk (about.[hash].js) for this route
            // which is lazy-loaded when the route is visited.
            component: () =>
              import(/* webpackChunkName: "about" */ "../views/AboutView.vue"),
          },
        ];
    
        const router = new VueRouter({
          mode: "history",
          base: process.env.BASE_URL,
          routes,
        });
    
        export default router;
    
  • 运行,发现页面报错如下

    原因:组件命名不规范,应该是有多个单词组成的组件名,使用驼峰命名

    解决方法:vue.config.js文件中加入下面一行代码,重新运行即可解决

      lintOnSave: false,
    
  • 重新运行,分别访问http://localhost:8080/user/loginhttp://localhost:8080/user/register,页面效果如下所示

  • 在src下创建一个layouts文件夹,并在该文件夹中创建一个UserLayout.vue,用于存放user相关的布局页面

  
  
  
  • routes/index.js中user的component改成如下代码
  component: () =>
          import(/* webpackChunkName: "userlayout" */ "../layouts/UserLayout.vue"),
  • 重新运行,分别访问http://localhost:8080/user/loginhttp://localhost:8080/user/register,页面效果如下所示

  • 为了让访问http://localhost:8080/user重定向到登录页面,在routes/index.js中的user路由配置表的children中添加下面代码

  {
    path:"/user",
    redirect:"/user/login"
  }

5.3.2Dashboard和表单的路由

  • 在routes/index.js中加入Dashboard和表单的路由
  {
    path: "/",
    component: () =>
          import(/* webpackChunkName: "basiclayout" */ "../layouts/BasicLayout.vue"),
    children:[
      {
        path:"/",
        redirect:"/dashboard/analysis"
      },
      {
        path:"/dashboard",
        name:"dashboard",
        component:{render: (h) => h("router-view")},
        children: [{
          path: "/dashboard/analysis",
          name: "analysis",
          component: () =>
          import(/* webpackChunkName: "register" */ "../views/dashboard/Analysis.vue"),
        }]
      },
      {
        path:"/form",
        name:"form",
        component: {render: (h) => h("router-view")},
        children: [
          {
            path:"/form/basic-form",
            name:"basicform",
            component: () =>
              import(/* webpackChunkName: "form" */ "../views/forms/BasicForm.vue"),
          },
          {
            path:"/form/step-form",
            name:"stepform",
            component: () =>
              import(/* webpackChunkName: "form" */ "../views/forms/StepForm.vue"),
            children: [
              {
                path:"/form/step-form",
                redirect:"/form/step-form/info"
              },
              {
                path:"/form/step-form/info",
                name:"info",
                component: () =>
                  import(/* webpackChunkName: "form" */ "../views/forms/stepform/Step1.vue"),
              },
              {
                path:"/form/step-form/confirm",
                name:"confirm",
                component: () =>
                  import(/* webpackChunkName: "form" */ "../views/forms/stepform/Step2.vue"),
              },
              {
                path:"/form/step-form/result",
                name:"result",
                component: () =>
                  import(/* webpackChunkName: "form" */ "../views/forms/stepform/Step3.vue"),
              }
            ]
          },
        ]
      }
    ]
  },
  • 编写首页布局页面

    • Header.vue
      
      
      
    
    • SiderMenu.vue
      
      
      
    
    • Footer.vue
      
      
      
    
  • 根据路由编写对应的组件

    • BasicLayout.vue
      
      
      
    
    • Analysis.vue
      
      
      
    
    • BasicForm.vue
      
      
      
    
    • StepForm.vue
      
    
    
    
    • Step1.vue
      
      
      
    
    • Step2.vue
      
      
      
    
    • Step3.vue
      
      
      
    
  • 重新运行,分别访问http://localhost:8080/dashboardhttp://localhost:8080/dashboard/analysis,都可以访问到对应的信息,如下面所示

  • 在views目录下添加404.vue页面

  
  
  
  • 在routes/index.js中添加404.vue相应的路由信息
  {
    path: "*",
    name: "404",
    component: NotFound,
  }
  • 重新运行,访问不存在的路由http://localhost:8080/dashboard1,会跳转到404页面,如下图所示

5.4全局前置守卫

  • 安装nprogress

  • 在routes/index.js中引入nprogress以及编写全局前置守卫相应的代码

      //引入nprogress
      import Nprogress from "nprogress";
      import "nprogress/nprogress.css";
    
    
      //路由前置守卫
      router.beforeEach((to, from, next)=>{
        Nprogress.start();
        next();
      });
    
      router.afterEach(()=>{
        Nprogress.done();
      })
    
  • 修改App.vue中的代码

      
      
    
  • 重新运行项目,访问http://localhost:8080/,直接跳转到如下图一,点击dashboard也跳转到如下图一,点击form链接跳转到如下图二

5.5项目整体页面布局

5.5.1首页基本布局

  • App.vue中的两个链接删掉

  • ant-design-vue管网中找合适Layout模板,放到BasicLayout.vue中,并把之前引入的那几个组件放到刚才引入的代码中对应的位置上

      
      
      
    
  • 由于刚才引入的Layout模板没有引入到项目中,还无法使用,需要在main.js中引入

      import { Button, Layout } from "ant-design-vue";
      Vue.use(Layout)
    
  • 运行项目,可以看到效果

5.5.2自定义触发器

  • 将原来页面中的箭头(如下图红框所示)隐藏掉,在BasicLayout.vue中的标签中添加:trigger="null"即可

  • BasicLayout.vue中的标签之间添加一个图标,并给该图标添加样式

      
      
    
      .trigger{
        padding: 0 20px;
        line-height: 46px;
        font-size: 20px;
      }
      .trigger:hover{
        background: #eeeeee;
      }
    
  • main.js中引入Icon

      import { Button, Layout, Icon } from "ant-design-vue";
      Vue.use(Icon)
    
  • 修改Header.vue中Header的样式,让它向右浮动

      
      
      
    
  • 重新运行,页面基本布局出来了

5.5.3动态改变页面布局

  • 在components文件夹中添加一个文件夹settingdrawer,并在该文件夹中创建index.vue,去ant-design-vue官网中找drawer相关的模板,复制到index.vue

      
      
    
  • BasicLayout.vue中引入该组件,并在页面中使用

      
      
      
    
  • main.js中注册Drawer

      import { Button, Layout, Icon, Drawer } from "ant-design-vue";
      Vue.use(Drawer)
    
  • 重新运行,可以看到效果,点击open按钮打开drawer,再次点击就会关闭

  • 优化settingdrawer目录下的index.vue的代码

      
      
      
    
  • BasicLayout.vue根据优化后的settingdrawer目录下的index.vue进行修改

      
      
      
    
  • routes目录下的index.js中的router.beforeEach逻辑修改为如下

      //路由前置守卫
      router.beforeEach((to, from, next)=>{
        if(to.path !== from.path){
          Nprogress.start();
        }
        next();
      });
    
  • main.js中注册Radio

      import { Button, Layout, Icon, Drawer, Radio } from "ant-design-vue";
      Vue.use(Radio)
    
  • 重新运行,可以看到对应效果如下

5.5.4将路由和菜单结合

  • ant-design-vue官网中找菜单模板,复制到SiderMenu.vue中并作出对应修改

      
    
      
    
  • SiderMenu.vue中引入的组件SubMenu,单独编写一个组件SubMenu.vue存放

      
      
    
  • BasicLayout.vue 标签中设置width="256px",才能让引入的导航菜单与左侧栏同宽

  • main.js中注册Radio

      import { Button, Layout, Icon, Drawer, Radio, Menu } from "ant-design-vue";
      Vue.use(Menu )
    
  • 运行,修改整体风格布局为白色,发现导航菜单背景颜色还是没有改变

  • SiderMenu.vue中的export default{}中添加props属性

      props:{
        theme:{
          type: String,
          default: "dark"
        }
      },
    
  • SiderMenu.vue中的标签中修改theme="dark":theme="theme"

  • BasicLayout.vue标签添加:theme="navTheme",添加后变成

  • 运行,修改整体风格布局,发现导航菜单背景颜色跟着改变

5.5.5将路由的数据动态显示在菜单中

  • 将routes目录下的index.js文件中的每个路由项进行调整,将不需要显示的父级路由隐藏,添加hideMenu: true,,隐藏子路由添加hideChildrenMenu: true,

      import Vue from "vue";
      import VueRouter from "vue-router";
      import HomeView from "../views/HomeView.vue";
      import NotFound from "../views/404.vue";
      //引入nprogress
      import Nprogress from "nprogress";
      import "nprogress/nprogress.css";
    
      Vue.use(VueRouter);
    
      const routes = [
        {
          path: "/user",
          component: () =>
                import(/* webpackChunkName: "userlayout" */ "../layouts/UserLayout.vue"),
          hideMenu: true,
          children:[
            {
              path:"/user",
              redirect:"/user/login"
            },
            {
              path:"/user/login",
              name:"login",
              component: () =>
                import(/* webpackChunkName: "login" */ "../views/user/Login.vue"),
            },
            {
              path:"/user/register",
              name:"register",
              component: () =>
                import(/* webpackChunkName: "register" */ "../views/user/Register.vue"),
            }
          ]
        },
        {
          path: "/",
          component: () =>
                import(/* webpackChunkName: "basiclayout" */ "../layouts/BasicLayout.vue"),
          children:[
            {
              path:"/",
              redirect:"/dashboard/analysis"
            },
            {
              path:"/dashboard",
              name:"dashboard",
              meta: {icon: "dashboard", title: "仪表盘"},
              component:{render: (h) => h("router-view")},
              children: [{
                path: "/dashboard/analysis",
                name: "analysis",
                meta: {title: "分析页"},
                component: () =>
                import(/* webpackChunkName: "register" */ "../views/dashboard/Analysis.vue"),
              }]
            },
            {
              path:"/form",
              name:"form",
              meta: {icon: "form", title: "表单"},
              component: {render: (h) => h("router-view")},
              children: [
                {
                  path:"/form/basic-form",
                  name:"basicform",
                  meta: {title: "基础表单"},
                  component: () =>
                    import(/* webpackChunkName: "form" */ "../views/forms/BasicForm.vue"),
                },
                {
                  path:"/form/step-form",
                  name:"stepform",
                  meta: {title: "分布表单"},
                  component: () =>
                    import(/* webpackChunkName: "form" */ "../views/forms/StepForm.vue"),
                  hideChildrenMenu: true,
                  children: [
                    {
                      path:"/form/step-form",
                      redirect:"/form/step-form/info"
                    },
                    {
                      path:"/form/step-form/info",
                      name:"info",
                      component: () =>
                        import(/* webpackChunkName: "form" */ "../views/forms/stepform/Step1.vue"),
                    },
                    {
                      path:"/form/step-form/confirm",
                      name:"confirm",
                      component: () =>
                        import(/* webpackChunkName: "form" */ "../views/forms/stepform/Step2.vue"),
                    },
                    {
                      path:"/form/step-form/result",
                      name:"result",
                      component: () =>
                        import(/* webpackChunkName: "form" */ "../views/forms/stepform/Step3.vue"),
                    }
                  ]
                },
              ]
            }
          ]
        },
        {
          path: "*",
          name: "404",
          hideMenu: true,
          component: NotFound,
        }
      ];
    
      const router = new VueRouter({
        mode: "history",
        base: process.env.BASE_URL,
        routes,
      });
    
      //路由前置守卫
      router.beforeEach((to, from, next)=>{
        if(to.path !== from.path){
          Nprogress.start();
        }
        next();
      });
    
      router.afterEach(()=>{
        Nprogress.done();
      })
    
      export default router;
    
  • 修改SiderMenu.vue代码

      
    
      
    
  • 修改SubMenu.vue代码

      
      
    
  • 重新运行,可以看到左侧菜单栏的数据显示出来了

5.5.6自定义菜单项标签中:selectedKeysopenKeys

  • 修改SiderMenu.vue代码

      
    
      
    
  • 修改SubMenu.vue代码

      
      
    
  • 重新运行,出现下面错误

原因:连续点击两次同一个菜单项

解决方法:在SiderMenu.vue中的selectedMenu做判断,如果上一次的路径跟这一次的路径一样就不做跳转

  selectedMenu(item){
     // console.log(this.$route.path)
     // console.log("item.path",item.path) 
    if(this.$route.path === item.path){
      return
    }
    this.$router.push({path: item.path, query: this.$route.query})
  }
  • 再次运行,即可得到预期的效果

5.6使用路由管理用户权限

  • 在src目录下创建一个utils文件夹,并在该文件夹中创建一个存放权限逻辑的auth.js

      export function getCurrentAuthority(){
          return ["admin"];
      }
    
      export function check(authority){
          const current = getCurrentAuthority();
          return current.some(item => authority.includes(item));
      }
    
    
      export function isLogin(){
          const current = getCurrentAuthority();
          return current && current[0] !="guest";
      }
    
  • 在routes目录下的index.js中name分别为dashboardformmeta中添加authority,并分别赋予对应的权限,guest用户只拥有dashboard权限,admin同时拥有formdashboard权限

      meta: {icon: "dashboard", title: "仪表盘", authority: ["admin", "guest"]},
    
      meta: {icon: "form", title: "表单", authority: ["admin"]},
    
  • 安装lodash工具

  • 在routes目录下的index.js中引入lodash以及在utils/auth.js中的checkisLogin

      //引入lodash工具
      import findLast from "lodash/findLast";
      //引入auth中的check,isLogin方法
      import { check, isLogin} from "../utils/auth.js";
    
  • 修改routes目录下的index.jsrouter.beforeEach的逻辑

      //路由前置守卫
      router.beforeEach((to, from, next)=>{
        if(to.path !== from.path){
          Nprogress.start();
        }
    
        //遍历路由,返回路由中meta标记的authrity值
        const record = findLast(to.matched, record => record.meta.authority);
        if(record && !check(record.meta.authority)){
          if(!isLogin() && to.path != "/user/login"){
            next({
                path: "user/login"
            });
          }else if(to.path != "/403"){
            next({
              path: "/403"
            });
          }
          Nprogress.done();
        }
    
        next();
      });
    
  • 在src/views目录下创建一个403.vue页面

      
      
      
    
  • 在routes目录下的index.js中引入403.vue,并添加对应的路由

      import Forbidden from "../views/403.vue";
    
      {
        path: "/403",
        name: "403",
        hideMenu: true,
        component: Forbidden,
      }
    
  • 在``SiderMenu.vue中引入utils/auth.js中的checkisLogin,同时修改getMenuData`方法中的forEach为for的方式,并添加一个判断

      //获取菜单数据
      getMenuData(routes = [], parentKeys = [], selectedKey){
        const menuData = [];
        for(let item of routes){
          if(item.meta && item.meta.authority && !check(item.meta.authority)){
              break;
          }
          // console.log("item:",item);
          //如果当前的路由对象有name属性且没有hideMenu
          if(item.name && !item.hideMenu){
            this.openKeysMap[item.path] = parentKeys;
            this.selectedKeysMap[item.path] = [selectedKey || item.path];
            const newItem = { ...item };
            delete newItem.children;
            if(item.children && !item.hideChildrenMenu){
              newItem.children = this.getMenuData(item.children, [...parentKeys, item.path])
            }else{
              this.getMenuData(item.children, selectedKey ? parentKeys : [...parentKeys, item.path], selectedKey || item.path);
            }
            menuData.push(newItem);
          }else if(!item.hideMenu && !item.hideChildrenMenu && item.children){//如果当前的路由对象且没有hideMenu和没有hideChildrenMenu但有子节点的
            menuData.push(...this.getMenuData(item.children, [...parentKeys, item.path]));
          }
        }
        return menuData;
      },
    
  • 重新运行,可以看到所有菜单,修改utils/auth.jsgetCurrentAuthority方法的返回用户为user

      export function getCurrentAuthority(){
          return ["user"];
      }
    
  • 重新运行,只看到一个403的的页面,因为没有这个用户

  • 在routes目录下的index.js中引入notification ,并在router.beforeEach使用

      import { notification } from "ant-design-vue";
      
    
      router.beforeEach((to, from, next)=>{
        if(to.path !== from.path){
          Nprogress.start();
        }
    
        //遍历路由,返回路由中meta标记的authrity值
        const record = findLast(to.matched, record => record.meta.authority);
        if(record && !check(record.meta.authority)){
          if(!isLogin() && to.path != "/user/login"){
            next({
                path: "user/login"
            });
          }else if(to.path != "/403"){
            notification.error({
              message: '403',
              description:'你没有权限访问,请联系管理员!',
            });
            next({
              path: "/403"
            });
          }
          Nprogress.done();
        }
    
        next();
      });
    
  • 重新运行,右上角可以看到对应的提示信息

  • 修改utils/auth.jsgetCurrentAuthority方法的返回用户为guest,重新运行,只能看到仪表盘的菜单

5.7精细权限

5.7.1权限组件

  • src/components目录下创建一个权限组件Authorized.vue

      
        
    
  • main.js中引入Authorized.vue组件并注册,方便全局使用

      import Authorized from "./components/Authorized.vue";
      Vue.component("Authorized", Authorized);
    
  • BasicLayout.vue的抽屉中使用,让抽屉只有在admin用户下才能显示

      
        
      
    
  • 修改utils/auth.jsgetCurrentAuthority方法的返回用户为guest,重新运行,可以看到页面中不显示抽屉的按钮了

5.7.2权限指令(有弊端,删除了就用不了了)

  • 在src目录下创建一个directives文件夹,并在该文件夹中创建auth.js,用于自定义权限指令,这里自定义了一个v-auth指令

      import { check } from "../utils/auth";
      //自定义权限指令
      function install(Vue, options = {}){
        Vue.directive(options.name || "auth", {
          inserted(el, binding){
            if(!check(binding.value)){
              el.parentNode && el.parentNode.removeChild(el);
            }
          }
        });
      }
    
      export default { install };
    
  • main.js中全局注册

      //引入自定义权限指令
      import Auth from "./directives/auth";
      Vue.use(Auth);
    
  • BasicLayout.vue的左侧缩放图标中使用

      
      
      
    
  • 修改utils/auth.jsgetCurrentAuthority方法的返回用户为guest,重新运行,可以看到左右缩放的图标消失了

5.8在组件中使用echarts第三方插件

  • 安装echarts插件

  • main.js中全局引入echarts并挂载到vue中

      //全局引入echarts
      import echarts from "echarts";
      //需要挂载到Vue原型上,通过Vue.prototype将echarts保存为全局变量
      Vue.prototype.$echarts = echarts
    
  • 在src/components文件夹下创建Chart.vue

      
      
      
    
  • AnalysisLayout.vue中引入组件Chart.vue

      
      
      
    
  • 运行后发现浏览器控制台出现下面错误

    原因: 查阅文档发现可能是下载了Echarts的最新版本
    解决方法: 下载4.8.0版本的echarts即可

  • 重新运行,访问http://localhost:8080/dashboard/analysis,可以看到对应的echarts图

  • 切换左侧菜单展示效果的时候,右侧内容会对应变宽(如下图一所示),但此时的echarts并不能执行自适应效果,使用element-resize-detector监听元素宽度变化,安装resize-detector

  • Chart.vue组件中引入resize-detectoraddListenerremoveListener组件,并在对应位置使用

      
      
      
    
  • 重新运行,访问http://localhost:8080/dashboard/analysis,可以看到切换左侧菜单展示效果的时候,右侧内容对应变宽的问题得以解决

  • Chart.vue中的resize方法中添加控制台输出console.log("resize");,运行,访问http://localhost:8080/dashboard/analysis,打开浏览器控制台,可以看到每次切换左侧菜单展示效果的时候,下面都会输出很多条resize,这是很影响性能的,此时我们可以使用debounce来防抖,在Chart.vue中引入debounce并在created中使用

      import debounce from "lodash/debounce";
    
      created(){
        this.resize = debounce(this.resize, 300);
      },
    
  • 再次运行,访问http://localhost:8080/dashboard/analysis,打开浏览器控制台,可以看到每次切换左侧菜单展示效果时,控制台只输出一次resize,达到防抖的效果

  • Chart.vue中将初始化echarts放到一个方法renderChart中,option从父组件中传过来,在props中创建一个option,并把它丢进this.chart.setOption(this.option);,把option相关的数据以及存放图标的dom的高度放到Analysis.vue中设置,同时在Chart.vue中监听option的变化,Analysis.vue中设置每三秒数据变化一次并把最新的数据放到chartOption中传到Chart.vue中的props中,以便每次数据变化都能监听到

    • Chart.vue
     
     
     
    
    • Analysis.vue
     
     
     
    
  • 重新运行,访问http://localhost:8080/dashboard/analysis,可以看到图表数据每三秒变化一次,这里只给出两次的数据变化

5.9使用Mock数据进行开发

  • 安装axios

  • Analysis.vue中引入axios,并添加一个方法getChartData(该方法是通过axios的方式获取echarts图数据),并在mounted中的对应位置调用getChartData

      
      
      
    
  • 在项目目录下创建一个文件夹mock,并在该文件夹中创建一个文件dashboard_chart.js,在该文件中编写Analysis.vuegetChartData方法执行后返回数据的逻辑

      function chart(method){
        let res = null;
        switch (method) {
          case "GET":
            res = [20, 40, 78, 10, 30, 48];
            break;
          default:
            res = null;
        }
        return res;
      }
    
      module.exports = chart;
    
  • vue.config.js中使用devServer.proxy代理请求

      devServer:{
        proxy:{
            '/api':{
                target: 'http://localhost:3000',//代理地址,这里设置的地址会代替axios中设置的baseURL
                bypass: function(req, resp){
                  if(req.headers.accept.indexOf("htmlS") != -1){
                    console.log("Skipping proxy for browerser request");
                    return "index.html";
                  }else {
                    // console.log("req.path:", req.path);
                    // console.log("split[1]:",req.path.split("/api/")[1]);
                    //把请求路径"/api/dashboard/chart"分割成dashboard_chart
                    const apiSplit = req.path.split("/api/")[1];
                    if(!apiSplit){
                      return;
                    }
                    // console.log("split2:", apiSplit.split("/"));
                    const name = apiSplit.split("/").join("_");
                    const mock = require(`./mock/${name}`);
                    const result = mock(req.method);
                    //清除缓存
                    delete require.cache[require.resolve(`./mock/${name}`)];
                    return resp.send(result);
                  }
                }                
        }}
      },
    
  • 重新执行npm run serve,发现终端报错如下


原因:一切包含"/api"的html,css,js以及图片资源等,都会通过代理
解决方法:使用通配符匹配路径,即将'/api'修改为'@(/api)'
官方文档有说明:

  • 查看http-proxy-middlware中的Context matching 78

    • 通配符路径匹配:对于细粒度控制,您可以使用通配符匹配。全局模式匹配是通过微匹配完成的。访问 micromatch 或glob 26更多的例子。
  • 查看glob中的 Glob Primer

    • 匹配单个路径部分中的 0 个或多个字符
    • ? 匹配 1 个字符
    • [...] 匹配一系列字符,类似于 RegExp 范围。如果范围的第一个字符是 ! 或 ^ 然后它匹配不在范围内的任何字符。
    • !(pattern|pattern|pattern) 匹配与所提供的任何模式不匹配的任何内容。
    • ?(pattern|pattern|pattern) 匹配所提供模式的零次或一次出现。
    • +(pattern|pattern|pattern) 匹配提供的模式的一次或多次出现。
    • *(a|b|c) 匹配所提供模式的零次或多次出现
    • @(pattern|pat*|pat?erN)完全匹配提供的模式之一
    • 如果“globstar”在路径部分中单独存在,则它匹配零个或多个目录和搜索匹配项的子目录。它不会爬取符号链接的目录
  • 重新运行,访问http://localhost:8080/dashboard/analysis,可以看到对应的echarts图

  • 修改dashboard_chart.js中返回的数据为res = [40, 40, 78, 10, 30, 48](第一列的数据为40);可以看到对应的echarts图中的第一列数据的变化

5.10二次封装axios请求

  • 在utils包下创建request.js,二次封装axios请求

      import axios from "axios";
      import { notification } from "ant-design-vue";
    
      function request(options){
          return axios(options).then(resp => {
            return resp;
          }).catch(error => {
            const {
              response: {status, statusText }
            } = error;
            notification.error({
              message: status,
              description: statusText,
            });
            return Promise.reject(error);
          })
      }
    
      export default request;
    
  • Analysis.vue中引入request,并把原来的getChartData方法里面的请求换成request的方式

      getChartData(){//通过axios的方式获取echarts图数据
        request({
          url: "/api/dashboard/chart",
          methods: "get",
          params: {ID: "123456"}
        }).then(response => {
            this.chartOption= {
                title: {
              text: "Echarts入门案例"
            },
            tooltip:{},
            xAxis: {
              data: ["衬衫", "羊毛衫", "雪纺衫", "长裤", "靴子", "袜子"]
            },
            yAxis: {},
            series: [{
              name: "销量",
              type: "bar",
              data: response.data
            }]
            }
        })
      },
    
  • 重新执行npm run serve,发现终端报错

    原因:原来的页面没有关掉,而getChartData方法在项目启动渲染的时候就调用了
    解决方法:启动前先把原来的页面关掉再启动即可

  • 重新启动,访问http://localhost:8080/dashboard/analysis,即可得到echart图

  • getChartData方法复制一份,将新复制的那一份的url改成url: "/dashboard/chart"(这个请求不存在),重新运行,可以看到页面报404错误

  • 为了让vue支持jsx语法,这里安装@vue/babel-preset-jsx@vue/babel-helper-vue-jsx-merge-props

  • babe.config.js中加入"@vue/babel-preset-jsx"

  • 修改request.js中请求错误是message的写法,将status标红,并返回url

      import axios from "axios";
      import { notification } from "ant-design-vue";
    
      function request(options){
          return axios(options).then(resp => {
            return resp;
          }).catch(error => {
            const {
              response: {status, statusText }
            } = error;
            notification.error({
              message: h => (
                
    请求错误{status} : {options.url}
    ), description: statusText, }); return Promise.reject(error); }) } export default request;
  • 打开url为/dashboard/chartgetChartData方法,重新运行,可以看到弹出的错误框中的信息跟预期结果一样

5.11在表单页面使用ant-design-vue模板

5.11.1简单使用表单模板

  • main.js中引入Form和Input并注册
      import { Button, Layout, Icon, Drawer, Radio, Menu, Form, Input } from "ant-design-vue";
      Vue.use(Form);
      Vue.use(Input);
    
  • ant-design-vue官网中找Form模板,复制到BasicForm.vue
      
    
      
    
  • BasicForm.vue的第一个输入框中添加v-model="fieldA",然后再它上面的的标签中添加:validate-status="validateAStatus":help="helpA",在第二个输入框中添加v-model="fieldB",并把这些数据在data中定义,然后监听fieldA的变化,当fieldA长度小于等于5个字符就会报错误信息,否则就把错误信息置空,并在提交按钮中添加点击事件handlerForm,该事件处理逻辑是当fieldA长度小于等于5个字符就会报错误信息,否则就输出fieldA和fieldB的信息,全部代码如下所示
      
    
    
    

5.11.2初始数据,自动校验,动态赋值(全部都在BasicForm.vue页面进行修改)

  • 在data中的fieldA中添加初始化值hello,添加下面代码,用于初始化表单数据

      this.form = this.$form.createForm(this);
    
  • 标签中使用:form="form"绑定form的数据,``

  • 在FieldA输入框中添加如下代码,用于自定义校验

      v-decorator="[
         'fieldA',
        {
          initialValue: fieldA,
          rules: [{requied: true, min: 5, message: '必须大于5个字符'}]
        }
      ]" 
    
  • 修改handlerForm方法的代码

      handlerForm(){
        // if(this.fieldA.length <= 5){
        //     this.validateAStatus = "error";
        //     this.helpA = "必须大于5个字符";
        // }else{
        //   console.log("fieldA:"+this.fieldA,"fieldB:"+this.fieldB);
        // }
        this.form.validateFields((err, values) => {
          if(!err){
              console.log(values);
              Object.assign(this, values);
          }
        });
      }
    
  • 重新运行,访问http://localhost:8080/form/basic-form,可以看到fieldA的初始值为hello

    重新输入内容也会校验

    验证通过后提交,控制台有对应的输出内容

  • 如果想要修改输入框的初始值,可以在mounted中进行设置,并且通过this.form.setFieldsValue的方式,这里修改fieldA的初始值为field A,并且设置3秒后国企

      setTimeout(() => {
          this.form.setFieldsValue({ fieldA: 'field A' });
      }, 3000);
    ``
    
  • 重新运行,访问http://localhost:8080/form/basic-form,可以看到fieldA的初始值为field A,3秒后FieldA的值又恢复为hello

5.11.3分布表单

  • 在store文件夹中创建一个modules文件夹,并在该文件夹中传概念一个form.js文件

      import router from "@/router";
      import request from "@/utils/request";
    
      const state = {
        step: {
          payAccount: "123456"
        }
      };
    
      const actions = {
        async submitStepForm({ commit }, { payload }){
          await request({
            url: "/api/form",
            method: "POST",
            data: payload
          });
          commit("saveStepFormData", payload);
          router.push("/form/step-form/result")
        }
    
      };
    
    
      const mutations = {
        saveStepFormData(state, {payload}){
          state.step = {
            ...state.step,
            ...payload
          };
        }
      };
    
      export default {
        namespaced: true,
        state,
        actions,
        mutations
      }
    
  • store/index.js中引入modules/form.js文件,并把form加到mudules中

      import Vue from "vue";
      import Vuex from "vuex";
      import form from "./modules/form";
    
      Vue.use(Vuex);
    
      export default new Vuex.Store({
        state: {},
        getters: {},
        mutations: {},
        actions: {},
        modules: {
          form
        },
      });
    
  • 在mock文件夹中创建一个form.js文件,把dashboard_chart.js中的代码复制过来,修改method和数据,代码如下面所示

      function chart(method){
        let res = null;
        switch (method) {
          case "POST":
            res = {message: "OK"};
            break;
          default:
            res = null;
        }
        return res;
      }
      
      module.exports = chart;
    
  • 修改StepForm.vue代码中的表单页为

      
      
      
    ``
    
    
  • 修改routes/index.js中的name为"stepform"component: () =>import(/* webpackChunkName: "form" */ "../views/forms/BasicForm.vue"),component:{render: (h) => h("router-view")},,并添加redirect:"/form/step-form/info",

  • 修改Step1.vue

      
    
    
    
  • 修改Step2.vue

      
      
      
    
  • 修改Step3.vue

      
      
      
    
  • 运行,访问http://localhost:8080/form/step-form/info,可以看到第一步的页面

点击下一步后的页面

点击提交按钮后的页面

5.11.4自己封装一个支持自动校验的表单项

  • main.js中引入Select并注册
      import { Button, Layout, Icon, Drawer, Radio, Menu, Form, Input, Select } from "ant-design-vue";
      Vue.use(Select);
    
  • 在components文件夹中创建一个自定义的校验表单项ReceiverAccount.vue
      
    
      
    
      
    
  • Step1.vue中引入ReceiverAccount.vue组件并注册使用,同时添加校验规则
      
      
      
    
  • 运行,访问http://localhost:8080/form/step-form/info,可以看到自定义的表单项,不输入内容,直接下一步,自定义的表单项下面会有对应的提示信息,校验通过后就会跳转到下一步的页面

5.12在系统中使用图标

5.12.1使用网上的图标

  • 图标网址:阿里巴巴矢量图标库官网

  • 查看ant-design-vue官网中自定义图标的使用教程

  • 把需要的404图标在 iconfont.cn 上生成

  • main.js中引热议icon并全局注册

      const IconFont = Icon.createFromIconfontCN({
        scriptUrl: '//at.alicdn.com/t/font_3352144_vpwew18fpxk.js', // 在 iconfont.cn 上生成
      });
      Vue.component("IconFont", IconFont);
    
  • 404.vue中使用IconFont 组件

      
      
      
    

说明:
组件中的type是在iconfont.cn 上的图标上面复制过来的

  • 重新运行,访问一个不存在的请求路径,可以看到对应的404页面

5.12.2使用自己设计的图标

  • 截取vue图标并命名为logo.png,然后放到项目中的assets目录下

  • 404.vue中引入并使用

      
      
      
    
  • 重新运行,访问一个不存在的请求路径,可以看到404页面中新添加的图标

5.12.3使用组件的方式引入svg图标

  • 网上下载一个svg图标并命名为Smile.svg,然后放到项目中的assets目录下

  • 安装vue-svg-loader插件

  • 在Vue CLI官网中找到这一段代码,把它复制到项目中的vue.config.js文件中

  • 404.vue中引入Smile.svg组件并注册和使用

      
      
      
    
  • 重新运行,访问一个不存在的请求路径,发现新添加的图标没显示,浏览器控制台报错

    原因:vue.config.js中引入的配置是Vue CLI3的配置,我这里安装的是Vue CLI2
    解决方法:修改vue.config.js原来引入的配置为如下即可

      //1.需先删除默认配置中处理的svg
      chainWebpack:(config) => {
        config.module.rules.delete("svg");
      },
      //2.配置Loader
      configureWebpack: {
        module: {
          rules: [
            {
              test: /\.svg$/,
              loader: "vue-svg-loader",
            }
          ]
        }
      },
    
  • 重新运行,访问一个不存在的请求路径,可以看到404页面中新添加的图标

5.13定制主题与动态切换主题

5.13.1定制主题

  • 由于这个项目使用Vue CLi2,查看ant-design-vue官网定制主题相关内容,可以知道按如下页面配置即可

  • vue.config.js中的less配置中添加对应配置

      css: {
        loaderOptions: {
          less: {
            lessOptions:{
              javascriptEnabled: true,
              modifyVars: {//定制主题设置
                'primary-color': '#1DA57A',
              }
            },      
          }
        }
      },
    
  • 重新运行,访问http://localhost:8080/form/step-form/info,可以看到菜单项选中的颜色以及右边页面的按钮颜色变成了绿色

5.13.2动态切换主题

  • 安装antd-theme-webpack-plugin插件

  • vue.config.js中添加如下配置

      const path = require("path");
      const AntDesignThemePlugin = require('antd-theme-webpack-plugin');
    
      const options = {
        antDir: path.join(__dirname, './node_modules/ant-design-vue'),
        stylesDir: path.join(__dirname, './src'),
        varFile: path.join(__dirname, './node_modules/ant-design-vue/lib/style/themes/default.less'),
        mainLessFile: "",
        themeVariables: ["@primary-color"],
        generateOnce: false
      }
    
      const themePlugin = new AntDesignThemePlugin(options);
      module.exports = {
        configureWebpack: {
          plugins: [themePlugin]
        }
      }
    },
    
  • 然后在public文件夹下面deindex.html文件添加如下代码

      
      
      
    
  • 修改抽屉图标的主题,在components/settingdrawer目录下创建index.less,将抽屉按钮的样式放进该文件中,将背景颜色改为@primary-color,并引入theme的默认less文件,index.less的全部代码如下

      @import "~ant-design-vue/lib/style/themes/default.less";
      .setting-drawer-handle{
          position: absolute;
          top: 240px;
          right: 300px;
          width:48px;
          height: 48px;
          background: @primary-color;
          color: #fff;
          font-size: 20px;
          text-align: center;
          line-height: 48px;
          border-radius: 3px 0 0 3px;
        }
    
  • components/settingdrawer/index.vue的抽屉图标的class修改为setting-drawer-handle,并在style引入index.less

      
    
  • 重新运行,报如下错误

    原因:components/settingdrawer/index.vue中的

  • 重新运行,访问ocalhost:8080/form/step-form/info,可以看到抽屉图标的样式也变成绿色了

5.14使用ant-design-vue和moment库进行国际化

  • 根据ant-design-vue官网中国际化的使用方式进行使用

  • main.js中引入LocaleProvider并注册

      import { Button, Layout, Icon, Drawer, Radio, Menu, Form, Input, Select, LocaleProvider } from "ant-design-vue";
      Vue.use(LocaleProvider);
    
  • 修改App.vue的代码

      
      
      
    
  • main.js中引入Dropdown并注册

      import { Button, Layout, Icon, Drawer, Radio, Menu, Form, Input, Select, LocaleProvider, Dropdown } from "ant-design-vue";
      Vue.use(Dropdown);
    
  • Header.vue中使用Dropdown组件

      
      
      
    
  • 重新运行,访问http://localhost:8080/dashboard/analysis,可以看到页面右上角有个国际化的图标,切换语言,可以看到url上的locale参数

  • main.js中引入DatePicker并注册

      import { Button, Layout, Icon, Drawer, Radio, Menu, Form, Input, Select, LocaleProvider, Dropdown, DatePicker } from "ant-design-vue";
      Vue.use(DatePicker);
    
  • Analysis.vue中使用DatePicker组件(这是moment库下的)

      
    
  • 重新运行,访问http://localhost:8080/dashboard/analysis,切换语言,可以看到日期中的语言也跟着变化,但这仅仅只是针对moment库下的组件起作用,其他的则无效果