先上個圖

image.png
上圖說明
- 看起來是表格包含了很多個表單,其實是表單包含了表格;
- 表頭是通過插槽自定義的,也可以直接使用表格的 label 屬性;
- 外層是一個大表格,里面是一個小表格,然后小表格的每一列是根據(jù)大表格的主表列里的選項不同,數(shù)據(jù)不同自動循環(huán)出來的,有的是必填,有的是非必填,截圖上都是必填;
- 小表格可以進行動態(tài)添加數(shù)據(jù),小表格跟大表格是兩個表單,并且分開校驗;
element 表單校驗理解
- el-form 組件中的 model 屬性,即綁定校驗的外層對象,我們都知道對象里第一層數(shù)據(jù)的校驗,而這個對象里面可能還有對象、數(shù)組什么的,怎么去校驗被層層包裹住的數(shù)據(jù)呢,這就是表格校驗要過的第一個關(guān)卡;
- el-form 組件中的 rules 屬性,對數(shù)據(jù)的校驗規(guī)則對象,它通過 el-form-item 的 prop 屬性去找該字段匹配 rules 屬性綁定對象的哪一個字段,嵌套層級的話,可以通過 prop 屬性提現(xiàn)出來,但是如果嵌套比較煩,我們對 rules 對象的書寫有比較麻煩,el-form-item 組件中 也有一個 rules 屬性,可以直接傳遞該字段的校驗數(shù)組,清晰明了;
- 此處即用到了表格單行校驗,也用到了全表格校驗,全表格校驗和正常的表單校驗一樣,但是單行校驗就相對復(fù)雜了;
- 此處還有一個表格的展開行的坑,就是當(dāng)我們要默認(rèn)展開行或者要根據(jù)需求去做一些特殊的表格展開的時候,el-table 組件的 row-key 屬性一定要在數(shù)據(jù)中是字符串類型的,不然不會出效果;
上代碼
標(biāo)簽部分
<!-- iq 是自定義的,放到對象里時,一定要轉(zhuǎn)成字符串 -->
<el-button
@click="iq=iq+1,tableForm.tableData.unshift({iq:iq.toString(),manageCom:'',gradeCode:[],isEdit: true,list:[],add:1})"
:loading="isLoading" type="primary">新增配置
</el-button>
<el-form :model="tableForm" ref="tableForm" size="small">
<el-table :data="tableForm.tableData" ref="tableData" v-loading="isLoading" @expand-change="expandChange"
:expand-row-keys="expandRowKeys" row-key="iq">
<el-table-column type="expand">
<template slot-scope="props">
<!-- 給每一個小表格動態(tài)賦值 ref 屬性 -->
<el-form v-if="props.row.asalaryCode" :model="tableForm.tableData[props.$index]"
:ref="`tableForm${props.$index}`" size="mini">
<el-table :data="props.row.list" border default-expand-all>
<div v-for="(col, index) in salaryArr" :key="index">
<!-- 因為是根據(jù) asalaryCode 字段來生成小表格的,所以得先判斷 -->
<div v-if="col.value == props.row.asalaryCode">
<!-- 循環(huán)生成小表格表頭 :fixed="indexF == 0" 不知為什么在渲染的時候 順序不對,就加了這段 -->
<el-table-column v-for="(fileByte, indexF) in col.fileBytes" :key="indexF" align="center"
:fixed="indexF == 0">
<!-- 自定義表頭部 -->
<template slot="header">
<span>
<span v-if="!fileByte.notRequired" style="color: red; font-size: 16px">*</span>子表列{{indexF++}}
</span>
</template>
<template slot-scope="scope">
<!-- prop 屬性是組件用來找到指定校驗數(shù)據(jù)的關(guān)鍵,此處比較模糊,放在下面解釋 -->
<el-form-item :prop="`list.${scope.$index}.${fileByte.model}`"
:rules="rules[fileByte.rule || fileByte.model]" style="margin-top:18px">
<el-select v-if="fileByte.type == 'select'" v-model="scope.row[fileByte.model]"
:disabled="!props.row.isEdit" placeholder="請選擇" filterable class="item-width">
<!-- 動態(tài)獲取枚舉 -->
<el-option v-for="item in getNacosOne('parameterName' + props.row.asalaryCode)"
:key="item.value" :label="item.value" :value="item.value">
</el-option>
</el-select>
<el-input v-else-if="fileByte.type == 'input'" v-model.trim="scope.row[fileByte.model]"
placeholder="請輸入" clearable class="item-width" />
</el-form-item>
</template>
</el-table-column>
<el-table-column v-if="props.row.isEdit && tableForm.tableData[props.$index].asalaryCode"
width="100" label="操作" align="center">
<template slot-scope="scope">
<el-popconfirm title="該行將被刪除,請確認(rèn)" @confirm="deleteOne(props.row.list,scope.$index,1)">
<el-button slot="reference" type="text">刪除</el-button>
</el-popconfirm>
</template>
</el-table-column>
</div>
</div>
</el-table>
<div @click="childrenAdd(props.row.list" class="addBtn"
style="border: 1px solid rgba(24, 144, 255, 1);color: rgba(24, 144, 255, 1);">
<i class="el-icon-plus"></i>添加
</div>
</el-form>
</template>
</el-table-column>
<el-table-column align="center">
<template slot="header">
<span>
<span style="color: red; font-size: 16px">*</span>主表列1
</span>
</template>
<template slot-scope="scope">
<!-- prop 屬性是組件用來找到指定校驗數(shù)據(jù)的關(guān)鍵,用此處的 prop 來舉個栗子:tableForm['tableData'][scope.$index]['manageCom'],
這就是該字段在這個表格中的定位,即通過對 prop 值的拆解,再跟 form 表單的 model 屬性結(jié)合,就能找到指定數(shù)據(jù),再加上 rules,就能進行數(shù)據(jù)校驗 -->
<!-- 此處 el-form-item 組件的 rules 屬性也是為了定位相應(yīng)的校驗規(guī)則,直接傳,才能對動態(tài)表格進行操作 -->
<el-form-item :prop="`tableData.${scope.$index}.manageCom`" :rules="rules.manageCom"
style="margin-top:18px">
<el-cascader v-model="scope.row.manageCom" @change="edit(scope.row,'isEdit')" :options="manageComArr"
:props="{label:'label',value:'manageComCode',emitPath:false,checkStrictly: true}"
:show-all-levels="false" placeholder="請選擇" filterable class="item-width" />
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center">
<template slot="header">
<span>
<span style="color: red; font-size: 16px">*</span>主表列2
</span>
</template>
<template slot-scope="scope">
<el-form-item :prop="`tableData.${scope.$index}.gradeCode`" :rules="rules.gradeCode"
style="margin-top:18px">
<el-select v-model="scope.row.gradeCode" placeholder="請選擇" collapse-tags multiple clearable filterable
class="item-width">
<el-option v-for="item in getNacosOne('agentRank')" :key="item.value"
:label="item.value + '-' + item.label" disabled :value="item.value">
</el-option>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column align="center">
<template slot="header">
<span>
<span style="color: red; font-size: 16px">*</span>主表列3
</span>
</template>
<template slot-scope="scope">
<el-form-item :prop="`tableData.${scope.$index}.asalaryCode`" :rules="rules.asalaryCode"
style="margin-top:18px">
<el-select v-model="scope.row.asalaryCode" @change="salaryChange(scope)" clearable placeholder="請選擇"
filterable class="item-width">
<el-option v-for="item in salaryArrDis" :key="item.value" :label="item.value" :value="item.value">
</el-option>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center">
<template slot-scope="scope">
<el-button v-if="(scope.row.isEdit || scope.row.isConf) && scope.row.list.length > 0" @click="save(scope)"
:disabled="scope.row.isLoading" type="text">保存
</el-button>
<el-button v-else-if="scope.row.list.length > 0"
@click="expandChange(scope.row,[1,2]),edit(scope.row,'isEdit')" type="text">配置
</el-button>
<!-- 刪除大項 -->
<el-popconfirm title="該薪資項系數(shù)配置信息將被刪除,請確認(rèn)" @confirm="deleteOne(tableForm.tableData,scope.$index)"
:disabled="scope.row.isLoading" style="margin: 0 10px;">
<el-button slot="reference" type="text">刪除</el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</el-form>
js部分
export default {
data() {
return {
manageComArr: [],
iq: 0,
// el-form 組件需要用一層對象包裹起來
tableForm: {
tableData: [],
},
expandRowKeys: [], // 用于展開默認(rèn)項
// 校驗規(guī)則對象
rules: {
manageCom: [{ required: true, message: "請選擇", trigger: "change" }],
gradeCode: [
{
type: "array",
required: true,
message: "請選擇",
trigger: "change",
},
],
asalaryCode: [{ required: true, message: "請選擇", trigger: "change" }],
stdKey: [{ required: true, message: "請選擇", trigger: "change" }],
minStd: [
{
required: true,
message: "請輸入",
trigger: "blur",
},
{ validator: checkStd, trigger: "blur" },
],
maxStd: [
{ required: true, message: "請輸入", trigger: "blur" },
{ validator: checkStd, trigger: "blur" },
],
stdRatio: [
{ required: true, message: "請輸入", trigger: "blur" },
{
validator: (rule, value, callback) =>
value >= 0 ? callback() : callback(new Error("范圍大于等于0!")),
trigger: "blur",
},
],
secondRatio: [
{ required: true, message: "請輸入", trigger: "blur" },
{
validator: (rule, value, callback) =>
value >= 0 ? callback() : callback(new Error("范圍大于等于0!")),
trigger: "blur",
},
],
stdValue: [
{ required: true, message: "請輸入", trigger: "blur" },
{
validator: (rule, value, callback) =>
value >= 0 ? callback() : callback(new Error("請輸入整數(shù)!")),
trigger: "blur",
},
],
},
// 用于生成小表格的數(shù)據(jù)
salaryArr: [
{
value: "03",
fileBytes: [
{ model: "stdKey", type: "select" },
{ model: "minStd", type: "input" },
{ model: "maxStd", type: "input" },
{ model: "stdRatio", type: "input" },
],
},
{
value: "04",
fileBytes: [
{ model: "stdKey", type: "select" },
{ model: "minStd", type: "input" },
{ model: "maxStd", type: "input" },
{ model: "stdRatio", type: "input" },
],
},
{
value: "05",
fileBytes: [
{ model: "stdKey", type: "select" },
{ model: "minStd", type: "input" },
{ model: "maxStd", type: "input" },
{ model: "stdRatio", type: "input" },
],
},
{
value: "06",
fileBytes: [
{ model: "stdKey", type: "select" },
{
model: "stdRatio",
type: "input",
},
{ model: "stdValue", type: "input" },
],
},
{
value: "07",
fileBytes: [
{ model: "stdKey", type: "select" },
{ model: "minStd", type: "input" },
{ model: "maxStd", type: "input" },
{ model: "stdRatio", type: "input" },
],
},
{
value: "08",
fileBytes: [
{ model: "stdKey", type: "select" },
{
model: "grownDate",
type: "text",
notRequired: true,
},
{ model: "stdRatio", type: "input" },
{
model: "secondRatio",
type: "input",
},
],
},
{
value: "09",
fileBytes: [
{ model: "stdKey", type: "select" },
{ model: "minStd", type: "input" },
{ model: "maxStd", type: "input" },
{ model: "stdRatio", type: "input" },
],
},
{
value: "10",
fileBytes: [
{ model: "stdKey", type: "select" },
{ model: "minStd", type: "input" },
{ model: "maxStd", type: "input" },
{ model: "stdRatio", type: "input" },
],
},
],
};
},
methods: {
// 結(jié)構(gòu)為題,我們要保存的是主表中的一行(三個字段)和子表整個表格,所以主表的那三個單獨字段的校驗就需要做特殊處理
save(scope) {
let that = this;
let validateFieldList = [];
// 主表格單條數(shù)據(jù)提交校驗 三個字段
this.$refs.tableForm.validateField(
[
`tableData.${scope.$index}.manageCom`,
`tableData.${scope.$index}.gradeCode`,
`tableData.${scope.$index}.asalaryCode`,
],
async (valid) => {
validateFieldList.push(valid);
if (validateFieldList.length == 3) {
// 校驗子表
that.$refs[`tableForm${scope.$index}`].validate((validI) => {
// 都成功才成功
if (validateFieldList.every((item) => item === "") && validI) {
that.saveReq();
} else that.$message.error(VALID_ERR_TABLE);
});
}
}
);
},
// 展開行觸發(fā)事件,主動展開的是哪一行,就把哪一行的 row-key 放到 expandRowKeys 數(shù)組中,只要 expandRowKeys 只有一項,那就只展開一項,別的項就會收起
expandChange(row, expandedRows) {
this.$set(
this,
"expandRowKeys",
expandedRows.length == 0 ? [] : [row.iq]
);
},
// 子表添加一行
childrenAdd(tableData) {
let obj = { isEdit: true, add: 1, stdKey: "" };
tableData.push(obj);
},
// 獲取數(shù)據(jù)后將 iq 放到每一項數(shù)據(jù)中
tableThen(res) {
this.iq = 0;
res.data.list.forEach((item) => {
this.iq++;
item.iq = this.iq.toString(); // row-key="iq" 這個必須是字符串,蛋疼
if (item.list) item.list.forEach((itemC) => (itemC.iq = this.iq++));
});
this.$set(this.tableForm, "tableData", res.data.list);
},
},
};
總結(jié)
- 表格單行保存、編輯呢,一般可以使用 el-table-column 組件插槽中的 $index 就可以判斷出要編輯的是哪一行了,不需要在獲取到數(shù)據(jù)之后再手動給每一條數(shù)據(jù)添加 key 去判斷,此處是因為有別的用處;
- 表格的單個數(shù)據(jù)的編輯也可以使用 el-table-column 組件插槽中的 $index 加上字段名稱就可定位到修改哪一行的哪個數(shù)據(jù);
- 此功能的難點在表格單行數(shù)據(jù)的保存時的數(shù)據(jù)校驗,在校驗對象中,也可以加自定義校驗規(guī)則,不沖突,特別要注意的就是 prop 屬性,只要 prop 屬性是對的,組件能找到該數(shù)據(jù),就能進行規(guī)則校驗;