欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > IT业 > Vue3+element-plus表格筛选与文本展开收起

Vue3+element-plus表格筛选与文本展开收起

2024/10/23 13:46:50 来源:https://blog.csdn.net/Mirs_Zhu/article/details/143158586  浏览:    关键词:Vue3+element-plus表格筛选与文本展开收起

一、父组件代码

<template>

  <div class="newsDynamicMonitoring" v-loading="loading">

    <filterBox

      class="top-box"

      :dataTotal="dataTotal"

      :id="id"

      :securityId="securityId"

      :exchanges="exchanges"

      @getCompanyTypeSelect="getCompanyTypeSelect"

      @getSourceLabelSelect="getSourceLabelSelect"

      @getStockTypeSelect="getStockTypeSelect"

      @getImportance="getImportance"

      @getPositive="getPositive"

      @getDateRange="getDateRange"

      @getSearchVal="getSearchVal"

      @searchList="searchList"

      @exportTableList="exportTableList"

      ref="filterRef"

    ></filterBox>

    <tableList

      :id="id"

      :tableData="tableData"

      :currentPage="currentPage"

      :pageSize="pageSize"

      :total="total"

      @searchTableList="searchTableList"

      @tableSortByProp="tableSortByProp"

      @tableScrollTop="tableScrollTop"

    ></tableList>

  </div>

</template>

<script setup lang="ts">

import { ElMessage } from 'element-plus'

import filterBox from './filterBox.vue'

import tableList from './tableList.vue'

import common from '@/utils/common'

import { queryNewsList, exportNewsList } from '@/service/stockIndex/index'

const props = defineProps({

  id: {

    // 板块id

    type: String,

    default: ''

  },

  securityId: {

    // 证券id

    type: [String, Number],

    required: true

  },

  exchanges: {

    // 交易所筛选

    type: Array,

    default: () => []

  }

})

const emit = defineEmits(['getTableList'])

const loading = ref(false) // 加载状态

const dataTotal = ref('') // 表格右上角显示的条数

const tableData = ref([]) // 表格数据

const filterRef = ref() // 筛选框ref

const total = ref(0) // 总条数

const currentPage = ref(1) // 当前页

const pageSize = ref(10) // 一页展示条数

const startDate = ref() // 开始时间

const endDate = ref() //结束时间

const importance = ref() // 重要性值

const positive = ref('') // 正负面值

const searchVal = ref('') // 搜索框值

const tableProp = ref('') // 表格排序字段

const tableOrder = ref('') // 表格排序顺序

const scrollTop = ref(0) // 表格排序顺序

const nowTime = ref<any>() // 下载时间

const selectCompanyTypeArr = ref([]) // 公司类型标签筛选列表值

const selectSourceLabelArr = ref([]) // 来源标签筛选列表值

const selectStockTypelArr = ref([]) // 来源标签筛选列表值

const exchangeArr = ref<any>([]) // 交易所筛选字段

const securityId1 = ref(props.securityId) // 页面新的证券id

// 监听参数重新赋值

watch(

  () => [props.securityId, props.exchanges],

  (newValue: any, oldValue: any) => {

    if (newValue[0] != oldValue[0]) {

      securityId1.value = newValue[0]

    }

    if (newValue[1] != oldValue[1]) {

      exchangeArr.value = newValue[1]

    }

    selectCompanyTypeArr.value = []

    selectSourceLabelArr.value = []

    selectStockTypelArr.value = []

    tableData.value = []

    currentPage.value = 1

    getNewsList()

  },

  { deep: true }

)

// 获取时间范围

const getDateRange = (date1: any, date2: any) => {

  startDate.value = date1

  endDate.value = date2

}

// 获取重要性筛选列表值

const getImportance = (importanceVal: number) => {

  importance.value = importanceVal

}

// 获取正负面筛选列表值

const getPositive = (positiveVal: string) => {

  positive.value = positiveVal

}

// 获取搜索框输入值

const getSearchVal = (inputVal: string) => {

  searchVal.value = inputVal

}

// 获取公司类型筛选列表值

const getCompanyTypeSelect = (data: any) => {

  selectCompanyTypeArr.value = data

  currentPage.value = 1

  getNewsList()

}

// 获取来源标签筛选列表值

const getSourceLabelSelect = (data: any) => {

  selectSourceLabelArr.value = data

  currentPage.value = 1

  getNewsList()

}

// 获取证券类型筛选列表值

const getStockTypeSelect = (data: any) => {

  selectStockTypelArr.value = data

  currentPage.value = 1

  getNewsList()

}

// 获取筛选框子组件方法

const getChildMethod = () => {

  if (filterRef.value) {

    filterRef.value.getNewsLabelList()

  }

}

// 搜索查询表格

const searchList = () => {

  // 校验开始时间不能超过当前时间

  if (new Date(endDate.value).getTime() > new Date().getTime()) {

    // getTime() 方法返回一个时间的格林威治时间数值

    ElMessage({

      message: '结束时间不能大于当前时间',

      type: 'error'

    })

    return

  }

  currentPage.value = 1

  getChildMethod()

  getNewsList()

}

// 表格排序

const tableSortByProp = (data: any) => {

  tableProp.value = data[0]

  tableOrder.value = data[1]

  currentPage.value = 1

  getNewsList()

}

// 表格滚动条置顶

const tableScrollTop = (top: number) => {

  scrollTop.value = top

}

// 改变页数查询表格

const searchTableList = (current: number) => {

  currentPage.value = current

  getNewsList()

  // 滚动到顶部

  tableScrollTop(0)

}

// 查询表格数据

const getNewsList = () => {

  loading.value = true

  tableData.value = []

  const arr = toRaw(selectCompanyTypeArr.value)

  const arr1 = toRaw(selectSourceLabelArr.value)

  if (props.id === 'todaySummary') {

    exchangeArr.value = props.exchanges

  } else {

    exchangeArr.value = toRaw(selectStockTypelArr.value)

  }

  queryNewsList(

    props.id === 'todaySummary' ? 1 : 0,

    pageSize.value,

    currentPage.value,

    endDate.value,

    importance.value,

    searchVal.value,

    arr,

    arr1,

    exchangeArr.value,

    securityId1.value,

    positive.value,

    startDate.value,

    tableOrder.value,

    tableProp.value

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

    if (res.code === 200) {

      if (res.data) {

        tableData.value = res.data.data

        total.value = res.data.total

        dataTotal.value = common.money(res.data.total, 0, false)

        emit('getTableList', tableData.value)

      }

    } else {

      ElMessage({

        message: res.message,

        type: 'error'

      })

    }

    loading.value = false

  })

}

// 导出表格数据

const exportTableList = () => {

  loading.value = true

  const arr = toRaw(selectCompanyTypeArr.value)

  const arr1 = toRaw(selectSourceLabelArr.value)

  exchangeArr.value = toRaw(selectStockTypelArr.value)

  exportNewsList(

    endDate.value,

    exchangeArr.value,

    importance.value,

    searchVal.value,

    arr,

    pageSize.value,

    tableOrder.value,

    currentPage.value,

    securityId1.value,

    positive.value,

    tableProp.value,

    arr1,

    startDate.value

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

    if (res.type === 'application/json') {

      res.text().then((text: any) => {

        if (typeof text == 'string') {

          ElMessage({

            message: JSON.parse(text).message,

            type: 'error'

          })

        }

      })

      loading.value = false

      return

    }

    const blob = new Blob([res], {

      type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'

    })

    let name = ''

    nowTime.value = common.formatDate(new Date(), 'yyyy-MM-dd HH:mm:ss')

    let timeName = nowTime.value.replace(/:/g, '')

    name =

      props.id === 'stockNewsDynamicMonitoring'

        ? `表格${timeName}.xlsx`

        : `表格详情${timeName}.xlsx`

    const url = URL.createObjectURL(blob)

    const link = document.createElement('a')

    link.href = url

    link.download = name

    document.body.appendChild(link)

    link.click()

    document.body.removeChild(link)

    URL.revokeObjectURL(url)

    ElMessage({

      message: '导出成功',

      type: 'success'

    })

    loading.value = false

  })

}

onMounted(() => {

  getNewsList()

})

</script>

<style lang="less" scoped>

.newsDynamicMonitoring {

  margin: 24px 0;

  .top-box {

    margin-bottom: 20px;

  }

}

</style>

二、筛选框组件代码

<template>

  <div class="filter-box">

    <div class="top-box">

      <div class="left-box">

        <el-date-picker

          v-model="dateRange"

          value-format="YYYY-MM-DD HH:mm:ss"

          type="datetimerange"

          range-separator="至"

          start-placeholder="开始日期"

          end-placeholder="结束日期"

          :editable="false"

          :disabled-date="disabledDate"

          @change="changeDate"

          class="date-select"

          v-if="id !== 'todaySummary'"

        />

        <el-select

          v-model="importance"

          placeholder="重要性"

          suffix-icon="CaretBottom"

          clearable

          @change="changeImportance"

          class="select-box"

        >

          <el-option

            v-for="item in importOptions"

            :key="item.value"

            :label="item.label"

            :value="item.value"

          />

        </el-select>

        <el-select

          v-model="positive"

          placeholder="正负面"

          suffix-icon="CaretBottom"

          clearable

          @change="changePositive"

          class="select-box"

        >

          <el-option

            v-for="item in positiveOptions"

            :key="item.value"

            :label="item.label"

            :value="item.value"

          />

        </el-select>

        <el-input

          v-model.trim="searchVal"

          placeholder="请输入标题、综述等关键字"

          clearable

          @change="changeInputValue"

          class="ipt"

        />

        <div class="search-btn" @click="searchList">搜索</div>

      </div>

      <div class="right-box">

        <div class="total-text">

          共<span class="num">{{ dataTotal }}</span

          >条

        </div>

        <div

          class="upload-box"

          v-if="id !== 'todaySummary'"

          @click="exportDataList"

        >

          <span class="icon iconfont icon-biaoge"></span>

          <span class="upload-text">导出Excel</span>

        </div>

      </div>

    </div>

    <div class="bottom-box">

      <div class="label-options company-type">

        <div

          class="type-btn"

          @click="cancalTypeSelect"

          :class="companyTypeSelect1.length === 0 ? 'active' : ''"

        >

          不限

        </div>

        <div

          class="type-btn"

          v-for="(item, index) in sliceBtns(companyTypeOptions)"

          :key="index"

          :class="companyTypeSelect1.indexOf(item.id) != -1 ? 'active' : ''"

          @click="changeType(item)"

        >

          {{ item.name }} {{ item.count }}

        </div>

        <div

          class="more-btn"

          @click="openMoreDialog('0')"

          v-if="companyTypeOptions.length > 6"

        >

          <span class="icon iconfont icon-shaixuan"></span>

          <span>更多</span>

        </div>

      </div>

      <div class="label-options">

        <div

          class="type-btn"

          :class="sourceLabelSelect1.length === 0 ? 'active' : ''"

          @click="cancalSourceSelect"

        >

          不限

        </div>

        <div

          class="type-btn"

          v-for="(item1, index1) in sliceBtns(sourceLabelOptions)"

          :key="index1"

          :class="{ active: sourceLabelSelect1.indexOf(item1.id) != -1 }"

          @click="changeSourceLabel(item1)"

        >

          {{ item1.name }} {{ item1.count }}

        </div>

        <div

          class="more-btn"

          @click="openMoreDialog('1')"

          v-if="sourceLabelOptions.length > 6"

        >

          <span class="icon iconfont icon-shaixuan"></span>

          <span>更多</span>

        </div>

      </div>

      <div

        class="label-options stock-type"

        v-if="id === 'stockNewsDynamicMonitoring'"

      >

        <div

          class="type-btn"

          @click="cancalStockTypeSelect"

          :class="stockTypeSelect1.length === 0 ? 'active' : ''"

        >

          不限

        </div>

        <div

          class="type-btn"

          v-for="(item2, index2) in stockTypeOptions"

          :key="index2"

          :class="stockTypeSelect1.indexOf(item2.id) != -1 ? 'active' : ''"

          @click="changeStockType(item2)"

        >

          {{ item2.name }} {{ item2.count }}

        </div>

      </div>

    </div>

    <moreDialog

      :isShowDialog="isShowDialog"

      :btnOptions="btnOptions"

      :labelType="labelType"

      :btnSelectList="labelType === '0' ? companyTypeSelect : sourceLabelSelect"

      @getDialogState="getDialogStatus"

      @getSelectList="getOptionsList"

      @getSelectCancelList="getOptionsCancelList"

      v-if="isShowDialog"

    ></moreDialog>

  </div>

</template>

<script setup lang="ts">

import { ElMessage } from 'element-plus'

import moreDialog from './moreDialog.vue'

import { queryNewsLabelList } from '@/service/stockIndex/index'

const props = defineProps({

  dataTotal: {

    // 表格总条数

    type: String,

    default: ''

  },

  id: {

    // 板块id

    type: String,

    default: ''

  },

  securityId: {

    // 证券id

    type: [String, Number],

    required: true

  },

  exchanges: {

    // 交易所筛选

    type: Array,

    default: () => []

  }

})

const emit = defineEmits([

  'getDateRange',

  'getCompanyTypeSelect',

  'getSourceLabelSelect',

  'getStockTypeSelect',

  'getImportance',

  'getPositive',

  'getSearchVal',

  'searchList',

  'exportTableList'

])

// 日期范围值

const dateRange = ref<any>([])

const startDate = ref() // 开始时间

const endDate = ref() //结束时间

const importance = ref() // 重要性值

const positive = ref('') // 正负面值

const searchVal = ref('') // 搜索框值

const isShowDialog = ref(false) // 是否显示更多弹框

const labelType = ref<any>('0') // 标签类型值 0: 公司类型 1:来源标签

const companyTypeSelect = ref<any>([]) // 传给后端公司类型筛选值

const companyTypeSelect1 = ref<any>([]) // 备份公司类型筛选值用于页面显示

const sourceLabelSelect = ref<any>([]) // 传给后端来源标签筛选值

const sourceLabelSelect1 = ref<any>([]) // 备份来源标签筛选值用于页面显示

const stockTypeSelect = ref<any>([]) // 传给后端公司证券类型筛选值

const stockTypeSelect1 = ref<any>([]) // 备份公司证券类型筛选值用于页面显示

const btnOptions = ref<any>([]) // 弹框列表选项

const sessionArr = ref<any>([]) // 浏览器缓存变量

const companyTypeOptions = ref<any>([]) // 公司类型选项

const sourceLabelOptions = ref<any>([]) // 公司标签选项

const stockTypeOptions = ref<any>([]) // 公司证券类型选项

const exchangeArr = ref(props.exchanges) // 交易所筛选字段

const importOptions = ref([

  // 重要性选项

  {

    value: 4,

    label: '4星'

  },

  {

    value: 3,

    label: '3星'

  },

  {

    value: 2,

    label: '2星'

  },

  {

    value: 1,

    label: '1星'

  },

  {

    value: 0,

    label: '0星'

  }

])

const positiveOptions = ref([

  // 重要性值

  { value: '正面', label: '正面' },

  { value: '负面', label: '负面' },

  { value: '中性', label: '中性' }

])

const securityId1 = ref(props.securityId) // 页面新的证券id

// 监听参数重新赋值

watch(

  () => [props.securityId, props.exchanges],

  (newValue: any, oldValue: any) => {

    if (newValue[0] != oldValue[0]) {

      securityId1.value = newValue[0]

    }

    if (newValue[1] != oldValue[1]) {

      exchangeArr.value = newValue[1]

    }

    companyTypeSelect.value = []

    sourceLabelSelect.value = []

    stockTypeSelect.value = []

    companyTypeSelect1.value = []

    sourceLabelSelect1.value = []

    stockTypeSelect1.value = []

    btnOptions.value = []

    sessionArr.value = []

    dateRange.value = []

    startDate.value = null // 开始时间

    endDate.value = null //结束时间

    importance.value = null // 重要性值

    positive.value = '' // 正负面值

    searchVal.value = '' // 搜索框值

    sessionStorage.removeItem('selectedOptions')

    sessionStorage.removeItem('selectedSourceOptions')

    getNewsLabelList()

  },

  { deep: true }

)

// 查询筛选标签列表

const getNewsLabelList = () => {

  queryNewsLabelList(

    props.id === 'todaySummary' ? 1 : 0,

    endDate.value,

    importance.value,

    searchVal.value,

    securityId1.value,

    positive.value,

    startDate.value,

    exchangeArr.value

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

    if (res.code === 200) {

      if (res.data) {

        companyTypeOptions.value = res.data.labelQueryList

        sourceLabelOptions.value = res.data.sourcesQueryList

        stockTypeOptions.value = res.data.exchangeQueryList

      }

    } else {

      ElMessage({

        message: res.message,

        type: 'error'

      })

    }

  })

}

// 导出表格数据

const exportDataList = () => {

  emit('exportTableList')

}

defineExpose({

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

  getNewsLabelList

})

// 控制当前时间之后不能选

const disabledDate = (time: any) => {

  return time.getTime() > new Date().getTime()

}

// 改变时间

const changeDate = (value: any) => {

  if (value) {

    dateRange.value = toRaw(value)

    startDate.value = dateRange.value[0]

    endDate.value = dateRange.value[1]

    emit('getDateRange', startDate.value, endDate.value)

  } else {

    dateRange.value = toRaw(value)

    startDate.value = ''

    endDate.value = ''

    emit('getDateRange', startDate.value, endDate.value)

  }

}

// 改变重要性筛选

const changeImportance = (e: number) => {

  emit('getImportance', e)

}

// 改变正负面筛选

const changePositive = (e: string) => {

  emit('getPositive', e)

}

// 改变搜索框输入值

const changeInputValue = (e: string) => {

  emit('getSearchVal', e)

}

// 搜索

const searchList = () => {

  emit('searchList')

}

// 点击标签统一change方法

const changeLabel = (

  labelOptions: any,

  labelOptions1: any,

  optionsItem: any,

  type: string

) => {

  if (labelOptions1.indexOf(optionsItem) !== -1) {

    labelOptions1.splice(labelOptions1.indexOf(optionsItem), 1) //取消选中

  } else {

    labelOptions1.push(optionsItem) //选中添加到数组里

  }

  labelOptions = labelOptions1

  type === '0'

    ? sessionStorage.setItem('selectedOptions', labelOptions)

    : type === '1'

    ? sessionStorage.setItem('selectedSourceOptions', labelOptions)

    : ''

}

// 筛选公司类型

const changeType = (item: any) => {

  changeLabel(companyTypeSelect.value, companyTypeSelect1.value, item.id, '0')

  emit('getCompanyTypeSelect', toRaw(companyTypeSelect1.value))

}

// 筛选来源标签

const changeSourceLabel = (item: any) => {

  changeLabel(sourceLabelSelect.value, sourceLabelSelect1.value, item.id, '1')

  emit('getSourceLabelSelect', toRaw(sourceLabelSelect1.value))

}

// 筛选公司证券类型

const changeStockType = (item: any) => {

  changeLabel(stockTypeSelect.value, stockTypeSelect1.value, item.id, '2')

  console.log(toRaw(stockTypeSelect1.value))

  emit('getStockTypeSelect', toRaw(stockTypeSelect1.value))

}

// 点击不限取消公司类型后面列表选中

const cancalTypeSelect = () => {

  sessionStorage.removeItem('selectedOptions')

  companyTypeSelect.value = []

  companyTypeSelect1.value = []

  emit('getCompanyTypeSelect', toRaw(companyTypeSelect1.value))

}

// 点击不限取消来源标签后面列表选中

const cancalSourceSelect = () => {

  sessionStorage.removeItem('selectedSourceOptions')

  sourceLabelSelect.value = []

  sourceLabelSelect1.value = []

  emit('getSourceLabelSelect', toRaw(sourceLabelSelect1.value))

}

// 点击不限取消公司证券类型后面列表选中

const cancalStockTypeSelect = () => {

  stockTypeSelect.value = []

  stockTypeSelect1.value = []

  emit('getStockTypeSelect', toRaw(stockTypeSelect1.value))

}

// 点击打开更多弹框(type 为打开弹框类型)

const openMoreDialog = (type: string) => {

  isShowDialog.value = true

  labelType.value = type

  // 公司类型打开更多

  if (labelType.value === '0') {

    btnOptions.value = companyTypeOptions.value

    sessionArr.value = sessionStorage.getItem('selectedOptions')

    if (sessionArr.value && sessionArr.value.length > 0) {

      companyTypeSelect.value = sessionArr.value.split(',').map(String)

    } else {

      companyTypeSelect.value = []

    }

    companyTypeSelect1.value = companyTypeSelect.value

  } else {

    // 来源标签打开更多

    btnOptions.value = sourceLabelOptions.value

    sessionArr.value = sessionStorage.getItem('selectedSourceOptions')

    if (sessionArr.value && sessionArr.value.length > 0) {

      sourceLabelSelect.value = sessionArr.value.split(',').map(String)

    } else {

      sourceLabelSelect.value = []

    }

    sourceLabelSelect1.value = sourceLabelSelect.value

  }

}

// 获取弹框打开关闭状态数据

const getDialogStatus = (status: boolean) => {

  isShowDialog.value = status

}

// 点击取消获取弹框选中值数据

const getOptionsCancelList = (data: any) => {

  if (data[1] === '0') {

    sessionArr.value = sessionStorage.getItem('selectedOptions')

    if (sessionArr.value && sessionArr.value.length > 0) {

      companyTypeSelect.value = sessionArr.value.split(',').map(String)

      companyTypeSelect1.value = companyTypeSelect.value

    } else {

      companyTypeSelect1.value = []

      companyTypeSelect.value = data[0]

    }

    emit('getCompanyTypeSelect', toRaw(companyTypeSelect.value))

  } else {

    sessionArr.value = sessionStorage.getItem('selectedSourceOptions')

    if (sessionArr.value && sessionArr.value.length > 0) {

      sourceLabelSelect.value = sessionArr.value.split(',').map(String)

      sourceLabelSelect1.value = sourceLabelSelect.value

    } else {

      sourceLabelSelect1.value = []

      sourceLabelSelect.value = data[0]

    }

    emit('getSourceLabelSelect', toRaw(sourceLabelSelect.value))

  }

}

// 点击确定获取弹框选中值数据

const getOptionsList = (data: any) => {

  if (data[1] === '0') {

    sessionArr.value = sessionStorage.getItem('selectedOptions')

    if (sessionArr.value && sessionArr.value.length > 0) {

      companyTypeSelect.value = sessionArr.value.split(',').map(String)

      companyTypeSelect1.value = companyTypeSelect.value

    } else {

      companyTypeSelect.value = data[0]

    }

    sessionStorage.setItem('selectedOptions', companyTypeSelect1.value)

    emit('getCompanyTypeSelect', toRaw(companyTypeSelect1.value))

  } else {

    sessionArr.value = sessionStorage.getItem('selectedSourceOptions')

    if (sessionArr.value && sessionArr.value.length > 0) {

      sourceLabelSelect.value = sessionArr.value.split(',').map(String)

      sourceLabelSelect1.value = sourceLabelSelect.value

    } else {

      sourceLabelSelect.value = data[0]

    }

    sessionStorage.setItem('selectedSourceOptions', sourceLabelSelect1.value)

    emit('getSourceLabelSelect', toRaw(sourceLabelSelect1.value))

  }

}

// 按钮超过6个截取不显示后面的

const sliceBtns = (list: any) => {

  let ln = 0

  let index = list.length

  for (let i = 0; i < list.length; i++) {

    let value = ln + list[i].name.length * 6 + 13

    if (value < 300 - 50) {

      ln = value

    } else {

      index = i

      break

    }

  }

  return list.slice(0, index)

}

onMounted(() => {

  getNewsLabelList()

  sessionStorage.removeItem('selectedOptions')

  sessionStorage.removeItem('selectedSourceOptions')

})

</script>

<style>

.el-select-dropdown__item {

  font-size: 12px !important;

}

.el-date-editor .el-range-input {

  font-weight: 400;

  font-size: 12px !important;

  cursor: pointer;

}

.el-date-editor .el-range-separator {

  font-weight: 400;

  font-size: 12px;

}

</style>

<style lang="less" scoped>

.filter-box {

  border: 1px solid #ebeef8;

  .top-box {

    padding: 0 17px;

    height: 50px;

    background: #f4f7fc;

    border-bottom: 1px solid #ebeef8;

    display: flex;

    align-items: center;

    justify-content: space-between;

    .left-box {

      display: flex;

      align-items: center;

      :deep(.el-date-editor) {

        width: 350px;

        height: 32px;

        border-radius: 4px;

        font-size: 12px !important;

      }

      .select-box {

        width: 90px;

        margin-left: 14px;

        font-size: 12px;

        :deep(.el-input__inner) {

          font-size: 12px;

        }

        :deep(.el-select__caret.el-icon) {

          font-size: 12px;

        }

      }

      .ipt {

        width: 280px;

        margin-left: 14px;

        font-size: 12px;

      }

      .search-btn {

        width: 60px;

        display: flex;

        justify-content: center;

        align-items: center;

        height: 32px;

        background-color: #3a5bb7;

        border-radius: 4px;

        color: #ffffff;

        margin-left: 14px;

        font-size: 12px;

        cursor: pointer;

      }

    }

    .right-box {

      display: flex;

      align-items: center;

      .total-text {

        font-size: 12px;

        color: #666666;

        .num {

          color: #3a5bb7;

        }

      }

      .upload-box {

        margin-left: 30px;

        display: flex;

        align-items: center;

        cursor: pointer;

        .icon-biaoge {

          font-size: 16px;

          color: #3a5bb7;

        }

        .upload-text {

          font-size: 12px;

          color: #3a5bb7;

          margin-left: 6px;

        }

      }

    }

  }

  .bottom-box {

    padding: 14px 17px 21px;

    .company-type {

      margin-bottom: 10px;

    }

    .stock-type {

      margin-top: 10px;

    }

    .label-options {

      display: flex;

      .unlimited-btn {

        display: flex;

        align-items: center;

        justify-content: center;

        height: 24px;

        border-radius: 2px;

        border: 1px solid #3a5bb7;

        color: #3a5bb7;

        font-size: 12px;

        padding: 0 8px;

      }

      .type-btn,

      .more-btn {

        display: flex;

        align-items: center;

        justify-content: center;

        padding: 0 10px;

        height: 24px;

        border-radius: 2px;

        border: 1px solid #e6e8f0;

        font-size: 12px;

        color: #666666;

        cursor: pointer;

        margin-left: 14px;

        &:first-child {

          margin-left: 0;

        }

        .icon-shaixuan {

          margin-right: 6px;

        }

      }

      .active {

        border: 1px solid #3a5bb7;

        color: #3a5bb7;

      }

    }

  }

}

</style>

三、表格组件代码

<template>

  <div class="table-list">

    <el-table

      :data="tableData"

      :header-cell-style="headerCellStyle"

      :span-method="arraySpanMethod"

      max-height="560"

      row-key="id"

      :expand-row-keys="expandArr"

      @setScrollTop="setScrollTop"

      @sort-change="tableSort"

      class="my-table"

    >

      <el-table-column

        label="重要性"

        width="130"

        prop="importance"

        sortable="custom"

        align="center"

      >

        <template #default="scope">

          <div class="star-icon-box">

            <i

              v-for="(item, i) in 4"

              :key="'icon' + i"

              :style="{

                color: scope.row.importance > i ? '#F37D36' : '#fdeadf'

              }"

              class="icon iconfont icon-zhongyaoxing"

            />

          </div>

        </template>

      </el-table-column>

      <el-table-column

        label="发布时间"

        width="160"

        prop="releaseTime"

        sortable="custom"

        align="center"

      />

      <el-table-column align="center" type="expand" width="1">

        <!-- 展开列表 -->

        <template #default="props">

          <div class="correlation-list" v-loading="loading">

            <div class="list-box">

              <div

                class="list"

                v-for="(correlationItem, correlationIndex) in props.row

                  .deputyNews"

                :key="correlationIndex"

              >

                <div class="list-top">

                  <div class="time">{{ correlationItem.releaseTime }}</div>

                  <div class="title-box">

                    <el-popover

                      placement="top-start"

                      :width="500"

                      trigger="hover"

                      :content="correlationItem.title"

                      popper-class="switch-popper"

                      v-if="showTooltip"

                    >

                      <template #reference>

                        <span

                          class="name"

                          @mouseenter="isShowTooltip($event)"

                          @click="toCompanyPage(correlationItem)"

                          >{{ correlationItem.title }}</span

                        >

                      </template>

                    </el-popover>

                    <span

                      class="name"

                      @mouseenter="isShowTooltip($event)"

                      @click="toCompanyPage(correlationItem)"

                      v-else

                      >{{ correlationItem.title }}</span

                    >

                    <div class="label">

                      {{ correlationItem.source }}

                    </div>

                  </div>

                </div>

                <div class="line"></div>

              </div>

            </div>

          </div>

        </template>

      </el-table-column>

      <el-table-column label="内容" prop="content" align="left">

        <template #default="scope">

          <div class="top-box">

            <div class="left-box">

              <el-popover

                placement="top-start"

                :width="500"

                trigger="hover"

                :content="scope.row.title"

                popper-class="switch-popper"

                v-if="showTooltip"

              >

                <template #reference>

                  <span

                    class="company-name"

                    @mouseenter="isShowTooltip($event)"

                    @click="toCompanyPage(scope.row)"

                    >{{ scope.row.title }}</span

                  >

                </template>

              </el-popover>

              <span

                class="company-name"

                @mouseenter="isShowTooltip($event)"

                @click="toCompanyPage(scope.row)"

                v-else

                >{{ scope.row.title }}</span

              >

              <!-- 标签类型 0:负面 1:正面 -->

              <div

                class="label"

                :class="

                  scope.row.sentiment === '负面'

                    ? 'front-label'

                    : scope.row.sentiment === '正面'

                    ? 'negative-label'

                    : 'common-label'

                "

              >

                {{ scope.row.sentiment }}

              </div>

            </div>

            <div class="right-box">

              <div class="heat-label">热度</div>

              <div class="heat-icon-box">

                <i

                  v-for="(item, i) in 4"

                  :key="'icon' + i"

                  :style="{

                    color: scope.row.heat > i ? '#F37D36' : '#fdeadf'

                  }"

                  class="icon iconfont icon-redu"

                />

              </div>

            </div>

          </div>

          <div class="center-box">

            <div class="sorce-label">{{ scope.row.source }}</div>

            <div

              class="label-box"

              v-if="scope.row.labels && scope.row.labels.length > 0"

            >

              <span class="icon iconfont icon-canyuhuati"></span>

              <div

                class="topic-label"

                v-for="item in sliceBtns(scope.row.labels)"

                :key="item"

              >

                #{{ item }}

              </div>

            </div>

          </div>

          <textUnpack

            :textId="scope.row.id"

            :id="id"

            :newsDetailState="scope.row.collapse"

            :textOver="scope.row.showOpenButton"

          >

            <div

              v-html="scope.row.overview"

              class="info"

              :style="{

                marginBottom:

                  scope.row.deputyNews && scope.row.deputyNews.length > 0

                    ? '0'

                    : '12px'

              }"

            ></div>

          </textUnpack>

          <div

            class="correlation-btn"

            @click="openCorrelationInfo(scope.row)"

            v-if="scope.row.deputyNews && scope.row.deputyNews.length > 0"

          >

            <span>相关列表</span>

            <span

              class="icon iconfont icon-chuizhidaohang"

              v-if="expandArr.indexOf(scope.row.id) < 0"

            ></span>

            <span class="icon iconfont icon-shangla" v-else></span>

          </div>

        </template>

      </el-table-column>

      <el-table-column align="right">

        <template #header>

          <div class="right-box">

            <el-popover

              placement="top-start"

              :width="284"

              trigger="hover"

              popper-class="switch-popper"

            >

              <template #reference>

                <el-switch

                  v-model="switchStatus"

                  inactive-text="AI学习模式"

                  disabled

                />

              </template>

              <div class="popover-title">该功能开发中,敬请期待~</div>

              <div class="popover-detail">

                AI学习模式可自定义选择重要性、标签、正负面,

                提交后交由机器学习,更加符合用户使用习惯。

              </div>

            </el-popover>

            <div class="line"></div>

            <div class="all-open" @click="openNewsDetail">

              <span class="all-text" v-if="!newsDetailState">全部展开</span>

              <span class="all-text" v-else>全部收起</span>

              <div class="icon-box" v-if="!newsDetailState">

                <span class="icon iconfont icon-xiala"></span>

              </div>

              <div class="icon-box" v-else>

                <span class="icon iconfont icon-shang"></span>

              </div>

            </div>

          </div>

        </template>

      </el-table-column>

    </el-table>

    <!-- 表格分页组件 -->

    <paging

      :currentPage="currentPage"

      :pageSize="pageSize"

      :total="total"

      :background="true"

      @handleCurrentChange="changeCurrentPage"

    ></paging>

  </div>

</template>

<script setup lang="ts">

import textUnpack from './textUnpack.vue'

import paging from './paging.vue'

const props = defineProps({

  id: {

    // 板块id

    type: String,

    default: ''

  },

  currentPage: {

    // 当前页

    type: Number,

    default: 1

  },

  pageSize: {

    // 一页展示条数

    type: Number,

    default: 10

  },

  total: {

    // 总条数

    type: Number,

    default: 0

  },

  tableData: {

    // 表格数据

    type: Array,

    default: () => []

  }

})

const emit = defineEmits([

  'searchTableList',

  'tableSortByProp',

  'tableScrollTop'

])

// 开关状态

const switchStatus = ref(false)

const loading = ref(false)

// 展开收起内容状态

const newsDetailState = ref(false)

const showTooltip = ref(false) // 是否展示浮框

// 存放页面中被展开行的数据 对应的数组就是 expand-row-keys

const expandArr = ref<any>([])

interface SpanMethodProps {

  columnIndex: number

}

// 合并行单元格

const arraySpanMethod = ({ columnIndex }: SpanMethodProps) => {

  if (columnIndex === 3) {

    return [1, 3]

  }

}

// 表格排序

const tableSort = (dataObj: any) => {

  emit('tableSortByProp', [dataObj.prop, dataObj.order])

}

// 表格滚动条置顶

const setScrollTop = (top: number) => {

  emit('tableScrollTop', top)

}

// 标题与相关列表跳转

const toCompanyPage = (row: any) => {

  window.open(toRaw(row.url))

}

// 设置表头样式

const headerCellStyle = ({ columnIndex }: SpanMethodProps) => {

  if (columnIndex === 3) {

    return {

      background: '#f4f7fc',

      color: '#333333',

      fontSize: '13px',

      borderRight: 'none',

      padding: '1px 0'

    }

  } else {

    return {

      background: '#f4f7fc',

      color: '#333333',

      fontSize: '13px',

      borderRight: '1px solid #ebeef8',

      padding: '1px 0'

    }

  }

}

// 点击按钮展开列表

const openCorrelationInfo = (data: any) => {

  let rowData = toRaw(data)

  if (expandArr.value.indexOf(rowData.id) < 0) {

    expandArr.value.push(rowData.id) // 展开

  } else {

    expandArr.value.splice(expandArr.value.indexOf(rowData.id), 1) // 收起

  }

}

// 按钮超过10个截取不显示后面的

const sliceBtns = (list: any) => {

  let ln = 0

  let index = list.length

  for (let i = 0; i < list.length; i++) {

    let value = ln + list[i].length * 10 + 13

    if (value < 900 - 50) {

      ln = value

    } else {

      index = i

      break

    }

  }

  return list.slice(0, index)

}

// 判断是否展示文本浮框

const isShowTooltip = (event: any) => {

  const ev = event.target

  const evHeight = ev.scrollHeight

  const contentHeight = ev.clientHeight

  if (evHeight > contentHeight) {

    // 实际宽度 > 可视宽度  文字溢出

    showTooltip.value = true

  } else {

    // 否则为不溢出

    showTooltip.value = false

  }

}

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

const changeCurrentPage = (current: number) => {

  emit('searchTableList', current)

}

// 点击全部展开或收起内容

const openNewsDetail = () => {

  newsDetailState.value = !newsDetailState.value

  props.tableData.forEach((i: any) => {

    i.collapse = newsDetailState.value

  })

}

</script>

<style lang="less">

.switch-popper.el-popper {

  background: #ffffff;

  box-shadow: 0px 1px 14px 0px rgba(7, 12, 20, 0.1);

  border-radius: 4px;

  padding: 13px 15px;

  font-weight: 400;

  font-size: 14px;

  color: #666666;

  line-height: 20px;

  .popover-title {

    font-weight: normal;

    font-size: 12px;

    color: #3a5bb7;

  }

  .popover-detail {

    font-weight: normal;

    font-size: 12px;

    color: #666666;

  }

}

/* 当表格行被hover时,修改该行内展开收起按钮的样式 */

.my-table {

  border-right: 1px solid #ebeef8;

  border-left: 1px solid #ebeef8;

  tr:hover {

    .btn {

      background-color: #f5f7fa;

    }

  }

}

.el-table:not(.el-table--border) .el-table__cell {

  &:nth-child(3) {

    border-right: none !important;

  }

}

// 修改表格展开背景颜色

.el-table__expanded-cell {

  background: #f9fbfd;

}

// 去掉表格展开符

.el-table__expand-icon {

  display: none;

}

.el-backtop {

  right: 30px !important;

  bottom: 50px !important;

}

</style>

<style lang="less" scoped>

.table-list {

  :deep(.el-switch__label) {

    font-weight: 400;

    font-size: 13px;

    color: #a9b6de;

  }

  .content-title {

    font-weight: 600;

    font-size: 13px;

    color: #333333;

    float: left;

    line-height: 32px;

    margin-left: 12px;

  }

  .right-box {

    display: flex;

    align-items: center;

    float: right;

    .line {

      width: 1px;

      height: 32px;

      background: #ebeef8;

      margin-left: 24px;

    }

    .all-open {

      display: flex;

      align-items: center;

      margin-left: 17px;

      cursor: pointer;

      .all-text {

        font-weight: 400;

        font-size: 12px;

        color: #3a5bb7;

      }

      .icon-box {

        display: flex;

        justify-content: center;

        align-items: center;

        width: 16px;

        height: 14px;

        background: #e8ecf7;

        border-radius: 2px;

        margin: 0 5px;

        .icon-xiala,

        .icon-shang {

          font-size: 12px;

          color: #3a5bb7;

        }

      }

    }

  }

  .top-box {

    display: flex;

    align-items: center;

    justify-content: space-between;

    margin: 8px 10px 8px 12px;

    .left-box {

      position: relative;

      margin-right: 10px;

      .company-name {

        font-weight: 600;

        font-size: 16px;

        color: #333333;

        cursor: pointer;

        display: -webkit-box;

        -webkit-box-orient: vertical;

        -webkit-line-clamp: 1;

        overflow: hidden;

        text-overflow: ellipsis;

        margin-right: 43px;

        &:hover {

          color: #3a5bb7;

        }

      }

      .label {

        position: absolute;

        bottom: 2px;

        right: 0;

        background-color: #ffffff;

        width: 36px;

        height: 20px;

        border-radius: 2px;

        text-align: center;

        line-height: 20px;

        font-weight: 400;

        font-size: 12px;

        display: inline-block;

      }

      .front-label {

        border: 1px solid #ff0000;

        color: #ff0000;

      }

      .negative-label {

        border: 1px solid #009900;

        color: #009900;

      }

      .common-label {

        border: 1px solid #15a1f1;

        color: #15a1f1;

      }

    }

    .right-box {

      display: flex;

      .heat-label {

        width: 40px;

        height: 19px;

        background: #f37d36;

        border-radius: 10px;

        font-weight: 400;

        font-size: 12px;

        color: #ffffff;

        display: flex;

        align-items: center;

        justify-content: center;

        margin-right: 10px;

      }

    }

  }

  .center-box {

    margin: 0 10px 8px 12px;

    display: flex;

    align-items: center;

    .sorce-label {

      font-weight: 400;

      font-size: 12px;

      color: #3a5bb7;

      height: 22px;

      border-radius: 2px;

      border: 1px solid #b0bde2;

      padding: 0 9px;

      display: flex;

      align-items: center;

      justify-content: center;

    }

    .label-box {

      display: flex;

      flex-wrap: wrap;

      line-height: 22px;

      .icon-canyuhuati {

        font-size: 14px;

        color: #3a5bb7;

        margin-left: 10px;

        margin-right: 7px;

      }

      .topic-label {

        font-weight: 400;

        font-size: 13px;

        color: #3a5bb7;

        margin-right: 5px;

      }

    }

  }

  .info {

    margin: 0 12px;

    width: 96%;

  }

  .correlation-btn {

    width: 86px;

    height: 24px;

    border-radius: 12px;

    border: 1px solid #bbc8eb;

    font-weight: 400;

    font-size: 12px;

    color: #3a5bb7;

    display: flex;

    align-items: center;

    justify-content: center;

    position: relative;

    margin-top: 15px;

    left: 50%;

    transform: translateX(-50%);

    margin-bottom: 2px;

    cursor: pointer;

    .icon-chuizhidaohang,

    .icon-shangla {

      margin-left: 6px;

      font-size: 12px;

    }

  }

  .correlation-list {

    padding: 16px 0 0 2px;

    .list-box {

      display: flex;

      flex-direction: column;

      .list {

        display: flex;

        flex-direction: column;

        margin-left: 153px;

        .list-top {

          display: flex;

          align-items: center;

          .time {

            font-weight: 400;

            font-size: 13px;

            color: #666666;

            flex-shrink: 0;

            margin-right: 52px;

          }

          .title-box {

            width: 100%;

            display: flex;

            align-items: center;

            float: left;

            margin-right: 24px;

            .name {

              font-weight: 400;

              font-size: 13px;

              color: #333333;

              cursor: pointer;

              display: -webkit-box;

              -webkit-box-orient: vertical;

              -webkit-line-clamp: 1;

              overflow: hidden;

              text-overflow: ellipsis;

              float: left;

              &:hover {

                color: #3a5bb7;

              }

            }

            .label {

              float: right;

              margin-left: 10px;

              display: flex;

              flex-shrink: 0;

              align-items: center;

              justify-content: center;

              padding: 0 8px;

              background-color: #ffffff;

              height: 20px;

              border-radius: 2px;

              border: 1px solid #b0bde2;

              font-weight: 400;

              font-size: 12px;

              color: #3a5bb7;

            }

          }

        }

        .line {

          margin: 2px 36px;

          width: 1px;

          height: 18px;

          background: #d7dae3;

        }

        &:last-child {

          margin-bottom: 16px;

          .line {

            display: none;

          }

        }

      }

    }

  }

}

</style>

四、文本展开收起组件代码

<template>

  <section

    class="room_content"

    :style="{ marginBottom: infoTitle === '风险重要列表' ? '' : '12px' }"

  >

    <!-- 内容区域(这里的id是为了区分多个该组件共存问题,否则会冲突) -->

    <div

      :id="`id_${textId}`"

      :class="state.textOver1 && !state.foldBtn ? 'showEllipsis' : ''"

      :style="{

        '-webkit-line-clamp': textId === '1' ? 4 : 2

      }"

    >

      <slot></slot>

    </div>

    <!-- 展开折叠按钮 -->

    <span

      v-if="state.textOver1"

      class="btnWrap"

      :style="{

        right:

          textId === '1'

            ? '0'

            : infoTitle === '风险重要列表'

            ? '-15px'

            : id === 'commentSummary'

            ? '10px'

            : id === 'noticeDynamicMonitoring'

            ? '3px'

            : '6px'

      }"

      @click="uclick()"

    >

      <div class="btn">{{ state.foldBtn ? '收起' : '展开' }}</div>

    </span>

  </section>

</template>

<script setup lang="ts">

const props = defineProps({

  // 区分textId(必填,否则报错)

  textId: { type: String, required: true },

  infoTitle: {

    // 区分板块内容

    type: String,

    default: ''

  },

  id: {

    // 板块id

    type: String,

    default: ''

  },

  newsDetailState: {

    // 内容展开收起状态

    type: Boolean,

    default: false

  },

  textOver: {

    // 是否显示展开收起按钮

    type: Boolean,

    default: false

  }

})

const state = reactive({

  textOver1: false, // 是否显示展开收起按钮

  foldBtn: props.newsDetailState // 当前展开或收起状态

})

watch(

  () => props.newsDetailState,

  (newValue: any) => {

    state.foldBtn = newValue

  },

  { deep: true }

)

const getDomHeight = () => {

  // 获取内容元素

  const domRef = document.getElementById(`id_${props.textId}`)

  if (domRef) {

    // 计算并改变是否展开和折叠

    const height = window.getComputedStyle(domRef).height.replace('px', '')

    if (props.textId === '1') {

      if (+height > 96) {

        // 4行的高度

        state.textOver1 = true

      } else {

        state.textOver1 = false

      }

    } else {

      if (+height > 46) {

        // 2行的高度

        state.textOver1 = true

      } else {

        state.textOver1 = false

      }

    }

  }

}

// 处理操作

onMounted(async () => {

  nextTick(() => {

    getDomHeight()

  })

})

// 点击展开折叠按钮

const uclick = () => {

  // 直接取反

  state.foldBtn = !state.foldBtn

}

</script>

<style lang="less" scoped>

/* 根节点 */

.room_content {

  position: relative;

}

/* 文本溢出 */

.showEllipsis {

  overflow: hidden;

  text-overflow: ellipsis;

  display: -webkit-box;

  -webkit-line-clamp: 4;

  line-clamp: 4;

  -webkit-box-orient: vertical;

}

/* 展开/收起按钮位置及样式 */

.btnWrap {

  position: absolute;

  cursor: pointer;

  color: #3a5bb7;

  font-size: 14px;

  font-weight: 400;

  bottom: 0;

  z-index: 9999;

  .btn {

    background-color: #fff;

  }

}

</style>

五、表格分页组件代码

<template>

  <div class="paging-box">

    <el-pagination

      :current-page="currentPage"

      :page-size="pageSize"

      :background="background"

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

      :total="total"

      @current-change="handleCurrentChange"

      class="page-pagination"

    />

  </div>

</template>

<script setup lang="ts">

const props = defineProps({

  total: { type: Number, dafault: 0 }, // 总条数

  currentPage: { type: Number, dafault: 1 }, // 当前页

  pageSize: { type: Number, dafault: 10 }, // 一页展示条数

  background: { type: Boolean, dafault: false } // 分页按钮背景色

})

const emit = defineEmits(['handleCurrentChange'])

// 点击改变当前页

const handleCurrentChange = (val: number) => {

  emit('handleCurrentChange', val)

}

</script>

<style>

.el-pagination.is-background .btn-next,

.el-pagination.is-background .btn-prev,

.el-pagination.is-background .el-pager li {

  margin: 0 10px;

  background-color: #f4f7fc;

  min-width: 30px;

  height: 30px;

  border-radius: 2px;

  font-size: 12px;

  color: #666666;

  font-weight: 500;

}

.el-pagination.is-background .el-pager li:not(.is-disabled).is-active {

  background-color: #3a5bb7;

  color: #ffffff;

  font-weight: 500;

  font-size: 12px;

}

.el-pagination__total {

  font-weight: 500;

  font-size: 12px !important;

  color: #666666;

}

.el-pagination__jump {

  font-weight: 500;

  font-size: 12px !important;

  color: #666666;

}

</style>

<style lang="less" scoped>

.paging-box {

  display: flex;

  justify-content: center;

  .page-pagination {

    margin-top: 24px;

  }

}

</style>

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com