element-plus table封装表格筛选,选择框是否可选,溢出两行鼠标移入显示 tootip等



bash 复制代码
<template>
  <div class="jw-tables" @click="handleClickOutside">
    <div class="jw-title">
      <span>{{ title || '-' }}</span>
    </div>
    <div :style="{maxHeight: boxHeight+'px'}">
      <el-table ref="jwTables" tooltip-effect="light" :data="tableData" :max-height="boxHeight" @row-click="rowClick"
              @selection-change="handleSelectionChange" style="width: 100%">
        <el-table-column :fixed="true" type="selection" :selectable="selectable" width="55" v-if="showSelection"/>
        <el-table-column :fixed="true" type="index" v-if="showIndex" width="100" label="序号" align="center" :index="indexMethod"/>
        <el-table-column v-for="(item,index) in tableHeader" :key="index"
                        :label="item.label" :align="item.align==undefined?align:item.align" :fixed="item.fixed"
                        :width="item.width">
          <template #header v-if="item.isFilter">
            <el-popover placement="bottom" trigger="click" :width="'fit-content'">
              <template #default>
                <div>
                  <remoteSelect @handleChange="remoteSelectChange" 
                    v-if="item.formType === 'remoteSelect'" :taskId="taskId" :values="data.filterValue[item.prop]" 
                    :receiveId="receiveId" :submitLogId="submitLogId" :url="item.url" :propName="item.prop"
                    style="margin-top: 10px;width: 150px;"
                  />
                  <el-select v-else-if="item.formType === 'multipleSelect'" filterable
                    :reserve-keyword="false" v-model="data.filterValue[item.prop]"
                    @change="searchFilter(item.prop, data.filterValue[item.prop])" placeholder="请选择" style="margin-top: 10px;width: 150px;">
                    <el-option v-for="mult in item.selectList" :key="mult.value" :label="mult.text" :value="mult.value" />
                  </el-select>
                  <el-select v-else-if="item.formType === 'select'" filterable
                    :reserve-keyword="false" v-model="data.filterValue[item.prop]" placeholder="请选择"
                    @change="searchFilter(item.prop, data.filterValue[item.prop])" clearable style="margin-top: 10px; width: 150px;">
                    <el-option v-for="selVal in item.selectList" :key="selVal" :value="selVal.value"
                      :label="selVal.text" />
                  </el-select>
                  <el-select v-else-if="item.formType === 'select_group'" filterable
                             :reserve-keyword="false" v-model="data.filterValue[item.prop]" placeholder="请选择"
                             @change="searchFilter(item.prop, data.filterValue[item.prop])" clearable style="margin-top: 10px; width: 150px;">
                    <el-option-group v-for="selVal in item.selectList" :key="selVal.value"
                               :label="selVal.text" >
                      <el-option v-for="(op) in selVal.option"
                        :key="op.value"
                        :label="op.text"
                        :value="op.value"
                      />
                    </el-option-group>
                  </el-select>
                  <el-input v-else v-model="data.filterValue[item.prop]" placeholder="请输入关键字" class="marginT5" />
                  <div class="marginT10" style="text-align: right;">
                    <el-button type="info" link @click="cancelFilter(item.prop)">重置</el-button>
                    <el-button type="primary" link @click="searchFilter(item.prop, data.filterValue[item.prop])">筛选
                    </el-button>
                  </div>
                </div>
              </template>
              <template #reference>
                <div class="flexCenter">
                  <span>{{ item.label }}</span>
                  <el-icon style="cursor: pointer; margin-left: 6px;" size="20"
                          :style="{ color: data.filterValue[item.prop] ? '#ffffff !important' : '#ffffff80 !important' }">
                    <Filter/>
                  </el-icon>
                </div>
              </template>
            </el-popover>
          </template>
          <el-table-column v-if="item.children != ''"
                          v-for="(item1,index1) in item.children" :key="index1" :label="item1.label"
                          :align="item1.align==undefined?align:item1.align" :width="item1.width">
            <template #header v-if="item1.isFilter">
              <el-popover placement="bottom" trigger="click" :width="'fit-content'">
                <template #default>
                  <div>
                    <remoteSelect @handleChange="remoteSelectChange" 
                      v-if="item1.formType === 'remoteSelect'" :taskId="taskId" :values="data.filterValue[item.prop]" 
                      :receiveId="receiveId" :submitLogId="submitLogId" :url="item1.url" :propName="item1.prop"
                      style="margin-top: 10px;width: 150px;"
                    />
                    <el-select v-else-if="item1.formType === 'multipleSelect'" filterable
                      :reserve-keyword="false" v-model="data.filterValue[item1.prop]"
                      @change="searchFilter(item1.prop, data.filterValue[item1.prop])" placeholder="请选择" style="margin-top: 10px;width: 150px;">
                      <el-option v-for="mult in item1.selectList" :key="mult.value" :label="mult.text" :value="mult.value" />
                    </el-select>
                    <el-select v-else-if="item1.formType === 'select'" filterable :multiple="item1.multiple || false"
                      :reserve-keyword="false" v-model="data.filterValue[item1.prop]" placeholder="请选择"
                      @change="searchFilter(item1.prop, data.filterValue[item1.prop])" clearable style="margin-top: 10px; width: 150px;">
                      <el-option v-for="selVal in item1.selectList" :key="selVal" :value="selVal.value"
                        :label="selVal.text" />
                    </el-select>
                    <el-select v-else-if="item.formType === 'select_group'" filterable
                               :reserve-keyword="false" v-model="data.filterValue[item1.prop]" placeholder="请选择"
                               @change="searchFilter(item1.prop, data.filterValue[item1.prop])" clearable style="margin-top: 10px; width: 150px;">
                      <el-option-group v-for="selVal in item.selectList" :key="selVal"
                                       :label="selVal.text" >
                        <el-option v-for="(op,index) in item.option"
                                   :key="index"
                                   :label="op.text"
                                   :value="op.value"
                        />
                      </el-option-group>
                    </el-select>
                    <el-input v-else v-model="data.filterValue[item1.prop]" placeholder="请输入关键字" class="marginT5" />
                    <div class="marginT10" style="text-align: right;">
                      <el-button type="info" link @click="cancelFilter(item1.prop)">重置</el-button>
                      <el-button type="primary" link @click="searchFilter(item1.prop, data.filterValue[item1.prop])">
                        筛选
                      </el-button>
                    </div>
                  </div>
                </template>
                <template #reference>
                  <div class="flexCenter">
                    <span>{{ item1.label }}</span>
                    <el-icon style="cursor: pointer; margin-left: 6px;" size="20"
                            :style="{ color: data.filterValue[item1.prop] ? '#ffffff !important' : '#ffffff80 !important' }">
                      <Filter/>
                    </el-icon>
                  </div>
                </template>
              </el-popover>
            </template>
            <el-table-column v-if="item1.children != ''"
                            v-for="(item2,index2) in item1.children" :key="index2" :label="item2.label"
                            :align="item2.align==undefined?align:item2.align" :width="item2.width">
              <template #default="scope">
                <el-tooltip placement="top" :disabled="!isShowTooltip" effect="light">
                  <template #content>
                      <div>{{ item2.formatter==undefined?scope.row[item2.prop]:item2.formatter(scope.row,scope.row[item2.prop]) }}</div>
                  </template>

                  <div class="ellipsis-2" :class="item2.align==undefined?'text-center':`text-${item2.align}`" @mouseenter="visibilityChange($event)">{{ item2.formatter==undefined?scope.row[item2.prop]:item2.formatter(scope.row,scope.row[item2.prop]) }}</div>
                </el-tooltip>
              </template>
            </el-table-column>
            <template v-else #default="scope" v-if="item.children==undefined || item1.children == ''">
              <el-tooltip placement="top" :disabled="!isShowTooltip" effect="light">
                <template #content>
                    <div>{{ item1.formatter==undefined?scope.row[item1.prop]:item1.formatter(scope.row,scope.row[item1.prop]) }}</div>
                </template>

                <div class="ellipsis-2" :class="item1.align==undefined?'text-center':`text-${item1.align}`" @mouseenter="visibilityChange($event)">{{ item1.formatter==undefined?scope.row[item1.prop]:item1.formatter(scope.row,scope.row[item2.prop]) }}</div>
              </el-tooltip>
            </template>
          </el-table-column>
          <template #default="scope" v-if="item.children==undefined || item.children==null || item.children == ''">
            <el-tooltip placement="top" :disabled="!isShowTooltip" effect="light">
              <template #content>
                  <div>{{ item.formatter==undefined?scope.row[item.prop]:item1.formatter(scope.row,scope.row[item.prop]) }}</div>
              </template>

              <div class="ellipsis-2" :class="item.align==undefined?'text-center':`text-${item.align}`" @mouseenter="visibilityChange($event)">{{ item.formatter==undefined?scope.row[item.prop]:item.formatter(scope.row,scope.row[item2.prop]) }}</div>
            </el-tooltip>
          </template>
        </el-table-column>
        <el-table-column
          align="center"
          :label="opColumnLabel"
          :width="buttonWidth"
          fixed="right"
          v-if="isShow">
          <template #default="scope">
            <template v-if="isShowUserOp">
              <div v-if="selectUser && !scope.row.selectedUser" class="flex items-center"
                  style="justify-content: space-between;" @click.stop="selectWriteUser(scope.row.id, scope.row.fillObj)">
                <div class="paddingH10 css-users ellipsis-2">
                  <span v-for="user in (scope.row.fillObj)">{{ user.userName + '-' + user.orgName }}</span>
                </div>
                <span style="color: #0079fe;cursor: pointer;">选择人员</span>
              </div>
              <div v-else class="flex items-center" style="justify-content: space-between;">
                <div class="paddingH10 css-users ellipsis-2">
                  <span v-for="user in (scope.row.fillObj)">{{ user.userName + '-' + user.orgName }}</span>
                </div>
                <span style="color: #555;cursor: pointer;">已下发</span>
              </div>
            </template>
            <template v-for="(btn,index) in buttons">
              <el-link v-if="(btn.isShow ==undefined?true:btn.isShow(scope.row)) && !btn.url && btn.label!='删除'"
                      class="marginR20"
                      v-permission="btn.permission"
                      :underline="false" :type="btn.type==undefined?'primary':btn.type" :key="btn.label"
                      @click.stop="btn.click(scope.row)" :class="btn.click?'pointer':'no-pointer'"
                      :style="{color: btn.color}" size="small">
                {{ btn.label }}
              </el-link>
              <el-popconfirm v-else-if="btn.label=='删除'"
                            class="box-item"
                            title="确定删除当条数据吗?"
                            placement="left"
                            @confirm="btn.click(scope.row)"
              >
                <template #reference>
                  <el-link size="small" :type="btn.type==undefined?'primary':btn.type" @click.stop="">
                    {{ btn.label }}
                  </el-link>
                </template>
              </el-popconfirm>
            </template>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <el-pagination
      v-if="tableData.length > 0 && isPage"
      class="comtable-pagination marginT20" style="justify-content: flex-end;"
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
      :current-page="currentPage"
      :page-sizes="pageSizes"
      :page-size="pageSize"
      layout="total, prev, pager, next, sizes"
      :total="total"
    >
    </el-pagination>
  </div>
</template>

<script setup>
import {computed, reactive, ref, onMounted} from 'vue'
import remoteSelect from './remoteSelect.vue';
import router from "@/router";

const props = defineProps({
  title: '',
  description: '',
  tableHeader: Array,
  tableData: Array,
  align: {
    type: String,
    default: 'center'
  },
  // 是否显示选择人员审批
  isShowUserOp: {
    type: Boolean,
    default: true,
  },
  // 是否显示选择框
  showSelection: {
    type: Boolean,
    default: false,
  },
  // 是否显示序号
  showIndex: {
    type: Boolean,
    default: true,
  },
  // 操作列宽度
  buttonWidth: {
    type: Number,
    default: 300
  },
  // 操作按钮
  buttons: {
    type: Array,
    default: []
  },
  // 固定列
  fixed: {
    type: Boolean,
    default: true
  },
 
  selectUser: {
    type: Boolean,
    default: false,
  },
  selectedInfo: {
    type: Array,
    default: []
  },
  currentPage: {
    type: Number,
    default: 1
  },
  pageSize: {
    type: Number,
    default: 15
  },
  total: {
    type: Number,
    default: 0
  },
  isPage: {
    type: Boolean,
    default: true,
  },
  pageSizes: {
    type: Array,
    default: [10, 15, 20, 50, 100]
  },
  tableScrollHeight: {
    type: Number,
    default: 0
  },
  selectableFileds: {
    type: Array,
    default: []
  },
  opColumnLabel: {
    type: String,
    default: '操作'
  },
  isShowTableFormatter:{
    type:Boolean,
    default:false
  },
  taskId: {
    type: String,
    default: ''
  },
  receiveId: {
    type: String,
    default: ''
  },
  submitLogId: {
    type: String,
    default: ''
  }
})

const boxHeight = computed(() => window.innerHeight - props.tableScrollHeight);
const data = reactive({
  filterValue: []
})

const tableFormatter = ref("完整版")

const selectVal = ref([]);
const selectData = ref([]);
const emit = defineEmits(['searchFilter', 'cancelFilter', 'rowClick', 'selectWriteUser', 'handleSizeChange', 'handleCurrentChange', 'handleSelectionChange', 'cancelSelectRow'])

const tableFormatterChange = ()=>{
  emit("tableFormatterChange",tableFormatter.value)
}

const handleSelectionChange = (val) => {
  selectVal.value = val;
  selectVal.value.map((rows) => rows.checked = true);
  emit('handleSelectionChange', selectVal.value);
}
const selectable = (row) => {
  // 获取需要禁用checkbox的数据 id
  return !props.selectableFileds.includes(row.id)
  // return !row.selectedUser;
}
const jwTables = ref()
const toggleSelection = (rows, ignoreSelectable) => {
  if (rows) {
    rows.forEach((row) => {
      jwTables.value.toggleRowSelection(
        row,
        undefined,
        ignoreSelectable
      )
    })
  } else {
    jwTables.value.clearSelection()
  }
}
const handleSizeChange = (pageSize) => {
  emit("handleSizeChange", pageSize);
}
const handleCurrentChange = (currentPage) => {
  emit("handleCurrentChange", currentPage);
}

const remoteSelectChange = (prop, value) => {
  data.filterValue[prop] = value
  searchFilter(prop, value)
}
// 筛选
const searchFilter = (prop, value) => {
  data.filterValue[prop] = value;
  emit('searchFilter', data.filterValue)
}
// 重置
const cancelFilter = (prop) => {
  data.filterValue[prop] = '';
  emit('searchFilter', data.filterValue)
}
// 行点击事件
const rowClick = (row) => {
  emit('rowClick', row, props.tableData.indexOf(row))
}
const selectWriteUser = (id, fillObj) => {
  toggleSelection()
  emit('selectWriteUser', id, fillObj)
}
const isShow = computed(() => {
  if (props.buttons <= 0 && !props.selectUser) {
    return false;
  }
  return true;
})
const indexMethod=(index) => {
  return (props.currentPage-1) * props.pageSize + index + 1;
}

const isShowTooltip = ref(false);
// 判断是否显示 溢出的文本 el-tooltip
const visibilityChange = (event) => {
  const ev = event.target;
  // 获取当前内容是否需要显示tooTip,鼠标移入显示所有内容,((获取当前内容数据字数*字体大小)/两行)+两边边距
  const evWeight = ((ev.innerText.length*14)/2)+4;
  const contentWeight = ev.clientWidth;
  if (evWeight > contentWeight) {
    // 实际宽度 > 可视宽度  文字溢出
    isShowTooltip.value = true;
  } else {
    // 否则为不溢出
    isShowTooltip.value = false;
  }
};

defineExpose({toggleSelection});
</script>
<style scoped>
.css-users {
  width: 75%;
  height: 50px;
  border: 1px solid rgb(210 210 210);
  border-radius: 4px;
  display: flex;
  align-items: center;
}

.css-users span::after {
  display: inline-block;
  content: '、';
  margin-left: 5px;
}

.css-users span:last-child::after {
  display: none;
}

.jw-title {
  position: relative;
  text-align: center;
  padding: 10px;
  font-weight: 600;
  font-size: 1.1rem;
  background-color: #e3effb;
  border-top: 1px solid #000000;
  border-left: 1px solid #000000;
  border-right: 1px solid #000000;
}

.jw-tables >>> .el-table .el-table__cell {
  border-left: 1px solid #000000 !important;
}

.jw-tables >>> .el-table__header .el-table__cell[rowspan="1"],
.jw-tables >>> .el-table__header .el-table__cell[rowspan="2"] {
  /* border-bottom: 0 !important; */
}

.jw-tables >>> .el-table__header .el-table__cell[rowspan="1"],
.jw-tables >>> .el-table__header .el-table__cell[rowspan="2"] {
  /* border-left: 0 !important; */
}

.jw-tables >>> .el-table thead th,
.jw-tables >>> .el-table__header tr:last-child .el-table__cell,
.jw-tables >>> .el-table__header td.el-table__cell {
  border-bottom: 1px solid #000000 !important;
}

.jw-tables >>> .el-table__header th.el-table__cell {
  background-color: #5299da;
  color: #fff;
}

.jw-tables >>> .el-table__header tr:first-child th.el-table__cell {
  border-top: 1px solid #000000;
}

.jw-tables >>> .el-table thead {
  color: #333333;
}

/* .jw-tables >>> .is-center.el-table-fixed-column--left.is-last-column.is-leaf.el-table__cell,
.jw-tables >>> .is-center.el-table-fixed-column--left.is-last-column.el-table__cell {
  border-right: 1px solid #000000 !important;
} */
.jw-tables >>> .el-table td:not(.el-table-fixed-column--right).el-table__cell>div {
  margin-top: 2px;
  margin-bottom: 2px;
  min-height: 23px;
}

.jw-tables >>> .el-table .cell,
.jw-tables >>> .el-table thead th div.cell {
  padding: 2px 4px;
  white-space: pre-line;
}

.jw-tables >>> .el-table__body tr:hover td.el-table__cell div {
  color: var(--fontColor) !important;
}

.multi-table :deep td {
  height: 45px;
}

:deep .bold-row {
  font-weight: bold;
}

.jw-tables >>> .el-table thead tr:first-child th:first-child {
  border-left: 1px solid #000000 !important;
}

.jw-tables >>> .el-table thead tr:not(:first-child) th:last-child {
  border-right: 0px !important;
}

.jw-tables >>> .el-table tr td:last-child {
  border-right: 1px solid #000000 !important;
}

.jw-tables >>> .el-table tr td {
  border-bottom: 1px solid #000000;
}

.jw-tables >>> .el-table tbody tr td:first-child {
  border-left: 1px solid #000000 !important;
}

.jw-tables >>> .el-table .el-table__cell {
  padding: 0;
}

.jw-tables >>> .el-table thead th:last-child {
  border-right: 1px solid #000000 !important;
}

:deep(.el-popover) {
  width: fit-content !important;
}

:deep(.el-popover.el-popper) {
  width: fit-content !important;
}

:deep .el-link.is-hover-underline:hover:after {
  border-bottom: 0;
}

.jw-tables >>> .el-table__row:nth-child(odd) > td {
  background-color: #e3effb !important;
}

.jw-tables >>> .el-table__row:nth-child(even) > td {
  background-color: #fff !important;
}

.jw-tables:hover >>> .el-scrollbar__thumb {
  background-color: #94c2f0 !important;
}

.no-pointer {
  cursor: default;
}

.table-title-btns {
  position: absolute;
  right: -8px;
  top: 3px;
  z-index: 2;
  transform: scale(0.75);
}

>>> .el-radio-button .el-radio-button__inner {
  border: 1px solid #409eff;
  color: #409eff;
}

>>> .el-radio-button:first-child .el-radio-button__inner {
  border-top-left-radius: 20px;
  border-bottom-left-radius: 20px;
}

>>> .el-radio-button:last-child .el-radio-button__inner {
  border-top-right-radius: 20px;
  border-bottom-right-radius: 20px;
}

>>> .el-radio-button.is-active .el-radio-button__inner {
  color: #fff;
}

>>> .el-select__input::placeholder,
>>> .el-select__placeholder.is-transparent,
>>> .el-input.is-disabled .el-input__inner::placeholder,
>>> .el-input__inner::placeholder {
  color: #a8abb2 !important;
  -webkit-text-fill-color: #a8abb2 !important;
}
</style>