Практика avm, примеры интерфейсов#

В данном разделе будет расписана некоторая практика, связанная с разметкой в avm. Будут рассмотрены различные реализации интерфейсов.

Присоединение нескольких отображений (одновременно с закладками и без)#

Для этого в разметке нужно выбрать tabDynamicComposer, который позволяет присоединять, как с помощью dynamicItems, так и с помощью tabItems.

Примечание

Напомню, что:

  • dynamicItems - обычное присоединение отображения с возможность выбора выравнивания;

  • tabItems - присоединение отображения, как вкладка.

<representation name="Card" editMode="edit" stdFilter.isAvailable="false">
    <layout>
        <tabDynamicComposer>
            <frame filter.isVisible="false">
                <card/>
            </frame>
            <dynamicItems>
                <dynamicItem selection="gtk-ru.bitec.app.oilOil_RecievTaskAvi"representation="List_Statistics" align="right"/>
            </dynamicItems>
            <tabItems isVisible="true"
                selection="gtk-ru.bitec.app.btk.Btk_ObjectTypeTabAvi"
                representation="List_Tab"
                selection.selectionAttr="SSELECTIONNAME"
                selection.representationAttr="SREPRESENTATIONNAME"
                selection.captionAttr="SCAPTION"
                selection.imageIndexAttr="NIMAGE"
                selection.paramsAttr="JSONPARAMS"/>
        </tabDynamicComposer>
    </layout>
</representation>

Примечание

В примере разметки в tabItems присоединены вкладки, настроенные на типе объекта. Это часто встречающаяся практика, данный пример полезно понимать.

Как убрать кнопку сворачивания отображения в модальное окно#

Чтобы убрать кнопку сворачивания отображения, присоединенного dynamicItems, необходимо выключить header у frame:

<representation name="Card_Body" editMode="edit" stdFilter.isAvailable="false">
    <layout>
        <simpleComposer>
            <frame header.isVisible="false">
                <card/>
            </frame>
        </simpleComposer>
    </layout>
</representation>

Таким способом будет убран заголовок с кнопкой.

Заголовок можно вернуть с помощью конструкции vGroup.

<representation name="Card_Body" editMode="edit" stdFilter.isAvailable="false">
    <layout>
        <tabComposer>
            <frame filter.isVisible="false" toolBar.isVisible="false" header.isVisible="false">
                <card>
                    <layout>
                        <vBox>
                            <vGroup caption="Задание на приём нефтепродуктов">
                                <hBox>
                                    <vBox>
                                        <attr name="sRegNum"/>
                                        <attr name="dReg"/>
                                        <attr name="idStateHL"/>
                                    </vBox>
                                    <vBox>
                                        <attr name="idObjectTypeHL"/>
                                        <attr name="gidSrcHL"/>
                                        <attr name="gidSrc"/>
                                    </vBox>
                                </hBox>
                                <attr name="sDescription"/>
                            </vGroup>
                        </vBox>
                    </layout>
                </card>
            </frame>
            <tabItems isVisible="true"
                      selection="gtk-ru.bitec.app.btk.Btk_ObjectTypeTabAvi"
                      representation="List_Tab"
                      selection.selectionAttr="SSELECTIONNAME"
                      selection.representationAttr="SREPRESENTATIONNAME"
                      selection.captionAttr="SCAPTION"
                      selection.imageIndexAttr="NIMAGE"
                      selection.paramsAttr="JSONPARAMS"/>
        </tabComposer>
    </layout>
</representation>

Отображение с кнопкой сворачивания:

Отображение с выключенным header:

Отображение с vGroup:

Динамическая смена отображения#

Эта тема отвечает на вопрос, как при изменении значения поля менять присоединяемое отображение.

Примечание

Так работает tabItems на типе объекта: при смене типа объекта набор вкладок в tabItems меняется на те, которые настроены на выбранном типе объекта.

Смена отображений при изменении значения фильтра#

Пример реализации:

ru.bitec.app.oil.Oil_TaskMonitor.avm.xml - разметка отображения Card,

ru.bitec.app.oil.Oil_TaskMonitorAvi.Card - результат выборки отображения Card.

avm:

<representation name="Card">
    <filter name="Oil_TaskMonitorFilter">
        <macros name="DefFltReferenceMacro">
            <condition logicalOperator="and" id="attrs" isExpression="false">
                <filterAttr name="flt_idObjectType" attribute-type="Long" caption="Тип задания"
                            isLastInLine="false" order="10" editorType="edit" isVisible="false"/>
                <filterAttr name="flt_idObjectTypeHL" attribute-type="Varchar" caption="Тип задания"
                            isLastInLine="false" order="10.2" editorType="lookup">
                    <editor>
                        <lookup lookupQuery="gtk-Btk_ObjectTypeAvi#MainLookup" lookupKeyAttr="id"
                                lookupListAttr="sHeadLine" changeableAttr="flt_idObjectType"
                                isLookupLazyLoad="true"/>
                    </editor>
                </filterAttr>
                <filterAttr name="flt_dBegin" attribute-type="Date" caption="Дата, время с" isLastInLine="false"
                            order="20" editorType="dateTimePick">
                    <card controlWidth="60" isControlWidthFixed="true"/>
                </filterAttr>
                <filterAttr name="flt_dEnd" attribute-type="Date" caption="по" isLastInLine="true"
                            order="30" editorType="dateTimePick">
                    <card controlWidth="60" isControlWidthFixed="true"/>
                </filterAttr>
            </condition>
        </macros>
    </filter>
    <layout>
        <tabDynDetComposer>
            <frame filter.isVisible="true">
                <card/>
            </frame>
            <tabItems selection="gtk-Oil_TaskMonitorAvi" representation="List_RecievTask" isVisible="false"/>
            <dynDetail masterAlign="top" detailAlign="client" selectionAttr="sSelectionAttr"
                       representationAttr="sRepresentationAttr" paramsAttr="jsonParams" isVisible="true"/>
        </tabDynDetComposer>
    </layout>
    <attributes>
        <attr name="sSelectionAttr" isVisible="false"/>
        <attr name="sRepresentationAttr" isVisible="false"/>
        <attr name="sCaptionAttr" isVisible="false"/>
        <attr name="nImageIndexAttr" isVisible="false"/>
        <attr name="jsonParams" isVisible="false"/>
    </attributes>
</representation>

Это отображение Card.

В тэге filter определены фильтры. Панель фильтров включена свойством тэга <frame filter.isVisible="true">.

Присоединение отображений выполнено инструментом tabDynDetComposer, который позволяет присоединять как tabItems (закладками), так и dynDetail (аналогия dynamicItem). Закладка tabItems присоединена, как пустышка, чтобы заработал tabDynDetComposer (проблема была актуальна на 05.07.2023, обещали исправить).

В закладке dynDetail определены свойства, значения которых являются атрибутами отображения (см. перечень атрибутов отображения). Тем самым отображение Card, как результат выборки должно иметь настройку для dynDetail.

Для формирования такой выборки необходимо рассмотреть Avi:

trait List_CommonResource extends Default with List_CoreResource {
  override protected val sNameFieldForGST = "Brigade"
  override protected val sNameTableForGST = "Bs_Brigade"
}

trait Card extends Default with super.Card {
  //Типы объекта класса Oil_Task и соответствующие отображения к ним
  lazy val mapRepByOT: Map[NString, NString] =
    Map("Oil_TaskReceive".ns -> "List_RecievTask".ns
      , "Oil_TaskShip".ns -> "List_ShipTask".ns
      , "Oil_TaskCommon".ns -> "List_CommonTask".ns
    )

  @FlushBefore(mode = FlushBeforeMode.Disabled)
  override protected def onRefresh: Recs = {
    val idvOT = getVar("flt_idObjectType").asNLong
    if (idvOT.isNotNull) {
      val sRep = mapRepByOT.getOrElse(Btk_ObjectTypeApi().getMnemoCode(idvOT), throw AppException("Не найдено отображение."))
      s"""
        select
            'gtk-Oil_TaskMonitorAvi' as sSelectionAttr
            ,'$sRep' as sRepresentationAttr
            ,'${Btk_ObjectTypeApi().getShortCaption(idvOT)}' as sCaptionAttr
            ,cast(null as numeric) as nImageIndexAttr
            ,'{}' as jsonParams
        """
    } else {
      s"""
        select cast(null as int8) as id
          ,cast(null as numeric) as nOrder
          ,cast(null as varchar) as sCaptionAttr
          ,cast(null as numeric) as nImageIndexAttr
          ,cast(null as varchar) as sRepresentationAttr
          ,cast(null as varchar) as sSelectionAttr
          ,'{}' as jsonParams
        where false
        """
    }
  }

  override protected def selectStatement: JClob = s""
}

У отоборажения есть фильтр по типу объекта flt_idObjectType с редактором lookup. При выборе различных типов объекта к Card должны присоединяться различные отображения (их соответствие описано в Map mapRepByOT).

Алгоритм метода onRefresh:

  • Проверка, не пустое ли значение фильтра типа объекта

val idvOT = getVar("flt_idObjectType").asNLong
if (idvOT.isNotNull)
  • Получение соответствующего отображения для выбранного типа объекта из mapRepByOT:

val sRep = mapRepByOT.getOrElse(Btk_ObjectTypeApi().getMnemoCode(idvOT), throw AppException("..."))
  • Формирование выборки:

sSelectionAttr - выборка, отображение которого присоединяется (в рассматриваемом случае все отображения принадлежат одной и той же выборке);

sRepresentationAttr - соответствующее отображение

sCaptionAttr - описание отображения

nImageIndexAttr - картинка отображения

jsonParams - json параметры

Смена отображений при изменении значения поля отображения#

Если нужно менять отображения в зависимости от значения полей отображения Card, то нужно сделать промежуточное отображение с механикой, описанной выше (например, List_Tab), которое присоединить к Card.

Фиксация ширины столбца/поля и задание значения ширины#

В карточке:

Необходимо для атрибута задать свойства в тэге card:

<attr name="sRegNum" caption="Рег.№" editorType="edit" order="10" isReadOnly="true">
    <card controlWidth="30" isControlWidthFixed="true"/>
</attr>

В списке:

Необходимо для атрибута задать свойства в тэге grid:

<attr name="sRegNum" caption="Рег.№" editorType="edit" order="10">
    <grid controlWidth="30" isControlWidthFixed="true"/>
</attr>

controlWidth - ширина поля

isControlWidthFixed - true - ширина будет зафиксирована, false - доступна для редактирования в интерфейсе.

Убрать описание у поля#

Чтобы убрать заголовок у поля, писать <arrt caption=""> неправильно.

Необходимо задать свойство labelPosition:

<attr name="idTankerHL" caption="Танкер" order="120.1">
    <editor labelPosition="none"/>
</attr>

Помимо значения "none" свойство labelPosition имееть значения:

  • "right" - справа

  • "left" - слева

  • "above" - сверху

  • "under" - снизу

Как в списке покрасить ячейку#

Для этого необходимо иметь дополнительный атрибут, в который будет записываться системное имя стиля. Обычно такой атрибут называют sStyle.

Далее необходимо указать, что столбец такого-то атрибута имеет стиль, описанный в атрибуте sStyle:

<attr name="nRating" order="20" caption="Разряд">
    <style attr="sStyle"/>
</attr>

Стили можно посмотреть и создать в «Настройках систем»: