有一个简单的表格,产品要求实现双击可编辑
看了一下网上的帖子,大多数都是搞两部分dom,一块是输入框,用于编辑状态填写;另一块是普通标签,用于在不编辑显示状态下呈现单元格文字内容。再加上一个flag标识搭配v-if和v-else去控制编辑状态、还是显示状态。大致代码如下:
<el-table-column
align="center"
label="姓名"
>
<template slot-scope="scope">
<!--isClick就是标识状态,状态处于编辑时候,显示输入框,状态属于呈现状态就显示文本内容-->
<el-input v-if="scope.row.isClick" v-model="scope.row.name" @blur="blurFn(scope.row)"></el-input>
<span @click="clickCell(scope.row)" v-else>{{scope.row.name}}</span>
</template>
</el-table-column>
这种方式有其适用场景,但是得每个el-table-column列中都加上el-input和span以及v-if和v-else。我们尝试一下动态添加el-input,就是点击那个单元格,给那个单元格添加el-input让其处于可编辑状态,然后适时移除即可。这样的话,很多列的时候,就不用加很多个v-if和v-else啦。我们先看一下效果图
第2步:重点来喽
我们知道,如果是创建原生的input标签并指定一个值,比较简单,直接:
let input = document.createElement('input') // 创建一个input标签
input.value = '孙悟空' // 给input标签赋值
document.body.appendChild(input) // 把input标签追加到文档body中
不过el-input标签不能通过上述方式创建,因为document.createElement()方法虽然可以创建出来el-input标签,但是dom并不认识这个el-input标签,所以页面没有变化。毕竟饿了么的el-input也是把input标签做一个二次封装的
所以,这里我们可以使用vue.extend()方法去继承一个组件并暴露出去,而继承的这个组件中又有一个input标签,所以那个需要使用,那里就可以引入并new出来一个el-input了。关于Vue.extend()的定义啥的,这里不赘述,详情看官方文档。笔者之前也写过一篇Vue.extend文章,传送门:https://segmentfault.com/a/11...
// input.vue文件
<template>
<div class="cell">
<el-input
ref="elInputRef"
size="mini"
v-model.trim="cellValue"
></el-input>
</div>
</template>
props: {
cellValue: {
type: String | Number,
default: "",
},
}
// data.js
import Vue from "vue";
import definedInput from "./input.vue";
// vue继承这个input组件,就相当于一个构造函数了
const inputC = Vue.extend(definedInput);
// 暴露出去,哪里需要哪里引入
export default {
inputC,
}
// page.vue
import extendComponents from "./threeC/data"; // 1. 引入
new extendComponents.inputC({ // 2. 实例化
propsData: {
// 使用propsData对象传递参数,子组件在props中可以接收到
cellValue: cellValue, // 传递单元格的值
},
}).$mount(cell.children[0]);// 3. 挂载
propsData对象用于给继承的组件传递参数,也可以传递一个函数,从而继承组件通过这个函数通知外部使用组件,详情见后续完整代码
使用$mount方法去做来回替换,$mount可以把一个子dom元素追加到父dom元素内部,相当于appendChild
然后这里需要有一个替换的时机,就是实例化的组件中的el-input失去焦点的时候,去通知外部使用的组件,所以可以在外部使用是,在propsData中传递一个函数到继承的组件,如:
// 外部组件传递
new extendComponents.inputC({
propsData: {
cellValue: cellValue, // 传递单元格的值
saveRowData: this.saveRowData, // 传递回调函数用于通知,继承组件中可以触发之
},
}).$mount(cell.children[0]);
saveRowData(params){
console.log('收到继承组件消息通知啦参数为:',params)
}
// 内部组件失去焦点时候通知
<el-input
ref="elInputRef"
size="mini"
v-model.trim="cellValue"
@blur="blurFn"
></el-input>
props: {
cellValue: {
type: String | Number,
default: "",
},
saveRowData: Function, // 外部,传递进来一个函数,当这个el-input失去焦点的时候,通过此函数通知外部
}
blurFn() {
// 失去焦点,再抛出去,通知外部
this.saveRowData({
cellValue: this.cellValue,
// 其他参数
});
},
所以当内层失去焦点的时候,就可以通知外层去做一个替换了,就是把单元格dom重新做一个$mount挂载,就把el-input替换成了span了,为了进一步理解,这里的span我们也可以使用继承的方式,是new实例化使用,详情见下方完整代码
threeC
-- data.js
-- input.vue
-- span.vue
three.vue
input.vue
<template>
<div class="cell">
<el-input
ref="elInputRef"
size="mini"
v-model.trim="cellValue"
@blur="blurFn"
></el-input>
</div>
</template>
<script>
export default {
props: {
cellValue: {
type: String | Number,
default: "",
},
saveRowData: Function, // 外部,传递进来一个函数,当这个el-input失去焦点的时候,通过此函数通知外部
cellDom: Node, // 单元格dom
row: Object, // 单元格所在行数据
property: String, // 单元格的key
},
mounted() {
// 用户双击后,让其处于获取焦点的状态
this.$refs.elInputRef.focus();
},
methods: {
blurFn() {
// 失去焦点,再抛出去,通知外部
this.saveRowData({
cellValue: this.cellValue,
cellDom: this.cellDom,
row: this.row,
property: this.property,
});
},
},
};
</script>
<style>
.cell {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
padding: 0 8px;
}
</style>
span.vue
<template>
<span class="cell">{{ cellValue }}</span>
</template>
<script>
export default {
props: {
cellValue: {
type: String | Number,
default: "",
},
},
};
</script>
import Vue from "vue";
import definedInput from "./input.vue";
import definedSpan from "./span.vue";
const inputC = Vue.extend(definedInput);
const spanC = Vue.extend(definedSpan);
export default {
inputC,
spanC,
}
<template>
<div id="app">
<el-table
@cell-dblclick="dblclick"
:cell-class-name="cellClassName"
height="480"
:data="tableData"
border
>
<el-table-column align="center" type="index" label="序号" width="50">
</el-table-column>
<el-table-column align="center" prop="name" label="姓名" width="100">
</el-table-column>
<el-table-column align="center" prop="age" label="年龄" width="100">
</el-table-column>
<el-table-column align="center" prop="home" label="家乡">
</el-table-column>
</el-table>
</div>
</template>
<script>
// 引入继承组件对象,可取其身上的inputC构造函数、或spanC构造函数生成组件dom
import extendComponents from "./threeC/data";
export default {
data() {
return {
tableData: [
{
name: "孙悟空",
age: 500,
home: "花果山水帘洞",
},
{
name: "猪八戒",
age: 88,
home: "高老庄",
},
{
name: "沙和尚",
age: 1000,
home: "通天河",
},
],
/**
* 存一份旧的值,用于校验是否发生变化,是否修改
* */
oldCellValue: null,
};
},
methods: {
cellClassName({ row, column, rowIndex, columnIndex }) {
row.index = rowIndex; // 自定义指定一个索引,下方能够用到
},
dblclick(row, column, cell, event) {
// 1. 序号列单元格不允许编辑,别的列单元格可以编辑
if (column.label == "序号") {
this.$message({
type: "warning",
message: "序号列不允许编辑",
});
return;
}
// 2. 存一份旧的单元格的值
this.oldCellValue = row[column.property];
// 3. 然后把单元格的值,作为参数传递给实例化的input组件
let cellValue = row[column.property];
// 4. 实例化组件以后,带着参数,再挂载到对应位置
new extendComponents.inputC({
propsData: {
// 使用propsData对象传递参数,子组件在props中可以接收到
cellValue: cellValue, // 传递单元格的值
saveRowData: this.saveRowData, // 传递回调函数用于保存行数据,组件中可以触发之
cellDom: cell, // 传递这个dom元素
row: row, // 传递双击的行的数据
property: column.property, // 传递双击的是哪个字段
},
}).$mount(cell.children[0]); // 5. $mount方法,用于将某个dom挂载到某个dom上
},
/**
* 失去焦点的时候有以下操作
* 1. 校验新值是否等于原有值,若等于,说明用户未修改,就不发请求。若不等于就发请求,然后更新tableData数据
* 2. 然后使用$mount方法,挂载一个新的span标签dom在页面上,即恢复原样,而span标签也是实例化的哦
* */
saveRowData(params) {
console.log("继承的子组件传递过来的数据", params);
// 1. 看看用户是否修改了
if (params.cellValue == this.oldCellValue) {
console.log("未修改数据,不用发请求");
} else {
params.row[params.property] = params.cellValue;
// 这里模拟一下发了请求,得到最新表体数据以后,更新tableData
setTimeout(() => {
// 给那个数组的 第几项 修改为什么值
this.$set(this.tableData, params.row.index, params.row);
}, 300);
}
// 2. 恢复dom节点成为原来的样子,有下面两种方式
/**
* 方式一:使用官方推荐的$mount去挂载到某个节点上,上方也是
* */
new extendComponents.spanC({
propsData: {
cellValue: params.cellValue,
},
}).$mount(params.cellDom.children[0]);
/**
* 方式二:使用原生js去清空原节点内容,同时再添加子元素
* */
// let span = document.createElement("span"); // 创建一个span标签
// span.innerhtml = params.cellValue; // 指定span标签的内容的值
// span.classList.add("cell"); // 给span标签添加class为cell
// params.cellDom.innerHTML = ""; // 清空刚操作的input标签的内容
// params.cellDom.appendChild(span); // 再把span标签给追加上去,恢复原样
},
},
};
</script>
<style lang="less" scoped>
#app {
width: 100%;
height: 100vh;
box-sizing: border-box;
padding: 50px;
}
</style>
使用Vue.extend()方法,可以继承一些组件,甚至继承一些复杂的组件,在实际业务场景中会有巧妙的使用。具体业务场景具体分析。
此外,上述代码中是el-input的继承,其实,我们也可以做el-select的继承,思路和上方类似,这样就可以在表格中双击单元格,选择并更改对应的下拉框更改el-table的单元值了,比如如果有性别这一列,那是下拉框的形式的。道友们可以按照这个思路发散哦...
好记性不如烂笔头,记录一下吧 ^_^
来源:https://segmentfault.com/a/1190000041974425
Vue.js 是一个目前比较流行的前端框架,在业界也算很有名气,今天这里为大家罗列一下基于Vue的后端管理的框架。使用这些框架你会发现它包括了我们常用的路由,状态,交互等等,这篇文章主要介绍element和Vue Admin。
除了Document类型,我们Web编程中最常用的类型就是Element类型啦.Element 类型用于表现XML或HTML元素,提供了对元素标签名,子节点,特性的访问
在选择element-ui组件做开发的时候,发现直接使用@keyup.enter的形式监听不到回车事件,原因是element-ui自身封装了一层input标签,把原有的事件隐藏了。解决方法是:在enter后面加上.native
一般在后端管理项目中,都会涉及菜单管理。菜单都是树形结构的数据,我们在使用ElementUI的时候,发现并没提供相应的树形表格组件,所以需要自己来实现。下面就简单讲解下element-ui中table树形表格的实现代码
做管理平台的项目,用到了element-ui,需要通过监听el-table滚动的位置来获取最新的数据,那么怎么样监听el-table的滚动呢?我们默认的技术栈是 vue+element-ui
通过tree树形控件的default-checked-keys属性来设置默认选中的节点,Tree树形控件通过后台接口获取到数组数据,还需要再次遍历,将它再遍历为数组,这样我们才可以调用
使用ElementUi搭建框架的时候,大家应该都有考虑过怎么做全局验证,毕竟复制粘贴什么的是最烦了,一般验证规则,主要是是否必填,不为空,以及参数类型的验证。
使用element-ui中的Notification,只有一个message属性是有很大的操作空间,其余的都是写死的,无法进行扩展,达不到想要的效果。所以只能在message上下功夫。在element-ui官方文档中可以看到Notification中的message属性
Element UI的Message消息提示是点击一次触发一次的。在开发的时候经常会作为一些校验提示,但是公司的测试人员在进行测试时会一直点,然后就会出现如下图的情况。虽然客户使用的时候一般来说不会出现这种情况
如果程序报错Duplicate keys detected: tab-xxx. This may cause an update error.八成是key重复了,首先检查一下v-for循环的key是否有问题;在<el-tab-pane>尽量不使用v-show控制标签的显示
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!