el-form的二次封装,el-form嵌套使用

背景:公司项目的每一个页面基本上都有el-form。而且不同的el-form里面有部分el-form-item是相同的,于是就想把这部分相同的el-form-item封装成一个el-form即组件d2-page-form。为什么要封装成一个el-form?因为需要配置rules。d2-page-form是页面原来的el-form的子组件。在封装组件A的过程中,又想到了把el-form二次封装,这样就可以满足页面中不再写el-form、el-form-item的html代码了,直接封装一个组件,给组件传入对应的属性就可以了。
组件d2-page-form代码:

<template>
  <div>
    <el-form :inline="inline" :label-width="labelWidth" :model="data" :class="[formClass]" :rules="rules" ref="form">
      <template v-if="isCustomDept">
        <el-form-item label="自定义部门1" prop="customDeptId1">
          <el-select class="width-200" size='mini' v-model="data.customDeptId1" placeholder="请选择" clearable filterable
            allow-create default-first-option :disabled="disabledCustomDept" v-loading="loading1">
            <el-option v-for="item in customDept.departmentData1" :key="item.deptId" :label="item.deptName"
              :value="item.deptId">
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="自定义部门2" prop="customDeptId2">
          <el-select class="width-200" size='mini' v-model="data.customDeptId2" placeholder="请选择" clearable filterable
            allow-create default-first-option :disabled="disabledCustomDept" v-loading="loading2">
            <el-option v-for="item in customDept.departmentData2" :key="item.deptId" :label="item.deptName"
              :value="item.deptId">
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="自定义部门3" prop="customDeptId3">
          <el-select class="width-200" size='mini' v-model="data.customDeptId3" placeholder="请选择" clearable filterable
            allow-create default-first-option :disabled="disabledCustomDept" v-loading="loading3">
            <el-option v-for="item in customDept.departmentData3" :key="item.deptId" :label="item.deptName"
              :value="item.deptId">
            </el-option>
          </el-select>
        </el-form-item>
      </template>
      <template v-if="getConfigList.length">
        <el-form-item v-for="(item, index) in getConfigList" :key="index" :prop="item.value" :label="item.label"
          :class="item.className">
          <!-- solt -->
          <template v-if="item.type === 'slot'">
            <slot :name="'form-' + item.value" />
          </template>
          <!-- 普通输入框 -->
          <el-input v-if="item.type === 'input' || item.type === 'password'" v-model="data[item.value]"
            :type="item.type" clearable :disabled="item.disabled" :placeholder="getPlaceholder(item)"
            @focus="handleEvent(item.event)" />
          <!-- 文本输入框 -->
          <el-input v-if="item.type === 'textarea'" v-model.trim="data[item.value]" :type="item.type"
            :maxlength="item.maxlength" show-word-limit clearable :disabled="item.disabled"
            :placeholder="getPlaceholder(item)" :autosize="item.autosize || {minRows: 2, maxRows: 10}"
            @focus="handleEvent(item.event)" />
          <!-- 计数器 -->
          <el-input-number v-if="item.type === 'inputNumber'" v-model="data[item.value]" size="small" :min="item.min"
            :max="item.max" controls-position="right" @change="handleEvent(item.event)" />
          <!-- 选择框 -->
          <el-select v-if="item.type === 'select'" v-model="data[item.value]" :disabled="item.disabled" clearable
            :filterable="item.filterable" :placeholder="getPlaceholder(item)"
            @change="handleEvent(item.event, data[item.value])">
            <el-option v-for="(childItem, childIndex) in listTypeInfo[item.list]" :key="childIndex"
              :label="childItem.key" :value="childItem.value" />
          </el-select>
          <!-- 单选框 -->
          <el-radio-group v-if="item.type === 'radio'" v-model="data[item.value]" :disabled="item.disabled"
            :placeholder="getPlaceholder(item)" @change="handleEvent(item.event, data[item.value])">
            <el-radio-button v-for="(childItem, childIndex) in listTypeInfo[item.list]" :key="childIndex"
              :label="childItem.value">{{childItem.key}}</el-radio-button>
          </el-radio-group>
          <!-- 日期选择框 -->
          <el-date-picker v-if="item.type === 'date'" v-model="data[item.value]" :type="item.dateType"
            :picker-options="datePickerOptions[item.pickerOptions]" clearable :disabled="item.disabled"
            :default-time="item.defaultTime" :placeholder="getPlaceholder(item)" @focus="handleEvent(item.event)"
            start-placeholder="请选择开始日期" end-placeholder="请选择结束日期" />
          <!-- 信息展示框 -->
          <el-tag v-if="item.type === 'tag'">
            {{ $fn.getDataName({dataList: listTypeInfo[item.list], value: 'value', label: 'key', data: data[item.value]}) || '-' }}
          </el-tag>
        </el-form-item>
      </template>
      <slot></slot>
    </el-form>
    <el-divider v-if="inline&&getConfigList.length"></el-divider>
  </div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
  name: 'd2-custom-dept-form',
  props: {
    labelWidth: {
      type: String,
      default: '100px'
    },
    rules: {
      type: Object
    },
    inline: {
      type: Boolean,
      default: true
    },
    // 自定义类名
    className: {
      type: String
    },
    // 表单数据
    data: {
      type: Object
    },
    // 相关的列表
    listTypeInfo: {
      type: Object
    },
    refObj: {
      type: Object
    },
    // 相关字段
    fieldList: {
      type: Array,
      default () {
        return []
      }
    },
    // 是否是自定义部门
    isCustomDept: {
      type: Boolean,
      default: true
    },
    // 自定义部门是否能选
    disabledCustomDept: {
      type: Boolean,
      default: false
    },
    // 是否是内嵌表单
    embed: {
      type: Boolean,
      default: true
    }
  },
  data () {
    return {
      datePickerOptions: {
        // 不能选择当前之前的时间
        disabledCurrentBefore: {
          disabledDate (time) {
            const start = new Date()
            start.setTime(start.getTime() - 3600 * 1000 * 24 * 1)
            return time.getTime() < start.getTime()
          }
        }
      },
      loading1: false,
      loading2: false,
      loading3: false,
      customDept: {}
    }
  },
  computed: {
    ...mapGetters(['appId']),
    getConfigList () {
      if (this.fieldList.length) {
        return this.fieldList.filter(item => !item.hasOwnProperty('show') || (item.hasOwnProperty('show') && item.show))
      } else {
        return []
      }
    },
    formClass () {
      if (this.inline) {
        if (this.embed) {
          return ''
        } else {
          return 'search-form-box'
        }
      } else {
        return ''
      }
    }
  },
  methods: {
    // 得到placeholder的显示
    getPlaceholder (row) {
      let placeholder
      if (row.type === 'input' || row.type === 'textarea') {
        placeholder = '请输入' + row.label
      } else if (row.type === 'select' || row.type === 'time' || row.type === 'date') {
        placeholder = '请选择' + row.label
      } else {
        placeholder = row.label
      }
      return placeholder
    },
    // 绑定的相关事件
    handleEvent (e) {
      this.$emit('handleEvent', e)
    },
    getDepartment () {
      this.loading1 = true
      this.loading2 = true
      this.loading3 = true
      this.$http.post('/cl-applets/cms/content/department/search', {
        customDeptLevel: 'CUSTOM_DEPT_1',
        appId: this.appId
      }).then((res) => {
        this.customDept.departmentData1 = res.data.filter(item => item.deptId)
        this.loading1 = false
        this.saveDept('customDept.departmentData1', this.customDept.departmentData1)
      })
      this.$http.post('/cl-applets/cms/content/department/search', {
        customDeptLevel: 'CUSTOM_DEPT_2',
        appId: this.appId
      }).then((res) => {
        this.customDept.departmentData2 = res.data.filter(item => item.deptId)
        this.loading2 = false
        this.saveDept('customDept.departmentData2', this.customDept.departmentData2)
      })
      this.$http.post('/cl-applets/cms/content/department/search', {
        customDeptLevel: 'CUSTOM_DEPT_3',
        appId: this.appId
      }).then((res) => {
        this.customDept.departmentData3 = res.data.filter(item => item.deptId)
        this.loading3 = false
        this.saveDept('customDept.departmentData3', this.customDept.departmentData3)
      })
    },
    saveDept (path, value) {
      this.$store.dispatch('d2admin/db/set', {
        dbName: 'sys',
        path: path,
        value: value,
        user: true
      }, {
        root: true
      })
    },
    async initDept () {
      if (this.isCustomDept) {
        this.customDept = await this.$store.dispatch('d2admin/db/get', {
          dbName: 'sys',
          path: 'customDept',
          defaultValue: {},
          user: true
        }, {
          root: true
        })
        if (!this.customDept || !this.customDept.departmentData1) {
          this.getDepartment()
        }
      }
    }
  },
  watch: {
    appId () {
      if (this.isCustomDept) {
        this.getDepartment()
      }
    },
    data: {
      handler: function (val) {
        // 将form实例返回到父级
        this.$emit('update:refObj', this.$refs.form)
      },
      deep: true // 深度监听
    }
  },
  mounted () {
    this.initDept()
    // 将form实例返回到父级
    this.$emit('update:refObj', this.$refs.form)
  }
}
</script>
<style scoped>
.search-form-box {
  background: #f5f7fa;
  padding: 20px;
}
</style>

关于组件d2-page-form在页面的使用:

<template>
  <d2-container class="page" v-loading="loading">
    <template slot="header">
      <el-button type="primary" plain icon="el-icon-plus" @click="addmember">新建口令</el-button>
    </template>
    <d2-page-form :data="searchForm.data" :ref-obj.sync="searchForm.ref" :fieldList="searchForm.fieldList"
      :embed="false">
      <slot>
        <el-form-item>
          <el-button type="primary" @click="searchInput">筛选</el-button>
        </el-form-item>
      </slot>
    </d2-page-form>
    <d2-table :columns="columns" :table-data="list" :pagination="pagination" @page-size-change="handleOnPageSizeChange"
      @page-current-change="handleOnCurrntChange">
      <template slot-scope="{ scope }" slot="cz">
        <el-button type="text" @click="handleLook(scope.row)">查看</el-button>
        <el-button type="text" @click="handleEdit(scope.row)" class="d2-ml">编辑</el-button>
      </template>
      <template slot="status" slot-scope="{ scope }">
        <span v-if="scope.row.status === 'NOT_STARTED'" style="color: #409eff">即将开场</span>
        <span v-else-if="scope.row.status === 'OPENED'" style="color: #13ce66">进行中</span>
        <span v-else-if="scope.row.status === 'FINISHED'" style="color: #ff4949">已结束</span>
      </template>
    </d2-table>
    <el-dialog :title="isEdit === false ? '新建口令' : '编辑口令'" center :visible.sync="dialogVisible" width="50%"
      :before-close="beforeClose">
      <d2-page-form :data="commandForm.data" :ref-obj.sync="commandForm.ref" :rules="commandForm.rules" :inline="false"
        :fieldList="commandForm.fieldList" :list-type-info="commandForm.listTypeInfo" labelWidth="25%">
      </d2-page-form>
      <span slot="footer">
        <el-button type="primary" @click="submit">确定</el-button>
        <el-button @click="reset">取消</el-button>
      </span>
    </el-dialog>
  </d2-container>
</template>

<script>
import { mapGetters } from 'vuex'
export default {
  name: 'command',
  data () {
    var validatorStartDate = (rule, value, callback) => {
      if (!value) {
        callback(new Error('请选择开始时间'))
      } else {
        if (this.$moment(new Date()).isAfter(this.$moment(value))) {
          callback(new Error('开始时间不能早于当前时间'))
        } else {
          callback()
        }
      }
    }
    var validatorEndDate = (rule, value, callback) => {
      if (!value) {
        callback(new Error('请选择结束时间'))
      } else {
        if (this.$moment(this.commandForm.data.startTime).isAfter(this.$moment(value))) {
          callback(new Error('结束时间不能早于开始时间'))
        } else {
          callback()
        }
      }
    }
    return {
      loading: false,
      dialogVisible: false,
      columns: [
        {
          label: '口令时间范围',
          key: 'timeRange',
          width: 170
        }, {
          label: '自定义部门1',
          key: 'customDeptId1',
          width: 100
        }, {
          label: '口令名称',
          key: 'name'
        }, {
          label: '口令状态',
          key: 'status'
        }, {
          label: '口令描述',
          key: 'description'
        }, {
          label: '判分模式',
          key: 'scoreModeDisplay'
        }, {
          label: '允许刷分次数',
          key: 'joinCount',
          width: 120
        }, {
          label: '允许参与人数',
          key: 'joinUserCount',
          width: 140
        }, {
          label: '允许单个用户的练习次数',
          key: 'singlePersonPracticeCount',
          width: 180
        }, {
          label: '参与人数',
          key: 'joinUv'
        }, {
          label: '每人最大参与次数',
          key: 'singlePersonJoinCount',
          width: 140
        }, {
          label: '排序',
          key: 'sort'
        }, {
          label: '操作',
          key: 'cz',
          fixed: 'right',
          width: 100
        }
      ],
      list: [],
      commandForm: {
        ref: null,
        data: {},
        fieldList: [{
          label: '判分规则', value: 'scoreMode', type: 'radio', list: 'scoreModeList'
        }, {
          label: '开始时间', value: 'startTime', type: 'date', dateType: 'datetime', pickerOptions: 'disabledCurrentBefore'
        }, {
          label: '结束时间', value: 'endTime', type: 'date', dateType: 'datetime', pickerOptions: 'disabledCurrentBefore'
        }, {
          label: '口令名称', value: 'name', type: 'input', className: 'width-300'
        }, {
          label: '口令内容', value: 'content', type: 'textarea', className: 'width-300', maxlength: 350
        }, {
          label: '允许刷分次数', value: 'joinCount', type: 'inputNumber', min: 0
        }, {
          label: '允许参与人数', value: 'joinUserCount', type: 'inputNumber', min: 0
        }, {
          label: '允许单个用户的练习次数', value: 'singlePersonPracticeCount', type: 'inputNumber', min: 0
        }, {
          label: '每人最大参与次数', value: 'singlePersonJoinCount', type: 'inputNumber', min: 0
        }, {
          label: '排序', value: 'sort', type: 'inputNumber', min: 0
        }],
        rules: {
          startTime: [{ required: true, validator: validatorStartDate, trigger: 'change' }],
          endTime: [{ required: true, validator: validatorEndDate, trigger: 'change' }],
          name: [{ required: true, message: '请输入口令名称', trigger: 'blur' }],
          content: [{ required: true, message: '请输入口令内容', trigger: 'blur' }],
          chainDeptIdList: [{ required: true, message: '请选择连锁', trigger: 'blur' }],
          scoreMode: [{ required: true, message: '请选择判分规则', trigger: 'blur' }],
          customDeptId1: [{ required: true, message: '请选择自定义部门1', trigger: 'blur' }]
        },
        listTypeInfo: {
          scoreModeList: [{ key: '接口识别', value: 'AI' }, { key: '自适应识别', value: 'DEFAULT' }]
        }
      },
      isClicked: false,
      pagination: {
        auto: false,
        total: 0,
        pageSize: 20,
        current: 1
      },
      isEdit: false,
      searchForm: {
        ref: null,
        data: {},
        fieldList: [{
          label: '口令名称', value: 'name', type: 'input', className: 'width-300'
        }, {
          label: '口令时间', value: 'time', type: 'date', dateType: 'datetimerange', defaultTime: ['00:00:00', '23:59:59']
        }]
      }
    }
  },
  computed: {
    ...mapGetters(['appId'])
  },
  methods: {
    getList (searchData) {
      this.dialogVisible = false
      this.loading = true
      let data = {
        pageAsc: false,
        pageCurrent: this.pagination.current,
        pageSearchCount: true,
        pageSize: this.pagination.pageSize,
        appId: this.appId
      }
      if (searchData) {
        data = Object.assign(searchData, data)
      }
      this.$http.post('/cl-applets/word/search', data).then((res) => {
        this.loading = false
        this.pagination.total = Number(res.data.total)
        if (res.data.records) {
          this.list = res.data.records.map(item => {
            item.scoreModeDisplay = item.scoreMode === 'AI' ? '接口识别' : '自适应识别'
            item.timeRange = `${item.startTime}-${item.endTime}`
            return item
          })
        } else {
          this.list = []
        }
      }).catch((res) => {
        this.loading = false
      })
    },
    handleOnPageSizeChange (val) {
      this.pagination.pageSize = val
      this.pagination.current = 1
      this.getList()
    },
    handleOnCurrntChange (val) {
      this.pagination.current = val
      this.getList()
    },
    searchInput () {
      this.pagination.current = 1
      let searchData = { ...this.searchForm.data }
      if (searchData.time) {
        searchData.beginDateTime = this.$moment(searchData.time[0]).format(
          'YYYY-MM-DD HH:mm:ss'
        )
        searchData.endDateTime = this.$moment(searchData.time[1]).format(
          'YYYY-MM-DD HH:mm:ss'
        )
      }
      delete searchData.time
      this.getList(searchData)
    },
    addmember () {
      this.isEdit = false
      this.isClicked = false
      this.dialogVisible = true
      this.commandForm.data = {}
    },
    beforeClose () {
      this.dialogVisible = false
    },
    reset () {
      this.dialogVisible = false
    },
    handleLook (row) {
      this.$router.push({
        path: `/careflow/content/command/detail/${row.id}?data=${JSON.stringify(row)}`
      })
    },
    handleEdit (row) {
      this.isEdit = true
      this.dialogVisible = true
      this.commandForm.data = { ...row }
      this.isClicked = false
    },
    submit () {
      if (this.isClicked) {
        return
      }
      this.commandForm.ref.validate((valid) => {
        if (valid) {
          this.dialogVisible = false
          this.loading = true
          let data = { ...this.commandForm.data }
          data.appId = this.appId
          data.startTime = this.$moment(this.commandForm.data.startTime).format('YYYY-MM-DD HH:mm:ss')
          data.endTime = this.$moment(this.commandForm.data.endTime).format('YYYY-MM-DD HH:mm:ss')
          if (this.isEdit) {
            delete data.scoreModeDisplay
            delete data.timeRange
          }
          this.$http.post('/cl-applets/word', data).then((res) => {
            this.loading = false
            if (res.code === '200' || res.code === 200) {
              this.$message({
                message: '操作成功!',
                type: 'success'
              })
              this.getList()
            }
            this.isClicked = true
          }).catch((res) => {
            this.loading = false
            this.isClicked = true
          })
        }
      })
    }
  },
  mounted () {
    this.getList()
  }
}
</script>

<style scoped>
</style>

参考了以下博客:
组件化页面:封装el-form
配置化el-form的二次封装之思路分析附上代码可直接使用

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值