1
0
mirror of synced 2025-12-13 09:48:06 +08:00

修复虚拟滚动后筛选后滚动条显示错误问题

This commit is contained in:
xuliangzhan
2022-01-25 22:50:11 +08:00
parent 281b672ab1
commit dc394bbc66
11 changed files with 187 additions and 91 deletions

View File

@@ -36,7 +36,7 @@
"@vue/cli-plugin-typescript": "~4.5.0",
"@vue/cli-plugin-vuex": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/compiler-sfc": "^3.2.18",
"@vue/compiler-sfc": "^3.2.28",
"@vue/eslint-config-standard": "^5.1.2",
"@vue/eslint-config-typescript": "^7.0.0",
"core-js": "^3.6.5",
@@ -61,7 +61,7 @@
"sass": "^1.45.1",
"sass-loader": "^10.0.5",
"typescript": "~4.3.5",
"vue": "^3.2.26",
"vue": "^3.2.28",
"vue-i18n": "^9.1.7",
"vue-router": "^4.0.11",
"vuex": "^4.0.2",

View File

@@ -137,7 +137,7 @@ const tableFilterHook: VxeGlobalHooksHandles.HookOptions = {
* @param {Event} evnt 事件
*/
confirmFilterEvent (evnt: Event) {
const { filterStore, scrollXLoad, scrollYLoad } = reactData
const { filterStore, scrollXLoad: oldScrollXLoad, scrollYLoad: oldScrollYLoad } = reactData
const filterOpts = computeFilterOpts.value
const { column } = filterStore
const { property } = column
@@ -154,19 +154,26 @@ const tableFilterHook: VxeGlobalHooksHandles.HookOptions = {
if (!filterOpts.remote) {
$xetable.handleTableData(true)
$xetable.checkSelectionStatus()
$xetable.updateFooter()
if (scrollXLoad || scrollYLoad) {
$xetable.clearScroll()
if (scrollYLoad) {
$xetable.updateScrollYSpace()
}
}
}
$xetable.dispatchEvent('filter-change', { column, property, values, datas, filters: filterList, filterList }, evnt)
$xetable.closeFilter()
nextTick(() => {
$xetable.recalculate()
$xetable.updateFooter().then(() => {
const { scrollXLoad, scrollYLoad } = reactData
if ((oldScrollXLoad || scrollXLoad) || (oldScrollYLoad || scrollYLoad)) {
if (oldScrollXLoad || scrollXLoad) {
$xetable.updateScrollXSpace()
}
if (oldScrollYLoad || scrollYLoad) {
$xetable.updateScrollYSpace()
}
return $xetable.refreshScroll()
}
}).then(() => {
$xetable.updateCellAreas()
return $xetable.recalculate(true)
}).then(() => {
// 存在滚动行为未结束情况
setTimeout(() => $xetable.recalculate(), 50)
})
}
}

View File

@@ -0,0 +1,42 @@
import XEUtils from 'xe-utils'
import { VxeInputPropTypes } from '../../../types/all'
export function toStringTimeDate (str: VxeInputPropTypes.ModelValue) {
if (str) {
const rest = new Date()
let h = 0
let m = 0
let s = 0
if (XEUtils.isDate(str)) {
h = str.getHours()
m = str.getMinutes()
s = str.getSeconds()
} else {
str = XEUtils.toValueString(str)
const parses = str.match(/^(\d{1,2})(:(\d{1,2}))?(:(\d{1,2}))?/)
if (parses) {
h = XEUtils.toNumber(parses[1])
m = XEUtils.toNumber(parses[3])
s = XEUtils.toNumber(parses[5])
}
}
rest.setHours(h)
rest.setMinutes(m)
rest.setSeconds(s)
return rest
}
return new Date('')
}
export function getDateQuarter (date: Date) {
const month = date.getMonth()
if (month < 3) {
return 1
} else if (month < 6) {
return 2
} else if (month < 9) {
return 3
}
return 4
}

View File

@@ -5,6 +5,8 @@ import { useSize } from '../../hooks/size'
import { getFuncText, getLastZIndex, nextZIndex } from '../../tools/utils'
import { hasClass, getAbsolutePos, getEventTargetNode } from '../../tools/dom'
import { GlobalEvent, hasEventKey, EVENT_KEYS } from '../../tools/event'
import { toStringTimeDate, getDateQuarter } from './date'
import { handleNumber, toFloatValueFixed } from './number'
import { VNodeStyle, VxeInputConstructor, VxeInputEmits, InputReactData, InputMethods, VxeInputPropTypes, InputPrivateRef } from '../../../types/all'
@@ -52,33 +54,6 @@ const yearSize = 20
const monthSize = 20
const quarterSize = 8
function toStringTimeDate (str: VxeInputPropTypes.ModelValue) {
if (str) {
const rest = new Date()
let h = 0
let m = 0
let s = 0
if (XEUtils.isDate(str)) {
h = str.getHours()
m = str.getMinutes()
s = str.getSeconds()
} else {
str = XEUtils.toValueString(str)
const parses = str.match(/^(\d{1,2})(:(\d{1,2}))?(:(\d{1,2}))?/)
if (parses) {
h = XEUtils.toNumber(parses[1])
m = XEUtils.toNumber(parses[3])
s = XEUtils.toNumber(parses[5])
}
}
rest.setHours(h)
rest.setMinutes(m)
rest.setSeconds(s)
return rest
}
return new Date('')
}
export default defineComponent({
name: 'VxeInput',
props: {
@@ -410,18 +385,6 @@ export default defineComponent({
return XEUtils.chunk(yearList, 4)
})
const getDateQuarter = (date: Date) => {
const month = date.getMonth()
if (month < 3) {
return 1
} else if (month < 6) {
return 2
} else if (month < 9) {
return 3
}
return 4
}
const computeQuarterList = computed(() => {
const { selectMonth, currentDate } = reactData
const quarters: DateQuarterItem[] = []
@@ -609,18 +572,7 @@ export default defineComponent({
return immediate || !(type === 'text' || type === 'number' || type === 'integer' || type === 'float')
})
const handleNumber = (val: string | number) => {
return XEUtils.isString(val) ? val.replace(/,/g, '') : val
}
function toFloatValueFixed (inputValue: string | number, digitsValue: number) {
if (/^-/.test('' + inputValue)) {
return XEUtils.toFixed(XEUtils.ceil(inputValue, digitsValue), digitsValue)
}
return XEUtils.toFixed(XEUtils.floor(inputValue, digitsValue), digitsValue)
}
function getNumberValue (val: any) {
const getNumberValue = (val: any) => {
const { type, exponential } = props
const inpMaxlength = computeInpMaxlength.value
const digitsValue = computeDigitsValue.value

View File

@@ -0,0 +1,12 @@
import XEUtils from 'xe-utils'
export function handleNumber (val: string | number) {
return XEUtils.isString(val) ? val.replace(/,/g, '') : val
}
export function toFloatValueFixed (inputValue: string | number, digitsValue: number) {
if (/^-/.test('' + inputValue)) {
return XEUtils.toFixed(XEUtils.ceil(inputValue, digitsValue), digitsValue)
}
return XEUtils.toFixed(XEUtils.floor(inputValue, digitsValue), digitsValue)
}

View File

@@ -1,10 +1,10 @@
import { defineComponent, h, PropType, computed, inject, resolveComponent, ComponentOptions, ref, Ref, nextTick } from 'vue'
import { defineComponent, h, PropType, computed, inject, resolveComponent, ComponentOptions, ref, Ref, reactive, nextTick, watch } from 'vue'
import XEUtils from 'xe-utils'
import GlobalConfig from '../../v-x-e-table/src/conf'
import { hasEventKey, EVENT_KEYS } from '../../tools/event'
import { useSize } from '../../hooks/size'
import { VxePagerPropTypes, VxePagerConstructor, VxePagerEmits, VxeSelectEvents, PagerPrivateRef, VxeGridConstructor, PagerMethods, PagerPrivateMethods, VxePagerPrivateMethods } from '../../../types/all'
import { VxePagerPropTypes, VxePagerConstructor, VxePagerEmits, VxeSelectEvents, PagerPrivateRef, VxeGridConstructor, PagerMethods, PagerPrivateMethods, VxePagerPrivateMethods, PagerReactData } from '../../../types/all'
export default defineComponent({
name: 'VxePager',
@@ -57,6 +57,10 @@ export default defineComponent({
const $xegrid = inject('$xegrid', null as VxeGridConstructor | null)
const reactData = reactive<PagerReactData>({
inpCurrPage: props.currentPage
})
const refElem = ref() as Ref<HTMLDivElement>
const refMaps: PagerPrivateRef = {
@@ -165,6 +169,11 @@ export default defineComponent({
pagerMethods.dispatchEvent('page-change', { type: 'size', pageSize, currentPage: Math.min(props.currentPage, getPageCount(props.total, pageSize)) })
}
const jumpInputEvent = (evnt: KeyboardEvent) => {
const inputElem: HTMLInputElement = evnt.target as HTMLInputElement
reactData.inpCurrPage = inputElem.value
}
const jumpKeydownEvent = (evnt: KeyboardEvent) => {
if (hasEventKey(evnt, EVENT_KEYS.ENTER)) {
triggerJumpEvent(evnt)
@@ -337,9 +346,10 @@ export default defineComponent({
}, GlobalConfig.i18n('vxe.pager.goto')) : null,
h('input', {
class: 'vxe-pager--goto',
value: props.currentPage,
value: reactData.inpCurrPage,
type: 'text',
autocomplete: 'off',
onInput: jumpInputEvent,
onKeydown: jumpKeydownEvent,
onBlur: triggerJumpEvent
}),
@@ -405,6 +415,10 @@ export default defineComponent({
Object.assign($xepager, pagerMethods, pagerPrivateMethods)
watch(() => props.currentPage, (value) => {
reactData.inpCurrPage = value
})
const renderVN = () => {
const { align, layouts, className } = props
const childNodes = []

View File

@@ -3,7 +3,7 @@ import XEUtils from 'xe-utils'
import GlobalConfig from '../../v-x-e-table/src/conf'
import { VXETable } from '../../v-x-e-table'
import { mergeBodyMethod, getRowid, getPropClass, removeScrollListener, restoreScrollListener, XEBodyScrollElement } from './util'
import { browse, updateCellTitle } from '../../tools/dom'
import { browse, updateCellTitle, setScrollLeftAndTop } from '../../tools/dom'
import { isEnableConf } from '../../tools/utils'
import { VxeTablePrivateMethods, VxeTableConstructor, VxeTableDefines, VxeTableMethods, VxeGlobalRendererHandles, VxeColumnPropTypes, SizeType } from '../../../types/all'
@@ -418,7 +418,7 @@ export default defineComponent({
* 同步滚动条
*/
let scrollProcessTimeout: any
const syncBodyScroll = (scrollTop: number, elem1: XEBodyScrollElement | null, elem2: XEBodyScrollElement | null) => {
const syncBodyScroll = (fixedType: VxeColumnPropTypes.Fixed, scrollTop: number, elem1: XEBodyScrollElement | null, elem2: XEBodyScrollElement | null) => {
if (elem1 || elem2) {
if (elem1) {
removeScrollListener(elem1)
@@ -429,9 +429,32 @@ export default defineComponent({
elem2.scrollTop = scrollTop
}
clearTimeout(scrollProcessTimeout)
scrollProcessTimeout = setTimeout(function () {
scrollProcessTimeout = setTimeout(() => {
const tableBody = refTableBody.value
const leftBody = refTableLeftBody.value
const rightBody = refTableRightBody.value
const bodyElem = tableBody.$el as XEBodyScrollElement
const leftElem = leftBody ? leftBody.$el as XEBodyScrollElement : null
const rightElem = rightBody ? rightBody.$el as XEBodyScrollElement : null
restoreScrollListener(elem1)
restoreScrollListener(elem2)
// 检查滚动条是的同步
let targetTop = bodyElem.scrollTop
let targetLeft = bodyElem.scrollLeft
if (fixedType === 'left') {
if (leftElem) {
targetTop = leftElem.scrollTop
targetLeft = leftElem.scrollLeft
}
} else if (fixedType === 'right') {
if (rightElem) {
targetTop = rightElem.scrollTop
targetLeft = rightElem.scrollLeft
}
}
setScrollLeftAndTop(bodyElem, targetLeft, targetTop)
setScrollLeftAndTop(leftElem, targetLeft, targetTop)
setScrollLeftAndTop(rightElem, targetLeft, targetTop)
}, 300)
}
}
@@ -475,10 +498,10 @@ export default defineComponent({
}
if (leftElem && fixedType === 'left') {
scrollTop = leftElem.scrollTop
syncBodyScroll(scrollTop, bodyElem, rightElem)
syncBodyScroll(fixedType, scrollTop, bodyElem, rightElem)
} else if (rightElem && fixedType === 'right') {
scrollTop = rightElem.scrollTop
syncBodyScroll(scrollTop, bodyElem, leftElem)
syncBodyScroll(fixedType, scrollTop, bodyElem, leftElem)
} else {
if (isRollX) {
if (headerElem) {
@@ -491,7 +514,7 @@ export default defineComponent({
if (leftElem || rightElem) {
$xetable.checkScrolling()
if (isRollY) {
syncBodyScroll(scrollTop, leftElem, rightElem)
syncBodyScroll(fixedType, scrollTop, leftElem, rightElem)
}
}
}

View File

@@ -1,6 +1,6 @@
import { defineComponent, getCurrentInstance, h, createCommentVNode, ComponentPublicInstance, resolveComponent, ComponentOptions, reactive, ref, Ref, provide, inject, nextTick, onActivated, onDeactivated, onBeforeUnmount, onUnmounted, watch, computed, ComputedRef, onMounted } from 'vue'
import XEUtils from 'xe-utils'
import { browse, isPx, isScale, hasClass, addClass, removeClass, getEventTargetNode, getPaddingTopBottomSize, setScrollTop, setScrollLeft } from '../../tools/dom'
import { browse, isPx, isScale, hasClass, addClass, removeClass, getEventTargetNode, getPaddingTopBottomSize, setScrollTop, setScrollLeft, isNodeElement } from '../../tools/dom'
import { warnLog, errLog, getLog, getLastZIndex, nextZIndex, hasChildrenList, getFuncText, isEnableConf, formatText, eqEmptyValue } from '../../tools/utils'
import { createResizeEvent, XEResizeObserver } from '../../tools/resize'
import { GlobalEvent, hasEventKey, EVENT_KEYS } from '../../tools/event'
@@ -1420,7 +1420,7 @@ export default defineComponent({
}
} else if (layout === 'body') {
const emptyBlockElem = elemStore[`${name}-${layout}-emptyBlock`]
if (wrapperElem) {
if (isNodeElement(wrapperElem)) {
if (customMaxHeight) {
wrapperElem.style.maxHeight = `${fixedType ? customMaxHeight - headerHeight - (showFooter ? 0 : scrollbarHeight) : customMaxHeight - headerHeight}px`
} else {
@@ -1434,7 +1434,7 @@ export default defineComponent({
// 如果是固定列
if (fixedWrapperElem) {
if (wrapperElem) {
if (isNodeElement(wrapperElem)) {
wrapperElem.style.top = `${headerHeight}px`
}
fixedWrapperElem.style.height = `${(customHeight > 0 ? customHeight - headerHeight - footerHeight : tableHeight) + headerHeight + footerHeight - scrollbarHeight * (showFooter ? 2 : 1)}px`
@@ -1498,7 +1498,7 @@ export default defineComponent({
}
tWidth = tableColumn.reduce((previous, column) => previous + column.renderWidth, 0)
if (wrapperElem) {
if (isNodeElement(wrapperElem)) {
// 如果是固定列
if (fixedWrapperElem) {
wrapperElem.style.top = `${customHeight > 0 ? customHeight - footerHeight : tableHeight + headerHeight}px`
@@ -2387,11 +2387,29 @@ export default defineComponent({
})
},
/**
* 手动处理数据
* 手动处理数据,用于手动排序与筛选
* 对于手动更改了排序、筛选...等条件后需要重新处理数据时可能会用到
*/
updateData () {
return tablePrivateMethods.handleTableData(true).then(tableMethods.updateFooter).then(() => tableMethods.recalculate())
const { scrollXLoad, scrollYLoad } = reactData
return tablePrivateMethods.handleTableData(true).then(() => {
tableMethods.updateFooter()
if (scrollXLoad || scrollYLoad) {
if (scrollXLoad) {
tablePrivateMethods.updateScrollXSpace()
}
if (scrollYLoad) {
tablePrivateMethods.updateScrollYSpace()
}
return tableMethods.refreshScroll()
}
}).then(() => {
tablePrivateMethods.updateCellAreas()
return tableMethods.recalculate(true)
}).then(() => {
// 存在滚动行为未结束情况
setTimeout(() => $xetable.recalculate(), 50)
})
},
/**
* 重新加载数据,不会清空表格状态
@@ -2924,16 +2942,22 @@ export default defineComponent({
const leftBodyElem = leftBody ? leftBody.$el as HTMLDivElement : null
const rightBodyElem = rightBody ? rightBody.$el as HTMLDivElement : null
const tableFooterElem = tableFooter ? tableFooter.$el as HTMLDivElement : null
// 还原滚动条位置
if (lastScrollLeft || lastScrollTop) {
return restoreScrollLocation($xetable, lastScrollLeft, lastScrollTop)
}
// 重置
setScrollTop(tableBodyElem, lastScrollTop)
setScrollTop(leftBodyElem, lastScrollTop)
setScrollTop(rightBodyElem, lastScrollTop)
setScrollLeft(tableFooterElem, lastScrollLeft)
return nextTick()
return new Promise(resolve => {
// 还原滚动条位置
if (lastScrollLeft || lastScrollTop) {
return restoreScrollLocation($xetable, lastScrollLeft, lastScrollTop).then(resolve).then(() => {
// 存在滚动行为未结束情况
setTimeout(resolve, 30)
})
}
// 重置
setScrollTop(tableBodyElem, lastScrollTop)
setScrollTop(leftBodyElem, lastScrollTop)
setScrollTop(rightBodyElem, lastScrollTop)
setScrollLeft(tableFooterElem, lastScrollLeft)
// 存在滚动行为未结束情况
setTimeout(resolve, 30)
})
},
/**
* 计算单元格列宽,动态分配可用剩余空间

View File

@@ -89,6 +89,13 @@ export function setScrollLeft (elem: HTMLElement | null, scrollLeft: number) {
}
}
export function setScrollLeftAndTop (elem: HTMLElement | null, scrollLeft: number, scrollTop: number) {
if (elem) {
elem.scrollLeft = scrollLeft
elem.scrollTop = scrollTop
}
}
export function updateCellTitle (overflowElem: any, column: any) {
const content = column.type === 'html' ? overflowElem.innerText : overflowElem.textContent
if (overflowElem.getAttribute('title') !== content) {
@@ -146,3 +153,7 @@ export function triggerEvent (targetElem: Element, type: string) {
targetElem.dispatchEvent(new Event(type))
}
}
export function isNodeElement (elem: Element) {
return elem && elem.nodeType === 1
}

4
types/pager.d.ts vendored
View File

@@ -21,6 +21,10 @@ export interface PagerPrivateRef {
}
export interface VxePagerPrivateRef extends PagerPrivateRef { }
export interface PagerReactData {
inpCurrPage: string | number;
}
export type VxePagerProps = {
size?: VxePagerPropTypes.Size;
/**

13
types/table.d.ts vendored
View File

@@ -104,7 +104,7 @@ export interface TablePublicMethods {
*/
syncData(): Promise<any>;
/**
* 手动处理数据
* 手动处理数据,用于手动排序与筛选
* 对于手动更改了排序、筛选...等条件后需要重新处理数据时可能会用到
*/
updateData(): Promise<any>;
@@ -2098,10 +2098,13 @@ export namespace VxeTableDefines {
* 该属性已废弃,该属性被 field 替换
* @deprecated
*/
property: string;
property: VxeColumnPropTypes.Field;
/**
* 公开属性
*/
type: VxeColumnPropTypes.Type;
field: VxeColumnPropTypes.Type;
field: VxeColumnPropTypes.Field;
title: VxeColumnPropTypes.Title;
width: VxeColumnPropTypes.Width;
minWidth: VxeColumnPropTypes.MinWidth;
@@ -2136,6 +2139,10 @@ export namespace VxeTableDefines {
params: VxeColumnPropTypes.Params;
slots: VxeColumnPropTypes.Slots;
/**
* 以下内部属性
* 内部属性随时都会调整,不应该被使用
*/
id: string;
parentId: string;
level: number;