el-table——可编辑、拖拽排序与校验的formTableDrag
背景:
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的更多相关文章
- Vue 表单拖拽排序
Vue table表单拖拽 业务需求: 因为数据展示使用的是 elementUI 的 Table进行数据展示的,现在的需求是通过拖拽表单进行表单排序.同时,动态修改表单中的数据排列顺序.查阅了好多资料 ...
- jquery拖拽排序,针对后台列表table进行拖拽排序(超实用!)
现在很多后台列表为了方便均使用拖拽排序的功能,对列表进行随意的排序. 话不多说 ,我在网上找了一些demo,经过对比,现在把方便实用的一个demo列出来,基于jqueryUI.js 先上html代码, ...
- el-table——可编辑拖拽转换csv格式的表格
<!--可拖拽的表格:表格内容+行参数+按钮名称(对话框标题)--> <template> <div> <el-button size="mini& ...
- vue el-transfer新增拖拽排序功能---sortablejs插件
<template> <!-- target-order="unshift"必须设置,如果不设置的话后台穿的value值得顺序会被data重置 - --> ...
- JS组件系列——Bootstrap Table 表格行拖拽
前言:之前一直在研究DDD相关知识,好久没更新JS系列文章了.这两天做了一个简单的业务需求,觉得效果还可以,今天在这里分享给大家,欢迎拍砖~~ 一.业务需求及实现效果 项目涉及到订单模块,那天突然接到 ...
- html5 Sortable.js 拖拽排序源码分析
最近公司项目经常用到一个拖拽 Sortable.js插件,所以有空的时候看了 Sortable.js 源码,总共1300多行这样,写的挺完美的. 本帖属于原创,转载请出名出处. 官网http:// ...
- vue列表拖拽排序功能实现
1.实现目标:目标是输入一个数组,生成一个列表:通过拖拽排序,拖拽结束后输出一个经过排序的数组. 2.实现思路: 2.1是使用HTML5的drag功能来实现,每次拖拽时直接操作Dom节点排序,拖拽结束 ...
- vue中基于sortablejs与el-upload实现文件上传后拖拽排序
今天做冒烟测试的时候发现商品发布有一个拖拽图片排序功能没做,赶紧加上 之前别的同事基于 vuedraggable 实现过这个功能,我这里自己深度封装了 el-upload ,用这种方式改动很大,而且感 ...
- dragsort html拖拽排序
一.Jquery List DragSort 对于有些页面,如首页的定制,需要进行动态的拖拽排序.由于自己实现比较困难,我们一般会使用一些js插件来实现.dragsort 就是帮助我们完成这一需求.通 ...
随机推荐
- Laya的资源加载
白鹭中的资源加载,可以单个去加载.但是更多是通过资源组加载的. 比如进入登录界面,则加载登录资源组的资源.销毁登录界面,则卸载登录模资源. //加载登录模块资源组 RES.loadGroup(&quo ...
- vmware安装gho系统(win10上安装虚拟机然后在vmware上安装win7)
用ghost直接将gho转成vmdk将ghost32, gho文件放到同一目录, cmd里进入对应目录,输入以下命令ghost32 -clone,mode=restore,src=example.gh ...
- 一个php创建webservice,并通过c#调用的真实实例(转)
https://www.cnblogs.com/sequh/archive/2015/09/18/4819832.html 最近需要用php创建webservice供C#和JAVA来调用,通过3天的搜 ...
- asp.net Forms身份验证详解
在做网站的时候,都会用到用户登录的功能.对于一些敏感的资源,我们只希望被授权的用户才能够访问,这让然需要用户的身份验证.对于初学者,通常将用户登录信息存放在Session中,笔者在刚接触到asp.ne ...
- 树莓派3B安装arm64操作系统
pi64 pi64基于Debian 9,地址如下https://github.com/bamarni/pi64 烧录过程还是用SDFormatter格式化,用Win32DiskImager写入即可,没 ...
- 【GStreamer开发】GStreamer基础教程13——播放速度
目标 快进,倒放和慢放是trick模式的共同技巧,它们有一个共同点就是它们都修改了播放的速度.本教程会展示如何来获得这些效果和如何进行逐帧的跳跃.主要内容是: 如何来变换播放的速度,变快或者变慢,前进 ...
- js 次方 开方 对数
次方 ,用Math.pow(值,次方数) 如: Math.pow(3,2); 3的平方 Math.Pow(2,3); 2的立方 开方Math.sqrt(值) 如: Math.sqrt(9); ...
- canal启动报错ERROR c.a.o.canal.parse.inbound.mysql.dbsync.DirectLogFetcher - I/O error while reading from client socket
- Deepin Linux下为Wine创建文件关联
在Deepin Linux下,默认地,使用apt安装的Wine并没有创建文件关联,这使得在文件管理器中双击exe等Windows可执行文件时,不能直接运行.为此,必须手动在桌面环境中创建文件关联. 文 ...
- [WCF] - 使用 [DataMember] 标记的数据契约需要声明 Set 方法
WCF 数据结构中返回的只读属性 TotalCount 也需要声明 Set 方法. [DataContract]public class BookShelfDataModel{ public B ...