default(babel,eslint):默認設置(直接enter,沒有帶任何輔助功能的 npm包javascript
Manually select features:手動配置是咱們項目所須要的npm包css
babel-plugin-import
,而且修改文件babel.config.js
以下{
plugins: [
["import", { "libraryName": "ant-design-vue", "libraryDirectory": "es", "style": "css" }] // `style: true` 會加載 less 文件
]
}
複製代碼
main
文件引入antd-vue,引入後以下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");
複製代碼
解決辦法配置
vue.config.js
html
css: {
loaderOptions: {
less: {
javascriptEnabled: true,
}
}
}
複製代碼
src/router.js
配置用戶登陸註冊路由{
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")
},
]
}
複製代碼
src/views
下新建一個user
用戶目錄存放用戶相關的組件,新建用戶組件Login.vue
和Register.vue
routerView
佔位符,當匹配到組件時把它掛載到routerView
位置上,能夠寫一個routerView
組件引入,不過通常用render
方法比較簡便{
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")
},
]
}
複製代碼
routerView
掛載項,在src
新建layouts
目錄用來存放UserLayout及BasicLayout;在src
新建dashboard
目錄存放Analysis
分析頁面UserLayout:抽離出用於登錄註冊頁面的通用佈局vue
<template>
<div>
<div class="desc">Ant Desigin Vue Pro</div>
<router-view></router-view>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
複製代碼
src/router
引入UserLayout
佈局組件,當訪問user
重定向到/user/login
頁面{
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")
}
]
}
複製代碼
BasicLayout:基礎頁面佈局,包含了頭部導航,底部信息,側邊欄和內容部分java
<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>
複製代碼
src/router
引入BasicLayout
佈局組件,當地址欄路徑爲/
時,重定向到分析頁{
path: "/",
component: () =>
import(/* webpackChunkName: "layout" */ "./layouts/BasicLayout"),
children: [
// dashboard
{
path: "/",
redirect: "/dashboard/analysis"
},
{
path: "/dashboard",
name: "dashboard",
meta: { icon: "dashboard", title: "儀表盤" },
component: { render: h => h("router-view") },
children: [
{
path: "/dashboard/analysis",
name: "analysis",
meta: { title: "分析頁" },
component: () =>
import(
/* webpackChunkName: "dashboard" */ "./views/Dashboard/Analysis"
)
}
]
}
]
}
複製代碼
https://vue.ant.design
,找到layout佈局組件,找到相似pro的模板,以下圖src/layouts/BasicLayout.vue
中BasicLayout.vuewebpack
<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>
複製代碼
首先隱藏trigger原始圖標,Layout.Sider 有個trigger屬性,自定義 trigger,設置爲 null 時隱藏 triggerios
而後自定義a-icon 圖片及位置git
最後圖片添加click事件控制菜單切換github
BasicLayout.vueweb
<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>
複製代碼
src/components/SettingDrawer/Index.vue
新建設置主題組件,找到官網Drawer
基礎抽屜組件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>
複製代碼
-自定義抽屜按鈕及抽屜樣式
刪除原始按鈕及展現事件;
把圖標定位到右側及樣式,綁定展開收縮事件;
動態佈局設置項樣式
爲了能動態改變頁面佈局,咱們暫時先把設置參數放到
router
中,而後BasicLayout
經過computed
計算屬性從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'"></a-icon>
</div>
</template>
<div>
<h2>總體風格定製</h2>
<a-radio-group
:value="$route.query.navTheme || 'dark'"
@change="e => handleSettingChange('navTheme', e.target.value)"
>
<a-radio value="dark">黑色</a-radio>
<a-radio value="light">白色</a-radio>
</a-radio-group>
<h2>導航模式</h2>
<a-radio-group
:value="$route.query.navLayout || 'left'"
@change="e => handleSettingChange('navLayout', e.target.value)"
>
<a-radio value="left">左側</a-radio>
<a-radio value="lighn">頂部</a-radio>
</a-radio-group>
</div>
</a-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>
<style type="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>
複製代碼
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>
複製代碼
SiderMenu.vue
文件,因爲須要遞歸須要建立一個SubMenu.vue
文件,兩種方式:①函數式組件的形式 ②普通組件,推薦是函數式組件* 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
複製代碼
BasicLayout.vue
經過父子傳參的方式傳到SiderMenu.vue中BasicLayout.vue
<SiderMenu :theme="navTheme" :collapsed="collapsed" />
複製代碼
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>
複製代碼
hideChildrenInMenu 用於隱藏不須要在菜單中展現的子路由。
hideInMenu 能夠在菜單中不展現這個路由,包括子路由。
meta.title 和 meta.icon分別表明生成菜單項的文本和圖標。
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: "儀表盤" },
component: { render: h => h("router-view") },
children: [
{
path: "/dashboard/analysis",
name: "analysis",
meta: { title: "分析頁" },
component: () =>
import(
/* webpackChunkName: "dashboard" */ "./views/Dashboard/Analysis"
)
}
]
},
// form
{
path: "/form",
name: "form",
component: { render: h => h("router-view") },
meta: { icon: "form", title: "表單" },
children: [
{
path: "/form/basic-form",
name: "basicform",
meta: { title: "基礎表單" },
component: () =>
import(/* webpackChunkName: "form" */ "./views/Forms/BasicForm")
},
{
path: "/form/step-form",
name: "stepform",
hideChildrenInMenu: true,
meta: { title: "分佈表單" },
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;
複製代碼
router
路由配置,根據以上配置處理參數,生成菜單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])
);
}
}
return menuData;
}
}
};
</script>
複製代碼
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>
複製代碼
-在src\utils
新建auth.js
模擬獲取當前用戶權限及驗證權限
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();
return current && current[0] !== "guest";
}
複製代碼
router.js meta
中設置用戶權限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: "儀表盤" },
component: { render: h => h("router-view") },
children: [
{
path: "/dashboard/analysis",
name: "analysis",
meta: { title: "分析頁" },
component: () =>
import(
/* webpackChunkName: "dashboard" */ "./views/Dashboard/Analysis"
)
}
]
},
// form
{
path: "/form",
name: "form",
component: { render: h => h("router-view") },
meta: { icon: "form", title: "表單", authority: ["admin"] },
children: [
{
path: "/form/basic-form",
name: "basicform",
meta: { title: "基礎表單" },
component: () =>
import(/* webpackChunkName: "form" */ "./views/Forms/BasicForm")
},
{
path: "/form/step-form",
name: "stepform",
hideChildrenInMenu: true,
meta: { title: "分佈表單" },
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: "你沒有權限訪問,請聯繫管理員諮詢。"
});
next({
path: "/403"
});
}
NProgress.done();
}
next();
});
router.afterEach(() => {
NProgress.done();
});
export default router;
複製代碼
-在菜單中過濾掉沒有權限的路由
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])
);
}
}
return menuData;
}
}
};
</script>
複製代碼
若想控制table的增刪改查的功能,咱們須要控制單個按鈕的功能
components
中新建權限組件Authorized.vue
組件Authorized.vue
<script>
import { check } from "../utils/auth";
export default {
// 函數式組件
functional: true,
props: {
authority: {
type: Array,
required: true
}
},
render(h, context) {
const { props, scopedSlots } = context;
return check(props.authority) ? scopedSlots.default() : null;
}
};
</script>
複製代碼
src/core/lazy_use
,在main.js
中引入import "./core/lazy_use";
lazy_use.js
import Vue from "vue";
import Authorized from "@/components/Authorized";
Vue.component("Authorized", Authorized);
複製代碼
src/layouts/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 :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>
複製代碼
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 };
複製代碼
<a-icon
v-auth="['admin']"
class="trigger"
></a-icon>
複製代碼
npm i echarts
,在src/components
新建Chart.vue
組件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);//防抖
},
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() {
// 基於準備好的dom,初始化echarts實例
this.chart = echarts.init(this.$refs.chartDom);
this.chart.setOption(this.option);
}
}
};
</script>
<style></style>
複製代碼
src\views\Dashboard\Analysis.vue
引入Chart.vue
組件父子傳值把
chartOption
傳給組件新增定時器並引入
random
函數,每隔3秒數據變化數據變化後,給
chartOption
賦新值,組件監聽數據變化則更新組件,不須要深度監聽beforeDestroy時銷燬定時器
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: "ECharts 入門示例"
},
tooltip: {},
xAxis: {
data: ["襯衫", "羊毛衫", "雪紡衫", "褲子", "高跟鞋", "襪子"]
},
yAxis: {},
series: [
{
name: "銷量",
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>
複製代碼
axios
,因引入axios
,添加axios請求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: "ECharts 入門示例"
},
tooltip: {},
xAxis: {
data: ["襯衫", "羊毛衫", "雪紡衫", "褲子", "高跟鞋", "襪子"]
},
yAxis: {},
series: [
{
name: "銷量",
type: "bar",
data: response.data
}
]
};
});
}
},
beforeDestroy() {
clearInterval(this.interval);
},
components: {
Chart
}
};
</script>
<style></style>
複製代碼
mock\dashboard_chart.js
編寫接口文件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;
複製代碼
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);
return res.send(result);
}
}
}
}
}
};
複製代碼
以上都是本身寫的mock請求,下面使用第三方
mock.js
安裝
mock.js umi-mock-middleware
,umi-mock-middleware
中間件當修改mock數據的時候,中間件會自動刷新首先註冊中間件,修改
vue.config.js
,若是沒有在項目根文件夾下新建一個,關鍵代碼以下
const { createMockMiddleware } = require("umi-mock-middleware");
devServer: {
port: 8000,
open: true,
// 解析body,對接真實服務端環境須要註釋掉
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"
}
}
}
複製代碼
/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;
}
// 返回你的mock數據。好比:
res.json(result);
}
};
複製代碼
package.json
新增命令window下須要安裝一個
cross-env
的包才能使用修改
vue.config.js
配置
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"
}
複製代碼
vue.config.js
const { createMockMiddleware } = require("umi-mock-middleware");
module.exports = {
css: {
loaderOptions: {
less: {
javascriptEnabled: true
}
}
},
devServer: {
open: true,
// 解析body,對接真實服務端環境須要註釋掉
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"
}
}
}
};
複製代碼
通常狀況下,不會直接axios請求會進行一個二次封裝
src/utils/request.js
進行二次封裝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;
複製代碼
src\views\Dashboard\Analysis.vue
中把封裝好的axios替換掉以前的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: "ECharts 入門示例"
},
tooltip: {},
xAxis: {
data: ["襯衫", "羊毛衫", "雪紡衫", "褲子", "高跟鞋", "襪子"]
},
yAxis: {},
series: [
{
name: "銷量",
type: "bar",
data: response.data
}
]
};
});
}
},
beforeDestroy() {
clearInterval(this.interval);
},
components: {
Chart
}
};
</script>
<style></style>
複製代碼
utils.js
文件沒法寫單文件組件,可使用render函數,render函數仍是比較繁瑣的,另外一種時jsx;下面須要把jsx 配置到項目中安裝babel支持jsx 插件
@vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props
配置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 文件
]
};
複製代碼
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: h => (
<div>
請求錯誤 <span style="color: red">{status}</span> : {options.url}
</div>
),
description: statusText
});
return Promise.reject(error);
});
}
export default request;
複製代碼
src/view/Forms/BasicForm.vue
,這樣一個基本表單就完成了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>
複製代碼
validateStatus: 校驗狀態,可選 ‘success’, ‘warning’, ‘error’, ‘validating’。 hasFeedback:用於給輸入框添加反饋圖標。 help:設置校驗文案。
BasicForm.vue
...
<a-form-item
label="Field A"
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
validateStatus="error"
help="必須大於5個字符!"
>
<a-input v-model="FieldA" placeholder="input placeholder" />
</a-form-item>
...
複製代碼
validateStatus
和help
改成動態便可<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 = "必須大於5個字符!";
} 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 = "必須大於5個字符!";
} else {
console.dir({ FieldA: this.FieldA, FieldB: this.FieldB });
}
}
}
};
</script>
複製代碼
v-decorator
屬性設置組件名稱、初始化值initialValue
、表單驗證rules
<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: '必須大於5個字符' }] } ]"
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>
複製代碼
結合
vuex
和view-router
作一個比較複雜的表單,第一步保存付款帳戶、收款帳戶信息,第二步請求服務並保存付款帳戶和密碼信息及收款帳戶信息,第三步提示操做信息
store
,src\store\modules\form.js
step
狀態保存付款帳戶、收款帳戶信息
submitStepForm action
表單提交請求服務並保存付款帳戶和密碼信息及收款帳戶信
saveStepFormData mutations
保存第一步付款帳戶、收款帳戶信息
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
};
複製代碼
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
}
});
複製代碼
main.js
引入...
import store from "./store/index.js";
...
複製代碼
src\views\Forms\StepForm\Step1.vue
Step1.vue
<template>
<div>
<a-form layout="horizontal" :form="form">
<a-form-item
label="付款帳戶"
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
>
<a-input
v-decorator="[ 'payAccount', { initialValue: step.payAccount, rules: [{ required: true, message: '請輸入付款帳號' }] } ]"
placeholder="請輸入付款帳號"
/>
</a-form-item>
<a-form-item
label="收款帳戶"
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
>
<a-input
v-decorator="[ 'receiverAccount', { initialValue: step.receiverAccount, rules: [{ required: true, message: '請輸入收款帳號' }] } ]"
placeholder="請輸入收款帳號"
</a-form-item>
<a-form-item>
<a-button type="primary" @click="handleSubmit">下一步</a-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, $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>
複製代碼
src\views\Forms\StepForm\Step2.vue
Step2.vue
<template>
<div>
<a-form layout="horizontal" :form="form">
<a-form-item
label="付款帳戶"
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
>
{{ step.payAccount }}
</a-form-item>
<a-form-item
label="密碼"
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
>
<a-input
v-decorator="[ 'password', { initialValue: step.payAccount, rules: [{ required: true, message: '請輸入密碼' }] } ]"
type="password"
placeholder="請輸入付款密碼"
/>
</a-form-item>
<a-form-item>
<a-button type="primary" @click="handleSubmit">提交</a-button>
<a-button style="margin-left: 8px" @click="onPrev">上一步</a-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>
複製代碼
src\views\Forms\StepForm\Step3.vue
Step3.vue
<template>
<div>操做成功,預計兩小時到帳</div>
</template>
<script>
export default {};
</script>
<style></style>
複製代碼
自定義或第三方的表單控件,也能夠與 Form 組件一塊兒使用。只要該組件遵循如下的約定:
提供受控屬性 value 或其它與 valuePropName-參數) 的值同名的屬性。
提供 onChange 事件或 trigger-參數) 的值同名的事件。
不能是函數式組件。
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>
複製代碼
Step1.vue
···
<a-form-item
label="收款帳戶"
:label-col="formItemLayout.labelCol"
:wrapper-col="formItemLayout.wrapperCol"
>
<ReceiverAccount
v-decorator="[ 'receiverAccount', { initialValue: step.receiverAccount, rules: [ { required: true, message: '請輸入收款帳號', validator: (rule, value, callback) => { if (value && value.number) { callback(); } else { callback(false); } } } ] } ]"
/>
</a-form-item>
···
複製代碼
404
找到本身喜歡圖標加入購物車,添加至項目中symbol
svg模式,另外兩項都是字體
src\core\lazy_use.js
lazy_use.js
在 1.2.0 以後,咱們提供了一個 createFromIconfontCN 方法,方便開發者調用在 iconfont.cn 上自行管理的圖標。
const IconFont = Icon.createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js', // 在 iconfont.cn 上生成
});
Vue.component("IconFont", IconFont);
複製代碼
<IconFont type="icon-icon-404" style="font-size:100px"></IconFont>
複製代碼
iconfont.cn
項目中,也能夠放到vue項目中引用<template>
<div><img :src="logo"></div>
</template>
import logo from "@/assets/logo.svg";
<script>
data() {
return {
logo
};
}
</script>
複製代碼
首先安裝包
vue-svg-loader
配置
vue.config.js
使用
<template>
<div><logo></logo></div>
</template>
import Logo from "@/assets/logo.svg";
<script>
components: {
Logo
}
</script>
複製代碼
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");
// 清除已有的全部 loader。
// 若是你不這樣作,接下來的 loader 會附加在該規則現有的 loader 以後。
svgRule.uses.clear();
// 添加要替換的 loader
svgRule.use("vue-svg-loader").loader("vue-svg-loader");
},
devServer: {
port: 8000,
open: true,
// 解析body,對接真實服務端環境須要註釋掉
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"
}
}
}
};
複製代碼
vue inspect >output.js