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.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 中的路由为了⽅便管理,使⽤了中⼼化的⽅式,在 fig.js 统⼀配置和管理。
路由管理通过约定的语法根据在fig.js中配置路由。
菜单⽣成根据路由配置来⽣成菜单。菜单项名称,嵌套路径与路由⾼度耦合。
⾯包屑组件 PageHeader 中内置的⾯包屑也可由脚⼿架提供的配置信息⾃动⽣成。
路由
⽬前脚⼿架中所有的路由都通过 fig.js 来统⼀管理,在 vue-router 的配置中我们增加了⼀些参数,如 hideChildrenInMenu,meta.title,meta.icon,meta.permission,来辅助⽣成菜单。其中:hideChildr
enInMenu ⽤于隐藏不需要在菜单中展⽰的⼦路由。⽤法可以查看分步表单的配置。
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 组件中的页⾯带的⾯包屑和页⾯标题栏
}
}
菜单
菜单根据 fig.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 => {
$(`load user err: ${ssage}`)
})
},
queryUsers () {
getUsers(this.queryParam).then(res => {
this.list = res
})
}
}
}
</script>
**
* 获取裁剪后的图⽚
*/
cropImage () {
pimg = this.$CroppedCanvas().toDataURL();
},
/**
* 确认裁剪
*/
sureCrop () {
this.dialogVisible = false
},
/**
* 上传裁剪后的图⽚到服务器
*/
upCropImg () {
查看svg//判断是否是新增还是编辑
if (this.$route.query.id && this.$route.query.id != '') {
//如果是编辑的就直接提交
} else {
//否则先上传裁剪图⽚,将64位图⽚转换为⼆进制数据流
var formdata1 = new FormData();// 创建form对象
formdata1.append('file', convertBase64UrlToBlob(pimg), 'aaa.png');//
this.$ajax
.post(this.$api + "/upload/singleUploadImg", formdata1, { headers: { 'Content-Type': 'multipart/form-data' } })
.then(response => {
if (response.data.msg == "success" && de == 1) {
this.form.imgUrl = response.data.data.imgUrl
} else {
console.log(response)
this.$(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/ss'
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/ss'
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!', editor)
},
onEditorReady(editor) {
console.log('editor ready!', editor)
},
onEditorChange({ editor, html, text }) {
// console.log('editor change!', editor, html, text)
}
},
/
/ 如果你需要得到当前的editor对象来做⼀些事情,你可以像下⾯这样定义⼀个⽅法属性来获取当前的editor对象,实际上这⾥的$refs对应的是当前组件内所有关联了ref属性的组件元素对象 computed: {
editor() {
return this.$TextEditor.quillEditor
}
},
mounted() {
// you can use current editor object to do something(editor methods)
console.log('this is my editor', this.editor)
// this.editor to
}
}
</script>
引⼊业务图标
国际化
权限管理
⾃定义使⽤规则
修改⽹站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:antR </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\fig.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'
const whiteList = ['login', 'register', 'registerResult'] // no redirect whitelist配置⽩名单
router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar
//⽣成动态⽹站标题
if ((ACCESS_TOKEN)) {
/* has token 如果有token并且是从登录页来的就直接跳到⼯作空间*/
if (to.path === '/user/login') {
next({ path: '/dashboard/workplace' })
NProgress.done()
} else {
//否则检测是不是没有检测到规则,请求获取⽤户信息,获取⽤户权限
if (les.length === 0) {
/
/请求mock模拟数据获取⽤户权限
store
.dispatch('GetInfo')
.then(res => {
const roles = sult && le
//调⽤src\store\modules\permission.js⾥⾯的GenerateRoutes⽅法,处理数据
store.dispatch('GenerateRoutes', { roles }).then(() => {
// 根据roles权限⽣成可访问的路由表
// 动态添加可访问路由表
router.s.addRouters)
const redirect = decodeURIComponent(direct || 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(() => {
<({
message: '错误',
description: '请求⽤户信息失败,请重试'
})
store.dispatch('Logout').then(() => {
next({ path: '/user/login', query: { redirect: to.fullPath } })
})
})
} else {
next()
}
}
} else {
if (whiteList.includes(to.name)) {
// 在免登录⽩名单,直接进⼊
next()
} else {
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论