参考官方技术文档:http://jeecg-boot.mydoc.io/?t=345687
初始需求:如下图,当我修改产品名称的选项时,产品编码和产品规格的字段会根据我给出的数组自动改变。
1.首先我们需要将column
类型改为slot
才能自定义产品名称的可选列表以及添加同时修改功能。需要注意的是在技术文档中我们可以看到,当 type=slot
时所需的参数:
参数 |
类型 |
必填 |
说明 |
slotName |
string |
✔️ |
slot的名称 |
所以最终代码如下:
columns: [
{
title: '产品名称',
key: 'materialName',
type: FormTypes.slot,
slotName: 'materialName',
width: '20%',
},
{
title: '产品编码',
key: 'materialCode',
type: FormTypes.slot,
slotName: 'materialCode',
width: '20%',
},
{
title: '产品规格',
key: 'materialSpecs',
type: FormTypes.slot,
slotName: 'materialSpecs',
width: '60%',
},
2.然后再html
代码中添加对应的插槽:
<j-editable-table
:ref="tableName"
:loading="loading"
:columns="columns"
:dataSource="dataSource"
:maxHeight="300"
:rowNumber="true"
:rowSelection="true"
:actionButton="true"
>
<template v-slot:materialName="props">
<a-select
:default-value="props.text"
placeholder="请选择产品名称"
style="width: 100%"
show-search
@change="(value) => handleProChange(value, props.rowId)"
>
<a-select-option
v-for="(item, i) in productsList"
:key="i"
:value="item.materialName"
>
{{ item.materialName }}
</a-select-option>
</a-select>
</template>
<template v-slot:materialCode="props">
<a-input :value="props.text" disabled />
</template>
<template v-slot:materialSpecs="props">
<a-input :value="props.text" disabled />
</template>
</j-editable-table>
其中这个props
参数,在技术文档中我们已经可以了解到它的基本用途:
- props.index :当前行的下标
- props.text :当前值,可能是defaultValue定义的值,也可能是从dataSource中取出的值
- props.rowId :当前选中行的id,如果是新增行则是临时id
- props.column :当前操作的列
- props.getValue :这是一个function,执行后可以获取当前行的所有值(禁止在template中使用)例:const value = props.getValue()
- props.target :触发当前事件的实例,可直接调用该实例内的方法(禁止在template中使用)例:target.add()
其中props.getValue()
的返回值包含行id和conlums对应字段的值,如下图:
另外需要注意的是,选择器的@change
只有value
一个默认参数,如果我们需要传递额外的参数:
<a-select @change="(value) => handleProChange(value, props.rowId)">
//...
</a-select>
3.最后是响应修改的方法:
handleProChange(value, id) {
let item = this.productsList.find((ele) => ele.materialName == value)
let values = [
{
rowKey: id,
values: {
'materialName': value,
'materialCode': item.materialCode,
'materialSpecs': item.materialSpecs,
},
},
]
this.$refs.tableName.setValues(values)
}
这里用到了实例封装的setValues
方法,在技术文档中有功能介绍:
主动设置表格中某行某列的值
参数名 |
类型 |
必填 |
说明 |
values |
array |
|
传入一个数组,数组中的每项都是一行的新值,具体见下面的示例 |
setValues([
{
rowKey: id1, // 行的id
values: { // 在这里 values 中的 name 是你 columns 中配置的 key
'name': 'zhangsan',
'age': '20'
}
},
{
rowKey: id2,
values: {
'name': 'lisi',
'age': '23'
}
}
])
方法的调用也很简单,我们已经为Table设定了一个 ref="tableName"
的属性,那么在vue中就可以使用this.$refs.tableName
获取到该表格的实例,并调取其中的方法。在这里就是
this.$refs.tableName.setValues(values)
至此我们就实现了文章开头的需求。
开始接触这个组件遇到了很多麻烦,求助我的开发助手百度时,感觉这篇文章的入门也写得极为详细:JeecgBoot之自定义组件JEditableTable应用,费解的同学也可以多阅读~当然官方文档已然非常详尽。
需求更新2020/12/3:
今天发现同一个名称下存在不同的可选规格,然后对应唯一的编号,因此我们需要在选择产品名称后提供一个规格选择框,最后自动填上编码:
从后台传过来的数据格式大致如下:
data = [
{
materialName: '名字1',
materialSpec: '规格1.1',
materialCode: '编码1.1',
},
{
materialName: '名字1',
materialSpec: '规格1.2',
materialCode: '编码1.2',
},
{
materialName: '名字2',
materialSpec: '规格2.1',
materialCode: '编码2.2',
},
//.....
]
需求很明显可以看到,名称和规格是存在级联关系的,推荐使用antDesign中的Cascader
组件,但在后端不做更改的情况下我们需要保留前端的基本格式,所以仍然使用Selector
组件。
在开始之前,我们可以先参考官方文档中的省市联动例子,便于理解以下更复杂的运用。
data层需要绑定三个数据:
data() {
return {
//......
nameList: [], //产品名称列表
specList: [], //选定产品后对应的规格列表对象
specCodeList: {}, //名称对应的编码和规格对象
}
}
//其中specCodeList是所有数据的对应关系,类似于官方实例中的cityData,可以对比参考。在这里实例数据将转换为如下对象:
specCodeList = {
'名字1' : {
'规格1.1' : '编码1.1',
'规格1.2' : '编码1.2',
},
'名字2' : {
'规格2.1' : '编码2.1',
},
//......
}
关于从data
到specCodeList
的转化同时获取nameList
:(这里面会涉及很多对对象的操作、运用、处理)
let specCodeList = {}
for (let i =0; i < data.length; i++) {
let name = data[i].materialName
if (name in specCodeList) {
specCodeList[name][data[i].materialSpecs] = data[i].materialCode
} else {
specCodeList[name] = {}
specCodeList[name][data[i].materialSpecs] = data[i].materialCode
}
}
this.nameList = Object.keys(specCodeList)
this.specCodeList = specCodeList
- 首先修改
materialSepc
的插槽:
这其中的@focus
和@change
方法稍后会作解释,先把组件填上。需要注意的是由于我们有可能需要通过setValues()
方法来修改选择器显示的值,所以这里有:value="props.text"
而不是default-value
<a-select
:value="props.text"
placeholder="请选择产品规格"
style="width: 100%"
show-search
@focus="handleFocus(props)"
@change="(value) => handleSpecChange(value, props)"
>
<a-select-option v-for="(item, i) in specList" :key="i" :value="item">
{{ item }}
</a-select-option>
</a-select>
- 然后是
materialName
下的@change
方法:
/**
*@param [value, id] 用户选择的产品名,对应行id
*这里要实现两个功能:
*1. 给出规格可选列表;
*2. 把所选值赋值给Table对应位置
*/
handleProChange(value, id) {
//修改规格可选项:
this.specList = Object.keys(this.specCodeList[value])
//改变列表值:
let values = [
{
rowKey: id,
values: {
'materialName': value,
'materialCode': this.specCodeList[value][this.specList[0]], //编号跟随规格默认值
'materialSpecs': this.specList[0], //规格默认可选值列表的第一个
},
},
]
this.$refs.attrMaterialInfo.setValues(values)
}
- 选择好产品名称获得可选规格列表后,用户便会选择规格,对应的
@change
方法:
/**
*@param [value, props] 用户选择的规格,props
*功能:把新值赋给Table
*/
handleSpecChange(value, props) {
let { materialName: name, id } = props.getValue()
// console.log("对应编码是" + this.specCodeList[name][value])
let values = [
{
rowKey: id,
values: {
'materialName': name,
'materialCode': this.specCodeList[name][value],
'materialSpecs': value,
},
},
]
this.$refs.attrMaterialInfo.setValues(values)
}
要注意: 每次切换产品名称后,规格可选列表specList
都会变化,所以我们需要在所有选择器上绑定@focus
,以便当用户点击其他行的选择器时更新可选规格列表(否则会出现规格可选列表与产品名不对应的情况):
//specList需要根据聚焦行改变:
handleFocus(props) {
let {materialName} = props.getValue()
// console.log(props.getValue())
if (materialName) {
this.specList = Object.keys(this.specCodeList[materialName])
} else {
//对于没有选择名称的空行,可选列表也对应为空
this.specList = []
}
}
至此便实现了我们的第二个需求。总结来说,需求总是不停变化的,还是要对组件熟悉,对ES6熟悉才能解决问题。刚开始我想将规格可选列表specList
作为对象存储,key
是行id,value
是对应的可选列表。这样的话,在选择器访问时就需要写作specList[props.id]
,但是我发现选择器并接收对象内值得变化,始终是初始值,所以specList
必须是字符串数组,于是乎只能通过@focus
方法不断变幻specList
对应的值。