diff --git a/src/control/data-view/data-view-provider.ts b/src/control/data-view/data-view-provider.ts new file mode 100644 index 0000000000000000000000000000000000000000..97345c9894d33e2a61abbb743b8cddb00d1a4a94 --- /dev/null +++ b/src/control/data-view/data-view-provider.ts @@ -0,0 +1,11 @@ +import { IControlProvider } from '@ibiz-template/runtime'; +/** + * 数据视图(卡片)部件适配器 + * + * @export + * @class DataViewCtrlProvider + * @implements {IControlProvider} + */ +export class DataViewCtrlProvider implements IControlProvider { + component: string = 'IBizDataViewCtrlControl'; +} diff --git a/src/control/data-view/data-view.controller.ts b/src/control/data-view/data-view.controller.ts new file mode 100644 index 0000000000000000000000000000000000000000..d9d9744e5ec48ea492437a10a7bbfb42aea2182a --- /dev/null +++ b/src/control/data-view/data-view.controller.ts @@ -0,0 +1,261 @@ +import { RuntimeModelError } from '@ibiz-template/core'; +import { + MDControlController, + IDataViewCtrlEvent, + IDataViewCtrlController, + IDataViewCtrlState, + ButtonContainerState, + UIActionButtonState, + UIActionUtil, + IDataViewCtrlGroup, + CodeListItem, +} from '@ibiz-template/runtime'; +import { + IDEDataView, + IDEDataViewItem, + IUIActionGroupDetail, +} from '@ibiz/model-core'; +import { DataViewCtrlService } from './data-view.service'; + +export class DataViewCtrlController + extends MDControlController< + IDEDataView, + IDataViewCtrlState, + IDataViewCtrlEvent + > + implements IDataViewCtrlController +{ + /** + * 数据视图(卡片)部件服务 + * + * @type {DataViewCtrlService} + * @memberof DataViewCtrlController + */ + declare service: DataViewCtrlService; + + /** + * 加载的数据是否附加在items之后 + * + * @type {boolean} + * @memberof DataViewCtrlController + */ + isAddBehind: boolean = false; + + /** + * 初始化State + * + * @protected + * @memberof DataViewCtrlController + */ + protected initState(): void { + super.initState(); + this.state.noSort = this.model.noSort === true; + this.state.groups = []; + } + + /** + * 初始化 + * + * @protected + * @return {*} {Promise} + * @memberof DataViewCtrlController + */ + protected async doCreated(): Promise { + await super.doCreated(); + this.state.size = this.model.pagingSize || 20; + this.service = new DataViewCtrlService(this.model); + await this.service.init(this.context); + } + + /** + * 加载更多 + * + * @return {*} {Promise} + * @memberof DataViewCtrlController + */ + async loadMore(): Promise { + if (this.state.total > this.state.items.length) { + this.isAddBehind = true; + // 加载下一页数据 + this.state.curPage += 1; + await this.load(); + this.isAddBehind = false; + } + } + + /** + * 加载完成之后 + * + * @param {IData[]} items + * @return {*} {Promise} + * @memberof DataViewCtrlController + */ + async afterLoad(items: IData[]): Promise { + // 决定传入父的afterLoad是否为附加items后的数组 + const allItems: IData[] = this.isAddBehind + ? [...this.state.items, ...items] + : items; + await super.afterLoad(allItems); + await this.handleDataGroup(); + return this.state.items; + } + + /** + * 获取操作项模型 + * + * @return {*} {(IDEDataViewItem | null)} + * @memberof DataViewCtrlController + */ + getOptItemModel(): IDEDataViewItem | null { + let optItemModel: IDEDataViewItem | null = null; + const { dedataViewItems } = this.model; + if (dedataViewItems) { + for (let index = 0; index < dedataViewItems.length; index++) { + if (dedataViewItems[index].itemType === 'ACTIONITEM') { + optItemModel = dedataViewItems[index]; + } + } + } + return optItemModel; + } + + /** + * 获取操作项行为 + * + * @param {IData} item + * @return {*} + * @memberof DataViewCtrlController + */ + getOptItemAction(item: IData): ButtonContainerState { + const containerState = new ButtonContainerState(); + const optItemModel = this.getOptItemModel(); + if (optItemModel) { + if (!optItemModel.deuiactionGroup) { + throw new RuntimeModelError(this.model, '操作项没有配置界面行为组'); + } + if (!optItemModel.deuiactionGroup.uiactionGroupDetails?.length) { + ibiz.log.debug('操作项界面行为组没有配置界面行为'); + return containerState; + } + optItemModel.deuiactionGroup.uiactionGroupDetails.forEach(detail => { + const actionid = detail.uiactionId; + if (actionid) { + const buttonState = new UIActionButtonState( + detail.id!, + this.context.srfappid!, + actionid, + ); + containerState.addState(detail.id!, buttonState); + } + }); + containerState.update(item.getOrigin()); + } + return containerState; + } + + /** + * 行为点击 + * + * @param {IUIActionGroupDetail} detail + * @param {IData} item + * @param {MouseEvent} event + * @return {*} {Promise} + * @memberof DataViewCtrlController + */ + async onActionClick( + detail: IUIActionGroupDetail, + item: IData, + event: MouseEvent, + ): Promise { + const actionId = detail.uiactionId; + await UIActionUtil.execAndResolved(actionId!, { + context: this.context, + params: this.params, + data: [item], + view: this.view, + event, + }); + } + + /** + * 处理数据分组 + * + * @memberof DataViewCtrlController + */ + async handleDataGroup() { + const { enableGroup, groupMode } = this.model; + if (enableGroup && groupMode) { + if (groupMode === 'AUTO') { + this.handleAutoGroup(); + } else if (groupMode === 'CODELIST') { + await this.handleCodeListGroup(); + } + } + } + + /** + * 处理自动分组 + * + * @memberof DataViewCtrlController + */ + handleAutoGroup() { + const { groupAppDEFieldId } = this.model; + if (groupAppDEFieldId) { + const { items } = this.state; + const childrenMap: Map = new Map(); + items.forEach((item: IData) => { + const children = childrenMap.get(item[groupAppDEFieldId]) || []; + children.push(item); + childrenMap.set(item[groupAppDEFieldId], children); + }); + const groups: IDataViewCtrlGroup[] = []; + childrenMap.forEach((value: IData[], key: string) => { + groups.push({ + caption: key, + children: [...value], + }); + }); + this.state.groups = groups; + } + } + + /** + * 处理代码表分组 + * + * @memberof DataViewCtrlController + */ + async handleCodeListGroup() { + const { groupAppDEFieldId, groupCodeListId } = this.model; + if (groupAppDEFieldId && groupCodeListId) { + const { items } = this.state; + const groups: IDataViewCtrlGroup[] = []; + const app = ibiz.hub.getApp(this.context.srfappid); + const codeList = await app.codeList.get( + groupCodeListId, + this.context, + this.params, + ); + const keys: string[] = []; + codeList.forEach((codeListItem: CodeListItem) => { + const value = items.filter( + (item: IData) => item[groupAppDEFieldId] === codeListItem.value, + ); + groups.push({ + caption: codeListItem.text, + children: [...value], + }); + keys.push(codeListItem.value as string); + }); + const otherGroup = items.filter( + (item: IData) => keys.indexOf(item[groupAppDEFieldId]) === -1, + ); + if (otherGroup.length > 0) { + groups.push({ + caption: '其他', + children: [...otherGroup], + }); + } + this.state.groups = groups; + } + } +} diff --git a/src/control/data-view/data-view.scss b/src/control/data-view/data-view.scss new file mode 100644 index 0000000000000000000000000000000000000000..9c762a6b75802fd8ffeeef5233bc9cddfa37ed31 --- /dev/null +++ b/src/control/data-view/data-view.scss @@ -0,0 +1,76 @@ +$control-dataview: ( + 'text-color': getCssVar('text-color'), + 'hover-bg-color': getCssVar('color', 'primary', 'light-7'), + 'active-bg-color': getCssVar('color', 'primary', 'light-7'), + 'padding': 10px, + 'margin': 16px, +); + +@include b(control-dataview-item) { + @include set-component-css-var('control-dataview', $control-dataview); + + margin: getCssVar('control-dataview', 'margin'); + padding: getCssVar('control-dataview', 'padding'); + + &:hover { + border-color: getCssVar('control-dataview', 'hover-bg-color'); + } + + @include when(active) { + border-color: getCssVar('control-dataview', 'active-bg-color'); + } + +} + +@include b(control-dataview) { + display: flex; + flex-wrap: wrap; + + // 加载更多样式 + @include e(load-more) { + text-align: center; + cursor: pointer; + } +} + +@include b(control-dataview-item-content) { + @include set-component-css-var('control-dataview', $control-dataview); + + @include e(top) { + @include m(title) { + text-align: center; + } + + @include m(description) { + min-width: 150px; + text-align: justify; + text-justify: newspaper; + word-break: break-all; + } + } + + @include e(bottom) { + @include m(actions) { + display: flex; + justify-content: center; + padding-top: getCssVar('control-dataview', 'padding'); + } + } +} + +@include b(control-dataview-group-content) { + overflow: auto; + width: 100%; + + @include e(item) { + .el-collapse-item__content { + display: flex; + flex-wrap: wrap; + } + + @include m(empty) { + text-align: center; + width: 100%; + } + } +} \ No newline at end of file diff --git a/src/control/data-view/data-view.service.ts b/src/control/data-view/data-view.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..b2a79c0a7e831f11ad1a1c6b1fc60bd3e6996ba3 --- /dev/null +++ b/src/control/data-view/data-view.service.ts @@ -0,0 +1,37 @@ +import { MDControlService, UIMapField } from '@ibiz-template/runtime'; +import { IDEDataView } from '@ibiz/model-core'; + +/** + * 数据视图(卡片)部件服务 + * + * @export + * @class DataViewCtrlService + * @extends {MDControlService} + */ +export class DataViewCtrlService extends MDControlService { + /** + * 初始化属性映射 + * + * @memberof DataViewCtrlService + */ + initUIDataMap() { + super.initUIDataMap(); + // *初始化表格数据项的属性映射 + this.model.dedataViewDataItems?.forEach(item => { + const uiKey = item.id!.toLowerCase(); + const deField = item.appDEFieldId; + let mapField: UIMapField; + // 后台实体属性 + if (deField) { + const deFieldKey = deField.toLowerCase(); + mapField = new UIMapField(uiKey, deFieldKey, { + isOriginField: true, + }); + } else { + // 前台属性 + mapField = new UIMapField(uiKey, uiKey); + } + this.dataUIMap.set(uiKey, mapField); + }); + } +} diff --git a/src/control/data-view/data-view.tsx b/src/control/data-view/data-view.tsx new file mode 100644 index 0000000000000000000000000000000000000000..aefe6bccaf37b8f29335a7f671a02b7403a08ac9 --- /dev/null +++ b/src/control/data-view/data-view.tsx @@ -0,0 +1,149 @@ +import { useControlController, useNamespace } from '@ibiz-template/vue3-util'; +import { defineComponent, PropType } from 'vue'; +import { IDEDataView, IUIActionGroupDetail } from '@ibiz/model-core'; +import { DataViewCtrlController } from './data-view.controller'; +import { IDataViewCtrlGroup } from '@ibiz-template/runtime'; +import './data-view.scss'; + +export const DataViewCtrlControl = defineComponent({ + name: 'IBizDataViewCtrlControl', + props: { + modelData: { type: Object as PropType, required: true }, + context: { type: Object as PropType, required: true }, + params: { type: Object as PropType, default: () => ({}) }, + }, + setup() { + const c = useControlController((...args) => new DataViewCtrlController(...args)); + const ns = useNamespace(`control-${c.model.controlType!.toLowerCase()}`); + + // 绘制项行为 + const renderItemAction = (item: IData) => { + return ( + c.onActionClick(detail, item, event)} + > + ) + } + + // 绘制默认项 + const renderDefaultItem = (item: IData) => { + return ( +
+
+
+ {item.srfmajortext} +
+
+ {item.content} +
+
+ { + c.getOptItemModel() ? +
+ {renderItemAction(item)} +
: null + } +
+ ) + } + + // 绘制卡片 + const renderCard = (item: IData) => { + // 是否选中数据 + const findIndex = c.state.selectedData.findIndex(data => { + return data.srfkey === item.srfkey; + }); + const cardClass = [ns.b('item'), ns.is('active', findIndex !== -1)]; + const cardStyle = {}; + if (c.model.cardWidth) { + Object.assign(cardStyle, { + width: `${c.model.cardWidth}px` + }) + } + if (c.model.cardHeight) { + Object.assign(cardStyle, { + height: `${c.model.cardHeight}px` + }) + } + return ( + c.onRowClick(item)} + onDblclick={() => c.onDbRowClick(item)} + > + {renderDefaultItem(item)} + + ); + }; + + // 绘制分组 + const renderGroup = (group: IDataViewCtrlGroup) => { + return ( + + { + group.children.length > 0 ? + group.children.map(child => { + return renderCard(child) + }) : +
+ 无数据 +
+ } +
+ ) + } + + // 绘制卡片内容 + const renderDataViewContent = () => { + if (c.model.enableGroup) { + return ( + + { + c.state.groups.map(group => { + return renderGroup(group) + }) + } + + ) + } + return c.state.items.map(item => { + return renderCard(item) + }) + }; + + // 绘制加载更多 + const renderLoadMore = () => { + return c.state.total > c.state.items.length ? ( +
c.loadMore()} class={ns.e('load-more')}> + 加载更多 +
+ ) : null; + }; + + return { + c, + ns, + renderDataViewContent, + renderLoadMore, + }; + }, + render() { + return ( + + {this.c.state.hasCreated && [ + this.renderDataViewContent(), + this.renderLoadMore(), + ]} + + ); + }, +}); diff --git a/src/control/data-view/index.ts b/src/control/data-view/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..44e81102c5b9fe1a340c09f28049da54c4c6193e --- /dev/null +++ b/src/control/data-view/index.ts @@ -0,0 +1,21 @@ +import { registerControlProvider, ControlType } from '@ibiz-template/runtime'; +import { withInstall } from '@ibiz-template/vue3-util'; +import { App } from 'vue'; +import { DataViewCtrlControl } from './data-view'; +import { DataViewCtrlProvider } from './data-view-provider'; + +export * from './data-view-provider'; +export * from './data-view.controller'; + +export const IBizDataViewCtrlControl = withInstall( + DataViewCtrlControl, + function (v: App) { + v.component(DataViewCtrlControl.name, DataViewCtrlControl); + registerControlProvider( + ControlType.DATAVIEW, + () => new DataViewCtrlProvider(), + ); + }, +); + +export default IBizDataViewCtrlControl; diff --git a/src/control/index.ts b/src/control/index.ts index 00b23308710657540a48102fa47e34a6e27399f6..e790b9aee9b51941b811ebfb30073e157d00be0d 100644 --- a/src/control/index.ts +++ b/src/control/index.ts @@ -5,3 +5,4 @@ export * from './form'; export * from './toolbar'; export * from './panel'; export * from './caption-bar'; +export * from './data-view'; diff --git a/src/index.ts b/src/index.ts index 55eea2c4a92787dbbb9c664394f87828feb16da4..ed379129668775ee0d2e57b4a6a76543ef3dc445 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ import { IBizSearchFormControl, IBizListControl, IBizCaptionBarControl, + IBizDataViewCtrlControl, } from './control'; import IBizEditor from './editor'; import { IBizView } from './view'; @@ -28,6 +29,7 @@ export default { v.use(IBizPanelComponents); v.use(IBizView); // 部件 + v.use(IBizDataViewCtrlControl); v.use(IBizAppMenuControl); v.use(IBizGridControl); v.use(IBizListControl); diff --git a/src/panel-component/index.ts b/src/panel-component/index.ts index c3c55c07a798d3bcd4aa809a5ae10e236ed8ca57..bc08e325a59b319a2d5298a64bea5ea655853ab8 100644 --- a/src/panel-component/index.ts +++ b/src/panel-component/index.ts @@ -5,12 +5,14 @@ import IBizNavTabs from './nav-tabs'; import IBizPanelContainer from './panel-container'; import IBizPanelCtrlPos from './panel-ctrl-pos'; import IBizScrollContainer from './scroll-container'; +import IBizPanelButton from './panel-button'; export * from './panel-container'; export * from './panel-ctrl-pos'; export * from './scroll-container'; export * from './auth-userinfo'; export * from './nav-pos'; +export * from './panel-button'; export const IBizPanelComponents = { install: (v: App) => { @@ -20,6 +22,7 @@ export const IBizPanelComponents = { v.use(IBizAuthUserinfo); v.use(IBizNavPos); v.use(IBizNavTabs); + v.use(IBizPanelButton); }, }; diff --git a/src/panel-component/panel-button/index.ts b/src/panel-component/panel-button/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..e332a7c3e254caa8e3e7ab945bee2f90a1de63c2 --- /dev/null +++ b/src/panel-component/panel-button/index.ts @@ -0,0 +1,15 @@ +import { registerPanelItemProvider } from '@ibiz-template/runtime'; +import { withInstall } from '@ibiz-template/vue3-util'; +import { App } from 'vue'; +import PanelButton from './panel-button'; +import { PanelButtonProvider } from './panel-button.provider'; + +export * from './panel-button.provider'; +export * from './panel-button.controller'; + +export const IBizPanelButton = withInstall(PanelButton, function (v: App) { + v.component(PanelButton.name, PanelButton); + registerPanelItemProvider('BUTTON', () => new PanelButtonProvider()); +}); + +export default IBizPanelButton; diff --git a/src/panel-component/panel-button/panel-button.controller.ts b/src/panel-component/panel-button/panel-button.controller.ts new file mode 100644 index 0000000000000000000000000000000000000000..fc1db4881150f94535c14dce979d96df1b128bbd --- /dev/null +++ b/src/panel-component/panel-button/panel-button.controller.ts @@ -0,0 +1,128 @@ +import { + PanelNotifyState, + UIActionButtonState, + UIActionUtil, +} from '@ibiz-template/runtime'; +import { IPanelButton } from '@ibiz/model-core'; +import { + PanelController, + PanelItemController, + ViewLayoutPanelController, +} from '../../control'; + +/** + * 面板按钮控制器 + * + * @export + * @class PanelButtonController + * @extends {PanelItemController} + */ +export class PanelButtonController extends PanelItemController { + /** + * 面板控制器 + * + * @type {ViewLayoutPanelController} + * @memberof PanelButtonController + */ + declare panel: ViewLayoutPanelController; + + /** + * 按钮状态 + * + * @type {UIActionButtonState} + * @memberof PanelButtonController + */ + buttonState: UIActionButtonState; + + /** + * Creates an instance of PanelButtonController. + * @param {IPanelButton} model + * @param {PanelController} panel + * @param {PanelItemController} [parent] + * @memberof PanelButtonController + */ + constructor( + model: IPanelButton, + panel: PanelController, + parent?: PanelItemController, + ) { + super(model, panel, parent); + this.buttonState = this.createUIActionState(); + } + + /** + * 初始化 + * + * @return {*} {Promise} + * @memberof PanelButtonController + */ + async onInit(): Promise { + await super.onInit(); + this.updateButtonState(); + } + + /** + * 创建界面行为状态对象 + * + * @protected + * @return {*} {PanelButtonState} + * @memberof PanelButtonController + */ + protected createUIActionState(): UIActionButtonState { + const { uiactionId, name } = this.model; + return new UIActionButtonState( + name!, + this.panel.context.srfappid!, + uiactionId, + ); + } + + /** + * 面板数据变更通知(由面板控制器调用) + * + * @param {string[]} names + * @memberof PanelButtonController + */ + dataChangeNotify(names: string[]): void { + super.dataChangeNotify(names); + this.updateButtonState(); + } + + /** + * 面板状态变更通知 + * + * @param {PanelNotifyState} _state + * @memberof PanelButtonController + */ + panelStateNotify(_state: PanelNotifyState): void { + super.panelStateNotify(_state); + this.updateButtonState(); + } + + /** + * 更新按钮状态 + * + * @memberof PanelButtonController + */ + updateButtonState() { + this.buttonState.update(this.panel.data); + } + + /** + * 行为点击 + * + * @param {MouseEvent} event + * @return {*} {Promise} + * @memberof PanelButtonController + */ + async onActionClick(event: MouseEvent): Promise { + const { uiactionId } = this.model; + await UIActionUtil.execAndResolved(uiactionId!, { + context: this.panel.context, + params: this.panel.params, + data: [this.panel.data], + view: this.panel.view, + event, + }); + } +} diff --git a/src/panel-component/panel-button/panel-button.provider.ts b/src/panel-component/panel-button/panel-button.provider.ts new file mode 100644 index 0000000000000000000000000000000000000000..f3ee04474dbba3c52a6f28b2a0b5c2355ad805ed --- /dev/null +++ b/src/panel-component/panel-button/panel-button.provider.ts @@ -0,0 +1,27 @@ +import { IPanelItemProvider } from '@ibiz-template/runtime'; +import { IPanelItem } from '@ibiz/model-core'; +import { PanelController, PanelItemController } from '../../control'; +import { PanelButtonController } from './panel-button.controller'; + +/** + * 导航标签页占位适配器 + * + * @author lxm + * @date 2022-09-19 22:09:03 + * @export + * @class NavTabsProvider + * @implements {EditorProvider} + */ +export class PanelButtonProvider implements IPanelItemProvider { + component: string = 'IBizPanelButton'; + + async createController( + panelItem: IPanelItem, + panel: PanelController, + parent: PanelItemController | undefined, + ): Promise { + const c = new PanelButtonController(panelItem, panel, parent); + await c.init(); + return c; + } +} diff --git a/src/panel-component/panel-button/panel-button.scss b/src/panel-component/panel-button/panel-button.scss new file mode 100644 index 0000000000000000000000000000000000000000..d0e80634e6e8fbfa4bd11dfa0a7454ddda14c93b --- /dev/null +++ b/src/panel-component/panel-button/panel-button.scss @@ -0,0 +1,4 @@ +@include b(panel-button) { + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/src/panel-component/panel-button/panel-button.tsx b/src/panel-component/panel-button/panel-button.tsx new file mode 100644 index 0000000000000000000000000000000000000000..88b890e674a3856294415cdf20550d4cb36b960c --- /dev/null +++ b/src/panel-component/panel-button/panel-button.tsx @@ -0,0 +1,103 @@ +import { useNamespace } from '@ibiz-template/vue3-util'; +import { IPanelButton } from '@ibiz/model-core'; +import { defineComponent, PropType, computed } from 'vue'; +import { PanelButtonController } from './panel-button.controller'; +import './panel-button.scss'; + +export const PanelButton = defineComponent({ + name: 'IBizPanelButton', + props: { + modelData: { + type: Object as PropType, + required: true, + }, + controller: { + type: PanelButtonController, + required: true, + }, + }, + setup(props) { + const ns = useNamespace('panel-button'); + + const { + caption, + captionItemName, + renderMode, + buttonStyle, + showCaption, + sysImage, + codeName, + } = props.modelData; + + const { + panel, + state, + buttonState, + } = props.controller; + + const captionText = computed(() => { + if (captionItemName && panel.data) { + return panel.data[captionItemName]; + } + return caption; + }); + + const buttonType = computed(() => { + let type: string = 'default'; + if (Object.is(renderMode, 'LINK')) { + type = 'text'; + } else if (buttonStyle) { + if ( + Object.is(buttonStyle, 'DEFAULT') || + Object.is(buttonStyle, 'STYLE2') || + Object.is(buttonStyle, 'STYLE3') || + Object.is(buttonStyle, 'STYLE4') + ) { + type = 'default'; + } else if (Object.is(buttonStyle, 'DANGER')) { + type = 'error'; + } else if (Object.is(buttonStyle, 'INVERSE')) { + type = 'primary'; + } else { + type = buttonStyle.toLowerCase(); + } + } + return type + }) + + const handleButtonClick = (event: MouseEvent) => { + props.controller.onActionClick(event) + } + + return { + ns, + captionText, + buttonType, + showCaption, + sysImage, + codeName, + state, + buttonState, + handleButtonClick + }; + }, + render() { + if (this.state.visible && this.buttonState.visible) { + return ( + +
+ + + {this.showCaption ? this.captionText : null} + +
+
+ ); + } + }, +}); +export default PanelButton; diff --git a/src/view-engine/data-view.engine.ts b/src/view-engine/data-view.engine.ts new file mode 100644 index 0000000000000000000000000000000000000000..eb7cb7bbc2593aa03f0d0599ff48f77fd1a55817 --- /dev/null +++ b/src/view-engine/data-view.engine.ts @@ -0,0 +1,46 @@ +import { + MDViewEngine, + ViewController, + IDataViewState, + IDataViewEvent, + IDataViewCtrlController, +} from '@ibiz-template/runtime'; +import { IAppDEDataView } from '@ibiz/model-core'; + +export class DataViewEngine extends MDViewEngine { + /** + * 视图控制器 + * + * @protected + * @type {ViewController} + * @memberof DataViewEngine + */ + protected declare view: ViewController< + IAppDEDataView, + IDataViewState, + IDataViewEvent + >; + + /** + * 数据视图(卡片)部件 + * + * @readonly + * @memberof DataViewEngine + */ + get dataview() { + return this.view.getController('dataview') as IDataViewCtrlController; + } + + /** + * 视图mounted生命周期执行逻辑 + * + * @memberof DataViewEngine + */ + async doMounted() { + super.doMounted(); + const { model } = this.view; + this.dataview.setState({ + mdctrlActiveMode: model.mdctrlActiveMode!, + }); + } +} diff --git a/src/view-engine/index.ts b/src/view-engine/index.ts index a28d0ded0b61db79e10456dc5da2a2cbca6dfc52..a8dca62b8f2c9b755c01b8f2a6a2814cb315ee4a 100644 --- a/src/view-engine/index.ts +++ b/src/view-engine/index.ts @@ -4,9 +4,13 @@ import { EditVIewEngine } from './edit-view.engine'; import { GridViewEngine } from './grid-view.engine'; import { IndexViewEngine } from './index-view.engine'; import { ListViewEngine } from './list-view.engine'; +import { DataViewEngine } from './data-view.engine'; +import { OptVIewEngine } from './opt-view.engine'; export * from './grid-view.engine'; export * from './index-view.engine'; +export * from './data-view.engine'; +export * from './opt-view.engine'; export const IBizViewEngine = { install: (_v: App) => { @@ -30,5 +34,13 @@ export const IBizViewEngine = { 'VIEW_EditView', (c: IViewController) => new EditVIewEngine(c), ); + ibiz.engine.register( + 'VIEW_DataView', + (c: IViewController) => new DataViewEngine(c), + ); + ibiz.engine.register( + 'VIEW_OptionView', + (c: IViewController) => new OptVIewEngine(c), + ); }, }; diff --git a/src/view-engine/opt-view.engine.ts b/src/view-engine/opt-view.engine.ts new file mode 100644 index 0000000000000000000000000000000000000000..f1eb48e56e47f02d673af226493fb8036cb363af --- /dev/null +++ b/src/view-engine/opt-view.engine.ts @@ -0,0 +1,133 @@ +import { + ViewEngineBase, + ViewController, + IEditFormController, + SysUIActionTag, + EventBase, + IOptViewState, + IOptViewEvent, +} from '@ibiz-template/runtime'; +import { IAppDEEditView } from '@ibiz/model-core'; + +export class OptVIewEngine extends ViewEngineBase { + /** + * 视图控制器 + * + * @protected + * @type {ViewController} + * @memberof OptVIewEngine + */ + protected declare view: ViewController< + IAppDEEditView, + IOptViewState, + IOptViewEvent + >; + + /** + * 表单部件 + * + * @readonly + * @memberof OptVIewEngine + */ + get form() { + return this.view.getController('form') as IEditFormController; + } + + /** + * 视图created生命周期执行逻辑 + * + * @return {*} {Promise} + * @memberof OptVIewEngine + */ + async doCreated(): Promise { + await super.doCreated(); + const { childNames } = this.view; + childNames.push('form'); + } + + /** + * 视图mounted生命周期执行逻辑 + * + * @return {*} {Promise} + * @memberof OptVIewEngine + */ + async doMounted(): Promise { + await super.doMounted(); + const { model, evt } = this.view; + + // 监控form事件 + const formDeId = this.form.model.appDataEntityId; + const formDataStateChange = (event: EventBase) => { + const data = event.data[0]; + this.toolbar?.calcButtonState(data, formDeId); + evt.emit('onViewInfoChange', { dataInfo: data.srfmajortext }); + }; + + this.form.evt.on('onLoadSuccess', event => { + formDataStateChange(event); + }); + this.form.evt.on('onLoadDraftSuccess', event => { + formDataStateChange(event); + }); + this.form.evt.on('onSaveSuccess', event => { + formDataStateChange(event); + }); + + if (model.loadDefault) { + this.load(); + } + } + + /** + * 获取数据 + * + * @return {*} {IData[]} + * @memberof OptVIewEngine + */ + getData(): IData[] { + return this.form.getData(); + } + + /** + * 加载 + * + * @memberof OptVIewEngine + */ + load() { + this.form.load(); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + async call(key: string, args: any): Promise { + if (key === SysUIActionTag.CANCEL) { + this.cancel(); + return null; + } + if (key === SysUIActionTag.OK) { + await this.confirm(); + return null; + } + return super.call(key, args); + } + + /** + * 确认 + * + * @memberof OptVIewEngine + */ + async confirm() { + const { evt } = this.view; + await this.form.save(); + evt.emit('onCloseView', { ok: true, result: this.getData() }); + } + + /** + * 取消 + * + * @memberof OptVIewEngine + */ + cancel() { + const { evt } = this.view; + evt.emit('onCloseView', { ok: false, result: undefined }); + } +}