diff --git a/packages/er-design/src/draft-x6/controller/x6-controller.ts b/packages/er-design/src/draft-x6/controller/x6-controller.ts index 51a05ead31e5cc5ce93aaa4e721ac17c23774093..4933300827f0a73d72d6a5c5fd95afa5422f0467 100644 --- a/packages/er-design/src/draft-x6/controller/x6-controller.ts +++ b/packages/er-design/src/draft-x6/controller/x6-controller.ts @@ -475,6 +475,7 @@ export class X6Controller { this.handLinkRemoved = this.handLinkRemoved.bind(this); this.handleVerticesChange = this.handleVerticesChange.bind(this); this.onDataChange = this.onDataChange.bind(this); + this.onFieldDataChange = this.onFieldDataChange.bind(this); this.updateNode = debounce( this.updateNode.bind(this), 1000, @@ -489,6 +490,7 @@ export class X6Controller { this.evt.on('onLinkRemoved', this.handLinkRemoved); this.evt.on('onVerticesChange', this.handleVerticesChange); ibiz.mc.command.update.on(this.onDataChange); + ibiz.mc.command.change.on(this.onFieldDataChange); this.g.on('blank:click', () => { this.panel.view.call('onActiveRoot'); }); @@ -831,6 +833,27 @@ export class X6Controller { } } + /** + * 监听属性数据变更 + * + * @author zhanghengfeng + * @date 2025-03-25 20:03:54 + * @param {IPortalMessage} msg + */ + onFieldDataChange(msg: IPortalMessage) { + if (msg && msg.data && typeof msg.data === 'object') { + const data = msg.data as IData; + const srfdecodename = data.srfdecodename; + if (srfdecodename === 'UXDEField') { + const id = data.psdeid; + if (id && this.nodeList.find(item => item.id === id)) { + this.updateNodes.add(id); + this.updateNode(); + } + } + } + } + /** * 更新节点 * diff --git a/packages/er-design/src/index.ts b/packages/er-design/src/index.ts index 68f03913a156a91da0e27948bb29b907dc712ccf..ca8c77d3318fd2165b24cc6c94ec9cc67ddaa600 100644 --- a/packages/er-design/src/index.ts +++ b/packages/er-design/src/index.ts @@ -12,6 +12,7 @@ import { PSSysERMapDraftService, PSSysERMapSymbolService, UXDataEntityService, + UXDeFieldService, UXDerService, } from './service'; import Plugins from './plugins'; @@ -62,5 +63,11 @@ export default { return new UXDerService(srfSessionId, entityModel); }, ); + DEServiceUtil.register( + 'erdesign.uxdefield', + async (srfSessionId: string, entityModel: IAppDataEntity) => { + return new UXDeFieldService(srfSessionId, entityModel); + }, + ); }, }; diff --git a/packages/er-design/src/service/index.ts b/packages/er-design/src/service/index.ts index 66712a8a39368a2926e9cd34e100cc3050392f0f..848ad15069a501b6ffb2ec248cd54f921db50cfd 100644 --- a/packages/er-design/src/service/index.ts +++ b/packages/er-design/src/service/index.ts @@ -3,3 +3,4 @@ export { PSSysERMapDraftNodeService } from './pssysermapdraftnode/pssysermapdraf export { PSSysERMapSymbolService } from './pssysermapsymbol/pssysermapsymbol.service'; export { UXDataEntityService } from './ux-data-entity/ux-data-entity.service'; export { UXDerService } from './ux-der/ux-der.service'; +export { UXDeFieldService } from './ux-defield/ux-defield.service'; diff --git a/packages/er-design/src/service/ux-data-entity/ux-data-entity.service.ts b/packages/er-design/src/service/ux-data-entity/ux-data-entity.service.ts index bd0e655494107ab9c3de2c7666071ccf00676063..ff6c9ce8e328ad9fd6a1c994243ef405d7a155e9 100644 --- a/packages/er-design/src/service/ux-data-entity/ux-data-entity.service.ts +++ b/packages/er-design/src/service/ux-data-entity/ux-data-entity.service.ts @@ -1,9 +1,23 @@ +/* eslint-disable no-await-in-loop */ import { IHttpResponse } from '@ibiz-template/core'; -import { DEService, findModelChild } from '@ibiz-template/runtime'; +import { + DEService, + calcDeCodeNameById, + findModelChild, +} from '@ibiz-template/runtime'; import { IAppDEMethodDTO } from '@ibiz/model-core'; import { CustomMethodDto } from '../dto/custom-method.dto'; export class UXDataEntityService extends DEService { + /** + * 是否为本地模式(临时数据模式)服务 + * + * @author zhanghengfeng + * @date 2025-03-25 20:03:39 + * @type {boolean} + */ + isLocalMode: boolean = true; + /** * 创建方法DTO * @@ -35,6 +49,14 @@ export class UXDataEntityService extends DEService { await this.beforeCreate(context, params); } const result = await super.exec(id, context, params, params2, header); + if (result && result.ok && result.data) { + if (id.toUpperCase() === 'GET') { + await this.attachFields(context, result.data); + } + if (id.toUpperCase() === 'REMOVE') { + await this.afterRemove(context, result.data); + } + } return result; } @@ -61,4 +83,71 @@ export class UXDataEntityService extends DEService { } } } + + /** + * 附加属性字段 + * + * @author zhanghengfeng + * @date 2025-03-25 20:03:02 + * @param {IContext} context + * @param {(IData | IData[])} data + * @return {*} {Promise} + */ + async attachFields(context: IContext, data: IData | IData[]): Promise { + const app = ibiz.hub.getApp(this.model.appId); + const service = await app.deService.getService( + context, + 'erdesign.uxdefield', + ); + if (service) { + const items = Array.isArray(data) ? data : [data]; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + const tempContext = context.clone(); + const deName = calcDeCodeNameById(this.model.id!); + tempContext[deName] = item.srfkey; + const res = await service.exec('fetchcurde', tempContext); + if (res && res.ok && Array.isArray(res.data)) { + item.psdefields = res.data; + } + } + } + } + + /** + * 删除(删除关联数据) + * + * @author zhanghengfeng + * @date 2025-03-25 20:03:50 + * @param {IContext} context + * @param {IData} data + * @return {*} {Promise} + */ + async afterRemove(context: IContext, data: IData): Promise { + // 关联删除属性 + const app = ibiz.hub.getApp(this.model.appId); + const service = await app.deService.getService( + context, + 'erdesign.uxdefield', + ); + if (service) { + const items = Array.isArray(data) ? data : [data]; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + const tempContext = context.clone(); + const deName = calcDeCodeNameById(this.model.id!); + tempContext[deName] = item.srfkey; + const res = await service.exec('fetchcurde', tempContext); + if (res && res.ok && Array.isArray(res.data)) { + for (let j = 0; j < res.data.length; j++) { + const field = res.data[j]; + const tempContext2 = context.clone(); + const deName2 = calcDeCodeNameById(service.model.id!); + tempContext2[deName2] = field.srfkey; + await service.exec('remove', tempContext2); + } + } + } + } + } } diff --git a/packages/er-design/src/service/ux-defield/ux-defield.service.ts b/packages/er-design/src/service/ux-defield/ux-defield.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..334e89ebd873afa718bc037e9f985f7ad29e7a6a --- /dev/null +++ b/packages/er-design/src/service/ux-defield/ux-defield.service.ts @@ -0,0 +1,189 @@ +import { + HttpResponse, + IBizContext, + IHttpResponse, + RuntimeError, +} from '@ibiz-template/core'; +import { + DEDQCondUtil, + DEService, + FetchMethod, + IDataEntity, + PSDEDQCondEngine, + SearchFilter, + execFieldLogics, +} from '@ibiz-template/runtime'; +import { isArray } from 'lodash-es'; +import { clone, isEmpty, isNil } from 'ramda'; +import { ascSort, descSort } from 'qx-util'; + +export class UXDeFieldService extends DEService { + /** + * 是否为本地模式(临时数据模式)服务 + * + * @author zhanghengfeng + * @date 2025-03-25 20:03:20 + * @type {boolean} + */ + isLocalMode: boolean = true; + + /** + * 执行服务方法 + * + * @author zhanghengfeng + * @date 2025-03-25 20:03:50 + * @param {string} id + * @param {IContext} context + * @param {(IData | IData[])} [params] + * @param {IParams} [params2] + * @param {IData} [header] + * @return {*} {Promise} + */ + async exec( + id: string, + context: IContext, + params?: IData | IData[], + params2?: IParams, + header?: IData, + ): Promise { + if (id === 'fetchcurde') { + return this.handleFetchCurDE(id, context, params, params2, header); + } + return super.exec(id, context, params, params2, header); + } + + /** + * 获取当前数据实体的属性 + * + * @author zhanghengfeng + * @date 2025-03-25 20:03:31 + * @param {string} id + * @param {IContext} context + * @param {(IData | IData[])} [params] + * @param {IParams} [params2] + * @param {IData} [_header] + * @return {*} {Promise} + */ + async handleFetchCurDE( + id: string, + context: IContext, + params?: IData | IData[], + params2?: IParams, + _header?: IData, + ): Promise { + // 和原上下文解除关联,冻结当前行为上下文,方便后续判断上下文是否销毁 + const result: IData = {}; + Object.keys(context).forEach(key => { + result[key] = context[key]; + }); + const tempContext = IBizContext.create(result); + + const method = (await this.getMethod(id)) as FetchMethod; + if (!method) { + throw new RuntimeError( + ibiz.i18n.t('runtime.service.noSupportedMethod', { + codeName: this.model.codeName, + id, + }), + ); + } + // 若无srfappid,表示上下文已经销毁,则不执行后续逻辑直接返回 + if (!tempContext.srfappid) { + return new HttpResponse([]); + } + // 过滤参数,必须是对象 + const searchParams = params && !isArray(params) ? params : params2 || {}; + + // 根据数据集合来源查询数据 + let res: HttpResponse = new HttpResponse([]); + + if (this.isLocalMode) { + const cond = DEDQCondUtil.getCond(method.method); + const filter = new SearchFilter(tempContext, searchParams); + const { items, total } = await this.searchLocal(cond, filter); + res = new HttpResponse(items, 200); + res.headers['x-total'] = total; + } + + // 计算属性逻辑,每条数据走一遍 + if (res.data) { + await execFieldLogics( + this.model, + 'compute', + context, + res.data, + searchParams, + ); + } + + return res as HttpResponse; + } + + /** + * 搜索本地数据 + * + * @author zhanghengfeng + * @date 2025-03-25 21:03:08 + * @param {(PSDEDQCondEngine | null)} cond + * @param {SearchFilter} filter + * @param {string[]} [queryParamKeys=this.model.quickSearchAppDEFieldIds!] + * @return {*} {Promise<{ items: IDataEntity[]; total: number }>} + */ + async searchLocal( + cond: PSDEDQCondEngine | null, + filter: SearchFilter, + queryParamKeys: string[] = this.model.quickSearchAppDEFieldIds!, + ): Promise<{ items: IDataEntity[]; total: number }> { + let list: IDataEntity[] = this.local.getList(); + // 走查询条件 + if (cond) { + if (list.length > 0) { + list = list.filter(obj => cond.test(obj, filter)); + } + } + if (list.length > 0) { + if (filter.query && filter.query !== '') { + if (queryParamKeys) { + list = list.filter(obj => { + const reg = new RegExp(filter.query.toLowerCase()); + for (let i = 0; i < queryParamKeys.length; i += 1) { + const key = queryParamKeys[i]; + const val: string = obj[key]; + if (reg.test(val.toLowerCase())) { + return true; + } + } + return false; + }); + } + } + } + if (!isNil(filter.sortField) && !isEmpty(filter.sortField)) { + if (filter.sortMode === 'DESC') { + // 倒序 + list = descSort(list, filter.sortField); + } else { + // 正序 + list = ascSort(list, filter.sortField); + } + } + if (!isNil(filter.srfDefaultCond) && !isEmpty(filter.srfDefaultCond)) { + const defaultConds: IData = filter.srfDefaultCond; + const defaultCondKeys: string[] = Object.keys(defaultConds); + list = list.filter(obj => { + for (let i = 0; i < defaultCondKeys.length; i += 1) { + const key = defaultCondKeys[i]; + if (obj[key] === defaultConds[key]) { + return true; + } + } + return false; + }); + } + const { page, size } = filter; + const start = page * size; + const end = (page + 1) * size; + const items = list.slice(start, end).map(item => clone(item)); + return { items, total: list.length }; + } +} diff --git a/packages/er-design/src/service/ux-der/ux-der.service.ts b/packages/er-design/src/service/ux-der/ux-der.service.ts index 7786e7c3501a1e3f20c79bc232f42945bb2c9770..0141a1957f682b2e8d6a8d0424da631e042c330e 100644 --- a/packages/er-design/src/service/ux-der/ux-der.service.ts +++ b/packages/er-design/src/service/ux-der/ux-der.service.ts @@ -2,6 +2,15 @@ import { IHttpResponse } from '@ibiz-template/core'; import { DEService, findModelChild } from '@ibiz-template/runtime'; export class UXDerService extends DEService { + /** + * 是否为本地模式(临时数据模式)服务 + * + * @author zhanghengfeng + * @date 2025-03-25 21:03:07 + * @type {boolean} + */ + isLocalMode: boolean = true; + async exec( id: string, context: IContext, diff --git a/packages/er-design/src/views/er-draft-design-view/er-draft-design-view.engine.ts b/packages/er-design/src/views/er-draft-design-view/er-draft-design-view.engine.ts index ac70dac40871d24a74573fbff93632546e4469f4..53245ab836fdc696b4a3196e4fd79c5ab2bb2ccc 100644 --- a/packages/er-design/src/views/er-draft-design-view/er-draft-design-view.engine.ts +++ b/packages/er-design/src/views/er-draft-design-view/er-draft-design-view.engine.ts @@ -2,7 +2,6 @@ /* eslint-disable no-restricted-syntax */ import { DEMainViewEngine, - EditFormController, EditFormService, IDataEntity, IEditFormController, @@ -191,10 +190,9 @@ export class ERDraftDesignViewEngine extends DEMainViewEngine { this.view.evt.on('onMounted', () => this.sendViewDataAction()); } if (this.doActions.includes('EDIT')) { - this.erDesignContent?.x6?.evt.onAll(eventName => { - if (this.editEventNames.includes(eventName)) { - this.sendEditAction(); - } + const uiDomain = ibiz.uiDomainManager.get(this.view.context.srfsessionid); + uiDomain?.evt?.on('dataChange', () => { + this.sendEditAction(); }); } if (this.doActions.includes('UPDATE')) { @@ -349,19 +347,6 @@ export class ERDraftDesignViewEngine extends DEMainViewEngine { presetParams.view ) { this.activePropertyView = presetParams.view; - if (!this.activePropertyView) { - return; - } - if (this.doActions.includes('EDIT')) { - this.activePropertyView.evt?.on('onMounted', () => { - const propertyForm = this.activePropertyView?.getController( - 'form', - ) as EditFormController; - propertyForm?.evt?.on('onFormDataChange', () => { - this.sendEditAction(); - }); - }); - } } }); }