背景:

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的更多相关文章

  1. Vue 表单拖拽排序

    Vue table表单拖拽 业务需求: 因为数据展示使用的是 elementUI 的 Table进行数据展示的,现在的需求是通过拖拽表单进行表单排序.同时,动态修改表单中的数据排列顺序.查阅了好多资料 ...

  2. jquery拖拽排序,针对后台列表table进行拖拽排序(超实用!)

    现在很多后台列表为了方便均使用拖拽排序的功能,对列表进行随意的排序. 话不多说 ,我在网上找了一些demo,经过对比,现在把方便实用的一个demo列出来,基于jqueryUI.js 先上html代码, ...

  3. el-table——可编辑拖拽转换csv格式的表格

    <!--可拖拽的表格:表格内容+行参数+按钮名称(对话框标题)--> <template> <div> <el-button size="mini& ...

  4. vue el-transfer新增拖拽排序功能---sortablejs插件

    <template> <!-- target-order="unshift"必须设置,如果不设置的话后台穿的value值得顺序会被data重置 -  --> ...

  5. JS组件系列——Bootstrap Table 表格行拖拽

    前言:之前一直在研究DDD相关知识,好久没更新JS系列文章了.这两天做了一个简单的业务需求,觉得效果还可以,今天在这里分享给大家,欢迎拍砖~~ 一.业务需求及实现效果 项目涉及到订单模块,那天突然接到 ...

  6. html5 Sortable.js 拖拽排序源码分析

    最近公司项目经常用到一个拖拽 Sortable.js插件,所以有空的时候看了 Sortable.js 源码,总共1300多行这样,写的挺完美的.   本帖属于原创,转载请出名出处. 官网http:// ...

  7. vue列表拖拽排序功能实现

    1.实现目标:目标是输入一个数组,生成一个列表:通过拖拽排序,拖拽结束后输出一个经过排序的数组. 2.实现思路: 2.1是使用HTML5的drag功能来实现,每次拖拽时直接操作Dom节点排序,拖拽结束 ...

  8. vue中基于sortablejs与el-upload实现文件上传后拖拽排序

    今天做冒烟测试的时候发现商品发布有一个拖拽图片排序功能没做,赶紧加上 之前别的同事基于 vuedraggable 实现过这个功能,我这里自己深度封装了 el-upload ,用这种方式改动很大,而且感 ...

  9. dragsort html拖拽排序

    一.Jquery List DragSort 对于有些页面,如首页的定制,需要进行动态的拖拽排序.由于自己实现比较困难,我们一般会使用一些js插件来实现.dragsort 就是帮助我们完成这一需求.通 ...

随机推荐

  1. Qt连接数据库

    Qt连接数据库,参数设置 //连接数据库 bool VCManageDatabase::connectMYSQL() { //判断testConnect连接是否存在并连接 if (QSqlDataba ...

  2. 泡泡一分钟:Project AutoVision - Localization and 3D Scene Perception for an Autonomous Vehicle with a Multi-Camera System

    Project AutoVision - Localization and 3D Scene Perception for an Autonomous Vehicle with a Multi-Cam ...

  3. Python高级笔记(八)with、上下文管理器

    1. 上下文管理器 __enter__()方法返回资源对象,__exit__()方法处理一些清除资源 如:系统资源:文件.数据库链接.Socket等这些资源执行完业务逻辑之后,必须要关闭资源 #!/u ...

  4. Linux使用sleep进行延迟实验

    sleep命令可以延迟脚本执行一段时间(以秒为单位).下面的命令使用tput和sleep从0开始计时到40秒: #/bin/bash echo Count: tput sc # 循环40s for c ...

  5. LODOP判断没成功发送任务-重打一下

    一般情况下打印执行了PRINT()或PRINTA(),就会加入打印机队列,如果打印机脱机,就会在队列里排队,当打印机连上并取消脱机的时候,正在排队的任务就会打出,所以一般建议用是否加入队列来判断打印成 ...

  6. JS获当前网页元素高度offsetHeight

    本文测试的是offsetHeight,获取网页中某元素的高度,单位是像素,获取的类型是整型,可以进行数字运算.如图,网页中的元素本身的高度包括,自身的内容+padding+border,而margin ...

  7. C#图片水印类

    这个是学习用的呃,主要看一下水印在修改图片中距左边的宽度和高度是杂弄的就哦客了. using System; using System.Collections.Generic; using Syste ...

  8. Python3 IO编程之StringIO和BytesIO

    StringIO 很多时候,数据读写不一定是文件,也可以在内存中读写. 要把str写入StringIO,我们需要先创建一个StringIO,然后像文件一样写入即可 >>> from ...

  9. SpringBoot学习笔记:自定义拦截器

    SpringBoot学习笔记:自定义拦截器 快速开始 拦截器类似于过滤器,但是拦截器提供更精细的的控制能力,它可以在一个请求过程中的两个节点进行拦截: 在请求发送到Controller之前 在响应发送 ...

  10. 用MATLAB的Classficiation Learner工具箱对12个数据集进行各种分类与验证

    准备材料 以所有的特征集作为variable进行像Bayes吖.SVM吖.决策树吖......分类.同时对数据进行预处理,选出相关度高的特征子集作为新的一组data进行分类(预处理的代码不必放出来). ...