1、需求
1)、按后台返回的数据进行合并单元格;
2)、点击新增,表格里面内容新增一行新的数据到最下方,并且序号加一;
3)、批量删除,勾选复选框点击删除,删除所勾选的数据,并且序号重新排序;
4)、勾选复选框,点击复制,复制所选的数据到列表最下方,并且序号重新排序;
5)、表格里面也有新增和删除功能,删除是删除当前行,新增是新增到当前行的下面,并且前面列动态合并,序号也重新排序;
2、效果图
3、功能图片
1)、新增
2)、删除
3)、复制
4)、table表格里的删除新增
4、代码
1)、HTML结构
注:N20是我们公司自己封装的组件,但是底层还是elementUI,结构并不是很重要,主要是结构上的:span-method="objectSpanMethod"方法。
<template>
<div style="position: static !important;" >
<N20-expandable-pane title="风险场景规则" class="risk_scenario_form_class">
<template slot="tips">
<el-button type="primary" size="mini" @click="handleClick('add')">新增</el-button>
<el-button size="mini" plain @click="handleClick('copy')">复制</el-button>
<el-button type="danger" size="mini" plain @click="handleClick('delete')">删除</el-button>
</template>
<el-form :model="formTableData" ref="formTableData" >
<N20-table :showSetsize="true" :data="formTableData.tableData" :columns="columns" @select="handleTableSelection" @select-all="handleTableSelectionAll" :span-method="objectSpanMethod" border>
<el-table-column label="指标模型名称" slot="name" align="center" width="248" :render-header="addRedStar" >
<template slot-scope="scope">
<el-form-item :rules="[ { required: true, message: '', trigger: 'change' } ]" :prop="'tableData.' + scope.$index + '.name'">
<N20-input-search class="defind-input-width" v-model="scope.row.name" readonly @click.native="chooseIndicator(scope.$index,scope.row)" :is-clearable="true" v-title="scope.row.name"/>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="风险管控类型" slot="fxgklx" align="center" width="248" :render-header="addRedStar">
<template slot-scope="scope">
<el-form-item :rules="[ { required: true, message: '', trigger: 'change' } ]" :prop="'tableData.' + scope.$index + '.fxgklx'">
<el-select style="width:224px" v-model="scope.row.fxgklx" clearable>
<el-option v-for="item in fxgklxOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="触发数据/业务类型" slot="ywlx" align="center" width="248" :render-header="addRedStar">
<template slot-scope="scope">
<el-form-item :rules="[ { required: true, message: '', trigger: 'change' } ]" :prop="'tableData.' + scope.$index + '.ywlx'">
<N20-input-search class="defind-input-width" v-model="scope.row.ywlx" readonly @click.native="chooseIndicator(scope.$index,scope.row)" :is-clearable="true" v-title="scope.row.ywlx"/>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="频率" slot="pl" align="center" width="116" :render-header="addRedStar">
<template slot-scope="scope">
<el-form-item :rules="[ { required: true, message: '', trigger: 'change' } ]" :prop="'tableData.' + scope.$index + '.pl'">
<el-select style="width:100px" v-model="scope.row.pl" clearable>
<el-option v-for="item in fxgklxOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="适用单位设置" slot="sydwsz" align="center" width="118" >
<template slot-scope="scope">
<el-form-item>
<el-button type="text" @click="handleSetting(scope.row)">设置</el-button>
</el-form-item>
</template>
</el-table-column>
<el-table-column slot="yz" align="center" width="400">
<template slot="header">
<span><span style="color:red;margin-right: 4px">*</span>阈值</span><i class="n20-icon-xinxitishi" style="margin-left: 4px" v-title="'实际场景规则无对比阈值的,此项可空,比例类型阈值需转换成小数类型数值项输入'" />
</template>
<template slot-scope="scope">
<el-form-item :rules="[ { required: true, message: '', trigger: 'change' } ]" :prop="'tableData.' + scope.$index + '.yz'">
<el-select style="width:100px;margin-right:4px" v-model="scope.row.yz" clearable>
<el-option v-for="item in fxgklxOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
<el-input style="width:224px;margin-right:4px" v-model="scope.row.yz" clearable/>
<el-button icon="n20-icon-xinzeng" type="text" @click="handleTableAdd(scope.row,scope.$index)"></el-button>
<el-button icon="n20-icon-a-shanchuxuanzhong" type="text" @click="handleTableDel(scope.row,scope.$index)"></el-button>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="风险等级" slot="fxdj" align="center" width="118" :render-header="addRedStar">
<template slot-scope="scope">
<el-form-item :rules="[ { required: true, message: '', trigger: 'change' } ]" :prop="'tableData.' + scope.$index + '.fxdj'">
<el-select style="width:100px" v-model="scope.row.fxdj" clearable>
<el-option v-for="item in fxgklxOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="风险事件设置" slot="fxsjsz" align="center" width="118" >
<template slot-scope="scope">
<el-form-item>
<el-button type="text" @click="handleSetting(scope.row)">设置</el-button>
</el-form-item>
</template>
</el-table-column>
</N20-table>
</el-form>
</N20-expandable-pane>
<div class="operate-box" style="right:6px;width: calc(100% - 12px);left: 6px;">
<el-button @click="handleConfirm('saveAndStart')" type="primary">保存启用</el-button>
<el-button plain @click="handleConfirm('save')" >保存</el-button>
</div>
</div>
</template>
2)、JS代码
export default {
name: 'dispositionAdd',//风险场景配置 新增
data () {
return {
isIndexShow: false,
indexRecordData: {},
indexNum:0,
formData: {
yjfxcj: null,//一级风险场景
ywcjbh:null,//业务场景编号
ywcjmc:null,//业务场景名称
gzmc:null,//规则名称
gzlx:null,//规则类型
ywcjms:null,//业务场景描述
},
formUnitData: {
hyfl:null,//行业分类
dwmc:null,//单位名称
dwbh: null,//单位编号
current: 1,
size: 100,
total: 0,
},
flag:false,
rules: {
yjfxcj: [
{ required: true, message: '请输入', trigger: 'blur' }
],
ywcjbh: [
{ required: true, message: '请输入', trigger: 'blur' }
],
ywcjmc: [
{ required: true, message: '请输入', trigger: 'blur' }
],
gzmc: [
{ required: true, message: '请输入', trigger: 'blur' }
],
},
formTableData: {
tableData: [
{ name: '上下游单位同名', type: '指标', fxgklx: '事前预测', ywlx: '',index:'1' },
{ name: '上下游单位同名', type: '指标', fxgklx: '事前预测', ywlx: '',index:'1' },
{ name: '上下游单位同名', type: '指标', fxgklx: '事前预测', ywlx: '',index:'1' },
{ name: '同名双向结算', type: '指标', fxgklx: '事后预警', ywlx: '', index: '2' },
{ name: '贸易合同异常模型', type: '评价模型', fxgklx: '事后预警', ywlx:'',index:'3' },
{ name: '反洗钱模型', type: '计算模型', fxgklx: '事后预警', ywlx:'',index:'4' },
]
},
fxgklxOptions: [],
rowSpanArr: [],
columns: [
{ type: 'selection', align: 'center', width: '46' },
{ prop: 'index', label:'序号', align: 'center', width: '80' },
{ label: '指标模型名称', align: 'center', slotName: 'name' },
{ label: '类型', align: 'center', prop: 'type' },
{ label: '风险管控类型', align: 'center', slotName: 'fxgklx' },
{ label: '触发数据/业务类型', align: 'center', slotName: 'ywlx' },
{ label: '频率', align: 'center', slotName: 'pl' },
{ label: '适用单位设置', align: 'center', slotName: 'sydwsz' },
{ label: '阈值', align: 'center', slotName: 'yz' },
{ label: '风险等级', align: 'center', slotName: 'fxdj' },
{ label: '风险事件设置', align: 'center', slotName: 'fxsjsz' },
],
sltList: [],//table 复选框 选择集合
}
},
created() {
// 模拟 处理table数据 收集要合并的数据
this.handleTableData(this.formTableData.tableData)
},
methods: {
// 保存 和 保存启用
handleConfirm (type) {
console.log('formTableData',this.formTableData)
if (this.handleValidate()) {
switch (type) {
case 'saveAndStart':
break;
default:
break;
}
}
},
// 校验所有表单
handleValidate () {
let flag = false
let validateList = ['formTableData','formRef']
validateList.map(item=>{
this.$refs[item].validate((valid) => {
if (valid) {
flag = true
} else {
flag = false
}
})
})
return flag
},
// table 新增
handleTableAdd (row,index) {
let tableData = JSON.parse(JSON.stringify(this.formTableData.tableData))
tableData.splice(index,0,row)
console.log('tableData----新增---->>>',tableData)
this.$set(this.formTableData, 'tableData', tableData)
// 处理table数据 收集要合并的数据
this.handleTableData(this.formTableData.tableData)
},
// table 删除
handleTableDel (row, index) {
console.log('table---删除单个--->>>',row, index)
let tableData = JSON.parse(JSON.stringify(this.formTableData.tableData))
if (Array.isArray(tableData) && tableData.length && tableData.indexOf(row)) {
tableData.splice(index, 1);
let tableDataOther = JSON.parse(JSON.stringify(tableData))
this.handleSetArray(tableDataOther).map((item, index) => {
let num = index + 1
tableData.map(ele => {
if (item.index == ele.index) {
ele.index = num
}
})
})
this.$set(this.formTableData, 'tableData', tableData)
// 处理table数据 收集要合并的数据
this.handleTableData(this.formTableData.tableData)
}
},
// 新增 复制 删除
handleClick (type) {
// tableData table的数据源
let tableData = JSON.parse(JSON.stringify(this.formTableData.tableData))
// 复选之后数组收集
let sltList = JSON.parse(JSON.stringify(this.sltList))
if (type == 'add') {
// 新增
let num = this.handleSetArray(tableData).length
let tableDataItem = { name: '', type: '', fxgklx: '', ywlx: '',index:num+1 }
this.formTableData.tableData.push(tableDataItem)
} else if(type == 'copy'){
// 复制
if (!sltList.length) this.$message.info('请选择数据')
// this.flag为true则是全选 为false则为不全选,因为点击复制之后 复选框全部清除勾选,所以点击全选之后再点击单选,flag还是为true,则走的还是全选的逻辑,所以得重置为false
this.flag = false
// sltListOther 作用是 对比去重之后的数据 给index赋值
let sltListOther = JSON.parse(JSON.stringify(this.sltList))
// 收集 去重之后的table的长度,用于复制的序列号的使用
let num = this.handleSetArray(tableData).length
// 去重复选框勾选的集合
sltList = this.handleSetArray(sltList)
console.log('复制---sltList--->>>', sltList)
sltList.map(item => {
num++
sltListOther.map((ele=>{
if (item.index == ele.index) {
ele.index = num
}
}))
})
console.log('复制---sltListOther--->>>', sltListOther)
// 合并table和复选框选择的数据 最后赋值给tableData
let copyArr = this.formTableData.tableData.concat(sltListOther)
console.log('复制---copyArr--->>>',copyArr)
this.formTableData.tableData = copyArr
} else {
// 删除
if (sltList.length) {
sltList.map(item => {
tableData.map((ele,index) => {
if (item.index == ele.index) {
tableData.splice(index, 1);
}
})
})
console.log('删除---tableData--->>>',tableData)
let tableDataOther = JSON.parse(JSON.stringify(tableData))
console.log('删除---tableDataOther--->>>',tableDataOther)
this.handleSetArray(tableData).map((item, index) => {
let num = index + 1
tableDataOther.map(ele => {
if (item.index == ele.index) {
ele.index = num
}
})
})
this.$set(this.formTableData,'tableData',tableDataOther)
console.log('删除--->>>',this.formTableData.tableData)
} else {
this.$message.info('请选择数据')
}
}
// 处理table数据 收集要合并的数据
this.handleTableData(this.formTableData.tableData)
this.sltList = []
},
// 根据index去重
handleSetArray (arr = []) {
if (Array.isArray(arr)) {
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i].index == arr[j].index) {
arr.splice(j, 1);
j--
}
}
}
return arr
} else {
return []
}
},
// @select="handleTableSelection" table的单选事件
handleTableSelection(val,row){
// table的数据源
let tableData = [...this.formTableData.tableData]
// 点击table复选框 收集 数据源
let sltList = [...this.sltList]
// flag为true的时候代表的是全选 为false则代表不全选
// 全选--勾选or不勾选逻辑
if(this.flag){
// isSelect为true代表全选之后点击取消
// isSelect为false代表全选之后点击取消然后又点勾选
let isSelect = sltList.length && sltList.indexOf(row) > -1
// 收集第一次勾选取消之后 收集原来勾选的数据
let sltListCheck = []
if(isSelect){
tableData.forEach(item=>{
if(item.index != row.index){
sltListCheck.push(item)
}
})
this.sltList = sltListCheck
}else{
// 当再次勾选的时候 在原有的sltList集合 push新的勾选数据
tableData.forEach(item=>{
if(item.index == row.index){
this.sltList.push(item)
}
})
}
console.log('全选--勾选or不勾选逻辑-->>',this.sltList)
}else{
// 不全选--勾选or不勾选逻辑
let checkList = []
if (tableData.length && val.length) {
// tableData为table数据数组
// val为复选框选择之后的数组
// 两个数组相互比较 如果index相同 则收集到checkList数组里面
tableData.forEach(item => {
val.forEach(ele => {
if (item.index == ele.index) {
checkList.push(item)
}
})
})
}
this.sltList = checkList
console.log('不全选--勾选or不勾选--sltList-->>>',this.sltList)
}
},
// @select-all="handleTableSelectionAll" table的全选事件
handleTableSelectionAll(sltList){
if (sltList.length) {
// flag为true的时候代表的是全选 为false则代表不全选
// sltList 为点击table复选框 收集 数据源
this.flag = true
this.sltList = this.formTableData.tableData
}else{
this.flag = false
this.sltList = []
}
console.log('handleTableSelectionAll',this.sltList)
},
// 单位选择 确认
handleConfirmIndex (val) {
console.log('单位选择 确认', val)
let tableData = [ ...this.formTableData.tableData ]
let indexRecordData = this.indexRecordData
tableData.forEach((item,index) => {
if (item.index == indexRecordData.index) {
tableData[index].name = val.name
}
})
this.$set( this.formTableData,'tableData',tableData)
this.isIndexShow = false
},
// 单位选择 取消
handleCloseUnit () {
this.isIndexShow = false
},
// 设置
handleSetting (row) {
},
// 处理table数据 收集要合并的数据
handleTableData (tableData = []) {
let rowSpanArr = []
let position = 0
if (!!tableData && Array.isArray(tableData)) {
tableData.map((item, index) => {
if (index == 0) {
rowSpanArr.push(1)
position = 0
} else {
if (item.index == tableData[index - 1].index) {
rowSpanArr[position] += 1
rowSpanArr.push(0)
} else {
rowSpanArr.push(1)
position = index
}
}
})
}
this.rowSpanArr = rowSpanArr
},
// table合并单元格
objectSpanMethod ({ row, column, rowIndex, columnIndex }) {
if (columnIndex != 8 && columnIndex != 9 && columnIndex != 10) {
let rowspan = this.rowSpanArr[rowIndex]
return {
rowspan: rowspan,
colspan: 1
};
}
},
// 选择指标模型 并带出数据 和 下标
// index 下标 indicator带出的数据
chooseIndicator (index, indicator) {
this.isIndexShow = true
this.indexRecordData = indicator
this.indexNum = index
},
// 处理table 星号 变红
addRedStar(h, { column }) {
return [
h("span", { style: "color: red" }, "*"),
h("span", " " + column.label),
];
},
},
}
</script>