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 install
babel-plugin-import
And modify the filebabel.config.js
The 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
main
The 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
- in
src/router.js
Configure 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
- in
src/views
Let’s go ahead and create a new oneuser
The user directory stores user-related components. Create user componentsLogin.vue
andRegister.vue
Layout Components (routerView)
- The user component has been created, but the access route will not be loaded into our component, we need one
routerView
A placeholder to mount to when a component is matchedrouterView
I can write one in positionrouterView
Component introduced, but generally usedrender
The 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 layout
routerView
Mount item, insrc
newlayouts
The directory is used to store UserLayout and BasicLayout. insrc
newdashboard
Directory to storeAnalysis
Analysis 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/router
The introduction ofUserLayout
Layout components when accesseduser
Redirected to/user/login
page
{
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/router
The introduction ofBasicLayout
Layout 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 ANTD
https://vue.ant.design
Find the Layout component and find a template similar to Pro, as shown below
- Open code assignment to our project
src/layouts/BasicLayout.vue
In 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 in
src/components/SettingDrawer/Index.vue
Create the Settings theme component and go to the official websiteDrawer
Base 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 us
SiderMenu.vue
File, because you need to recurse you need to create oneSubMenu.vue
File, 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 again
BasicLayout.vue
Pass 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 load
router
Route 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
- in
router.js meta
To 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
- in
components
Create a permission component inAuthorized.vue
component
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 components
src/core/lazy_use
In themain.js
The introduction ofimport "./core/lazy_use";
lazy_use.js
import Vue from "vue";
import Authorized from "@/components/Authorized";
Vue.component("Authorized", Authorized);
Copy the code
- in
src/layouts/BasicLayout.vue
Importing 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 instruction
src/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 Echarts
npm i echarts
In thesrc/components
newChart.vue
component
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.vue
The introduction ofChart.vue
component
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 installation
axios
, 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.js
Writing 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 variables
package.json
The 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
- in
src/utils/request.js
Secondary 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
- in
src\views\Dashboard\Analysis.vue
To 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 in
utils.js
File 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 to
src/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 the
validateStatus
andhelp
Change 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-decorator
Property 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 form
store
.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
- in
main.js
The introduction of
. import store from"./store/index.js"; .Copy the code
- Writing the first step
src\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 step
src\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 step
src\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 component
src\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 icon
404
Find your favorite icon and add it to your shopping cart - View online links can also be downloaded locally, using
symbol
SVG 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 to
iconfont.cn
Project, 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 in
vue.config.js
Configure 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 browser
https://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