From aa837051777e716a968b567f2391762917026597 Mon Sep 17 00:00:00 2001 From: zhf <1204297681@qq.com> Date: Wed, 25 Sep 2024 21:08:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=A7=86=E5=9B=BE=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E8=A1=A5=E5=85=85=E5=B7=A6=E4=BE=A7=E7=B4=A0?= =?UTF-8?q?=E6=9D=90=E5=8C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../public/assets/svg/er-artifacts.svg | 20 ++ .../public/assets/svg/er-entity-list.svg | 24 ++ .../public/assets/svg/tab-container.svg | 11 + packages/view-design/src/components/index.ts | 17 ++ .../view-design-collapse-control.scss | 30 ++ .../view-design-collapse-control.tsx | 42 +++ .../view-design-collapse-list.scss | 30 ++ .../view-design-collapse-list.tsx | 42 +++ .../view-design-material-page.scss | 148 +++++++++ .../view-design-material-page.tsx | 287 ++++++++++++++++++ .../view-design-structure-tree.scss | 35 +++ .../view-design-structure-tree.tsx | 87 ++++++ packages/view-design/src/global.ts | 7 + packages/view-design/src/index.ts | 4 + packages/view-design/src/panel-items/index.ts | 10 + .../view-design-material-tab/index.ts | 17 ++ .../view-design-material-tab.controller.ts | 174 +++++++++++ .../view-design-material-tab.provider.ts | 21 ++ .../view-design-material-tab.scss | 159 ++++++++++ .../view-design-material-tab.state.ts | 39 +++ .../view-design-material-tab.tsx | 242 +++++++++++++++ .../view-design-material-tree/index.ts | 15 + ...w-design-material-tree.controller.state.ts | 30 ++ .../view-design-material-tree.controller.ts | 73 +++++ .../view-design-material-tree.provider.ts | 21 ++ .../view-design-material-tree.scss | 12 + .../view-design-material-tree.tsx | 73 +++++ 27 files changed, 1670 insertions(+) create mode 100644 packages/view-design/public/assets/svg/er-artifacts.svg create mode 100644 packages/view-design/public/assets/svg/er-entity-list.svg create mode 100644 packages/view-design/public/assets/svg/tab-container.svg create mode 100644 packages/view-design/src/components/index.ts create mode 100644 packages/view-design/src/components/view-design-collapse-control/view-design-collapse-control.scss create mode 100644 packages/view-design/src/components/view-design-collapse-control/view-design-collapse-control.tsx create mode 100644 packages/view-design/src/components/view-design-collapse-list/view-design-collapse-list.scss create mode 100644 packages/view-design/src/components/view-design-collapse-list/view-design-collapse-list.tsx create mode 100644 packages/view-design/src/components/view-design-material-page/view-design-material-page.scss create mode 100644 packages/view-design/src/components/view-design-material-page/view-design-material-page.tsx create mode 100644 packages/view-design/src/components/view-design-structure-tree/view-design-structure-tree.scss create mode 100644 packages/view-design/src/components/view-design-structure-tree/view-design-structure-tree.tsx create mode 100644 packages/view-design/src/global.ts create mode 100644 packages/view-design/src/panel-items/index.ts create mode 100644 packages/view-design/src/panel-items/view-design-material-tab/index.ts create mode 100644 packages/view-design/src/panel-items/view-design-material-tab/view-design-material-tab.controller.ts create mode 100644 packages/view-design/src/panel-items/view-design-material-tab/view-design-material-tab.provider.ts create mode 100644 packages/view-design/src/panel-items/view-design-material-tab/view-design-material-tab.scss create mode 100644 packages/view-design/src/panel-items/view-design-material-tab/view-design-material-tab.state.ts create mode 100644 packages/view-design/src/panel-items/view-design-material-tab/view-design-material-tab.tsx create mode 100644 packages/view-design/src/panel-items/view-design-material-tree/index.ts create mode 100644 packages/view-design/src/panel-items/view-design-material-tree/view-design-material-tree.controller.state.ts create mode 100644 packages/view-design/src/panel-items/view-design-material-tree/view-design-material-tree.controller.ts create mode 100644 packages/view-design/src/panel-items/view-design-material-tree/view-design-material-tree.provider.ts create mode 100644 packages/view-design/src/panel-items/view-design-material-tree/view-design-material-tree.scss create mode 100644 packages/view-design/src/panel-items/view-design-material-tree/view-design-material-tree.tsx diff --git a/packages/view-design/public/assets/svg/er-artifacts.svg b/packages/view-design/public/assets/svg/er-artifacts.svg new file mode 100644 index 00000000..73d6f948 --- /dev/null +++ b/packages/view-design/public/assets/svg/er-artifacts.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/view-design/public/assets/svg/er-entity-list.svg b/packages/view-design/public/assets/svg/er-entity-list.svg new file mode 100644 index 00000000..0794b8cb --- /dev/null +++ b/packages/view-design/public/assets/svg/er-entity-list.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/view-design/public/assets/svg/tab-container.svg b/packages/view-design/public/assets/svg/tab-container.svg new file mode 100644 index 00000000..8bcf50e8 --- /dev/null +++ b/packages/view-design/public/assets/svg/tab-container.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/packages/view-design/src/components/index.ts b/packages/view-design/src/components/index.ts new file mode 100644 index 00000000..8d9bb06a --- /dev/null +++ b/packages/view-design/src/components/index.ts @@ -0,0 +1,17 @@ +import { App } from 'vue'; +import IBizViewDesignMaterialPage from './view-design-material-page/view-design-material-page'; +import IBizViewDesignStructureTree from './view-design-structure-tree/view-design-structure-tree'; +import IBizViewDesignCollapseList from './view-design-collapse-list/view-design-collapse-list'; +import IBizViewDesignCollapseControl from './view-design-collapse-control/view-design-collapse-control'; + +export default { + install(app: App): void { + app.component('IBizViewDesignMaterialPage', IBizViewDesignMaterialPage); + app.component('IBizViewDesignStructureTree', IBizViewDesignStructureTree); + app.component('IBizViewDesignCollapseList', IBizViewDesignCollapseList); + app.component( + 'IBizViewDesignCollapseControl', + IBizViewDesignCollapseControl, + ); + }, +}; diff --git a/packages/view-design/src/components/view-design-collapse-control/view-design-collapse-control.scss b/packages/view-design/src/components/view-design-collapse-control/view-design-collapse-control.scss new file mode 100644 index 00000000..d4253077 --- /dev/null +++ b/packages/view-design/src/components/view-design-collapse-control/view-design-collapse-control.scss @@ -0,0 +1,30 @@ +/* stylelint-disable no-descending-specificity */ +/* stylelint-disable selector-class-pattern */ +@include b(view-design-collapse-control) { + &:not(:last-child) { + .el-collapse { + border-bottom: none; + } + } + + .el-collapse { + --el-collapse-header-height: 40px; + --el-collapse-header-bg-color: transparent; + --el-collapse-content-bg-color: #{getCssVar(color, bg, 1)}; + --el-collapse-header-text-color: #{getCssVar(color, text, 1)}; + --el-collapse-header-font-size: 12px; + + .el-collapse-item__header { + padding: 0 15px; + border: none; + } + + .el-collapse-item__wrap { + border: none; + } + + .el-collapse-item__content { + padding: 0; + } + } +} diff --git a/packages/view-design/src/components/view-design-collapse-control/view-design-collapse-control.tsx b/packages/view-design/src/components/view-design-collapse-control/view-design-collapse-control.tsx new file mode 100644 index 00000000..fef6d801 --- /dev/null +++ b/packages/view-design/src/components/view-design-collapse-control/view-design-collapse-control.tsx @@ -0,0 +1,42 @@ +import { defineComponent } from 'vue'; +import { useNamespace } from '@ibiz-template/vue3-util'; +import './view-design-collapse-control.scss'; + +export default defineComponent({ + name: 'IBizViewDesignCollapseControl', + props: { + title: { + type: String, + default: '', + }, + }, + setup() { + const ns = useNamespace('view-design-collapse-control'); + + return { + ns, + }; + }, + render() { + return ( +
+ + + {{ + title: () => { + return ( +
+
+ {this.title} +
+
+
+ ); + }, + }} +
+
+
+ ); + }, +}); diff --git a/packages/view-design/src/components/view-design-collapse-list/view-design-collapse-list.scss b/packages/view-design/src/components/view-design-collapse-list/view-design-collapse-list.scss new file mode 100644 index 00000000..633c44e5 --- /dev/null +++ b/packages/view-design/src/components/view-design-collapse-list/view-design-collapse-list.scss @@ -0,0 +1,30 @@ +/* stylelint-disable no-descending-specificity */ +/* stylelint-disable selector-class-pattern */ +@include b(view-design-collapse-list) { + &:not(:last-child) { + .el-collapse { + border-bottom: none; + } + } + + .el-collapse { + --el-collapse-header-height: 40px; + --el-collapse-header-bg-color: transparent; + --el-collapse-content-bg-color: #{getCssVar(color, bg, 1)}; + --el-collapse-header-text-color: #{getCssVar(color, text, 1)}; + --el-collapse-header-font-size: 12px; + + .el-collapse-item__header { + padding: 0 15px; + border: none; + } + + .el-collapse-item__wrap { + border: none; + } + + .el-collapse-item__content { + padding: 0; + } + } +} \ No newline at end of file diff --git a/packages/view-design/src/components/view-design-collapse-list/view-design-collapse-list.tsx b/packages/view-design/src/components/view-design-collapse-list/view-design-collapse-list.tsx new file mode 100644 index 00000000..34cf9476 --- /dev/null +++ b/packages/view-design/src/components/view-design-collapse-list/view-design-collapse-list.tsx @@ -0,0 +1,42 @@ +import { defineComponent } from 'vue'; +import { useNamespace } from '@ibiz-template/vue3-util'; +import './view-design-collapse-list.scss'; + +export default defineComponent({ + name: 'IBizViewDesignCollapseList', + props: { + title: { + type: String, + default: '', + }, + }, + setup() { + const ns = useNamespace('view-design-collapse-list'); + + return { + ns, + }; + }, + render() { + return ( +
+ + + {{ + title: () => { + return ( +
+
+ {this.title} +
+
+
+ ); + }, + }} +
+
+
+ ); + }, +}); diff --git a/packages/view-design/src/components/view-design-material-page/view-design-material-page.scss b/packages/view-design/src/components/view-design-material-page/view-design-material-page.scss new file mode 100644 index 00000000..46c1e28b --- /dev/null +++ b/packages/view-design/src/components/view-design-material-page/view-design-material-page.scss @@ -0,0 +1,148 @@ +/* stylelint-disable selector-class-pattern */ +@include b(view-design-material-page) { + display: flex; + flex-direction: column; + height: 100%; + background: rgba(var(#{getCssVarName(grey, 0)}), 1); + + @include m(single) { + @include b(view-design-material-page-group-item) { + width: 100%; + } + } +} + +@include b(view-design-material-page-header) { + display: flex; + flex: 0 0 auto; + align-items: center; + margin: 8px 0; +} + +@include b(view-design-material-page-header-btn-group) { + display: flex; + flex: 0 0 auto; + align-items: center; + margin-right: 8px; + margin-left: 16px; +} + +@include b(view-design-material-page-header-btn) { + display: flex; + align-items: center; + justify-content: center; + margin-right: 2px; + font-size: 14px; + color: getCssVar(color, text, 3); + cursor: pointer; + + > svg { + width: 1em; + height: 1em; + fill: currentcolor; + } + + @include when(active) { + color: getCssVar(color, text, 1); + } +} + +@include b(view-design-material-page-header-search-btn) { + display: flex; + align-items: center; + justify-content: center; + height: 26px; + color: getCssVar(color, text, 3); + cursor: pointer; +} + +@include b(view-design-material-page-header-search) { + flex: 1 1 0; + padding-right: 10px; + + > .el-input { + --el-input-height: 26px; + + .el-input__inner::placeholder { + font-size: 12px; + } + } +} + +@include b(view-design-material-page-content) { + flex: 1 1 0; + overflow: auto; + + .el-collapse { + --el-collapse-header-height: 28px; + --el-collapse-header-bg-color: transparent; + --el-collapse-content-bg-color: #{getCssVar(color, bg, 1)}; + --el-collapse-header-text-color: #{getCssVar(color, text, 1)}; + --el-collapse-header-font-size: 12px; + + border: none; + + .el-collapse-item__header { + border: none; + } + + .el-collapse-item__wrap { + border: none; + } + + .el-collapse-item__content { + display: flex; + flex-wrap: wrap; + padding: 0; + } + } +} + +@include b(view-design-material-page-group-header) { + padding-left: 6px; + overflow: hidden; + font-size: 12px; + color: getCssVar(color, text, 1); + text-overflow: ellipsis; + white-space: nowrap; +} + +@include b(view-design-material-page-group-item) { + width: calc(100% / 3); + height: 80px; + overflow: hidden; + text-overflow: ellipsis; + + @include e(icon) { + display: flex; + align-items: center; + justify-content: center; + width: 56px; + height: 56px; + margin: 0 auto; + + ion-icon { + font-size: 56px; + } + + img { + max-width: 100%; + } + } + + @include e(text) { + padding: 0 4px; + overflow: hidden; + line-height: 20px; + text-align: center; + text-overflow: ellipsis; + white-space: nowrap; + } +} + +@include b(view-design-material-page-group-item-content) { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; +} diff --git a/packages/view-design/src/components/view-design-material-page/view-design-material-page.tsx b/packages/view-design/src/components/view-design-material-page/view-design-material-page.tsx new file mode 100644 index 00000000..a6ce3b59 --- /dev/null +++ b/packages/view-design/src/components/view-design-material-page/view-design-material-page.tsx @@ -0,0 +1,287 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable no-return-assign */ +import { PropType, defineComponent, onMounted, ref } from 'vue'; +import { useClickOutside, useNamespace } from '@ibiz-template/vue3-util'; +import { IDEFormGroupPanel } from '@ibiz/model-core'; +import { resource } from '../../global'; +import './view-design-material-page.scss'; + +export default defineComponent({ + name: 'IBizViewDesignMaterialPage', + props: { + groups: { + type: Array as PropType, + default: () => [], + }, + columnMode: { + type: String as PropType<'multiple' | 'single'>, + default: 'multiple', + }, + activeGroups: { + type: Array as PropType, + default: () => [], + }, + }, + setup(props) { + const ns = useNamespace('view-design-material-page'); + + // 收缩面板值 + const collapseValue = ref(props.groups.map(group => group.codeName)); + + // 搜索值 + const searchValue = ref(''); + + // 搜索框容器元素 + const search = ref(); + + // 是否展示搜索框 + const isShowSearch = ref(false); + + onMounted(() => { + if (search.value) { + useClickOutside(search as any, (): void => { + isShowSearch.value = false; + }); + } + }); + + // 收缩分组 + const collapse = (): void => { + collapseValue.value = []; + }; + + // 展开分组 + const expand = (): void => { + collapseValue.value = props.groups.map(group => group.codeName); + }; + + // 渲染图标 + const renderIcon = (icon?: string): any => { + if (icon) { + if (icon.startsWith('http')) { + return ; + } + if (icon.endsWith('')) { + const svg = URL.createObjectURL( + new Blob([icon], { type: 'image/svg+xml;charset=utf-8' }), + ); + return ; + } + if (icon.startsWith('data:image')) { + return ; + } + } + return ( + + ); + }; + + return { + ns, + collapseValue, + searchValue, + isShowSearch, + search, + collapse, + expand, + renderIcon, + }; + }, + render() { + const isExpandAll = this.groups.every(group => + this.collapseValue.includes(group.codeName), + ); + return ( +
+
+
+
this.expand()} + > + + + + + + + + + + + + + + + + + +
+
this.collapse()} + > + + + + + + + + + + + + + + + + + +
+
+
(this.isShowSearch = true)} + > + +
+
+ + {{ + prefix: () => { + return ; + }, + }} + +
+
+
+ + {this.groups.map(group => { + if (!this.activeGroups.includes(group.codeName!)) { + return; + } + return ( + + {{ + title: () => { + return ( +
+ {group.caption} +
+ ); + }, + default: () => { + return group.deformDetails?.map(item => { + if (this.searchValue) { + const regExp = this.searchValue.trim().toLowerCase(); + const text = item.caption?.toLowerCase(); + if (text && text.indexOf(regExp) === -1) { + return; + } + } + return ( +
+
+
+ {this.renderIcon(item.sysImage?.imagePath)} +
+
+ {item.caption || ''} +
+
+
+ ); + }); + }, + }} +
+ ); + })} +
+
+
+ ); + }, +}); diff --git a/packages/view-design/src/components/view-design-structure-tree/view-design-structure-tree.scss b/packages/view-design/src/components/view-design-structure-tree/view-design-structure-tree.scss new file mode 100644 index 00000000..02e0dd6d --- /dev/null +++ b/packages/view-design/src/components/view-design-structure-tree/view-design-structure-tree.scss @@ -0,0 +1,35 @@ +/* stylelint-disable selector-class-pattern */ +@include b(view-design-structure-tree) { + width: 100%; + height: 100%; + background: rgba(var(#{getCssVarName(grey, 0)}), 1); +} + +@include b(view-design-structure-tree-search) { + padding: 2px 10px 2px 0; + margin-left: 8px; + + > .el-input { + --el-input-height: 26px; + + .el-input__inner::placeholder { + font-size: 12px; + } + } +} + +@include b(view-design-structure-tree-header) { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 12px 0 28px; + font-size: 12px; + line-height: 28px; + + @include e(action) { + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + } +} diff --git a/packages/view-design/src/components/view-design-structure-tree/view-design-structure-tree.tsx b/packages/view-design/src/components/view-design-structure-tree/view-design-structure-tree.tsx new file mode 100644 index 00000000..e3e70bb1 --- /dev/null +++ b/packages/view-design/src/components/view-design-structure-tree/view-design-structure-tree.tsx @@ -0,0 +1,87 @@ +import { defineComponent, ref } from 'vue'; +import { useNamespace } from '@ibiz-template/vue3-util'; +import './view-design-structure-tree.scss'; + +export default defineComponent({ + name: 'IBizViewDesignStructureTree', + setup() { + const ns = useNamespace('view-design-structure-tree'); + + // 搜索值 + const searchValue = ref(''); + + return { + ns, + searchValue, + }; + }, + render() { + return ( +
+
+ + {{ + prefix: () => { + return ; + }, + }} + +
+
+
布局结构
+
+ + + + + + + + + + + + +
+
+
+ +
+
+ ); + }, +}); diff --git a/packages/view-design/src/global.ts b/packages/view-design/src/global.ts new file mode 100644 index 00000000..0fa10431 --- /dev/null +++ b/packages/view-design/src/global.ts @@ -0,0 +1,7 @@ +// 文件路径: [插件项目的根目录]/src/global.ts + +import { PluginStaticResource } from '@ibiz-template/runtime'; + +const resource = new PluginStaticResource(import.meta.url); + +export { resource }; diff --git a/packages/view-design/src/index.ts b/packages/view-design/src/index.ts index b17714e6..a35de149 100644 --- a/packages/view-design/src/index.ts +++ b/packages/view-design/src/index.ts @@ -3,10 +3,14 @@ import { App } from 'vue'; import Views from './views'; import Services from './service'; +import PanelItems from './panel-items'; +import Components from './components'; export default { install(app: App) { app.use(Views); app.use(Services); + app.use(PanelItems); + app.use(Components); }, }; diff --git a/packages/view-design/src/panel-items/index.ts b/packages/view-design/src/panel-items/index.ts new file mode 100644 index 00000000..bd679f7c --- /dev/null +++ b/packages/view-design/src/panel-items/index.ts @@ -0,0 +1,10 @@ +import { App } from 'vue'; +import ViewDesignMaterialTree from './view-design-material-tree'; +import ViewDesignMaterialTab from './view-design-material-tab'; + +export default { + install(app: App): void { + app.use(ViewDesignMaterialTree); + app.use(ViewDesignMaterialTab); + }, +}; diff --git a/packages/view-design/src/panel-items/view-design-material-tab/index.ts b/packages/view-design/src/panel-items/view-design-material-tab/index.ts new file mode 100644 index 00000000..a02be406 --- /dev/null +++ b/packages/view-design/src/panel-items/view-design-material-tab/index.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import { App } from 'vue'; +import { registerPanelItemProvider } from '@ibiz-template/runtime'; +import ViewDesignMaterialTab from './view-design-material-tab'; +import { ViewDesignMaterialTabProvider } from './view-design-material-tab.provider'; + +export default { + install(app: App) { + // 注册组件 + app.component('IBizViewDesignMaterialTab', ViewDesignMaterialTab); + registerPanelItemProvider( + 'RAWITEM_VIEW_DESIGN_MATERIAL_TAB', + () => new ViewDesignMaterialTabProvider(), + ); + }, +}; diff --git a/packages/view-design/src/panel-items/view-design-material-tab/view-design-material-tab.controller.ts b/packages/view-design/src/panel-items/view-design-material-tab/view-design-material-tab.controller.ts new file mode 100644 index 00000000..bc3da886 --- /dev/null +++ b/packages/view-design/src/panel-items/view-design-material-tab/view-design-material-tab.controller.ts @@ -0,0 +1,174 @@ +import { + PanelItemController, + calcLayoutHeightWidth, + getControl, +} from '@ibiz-template/runtime'; +import { + IDEEditForm, + IDEFormGroupPanel, + IDEFormPage, + IDEFormTabPanel, +} from '@ibiz/model-core'; +import { ViewDesignMaterialTabState } from './view-design-material-tab.state'; +import { ViewDesignMaterialTreeController } from '../view-design-material-tree/view-design-material-tree.controller'; + +export class ViewDesignMaterialTabController extends PanelItemController { + declare state: ViewDesignMaterialTabState; + + protected createState(): ViewDesignMaterialTabState { + return new ViewDesignMaterialTabState(this.parent?.state); + } + + get materialForm(): IDEEditForm { + return getControl(this.panel.view.model, 'material') as IDEEditForm; + } + + get viewDesignMaterialTree(): ViewDesignMaterialTreeController | undefined { + return this.panel.panelItems + .view_design_material_tree as ViewDesignMaterialTreeController; + } + + protected async onInit(): Promise { + await super.onInit(); + this.initItems(); + } + + /** + * 初始化项数据 + * + * @author zhanghengfeng + * @date 2024-09-25 20:09:21 + * @return {*} {void} + */ + initItems(): void { + const formPages = this.materialForm.deformPages; + if (!formPages) { + return; + } + const pages: IDEFormPage[] = []; + formPages.forEach(formPage => { + pages.push(formPage); + const details = formPage.deformDetails; + details?.forEach(detail => { + if (detail.detailType === 'TABPANEL') { + const tabPages = (detail as IDEFormTabPanel).deformTabPages; + if (tabPages) { + pages.push(...tabPages); + } + } + }); + }); + const materialPage = pages.find(page => page.codeName === 'controls'); + if (materialPage) { + const details = materialPage.deformDetails; + const groups: IDEFormGroupPanel[] = []; + details?.forEach(detail => { + if ( + detail.detailType === 'GROUPPANEL' && + detail.codeName?.toUpperCase() !== 'HIDDENGROUP' + ) { + groups.push(detail); + } + }); + this.state.pageItems = formPages?.map(page => { + return { + id: page.codeName === 'toolkit' ? 'controls' : page.codeName || '', + text: page.caption || '', + imagePath: page.sysImage?.imagePath, + }; + }); + this.state.menuItems.push( + ...groups.map(group => { + return { + id: group.codeName || '', + text: group.caption || '', + }; + }), + ); + this.state.activeItems.push(...groups.map(group => group.codeName || '')); + } + } + + /** + * 处理选中改变 + * + * @author zhanghengfeng + * @date 2024-09-25 20:09:47 + * @param {string} value + * @return {*} {void} + */ + onSelect(value: string): void { + if (!this.viewDesignMaterialTree) { + return; + } + if (this.state.pageItems.some(page => page.id === value)) { + const parent = this.viewDesignMaterialTree.parent; + if (!parent) { + return; + } + const { layoutPos } = parent.model; + if (!layoutPos) { + return; + } + const { width } = calcLayoutHeightWidth(layoutPos); + parent.state.layout.width = `${width}`; + this.state.columnMode = 'multiple'; + this.viewDesignMaterialTree.state.columnMode = this.state.columnMode; + this.viewDesignMaterialTree.state.activePage = value; + return; + } + this.viewDesignMaterialTree.state.activePage = 'controls'; + if (value === '$multiple' || value === '$single') { + const parent = this.viewDesignMaterialTree.parent; + if (!parent) { + return; + } + const { layoutPos } = parent.model; + if (!layoutPos) { + return; + } + const { width } = calcLayoutHeightWidth(layoutPos); + if (value === '$multiple') { + parent.state.layout.width = `${width}`; + } + if (value === '$single') { + parent.state.layout.width = `calc(${width} / 3)`; + } + this.state.columnMode = value.slice(1) as 'multiple' | 'single'; + this.viewDesignMaterialTree.state.columnMode = this.state.columnMode; + return; + } + if (value === '$component') { + this.state.activeItems = []; + this.viewDesignMaterialTree.state.activeGroups = [ + ...this.state.activeItems, + ]; + return; + } + if (value === '$all') { + const result = this.state.menuItems.every(item => + this.state.activeItems.includes(item.id), + ); + if (!result) { + this.state.activeItems = this.state.menuItems.map(item => item.id); + } else { + this.state.activeItems = this.state.menuItems[0]?.id + ? [this.state.menuItems[0].id] + : []; + } + this.viewDesignMaterialTree.state.activeGroups = [ + ...this.state.activeItems, + ]; + return; + } + const index = this.state.activeItems.findIndex(v => v === value); + if (index !== -1) { + this.state.activeItems.splice(index, 1); + } else { + this.state.activeItems.push(value); + } + this.viewDesignMaterialTree.state.activeGroups = [ + ...this.state.activeItems, + ]; + } +} diff --git a/packages/view-design/src/panel-items/view-design-material-tab/view-design-material-tab.provider.ts b/packages/view-design/src/panel-items/view-design-material-tab/view-design-material-tab.provider.ts new file mode 100644 index 00000000..fff0daa7 --- /dev/null +++ b/packages/view-design/src/panel-items/view-design-material-tab/view-design-material-tab.provider.ts @@ -0,0 +1,21 @@ +import { + IPanelItemController, + IPanelItemProvider, + IPanelController, +} from '@ibiz-template/runtime'; +import { IPanelItem } from '@ibiz/model-core'; +import { ViewDesignMaterialTabController } from './view-design-material-tab.controller'; + +export class ViewDesignMaterialTabProvider implements IPanelItemProvider { + component = 'IBizViewDesignMaterialTab'; + + async createController( + panelItem: IPanelItem, + panel: IPanelController, + parent?: IPanelItemController, + ): Promise { + const c = new ViewDesignMaterialTabController(panelItem, panel, parent); + await c.init(); + return c; + } +} diff --git a/packages/view-design/src/panel-items/view-design-material-tab/view-design-material-tab.scss b/packages/view-design/src/panel-items/view-design-material-tab/view-design-material-tab.scss new file mode 100644 index 00000000..8b2a0e10 --- /dev/null +++ b/packages/view-design/src/panel-items/view-design-material-tab/view-design-material-tab.scss @@ -0,0 +1,159 @@ +/* stylelint-disable selector-class-pattern */ +@include b(view-design-material-tab) { + display: flex; + align-items: center; + padding: 0 16px 0 10px; + + .el-menu { + --el-menu-hover-text-color: #{getCssVar(color, primary, hover)}; + --el-menu-hover-bg-color: #{getCssVar(color, primary, light, default)}; + --el-menu-text-color: #{getCssVar(color, text, 2)}; + + &.el-menu--horizontal.el-menu { + --el-menu-horizontal-height: 24px; + --el-menu-item-height: 24px; + --el-menu-icon-width: 20px; + --el-menu-base-level-padding: #{getCssVar('spacing', 'tight')}; + + border: none; + + @include b(view-design-material-tab-menu-item) { + @include m(node) { + margin-left: getCssVar('spacing', 'base-loose'); + border-radius: getCssVar(border, radius, small); + } + } + } + + &.el-menu--horizontal > .el-menu-item { + border-bottom: none; + } + + &.el-menu--horizontal > .el-menu-item.is-active { + border-bottom: none; + } + + &.el-menu--horizontal .el-menu-item:not(:hover):focus { + background-color: transparent; + } + + &.el-menu--horizontal > .el-sub-menu { + margin-right: 20px; + } + + &.el-menu--horizontal > .el-sub-menu .el-sub-menu__title { + color: var(--el-menu-text-color); + border: none; + border-radius: 4px; + + &:hover { + background-color: var(--el-menu-hover-bg-color); + } + } + } +} + +@include b(view-design-material-tab-menu-header) { + display: flex; + align-items: center; + + @include e(icon) { + display: flex; + flex: 0 0 auto; + align-items: center; + justify-content: center; + } + + @include e(text) { + margin-left: 6px; + } +} + +@include b(view-design-material-tab-page-item) { + border-radius: 4px; + + @include e(icon) { + display: flex; + align-items: center; + justify-content: center; + margin-right: 6px; + color: var(--el-menu-text-color); + } + + @include e(text) { + margin-right: 6px; + color: var(--el-menu-text-color); + } +} + +@include b(view-design-material-tab-sub-menu-header) { + display: flex; + align-items: center; + + @include e(text) { + color: var(--el-menu-text-color); + } +} + +@include b(view-design-material-tab-menu-item) { + @include e(icon) { + display: flex; + align-items: center; + justify-content: center; + margin-right: 4px; + color: var(--el-menu-text-color); + } + + @include e(text) { + margin-right: 4px; + color: var(--el-menu-text-color); + } + + @include e(select-icon) { + display: flex; + align-items: center; + justify-content: center; + margin-left: auto; + visibility: hidden; + } + + @include m(active) { + color: var(--el-menu-hover-text-color); + background-color: var(--el-menu-hover-bg-color); + + @include e(select-icon) { + visibility: visible; + } + } +} + +@include b(view-design-material-tab-popper) { + --el-menu-horizontal-sub-item-height: 32px; + --el-menu-base-level-padding: #{getCssVar('spacing', 'base-tight')}; + --el-menu-hover-text-color: #{getCssVar(color, primary, hover)}; + --el-menu-hover-bg-color: #{getCssVar(color, primary, light, default)}; + --el-menu-text-color: #{getCssVar(color, text, 2)}; + + .el-menu--popup { + min-width: 120px; + } + + &.el-menu--horizontal .el-menu { + @include b(view-design-material-tab-menu-item) { + @include m(active) { + color: var(--el-menu-hover-text-color); + background-color: var(--el-menu-hover-bg-color); + + @include e(select-icon) { + visibility: visible; + } + } + } + } + + &.el-menu--horizontal .el-menu .el-sub-menu__title { + &:hover { + background-color: var(--el-menu-hover-bg-color); + } + } +} diff --git a/packages/view-design/src/panel-items/view-design-material-tab/view-design-material-tab.state.ts b/packages/view-design/src/panel-items/view-design-material-tab/view-design-material-tab.state.ts new file mode 100644 index 00000000..7f41c98d --- /dev/null +++ b/packages/view-design/src/panel-items/view-design-material-tab/view-design-material-tab.state.ts @@ -0,0 +1,39 @@ +import { PanelItemState } from '@ibiz-template/runtime'; + +export class ViewDesignMaterialTabState extends PanelItemState { + /** + * 分页项 + * + * @author zhanghengfeng + * @date 2024-09-25 20:09:24 + * @type {{ id: string; text: string; imagePath?: string }[]} + */ + pageItems: { id: string; text: string; imagePath?: string }[] = []; + + /** + * 菜单项 + * + * @author zhanghengfeng + * @date 2024-09-25 20:09:35 + * @type {{ id: string; text: string }[]} + */ + menuItems: { id: string; text: string }[] = []; + + /** + * 激活菜单项 + * + * @author zhanghengfeng + * @date 2024-09-25 20:09:46 + * @type {string[]} + */ + activeItems: string[] = []; + + /** + * 布局模式 + * + * @author zhanghengfeng + * @date 2024-09-25 20:09:57 + * @type {('multiple' | 'single')} + */ + columnMode: 'multiple' | 'single' = 'multiple'; +} diff --git a/packages/view-design/src/panel-items/view-design-material-tab/view-design-material-tab.tsx b/packages/view-design/src/panel-items/view-design-material-tab/view-design-material-tab.tsx new file mode 100644 index 00000000..bb5b8a2d --- /dev/null +++ b/packages/view-design/src/panel-items/view-design-material-tab/view-design-material-tab.tsx @@ -0,0 +1,242 @@ +import { PropType, defineComponent } from 'vue'; +import { useNamespace } from '@ibiz-template/vue3-util'; +import { IPanelItem } from '@ibiz/model-core'; +import { ViewDesignMaterialTabController } from './view-design-material-tab.controller'; +import { resource } from '../../global'; +import './view-design-material-tab.scss'; + +export default defineComponent({ + name: 'IBizViewDesignMaterialTab', + props: { + modelData: { + type: Object as PropType, + required: true, + }, + controller: { + type: ViewDesignMaterialTabController, + required: true, + }, + }, + setup() { + const ns = useNamespace('view-design-material-tab'); + + return { + ns, + }; + }, + render() { + const renderSelectIcon = () => ( + + ); + return ( +
+ this.controller.onSelect(value)} + > + {this.controller.state.pageItems.map(page => { + if (page.id === 'controls') { + return ( + { + this.controller.onSelect('$multiple'); + const result = this.controller.state.menuItems.every(item => + this.controller.state.activeItems.includes(item.id), + ); + if (!result) { + this.controller.onSelect('$all'); + } + }} + > + {{ + title: () => { + return ( +
+
+ +
+
+ {page.text} +
+
+ ); + }, + default: () => { + return [ + + {{ + title: () => { + return ( +
+
+ 显示 +
+
+ ); + }, + default: () => { + return [ + +
+ +
+
+ 多栏 +
+
+ {renderSelectIcon()} +
+
, + +
+ +
+
+ 单栏 +
+
+ {renderSelectIcon()} +
+
, + ]; + }, + }} +
, + + this.controller.state.activeItems.includes( + item.id, + ), + ) && this.ns.bm('menu-item', 'active'), + ]} + > +
+ 全部 +
+
+ {renderSelectIcon()} +
+
, + this.controller.state.menuItems.map(item => { + return ( + +
+ {item.text} +
+
+ {renderSelectIcon()} +
+
+ ); + }), + +
+ 组件 +
+
+ {renderSelectIcon()} +
+
, + ]; + }, + }} +
+ ); + } + + return ( + +
+ +
+
{page.text}
+
+ ); + })} +
+
+ ); + }, +}); diff --git a/packages/view-design/src/panel-items/view-design-material-tree/index.ts b/packages/view-design/src/panel-items/view-design-material-tree/index.ts new file mode 100644 index 00000000..002c2a8f --- /dev/null +++ b/packages/view-design/src/panel-items/view-design-material-tree/index.ts @@ -0,0 +1,15 @@ +import { App } from 'vue'; +import { registerPanelItemProvider } from '@ibiz-template/runtime'; +import ViewDesignMaterialTree from './view-design-material-tree'; +import { ViewDesignMaterialTreeProvider } from './view-design-material-tree.provider'; + +export default { + install(app: App): void { + // 注册组件 + app.component('IBizViewDesignMaterialTree', ViewDesignMaterialTree); + registerPanelItemProvider( + 'RAWITEM_VIEW_DESIGN_MATERIAL_TREE', + () => new ViewDesignMaterialTreeProvider(), + ); + }, +}; diff --git a/packages/view-design/src/panel-items/view-design-material-tree/view-design-material-tree.controller.state.ts b/packages/view-design/src/panel-items/view-design-material-tree/view-design-material-tree.controller.state.ts new file mode 100644 index 00000000..61973f5a --- /dev/null +++ b/packages/view-design/src/panel-items/view-design-material-tree/view-design-material-tree.controller.state.ts @@ -0,0 +1,30 @@ +import { PanelItemState } from '@ibiz-template/runtime'; + +export class ViewDesignMaterialTreeState extends PanelItemState { + /** + * 布局模式 + * + * @author zhanghengfeng + * @date 2024-09-25 20:09:52 + * @type {('multiple' | 'single')} + */ + columnMode: 'multiple' | 'single' = 'multiple'; + + /** + * 激活分组 + * + * @author zhanghengfeng + * @date 2024-09-25 20:09:02 + * @type {string[]} + */ + activeGroups: string[] = []; + + /** + * 激活分页 + * + * @author zhanghengfeng + * @date 2024-09-25 20:09:15 + * @type {string} + */ + activePage: string = ''; +} diff --git a/packages/view-design/src/panel-items/view-design-material-tree/view-design-material-tree.controller.ts b/packages/view-design/src/panel-items/view-design-material-tree/view-design-material-tree.controller.ts new file mode 100644 index 00000000..e748fde8 --- /dev/null +++ b/packages/view-design/src/panel-items/view-design-material-tree/view-design-material-tree.controller.ts @@ -0,0 +1,73 @@ +import { PanelItemController, getControl } from '@ibiz-template/runtime'; +import { + IDEEditForm, + IDEFormGroupPanel, + IDEFormPage, + IDEFormTabPanel, +} from '@ibiz/model-core'; +import { clone } from 'ramda'; +import { ViewDesignMaterialTreeState } from './view-design-material-tree.controller.state'; + +export class ViewDesignMaterialTreeController extends PanelItemController { + declare state: ViewDesignMaterialTreeState; + + protected createState(): ViewDesignMaterialTreeState { + return new ViewDesignMaterialTreeState(this.parent?.state); + } + + get materialForm(): IDEEditForm { + return getControl(this.panel.view.model, 'material') as IDEEditForm; + } + + /** + * 素材分组 + * + * @author zhanghengfeng + * @date 2024-09-25 20:09:44 + * @type {IDEFormGroupPanel[]} + */ + materialGroups: IDEFormGroupPanel[] = []; + + protected async onInit(): Promise { + await super.onInit(); + this.initMaterialGroups(); + this.state.activeGroups = this.materialGroups.map(group => group.codeName!); + } + + /** + * 初始化素材分组 + * + * @author zhanghengfeng + * @date 2024-09-25 21:09:04 + */ + initMaterialGroups(): void { + const formPages = this.materialForm.deformPages; + const pages: IDEFormPage[] = []; + formPages?.forEach(formPage => { + pages.push(formPage); + const details = formPage.deformDetails; + details?.forEach(detail => { + if (detail.detailType === 'TABPANEL') { + const tabPages = (detail as IDEFormTabPanel).deformTabPages; + if (tabPages) { + pages.push(...tabPages); + } + } + }); + }); + const materialPage = pages.find(page => page.codeName === 'controls'); + if (materialPage) { + const details = materialPage.deformDetails; + const groups: IDEFormGroupPanel[] = []; + details?.forEach(detail => { + if ( + detail.detailType === 'GROUPPANEL' && + detail.codeName?.toUpperCase() !== 'HIDDENGROUP' + ) { + groups.push(detail); + } + }); + this.materialGroups = clone(groups); + } + } +} diff --git a/packages/view-design/src/panel-items/view-design-material-tree/view-design-material-tree.provider.ts b/packages/view-design/src/panel-items/view-design-material-tree/view-design-material-tree.provider.ts new file mode 100644 index 00000000..fc839654 --- /dev/null +++ b/packages/view-design/src/panel-items/view-design-material-tree/view-design-material-tree.provider.ts @@ -0,0 +1,21 @@ +import { + IPanelItemController, + IPanelItemProvider, + IPanelController, +} from '@ibiz-template/runtime'; +import { IPanelItem } from '@ibiz/model-core'; +import { ViewDesignMaterialTreeController } from './view-design-material-tree.controller'; + +export class ViewDesignMaterialTreeProvider implements IPanelItemProvider { + component = 'IBizViewDesignMaterialTree'; + + async createController( + panelItem: IPanelItem, + panel: IPanelController, + parent?: IPanelItemController, + ): Promise { + const c = new ViewDesignMaterialTreeController(panelItem, panel, parent); + await c.init(); + return c; + } +} diff --git a/packages/view-design/src/panel-items/view-design-material-tree/view-design-material-tree.scss b/packages/view-design/src/panel-items/view-design-material-tree/view-design-material-tree.scss new file mode 100644 index 00000000..fbe6416b --- /dev/null +++ b/packages/view-design/src/panel-items/view-design-material-tree/view-design-material-tree.scss @@ -0,0 +1,12 @@ +@include b(view-design-material-tree) { + width: 100%; + height: 100%; + border: 1px solid getCssVar(color, border); +} + +@include b(view-design-material-tree-caption) { + padding: 0 10px; + font-size: 18px; + line-height: 40px; + cursor: pointer; +} diff --git a/packages/view-design/src/panel-items/view-design-material-tree/view-design-material-tree.tsx b/packages/view-design/src/panel-items/view-design-material-tree/view-design-material-tree.tsx new file mode 100644 index 00000000..c1234fd4 --- /dev/null +++ b/packages/view-design/src/panel-items/view-design-material-tree/view-design-material-tree.tsx @@ -0,0 +1,73 @@ +import { PropType, defineComponent, ref } from 'vue'; +import { useNamespace } from '@ibiz-template/vue3-util'; +import { IPanelItem } from '@ibiz/model-core'; +import { ViewDesignMaterialTreeController } from './view-design-material-tree.controller'; +import './view-design-material-tree.scss'; + +export default defineComponent({ + name: 'IBizViewDesignMaterialTree', + props: { + modelData: { + type: Object as PropType, + required: true, + }, + controller: { + type: ViewDesignMaterialTreeController, + required: true, + }, + }, + setup() { + const ns = useNamespace('view-design-material-tree'); + + // 分割值 + const splitValue = ref(0.5); + + return { + ns, + splitValue, + }; + }, + render() { + if (this.controller.state.activePage === 'viewdesign') { + return ( +
+ + {{ + top: () => { + return ( + + ); + }, + bottom: () => { + return ( +
+
+ {this.controller.panel.view?.state?.caption || ''} +
+ + + +
+ ); + }, + }} +
+
+ ); + } + return ( +
+ +
+ ); + }, +}); -- Gitee