記錄下自己封裝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
>