20260810172235
This commit is contained in:
1 parent
c4891538a7
commit
ed5ab3885d
35 files changed
+1314
-653
No files matched your search
@@ -0,0 +1,174 @@
|
||||
import { ref, watch } from "vue";
|
||||
import { useDebounceFn } from "@vueuse/core";
|
||||
import { pageDataApi } from "@/services/api";
|
||||
|
||||
export interface LookupOptionsConfig {
|
||||
/** 后端表名/视图名(必填) */
|
||||
viewName: string;
|
||||
/** 显示文本字段名,默认 "b_name" */
|
||||
labelKey?: string;
|
||||
/** 取值字段名,默认 "b_id" */
|
||||
valueKey?: string;
|
||||
/** 额外的固定查询条件 */
|
||||
searchCondition?: string;
|
||||
/** 排序规则 */
|
||||
orderBy?: string;
|
||||
/** 需要返回的字段列表 */
|
||||
searchColumns?: string[];
|
||||
/** 每页加载数量,默认 20 */
|
||||
pageSize?: number;
|
||||
/** 搜索防抖延迟(ms),默认 300 */
|
||||
searchDelay?: number;
|
||||
}
|
||||
|
||||
export function useLookupOptions(config: LookupOptionsConfig) {
|
||||
const {
|
||||
viewName,
|
||||
labelKey = "b_name",
|
||||
valueKey = "b_id",
|
||||
searchCondition = "",
|
||||
orderBy = "",
|
||||
searchColumns = [],
|
||||
pageSize = 20,
|
||||
searchDelay = 300,
|
||||
} = config;
|
||||
|
||||
const rows = ref<Record<string, any>[]>([]);
|
||||
const loading = ref(false);
|
||||
const total = ref(0);
|
||||
const keyword = ref("");
|
||||
// Keep the unfiltered first page available while a later open is refreshing.
|
||||
const defaultRows = ref<Record<string, any>[]>([]);
|
||||
const defaultTotal = ref(0);
|
||||
|
||||
let requestVersion = 0;
|
||||
let currentPage = 1;
|
||||
|
||||
function buildSearchCondition() {
|
||||
const k = keyword.value.trim();
|
||||
if (!k) return searchCondition;
|
||||
const escaped = k.replaceAll("'", "''");
|
||||
const like = `${labelKey} LIKE '%${escaped}%'`;
|
||||
return searchCondition ? `${searchCondition} AND ${like}` : like;
|
||||
}
|
||||
|
||||
async function load(reset = false, preserveRows = false) {
|
||||
if (reset) {
|
||||
currentPage = 1;
|
||||
if (!preserveRows) {
|
||||
rows.value = [];
|
||||
total.value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const version = ++requestVersion;
|
||||
loading.value = true;
|
||||
const requestedKeyword = keyword.value.trim();
|
||||
|
||||
try {
|
||||
const condition = buildSearchCondition();
|
||||
const result = await pageDataApi({
|
||||
viewName,
|
||||
orderBy: orderBy || `${labelKey} ASC`,
|
||||
searchColumns,
|
||||
searchCondition: condition,
|
||||
pageNo: currentPage,
|
||||
pageSize,
|
||||
});
|
||||
|
||||
if (version !== requestVersion) return;
|
||||
|
||||
const data = result.data;
|
||||
if (reset) {
|
||||
rows.value = data.rows;
|
||||
} else {
|
||||
rows.value.push(...data.rows);
|
||||
}
|
||||
total.value = data.total;
|
||||
|
||||
if (!requestedKeyword) {
|
||||
defaultRows.value = [...rows.value];
|
||||
defaultTotal.value = total.value;
|
||||
}
|
||||
} finally {
|
||||
if (version === requestVersion) {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索:防抖后重置并加载首页
|
||||
*/
|
||||
const search = useDebounceFn((q: string) => {
|
||||
keyword.value = q;
|
||||
load(true);
|
||||
}, searchDelay);
|
||||
|
||||
/**
|
||||
* 立即搜索(focus/打开时使用,不防抖)
|
||||
*/
|
||||
function immediate(q: string) {
|
||||
keyword.value = q;
|
||||
if (!q && defaultRows.value.length) {
|
||||
rows.value = [...defaultRows.value];
|
||||
total.value = defaultTotal.value;
|
||||
load(true, true);
|
||||
return;
|
||||
}
|
||||
load(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开下拉:若尚无数据则首页加载
|
||||
*/
|
||||
function open() {
|
||||
if (!rows.value.length) {
|
||||
load(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动触底时加载下一页
|
||||
*/
|
||||
function loadMore() {
|
||||
if (loading.value) return;
|
||||
if (rows.value.length >= total.value) return;
|
||||
currentPage++;
|
||||
load(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置:清空数据、关键词、分页
|
||||
*/
|
||||
function reset(clearCache = false) {
|
||||
requestVersion++;
|
||||
rows.value = [];
|
||||
total.value = 0;
|
||||
keyword.value = "";
|
||||
currentPage = 1;
|
||||
loading.value = false;
|
||||
if (clearCache) {
|
||||
defaultRows.value = [];
|
||||
defaultTotal.value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 关键 props 变化时自动重置
|
||||
watch(
|
||||
() => [viewName, labelKey, valueKey, orderBy, searchCondition, searchColumns],
|
||||
() => reset(true),
|
||||
);
|
||||
|
||||
return {
|
||||
rows,
|
||||
loading,
|
||||
total,
|
||||
keyword,
|
||||
search,
|
||||
immediate,
|
||||
open,
|
||||
loadMore,
|
||||
reset,
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user