feat(VnSelect): refs #7136 add scroll
gitea/salix-front/pipeline/pr-dev This commit looks good
Details
gitea/salix-front/pipeline/pr-dev This commit looks good
Details
This commit is contained in:
parent
89dfc0aff5
commit
bf4bee0f95
|
@ -1,7 +1,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, toRefs, computed, watch, onMounted, useAttrs } from 'vue';
|
import { ref, toRefs, computed, watch, onMounted, useAttrs } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import FetchData from 'src/components/FetchData.vue';
|
import { useArrayData } from 'src/composables/useArrayData';
|
||||||
import { useValidator } from 'src/composables/useValidator';
|
import { useValidator } from 'src/composables/useValidator';
|
||||||
const emit = defineEmits(['update:modelValue', 'update:options', 'remove']);
|
const emit = defineEmits(['update:modelValue', 'update:options', 'remove']);
|
||||||
|
|
||||||
|
@ -86,7 +86,12 @@ const $props = defineProps({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
dataKey: {
|
||||||
|
type: String,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { validations } = useValidator();
|
const { validations } = useValidator();
|
||||||
const requiredFieldRule = (val) => validations().required($attrs.required, val);
|
const requiredFieldRule = (val) => validations().required($attrs.required, val);
|
||||||
const $attrs = useAttrs();
|
const $attrs = useAttrs();
|
||||||
|
@ -97,14 +102,14 @@ const { optionLabel, optionValue, optionFilter, optionFilterValue, options, mode
|
||||||
const myOptions = ref([]);
|
const myOptions = ref([]);
|
||||||
const myOptionsOriginal = ref([]);
|
const myOptionsOriginal = ref([]);
|
||||||
const vnSelectRef = ref();
|
const vnSelectRef = ref();
|
||||||
const dataRef = ref();
|
|
||||||
const lastVal = ref();
|
const lastVal = ref();
|
||||||
const noOneText = t('globals.noOne');
|
const noOneText = t('globals.noOne');
|
||||||
const noOneOpt = ref({
|
const noOneOpt = ref({
|
||||||
[optionValue.value]: false,
|
[optionValue.value]: false,
|
||||||
[optionLabel.value]: noOneText,
|
[optionLabel.value]: noOneText,
|
||||||
});
|
});
|
||||||
|
const isLoading = ref(false);
|
||||||
|
const useURL = computed(() => $props.url);
|
||||||
const value = computed({
|
const value = computed({
|
||||||
get() {
|
get() {
|
||||||
return $props.modelValue;
|
return $props.modelValue;
|
||||||
|
@ -114,6 +119,9 @@ const value = computed({
|
||||||
emit('update:modelValue', value);
|
emit('update:modelValue', value);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const arrayDataKey =
|
||||||
|
$props.dataKey ?? ($props.url?.length > 0 ? $props.url : $attrs.name ?? $attrs.label);
|
||||||
|
const arrayData = useArrayData(arrayDataKey, { url: $props.url, searchUrl: false });
|
||||||
|
|
||||||
watch(options, (newValue) => {
|
watch(options, (newValue) => {
|
||||||
setOptions(newValue);
|
setOptions(newValue);
|
||||||
|
@ -128,7 +136,7 @@ watch(modelValue, async (newValue) => {
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
setOptions(options.value);
|
setOptions(options.value);
|
||||||
if ($props.url && $props.modelValue && !findKeyInOptions())
|
if (useURL.value && $props.modelValue && !findKeyInOptions())
|
||||||
fetchFilter($props.modelValue);
|
fetchFilter($props.modelValue);
|
||||||
if ($props.focusOnMount) setTimeout(() => vnSelectRef.value.showPopup(), 300);
|
if ($props.focusOnMount) setTimeout(() => vnSelectRef.value.showPopup(), 300);
|
||||||
});
|
});
|
||||||
|
@ -166,7 +174,7 @@ function filter(val, options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchFilter(val) {
|
async function fetchFilter(val) {
|
||||||
if (!$props.url || !dataRef.value) return;
|
if (!$props.url) return;
|
||||||
|
|
||||||
const { fields, include, sortBy, limit } = $props;
|
const { fields, include, sortBy, limit } = $props;
|
||||||
const key =
|
const key =
|
||||||
|
@ -188,8 +196,8 @@ async function fetchFilter(val) {
|
||||||
const fetchOptions = { where, include, limit };
|
const fetchOptions = { where, include, limit };
|
||||||
if (fields) fetchOptions.fields = fields;
|
if (fields) fetchOptions.fields = fields;
|
||||||
if (sortBy) fetchOptions.order = sortBy;
|
if (sortBy) fetchOptions.order = sortBy;
|
||||||
|
arrayData.reset(['skip', 'filter.skip', 'page']);
|
||||||
return dataRef.value.fetch(fetchOptions);
|
return (await arrayData.applyFilter({ filter: fetchOptions }))?.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function filterHandler(val, update) {
|
async function filterHandler(val, update) {
|
||||||
|
@ -229,20 +237,25 @@ function nullishToTrue(value) {
|
||||||
|
|
||||||
const getVal = (val) => ($props.useLike ? { like: `%${val}%` } : val);
|
const getVal = (val) => ($props.useLike ? { like: `%${val}%` } : val);
|
||||||
|
|
||||||
|
async function onScroll({ to, direction, from, index }) {
|
||||||
|
const lastIndex = myOptions.value.length - 1;
|
||||||
|
|
||||||
|
if (from === 0 && index === 0) return;
|
||||||
|
if (!useURL.value && !$props.fetchRef) return;
|
||||||
|
if (direction === 'decrease') return;
|
||||||
|
if (to === lastIndex && arrayData.store.hasMoreData && !isLoading.value) {
|
||||||
|
isLoading.value = true;
|
||||||
|
await arrayData.loadMore();
|
||||||
|
setOptions(arrayData.store.data);
|
||||||
|
vnSelectRef.value.scrollTo(lastIndex);
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({ opts: myOptions });
|
defineExpose({ opts: myOptions });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<FetchData
|
|
||||||
ref="dataRef"
|
|
||||||
:url="$props.url"
|
|
||||||
@on-fetch="(data) => setOptions(data)"
|
|
||||||
:where="where || { [optionValue]: value }"
|
|
||||||
:limit="limit"
|
|
||||||
:sort-by="sortBy"
|
|
||||||
:fields="fields"
|
|
||||||
:params="params"
|
|
||||||
/>
|
|
||||||
<QSelect
|
<QSelect
|
||||||
v-model="value"
|
v-model="value"
|
||||||
:options="myOptions"
|
:options="myOptions"
|
||||||
|
@ -261,6 +274,9 @@ defineExpose({ opts: myOptions });
|
||||||
:rules="mixinRules"
|
:rules="mixinRules"
|
||||||
virtual-scroll-slice-size="options.length"
|
virtual-scroll-slice-size="options.length"
|
||||||
hide-bottom-space
|
hide-bottom-space
|
||||||
|
:input-debounce="useURL ? '300' : '0'"
|
||||||
|
:loading="isLoading"
|
||||||
|
@virtual-scroll="onScroll"
|
||||||
>
|
>
|
||||||
<template v-if="isClearable" #append>
|
<template v-if="isClearable" #append>
|
||||||
<QIcon
|
<QIcon
|
||||||
|
|
|
@ -53,7 +53,7 @@ defineProps({
|
||||||
<QItemSection>
|
<QItemSection>
|
||||||
<VnSelect
|
<VnSelect
|
||||||
url="Workers/activeWithInheritedRole"
|
url="Workers/activeWithInheritedRole"
|
||||||
:filter="{ where: { role: 'salesPerson' } }"
|
:where="{ role: 'salesPerson' }"
|
||||||
auto-load
|
auto-load
|
||||||
:label="t('Salesperson')"
|
:label="t('Salesperson')"
|
||||||
v-model="params.salesPersonFk"
|
v-model="params.salesPersonFk"
|
||||||
|
@ -67,7 +67,6 @@ defineProps({
|
||||||
dense
|
dense
|
||||||
outlined
|
outlined
|
||||||
rounded
|
rounded
|
||||||
:input-debounce="0"
|
|
||||||
/>
|
/>
|
||||||
</QItemSection>
|
</QItemSection>
|
||||||
</QItem>
|
</QItem>
|
||||||
|
|
Loading…
Reference in New Issue