<!-- 自动滚动的列表 --> <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>