diff --git a/client/src/pages/FormEditor.tsx b/client/src/pages/FormEditor.tsx index 30f3d70..bc74686 100644 --- a/client/src/pages/FormEditor.tsx +++ b/client/src/pages/FormEditor.tsx @@ -3761,7 +3761,20 @@ return function Layout({ ctx }) { // 3. Простой: assigneeUserId установлен, assigneeConditions = null const raw = transition.assigneeConditions as any; - let assigneeBlocks: { type: 'user' | 'role' | 'field_user' | 'table_column_user'; id: number; fieldCode?: string; columnIndex?: number; rules: { fieldCode: string; value: string; operator?: string }[] }[] = []; + let assigneeBlocks: { type: 'user' | 'role' | 'field_user' | 'table_column_user'; id: number; fieldCode?: string; columnIndex?: number; rules: { fieldCode: string; value: string; operator?: string; tableMode?: 'value' | 'column_user' }[] }[] = []; + + const resolveTableMode = (r: any, fieldCode?: string): 'value' | 'column_user' | undefined => { + if (r.tableMode === 'value' || r.tableMode === 'column_user') return r.tableMode; + // Обратная совместимость: раньше для table-поля в value хранился индекс колонки + const field = fields.find((f: FormField) => f.code === (fieldCode || r.fieldCode)); + if (field?.type === 'table') { + const tableId = (field.options as any)?.tableId as number | undefined; + const cols = tablesData?.tables?.find((t: any) => t.id === tableId)?.columns || []; + const idx = Number(r.value); + if (!isNaN(idx) && idx >= 0 && idx < cols.length) return 'column_user'; + } + return 'value'; + }; if (raw?.assignees && Array.isArray(raw.assignees)) { assigneeBlocks = raw.assignees.map((a: any) => ({ @@ -3769,15 +3782,20 @@ return function Layout({ ctx }) { id: a.id ?? 0, fieldCode: a.fieldCode, columnIndex: a.columnIndex, - rules: (a.rules || []).map((r: any) => ({ fieldCode: r.fieldCode || '', value: r.value || '', operator: r.operator || 'contains' })), + rules: (a.rules || []).map((r: any) => ({ + fieldCode: r.fieldCode || '', + value: r.value || '', + operator: r.operator || 'contains', + tableMode: resolveTableMode(r, a.fieldCode), + })), })); } else if (raw?.rules && Array.isArray(raw.rules)) { - const merged = new Map(); + const merged = new Map(); for (const r of raw.rules) { if (r.tableUserColumnIndex !== undefined && !r.tableId && r.fieldCode) { const key = `table_column_user:${r.fieldCode}:${r.tableUserColumnIndex}`; if (!merged.has(key)) merged.set(key, { type: 'table_column_user', id: 0, fieldCode: r.fieldCode, columnIndex: r.tableUserColumnIndex, rules: [] }); - if (r.fieldCode) merged.get(key)!.rules.push({ fieldCode: r.fieldCode, value: r.value || '', operator: r.operator || 'contains' }); + if (r.fieldCode) merged.get(key)!.rules.push({ fieldCode: r.fieldCode, value: r.value || '', operator: r.operator || 'contains', tableMode: 'column_user' }); continue; } if (r.tableId !== undefined && r.tableUserColumnIndex !== undefined) { @@ -3788,7 +3806,7 @@ return function Layout({ ctx }) { if (!id) continue; const key = `${type}:${id}`; if (!merged.has(key)) merged.set(key, { type, id, rules: [] }); - if (r.fieldCode) merged.get(key)!.rules.push({ fieldCode: r.fieldCode, value: r.value || '', operator: r.operator || 'contains' }); + if (r.fieldCode) merged.get(key)!.rules.push({ fieldCode: r.fieldCode, value: r.value || '', operator: r.operator || 'contains', tableMode: resolveTableMode(r) }); } assigneeBlocks = Array.from(merged.values()); } @@ -3810,7 +3828,7 @@ return function Layout({ ctx }) { id: (b.type === 'user' || b.type === 'role') ? b.id : undefined, fieldCode: (b.type === 'field_user' || b.type === 'table_column_user') ? b.fieldCode : undefined, columnIndex: b.type === 'table_column_user' ? b.columnIndex : undefined, - rules: b.rules.map(r => ({ fieldCode: r.fieldCode, value: r.value, operator: r.operator || 'contains' })), + rules: b.rules.map(r => ({ fieldCode: r.fieldCode, value: r.value, operator: r.operator || 'contains', tableMode: r.tableMode })), })) } : null; @@ -3822,7 +3840,7 @@ return function Layout({ ctx }) { updateTransition(index, { assigneeConditions: payload, assigneeUserId }); }; - const emptyRule = { fieldCode: '', value: '', operator: 'contains' }; + const emptyRule = { fieldCode: '', value: '', operator: 'contains', tableMode: undefined as 'value' | 'column_user' | undefined }; const addRule = (bi: number) => { const blocks = [...assigneeBlocks]; blocks[bi] = { ...blocks[bi], rules: [...blocks[bi].rules, { ...emptyRule }] }; @@ -3833,7 +3851,7 @@ return function Layout({ ctx }) { blocks[bi] = { ...blocks[bi], rules: blocks[bi].rules.filter((_, i) => i !== ri) }; saveAssigneeBlocks(blocks); }; - const updateRule = (bi: number, ri: number, patch: Partial<{ fieldCode: string; value: string; operator?: string }>) => { + const updateRule = (bi: number, ri: number, patch: Partial<{ fieldCode: string; value: string; operator?: string; tableMode?: 'value' | 'column_user' }>) => { const blocks = [...assigneeBlocks]; blocks[bi] = { ...blocks[bi], rules: blocks[bi].rules.map((r, i) => i === ri ? { ...r, ...patch } : r) }; saveAssigneeBlocks(blocks); @@ -3855,7 +3873,7 @@ return function Layout({ ctx }) { id: b.id, fieldCode: b.fieldCode, columnIndex: b.columnIndex, - rules: b.rules.map(r => ({ fieldCode: r.fieldCode, value: r.value })), + rules: b.rules.map(r => ({ fieldCode: r.fieldCode, value: r.value, operator: r.operator, tableMode: r.tableMode })), })), }, assigneeUserId: undefined, @@ -3965,21 +3983,66 @@ return function Layout({ ctx }) { {isTableField ? ( <> - → пользователь из колонки + + {rule.tableMode === 'column_user' ? ( + + ) : ( + <> + + {rule.operator !== 'empty' && ( + updateRule(bi, ri, { value: e.target.value })} + /> + )} + + )} ) : ( <> diff --git a/shared/field-conditions.ts b/shared/field-conditions.ts index 5f7c4c0..5c5cd33 100644 --- a/shared/field-conditions.ts +++ b/shared/field-conditions.ts @@ -292,7 +292,7 @@ export function buildSystemFieldValuesForConditions( * Проверяет, соответствуют ли значения полей заданным правилам. */ export function fieldConditionsMatch( - rules: { fieldCode?: string; value?: string; operator?: "contains" | "empty" | "filled" }[], + rules: { fieldCode?: string; value?: string; operator?: "contains" | "empty" | "filled"; tableMode?: "value" | "column_user" }[], fieldValues: Array<{ fieldId: number; value: unknown }>, fields: Array<{ id: number; @@ -311,6 +311,10 @@ export function fieldConditionsMatch( const field = fields.find((f) => f.code === rule.fieldCode); if (!field) return false; + // Режим "пользователь из колонки" — это конфигурация выборки согласующего, + // а не условие на значение поля. Пропускаем, чтобы не ломать проверку. + if (rule.tableMode === "column_user") continue; + const fv = fieldValues.find((fv) => fv.fieldId === field.id); const operator = rule.operator || "contains"; @@ -364,6 +368,7 @@ export interface FieldRule { fieldCode: string; operator?: "contains" | "empty" | "filled"; value?: string; + tableMode?: "value" | "column_user"; }>; }