內(nèi)容摘要:
- 需求分析
- 定義 interface
- 定義 json 文件
- 定義列表控件的 props
- 基于 el-table 封裝,實(shí)現(xiàn)依賴 json 渲染
- 實(shí)現(xiàn)內(nèi)置功能:選擇行(單選、多選),格式化、鎖定等。
- 使用 slot 實(shí)現(xiàn)自定義擴(kuò)展
- 做個(gè)工具維護(hù) json 文件(下篇介紹)
管理后臺(tái)里面,列表是一個(gè)常用的功能,UI庫提供了列表組件和分頁組件實(shí)現(xiàn)功能。雖然功能強(qiáng)大,也很靈活,只是還不能稱為低代碼,不過沒關(guān)系,我們可以寫點(diǎn)代碼讓UI庫變?yōu)槊~神器!
本篇介紹列表的設(shè)計(jì)思路和封裝方式。
需求分析
如果基于原生HTML來實(shí)現(xiàn)顯示數(shù)據(jù)列表的功能的話,那么需考慮如何創(chuàng)建 table,如何設(shè)置css等。
如果直接使用UI庫的話,那么可以簡單很多,只需要設(shè)置各種屬性,然后綁定數(shù)據(jù)即可。
以 el-table 為例:
<el-table
:data="tableData"
border
stripe
style="width: 100%"
>
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="address" label="Address" />
</el-table>
設(shè)置好屬性、記錄集合,然后設(shè)置列(el-table-column)即可。
這樣一個(gè)列表就搞定了,再加上 el-pagination 分頁組件,編寫一些代碼即可實(shí)現(xiàn)分頁的功能。
如果只是一個(gè)列表的話,這種方式?jīng)]啥問題,但是管理后臺(tái)項(xiàng)目,往往需要n個(gè)列表,而每個(gè)列表都大同小異,如果要一個(gè)一個(gè)手?jǐn)]出來,那就有點(diǎn)麻煩了。
那么如何解決呢?我們可以參考低代碼,基于 el-talbe 封裝一個(gè)列表控件,
實(shí)現(xiàn)依賴 json 動(dòng)態(tài)渲染列表,同時(shí)支持自定義擴(kuò)展。
定義 interface
最近開始學(xué)習(xí) Typescript,發(fā)現(xiàn)了一個(gè)現(xiàn)象,如果可以先定義好類型,那么代碼就可以更清晰的展現(xiàn)出來。
另外 Vue3 的最新文檔,也采用了通過 interface 來介紹API功能的方式,所以我們也可以借鑒一下。
依據(jù) el-table 的屬性,定義列表控件屬性的 interface。
Vue3 的 props 有一套約束方式,這個(gè)似乎和TS的方式有點(diǎn)沖突,沒想出了更好的方法(option API 和 script setup兩種定義props的方式,都有不足 ),所以只好做兩個(gè) interface,一個(gè)用于定義組件的 props ,一個(gè)用于取值。
- IGridPropsComp:定義組件的 props
/**
* 列表控件的屬性的描述,基于el-table
*/
export interface IGridPropsComp {
/**
* 模塊ID,number | string
*/
moduleId: IPropsValidation,
/**
* 主鍵字段的名稱 String,對(duì)應(yīng) row-key
*/
idName: IPropsValidation,
/**
* table的高度, Number
*/
height: IPropsValidation,
/**
* 列(字段)顯示的順序 Array<number|string>
*/
colOrder: IPropsValidation,
/**
* 斑馬紋,Boolean
*/
stripe: IPropsValidation,
/**
* 縱向邊框,Boolean
*/
border: IPropsValidation,
/**
* 列的寬度是否自撐開,Boolean
*/
fit: IPropsValidation,
/**
* 要高亮當(dāng)前行,Boolean
*/
highlightCurrentRow: IPropsValidation,
/**
* 鎖定的列數(shù) Number,設(shè)置到 el-table-column 的 fixed
*/
fixedIndex: IPropsValidation,
/**
* table的列的 IGridItem
* * id: number | string,
* * colName: string, 字段名稱
* * label: string, 列的標(biāo)簽、標(biāo)題
* * width: number, 列的寬度
* * align: EAlign, 內(nèi)容對(duì)齊方式
* * headerAlign: EAlign 列標(biāo)題對(duì)齊方式
*/
itemMeta: IPropsValidation, //
/**
* 記錄選擇的行:IGridSelection
* * dataId: '', 單選ID number 、string
* * row: {}, 單選的數(shù)據(jù)對(duì)象 {}
* * dataIds: [], 多選ID []
* * rows: [] 多選的數(shù)據(jù)對(duì)象 []
*/
selection: IPropsValidation,
/**
* 綁定的數(shù)據(jù) Array, 對(duì)應(yīng) data
*/
dataList: IPropsValidation
// 其他擴(kuò)展屬性
[propName: string]: IPropsValidation
}
- moduleId:模塊ID,一個(gè)模塊菜單只能有一個(gè)列表,菜單可以嵌套。
- itemMeta:列的屬性集合,記錄列表的列的屬性。
- selection:記錄列表的單選、多選的 row。
- dataList:顯示的數(shù)據(jù),對(duì)應(yīng) el-table 的 data
- 其他:對(duì)應(yīng) el-table 的屬性
IGridPropsComp 的作用是,約束列表控件需要設(shè)置哪些屬性,屬性的具體類型,就無法在這里約束了。
- IPropsValidation (不知道vue內(nèi)部有沒有這樣的 interface)
/**
* vue 的 props 的驗(yàn)證的類型約束
*/
export interface IPropsValidation {
/**
* 屬性的類型,比較靈活,可以是 String、Number 等,也可以是數(shù)組、class等
*/
type: Array<any> | any,
/**
* 是否必須傳遞屬性
*/
required?: boolean,
/**
* 自定義類型校驗(yàn)函數(shù)(箭頭函數(shù)),value:屬性值
*/
validator?: (value: any) => boolean,
/**
* 默認(rèn)值,可以是值,也可以是函數(shù)(箭頭函數(shù))
*/
default?: any
}
取 props 用的 interface
IGridPropsComp 無法約束屬性的具體類型,所以只好再做一個(gè) interface。
- IGridProps
/**
* 列表控件的屬性的類型,基于el-table
*/
export interface IGridProps {
/**
* 模塊ID,number | string
*/
moduleId: number | string,
/**
* 主鍵字段的名稱 String,對(duì)應(yīng) row-key
*/
idName: String,
/**
* table的高度, Number
*/
height: number,
/**
* 列(字段)顯示的順序 Array<number|string>
*/
colOrder: Array<number|string>,
/**
* 斑馬紋,Boolean
*/
stripe: boolean,
/**
* 縱向邊框,Boolean
*/
border: boolean,
/**
* 列的寬度是否自撐開,Boolean
*/
fit: boolean,
/**
* 要高亮當(dāng)前行,Boolean
*/
highlightCurrentRow: boolean,
/**
* 鎖定的列數(shù) Number,設(shè)置到 el-table-column 的 fixed
*/
fixedIndex: number,
/**
* table的列的 Object< IGridItem >
* * id: number | string,
* * colName: string, 字段名稱
* * label: string, 列的標(biāo)簽、標(biāo)題
* * width: number, 列的寬度
* * align: EAlign, 內(nèi)容對(duì)齊方式
* * headerAlign: EAlign 列標(biāo)題對(duì)齊方式
*/
itemMeta: {
[key:string | number]: IGridItem
}, //
/**
* 選擇行的情況:IGridSelection
* * dataId: '', 單選ID number 、string
* * row: {}, 單選的數(shù)據(jù)對(duì)象 {}
* * dataIds: [], 多選ID []
* * rows: [] 多選的數(shù)據(jù)對(duì)象 []
*/
selection: IGridSelection,
/**
* 綁定的數(shù)據(jù) Array, 對(duì)應(yīng) data
*/
dataList: Array<any>
// 其他擴(kuò)展屬性
[propName: string]: any
}
對(duì)比一下就會(huì)發(fā)現(xiàn),屬性的類型不一樣。因?yàn)槎x props 需要使用一套特定的對(duì)象格式,而使用 props 的時(shí)候需要的是屬性自己的類型。
理想情況下,應(yīng)該可以在 script setup 里面,引入外部文件 定義的 interface ,然后設(shè)置給組件的 props,但是到目前為止還不支持,只能在( script setup方式的)組件內(nèi)部定義 props。希望早日支持,支持了就不會(huì)這么糾結(jié)和痛苦了。
依據(jù) el-table-column 定義列屬性的 interface。
- IGridItem:列表里面列的屬性
/**
* 列的屬性,基于 el-table-column
*/
export interface IGridItem {
/**
* 字段ID、列ID
*/
id: number | string,
/**
* 字段名稱
*/
colName: string,
/**
* 列的標(biāo)簽、標(biāo)題
*/
label: string,
/**
* 列的寬度
*/
width: number,
/**
* 內(nèi)容對(duì)齊方式 EAlign
*/
align: EAlign,
/**
* 列標(biāo)題對(duì)齊方式
*/
headerAlign: EAlign,
// 其他擴(kuò)展屬性
[propName: string]: any
}
還是需要擴(kuò)展屬性的,因?yàn)檫@里只是列出來目前需要的屬性,el-table-column 的其他屬性、方法還有很多,而且以后也可能會(huì)新增。
這個(gè)屬性不是直接設(shè)置給組件的 props,所以不用定義兩套了。
對(duì)齊方式的枚舉
枚舉可以理解為常量,定義之后可以避免低級(jí)錯(cuò)誤,避免手滑。
- EAlign
export const enum EAlign {
left = 'left',
center = 'center',
right = 'right'
}
選擇記錄的 interface。
列表可以單選也可以多選,el-table 在默認(rèn)情況下似乎是二選一,覺得有點(diǎn)不方便,為啥不能都要?
- 單選:鼠標(biāo)單一任意一行就是單選;(清空其他已選項(xiàng))
- 多選:單擊第一列的(多個(gè))復(fù)選框,就是多選;
這樣用戶就可以愉快的想單選就單選,想多選就多選了。
- IGridSelection
/**
* 列表里選擇的數(shù)據(jù)
*/
export interface IGridSelection {
/**
* 單選ID number 、string
*/
dataId: number | string,
/**
* 單選的數(shù)據(jù)對(duì)象 {}
*/
row: any,
/**
* 多選ID []
*/
dataIds: Array<number | string>,
/**
* 多選的數(shù)據(jù)對(duì)象 []
*/
rows: Array<any>
}
其實(shí)我覺得只記錄ID即可,不過既然 el-talble 提供的 row,那么還是都記錄下來吧。
定義 json 文件
接口定義好之后,我們可以依據(jù) interface 編寫 json 文件:
{
"moduleId": 142,
"height": 400,
"idName": "ID",
"colOrder": [
90, 100, 101
],
"stripe": true,
"border": true,
"fit": true,
"highlightCurrentRow": true,
"highlight-current-row": true,
"itemMeta": {
"90": {
"id": 90,
"colName": "kind",
"label": "分類",
"width": 140,
"title": "分類",
"align": "center",
"header-align": "center"
},
"100": {
"id": 100,
"colName": "area",
"label": "多行文本",
"width": 140,
"title": "多行文本",
"align": "center",
"header-align": "center"
},
"101": {
"id": 101,
"colName": "text",
"label": "文本",
"width": 140,
"title": "文本",
"align": "center",
"header-align": "center"
}
}
}
為什么直接設(shè)置 json 文件而不是 js 對(duì)象呢?
因?yàn)閷?duì)象會(huì)比較長,如果是代碼形式的話,那還不如直接使用UI庫組件來的方便呢。你可能又會(huì)問了,既然直接用 json文件,為啥還要設(shè)計(jì) interface 呢?
當(dāng)然是為了明確各種類型,interface 可以當(dāng)做文檔使用,另外封裝UI庫的組件的時(shí)候,也可以用到這些 interface。使用列表控件的時(shí)候也可以使用這些 interface。
其實(shí)json文件不用手動(dòng)編寫,而是通過工具來編寫和維護(hù)。
定義列表控件的 props
封裝組件之前需要先定義一下組件需要的 props:
- props-grid.ts
import type { PropType } from 'vue'
import type {
IGridProps,
IGridItem,
IGridSelection
} from '../types/50-grid'
/**
* 表單控件需要的屬性propsForm
*/
export const gridProps: IGridProps = {
/**
* 模塊ID,number | string
*/
moduleId: {
type: Number,
required: true
},
/**
* 主鍵字段的名稱
*/
idName: {
type: String,
default: 'ID'
},
/**
* 字段顯示的順序
*/
colOrder: {
type: Array as PropType<Array<number | string>>,
default: () => []
},
/**
* 鎖定的列數(shù)
*/
fixedIndex: {
type: Number,
default: 0
},
/**
* table的列的 meta
*/
itemMeta: {
type: Object as PropType<{
[key:string | number]: IGridItem
}>
},
/**
* 選擇的情況 IGridSelection
*/
selection: {
type: Object as PropType<IGridSelection>,
default: () => {
return {
dataId: '', // 單選ID number 、string
row: {}, // 單選的數(shù)據(jù)對(duì)象 {}
dataIds: [], // 多選ID []
rows: [] // 多選的數(shù)據(jù)對(duì)象 []
}
}
},
/**
* 綁定的數(shù)據(jù)
*/
dataList: {
type: Array as PropType<Array<any>>,
default: () => []
},
其他略。。。
}
按照 Option API 的方式設(shè)置 props 的定義,這樣便于共用屬性的定義(好吧似乎也沒有需要共用的地方,不過我還是喜歡把 props 的定義寫在一個(gè)單獨(dú)的文件里)。
封裝列表控件
定義好 json 、props之后,我們基于 el-table 封裝列表控件:
- template 模板
<el-table
ref="gridRef"
v-bind="$attrs"
:data="dataList"
:height="height"
:stripe="stripe"
:border="border"
:fit="fit"
:highlight-current-row="highlightCurrentRow"
:row-key="idName"
@selection-change="selectionChange"
@current-change="currentChange"
>
<!--多選框,實(shí)現(xiàn)多選功能-->
<el-table-column
type="selection"
width="55"
align="center"
header-align="center"
@click="clickCheck"
>
</el-table-column>
<!--依據(jù) json 渲染的字段列表-->
<el-table-column
v-for="(id, index) in colOrder"
:key="'grid_list_' + index + '_' + id"
v-bind="itemMeta[id]"
:column-key="'col_' + id"
:fixed="index < fixedIndex"
:prop="itemMeta[id].colName"
>
</el-table-column>
</el-table>
設(shè)置 type="selection"列,實(shí)現(xiàn)多選的功能。
使用 v-for 的方式,遍歷出動(dòng)態(tài)列。
設(shè)置 :fixed="index < fixedIndex",實(shí)現(xiàn)鎖定左面列的功能。
- js 代碼
import { defineComponent, ref } from 'vue'
// 列表控件的屬性
import { gridProps } from '../map'
/**
* 普通列表控件
*/
export default defineComponent({
name: 'nf-elp-grid-list',
inheritAttrs: false,
props: {
...gridProps // 解構(gòu)共用屬性
},
setup (props, ctx) {
// 獲取 el-table
const gridRef = ref(null)
return {
gridRef
}
}
})
把 props 的定義放在單獨(dú)的 ts文件 里面,組件內(nèi)部的代碼就可以簡潔很多。
實(shí)現(xiàn)內(nèi)置功能
可以按照自己的喜好,設(shè)置一些內(nèi)部功能,比如單選/多選的功能,格式化的功能等。
- 定義控制函數(shù) controller.ts
import type { ElTable } from 'element-plus'
// 列表控件的屬性
import type { IGridProps } from '../map'
export interface IRow {
[key: string | number]: any
}
/**
* 列表的單選和多選的事件
* @param props 列表組件的 props
* @param gridRef el-table 的 $ref
*/
export default function choiceManage<T extends IGridProps, V extends typeof ElTable>(props: T, gridRef: V) {
// 是否單選觸發(fā)
let isCurrenting = false
// 是否多選觸發(fā)
let isMoring = false
// 單選
const currentChange = (row: IRow) => {
if (isMoring) return // 多選代碼觸發(fā)
if (!row) return // 清空
if (gridRef.value) {
isCurrenting = true
gridRef.value.clearSelection() // 清空多選
gridRef.value.toggleRowSelection(row) // 設(shè)置復(fù)選框
gridRef.value.setCurrentRow(row) // 設(shè)置單選
// 記錄
props.selection.dataId = row[props.idName]
props.selection.dataIds = [ row[props.idName] ]
props.selection.row = row
props.selection.rows = [ row ]
isCurrenting = false
}
}
// 多選
const selectionChange = (rows: Array<IRow>) => {
if (isCurrenting) return
// 記錄
if (typeof props.selection.dataIds === 'undefined') {
props.selection.dataIds = []
}
props.selection.dataIds.length = 0 // 清空
// 設(shè)置多選
rows.forEach((item: IRow) => {
if (typeof item !== 'undefined' && item !== null) {
props.selection.dataIds.push(item[props.idName])
}
})
props.selection.rows = rows
// 設(shè)置單選
switch (rows.length) {
case 0:
// 清掉單選
gridRef.value.setCurrentRow()
props.selection.dataId = ''
props.selection.row = {}
break
case 1:
isMoring = true
// 設(shè)置新單選
gridRef.value.setCurrentRow(rows[0])
isMoring = false
props.selection.row = rows[0]
props.selection.dataId = rows[0][props.idName]
break
default:
// 去掉單選
gridRef.value.setCurrentRow()
props.selection.row = rows[rows.length - 1]
props.selection.dataId = props.selection.row[props.idName]
}
}
return {
currentChange, // 單選
selectionChange // 多選
}
}
- 列表控件的 setup 里調(diào)用
setup (props, ctx) {
// 獲取 el-table
const gridRef = ref<InstanceType<typeof ElTable>>()
// 列表選項(xiàng)的事件
const {
currentChange, // 單選
selectionChange // 多選
} = choiceManage(props, gridRef)
return {
selectionChange, // 多選
currentChange, // 單選
gridRef // table 的 dom
}
}
這里有一個(gè)“度”的問題:
el-table 完全通過 slot 的方式實(shí)現(xiàn)各種功能,這種方法的特點(diǎn)是:非常靈活,可以各種組合;缺點(diǎn)是比較繁瑣。
而我們需要尋找到一個(gè)適合的“折中點(diǎn)”,顯然這個(gè)折中點(diǎn)很難統(tǒng)一,這也是過渡封裝帶來的問題。不能遇到新的需求,就增加內(nèi)部功能,這樣就陷入了《人月神話》里說的“焦油坑”,進(jìn)去了就很難出來。
這也是低代碼被詬病的因素。
支持?jǐn)U展
那么如何找到這個(gè)折中點(diǎn)呢?可以按照 “開閉原則”,按照不同的需求,設(shè)置多個(gè)不同功能的列表控件,使用 slot 實(shí)現(xiàn)擴(kuò)展功能。或者干脆改為直接使用 el-table 的方式。(要靈活,不要一刀切)
比如簡單需求,不需要擴(kuò)展功能的情況,設(shè)置一個(gè)基礎(chǔ)列表控件:nf-grid。
需要擴(kuò)展列的情況,設(shè)置一個(gè)可以擴(kuò)展的列表控件:nf-grid-slot。
如果需要多表頭、樹形數(shù)據(jù)等需求,可以設(shè)置一個(gè)新的列表控件,不過需要先想想,是不是直接用 el-table 更方便。
要不要新增一個(gè)控件,不要慣性思維,而要多方面全局考慮。
這里介紹一下支持 slot 擴(kuò)展的列表控件的封裝方式:
<el-table
ref="gridDom"
v-bind="$attrs"
size="mini"
style="width: 100%"
:data="dataList"
:height="height"
:stripe="stripe"
:border="border"
:fit="fit"
:highlight-current-row="highlightCurrentRow"
:current-row-key="idName"
:row-key="idName"
@selection-change="selectionChange"
@current-change="currentChange"
>
<!--顯示選擇框-->
<el-table-column
type="selection"
width="55">
</el-table-column>
<!--顯示字段列表-->
<template
v-for="(id, index) in colOrder"
:key="'grid_list_' + index + '_' + id"
>
<!--檢查插槽里是否包含 字段名,作為判斷依據(jù)-->
<!--不帶插槽的列-->
<el-table-column
v-if="!(slotsKey.includes(itemMeta[id].colName))"
:fixed="index < fixedIndex"
v-bind="itemMeta[id]"
:prop="itemMeta[id].colName"
:min-width="50"
>
</el-table-column>
<!--帶插槽的列-->
<el-table-column v-else
v-bind="itemMeta[id]"
>
<template #default="scope">
<!--讀取外部插槽內(nèi)容,并且傳遞 scope -->
<slot :name="itemMeta[id].colName" v-bind="scope"></slot>
</template>
</el-table-column>
</template>
</el-table>
模板部分,首先判斷一下是否需要使用 slot,做一個(gè)分支。
需要使用 slot 的列,通過 <template #default="scope"> 設(shè)置 slot。
- 代碼部分
import { defineComponent, ref } from 'vue'
// 表單控件的屬性
import { gridProps } from '../map'
import choiceManage from './controller'
export default defineComponent({
name: 'nf-elp-grid-slot',
inheritAttrs: false,
props: {
...gridProps
},
setup (props, ctx) {
// 記錄插槽 的 名稱
const slots = ctx.slots
const slotsKey = Object.keys(slots)
// 列表選項(xiàng)的事件
const {
currentChange, // 單選
selectionChange // 多選
} = choiceManage(props, gridRef)
return {
slotsKey,
selectionChange, // 多選
currentChange // 單選
}
}
})
一般列表的使用方法
封裝之后,使用起來就很方便了,引入 json文件,設(shè)置屬性即可。
- template
<nf-grid
v-bind="gridMeta"
:dataList="dataList"
:selection="selection"
size="small"
/>
是不是簡單多了。
- 代碼部分
import { defineComponent, reactive } from 'vue'
import { nfGrid, createDataList } from '../../../../lib-elp/main'
import _gridMeta from '../../grid/grid.json'
import _formMeta from '../../form/form.json'
export default defineComponent({
name: 'nf-elp-grid-page',
components: { nfGrid },
setup(props) {
// 不需要?jiǎng)討B(tài)改變的話,可以不使用 reactive。
const gridMeta = reactive(_gridMeta)
// 設(shè)置選擇的行
const selection = reactive({
dataId: '', // 單選ID number 、string
row: {}, // 單選的數(shù)據(jù)對(duì)象 {}
dataIds: [], // 多選ID []
rows: [] // 多選的數(shù)據(jù)對(duì)象 []
})
// 設(shè)置記錄集。
const dataList = reactive(_dataList)
return {
dataList,
selection,
gridMeta
}
}
})
控件可以做成全局組件的形式。
- 看看效果

擴(kuò)展列表的使用方法
首先還是依據(jù) json 渲染列表,然后根據(jù)需要設(shè)置插槽即可,設(shè)置插槽后會(huì)替換默認(rèn)的列。
- template
可以使用 slot 自定義擴(kuò)展列 <br>
<!--表單控件-->
<nf-grid
v-grid-drag="gridMeta"
v-bind="gridMeta"
:dataList="dataList"
:selection="selection"
size="small"
>
<!--普通字段,用字段名作為插槽的名稱-->
<template #text="scope">
<div style="display: flex; align-items: center">
<el-icon><timer /></el-icon>
<span style="margin-left: 10px">擴(kuò)展:{{ scope.row.text }}</span>
</div>
</template>
<!--普通字段-->
<template #week="scope">
<span style="margin-left: 10px">{{ scope.row.week.replace('-w','年 第') + '周' }}</span>
</template>
<!--操作按鈕-->
<template #option="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)">修改</el-button>
<el-button
size="small"
type="danger"
@click="handleDelete(scope.$index, scope.row)">刪除</el-button>
</template>
</nf-grid>
通過 slot 擴(kuò)展列,可以按照 Table-column 的匿名插槽的方式進(jìn)行設(shè)置。
列的先后順序還是由 colOrder 控制,和插槽的先后順序無關(guān)。
- 代碼部分
import { defineComponent, reactive } from 'vue'
// 使用 圖標(biāo)
import { Timer } from '@element-plus/icons-vue'
import { nfGridSlot, createDataList } from '../../../../lib-elp/main'
import _gridMeta from '../../grid/grid.json'
import _formMeta from '../../form/form.json'
import { EAlign } from '../../../../lib/types/enum'
import type { IGridSelection, IGridItem } from '../../../../lib/types/50-grid'
export default defineComponent({
name: 'nf-elp-grid-slot-page',
components: {
Timer,
nfGrid: nfGridSlot
},
props: {
moduleID: { // 模塊ID
type: [Number, String],
default: 1
}
},
setup(props) {
const gridMeta = reactive(_gridMeta)
// 設(shè)置列的先后順序和是否顯示
gridMeta.colOrder = [90, 100, 101, 102, 105, 113, 115, 116, 120,121,150, 2000]
// 設(shè)置一個(gè)操作按鈕列
const optionCol: IGridItem = {
id: 2000,
colName: "option",
label: "操作",
width: 180,
fixed: EAlign.right,
align: EAlign.center, // 使用枚舉
headerAlign: EAlign.center
}
gridMeta.itemMeta['2000'] = optionCol // 設(shè)置操作列,也可以直接在json文件里設(shè)置。
const dataList = reactive(_dataList)
const handleEdit = (index: number, row: any) => {
console.log(index, row)
}
const handleDelete = (index: number, row: any) => {
console.log(index, row)
}
return {
handleEdit,
handleDelete,
dataList,
gridMeta
}
}
})
使用字段名稱作為插槽的名稱,可以把任意字段變成插槽的形式。
如果要添加操作按鈕這類的列,可以給 itemMeta 添加對(duì)應(yīng)的列屬性。
- 看看效果:

管理 json
其實(shí),前面介紹的那些大家可能都會(huì)想到,也許早就實(shí)踐過了,然后發(fā)現(xiàn)雖然看著挺好,但是其實(shí)沒有解決根本問題!只是把 template 里的問題轉(zhuǎn)移到 json 里面。
雖然不需要設(shè)置模板,但是需要設(shè)置 json,還不是一樣,有啥本質(zhì)區(qū)別嗎?
其實(shí)不一樣的,管理 json 的難度明顯比管理模板要簡單得多。
比如我們可以做一個(gè)維護(hù) json 的小工具:
- 首先從數(shù)據(jù)庫文檔生成基礎(chǔ)的 json(毛坯房);
- 然后使用可視化+拖拽的方式設(shè)置格子細(xì)節(jié)(精裝修)。
這樣就可以很方便的維護(hù) json 了。具體實(shí)現(xiàn)方式,將在下一篇再介紹。