Add form layout patterns guide with real-world conventions

Design patterns extracted from 1C:Accounting 8.3.24 forms:
- 5 archetypes: document, processor, list, catalog, wizard
- Naming conventions for groups, elements, event handlers
- Layout principles (2-column header, footer totals, filter pairs)
- Two complete DSL examples (processing form, list with filters)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-02-09 18:12:17 +03:00
parent c5fa794cf3
commit 5c07dec82c
3 changed files with 222 additions and 0 deletions
+219
View File
@@ -0,0 +1,219 @@
# Паттерны компоновки управляемых форм
Рекомендации по дизайну форм, извлечённые из типовых конфигураций 1С. Используйте при создании форм через `/form-compile`, когда требования пользователя не детализируют расположение элементов.
## Архетипы форм
### Форма документа
```
Шапка (horizontal, 2 колонки)
├─ Левая (vertical): НомерДата (H: Номер + Дата "от"), Контрагент, Договор
├─ Правая (vertical): Организация, Подразделение, ЦеныИВалюта (надпись-ссылка)
Страницы (pages)
├─ Товары: таблица Объект.Товары
├─ Услуги: таблица Объект.Услуги (опционально)
└─ Дополнительно: прочие реквизиты
Подвал (vertical)
├─ Итоги (horizontal): Всего, НДС, Скидка
└─ КомментарийОтветственный (horizontal): Комментарий + Ответственный
```
**Типичные события:** OnCreateAtServer, OnReadAtServer, OnOpen, BeforeWriteAtServer, AfterWriteAtServer, AfterWrite, NotificationProcessing
**Свойства:** autoTitle=false, командная панель со стандартными + глобальными командами
### Форма обработки (DataProcessor)
```
Параметры (vertical)
├─ Группа полей ввода (Организация, Период, режимы работы)
├─ Информационные надписи (label, hyperlink)
Рабочая область
├─ Таблица данных или Pages с вкладками
Кнопки действий
├─ Выполнить / Применить (defaultButton)
├─ Закрыть (stdCommand: Close)
```
**Типичные события:** OnCreateAtServer, OnOpen, NotificationProcessing
**Свойства:** windowOpeningMode=LockOwnerWindow (если диалог), autoTitle=false
### Форма списка
```
Отборы (group: alwaysHorizontal)
├─ ГруппаОтбор[Поле] (H): Флажок + Поле ввода (для каждого фильтра)
Список (table, DynamicList)
├─ Колонки: labelField (не input — данные только для чтения)
```
**Типичные события:** OnCreateAtServer, OnOpen, NotificationProcessing, OnLoadDataFromSettingsAtServer
**Свойства:** autoSaveDataInSettings=Use (запомнить отборы)
**Фильтры:** пара реквизитов на каждый фильтр — `Отбор[Поле]` (значение) + `Отбор[Поле]Использование` (boolean, флажок вкл/выкл)
### Форма элемента справочника
**Простая:**
```
ГруппаРеквизитов (horizontal)
├─ Наименование -> Объект.Description
└─ Код -> Объект.Code (если нужен)
```
**Сложная:**
```
Главное (vertical)
├─ Наименование -> Объект.Description
├─ Параметры (horizontal, 2 колонки)
│ ├─ Левая: основные реквизиты
│ └─ Правая: дополнительные реквизиты
└─ КонтактныеДанные / Дополнительно (vertical)
```
**Типичные события:** OnCreateAtServer, OnReadAtServer, BeforeWriteAtServer, NotificationProcessing
### Мастер (Wizard)
```
Страницы (pages, OnCurrentPageChange)
├─ Шаг1: описание + параметры
├─ Шаг2: основная работа
└─ Шаг3: результат
Кнопки (horizontal)
├─ Назад (command), Далее (command, defaultButton), Выполнить (command)
└─ Закрыть (stdCommand: Close)
```
**Свойства:** windowOpeningMode=LockOwnerWindow
## Конвенции именования
### Группы
| Назначение | Имя | Тип |
|-----------|-----|-----|
| Шапка | `ГруппаШапка` | horizontal |
| Левая колонка | `ГруппаШапкаЛевая` | vertical |
| Правая колонка | `ГруппаШапкаПравая` | vertical |
| Номер+Дата | `ГруппаНомерДата` | horizontal |
| Подвал | `ГруппаПодвал` | vertical |
| Итоги | `ГруппаИтоги` | horizontal |
| Кнопки | `ГруппаКнопок` | horizontal |
| Страницы | `ГруппаСтраницы` / `Страницы` | pages |
### Элементы
| Назначение | Имя | Суффикс |
|-----------|-----|---------|
| Поле в таблице | `[Таблица][Поле]` | — |
| Итог | `Итоги[Поле]` | — |
| Надпись-ссылка | `[Поле]Надпись` | — |
| Фильтр | `Отбор[Поле]` | — |
| Флажок фильтра | `Отбор[Поле]Использование` | — |
| Кнопка команды | `[Команда]Кнопка` | — |
### Обработчики событий
Имя обработчика = имя элемента + суффикс события на русском:
| Событие | Суффикс | Пример |
|---------|---------|--------|
| OnChange | ПриИзменении | `ОрганизацияПриИзменении` |
| StartChoice | НачалоВыбора | `КонтрагентНачалоВыбора` |
| Click | Нажатие | `ЦеныИВалютаНажатие` |
| OnEditEnd | ПриОкончанииРедактирования | `ТоварыПриОкончанииРедактирования` |
| OnStartEdit | ПриНачалеРедактирования | `ТоварыПриНачалеРедактирования` |
Обработчики формы — стандартные имена: `ПриСозданииНаСервере`, `ПриОткрытии`, `ПередЗакрытием`, `ОбработкаОповещения`.
## Принципы компоновки
1. **Порядок чтения.** Сверху вниз, слева направо. Самое важное — вверху.
2. **Двухколоночная шапка.** Основные реквизиты слева (контрагент, склад), организационные справа (организация, подразделение).
3. **Кнопки действий внизу.** Главная кнопка — `defaultButton: true`. Закрыть — всегда последняя.
4. **Таблицы — основная область.** Табличные части занимают большую часть формы, обычно на Pages.
5. **Итоги рядом с таблицей.** В подвале, горизонтальная группа, все поля readOnly.
6. **Фильтры — отдельная зона.** Над списком, горизонтальная группа (alwaysHorizontal), пара "флажок + поле" на каждый фильтр.
7. **Скрытые элементы для состояний.** Баннеры, предупреждения — `visible: false` по умолчанию, показываются программно.
8. **Надписи-ссылки для диалогов.** `labelField` с `hyperlink: true` и событием Click — для открытия подформ (ЦеныИВалюта, УчётнаяПолитика).
## Примеры DSL
### Типичная форма обработки
```json
{
"title": "Загрузка данных из CSV",
"properties": { "autoTitle": false, "windowOpeningMode": "LockOwnerWindow" },
"events": { "OnCreateAtServer": "ПриСозданииНаСервере" },
"elements": [
{ "group": "vertical", "name": "ГруппаПараметры", "children": [
{ "input": "ФайлЗагрузки", "path": "ФайлЗагрузки", "title": "Файл", "clearButton": true, "horizontalStretch": true, "on": ["StartChoice"] },
{ "input": "Кодировка", "path": "Кодировка", "title": "Кодировка" },
{ "input": "Разделитель", "path": "Разделитель", "title": "Разделитель колонок" }
]},
{ "table": "Данные", "path": "Объект.Данные", "on": ["OnStartEdit"], "columns": [
{ "input": "ДанныеНомерСтроки", "path": "Объект.Данные.LineNumber", "readOnly": true, "title": "№" },
{ "input": "ДанныеНаименование", "path": "Объект.Данные.Наименование" },
{ "input": "ДанныеКоличество", "path": "Объект.Данные.Количество", "on": ["OnChange"] },
{ "input": "ДанныеСумма", "path": "Объект.Данные.Сумма", "readOnly": true }
]},
{ "group": "horizontal", "name": "ГруппаКнопок", "children": [
{ "button": "Загрузить", "command": "Загрузить", "title": "Загрузить из файла", "defaultButton": true },
{ "button": "Очистить", "command": "Очистить", "title": "Очистить таблицу" },
{ "button": "Закрыть", "stdCommand": "Close" }
]}
],
"attributes": [
{ "name": "Объект", "type": "ExternalDataProcessorObject.ЗагрузкаИзCSV", "main": true },
{ "name": "ФайлЗагрузки", "type": "string" },
{ "name": "Кодировка", "type": "string(20)" },
{ "name": "Разделитель", "type": "string(5)" }
],
"commands": [
{ "name": "Загрузить", "action": "ЗагрузитьОбработка" },
{ "name": "Очистить", "action": "ОчиститьОбработка" }
]
}
```
### Типичная форма со списком и фильтрами
```json
{
"properties": { "autoTitle": false, "autoSaveDataInSettings": "Use" },
"events": {
"OnCreateAtServer": "ПриСозданииНаСервере",
"NotificationProcessing": "ОбработкаОповещения"
},
"elements": [
{ "group": "alwaysHorizontal", "name": "ГруппаОтборы", "children": [
{ "group": "horizontal", "name": "ГруппаОтборОрганизация", "children": [
{ "check": "ОтборОрганизацияИспользование", "path": "ОтборОрганизацияИспользование", "titleLocation": "none", "on": ["OnChange"] },
{ "input": "ОтборОрганизация", "path": "ОтборОрганизация", "title": "Организация", "on": ["OnChange"] }
]},
{ "group": "horizontal", "name": "ГруппаОтборКонтрагент", "children": [
{ "check": "ОтборКонтрагентИспользование", "path": "ОтборКонтрагентИспользование", "titleLocation": "none", "on": ["OnChange"] },
{ "input": "ОтборКонтрагент", "path": "ОтборКонтрагент", "title": "Контрагент", "on": ["OnChange"] }
]}
]},
{ "table": "Список", "path": "Список", "on": ["Selection", "OnActivateRow"], "columns": [
{ "labelField": "СписокДата", "path": "Список.Дата", "title": "Дата" },
{ "labelField": "СписокНомер", "path": "Список.Номер", "title": "Номер" },
{ "labelField": "СписокКонтрагент", "path": "Список.Контрагент" },
{ "labelField": "СписокСумма", "path": "Список.Сумма" }
]}
],
"attributes": [
{ "name": "Список", "type": "DynamicList", "mainTable": "Document.РеализацияТоваров" },
{ "name": "ОтборОрганизация", "type": "CatalogRef.Организации" },
{ "name": "ОтборОрганизацияИспользование", "type": "boolean" },
{ "name": "ОтборКонтрагент", "type": "CatalogRef.Контрагенты" },
{ "name": "ОтборКонтрагентИспользование", "type": "boolean" }
]
}
```