Commit a770f2f3 authored by drigle's avatar drigle

fix: 用户类型字段不显示标题

parent f3dcd903
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useRef, useState } from 'react';
import { Alert, Label } from '@heroui/react'; import { Alert, Label } from '@heroui/react';
import XinyuanAutocomplete from '@/components/XinyuanAutocomplete'; import XinyuanAutocomplete from '@/components/XinyuanAutocomplete';
import { queryRecords } from '@/services/DataService'; import { queryRecords } from '@/services/DataService';
...@@ -9,6 +9,8 @@ import { ...@@ -9,6 +9,8 @@ import {
toArray, toArray,
} from '@/utils/subjectView'; } from '@/utils/subjectView';
const ID_LOOKUP_CHUNK_SIZE = 50;
export default function ReferenceSearchSelect({ export default function ReferenceSearchSelect({
fieldDef, fieldDef,
value, value,
...@@ -18,6 +20,8 @@ export default function ReferenceSearchSelect({ ...@@ -18,6 +20,8 @@ export default function ReferenceSearchSelect({
}) { }) {
const [keyword, setKeyword] = useState(''); const [keyword, setKeyword] = useState('');
const [options, setOptions] = useState([]); const [options, setOptions] = useState([]);
const [selectedById, setSelectedById] = useState({});
const lookedUpIdsRef = useRef(new Set());
const subjectName = getReferenceSubjectName(fieldDef); const subjectName = getReferenceSubjectName(fieldDef);
const multiple = Boolean(fieldDef?.multiple); const multiple = Boolean(fieldDef?.multiple);
const selectedIds = useMemo( const selectedIds = useMemo(
...@@ -25,6 +29,99 @@ export default function ReferenceSearchSelect({ ...@@ -25,6 +29,99 @@ export default function ReferenceSearchSelect({
[value], [value],
); );
useEffect(() => {
const selectedRecords = toArray(value).filter((item) => item && typeof item === 'object');
if (selectedRecords.length === 0) {
return;
}
setSelectedById((current) => {
let changed = false;
const next = { ...current };
for (const record of selectedRecords) {
const recordId = normalizeRecordId(record);
if (!recordId) {
continue;
}
const option = {
id: recordId,
label: normalizeRecordLabel(record) || recordId,
description: record.name || recordId,
};
if (
next[recordId]?.label === option.label &&
next[recordId]?.description === option.description
) {
continue;
}
next[recordId] = option;
changed = true;
}
return changed ? next : current;
});
}, [value]);
useEffect(() => {
const missingIds = selectedIds.filter(
(id) => id && !selectedById[id] && !lookedUpIdsRef.current.has(id),
);
if (!subjectName || missingIds.length === 0) {
return undefined;
}
let cancelled = false;
const loadSelectedRecords = async () => {
try {
missingIds.forEach((id) => lookedUpIdsRef.current.add(id));
for (let index = 0; index < missingIds.length; index += ID_LOOKUP_CHUNK_SIZE) {
const chunk = missingIds.slice(index, index + ID_LOOKUP_CHUNK_SIZE);
const result = await queryRecords({
subject: subjectName,
page: 0,
page_size: Math.max(chunk.length, 1),
filter: { _id: { $in: chunk } },
mode: 'basic',
});
if (cancelled) {
return;
}
setSelectedById((current) => {
const next = { ...current };
for (const record of result?.records || []) {
const recordId = normalizeRecordId(record);
if (!recordId) {
continue;
}
next[recordId] = {
id: recordId,
label: normalizeRecordLabel(record) || recordId,
description: record.name || recordId,
};
}
return next;
});
}
} catch (error) {
// 查询失败时保持 ID 兜底显示,避免阻断筛选输入。
}
};
loadSelectedRecords();
return () => {
cancelled = true;
};
}, [selectedById, selectedIds, subjectName]);
useEffect(() => { useEffect(() => {
let cancelled = false; let cancelled = false;
...@@ -44,7 +141,23 @@ export default function ReferenceSearchSelect({ ...@@ -44,7 +141,23 @@ export default function ReferenceSearchSelect({
}); });
if (!cancelled) { if (!cancelled) {
setOptions(result?.records || []); const nextRecords = result?.records || [];
setOptions(nextRecords);
setSelectedById((current) => {
const next = { ...current };
for (const record of nextRecords) {
const recordId = normalizeRecordId(record);
if (!recordId) {
continue;
}
next[recordId] = {
id: recordId,
label: normalizeRecordLabel(record) || recordId,
description: record.name || recordId,
};
}
return next;
});
} }
} catch (requestError) { } catch (requestError) {
if (!cancelled) { if (!cancelled) {
...@@ -84,7 +197,7 @@ export default function ReferenceSearchSelect({ ...@@ -84,7 +197,7 @@ export default function ReferenceSearchSelect({
); );
} }
const autocompleteOptions = options.map((record) => { const searchOptions = options.map((record) => {
const recordId = normalizeRecordId(record); const recordId = normalizeRecordId(record);
return { return {
id: recordId, id: recordId,
...@@ -92,11 +205,23 @@ export default function ReferenceSearchSelect({ ...@@ -92,11 +205,23 @@ export default function ReferenceSearchSelect({
description: record.name || recordId, description: record.name || recordId,
}; };
}); });
const selectedOptions = selectedIds.map((id) => (
selectedById[id] || {
id,
label: id,
description: '',
}
));
const selectedIdSet = new Set(selectedOptions.map((option) => option.id));
const autocompleteOptions = [
...selectedOptions,
...searchOptions.filter((option) => !selectedIdSet.has(option.id)),
];
const selectionProps = multiple const selectionProps = multiple
? { selectedKeys: new Set(selectedIds) } ? { selectedKeys: new Set(selectedIds) }
: { selectedKey: selectedIds[0] || null }; : { selectedKey: selectedIds[0] || null };
const valueText = toArray(value) const valueText = selectedOptions
.map((item) => normalizeRecordLabel(item) || normalizeRecordId(item)) .map((item) => item.label || item.id)
.filter(Boolean) .filter(Boolean)
.join('、'); .join('、');
......
...@@ -14,6 +14,7 @@ import FileUploadField from '@/components/FileUploadField'; ...@@ -14,6 +14,7 @@ import FileUploadField from '@/components/FileUploadField';
import UserSearchSelect from '@/components/UserSearchSelect'; import UserSearchSelect from '@/components/UserSearchSelect';
import { uploadFile } from '@/services/DataService'; import { uploadFile } from '@/services/DataService';
import ReferenceSearchSelect from '@/components/ReferenceSearchSelect'; import ReferenceSearchSelect from '@/components/ReferenceSearchSelect';
import UserSearchSelect from '@/components/UserSearchSelect';
import XinyuanAutocomplete from '@/components/XinyuanAutocomplete'; import XinyuanAutocomplete from '@/components/XinyuanAutocomplete';
import IntegrityRiskMaterialField from '@/pages/integrity_risk_prevention/IntegrityRiskMaterialField'; import IntegrityRiskMaterialField from '@/pages/integrity_risk_prevention/IntegrityRiskMaterialField';
import ReferenceRecordsTable from './ReferenceRecordsTable'; import ReferenceRecordsTable from './ReferenceRecordsTable';
...@@ -295,6 +296,7 @@ function ControlledFieldEditor({ ...@@ -295,6 +296,7 @@ function ControlledFieldEditor({
value={value} value={value}
onChange={onChange} onChange={onChange}
multiple={Boolean(fieldDef?.multiple)} multiple={Boolean(fieldDef?.multiple)}
ariaLabel={getFieldDisplayLabel(fieldDef) || '用户'}
placeholder="请选择用户" placeholder="请选择用户"
variant={fieldVariant} variant={fieldVariant}
/> />
...@@ -633,7 +635,8 @@ export default function TailwindEditableField({ ...@@ -633,7 +635,8 @@ export default function TailwindEditableField({
<UserSearchSelect <UserSearchSelect
value={draft} value={draft}
onChange={setDraft} onChange={setDraft}
multiple={fieldDef?.multiple} multiple={Boolean(fieldDef?.multiple)}
ariaLabel={getFieldDisplayLabel(fieldDef) || '用户'}
placeholder="请选择用户" placeholder="请选择用户"
variant={fieldVariant} variant={fieldVariant}
/> />
......
...@@ -40,6 +40,9 @@ ...@@ -40,6 +40,9 @@
不删除已有标题数据,也不改变标题搜索、排序、筛选或详情身份。 不删除已有标题数据,也不改变标题搜索、排序、筛选或详情身份。
8. Subject 配置为多选的选择字段,在新建弹窗和详情编辑中使用可搜索的下拉勾选器; 8. Subject 配置为多选的选择字段,在新建弹窗和详情编辑中使用可搜索的下拉勾选器;
已选项以标签展示并支持逐项移除或清空,保存值仍为 `settings.options` 中的 value 数组。 已选项以标签展示并支持逐项移除或清空,保存值仍为 `settings.options` 中的 value 数组。
用户字段和引用字段进入详情编辑、弹窗编辑或筛选选择器时,若当前已保存 ID 不在首屏下拉
结果中,前端必须按已选 ID 补查用户资料或引用记录并补齐显示名称;补查失败时才以 ID
兜底展示,写入仍只提交 `_id`/`_id[]`。
9. 列表工具栏默认只显示新建、导入和主要导出等常用入口;展示表的次要导出与归档动作 9. 列表工具栏默认只显示新建、导入和主要导出等常用入口;展示表的次要导出与归档动作
收纳在更多操作菜单中,推送、归档、导出所选和批量删除仅在勾选记录后可用。 收纳在更多操作菜单中,推送、归档、导出所选和批量删除仅在勾选记录后可用。
所有业务操作沿用旧应用按钮文案,不改变原操作结果和确认流程。 所有业务操作沿用旧应用按钮文案,不改变原操作结果和确认流程。
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment