当前位置: 首页 > news >正文

vue3+element-plus动态与静态表格数据渲染

一、表格组件:

<template>

  <el-table

    ref="myTable"

    :data="tableData"

    :header-cell-style="headerCellStyle"

    header-row-class-name="my-table-header"

    cell-class-name="my-td-cell"

    :row-style="rowStyle"

    :cell-style="cellStyle"

    :span-method="dynamic ? objectSpanMethod : handleSpanMethod"

    @sort-change="tableSort"

    @selection-change="handleSelectionChange"

    class="stock-table"

    border

    :max-height="height"

    row-key="securityID"

    :show-overflow-tooltip="dynamic || !showOverflowTooltip ? false : true"

    stripe

  >

    <slot name="selection"></slot>

    <el-table-column v-if="indexShow" label="序号" width="50" align="center">

      <template #default="scope">

        <span>{{ (currentPage - 1) * pageSize + scope.$index + 1 }}</span>

      </template>

    </el-table-column>

    <!-- 表头渲染 -->

    <template v-for="(item, index) in tableColums" :key="index">

      <el-table-column

        v-if="!item.hidden"

        :fixed="item.fixed"

        :width="item.width"

        :min-width="item.minWidth || ''"

        :prop="item.prop"

        :sortable="item.sortable"

        header-align="center"

        :align="item.align || 'center'"

        :resizable="false"

      >

        <template #header>

          <div class="heard-text static-heard">

            <div class="heard-name" v-html="item.label"></div>

          </div>

        </template>

        <!-- 静态表格表内容渲染 -->

        <!-- 使用 scoped slot 将列的内容交给父组件自定义 -->

        <template #default="scope">

          <slot :name="`column-${item.prop}`" :row="scope.row" :column="item">

            <!-- 处理监管处罚第一个表格链接跳转问题 -->

            <el-link

              type="primary"

              :underline="false"

              @click="openLink(scope.row.link)"

              class="link-ellipsis"

              v-if="tableId === 'yearTable' && item.prop === 'title'"

              >{{ scope.row[item.prop] }}</el-link

            >

            <!-- 触及ST指标的次数 悬浮查看年份 -->

            <div v-else-if="item.isST">

              <el-tooltip

                effect="dark"

                :content="[...new Set(scope.row[item.prop])].join('、')"

                placement="top"

                v-if="

                  scope.row[item.prop] &&

                  Array.isArray(scope.row[item.prop]) &&

                  scope.row[item.prop].length > 0

                "

              >

                <span class="red">{{ scope.row[item.prop].length }}</span>

              </el-tooltip>

              <span v-else>{{

                Array.isArray(scope.row[item.prop])

                  ? scope.row[item.prop].length

                  : scope.row[item.prop]

              }}</span>

            </div>

            <!-- 触及ST指标的次数为拼接的字段 悬浮查看年份 -->

            <div

              v-else-if="item.isJointST"

              :style="{

                textAlign:

                  scope.row[item.prop] && scope.row[item.prop].length === 0

                    ? 'center'

                    : item.align

              }"

            >

              <template

                v-if="

                  scope.row[item.prop] &&

                  scope.row[item.prop].length > 0 &&

                  !item.isNumber

                "

              >

                <span v-for="(i, idx) in scope.row[item.prop]" :key="idx">

                  <!-- 辖区募投异常变更项目字段根据结构特殊处理 -->

                  <span v-if="item.noShowYear"

                    ><span class="dot">·</span>{{ i.name

                    }}<span v-if="idx < scope.row[item.prop].length - 1"

                      ><br /></span

                  ></span>

                  <span v-else>

                    <span

                      >{{ i.name }}

                      <el-tooltip

                        effect="dark"

                        :content="i.years"

                        placement="top"

                      >

                        <span class="red">{{ i.count }}</span>

                      </el-tooltip></span

                    >

                    <!-- 只有当不是最后一个元素时才显示顿号 -->

                    <span v-if="idx < scope.row[item.prop].length - 1">、</span>

                  </span>

                </span>

              </template>

              <!-- 如果是延期回复次数根据后端返回结构单独处理 -->

              <template v-else-if="item.isNumber">

                <el-tooltip

                  effect="dark"

                  :content="scope.row[item.prop] && scope.row[item.prop].years"

                  placement="top"

                  v-if="

                    scope.row[item.prop] && scope.row[item.prop].count !== 0

                  "

                >

                  <span class="red">{{ scope.row[item.prop].count }}</span>

                </el-tooltip>

                <span v-else>{{

                  scope.row[item.prop] && scope.row[item.prop].count

                }}</span>

              </template>

              <template v-else>

                <span>/</span>

              </template>

            </div>

            <!-- 默认内容(文本为是,则字体颜色标红) -->

            <div

              :style="{

                color:

                  scope.row[item.prop] === '是'

                    ? '#ff0000'

                    : scope.row[item.prop] !== '/' &&

                      scope.row.announceUrl !== null

                    ? item.color

                    : '',

                cursor:

                  scope.row[item.prop] !== '/' && scope.row.announceUrl !== null

                    ? item.cursor

                    : '',

                textAlign: scope.row[item.prop] === '/' ? 'center' : item.align

              }"

              @click="

                item.cursor && scope.row.announceUrl !== null

                  ? toPage(scope.row)

                  : ''

              "

              :class="tableId === 'warnDetail2' ? '' : 'table-content'"

              v-else

            >

              {{

                (item.prop === 'declareDate' &&

                  scope.row[item.prop] === null) ||

                (item.prop === 'guaranteeOverdueNum' &&

                  scope.row[item.prop] === null)

                  ? '/'

                  : item.prop === 'cancelDate' && scope.row[item.prop] === null

                  ? '未撤销'

                  : scope.row[item.prop]

              }}

            </div>

          </slot>

        </template>

      </el-table-column>

    </template>

    <!-- 动态表格动态表头表内容渲染 -->

    <template v-if="dynamic">

      <el-table-column

        v-for="(item, index) in tableData && tableData[0]?.data"

        :key="index"

        :min-width="200"

        :prop="item.sortColumnCode || ''"

        :sortable="item.sortColumnCode ? 'custom' : false"

        header-align="center"

      >

        <template #header>

          <div class="heard-text">

            <div class="heard-name">{{ item.dataName }}</div>

            <div class="change-text" v-if="item.headFlag === 1">更</div>

          </div>

        </template>

        <template #default="scope">

          <!-- 默认内容(valFlag后端返回1的字段,则字体颜色标红) -->

          <div

            :style="{

              color:

                scope.row.data &&

                scope.row.data[index] &&

                scope.row.data[index].valFlag === 1

                  ? '#FF0000'

                  : '',

              textAlign:

                scope.row.data &&

                scope.row.data[index] &&

                scope.row.data[index].textAlign === 'left' &&

                scope.row.data[index].dataVal !== '/'

                  ? 'left'

                  : 'center'

            }"

          >

            {{

              scope.row.data &&

              scope.row.data[index] &&

              scope.row.data[index].dataVal

            }}

          </div></template

        >

      </el-table-column>

    </template>

  </el-table>

  <!--  分页组件  -->

  <paging

    v-if="paginationShow"

    :currentPage="currentPage"

    :pageSize="pageSize"

    layout="total, sizes, prev, pager, next, jumper"

    :pageSizes="[10, 20, 50, 100]"

    :total="total"

    :background="true"

    @handleCurrentChange="changeCurrentPage"

    @handleSizeChange="changePageSize"

  />

</template>

<script setup lang="ts">

import Paging from '@/views/stock-index/components/paging.vue'

import { ElMessage } from 'element-plus'

import { exportFileUrl } from '@/pages/monitor/monitorApi/stockMonitor'

import { useReportStore } from '@/store/modules/report'

// 定义合并规则的类型

interface MergeRule {

  rowIndex: number // 行索引

  columnIndex: number // 列索引

  rowspan: number // 合并的行数

  colspan: number // 合并的列数

}

const props = defineProps({

  showOverflowTooltip: {

    // 表格是否展示浮框

    type: Boolean,

    default: true

  },

  dynamic: {

    // 是否动态表格

    type: Boolean,

    default: false

  },

  paginationShow: {

    // 是否显示分页

    type: Boolean,

    default: false

  },

  currentPage: {

    // 当前页

    type: Number,

    default: 1

  },

  pageSize: {

    // 一页展示条数

    type: Number,

    default: 10

  },

  total: {

    // 总条数

    type: Number,

    default: 0

  },

  height: {

    type: String,

    default: '400'

  },

  rowHeight: {

    type: Number,

    default: 40 // 默认行高

  },

  // 静态表格合并规则

  mergeRules: {

    type: Array as any,

    default: () => []

  },

  // 动态表格合并字段

  mergeKey: {

    type: String,

    default: ''

  },

  // 表格id

  tableId: {

    type: String,

    default: ''

  },

  // 表头数据

  tableColums: {

    type: Array as () => any[],

    default: () => []

  },

  // 表内容数据

  tableData: {

    type: Array as () => any[],

    default: () => []

  },

  //是否显示列表序号

  indexShow: {

    type: Boolean,

    default: false

  }

})

const emit = defineEmits([

  'tableSort',

  'handleSelectionChange',

  'changeCurrentPage',

  'changePageSize'

])

const myTable = ref()

// 设置表头样式

const headerCellStyle = () => {

  return {

    height: '34px',

    background: '#F9FBFD',

    color: '#333333',

    fontSize: '14px',

    borderRight: '1px solid #ebeef8',

    padding: '1px 0',

    fontWeight: 'normal'

  }

}

// 动态设置行高

const rowStyle = () => {

  return {

    height: `${props.rowHeight}px`,

    lineHeight: `${props.rowHeight}px`

  }

}

// 设置单元格样式

const cellStyle = ({

  row,

  column,

  rowIndex,

  columnIndex

}: {

  row: any

  column: any

  rowIndex: number

  columnIndex: number

}) => {

  // 设置监管处罚科目(行业排名)单元格为/时居中显示,否则居左显示

  if (

    column.property === 'subject' &&

    row.subject === '/' &&

    columnIndex === 1

  ) {

    return {

      fontSize: '14px',

      color: '#666666',

      textAlign: 'center'

    }

  } else {

    return {

      fontSize: '14px',

      color: '#666666'

    }

  }

}

//监听全局参数搜索

watch(

  () => useReportStore().topSelectObj,

  (newVal) => {

    myTable.value.clearSort() // 清除表格排序

  }

)

const openedWindows = new Map()

// 公告跳转

const toPage = (row: any) => {

  if (row.announceUrl && row.announceUrl !== null) {

    exportFileUrl(row.announceUrl).then((res: any) => {

      if (res.code === 200) {

        // 检查是否已经有一个窗口打开了该链接

        if (openedWindows.has(res.data)) {

          const windowRef = openedWindows.get(res.data)

          if (!windowRef.closed) {

            windowRef.focus() // 聚焦到已打开的窗口

            return

          }

        }

        // 如果没有打开的窗口,则打开一个新窗口

        const newWindow = window.open(res.data, '_blank')

        openedWindows.set(res.data, newWindow) // 存储窗口引用

      } else {

        ElMessage({

          type: 'error',

          message: res.message

        })

      }

    })

  }

}

// 监管处罚链接跳转

const openLink = (url: any) => {

  if (url === '') return // 如果链接为空,直接返回

  // 如果没有打开的窗口,则打开一个新窗口

  const newWindow = window.open(url, '_blank', 'noreferrer,noopener')

  openedWindows.set(url, newWindow) // 存储窗口引用

}

// 动态表格合并规则

const objectSpanMethod = ({

  row,

  column,

  rowIndex,

  columnIndex

}: {

  row: any

  column: any

  rowIndex: number

  columnIndex: number

}) => {

  if (columnIndex !== 0) {

    return { rowspan: 1, colspan: 1 }

  }

  if (!props.mergeKey) {

    return { rowspan: 1, colspan: 1 }

  }

  if (

    rowIndex === 0 ||

    row[props.mergeKey] !== props.tableData[rowIndex - 1][props.mergeKey]

  ) {

    let rowspan = 1

    for (let i = rowIndex + 1; i < props.tableData.length; i++) {

      if (props.tableData[i][props.mergeKey] === row[props.mergeKey]) {

        rowspan++

      } else {

        break

      }

    }

    return { rowspan, colspan: 1 }

  } else {

    return { rowspan: 0, colspan: 0 }

  }

}

// 定义静态表格合并参数字段

type TableId = 'warnDetail' | 'warnDetail1' | 'warnDetail2' | 'warnDetail3' // 定义可能的 tableId 类型

type MergeConfig = Record<TableId, Record<number, string>> // 定义 mergeConfig 的类型

const mergeConfig: MergeConfig = {

  warnDetail: { 0: 'year' },

  warnDetail1: { 0: 'name', 1: 'declareDate' },

  warnDetail2: {

    0: 'correctionAccouPeri',

    1: 'iserror',

    2: 'declareDate',

    3: 'announce'

  },

  warnDetail3: {

    0: 'declareDate'

  }

}

// 静态表格合并规则

const handleSpanMethod = ({

  row,

  column,

  rowIndex,

  columnIndex

}: {

  row: any

  column: any

  rowIndex: number

  columnIndex: number

}) => {

  const tableId = props.tableId as TableId // 使用类型断言确保 tableId 是 TableId 类型

  const config = mergeConfig[tableId] // 现在 TypeScript 知道 config 的类型

  if (config && config[columnIndex]) {

    return mergeRows({ row, rowIndex, key: config[columnIndex] })

  } else {

    return handleMergeRules({ rowIndex, columnIndex })

  }

}

// 静态表格统一合并方法

const mergeRows = ({

  row,

  rowIndex,

  key

}: {

  row: any

  rowIndex: number

  key: string

}) => {

  if (rowIndex === 0 || row[key] !== props.tableData[rowIndex - 1][key]) {

    let rowspan = 1

    for (let i = rowIndex + 1; i < props.tableData.length; i++) {

      if (props.tableData[i][key] === row[key]) {

        rowspan++

      } else {

        break

      }

    }

    return { rowspan, colspan: 1 }

  } else {

    return { rowspan: 0, colspan: 0 }

  }

}

// 外部传进来的表格合并方法

const handleMergeRules = ({

  rowIndex,

  columnIndex

}: {

  rowIndex: number

  columnIndex: number

}) => {

  const rule = props.mergeRules.find(

    (rule: MergeRule) =>

      rule.rowIndex === rowIndex && rule.columnIndex === columnIndex

  )

  if (rule) {

    return { rowspan: rule.rowspan, colspan: rule.colspan }

  }

  for (const r of props.mergeRules) {

    if (

      rowIndex > r.rowIndex &&

      rowIndex < r.rowIndex + r.rowspan &&

      columnIndex >= r.columnIndex &&

      columnIndex < r.columnIndex + r.colspan

    ) {

      return { rowspan: 0, colspan: 0 }

    }

  }

  return { rowspan: 1, colspan: 1 }

}

// 表格排序

const tableSort = (dataObj: any) => {

  const col: any = props.tableColums?.filter((v: any) => {

    return v.prop === dataObj.prop

  })[0]

  let prop = ''

  let order = dataObj.order

  if (col) {

    prop = col.prop

  } else {

    prop = dataObj.prop

  }

  emit('tableSort', [order ? prop : '', order])

}

// 表格多选

const handleSelectionChange = (selection: any) => {

  emit('handleSelectionChange', selection)

}

// 点击改变当前页重新渲染表格数据

const changeCurrentPage = (current: number) => {

  emit('changeCurrentPage', current)

}

//size改变

const changePageSize = (size: number) => {

  emit('changePageSize', size)

}

//清空选中

const clearSelection = () => {

  myTable.value.clearSelection()

}

const clearTableSort = () => {

  myTable.value && myTable.value.clearSort()

}

defineExpose({

  // 暴露子组件方法给父组件调用

  clearSelection,

  clearTableSort

})

</script>

<style lang="less" scoped>

.dot {

  font-size: 40px;

  margin-right: 5px;

  vertical-align: middle;

}

.link-ellipsis {

  &:hover {

    color: #3a5bb7;

  }

}

.red {

  color: @text-red;

}

.table-content {

  white-space: nowrap; /* 强制文本在一行显示 */

  overflow: hidden; /* 隐藏超出容器的内容 */

  text-overflow: ellipsis; /* 超出部分显示省略号 */

}

:deep(.my-table-header .cell) {

  font-size: 13px;

  padding: 6px 4px;

  font-weight: 600;

}

:deep(.my-selection .cell) {

  display: flex;

  justify-content: center;

  align-items: center;

}

.heard-text {

  // display: flex;

  // justify-content: center;

  display: inline-block;

  vertical-align: middle;

  &.static-heard {

    max-width: calc(100% - 15px);

  }

  .heard-name {

    display: inline-block;

  }

  .change-text {

    width: 14px;

    height: 14px;

    background: #fbfdfe;

    border-radius: 2px;

    border: 1px solid #eb9c35;

    font-size: 12px;

    color: #eb9c35;

    // display: flex;

    // align-items: center;

    // justify-content: center;

    display: inline-block;

    line-height: 14px;

    text-align: center;

    position: relative;

    top: -1px;

    margin-left: 4px;

    // margin-top: 2px;

  }

  & + :deep(.caret-wrapper) {

    top: 1px;

  }

}

</style>

二、使用表格组件:

<template>

<!-- 静态表格使用 -->

        <div class="table-box">

          <TableList

            :table-colums="colums"

            :table-data="requestParams.tableData"

            pagination-show

            index-show

            :show-overflow-tooltip="false"

            :page-size="requestParams.limit"

            :current-page="requestParams.page"

            :total="requestParams.total"

            @change-page-size="($event) => changePageSize($event)"

            @change-current-page="($event) => changeCurrentPage($event)"

            @table-sort="($event) => tableDataSort($event)"

            height="450"

          >

            <template v-slot:column-symbol="{ row }">

              <div class="name-symbol">

                <p>{{ row.shortName }}</p>

                <p>({{ row.symbol }})</p>

              </div>

            </template>

          </TableList>

<!-- 动态表格使用(mergeKey为表格合并字段) -->

        <TableList

          :table-colums="colums"

          :table-data="tableData"

          dynamic

          height="450"

          mergeKey="indicatorType"

        ></TableList>

        </div>

</template>

<script setup lang="ts">

import { ElMessage } from 'element-plus'

import TableList from './TableList.vue'

import { queryOtherUnitsData } from '@/pages/monitor/monitorApi/jurisdiction/index'

const requestParams = ref({

  page: 1,

  limit: 10,

  total: 0,

  order: '',

  sort: '',

  abnormalCount: 0

  tableData: [], // 表格数据

  loading: false // 加载状态

})

const colums = ref([

  {

    prop: 'symbol',

    label: '公司(代码)',

    sortable: true,

    width: '120'

  },

  {

    prop: 'violationTypeList',

    label: '违规类型',

    isJointST: true,

    align: 'left'

  },

  {

    prop: 'supervisorList',

    label: '处罚单位',

    isJointST: true,

    align: 'left'

  }

])

//排序点击

const tableDataSort = (arr: any) => {

  requestParams.value.page = 1

  requestParams.value.sort = arr[0]

  requestParams.value.order = arr[1]

  getOtherUnitsData()

}

//页码更改

const changePageSize = (val: number) => {

  requestParams.value.limit = val

  getOtherUnitsData()

}

//分页

const changeCurrentPage = (val: number) => {

  requestParams.value.page = val

  getOtherUnitsData()

}

// 查询交易所所有表格数据

const getOtherUnitsData = () => {

  requestParams.value.loading = true

  requestParams.value.tableData = []

  const params = {

    limit: requestParams.value.limit,

    page: requestParams.value.page,

    sort: requestParams.value.sort,

    order: requestParams.value.order

  }

  queryOtherUnitsData(params).then((res: any) => {

    if (res.code === 200) {

      requestParams.value.tableData = res.data.data

      requestParams.value.total = res.data.total

    } else {

      ElMessage({

        type: 'error',

        message: res.message

      })

    }

    requestParams.value.loading = false

  })

}

onMounted(() => {

  getOtherUnitsData()

})

</script>

<style scoped lang="less">

.item {

  border: 1px solid #ebeef8;

  border-top: none;

  .title {

    display: flex;

    align-items: center;

    padding-left: 26px;

    height: 34px;

    background: #f4f7fc;

    border: 1px solid #ebeef8;

    border-left: none;

    border-right: none;

    font-weight: bold;

    font-size: 14px;

    color: #333333;

    position: relative;

    &::before {

      content: '';

      position: absolute;

      left: 15px;

      top: 50%;

      margin-top: -5px;

      width: 4px;

      height: 10px;

      background: #3a5bb7;

      border-radius: 0px 2px 2px 0px;

    }

    .more {

      margin-left: auto;

      margin-right: 15px;

      font-size: 12px;

      color: #666666;

      font-weight: normal !important;

      .times {

        color: @text-red;

      }

    }

  }

  .table-box {

    padding: 15px;

    .name-symbol {

      color: @text-blue;

      cursor: pointer;

    }

  }

}

</style>


http://www.mrgr.cn/news/97708.html

相关文章:

  • 0.机器学习基础
  • 循环神经网络 - 参数学习之随时间反向传播算法
  • Android Input——输入系统介绍(一)
  • 实现usb的MTP功能
  • window上 docker使用ros2开发并usbip共享usb设备
  • Docker+Jenkins+Gitee自动化项目部署
  • 【Linux】系统进程管理
  • oracle 11g密码长度和复杂度查看与设置
  • 处理Excel的python库openpyxl、xlrd、xlwt、pandas有什么区别,搞懂它
  • python三大库之---pandas(二)
  • this指针 和 类的继承
  • Qt项目——记事本
  • HarmonyOS应用开发者高级-编程题-001
  • 构建一个最简单的UDP服务器和客户端并逐行解析
  • 新一代达梦官方管理工具SQLark:可视化建表操作指南
  • 【QT】QT编译链接 msql 数据库
  • 用PointNet++训练自己的数据集(语义分割模型semseg)
  • vscode调试vite项目断点(debugger)
  • linux开发环境
  • 如何高效生成达梦数据库测试数据?官方管理工具来了!