背景:
1.利用form进行校验输入;
2.利用sortable操作Dom替换表格数据顺序;
3.利用lodash实现数据深拷贝与参数替换等
一:最外层的数组校验
<template> <el-form :rules="rules" :model="form" ref="rulesForm"> <el-form-item prop="table"> <formTableDrag :table-data="form.table" :drop-col="column" tab-show dialog-title="编辑" @save-drag-table="saveDragTable" /> </el-form-item> </el-form> </template> <script> import formTableDrag from ‘./formTableDrag‘ export default { components: { dragTableDialog }, data () { return { rules: { table: { type: ‘array‘, required: true, message: ‘输出列表不可为空‘, trigger: ‘blur‘ } }, form: { table: [] }, column: [ { default: ‘‘, label: ‘字段‘, prop: ‘field_name‘ }, { default: ‘string‘, label: ‘类型‘, prop: ‘field_type‘ }, { default: ‘‘, label: ‘描述‘, prop: ‘field_desc‘ } ] } }, methods: { saveDragTable (val) {this.form.table= val if(val.length===0){ this.$refs[‘rulesForm‘].validateField(‘schema‘) //校验某个字段 }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 v-model="strSplit" type="textarea" :rows="6" placeholder="例:a,int,描述a,类型int。" spellcheck="false" /> <h4 style="margin:5px 0">注意:</h4> <ul style="text-align:left"> <li>1、可将导出的csv文件内容,直接复制过来使用,若有数据类型且不符合规范,转换后默认为string;</li> <li>2、手动编辑时,注意分隔符为英文逗号(第3个逗号后面的内容合并到最后一列),新的一行用Enter键换行。</li> </ul> </el-tab-pane> </el-tabs> <!--保存操作 --> <span slot="footer" class="dialog-footer"> <el-button size="mini" 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: [] } }, created () { 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.tableDataBeigin = tableData this.tableDataDialog = _.cloneDeep(tableData) }, watch: { tableData () { 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.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 }, submitDialog () { if (this.activeName === ‘txt‘) { 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) }) 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>
/*** * 通用的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 id="kp_but_2982" 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() // 拖拽 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.去重 this.fieldName = this.fieldName.filter(item => item !== row[this.tableColumn[0].prop]) }, isRepeat (arr) { return _.uniq(arr).length !== arr.length; } } } </script>
el-table——可编辑、拖拽排序与校验的formTableDrag
原文:https://www.cnblogs.com/wheatCatcher/p/11432590.html