業務需求
在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就可以完成。