业务需求

在oa开发中,有许多流程,每个流程里都会有很多字段,比如流程标题、拉下选择,附件等等,有些是每个流程都会有的,有些是特有的,按常规的方法开发,就为为一个流程写一个表单,校验,提交。如果新来流程,就复制一个表达,修改需要变更的地方。这样开发会导致很多重复的代码,而且比较凌乱

简化实现

  • 将每一个输入框写成共用的,将必填校验判断也一并写入,比如:流程标题组件: iProcessTitle,使用详情看下方注释

  1. <template>
  2. <div>
  3. <div v-if="!isShow">
  4. <Row>
  5. <Col :xs="6" :md='mdModelLeft'><span class="t-span">{{config.title}}:</span></Col>
  6. <Col :xs="18" :md='mdModelRight'>
  7. <FormItem :prop="config.key" :rules="rules">
  8. <Input
  9. v-model="postData[config.key]"
  10. :placeholder="placeholder"
  11. :maxlength="config.maxLength"
  12. :disabled="config.disabled || false"
  13. ></Input>
  14. </FormItem>
  15. </Col>
  16. </Row>
  17. </div>
  18. <div v-else>
  19. <div class="cont-i" v-if="config.title">
  20. <span class="gray gray-f">{{ config.title }}</span>
  21. <div class="attachment-i">{{ postData[config.key] }}</div>
  22. </div>
  23. </div>
  24. </div>
  25. </template>
  26. <script>
  27. import { mapState } from 'vuex'
  28. var validateData = {}
  29. export default {
  30. name: "i-process-title",
  31. computed: {
  32. ...mapState([
  33. 'postData'
  34. ]),
  35. placeholder: function () {
  36. // 更具传入标题显示placeholder
  37. let placeholder = '请选择输入' + this.config.title
  38. if (this.config.maxLength) {
  39. placeholder += '(' + this.config.maxLength +'个字以内)'
  40. }
  41. return placeholder
  42. },
  43. rules: function () {
  44. return {
  45. validator: validateData,
  46. trigger: 'blur'
  47. }
  48. },
  49. isShow: function () {
  50. return this.config.isShow
  51. }
  52. },
  53. props: {
  54. // 当前输入框配置
  55. config: {
  56. default(){
  57. return {
  58. title: '流程标题', // 输入框标题
  59. key: 'processTitle', // 要提交的字段
  60. required: false, // 是否必填
  61. disabled: false, // 是否禁止编辑
  62. isShow: true, // 是否是流程发起状态 true:流程发起,展示输入框; false: 审批过程/打印,展示结果
  63. }
  64. },
  65. type: Object
  66. }
  67. },
  68. data() {
  69. // 输入校验
  70. validateData = (rule, value, callback) => {
  71. let reg = /^[0-9]*$/;
  72. // 是否必填
  73. if (this.config.required) {
  74. if (value === '' || value === undefined) {
  75. callback(new Error(this.config.title + '必填'));
  76. return
  77. }
  78. }
  79. // 纯数字校验
  80. if (this.config.type && this.config.type === 'Number') {
  81. if (!reg.test(value) && value !== '' && value !== undefined) {
  82. callback(new Error('格式不符合'));
  83. return
  84. }
  85. }
  86. callback();
  87. }
  88. return {
  89. }
  90. },
  91. methods: {
  92. },
  93. mounted(){
  94. this.postData.department = this.$store.state.department
  95. }
  96. }
  97. </script>
  98. <style scoped>
  99. </style>
  • 选择框组件: iSelectType

  1. <template>
  2. <Row>
  3. <Col :xs="6" :md='mdModelLeft'><span class="t-span">{{config.title}}:</span></Col>
  4. <Col :xs="18" :md='mdModelRight'>
  5. <FormItem :prop="config.key" :rules="rules">
  6. <Select v-model="postData[config.key]">
  7. <Option v-for="(item, key) in config.list" :value="item" :key="item">{{ key }}</Option>
  8. </Select>
  9. </FormItem>
  10. </Col>
  11. </Row>
  12. </template>
  13. <script>
  14. import UImodel from '../../assets/js/UIModel'
  15. import {mapState, mapMutations} from 'vuex'
  16. export default {
  17. name: 'i-select-type',
  18. props: {
  19. config: {
  20. default(){
  21. return {
  22. title: '是否超标', // 默认标题
  23. key: 'excessive', // 默认字段
  24. list: { // 默认列表
  25. '是': 'true',
  26. '否': 'false'
  27. }
  28. }
  29. },
  30. type: Object
  31. }
  32. },
  33. computed: {
  34. ...mapState([
  35. 'postData'
  36. ]),
  37. rules: function () {
  38. // 必填校验
  39. if (this.config.required) {
  40. return {
  41. required: true,
  42. message: '选择' + this.config.title,
  43. trigger: 'change'
  44. }
  45. }
  46. }
  47. },
  48. data () {
  49. return {
  50. mdModelLeft: UImodel.mdModelLeft,
  51. mdModelRight: UImodel.mdModelRight
  52. }
  53. }
  54. }
  55. </script>
  • 时间选择组件:iDate

  1. <template>
  2. <div>
  3. <Row>
  4. <Col :xs="6" :md='mdModelLeft'><span class="t-span">{{config.title}}</span></Col>
  5. <Col :xs="18" :md='mdModelRight'>
  6. <FormItem prop="startTimeFlag" :rules="startRules">
  7. <DatePicker
  8. :type="type"
  9. v-model="postData.startTimeFlag"
  10. :format="format"
  11. placeholder="请选择时间"
  12. @on-change="sdateChange"
  13. style="width: 200px">
  14. </DatePicker>
  15. </FormItem>
  16. </Col>
  17. </Row>
  18. <Row v-if="config.endate">
  19. <Col :xs="6" :md='mdModelLeft'><span class="t-span">结束时间:</span></Col>
  20. <Col :xs="18" :md='mdModelRight'>
  21. <FormItem prop="endTimeFlag" :rules="endRules">
  22. <DatePicker
  23. :type="type"
  24. v-model="postData.endTimeFlag"
  25. :format="format"
  26. :options="endDateOptions"
  27. placeholder="请选择时间"
  28. @on-change="edateChange"
  29. style="width: 200px">
  30. </DatePicker>
  31. </FormItem>
  32. </Col>
  33. </Row>
  34. </div>
  35. </template>
  36. <script>
  37. import UImodel from '../../assets/js/UIModel'
  38. import {mapState, mapMutations} from 'vuex'
  39. var datePassCheck = {}
  40. export default {
  41. name: 'i-date',
  42. props: {
  43. config: {
  44. default () {
  45. return {
  46. title: '开始时间'
  47. }
  48. },
  49. type: Object
  50. }
  51. },
  52. computed: {
  53. ...mapState([
  54. 'postData'
  55. ]),
  56. // 开始时间校验
  57. startRules: function () {
  58. //是否必填
  59. if (this.config.required) {
  60. return {
  61. type: 'date',
  62. required: true,
  63. message: this.config.title + '不能为空',
  64. trigger: 'change'
  65. }
  66. }
  67. },
  68. // 结束时间校验
  69. endRules: function () {
  70. // 是否必填
  71. if (this.config.endate && this.config.endrequired) {
  72. return {
  73. validator: datePassCheck, trigger: 'change'
  74. }
  75. }
  76. },
  77. // 时间显示格式
  78. format: function () {
  79. if (this.config.type === 'datetime') {
  80. this.type = 'datetime'
  81. return 'yyyy-MM-dd HH:mm'
  82. }
  83. return 'yyyy-MM-dd'
  84. }
  85. },
  86. methods: {
  87. ...mapMutations([
  88. 'SET_POSTDATAKEY'
  89. ]),
  90. sdateChange: function (val) {
  91. this.$set(this.postData, this.config.key, val)
  92. this.$set(this.postData, 'startTime', val)
  93. },
  94. edateChange: function (val) {
  95. this.postData.endTime = val
  96. }
  97. },
  98. watch: {
  99. // 开始时间改变,需清空结束时间
  100. 'postData.startTime': function (val) {
  101. let _this = this
  102. let v = this.postData.startTimeFlag
  103. let date = new Date(v)
  104. let time = date.getFullYear() + '-' +
  105. (date.getMonth() + 1) + '-' +
  106. date.getDate()
  107. this.endDateOptions.disabledDate = function (date) {
  108. return _this.config.isYesterday ? date.valueOf() < (new Date(time) - 23 * 60 * 60 * 1000) : date.valueOf() < new Date(time)
  109. // return date.valueOf() < new Date(time)
  110. }
  111. // 清空后面的日期
  112. this.postData.endTimeFlag = ''
  113. this.postData.endTime = ''
  114. this.showError = true
  115. }
  116. },
  117. data () {
  118. // 结束时间校验
  119. datePassCheck = (rule, value, callback) => {
  120. if (value === '') {
  121. callback(new Error('结束时间不能为空'))
  122. } else if (this.postData.endTime < this.postData.startTime) {
  123. callback(new Error('结束时间需大于开始时间'))
  124. } else {
  125. callback()
  126. }
  127. }
  128. return {
  129. mdModelLeft: UImodel.mdModelLeft,
  130. mdModelRight: UImodel.mdModelRight,
  131. // 结束日期的 起点规则
  132. endDateOptions: {
  133. disabledDate (date) { }
  134. },
  135. type: 'date'
  136. }
  137. },
  138. mounted () {
  139. }
  140. }
  141. </script>
  142. <style></style>
  • 如果还需要其他组件,按照上述方法添加即可,下面写申请界面的公共部分:apply

  1. <template>
  2. <Form ref="formValidate" :model="postData" :rules="ruleValidate" class="leave">
  3. <div class="disabledBox">
  4. <!-- 这里是每个流程的表单部分 -->
  5. <slot></slot>
  6. <!-- 附件组件 -->
  7. <uploadAttachments
  8. :processKey="processKey"
  9. :fileData="fileData"
  10. :fileAry="temporary.file"
  11. @deleteFileAry="deleteFileAry">
  12. </uploadAttachments>
  13. <div class="disabled" ref="disabled" v-if="submitAfret"></div>
  14. </div>
  15. <Row v-if="!submitAfret">
  16. <Col :span="6" :offset="18">
  17. <Button type="info" @click="submitData('formValidate')">转下一步</Button>
  18. </Col>
  19. </Row>
  20. </Form>
  21. </template>
  22. <script>
  23. import {mapState, mapMutations} from 'vuex'
  24. import uploadAttachments from './../process/common/uploadAttachments.vue'
  25. import tools from 'static/js/tools.js'
  26. export default {
  27. components: {
  28. uploadAttachments
  29. },
  30. props: {
  31. ruleValidate: {
  32. default(){
  33. return {}
  34. },
  35. type: Object
  36. },
  37. processKey: {
  38. type: String
  39. },
  40. candidate: {
  41. type: Array
  42. }
  43. },
  44. data () {
  45. return {
  46. processStart: true,
  47. // 提交之后显示推荐人
  48. submitAfret: false,
  49. // 转下一步数据
  50. nextStep: {},
  51. temporary: {
  52. },
  53. fileData: []
  54. }
  55. },
  56. computed: {
  57. ...mapState([
  58. 'postData', 'processData'
  59. ])
  60. },
  61. methods: {
  62. ...mapMutations([
  63. 'SET_POSTDATA'
  64. ]),
  65. submitData: function () {
  66. // console.log(this.postData)
  67. console.log(this.processStart)
  68. // 验证
  69. this.$refs.formValidate.validate(res => {
  70. //验证通过,则提交
  71. if (res) {
  72. // 这里执行提交操作
  73. }
  74. this.$Message.error("请根据页面提示填写内容!");
  75. })
  76. }
  77. }
  78. }
  79. </script>

如上:<slot></slot>是每个流程的表单部分,其他则为每个流程共有的,比如附件、提交操作等。

  • 用上面的资源写一个休假流程:leave

  1. &lt;template&gt;
  2. &lt;apply :processKey="processKey" :candidate="candidate"&gt;
  3. &lt;!-- applyslot部分,即为每个流程的表单部分 --&gt;
  4. &lt;component :is="item.component" v-for="(item, index) in items" :config="item" :key="index"&gt;
  5. &lt;/component&gt;
  6. &lt;/apply&gt;
  7. &lt;/template&gt;
  8. &lt;script&gt;
  9. import apply from './../comm/apply.vue'
  10. import {mapState, mapMutations} from 'vuex'
  11. const getComponent = name =&gt; {
  12. return resolve =&gt; require([`./../comm/${name}.vue`], resolve)
  13. }
  14. export default {
  15. components: {
  16. apply
  17. },
  18. props: {
  19. candidate: {
  20. type: Array
  21. },
  22. processKey: {
  23. type: String
  24. }
  25. },
  26. data () {
  27. return {
  28. //表单配置
  29. items: [
  30. {
  31. component: getComponent('iProcessTitle'),
  32. title: '流程标题',
  33. key: 'processTitle',
  34. required: true
  35. },
  36. {
  37. component: getComponent('iSelectType'),
  38. title: '休假类别',
  39. key: 'leave',
  40. required: true,
  41. list: {
  42. '事假': 'busy',
  43. '病假': 'sick',
  44. '婚假': 'marriage',
  45. '产假': 'maternity',
  46. '丧假': 'funeral',
  47. '陪产假': 'paternity',
  48. '姨妈假': 'menstruation',
  49. '年假': 'annual'
  50. }
  51. },
  52. /**
  53. * @author Liangyuhong
  54. * @date 2018/9/21 10:33
  55. * @Description: 精确到分钟
  56. */
  57. {
  58. component: getComponent('iDate'),
  59. title: '开始时间',
  60. type: 'datetime',
  61. required: true,
  62. endate: true, // 需要显示结束时间
  63. endrequired: true, // 结束时间必填
  64. isYesterday: true // 是否可以选择当天
  65. },
  66. {
  67. component: getComponent('iDays'),
  68. title: '休假天数',
  69. key: 'day',
  70. required: true
  71. },
  72. {
  73. component: getComponent('iRemarks'),
  74. title: '请假理由',
  75. key: 'state',
  76. required: true
  77. }
  78. ]
  79. }
  80. },
  81. methods: {
  82. ...mapMutations([
  83. 'SET_POSTDATA'
  84. ]),
  85. init: function (data) {
  86. this.SET_POSTDATA(data)
  87. this.$root.Bus.$emit('initPostData', data)
  88. this.postData = data
  89. this.postData.processInstanceId = data.processInstanceId
  90. }
  91. },
  92. mounted () {
  93. this.SET_POSTDATA({})
  94. }
  95. }
  96. &lt;/script&gt;
  97. &lt;style lang="less" scoped&gt;
  98. @import './../../../static/css/process/process.less';
  99. &lt;/style&gt;

这样再开发新流程的过程中就不用去重写template部分了,只需要配置好data里的items,这里指明了当前流程所需要的字段,每个字段的各种属性,其他的都基本不用动

注:以上为不完全代码,依赖于ivew,提交的数据为postData 。存的全局变量

总结

  • 将每一个输入框都写成单独的,可配置的组件,借助ivew,把校验都放在单个表单组件内部完成
  • 写一个公共组件apply,利用slot提供可变部分传入,把不变的,比如附件,提交这些写入这个组件,提供的便利在于不用在每一个流程去关注提交,校验等等一系列每个流程都需要的操作
  • 具体到某一个流程时只需关注data的items,也就是开发一个流程,只写items就可以完成。

原文地址:https://segmentfault.com/a/1190000017306664

用slot和component实现表单共用的更多相关文章

  1. amazeUI表单提交验证--input框required

    效果: html: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> < ...

  2. Vue 2.x折腾记 - (17) 基于Ant Design Vue 封装一个配置式的表单组件

    前言 写了个类似上篇搜索的封装,但是要考虑的东西更多. 具体业务比展示的代码要复杂,篇幅太长就不引入了. 效果图 2019-04-25 添加了下拉多选的渲染,并搜索默认过滤文本而非值 简化了渲染的子组 ...

  3. Salesforce Lightning开发学习(三)Component表单初解

    初步了解了Lightning的组件开发流程后,我们来认识下lightning的表单 点击对象管理器,选择对象:电影(Movie__c),创建字段 标签 API 数据类型  票价  Number__c ...

  4. 表单配置项写法,表单写成JSON数组套对象,一行是一个数组单位,一列是一个对象单位,然后再写一个公共组件读取这个配置,循环加载slot,外层载入slot的自定义部分,比如input select等,这种写法就是把组件嵌套改为配置方式

    表单配置项写法,表单写成JSON数组套对象,一行是一个数组单位,一列是一个对象单位,然后再写一个公共组件读取这个配置,循环加载slot,外层载入slot的自定义部分,比如input select等,这 ...

  5. 数据表格,查询、导出共用一个form表单,实现文件流方式下载

    在开发中遇到问题是这样的: 在维护老的管理系统的过程中,老板说让加导出功能:项目中,查询的筛选条件是用的表单提交的方式写的. 解决方案有两种: 一.用ajax方式导出 var array = $('# ...

  6. antd做form表单的组件共用,利用mapPropsToFields填写默认值

    做单页应用,不管是用Vue还是React,或者其他,有一个重要的原则,就是:组件重用. 既然组件可以重用,那么当添加一个信息,和修改该信息的布局必然是一致的,这时候,最好的方法自然是利用同一个组件,在 ...

  7. 前端MVC Vue2学习总结(五)——表单输入绑定、组件

    一.表单输入绑定 1.1.基础用法 你可以用 v-model 指令在表单控件元素上创建双向数据绑定.它会根据控件类型自动选取正确的方法来更新元素.尽管有些神奇,但 v-model 本质上不过是语法糖, ...

  8. 如何用elementui去实现图片上传和表单提交,用axios的post方法

    下面是在vue搭建的脚手架项目中的组件component文件夹下面的upload.vue文件中的内容 <!--这个组件主要用来研究upload这个elementui的上传插件组件--> & ...

  9. vue的表单编辑删除,保存取消功能

    过年回来第一篇博客,可能说的不是很清楚,而且心情可能也不是特别的high,虽然今天是元宵,我还在办公室11.30在加班,但就是想把写过的代码记下来,怕以后可能真的忘了.(心将塞未塞,欲塞未满) VUE ...

随机推荐

  1. CentOS7 服务器上如何安装python3

    1.官网下载python3的源码包 网址:https://www.python.org/ 进去之后点击导航栏的Downloads,也可以鼠标放到Downloads上弹出菜单选择Source code, ...

  2. 工具类--BeanUtils----Bean转换工具

    package com.zhouyy.netBank.util; import java.beans.PropertyDescriptor; import java.lang.reflect.Fiel ...

  3. JS框架_(JQuery.js)点赞按钮动画

    百度云盘 传送门 密码: 0ihy 点赞按钮动画效果: (点击一次随机生成一颗小爱心,作为点赞动画~) <!doctype html> <html lang="en&quo ...

  4. JS框架_(coolShow.js)图片旋转动画特效

    百度云盘 传送门 密码:ble6 coolShow.js插件图片旋转动画效果 <!DOCTYPE HTML> <head> <meta http-equiv=" ...

  5. C++入门经典-例2.1-利用实数精度进行实数比较

    1:代码如下: // 2.1.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" void main() { float eps = 0.000 ...

  6. redis哨兵集群搭建

    下载redis jar包redis-4.0.11.tar.gz放在/data/redis目录下 解压 命令:tar -zxvf redis-4.0.11.tar.gz 解压后如图所示 在/usr/lo ...

  7. Linux高级调试与优化——用户态堆

    内存问题是软件世界的住房问题 嵌入式Linux系统中,物理内存资源通常比较紧张,而不同的进程可能不停地分配和释放不同大小的内存,因此需要一套高效的内存管理机制. 内存管理可以分为三个层次,自底向上分别 ...

  8. 浏览器端-W3School-HTML:HTML DOM Select 对象

    ylbtech-浏览器端-W3School-HTML:HTML DOM Select 对象 1.返回顶部 1. HTML DOM Select 对象 Select 对象 Select 对象代表 HTM ...

  9. 【flask】环境配置-python-dotenv的使用

    [自动发现程序实例] 一般来说,在执行flask run命令运行程序前,我们需要提供程序实例所在模块的位置 . Flask会自动探测程序实例,自动探测存在下面这些规则: 从当前目录寻找app.py和w ...

  10. flutter tabbar创建与显示

    效果图 main.dart import 'package:flutter/material.dart'; import 'pages/index_page.dart'; void main() =& ...