vue+typescript element expandTable展开表格封装

燕经国
2023-12-01
<template>
  <div>
    <el-table
      ref="expandTable"
      class="expand-table"
      :data="tableList"
      :show-header="true"
      :size="size"
      @expand-change="open"
    >
      <!-- 表格展开 -->
      <el-table-column type="expand">
        <template slot-scope="props">
          <p class="el-title">{{ increment.title }}</p>
          <el-form label-position="left" inline class="demo-table-expand">
            <el-row>
              <!-- 全选按钮 -->
              <el-col :span="4">
                <div class="grid-content bg-purple">
                  <div class="col-grid">
                    <el-checkbox
                      v-for="(i, v) in props.row.selectAll"
                      :key="v"
                      v-model="i.checked"
                      class="col-checkbox"
                      :checked="i.checked"
                      :label="i.value"
                      @change="handleCkecked(props.row, i, v)"
                    />
                  </div>
                </div>
              </el-col>
              <!-- 单选按钮 -->
              <el-col :span="20">
                <div class="bg-purple-light">
                  <div class="grid-boxcheck">
                    <el-checkbox
                      v-for="(i, v) in props.row.childrenList"
                      v-show="i.type == '0'"
                      :key="v"
                      v-model="i.checked"
                      :label="i.name"
                      :checked="i.checked"
                      class="prop-checkbox"
                      @change="changeChecked(props.row, i, v)"
                    />
                  </div>
                  <div class="grid-boxcheck">
                    <el-checkbox
                      v-for="(i, v) in props.row.childrenList"
                      v-show="i.type == '1'"
                      :key="v"
                      v-model="i.checked"
                      :checked="i.checked"
                      :label="i.name"
                      class="row-checkbox"
                      @change="changeChecked(props.row, i, v)"
                    />
                  </div>
                </div>
              </el-col>
            </el-row>
          </el-form>
          <!-- 文字 -->
          <p class="el-bottom">
            <span class="sp-settlement">{{ increment.settlement }}</span>
            <span class="sp-receipt">{{ increment.receipt }}</span>
            <span class="sp-charge">{{ increment.charge }}</span>
            <span class="sp-money">¥{{ monery(props.row) }}</span>
          </p>
        </template>
      </el-table-column>``
      <!-- 表格数据栏 -->
      <el-table-column
        v-for="(item, index) in tableCols"
        :key="index"
        :render-header="item.column?renderHeader:null"
        :label="item.label"
        :prop="item.prop"
        :width="item.width"
      >
        <template slot-scope="scope">
          <!-- 文字按钮 -->
          <div v-if="item.type === 'Button'">
            <el-button
              v-for="(btn, i) in scope.row.btnList"
              :key="i"
              :type="btn.type"
              @click="changeEvent(scope.row, btn, i)"
            >
              {{ btn.label }}
            </el-button>
          </div>
          <!--输入框  -->
          <div v-if="item.type === 'Input'">
            <el-form :model="scope.row" :rules="item.rules">
              <el-form-item :prop="item.prop">
                <el-input
                  v-model="scope.row[item.prop]"
                  :placeholder="item.mold?'':'请输入' + `${item.label}`"
                  :class="item.mold?'number-input':''"
                  :type="item.mold"
                  @change="handleChange(scope.row)"
                />
              </el-form-item>
            </el-form>
          </div>
        </template>
      </el-table-column>
    </el-table>
    <div class="add-commodity">
      <i class="el-icon-circle-plus-outline" />
      <el-button type="text" class="btn-text" @click="addCommodity">
        {{ increment.addCommodity }}
      </el-button>
    </div>
  </div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator'
import _ from 'lodash'
@Component
export default class FormTable extends Vue {
  // 表格型号:big,small,mini
  @Prop({
    type: String,
    default: 'mini'
  })
  readonly size!: string
  @Prop({
    type: Object,
    default: () => null
  })
  readonly increment!: object
  // 表格数据
  @Prop({
    type: Array,
    default: () => []
  })
  readonly tableData!: any[]
  // 表格列配置
  @Prop({
    type: Array,
    default: () => []
  })
  readonly tableCols!: any[]
  // 表格数据
  public tableList: any = []
  // 添加商品对象
  public form: object = {}
  // 输入框change事件
  handleChange(row: any) {
    const formData = {
      tradeName: row.tradeName,
      skuModel: row.skuModel,
      mount: row.mount,
      codeModel: row.codeModel
    }
    this.$emit('handleChange', formData)
  }
  removeAaary(_arr: any, _obj: any) {
    const length = _arr.length
    for (let i = 0; i < length; i++) {
      if (_arr[i] === _obj) {
        if (i === 0) {
          _arr.shift() // 删除并返回数组的第一个元素
          return _arr
        } else if (i === length - 1) {
          _arr.pop() // 删除并返回数组的最后一个元素
          return _arr
        } else {
          _arr.splice(i, 1) // 删除下标为i的元素
          return _arr
        }
      }
    }
  }
  // 操作项 收起服务,删除
  changeEvent(row: any, item:any, btn: any, index: number) {
    const el:any = this.$refs.expandTable
    if (index === 0) {
    // eslint-disable-next-line eqeqeq
      if (row.open == true) {
        btn.label = '收起服务项'
      } else {
        btn.label = '展开服务项'
      }
      el.toggleRowExpansion(row)
    } else {
      this.$emit('removeAaary', row)
      this.tableList.splice(row, 1)
    }
  }
  // 表格收起,文字按钮是展开服务项
  loopTraversal() {
    this.tableList.forEach((v: any) => {
    // eslint-disable-next-line eqeqeq
      if (v.open == false) {
        v.btnList[0].label = '展开服务项'
      }
    })
  }
  // 添加商品
  addCommodity() {
    this.tableList.forEach((i: any) => {
      i.open = false
    })
    this.tableData.forEach((v: any) => {
      this.form = _.cloneDeep(v)
    })
    this.tableList.push(this.form)
    this.loopTraversal()
  }
  // 单选事件
  changeChecked(item: any, i: any, v: any) {
    const arr: any = item.childrenList.filter(
      (ever: any) => ever.type === i.type
    )
    if (i.type === '0') {
      item.selectAll[1].checked = arr.every((v: any) => v.checked)
    } else if (i.type === '1') {
      item.selectAll[2].checked = arr.every((v: any) => v.checked)
    }
    item.selectAll[0].checked = item.childrenList.every((v: any) => v.checked)
    const valarr: any = item.childrenList.filter((v: any) => v.checked)
    this.$emit('changeChecked', valarr)
  }
  // 全选事件
  handleCkecked(row: any, item: any, index: number) {
    switch (index) {
      case 0:
        row.childrenList.forEach((v: any) => {
          v.checked = item.checked
        })
        row.selectAll.forEach((v: any, index: number) => {
        // eslint-disable-next-line no-unused-expressions
          index !== 0 ? (v.checked = item.checked) : null
        })
        break
      case 1:
        row.childrenList.forEach((v: any) => {
        // eslint-disable-next-line no-unused-expressions
          v.type === '0' ? (v.checked = item.checked) : null
        })
        break
      case 2:
        row.childrenList.forEach((v: any) => {
        // eslint-disable-next-line no-unused-expressions
          v.type === '1' ? (v.checked = item.checked) : null
        })
        break
      default:
        break
    }
    const copyArr: any = [row.selectAll[1], row.selectAll[2]]
    row.selectAll[0].checked = copyArr.every(
      (v: any, index: number) => v.checked
    )
    const arr: any = row.childrenList.filter((v: any) => v.checked)
    this.$emit('handleCkecked', arr)
  }
  // 计算服务费
  monery(row: any) {
    const arr: any = row.childrenList.filter((v: any) => v.checked)
    let num = 0
    arr.forEach((v: any) => {
      num += v.price
    })
    return num
  }
  // 表格展开行
  open(row: any) {
    row.open = !row.open
    // eslint-disable-next-line eqeqeq
    if (row.open == true) {
      row.btnList[0].label = '收起服务项'
    } else {
      row.btnList[0].label = '展开服务项'
    }
  }
  // 表格表头增加红色*
  renderHeader(cerateElement:Function, { column }:any) {
    return cerateElement('div', [
      cerateElement('span', column.label),
      cerateElement('span', {
        domProps: {
          innerHTML: '*'
        },
        style: {
          color: 'red',
          fontSize: '16px',
          marginRight: '2px',
          marginTop: '1px',
          float: 'left'
        }
      })
    ])
  }
  created() {
    this.tableList = _.cloneDeep(this.tableData)
  }
}
</script>
<style lang="scss">
@import '@/components/formTable/index.scss';
</style>

.col-checkbox {
  margin-left: 10px;
  margin-top: 10px;
}
.grid-boxcheck {
  display: flex;
  flex-wrap:wrap;
  height: 100%;
  .row-checkbox {
    padding-bottom: 10px;
    width: 150px;
    margin-left: 10px;
  }
  .prop-checkbox {
    padding-top: 10px;
    margin-left: 10px;
    width: 150px;
  }
}
.col-grid:nth-child(3) {
  padding-bottom: 10px;
}
.bg-purple-dark {
  background: #99a9bf;
}
.bg-purple {
  position: absolute;
  height: 100%;
  width: 16%;
  background: #f3f3f7;
}
.bg-purple-light {
  background: #f8f9fb;
}
.demo-table-expand {
  font-size: 0;
}
.demo-table-expand label {
  width: 90px;
  color: #99a9bf;
}
.demo-table-expand .el-form-item {
  margin-right: 0;
  margin-bottom: 0;
  width: 50%;
}
.number-input {
  width: 50px !important;
}
//去掉input为number 箭头
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
  -webkit-appearance: none;
}
input[type='number'] {
  -moz-appearance: textfield;
}
.el-table__expanded-cell[class*=cell] {
    padding:10px 10px 20px 10px;
}
.el-col-20{
  margin-left: 16%;
}
.el-title{
  font-weight:600;
  margin-bottom: 10px;
}
.el-bottom{
  float:right;
  margin-top: 10px;
  margin-right: 20px;
  .sp-money{
    color:red;
    font-weight: 700;
    font-size: 18px;
    margin-left: 8px;
  }
  .sp-charge{
    margin-left: 8px;
  }
  .sp-receipt{
    margin-left: 5px;
  }
}
.add-commodity{
  text-align: center;
  height: 50px;
  margin-top:10px;
  border:1px dashed #D9D9D9;
  line-height: 50px;
  border-radius: 4px;
}
.btn-text{
  color: black;
}
.expand-table{
  width: 100%;
}
.el-form-item{
  margin-bottom: 16px;
  margin-top: 10px;
}
.el-icon-circle-plus-outline:hover{
  color: #3c6ef0;
}
.expand-table {
  /deep/ .el-icon-arrow-right::before {
    content: "\e590";
    color: #3c6ef0;
  }
}
<template>
  <div>
    <FormTable
      size="mini"
      :table-data="tableData"
      :increment="increment"
      :table-cols="tableCols"
      @handleChange="handleChange"
      @changeChecked="changeChecked"
      @handleCkecked="handleCkecked"
      @removeAaary="removeAaary"
    />
    <el-button type="primary" size="medium" @click="submit">提交</el-button>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import FormTable from '@/components/formTable/index.vue'
@Component({
  components: { FormTable }
})
export default class DemoformTable extends Vue {
  public increment: object = {
    title: '增值服务',
    settlement: '服务项默认结算方式:',
    receipt: '收件月结',
    charge: '增值服务费',
    addCommodity: '增加商品'
  }
  public tableData: any[] = [
    {
      skuModel: '',
      codeModel: '',
      mount: '',
      tradeName: '',
      open: false,
      childrenList: [
        { id: '1', name: '原包装丢失不取', type: '0', checked: false, price: 2 },
        { id: '2', name: '划痕超过3cm不取', type: '1', checked: false, price: 2 },
        { id: '3', name: '原包装破损不取', type: '0', checked: false, price: 2 },
        { id: '4', name: '商品已使用不取', type: '0', checked: false, price: 2 },
        { id: '5', name: '塑封包装不完整不取', type: '0', checked: false, price: 2 },
        { id: '6', name: '校验条码', type: '0', checked: false, price: 2 },
        { id: '7', name: '破损面积大于3cm不取', type: '1', checked: false, price: 2 },
        { id: '8', name: '无外包装不取', type: '0', checked: false, price: 2 },
        { id: '9', name: '破损面积大于3cm不取', type: '1', checked: false, price: 2 },
        { id: '10', name: '破损面积大于3cm不取', type: '1', checked: false, price: 2 },
        { id: '11', name: '测试项可配置', type: '0', checked: false, price: 2 },
        { id: '12', name: '无保修卡不取', type: '0', checked: false, price: 2 },
        { id: '13', name: '原包装丢失', type: '0', checked: false, price: 2 },
        { id: '14', name: '原包装丢失', type: '0', checked: false, price: 2 },
        { id: '15', name: '原包装丢失', type: '0', checked: false, price: 2 },
        { id: '16', name: '原包装丢失', type: '0', checked: false, price: 2 }
      ],
      selectAll: [
        { value: '全部全选', checked: false },
        { value: '检查附件全选', checked: false },
        { value: '检查外观全选', checked: false }
      ],
      btnList: [
        { type: 'text', label: '展开服务项' },
        { type: 'text', label: '删除' }
      ]
    }
  ]
  public tableCols: any[] = [
    { label: '商品名称',
      type: 'Input',
      prop: 'tradeName',
      column: true,
      rules: {
        tradeName: {
          required: true,
          trigger: 'blur',
          message: '请填写商品名称'
        }
      } },
    { label: '数量',
      type: 'Input',
      prop: 'mount',
      column: true,
      mold: 'number',
      rules: {
        mount: {
          required: true,
          trigger: 'blur',
          message: '请填写商品数量'
        }
      } },
    { label: 'SKU', type: 'Input', prop: 'skuModel' },
    { label: '商品条码', type: 'Input', prop: 'codeModel' },
    { label: '操作', type: 'Button' }
  ]
  public obj:any = {}
  public arr:any = []
  // input
  handleChange(obj: any) {
    this.obj = obj
  }
  // 单选
  changeChecked(radio: any) {
    this.arr = radio
    console.log(radio, '单选')
  }
  // 全选
  handleCkecked(all: any) {
    this.arr = all
    console.log(all, '全选')
  }
  // 删除
  removeAaary(item:any) {
    console.log(item, '删除')
  }
  submit() {
    console.log(this.obj, this.arr)
  }
}
</script>

 类似资料: