最近开发项目用的是 Vue + Element-UI 框架,期间遇到了几个 bug,整理一下以便记忆和巩固。
1. el-table 展开行问题
问题描述: 使用 el-table 组件的展开行功能,希望可以做到显示的 table 表格全部展开,然后切换页签的时候显示的内容又全部收起。于是想当然的动态设置 default-expand-all 属性,当活跃页签改变时,default-expand-all 属性也随之改变。但是事实证明这个方法是不可行的。因为切换页签时,el-table 组件是共用的, default-expand-all 属性只能渲染一次。之后不管怎么改变这个属性,都不起作用。
解决办法: 目前想到的方法是仍然动态设置 default-expand-all 属性,在切换页签时,default-expand-all 属性改变,同时刷新 el-table 组件。 代码如下:
<ItemBomTable
v-if="refreshTable"
:bom-data="bomData"
:status="status"
:expand-all="expandAll"
>
<template slot="itemButton" slot-scope="item">
<el-button
v-if="status !== 'cancel'"
type="primary"
size="mini"
plain
class="project-btn"
@click="onShowDialog(item.item.row)"
>修改数量</el-button>
<el-button
v-if="status === 'doing'"
type="primary"
size="mini"
class="project-btn"
@click="onReturn(item.item.row)"
>退回物料车</el-button>
<el-button
type="success"
size="mini"
class="project-btn"
@click="downloadFile(item.item.row)"
>下载资料</el-button>
</template>
<template slot="bomButton" slot-scope="item">
<div v-if="status === 'doing'">
<el-button
type="primary"
size="mini"
class="project-btn"
@click="onDownload(item.item.row)"
>导出 BOM</el-button>
<el-button
type="success"
placement="top"
effect="light"
size="mini"
class="project-btn"
@click="onFinish(item.item.row)"
>完成 BOM</el-button>
<el-button
type="info"
placement="top"
effect="light"
size="mini"
class="project-btn"
@click="onCancel(item.item.row)"
>取消 BOM</el-button>
</div>
<div v-else>
<el-button
type="primary"
size="mini"
class="project-btn"
@click="onMultiReturn(item.item.row)"
>加入物料车</el-button>
</div>
</template>
</ItemBomTable>
在组件中用 v-if=“refreshTable” 语句判断是否刷新 table 表格。
// 切换按钮
changeStatus(value) {
this.status = value;
this.refreshTable = false;
if (value !== 'doing') {
this.expandAll = false;
} else {
this.expandAll = true;
}
this.$nextTick(() => {
this.refreshTable = true;
});
}
在 methods 方法中定义函数 changeStatus,当切换页签时,status 相应改变,然后根据 status 的值来改变 expandAll 的值,将此值传给子组件 ItemBOMTable,从而改变 default-expand-all 属性。同时给 refreshTable 赋值为 false, 当状态改变时使用 this.$nexTick(()=>{ this.refreshTable = true;}) 来刷新 table 表格。Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。例如,当你设置 vm.someData = 'new value',该组件不会立即重新渲染。当刷新队列时,组件会在下一个事件循环“tick”中更新。为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick(callback)。这样回调函数将在 DOM 更新完成后被调用。
2. el-tree 数据响应问题
问题描述:使用 el-tree 组件时,希望修改节点内容,通过改变节点数据实现 input 输入框输入新的内容。于是在点击编辑按钮的同时给节点对象新增属性 editable,然后根据 editable 属性来判断是否显示 input 输入框。但是并不起作用,于是查看 vue.js 文档发现,由于 JavaScript 的限制,Vue 不能检测数组和对象的变化并响应渲染。
解决办法:根据 vue.js 文档描述,由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。对于已经创建的实例,Vue 不允许动态添加根级别的响应式 property。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式 property。于是代码如下:
<div class="block">
<el-tree
:data="locationTreeData"
node-key="id"
:expand-on-click-node="false"
default-expand-all
:indent="30"
style="font-size: 14px"
>
<span slot-scope="{ node, data }" class="custom-tree-node">
<template v-if="node.editable">
<el-input
ref="slotTreeInput"
v-model="data.location"
class="put-style"
size="mini"
><el-button
slot="suffix"
size="mini"
icon="el-icon-check"
type="text"
@click="confirmEdit(node, data)"
/>
</el-input>
</template>
<template v-else>
<span>
{{ data.location }}
</span>
</template>
<span>
<el-button
type="text"
icon="el-icon-edit"
@click="handleLocateClick(node, data)"
/>
<el-button
type="text"
icon="el-icon-circle-plus-outline"
@click="() => addLocation(node, data)"
/>
<el-button
icon="el-icon-remove-outline"
type="text"
@click="() => removeLocation(node, data)"
/>
</span>
</span>
</el-tree>
<el-row type="flex" justify="center" style="margin: 10px">
<el-button
type="success"
class="button-new-tag"
size="small"
round
@click="showLocateInput"
>+ 区域</el-button>
</el-row>
</div>
网页代码用 v-if = “node.editable” 来判断是否显示 input 输入框。
handleLocateClick(node, data) {
// console.log(data);
if (data.pid > 0 || data.pid === 0) {
if (!node.editable) {
this.$set(node, 'editable', true);
}
// 输入框聚焦
this.$nextTick(() => {
this.$refs.slotTreeInput.$refs.input.focus();
});
} else {
this.isAddArea = false;
this.$store.dispatch('admin/getAreaList').then((res) => {
console.log(res);
this.inputLocateVisible = true;
res.forEach((el) => {
if (el.name === data.location) {
this.areaForm.name = el.name;
this.areaForm.egname = el.egname;
this.areaForm.department_id = el.department_id;
this.areaForm.id = el.id;
}
});
});
}
},
逻辑代码用 this.$set(node, ‘editable’, true) 来给节点对象赋值,
这样就给节点对象添加了 editable 为 true 的属性,从而完成数据响应。
参考资料:Vue.js 中文文档