spec/area_ls.cfm
2025-06-02 16:16:51 +03:00

145 lines
12 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<cfsilent>
<cfimport prefix="m" taglib="lib"/>
<cfimport prefix="c" taglib="lib/controls"/>
<cfimport prefix="d" taglib="lib/data"/>
<cfimport prefix="layout" taglib="layout"/>
</cfsilent><m:silent silent="No">
<cffunction name="hideNonPositive">
<cfargument name="a"/>
<cfreturn (a GT 0)? a : ""/>
</cffunction>
<m:prepare_ls entity="area" pageInfoOut="pageInfo" trackOut="tr"/>
<m:filter_settings target="#pageInfo.entity#_ls">
<m:filterparam filter=#filter# param="quickfilter" ftype="string" prefix="%" suffix="%" expression="((p.project like ?) OR (p.customer like ?) OR (p.customer_alias like ?) OR (p.area_class_type like ?) OR (p.division like ?) OR (p.performer_short like ?))" default=""/>
</m:filter_settings>
<cfset pageInfo.settings.filter=#filter#/>
<cftry>
<cfquery name="qRead" datasource="#request.DS#">
select
<d:field_set titleMapOut="titleMap" lengthOut="fieldCount">
<d:field title="ID" cfSqlType="CF_SQL_INTEGER">m.area_id</d:field>
<d:field title="Код группы">m.area_code</d:field>
<d:field title="Аналитический код">m.analytic_code</d:field>
<d:field title="Группа (RUS)">m.area</d:field>
<d:field title="Группа (ENG)">m.area_en</d:field>
<d:field title="Абстр. услуг">(select count(*) from abstract_service a where a.area_id=m.area_id) as as_cnt</d:field>
<d:field title="Вариантов услуг">(select count(*) from abstract_service a join service s on (a.abstract_service_id=s.abstract_service_id) where a.area_id=m.area_id) as svc_cnt</d:field>
</d:field_set>
from area m
where 1=1 <m:filter_build filter=#pageInfo.settings.filter#/>
order by <m:order_build sortArray=#pageInfo.settings.sort.sortArray# fieldCount=#fieldCount#/>
</cfquery>
<cfcatch type="database">
<m:ls_catch catch=#cfcatch# status=#pageInfo.status#/>
</cfcatch>
</cftry>
<cfquery name="qCountTotal" datasource="#request.DS#">
select count(*) as cnt from area where 1=1
</cfquery>
</m:silent><!---
-----------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------
---><cfif isDefined("output_xls")>
<layout:xml qRead=#qRead# titleMap=#titleMap# filename="#pageInfo.entity#.xml"/>
<cfabort/>
</cfif><cfif isDefined("output_json")>
<layout:json qRead=#qRead# titleMap=#titleMap# filename="#pageInfo.entity#.json"/>
<cfabort/>
</cfif><!---
---><layout:page section="header" pageInfo=#pageInfo#>
<layout:attribute name="title">
<cfoutput><b>Группы услуг</b></cfoutput>
</layout:attribute>
<layout:attribute name="controls">
<!---skip filter link, filter is not implemented--->
<!---<layout:language_switch/>--->
</layout:attribute>
</layout:page>
<cfif pageInfo.readPermitted() AND !pageInfo.status.errorState>
<layout:grid_summary
recordCount=#qRead.recordCount#
totalCount=#qCountTotal.cnt#
footerOut="gridFooter"
excelLink="Yes"
jsonLink="Yes"
/>
<!--- так довольно громоздко, но можно обойтись без Dynamic Evaluation --->
<cfset queryAddColumn(qRead,'f_link_view_edit')/>
<cfset titleMap.f_link_view_edit={ordinal=#StructCount(titleMap)+1#}/><!--- *** Это легко забыть, потому что TitleMap никак не сцеплен с qRead. Напрашивается: упаковать их в одну обертку (но тогда обращение станет длиннее). Еще идея - назвать qRead и TitleMap так, чтобы была видна их связь. --->
<!--- <cfdump var=#titleMap#/> --->
<cfset queryAddColumn(qRead,'f_link_del')/>
<cfset titleMap.f_link_del={ordinal=0}/>
<!--- <cfdump var=#titleMap#/> --->
<cfoutput query=#qRead# startRow=#pageInfo.nStart# maxRows=#pageInfo.recordsPerPage#><!--- *** Здесь некомфортное дублирование startRow, maxRows с вызовом c:table ниже, причем в c:table оно закопано в модуль, и аргументы называются по-разному... но мы хотим немного сэкономить и не обрабатывать весь резалтсет --->
<cfsavecontent variable="qRead.f_link_view_edit">
<a href="#pageInfo.entity#.cfm?#pageInfo.entity#_id=#area_id#&#tr.fwx#" name="#area_id#" <cfif pageInfo.writePermitted()>title="редактировать" class="edit"<cfelse>title="просмотр" class="view"</cfif>></a>
</cfsavecontent>
<cfsavecontent variable="qRead.f_link_del">
<cfif pageInfo.writePermitted()><a href="#pageInfo.entity#_del.cfm?#pageInfo.entity#_id=#area_id#&#tr.fwx#" name="#area_id#" class="del" title="удалить"></a></cfif>
</cfsavecontent>
</cfoutput>
<c:table query=#qRead# recordsPerPage=#pageInfo.recordsPerPage# nStart=#pageInfo.nStart# titleMap=#titleMap# sortArray=#pageInfo.settings.sort.sortArray# class="worktable wide">
<c:column width="1%" sortable="false"><!---*** class="c" не пробрасывается --->
<c:th><c:link_add canWrite=#pageInfo.writePermitted()# entity=#pageInfo.entity# fwx=#tr.fwx#/></c:th>
<c:td field="f_link_view_edit" class="c"/>
<!--- <a href="#CALLER.pageInfo.entity#.cfm?#CALLER.pageInfo.entity#_id=#area_id#&#CALLER.tr.fwx#" name="#area_id#" <cfif pageInfo.writePermitted()>title="редактировать" class="edit"<cfelse>title="просмотр" class="view"</cfif>></a> --->
</c:column>
<c:column width="3%" field="area_id"/>
<c:column width="5%" field="area_code"/>
<c:column width="5%" field="analytic_code"/>
<c:column width="20%" field="area"/>
<c:column width="20%" field="area_en"/>
<c:column width="5%" field="as_cnt" formatter=#hideNonPositive#><c:td class="c"/></c:column>
<c:column width="5%" field="svc_cnt" formatter=#hideNonPositive#><c:td class="c"/></c:column>
<c:column width="1%" sortable="false">
<c:td field="f_link_del" class="c"/>
</c:column>
</c:table>
<cfoutput>#gridFooter#</cfoutput>
</cfif>
<layout:page section="footer"/>
<!---*** критика синтаксиса: CALLER в данном контексте выглядит ужасно. Когда применять #service_id#, когда ##service_id##
Может быть, стоит отказаться от пользовательского тега и обратиться к более ровному способу форматирования.
Например (явно плохая идея, но для примера) печатать таблицу, а потом ее парсить и переформатировать... некоторые это могли сделать на клиенте.
На самом деле все, что нужно нам из скоупа CALLER, уже определено в момент вызова тега, и мы можем заменить CALLER на <cfoutput>. Это тоже неуклюже. То есть тащить весь CALLER внутрь как бы избыточно (не говоря о том, что можно словить каких-то сложносочиненных глюков)
Сортировка должна происходить на сервере БД, иначе паджинатор бесполезен. Вытаскивать резалтсет на сервер приложений и там его сортировать выглядит очень плохим решением (еще глупее сортировать на клиенте). Для корректной сортировки нужно делать композицию колонок на сервере, что не очень хорошо, например, с нуллами надо обращаться осторожно (вот аргумент в пользу NOT NULL), и синтаксис становится зависим от диалекта SQL. Возможный вариант, если у нас композиция из 2 колонок, сортировать по обеим. Либо отказаться от сортировки по композитной колонке, что кажется проще всего.
Контекст query выглядит естественно, но эта простота вводит в заблуждение - на вид обычный синтаксис, а функции недоступны (паковать в реквест еще то удовольствие), контекст страницы недоступен, ищи его через CALLER - догадаться невозможно.
Можно передавать внутрь тега все, что может понадобиться - функции, переменные?
Можно придумать собственный синтаксис, заменяющий cfif
--->
<!--- тонкости
в примере ниже
Атрибуты тега вычисляются до вызова, в контексте вызывающей страницы, а содержимое обрабатывается самим тегом в его контексте (собственно, мы сказали ему так делать). Но при чтении исходника это совсем не очевидно и может порвать мозг.
Можно, конечно, обрабатывать исключение и подставлять контекст вызывающей страницы? Как заставить тег видеть контекст вызывающей страницы? Попробовал взять весь тег в cfoutput. В результате перестал видеть поля query. Откатился.
Печаль в том, что мы в одном месте (чего и хотели - чтобы рядом) получаем контекст вызывающей страницы и контекст query, поля которого хотим называть неквалифицированными именами.
Вариант: действуем в 2 прохода, сначала формируем структуру, а потом рендерим ее. В принципе, с учетом пагинации, может потреблять памяти меньше, чем сам query
<c:th><c:link_add canWrite=#pageInfo.writePermitted()# entity=#pageInfo.entity# fwx=#tr.fwx#/><cfoutput>#pageInfo.entity#</cfoutput>#pageInfo.entity#</c:th> --->
<!--- Хотелось бы:
чтобы контекст страницы был явно доступен во всех вложенных тегах. >> The custom tag's Caller scope is a reference to the calling page's Variables scope (добавить все из CALLER в Variables? А не зациклимся?
чтобы итератор работал прозрачно, как в cfoutput query
Как обойтись без динамического исполнения? Для всех форматтеров снаружи объявлять closure? нечитабельно.
Хотя читабельность простыней ниже - так себе
В общем, получается пока плохо
? полиморфизм i18(measure_short) - как бы избежать отдельной далекой декларации длинного списка похожих функций. Можно полю придать признак языка? Соглашение с суффиксами? Это уже какая-то морфология получается!
--->