Vue CLI3 builds ANTD-VUE project

Vue CLI 3 Quickly create a project

  • Enter the command vue create ant-design-vue-pro to create the project
  • Manually select features,default (Babel, esLint) (default installation)

Default (Babel, esLint) : Default setting (enter directly, NPM package without any accessibility

Manually select features: Manual configuration is the NPM package required for our project

  • Press up or down to select the function to be installed, press the space bar to confirm, and press Enter to proceed to the next step

  • Press up and down and Enter keys to select the function to install;

Customize Webpack and Babel configurations

  • Antd imports component dependencies as needed by entering the command NPM I –save-dev babel-plugin-import to installbabel-plugin-importAnd modify the filebabel.config.jsThe following
 module.exports = {
  presets: ["@vue/app"],
+  plugins: [
+    [
+      "import",
+      { libraryName: "ant-design-vue", libraryDirectory: "es", style: true} +] +]};Copy the code
  • Enter the command NPM I ant-design-vue installation group ANTD-vue piece library
  • mainThe antD-vue file is introduced as follows
import Vue from "vue";
import { Button } from "ant-design-vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";

Vue.config.productionTip = false;

Vue.use(Button);

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount("#app");
Copy the code
  • NPM run serve Runs the project. If less problems occur

Solution Configure vue.config.js

 module.exports = {
    css: {
    	loaderOptions:{
    		less:{
    			javascriptEnabled:true,}}}}Copy the code

How to design a high-scale route

Create users to log in, register, build and configure routes

  • insrc/router.jsConfigure a user to log in and register a route
{
      path: "/user",
      children: [
        {
          path: "/user/login",
          name: "login",
          component: () => import(/* webpackChunkName: "user"* /"./views/user/Login.vue")
        },
        {
          path: "/user/register",
          name: "register",
          component: () => import(/* webpackChunkName: "user"* /"./views/user/Register.vue")},]}Copy the code
  • insrc/viewsLet’s go ahead and create a new oneuserThe user directory stores user-related components. Create user componentsLogin.vueandRegister.vue

Layout Components (routerView)

  • The user component has been created, but the access route will not be loaded into our component, we need onerouterViewA placeholder to mount to when a component is matchedrouterViewI can write one in positionrouterViewComponent introduced, but generally usedrenderThe method is relatively simple
{
      path: "/user",
      component: { render: h=> h("router-view") },
      children: [
        {
          path: "/user/login",
          name: "login",
          component: () => import(/* webpackChunkName: "user"* /"./views/user/Login.vue")
        },
        {
          path: "/user/register",
          name: "register",
          component: () => import(/* webpackChunkName: "user"* /"./views/user/Register.vue")},]}Copy the code
  • Design layout components and provide them in the layoutrouterViewMount item, insrcnewlayoutsThe directory is used to store UserLayout and BasicLayout. insrcnewdashboardDirectory to storeAnalysisAnalysis of the page

UserLayout: Extract the generic layout used for the login and registration page

<template>
  <div>
    <div class="desc">Ant Desigin Vue Pro</div>
    <router-view></router-view>
  </div>
</template>

<script>
export default {

}
</script>

<style>

</style>


Copy the code
  • src/routerThe introduction ofUserLayoutLayout components when accesseduserRedirected to/user/loginpage
{
      path: "/user",
      component: () =>
        import(/* webpackChunkName: "layout"* /"./layouts/UserLayout"),
      children: [
        {
          path: "/user",
          redirect: "/user/login"
        },
        {
          path: "/user/login",
          name: "login",
          component: () =>
            import(/* webpackChunkName: "user"* /"./views/user/Login")
        },
        {
          path: "/user/register",
          name: "register",
          component: () =>
            import(/* webpackChunkName: "user"* /"./views/user/Register")}}]Copy the code

BasicLayout: A basic page layout that includes header navigation, bottom information, sidebars, and content sections

<template>
  <div>
    <Header />
    <SiderMenu />
    <router-view></router-view>
    <Footer />
  </div>
</template>

<script>
import Header from "./Header";
import Footer from "./Footer";
import SiderMenu from "./SiderMenu";
export default {
  components: {
    Header,
    Footer,
    SiderMenu
  }
};
</script>

<style></style>
Copy the code
  • src/routerThe introduction ofBasicLayoutLayout component when the address bar path is/Is redirected to the analysis page
   {
      path: "/",
      component: () =>
        import(/* webpackChunkName: "layout"* /"./layouts/BasicLayout"),
      children: [
        // dashboard
        {
          path: "/",
          redirect: "/dashboard/analysis"
        },
        {
          path: "/dashboard",
          name: "dashboard",
          meta: { icon: "dashboard", title: "Dashboard" },
          component: { render: h => h("router-view") },
          children: [
            {
              path: "/dashboard/analysis",
              name: "analysis",
              meta: { title: "Analysis page" },
              component: () =>
                import(
                  /* webpackChunkName: "dashboard"* /"./views/Dashboard/Analysis"}]}]}Copy the code

Implement a dynamically changing page layout

  • Open the official website of VUE ANTDhttps://vue.ant.designFind the Layout component and find a template similar to Pro, as shown below

  • Open code assignment to our projectsrc/layouts/BasicLayout.vueIn the

BasicLayout.vue

<template>
  <div>
    <a-layout id="components-layout-demo-side" style="min-height: 100vh">
    <a-layout-sider
      collapsible
      v-model="collapsed"
    >
      <div class="logo" />
      <SiderMenu />
    </a-layout-sider>
    <a-layout>
        <a-layout-header style="background: #fff; padding: 0" >
          <Header />
        </a-layout-header>
        <a-layout-content style="margin: 0 16px">
          <router-view></router-view>
        </a-layout-content>
        <a-layout-footer style="text-align: center">
          <Footer />
        </a-layout-footer>
      </a-layout>
    </a-layout>
  </div>
</template>

<script>
import Header from "./Header";
import Footer from "./Footer";
import SiderMenu from "./SiderMenu";
export default {
  data() {
    return {
      collapsed: false,
    }
  },
  components: {
    Header,
    Footer,
    SiderMenu
  }
};
</script>

<style></style>

Copy the code
  • There are some differences between menu switch trigger and ANTD Pro, which need to be customized by ourselves.

First hide trigger original icon, layout. Sider has a trigger attribute, custom trigger, set to NULL to hide trigger

Then customize the A-icon image and position

Finally, add a Click event to control menu switching

BasicLayout.vue

<template>
  <div>
    <a-layout id="components-layout-demo-side" style="min-height: 100vh">
    <a-layout-sider
      collapsible
      v-model="collapsed"
      :trigger="null"
    >
      <div class="logo" />
      <SiderMenu />
    </a-layout-sider>
    <a-layout>
        <a-layout-header style="background: #fff; padding: 0" >
          <a-icon 
            class="trigger" 
            :type="collapsed ? 'menu-unfold' : 'menu-fold'" 
            @click="collapsed = ! collapsed"
          ></a-icon>
          <Header />
        </a-layout-header>
        <a-layout-content style="margin: 0 16px">
          <router-view></router-view>
        </a-layout-content>
        <a-layout-footer style="text-align: center">
          <Footer />
        </a-layout-footer>
      </a-layout>
    </a-layout>
  </div>
</template>

<script>
import Header from "./Header";
import Footer from "./Footer";
import SiderMenu from "./SiderMenu";
export default {
  data() {
    return {
      collapsed: false,
    }
  },
  components: {
    Header,
    Footer,
    SiderMenu
  }
};
</script>

<style lang="less" scoped>
.trigger{
  padding: 0 20px;
  line-height: 64px;
  font-size: 20px;
}
.trigger:hover{
  background-color: #eeeeee;
}
</style>

</style>

Copy the code
  • Antd Pro right drawer dynamically changes page layout insrc/components/SettingDrawer/Index.vueCreate the Settings theme component and go to the official websiteDrawerBase drawer assembly

Index.vue

<template>
  <div>
    <a-button type="primary" @click="showDrawer">
      Open
    </a-button>
    <a-drawer
      title="Basic Drawer"
      placement="right"
      :closable="false"
      @close="onClose"
      :visible="visible"> <p>Some contents... </p> <p>Some contents... </p> <p>Some contents... </p> </a-drawer> </div> </template> <script>export default {
  data() {
    return {
      visible: false,
    }
  },
  methods: {
    showDrawer() {
      this.visible = true
    },
    onClose() {
      this.visible = false
    },
  },
}
</script>
Copy the code

– Custom drawer buttons and drawer styles

Delete the original button and display event;

Position icon to right and style, bind expand shrink event;

Dynamic layout Settings item styles

To dynamically change the page layout, we temporarily put the Settings parameters into the router, and BasicLayout dynamically changes the layout by reading computed properties from the router

Index.vue

<template>
  <div>
    <a-drawer
      placement="right"
      :closable="false"
      @close="onClose"
      :visible="visible"
      width="300px"
    >
      <template v-slot:handle>
        <div class="handle" @click="visible = ! visible">
          <a-icon :type="visible ? 'close' : 'setting'"- icon > < / a > < / div > < / template > < div > < h2 > whole style custom < / h2 > < a - radio - group: value ="$route.query.navTheme || 'dark'"
          @change="e => handleSettingChange('navTheme', e.target.value)"
        >
          <a-radio value="dark"> Black </a-radio> <a-radio value="light"> White </a-radio> </a-radio-group> <h2> Navigation mode </h2> < A-radio-group :value="$route.query.navLayout || 'left'"
          @change="e => handleSettingChange('navLayout', e.target.value)"
        >
          <a-radio value="left"> left </a-radio> <a-radio value="lighn"- radio > top < / a > < / a - radio - group > < / div > < / a - the drawer > < / div > < / template > < script >export default {
  data() {
    return {
      visible: false
    };
  },
  methods: {
    onClose() {
      this.visible = false;
    },
    handleSettingChange(type, value) {
      this.$router.push({ query: { ... this.$route.query, [type]: value } }); }}}; </script> <styletype="less" scoped>
.handle {
  position: absolute;
  top: 240px;
  right: 300px;
  width: 48px;
  height: 48px;
  background: #1890ff;
  color: #fff;
  font-size: 20px;
  text-align: center;
  line-height: 48px;
  border-radius: 3px 0 0 3px;
}
</style>


Copy the code

BasicLayout.vue

<template>
  <div :class="[`nav-theme-${navTheme}`, `nav-layout-${navLayout}`]. "">
    <a-layout id="components-layout-demo-side" style="min-height: 100vh">
      <a-layout-sider
        v-if="navLayout === 'left'"
        :theme="navTheme"
        :trigger="null"
        collapsible
        v-model="collapsed"
        width="256px"
      >
        <div class="logo">
          <h1>Ant Design Pro</h1>
        </div>
        <SiderMenu />
      </a-layout-sider>
      <a-layout>
        <a-layout-header style="background: #fff; padding: 0">
          <a-icon
            class="trigger"
            :type="collapsed ? 'menu-unfold' : 'menu-fold'"
            @click="collapsed = ! collapsed"
          ></a-icon>
          <Header />
        </a-layout-header>
        <a-layout-content style="margin: 0 16px">
          <router-view></router-view>
        </a-layout-content>
        <a-layout-footer style="text-align: center">
          <Footer />
        </a-layout-footer>
      </a-layout>
    </a-layout>
    <setting-drawer />
  </div>
</template>

<script>
import Header from "./Header";
import Footer from "./Footer";
import SiderMenu from "./SiderMenu";
import SettingDrawer from ".. /components/SettingDrawer/Index";
export default {
  data() {
    return {
      collapsed: false
    };
  },
  computed: {
    navTheme() {
      return this.$route.query.navTheme || "dark";
    },
    navLayout() {
      return this.$route.query.navLayout || "left";
    }
  },
  components: {
    Header,
    Footer,
    SiderMenu,
    SettingDrawer
  }
};
</script>

<style lang="less" scoped>
.trigger {
  padding: 0 20px;
  line-height: 64px;
  font-size: 20px;
  &:hover {
    background: #eeeeee;
  }
}
.logo {
  position: relative;
  height: 64px;
  padding-left: 24px;
  overflow: hidden;
  line-height: 64px;
  svg {
    width: 32px;
    height: 32px;
    display: inline-block;
    vertical-align: middle;
  }
  h1 {
    display: inline-block;
    margin: 0 0 0 12px;
    font-size: 20px;
    font-family: Avenir, "Helvetica Neue", Arial, Helvetica, sans-serif;
    font-weight: 600;
    vertical-align: middle;
  }
}
.nav-theme-dark {
  /deep/ .logo {
    h1 {
      color: #ffffff;
    }
  }
}
</style>

Copy the code

How to combine menus with routing

  • Menu we will use the official website single file recursive menu example

  • Copy the sample code directly to modify usSiderMenu.vueFile, because you need to recurse you need to create oneSubMenu.vueFile, two ways: ① functional component form ② common component, recommended functional component
 * recommend SubMenu.vue
 https://github.com/vueComponent/ant-design-vue/blob/master/components/menu/demo/SubMenu.vue 
 * SubMenu1.vue https://github.com/vueComponent/ant-design-vue/blob/master/components/menu/demo/SubMenu1.vue
Copy the code
  • Switch the theme menu is unchanged, we need to get the theme parameters set, can be againBasicLayout.vuePass to sidermenu. vue by means of father-child transmission

BasicLayout.vue

<SiderMenu :theme="navTheme" :collapsed="collapsed" />
Copy the code

SiderMenu.vue

<template>
  <div style="width: 256px">
    <a-menu
      :defaultSelectedKeys="[' 1 ']"
      :defaultOpenKeys="(' 2 ')"
      mode="inline"
      :theme="theme"
      :inlineCollapsed="collapsed"
    >
      <template v-for="item in list">
        <a-menu-item v-if=! "" item.children" :key="item.key">
          <a-icon type="pie-chart" />
          <span>{{ item.title }}</span>
        </a-menu-item>
        <sub-menu v-else :menu-info="item" :key="item.key" />
      </template>
    </a-menu>
  </div>
</template>

<script>
/*
 * recommend SubMenu.vue https://github.com/vueComponent/ant-design-vue/blob/master/components/menu/demo/SubMenu.vue
 * SubMenu1.vue https://github.com/vueComponent/ant-design-vue/blob/master/components/menu/demo/SubMenu1.vue
 * */
import SubMenu from "./SubMenu";
export default {
  props: {
    theme: {
      type: String,
      default: "dark"
    }
  },
  components: {
    "sub-menu": SubMenu
  },
  data() {
    return {
      collapsed: false,
      list: [
        {
          key: "1",
          title: "Option 1"
        },
        {
          key: "2",
          title: "Navigation 2",
          children: [
            {
              key: "2.1",
              title: "Navigation 3",
              children: [{ key: 2.1.1 "", title: "Option 2.1.1"}]}]}; }, methods: {toggleCollapsed() { this.collapsed = ! this.collapsed; }}}; </script>Copy the code
  • Generate our menu data items through the configuration of routes. Some routes do not need to be displayed in the menu bar, so we added some parameters in the configuration of VUe-Router. Such as hideChildrenInMenu hideInMenu, meta title, meta. Icon to help generate the menu

HideChildrenInMenu Is used to hide child routes that do not need to be displayed in the menu.

HideInMenu can not show this route in the menu, including child routes.

Meta. Title and meta. Icon represent the text and icon of the generated menu item, respectively.

router.js

import Vue from "vue";
import Router from "vue-router";
import NProgress from "nprogress";
import "nprogress/nprogress.css";

Vue.use(Router);

const router = new Router({
  mode: "history",
  base: process.env.BASE_URL,
  routes: [
    {
      path: "/user",
      hideInMenu: true,
      component: () =>
        import(/* webpackChunkName: "layout"* /"./layouts/UserLayout"),
      children: [
        {
          path: "/user",
          redirect: "/user/login"
        },
        {
          path: "/user/login",
          name: "login",
          component: () =>
            import(/* webpackChunkName: "user"* /"./views/user/Login")
        },
        {
          path: "/user/register",
          name: "register",
          component: () =>
            import(/* webpackChunkName: "user"* /"./views/user/Register")
        }
      ]
    },
    {
      path: "/",
      component: () =>
        import(/* webpackChunkName: "layout"* /"./layouts/BasicLayout"),
      children: [
        // dashboard
        {
          path: "/",
          redirect: "/dashboard/analysis"
        },
        {
          path: "/dashboard",
          name: "dashboard",
          meta: { icon: "dashboard", title: "Dashboard" },
          component: { render: h => h("router-view") },
          children: [
            {
              path: "/dashboard/analysis",
              name: "analysis",
              meta: { title: "Analysis page" },
              component: () =>
                import(
                  /* webpackChunkName: "dashboard"* /"./views/Dashboard/Analysis"
                )
            }
          ]
        },
        // form
        {
          path: "/form",
          name: "form",
          component: { render: h => h("router-view") },
          meta: { icon: "form", title: "The form" },
          children: [
            {
              path: "/form/basic-form",
              name: "basicform",
              meta: { title: "Basic form" },
              component: () =>
                import(/* webpackChunkName: "form"* /"./views/Forms/BasicForm")
            },
            {
              path: "/form/step-form",
              name: "stepform",
              hideChildrenInMenu: true,
              meta: { title: "Distributed form" },
              component: () =>
                import(
                  /* webpackChunkName: "form"* /"./views/Forms/StepForm/Index"
                ),
              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"
                    )
                },
                {
                  path: "/form/step-form/confirm",
                  name: "confirm",
                  component: () =>
                    import(
                      /* webpackChunkName: "form"* /"./views/Forms/StepForm/Step2"
                    )
                },
                {
                  path: "/form/step-form/result",
                  name: "result",
                  component: () =>
                    import(
                      /* webpackChunkName: "form"* /"./views/Forms/StepForm/Step3"
                    )
                }
              ]
            }
          ]
        }
      ]
    },
    {
      path: "*",
      name: "404",
      hideInMenu: true,
      component: () =>
        import(/* webpackChunkName: "exception"* /"@/views/Exception/404")}}); router.beforeEach((to, from, next) => { NProgress.start(); next(); }); router.afterEach(() => { NProgress.done(); });export default router;

Copy the code
  • Dynamic loadrouterRoute configuration, process parameters according to the above configuration, generate menu

SiderMenu

<template>
  <div style="width: 256px">
    <a-menu
      :selectedKeys="selectedKeys"
      :openKeys.sync="openKeys"
      mode="inline"
      :theme="theme"
    >
      <template v-for="item in menuData">
        <a-menu-item
          v-if=! "" item.children"
          :key="item.path"
          @click="() = >$router.push({ path: item.path, query: $route.query })"
        >
          <a-icon v-if="item.meta.icon" :type="item.meta.icon" />
          <span>{{ item.meta.title }}</span>
        </a-menu-item>
        <sub-menu v-else :menu-info="item" :key="item.path" />
      </template>
    </a-menu>
  </div>
</template>

<script>
/*
 * recommend SubMenu.vue https://github.com/vueComponent/ant-design-vue/blob/master/components/menu/demo/SubMenu.vue
 * SubMenu1.vue https://github.com/vueComponent/ant-design-vue/blob/master/components/menu/demo/SubMenu1.vue
 * */
import SubMenu from "./SubMenu";
export default {
  props: {
    theme: {
      type: String,
      default: "dark"
    },
    collapsed: {
      type: Boolean,
      default: false
    }
  },
  components: {
    "sub-menu": SubMenu
  },
  watch: {
    "$route.path": function(val) {
      this.selectedKeys = this.selectedKeysMap[val];
      this.openKeys = this.collapsed ? [] : this.openKeysMap[val];
    },
    collapsed(val) {
      if (val) {
        this.cacheOpenKeys = this.openKeys;
        this.openKeys = [];
      } else{ this.openKeys = this.cacheOpenKeys; }}},data() {
    this.selectedKeysMap = {};
    this.openKeysMap = {};
    const menuData = this.getMenuData(this.$router.options.routes);
    return {
      menuData,
      selectedKeys: this.selectedKeysMap[this.$route.path],
      openKeys: this.collapsed ? [] : this.openKeysMap[this.$route.path]
    };
  },
  methods: {
    toggleCollapsed() { this.collapsed = ! this.collapsed; }, getMenuData(routes = [], parentKeys = [], selectedKey) { const menuData = [];for (let item of routes) {
        if(item.name && ! item.hideInMenu) { this.openKeysMap[item.path] = parentKeys; this.selectedKeysMap[item.path] = [selectedKey || item.path]; const newItem = { ... item }; delete newItem.children;if(item.children && ! item.hideChildrenInMenu) { 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.hideInMenu && ! item.hideChildrenInMenu && item.children ) { menuData.push( ... this.getMenuData(item.children, [...parentKeys, item.path]) ); }}returnmenuData; }}}; </script>Copy the code

SubMenu.vue

<template functional>
  <a-sub-menu :key="props.menuInfo.path">
    <span slot="title">
      <a-icon
        v-if="props.menuInfo.meta.icon"
        :type="props.menuInfo.meta.icon"
      /><span>{{ props.menuInfo.meta.title }}</span>
    </span>
    <template v-for="item in props.menuInfo.children">
      <a-menu-item
        v-if=! "" item.children"
        :key="item.path"
        @click="
          () =>
            parent.$router.push({ path: item.path, query: parent.$route.query })
        "
      >
        <a-icon v-if="item.meta.icon" :type="item.meta.icon" />
        <span>{{ item.meta.title }}</span>
      </a-menu-item>
      <sub-menu v-else :key="item.meta.path" :menu-info="item" />
    </template>
  </a-sub-menu>
</template>
<script>
export default {
  props: ["menuInfo"]}; </script>Copy the code

How do I use route management user rights

– Create auth.js in SRC \utils to impersonate current user permissions and authentication permissions

auth.js

const currentAuth = ["admin"];
export { currentAuth };

export function getCurrentAuthority() {
  return currentAuth;
}

export function check(authority) {
  const current = getCurrentAuthority();
  return current.some(item => authority.includes(item));
}

export function isLogin() {
  const current = getCurrentAuthority();
  returncurrent && current[0] ! = ="guest";
}

Copy the code
  • inrouter.js metaTo set user permissions

router.js

import Vue from "vue";
import Router from "vue-router";
import findLast from "lodash/findLast";
import { notification } from "ant-design-vue";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
import { check, isLogin } from "./utils/auth";

Vue.use(Router);

const router = new Router({
  mode: "history",
  base: process.env.BASE_URL,
  routes: [
    {
      path: "/user",
      hideInMenu: true,
      component: () =>
        import(/* webpackChunkName: "layout"* /"./layouts/UserLayout"),
      children: [
        {
          path: "/user",
          redirect: "/user/login"
        },
        {
          path: "/user/login",
          name: "login",
          component: () =>
            import(/* webpackChunkName: "user"* /"./views/user/Login")
        },
        {
          path: "/user/register",
          name: "register",
          component: () =>
            import(/* webpackChunkName: "user"* /"./views/user/Register")
        }
      ]
    },
    {
      path: "/",
      meta: { authority: ["user"."admin"] },
      component: () =>
        import(/* webpackChunkName: "layout"* /"./layouts/BasicLayout"),
      children: [
        // dashboard
        {
          path: "/",
          redirect: "/dashboard/analysis"
        },
        {
          path: "/dashboard",
          name: "dashboard",
          meta: { icon: "dashboard", title: "Dashboard" },
          component: { render: h => h("router-view") },
          children: [
            {
              path: "/dashboard/analysis",
              name: "analysis",
              meta: { title: "Analysis page" },
              component: () =>
                import(
                  /* webpackChunkName: "dashboard"* /"./views/Dashboard/Analysis"
                )
            }
          ]
        },
        // form
        {
          path: "/form",
          name: "form",
          component: { render: h => h("router-view") },
          meta: { icon: "form", title: "The form", authority: ["admin"] },
          children: [
            {
              path: "/form/basic-form",
              name: "basicform",
              meta: { title: "Basic form" },
              component: () =>
                import(/* webpackChunkName: "form"* /"./views/Forms/BasicForm")
            },
            {
              path: "/form/step-form",
              name: "stepform",
              hideChildrenInMenu: true,
              meta: { title: "Distributed form" },
              component: () =>
                import(
                  /* webpackChunkName: "form"* /"./views/Forms/StepForm/Index"
                ),
              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"
                    )
                },
                {
                  path: "/form/step-form/confirm",
                  name: "confirm",
                  component: () =>
                    import(
                      /* webpackChunkName: "form"* /"./views/Forms/StepForm/Step2"
                    )
                },
                {
                  path: "/form/step-form/result",
                  name: "result",
                  component: () =>
                    import(
                      /* webpackChunkName: "form"* /"./views/Forms/StepForm/Step3"
                    )
                }
              ]
            }
          ]
        }
      ]
    },
    {
      path: "/ 403",
      name: "403",
      hideInMenu: true,
      component: () =>
        import(/* webpackChunkName: "exception"* /"@/views/Exception/403")
    },
    {
      path: "*",
      name: "404",
      hideInMenu: true,
      component: () =>
        import(/* webpackChunkName: "exception"* /"@/views/Exception/404")}}); router.beforeEach((to, from, next) => {if(to.path ! == from.path) { NProgress.start(); } 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: "You do not have access, please contact the administrator."
      });
      next({
        path: "/ 403"
      });
    }
    NProgress.done();
  }
  next();
});

router.afterEach(() => {
  NProgress.done();
});

export default router;

Copy the code

– Filter out routes that do not have permission in the menu

SiderMenu.vue

<template>
  <div style="width: 256px">
    <a-menu
      :selectedKeys="selectedKeys"
      :openKeys.sync="openKeys"
      mode="inline"
      :theme="theme"
    >
      <template v-for="item in menuData">
        <a-menu-item
          v-if=! "" item.children"
          :key="item.path"
          @click="() = >$router.push({ path: item.path, query: $route.query })"
        >
          <a-icon v-if="item.meta.icon" :type="item.meta.icon" />
          <span>{{ item.meta.title }}</span>
        </a-menu-item>
        <sub-menu v-else :menu-info="item" :key="item.path" />
      </template>
    </a-menu>
  </div>
</template>

<script>
/*
 * recommend SubMenu.vue https://github.com/vueComponent/ant-design-vue/blob/master/components/menu/demo/SubMenu.vue
 * SubMenu1.vue https://github.com/vueComponent/ant-design-vue/blob/master/components/menu/demo/SubMenu1.vue
 * */
import SubMenu from "./SubMenu";
import { check } from ".. /utils/auth";
export default {
  props: {
    theme: {
      type: String,
      default: "dark"
    },
    collapsed: {
      type: Boolean,
      default: false
    }
  },
  components: {
    "sub-menu": SubMenu
  },
  watch: {
    "$route.path": function(val) {
      this.selectedKeys = this.selectedKeysMap[val];
      this.openKeys = this.collapsed ? [] : this.openKeysMap[val];
    },
    collapsed(val) {
      if (val) {
        this.cacheOpenKeys = this.openKeys;
        this.openKeys = [];
      } else{ this.openKeys = this.cacheOpenKeys; }}},data() {
    this.selectedKeysMap = {};
    this.openKeysMap = {};
    const menuData = this.getMenuData(this.$router.options.routes);
    return {
      menuData,
      selectedKeys: this.selectedKeysMap[this.$route.path],
      openKeys: this.collapsed ? [] : this.openKeysMap[this.$route.path]
    };
  },
  methods: {
    toggleCollapsed() { this.collapsed = ! this.collapsed; }, getMenuData(routes = [], parentKeys = [], selectedKey) { const menuData = [];for (let item of routes) {
        if(item.meta && item.meta.authority && ! check(item.meta.authority)) {continue;
        }
        if(item.name && ! item.hideInMenu) { this.openKeysMap[item.path] = parentKeys; this.selectedKeysMap[item.path] = [selectedKey || item.path]; const newItem = { ... item }; delete newItem.children;if(item.children && ! item.hideChildrenInMenu) { 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.hideInMenu && ! item.hideChildrenInMenu && item.children ) { menuData.push( ... this.getMenuData(item.children, [...parentKeys, item.path]) ); }}returnmenuData; }}}; </script>Copy the code

More refined permission design (permission components, permission directives)

In order to control the function of adding, deleting, modifying, and checking table, we need to control the function of a single button

Component permission control

  • incomponentsCreate a permission component inAuthorized.vuecomponent

Authorized.vue

<script>
import { check } from ".. /utils/auth";
exportDefault {// function:true,
  props: {
    authority: {
      type: Array,
      required: true
    }
  },
  render(h, context) {
    const { props, scopedSlots } = context;
    returncheck(props.authority) ? scopedSlots.default() : null; }}; </script>Copy the code
  • Permission components are used in individual components, registered as global components, and unified import files in on-demand componentssrc/core/lazy_useIn themain.jsThe introduction ofimport "./core/lazy_use";

lazy_use.js

import Vue from "vue";
import Authorized from "@/components/Authorized";

Vue.component("Authorized", Authorized);
Copy the code
  • insrc/layouts/BasicLayout.vueImporting permission Components
<template>
  <div :class="[`nav-theme-${navTheme}`, `nav-layout-${navLayout}`]. "">
    <a-layout id="components-layout-demo-side" style="min-height: 100vh">
      <a-layout-sider
        v-if="navLayout === 'left'"
        :theme="navTheme"
        :trigger="null"
        collapsible
        v-model="collapsed"
        width="256px"
      >
        <div class="logo">
          <h1>Ant Design Pro</h1>
        </div>
        <SiderMenu :theme="navTheme" :collapsed="collapsed" />
      </a-layout-sider>
      <a-layout>
        <a-layout-header style="background: #fff; padding: 0">
          <a-icon
            class="trigger"
            :type="collapsed ? 'menu-unfold' : 'menu-fold'"
            @click="collapsed = ! collapsed"
          ></a-icon>
          <Header />
        </a-layout-header>
        <a-layout-content style="margin: 0 16px">
          <router-view></router-view>
        </a-layout-content>
        <a-layout-footer style="text-align: center">
          <Footer />
        </a-layout-footer>
      </a-layout>
    </a-layout>
    <Authorized :authority="['admin']">
      <SettingDrawer />
    </Authorized>
  </div>
</template>
Copy the code
  • If the current user is admin, the dynamic layout button is displayed

Directive permission

  • Create a new file to store the instructionsrc/directives/auth.js

auth.js

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 };
Copy the code
<a-icon
        v-auth="['admin']"
        class="trigger"
></a-icon>
Copy the code

How do I use Echarts, Antv, and other third-party libraries in a component

  • Enter the command to install Echartsnpm i echartsIn thesrc/componentsnewChart.vuecomponent

Chart.vue

<template>
  <div ref="chartDom"></div>
</template>

<script>
import echarts from "echarts/lib/echarts";
import "echarts/lib/chart/bar";
import "echarts/lib/component/title";

import debounce from "lodash/debounce";
import { addListener, removeListener } from "resize-detector";
export default {
  props: {
    option: {
      type: Object, default: () => {} } }, watch: { option(val) { this.chart.setOption(val); }},created() { this.resize = debounce(this.resize, 300); / / stabilization},mounted() {
    this.renderChart();
    addListener(this.$refs.chartDom, this.resize);
  },
  beforeDestroy() {
    removeListener(this.$refs.chartDom, this.resize);
    this.chart.dispose();
    this.chart = null;
  },
  methods: {
    resize() {
      console.log("resize");
      this.chart.resize();
    },
    renderChart() {// Based on the prepared DOM, initialize the echarts instance this.chart = echarts.init(this).$refs.chartDom); this.chart.setOption(this.option); }}}; </script> <style></style>Copy the code
  • src\views\Dashboard\Analysis.vueThe introduction ofChart.vuecomponent

Parent-child transmission passes the chartOption to the component

Add timer and introduce random function, data changes every 3 seconds

After data changes, a new value is assigned to the chartOption. If the component monitors data changes, the component is updated without deep monitoring

BeforeDestroy Destroys the timer

Analysis.vue

<template>
  <div>
    <Chart :option="chartOption" style="height: 400px" />
  </div>
</template>

<script>
import random from "lodash/random";
import Chart from ".. /.. /components/Chart";
export default {
  data() {
    return {
      chartOption: {
        title: {
          text: "Getting started with ECharts"
        },
        tooltip: {},
        xAxis: {
          data: ["Shirt"."Cardigan"."Chiffon shirt."."Pants"."High heels"."Socks"]
        },
        yAxis: {},
        series: [
          {
            name: "Sales".type: "bar",
            data: [5, 20, 36, 10, 10, 20]
          }
        ]
      }
    };
  },
  mounted() {
    this.interval = setInterval(() => { this.chartOption.series[0].data = this.chartOption.series[0].data.map( () => random(100) ); this.chartOption = { ... this.chartOption }; }, 3000); },beforeDestroy() {
    clearInterval(this.interval);
  },
  components: {
    Chart
  }
};
</script>

<style></style>

Copy the code

How to develop effectively with Mock data

  • The installationaxios, due to the introduction ofaxios, add the AXIos request

Analysis.vue

<template>
  <div>
    <Chart :option="chartOption" style="height: 400px" />
  </div>
</template>

<script>
import axios from "axios";
import Chart from ".. /.. /components/Chart";
export default {
  data() {
    return {
      chartOption: {}
    };
  },
  mounted() {
    this.getChartData();
    this.interval = setInterval(() => {
      this.getChartData();
    }, 3000);
  },
  methods: {
    getChartData() {
      axios
        .get("/api/dashboard/chart", { params: { ID: 12345 } })
        .then(response => {
          this.chartOption = {
            title: {
              text: "Getting started with ECharts"
            },
            tooltip: {},
            xAxis: {
              data: ["Shirt"."Cardigan"."Chiffon shirt."."Pants"."High heels"."Socks"]
            },
            yAxis: {},
            series: [
              {
                name: "Sales".type: "bar", data: response.data } ] }; }); }},beforeDestroy() {
    clearInterval(this.interval);
  },
  components: {
    Chart
  }
};
</script>

<style></style>

Copy the code
  • mock\dashboard_chart.jsWriting interface files

dashboard_chart.js

function chart(method) {
  let res = null;
  switch (method) {
    case "GET":
      res = [20, 40, 79, 10, 30, 48];
      break;
    default:
      res = null;
  }
  return res;
}

module.exports = chart;

Copy the code
  • Add the agent

vue.config.js

module.exports = {
  lintOnSave: false,
  css: {
    loaderOptions: {
      less: {
        javascriptEnabled: true
      }
    }
  },
  devServer: {
    port: 8000,
    proxy: {
      "/api": {
        target: "http://localhost:3000",
        bypass: function(req, res) {
          if (req.headers.accept.indexOf("html") !== -1) {
            console.log("Skipping proxy for browser request.");
            return "/index.html";
          } else {
            console.log(req.path);
            const name = req.path
              .split("/api/")[1]
              .split("/")
              .join("_");
            const mock = require(`./mock/${name}`);
            const result = mock(req.method);
            delete require.cache[require.resolve(`./mock/${name}`)];
            console.dir(result);
            returnres.send(result); }}}}}};Copy the code
  • mock.js

All of the above are mock requests written by yourself. Next, use a third party mock.js

Install mock.js umi-mock-middleware,umi-mock-middleware will automatically refresh when modifying mock data

First register the middleware and modify vue.config.js. If you do not create one in the project root folder, the key code is as follows

const { createMockMiddleware } = require("umi-mock-middleware");
  devServer: {
    port: 8000,
    open: true// Before:function(app) {
      // var bodyParser = require("body-parser");
      // app.use(bodyParser.json());
      if(process.env.MOCK ! = ="none") {
        app.use(createMockMiddleware());
      }
    },
    proxy: {
      "/api": {
        target: "http://localhost:3000"}}}Copy the code

/mock/chart.js

module.exports = { 
  `GET /api/dashboard/chart`: (req, res) {
    let result = null;
    switch (req.method) {
      case "GET":
        result = [100, 40, 78, 10, 30, 50];
        break; default: result = null; } // return your mock data. Such as: res. Json (result); }};Copy the code

How do I interact with the server

After the development and back-end joint adjustment

  • You need a simple way to distinguish mock environments from syncing environments in environment variablespackage.jsonThe new command

Windows requires a cross-env package to use

Modify vue.config.js configuration

package.json

// mac
"scripts": {
    "serve": "vue-cli-service serve"."serve:no-mock":"MOCK=none vue-cli-service serve"
    "build": "vue-cli-service build"."lint": "vue-cli-service lint"."test:unit": "vue-cli-service test:unit"
  }
  //window
  "scripts": {
    "serve": "vue-cli-service serve"."serve:no-mock":"cross-env MOCK=none vue-cli-service serve"
    "build": "vue-cli-service build"."lint": "vue-cli-service lint"."test:unit": "vue-cli-service test:unit"
  }
Copy the code

vue.config.js

const { createMockMiddleware } = require("umi-mock-middleware");

module.exports = {
  css: {
    loaderOptions: {
      less: {
        javascriptEnabled: true
      }
    }
  },
  devServer: {
    open: true// Before:function(app) {
      // var bodyParser = require("body-parser");
      // app.use(bodyParser.json());
      if(process.env.MOCK ! = ="none") {
        app.use(createMockMiddleware());
      }
    },
    proxy: {
      "/api": {
        target: "http://localhost:3000"}}}};Copy the code

Encapsulation axios

Normally, no direct AXIOS request will do a secondary wrapper

  • insrc/utils/request.jsSecondary encapsulation

request.js

import axios from "axios";
import { notification } from "ant-design-vue";

function request(options) {
  return axios(options)
    .then(res => {
      return res;
    })
    .catch(error => {
      const {
        response: { status, statusText }
      } = error;
      notification.error({
        // eslint-disable-next-line no-unused-vars
        message:status,
        description: statusText
      });
      return Promise.reject(error);
    });
}

export default request;

Copy the code
  • insrc\views\Dashboard\Analysis.vueTo replace the wrapped AXIos with the previous axios
<template>
  <div>
    <Chart :option="chartOption" style="height: 400px" />
  </div>
</template>

<script>
import request from ".. /.. /utils/request";
import Chart from ".. /.. /components/Chart";
export default {
  data() {
    return {
      chartOption: {}
    };
  },
  mounted() {
    this.getChartData();
    this.interval = setInterval(() => {
      this.getChartData();
    }, 3000);
  },
  methods: {
    getChartData() {
      request({
        url: "/api/dashboard/chart",
        method: "get",
        params: { ID: 12345 }
      }).then(response => {
        this.chartOption = {
          title: {
            text: "Getting started with ECharts"
          },
          tooltip: {},
          xAxis: {
            data: ["Shirt"."Cardigan"."Chiffon shirt."."Pants"."High heels"."Socks"]
          },
          yAxis: {},
          series: [
            {
              name: "Sales".type: "bar", data: response.data } ] }; }); }},beforeDestroy() {
    clearInterval(this.interval);
  },
  components: {
    Chart
  }
};
</script>

<style></style>

Copy the code
  • Error prompt to add some styles inutils.jsFile can not write a single file component, you can use the render function, render function is more cumbersome, another kind of JSX; Now you need to configure JSX into your project

Install Babel support JSX plugin @vue/babel-preset- JSX @vue/babel-helper-vue-jsx-merge-props

Configure the Babel

babel.config.js

module.exports = {
  presets: [
    "@vue/app"["@vue/babel-preset-jsx",
      {
        injectH: false
      }
    ]
  ],
  plugins: [
    [
      "import",
      { libraryName: "ant-design-vue", libraryDirectory: "es", style: true }
    ] // `style: true'less file will be loaded]};Copy the code

request.js

import axios from "axios";
import { notification } from "ant-design-vue";

function request(options) {
  return axios(options)
    .then(res => {
      returnres; }) .catch(error => { const { response: { status, statusText } } = error; Notification. error({// eslint-disable-next-line no-unused-vars message: h => (<div> request error <span style="color: red">{status}</span> : {options.url}
          </div>
        ),
        description: statusText
      });
      return Promise.reject(error);
    });
}

export default request;
Copy the code

Create a normal form

Find a basic form belt layout on the ANTD Vue website

  • Copy code tosrc/view/Forms/BasicForm.vue, and a basic form is complete

BasicForm.vue

<template>
  <div>
    <a-form :layout="formLayout">
      <a-form-item
        label="Form Layout"
        :label-col="formItemLayout.labelCol"
        :wrapper-col="formItemLayout.wrapperCol"
      >
        <a-radio-group
          default-value="horizontal"
          @change="handleFormLayoutChange"
        >
          <a-radio-button value="horizontal">
            Horizontal
          </a-radio-button>
          <a-radio-button value="vertical">
            Vertical
          </a-radio-button>
          <a-radio-button value="inline">
            Inline
          </a-radio-button>
        </a-radio-group>
      </a-form-item>
      <a-form-item
        label="Field A"
        :label-col="formItemLayout.labelCol"
        :wrapper-col="formItemLayout.wrapperCol"
      >
        <a-input placeholder="input placeholder" />
      </a-form-item>
      <a-form-item
        label="Field B"
        :label-col="formItemLayout.labelCol"
        :wrapper-col="formItemLayout.wrapperCol"
      >
        <a-input placeholder="input placeholder" />
      </a-form-item>
      <a-form-item
        :wrapper-col="buttonItemLayout.wrapperCol"
      >
        <a-button type="primary">
          Submit
        </a-button>
      </a-form-item>
    </a-form>
  </div>
</template>

<script>
export default {
  data () {
    return {
      formLayout: 'horizontal'}; }, computed: {formItemLayout () {
      const { formLayout } = this;
      return formLayout === 'horizontal' ? {
        labelCol: { span: 4 },
        wrapperCol: { span: 14 },
      } : {};
    },
    buttonItemLayout () {
      const { formLayout } = this;
      return formLayout === 'horizontal'? { wrapperCol: { span: 14, offset: 4 }, } : {}; }, }, methods: { handleFormLayoutChange (e) { this.formLayout = e.target.value; ,}}}; </script>Copy the code
  • Form validation, AntD Vue provides properties such as validateStatus Help hasFeedback. You can define the timing and content of validation yourself without using form. create and getFieldDecorator.

ValidateStatus: Indicates the validation state. The options are AS follows: SUCCESS, Warning, Error, or Validating. HasFeedback: Used to add feedback ICONS to input boxes. Help: Sets the verification copy.

BasicForm.vue

. <a-form-item label="Field A"
        :label-col="formItemLayout.labelCol"
        :wrapper-col="formItemLayout.wrapperCol"
        validateStatus="error"
        help="Must be longer than 5 characters!"
      >
        <a-input v-model="FieldA" placeholder="input placeholder" />
</a-form-item>
...
Copy the code
  • Dynamic input validation, as long as thevalidateStatusandhelpChange it to dynamic
<template>
  <div>
    <a-form :layout="formLayout">
      <a-form-item
        label="Form Layout"
        :label-col="formItemLayout.labelCol"
        :wrapper-col="formItemLayout.wrapperCol"
      >
        <a-radio-group
          default-value="horizontal"
          @change="handleFormLayoutChange"
        >
          <a-radio-button value="horizontal">
            Horizontal
          </a-radio-button>
          <a-radio-button value="vertical">
            Vertical
          </a-radio-button>
          <a-radio-button value="inline">
            Inline
          </a-radio-button>
        </a-radio-group>
      </a-form-item>
      <a-form-item
        label="Field A"
        :label-col="formItemLayout.labelCol"
        :wrapper-col="formItemLayout.wrapperCol"
        :validateStatus="FieldAStatus"
        :help="FieldAHelp"
      >
        <a-input v-model="FieldA" placeholder="input placeholder" />
      </a-form-item>
      <a-form-item
        label="Field B"
        :label-col="formItemLayout.labelCol"
        :wrapper-col="formItemLayout.wrapperCol"
      >
        <a-input v-model="FieldB" placeholder="input placeholder" />
      </a-form-item>
      <a-form-item :wrapper-col="buttonItemLayout.wrapperCol">
        <a-button type="primary" @click="handleSubmit">
          Submit
        </a-button>
      </a-form-item>
    </a-form>
  </div>
</template>

<script>
export default {
  data() {
    return {
      formLayout: "horizontal",
      FieldA: "",
      FieldB: "",
      FieldAStatus: "",
      FieldAHelp: ""
    };
  },
  watch: {
    FieldA(val) {
      if (val.length <= 5) {
        this.FieldAStatus = "error";
        this.FieldAHelp = "Must be longer than 5 characters!";
      } else {
        this.FieldAStatus = "";
        this.FieldAHelp = "";
      }
    }
  },
  computed: {
    formItemLayout() {
      const { formLayout } = this;
      return formLayout === "horizontal"
        ? {
            labelCol: { span: 4 },
            wrapperCol: { span: 14 }
          }
        : {};
    },
    buttonItemLayout() {
      const { formLayout } = this;
      return formLayout === "horizontal"
        ? {
            wrapperCol: { span: 14, offset: 4 }
          }
        : {};
    }
  },
  methods: {
    handleFormLayoutChange(e) {
      this.formLayout = e.target.value;
    },
    handleSubmit() {
      if (this.FieldA.length <= 5) {
        this.FieldAStatus = "error";
        this.FieldAHelp = "Must be longer than 5 characters!";
      } else{ console.dir({ FieldA: this.FieldA, FieldB: this.FieldB }); }}}}; </script>Copy the code

Initial data, automatic verification, dynamic assignment

  • this.form = this.$form.createForm(this); Registration form
  • v-decoratorProperty sets the component name, initialization valueinitialValue, form verificationrules
  • This.form.setfieldsvalue ({fieldA: “Hello world”}) dynamically assigned
<template>
  <div>
    <a-form :layout="formLayout" :form="form">
      <a-form-item
        label="Form Layout"
        :label-col="formItemLayout.labelCol"
        :wrapper-col="formItemLayout.wrapperCol"
      >
        <a-radio-group
          default-value="horizontal"
          @change="handleFormLayoutChange"
        >
          <a-radio-button value="horizontal">
            Horizontal
          </a-radio-button>
          <a-radio-button value="vertical">
            Vertical
          </a-radio-button>
          <a-radio-button value="inline">
            Inline
          </a-radio-button>
        </a-radio-group>
      </a-form-item>
      <a-form-item
        label="Field A"
        :label-col="formItemLayout.labelCol"
        :wrapper-col="formItemLayout.wrapperCol"
      >
        <a-input
          v-decorator="['fieldA', {initialValue: fieldA, rules: [{required: true, min: 6, message: 'Must be more than 5 characters'}]}]"
          placeholder="input placeholder"
        />
      </a-form-item>
      <a-form-item
        label="Field B"
        :label-col="formItemLayout.labelCol"
        :wrapper-col="formItemLayout.wrapperCol"
      >
        <a-input v-decorator="['fieldB']" placeholder="input placeholder" />
      </a-form-item>
      <a-form-item :wrapper-col="buttonItemLayout.wrapperCol">
        <a-button type="primary" @click="handleSubmit">
          Submit
        </a-button>
      </a-form-item>
    </a-form>
  </div>
</template>

<script>
export default {
  data() {
    this.form = this.$form.createForm(this);
    return {
      formLayout: "horizontal",
      fieldA: "hello",
      fieldB: ""
    };
  },
  mounted() {
    setTimeout(() => {
      this.form.setFieldsValue({ fieldA: "hello world" });
    }, 3000);
  },
  computed: {
    formItemLayout() {
      const { formLayout } = this;
      return formLayout === "horizontal"
        ? {
            labelCol: { span: 4 },
            wrapperCol: { span: 14 }
          }
        : {};
    },
    buttonItemLayout() {
      const { formLayout } = this;
      return formLayout === "horizontal"
        ? {
            wrapperCol: { span: 14, offset: 4 }
          }
        : {};
    }
  },
  methods: {
    handleFormLayoutChange(e) {
      this.formLayout = e.target.value;
    },
    handleSubmit() {
      this.form.validateFields((err, values) => {
        if(! err) { console.log(values); Object.assign(this, values); }}); }}}; </script>Copy the code

Create a distribution form

Combine VUEX and View-Router to make a complex form. The first step is to save the information of payment account and receiving account; the second step is to request service and save the information of payment account and password and receiving account; the third step is to prompt operation information

  • Create a formstore.src\store\modules\form.js

Step state Saves the information of paying account and receiving account

Submit the submitStepForm Action form to request the service and save the payment account and password information as well as the payment account letter

SaveStepFormData mutations save the information of the first payment account and receiving account

form.js

import router from ".. /.. /router";
import request from ".. /.. /utils/request";

const state = {
  step: {
    payAccount: "123456",
    receiverAccount: {
      type: "alipay",
      number: ""}}}; const actions = { async submitStepForm({ commit }, { payload }) { await request({ url:"/api/forms",
      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
};

Copy the code
  • Store export,src/store/index.js

index.js

import Vue from "vue";
import Vuex from "vuex";
import form from "./modules/form";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {},
  modules: {
    form
  }
});

Copy the code
  • inmain.jsThe introduction of
. import store from"./store/index.js"; .Copy the code
  • Writing the first stepsrc\views\Forms\StepForm\Step1.vue

Step1.vue

<template>
  <div>
    <a-form layout="horizontal" :form="form">
      <a-form-item
        label="Payment account"
        :label-col="formItemLayout.labelCol"
        :wrapper-col="formItemLayout.wrapperCol"
      >
        <a-input
          v-decorator="['payAccount', {initialValue: step.payAccount, rules: [{required: true, message: 'Please enter payment account'}]}]"
          placeholder="Please enter the payment account number"
        />
      </a-form-item>
      <a-form-item
        label="Receiving Account"
        :label-col="formItemLayout.labelCol"
        :wrapper-col="formItemLayout.wrapperCol"
      >
        <a-input
          v-decorator="['receiverAccount', {initialValue: step. ReceiverAccount, rules: [{required: true, message: 'Please input receiverAccount account'}]}]"
          placeholder="Please enter your account number"
      </a-form-item>
      <a-form-item>
        <a-button type="primary" @click="handleSubmit"- button > next < / a > < / a - form - item > < / a - form > < / div > < / template > < script >export default {
  data() {
    this.form = this.$form.createForm(this);
    return {
      formItemLayout: {
        labelCol: { span: 4 },
        wrapperCol: { span: 14 }
      }
    };
  },
  computed: {
    step() {
      return this.$store.state.form.step;
    }
  },
  methods: {
    handleSubmit() {
      const { form, $router.$store } = this;
      form.validateFields((err, values) => {
        if(! err) {$store.commit({
            type: "form/saveStepFormData",
            payload: values
          });
          $router.push("/form/step-form/confirm"); }}); }}}; </script> <style></style>Copy the code
  • The second stepsrc\views\Forms\StepForm\Step2.vue

Step2.vue

<template>
  <div>
    <a-form layout="horizontal" :form="form">
      <a-form-item
        label="Payment account"
        :label-col="formItemLayout.labelCol"
        :wrapper-col="formItemLayout.wrapperCol"
      >
        {{ step.payAccount }}
      </a-form-item>
      <a-form-item
        label="Password"
        :label-col="formItemLayout.labelCol"
        :wrapper-col="formItemLayout.wrapperCol"
      >
        <a-input
          v-decorator="['password', {initialValue: step.payAccount, rules: [{required: true, message: 'Please enter password'}]}]"
          type="password"
          placeholder="Please enter your payment password"
        />
      </a-form-item>
      <a-form-item>
        <a-button type="primary" @click="handleSubmit"< p style= "max-width: 100%; clear: both; min-height: 1em"margin-left: 8px" @click="onPrev"< / a > on step - button > < / a - form - item > < / a - form > < / div > < / template > < script >export default {
  data() {
    this.form = this.$form.createForm(this);
    return {
      formItemLayout: {
        labelCol: { span: 4 },
        wrapperCol: { span: 14 }
      }
    };
  },
  computed: {
    step() {
      return this.$store.state.form.step;
    }
  },
  methods: {
    handleSubmit() {
      const { form, $store, step } = this;
      form.validateFields((err, values) => {
        if(! err) {$store.dispatch({
            type: "form/submitStepForm", payload: { ... step, ... values } }); }}); },onPrev() {
      this.$router.push("/form/step-form/info"); }}}; </script> <style></style>Copy the code
  • The third stepsrc\views\Forms\StepForm\Step3.vue

Step3.vue

<template> <div> operation is successful, it is expected to arrive in two hours </div> </template> <script>export default {};
</script>

<style></style>

Copy the code

Encapsulates a form entry for automatic validation

Custom or third-party Form controls can also be used with the Form component. As long as the component follows the following conventions:

Provides the controlled property value or another property with the same name as the value of valuePropName- argument).

Provides an event with the same name as the value of the onChange event or trigger-argument.

Cannot be a functional component.

  • Create a form item componentsrc\components\ReceiverAccount.vue

ReceiverAccount.vue

<template>
  <a-input-group compact>
    <a-select v-model="type" style="width: 130px" @change="handleTypeChange">
      <a-select-option value="alipay"> </a-select-option> <a-select-option value="bank"> </a-select-option> </a-select> < A-input style="width: calc(100% - 130px)"
      v-model="number"
      @change="handleNumberChange"
    />
  </a-input-group>
</template>

<script>
export default {
  props: {
    value: {
      type: Object } }, watch: { value(val) { Object.assign(this, val); }},data() {
    const { type, number } = this.value || {};
    return {
      type: type || "alipay",
      number: number || ""
    };
  },
  methods: {
    handleTypeChange(val) {
      this.$emit("change", { ...this.value, type: val });
    },
    handleNumberChange(e) {
      this.$emit("change", {... this.value, number: e.target.value }); }}}; </script> <style></style>Copy the code
  • Introduce components to add validation

Step1.vue

... "a - form - the item label ="Receiving Account"
        :label-col="formItemLayout.labelCol"
        :wrapper-col="formItemLayout.wrapperCol"
      >
        <ReceiverAccount
          v-decorator=ReceiverAccount, rules: [{required: true, message: 'Please input your account number ', validator: (rule, value, callback) => { if (value && value.number) { callback(); } else { callback(false); } } } ] } ]"/ > < / a - form - item >...Copy the code

Manage ICONS used in the system

Iconfont. Cn use

  • Search icon404Find your favorite icon and add it to your shopping cart
  • View online links can also be downloaded locally, usingsymbolSVG mode, the other two are fonts

  • Introduction to the project

src\core\lazy_use.js

lazy_use.js

After 1.2.0, we provided a createFromIconfontCN method for developers to call self-managed ICONS on iconfont.cn. const IconFont = Icon.createFromIconfontCN({ scriptUrl:'//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js'}); Vue.component("IconFont", IconFont);
Copy the code
  • use
<IconFont type="icon-icon-404" style="font-size:100px"></IconFont>
Copy the code

Self-designed logo

  • Self-designed logo can upload pictures toiconfont.cnProject, can also be placed in the VUE project reference
<template>
 <div><img :src="logo"></div>
</template>
import logo from "@/assets/logo.svg";
<script>
    data() {
        return {
         logo
        };
      }
</script>
Copy the code
  • SVG images can also be introduced as components

First install the package vuE-SVG-loader

Configure vue.config.js to use

<template>
 <div><logo></logo></div>
</template>
import Logo from "@/assets/logo.svg";
<script>
 components: {
    Logo
  }
</script>
Copy the code

vue.config.js

const { createMockMiddleware } = require("umi-mock-middleware");

module.exports = {
  lintOnSave: false,
  css: {
    loaderOptions: {
      less: {
        javascriptEnabled: true
      }
    }
  },
  chainWebpack: config => {
    const svgRule = config.module.rule("svg"); // Clear all existing loaders. // If you do not do this, the following loader will be appended to the existing loader for this rule. svgRule.uses.clear(); // Add the loader svgRule. Use ("vue-svg-loader").loader("vue-svg-loader");
  },
  devServer: {
    port: 8000,
    open: true// Before:function(app) {
      // var bodyParser = require("body-parser");
      // app.use(bodyParser.json());
      if(process.env.MOCK ! = ="none") {
        app.use(createMockMiddleware());
      }
    },
    proxy: {
      "/api": {
        target: "http://localhost:3000"}}}};Copy the code

Customize themes and dynamically switch themes

  • Can be found invue.config.jsConfigure the theme we want

  • The set variables can be found in the figure below

  • The community provides antD – Theme WebPack- plug-in, which is used to generate color-specific LESS/CSS and inject it into your index.html file so that you can change Ant Design-specific color themes in your browserhttps://github.com/mzohaibqc/antd-theme-webpack-plugin

vue.config

const path = require("path");
const webpack = require("webpack");
const AntDesignThemePlugin = require("antd-theme-webpack-plugin");
const { createMockMiddleware } = require("umi-mock-middleware");

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 = {
  lintOnSave: false,
  css: {
    loaderOptions: {
      less: {
        modifyVars: {
          "primary-color": "#1DA57A"
        },
        javascriptEnabled: true
      }
    }
  },
  configureWebpack: {
    plugins: [themePlugin, new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)]
  },
  chainWebpack: config => {
    const svgRule = config.module.rule("svg"); // Clear all existing loaders. // If you do not do this, the following loader will be appended to the existing loader for this rule. svgRule.uses.clear(); // Add the loader svgRule. Use ("vue-svg-loader").loader("vue-svg-loader");
  },
  devServer: {
    port: 8000,
    open: true// Before:function(app) {
      // var bodyParser = require("body-parser");
      // app.use(bodyParser.json());
      if(process.env.MOCK ! = ="none") {
        app.use(createMockMiddleware());
      }
    },
    proxy: {
      "/api": {
        target: "http://localhost:3000"}}}};Copy the code

public\index.html

<! DOCTYPE html> <html lang="en">
  <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 %>favicon.ico">
    <title>ant-design-vue-pro</title>
  </head>
  <body>
    <div id="app"></div> <! -- built files will be auto injected --> <link rel="stylesheet/less" type="text/css" href="/color.less" />
    <script>
        window.less = {
            async: false,
            env: 'production',
            javascriptEnabled: true,
            modifyVars: {
                "primary-color": "#1DA57A"}}; </script> <scripttype="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/less.js/2.7.2/less.min.js"></script>
  </body>
</html>

Copy the code

internationalization

The default copy of Ant-Design-Vue is currently in English. If you need to use other languages, please refer to the following scheme.

LocaleProvider

The LocaleProvider component is first registered with the project

LocaleProvider, the introduction of Chinese and English language package, calendar components

Now you can switch the calendar between English and Chinese, but this is provided by the moment library. To internationalize your own components, install the vuE-i18n library main.js reference, create a new language package, and use it in analysis.vue

App.vue

<template>
  <div id="app">
    <a-locale-provider :locale="locale">
      <router-view />
    </a-locale-provider>
  </div>
</template>
<script>
import zhCN from "ant-design-vue/lib/locale-provider/zh_CN";
import enUS from "ant-design-vue/lib/locale-provider/en_US";
import moment from "moment";
import "moment/locale/zh-cn";
export default {
  data() {
    return {
      locale: zhCN
    };
  },
  watch: {
    "$route.query.locale": function(val) {
      this.locale = val === "enUS" ? enUS : zhCN;
      moment.locale(val === "enUS" ? "en" : "zh-cn"); }}}; </script> <style lang="less">
#app {
  height: 100%;
}
</style>

Copy the code

Header.vue

<template>
  <div class="header">
    <notice-icon-view />
    <a-dropdown>
      <a-icon type="global" />
      <a-menu
        slot="overlay"
        @click="localeChange"
        :selectedKeys="[$route.query.locale || 'zhCN']"
      >
        <a-menu-item key="zhCN"<a-menu-item > <a-menu-item key="enUS">
          English
        </a-menu-item>
      </a-menu>
    </a-dropdown>
  </div>
</template>

<script>
export default {
  methods: {
    localeChange({ key }) {
      this.$router.push({ query: { ... this.$route.query, locale: key } });
      this.$i18n.locale = key; }}}; </script> <style scoped> .header {float: right;
  margin-right: 30px;
}
</style>

Copy the code

Analysis.vue

<template>
  <div>
   {{ $t("message") ["app.dashboard.analysis.timeLabel"] }} :
    <a-date-picker></a-date-picker>
    <Chart :option="chartOption" style="height: 400px" />
  </div>
</template>
Copy the code

main

import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store/index.js";
import VueI18n from "vue-i18n";
import enUS from "./locale/enUS";
import zhCN from "./locale/zhCN";
import queryString from "query-string"; // Import a unified import file import for on-demand components"./core/lazy_use";
Vue.use(VueI18n);

const i18n = new VueI18n({
  locale: queryString.parse(location.search).locale || "zhCN",
  messages: {
    zhCN: { message: zhCN },
    enUS: { message: enUS }
  }
});

Vue.config.productionTip = false;

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

Copy the code

Build and package releases efficiently

Run NPM run build — –report