Render forms from indexed form elements
This commit is contained in:
@@ -3049,7 +3049,7 @@ function FormDesignerPanel({
|
|||||||
const form = objectForms.find((item) => item.form.lineage_id === selectedFormId) ?? objectForms[0];
|
const form = objectForms.find((item) => item.form.lineage_id === selectedFormId) ?? objectForms[0];
|
||||||
const commands = form?.commands.slice(0, 6) ?? [];
|
const commands = form?.commands.slice(0, 6) ?? [];
|
||||||
const formKey = form?.form.lineage_id ?? "draft";
|
const formKey = form?.form.lineage_id ?? "draft";
|
||||||
const baseElements = useMemo(() => buildIdeFormElements(data, form), [data, form]);
|
const baseElements = useMemo(() => buildIdeFormElements(form), [form]);
|
||||||
const elements = elementDrafts[formKey] ?? baseElements;
|
const elements = elementDrafts[formKey] ?? baseElements;
|
||||||
const formTitle = titleByForm[formKey] ?? form?.form.name ?? "ФормаДокумента";
|
const formTitle = titleByForm[formKey] ?? form?.form.name ?? "ФормаДокумента";
|
||||||
const formObjectCaption = language === "ru" ? `${formTitle} (форма 1С 8.5)` : `${formTitle} (1C 8.5 form)`;
|
const formObjectCaption = language === "ru" ? `${formTitle} (форма 1С 8.5)` : `${formTitle} (1C 8.5 form)`;
|
||||||
@@ -3057,9 +3057,9 @@ function FormDesignerPanel({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (form && !elementDrafts[form.form.lineage_id]) {
|
if (form && !elementDrafts[form.form.lineage_id]) {
|
||||||
setElementDrafts((current) => ({ ...current, [form.form.lineage_id]: buildIdeFormElements(data, form) }));
|
setElementDrafts((current) => ({ ...current, [form.form.lineage_id]: buildIdeFormElements(form) }));
|
||||||
}
|
}
|
||||||
}, [data, elementDrafts, form]);
|
}, [elementDrafts, form]);
|
||||||
|
|
||||||
const updateElement = (id: string, patch: Partial<IdeFormElementDraft>) => {
|
const updateElement = (id: string, patch: Partial<IdeFormElementDraft>) => {
|
||||||
setElementDrafts((current) => ({
|
setElementDrafts((current) => ({
|
||||||
@@ -3175,9 +3175,23 @@ function FormDesignerPanel({
|
|||||||
"grid grid-cols-12 gap-x-3 gap-y-2 p-5",
|
"grid grid-cols-12 gap-x-3 gap-y-2 p-5",
|
||||||
layout === "compact" ? "gap-y-1" : ""
|
layout === "compact" ? "gap-y-1" : ""
|
||||||
].join(" ")}>
|
].join(" ")}>
|
||||||
{elements.map((element) => (
|
{elements.length ? (
|
||||||
<IdeFormControl element={element} forceHalf={layout === "columns"} key={element.id} />
|
elements.map((element) => (
|
||||||
))}
|
<IdeFormControl element={element} forceHalf={layout === "columns"} key={element.id} />
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<div className="col-span-12 border border-dashed border-[#aeb8c6] bg-white px-4 py-6 text-sm text-slate-600" data-ide-form-empty>
|
||||||
|
<div className="font-semibold text-slate-900">{language === "ru" ? "Структура элементов формы не загружена" : "Form element structure is not loaded"}</div>
|
||||||
|
<div className="mt-1 text-xs">
|
||||||
|
{form?.form.qualified_name ?? form?.form.name ?? data.projectId}
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 text-xs">
|
||||||
|
{language === "ru"
|
||||||
|
? "В текущем индексе для этой формы нет узлов элементов. SFERA не подставляет шаблонные поля, чтобы не искажать объект 1С."
|
||||||
|
: "The current index has no element nodes for this form. SFERA does not insert template fields because that would distort the 1C object."}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-auto border-t border-[#ccd4df] bg-white px-4 py-3">
|
<div className="mt-auto border-t border-[#ccd4df] bg-white px-4 py-3">
|
||||||
@@ -3306,7 +3320,7 @@ function ideFormControlInput(element: IdeFormElementDraft) {
|
|||||||
if (element.controlKind === "text") {
|
if (element.controlKind === "text") {
|
||||||
return <textarea className="min-h-16 resize-none border border-[#aeb8c6] bg-white px-2 py-1 text-xs" readOnly value={element.binding} />;
|
return <textarea className="min-h-16 resize-none border border-[#aeb8c6] bg-white px-2 py-1 text-xs" readOnly value={element.binding} />;
|
||||||
}
|
}
|
||||||
return <input className="h-7 border border-[#aeb8c6] bg-white px-2 text-xs" readOnly value={element.controlKind === "date" ? "21.05.2026 0:00:00" : element.binding} />;
|
return <input className="h-7 border border-[#aeb8c6] bg-white px-2 text-xs" readOnly value={element.binding} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function IdeFormMetric({ label, value }: Readonly<{ label: string; value: number }>) {
|
function IdeFormMetric({ label, value }: Readonly<{ label: string; value: number }>) {
|
||||||
@@ -3318,7 +3332,7 @@ function IdeFormMetric({ label, value }: Readonly<{ label: string; value: number
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildIdeFormElements(data: ProjectWorkspaceData, form: ProjectWorkspaceData["forms"][number] | undefined): IdeFormElementDraft[] {
|
function buildIdeFormElements(form: ProjectWorkspaceData["forms"][number] | undefined): IdeFormElementDraft[] {
|
||||||
const explicitElements = form?.elements ?? [];
|
const explicitElements = form?.elements ?? [];
|
||||||
if (explicitElements.length) {
|
if (explicitElements.length) {
|
||||||
return explicitElements.map((element, index) => ({
|
return explicitElements.map((element, index) => ({
|
||||||
@@ -3330,38 +3344,7 @@ function buildIdeFormElements(data: ProjectWorkspaceData, form: ProjectWorkspace
|
|||||||
width: "stretch"
|
width: "stretch"
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
const attributes = data.selectedObjectSchema?.attributes ?? [];
|
return [];
|
||||||
const tabularSections = data.selectedObjectSchema?.tabular_sections ?? [];
|
|
||||||
const fromSchema: IdeFormElementDraft[] = [
|
|
||||||
...attributes.map((attribute, index) => ({
|
|
||||||
id: attribute.lineage_id || `attribute.${index}`,
|
|
||||||
name: attribute.name,
|
|
||||||
caption: attribute.name,
|
|
||||||
controlKind: controlKindForFormNode(attribute.name, attribute.kind),
|
|
||||||
binding: attribute.name,
|
|
||||||
width: index < 2 ? "half" : "stretch"
|
|
||||||
} satisfies IdeFormElementDraft)),
|
|
||||||
...tabularSections.map((section, index) => ({
|
|
||||||
id: section.tabular_section.lineage_id || `table.${index}`,
|
|
||||||
name: section.tabular_section.name,
|
|
||||||
caption: section.tabular_section.name,
|
|
||||||
controlKind: "table" as const,
|
|
||||||
binding: section.tabular_section.name,
|
|
||||||
width: "stretch" as const
|
|
||||||
}))
|
|
||||||
];
|
|
||||||
if (fromSchema.length) {
|
|
||||||
return fromSchema;
|
|
||||||
}
|
|
||||||
const formName = form?.form.name ?? "ФормаДокумента";
|
|
||||||
if (formName.includes("ФормаДокумента")) {
|
|
||||||
return [
|
|
||||||
{ id: "default.number", name: "Номер", caption: "Номер", controlKind: "input", binding: "Объект.Номер", width: "half" },
|
|
||||||
{ id: "default.date", name: "Дата", caption: "Дата", controlKind: "date", binding: "Объект.Дата", width: "half" },
|
|
||||||
{ id: "default.table", name: "Товары", caption: "Товары", controlKind: "table", binding: "Объект.Товары", width: "stretch" }
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return [{ id: "default.name", name: "Наименование", caption: "Наименование", controlKind: "input", binding: "Объект.Наименование", width: "stretch" }];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function controlKindForFormNode(name: string, kind: string): IdeFormElementDraft["controlKind"] {
|
function controlKindForFormNode(name: string, kind: string): IdeFormElementDraft["controlKind"] {
|
||||||
|
|||||||
@@ -5531,7 +5531,7 @@ function FormDesignerPanel({ projectId, object }: Readonly<{ projectId: string;
|
|||||||
const [extraElementsByForm, setExtraElementsByForm] = useState<Record<string, FormDesignerElementDraft[]>>({});
|
const [extraElementsByForm, setExtraElementsByForm] = useState<Record<string, FormDesignerElementDraft[]>>({});
|
||||||
const [newElementName, setNewElementName] = useState("");
|
const [newElementName, setNewElementName] = useState("");
|
||||||
const [newElementKind, setNewElementKind] = useState<FormDesignerElementDraft["kind"]>("input");
|
const [newElementKind, setNewElementKind] = useState<FormDesignerElementDraft["kind"]>("input");
|
||||||
const baseElements = activeForm ? buildFormDesignerElements(object, activeForm) : [];
|
const baseElements = activeForm ? buildFormDesignerElements(activeForm) : [];
|
||||||
const elements = [...baseElements, ...(extraElementsByForm[activeFormKey] ?? [])].map((element) => ({
|
const elements = [...baseElements, ...(extraElementsByForm[activeFormKey] ?? [])].map((element) => ({
|
||||||
...element,
|
...element,
|
||||||
...(elementOverrides[element.id] ?? {})
|
...(elementOverrides[element.id] ?? {})
|
||||||
@@ -5617,9 +5617,17 @@ function FormDesignerPanel({ projectId, object }: Readonly<{ projectId: string;
|
|||||||
className={`grid grid-cols-12 gap-x-3 gap-y-2 p-4 ${layout === "compact" ? "gap-y-1" : ""}`}
|
className={`grid grid-cols-12 gap-x-3 gap-y-2 p-4 ${layout === "compact" ? "gap-y-1" : ""}`}
|
||||||
data-legacy-form-layout={layout}
|
data-legacy-form-layout={layout}
|
||||||
>
|
>
|
||||||
{elements.map((element) => (
|
{elements.length ? (
|
||||||
<LegacyFormControl key={element.id} element={element} forceHalf={layout === "columns"} />
|
elements.map((element) => (
|
||||||
))}
|
<LegacyFormControl key={element.id} element={element} forceHalf={layout === "columns"} />
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<div className="col-span-12 border border-dashed border-[#aeb8c6] bg-white px-4 py-6 text-sm text-muted-foreground">
|
||||||
|
<div className="font-semibold text-foreground">Структура элементов формы не загружена</div>
|
||||||
|
<div className="mt-1 text-xs">{formQualifiedName(object, activeForm)}</div>
|
||||||
|
<div className="mt-3 text-xs">SFERA не подставляет реквизиты объекта вместо элементов формы, чтобы не искажать объект 1С.</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -5763,29 +5771,48 @@ function legacyFormControlInput(element: FormDesignerElementDraft): ReactNode {
|
|||||||
if (element.kind === "text") {
|
if (element.kind === "text") {
|
||||||
return <textarea value={element.binding} readOnly className="min-h-16 resize-none border border-[#aeb8c6] bg-white px-2 py-1 text-xs" />;
|
return <textarea value={element.binding} readOnly className="min-h-16 resize-none border border-[#aeb8c6] bg-white px-2 py-1 text-xs" />;
|
||||||
}
|
}
|
||||||
return <input value={element.kind === "date" ? "21.05.2026 0:00:00" : element.binding} readOnly className="h-7 border border-[#aeb8c6] bg-white px-2 text-xs" />;
|
return <input value={element.binding} readOnly className="h-7 border border-[#aeb8c6] bg-white px-2 text-xs" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildFormDesignerElements(object: NormalizedMetadataObject, form: ObjectPart): FormDesignerElementDraft[] {
|
function buildFormDesignerElements(form: ObjectPart): FormDesignerElementDraft[] {
|
||||||
const fields = [...object.attributes, ...object.tabular_sections];
|
const rawElements = form.attributes.elements;
|
||||||
if (fields.length) {
|
if (Array.isArray(rawElements)) {
|
||||||
return fields.map((field, index) => ({
|
return rawElements
|
||||||
id: `${form.name}-${field.kind}-${field.name}`,
|
.filter((item): item is Record<string, unknown> => typeof item === "object" && item !== null)
|
||||||
name: field.name,
|
.map((item, index) => {
|
||||||
caption: field.name,
|
const name = String(item.name ?? item.caption ?? `Элемент${index + 1}`);
|
||||||
kind: field.kind === "TABULAR_SECTION" ? "table" : field.name.toLowerCase().includes("дата") ? "date" : "input",
|
const kind = String(item.control_kind ?? item.control ?? item.type ?? item.kind ?? "");
|
||||||
binding: field.kind === "TABULAR_SECTION" ? `Объект.${field.name}` : field.name,
|
return {
|
||||||
width: field.kind === "TABULAR_SECTION" ? "stretch" : index < 2 ? "half" : "stretch"
|
id: String(item.lineage_id ?? item.id ?? `${form.name}-${name}-${index}`),
|
||||||
}));
|
name,
|
||||||
|
caption: String(item.caption ?? name),
|
||||||
|
kind: formDesignerKindFor(kind, name),
|
||||||
|
binding: String(item.binding ?? item.path ?? name),
|
||||||
|
width: String(item.width ?? "") === "half" ? "half" : String(item.width ?? "") === "third" ? "third" : "stretch"
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (form.name.includes("ФормаДокумента")) {
|
return [];
|
||||||
return [
|
}
|
||||||
{ id: `${form.name}-number`, name: "Номер", caption: "Номер", kind: "input", binding: "Объект.Номер", width: "half" },
|
|
||||||
{ id: `${form.name}-date`, name: "Дата", caption: "Дата", kind: "date", binding: "Объект.Дата", width: "half" },
|
function formDesignerKindFor(kind: string, name: string): FormDesignerElementDraft["kind"] {
|
||||||
{ id: `${form.name}-table`, name: "Товары", caption: "Товары", kind: "table", binding: "Объект.Товары", width: "stretch" }
|
const raw = `${kind} ${name}`.toLowerCase();
|
||||||
];
|
if (raw.includes("table") || raw.includes("табли") || raw.includes("список")) {
|
||||||
|
return "table";
|
||||||
}
|
}
|
||||||
return [{ id: `${form.name}-name`, name: "Наименование", caption: "Наименование", kind: "input", binding: "Объект.Наименование", width: "stretch" }];
|
if (raw.includes("check") || raw.includes("boolean") || raw.includes("флаж") || raw.includes("булево")) {
|
||||||
|
return "checkbox";
|
||||||
|
}
|
||||||
|
if (raw.includes("date") || raw.includes("дата")) {
|
||||||
|
return "date";
|
||||||
|
}
|
||||||
|
if (raw.includes("group") || raw.includes("груп")) {
|
||||||
|
return "group";
|
||||||
|
}
|
||||||
|
if (raw.includes("text") || raw.includes("надпись")) {
|
||||||
|
return "text";
|
||||||
|
}
|
||||||
|
return "input";
|
||||||
}
|
}
|
||||||
|
|
||||||
function formQualifiedName(object: NormalizedMetadataObject, form: ObjectPart): string {
|
function formQualifiedName(object: NormalizedMetadataObject, form: ObjectPart): string {
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ def build_form_editor_draft(
|
|||||||
commands=_commands_from_semantics(form_semantics),
|
commands=_commands_from_semantics(form_semantics),
|
||||||
elements=_elements_from_semantics(form_semantics),
|
elements=_elements_from_semantics(form_semantics),
|
||||||
)
|
)
|
||||||
if not draft.elements:
|
|
||||||
draft.elements = _default_elements_for_form(draft)
|
|
||||||
if form_data:
|
if form_data:
|
||||||
_apply_form_data(draft, form_data)
|
_apply_form_data(draft, form_data)
|
||||||
return draft
|
return draft
|
||||||
@@ -90,22 +88,6 @@ def _apply_form_data(draft: FormEditorDraft, form_data: dict[str, list[str]]) ->
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _default_elements_for_form(draft: FormEditorDraft) -> list[FormEditorElement]:
|
|
||||||
if "ФормаСписка" in draft.form_name:
|
|
||||||
return [
|
|
||||||
FormEditorElement("default.list", "Список", f"{draft.form_name}.Список", "Список", "table", "Список", row=0),
|
|
||||||
]
|
|
||||||
if "ФормаДокумента" in draft.form_name:
|
|
||||||
return [
|
|
||||||
FormEditorElement("default.number", "Номер", f"{draft.form_name}.Номер", "Номер", "input", "Объект.Номер", "half", 0),
|
|
||||||
FormEditorElement("default.date", "Дата", f"{draft.form_name}.Дата", "Дата", "date", "Объект.Дата", "half", 1),
|
|
||||||
FormEditorElement("default.table", "Товары", f"{draft.form_name}.Товары", "Товары", "table", "Объект.Товары", row=2),
|
|
||||||
]
|
|
||||||
return [
|
|
||||||
FormEditorElement("default.name", "Наименование", f"{draft.form_name}.Наименование", "Наименование", "input", "Объект.Наименование", row=0),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def _control_kind_for(name: str, attributes: dict) -> str:
|
def _control_kind_for(name: str, attributes: dict) -> str:
|
||||||
raw = str(attributes.get("control") or attributes.get("control_kind") or attributes.get("type") or "").casefold()
|
raw = str(attributes.get("control") or attributes.get("control_kind") or attributes.get("type") or "").casefold()
|
||||||
if "table" in raw or "табли" in raw:
|
if "table" in raw or "табли" in raw:
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ def _canvas_window(draft: FormEditorDraft) -> str:
|
|||||||
{''.join(_canvas_command(command) for command in draft.commands) or '<span class="muted">Команды не описаны</span>'}
|
{''.join(_canvas_command(command) for command in draft.commands) or '<span class="muted">Команды не описаны</span>'}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-fields">
|
<div class="form-fields">
|
||||||
{''.join(_canvas_element(element) for element in draft.elements)}
|
{''.join(_canvas_element(element) for element in draft.elements) or _empty_form_structure(draft)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
@@ -265,6 +265,16 @@ def _canvas_element(element: FormEditorElement) -> str:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _empty_form_structure(draft: FormEditorDraft) -> str:
|
||||||
|
metadata = f"{draft.form_name} · {draft.owner_name}"
|
||||||
|
return f"""
|
||||||
|
<div class="form-empty-structure" data-html5-form-empty>
|
||||||
|
<strong>Структура элементов формы не загружена</strong>
|
||||||
|
<span>{escape(metadata)}</span>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def _control_markup(element: FormEditorElement) -> str:
|
def _control_markup(element: FormEditorElement) -> str:
|
||||||
if element.control_kind == "table":
|
if element.control_kind == "table":
|
||||||
return f"""
|
return f"""
|
||||||
@@ -277,7 +287,7 @@ def _control_markup(element: FormEditorElement) -> str:
|
|||||||
if element.control_kind == "checkbox":
|
if element.control_kind == "checkbox":
|
||||||
return f'<label class="form-check-control"><input type="checkbox" checked /> <span>{escape(element.binding)}</span></label>'
|
return f'<label class="form-check-control"><input type="checkbox" checked /> <span>{escape(element.binding)}</span></label>'
|
||||||
if element.control_kind == "date":
|
if element.control_kind == "date":
|
||||||
return '<input class="form-input-control" value="21.05.2026 0:00:00" readonly />'
|
return f'<input class="form-input-control" value="{escape(element.binding)}" readonly />'
|
||||||
if element.control_kind == "group":
|
if element.control_kind == "group":
|
||||||
return f'<div class="form-group-control">{escape(element.binding)}</div>'
|
return f'<div class="form-group-control">{escape(element.binding)}</div>'
|
||||||
if element.control_kind == "text":
|
if element.control_kind == "text":
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
.setup-layout{display:grid;grid-template-columns:340px minmax(0,1fr);gap:16px;max-width:1180px;margin:18px auto;padding:0 16px}.setup-workspace{background:#f3f6fb}.setup-card{padding:16px;border-bottom:1px solid var(--line)}.setup-card h1{margin:0;font-size:24px}.setup-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:14px}.setup-main{min-height:620px}.settings-form{display:grid;grid-template-columns:2fr 1fr 1fr auto;gap:8px;align-items:end;padding:12px 16px;border-bottom:1px solid var(--line);background:#fff}.settings-form label{display:grid;gap:4px;font-size:12px;font-weight:800;color:var(--muted);text-transform:uppercase}.settings-form input{height:32px;border:1px solid var(--line);padding:0 8px}.saved{float:right;color:var(--ok);font-weight:900}.setup-actions-panel,.ops-filter{display:flex;gap:8px;flex-wrap:wrap;align-items:end;padding:12px 16px;border-bottom:1px solid var(--line);background:#fbfcfe}.ops-filter input{height:32px;min-width:180px;border:1px solid var(--line);padding:0 8px}.inline-form{display:flex;gap:8px;align-items:end}.inline-form label{display:grid;gap:4px;font-size:12px;font-weight:800;color:var(--muted);text-transform:uppercase}.inline-form select{height:32px;min-width:240px;border:1px solid var(--line);background:#fff;padding:0 8px}.rollback-form,.authoring-preview-form{display:grid;grid-template-columns:1fr 1fr;gap:8px;padding:12px;border-bottom:1px solid var(--line);background:#fbfcfe}.rollback-form input,.authoring-preview-form input,.authoring-preview-form textarea{min-width:0;border:1px solid var(--line);padding:0 8px}.rollback-form input,.authoring-preview-form input{height:32px}.authoring-preview-form textarea{grid-column:1/-1;min-height:88px;padding:8px;resize:vertical;font:12px/1.45 ui-monospace,SFMono-Regular,Consolas,monospace}.rollback-form button,.authoring-preview-form button{grid-column:1/-1}.setup-summary{display:grid;gap:0}.compact-lead{margin:0;padding:0 16px 16px}.setup-metrics,.ops-summary{display:grid;grid-template-columns:repeat(4,1fr);margin:0;border-top:1px solid var(--line)}.ops-summary{grid-template-columns:repeat(5,1fr);margin:12px 0}.setup-metrics div,.ops-summary div{padding:16px;border-right:1px solid var(--line);border-bottom:1px solid var(--line);background:#fff}.setup-metrics div:last-child,.ops-summary div:last-child{border-right:0}.setup-metrics dt,.ops-summary dt{font-size:12px;color:var(--muted)}.setup-metrics dd,.ops-summary dd{margin:4px 0 0;font-size:28px;font-weight:900}.status-pill{border:1px solid var(--line);padding:6px 10px;background:#eef6f1;color:var(--ok);font-weight:800}.flush{border-top:1px solid var(--line)}.padded{padding:12px 16px;margin:0}.setup-detail,.history-item,.source-card,.check-item{display:grid;gap:3px;padding:12px 16px;border-bottom:1px solid var(--line)}.setup-detail span,.setup-detail small,.history-item span,.history-item small,.source-card span,.source-card small,.check-item span,.check-item small,.check-head span,.check-head small{color:var(--muted)}.source-list,.history-list,.check-list{display:grid}.check-head{display:flex;gap:10px;align-items:center;padding:12px 16px;border-bottom:1px solid var(--line);background:#fff}.job-log{margin:0;padding:0;list-style:none}.job-log li{padding:8px 16px;border-bottom:1px solid var(--line);color:var(--muted);font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px}
|
.setup-layout{display:grid;grid-template-columns:340px minmax(0,1fr);gap:16px;max-width:1180px;margin:18px auto;padding:0 16px}.setup-workspace{background:#f3f6fb}.setup-card{padding:16px;border-bottom:1px solid var(--line)}.setup-card h1{margin:0;font-size:24px}.setup-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:14px}.setup-main{min-height:620px}.settings-form{display:grid;grid-template-columns:2fr 1fr 1fr auto;gap:8px;align-items:end;padding:12px 16px;border-bottom:1px solid var(--line);background:#fff}.settings-form label{display:grid;gap:4px;font-size:12px;font-weight:800;color:var(--muted);text-transform:uppercase}.settings-form input{height:32px;border:1px solid var(--line);padding:0 8px}.saved{float:right;color:var(--ok);font-weight:900}.setup-actions-panel,.ops-filter{display:flex;gap:8px;flex-wrap:wrap;align-items:end;padding:12px 16px;border-bottom:1px solid var(--line);background:#fbfcfe}.ops-filter input{height:32px;min-width:180px;border:1px solid var(--line);padding:0 8px}.inline-form{display:flex;gap:8px;align-items:end}.inline-form label{display:grid;gap:4px;font-size:12px;font-weight:800;color:var(--muted);text-transform:uppercase}.inline-form select{height:32px;min-width:240px;border:1px solid var(--line);background:#fff;padding:0 8px}.rollback-form,.authoring-preview-form{display:grid;grid-template-columns:1fr 1fr;gap:8px;padding:12px;border-bottom:1px solid var(--line);background:#fbfcfe}.rollback-form input,.authoring-preview-form input,.authoring-preview-form textarea{min-width:0;border:1px solid var(--line);padding:0 8px}.rollback-form input,.authoring-preview-form input{height:32px}.authoring-preview-form textarea{grid-column:1/-1;min-height:88px;padding:8px;resize:vertical;font:12px/1.45 ui-monospace,SFMono-Regular,Consolas,monospace}.rollback-form button,.authoring-preview-form button{grid-column:1/-1}.setup-summary{display:grid;gap:0}.compact-lead{margin:0;padding:0 16px 16px}.setup-metrics,.ops-summary{display:grid;grid-template-columns:repeat(4,1fr);margin:0;border-top:1px solid var(--line)}.ops-summary{grid-template-columns:repeat(5,1fr);margin:12px 0}.setup-metrics div,.ops-summary div{padding:16px;border-right:1px solid var(--line);border-bottom:1px solid var(--line);background:#fff}.setup-metrics div:last-child,.ops-summary div:last-child{border-right:0}.setup-metrics dt,.ops-summary dt{font-size:12px;color:var(--muted)}.setup-metrics dd,.ops-summary dd{margin:4px 0 0;font-size:28px;font-weight:900}.status-pill{border:1px solid var(--line);padding:6px 10px;background:#eef6f1;color:var(--ok);font-weight:800}.flush{border-top:1px solid var(--line)}.padded{padding:12px 16px;margin:0}.setup-detail,.history-item,.source-card,.check-item{display:grid;gap:3px;padding:12px 16px;border-bottom:1px solid var(--line)}.setup-detail span,.setup-detail small,.history-item span,.history-item small,.source-card span,.source-card small,.check-item span,.check-item small,.check-head span,.check-head small{color:var(--muted)}.source-list,.history-list,.check-list{display:grid}.check-head{display:flex;gap:10px;align-items:center;padding:12px 16px;border-bottom:1px solid var(--line);background:#fff}.job-log{margin:0;padding:0;list-style:none}.job-log li{padding:8px 16px;border-bottom:1px solid var(--line);color:var(--muted);font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px}
|
||||||
.form-editor-layout{grid-template-columns:280px minmax(0,1.2fr) minmax(320px,.8fr)}.form-editor-actions{display:flex;gap:8px;flex-wrap:wrap}.tree-item[data-html5-form-selected="true"]{background:#f8fbff;border-left-color:var(--brand)}.form-designer{height:calc(100% - 72px);display:grid;grid-template-rows:auto minmax(0,1fr) auto;overflow:hidden;background:#f7f9fc}.form-designer-head,.form-designer-foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border-bottom:1px solid var(--line);background:#fff}.form-designer-head strong,.form-designer-head small{display:block}.form-designer-head small{color:var(--muted)}.form-designer-head span{border:1px solid var(--line);background:#eef6f1;color:var(--ok);padding:5px 8px;font-size:12px;font-weight:900}.form-designer-foot{border-top:1px solid var(--line);border-bottom:0}.form-canvas{min-height:0;overflow:auto;padding:18px}.form-window{max-width:720px;min-height:420px;margin:0 auto;border:1px solid var(--line);background:#fff;box-shadow:0 8px 24px rgba(15,23,42,.08)}.form-window-title{height:38px;display:flex;align-items:center;border-bottom:1px solid var(--line);padding:0 12px;font-weight:900;background:#fbfcfe}.form-command-bar{display:flex;gap:6px;flex-wrap:wrap;padding:10px 12px;border-bottom:1px solid var(--line)}.form-command-bar button{height:28px;font-size:12px}.form-fields{display:grid;gap:10px;padding:14px}.form-field{display:grid;grid-template-columns:160px minmax(0,1fr);gap:8px;align-items:center}.form-field span{font-size:12px;font-weight:800;color:var(--muted)}.form-field input{height:30px;border:1px solid var(--line);padding:0 8px;background:#fbfaf7}
|
.form-editor-layout{grid-template-columns:280px minmax(0,1.2fr) minmax(320px,.8fr)}.form-editor-actions{display:flex;gap:8px;flex-wrap:wrap}.tree-item[data-html5-form-selected="true"]{background:#f8fbff;border-left-color:var(--brand)}.form-designer{height:calc(100% - 72px);display:grid;grid-template-rows:auto minmax(0,1fr) auto;overflow:hidden;background:#f7f9fc}.form-designer-head,.form-designer-foot{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border-bottom:1px solid var(--line);background:#fff}.form-designer-head strong,.form-designer-head small{display:block}.form-designer-head small{color:var(--muted)}.form-designer-head span{border:1px solid var(--line);background:#eef6f1;color:var(--ok);padding:5px 8px;font-size:12px;font-weight:900}.form-designer-foot{border-top:1px solid var(--line);border-bottom:0}.form-canvas{min-height:0;overflow:auto;padding:18px}.form-window{max-width:720px;min-height:420px;margin:0 auto;border:1px solid var(--line);background:#fff;box-shadow:0 8px 24px rgba(15,23,42,.08)}.form-window-title{height:38px;display:flex;align-items:center;border-bottom:1px solid var(--line);padding:0 12px;font-weight:900;background:#fbfcfe}.form-command-bar{display:flex;gap:6px;flex-wrap:wrap;padding:10px 12px;border-bottom:1px solid var(--line)}.form-command-bar button{height:28px;font-size:12px}.form-fields{display:grid;gap:10px;padding:14px}.form-field{display:grid;grid-template-columns:160px minmax(0,1fr);gap:8px;align-items:center}.form-field span{font-size:12px;font-weight:800;color:var(--muted)}.form-field input{height:30px;border:1px solid var(--line);padding:0 8px;background:#fbfaf7}
|
||||||
.form-designer-body{min-height:0;display:grid;grid-template-columns:minmax(0,1fr) 340px;overflow:hidden}.form-property-panel{min-height:0;overflow:auto;border-left:1px solid var(--line);background:#fff}.property-row{display:grid;grid-template-columns:1fr 132px;gap:8px;padding:12px;border-bottom:1px solid var(--line)}.property-row label,.form-editor-row label{display:grid;gap:4px;font-size:11px;font-weight:900;color:var(--muted);text-transform:uppercase}.property-row input,.property-row select,.form-editor-row input,.form-editor-row select,.form-add-row input,.form-add-row select{height:30px;min-width:0;border:1px solid var(--line);background:#fff;padding:0 8px;color:var(--text);font:13px/1.2 system-ui,-apple-system,Segoe UI,sans-serif;text-transform:none}.form-window{border-color:#b9c1cd;background:#fdfdfd;box-shadow:0 12px 28px rgba(33,43,54,.12)}.form-window-title{justify-content:space-between;height:34px;background:linear-gradient(#f8f9fb,#e9edf3);border-bottom-color:#cbd3df;color:#1f2937}.form-window-title small{font-size:11px;color:#687385;font-weight:800}.form-command-bar{min-height:42px;background:#f4f6f9;border-bottom-color:#ccd4df}.form-command-bar button{height:27px;border-color:#aeb8c6;background:linear-gradient(#fff,#edf1f6);box-shadow:inset 0 1px 0 #fff;color:#1f2937}.form-command-bar button:hover{background:#fff}.form-fields{grid-template-columns:repeat(12,minmax(0,1fr));align-content:start;gap:8px 12px;padding:14px 16px 22px;background:#fbfbfc}.form-field{grid-column:1/-1;grid-template-columns:150px minmax(0,1fr);min-height:32px;padding:4px 6px;border:1px solid transparent}.form-field:hover{border-color:#b9c1cd;background:#fff}.form-field[data-html5-form-width="half"]{grid-column:span 6}.form-field[data-html5-form-width="third"]{grid-column:span 4}.form-field>label{font-size:12px;font-weight:800;color:#4b5563;align-self:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.form-input-control,.form-text-control{width:100%;border:1px solid #aeb8c6;background:#fff;padding:0 7px;color:#17202a;box-shadow:inset 0 1px 2px rgba(15,23,42,.06)}.form-input-control{height:28px}.form-text-control{min-height:64px;resize:none}.form-check-control{display:flex;align-items:center;gap:8px;color:#374151}.form-check-control input{width:16px;height:16px}.form-table-control{border:1px solid #aeb8c6;background:#fff;min-height:108px;overflow:hidden}.form-table-control div{display:grid;grid-template-columns:2fr 1fr 1fr}.form-table-control span{min-height:27px;border-right:1px solid #d7dde6;border-bottom:1px solid #d7dde6;padding:5px 7px;color:#374151;font-size:12px}.form-table-control div:first-child span{background:#eef2f7;font-weight:900}.form-group-control{min-height:44px;border:1px dashed #aeb8c6;background:#f6f8fb;padding:10px;color:#687385;font-weight:800}.form-editor-row{display:grid;gap:7px;padding:10px 12px;border-bottom:1px solid var(--line)}label.form-editor-row{grid-template-columns:88px minmax(0,1fr);align-items:center;text-transform:none}.form-editor-row small{color:var(--muted);font-size:11px}.form-editor-row[data-html5-form-element-editor]{grid-template-columns:1fr 118px 1fr 104px}.form-add-row{display:grid;grid-template-columns:minmax(0,1fr) 118px;gap:8px;padding:12px;border-bottom:1px solid var(--line);background:#fbfcfe}.button.primary{background:var(--brand);border-color:var(--brand);color:#fff;margin:12px}.form-designer-foot small{color:var(--muted);font-weight:800}.form-window[data-html5-form-layout="compact"] .form-fields{gap:4px 8px}.form-window[data-html5-form-layout="compact"] .form-field{min-height:28px}.form-window[data-html5-form-layout="columns"] .form-field{grid-column:span 6}.form-window[data-html5-form-layout="columns"] .form-field[data-html5-form-control="table"],.form-window[data-html5-form-layout="columns"] .form-field[data-html5-form-control="group"]{grid-column:1/-1}
|
.form-designer-body{min-height:0;display:grid;grid-template-columns:minmax(0,1fr) 340px;overflow:hidden}.form-property-panel{min-height:0;overflow:auto;border-left:1px solid var(--line);background:#fff}.property-row{display:grid;grid-template-columns:1fr 132px;gap:8px;padding:12px;border-bottom:1px solid var(--line)}.property-row label,.form-editor-row label{display:grid;gap:4px;font-size:11px;font-weight:900;color:var(--muted);text-transform:uppercase}.property-row input,.property-row select,.form-editor-row input,.form-editor-row select,.form-add-row input,.form-add-row select{height:30px;min-width:0;border:1px solid var(--line);background:#fff;padding:0 8px;color:var(--text);font:13px/1.2 system-ui,-apple-system,Segoe UI,sans-serif;text-transform:none}.form-window{border-color:#b9c1cd;background:#fdfdfd;box-shadow:0 12px 28px rgba(33,43,54,.12)}.form-window-title{justify-content:space-between;height:34px;background:linear-gradient(#f8f9fb,#e9edf3);border-bottom-color:#cbd3df;color:#1f2937}.form-window-title small{font-size:11px;color:#687385;font-weight:800}.form-command-bar{min-height:42px;background:#f4f6f9;border-bottom-color:#ccd4df}.form-command-bar button{height:27px;border-color:#aeb8c6;background:linear-gradient(#fff,#edf1f6);box-shadow:inset 0 1px 0 #fff;color:#1f2937}.form-command-bar button:hover{background:#fff}.form-fields{grid-template-columns:repeat(12,minmax(0,1fr));align-content:start;gap:8px 12px;padding:14px 16px 22px;background:#fbfbfc}.form-field{grid-column:1/-1;grid-template-columns:150px minmax(0,1fr);min-height:32px;padding:4px 6px;border:1px solid transparent}.form-field:hover{border-color:#b9c1cd;background:#fff}.form-field[data-html5-form-width="half"]{grid-column:span 6}.form-field[data-html5-form-width="third"]{grid-column:span 4}.form-field>label{font-size:12px;font-weight:800;color:#4b5563;align-self:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.form-input-control,.form-text-control{width:100%;border:1px solid #aeb8c6;background:#fff;padding:0 7px;color:#17202a;box-shadow:inset 0 1px 2px rgba(15,23,42,.06)}.form-input-control{height:28px}.form-text-control{min-height:64px;resize:none}.form-check-control{display:flex;align-items:center;gap:8px;color:#374151}.form-check-control input{width:16px;height:16px}.form-table-control{border:1px solid #aeb8c6;background:#fff;min-height:108px;overflow:hidden}.form-table-control div{display:grid;grid-template-columns:2fr 1fr 1fr}.form-table-control span{min-height:27px;border-right:1px solid #d7dde6;border-bottom:1px solid #d7dde6;padding:5px 7px;color:#374151;font-size:12px}.form-table-control div:first-child span{background:#eef2f7;font-weight:900}.form-group-control{min-height:44px;border:1px dashed #aeb8c6;background:#f6f8fb;padding:10px;color:#687385;font-weight:800}.form-editor-row{display:grid;gap:7px;padding:10px 12px;border-bottom:1px solid var(--line)}label.form-editor-row{grid-template-columns:88px minmax(0,1fr);align-items:center;text-transform:none}.form-editor-row small{color:var(--muted);font-size:11px}.form-editor-row[data-html5-form-element-editor]{grid-template-columns:1fr 118px 1fr 104px}.form-add-row{display:grid;grid-template-columns:minmax(0,1fr) 118px;gap:8px;padding:12px;border-bottom:1px solid var(--line);background:#fbfcfe}.button.primary{background:var(--brand);border-color:var(--brand);color:#fff;margin:12px}.form-designer-foot small{color:var(--muted);font-weight:800}.form-window[data-html5-form-layout="compact"] .form-fields{gap:4px 8px}.form-window[data-html5-form-layout="compact"] .form-field{min-height:28px}.form-window[data-html5-form-layout="columns"] .form-field{grid-column:span 6}.form-window[data-html5-form-layout="columns"] .form-field[data-html5-form-control="table"],.form-window[data-html5-form-layout="columns"] .form-field[data-html5-form-control="group"]{grid-column:1/-1}
|
||||||
|
.form-empty-structure{grid-column:1/-1;border:1px dashed #aeb8c6;background:#fff;padding:18px;color:#687385}.form-empty-structure strong,.form-empty-structure span{display:block}.form-empty-structure strong{color:#1f2937}
|
||||||
@media(max-width:980px){.layout{grid-template-columns:1fr;height:auto}.tree,.inspector{max-height:320px}.editor{min-height:520px}.hero{display:block}.shell{padding:16px}}
|
@media(max-width:980px){.layout{grid-template-columns:1fr;height:auto}.tree,.inspector{max-height:320px}.editor{min-height:520px}.hero{display:block}.shell{padding:16px}}
|
||||||
@media(max-width:980px){.form-designer-body{grid-template-columns:1fr}.form-property-panel{border-left:0;border-top:1px solid var(--line)}.form-field[data-html5-form-width="half"],.form-field[data-html5-form-width="third"]{grid-column:1/-1}.form-editor-row[data-html5-form-element-editor]{grid-template-columns:1fr}.property-row{grid-template-columns:1fr}}
|
@media(max-width:980px){.form-designer-body{grid-template-columns:1fr}.form-property-panel{border-left:0;border-top:1px solid var(--line)}.form-field[data-html5-form-width="half"],.form-field[data-html5-form-width="third"]{grid-column:1/-1}.form-editor-row[data-html5-form-element-editor]{grid-template-columns:1fr}.property-row{grid-template-columns:1fr}}
|
||||||
@media(max-width:980px){.setup-layout{grid-template-columns:1fr}.setup-metrics{grid-template-columns:1fr 1fr}.settings-form{grid-template-columns:1fr}}
|
@media(max-width:980px){.setup-layout{grid-template-columns:1fr}.setup-metrics{grid-template-columns:1fr 1fr}.settings-form{grid-template-columns:1fr}}
|
||||||
|
|||||||
Reference in New Issue
Block a user