<div id="app">
<el-table :data="tableData" :span-method="tableSpanMethod" row-key="id">
<el-table-column align="center" prop="product" label="产品" width="320"></el-table-column>
<el-table-column align="center" prop="progress" label="积分加权(进度)" width="150">
<template slot-scope="scope">
<editable-cell :text="scope.row.progress" @change-value="onCellChange(arguments, scope)"></editable-cell>
</template>
</el-table-column>
<el-table-column align="center" prop="quality" label="积分加权(质量)" width="150">
<template slot-scope="scope">
<editable-cell :text="scope.row.quality" @change-value="onCellChange(arguments, scope)"></editable-cell>
</template>
</el-table-column>
<el-table-column align="center" prop="scoreTotal" label="综合积分累计" width="120"></el-table-column>
<el-table-column align="center" prop="handler" label="处理人"></el-table-column>
<el-table-column align="center" prop="summary" label="汇总"></el-table-column>
<el-table-column align="center" prop="workingTime" label="考勤累计工时" width="150"></el-table-column>
<el-table-column align="center" prop="floatingIntegral" label="浮动积分" width="150">
<template slot-scope="scope">
<editable-cell :text="scope.row.floatingIntegral" @change-value="onCellChange(arguments, scope)"></editable-cell>
</template>
</el-table-column>
<el-table-column align="center" prop="reason" label="浮动原因" width="150" class-name="textareaColumn">
<template slot-scope="scope">
<editable-cell :text="scope.row.reason" input-type="textarea" @change-value="onCellChange(arguments, scope)"></editable-cell>
</template>
</el-table-column>
<el-table-column align="center" prop="finalScore" label="最终得分" width="150"></el-table-column>
<el-table-column align="center" prop="finalPerformance" label="最终绩效" width="150" class-name="textareaColumn">
<template slot-scope="scope">
<editable-cell :text="scope.row.finalPerformance" input-type="textarea" @change-value="onCellChange(arguments, scope)"></editable-cell>
</template>
</el-table-column>
</el-table>
<script>
export default{
components: {
EditableCell: () => import('@/components/EditableCell/EditableCell.vue'),
data() {
return {
tableData:[] // 自行填充数据
methods: {
* 如果单元格存在跨行,并且想要获取当前单元格的跨行跨列的值
* @param {Object} val
* @param {Object} row
* @param {Object} column
* @param {Object} $index
onCellChange(val, { row, column, $index }) {
let tr = document.querySelectorAll('.el-table .el-table__body-wrapper tbody tr');
let td = tr[$index].getElementsByClassName(column.id)[0];
let rowspan = td.getAttribute('rowspan');
for(let i = 0; i < rowspan; i++){
this.tableData[$index + i][column.property] = val[0];
val[1](); // 调用关闭输入框的回调方法
* 合并行或列的计算方法
tableSpanMethod({row, column, rowIndex, columnIndex}){
return {
rowspan: columnIndex >= 4 ? this.mergeRows(row[column.property], this.tableData, rowIndex, column.property) : 1,
colspan: 1
* 表格单元格合并-----行
* @param {Object} value 当前单元格的值
* @param {Object} data 当前表格所有数据
* @param {Object} index 当前单元格的值所在 行 索引
* @param {Object} property 当前列的property
* @returns {number} 待合并单元格数量
mergeRows(value, data, index, property) {
// 判断 当前行的该列数据 与 上一行的该列数据 是否相等
if (index !== 0 && value === data[index - 1][property] && data[index].name === data[index - 1].name) {
// 返回 0 使表格被跨 行 的那个单元格不会渲染
return 0;
// 判断 当前行的该列数据 与 下一行的该列数据 是否相等
let rowSpan = 1;
for (let i = index + 1; i < data.length; i++) {
if (value == data[i][property] && data[i].name == data[i - 1].name){
rowSpan++;
}else{
break;
return rowSpan;
</script>
<template>
<div class="editable-cell">
<div v-if="editable" class="editable-cell-input-wrapper clearfix">
<el-input v-if="inputType == 'input'" class="editable-input" ref="editableInput" v-model="value" size="mini" @input="handleInput" @keyup.enter.native="check"></el-input>
<el-input v-if="inputType == 'textarea'" type="textarea" autosize ref="editableTextarea" v-model="value" @input="handleInput" @keyup.enter.native="check"></el-input>
<i class="editable-cell-icon-check el-icon-check" @click="check"></i>
<div v-else class="editable-cell-text-wrapper">
{{ value || ' ' }}
<i class="editable-cell-icon el-icon-edit" @click="edit"></i>
</template>
<script>
export default{
props: {
text: [String, Number],
inputType: {
type: String,
default: () => 'input'
watch: {
text(newValue, oldValue){
this.value = newValue;
data() {
return {
value: this.text,
editable: false,
methods: {
handleInput(e) {
const value = e;
this.value = value;
check() {
this.$emit('changeValue', this.value, () => {
this.editable = false;
edit() {
this.editable = true;
this.$nextTick(() => {
this.$refs.editableInput?.focus();
this.$refs.editableTextarea?.focus();
</script>
<style>
.cl:after,.clearfix:after{ content: "."; display: block; height: 0; clear: both; visibility: hidden; }.cl,.clearfix{ zoom: 1; }
.editable-cell {
position: relative;
.editable-cell-input-wrapper,.editable-cell-text-wrapper {
padding-right: 24px;
height: 28px;
line-height: 28px;
.editable-cell-text-wrapper {
/* padding: 5px 24px 5px 5px; */
.editable-cell-input-wrapper .editable-input{
float: left;
.editable-cell-input-wrapper .el-textarea{
height: 100%;
.editable-cell-input-wrapper .el-textarea textarea{
height: 100%;
overflow: hidden;
.editable-cell-icon,.editable-cell-icon-check {
position: absolute;
top:0;
bottom:0;
right: 0;
margin:auto;
cursor: pointer;
.editable-cell-icon {
width:16px;
height:16px;
line-height: 16px;
text-align: center;
display: none;
.editable-cell-icon-check {
width:16px;
height:16px;
line-height: 16px;
text-align: center;
.editable-cell:hover .editable-cell-icon {
display: inline-block;
.editable-cell-icon:hover,.editable-cell-icon-check:hover {
color: #108ee9;
td.textareaColumn *:not(i){
height: 100%;