element-ui 通用表單封裝及VUE JSX應用

CoderTan發表於2019-04-10

一、存在及需要解決的問題

一般在做後臺OA的時候會發現表單重複程式碼比較多,且邏輯基本一樣,每次新加一個表單都需要拷貝基本一致的程式碼結構,然後只是簡單地修改對應的欄位進行開發

二、預期結果

提取重複的表單邏輯形成通用的元件,通過約定的JSON資料結構配置生成表單

1、使用方法

<common-form :form-option="formOption" :is-reset-form-flag="isResetFormFlag"></common-form>
複製程式碼

接收的props:

isResetFormFlag:是否更新表單內容標誌,用於觸發更新表單的formModel

formOption:表單配置,下有詳細配置說明

isDisabled:表單是否可編輯

2、單一表單組配置

{
  name: 'channel-form',
  data: {},
  items: [
    {
      label: '型別',
      name: 'biz_type',
      type: 'select',
      dataList: [{
        index: 1,
        text: '業務部'
      }]
    }
  ],
  rules: {
    name: [{ required: true, message: '請輸入產品名稱', trigger: 'blur' }]
  },
  btnList: [{
    text: '儲存',
    type: 'primary',
    onClick: this.commitForm
  }]
}
複製程式碼

3、多表單組配置

formOption: {
 name: 'channel-form',
 data: {},
 groups: [{
   title: '',// 組標題
   tips: ''// 組提示
   items: [] // 組表單項,和單一組配置一致
 }]
}
複製程式碼

三、實現邏輯

根據配置輸出不同的form-item,需要特殊處理的表單項通過jsx由使用的地方自定義實現

什麼是JSX:cn.vuejs.org/v2/guide/re…

文件:egoist.moe/2017/09/21/…

使用JSX原因:表單包含了大部分的選項,但是也有很多不確定的情況需要依賴外部自己實現,相對於template的方式jsx使用起來更加靈活

四、配置文件

節點 描述 型別 是否必須 備註
name 表單名 String 預設名 oa-form
data 表單資料 Object 用於編輯場景非同步請求的表單填充資料
groups 表單組 Array groups 和 items 不應該同時存在,groups 中包含了items,如果groups為空取外部的items渲染,groups不為空僅渲染groups組內容
items 表單項 Array 支援的type型別:輸入框:input、textarea;多選框:checkbox;單選框:radio;下拉選單:select
rules 表單校驗規則 Object 節點名需要與items配置的name一一對應
btnList 按鈕列表 Array 會在回撥函式包含表單的資料及表單引用

六、VUE JSX 遇到的一些問題

v-model支援: babel-plugin-jsx-v-model

sync 修飾符寫法

visible={ this.dialogImgVisible } {...{on: {'update:visible': (val) => { this.dialogImgVisible = val }}}}
複製程式碼

七、部分實現程式碼

1、生成列表

generateList (itemObj) {
  let itemEle = []
  for (let index = 0; index < itemObj.dataList.length; index++) {
    const item = itemObj.dataList[index]
    switch (itemObj.type) {
      // 下拉選單
      case 'select':
        itemEle.push(<el-option key={ item.index } label={ item.text } value={ item.index }></el-option>)
        break
      // 多選框
      case 'checkbox':
        itemEle.push(<el-checkbox label={ item.index }>{ item.text }</el-checkbox>)
        break
      // 單選框
      case 'radio':
        itemEle.push(<el-radio label={ item.index }>{ item.text }</el-radio>)
        break
    }
  }
  return itemEle
}
複製程式碼

2、生成下拉選單

generateSelect (item) {
  return <el-select v-model={ this.formModel[item.name] } style={ item.style || this.defaultStyle }>{ this.generateList(item) }</el-select>
}
複製程式碼

備註:其他項實現類似

3、render函式

render (h) {
    let ele = []
    // 表單內容
    if (this.isGroup) {
      ele = this.generateGroup()
    } else {
      ele = this.generateFormItems(this.formOption.items)
    }
    // 按鈕列表
    let btnListEle = []
    this.formOption.btnList.forEach((btn) => {
      btnListEle.push(<el-button type={ btn.type } on-click={ () => { btn.onClick(this.$refs[this.formName], this.formModel) } }>{ btn.text }</el-button>)
    })
    return (
      <div class="oa-form-container">
        <el-form ref={ this.formName } model={ this.formModel } rules={ this.formOption.rules } inline={ this.inline } disabled={ this.isDisabled } label-width={ this.formOption.labelWidth || '150px'}>
          { ele }
          <el-form-item label-width={ this.isGroup ? '0' : '150px'}>{ btnListEle }</el-form-item>
        </el-form>
      </div>
    )
  }
複製程式碼

八、最後貼一個表格的封裝

<script>
export default {
  props: {
    // 表格列
    columns: {
      type: Array,
      default: _ => { return [] }
    },
    // 表格資料
    tableData: {
      type: Array,
      default: _ => { return [] }
    },
    // loading 標誌
    loading: {
      type: Boolean,
      default: false
    }
  },
  methods: {
    sortChange (obj) {
      this.$emit('sortChange', obj)
    }
  },
  render () {
    return (
      <el-table border stripe v-loading={ this.loading } element-loading-text="拼命載入中" data={ this.tableData } on-sort-change={ obj => { this.sortChange(obj) } } style="width: 100%">
        {
          this.columns.map(columnObj => {
            return <el-table-column prop={ columnObj.prop } label={ columnObj.label } sortable={ columnObj.sortable } width={ columnObj.width }
              {...{
                scopedSlots: {
                  default: scope => {
                    return columnObj.hasOwnProperty('render') ? columnObj.render(scope.index, scope.row) : scope.row[columnObj.prop]
                  }
                }
              }}
            >
            </el-table-column>
          })
        }
      </el-table>
    )
  }
}
</script>
複製程式碼

1、使用方法

<common-table :columns="columns" :table-data="tableData" :loading="loading" @sort-change="sortChange"></common-table>
複製程式碼

2、columns 配置

{
    label: '',
    prop: '',
    width: '100',
    sortable: true
    render: ()=>{}
}
複製程式碼

自定義配置渲染,傳入render函式,如果有render函式,優先使用render函式結果

相關文章