代码
<template>
<div class="edit-table">
<el-button
ref="button"
type="primary"
size="medium"
@click.native.prevent="handleAddRow"
>
<i class="el-icon-plus"></i>
添加
</el-button>
<el-form :rules="rules" :model="form" ref="ruleForm">
<el-table
id="dialog-table"
:data="form.tableData"
size="small"
class="table-wrap"
@cell-mouse-enter="handleCellEnter"
@cell-mouse-leave="handleCellLeave"
@cell-click="handleCellClick"
>
<el-table-column prop="categoryValue" label="码表值" width="300">
<template slot-scope="scope">
<el-form-item
:prop="'tableData.' + scope.$index + '.categoryValue'"
:rules="rules.categoryValue"
>
<el-input
ref="categoryValueInput"
class="item__input"
v-model="scope.row.categoryValue"
placeholder="请输入内容"
@blur="handleSaveRow(scope.row)"
maxlength="50"
show-word-limit
></el-input>
</el-form-item>
<div ref="categoryValueTxt" class="item__txt l-box-start-center">
{{ scope.row.categoryValue }}
</div>
</template>
</el-table-column>
<el-table-column prop="categoryKey" label="码表项" width="300">
<div class="item" slot-scope="scope">
<el-form-item
:prop="'tableData.' + scope.$index + '.categoryKey'"
:rules="rules.categoryKey"
>
<el-input
ref="categoryKeyInput"
class="item__input"
v-model="scope.row.categoryKey"
placeholder="请输入内容"
@blur="handleSaveRow(scope.row)"
maxlength="50"
show-word-limit
></el-input>
</el-form-item>
<div ref="categoryKeyTxt" class="item__txt l-box-start-center">
{{ scope.row.categoryKey }}
</div>
</div>
</el-table-column>
<el-table-column prop="notValid" label="有效标识">
<div class="item" slot-scope="scope">
<el-select
class="item__input select__input l-box-start-center"
popper-class="edittable-select"
v-model="scope.row.notValid"
placeholder="请选择"
@blur="handleSaveRow(scope.row)"
>
<el-option
v-for="item in validOptions"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
<div class="item__txt l-box-start-center">
{{ validLabel(scope.row.notValid) }}
</div>
</div>
</el-table-column>
<el-table-column label="操作" width="100">
<template slot-scope="scope">
<el-button
class="edit-table-btn-oper"
@click.native.prevent="handleDeleteRow(scope.$index, scope.row)"
type="text"
size="small"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
</el-form>
</div>
</template>
<script>
export default {
name: 'EditTable',
props: {
categoryList: {
type: Array,
default: () => [],
},
isEditMode: {
type: Boolean,
default: false,
},
},
data() {
return {
// 下拉选项
validOptions: [
{
value: '00',
label: '有效',
},
{
value: '01',
label: '无效',
},
],
// 需要编辑的属性
editProp: ['categoryKey', 'categoryValue', 'notValid'],
clickCellMap: {}, // 记录当前编辑行所有cell的id,用于失焦时取消编辑状态
changeCnt: 0,
form: {
tableData: _.cloneDeep(this.categoryList),
},
rules: {
categoryValue: [
{
required: true,
message: '码表值不能为空',
trigger: 'blur',
},
],
categoryKey: [
{
required: true,
message: '码表项不能为空',
trigger: 'blur',
},
],
},
}
},
computed: {
validLabel() {
return (val) => {
if (!val) {
return '有效'
}
return this.validOptions.find((o) => o.value === val).label
}
},
},
created() {
// this.init()
},
methods: {
init() {
// this.$nextTick(() => {
// console.log(this.form.tableData)
// })
},
/** 鼠标移入cell */
handleCellEnter(row, column, cell, event) {
const property = column.property
if (this.editProp.includes(property)) {
cell.querySelector('.item__txt').classList.add('item__txt--hover')
}
},
/** 鼠标移出cell */
handleCellLeave(row, column, cell, event) {
const property = column.property
if (this.editProp.includes(property)) {
cell.querySelector('.item__txt').classList.remove('item__txt--hover')
}
},
/** 鼠标点击cell */
handleCellClick(row, column, cell, event) {
const property = column.property
if (this.editProp.includes(property)) {
// 保存cell
this.saveCellClick(row, cell)
this.setEditable(cell)
// 下拉选框点击展开
let selectCell = cell.querySelector('.item__input.select__input')
selectCell && selectCell.__vue__.toggleMenu()
}
},
/** 保存数据(失焦时触发) */
handleSaveRow(curRow) {
this.$refs.ruleForm.validate((valid, notValidObj) => {
// 根据校验情况设置按钮状态
this.$emit('setBtnDisabled', !valid)
// 若当前行通过校验,则取消编辑状态
const notValidRowIdx = Object.keys(notValidObj).map(
(key) => key.match(/tableData\.(\S*)\.category/)[1]
)
const curRowValid = !notValidRowIdx.some(
(idx) => idx == curRow.id.replace('row', '')
)
curRowValid && this.deleteCellClick(curRow.id)
})
},
/** 保存进入编辑的cell */
saveCellClick(row, cell) {
const id = row.id
if (this.clickCellMap[id] == undefined) {
this.clickCellMap[id] = [cell]
return
}
if (!this.clickCellMap[id].includes(cell)) {
this.clickCellMap[id].push(cell)
}
},
/** 删除取消编辑状态的cell */
deleteCellClick(rowId) {
// 取消当前行所有cell的编辑状态
if (this.clickCellMap[rowId]) {
this.clickCellMap[rowId].forEach((cell) => {
this.cancelEditable(cell)
})
delete this.clickCellMap[rowId]
}
},
/** 添加数据(点击添加触发) */
handleAddRow() {
const lastItemIndex = this.form.tableData.length
const item = {
id: `row${lastItemIndex}`, // 初始化,保存到后台会自动生成id
categoryKey: '',
categoryValue: '',
notValid: '00',
}
this.form.tableData.push(item)
this.$nextTick(() => {
// 记录编辑状态
const tableDom = document.getElementById('dialog-table')
const rowDomList = tableDom.getElementsByClassName('el-table__row')
const cell = rowDomList[lastItemIndex].querySelector('td')
this.saveCellClick(item, cell)
// 聚焦
this.$refs.categoryValueTxt.style.display = 'none'
this.$refs.categoryValueInput.$el.style.display = 'flex'
this.$refs.categoryValueInput.$refs.input.focus()
})
},
/** 删除数据(点击删除触发) */
handleDeleteRow(index, row) {
this.form.tableData.splice(index, 1)
this.deleteCellClick(row.id)
// 待界面更新后再进行验证
this.$nextTick(() => {
this.$refs.ruleForm.validate((valid) => {
if (!valid) return
// 列表不为空时, 确定按钮设为可用
!_.isEmpty(this.categoryList) && this.$emit('setBtnDisabled', false)
})
})
},
/** 进入编辑状态 */
setEditable(cell) {
cell.querySelector('.item__txt').style.display = 'none'
cell.querySelector('.item__input').style.display = 'flex'
cell.querySelector('input').focus()
},
/** 取消编辑状态 */
cancelEditable(cell) {
cell.querySelector('.item__txt').style.display = 'flex'
cell.querySelector('.item__input').style.display = 'none'
},
// moveToErr() {
// this.$nextTick(() => {
// let isError = document.getElementsByClassName('is-error')
// if (isError.length) {
// isError[0].scrollIntoView({
// block: 'center',
// behavior: 'smooth',
// })
// // 这个当滑动到报错项之后自动获取输入框的焦点,方便用户直接进行输入,延迟 800ms 是因为需要都能到定位成功后在进行获取焦点体验更好一些
// setTimeout(() => {
// if (isError[0]?.querySelector('input')) {
// isError[0].querySelector('input').focus()
// }
// }, 800)
// }
// })
// },
},
watch: {
// 监听表单变动更新状态
'form.tableData': {
handler(val) {
// this.changeCnt++
// if (
// // 编辑模式下第一次赋值不触发变动
// (!this.isEditMode && this.changeCnt > 0) ||
// (this.isEditMode && this.changeCnt > 1)
// ) {
// this.$emit('updateCategoryList', _.cloneDeep(val))
// }
this.$emit('updateCategoryList', _.cloneDeep(val))
},
deep: true,
},
},
}
</script>
<style lang="less" scoped>
/deep/ .el-input__inner {
padding-left: 8px;
}
.edit-table {
height: 100%;
overflow-y: auto;
&-btn-oper {
color: #358df6;
font-weight: bold;
font-size: 14px;
}
.table-wrap {
width: 100%;
height: calc(100% - 36px);
overflow-y: auto;
}
.el-form-item {
padding: 0;
&.is-error {
padding: 0 0 16px 0;
.item__input {
display: block !important;
}
& + .item__txt {
display: none !important ;
}
}
}
}
</style>
<style lang="less">
.el-table thead,
.el-table__row {
color: #333333;
font-size: 14px;
}
.item__input {
display: none;
/* 调整elementUI中样式 如果不需要调整请忽略 */
.el-input__inner {
height: 32px !important;
}
/* 调整elementUI中样式 如果不需要调整请忽略 */
.el-input__suffix {
i {
font-size: 12px !important;
line-height: 26px !important;
}
}
}
.item__txt {
box-sizing: border-box;
border: 1px solid transparent;
width: 100%;
height: 32px;
line-height: 24px;
padding: 0 8px;
}
.item__txt--hover {
border: 1px solid #dddddd;
border-radius: 4px;
cursor: text;
}
.edittable-select {
min-width: 311px !important;
}
.edit-table .el-table__body-wrapper {
height: calc(100% - 40px);
overflow-y: scroll;
}
</style>
可编辑表格未通过校验时,父组件不可提交表单
提交按钮设置disabled,校验通过才为true
未通过时,input框为可编辑状态;用样式改
&.is-error {
padding: 0 0 16px 0;
.item__input {
display: block !important;
}
& + .item__txt {
display: none !important ;
}
}
问题:父组件从后台拿值后传给子组件,当子组件的数据依赖该值时,无法正常更新数据
注:直接用prop的值,数据更新正常
例:子组件中,某个数据依赖父组件发送请求传回的值
<el-form :model="form" ref="ruleForm">
<el-table
id="dialog-table"
:data="form.tableData"
size="small"
class="table-wrap"
>
...
</el-table>
</el-form>
export default {
props: {
list: {
type: Array,
default: () => [],
}
},
data() {
return {
form: {
tableData: this.list,
}
}
}
解决:父组件中,可利用 v-if 条件判断,在数据拿回来后,再渲染子组件
(可加上loading防止出现组件区域空白问题)
<div
v-loading="!ruleForm.codeTableCategoryList.length"
>
<div v-if="ruleForm.codeTableCategoryList.length">
<comp :refer="ref" :basic="basic" :list="ruleForm.codeTableCategoryList" />
</div>
</div>