element ui 表格动态列,单行合并同时可行内编辑

效果如下:


image.png

image.png

版本信息如下:

"vue": "^2.5.2", "element-ui": "^2.7.2",

代码如下:

<!--表格行合并-->
<template>
  <div style="margin: 20px">
    <div style="margin: 20px 0;text-align: right;">
      <el-button type="primary" @click="logTableData">打印表格数据</el-button>
    </div>
    <el-table
      v-loading="tableLoading"
      :data="tableData"
      :span-method="spanMethod"
      :header-row-style="headerRowStyle"
      style="width: 100%">

      <el-table-column
        align="center"
        v-for="(col,colIndex) in tableHeader"
        :key="`col-${colIndex}`"
        :prop="col.prop"
        :min-width="col.width || 80"
        :label="col.label">
        <template slot-scope="{row, $index}">
          <el-input v-if="!col.noEdit && row._edit" class="inputNumber" :placeholder="`请输入${col.label}`"
                    v-model="row[col.prop]"/>
          <span v-else>{{ row[col.prop] }}</span>
        </template>
      </el-table-column>

      <el-table-column align="center" label="配比">
        <el-table-column label="" align="center" v-for="rateColIndex in maxTableColLength" :key="`rate-${rateColIndex}`"
                         :min-width="80">
          <template slot-scope="{row, $index}">


            <!--                第一行-->
            <template v-if="row.rowSpan">
              <span v-if="rateColIndex <= row.list.length">{{ row.list[rateColIndex - 1].name }}</span>
            </template>

            <!--                第二行,操作的也是上一行的数据-->
            <!--              有这项的才可编辑-->
            <template v-else-if="rateColIndex <= tableData[$index-1].list.length">

              <el-input class="inputNumber" type="number" v-if="tableData[$index-1]._edit" placeholder="请输入"
                        v-model="tableData[$index-1].list[rateColIndex-1].rate"/>
              <span v-else>{{ valFixed(tableData[$index-1].list[rateColIndex - 1].rate, 2) }}</span>

            </template>

          </template>
        </el-table-column>
      </el-table-column>

      <el-table-column
        fixed="right"
        align="center"
        label="操作"
        width="120">
        <template slot-scope="{row,$index}">
          <template v-if="!row._edit">
            <el-button @click="rowEdit(row, $index)" type="text">修改</el-button>
          </template>
          <template v-else>
            <el-button @click="rowSave(row, $index)" type="text">保存</el-button>
            <el-button @click="rowCancel(row, $index)" type="text">取消</el-button>
          </template>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script>
export default {
  data() {
    return {
      tableLoading: false,
      tableHeader: [
        {
          prop: 'pileNo',
          label: '堆号',
          noEdit: true
        },
        {
          prop: 'tfe',
          label: '品位'
        },
        {
          prop: 'ironWeight',
          label: '铁水产量'
        }
      ],
      tableData: [],
      historyRow: null, // 行编辑时存储历史数据
    }
  },
  computed: {
    // 表格中最大配比列长度
    maxTableColLength() {
      let arr = this.tableData.map(item => {
        return (item.list || []).length
      })
      return arr.length ? Math.max(...arr) : 0
    },
  },
  created() {
    this.initData()
  },
  methods: {
    // 判断值是否为空
    isNull(val) {
      return val === null || val === void 0 || val === '' || (val).toString() === 'NaN'
    },
    /**
     * 值保留小数处理
     * @val: 需要保留小数的值
     * @fixed:小数位数
     * @dt:为空时的默认值
     * @unit:单位
     * */
    valFixed(val, fixed = 3, dt = '', unit = '') {
      return this.isNull(val) ? dt : (parseFloat(val).toFixed(fixed) + unit)
    },
    initData() {
      // 模拟数据
      let data = [
        {
          pileNo: '11111111',
          tfe: 56.2,
          ironWeight: 5000,
          list: [
            {
              name: '精粉1',
              rate: 10,
            },
            {
              name: '精粉2',
              rate: 5,
            },
            {
              name: '混合粉2',
              rate: 8,
            },
            {
              name: '混合粉4',
              rate: 3,
            }
          ],
        },
        {
          pileNo: '22222222',
          tfe: 50.34,
          ironWeight: 5500,
          list: [
            {
              name: '精粉5',
              rate: 10,
            },
            {
              name: '混合粉1',
              rate: 8,
            },
            {
              name: '混合粉2',
              rate: 3,
            }
          ],
        },
        {
          pileNo: '33333333',
          tfe: 49,
          ironWeight: 4900,
          list: [
            {
              name: '精粉5',
              rate: 5,
            },
            {
              name: '混合粉1',
              rate: 10,
            },
            {
              name: '混合粉2',
              rate: 20,
            },
            {
              name: '混合粉3',
              rate: 20,
            }
          ],
        }
      ]

      let tableData = []
      data.map(item => {
        let list = item.list || []
        tableData = tableData.concat([
          {
            ...item,
            // 辅助变量
            rowSpan: 2
          },
          {}
        ])
      })
      this.tableData = tableData
    },

    // 表头隐藏
    headerRowStyle({row, rowIndex}) {
      if (rowIndex === 1) {
        return {'display': 'none'}
      }
    },
    // 表格行合并
    spanMethod({row, column, rowIndex, columnIndex}) {
      let startColIndex = this.tableHeader.length // 数的,第五列开始是配比列
      let endColIndex = startColIndex + (this.maxTableColLength - 1)
      let isRateCol = columnIndex >= startColIndex && columnIndex <= endColIndex // 配比列不进行行合并
      if (row.rowSpan) { // name行
        if (!isRateCol) {
          return [row.rowSpan, 1]
        } else {
          return [1, 1]
        }
      } else { // 配比行
        if (!isRateCol) {
          return [0, 1]
        } else {
          return [1, 1]
        }
      }

    },

    // 打印数据
    logTableData() {
      let data = this.tableData.filter(item => item.rowSpan)
      data.map((item) => {
        let str = ''
        this.tableHeader.map(sub => {
          str += `${sub.label}: ${item[sub.prop]} `
        })
        item.list.map(sub => {
          str += `${sub.name}: ${sub.rate} `
        })
        console.log(`%c${str}`, 'color: #43bb88;font-weight: bold;');
      })

      console.log('最终数据为:', data)
    },

    // 行编辑 --------------- start

    // 修改
    rowEdit(row, rowIndex) {
      // console.log(row, rowIndex)
      if (this.tableData.find(item => item._edit)) return this.$message({
        message: '请保存正在编辑的行',
        type: 'warning'
      });

      let editRow = row
      this.historyRow = JSON.parse(JSON.stringify(editRow))
      this.$set(editRow, '_edit', true)
    },
    // 保存正在编辑的
    rowSave(row, rowIndex) {
      // console.log(row, rowIndex)
      this.historyRow = {}
      row._edit = false
    },
    // 取消保存
    rowCancel(row, rowIndex) {
      // console.log(row, rowIndex)
      this.tableData.splice(rowIndex, 1, {
        ...this.historyRow,
        _edit: false
      })
      this.historyRow = {}
    }

    // 行编辑 --------------- end
  }
}
</script>

<style scoped lang="less">

.inputNumber {
  /deep/ input::-webkit-outer-spin-button,
  /deep/ input::-webkit-inner-spin-button {
    -webkit-appearance: none !important;
  }

  /deep/ input[type='number'] {
    -moz-appearance: textfield;
  }
}
</style>

大体思路

image.png

1.表头其实有两行,下面这行被隐藏了。
2.表格中每行数据是两行,表格行合并了,第二行是空行,编辑时操作的是第一行的数据。
3.这里使用行合并是为了让配比列表格宽度自适应。

若对你有帮助,请点个赞吧,谢谢支持!
本文地址:https://www.jianshu.com/p/0fdfbb68a73e?v=1668668903593,转载请注明出处,谢谢。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,009评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,808评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,891评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,283评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,285评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,409评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,809评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,487评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,680评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,499评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,548评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,268评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,815评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,872评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,102评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,683评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,253评论 2 341