diff --git a/packages/dnd-design/public/assets/svg/lightning.svg b/packages/dnd-design/public/assets/svg/lightning.svg new file mode 100644 index 0000000000000000000000000000000000000000..58f4201bcda31d8bff8416d2213085967a17d186 --- /dev/null +++ b/packages/dnd-design/public/assets/svg/lightning.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/dnd-design/src/components/dnd-draggable-item/dnd-draggable-item.scss b/packages/dnd-design/src/components/dnd-draggable-item/dnd-draggable-item.scss index 2a3c6b571e9278dbad9fb083d673ec911505b30b..04fbd01a99072e7cf9ec13bbce2d413bb56fdaeb 100644 --- a/packages/dnd-design/src/components/dnd-draggable-item/dnd-draggable-item.scss +++ b/packages/dnd-design/src/components/dnd-draggable-item/dnd-draggable-item.scss @@ -23,10 +23,6 @@ > .#{bem(dnd-design-draggable-item-content)} { outline: 2px solid #{getCssVar(color-primary)}; } - - > .#{bem(dnd-design-draggable-item-actions)} { - display: flex; - } } &:hover { @@ -54,20 +50,26 @@ top: 0; right: 0; z-index: 10; - display: none; + display: flex; flex-direction: row; + padding: 0; margin: 0; list-style: none; @include e(item) { + display: flex; + align-items: center; + justify-content: center; width: 26px; height: 26px; padding: 3px 4px; color: #{getCssVar(color-primary-text)}; cursor: pointer; background-color: #{getCssVar(color-primary)}; - border: 1px solid #{getCssVar(color-border)}; - border-radius: 3px; + + &:hover { + background-color: #{getCssVar(color, primary, hover)}; + } } } diff --git a/packages/dnd-design/src/components/dnd-draggable-item/dnd-draggable-item.tsx b/packages/dnd-design/src/components/dnd-draggable-item/dnd-draggable-item.tsx index 85d2f6d725630d824f0afb4236f31af58b2a6ebe..3c92eda96823f5a71f5d5d9d10e07638a9717265 100644 --- a/packages/dnd-design/src/components/dnd-draggable-item/dnd-draggable-item.tsx +++ b/packages/dnd-design/src/components/dnd-draggable-item/dnd-draggable-item.tsx @@ -26,6 +26,9 @@ export default defineComponent({ isShowMask: { type: Boolean, }, + isShowLogicButton: { + type: Boolean, + }, }, setup(props, { emit }) { const ns = useNamespace('dnd-design-draggable-item'); @@ -50,6 +53,16 @@ export default defineComponent({ class={[this.ns.b(), this.ns.is('select', this.isSelect)]} style={this.layoutStyle} > +
+ {this.isShowMask &&
} + {this.$slots.default?.()} +
+ {modelStateUtil.isEnableDrag(this.modelState) && (
) : null} - -
- {this.isShowMask &&
} - {this.$slots.default?.()} -
); }, diff --git a/packages/dnd-design/src/controller/dnd-item-controller/dnd-item-controller.tsx b/packages/dnd-design/src/controller/dnd-item-controller/dnd-item-controller.tsx index 3e23e596275199ae696b8f1e8ea6e060365f136a..1f0ee0cdd30a514598a8b11e6b459b6172249ae8 100644 --- a/packages/dnd-design/src/controller/dnd-item-controller/dnd-item-controller.tsx +++ b/packages/dnd-design/src/controller/dnd-item-controller/dnd-item-controller.tsx @@ -7,6 +7,7 @@ import { IDndItemController, } from '../../interface'; import { SelectState } from '../../utils'; +import { resource } from '../../global'; /** * 拖拽项控制器 @@ -22,9 +23,18 @@ export class DndItemController implements IDndItemController { readonly select!: SelectState; actions: IActionItem[] = [ + { + icon: ( + + ), + text: '表单项逻辑', + tooltip: '表单项逻辑', + command: 'logic', + }, { icon: , text: '删除', + tooltip: '删除', command: 'delete', }, ]; diff --git a/packages/dnd-design/src/panel-items/dnd-design/dnd-design.controller.ts b/packages/dnd-design/src/panel-items/dnd-design/dnd-design.controller.ts index fa7b0e712c9a802b711962611215311f5c1c1fcc..e072ef905939326b17aff1de1ddd25583c4e59f9 100644 --- a/packages/dnd-design/src/panel-items/dnd-design/dnd-design.controller.ts +++ b/packages/dnd-design/src/panel-items/dnd-design/dnd-design.controller.ts @@ -1,12 +1,23 @@ import { reactive } from 'vue'; -import { IPortalMessage, RuntimeModelError } from '@ibiz-template/core'; +import { + IPortalMessage, + RuntimeError, + RuntimeModelError, +} from '@ibiz-template/core'; import { IDataEntity, IAppDEService, PanelItemController, getControl, + getUIActionById, + UIActionUtil, } from '@ibiz-template/runtime'; -import { IPanelContainer, IDEForm } from '@ibiz/model-core'; +import { + IPanelContainer, + IDEForm, + IDEToolbar, + IDETBUIActionItem, +} from '@ibiz/model-core'; import { ascSort, createUUID } from 'qx-util'; import { DndDesignPanelItemState } from './dnd-design.state'; import { dndProviderRegister, modelStateUtil } from '../../utils'; @@ -94,10 +105,196 @@ export class DndDesignPanelItemController await this.load(); this.state.uuid = createUUID(); }); + this.view.evt.on('onStencilAdd', async e => { + if (!e || !e.stencil) { + return; + } + const id = this.view.select.data?.srfkey; + this.handleStencilAdd(e.stencil, id); + }); await this.load(); this.subscribeMessage(); } + /** + * 处理素材添加 + * + * @author zhanghengfeng + * @date 2025-02-12 22:02:48 + * @param {IData} stencil + * @param {string} [id] + * @param {IDataEntity} [child] + * @return {*} {void} + */ + handleStencilAdd(stencil: IData, id?: string, child?: IDataEntity): void { + if (!id) { + return; + } + const parent = this.items.find(item => item.srfkey === id); + if (!parent) { + return; + } + const type = parent.srftype; + if (!type) { + return; + } + const container = [ + 'GROUPPANEL', + 'FORMPAGE', + 'TABPAGE', + 'TABPANEL', + 'MDCTRL', + ]; + if (!container.includes(type)) { + this.handleStencilAdd(stencil, parent.srfpkey, child || parent); + return; + } + if (type === 'TABPANEL') { + return; + } + if (type === 'MDCTRL' && parent.mdctrltype !== 'REPEATER') { + return; + } + const children: IDataEntity[] = parent.children || []; + let newIndex = children.length; + if (child) { + const index = children.findIndex(item => item.srfkey === child.srfkey); + if (index !== -1) { + newIndex = index + 1; + } + } + this.add( + { + added: { + element: stencil, + newIndex, + }, + }, + children, + parent, + ); + } + + /** + * 更新逻辑映射map + * + * @author zhanghengfeng + * @date 2025-02-12 22:02:38 + * @protected + * @return {*} {Promise} + */ + protected async updateLogicMap(): Promise { + const appId = this.view.context.srfappid; + if (!appId) { + return; + } + const app = ibiz.hub.getApp(appId); + if (!app) { + return; + } + const entity = await ibiz.hub.getAppDataEntity('PSDEFDLogic', appId); + if (!entity) { + return; + } + const service = await app.deService.getService( + this.view.context, + entity.id!, + ); + if (!service) { + return; + } + const res = await service.fetchDefault(this.view.context); + const map: Record = {}; + if (res && Array.isArray(res.data)) { + res.data.forEach(item => { + const key = item.psdeformdetailid; + if (map[key]) { + map[key] += 1; + } else { + map[key] = 1; + } + }); + } + this.state.logicMap = map; + } + + /** + * 监听数据变化 + * + * @author zhanghengfeng + * @date 2025-02-12 22:02:29 + * @protected + * @param {IPortalMessage} msg + */ + protected onDEDataChange(msg: IPortalMessage): void { + if (msg && msg.data && typeof msg.data === 'object') { + const data = msg.data as IDataEntity; + if (data.srfdecodename === 'PSDEFDLogic') { + this.updateLogicMap(); + } + } + } + + /** + * 打开逻辑视图 + * + * @author zhanghengfeng + * @date 2025-02-12 22:02:40 + * @param {IDataEntity} item + * @return {*} {Promise} + */ + async openLogicView(item: IDataEntity): Promise { + if (!item) { + return; + } + const structure = getControl( + this.view.model, + 'structuretoolbar', + ) as IDEToolbar; + if (!structure) { + return; + } + const toolbarItems = structure.detoolbarItems || []; + if (toolbarItems && toolbarItems.length) { + const action = toolbarItems.find(toolbarItem => { + if (item.srftype === 'FORMITEM' || item.srftype === 'FORMITEMEX') { + return toolbarItem.id === 'formitem'; + } + return toolbarItem.id === 'default'; + }); + if (action && action.itemType === 'DEUIACTION') { + const actionId = (action as IDETBUIActionItem).uiactionId; + if (!actionId) { + return; + } + const uiAction = await getUIActionById(actionId, action.appId); + if (!uiAction) { + throw new RuntimeError( + ibiz.i18n.t('runtime.controller.control.toolbar.noFound', { + actionId, + }), + ); + } + const context = this.view.context.clone(); + delete context.srfrunmode; + const modelState = this.getModelState(item); + if (!modelStateUtil.isEnableUpdate(modelState)) { + context.srfreadonly = true; + } + await UIActionUtil.execAndResolved( + actionId, + { + context, + params: this.view.params, + data: [item], + view: this.view, + }, + action.appId, + ); + } + } + } + /** * 更新隐藏表单项可见性 * @@ -189,9 +386,11 @@ export class DndDesignPanelItemController */ protected subscribeMessage(): void { this.onDataUpdate = this.onDataUpdate.bind(this); + this.onDEDataChange = this.onDEDataChange.bind(this); this.refresh = this.refresh.bind(this); this.view.evt.on('onRefreshView', this.refresh); ibiz.mc.command.update.on(this.onDataUpdate); + ibiz.mc.command.change.on(this.onDEDataChange); } /** @@ -203,6 +402,7 @@ export class DndDesignPanelItemController protected unsubscribeMessage(): void { this.view.evt.off('onRefreshView', this.refresh); ibiz.mc.command.update.off(this.onDataUpdate); + ibiz.mc.command.change.off(this.onDEDataChange); } /** @@ -245,6 +445,7 @@ export class DndDesignPanelItemController * @return {*} {Promise} */ protected async load(): Promise { + await this.updateLogicMap(); const result = await this.service.fetchDefault(this.panel.context); this.items = result.data as IDataEntity[]; this.items.forEach(item => { @@ -325,12 +526,10 @@ export class DndDesignPanelItemController // 移入到根节点修改父为空 item.srfpkey = undefined; } - const { oldIndex, newIndex } = added; - this.move(items, oldIndex, newIndex); + this.move(items, 0, items.length - 1); } } else if (moved && moved.element) { - const { oldIndex, newIndex } = moved; - this.move(items, oldIndex, newIndex); + this.move(items, 0, items.length - 1); } } } diff --git a/packages/dnd-design/src/panel-items/dnd-design/dnd-design.state.ts b/packages/dnd-design/src/panel-items/dnd-design/dnd-design.state.ts index 7f37a874c2448a17c37ca36a849e3631a3161e7a..c3c65eec11b8f00b722968f57157b88e2cc9bc60 100644 --- a/packages/dnd-design/src/panel-items/dnd-design/dnd-design.state.ts +++ b/packages/dnd-design/src/panel-items/dnd-design/dnd-design.state.ts @@ -38,4 +38,13 @@ export class DndDesignPanelItemState extends PanelItemState { * @type {boolean} */ hiddenFormItemVisible: boolean = true; + + /** + * 逻辑映射map + * + * @author zhanghengfeng + * @date 2025-02-12 22:02:01 + * @type {Record} + */ + logicMap: Record = {}; } diff --git a/packages/dnd-design/src/panel-items/dnd-design/dnd-design.tsx b/packages/dnd-design/src/panel-items/dnd-design/dnd-design.tsx index 97f25e6921e13220759482e6861edebdb200dbce..77864cedeba8ebc771a2c6e288ce812843b8887d 100644 --- a/packages/dnd-design/src/panel-items/dnd-design/dnd-design.tsx +++ b/packages/dnd-design/src/panel-items/dnd-design/dnd-design.tsx @@ -110,8 +110,13 @@ export default defineComponent({ actions={controller.actions} modelState={state} isShowMask={c.isShowMask(item)} + isShowLogicButton={!!c.state.logicMap[item.srfkey]} onClick={(e: MouseEvent) => onSelect(e, item)} onAction={(action: IActionItem) => { + if (action.command === 'logic') { + c.openLogicView(item); + return; + } controller.onAction(action).then((result: boolean) => { if (result === false) { if (action.command === 'delete') { diff --git a/packages/dnd-design/src/panel-items/dnd-stencil/dnd-stencil.tsx b/packages/dnd-design/src/panel-items/dnd-stencil/dnd-stencil.tsx index 057daa82c42e747efa56728a84d0f31cdd68d2ed..e4fb42160a42246ae2af3d820a11b19794b0243f 100644 --- a/packages/dnd-design/src/panel-items/dnd-stencil/dnd-stencil.tsx +++ b/packages/dnd-design/src/panel-items/dnd-stencil/dnd-stencil.tsx @@ -132,6 +132,12 @@ export default defineComponent({ key={data.index} class={[ns.b('handle')]} title={item.text} + onDblclick={e => { + e.stopPropagation(); + props.controller.view?.evt.emit('onStencilAdd', { + stencil: clone(item), + }); + }} > { + const id = this.view.context.srfsessionid; + this.view.context.srfsessionid = id; + await super.onCreated(); + } + + async onDestroyed(): Promise { + if ( + this.form && + this.form.state.modified && + this.form.model.enableAutoSave && + this.view.context.srfsessionid && + ibiz.uiDomainManager.has(this.view.context.srfsessionid) + ) { + await this.form.immediateAutoSave(); + } + await super.onDestroyed(); + } +} diff --git a/packages/global-util-design/src/views/index.ts b/packages/global-util-design/src/views/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..89f89e2eec750b2759c6754b0d1f7c00f1a0cbfa --- /dev/null +++ b/packages/global-util-design/src/views/index.ts @@ -0,0 +1,15 @@ +import { IViewController } from '@ibiz-template/runtime'; +import { GlobalEditViewEngine } from './global-edit-view.engine'; + +export default { + install(): void { + ibiz.engine.register( + 'VIEW_EditView', + (c: IViewController) => new GlobalEditViewEngine(c), + ); + ibiz.engine.register( + 'VIEW_EditView9', + (c: IViewController) => new GlobalEditViewEngine(c), + ); + }, +}; diff --git a/packages/panel-design/src/entity/pssys-view-panel-logic/pssys-view-panel-logic.ts b/packages/panel-design/src/entity/pssys-view-panel-logic/pssys-view-panel-logic.ts index 556160cd7bc2b6587afbe19845d4ad1f8cf839c2..6246df29413154c4ad34b81c8aa75b240dbf9bad 100644 --- a/packages/panel-design/src/entity/pssys-view-panel-logic/pssys-view-panel-logic.ts +++ b/packages/panel-design/src/entity/pssys-view-panel-logic/pssys-view-panel-logic.ts @@ -11,7 +11,10 @@ export class PSSysViewPanelLogic extends AppDataEntity { } // @ts-expect-error - set srfordervalue(value): number { + set srfordervalue(value): void { + if (this.ordervalue == null) { + return; + } this.ordervalue = value; }