ant-design表單處理和常用方法及自定義驗證操作
首先要說一下antdesign這個框架API和demo豐富,而且開發環境提供對應的warning來糾正使用者的錯誤。是一個很好的元件庫。
關於表單驗證方面是依賴於 async-validator 庫。百度的san-xui元件庫的表單驗證也是依賴與async-validator。說明這個庫的實用性還是比較高,可以多瞭解一下。
首先按照antDesign官網Demo。我們可以copy一個Form表單的demo。 LoginForm是表單的元件,下面程式碼,是React 高階元件(Hoc)。 用於使元件獲取 this.props.form
下面介紹一些常用的 this.props.form的方法。
const { form } = this.props
form.resetFields() 用於清空輸入空
form.validateFields 用於驗證
1.給輸入框新增 鍵名。rules 規定輸入規則。 validator可以自定義輸入標準
value 標識輸入內容
callback 回撥函式,如果裡面有字串,代表錯誤提示。如果為空。代表輸入正確 成功返回。
補充知識:Ant Design Pro Vue使用心得
目錄結構
├── public
│ └── logo.png # LOGO
| └── index.html # Vue 入口模板
├── src
│ ├── api # Api ajax 等
│ ├── assets # 本地靜態資源
│ ├── config # 專案基礎配置,包含路由,全域性設定
│ ├── components # 業務通用元件
│ ├── core # 專案引導,全域性配置初始化,依賴包引入等
│ ├── router # Vue-Router
│ ├── store # Vuex
│ ├── utils # 工具庫
│ ├── locales # 國際化資源
│ ├── views # 業務頁面入口和常用模板
│ ├── App.vue # Vue 模板入口
│ └── main.js # Vue 入口 JS
│ └── permission.js # 路由守衛(路由許可權控制)
├── tests # 測試工具
├── README.md
└── package.json
路由和選單
基本結構
路由和選單是組織起一個應用的關鍵骨架,pro 中的路由為了方便管理,使用了中心化的方式,在 router.config.js 統一配置和管理。
路由管理 通過約定的語法根據在router.config.js中配置路由。
選單生成 根據路由配置來生成選單。選單項名稱,巢狀路徑與路由高度耦合。
麵包屑 元件 PageHeader 中內建的麵包屑也可由腳手架提供的配置資訊自動生成。
路由
目前腳手架中所有的路由都通過 router.config.js 來統一管理,在 vue-router 的配置中我們增加了一些引數,如 hideChildrenInMenu,meta.title,meta.icon,meta.permission,來輔助生成選單。其中:
hideChildrenInMenu 用於隱藏不需要在選單中展示的子路由。用法可以檢視 分步表單 的配置。
hidden 可以在選單中不展示這個路由,包括子路由。效果可以檢視 other 下的路由配置。
meta.title 和 meta.icon分別代表生成選單項的文字和圖示。
meta.permission 用來配置這個路由的許可權,如果配置了將會驗證當前使用者的許可權,並決定是否展示 *(預設情況下)。
meta.hidden 可以強制子選單不顯示在選單上(和父級 hideChildrenInMenu 配合)
meta.hiddenHeaderContent 可以強制當前頁面不顯示 PageHeader 元件中的頁面帶的 麵包屑和頁面標題欄
路由配置項
/** * 路由配置說明: * 建議:sider menu 請不要超過三級選單,若超過三級選單,則應該設計為頂部主選單 配合左側次級選單 * **/ { redirect: noredirect,//重定向 name: 'router-name',//路由名稱 hidden: true,//可以在選單中不展示這個路由,包括子路由。效果可以檢視 other 下的路由配置。 meta: { title: 'title',//選單項名稱 icon: 'a-icon',//選單項圖示 keepAlive: true,//快取頁面 permission:[string] //用來配置這個路由的許可權,如果配置了將會驗證當前使用者的許可權,並決定是否展示 *(預設情況下) hiddenHeaderContent: true,//可以強制當前頁面不顯示 PageHeader 元件中的頁面帶的 麵包屑和頁面標題欄 } }
具體請參考 https://pro.loacg.com/docs/router-and-nav
選單
選單根據 router.config.js 生成,具體邏輯在 src/store/modules/permission.js 中的 actions.GenerateRoutes 方法實現。
Ant Design Pro 的佈局
在 Ant Design Pro 中,我們抽離了使用過程中的通用佈局,都放在 /components/layouts 目錄中,分別為:
BasicLayout:基礎頁面佈局,包含了頭部導航,側邊欄和通知欄:
UserLayout:抽離出用於登陸註冊頁面的通用佈局
PageView:基礎佈局,包含了麵包屑,和中間內容區 (slot)
RouterView:空佈局,專門為了二級選單內容區自定義
BlankLayout:空白的佈局
定義全域性樣式
/* 定義全域性樣式 */ :global(.text) { font-size: 16px; } /* 定義多個全域性樣式 */ :global { .footer { color: #ccc; } .sider { background: #ebebeb; } } //覆蓋元件樣式 // 使用 css 時可以用 >>> 進行樣式穿透 .test-wrapper >>> .ant-select { font-size: 16px; } // 使用 scss,less 時,可以用 /deep/ 進行樣式穿透 .test-wrapper /deep/ .ant-select { font-size: 16px; } // less CSS modules 時亦可用 :global 進行覆蓋 .test-wrapper { :global { .ant-select { font-size: 16px; } } }
與伺服器互動
在 Ant Design Pro 中,一個完整的前端 UI 互動到服務端處理流程是這樣的:
UI 元件互動操作;
呼叫統一管理的 api service 請求函式;
使用封裝的 request.js 傳送請求;
獲取服務端返回;
更新 data。
從上面的流程可以看出,為了方便管理維護,統一的請求處理都放在 @/src/api 資料夾中,並且一般按照 model 緯度進行拆分檔案,如:
api/
user.js
permission.js
goods.js
...
其中,@/src/utils/request.js 是基於 axios 的封裝,便於統一處理 POST,GET 等請求引數,請求頭,以及錯誤提示資訊等。具體可以參看 request.js。 它封裝了全域性 request 攔截器、response 攔截器、統一的錯誤處理、baseURL 設定等。
例如在 api 中的一個請求使用者資訊的例子:
// api/user.js import { axios } fromm '@/utils/request' const api = { info: '/user',list: '/users' } // 根據使用者 id 獲取使用者資訊 export function getUser (id) { return axios({ url: `${api.user}/${id}`,method: 'get' }) } // 增加使用者 export function addUser (parameter) { return axios({ url: api.user,method: 'post',data: parameter }) } // 更新使用者 // or (id,parameter) export function updateUser (parameter) { return axios({ url: `${api.user}/${parameter.id}`,// or `${api.user}/${id}` method: 'put',data: parameter }) } // 刪除使用者 export function deleteUser (id) { return axios({ url: `${api.user}/${id}`,method: 'delete',data: parameter }) } // 獲取使用者列表 parameter: { pageSize: 10,pageNo: 1 } export function getUsers (parameter) { return axios({ url: api.list,method: 'get',params: parameter }) }
<template> <div> <a-button @click="queryUser"></a-button> <a-table :dataSource="list"> </a-table> </div> </template> <script> import { getUser,getUsers } from '@/api/user' export default { data () { return { id: 0,queryParam: { pageSize: 10,pageNo: 1,username: '' },info: {},list: [] } },methods: { queryUser () { const { $message } = this getUser(this.id).then(res => { this.info = res.data }).catch(err => { $message.error(`load user err: ${err.message}`) }) },queryUsers () { getUsers(this.queryParam).then(res => { this.list = res }) } } } </script>
** * 獲取裁剪後的圖片 */ cropImage () { this.form.cropimg = this.$refs.cropper.getCroppedCanvas().toDataURL(); },/** * 確認裁剪 */ sureCrop () { this.dialogVisible = false },/** * 上傳裁剪後的圖片到伺服器 */ upCropImg () { //判斷是否是新增還是編輯 if (this.$route.query.id && this.$route.query.id != '') { //如果是編輯的就直接提交 this.onSubmit() } else { //否則先上傳裁剪圖片,將64點陣圖片轉換為二進位制資料流 var formdata1 = new FormData();// 建立form物件 formdata1.append('file',convertBase64UrlToBlob(this.form.cropimg),'aaa.png');// this.$ajax .post(this.$api + "/upload/singleUploadImg",formdata1,{ headers: { 'Content-Type': 'multipart/form-data' } }) .then(response => { if (response.data.msg == "success" && response.data.code == 1) { this.form.imgUrl = response.data.data.imgUrl this.onSubmit() } else { console.log(response) this.$message.error(response.data.msg); } }) .catch(function (error) { console.log(error); }); } },
引入外部模組
$ npm install '元件名字' --save
使用
//全域性引入 import Vue from 'vue' import VueQuillEditor from 'vue-quill-editor' // require styles import 'quill/dist/quill.core.css' import 'quill/dist/quill.snow.css' import 'quill/dist/quill.bubble.css' Vue.use(VueQuillEditor,/* { default global options } */)
<template> <div> <quill-editor ref="myTextEditor" v-model="content" :config="editorOption" @blur="onEditorBlur($event)" @focus="onEditorFocus($event)" @ready="onEditorReady($event)"> </quill-editor> </div> </template>
<script> //按需載入 import 'quill/dist/quill.core.css' import 'quill/dist/quill.snow.css' import 'quill/dist/quill.bubble.css' import { quillEditor } from 'vue-quill-editor' export default { components: { quillEditor },data () { return { content: '<h2>I am Example</h2>',editorOption: { // something config } } },// 如果需要手動控制資料同步,父元件需要顯式地處理changed事件 methods: { onEditorBlur(editor) { console.log('editor blur!',editor) },onEditorFocus(editor) { console.log('editor focus!',onEditorReady(editor) { console.log('editor ready!',onEditorChange({ editor,html,text }) { // console.log('editor change!',editor,text) this.content = html } },// 如果你需要得到當前的editor物件來做一些事情,你可以像下面這樣定義一個方法屬性來獲取當前的editor物件,實際上這裡的$refs對應的是當前元件內所有關聯了ref屬性的元件元素物件 computed: { editor() { return this.$refs.myTextEditor.quillEditor } },mounted() { // you can use current editor object to do something(editor methods) console.log('this is my editor',this.editor) // this.editor to do something... } } </script>
引入業務圖示
參考:https://pro.loacg.com/docs/biz-icon、
國際化
參考:https://pro.loacg.com/docs/i18n
許可權管理
參考:https://pro.loacg.com/docs/authority-management
自定義使用規則
修改網站icon的檔案地址在 public資料夾中把logo.png換成自定義的,也可在public/index.html自定義
<!DOCTYPE html> <html lang="zh-cmn-Hans"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <link rel="icon" href="<%= BASE_URL %>logo.png" rel="external nofollow" > <title>共享雲店</title> <style>#loading-mask{position:fixed;left:0;top:0;height:100%;width:100%;background:#fff;user-select:none;z-index:9999;overflow:hidden}.loading-wrapper{position:absolute;top:50%;left:50%;transform:translate(-50%,-100%)}.loading-dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:64px;width:64px;height:64px;box-sizing:border-box}.loading-dot i{width:22px;height:22px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.loading-dot i:nth-child(1){top:0;left:0}.loading-dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.loading-dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.loading-dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}</style> </head> <body> <noscript> <strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> </noscript> <div id="app"> <div id="loading-mask"> <div class="loading-wrapper"> <span class="loading-dot loading-dot-spin"><i></i><i></i><i></i><i></i></span> </div> </div> </div> <!-- built files will be auto injected --> </body> </html>
-更換logo在src\components\tools\Logo.vue中更換
<template> <div class="logo"> <router-link :to="{name:'dashboard'}"> <LogoSvg alt="logo" /> //這是logo <h1 v-if="showTitle">{{ title }}</h1> //這是網站標題 </router-link> </div> </template> <script> import LogoSvg from '@/assets/logo.svg?inline'; export default { name: 'Logo',components: { LogoSvg },props: { title: { type: String,default: 'Admin For Ok',//網站預設標題 required: false },showTitle: { //是否顯示網站標題,預設不顯示 type: Boolean,default: true,required: false } } } </script>
pro許可權管理和路由控制思路分析(粗略分析)
主要是通過三個檔案實現,src\mock\services\user.js檔案通過登陸的角色獲取對應的鑑權規則,具體可檢視該檔案下的原始碼
src\config\router.config.js檔案為路由配置檔案,可增加路由取消路由等,變數asyncRouterMap為主要路由陣列集合,可配置鑑權許可權,變數constantRouterMap為基礎路由,不參與鑑權
src\permission.js檔案為動態配置路由的主要邏輯,程式碼如下
import Vue from 'vue' import router from './router' import store from './store' import NProgress from 'nprogress' // progress bar import 'nprogress/nprogress.css' // progress bar style import notification from 'ant-design-vue/es/notification' import { setDocumentTitle,domTitle } from '@/utils/domUtil' import { ACCESS_TOKEN } from '@/store/mutation-types' NProgress.configure({ showSpinner: false }) // NProgress Configuration const whiteList = ['login','register','registerResult'] // no redirect whitelist配置白名單 router.beforeEach((to,from,next) => { NProgress.start() // start progress bar //生成動態網站標題 to.meta && (typeof to.meta.title !== 'undefined' && setDocumentTitle(`${to.meta.title} - ${domTitle}`)) if (Vue.ls.get(ACCESS_TOKEN)) { /* has token 如果有token並且是從登入頁來的就直接跳到工作空間*/ if (to.path === '/user/login') { next({ path: '/dashboard/workplace' }) NProgress.done() } else { //否則檢測是不是沒有檢測到規則,請求獲取使用者資訊,獲取使用者許可權 if (store.getters.roles.length === 0) { //請求mock模擬資料獲取使用者許可權 store .dispatch('GetInfo') .then(res => { const roles = res.result && res.result.role //呼叫src\store\modules\permission.js裡面的GenerateRoutes方法,處理資料 store.dispatch('GenerateRoutes',{ roles }).then(() => { // 根據roles許可權生成可訪問的路由表 // 動態新增可訪問路由表 router.addRoutes(store.getters.addRouters) const redirect = decodeURIComponent(from.query.redirect || to.path) if (to.path === redirect) { // hack方法 確保addRoutes已完成,set the replace: true so the navigation will not leave a history record next({ ...to,replace: true }) } else { // 跳轉到目的路由 next({ path: redirect }) } }) }) .catch(() => { notification.error({ message: '錯誤',description: '請求使用者資訊失敗,請重試' }) store.dispatch('Logout').then(() => { next({ path: '/user/login',query: { redirect: to.fullPath } }) }) }) } else { next() } } } else { if (whiteList.includes(to.name)) { // 在免登入白名單,直接進入 next() } else { next({ path: '/user/login',query: { redirect: to.fullPath } }) NProgress.done() // if current page is login will not trigger afterEach hook,so manually handle it } } }) router.afterEach(() => { NProgress.done() // finish progress bar })
src\store\modules\permission.js檔案為路由資料的詳細處理邏輯,配合src\permission.js檔案使用,程式碼如下: import { asyncRouterMap,constantRouterMap } from '@/config/router.config'
/** * 過濾賬戶是否擁有某一個許可權,並將選單從載入列表移除 * * @param permission * @param route * @returns {boolean} */ function hasPermission (permission,route) { if (route.meta && route.meta.permission) { let flag = false for (let i = 0,len = permission.length; i < len; i++) { flag = route.meta.permission.includes(permission[i]) if (flag) { return true } } return false } return true } /** * 單賬戶多角色時,使用該方法可過濾角色不存在的選單 * * @param roles * @param route * @returns {*} */ // eslint-disable-next-line function hasRole(roles,route) { if (route.meta && route.meta.roles) { return route.meta.roles.includes(roles.id) } else { return true } } function filterAsyncRouter (routerMap,roles) { const accessedRouters = routerMap.filter(route => { if (hasPermission(roles.permissionList,route)) { if (route.children && route.children.length) { route.children = filterAsyncRouter(route.children,roles) } return true } return false }) return accessedRouters } const permission = { state: { routers: constantRouterMap,addRouters: [] },mutations: { SET_ROUTERS: (state,routers) => { state.addRouters = routers state.routers = constantRouterMap.concat(routers) } },actions: { GenerateRoutes ({ commit },data) { return new Promise(resolve => { const { roles } = data const accessedRouters = filterAsyncRouter(asyncRouterMap,roles) commit('SET_ROUTERS',accessedRouters) resolve() }) } } } export default permission
跨域請求設定
在vue.config.js檔案中修改
// 配置跨域 devServer: { // development server port 8000 // port: 8000,proxy: { '/apis': { // target: 'https://mock.ihx.me/mock/5baf3052f7da7e07e04a5116/antd-pro',target: 'http://192.168.1.73:8092/okcloud/',// target: 'http://39.107.78.120:8083/okcloud/',ws: false,changeOrigin: true,//是否允許跨域 pathRewrite: { '^/apis': '' } } }
axios攔截器
在檔案src\utils\request.js中設定
// request interceptor service.interceptors.request.use(config => { const token = Vue.ls.get(ACCESS_TOKEN) if (token) { config.headers['okcloud_token'] = token // 讓每個請求攜帶自定義 token 請根據實際情況自行修改 } return config },err) // response interceptor service.interceptors.response.use((response) => { if (response.data.code === 10000) { notification.warning({ message: '提示',description: response.data.message }) } else { return response.data } },err)
自定義角色的選單許可權
在src\main.js檔案中註釋掉"// import ‘./permission' // permission control 許可權控制"
自定義路由許可權檔案src\routeGuard.js,程式碼如下
import Vue from 'vue' import router from './router' // import store from './store' import NProgress from 'nprogress' // progress bar import 'nprogress/nprogress.css' // progress bar style import { setDocumentTitle,domTitle } from '@/utils/domUtil' import { ACCESS_TOKEN } from '@/store/mutation-types' NProgress.configure({ showSpinner: false }) // NProgress Configuration router.beforeEach((to,next) => { NProgress.start() // start progress bar to.meta && (typeof to.meta.title !== 'undefined' && setDocumentTitle(`${to.meta.title} - ${domTitle}`)) if (to.path === '/user/login' && Vue.ls.get(ACCESS_TOKEN)) { next({ path: '/dashboard/workplace' }) NProgress.done() } else if (to.path !== '/user/login' && !Vue.ls.get(ACCESS_TOKEN)) { next({ path: '/user/login' }) NProgress.done() } else { next() NProgress.done() } }) router.afterEach(() => { NProgress.done() // finish progress bar })
在main.js中引入import ‘./routeGuard'
對src\components\Menu\menu.js檔案進行自定義選單改造
在src\store\modules\app.js檔案中store加入menu,在actions中進行獲取選單的非同步操作,獲取選單資訊,然後進行全域性變數動態獲取
在src\layouts\BasicLayout.vue中進行全域性變數的引用
...mapState({ // 動態主路由 menus: state => state.app.menu }),
動態方法的引用
...mapActions(['setSidebar','setMenu']),
呼叫獲取動態方法
this.setMenu()
以上這篇ant-design表單處理和常用方法及自定義驗證操作就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。