feat: 增加工作人员组织联动
This commit is contained in:
@@ -3,29 +3,95 @@
|
||||
* 版本:v1.0.0
|
||||
*/
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { reactive } from 'vue';
|
||||
import { onBeforeUnmount, reactive } from 'vue';
|
||||
import { resourceApi } from '@/api/resource';
|
||||
import type { ResourceField } from '@/api/resources';
|
||||
import type { ResourceRow } from '@/api/resource-page-rules';
|
||||
import { createRelationRequestVersionGuard } from './resource-relation-linkage-policy';
|
||||
|
||||
export type ResourceRelationLoadOptions = {
|
||||
filters?: Record<string, string>;
|
||||
preserveIdentities?: string[];
|
||||
};
|
||||
|
||||
export function useResourceRelations() {
|
||||
const options = reactive<Record<string, ResourceRow[]>>({});
|
||||
const loading = reactive<Record<string, boolean>>({});
|
||||
const timers = new Map<string, ReturnType<typeof setTimeout>>();
|
||||
const requestGuard = createRelationRequestVersionGuard();
|
||||
|
||||
/** 加载一类关联资源,工作人员关系默认只选择配送人员。 */
|
||||
async function load(resource: string, keyword = '') {
|
||||
/** 合并需要保留的当前选项,搜索和筛选时不让下拉框退回显示原始标识。 */
|
||||
function mergePreserved(
|
||||
resource: string,
|
||||
rows: ResourceRow[],
|
||||
identities: string[] = [],
|
||||
) {
|
||||
const merged = [...rows];
|
||||
const seen = new Set(rows.map((row) => String(row.identity ?? '')));
|
||||
for (const identity of identities.filter(Boolean)) {
|
||||
if (seen.has(identity)) continue;
|
||||
const current = (options[resource] ?? []).find(
|
||||
(row) => String(row.identity ?? '') === identity,
|
||||
);
|
||||
if (current) {
|
||||
merged.push(current);
|
||||
seen.add(identity);
|
||||
}
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
/** 加载一类关联资源,并丢弃已经过期的搜索或筛选响应。 */
|
||||
async function load(
|
||||
resource: string,
|
||||
keyword = '',
|
||||
request: ResourceRelationLoadOptions = {},
|
||||
) {
|
||||
const version = requestGuard.begin(resource);
|
||||
loading[resource] = true;
|
||||
try {
|
||||
const filters: Record<string, string> = keyword ? { keyword } : {};
|
||||
const filters: Record<string, string> = {
|
||||
...(request.filters ?? {}),
|
||||
...(keyword ? { keyword } : {}),
|
||||
};
|
||||
if (resource === '/staff_account') filters.role_code = 'delivery';
|
||||
options[resource] = (
|
||||
const rows = (
|
||||
await resourceApi.list<ResourceRow>(resource, 1, 100, filters)
|
||||
).list;
|
||||
if (!requestGuard.isLatest(resource, version)) return;
|
||||
options[resource] = mergePreserved(
|
||||
resource,
|
||||
rows,
|
||||
request.preserveIdentities,
|
||||
);
|
||||
} catch (error) {
|
||||
if (!requestGuard.isLatest(resource, version)) return;
|
||||
Message.error(`关联数据加载失败:${(error as Error).message}`);
|
||||
} finally {
|
||||
loading[resource] = false;
|
||||
if (requestGuard.isLatest(resource, version)) {
|
||||
loading[resource] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 按唯一标识补载当前选项,解决编辑记录不在列表前 100 条时的回显。 */
|
||||
async function ensure(resource: string, identity: string) {
|
||||
if (!identity) return undefined;
|
||||
const current = (options[resource] ?? []).find(
|
||||
(row) => String(row.identity ?? '') === identity,
|
||||
);
|
||||
if (current) return current;
|
||||
try {
|
||||
const row = await resourceApi.detail<ResourceRow>(resource, identity);
|
||||
const resolved = (options[resource] ?? []).find(
|
||||
(item) => String(item.identity ?? '') === identity,
|
||||
);
|
||||
if (resolved) return resolved;
|
||||
options[resource] = [...(options[resource] ?? []), row];
|
||||
return row;
|
||||
} catch (error) {
|
||||
Message.error(`关联数据加载失败:${(error as Error).message}`);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,15 +106,42 @@ export function useResourceRelations() {
|
||||
}
|
||||
|
||||
/** 对远程关系选项进行防抖搜索。 */
|
||||
function search(resource: string | undefined, keyword: string) {
|
||||
function search(
|
||||
resource: string | undefined,
|
||||
keyword: string,
|
||||
request: ResourceRelationLoadOptions = {},
|
||||
) {
|
||||
if (!resource) return;
|
||||
const timer = timers.get(resource);
|
||||
if (timer) clearTimeout(timer);
|
||||
cancelSearch(resource);
|
||||
timers.set(
|
||||
resource,
|
||||
setTimeout(() => load(resource, keyword.trim()), 250),
|
||||
setTimeout(() => {
|
||||
timers.delete(resource);
|
||||
void load(resource, keyword.trim(), request);
|
||||
}, 250),
|
||||
);
|
||||
}
|
||||
|
||||
return { options, loading, load, preload, search };
|
||||
/** 取消关联资源尚未触发的防抖搜索。 */
|
||||
function cancelSearch(resource: string) {
|
||||
const timer = timers.get(resource);
|
||||
if (timer) clearTimeout(timer);
|
||||
timers.delete(resource);
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
for (const resource of timers.keys()) cancelSearch(resource);
|
||||
});
|
||||
|
||||
return {
|
||||
options,
|
||||
loading,
|
||||
load,
|
||||
ensure,
|
||||
preload,
|
||||
search,
|
||||
cancelSearch,
|
||||
};
|
||||
}
|
||||
|
||||
export type ResourceRelations = ReturnType<typeof useResourceRelations>;
|
||||
|
||||
Reference in New Issue
Block a user