From 9f1f1a8ee1f5058d796e1f4ab6ddf809c306b69e Mon Sep 17 00:00:00 2001 From: Mikhail Date: Thu, 21 May 2026 17:03:32 +0300 Subject: [PATCH] Fix form selection and common form rendering --- .../src/components/editor/ide-workspace.tsx | 25 ++++++-- frontend/sfera-web/src/lib/api.ts | 3 + services/api-server/src/api_server/main.py | 64 +++++++++++++++++-- 3 files changed, 82 insertions(+), 10 deletions(-) diff --git a/frontend/sfera-web/src/components/editor/ide-workspace.tsx b/frontend/sfera-web/src/components/editor/ide-workspace.tsx index 7a5061d..111540c 100644 --- a/frontend/sfera-web/src/components/editor/ide-workspace.tsx +++ b/frontend/sfera-web/src/components/editor/ide-workspace.tsx @@ -3055,13 +3055,16 @@ function FormDesignerPanel({ } }, [objectForms, selectedFormId, selectedFormQualifiedName]); const form = - objectForms.find((item) => item.form.lineage_id === selectedFormId) ?? objectForms.find((item) => item.form.qualified_name === selectedFormQualifiedName) ?? + objectForms.find((item) => item.form.lineage_id === selectedFormId) ?? objectForms[0]; const commands = form?.commands.slice(0, 6) ?? []; const formKey = form?.form.lineage_id ?? "draft"; const baseElements = useMemo(() => buildIdeFormElements(form), [form]); const elements = elementDrafts[formKey] ?? baseElements; + const flatElements = useMemo(() => flattenIdeFormElements(elements), [elements]); + const sidebarElements = flatElements.slice(0, 160); + const propertyElements = flatElements.slice(0, 48); const formTitle = titleByForm[formKey] ?? form?.form.name ?? "ФормаДокумента"; const formObjectCaption = language === "ru" ? `${formTitle} (форма 1С 8.5)` : `${formTitle} (1C 8.5 form)`; const layout = layoutByForm[formKey] ?? "auto"; @@ -3131,10 +3134,10 @@ function FormDesignerPanel({
{t.formElements}
- {flattenIdeFormElements(elements).length === 0 ? ( + {flatElements.length === 0 ? (
{t.none}
) : ( - flattenIdeFormElements(elements).map((element) => ( + sidebarElements.map((element) => (
@@ -3144,6 +3147,11 @@ function FormDesignerPanel({
)) )} + {flatElements.length > sidebarElements.length ? ( +
+ +{flatElements.length - sidebarElements.length} {language === "ru" ? "элементов в макете" : "layout items"} +
+ ) : null}
{t.commands}
@@ -3240,7 +3248,7 @@ function FormDesignerPanel({
- +
@@ -3261,7 +3269,7 @@ function FormDesignerPanel({
- {flattenIdeFormElements(elements).map((element) => ( + {propertyElements.map((element) => (
{element.name}
updateElement(element.id, { caption: event.target.value })} /> @@ -3283,6 +3291,13 @@ function FormDesignerPanel({ updateElement(element.id, { binding: event.target.value })} />
))} + {flatElements.length > propertyElements.length ? ( +
+ {language === "ru" + ? `Показаны первые ${propertyElements.length} свойств из ${flatElements.length}. Остальные элементы доступны в макете формы.` + : `Showing first ${propertyElements.length} properties out of ${flatElements.length}. Other items are available in the form layout.`} +
+ ) : null}
diff --git a/frontend/sfera-web/src/lib/api.ts b/frontend/sfera-web/src/lib/api.ts index 1c4b7aa..bd6e603 100644 --- a/frontend/sfera-web/src/lib/api.ts +++ b/frontend/sfera-web/src/lib/api.ts @@ -819,6 +819,9 @@ export async function getBslCompletions( function ownerQualifiedNameForForm(formQualifiedName: string) { const parts = formQualifiedName.split("."); + if (parts[0] === "ОбщаяФорма") { + return formQualifiedName; + } return parts.length > 1 ? parts.slice(0, -1).join(".") : formQualifiedName; } diff --git a/services/api-server/src/api_server/main.py b/services/api-server/src/api_server/main.py index a3709a0..620c20e 100644 --- a/services/api-server/src/api_server/main.py +++ b/services/api-server/src/api_server/main.py @@ -4748,12 +4748,20 @@ async def get_object_ui(project_id: str, object_name: str) -> ObjectUiResponse: snapshot, graph = _snapshot_and_graph(project_id) object_node = _find_graph_node(graph, object_name, _ACCESS_TARGET_KINDS) if object_node is None: + object_node = _find_graph_node(graph, object_name, {NodeKind.FORM}) + if object_node is None: + common_form = _common_form_ui_from_source(project_id, object_name) + if common_form is not None: + return common_form raise HTTPException(status_code=404, detail=f"Object not found: {object_name}") - form_lineages = { - edge.target_lineage - for edge in graph.edges.values() - if edge.kind == EdgeKind.HAS_FORM and edge.source_lineage == object_node.lineage_id - } + if object_node.kind == NodeKind.FORM: + form_lineages = {object_node.lineage_id} + else: + form_lineages = { + edge.target_lineage + for edge in graph.edges.values() + if edge.kind == EdgeKind.HAS_FORM and edge.source_lineage == object_node.lineage_id + } return ObjectUiResponse( object=_named_node(object_node), forms=_hydrate_object_ui_forms_from_source( @@ -7924,6 +7932,52 @@ def _hydrate_object_ui_forms_from_source( ] +def _common_form_ui_from_source(project_id: str, object_name: str) -> ObjectUiResponse | None: + if not object_name.startswith("ОбщаяФорма."): + return None + source_root = _current_project_source_root(project_id) + if source_root is None: + return None + form_name = object_name.split(".", 1)[1] + form_file = _edt_form_file_for_object(source_root, object_name, form_name) + if form_file is None: + return None + try: + xml_objects = parse_one_c_xml_file(form_file) + except (OSError, UnicodeDecodeError, ET.ParseError): + return None + form_object = next((item for item in xml_objects if item.object_kind == "FORM"), None) + if form_object is None: + return None + source_path = form_file.as_posix() + form_node = NamedNode( + lineage_id=make_lineage_id( + NodeKind.FORM.value, + f"{project_id}:{source_path}:FORM:{form_object.qualified_name}", + ), + kind=NodeKind.FORM.value, + name=form_object.name, + qualified_name=form_object.qualified_name, + attributes=form_object.attributes, + ) + elements = [ + NamedNode( + lineage_id=make_lineage_id( + NodeKind.FORM_ELEMENT.value, + f"{project_id}:{source_path}:ELEMENT:{item.qualified_name}", + ), + kind=NodeKind.FORM_ELEMENT.value, + name=item.name, + qualified_name=item.qualified_name, + attributes=item.attributes, + ) + for item in xml_objects + if item.object_kind == "ELEMENT" and item.qualified_name.startswith(f"{form_node.qualified_name}.") + ] + form = FormSemanticsResponse(form=form_node, commands=[], elements=elements, command_handlers={}) + return ObjectUiResponse(object=form_node, forms=[form]) + + def _hydrate_form_elements_from_source( project_id: str, source_root: Path,