对话框的表单校验——模板笔记

目的:

减少重复工作、简化开发速度

案例:

1.设置一个对话框:

<el-dialog
    width="50%"
    top="60px"
    :title='dialogTitle'
    :visible.sync="dialogVisible"
    :show-close="false"
    :append-to-body="false"
    :close-on-click-modal='false'
    :lock-scroll="false"
>
  111
</el-dialog>

 

2.抽离表单:

<template>
  <div>
    <el-form
      :model="form"
      :rules="rules"
      ref="ruleForm"
      label-width="120px"
    >
      <el-form-item
        label="中文名"
        prop="name">
        <template slot="label">
          <span>
            <span>特征中文名</span>
            <el-tooltip
              content="同一业务下'中文名'不可重名"
              placement="bottom"><i class="fa fa-info-circle"/>
            </el-tooltip>
          </span>
        </template>
        <el-input
          v-model="form.name"
          placeholder="请输入中文名"/>
      </el-form-item>
      <el-form-item>
        <span class="form-item-submit">
          <el-button
            @click="cancelForm">取消</el-button>
          <el-button
            type="primary"
            :loading="loadSubmit"
            @click="submitForm">确定</el-button>
        </span>
      </el-form-item>
    </el-form>
    <!--嵌套的对话框-->
    <el-dialog
      title="特征模板列表"
      width="50%"
      top="60px"
      :visible.sync="dialogVisibleNest"
      :append-to-body="true"
      :show-close="false"
      :close-on-click-modal='false'
      :lock-scroll="false"
    >
      <div>222
      </div>
    </el-dialog>
  </div>
</template>

<script>

export default {
  props: {// 需要的参数统一传过来
    formInfo: {
      type: Object,
      default () {
        return {
          isEdit: false, // 新建与编辑
          allName: [],
          form: {
            name: ''
          }
        }
      }
    }
  },
  data () {
    const validateName = (rule, value, callback) => {
      if (value) {
        const rtx = /^[\u4E00-\u9FA5a-zA-Z0-9_]+$/
        if (!value.match(rtx)) {
          callback(new Error('只允许中英文、数字、下划线'))
        } else if (this.formInfo.allName.indexOf(value) !== -1) {
          callback(new Error('该命名已存在'))
        } else {
          callback()
        }
      } else {
        callback(new Error('不能为空'))
      }
    }
    const validateType = (rule, value, callback) => {
      if (value) {
        const rtx = /^[a-zA-Z][a-zA-Z0-9_]{4,15}$/
        if (!value.match(rtx)) {
          callback(new Error('字母开头,允许5-16字节,允许字母数字下划线'))
        } else if (this.formInfo.allName.indexOf(value) !== -1) {
          callback(new Error('该命名已存在'))
        } else {
          callback()
        }
      } else {
        callback(new Error('不能为空'))
      }
    }
    // const validatePass = (rule, value, callback) => {
    //   if (value === '') {
    //     callback(new Error('不可为空'))
    //   } else {
    //     let result = this.removeStrNotes(value) // 去除注释
    //     if (typeof result === 'string') {
    //       try {
    //         let obj = JSON.parse(result) // 转换成json对象
    //         if (typeof obj === 'object' && obj) {
    //           callback()
    //         } else {
    //           callback(new Error('json格式有误'))
    //         }
    //       } catch (e) {
    //         callback(new Error(e))
    //       }
    //     } else {
    //       callback(new Error('json格式有误'))
    //     }
    //   }
    // }
    return {
      form: {
        name: '',
        type: '',
        user: '',
        checked: 0
      },
      rules: {
        name: [
          { required: true, validator: validateName, trigger: 'blur' }
        ],
        type: [
          { required: true, validator: validateType, trigger: 'blur' }
        ],
        checked: [
          { required: true, message: '请选择是否使用模板', trigger: 'change' }
        ],
        user: [// 多选是数组
          { required: false, type: 'array', message: '请输入关键字,匹配特征关系人,可多选', trigger: 'change' }
        ]
      },
      dialogVisibleNest: false, // 查看嵌套的对话框
      loadSubmit: false // 提交
    }
  },
  watch: {
    'formInfo': {
      immediate: true,
      handler (val) { // 初始化
        console.log(val, '初始化')
        if (!val.isEdit) {
          this.allName = val.allName
          this.form = {
            name: '',
          }
        } else{
          this.allName = []
          this.form = val.form
        }
        this.$nextTick(function () {
          // this.$refs['ruleForm'].resetFields()// 清空表单内容
          this.$refs['ruleForm'].clearValidate()// 清空报错
          this.$refs['inputFocus'].$el.querySelector('input').focus()
        })
      }
    }
  },
  methods: {
    submitForm () {
      this.$refs['ruleForm'].validate((valid) => {
        if (valid) {
          this.$emit('form-change', this.form, this.formInfo.type)// 回调:成功提交
        }
      })
    },
    cancelForm () {
      this.$emit('form-change')// 回调:关闭对话框
      this.$refs['ruleForm'].resetFields()// 清空表单内容
      this.$refs['ruleForm'].clearValidate()// 清空报错
    }
  }
}
</script>

 

【实例】:可编辑的表格:对话框形式+下拉选项+表格内容编辑

<template>
  <div>
    <el-table 
    ref="multipleTable" 
    :data="tableData" 
    border 
    @selection-change="handleSelectionChange">
    <el-table-column type="selection" width="55">
    </el-table-column>
      <el-table-column label="名称(弹框编辑)" align="center" prop="name">
        <template slot-scope="scope">
            <editRowDialog :allName="allName" :scope="scope" @dialog-change="dialogChange"></editRowDialog>
        </template>
      </el-table-column>
      <el-table-column label="类型(点击切换)" align="center" prop="type">
        <template slot-scope="scope">
          <editRowSelect :options="options" :scope="scope" @select-change="selectChange"></editRowSelect>
        </template>
      </el-table-column>
      <el-table-column label="描述(点击编辑)" align="center" prop="desc">
        <template slot-scope="scope">
          <editRowInput :scope="scope" @input-change="inputChange"></editRowInput>
        </template>
      </el-table-column>
    </el-table>
    <el-button @click="handleCheck">手动选中</el-button>
  </div>
</template>

<script>
import editRowDialog from './editRowDialog'
import editRowSelect from './editRowSelect'
import editRowInput from './editRowInput'
export default {
  components:{
    editRowDialog,
    editRowSelect,
    editRowInput,
  },
  data () {
    return {
      saveFlag:false,
      options: ['int', 'string', 'number'],
      tableData: [{
        name:'a',
        type:'int',
        desc:0,
      }],
    }
  },
  computed: {
    allName () {
      return this.tableData.map(item =>item.name)
    }
  },
  methods: {
    handleSelectionChange (val) {
      console.log(val,"全选");
    },
     // 手动触发
    handleCheck () {
      const rows = [this.tableData[0]]
      if (rows) {
        rows.forEach(row => {
          this.$refs.multipleTable.toggleRowSelection(row);
        });
      } else {
        this.$refs.multipleTable.clearSelection();
      }
    },
    // 抽离编辑的组件
    dialogChange (val,index) {
      this.$message.success("修改成功")
      this.tableData[index].name = val.name
    },
    selectChange (val,index) {
      this.$message.success("修改成功")
      this.tableData[index].type = val
    },
    inputChange (val,index) {
      this.$message.success("修改成功")
      // this.tableData[index].desc = val
      this.tableData[index].name = val
    }
  }
}
</script>

1.新建或重命名:对话框或表格内操作表单

<template>
  <div @click.self="dialogVisible = true">
    <span>
      {{scope.row.name}}
      <i class="el-icon-edit" />
    </span>
    <el-dialog
      style="text-align: left;"
      title="重命名:"
      width="30%"
      :visible.sync="dialogVisible"
      :show-close="false"
      :append-to-body="false"
      :close-on-click-modal="false"
      :lock-scroll="false"
    >
      <newEditForm :dialogVisible="dialogVisible" :data="data" @form-call-back="formCallBack" />
    </el-dialog>
  </div>
</template>

<script>
import newEditForm from './newEditForm'
export default {
  components: {
    newEditForm
  },
  props: {
    scope: {
      type: Object,
      default(){
        return {
          $index:0,
          row:{name:''}
        }
      }
    },
    allName: {
      type: Array,
      default(){
        return []
      }
    },
    isEdit: {
      type: Boolean,
      default: false
    }
  },
  computed:{
    data(){
      return {
        isEdit: this.isEdit,
        allName: this.allName,
        form: {
          name: this.scope.row.name
        },
      }
    }
  },
  data () {
    return {
      dialogVisible: false,
    }
  },
  methods: {
    formCallBack (val) {
      console.log(val,"提交或取消");
      if(val && val.name){
        this.$emit('dialog-change', val, this.scope.$index)
      }else{
        this.$message("已取消修改")
      }
      this.dialogVisible = false
    }
  }
}
</script>

设置了对话框的话,把表单抽离:

<template>
  <div>
    <el-form
      :model="form"
      :rules="rules"
      ref="ruleForm"
      label-position="left"
      label-width="80px"
      @submit.native.prevent
      @keyup.enter.native="submitForm('ruleForm')"
    >
    <el-form-item
      label="画布名:"
      prop="name">
      <el-input
        placeholder="请输入画布名"
        clearable
        v-model="form.name"
        ref="inputFocus"
        spellcheck="false"/>
    </el-form-item>
      <el-form-item>
        <span class="form-item-submit">
          <el-button
            @click="cancelForm">取消</el-button>
          <el-button
            type="primary"
            :loading="loadSubmit"
            @click="submitForm('ruleForm')">确定</el-button>
        </span>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  props: {
    dialogVisible:{
      type:Boolean,
      default:false
    },
    data: {
      type: Object,
      default () {
        return {
          isEdit:false,
          allName: [],
          form: {
            name: ''
          }
        }
      }
    }
  },
  data () {
    const validateName = (rule, value, callback) => {
      if (value) {
        const rtx = /^[\u4E00-\u9FA5a-zA-Z0-9_]+$/
        if (!value.match(rtx)) {
          callback(new Error('只允许中英文、数字、下划线'))
        } else if (this.allName.indexOf(value) !== -1) {
          callback(new Error('该命名已存在'))
        } else if (this.oldName === value) {
          callback(new Error('该命名未修改'))
        } else {
          callback()
        }
      } else {
        callback(new Error('不能为空'))
      }
    }
    return {
      allName:[],
      oldName:'',
      form: {
        name: '',
      },
      rules: {
        name: [
          { required: true, validator: validateName, trigger: 'blur' }
        ]
      },
      loadSubmit: false // 提交
    }
  },
  watch: {
    'dialogVisible': {
      immediate: true,
      // deep:true,
      handler (val) { // 初始化
        if(val){
          this.$nextTick(function () {
            // this.$refs['ruleForm'].resetFields()// 清空表单内容
            this.$refs['ruleForm'].clearValidate()// 清空报错
            this.$refs['inputFocus'].$el.querySelector('input').focus()
          })
          if (this.data && this.data.isEdit) { //新建
            this.allName = this.data.allName
            this.form = {name: ''}
          } else { // 编辑:默认
            this.allName = []
            this.oldName = this.form.name = this.data.form.name
          }
        }
      }
    },
    'data': {
      immediate: false,
      // deep:true,
      handler (val) {
        if (val && val.isEdit) { //新建
          this.allName = val.allName
          this.form = {name: ''}
        } else { // 编辑:默认
          this.allName = []
          this.oldName = this.form.name = val.form.name
        }
      }
    },
  },
  methods: {
    submitForm () {
      this.$refs['ruleForm'].validate((valid) => {
        if (valid) {
          this.$emit('form-call-back', this.form)// 回调:成功提交
          this.$refs['ruleForm'].resetFields()// 清空表单内容
          this.$refs['ruleForm'].clearValidate()// 清空报错
        }else{
          return
        }
      })
    },
    cancelForm () {
      this.$emit('form-call-back')// 回调:关闭对话框
      this.$refs['ruleForm'].resetFields()// 清空表单内容
      this.$refs['ruleForm'].clearValidate()// 清空报错
    }
  }
}
</script>
<!-- 不校验去除低栏边距 -->
<style scoped>
.form-item-bottom{
  margin-bottom: 0;
}
.form-item-submit{
  margin-top: 20px;
  float: right
}
</style>

2.表格内编辑,不需要进行表单校验,即:未修改直接提示,不拦截

<template>
  <div @click="getInputFocus(scope.row.name)">
    <span v-if="!isEdit">
      {{scope.row.name}}
      <i class="el-icon-edit"/>
    </span> 
    <el-input
      v-else
      :ref="`focusInput${scope.$index}`"
      v-model="inputVal"
      @blur="blurChange(inputVal)"
      spellcheck="false"
    />
  </div>
</template>

<script>
export default {
  props: {
    scope: {
      type: Object,
      default(){
        return {
          $index:0,
          row:{name:''}
        }
      }
    }
  },
  data () {
    return {
      isEdit:false,
      oldName:'',
      inputVal:''
    }
  },
  methods:{
    getInputFocus(val){ // 获取焦点
      this.oldName = this.inputVal = val
      this.isEdit = true
      this.$nextTick(() => {
        this.$refs[`focusInput${this.scope.$index}`].focus()
      })
    },
    blurChange(val){ // 失去焦点
      if(val !== this.oldName){
        this.$emit('input-change', val,this.scope.$index) 
      }else{
        this.$message.warning("内容未修改")
      }
      this.isEdit = false
    }
  }
}
</script>

3.表格内操作:下拉选项

<template>
  <div @click="getSelectFocus(scope.row.type)">
    <span v-if="!isEdit">
      {{scope.row.type}}
      <i class="el-icon-edit"/>
    </span>      
    <el-select
      v-else
      :ref="`toFoucus${scope.$index}`"
      automatic-dropdown
      v-model="selectedVal"
      placeholder="请选择"
      style="text-align:center;width: 100%"
      @change='selectChange'
      @visible-change='visibleChange'
    >
      <el-option v-for="item in options" :key="item" :label="item" :value="item" />
    </el-select>
  </div>
</template>

<script>
export default {
  props: {
    scope: {
      type: Object,
      default(){
        return {
          $index:0,
          row:{type:'int'}
        }
      }
    },
    options: {
      type: Array,
      default () {
        return []
      }
    }
  },
  data () {
    return {
      isEdit:false,
      selectedOld: '',
      selectedVal: ''
    }
  },
  methods:{
    getSelectFocus(val){ // 获取焦点
      this.selectedOld = this.selectedVal = val
      this.isEdit = true
      this.$nextTick(() => {
        this.$refs[`toFoucus${this.scope.$index}`].focus()
      })
    },
    selectChange(val){ //提交
      this.$emit('select-change', val,this.scope.$index) 
    },
    visibleChange(val){ // 失去焦点
      if(!val){
        if(this.selectedOld === this.selectedVal){
          this.$message.warning("内容未修改")
        }
        this.isEdit = false
      }
    }
  }

}
</script>

其他:新增一行、删除一行的校验与重置按钮(禁止添加或校验重名)

最外层的结构:

<template>
  <el-form :rules="rules" :model="form" ref="rulesForm">
    <el-form-item prop="tableData">
      <dragTableDialog
        :table-data="form.tableData"
        :drop-col="column"
        tab-show
        dialog-title="编辑"
        @save-drag-table="saveDragTable"
      />
    </el-form-item>
  </el-form>
</template>

<script>
import dragTableDialog from './module-components/formTable/dragTableDialog'
export default {
  components: {
    dragTableDialog
  },
  data () {
    return {
      rules: {
        tableData: { type: 'array', required: true, message: '输出列表不可为空', trigger: 'blur' }
      },
      form: {
        tableData: []
      },
      column: [
        {
          default: '',
          label: '字段',
          prop: 'field_name'
        },
        {
          default: 'string',
          label: '类型',
          prop: 'field_type'
        },
        {
          default: '',
          label: '描述',
          prop: 'field_desc'
        }
      ]
    }
  },
  methods: {
    saveDragTable (val) {
      this.form.tableData = val
      if(val.length===0){
        this.$refs['rulesForm'].validateField('tableData') //校验某个字段
      }else{
        // this.$refs['copyForm'].resetFields()// 清空表单内容
        this.$refs['rulesForm'].clearValidate()// 清空报错
      }
    },
  }
}
</script>

表格与字符串的切换:

<!--可拖拽的表格:表格内容+行参数+按钮名称(对话框标题)
-->
<template>
  <div>
    <el-button type="primary" @click="showDialog">{{ dialogTitle }}</el-button>
    <CommonTable style="marginTop:10px" :table-data="tableDataBeigin" :table-column="dropCol" />
    <el-dialog
      :visible.sync="dialogVisible"
      :close-on-click-modal="false"
      append-to-body
      show-close
      :before-close="beforeClose"
      :title="dialogTitle"
      width="40%"
    >
      <div v-if="!tabShow" style="margin-top:-20px;">
        <dragTableForm
          :table-data="tableDataDialog"
          :table-column="dropCol"
          :save-disabled="saveDisabled"
          @save-call-back="saveCallBack"
          @save-data-back="saveDataBack"
        />
      </div>
      <el-tabs
        v-else
        @tab-click="handleClickTab"
        style="margin-top:-20px;"
        v-model="activeName"
        type="card"
      >
        <el-tab-pane label="表格编辑模式" name="table">
          <dragTableForm
            :size="size"
            :table-data="tableDataDialog"
            :drop-col="dropCol"
            :save-disabled="saveDisabled"
            @save-call-back="saveCallBack"
            @save-data-back="saveDataBack"
          />
        </el-tab-pane>
        <el-tab-pane label="文本编辑模式" name="txt">
          <el-input
            @change="inputChange"
            v-model="strSplit"
            type="textarea"
            :rows="6"
            placeholder="例:a,int,描述a,类型int。"
            spellcheck="false"
          />
          <h4 style="margin:5px 0">注意:</h4>
          <ol style="text-align:left">
            <li>可将导出的csv文件内容,直接复制过来使用,若有数据类型且不符合规范,转换后默认为string;</li>
            <li>手动编辑时,注意分隔符为英文逗号(第3个逗号后面的内容合并到最后一列),新的一行用Enter键换行。</li>
          </ol>
        </el-tab-pane>
      </el-tabs>
      <!--保存操作  -->
      <span slot="footer" class="dialog-footer">
        <el-button :size="size" type="primary" @click="submitDialog" :disabled="saveDisabled">保存</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<script>
import _ from 'lodash'
import CommonTable from './commonTable'
import dragTableForm from './dragTableForm'
export default {
  components: {
    CommonTable,
    dragTableForm
  },
  props: {
    'size': {
      type: String,
      default: 'mini'
    },
    'tableData': {
      type: Array,
      default () {
        return []
      }
    },
    'dropCol': {
      type: Array,
      default () {
        return [
          {
            default: '',
            label: '字段',
            prop: 'field_name'
          },
          {
            default: 'string',
            label: '类型',
            prop: 'field_type'
          },
          {
            default: '',
            label: '描述',
            prop: 'field_desc'
          }
        ]
      }
    },
    'dialogTitle': {
      type: String,
      default: '新建'
    },
    'tabShow': {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      strSplit: '',
      activeName: 'table',
      dialogVisible: false,
      saveDisabled: false,
      tableDataBeigin: [],
      tableDataDialog: []
    }
  },
  watch: {
    'tableData': {
      immediate: true,
      handler (val) {
        const tableData = []
        val.forEach((item, index) => {
          const obj = {}
          obj.id = index
          this.dropCol.forEach(e => {
            obj[e.prop] = item[e.prop]
          })
          tableData.push(obj)
        })
        console.log("监听提交");
        this.tableDataBeigin = tableData
        this.tableDataDialog = _.cloneDeep(tableData)
      }
    }
  },
  methods: {
    showDialog () {
      if (this.activeName === 'txt') {
        let str = ''
        this.tableDataDialog.forEach(item => {
          delete item.id
          str += Object.values(item) + '\n'
        })
        this.strSplit = str
      }
      this.dialogVisible = true
    },
    beforeClose () {
      const tableData = []
      this.tableData.forEach((item, index) => {
        const obj = {}
        obj.id = index
        this.dropCol.forEach(e => {
          obj[e.prop] = item[e.prop]
        })
        tableData.push(obj)
      })
      this.tableDataDialog = _.cloneDeep(tableData)
      this.dialogVisible = false
      this.saveDisabled = false
    },
    findStrIndex (str, cha, num) {
      var x = str.indexOf(cha)
      for (var i = 0; i < num; i++) {
        x = str.indexOf(cha, x + 1)
      }
      return x
    },
    handleClickTab (tab, event) {
      if (tab.name === 'txt') {
        let str = ''
        this.tableDataDialog.forEach(item => {
          delete item.id
          str += Object.values(item) + '\n'
        })
        this.strSplit = str
      } else {
        const array = this.strSplit.split('\n')
        if (!array[array.length - 1]) {
          array.pop()
        }
        const tableDataDialog = []
        array.forEach((item, index) => {
          const allIndex = this.findStrIndex(item, ',', 1)
          let array2 = []
          if (item.split(',').length > 3) {
            array2 = item.substring(0, allIndex).split(',')
            array2.push(item.substring(allIndex + 1))
          } else {
            if (item.split(',').length === 1) {
              array2 = [item, this.dropCol[1].prop === 'field_type' ? 'string' : '', '']
            } else {
              array2 = item.split(',')
            }
          }
          const obj = {}
          array2.forEach((e, i) => {
            obj.id = index
            if (this.dropCol[i].prop === 'field_type') {
              const options = ['tinyint', 'smallint', 'int', 'bigint', 'boolean', 'float', 'double', 'string']
              obj[this.dropCol[i].prop] = options.indexOf(array2[i]) !== -1 ? array2[i] : 'string'
            } else if (this.dropCol[i].prop === 'field_key') {
              const keyOptions = ['qq', 'area', 'roleid', 'os', 'commid', 'openid', 'null']
              obj[this.dropCol[i].prop] = keyOptions.indexOf(array2[i]) !== -1 ? array2[i] : 'null'
            } else {
              obj[this.dropCol[i].prop] = array2[i] ? array2[i] : ''
            }
          })
          tableDataDialog.push(obj)
        })

        this.tableDataDialog = tableDataDialog
      }
    },
    saveCallBack (disabled) {
      this.saveDisabled = disabled
    },
    saveDataBack (data) {
      this.tableDataDialog = data
    },
    inputChange (val) { // 转换+校验
      const array = this.strSplit.split('\n')
      if (!array[array.length - 1]) {
        array.pop()
      }
      const tableDataDialog = []
      array.forEach((item, index) => {
        const allIndex = this.findStrIndex(item, ',', 1)
        let array2 = []
        if (item.split(',').length > 3) {
          array2 = item.substring(0, allIndex).split(',')
          array2.push(item.substring(allIndex + 1))
        } else {
          if (item.split(',').length === 1) {
            array2 = [item, this.dropCol[1].prop === 'field_type' ? 'string' : '', '']
          } else {
            array2 = item.split(',')
          }
        }
        const obj = {}
        array2.forEach((e, i) => {
          obj.id = index
          if (this.dropCol[i].prop === 'field_type') {
            const options = ['tinyint', 'smallint', 'int', 'bigint', 'boolean', 'float', 'double', 'string']
            obj[this.dropCol[i].prop] = options.indexOf(array2[i]) !== -1 ? array2[i] : 'string'
          } else if (this.dropCol[i].prop === 'field_key') {
            const keyOptions = ['qq', 'area', 'roleid', 'os', 'commid', 'openid', 'null']
            obj[this.dropCol[i].prop] = keyOptions.indexOf(array2[i]) !== -1 ? array2[i] : 'null'
          } else {
            obj[this.dropCol[i].prop] = array2[i] ? array2[i] : ''
          }
        })
        tableDataDialog.push(obj)
      })
      this.tableDataDialog = tableDataDialog
      //校验
      const tableData = []
      this.tableDataDialog.forEach((item, index) => {
        const obj = {}
        this.dropCol.forEach(e => {
          obj[e.prop] = item[e.prop]
        })
        tableData.push(obj)
      })
      const arr = tableData.map(item => item[this.dropCol[0].prop])
      if ((new Set(arr)).size !== arr.length) {
        this.$message.warning(this.dropCol[0].label + '不可重名')
      }
    },
    submitDialog () {
      console.log("提交");
      const tableData = []
      this.tableDataDialog.forEach((item, index) => {
        const obj = {}
        this.dropCol.forEach(e => {
          obj[e.prop] = item[e.prop]
        })
        tableData.push(obj)
      })
      this.tableDataBeigin = tableData
      const arr = tableData.map(item => item[this.dropCol[0].prop])
      if ((new Set(arr)).size !== arr.length) {
        this.$message.warning(this.dropCol[0].label + '不可重名')
      } else {
        this.$emit('save-drag-table', tableData)
        this.dialogVisible = false
      }
    }
  }
}
</script>

设置公共组件:el-table

/***
* 通用的table展示
* @param  {Array} tableData
* @param  {Array} tableColumn
* @return {Number/String} height(参考element)
* @return {String} size(参考element)
* @return {Boolean} stripe 默认显示
* @return {Boolean} sortable 默认显示
* @return {Boolean} loading
* @return {Function} filterChange
* @return {Function / String} tableRowClassName 底色
* @return {String} slot 插入的位置:header、footer
* */
<template>
  <div>
    <el-table
      ref="commonTable"
      :data="tableData"
      :size="size"
      :stripe="stripe"
      border
      highlight-current-row
      v-loading="loading"
      :row-class-name="tableRowClassName"
      @filter-change="filterChange"
      @selection-change="handleSelectionChange"
      :row-key="rowKey"
    >
      <!--自定义插入-->
      <slot name="header" />
      <el-table-column
        v-for="(item, index) in tableColumn"
        :key="`key_${index}`"
        :prop="item.prop"
        :label="item.label"
        show-overflow-tooltip
        :sortable="sortable"
        align="center"
      >
        <template slot-scope="scope">
          <div v-if="tableColumn[index].prop === 'field_key'">
            <span>{{ keyOptionsObj[scope.row.field_key] || '-空-' }}</span>
          </div>
          <div v-else>
            <span>{{ scope.row[tableColumn[index].prop] || '-空-' }}</span>
          </div>
        </template>
      </el-table-column>
      <!--自定义插入-->
      <slot name="footer" />
    </el-table>
  </div>
</template>

<script>
export default {
  props: {
    tableData: {
      type: Array,
      default () {
        return []
      }
    },
    tableColumn: {
      type: Array,
      default () {
        return [
          {
            default: '',
            label: '字段名称',
            prop: 'field_name'
          }, {
            default: 'string',
            label: '字段类型',
            prop: 'field_type'
          }, {
            default: '',
            label: '字段描述',
            prop: 'field_desc'
          }
        ]
      }
    },
    size: {
      type: String,
      default: 'mini'
    },
    sortable: {
      type: Boolean,
      default: true
    },
    stripe: {
      type: Boolean,
      default: true
    },
    loading: {
      type: Boolean,
      default: false
    },
    filterChange: {
      type: Function,
      default () {
        return ''
      }
    },
    tableRowClassName: {
      type: Function,
      default () {
        return ''
      }
    },
    rowKey: {
      type: String,
      default: ''
    },
    initSelection: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      keyOptionsObj: {
        qq: 'QQ号',
        area: '大区ID',
        roleid: '角色ID',
        os: '手机操作系统',
        commid: '微信Commid',
        openid: 'Open ID',
        null: '不关联'
      }
    }
  },
  watch: {
    initSelection: {
      immediate: true,
      handler (val) {
        if (val) {
          this.$nextTick(() => {
            this.$refs.commonTable.clearSelection()
          })
        }
      }
    }
  },
  methods: {
    handleSelectionChange (val) {
      this.$emit('handleSelectionChange', val)
    }
  }
}
</script>

设置可拖拽的表格:

<template>
  <div>
    <el-button
      :size="size"
      type="primary"
      @click="addRow"
      style="margin-bottom: 10px"
      :disabled="disabledAdd"
    >新增一行</el-button>
    <el-form :model="form" :rules="rules" ref="form">
      <el-table
        border
        :size="size"
        id="dragTable_sql"
        :row-key="getRowKeys"
        :data="form.tableData"
        style="width: 100%;"
      >
        <!-- 拖拽图标 -->
        <el-table-column width="40" align="center">
          <template>
            <i class="el-icon-rank" style="font-size:large;cursor:grab" />
          </template>
        </el-table-column>
        <!-- 输入选择 -->
        <el-table-column
          v-for="(item, index) in tableColumn"
          :key="index"
          :prop="item.prop"
          :label="item.label"
          align="center"
        >
          <template slot-scope="scope">
            <el-form-item
              v-if="index===0"
              :size="size"
              :prop="`tableData.${scope.$index}.${item.prop}`"
              :rules="rules[item.prop]"
            >
              <el-input
                v-focus
                clearable
                v-model="scope.row[item.prop]"
                :placeholder="`请输入${item.label}`"
                @change="inputChange"
                @clear="inputChange"
              />
            </el-form-item>
            <el-form-item v-else-if="item.prop === 'field_type'" :size="size">
              <el-select
                @change="saveChange"
                :size="size"
                v-model="scope.row[item.prop]"
                :placeholder="'请选择'+item.label"
              >
                <el-option
                  v-for="item in options"
                  :key="item"
                  :label="item"
                  :value="item"
                  style="text-align: center;"
                />
              </el-select>
            </el-form-item>
            <el-form-item v-else-if="item.prop === 'field_key'" :size="size">
              <el-select
                clearable
                v-model="scope.row[item.prop]"
                :placeholder="'请选择'+item.label"
                @change="saveChange"
              >
                <el-option
                  style="text-align: center;"
                  v-for="(item,index) in keyOptions"
                  :key="index"
                  :label="item.label"
                  :value="item.value"
                />
              </el-select>
            </el-form-item>
            <el-form-item v-else :size="size">
              <el-input
                clearable
                v-model="scope.row[item.prop]"
                :placeholder="`请输入${item.label}`"
                @change="saveChange"
                @clear="saveChange"
              />
            </el-form-item>
          </template>
        </el-table-column>
        <!--操作  -->
        <el-table-column width="80" align="center" label="操作" fixed="right">
          <template slot-scope="scope">
            <el-button :size="size" type="danger" @click="deleteRow(scope.$index, scope.row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-form>
    <el-link type="danger" v-show="isRepeatName">{{tableColumn[0].label}}命名已存在!</el-link>
  </div>
</template>

<script>
import _ from 'lodash'
import Sortable from 'sortablejs'
export default {
  directives: {
    focus: {
      inserted: function (el) {
        el.querySelector('input').focus()
      }
    }
  },
  props: {
    'size': {
      type: String,
      default: 'mini'
    },
    'tableData': {
      type: Array,
      default () {
        return []
      }
    },
    'tableColumn': {
      type: Array,
      default () {
        return [
          {
            default: '',
            label: '字段',
            prop: 'field_name'
          },
          {
            default: 'string',
            label: '类型',
            prop: 'field_type'
          },
          {
            default: '',
            label: '描述',
            prop: 'field_desc'
          }
        ]
      }
    }
  },
  watch: {
    'form.tableData': {
      immediate: false,
      handler (val) {
        this.$emit('save-data-back', val)
        if (val.length > 0) {
          const fieldName = val.map(item => item[this.tableColumn[0].prop])
          this.isRepeatName = this.isRepeat(fieldName)
          val.forEach(item => {
            if (!item[this.tableColumn[0].prop]) {// 只有有空就禁止提交
              this.disabledAdd = true
              this.$emit('save-call-back', true)
            } else {
              this.disabledAdd = false
              this.$emit('save-call-back', false)
            }
          })
          if (this.isRepeatName) { // 有重复值
            this.disabledAdd = true
            this.$emit('save-call-back', true)
          }
        }
      }
    },
    'tableData': {
      immediate: true,
      handler (val) {
        this.$nextTick(function () {
          this.rowDropDialog()
        })
        if(val.length > 0){
          this.form.tableData = val
        }
      }
    }
  },
  computed: {
    rules () {
      const rules = {}
      this.tableColumn.forEach((item, index) => {
        rules[item.prop] = [
          { required: true, message: '请输入' + item.label, trigger: 'blur' },
          { pattern: /^[a-zA-Z][a-zA-Z0-9_]*$/, message: '须字母开头,不含特殊符号', trigger: 'blur' },
        ]
      })
      return rules
    }
  },
  data () {
    return {
      getRowKeys (row) {
        return row.id
      },
      form: {
        tableData: []
      },
      fieldName: [],
      disabledAdd: false,
      isRepeatName: false,
      options: [
        'tinyint',
        'smallint',
        'int',
        'bigint',
        'boolean',
        'float',
        'double',
        'string'
      ],
      keyOptions: [
        { value: 'qq', label: 'QQ号' },
        { value: 'area', label: '大区ID' },
        { value: 'roleid', label: '角色ID' },
        { value: 'os', label: '手机操作系统' },
        { value: 'commid', label: '微信Commid' },
        { value: 'openid', label: 'Open ID' },
        { value: 'null', label: '不关联' }
      ]
    }
  },
  methods: {
    rowDropDialog () {
      const tbody = document.querySelector('#dragTable_sql tbody')
      const _this = this
      Sortable.create(tbody, {
        handle: '.el-icon-rank',
        animation: 150,
        onEnd ({ newIndex, oldIndex }) {
          const currRow = _this.form.tableData.splice(oldIndex, 1)[0]
          _this.form.tableData.splice(newIndex, 0, currRow)
        }
      })
    },
    inputChange (val) {
      if (val) { //必要字段更新
        this.disabledAdd = this.fieldName.indexOf(val) !== -1
        this.isRepeatName = this.fieldName.indexOf(val) !== -1
        this.$emit('save-call-back', this.disabledAdd)
        this.$emit('save-data-back', this.form.tableData)
      } else {
        this.$refs['form'].validate(valid => {
          if (valid) {
            //清除不计重复
            this.$emit('save-data-back', this.form.tableData)
          } else {
            this.disabledAdd = true
            this.$emit('save-call-back', true)
            return valid
          }
        });
      }
    },
    saveChange () {
      this.$emit('save-data-back', this.form.tableData)
    },
    addRow () {
      this.$refs['form'].validate((valid) => {
        if (valid) {
          // 1.读取已有命名
          if (this.form.tableData.length > 0) {
            this.fieldName = this.form.tableData.map(item => item[this.tableColumn[0].prop])
          }
          // 2.添加一行:id++1
          const tableRowKey = this.tableColumn.map(item => item.prop)
          const tableRowVal = this.tableColumn.map(item => item.default)
          const tableRow = _.zipObject(tableRowKey, tableRowVal) // 映射
          tableRow.id = _.uniqueId(Date.now().toString()) // 拖拽
          this.form.tableData.push(tableRow)
          this.disabledAdd = true
          this.$emit('save-call-back', true)
        } else {
          return false;
        }
      });
    },
    deleteRow (index, row) {
      //1.删除
      this.form.tableData.splice(index, 1)
      // 2.去重
      if(this.form.tableData.length === 0){
        this.fieldName = []
      }else{
        this.fieldName = this.fieldName.filter(item => item !== row[this.tableColumn[0].prop])
      }
    },
    isRepeat (arr) {
      return _.uniq(arr).length !== arr.length;
    }
  }
}
</script>

-end-

转载于:https://www.cnblogs.com/wheatCatcher/p/11453423.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值