283 lines
6.8 KiB
Vue
283 lines
6.8 KiB
Vue
<template>
|
|
<div
|
|
v-if="dataSource.length"
|
|
class="metadata-edit-table-body-viewport" :style="{ ...style, height: height + 'px'}" ref="viewScrollRef" @scroll="onScroll">
|
|
<div :style="{position: 'relative'}">
|
|
<div class="metadata-edit-scrollbar" :style="containerStyle"> </div>
|
|
<div class="metadata-edit-table-center" ref="tableCenterRef" >
|
|
<div
|
|
v-if="virtualData.length"
|
|
v-for="(item, index) in virtualData"
|
|
:class="{
|
|
'metadata-edit-table-row': true,
|
|
'metadata-edit-table-row-selected': selectedRowKeys?.includes(item[rowKey] || virtualRang.start + index + 1)
|
|
}"
|
|
:key="`record_${item.__key}`"
|
|
:style="{height: `${cellHeight}px`,}"
|
|
:data-row-key="item[rowKey] || virtualRang.start + index + 1"
|
|
@click.right.native="(e) => showContextMenu(e,item, virtualRang.start + index)"
|
|
@click.stop="() => rowClick(item)"
|
|
>
|
|
<div
|
|
v-for="column in columns"
|
|
class="metadata-edit-table-cell"
|
|
:style="{
|
|
width: `${column.width}px`,
|
|
left: `${column.left}px`,
|
|
}"
|
|
>
|
|
<div v-if="column.dataIndex === '__serial'" class="body-cell-box">
|
|
<slot name="serial" :record="item" :index="item.__dataIndex" :column="column" >
|
|
{{ virtualRang.start + index + 1 }}
|
|
</slot>
|
|
</div>
|
|
<div v-else class="body-cell-box">
|
|
<slot :name="column.dataIndex" :record="item" :index="item.__dataIndex" :column="column" >
|
|
{{ item[column.dataIndex] }}
|
|
</slot>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<template v-else>
|
|
<slot name="empty">
|
|
<div class="metadata-edit-table-body-empty">
|
|
<j-empty />
|
|
</div>
|
|
</slot>
|
|
</template>
|
|
</template>
|
|
|
|
<script setup name="MetadataBaseTableBody">
|
|
import ContextMenu from './components/ContextMenu'
|
|
import {useRightMenuContext} from "@/components/Metadata/Table/context";
|
|
import {randomString} from "@/utils/utils";
|
|
import {bodyProps} from "./props";
|
|
|
|
const props = defineProps({
|
|
...bodyProps(),
|
|
groupKey: {
|
|
type: [String, Number],
|
|
default: undefined
|
|
}
|
|
})
|
|
|
|
const emit = defineEmits(['update:dataSource', 'scrollDown'])
|
|
|
|
const viewScrollRef = ref()
|
|
const tableCenterRef = ref()
|
|
// const virtualData = ref([])
|
|
const virtualRang = reactive({
|
|
start: 0,
|
|
end: 15
|
|
})
|
|
const containerStyle = ref(0)
|
|
const context = useRightMenuContext()
|
|
|
|
let scrollLock = ref(false)
|
|
let menuInstance
|
|
|
|
const maxLen = computed(() => {
|
|
return Math.trunc(props.height / props.cellHeight)
|
|
})
|
|
|
|
const selectedRowKeys = ref([])
|
|
|
|
const virtualData = computed(()=> {
|
|
|
|
const array = props.dataSource.slice(virtualRang.start, virtualRang.end)
|
|
if (tableCenterRef.value) {
|
|
tableCenterRef.value.style.webkitTransform = `translate3d(0, ${virtualRang.start * props.cellHeight}px, 0)`
|
|
}
|
|
return array
|
|
})
|
|
// const updateVirtualData = (start, end) => {
|
|
// debugger
|
|
// virtualData.value = props.dataSource.slice(start, end)
|
|
// if (tableCenterRef.value) {
|
|
// tableCenterRef.value.style.webkitTransform = `translate3d(0, ${start * props.cellHeight}px, 0)`
|
|
// }
|
|
// }
|
|
|
|
const onScroll = () => {
|
|
const height = viewScrollRef.value.scrollTop
|
|
const clientHeight = viewScrollRef.value.clientHeight
|
|
const scrollHeight = viewScrollRef.value.scrollHeight
|
|
|
|
const _index = Math.round(height / props.cellHeight) - 1
|
|
|
|
|
|
const start = _index < 0 ? 0 : _index
|
|
const end = start + maxLen.value + 4
|
|
|
|
if (height + clientHeight >= props.dataSource.length * props.cellHeight && !scrollLock.value) { // 滚动到底
|
|
emit('scrollDown')
|
|
scrollLock.value = true
|
|
}
|
|
|
|
virtualRang.start = start
|
|
virtualRang.end = end
|
|
// updateVirtualData(start, end)
|
|
}
|
|
|
|
const scrollTo = (index) => {
|
|
if (viewScrollRef.value) {
|
|
let top = index * props.cellHeight
|
|
viewScrollRef.value.scrollTop = top
|
|
}
|
|
}
|
|
|
|
const showContextMenu = (e, record, _index) => {
|
|
e.preventDefault()
|
|
if (props.disableMenu) {
|
|
record = {
|
|
...record,
|
|
__index: _index
|
|
}
|
|
menuInstance = ContextMenu(e, record, context)
|
|
}
|
|
}
|
|
|
|
// const updateView = () => {
|
|
// updateVirtualData(virtualRang.start, virtualRang.start + maxLen.value)
|
|
// }
|
|
|
|
const rowClick = (record) => {
|
|
if (props.rowSelection?.selectedRowKeys) {
|
|
const rowSet = new Set(selectedRowKeys.value)
|
|
const key = record[props.rowKey]
|
|
const selected = !rowSet.has(key)
|
|
|
|
if (selected) {
|
|
rowSet.delete(key)
|
|
} else {
|
|
rowSet.add(key)
|
|
}
|
|
|
|
props.rowSelection.onSelect?.(record, selected )
|
|
|
|
selectedRowKeys.value = [...rowSet.values()]
|
|
}
|
|
}
|
|
|
|
const updateSelectedKeys = (keys) => {
|
|
selectedRowKeys.value = keys
|
|
}
|
|
|
|
onMounted(() => {
|
|
nextTick(() => {
|
|
onScroll()
|
|
})
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
menuInstance?.destroy()
|
|
menuInstance?.cleanCopy()
|
|
})
|
|
|
|
watch(() => JSON.stringify(props.rowSelection?.selectedRowKeys), (val) => {
|
|
selectedRowKeys.value = JSON.parse(val || '[]')
|
|
}, { immediate: true })
|
|
|
|
watch(() => props.dataSource, (val, oldVal) => {
|
|
|
|
props.dataSource.forEach((item, index) => {
|
|
if (!item.__key) {
|
|
item.__key = randomString()
|
|
}
|
|
})
|
|
|
|
// updateView()
|
|
|
|
}, {
|
|
immediate: true,
|
|
deep: true
|
|
})
|
|
|
|
watch(() => props.dataSource.length, () => {
|
|
scrollLock.value = false
|
|
containerStyle.value = {
|
|
height: props.dataSource.length * props.cellHeight + 'px'
|
|
}
|
|
|
|
if (props.dataSource.length <= maxLen.value || props.dataSource.length === 0) {
|
|
emit('scrollDown', maxLen.value - props.dataSource.length + 3)
|
|
}
|
|
}, { immediate: true})
|
|
|
|
// watch(() => props.height, () => {
|
|
// updateView()
|
|
// })
|
|
|
|
watch(() => props.groupKey, () => {
|
|
if (props.openGroup) {
|
|
scrollTo(0)
|
|
}
|
|
})
|
|
|
|
defineExpose({
|
|
scrollTo,
|
|
updateSelectedKeys
|
|
})
|
|
|
|
</script>
|
|
|
|
<style scoped lang="less">
|
|
.metadata-edit-table-body-viewport {
|
|
max-height: 100%;
|
|
width: 100%;
|
|
overflow: hidden auto;
|
|
position: relative;
|
|
|
|
.metadata-edit-scrollbar {
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
right: 0;
|
|
z-index: -1;
|
|
}
|
|
|
|
.metadata-edit-table-body-container {
|
|
overflow: hidden;
|
|
height: 100%;
|
|
}
|
|
|
|
.metadata-edit-table-center {
|
|
position: relative;
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
height: 100%;
|
|
|
|
.metadata-edit-table-row {
|
|
width: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
//position: absolute;
|
|
transition: top .2s, height .2s, background-color .1s;
|
|
border-bottom: 1px solid #ebebeb;
|
|
|
|
&:hover {
|
|
background-color: rgb(248, 248, 248);
|
|
}
|
|
|
|
&.metadata-edit-table-row-selected {
|
|
background-color: var(--ant-primary-1);
|
|
}
|
|
|
|
.body-cell-box {
|
|
padding: 0 12px;
|
|
position: relative;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.metadata-edit-table-body-empty {
|
|
display: flex;
|
|
width: 100%;
|
|
justify-content: center;
|
|
padding-top: 24px;
|
|
}
|
|
</style>
|