Newer
Older
xc-business-system / src / components / ScrollTable / index.vue
lyg on 9 May 2024 9 KB 铯原子bug修改
<!-- 自动滚动的列表 -->
<script lang="ts" setup name="NormalTable">
import type { Ref } from 'vue'
import { ElTable } from 'element-plus'
import { defineExpose, ref } from 'vue'
import type { TableColumn } from './table_interface'
import TableColumnCom from './tableColumn.vue'
// ------------------定义props、 emit-------------------
const props = defineProps({
  // 是否显示多选表格
  isShowmultiSelect: {
    type: Boolean,
    default: false,
  },
  // 是否允许多选
  isMulti: {
    type: Boolean,
    default: true,
  },
  // 查询条件,此处主要需要分页的条件
  query: {
    type: Object,
    default() {
      return {
        offset: 1,
        limit: 20,
      }
    },
  },
  // loading状态
  listLoading: {
    type: Boolean,
    default: false,
  },
  // 数据
  data: {
    type: Array,
    default() {
      return []
    },
  },
  // 表格高度
  height: {
    type: Number,
    default() {
      return null
    },
  },
  // 数据总数
  total: {
    type: Number,
    default: 0,
  },
  // 数据列配置
  columns: {
    type: Array<TableColumn>,
    default() {
      return []
    },
  },
  // 是否需要分页控件
  pagination: {
    type: Boolean,
    default: true,
  },
  // 配置项
  options: {
    type: Object,
    default() {
      return {
        needIndex: true, // 是否需要序号列
        border: true, // 是否需要上方边框
      }
    },
  },
  // 可选单页显示条数
  pageSizes: {
    type: Array,
    default() {
      return [10, 20, 30]
    },
  },
  // 表格大小
  size: {
    type: String,
    default: 'default',
  }, // 表格大小,默认,small,mini等,与el-table条件相同
  // 大于这个数才会滚动
  scrollLimit: {
    type: Number,
    default: 10,
  },
})
const emit = defineEmits(['change', 'selectionChange', 'rowClick', 'rowDbClick', 'multiSelect', 'filterChange', 'rowDisabled', 'handleClickFollowLink'])
// -------定义数据--------------
interface columnsCheckInfo {
  text: string
  show: boolean
}
const columnsChecked: Ref<columnsCheckInfo[]> = ref([])
const scrollTable = ref<InstanceType<typeof ElTable>>()
const singleChecked = ref(' ') // 单选选中id
// 初始化列显示状态
function initColumnsState() {
  columnsChecked.value = []
  for (const column of props.columns) {
    columnsChecked.value.push({ text: column.text, show: !!column.show })
  }
}

// 最终展示列
const columnsFiltered: Ref<TableColumn[]> = ref([])
// 切换列
function changeColumns() {
  columnsFiltered.value = []
  for (const i in props.columns) {
    if (columnsChecked.value[i].show === true) {
      columnsFiltered.value.push(props.columns[i])
    }
  }
}
// 计算索引值方法
function indexMethod(index: number) {
  return props.query.limit * (props.query.offset - 1) + index + 1
}
// 刷新
function refresh() {
  emit('change')
}
// 改变页容量
function handleSizeChange(val: number) {
  emit('change', { size: val })
}
// 改变当前页
function handleCurrentChange(val: number) {
  emit('change', { page: val })
}
// 多选选中结果
function selectionChange(selection: []) {
  emit('selectionChange', selection)
}
// 点击行
function rowClick(row: object, column?: any, event?: any) {
  emit('rowClick', row)
}
// 双击行
function rowDbClick(row: object, column?: any, event?: any) {
  emit('rowDbClick', row)
}
// 监听columns
watch(props.columns, (val) => {
  initColumnsState()
  changeColumns()
})
// 多选
const handleSelectionChange = (val: any) => {
  // console.log(val)
  emit('multiSelect', val)
}
// 单选选中
const radioChange = () => {
  const checkValue = props.data.find((item: any) => item.id == singleChecked.value)
  emit('multiSelect', [checkValue])
}
// 清除多选选中
const clearMulti = () => {
  console.log('清理选中')
  scrollTable.value!.clearSelection()
  singleChecked.value = ''
}
// elementUI的合并行方法
function mergeTableRow({ row, column, rowIndex, columnIndex }: any) {
  // console.log(row, column, rowIndex, columnIndex);
  const span = `${column.property}-span`
  if (row[span]) {
    return row[span]
  }
}
defineExpose({
  clearMulti, initColumnsState, scrollTable,
})
onBeforeMount(() => {
  initColumnsState()
  changeColumns()
})
// 多选框单选功能
const selectClick = (selection: any, row: any) => {
  if (!props.isMulti) {
    console.log('selectClick')
    if (selection.length > 1) {
      const del_row = selection.shift()
      scrollTable.value?.toggleRowSelection(del_row, false) // 用于多选表格,切换某一行的选中状态,如果使用了第二个参数,则是设置这一行选中与否(selected 为 true 则选中)
    }
  }
}
// 筛选条件发生变化
const filterChange = (val: { [key: string]: string }) => {
  emit('filterChange', val)
}
const selectable = (row: any, index: number) => {
  let result = true
  // status 接收参数  true 禁用  false 可选
  emit('rowDisabled', row, index, (status: string) => {
    if (status === 'true') {
      result = false
    }
    else if (status === 'false') {
      result = true
    }
    else {
      result = true
    }
  })
  return result
}

const handleClickFollowLink = (row: any, title: string) => {
  emit('handleClickFollowLink', row, title)
}

const scrolltimer = ref()
// 设置自动滚动
const autoScroll = (stop: boolean, list: any[]) => {
  clearInterval(scrolltimer.value)
  scrolltimer.value = null
  const table = scrollTable.value
  // 拿到表格中承载数据的div元素
  const divData = table?.$refs.bodyWrapper.getElementsByClassName('el-scrollbar__wrap')[0]
  // console.log(divData, 'divData')
  // 拿到元素后,对元素进行定时增加距离顶部距离,实现滚动效果(此配置为每100毫秒移动1像素)
  if (stop || list.length < 5) {
    // 再通过事件监听,监听到 组件销毁 后,再执行关闭计时器。
    window.clearInterval(scrolltimer.value)
    scrolltimer.value = null
  }
  else {
    if (list.length < props.scrollLimit || stop) {
      return
    }
    if (divData) {
      scrolltimer.value = window.setInterval(() => {
        divData.scrollTop += 1
        if (divData.clientHeight + divData.scrollTop == divData.scrollHeight) {
          divData.scrollTop = 0
        }
      }, 100) // 滚动速度
    }
  }
}
watch(() => props.data, (newVal) => {
  if (newVal && newVal.length) {
    autoScroll(false, props.data)
  }
}, {
  deep: true,
})
onBeforeUnmount(() => {
  autoScroll(true, [])
})
</script>

<template>
  <div id="normal-table-com">
    <el-table
      id="print"
      ref="scrollTable"
      v-loading="listLoading"
      :data="data"
      :height="height"
      border
      stripe
      :size="size"
      style="width: 100%;"
      :row-key="(row) => { return row.id || row.index }"
      :span-method="mergeTableRow"
      @selection-change="handleSelectionChange"
      @select="selectClick"
      @row-click="rowClick"
      @row-dblclick="rowDbClick"
      @filter-change="filterChange"
      @mouseenter="autoScroll(true, [])"
      @mouseleave="autoScroll(false, data)"
    >
      <slot name="preColumns" />

      <el-table-column v-if="!isMulti" label="" width="60" align="center" fixed>
        <template #default="scope">
          <el-radio v-model="singleChecked" :label="scope.row.id" class="radio" @change="radioChange" />
        </template>
      </el-table-column>
      <el-table-column v-if="isShowmultiSelect && isMulti" :reserve-selection="true" type="selection" width="38" :selectable="selectable" fixed="left" />
      <template v-for="column of columns" :key="column.value">
        <el-table-column
          v-if="!column.isFilters"
          :label="column.text"
          :prop="column.value"
          :width="column.width"
          :align="column.align"
          :show-overflow-tooltip="column.showOverflow ? column.showOverflow : true"
          :fixed="column.fixed"
        >
          <template #default="scope">
            <span v-if="!column.filter && !column.isLink" :style="column.styleFilter ? column.styleFilter(scope.row) : ''">{{ scope.row[column.value] }}</span>
            <span v-if="column.filter && !column.isLink" :style="column.styleFilter ? column.styleFilter(scope.row) : ''">{{ column.filter(scope.row) }}</span>
            <div v-if="column.isLink">
              <show-photo v-for="(item, index) in scope.row.fileArr" :key="index" :minio-file-name="item" /><br>
            </div>
            <div v-if="column.followLink">
              <div v-for="(item, index) in scope.row.followLinkArr" :key="index" @click="handleClickFollowLink(scope.row, item)">
                <span :class="item !== '/' ? 'follow-link' : ''">{{ item }}</span>
              </div>
            </div>
          </template>
        </el-table-column>
        <el-table-column
          v-if="column.isFilters"
          :label="column.text"
          :prop="column.value"
          :width="column.width"
          :align="column.align"
          :show-overflow-tooltip="column.showOverflow ? column.showOverflow : true"
          :fixed="column.fixed"
          :column-key="column.value"
          :filters="column.filters"
          :filter-multiple="false"
        >
          <template #default="scope">
            <span v-if="!column.filter" :style="column.styleFilter ? column.styleFilter(scope.row) : ''">{{ scope.row[column.value] }}</span>
            <span v-else :style="column.styleFilter ? column.styleFilter(scope.row) : ''">{{ column.filter(scope.row) }}</span>
          </template>
        </el-table-column>
      </template>

      <slot name="columns" />
    </el-table>
  </div>
</template>

<style lang="scss" scoped>
#normal-table-com {
  width: 100%;
}

.single-table {
  width: 100%;

  :deep(.el-table th.el-table__cell:nth-child(1) .cell) {
    visibility: hidden;
  }
}

.follow-link {
  color: #3d7eff;
  cursor: pointer;

  &:hover {
    color: #04395e;
  }
}
</style>

<style lang="scss">
#normal-table-com {
  .el-radio__label {
    display: none !important;
  }
}
</style>