diff --git a/src/components/divider/index.ts b/src/components/divider/index.ts index 67932124..1b853764 100644 --- a/src/components/divider/index.ts +++ b/src/components/divider/index.ts @@ -13,7 +13,7 @@ const AtDivider = defineComponent({ default: '', }, height: { - type: Number as PropType, + type: [Number, String] as PropType, default: 0, }, fontColor: { @@ -21,7 +21,7 @@ const AtDivider = defineComponent({ default: '', }, fontSize: { - type: Number as PropType, + type: [Number, String] as PropType, default: 0, }, lineColor: { diff --git a/src/components/virtual-scroll/index.ts b/src/components/virtual-scroll/index.ts index 18437a77..100e9e9d 100644 --- a/src/components/virtual-scroll/index.ts +++ b/src/components/virtual-scroll/index.ts @@ -106,20 +106,26 @@ const AtVirtualScroll = defineComponent({ watch(() => props.height, updateFirstAndLast) watch(() => props.itemHeight, updateFirstAndLast) - watch(() => props.scrollIntoItem, (index, prevIndex) => { - const parsedIndex = parseInt(`${index}`, 10) - if (parsedIndex >= 0 && parsedIndex < props.items.length) { + watch(() => props.scrollIntoItem, (itemIndex, prevItemIndex) => { + let parsedIndex = parseInt(`${itemIndex || 0}`, 10) + + // make sure index is within length of items + parsedIndex = Math.min(props.items.length - 1, Math.max(0, parsedIndex)) + + scrollTop.value = parsedIndex * __itemHeight.value + updateFirstAndLast() + }) + + onMounted(() => { + if (Boolean(props.scrollIntoItem)) { + let parsedIndex = parseInt(`${props.scrollIntoItem || 0}`, 10) scrollTop.value = parsedIndex * __itemHeight.value updateFirstAndLast() } else { - warn(`index should not exceed the length of items: ${index}`) + last.value = getLast(0) } }) - onMounted(() => { - last.value = getLast(0) - }) - function getChildren() { return props.items.slice( firstToRender.value, @@ -163,14 +169,23 @@ const AtVirtualScroll = defineComponent({ } return () => { - const content = h(View, { - class: 'at-virtual-scroll__container', - style: { - height: convertToUnit((props.items.length * __itemHeight.value)), - } - }, { default: () => getChildren() }) - - return h(ScrollView, mergeProps( + const content = h(View, null, { + default: () => [ + h(View, { + class: 'at-virtual-scroll__container', + style: { + height: convertToUnit((props.items.length * __itemHeight.value)), + } + }, { default: () => getChildren() }), + + h(View, { + class: 'at-virtual-scroll__footer' + }, { default: () => slots.footer && slots.footer() }), + ] + }) + + + const scrollViewNode = h(ScrollView, mergeProps( isWeb.value ? { scrollTop: scrollTop.value @@ -190,6 +205,15 @@ const AtVirtualScroll = defineComponent({ onScrollToUpper: props.onReachTop, onScrollToLower: props.onReachBottom, }), { default: () => [content] }) + + return h(View, null, { + default: () => [ + h(View, { + class: 'at-virtual-scroll__header', + }, { default: () => slots.header && slots.header() }), + scrollViewNode, + ] + }) } } }) diff --git a/src/pages/advanced/virtual-scroll/index.scss b/src/pages/advanced/virtual-scroll/index.scss index 686047b2..bf4b4401 100644 --- a/src/pages/advanced/virtual-scroll/index.scss +++ b/src/pages/advanced/virtual-scroll/index.scss @@ -3,4 +3,9 @@ margin-bottom: 12px; color: #333; font-size: 24px; +} + +.at-switch__title { + font-size: 24px; + color: red; } \ No newline at end of file diff --git a/src/pages/advanced/virtual-scroll/index.vue b/src/pages/advanced/virtual-scroll/index.vue index bfaf21ea..01772ca6 100644 --- a/src/pages/advanced/virtual-scroll/index.vue +++ b/src/pages/advanced/virtual-scroll/index.vue @@ -3,113 +3,206 @@ class="virtual-scroll-page" header-title="VirtualScroll 虚拟列表" > - + - - - - - - - - - - - 随机跳转至第 N 条数据 + * 长列表组件的高度, 用作 css 样式值 + + + + itemHeight + + + + + * 显示列表单项的高度,单位为 px + + + + items.length + + + + + * 列表长度 + + + + + + + + + + + + + + + + + + + + @@ -120,13 +213,18 @@ import { defineComponent, computed, ref } from 'vue' import { AtFab, AtFlex, + AtCard, AtButton, AtSlider, + AtSwitch, + AtDivider, AtFlexItem, AtListItem, + AtLoadMore, AtInputNumber, AtVirtualScroll, AtSwipeAction, + AtSearchBar, } from '@/components/index' import { Page, Panel, ExampleItem } from '../../components/demo-page' @@ -139,13 +237,18 @@ export default defineComponent({ components: { AtFab, AtFlex, + AtCard, AtButton, AtSlider, + AtDivider, + AtSwitch, AtFlexItem, AtListItem, + AtLoadMore, AtInputNumber, AtVirtualScroll, AtSwipeAction, + AtSearchBar, Page, Panel, ExampleItem @@ -153,11 +256,15 @@ export default defineComponent({ setup() { const benched = ref(5) - const length = ref(7000) - const height = ref(300) + const viewportRows = ref(5) + const length = ref(20) const itemHeight = ref(80) - const elRef = ref(null) - const toItem = ref(0) + const height = ref(300) + const toItem = ref(10) + const searchbarValue = ref(undefined) + const reachTopOn = ref(true) + const reachBottomOn = ref(true) + const loadMore = ref({ status: 'loading', show: false }) const colors = ref(['#2196F3', '#90CAF9', '#64B5F6', '#42A5F5', '#1E88E5', '#1976D2', '#1565C0', '#0D47A1', '#82B1FF', '#448AFF', '#2979FF', '#2962FF']) const names = ref(['Oliver', 'Jake', 'Noah', 'James', 'Jack', 'Connor', 'Liam', 'John', 'Harry', 'Callum', 'Mason', 'Robert', 'Jacob', 'Jacob', 'Jacob', 'Michael', 'Charlie', 'Kyle', 'William', 'William', 'Thomas', 'Joe', 'Ethan', 'David', 'George', 'Reece', 'Michael', 'Richard', 'Oscar', 'Rhys', 'Alexander', 'Joseph', 'James', 'Charlie', 'James', 'Charles', 'William', 'Damian', 'Daniel', 'Thomas', 'Amelia', 'Margaret', 'Emma', 'Mary', 'Olivia', 'Samantha', 'Olivia', 'Patricia', 'Isla', 'Bethany']) @@ -178,23 +285,12 @@ export default defineComponent({ } ]) - const items = computed(() => { - return Array.from({ - length: length.value - }, (_, v) => v + 1) - }) - - const directoryLength = ref(20) - const directoryItemHeight = computed(() => { - return Taro.getEnv() === Taro.ENV_TYPE.WEB ? 100 : 64 - }) - const directoryItems = computed(() => { const namesLength = names.value.length const surnamesLength = surnames.value.length const colorsLength = colors.value.length - return Array.from({ length: directoryLength.value }, () => { + return Array.from({ length: length.value }, () => { const name = names.value[genRandomIndex(namesLength)] const surname = surnames.value[genRandomIndex(surnamesLength)] @@ -214,7 +310,7 @@ export default defineComponent({ showCancel: true, success: (res) => { if (res.confirm) { - directoryLength.value -= 1 + length.value -= 1 } else { return } @@ -249,58 +345,96 @@ export default defineComponent({ height.value = value } + function handleViewportChange(value) { + viewportRows.value = value + } + function handleReachTop() { - Taro.showToast({ - title: `reachTop 刷新数据`, - icon: 'loading', - duration: genRandomIndex(3000), - success(_) { - // 模拟刷新数据 -> - const randIndex = genRandomIndex(colors.value.length) - names.value.push(names.value[randIndex]) - surnames.value.push(surnames.value[randIndex]) - directoryLength.value = 20 - } - }) + if (reachTopOn.value) { + Taro.showToast({ + title: `刷新数据中...`, + icon: 'loading', + duration: genRandomIndex(3000), + success(_) { + // 模拟刷新数据 -> + const randIndex = genRandomIndex(colors.value.length) + names.value.push(names.value[randIndex]) + surnames.value.push(surnames.value[randIndex]) + length.value = 20 + } + }) + } else { + Taro.showToast({ + title: `无触顶逻辑`, + icon: 'none', + duration: genRandomIndex(3000), + }) + } } function handleReachBottom() { - Taro.showToast({ - title: `reachBottom 加载数据`, - icon: 'loading', - duration: genRandomIndex(3000), - success: (_) => { - // 模拟加载数据 -> 附加 10 条数据 - directoryLength.value += 10 - } - }) + loadMore.value.show = true + if (reachBottomOn.value) { + loadMore.value.status = 'loading' + + Taro.showToast({ + title: `reachBottom 加载数据`, + icon: 'loading', + duration: genRandomIndex(3000), + success: (_) => { + // 模拟加载数据 -> 附加 10 条数据 + setTimeout(() => { + length.value += 10 + loadMore.value.show = false + }, 3000) + } + }) + } else { + loadMore.value.status = 'noMore' + } + } + + function handleActionClick() { + toItem.value = searchbarValue.value || 0 + } + + function handleChange(value) { + searchbarValue.value = value + } + + function handleReachToOn(value) { + reachTopOn.value = value } - function scrollToItem() { - toItem.value = genRandomIndex(directoryLength.value) - console.log(`随机跳转至: 第 ${toItem.value + 1} 条`) + function handleReachBottomOn(value) { + reachBottomOn.value = value } return { + loadMore, + reachTopOn, + reachBottomOn, + searchbarValue, toItem, - elRef, OPTIONS, benched, - items, + viewportRows, length, height, itemHeight, directoryItems, - directoryLength, - directoryItemHeight, handleClick, - scrollToItem, handleBenchChange, handleLengthChange, handleHeightChange, handleItemHeightChange, handleReachTop, - handleReachBottom + handleReachBottom, + handleChange, + handleActionClick, + handleViewportChange, + handleReachToOn, + handleReachBottomOn } } }) diff --git a/src/style/components/virtual-scroll.scss b/src/style/components/virtual-scroll.scss index d4ceee6c..1a23f2df 100644 --- a/src/style/components/virtual-scroll.scss +++ b/src/style/components/virtual-scroll.scss @@ -6,10 +6,18 @@ overflow: auto; position: relative; + &__header { + display: block; + } + &__container { display: block; } + &__footer { + display: block; + } + &__item { left: 0; position: absolute; diff --git a/src/utils/common.ts b/src/utils/common.ts index 3a371825..615ccd92 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -19,17 +19,14 @@ const getEnvs = (): ENVS => { } } -function pxTransform(size: number): string { +function pxTransform(size: number, designWidth?: number): string { if (!size) return '' - const designWidth = 750 - const deviceRatio = { - 640: 2.34 / 2, - 750: 1, - 828: 1.81 / 2, + if (!designWidth) { + designWidth = 750 } - return `${size / deviceRatio[designWidth]}rpx` + return Taro.pxTransform(size, designWidth) } function delay(delayTime = 500): Promise {