用slot和component实现表单共用
业务需求
在oa开发中,有许多流程,每个流程里都会有很多字段,比如流程标题、拉下选择,附件等等,有些是每个流程都会有的,有些是特有的,按常规的方法开发,就为为一个流程写一个表单,校验,提交。如果新来流程,就复制一个表达,修改需要变更的地方。这样开发会导致很多重复的代码,而且比较凌乱
简化实现
- 将每一个输入框写成共用的,将必填校验判断也一并写入,比如:流程标题组件: iProcessTitle,使用详情看下方注释
<template>
<div>
<div v-if="!isShow">
<Row>
<Col :xs="6" :md='mdModelLeft'><span class="t-span">{{config.title}}:</span></Col>
<Col :xs="18" :md='mdModelRight'>
<FormItem :prop="config.key" :rules="rules">
<Input
v-model="postData[config.key]"
:placeholder="placeholder"
:maxlength="config.maxLength"
:disabled="config.disabled || false"
></Input>
</FormItem>
</Col>
</Row>
</div>
<div v-else>
<div class="cont-i" v-if="config.title">
<span class="gray gray-f">{{ config.title }}</span>
<div class="attachment-i">{{ postData[config.key] }}</div>
</div>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
var validateData = {}
export default {
name: "i-process-title",
computed: {
...mapState([
'postData'
]),
placeholder: function () {
// 更具传入标题显示placeholder
let placeholder = '请选择输入' + this.config.title
if (this.config.maxLength) {
placeholder += '(' + this.config.maxLength +'个字以内)'
}
return placeholder
},
rules: function () {
return {
validator: validateData,
trigger: 'blur'
}
},
isShow: function () {
return this.config.isShow
}
},
props: {
// 当前输入框配置
config: {
default(){
return {
title: '流程标题', // 输入框标题
key: 'processTitle', // 要提交的字段
required: false, // 是否必填
disabled: false, // 是否禁止编辑
isShow: true, // 是否是流程发起状态 true:流程发起,展示输入框; false: 审批过程/打印,展示结果
}
},
type: Object
}
},
data() {
// 输入校验
validateData = (rule, value, callback) => {
let reg = /^[0-9]*$/;
// 是否必填
if (this.config.required) {
if (value === '' || value === undefined) {
callback(new Error(this.config.title + '必填'));
return
}
}
// 纯数字校验
if (this.config.type && this.config.type === 'Number') {
if (!reg.test(value) && value !== '' && value !== undefined) {
callback(new Error('格式不符合'));
return
}
}
callback();
}
return {
}
},
methods: {
},
mounted(){
this.postData.department = this.$store.state.department
}
}
</script>
<style scoped>
</style>
- 选择框组件: iSelectType
<template>
<Row>
<Col :xs="6" :md='mdModelLeft'><span class="t-span">{{config.title}}:</span></Col>
<Col :xs="18" :md='mdModelRight'>
<FormItem :prop="config.key" :rules="rules">
<Select v-model="postData[config.key]">
<Option v-for="(item, key) in config.list" :value="item" :key="item">{{ key }}</Option>
</Select>
</FormItem>
</Col>
</Row>
</template>
<script>
import UImodel from '../../assets/js/UIModel'
import {mapState, mapMutations} from 'vuex'
export default {
name: 'i-select-type',
props: {
config: {
default(){
return {
title: '是否超标', // 默认标题
key: 'excessive', // 默认字段
list: { // 默认列表
'是': 'true',
'否': 'false'
}
}
},
type: Object
}
},
computed: {
...mapState([
'postData'
]),
rules: function () {
// 必填校验
if (this.config.required) {
return {
required: true,
message: '选择' + this.config.title,
trigger: 'change'
}
}
}
},
data () {
return {
mdModelLeft: UImodel.mdModelLeft,
mdModelRight: UImodel.mdModelRight
}
}
}
</script>
- 时间选择组件:iDate
<template>
<div>
<Row>
<Col :xs="6" :md='mdModelLeft'><span class="t-span">{{config.title}}</span></Col>
<Col :xs="18" :md='mdModelRight'>
<FormItem prop="startTimeFlag" :rules="startRules">
<DatePicker
:type="type"
v-model="postData.startTimeFlag"
:format="format"
placeholder="请选择时间"
@on-change="sdateChange"
style="width: 200px">
</DatePicker>
</FormItem>
</Col>
</Row>
<Row v-if="config.endate">
<Col :xs="6" :md='mdModelLeft'><span class="t-span">结束时间:</span></Col>
<Col :xs="18" :md='mdModelRight'>
<FormItem prop="endTimeFlag" :rules="endRules">
<DatePicker
:type="type"
v-model="postData.endTimeFlag"
:format="format"
:options="endDateOptions"
placeholder="请选择时间"
@on-change="edateChange"
style="width: 200px">
</DatePicker>
</FormItem>
</Col>
</Row>
</div>
</template>
<script>
import UImodel from '../../assets/js/UIModel'
import {mapState, mapMutations} from 'vuex'
var datePassCheck = {}
export default {
name: 'i-date',
props: {
config: {
default () {
return {
title: '开始时间'
}
},
type: Object
}
},
computed: {
...mapState([
'postData'
]),
// 开始时间校验
startRules: function () {
//是否必填
if (this.config.required) {
return {
type: 'date',
required: true,
message: this.config.title + '不能为空',
trigger: 'change'
}
}
},
// 结束时间校验
endRules: function () {
// 是否必填
if (this.config.endate && this.config.endrequired) {
return {
validator: datePassCheck, trigger: 'change'
}
}
},
// 时间显示格式
format: function () {
if (this.config.type === 'datetime') {
this.type = 'datetime'
return 'yyyy-MM-dd HH:mm'
}
return 'yyyy-MM-dd'
}
},
methods: {
...mapMutations([
'SET_POSTDATAKEY'
]),
sdateChange: function (val) {
this.$set(this.postData, this.config.key, val)
this.$set(this.postData, 'startTime', val)
},
edateChange: function (val) {
this.postData.endTime = val
}
},
watch: {
// 开始时间改变,需清空结束时间
'postData.startTime': function (val) {
let _this = this
let v = this.postData.startTimeFlag
let date = new Date(v)
let time = date.getFullYear() + '-' +
(date.getMonth() + 1) + '-' +
date.getDate()
this.endDateOptions.disabledDate = function (date) {
return _this.config.isYesterday ? date.valueOf() < (new Date(time) - 23 * 60 * 60 * 1000) : date.valueOf() < new Date(time)
// return date.valueOf() < new Date(time)
}
// 清空后面的日期
this.postData.endTimeFlag = ''
this.postData.endTime = ''
this.showError = true
}
},
data () {
// 结束时间校验
datePassCheck = (rule, value, callback) => {
if (value === '') {
callback(new Error('结束时间不能为空'))
} else if (this.postData.endTime < this.postData.startTime) {
callback(new Error('结束时间需大于开始时间'))
} else {
callback()
}
}
return {
mdModelLeft: UImodel.mdModelLeft,
mdModelRight: UImodel.mdModelRight,
// 结束日期的 起点规则
endDateOptions: {
disabledDate (date) { }
},
type: 'date'
}
},
mounted () {
}
}
</script>
<style></style>
- 如果还需要其他组件,按照上述方法添加即可,下面写申请界面的公共部分:apply
<template>
<Form ref="formValidate" :model="postData" :rules="ruleValidate" class="leave">
<div class="disabledBox">
<!-- 这里是每个流程的表单部分 -->
<slot></slot>
<!-- 附件组件 -->
<uploadAttachments
:processKey="processKey"
:fileData="fileData"
:fileAry="temporary.file"
@deleteFileAry="deleteFileAry">
</uploadAttachments>
<div class="disabled" ref="disabled" v-if="submitAfret"></div>
</div>
<Row v-if="!submitAfret">
<Col :span="6" :offset="18">
<Button type="info" @click="submitData('formValidate')">转下一步</Button>
</Col>
</Row>
</Form>
</template>
<script>
import {mapState, mapMutations} from 'vuex'
import uploadAttachments from './../process/common/uploadAttachments.vue'
import tools from 'static/js/tools.js'
export default {
components: {
uploadAttachments
},
props: {
ruleValidate: {
default(){
return {}
},
type: Object
},
processKey: {
type: String
},
candidate: {
type: Array
}
},
data () {
return {
processStart: true,
// 提交之后显示推荐人
submitAfret: false,
// 转下一步数据
nextStep: {},
temporary: {
},
fileData: []
}
},
computed: {
...mapState([
'postData', 'processData'
])
},
methods: {
...mapMutations([
'SET_POSTDATA'
]),
submitData: function () {
// console.log(this.postData)
console.log(this.processStart)
// 验证
this.$refs.formValidate.validate(res => {
//验证通过,则提交
if (res) {
// 这里执行提交操作
}
this.$Message.error("请根据页面提示填写内容!");
})
}
}
}
</script>
如上:<slot></slot>
是每个流程的表单部分,其他则为每个流程共有的,比如附件、提交操作等。
- 用上面的资源写一个休假流程:leave
<template>
<apply :processKey="processKey" :candidate="candidate">
<!-- apply的slot部分,即为每个流程的表单部分 -->
<component :is="item.component" v-for="(item, index) in items" :config="item" :key="index">
</component>
</apply>
</template>
<script>
import apply from './../comm/apply.vue'
import {mapState, mapMutations} from 'vuex'
const getComponent = name => {
return resolve => require([`./../comm/${name}.vue`], resolve)
}
export default {
components: {
apply
},
props: {
candidate: {
type: Array
},
processKey: {
type: String
}
},
data () {
return {
//表单配置
items: [
{
component: getComponent('iProcessTitle'),
title: '流程标题',
key: 'processTitle',
required: true
},
{
component: getComponent('iSelectType'),
title: '休假类别',
key: 'leave',
required: true,
list: {
'事假': 'busy',
'病假': 'sick',
'婚假': 'marriage',
'产假': 'maternity',
'丧假': 'funeral',
'陪产假': 'paternity',
'姨妈假': 'menstruation',
'年假': 'annual'
}
},
/**
* @author Liangyuhong
* @date 2018/9/21 10:33
* @Description: 精确到分钟
*/
{
component: getComponent('iDate'),
title: '开始时间',
type: 'datetime',
required: true,
endate: true, // 需要显示结束时间
endrequired: true, // 结束时间必填
isYesterday: true // 是否可以选择当天
},
{
component: getComponent('iDays'),
title: '休假天数',
key: 'day',
required: true
},
{
component: getComponent('iRemarks'),
title: '请假理由',
key: 'state',
required: true
}
]
}
},
methods: {
...mapMutations([
'SET_POSTDATA'
]),
init: function (data) {
this.SET_POSTDATA(data)
this.$root.Bus.$emit('initPostData', data)
this.postData = data
this.postData.processInstanceId = data.processInstanceId
}
},
mounted () {
this.SET_POSTDATA({})
}
}
</script>
<style lang="less" scoped>
@import './../../../static/css/process/process.less';
</style>
这样再开发新流程的过程中就不用去重写template部分了,只需要配置好data里的items,这里指明了当前流程所需要的字段,每个字段的各种属性,其他的都基本不用动
注:以上为不完全代码,依赖于ivew,提交的数据为postData 。存的全局变量
总结
- 将每一个输入框都写成单独的,可配置的组件,借助ivew,把校验都放在单个表单组件内部完成
- 写一个公共组件apply,利用slot提供可变部分传入,把不变的,比如附件,提交这些写入这个组件,提供的便利在于不用在每一个流程去关注提交,校验等等一系列每个流程都需要的操作
- 具体到某一个流程时只需关注data的items,也就是开发一个流程,只写items就可以完成。
原文地址:https://segmentfault.com/a/1190000017306664
用slot和component实现表单共用的更多相关文章
- amazeUI表单提交验证--input框required
效果: html: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> < ...
- Vue 2.x折腾记 - (17) 基于Ant Design Vue 封装一个配置式的表单组件
前言 写了个类似上篇搜索的封装,但是要考虑的东西更多. 具体业务比展示的代码要复杂,篇幅太长就不引入了. 效果图 2019-04-25 添加了下拉多选的渲染,并搜索默认过滤文本而非值 简化了渲染的子组 ...
- Salesforce Lightning开发学习(三)Component表单初解
初步了解了Lightning的组件开发流程后,我们来认识下lightning的表单 点击对象管理器,选择对象:电影(Movie__c),创建字段 标签 API 数据类型 票价 Number__c ...
- 表单配置项写法,表单写成JSON数组套对象,一行是一个数组单位,一列是一个对象单位,然后再写一个公共组件读取这个配置,循环加载slot,外层载入slot的自定义部分,比如input select等,这种写法就是把组件嵌套改为配置方式
表单配置项写法,表单写成JSON数组套对象,一行是一个数组单位,一列是一个对象单位,然后再写一个公共组件读取这个配置,循环加载slot,外层载入slot的自定义部分,比如input select等,这 ...
- 数据表格,查询、导出共用一个form表单,实现文件流方式下载
在开发中遇到问题是这样的: 在维护老的管理系统的过程中,老板说让加导出功能:项目中,查询的筛选条件是用的表单提交的方式写的. 解决方案有两种: 一.用ajax方式导出 var array = $('# ...
- antd做form表单的组件共用,利用mapPropsToFields填写默认值
做单页应用,不管是用Vue还是React,或者其他,有一个重要的原则,就是:组件重用. 既然组件可以重用,那么当添加一个信息,和修改该信息的布局必然是一致的,这时候,最好的方法自然是利用同一个组件,在 ...
- 前端MVC Vue2学习总结(五)——表单输入绑定、组件
一.表单输入绑定 1.1.基础用法 你可以用 v-model 指令在表单控件元素上创建双向数据绑定.它会根据控件类型自动选取正确的方法来更新元素.尽管有些神奇,但 v-model 本质上不过是语法糖, ...
- 如何用elementui去实现图片上传和表单提交,用axios的post方法
下面是在vue搭建的脚手架项目中的组件component文件夹下面的upload.vue文件中的内容 <!--这个组件主要用来研究upload这个elementui的上传插件组件--> & ...
- vue的表单编辑删除,保存取消功能
过年回来第一篇博客,可能说的不是很清楚,而且心情可能也不是特别的high,虽然今天是元宵,我还在办公室11.30在加班,但就是想把写过的代码记下来,怕以后可能真的忘了.(心将塞未塞,欲塞未满) VUE ...
随机推荐
- 高性能JavaScript之加载和执行
JS在浏览器中的性能,可以认为是开发者所面临的最重要的可行性问题.这个问题因JS的阻塞特性变得复杂,也就是说当浏览器在执行JS代码时,不能同时做其他任何事情.事实上,大多数浏览器都使用单一进程来处理U ...
- Java常考面试题整理(三)
明天又要去面试,Good luck to me.,让我在这段时间换个新的工作吧. 41.在Java中,对象什么时候可以被垃圾回收? 参考答案: 当对象对当前使用这个对象的应用程序变得不可触及的时候,这 ...
- C++入门经典-例3.16-使用do-while循环进行计算
1:代码如下: // 3.16.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> usin ...
- C++入门经典-例3.10-根据输入的字符输出字符串
1:代码如下: // 3.10.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> #inc ...
- MacPorts镜像
/opt/local/etc/macports/macports.conf: rsync_server pek.cn.rsync.macports.org rsync_dir macports/rel ...
- Error-ASP.NET:编译器错误消息: CS0016: 未能写入输出文件
ylbtech-Error-ASP.NET:编译器错误消息: CS0016: 未能写入输出文件 1.返回顶部 1. “/”应用程序中的服务器错误. 编译错误 说明: 在编译向该请求提供服务所需资源的过 ...
- prism 4 模块配置 管理
本章导读: 第四章讲述了模块化应用程序开发中模块的生命周期,生成方法,实例引用的存活时间等关键内容,和经常会应用到的包含定义模块在内的7种场景(以Unity为例,也说明了MEF与Unity中可能不同的 ...
- OpenStack 2018 年终盘点
目录 文章目录 目录 前言 OpenStack 一年来的成长 Nova Cinder Neutron Ironic Cyborg Octavia Kolla Magnum Zun Kuryr 从 Op ...
- python学习笔记:(十四)面向对象
1.类(class): 用来描述具有相同的属性和方法的对象的集合.它定义了该集合中每个对象所共有的属性和方法 2.类变量: 类变量在整个实例化的对象中是公用的.类变量定义在类中且在函数体之外.类变量通 ...
- Windows客户端 Linux服务器通讯 字符编码问题
Windows下的字符编码默认是gb2312 在Linux下需要转成utf8 才能正确的看到对应的中文编码 提供转换函数 /*------------------------------------- ...