diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts
index 2eefef0a..0b07af02 100644
--- a/docs/.vitepress/config.ts
+++ b/docs/.vitepress/config.ts
@@ -65,6 +65,7 @@ export default defineConfig({
text: "下拉选择表格组件",
link: "/components/TSelectTable/base.md"
},
+ { text: "Virtualized TSelectTablev1.4.13", link: "/components/multipleVirtual/base.md" },
{
text: "条件查询组件",
link: "/components/TQueryCondition/base.md"
diff --git a/docs/components/TSelectTable/base.md b/docs/components/TSelectTable/base.md
index 08edfa97..44401e7a 100644
--- a/docs/components/TSelectTable/base.md
+++ b/docs/components/TSelectTable/base.md
@@ -129,6 +129,7 @@ TSelectTable/slotUse
TSelectTable/isShowInput
:::
+
### TSelectTable 参数配置
---
@@ -147,48 +148,53 @@ TSelectTable/isShowInput
### 2、配置参数(Attributes)继承 el-table 及 el-select 属性
-| 参数 | 说明 | 类型 | 默认值 |
-| :------------------ | :-------------------------------------------------------------------------------------- | :------------------------ | :--------- |
-| v-model | 绑定值 | boolean / string / number | 仅显示 |
-| v-model:input-value | 输入框回显值 | boolean / string / number | - |
-| isShowInput | 是否输入框显示 | Boolean | false |
-| table | 表格数据对象 | Object | {} |
-| ---data | 展示下拉数据源 | Array | [] |
-| ---total | 数据总条数 | Number | - |
-| ---pageSize | 每页显示条目个数 | Number | - |
-| ---currentPage | 当前页数 | Number | - |
-| columns | 表头信息 | Array | [] |
-| ----bind | el-table-column Attributes | Object | - |
-| ----fixed | 列是否固定( left, right) | string, boolean | - |
-| ----align | 对齐方式(left/center/right) | String | center |
-| ----render | 返回三个参数(text:当前值,row:当前整条数据 ,index:当前行) | function | - |
-| ----slotName | 插槽显示此列数据(其值是具名作用域插槽) | String | - |
-| ------scope | 具名插槽获取此行数据必须用解构接收{scope} | Object | 当前行数据 |
-| keywords | 关键字配置(value-key 配置) | Object | 无 |
-| ------label | 选项的标签 | String | ‘label’ |
-| ------value | 选项的值 | String / number | ‘value’ |
-| radioTxt | 单选文案 | String | 单选 |
-| multiple | 是否开启多选 | Boolean | false |
-| filterMethod | 自定义过滤 | function | - |
-| rowClickRadio | 是否开启整行选中(单选) | boolean | true |
-| isShowFirstColumn | 是否显示首列(单选) | boolean | true |
-| defaultSelectVal | 设置第一页默认选中项--keywords.value 值 | Array | [] |
-| filterable | 是否开启过滤(根据 keywords 的 label 值进行过滤) | Boolean | true |
-| reserveSelection | 是否支持翻页选中 | Boolean | true |
-| isShowPagination | 开启分页 | Boolean | false |
-| tableWidth | table 宽度(单位:px) 若设置 0,宽度 100% | Number/String | 550 |
-| selectWidth | select 宽度(单位:px) 若设置 0,宽度 100% | Number/String | 550 |
-| inputWidth | input 输入框的宽度(单位:px) 若设置 0,宽度 100% | Number/String | 550 |
-| inputAttr | 继承所有 el-input 的属性 | Object | - |
-| isKeyup | 单选是否开启键盘事件 | Boolean | false |
-| isShowQuery | 是否允许配置查询条件(继承 TQueryCondition 的所有属性、事件、插槽) | Boolean | false |
-| isShowBlurBtn | 条件查询组件是否显示隐藏下拉框按钮 | Boolean | false |
-| btnBind | 显示下拉框按钮配置,继承`el-button`所有属性;`默认值{type:'danger',btnTxt:'关闭下拉框'}` | Object | - |
-| isClearQuery | 关闭下拉框是否清空搜索条件 | Boolean | false |
-| selfExpanded | 是否始终显示下拉框 | Boolean | false |
-| isExpanded | 是否显示下拉框 | Boolean | false |
-| toolbar | el-table 头部插槽(位置:查询条件下面) | slot | - |
-| footer | el-table 底部插槽(位置:分页器上面) | slot | - |
+| 参数 | 说明 | 类型 | 默认值 |
+| :------------------------------------- | :-------------------------------------------------------------------------------------- | :------------------------ | :--------- |
+| v-model | 绑定值 | boolean / string / number | 仅显示 |
+| v-model:input-value | 输入框回显值 | boolean / string / number | - |
+| isShowInput | 是否输入框显示 | Boolean | false |
+| table | 表格数据对象 | Object | {} |
+| ---data | 展示下拉数据源 | Array | [] |
+| ---total | 数据总条数 | Number | - |
+| ---pageSize | 每页显示条目个数 | Number | - |
+| ---currentPage | 当前页数 | Number | - |
+| columns | 表头信息 | Array | [] |
+| ----bind | el-table-column Attributes | Object | - |
+| ----fixed | 列是否固定( left, right) | string, boolean | - |
+| ----align | 对齐方式(left/center/right) | String | center |
+| ----render | 返回三个参数(text:当前值,row:当前整条数据 ,index:当前行) | function | - |
+| ----slotName | 插槽显示此列数据(其值是具名作用域插槽) | String | - |
+| ------scope | 具名插槽获取此行数据必须用解构接收{scope} | Object | 当前行数据 |
+| keywords | 关键字配置(value-key 配置) | Object | 无 |
+| ------label | 选项的标签 | String | ‘label’ |
+| ------value | 选项的值 | String / number | ‘value’ |
+| radioTxt | 单选文案 | String | 单选 |
+| multiple | 是否开启多选 | Boolean | false |
+| filterMethod | 自定义过滤 | function | - |
+| rowClickRadio | 是否开启整行选中(单选) | boolean | true |
+| isShowFirstColumn | 是否显示首列(单选) | boolean | true |
+| defaultSelectVal | 设置第一页默认选中项--keywords.value 值 | Array | [] |
+| filterable | 是否开启过滤(根据 keywords 的 label 值进行过滤) | Boolean | true |
+| reserveSelection | 是否支持翻页选中 | Boolean | true |
+| isShowPagination | 开启分页 | Boolean | false |
+| tableWidth | table 宽度(单位:px) 若设置 0,宽度 100% | Number/String | 550 |
+| selectWidth | select 宽度(单位:px) 若设置 0,宽度 100% | Number/String | 550 |
+| inputWidth | input 输入框的宽度(单位:px) 若设置 0,宽度 100% | Number/String | 550 |
+| inputAttr | 继承所有 el-input 的属性 | Object | - |
+| isKeyup | 单选是否开启键盘事件 | Boolean | false |
+| isShowQuery | 是否允许配置查询条件(继承 TQueryCondition 的所有属性、事件、插槽) | Boolean | false |
+| isShowBlurBtn | 条件查询组件是否显示隐藏下拉框按钮 | Boolean | false |
+| btnBind | 显示下拉框按钮配置,继承`el-button`所有属性;`默认值{type:'danger',btnTxt:'关闭下拉框'}` | Object | - |
+| isClearQuery | 关闭下拉框是否清空搜索条件 | Boolean | false |
+| selfExpanded | 是否始终显示下拉框 | Boolean | false |
+| isExpanded | 是否显示下拉框 | Boolean | false |
+| toolbar | el-table 头部插槽(位置:查询条件下面) | slot | - |
+| footer | el-table 底部插槽(位置:分页器上面) | slot | - |
+| multipleFixed1.4.13 | table 是否固定多选 | Boolean | false |
+| radioFixed1.4.13 | table 是否固定单选 | Boolean | false |
+| useVirtual1.4.13 | table 是否开启虚拟滚动 | Boolean | false |
+| virtualShowSize1.4.13 | 虚拟列表的渲染行数 | Number | 30 |
+
### 3、事件(events)继承 el-table 及 el-select 属性
diff --git a/docs/components/multipleVirtual/base.md b/docs/components/multipleVirtual/base.md
new file mode 100644
index 00000000..cc603f31
--- /dev/null
+++ b/docs/components/multipleVirtual/base.md
@@ -0,0 +1,19 @@
+# Virtualized TSelectTable
+>默认显示`30`条数据,可以设置`virtualShowSize`属性来控制显示条数
+
+
+### 单选--虚拟滚动Table1.4.13
+
+:::demo `TSelectTable组件`标签添加属性`useVirtual`--开启虚拟滚动;`columns`中的项宽度需要使用`minWidth`;`单选`开启虚拟滚动`将自动隐藏单选项首列`。
+
+TSelectTable/radioVirtual
+:::
+
+
+### 多选--虚拟滚动Table1.4.13
+
+:::demo `TSelectTable组件`标签添加属性`useVirtual`--开启虚拟滚动。
+
+TSelectTable/multipleVirtual
+:::
+
diff --git a/docs/examples/TSelectTable/multipleVirtual.vue b/docs/examples/TSelectTable/multipleVirtual.vue
new file mode 100644
index 00000000..e23d65b3
--- /dev/null
+++ b/docs/examples/TSelectTable/multipleVirtual.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
diff --git a/docs/examples/TSelectTable/radioVirtual.vue b/docs/examples/TSelectTable/radioVirtual.vue
new file mode 100644
index 00000000..9510eae0
--- /dev/null
+++ b/docs/examples/TSelectTable/radioVirtual.vue
@@ -0,0 +1,45 @@
+
+
+
+ radioChange(args, '单选')"
+ />
+
+
+
+
diff --git a/packages/select-table/src/index.vue b/packages/select-table/src/index.vue
index ba1edb42..ccf7c756 100644
--- a/packages/select-table/src/index.vue
+++ b/packages/select-table/src/index.vue
@@ -60,7 +60,9 @@
:class="{
radioStyle: !multiple,
highlightCurrentRow: isRadio,
- keyUpStyle: isKeyup
+ keyUpStyle: isKeyup,
+ t_select_table_multiple: useVirtual && multiple,
+ t_select_table_radio: useVirtual && !multiple
}"
highlight-current-row
border
@@ -78,15 +80,15 @@
align="center"
:reserve-selection="reserveSelection"
:selectable="selectable"
- fixed
+ :fixed="multipleFixed"
>
{
@@ -193,7 +206,9 @@ const isRadio = ref(false)
const isQueryVisible = ref(false) // 查询条件是否显示隐藏下拉框
const isVisible = ref(false) // 是否显示隐藏下拉框
const radioVal = ref("")
+const isShowFirstRadio = ref(props.isShowFirstColumn) // 是否显示第一列
const selectDefaultLabel: any = ref(props.modelValue) // 单选赋值
+const scrollTopNum = ref(0) // 滚动条位置
// input回显值
let selectInputVal: any = computed({
get() {
@@ -219,7 +234,12 @@ const nowIndex = ref(-1)
watch(
() => props.table.data,
val => {
- state.tableData = val
+ if (props.useVirtual) {
+ saveDATA.value = val
+ updateRenderData(scrollTopNum.value)
+ } else {
+ state.tableData = val
+ }
nextTick(() => {
state.tableData &&
state.tableData.length > 0 &&
@@ -249,6 +269,46 @@ onMounted(() => {
if (props.selfExpanded) {
selectRef.value.expanded = true
}
+ if (props.useVirtual) {
+ saveDATA.value = props.table.data
+ isShowFirstRadio.value = false
+ getDom(props)
+ scrollContainerEl.value?.addEventListener("scroll", handleScroll)
+ }
+})
+
+// 更新实际渲染数据
+const updateRenderData = (scrollTop: number) => {
+ // console.log("更新实际渲染数据---scrollTop", scrollTop)
+ let startIndex = 0
+ let offsetHeight = 0
+ for (let i = 0; i < saveDATA.value.length; i++) {
+ offsetHeight += getItemHeightFromCache(i)
+ if (offsetHeight >= scrollTop) {
+ startIndex = i
+ break
+ }
+ }
+ // 计算得出的渲染数据
+ state.tableData = saveDATA.value.slice(startIndex, startIndex + props.virtualShowSize)
+ // 缓存最新的列表项高度
+ updateRenderedItemCache(startIndex)
+ // 更新偏移值
+ updateOffset(offsetHeight - getItemHeightFromCache(startIndex))
+}
+// 滚动事件
+const handleScroll = (e: any) => {
+ scrollTopNum.value = e.target.scrollTop
+ // 渲染正确的数据
+ updateRenderData(scrollTopNum.value)
+ // console.log("滚动事件---handleScroll")
+}
+// 移除滚动事件
+onBeforeUnmount(() => {
+ // console.log("移除滚动事件")
+ if (props.useVirtual) {
+ scrollContainerEl.value?.removeEventListener("scroll", handleScroll)
+ }
})
// 解决查询条件下拉选择table闪烁问题
onUpdated(() => {
@@ -258,7 +318,7 @@ onUpdated(() => {
}
})
// 表格显示隐藏回调
-const visibleChange = (visible: boolean) => {
+const visibleChange = async (visible: boolean) => {
// console.log('表格显示隐藏回调', visible)
isVisible.value = visible
if (isQueryVisible.value) {
@@ -274,6 +334,10 @@ const visibleChange = (visible: boolean) => {
defaultSelect(state.defaultSelectValue)
}
initTableData()
+ if (props.useVirtual) {
+ saveDATA.value = props.table.data
+ updateRenderData(scrollTopNum.value)
+ }
} else {
if (
tQueryConditionRef.value &&
@@ -286,6 +350,11 @@ const visibleChange = (visible: boolean) => {
}
findLabel()
filterMethodHandle("")
+ if (props.useVirtual) {
+ // console.log("props.useVirtual---清空")
+ state.tableData = []
+ saveDATA.value = []
+ }
}
if (props.selfExpanded) {
selectRef.value.expanded = true
diff --git a/packages/select-table/src/useProps.ts b/packages/select-table/src/useProps.ts
index aeacefd5..c84b96b0 100644
--- a/packages/select-table/src/useProps.ts
+++ b/packages/select-table/src/useProps.ts
@@ -1,4 +1,4 @@
-import type { PropType,ExtractPropTypes } from "vue"
+import type { PropType, ExtractPropTypes } from "vue"
export const selectTableProps = {
// input输入框的值(modelValue)
inputValue: {
@@ -148,7 +148,18 @@ export const selectTableProps = {
// Function(row: any, index: number) 的返回值用来决定这一行的 CheckBox 是否可以勾选
selectable: {
type: Function as PropType<(row: any, index: number) => boolean>
- }
+ },
+ // 是否开启虚拟列表
+ useVirtual: Boolean,
+ // 虚拟列表显示条数
+ virtualShowSize: {
+ type: Number,
+ default: 30
+ },
+ // 是否固定多选
+ multipleFixed: Boolean,
+ // 是否固定单选
+ radioFixed: Boolean,
}
export type TSelectTableProps = ExtractPropTypes
\ No newline at end of file
diff --git a/packages/select-table/src/useVirtualized.ts b/packages/select-table/src/useVirtualized.ts
new file mode 100644
index 00000000..06585e12
--- /dev/null
+++ b/packages/select-table/src/useVirtualized.ts
@@ -0,0 +1,73 @@
+import { nextTick, ref } from "vue"
+import type { Ref } from "vue"
+export function useVirtualized() {
+ // 渲染实际高度的容器
+ const actualHeightContainerEl = ref(null)
+ // 用于偏移的元素选择器
+ const translateContainerEl = ref(null)
+ // 滚动容器的元素选择器
+ const scrollContainerEl = ref(null)
+ const isMultiple = ref(false)
+ // 所有数据
+ const saveDATA: Ref = ref([])
+ // 缓存已渲染元素的高度
+ const RenderedItemsCache: any = {}
+ // 获取dom元素
+ const getDom = (props: { multiple: boolean }) => {
+ const getElements = (className: string) => {
+ actualHeightContainerEl.value = document.querySelector(`${className} .el-scrollbar__view`);
+ translateContainerEl.value = document.querySelector(`${className} .el-table__body`);
+ scrollContainerEl.value = document.querySelector(`${className} .el-scrollbar__wrap`);
+ };
+ isMultiple.value = props.multiple;
+ if (props.multiple) {
+ getElements(".t_select_table_multiple");
+ } else {
+ getElements(".t_select_table_radio");
+ }
+ // console.log('actualHeightContainerEl.value', actualHeightContainerEl.value)
+ // console.log('translateContainerEl.value', translateContainerEl.value)
+ // console.log('scrollContainerEl.value', scrollContainerEl.value)
+ }
+ // 获取缓存高度,无缓存,取配置项的 itemHeight
+ const getItemHeightFromCache = (index: number | string) => {
+ const val = RenderedItemsCache[index]
+ return val === void 0 ? 50 : val
+ }
+ // 更新实际高度
+ const updateActualHeight = () => {
+ let actualHeight = 0
+ saveDATA.value.forEach((_, i) => {
+ actualHeight += getItemHeightFromCache(i)
+ })
+ // if (actualHeightContainerEl.value && actualHeightContainerEl.value.style) {
+ actualHeightContainerEl.value!.style.height = actualHeight + "px"
+ // }
+ }
+ // 更新偏移值
+ const updateOffset = (offset: number) => {
+ if (translateContainerEl.value && translateContainerEl.value.style) {
+ translateContainerEl.value!.style.transform = `translateY(${offset}px)`
+ }
+ }
+ // 更新已渲染列表项的缓存高度
+ const updateRenderedItemCache = (index: number) => {
+ // 当所有元素的实际高度更新完毕,就不需要重新计算高度
+ const shouldUpdate = Object.keys(RenderedItemsCache).length < saveDATA.value.length
+ if (!shouldUpdate) return
+ nextTick(() => {
+ // 获取所有列表项元素
+ const Items: HTMLElement[] = Array.from(document.querySelectorAll(`${isMultiple.value ? `.t_select_table_multiple` : `.t_select_table_radio`} .el-table__row`))
+ // 进行缓存
+ Items.forEach(el => {
+ if (!RenderedItemsCache[index]) {
+ RenderedItemsCache[index] = el.offsetHeight
+ }
+ index++
+ })
+ // 更新实际高度
+ updateActualHeight()
+ })
+ }
+ return { scrollContainerEl, updateRenderedItemCache, updateOffset, getDom, getItemHeightFromCache, saveDATA }
+}
\ No newline at end of file