diff --git a/packages/runtime/CHANGELOG.md b/packages/runtime/CHANGELOG.md index 1291c68b6314bf67988b236640758920903d402f..ae576687021c077aa0ece5f08e7869244fd1b5f3 100644 --- a/packages/runtime/CHANGELOG.md +++ b/packages/runtime/CHANGELOG.md @@ -7,6 +7,16 @@ ## [Unreleased] +### Added + +- 新增html视图控制器,新增html视图状态属性htmlUrl( iframe元素的URL地址) +- 新增搜索表单全局参数convertParamMode与搜索表单部件参数convertparammode,参数值为searchconds时将对象格式的参数转换为结构化的搜索条件数组 + +### Changed + +- 合并搜索栏部件与搜索表单searchconds值 +- 调整门户部件的srfsearchconds参数为标准格式 + ## [0.7.41-alpha.27] - 2025-09-19 ### Added diff --git a/packages/runtime/src/config/global-config.ts b/packages/runtime/src/config/global-config.ts index 70c32321668debe51cd1329c5047ee00fb47fc9d..f4b3f8c478470bab23189922efbd95258e1645fa 100644 --- a/packages/runtime/src/config/global-config.ts +++ b/packages/runtime/src/config/global-config.ts @@ -91,6 +91,7 @@ export class GlobalConfig implements IGlobalConfig { // 全局搜索表单配置 searchform: IGlobalSearchFormConfig = { enableStoredFilters: true, + convertParamMode: 'default', }; // 全局树配置 diff --git a/packages/runtime/src/controller/common/control/md-control.controller.ts b/packages/runtime/src/controller/common/control/md-control.controller.ts index 951ca22e04e61906ffde5d5d9329d9c474f53601..bc34c18e732eba6b19abf482a55c95df4bb5cd3e 100644 --- a/packages/runtime/src/controller/common/control/md-control.controller.ts +++ b/packages/runtime/src/controller/common/control/md-control.controller.ts @@ -411,7 +411,7 @@ export class MDControlController< }, ]; } else { - resultParams.searchconds = [...resultParams.srfsearchconds]; + resultParams.searchconds = [...srfsearchconds]; } delete resultParams.srfsearchconds; } diff --git a/packages/runtime/src/controller/common/index.ts b/packages/runtime/src/controller/common/index.ts index 3c5dc117ae58f969ad9ce5932b8515f2378c530d..27c608146d92f06108a1ac1186571b1888a707ee 100644 --- a/packages/runtime/src/controller/common/index.ts +++ b/packages/runtime/src/controller/common/index.ts @@ -3,6 +3,7 @@ export { MobViewController } from './view/mob-view.controller'; export { ViewController } from './view/view.controller'; export { WFStepTraceViewController } from './view/wf-step-trace-view.controller'; export { MDCustomViewController } from './view/md-custom-view.controller'; +export { HtmlViewController } from './view/html-view.controller'; export { BaseController } from './base.controller'; export { EditorController } from './editor/editor.controller'; export { CodeListEditorController } from './editor/code-list-editor.controller'; diff --git a/packages/runtime/src/controller/common/view/html-view.controller.ts b/packages/runtime/src/controller/common/view/html-view.controller.ts new file mode 100644 index 0000000000000000000000000000000000000000..74fbbd03fe66685bb6141bc63e207b08cd0723ba --- /dev/null +++ b/packages/runtime/src/controller/common/view/html-view.controller.ts @@ -0,0 +1,34 @@ +import { IAppDEHtmlView } from '@ibiz/model-core'; +import { StringUtil } from '@ibiz-template/core'; +import { + IHtmlViewState, + IViewController, + IViewEvent, +} from '../../../interface'; +import { ViewController } from './view.controller'; +/** + * @description 实体html视图 + * @export + * @class HtmlViewController + * @extends {ViewController} + * @implements {IViewController} + * @template T + * @template S + * @template E + */ +export class HtmlViewController< + T extends IAppDEHtmlView = IAppDEHtmlView, + S extends IHtmlViewState = IHtmlViewState, + E extends IViewEvent = IViewEvent, + > + extends ViewController + implements IViewController +{ + protected initState(): void { + const { htmlUrl } = this.model as unknown as IAppDEHtmlView; + super.initState(); + this.state.htmlUrl = htmlUrl + ? StringUtil.fill(htmlUrl, this.context, this.params) + : ''; + } +} diff --git a/packages/runtime/src/controller/control/dashboard/dashboard.controller.ts b/packages/runtime/src/controller/control/dashboard/dashboard.controller.ts index 8a1327c39265fc3078af40d2df1373320ff236e7..65dcfe6c887b2c8423a1f611b615004cd6e77823 100644 --- a/packages/runtime/src/controller/control/dashboard/dashboard.controller.ts +++ b/packages/runtime/src/controller/control/dashboard/dashboard.controller.ts @@ -585,7 +585,7 @@ export class DashboardController } } if (params.searchconds && params.searchconds.length > 0) { - result.params = params; + result.params = [params]; } return result; } diff --git a/packages/runtime/src/controller/control/form/search-form/search-form.controller.ts b/packages/runtime/src/controller/control/form/search-form/search-form.controller.ts index 90bfcef57baba2a1215cd7f181cb146c8ab72aaa..6e46744e1122959c84280780e7e8c8ac8f8389b6 100644 --- a/packages/runtime/src/controller/control/form/search-form/search-form.controller.ts +++ b/packages/runtime/src/controller/control/form/search-form/search-form.controller.ts @@ -9,6 +9,7 @@ import { FormNotifyState } from '../../../constant'; import { FormController } from '../form/form.controller'; import { SearchFormService } from './search-form.service'; import { ConfigService, ControlVO } from '../../../../service'; +import { paramsToSearchconds } from '../../../../utils'; /** * 搜索表单控制器 @@ -51,6 +52,19 @@ export class SearchFormController */ config!: ConfigService; + /** + * @description 搜索过滤参数转换模式 + * @readonly + * @type {('default' | 'searchconds')} + * @memberof SearchFormController + */ + get convertparammode(): 'default' | 'searchconds' { + if (this.controlParams.convertparammode) { + return this.controlParams.convertparammode; + } + return ibiz.config.searchform.convertParamMode; + } + protected initState(): void { super.initState(); this.state.storedFilters = []; @@ -145,13 +159,27 @@ export class SearchFormController */ getFilterParams(): IParams { const filterParams: IParams = {}; - Object.keys(this.state.data).forEach(key => { - const value = this.state.data[key]; - // 排除空值 - if (value !== null && value !== undefined && value !== '') { - filterParams[key] = value; - } - }); + if (this.convertparammode === 'searchconds') { + const searchconds = paramsToSearchconds(this.state.data); + if (searchconds.length > 0) + Object.assign(filterParams, { + searchconds: [ + { + condop: 'AND', + condtype: 'GROUP', + searchconds, + }, + ], + }); + } else { + Object.keys(this.state.data).forEach(key => { + const value = this.state.data[key]; + // 排除空值 + if (value !== null && value !== undefined && value !== '') { + filterParams[key] = value; + } + }); + } return filterParams; } diff --git a/packages/runtime/src/engine/md-view.engine.ts b/packages/runtime/src/engine/md-view.engine.ts index b3eb2f2a7d35a85f02c5b096334ac7544a3db044..86bfd44000cee8b862bb6a5f7019a8b261992896 100644 --- a/packages/runtime/src/engine/md-view.engine.ts +++ b/packages/runtime/src/engine/md-view.engine.ts @@ -469,25 +469,51 @@ export class MDViewEngine extends ViewEngineBase { */ protected getSearchParams(): IParams { const params: IParams = {}; - // 有搜索表单的整合相关参数 - if (this.searchForm) { - Object.assign(params, this.searchForm.getFilterParams()); - } // 有搜索栏的整合相关参数 if (this.searchBar) { Object.assign(params, this.searchBar.getFilterParams()); } // 有搜索表单的整合相关参数 - if (this.tabSearchForm) { - Object.assign(params, this.tabSearchForm.getFilterParams()); + if (this.searchForm) { + const resultParams = this.searchForm.getFilterParams(); + Object.assign(params, this.handleSearchParams(params, resultParams)); } // 有搜索栏的整合相关参数 if (this.tabSearchBar) { Object.assign(params, this.tabSearchBar.getFilterParams()); } + // 有搜索表单的整合相关参数 + if (this.tabSearchForm) { + const resultParams = this.tabSearchForm.getFilterParams(); + Object.assign(params, this.handleSearchParams(params, resultParams)); + } return params; } + /** + * @description 处理搜索参数 + * @protected + * @param {IParams} params + * @param {IParams} newParams + * @returns {*} {IParams} + * @memberof MDViewEngine + */ + protected handleSearchParams(params: IParams, newParams: IParams): IParams { + const _params = { ...newParams }; + if (params.searchconds && newParams.searchconds) { + Object.assign(_params, { + searchconds: [ + { + condop: 'AND', + condtype: 'GROUP', + searchconds: [...params.searchconds, ...newParams.searchconds], + }, + ], + }); + } + return _params; + } + /** * 导入数据 * @author lxm diff --git a/packages/runtime/src/interface/api/common/global-config/i-api-global-search-form-config.ts b/packages/runtime/src/interface/api/common/global-config/i-api-global-search-form-config.ts index 8fa198405a1ada704234043ae1b688d4231d8d87..ef517dd0247b6368db014d715eee3f4074bd7e5a 100644 --- a/packages/runtime/src/interface/api/common/global-config/i-api-global-search-form-config.ts +++ b/packages/runtime/src/interface/api/common/global-config/i-api-global-search-form-config.ts @@ -11,4 +11,12 @@ export interface IApiGlobalSearchFormConfig { * @memberof IApiGlobalSearchFormConfig */ enableStoredFilters: boolean; + + /** + * @description 搜索过滤参数转换模式(default:默认模式,过滤参数保持键值对格式(如 {"n_name_like":"名称"});searchconds:搜索条件模式,将对象格式的查询参数转换为结构化的搜索条件数组,并将其作为新的过滤参数(如 {"searchconds": [{"condop": "AND","condtype": "GROUP","searchconds": [{"condtype": "DEFIELD","fieldname": "name","value": "名称","condop": "LIKE"}]}]}) + * @type {string} + * @default 'default' + * @memberof IApiGlobalFormConfig + */ + convertParamMode: 'default' | 'searchconds'; } diff --git a/packages/runtime/src/interface/api/controller/control/i-api-search-form.controller.ts b/packages/runtime/src/interface/api/controller/control/i-api-search-form.controller.ts index 5d5205e8fe0336dfeaa668f2a9897b8cebb0d116..80dd1fe93536972e4506a8489896c65ce201388b 100644 --- a/packages/runtime/src/interface/api/controller/control/i-api-search-form.controller.ts +++ b/packages/runtime/src/interface/api/controller/control/i-api-search-form.controller.ts @@ -13,6 +13,7 @@ import { IApiSearchFormState } from '../../state'; * @ctrlparams {name:emptyhiddenunit,title:无值是否隐藏,parameterType:boolean,defaultvalue:false,description:表单项无值时,其对应的值单位(如'天'、'%'等)是否隐藏,effectPlatform:web} * @ctrlparams {name:enablestoredfilters,title:启用存储过滤条件,parameterType:boolean,defaultvalue:true,description:设置为true的时候初始化时就会去加载保存的过滤条件,并将过滤条件附加在后续搜索行为的查询参数中,effectPlatform:web} * @ctrlparams {"name":"validatemode","title":"校验模式","parameterType":"'default' | 'notification'","defaultvalue":"'default'","description":"default:默认模式,错误信息显示在表单项下方;notification:通知模式,错误信息显示在页面右上角弹框中"} + * @ctrlparams {"name":"convertparammode","title":"搜索过滤参数转换模式","parameterType":"'default' | 'searchconds'","defaultvalue":"'default'","description":"default:默认模式,过滤参数保持键值对格式(如 \\{\"n_name_like\":\"名称\"\\});searchconds:搜索条件模式,将对象格式的查询参数转换为结构化的搜索条件数组,并将其作为新的过滤参数(如 \\{\"searchconds\": [{\"condop\": \"AND\",\"condtype\": \"GROUP\",\"searchconds\": [{\"condtype\": \"DEFIELD\",\"fieldname\": \"name\",\"value\": \"名称\",\"condop\": \"LIKE\"}]}]\\}"} * @template T * @template S */ diff --git a/packages/runtime/src/interface/api/state/view/i-api-html-view.state.ts b/packages/runtime/src/interface/api/state/view/i-api-html-view.state.ts new file mode 100644 index 0000000000000000000000000000000000000000..265b964662f387690aca8f259945b0b7969e4c41 --- /dev/null +++ b/packages/runtime/src/interface/api/state/view/i-api-html-view.state.ts @@ -0,0 +1,16 @@ +import { IApiViewState } from './i-api-view.state'; + +/** + * @description 实体html视图UI状态 + * @export + * @interface IApiHtmlViewState + * @extends {IApiViewState} + */ +export interface IApiHtmlViewState extends IApiViewState { + /** + * @description 指定html视图的 iframe 元素所加载的目标网页 URL 地址 + * @type {string} + * @memberof IApiHtmlViewState + */ + htmlUrl: string; +} diff --git a/packages/runtime/src/interface/api/state/view/index.ts b/packages/runtime/src/interface/api/state/view/index.ts index 6614bce8f9ebf99d1cf0a8b496e1d6b3fa00d46d..24f6cd4b8afd8d13e61415ab6b70c3ff99830519 100644 --- a/packages/runtime/src/interface/api/state/view/index.ts +++ b/packages/runtime/src/interface/api/state/view/index.ts @@ -52,3 +52,4 @@ export type { IApiWFEditViewState } from './i-api-wf-edit-view.state'; export type { IApiWFStepTraceViewState } from './i-api-wf-step-trace-view.state'; export type { IApiWizardViewState } from './i-api-wizard-view.state'; export type { IApiMDCustomViewState } from './i-api-md-custom-view.state'; +export type { IApiHtmlViewState } from './i-api-html-view.state'; diff --git a/packages/runtime/src/interface/controller/state/view/i-html-view.state.ts b/packages/runtime/src/interface/controller/state/view/i-html-view.state.ts new file mode 100644 index 0000000000000000000000000000000000000000..a37cdf8c81c79d036f107287fd9260014b20552f --- /dev/null +++ b/packages/runtime/src/interface/controller/state/view/i-html-view.state.ts @@ -0,0 +1,11 @@ +import { IApiHtmlViewState } from '../../../api'; +import { IViewState } from './i-view.state'; + +/** + * @description 实体html视图UI状态 + * @export + * @interface IHtmlViewState + * @extends {IViewState} + * @extends {IApiHtmlViewState} + */ +export interface IHtmlViewState extends IViewState, IApiHtmlViewState {} diff --git a/packages/runtime/src/interface/controller/state/view/index.ts b/packages/runtime/src/interface/controller/state/view/index.ts index 849e4121fc544614735e52e3d380bcc40a781d99..9f231bdc6ccae831c6f501d7e87cb6f5bad822da 100644 --- a/packages/runtime/src/interface/controller/state/view/index.ts +++ b/packages/runtime/src/interface/controller/state/view/index.ts @@ -51,3 +51,4 @@ export * from './i-tab-search-view.state'; export * from './i-app-data-upload-view.state'; export * from './i-app-login-view.state'; export * from './i-md-custom-view.state'; +export * from './i-html-view.state'; diff --git a/packages/runtime/src/utils/dr-ctrl-util/index.ts b/packages/runtime/src/utils/dr-ctrl-util/index.ts index 60c098e44b12152d55e9cdeac1870791a54b5439..4d809386e48eeb873653e2caefffc554e5003c7b 100644 --- a/packages/runtime/src/utils/dr-ctrl-util/index.ts +++ b/packages/runtime/src/utils/dr-ctrl-util/index.ts @@ -1,9 +1,14 @@ import { IAppDERS } from '@ibiz/model-core'; import { execDELogicAction } from '../../de-logic'; -import { IDRBarItemsState, IDRTabPagesState } from '../../interface'; +import { + IDRBarItemsState, + IDRTabPagesState, + ISearchCondField, +} from '../../interface'; import { calcDeCodeNameById, findDELogic } from '../../model'; import { AppCounter } from '../../service'; import { ScriptFactory } from '../script'; +import { ValueOP } from '../../constant'; /** * 根据计数器数据,计算项显示状态 @@ -136,3 +141,55 @@ export async function getDeDataMajorField( } return majorData; } + +/** + * 将对象格式的查询参数转换为结构化的搜索条件数组 + * + * @export + * @param {IParams} _params + * @return {*} {ISearchCondField[]} + * + * @example + * 转换规则: + * 1. 仅处理键以'N_'开头且包含有效操作符后缀的参数 + * 2. 从键中提取字段名(去除'N_'前缀和操作符后缀) + * 3. 仅当参数值不为null、undefined或空字符串时才生成条件 + * + * 示例: + * 输入:{n_age_gt: 18, n_name_like: '慧', invalidKey: 'value'} + * 输出:[ + * {condtype: 'DEFIELD', fieldname: 'age', value: 18, condop: 'GT'}, + * {condtype: 'DEFIELD', fieldname: 'name', value: '慧', condop: 'LIKE'} + * ] + */ +export function paramsToSearchconds(_params: IParams): ISearchCondField[] { + const valueOPs: string[] = []; + for (const key in ValueOP) { + if (key !== ValueOP.EXISTS && key !== ValueOP.NOT_EXISTS) { + const value = ValueOP[key as keyof typeof ValueOP]; + valueOPs.push(value); + } + } + const conds: ISearchCondField[] = []; + Object.keys(_params).forEach(_key => { + const value = _params[_key]; + const tempKey = _key.toLocaleUpperCase(); + let fieldname = ''; + + // 检查键是否以'N_'开头 + const condop = valueOPs.find(filter => tempKey.endsWith(`_${filter}`)); + if (tempKey.startsWith('N_') && condop) { + // 去除'N_'前缀与条件后缀 + fieldname = _key.slice(2).slice(0, -`_${condop}`.length); + } + const isValue = value !== null && value !== undefined && value !== ''; + if (fieldname && isValue && condop) + conds.push({ + condtype: 'DEFIELD', + fieldname, + value, + condop: condop.toLocaleUpperCase() as ValueOP, + }); + }); + return conds; +} diff --git a/packages/vue3-util/CHANGELOG.md b/packages/vue3-util/CHANGELOG.md index da8f4bf0ff845c6140e86a30a38f9364bed32f07..998fd6922bf62cf529d54fb55d4b0527b84e7779 100644 --- a/packages/vue3-util/CHANGELOG.md +++ b/packages/vue3-util/CHANGELOG.md @@ -10,6 +10,7 @@ ### Added - 面板容器类(栅格容器、多项数据容器、多项数据容器(仅数据)、面板容器、分组容器、图片背景容器、分页容器、面板分页、滚动容器、面板滚动容器项、单项数据容器),新增状态属性loading(是否显示loading状态),并新增控制器能力startLoading(开始加载中)和endLoading(结束加载中) +- 新增html视图控制器,新增html视图状态属性htmlUrl( iframe元素的URL地址) ## [0.7.41-alpha.24] - 2025-09-11 diff --git a/packages/vue3-util/src/view/html-view/html-view.tsx b/packages/vue3-util/src/view/html-view/html-view.tsx index 46f25a8c08a50ceee029a9b44c3a549ac23b8942..19c45ae5ade28d49490737d6fe90dfbcdb49cc50 100644 --- a/packages/vue3-util/src/view/html-view/html-view.tsx +++ b/packages/vue3-util/src/view/html-view/html-view.tsx @@ -1,11 +1,12 @@ -import { StringUtil } from '@ibiz-template/core'; import { IAppDEHtmlView } from '@ibiz/model-core'; -import { ViewController, getErrorViewProvider } from '@ibiz-template/runtime'; +import { + HtmlViewController, + getErrorViewProvider, +} from '@ibiz-template/runtime'; import { h, ref, PropType, - computed, onBeforeMount, defineComponent, resolveComponent, @@ -24,7 +25,10 @@ export const HtmlView = defineComponent({ }, setup() { const ns = useNamespace('view'); - const c = useViewController((...args) => new ViewController(...args)); + const c = useViewController( + (model, ...args) => + new HtmlViewController(model as IAppDEHtmlView, ...args), + ); // 视图部件模型在viewlayoutPanel里面。 const controls = c.model.viewLayoutPanel?.controls || c.model.controls; const { viewType, sysCss, codeName } = c.model; @@ -38,14 +42,8 @@ export const HtmlView = defineComponent({ ]; const isLoading = ref(false); - const url = computed(() => { - const { htmlUrl } = c.model as IAppDEHtmlView; - if (htmlUrl) return StringUtil.fill(htmlUrl, c.context, c.params); - return ''; - }); - onBeforeMount(() => { - if (url.value) { + if (c.state.htmlUrl) { isLoading.value = true; } }); @@ -54,13 +52,18 @@ export const HtmlView = defineComponent({ isLoading.value = false; }; - return { c, ns, controls, viewClassNames, url, isLoading, onLoad }; + return { c, ns, controls, viewClassNames, isLoading, onLoad }; }, render() { - if (this.url) { + if (!this.c.state.isCreated) return; + + if (this.c.state.htmlUrl) { return (
- +
); }