diff --git a/.claude/skills/form-compile/SKILL.md b/.claude/skills/form-compile/SKILL.md index 7720bffa..d8ecfa9b 100644 --- a/.claude/skills/form-compile/SKILL.md +++ b/.claude/skills/form-compile/SKILL.md @@ -13,6 +13,8 @@ allowed-tools: Принимает компактное JSON-определение формы (20–50 строк) и генерирует полный корректный Form.xml (100–500+ строк) с namespace-декларациями, автогенерированными companion-элементами, последовательными ID. +> При проектировании формы с нуля — см. [паттерны компоновки](../../docs/form-patterns.md): типовые архетипы (документ, обработка, список, справочник, мастер), конвенции именования, примеры DSL. + ## Использование ``` diff --git a/docs/form-guide.md b/docs/form-guide.md index d9c8738a..e408cb75 100644 --- a/docs/form-guide.md +++ b/docs/form-guide.md @@ -211,3 +211,4 @@ Claude создаст JSON-определение и вызовет `/form-compi - [Управляемая форма](1c-form-spec.md) — Form.xml, элементы, команды, реквизиты, система типов - [Form DSL](form-dsl-spec.md) — JSON-формат описания формы для `/form-compile` и `/form-add` +- [Паттерны компоновки](form-patterns.md) — типовые архетипы форм, конвенции именования, примеры DSL diff --git a/docs/form-patterns.md b/docs/form-patterns.md new file mode 100644 index 00000000..5e9ef562 --- /dev/null +++ b/docs/form-patterns.md @@ -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" } + ] +} +```