element ui 表格与锚点定位

发布时间 2023-12-22 10:46:31作者: &蝶儿&

父组件表格页面代码:

<template>
  <div class="wrapper">
    <div class="overview-box">
      <div class="box-name">
        <div class="flag" />
        标的事件
      </div>
      <p class="con-txt">
        {{ similarEventDate
        }}{{ queryInfo.symbol }}{{ queryInfo.shortname
        }}{{ queryInfo.eventtypename }}{{ pilesNum }}万股
      </p>
    </div>
    <div class="box-name">
      <div class="flag" />
      事件相似度
    </div>
    <Table
      :heads="columns"
      :data="tableData"
      :maxheight="500"
      :haspage="false"
      class="table-item"
      @sortChange="sortChange"
      @goToSimilarDetail="goToSimilarDetail"
    />
  </div>
</template>
<script>
import Table from '@/components/TableDynamic/index.vue'
import {
    querySimilarEventData
} from '@/api/queryEvent'
import { mapState } from 'vuex'
export default {
    name: 'EventDeatil',
    components: {
        Table
    },
    data() {
        return {
            queryInfo: {},
            code: '', // 代码
            eventType: '', // 事件类型
            eventDate: '', // 事件时间
            similarEventDate: '', // 事件时间
            pilesNum: '', // 股数
            detailInfo: {}, // 明细概述信息
            columns: [
                {
                    label: '事件名称',
                    width: 160,
                    sortable: true,
                    prop: 'eventname'
                },
                {
                    label: '代码',
                    width: 93,
                    sortable: true,
                    prop: 'symbol'
                },
                {
                    label: '事后1日涨跌幅',
                    type: 'percent',
                    sortable: true,
                    prop: 'onedayratio'
                },
                {
                    label: '事后3日涨跌幅',
                    sortable: true,
                    type: 'percent',
                    prop: 'threedayratio'
                },
                {
                    label: '事后5日涨跌幅',
                    sortable: true,
                    type: 'percent',
                    prop: 'fivedayratio'
                },
                {
                    label: '事件偏向',
                    sortable: true,
                    prop: 'eventbias',
                    type: 'deviation'
                },
                {
                    label: '相似度',
                    sortable: true,
                    prop: 'similarvalue'
                }
            ],
            tableData: [],
            sortBackupData: []
        }
    },
    computed: {
        ...mapState({
            eventDisplayInfo: (state) => state.event.eventDisplayInfo
        })
    },
    watch: {
        eventDisplayInfo: {
            handler(newObj) {
                this.code = newObj.symbol || ''
                this.eventType = newObj.eventtype || ''
                this.eventDate = newObj.eventdate || ''
                this.queryInfo = newObj || {}
                this.getSimilarEventData()
            },
            deep: true
        }
    },
    created() {},
    mounted() {
        this.code = this.$route.query.symbol || ''
        this.eventType = this.$route.query.eventtype || ''
        this.eventDate = this.$route.query.eventdate || ''
        this.queryInfo = this.$route.query || {}
        this.similarEventDate = this.getDate(this.$route.query.eventdate.split(' ')[0])
        this.getSimilarEventData()
    },
    beforeDestroy() {
        window.removeEventListener('resize', this.handleResize, false)
    },
    methods: {
        // 获取相似事件表格数据
        getSimilarEventData() {
            const date = this.eventDate.split(' ')[0]
            if (!date) {
                this.tableData = []
                return
            }
            const params = {
                symbol: this.code,
                eventdate: date,
                size: 10
            }
            querySimilarEventData(params).then((res) => {
                const data = res.similarItems || []
                const arr = []
                this.pilesNum = res.Listedshares
                data.forEach(el => {
                    el.similarvalue = this.toDecimal(this.numMul(el.similarvalue, 100)) + '%'
                    arr.push(el)
                })
                this.tableData = arr
                Object.assign(this, {
                    tableData: this.tableData,
                    sortBackupData: this.tableData
                })
                sessionStorage.setItem('tableData', JSON.stringify(this.tableData))
            })
        },
        // 格式化日期
        getDate(date) {
            const date1 = new Date(date)
            // 获取年份、月份和日期
            const year = date1.getFullYear()
            const month = date1.getMonth() + 1 // 注意月份从0开始计数,所以需要加上1得到真正的月份(12)
            const day = date1.getDate()
            return `${year}年${month}月${day}日`
        },
        // 排序
        sortChange({ column, prop, order }) {
            if (column.sortable === 'custom') {
                if (order) {
                    const { tableData } = this
                    this.tableData = this.customSortBuyProp(order, tableData, prop)
                } else {
                    this.tableData = this.sortBackupData
                }
            }
        },
        /**
         * 根据选定prop排序(无效值排最后)
         * @param {String} order 排序顺序ascending/descending
         * @param {Array} data 数据
         * @param {String} prop 排序属性
         * @param {Array} invalidList 无效值定义
         */
        customSortBuyProp(order, data, prop, invalidList) {
            if (!order) {
                return data
            }
            invalidList = invalidList || [null, undefined, '', '/']
            const isAsc = order === 'ascending'
            const effectiveArr = [] // 参与排序的数据
            const invalidArr = [] // 不参与排序的数据
            data.forEach((item) => {
                const value = item[prop]
                if (invalidList.includes(value)) {
                    invalidArr.push(item)
                } else {
                    effectiveArr.unshift(item)
                }
            })
            effectiveArr.sort((a, b) => {
                const aValue = a[prop]
                const bValue = b[prop]
                if (aValue > bValue) {
                    return isAsc ? 1 : -1
                } else if (aValue < bValue) {
                    return isAsc ? -1 : 1
                } else {
                    return 0
                }
            })
            return effectiveArr.concat(invalidArr)
        },
        // 跳转相似度详情页面
        goToSimilarDetail(row) {
            let arr = []
            arr = [
                { event_date: this.eventDate.substring(0, 10),
                    event_type: Number(this.eventType),
                    shortname: this.queryInfo.shortname,
                    symbol: this.code },
                { event_date: row.eventdate.substring(0, 10),
                    event_type: row.eventtype,
                    shortname: row.shortname,
                    symbol: row.symbol }
            ]
            sessionStorage.setItem('similarParams', JSON.stringify(arr))
            const routeUrl = this.$router.resolve({
                path: '/similarDetail/index',
                query: row
            })
            window.open(routeUrl.href, '_blank')
        }
    }
}
</script>
<style lang="scss">
.baseDataPopper {
  .content-box {
    max-width: 500px;
    max-height: 400px;
    overflow-y: auto;
    white-space: pre-line;
  }
}
</style>
<style lang="scss" scoped>
.wrapper {
  padding: 0 16px 20px;
  background: #ffffff;
  box-sizing: border-box;
  -webkit-box-sizing: border-box;
  .table-item {
    padding: 0;
    /deep/.table-box {
      padding: 0;
    }
  }
  .overview-box {
    margin-bottom: 16px;
    border: 1px solid #e6e6e6;
    border-top: none;
    .con-txt {
      padding: 0px 17px 10px;
      margin: 0;
      line-height: 24px;
      font-size: 14px;
      color: #666666;
      .spec {
        font-weight: bold;
        color: #f33030;
        cursor: pointer;
        border-bottom: 1px dashed #f33030;
        &:hover {
          color: #1e81ff;
          border-bottom: 1px dashed #1e81ff;
        }
      }
    }
  }
  .box-name {
    padding: 0 16px;
    display: flex;
    align-items: center;
    height: 44px;
    font-size: 14px;
    font-weight: bold;
    color: #333333;
    box-sizing: border-box;
    -webkit-box-sizing: border-box;
    .flag {
      margin-right: 8px;
      width: 5px;
      height: 14px;
      background: #1e81ff;
      border-radius: 0px 2px 2px 0px;
    }
  }
  .item-title {
    margin: 26px 0 16px;
    padding-left: 14px;
    height: 28px;
    line-height: 28px;
    font-size: 14px;
    font-weight: 600;
    color: #333333;
    background: url("~@/assets/images/title_bg.png") no-repeat;
    background-size: 130px 100%;
    position: relative;
    &::before {
      position: absolute;
      content: "";
      top: 5px;
      left: 0;
      width: 3px;
      height: 18px;
      background: #1e81ff;
    }
  }
  .pagin-box {
    margin-top: 20px;
    padding-top: 20px;
    text-align: center;
    position: relative;
    &::before {
      position: absolute;
      content: "";
      top: 0;
      left: -16px;
      right: -16px;
      height: 1px;
      background: #eeeff1;
    }
  }
  /deep/.pub-pagin-box {
    .el-pagination.is-background .btn-next,
    .el-pagination.is-background .btn-prev,
    .el-pagination.is-background .el-pager li {
      font-size: 12px;
      font-weight: normal;
      color: #666666;
      background: #f6f6f6;
      border-radius: 3px;
    }
    .el-pagination.is-background .el-pager li:not(.disabled).active {
      color: #ffffff;
      background-color: #1e81ff;
    }
    .el-pagination.is-background .btn-next.disabled,
    .el-pagination.is-background .btn-next:disabled,
    .el-pagination.is-background .btn-prev.disabled,
    .el-pagination.is-background .btn-prev:disabled,
    .el-pagination.is-background .el-pager li.disabled {
      color: #c0c4cc;
    }
    .el-pagination__jump,
    .el-input__inner {
      font-size: 12px;
      color: #666666;
    }
  }
  .chart-area {
    margin-top: 26px;
    padding: 10px 0;
    width: 100%;
    height: 380px;
    background: #ffffff;
    border: 1px solid #e6e6e6;
    box-sizing: border-box;
    -webkit-box-sizing: border-box;
  }
  /deep/.detail-dialog {
    .el-dialog__header {
      padding-top: 15px;
      padding-bottom: 15px;
      border-bottom: 1px solid #f3f3f3;
    }
    .title-box {
      font-size: 16px;
      position: relative;
      .iconfont {
        position: absolute;
        right: 0;
        cursor: pointer;
      }
    }
    .el-dialog__body {
      max-height: 350px;
      min-height: 200px;
      overflow-y: auto;
    }
    .content-box {
      display: flex;
      flex-wrap: wrap;
      .item {
        margin-bottom: 20px;
        display: flex;
        flex-shrink: 0;
        width: 50%;
        padding-right: 10px;
        box-sizing: border-box;
        &:nth-of-type(2n) {
          padding-right: 0;
          padding-left: 10px;
        }
        .name {
          width: 120px;
          font-weight: bold;
        }
        .con {
          margin-left: 10px;
          flex: 1;
        }
      }
    }
  }
}
</style>
table组件代码:
<template>
  <!-- table-wrapper 具体页面可能也用了该类名控制表格特殊样式 -->
  <div :class="'table-wrapper ' + headbg">
    <div class="table-box">
      <el-table
        ref="table"
        :data="data"
        :height="height === 0 ? undefined : height"
        :max-height="maxheight === 0 ? undefined : maxheight"
        :highlight-current-row="highlightCurrentRow"
        :empty-text="emptyText"
        :span-method="spanMethod"
        :size="size"
        :row-class-name="rowclassname"
        :tree-props="treeProps"
        :row-key="rowKey"
        :row-style="size == 'small' ? { height: '30px' } : {}"
        :cell-style="size == 'small' ? { padding: '0px' } : {}"
        :stripe="stripe"
        style="width: 100%"
        tooltip-effect="dark"
        border
        @cell-dblclick="cellDblclick"
        @row-click="rowClick"
        @current-change="handleRowChange"
        @selection-change="handleSelectionChange"
        @sort-change="sortChange"
      >
        <el-table-column
          v-if="hasselection"
          :reserve-selection="reserveSelection"
          type="selection"
          header-align="center"
          align="center"
          width="40"
        />
        <el-table-column
          v-if="hasindex && data.length > 0"
          :index="indexMethod"
          label="序号"
          type="index"
          header-align="center"
          align="center"
          class-name="rw-table-index"
          width="80"
        />
        <el-table-column
          v-for="h in heads"
          :key="'col' + h.prop"
          :prop="h.prop"
          :fixed="h.hasFixed"
          :align="h.align ? h.align : 'center'"
          :header-align="h.hAlign ? h.hAlign : 'center'"
          :min-width="h.minWidth ? h.minWidth : '128'"
          :width="h.width ? h.width: 'auto'"
          :sortable="h.sortable ? 'custom' : false"
          :label-class-name="h.hasWrap ? 'hasWrap' : ''"
        >
          <template #header>
            <el-tooltip
              :content="h.label"
              overflow
              effect="dark"
              placement="top-start"
            >
              <div class="dialog-box-title">
                {{ h.label }}
              </div>
            </el-tooltip>
            <div v-if="h.label === '相似度'">
              <span
                style="margin-right:8px;"
              />
              <el-popover
                placement="top-start"
                width="250"
                trigger="hover"
                content="针对付费订单,使用截止时间来源于合同,可能和最终实施的使用截止日期存在差异。"
              >
                <i
                  slot="reference"
                  class="tips-icon el-icon-question"
                />
              </el-popover>
            </div>
          </template>
          <template slot-scope="scope">
            <div
              v-if="h.type === 'starIcon'"
              class="star-icon-box">
              <i
                v-for="(item, i) in 5"
                :key="'icon' + i"
                :style="{
                  color: scope.row[h.prop] > i ? '#F7BA2A' : '#DAE0E7',
                }"
                :class="
                  scope.row[h.prop] > i
                    ? 'iconxingxing-tianchong'
                    : 'iconxingxing'
                "
                class="iconfont star-icon"
              />
            </div>
            <template v-else-if="h.type === 'largeText'">
              <el-tooltip
                popper-class="baseDataPopper"
                effect="dark"
                placement="bottom-start">
                <div :style="scope.row[h.prop + 'Style']">{{ scope.row[h.prop] }}</div>
                <template slot="content">
                  <div class="content-box">{{ scope.row[h.prop] }}</div>
                </template>
              </el-tooltip>
            </template>
            <template v-else-if="h.prop === 'similarvalue'">
              <div
                class="similarText"
                @click="goToSimilarDetail(scope.row)">{{ scope.row[h.prop] }}</div>
            </template>
            <template v-else-if="h.type === 'date'">
              <div >{{ formatDate(new Date(scope.row[h.prop])) }}</div>
            </template>
            <div
              v-else
              :class="h.type == 'price' || h.type == 'percent'
                ? (scope.row[h.prop] == 0 || scope.row[h.prop] == null)
                  ? 'zero'
                  : scope.row[h.prop] > 0
                    ? 'up'
                    : 'down'
                : ''
              "
              :style="scope.row[h.prop + 'Style']"
            >
              {{
                scope.row[h.prop] === "" ? '' : (h.type == "price"
                  ? toDecimal(scope.row[h.prop], 4)
                  : h.type == "percent"
                    ? ( scope.row[h.prop] ? toDecimal(numMul(scope.row[h.prop], 100)) + '%' : '-')
                    : h.type == "date"
                      ? scope.row[h.prop].split(' ')[0].replace(/\-/g, "")
                      : h.type == "deviation"? eventBiasClick(scope.row[h.prop]): scope.row[h.prop])
              }}
            </div>
          </template>
        </el-table-column>

        <slot />
        <div slot="empty">暂无数据</div>
      </el-table>
    </div>
    <div
      v-if="haspage"
      class="table-pagination">
      <el-pagination
        :total="total"
        :current-page="pageindex"
        :page-size="pagesize"
        background
        layout="prev, pager, next, jumper"
        @current-change="pageChange"
        @size-change="pageSizeChange"
      />
    </div>
  </div>
</template>

<script>
import { eventBiasHandle } from '@/utils/business'
import common from '@/utils/common'
export default {
    name: 'CsTable',
    props: {
        heads: {
            type: Array,
            required: true
        },
        // 表格的数据
        data: {
            type: Array,
            required: true
        },
        stripe: {
            type: Boolean,
            default: true
        },
        rowKey: {
            type: String,
            default: ''
        },
        emptydata: {
            type: String,
            default: ''
        },
        height: {
            type: [Number, String],
            default: 0
        },
        maxheight: {
            type: [Number, String],
            default: 0
        },
        total: {
            type: Number,
            default: 0
        },
        url: {
            type: String,
            default: ''
        },
        pagesize: {
            type: Number,
            default: 0
        },
        pageindex: {
            type: Number,
            default: 1
        },
        hasindex: {
            type: Boolean,
            default: false
        },
        hasselection: {
            type: Boolean,
            default: false
        },
        reserveSelection: {
            type: Boolean,
            default: false
        },
        haspage: {
            type: Boolean,
            default: true
        },
        highlightCurrentRow: {
            // 是否有点击选中效果
            type: Boolean,
            default: false
        },
        size: {
            type: String,
            default: 'medium'
        },
        headbg: {
            type: String,
            default: ''
        },
        spanMethod: {
            type: Function,
            default: function({ row, column, rowIndex, columnIndex }) {}
        },
        // eslint-disable-next-line vue/require-default-prop
        rowclassname: [String, Function],
        treeProps: {
            type: Object,
            default: () => {
                return { children: 'children', hasChildren: 'hasChildren' }
            }
        }
    },
    data() {
        return {
            emptyText: '暂无数据'
        }
    },
    mounted() {
        if (this.emptydata !== '') {
            this.emptyText = this.emptydata
        }
    },
    methods: {
        goToSimilarDetail(row) {
            this.$emit('goToSimilarDetail', row)
        },
        eventBiasClick(val) {
            return eventBiasHandle(val)
        },
        pageChange(val) {
            this.$emit('pageChange', this.pagesize, val)
        },
        pageSizeChange(val) {
            if (val * (this.pageindex - 1) <= this.total) {
                this.$emit('pageChange', val, this.pageindex)
            } else {
                this.$emit('pageChange', val, 1)
            }
        },
        sortChange(column, prop, order) {
            this.$emit('sortChange', column)
        },
        indexMethod(index) {
            return this.pagesize * (this.pageindex - 1) + index + 1
        },
        handleSelectionChange(val) {
            this.$emit('selection-change', val)
        },
        cellDblclick(row, column, cell, event) {
            this.$emit('cell-dblclick', row, column, cell, event)
        },
        rowClick(row, column, event) {
            this.$emit('rowClick', row, column, event)
        },
        clearSelection() {
            this.$refs.table.clearSelection()
        },
        toggleAllSelection() {
            this.$refs.table.toggleAllSelection()
        },
        toggleRowSelection(row) {
            this.$refs.table.toggleRowSelection(row)
        },
        handleRowChange(row) {
            if (this.highlightCurrentRow) {
                this.$emit('handleCurRow', row)
            }
        },
        doLayout() {
            this.$refs.table.doLayout()
        },
        formatDate(date, format = 'YYYY-MM-dd') {
            return common.formatDate(date, format)
        }
    }
}
</script>
<style lang="scss">
.baseDataPopper {
  .content-box {
    max-width: 500px;
    max-height: 400px;
    overflow-y: auto;
    white-space: pre-line;
  }
}
</style>
<style scoped lang="scss">
.el-icon-question {
  color: #1e81ff;
  margin-right: 8px;
}
.similarText {
  text-decoration: underline;
  color: #1e81ff;
  cursor: pointer;
}
.table-wrapper {
  padding: 17px 0 20px;
  .table-box {
    padding: 0 16px;
  }
}
.el-table {
  /deep/thead {
    tr,
    th {
      padding-top: 0;
      padding-bottom: 0;
      font-size: 14px;
      color: #333333;
      font-weight: normal;
      background: #fcfcfc;
    }
    th {
      .cell {
        display: flex;
        justify-content: center;
        align-items: center;
        line-height: 16px;
        white-space: pre;
        .dialog-box-title{
          overflow: hidden;
          text-overflow: ellipsis;
          white-space: nowrap;
        }
      }
      .hasWrap {
        display: flex;
        justify-content: center;
      }
    }
    .caret-wrapper {
      width: 14px;
      .sort-caret {
        left: 2px;
      }
    }
  }
  /deep/tr {
    height: 44px !important;
    td {
      padding-top: 4px;
      padding-bottom: 4px;
      color: #666666;
      font-size: 14px;
    }
    &.el-table__row--striped td {
      background: #fcfcfc !important;
    }
  }
  /deep/.el-table__body tr.current-row > td {
    background-color: #eff4f9;
  }
  .star-icon-box {
    .star-icon {
      margin-right: 6px;
      font-size: 14px;
      &:nth-last-of-type(1) {
        margin-right: 0;
      }
    }
  }
}
/deep/.table-pagination {
  margin-top: 20px;
  padding-top: 20px;
  text-align: center;
  border-top: 1px solid #eeeff1;
  .el-pagination.is-background .btn-next,
  .el-pagination.is-background .btn-prev,
  .el-pagination.is-background .el-pager li {
    font-size: 12px;
    font-weight: normal;
    color: #666666;
    background: #f6f6f6;
    border-radius: 3px;
  }
  .el-pagination.is-background .el-pager li:not(.disabled).active {
    color: #ffffff;
    background-color: #1e81ff;
  }
  .el-pagination.is-background .btn-next.disabled,
  .el-pagination.is-background .btn-next:disabled,
  .el-pagination.is-background .btn-prev.disabled,
  .el-pagination.is-background .btn-prev:disabled,
  .el-pagination.is-background .el-pager li.disabled {
    color: #c0c4cc;
  }
  .el-pagination__jump,
  .el-input__inner {
    font-size: 12px;
    color: #666666;
  }
}
.up {
  color: #ed5959;
}

.down {
  color: #33ad34;
}

.zero {
  color: #333;
}

</style>
父组件详情页面代码:
<template>
  <div>
    <div
      v-loading="loading"
      class="main-wrapper">
      <div class="main-box">
        <el-card class="box-card">
          <div class="title">相似度详情</div>
          <div class="tab-box">
            <div
              v-for="(item, index) in tabList"
              :key="index"
              class="item-tab"
              @click="handleClick(item, index)">
              <div
                :class="activeIndex === index ? 'color' : ''"
                class="tab"
              >{{ item }}</div>
              <div
                v-if="activeIndex === index"
                class="line-box"/>
            </div>
          </div>
          <div class="table">
            <ColumnTable
              ref="columnTable"
              :header-list="headerList"
              :table-data="tableData"
              :active-index="activeIndex"
              @addRow ="addRow"
              @delRow ="delRow"
            />
          </div>
        </el-card>
      </div>
    </div>
    <!-- 添加事件表格 -->
    <el-dialog
      v-if="dialogVisible"
      :visible.sync="dialogVisible"
      title="选择事件"
      width="60%"
      class="dialog"
      @close="cancelSelect">
      <el-form
        ref="ruleForm"
        :model="ruleForm"
        :rules="rules"
        label-width="100px"
        class="demo-ruleForm">
        <el-form-item
          label="事件"
          prop="similarVal">
          <el-select
            v-model="ruleForm.similarVal"
            :multiple-limit="2"
            placeholder="请选择"
            multiple
            collapse-tags>
            <el-option
              v-for="item in similarOptions"
              :key="`${getDate(item.eventdate.substring(0, 10))}${item.symbol}${item.shortname}解禁`"
              :label="`${getDate(item.eventdate.substring(0, 10))}${item.symbol}${item.shortname}解禁`"
              :value="`${getDate(item.eventdate.substring(0, 10))}${item.symbol}${item.shortname}解禁`"/>
          </el-select>
        </el-form-item>
        <el-form-item class="btnOptions">
          <el-button
            @click="cancelSelect">取消</el-button>
          <el-button
            type="primary"
            style="margin-left:20px"
            @click="submitForm('ruleForm')">确定</el-button>
        </el-form-item>
      </el-form>
    </el-dialog>
  </div>
</template>
<script>
import ColumnTable from '@/components/ColumnTable' // 引入父组件表格
import { querySimilarTableData } from '@/api/queryEvent' // 后端接口
export default {
    components: {
        ColumnTable
    },
    data() {
        return {
            tabList: ['基本信息', '财务信息', '行情走势', '分类业务'],
            activeIndex: 0,
            headerList: [
                {
                    label: '',
                    value: 'class',
                    headerAlign: 'left',
                    minWidth: '110',
                    align: 'center'
                },
                {
                    label: '基本信息',
                    value: 'baseInfo',
                    headerAlign: 'left',
                    minWidth: '80',
                    align: 'center'
                },
                {
                    label: '公司规模',
                    value: 'CirculatedMarketValue',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '公司性质',
                    value: 'OwnshipID',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '注册地址',
                    value: 'RegisterAddress',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '注册资本(元)',
                    value: 'RegisterCapital',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '办公地址',
                    value: 'OfficeAddress',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '上市日期',
                    value: 'IPODate',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '主营业务',
                    value: 'MainBusiness',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '所属行业',
                    value: 'IndustryCode',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '总股本(万股)',
                    value: 'TotalShare',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '流通股本(万股)',
                    value: 'CirculatedShare',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '财务信息',
                    value: 'financialInfo',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '净资产收益率(%)',
                    value: 'ROEA',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '净利润率(%)',
                    value: 'ROA2A',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '营业成本率(%)',
                    value: 'OperatingCostRatio',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '营业利润率(%)',
                    value: 'OperatingProfiToRevenue',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '销售期间费用率(%)',
                    value: 'PeriodExpenseRate',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '投资收益率(%)',
                    value: 'ReturnOnInvestment',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '应收账款周转率(%)',
                    value: 'ReceivableTurnoverA',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '总资产周转率(%)',
                    value: 'AssetTurnoverA',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '股东权益周转率(%)',
                    value: 'EequityTurnoverA',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '行情走势',
                    value: 'marketTrend',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '个股事后1日涨跌幅(%)',
                    value: 'onedayratio',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '个股事后3日涨跌幅(%)',
                    value: 'threedayratio',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '个股事后5日涨跌幅(%)',
                    value: 'fivedayratio',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '大盘收益率(%)',
                    value: 'marketreturn',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '行业收益率(%)',
                    value: 'industryreturn',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '分类业务',
                    value: 'age',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '解禁占比(%)',
                    value: 'Proportion2',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '总限售股数(万股)',
                    value: 'TotalLockShares',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '剩余限售股数量(万股)',
                    value: 'RemainedLockShares',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                },
                {
                    label: '解禁股东数',
                    value: 'HolderNumber',
                    headerAlign: 'left',
                    minWidth: '90',
                    align: 'center'
                }
            ],
            ruleForm: {
                similarVal: [] // 下拉框选中值
            },
            rules: {
                similarVal: [
                    { required: true, message: '请选择相似事件', trigger: 'change' }
                ]
            },
            tableData: [], // 表格数据
            queryParams: [], // 请求参数
            queryParams1: [], // 请求参数
            loading: false, // 加载状态
            dialogVisible: false, // 是否显示添加事件弹框
            similarOptions: [] // 下拉框选项数据
        }
    },
    mounted() {
        let arr = []
        arr = sessionStorage.getItem('similarParams')
        this.queryParams = JSON.parse(arr)
        this.queryParams.forEach(i => {
            const tableList = sessionStorage.getItem('tableData')
            this.similarOptions = JSON.parse(tableList).filter((item) => item.symbol !== i.symbol)
        })
        this.getSimilarTableData(this.queryParams)
    },
    methods: {
        // tab栏切换点击(滚动条锚点定位)
        handleClick(item, index) {
            this.activeIndex = index
            if (this.activeIndex == 0) {
                document.documentElement.scrollTop = 0
            } else {
                // 获取需要滚动到的元素
                const targetElement = document.getElementById('title')
                // 将页面滚动至目标元素处
                targetElement.scrollIntoView({ behavior: 'smooth' })
            }
        },
        // 获取表格数据
        getSimilarTableData(params) {
            this.loading = true
            this.tableData = []
            querySimilarTableData(params).then(res => {
                this.loading = false
                res.forEach((item, index) => {
                    params.forEach((i, idx) => {
                        if (index === idx) {
                            const title = this.getDate(i.event_date)
                            item.RegisterCapital = this.money(item.RegisterCapital)
                            item.ROEA = this.toDecimal(item.ROEA, 4)
                            item.ROA2A = this.toDecimal(item.ROA2A, 4)
                            item.OperatingCostRatio = this.toDecimal(item.OperatingCostRatio, 4)
                            item.OperatingProfiToRevenue = this.toDecimal(item.OperatingProfiToRevenue, 4)
                            item.PeriodExpenseRate = this.toDecimal(item.PeriodExpenseRate, 4)
                            item.ReturnOnInvestment = this.toDecimal(item.ReturnOnInvestment, 4)
                            item.ReceivableTurnoverA = this.toDecimal(item.ReceivableTurnoverA, 4)
                            item.AssetTurnoverA = this.toDecimal(item.AssetTurnoverA, 4)
                            item.EequityTurnoverA = this.toDecimal(item.EequityTurnoverA, 4)
                            item.onedayratio = this.toDecimal(item.onedayratio, 2)
                            item.threedayratio = this.toDecimal(item.threedayratio, 2)
                            item.fivedayratio = this.toDecimal(item.fivedayratio, 2)
                            item.marketreturn = this.toDecimal(item.marketreturn, 2)
                            item.industryreturn = this.toDecimal(item.industryreturn, 2)
                            item.Proportion2 = this.toDecimal(item.Proportion2, 4)
                            const obj = {
                                class: `${title}${i.symbol}${i.shortname}解禁`,
                                ...item
                            }
                            this.tableData.push(obj)
                        }
                    })
                })
                const obj1 = {
                    class: '添加其他事件',
                    AssetTurnoverA: '', CirculatedMarketValue: '', CirculatedShare: null, EequityTurnoverA: '', HolderNumber: null, IPODate: '', IndustryCode: '', MainBusiness: '', OfficeAddress: '', OperatingCostRatio: '', OperatingProfiToRevenue: '', OwnshipID: '', PeriodExpenseRate: '', Proportion2: '', ROA2A: '', ROEA: '', ReceivableTurnoverA: '', RegisterAddress: '', RegisterCapital: '', RemainedLockShares: null, ReturnOnInvestment: '', Symbol: '', TotalLockShares: null, TotalShare: null, fivedayratio: '', industryreturn: '', marketreturn: '', onedayratio: '', threedayratio: ''
                }
                this.tableData.push(obj1)
            })
        },
        // 格式化日期
        getDate(date) {
            const date1 = new Date(date)
            // 获取年份、月份和日期
            const year = date1.getFullYear()
            const month = date1.getMonth() + 1 // 注意月份从0开始计数,所以需要加上1得到真正的月份(12)
            const day = date1.getDate()
            return `${year}年${month}月${day}日`
        },
        // 新增事件
        addRow() {
            this.ruleForm.similarVal = []
            this.tableData.forEach(res => {
                this.similarOptions.forEach(i => {
                    if (res.class === `${this.getDate(i.eventdate.substring(0, 10))}${i.symbol}${i.shortname}解禁`) {
                        this.ruleForm.similarVal.push(res.class)
                    }
                })
            })
            this.dialogVisible = true
        },
        // 删除事件
        delRow(label) {
            const index = this.tableData.findIndex(item => item.class === label)
            const selectItem = this.tableData.splice(index, 1)
            const arr = this.ruleForm.similarVal.filter(i => i !== selectItem[0].class)
            this.ruleForm.similarVal = arr
        },
        // 取消选择事件
        cancelSelect() {
            this.dialogVisible = false
        },
        // 点击确定添加事件
        submitForm(formName) {
            this.$refs[formName].validate((valid) => {
                if (valid) {
                    this.queryParams1 = []
                    let queryParams2 = []
                    let tableParams = []
                    this.ruleForm.similarVal.forEach(i => {
                        queryParams2 = this.similarOptions.filter((item) => `${this.getDate(item.eventdate.substring(0, 10))}${item.symbol}${item.shortname}解禁` === i)
                        queryParams2.forEach(item => {
                            const obj = {
                                event_date: item.eventdate.substring(0, 10),
                                event_type: Number(item.eventtype),
                                shortname: item.shortname,
                                symbol: item.symbol
                            }
                            this.queryParams1.push(obj)
                            tableParams = this.queryParams.concat(this.queryParams1)
                        })
                    })
                    this.getSimilarTableData(tableParams)
                    this.dialogVisible = false
                } else {
                    console.log('error submit!!')
                    return false
                }
            })
        }
    }
}
</script>
<style lang="scss" scoped>
/deep/ .el-select {
    width: 320px !important;
}
/deep/ .el-select > .el-input {
    width: 350px;
}
/deep/ .el-dialog {
    height: 50%;
    position: relative;
}
/deep/ .el-dialog__footer {
    position: absolute;
    bottom: 20px;
    right: 20px;
}
.select-title {
    margin-left: 100px;
}
.main-wrapper {
    margin: 77px auto 0;
    max-width: 1200px;
    box-sizing: border-box;
    .main-box {
        display: flex;
        .box-card {
            width: 100%;
            .title {
                font-size: 14px;
                font-family: Noto Sans S Chinese;
                font-weight: bold;
                color: #333333;
            }
            .tab-box {
                display: flex;
                align-items: center;
                margin: 35px 57px 37px 57px;
                .item-tab {
                    margin-right: 36px;
                    width: 67px;
                    display: flex;
                    justify-content: center;
                    align-items: center;
                    flex-direction: column;
                    cursor: pointer;
                    position: relative;
                    .tab {
                        font-size: 14px;
                        font-family: Noto Sans S Chinese;
                        font-weight: 400;
                        color: #333333;
                    }
                    .color {
                        color: #1E81FF;
                    }
                }
                .line-box {
                    width: 67px;
                    height: 1px;
                    background: #1E81FF;
                    position: absolute;
                    content: "";
                    bottom: -3px;
                    left: 0;
                }
            }
            .table {
                margin: 33px 57px 56px;
            }
        }
    }
}
.btnOptions {
    position: absolute;
    right: 20px;
    bottom: 20px;
}
</style>
加减表格子组件代码:

<!-- 表格行列转换 -->
<template>
  <el-table
    ref="table"
    :data="getTableData[0]"
    border
    style="width: 100%;"
  >
    <template v-for="(item, index) in getTableData[1]">
      <el-table-column
        :key="index"
        :label="item.label"
        :prop="item.value"
        :header-align="item.headerAlign || 'center'"
        :min-width="item.minWidth || 80"
        :align="item.align || 'center'"
      >
        <template slot-scope="scope">
          <!-- {{ tableFormatter(item.value, scope.row) }}-{{ index }}-{{ scope.$index }} index代表表格列索引,scope.$index代表表格行索引 -->
          <div
            :class="(scope.$index === 0 && activeIndex == 0) || (scope.$index === 11 && activeIndex == 1) || (scope.$index === 21 && activeIndex == 2) || (scope.$index === 27 && activeIndex == 3) ? 'title-text' : ''"
            :style="{textAlign: (scope.$index === 4 || scope.$index > 8) && index > 0 ? 'right' : (index === 0 ? 'center' : 'left')}"
            :id="(scope.$index === 0 && activeIndex == 0) || (scope.$index === 11 && activeIndex == 1) || (scope.$index === 21 && activeIndex == 2) || (scope.$index === 27 && activeIndex == 3) ? 'title' : ''">
            {{ tableFormatter(item.value, scope.row) }}
          </div>
        </template>
        <template
          slot="header"
          slot-scope="scope">
          <div
            v-if="scope.$index == tableData.length"
            class="ceil">
            {{ scope.column.label }}
            <i
              class="el-icon-circle-plus-outline"
              @click="addRow()" />
          </div>
          <div
            v-else-if="scope.$index>2&&scope.$index<tableData.length"
            class="ceil">
            {{ scope.column.label }}
            <i
              class="el-icon-remove-outline"
              @click="delRow(scope.column.label)"/>
          </div>
          <span v-else>{{ scope.column.label }}</span>
        </template>
      </el-table-column>
    </template>
  </el-table>
</template>

<script>

export default {
    props: {
        headerList: {
            type: Array,
            default: () => []
        },
        tableData: {
            type: Array,
            default: () => []
        },
        activeIndex: {
            type: Number,
            default: 0
        }
    },
    data() {
        return {
        }
    },
    computed: {
        getTableData() {
            return this.fotmatterTableData(this.tableData, this.headerList)
        }
    },
    methods: {
        fotmatterTableData(data, header) {
            const enddata = []
            const endheader = []
            header.forEach((item, index) => {
                if (index === 0) {
                    endheader.push({
                        label: header[index].label,
                        value: 'mainIndex',
                        headerAlign: header[index].headerAlign,
                        minWidth: header[index].minWidth,
                        align: header[index].align
                    })
                    data.forEach((ele, idx) => {
                        endheader.push({
                            label: ele[header[index].value],
                            value: `type${idx}`
                        })
                    })
                } else {
                    const obj = {
                        mainIndex: header[index].label
                    }
                    data.forEach((ele, ind) => {
                        obj[`type${ind}`] = ele[header[index].value]
                    })
                    enddata.push(obj)
                }
            })
            console.log('end===', enddata, endheader)
            return [enddata, endheader]
        },

        // 列表格式化
        tableFormatter(prop, row) {
            if (row[prop] || row[prop] == 0) {
                if (prop === 'mainIndex') {
                    if (row.unit) {
                        return `${row[prop]}(${row.unit})`
                    }
                    return row[prop]
                }
                return row[prop]
            }

            return ''
        },
        randomStr() {
            return Math.random().toString().slice(3, 8)
        },
        addRow() {
            this.$emit('addRow')
        },
        delRow(label) {
            this.$emit('delRow', label)
        }
    }
}
</script>
<style scoped>
.title-text {
    background: rgba(30,129,255,0.1);
    color: #1E81FF;
    width: 100%;
    height: 24px;
}
/deep/ .el-table th > .cell {
    padding-left: 0 !important;
    padding-right: 0 !important;
}
/deep/ .cell {
    padding-left: 0 !important;
    padding-right: 0 !important;
    height: 100%;
}
/deep/ td {
    padding: 0;
}
.el-icon-circle-plus-outline {
    cursor: pointer;
    color: #1E81FF;
    font-size: 16px;
}
.el-icon-remove-outline {
    cursor: pointer;
    color: #707070;
    font-size: 16px;
}
</style>