update list load

This commit is contained in:
Timi
2026-09-09 22:57:57 +08:00
parent 8e77b114f9
commit 5991cbc1d4
+56 -69
View File
@@ -8,29 +8,28 @@
@right-button-click="confirmSelection" @right-button-click="confirmSelection"
> >
<div class="customer-selector"> <div class="customer-selector">
<div class="search-wrapper"> <div class="search-wrap">
<div class="search-row"> <t-input
<t-input v-model.trim="keyword"
v-model.trim="keyword" class="search"
class="search" borderless
borderless clearable
clearable placeholder="搜索编码、姓名(拼音)、电话"
placeholder="搜索编码、姓名(拼音)、电话" />
/> <t-button
<t-button v-if="props.enableScan"
v-if="props.enableScan" class="scan"
class="scan-button" size="small"
theme="primary" theme="primary"
@click="toggleScanner" @click="toggleScanner"
> >
<template #icon> <template #icon>
<t-icon name="scan" /> <t-icon name="scan" />
</template> </template>
<span v-text="scannerVisible ? '关闭扫码' : '扫码'" /> <span v-text="scannerVisible ? '关闭扫码' : '扫码'" />
</t-button> </t-button>
</div>
</div> </div>
<div v-if="scannerVisible" class="scanner-wrapper"> <div v-if="scannerVisible" class="scanner-wrap">
<code-scanner <code-scanner
:visible="scannerVisible" :visible="scannerVisible"
:status-text="scanStatus" :status-text="scanStatus"
@@ -39,7 +38,13 @@
@status-change="handleScanStatusChange" @status-change="handleScanStatusChange"
/> />
</div> </div>
<div ref="resultListRef" class="result-list"> <t-pull-down-refresh
class="result-list"
v-model="isRefreshing"
:refresh-timeout="30000"
@refresh="refresh"
@scrolltolower="loadCustomers"
>
<customer-list <customer-list
:customer-list="customerOptions" :customer-list="customerOptions"
:selected-id-list="draftSelectedIdList" :selected-id-list="draftSelectedIdList"
@@ -47,13 +52,13 @@
:empty-text="loading ? '正在加载客户' : '未找到匹配客户'" :empty-text="loading ? '正在加载客户' : '未找到匹配客户'"
@select="toggleCustomer" @select="toggleCustomer"
/> />
</div> </t-pull-down-refresh>
</div> </div>
</bottom-popup> </bottom-popup>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Scroller } from "timi-web"; import { Toolkit } from "timi-web";
import { BottomPopup } from "timi-tdesign-mobile"; import { BottomPopup } from "timi-tdesign-mobile";
import CustomerAPI from "@/api/CustomerAPI"; import CustomerAPI from "@/api/CustomerAPI";
import CodeScanner from "@/components/CodeScanner.vue"; import CodeScanner from "@/components/CodeScanner.vue";
@@ -86,18 +91,16 @@ const emit = defineEmits<{
}>(); }>();
const pageSize = 20; const pageSize = 20;
const scrollListenerName = "CustomerSelector";
const keyword = ref(""); const keyword = ref("");
const loading = ref(false); const loading = ref(false);
const noMore = ref(false); const noMore = ref(false);
const pageIndex = ref(0); const pageIndex = ref(0);
const totalCount = ref(0); const totalCount = ref(0);
const customerOptions = ref<Customer[]>([]); const customerOptions = ref<Customer[]>([]);
const resultListRef = ref<HTMLElement>(); const isRefreshing = ref(false);
const draftSelectedIdList = ref<string[]>([]); const draftSelectedIdList = ref<string[]>([]);
const selectedCustomerCache = new Map<string, Customer>(); const selectedCustomerCache = new Map<string, Customer>();
let searchToken = 0; let searchToken = 0;
let keywordTimer: number | undefined;
const scannerVisible = ref(false); const scannerVisible = ref(false);
const scanStatus = ref("点击扫码开始识别"); const scanStatus = ref("点击扫码开始识别");
let latestDecodedText = ""; let latestDecodedText = "";
@@ -122,6 +125,18 @@ async function reloadCustomers(): Promise<void> {
await loadCustomers(); await loadCustomers();
} }
async function refresh(): Promise<void> {
try {
await reloadCustomers();
} finally {
isRefreshing.value = false;
}
}
const reloadByKeyword = Toolkit.debounce(async () => {
await reloadCustomers();
}, false);
async function loadCustomers(): Promise<void> { async function loadCustomers(): Promise<void> {
if (loading.value || noMore.value) { if (loading.value || noMore.value) {
return; return;
@@ -161,19 +176,6 @@ async function loadCustomers(): Promise<void> {
} }
} }
async function bindScrollListener(): Promise<void> {
if (!props.visible || 0 === customerOptions.value.length) {
Scroller.removeListener(scrollListenerName);
return;
}
await nextTick();
Scroller.addBottomListener(scrollListenerName, loadCustomers, {
distance: 48,
canLoad: () => props.visible && !loading.value && !noMore.value,
getElement: () => resultListRef.value
});
}
function getModelValueIdList(): string[] { function getModelValueIdList(): string[] {
const value = props.modelValue; const value = props.modelValue;
return (Array.isArray(value) ? value : [value]).filter((item): item is string => Boolean(item)); return (Array.isArray(value) ? value : [value]).filter((item): item is string => Boolean(item));
@@ -283,13 +285,11 @@ watch(() => props.visible, async (value) => {
latestDecodedText = ""; latestDecodedText = "";
scanToken++; scanToken++;
scanStatus.value = "点击扫码开始识别"; scanStatus.value = "点击扫码开始识别";
window.clearTimeout(keywordTimer); reloadByKeyword.cancel();
Scroller.removeListener(scrollListenerName);
return; return;
} }
syncDraftSelection(); syncDraftSelection();
await reloadCustomers(); await reloadCustomers();
await bindScrollListener();
}); });
watch(() => props.enableScan, (value) => { watch(() => props.enableScan, (value) => {
@@ -305,27 +305,17 @@ watch(() => props.storeId, async () => {
return; return;
} }
await reloadCustomers(); await reloadCustomers();
await bindScrollListener();
}); });
watch(keyword, async () => { watch(keyword, () => {
if (!props.visible) { if (!props.visible) {
return; return;
} }
window.clearTimeout(keywordTimer); reloadByKeyword();
keywordTimer = window.setTimeout(async () => {
await reloadCustomers();
await bindScrollListener();
}, 300);
});
watch(() => customerOptions.value.length, async () => {
await bindScrollListener();
}); });
onUnmounted(() => { onUnmounted(() => {
window.clearTimeout(keywordTimer); reloadByKeyword.cancel();
Scroller.removeListener(scrollListenerName);
}); });
</script> </script>
@@ -335,34 +325,31 @@ onUnmounted(() => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.search-wrapper { .search-wrap {
gap: .5rem;
display: flex;
z-index: 1; z-index: 1;
padding: 1rem 0 .5rem; padding: .5rem;
box-shadow: 0 4px 4px rgba(0, 0, 0, .05); box-shadow: 0 4px 4px rgba(0, 0, 0, .05);
align-items: center;
.search-row {
gap: .5rem;
display: flex;
padding: 0 1rem;
align-items: center;
}
.search { .search {
--td-input-vertical-padding: 8px 14px; --td-input-vertical-padding: 8px 14px;
flex: 1; flex: 1;
border: 1px solid var(--td-component-border); border: 1px solid var(--td-component-border);
padding: .25rem .5rem;
min-width: 0; min-width: 0;
border-radius: .875rem; border-radius: .75rem;
} }
.scan-button { .scan {
--td-button-border-radius: .75rem; --td-button-border-radius: .75rem;
flex-shrink: 0; flex-shrink: 0;
} }
} }
.scanner-wrapper { .scanner-wrap {
padding: .5rem 1rem 0; padding: .5rem 1rem 0;
} }