记录下自己封装table基础crud方便开发
2021-04-21 支持key为‘companies[0].pivot.name’的特殊字符串
2021-04-25 优化特殊字符串判断处理 参考大佬microanswer的写法
2021-04-26 增加方法二eval()
2021-05-21 增加表单嵌套expand
2021-07-05 增加自定义头部信息 (v-slot:xxx.header)
2021-08-24 使用v-bind="$attrs"及v-on=“$listeners”简化代码,修改使用文档
待解决问题:
无法在封装后的组件中用refs直接调用el-table自带的方法,需要自己在methods中转
效果图

dw-crud的使用 columnChange组件
<template>
<dw-crud selection :columns="columns" :data="data" @selection-change="handleSelectionChange">
<!-- 当column中的slot为true 则以key为插槽名(#key)传入 -->
<template #depot="{ row }">
<column-change type="select" :value="row.depot" :options="depotOption" @change="changeNum(row.id, $event)" />
</template>
<template #number="{ row }">
<column-change type="input" search :value="row.number" @change="changeNum(row.id, $event)" />
</template>
<!-- #op特指操作列 可以自行去dw-crud修改 -->
<template #op="{ row }">
<el-button type="text">加入销售订单</el-button>
<el-button type="text">删除</el-button>
</template>
</dw-crud>
</template>
<script>
import dwCrud from './dwCrud'
// 个人封装的行内编辑input和select
import columnChange from './columnChange'
export default {
components: {
dwCrud,
columnChange,
},
data() {
return {
columns: [
{
title: 'ID',
key: 'id',
width: '50px',
},
{
title: '仓库',
slot: 'depot',
},
{
title: '数量',
slot: 'number',
},
],
// 测试数据
depotOption: [
{ label: '仓库A', value: 1 },
{ label: '仓库B', value: 2 },
{ label: '仓库C', value: 3 },
],
data: [
{
id: 1,
depot: 1,
number: 7000,
},
{
id: 2,
depot: 2,
number: 5000,
},
],
}
},
methods: {
changeNum(id, val) {
console.log('父组件取值:', id, val)
},
handleSelectionChange() {},
},
}
</script>
<style></style>
dwCrud组件(el-table)
根据目前的业务需求封装。用了slot具名插槽去区分特殊的行内展示<template>
<template>
<el-table ref="table" v-bind="$attrs" v-on="$listeners" :size="size">
<!-- 多选框 -->
<el-table-column v-if="selection" type="selection" align="center" width="55" />
<!-- 表单嵌套 -->
<el-table-column v-if="expand" type="expand">
<template slot-scope="{ row, $index }">
<slot name="expand" :row="row" :index="$index"></slot>
</template>
</el-table-column>
<!-- 文本数据渲染 -->
<template v-for="(column, index) in columns">
<el-table-column :key="index" :min-width="column.width" :sortable="column.sortable" :prop="column.key">
<!-- 自定义头部 slotName.header -->
<template slot="header" slot-scope="{ row, $index }">
<slot v-if="$scopedSlots[`${column.slot}.header`]" :name="`${column.slot}.header`"></slot>
<span v-else>{{ column.title }}</span>
</template>
<!-- 主体内容 -->
<template slot-scope="{ row, $index }">
<!-- 如果有插槽 -->
<slot v-if="column.slot" :name="column.slot" :row="row" :index="$index"></slot>
<!-- 过滤 (暂未完善) -->
<!-- <span v-else-if="column.format">{{ key2val(row, column.key) | column.format }}</span> -->
<!-- 默认 -->
<span v-else>{{ key2val(row, column.key) }}</span>
</template>
</el-table-column>
</template>
<!-- 特殊处理的操作布局 默认存在 -->
<el-table-column :fixed="opFixed" label="操作" v-if="operation" :min-width="opWidth">
<template slot-scope="{ row, $index }">
<slot name="op" :row="row" :index="$index"></slot>
</template>
</el-table-column>
</el-table>
</template>
<script>
import { errLog } from '@/util/index'
export default {
/**
* @name: HDW
* @msg: 基于el-table二次封装
* @param {
* 接收参数:
* size table size | String
* selection 是否需要多选 | Boolean
* columns 表头 | Array
* data 内容 | Array
* operation 是否需要操作 | Boolean
* op-width 操作宽度 | String
* expand 表单嵌套表单 | Boolean
* op-fixed 操作固定 | string, boolean
*
* el-table 参数 : (v-bind="$attrs") 全部支持
*
* el-table-column 参数:(可自行在columns添加) 默认参数
* sortable true, false, 'custom'
*
* columns 参数:
* {
* title: 'ID',
* key: 'id', // 页面展示 row['id']
* width: '50px', // min-width
* slot: 'depot', // 是否为插槽 value 值为插槽名字
* sortable:'custom'
* }
* ],
*
* el-table 事件: (v-on="listeners") 全部支持
* 多选监听(selection) @selection-change
* 单选监听 @row-click
* 当前所选行 @current-change
* 切换排序 @sort-change
*
* el-table 方法:(暂未解决 refs 穿透,需自行在 methods 添加)
* clearSelection()
* toggleRowSelection()
* toggleAllSelection()
* toggleRowExpansion()
* setCurrentRow()
* }
*
*/
name: 'dwCrud',
// inheritAttrs: true,
props: {
size: {
type: String,
default: 'mini', // medium / small / mini
},
columns: {
type: Array,
default: () => [],
},
selection: {
type: Boolean,
default: false,
},
expand: {
type: Boolean,
default: false,
},
operation: {
type: Boolean,
default: true,
},
'op-fixed': {
type: String,
default: 'right',
},
'op-width': {},
},
methods: {
clearSelection() {
this.$refs['table'].clearSelection()
},
toggleRowSelection(row, selected) {
this.$refs['table'].toggleRowSelection(row, selected)
},
toggleAllSelection() {
this.$refs['table'].toggleAllSelection()
},
toggleRowExpansion(row, expanded) {
this.$refs['table'].toggleRowExpansion(row, expanded)
},
setCurrentRow(row) {
this.$refs['table'].setCurrentRow(row)
},
/**
* 大佬的写法 (microanswer)
*
* obj = {a:{b:{c:1},b1:[2,3,4]}}
*
* get(obj,'a.b.c') // 1
* get(obj,'a.b1[2]') // 4
* get(obj,['a','b','c']) // 1
*
*/
get(obj, key) {
key = Array.isArray(key) ? key.join('.') : key
// 方法一 用到了new Funcion()的语法拼接 和 function()()立即执行函数
return new Function('obj', `return obj${key ? `.${key}` : ''}`)(obj, key)
// 方法二 eval()
return eval(`obj${key ? `.${key}` : ''}`)
},
// 根据大佬提供的写法优化
key2val(row, key) {
key = Array.isArray(key) ? key.join('.') : key
let res
try {
res = new Function('row', `return row${key ? `.${key}` : ''}`)(row, key)
} catch (err) {
errLog('dw-crud', err, 'warn')
res = null
}
return res
},
// 以前的思路
// 根据key找到对应的字段
old_key2val(row, key) {
// return this.get(row,key)
const arr = key.split('.')
return this.recKey(row, arr)
},
// 递归key
recKey(obj, key = []) {
// 如果是最底层
if (!key.length) return obj
// 未到最底层但已是 null/undefined
if (key.length && !obj) {
errLog('dw-crud', `${key.join('.')}不存在,请填写正确的key名`, 'warn')
return null
}
const key0 = key.shift()
const regObj = this.regKey(key0)
// 如果匹配出当前key是数组 eg: companies[11]
if (regObj) {
const { name, inx } = regObj
return this.recKey(obj[name][inx], key)
} else {
return this.recKey(obj[key0], key)
}
},
// 正则找出 eg: companies[11]
regKey(key) {
const reg = /([A-Za-z]+)\[(.*?)\]/g
// 匹配出 companies(i=1) 和 11(i=2)
const res = reg.exec(key)
if (!res) return null
const name = res[1]
const inx = res[2]
return { name, inx }
},
},
mounted() {},
}
</script>
errLog函数
export const errLog = (component, message, status = 'error') => {
console[status](`[${component} error]:\n${message}`)
}
<
template
>
<
el-table
ref=
"
table
"
v-bind
=
"$attrs
"
v-on
=
"$listeners
"
:
size
=
"size
"
>
<!-- 多选框 -->
<
el-table-column
v-if
=
"selection
"
type=
"
selection
"
align=
"
center
"
width=
"
55
" /
>
<!-- 表单嵌套 -->
<
el-table-column
v-if
=
"expand
"
type=
"
expand
"
>
<
template
slot-scope=
"
{ row, $index }
"
>
<
slot
name=
"
expand
"
:
row
=
"row
"
:
index
=
"$index
"
></
slot
>
</
template
>
</
el-table-column
>
<!-- 文本数据渲染 -->
<
template
v-for
=
"
(column
, index
)
in columns
"
>
<
el-table-column
:
key
=
"index
"
:
min-width
=
"column
.width
"
:
sortable
=
"column
.sortable
"
:
prop
=
"column
.key
"
>
<!-- 自定义头部 slotName.header -->
<
template
slot=
"
header
"
slot-scope=
"
{ row, $index }
"
>
<
slot
v-if
=
"$scopedSlots
[
`${column
.slot
}
.header
`
]
"
:
name
=
"
`${column
.slot
}
.header
`
"
></
slot
>
<
span
v-else
>{{ column
.title
}}</
span
>
</
template
>
<!-- 主体内容 -->
<
template
slot-scope=
"
{ row, $index }
"
>
<!-- 如果有插槽 -->
<
slot
v-if
=
"column
.slot
"
:
name
=
"column
.slot
"
:
row
=
"row
"
:
index
=
"$index
"
></
slot
>
<!-- 过滤 (暂未完善) -->
<!-- <span v-else-if="column.format">{{ key2val(row, column.key) | column.format }}</span> -->
<!-- 默认 -->
<
span
v-else
>{{
key2val
(row
, column
.key
)
}}</
span
>
</
template
>
</
el-table-column
>
</
template
>
<!-- 特殊处理的操作布局 默认存在 -->
<
el-table-column
:
fixed
=
"opFixed
"
label=
"
操作
"
v-if
=
"operation
"
:
min-width
=
"opWidth
"
>
<
template
slot-scope=
"
{ row, $index }
"
>
<
slot
name=
"
op
"
:
row
=
"row
"
:
index
=
"$index
"
></
slot
>
</
template
>
</
el-table-column
>
</
el-table
>
</
template
>
<
script
>
import
{ errLog
}
from
'
@/util/index
'
export
default
{
/**
* @name: HDW
* @msg: 基于el-table二次封装
* @param {
* 接收参数:
* size table size | String
* selection 是否需要多选 | Boolean
* columns 表头 | Array
* data 内容 | Array
* operation 是否需要操作 | Boolean
* op-width 操作宽度 | String
* expand 表单嵌套表单 | Boolean
* op-fixed 操作固定 | string, boolean
*
* el-table 参数 : (v-bind="$attrs") 全部支持
*
* el-table-column 参数:(可自行在columns添加) 默认参数
* sortable true, false, 'custom'
*
* columns 参数:
* {
* title: 'ID',
* key: 'id', // 页面展示 row['id']
* width: '50px', // min-width
* slot: 'depot', // 是否为插槽 value 值为插槽名字
* sortable:'custom'
* }
* ],
*
* el-table 事件: (v-on="listeners") 全部支持
* 多选监听(selection) @selection-change
* 单选监听 @row-click
* 当前所选行 @current-change
* 切换排序 @sort-change
*
* el-table 方法:(暂未解决 refs 穿透,需自行在 methods 添加)
* clearSelection()
* toggleRowSelection()
* toggleAllSelection()
* toggleRowExpansion()
* setCurrentRow()
*
}
*
*/
name
:
'
dwCrud
'
,
// inheritAttrs: true,
props
:
{
size
:
{
type
:
String
,
default
:
'
mini
'
,
// medium / small / mini
},
columns
:
{
type
:
Array
,
default
:
()
=>
[],
},
selection
:
{
type
:
Boolean
,
default
:
false
,
},
expand
:
{
type
:
Boolean
,
default
:
false
,
},
operation
:
{
type
:
Boolean
,
default
:
true
,
},
'
op-fixed
'
:
{
type
:
String
,
default
:
'
right
'
,
},
'
op-width
'
:
{},
},
methods
:
{
clearSelection
()
{
this
.$refs
[
'
table
'
].
clearSelection
()
},
toggleRowSelection
(
row
,
selected
)
{
this
.$refs
[
'
table
'
].
toggleRowSelection
(row
, selected
)
},
toggleAllSelection
()
{
this
.$refs
[
'
table
'
].
toggleAllSelection
()
},
toggleRowExpansion
(
row
,
expanded
)
{
this
.$refs
[
'
table
'
].
toggleRowExpansion
(row
, expanded
)
},
setCurrentRow
(
row
)
{
this
.$refs
[
'
table
'
].
setCurrentRow
(row
)
},
/**
* 大佬的写法 (microanswer)
*
* obj = {a:{b:{c:1},b1:[2,3,4]}}
*
* get(obj,'a.b.c') // 1
* get(obj,'a.b1[2]') // 4
* get(obj,['a','b','c']) // 1
*
*/
get
(
obj
,
key
)
{
key
=
Array
.
isArray
(key
)
? key
.
join
(
'
.
'
)
: key
// 方法一 用到了new Funcion()的语法拼接 和 function()()立即执行函数
return
new
Function
(
'
obj
'
,
`
return obj
${key
?
`
.
${key
}`
:
''
}`
)(obj
, key
)
// 方法二 eval()
return
eval
(
`
obj
${key
?
`
.
${key
}`
:
''
}`
)
},
// 根据大佬提供的写法优化
key2val
(
row
,
key
)
{
key
=
Array
.
isArray
(key
)
? key
.
join
(
'
.
'
)
: key
let res
try
{
res
=
new
Function
(
'
row
'
,
`
return row
${key
?
`
.
${key
}`
:
''
}`
)(row
, key
)
}
catch
(err
)
{
errLog
(
'
dw-crud
'
, err
,
'
warn
'
)
res
=
null
}
return res
},
// 以前的思路
// 根据key找到对应的字段
old_key2val
(
row
,
key
)
{
// return this.get(row,key)
const
arr
= key
.
split
(
'
.
'
)
return
this
.
recKey
(row
, arr
)
},
// 递归key
recKey
(
obj
,
key
=
[])
{
// 如果是最底层
if
(
!key
.
length
)
return obj
// 未到最底层但已是 null/undefined
if
(key
.
length
&&
!obj
)
{
errLog
(
'
dw-crud
'
,
`${key
.
join
(
'
.
'
)
}
不存在,请填写正确的key名
`
,
'
warn
'
)
return
null
}
const
key0
= key
.
shift
()
const
regObj
=
this
.
regKey
(key0
)
// 如果匹配出当前key是数组 eg: companies[11]
if
(regObj
)
{
const
{
name
,
inx
}
= regObj
return
this
.
recKey
(obj
[name
][inx
], key
)
}
else
{
return
this
.
recKey
(obj
[key0
], key
)
}
},
// 正则找出 eg: companies[11]
regKey
(
key
)
{
const
reg
=
/(
[A-Za-z
]
+)
\[(.
*?)
\]
/g
// 匹配出 companies(i=1) 和 11(i=2)
const
res
= reg
.
exec
(key
)
if
(
!res
)
return
null
const
name
= res
[
1
]
const
inx
= res
[
2
]
return
{ name
, inx
}
},
},
mounted
()
{},
}
</
script
>
