diff --git a/packages/view-design/src/components/panel-item-container/panel-item-container.scss b/packages/view-design/src/components/panel-item-container/panel-item-container.scss index 965b1eabf3b5c56606a938d6bcd4ea5869499f40..80d5488bda0449d7d572f35a3c4ae9efb73d9f57 100644 --- a/packages/view-design/src/components/panel-item-container/panel-item-container.scss +++ b/packages/view-design/src/components/panel-item-container/panel-item-container.scss @@ -14,7 +14,7 @@ } } - .CONTAINER { + &.CONTAINER { overflow: hidden; } diff --git a/packages/view-design/src/components/panel-item-widgets/hooks/use-layout-item/use-layout-item.ts b/packages/view-design/src/components/panel-item-widgets/hooks/use-layout-item/use-layout-item.ts index 3d7972c1ccf9f1ec850216251496f604400b5a9f..2a4915340303949ef7c89446fd855c4bba415666 100644 --- a/packages/view-design/src/components/panel-item-widgets/hooks/use-layout-item/use-layout-item.ts +++ b/packages/view-design/src/components/panel-item-widgets/hooks/use-layout-item/use-layout-item.ts @@ -45,7 +45,7 @@ export function useLayoutItem(props, ctx) { // 当前实体 if (data && (data as IData).srfdecodename === 'PSSysViewPanelItem') { if (data) { - if (!props.data && !(data as IData).ppssysviewpanelitemid) { + if (props.data && !(data as IData).ppssysviewpanelitemid) { loadChildren(); } else if ( props.data && diff --git a/packages/view-design/src/components/panel-item-widgets/hooks/use-sortable-layout/use-sortable-layout.ts b/packages/view-design/src/components/panel-item-widgets/hooks/use-sortable-layout/use-sortable-layout.ts index dcf0bb3c3ef72511bbf48bdc2e07528160daf70c..e39805c87a458a72bbf98e9dd22d011641d389e8 100644 --- a/packages/view-design/src/components/panel-item-widgets/hooks/use-sortable-layout/use-sortable-layout.ts +++ b/packages/view-design/src/components/panel-item-widgets/hooks/use-sortable-layout/use-sortable-layout.ts @@ -23,7 +23,7 @@ export function useSortableLayout(props, ctx) { // 拖拽开始 const start = (evt: SortableEvent) => { dragging.from = props.data; - const data = children[evt.oldIndex!]; + const data = children.value[evt.oldIndex!]; dragging.item = data; }; @@ -100,7 +100,7 @@ export function useSortableLayout(props, ctx) { // 更新项 const update = async (_evt: SortableEvent) => { dragging.to = props.data; - const data = children.value[_evt.oldIndex!]; + const data = children.value[_evt.newIndex!]; const provider = getProvider(data.type) as PanelLayoutProvider; const newData = await provider.moveItem( props.context, @@ -112,15 +112,13 @@ export function useSortableLayout(props, ctx) { true, ); if (newData) { - children.value.splice(_evt.oldIndex!, 1); + children.value.splice(_evt.newIndex!, 1); children.value.splice( _evt.newIndex!, 0, newData as PanelLayoutDataProvider, ); - sortChildren( - _evt.oldIndex! < _evt.newIndex! ? _evt.oldIndex! : _evt.newIndex!, - ); + sortChildren(0); } }; @@ -215,5 +213,6 @@ export function useSortableLayout(props, ctx) { removeNode, getLayoutFlexStyle, getDraggableClass, + getProvider, }; } diff --git a/packages/view-design/src/components/panel-item-widgets/widgets/layout-panel/grid-layout/grid-layout.scss b/packages/view-design/src/components/panel-item-widgets/widgets/layout-panel/grid-layout/grid-layout.scss index c4456a4cfa87ef7cc1bea1ed3fe2f7ab40f43e33..76a34ac2d222aaed4e6662535452e08eb7efb348 100644 --- a/packages/view-design/src/components/panel-item-widgets/widgets/layout-panel/grid-layout/grid-layout.scss +++ b/packages/view-design/src/components/panel-item-widgets/widgets/layout-panel/grid-layout/grid-layout.scss @@ -7,8 +7,4 @@ flex-flow: column nowrap; min-height: 30px; padding-bottom: 30px; - - &.flex-layout { - flex-wrap: wrap; - } } diff --git a/packages/view-design/src/components/panel-item-widgets/widgets/panel-design-tab-panel/panel-design-tab-panel.scss b/packages/view-design/src/components/panel-item-widgets/widgets/panel-design-tab-panel/panel-design-tab-panel.scss index ef34058ce1a2b9b7523b412f29351597cce96215..b4462dd97227123c676764e9aac513a6425e27ac 100644 --- a/packages/view-design/src/components/panel-item-widgets/widgets/panel-design-tab-panel/panel-design-tab-panel.scss +++ b/packages/view-design/src/components/panel-item-widgets/widgets/panel-design-tab-panel/panel-design-tab-panel.scss @@ -1,4 +1,9 @@ /* stylelint-disable selector-class-pattern */ +@include b(panel-design-tab-panel) { + width: 100%; + height: 100%; +} + @include b(panel-design-tab-panel-draggable) { display: flex; height: 43px; @@ -28,7 +33,6 @@ @include when(active) { color: #1890ff; background-color: #fff; - border-color: getCssVar(color, border); } } @@ -79,6 +83,13 @@ border-radius: 4px; box-shadow: 0 2px 8px rgb(0 0 0 / 15%); + .el-popper__arrow { + display: block; + + --el-border-color-light: #{getCssVar(color, bg, 3)}; + --el-bg-color-overlay: #{getCssVar(color, bg, 3)}; + } + .el-popconfirm__main { padding-top: 4px; padding-bottom: 12px; @@ -104,7 +115,7 @@ .el-button.el-button.is-text { color: rgb(0 0 0 / 65%); background-color: transparent; - border:1px solid #d9d9d9; + border: 1px solid #d9d9d9; --el-border-radius-base: 4px; diff --git a/packages/view-design/src/components/panel-item-widgets/widgets/panel-scroll-container/panel-scroll-container-item/panel-scroll-container-item.tsx b/packages/view-design/src/components/panel-item-widgets/widgets/panel-scroll-container/panel-scroll-container-item/panel-scroll-container-item.tsx index f063d73f08e7990ca82e42780f8379269f728a3a..ad07fd521315025e706adcb3160e1b22089669e0 100644 --- a/packages/view-design/src/components/panel-item-widgets/widgets/panel-scroll-container/panel-scroll-container-item/panel-scroll-container-item.tsx +++ b/packages/view-design/src/components/panel-item-widgets/widgets/panel-scroll-container/panel-scroll-container-item/panel-scroll-container-item.tsx @@ -24,6 +24,7 @@ export default defineComponent({ }, pos: { type: String, + default: 'main', }, }, setup(props, ctx) { diff --git a/packages/view-design/src/components/panel-item-widgets/widgets/panel-scroll-container/panel-scroll-container.scss b/packages/view-design/src/components/panel-item-widgets/widgets/panel-scroll-container/panel-scroll-container.scss index 2f75a8e5b50d0ac633af7be59796c3431ab02fac..005456e9d7608fb22b934f67f7657b174a493f25 100644 --- a/packages/view-design/src/components/panel-item-widgets/widgets/panel-scroll-container/panel-scroll-container.scss +++ b/packages/view-design/src/components/panel-item-widgets/widgets/panel-scroll-container/panel-scroll-container.scss @@ -5,6 +5,7 @@ } @include b(panel-scroll-container-title) { + flex: 0 0 auto; height: 20px; padding-right: 10px; overflow: hidden; diff --git a/packages/view-design/src/components/panel-item-widgets/widgets/panel-scroll-container/panel-scroll-container.tsx b/packages/view-design/src/components/panel-item-widgets/widgets/panel-scroll-container/panel-scroll-container.tsx index 6f747378efccf682fac4bea6ccf3c004988ef08e..ca236fb7b7c5f27f3f10428ee0ec1d9a00c40637 100644 --- a/packages/view-design/src/components/panel-item-widgets/widgets/panel-scroll-container/panel-scroll-container.tsx +++ b/packages/view-design/src/components/panel-item-widgets/widgets/panel-scroll-container/panel-scroll-container.tsx @@ -1,4 +1,4 @@ -import { PropType, computed, defineComponent, ref, watch } from 'vue'; +import { PropType, computed, defineComponent, inject, ref, watch } from 'vue'; import { useNamespace } from '@ibiz-template/vue3-util'; import { AppDataEntity } from '@ibiz-template/runtime'; import { @@ -6,7 +6,8 @@ import { PanelLayoutDataProvider, } from '../../../../providers'; import { resource } from '../../../../global'; -import { useLayoutItem } from '../../hooks'; +import { useSortableLayout } from '../../hooks'; +import { PanelDesignContext } from '../../../../utils'; import './panel-scroll-container.scss'; // 滚动条容器 @@ -30,7 +31,12 @@ export default defineComponent({ setup(props, ctx) { const ns = useNamespace('panel-scroll-container'); - const { children } = useLayoutItem(props, ctx); + const panelDesignContext = inject( + 'panelDesignContext', + ) as PanelDesignContext; + const select = panelDesignContext.select; + + const { children, getProvider, setSelect } = useSortableLayout(props, ctx); // 顶部数据 const top = ref>(); @@ -110,17 +116,157 @@ export default defineComponent({ return style; }; + // 新建布局 + const createdChild = async (type: string) => { + const provider = getProvider(type); + const params: IData = { predefinedtype: type, itemtype: 'CONTAINER' }; + switch (type) { + case 'CONTAINER_SCROLL_HEADER': + params.bl_pos = 'NORTH'; + params.heightmode = 'PX'; + params.height = '80'; + break; + case 'CONTAINER_SCROLL_BOTTOM': + params.bl_pos = 'SOUTH'; + params.heightmode = 'PX'; + params.height = '80'; + break; + case 'CONTAINER_SCROLL_LEFT': + params.bl_pos = 'WEST'; + params.widthmode = 'PX'; + params.width = '200'; + break; + case 'CONTAINER_SCROLL_RIGHT': + params.bl_pos = 'EAST'; + params.widthmode = 'PX'; + params.width = '200'; + break; + case 'CONTAINER_SCROLL_MAIN': + params.bl_pos = 'CENTER'; + break; + default: + break; + } + const data = await provider.createItem(props.context, props.data, params); + if (data) { + setChildData(data as PanelLayoutDataProvider); + } + }; + + // 动态计算滚动容器main区域的宽度 + const updateChildData = (provider: PanelContentProvider) => { + let mainWidth: number = 100; + if ( + left.value && + Object.is(left.value.widthMode, 'PERCENTAGE') && + left.value.width + ) { + mainWidth = Number(mainWidth - left.value.width); + } + if ( + right.value && + Object.is(right.value.widthMode, 'PERCENTAGE') && + right.value.width + ) { + mainWidth = Number(mainWidth - right.value.width); + } + if (main.value) { + main.value.width = mainWidth; + provider.updateItem(props.context, props.data, main.value); + } + }; + + // 删除布局 + const removeChild = async (type: string) => { + const provider = getProvider(type); + let data: IData | undefined; + switch (type) { + case 'CONTAINER_SCROLL_HEADER': + data = top.value; + if (data && data.data) { + data.data.heightmode = ''; + data.data.height = ''; + } + top.value = undefined; + break; + case 'CONTAINER_SCROLL_BOTTOM': + data = bottom.value; + if (data && data.data) { + data.data.heightmode = ''; + data.data.height = ''; + } + bottom.value = undefined; + break; + case 'CONTAINER_SCROLL_LEFT': + data = left.value; + if (data && data.data) { + data.data.widthmode = ''; + data.data.width = ''; + } + left.value = undefined; + break; + case 'CONTAINER_SCROLL_RIGHT': + data = right.value; + if (data && data.data) { + data.data.widthmode = ''; + data.data.width = ''; + } + right.value = undefined; + break; + case 'CONTAINER_SCROLL_MAIN': + data = main.value; + main.value = undefined; + break; + default: + break; + } + if (!data) { + return; + } + const result = await provider.removeItem( + props.context, + props.data, + data as PanelLayoutDataProvider, + ); + if (result) { + if (select.data?.id === data.id) { + setSelect(props.data); + } + } + updateChildData(provider); + }; + // 面板布局过滤 const onLayoutChange = (e: MouseEvent, type: string) => { e.stopPropagation(); switch (type) { case 'CONTAINER_SCROLL_HEADER': + if (existTop.value) { + removeChild(type); + } else { + createdChild(type); + } break; case 'CONTAINER_SCROLL_RIGHT': + if (existRight.value) { + removeChild(type); + } else { + createdChild(type); + } break; case 'CONTAINER_SCROLL_BOTTOM': + if (existBottom.value) { + removeChild(type); + } else { + createdChild(type); + } break; case 'CONTAINER_SCROLL_LEFT': + if (existLeft.value) { + removeChild(type); + } else { + createdChild(type); + } break; default: break; @@ -133,6 +279,9 @@ export default defineComponent({ children.value.forEach(item => setChildData(item as PanelLayoutDataProvider), ); + if (!existMain.value) { + createdChild('CONTAINER_SCROLL_MAIN'); + } }, ); diff --git a/packages/view-design/src/components/panel-item-widgets/widgets/split-layout-container/split-layout-container.scss b/packages/view-design/src/components/panel-item-widgets/widgets/split-layout-container/split-layout-container.scss index 6c5fc3bd01b863f8fd51f53824494f5b5974da9c..bf87cee3c7a5bfa1def4d5a3afd1cc8dc44ed8a3 100644 --- a/packages/view-design/src/components/panel-item-widgets/widgets/split-layout-container/split-layout-container.scss +++ b/packages/view-design/src/components/panel-item-widgets/widgets/split-layout-container/split-layout-container.scss @@ -40,13 +40,6 @@ text-align: center; } - .el-radio-button { - --el-font-size-base: 12px; - --el-radio-button-checked-bg-color: #ffe9c0; - --el-radio-button-checked-text-color: #c0c4cc; - --el-radio-button-checked-border-color: #ffa500; - } - @include m(row) { flex-direction: row; padding: 8px 0; @@ -56,21 +49,50 @@ margin-left: 3px; } - .el-radio-group { - flex-direction: row; + .#{bem('split-layout-container-trigger-con-radio-group')} { + display: flex; + align-items: center; margin-left: 4px; - .el-radio-button__inner { - padding-top: 9px; - padding-bottom: 9px; - } + .#{bem('split-layout-container-trigger-con-radio-group-item')} { + display: flex; + align-items: center; + justify-content: center; + height: 32px; + padding: 0 15px; + cursor: pointer; + border: 1px solid #dcdfe6; + + &:first-child { + align-items: flex-start; + padding-top: 4px; + border-right: none; + border-radius: 4px 0 0 4px; + } - .el-radio-button__original-radio:checked + .el-radio-button__inner { - color: var(--el-radio-button-checked-text-color); - background-color: var(--el-radio-button-checked-bg-color); - border-color: var(--el-radio-button-checked-border-color); - border-bottom: var(--el-border); - border-bottom-color: var(--el-radio-button-checked-border-color); + &:last-child { + border-radius: 0 4px 4px 0; + } + + &:hover { + color: #409eff; + } + + @include when(disabled) { + color: #c0c4cc; + border-color: #ebeef5; + } + + @include when(active) { + color: #c0c4cc; + background-color: #ffe9c0; + border: 1px solid orange; + &:first-child { + + .#{bem('split-layout-container-trigger-con-radio-group-item')} { + border-left: none; + } + } + } } } } @@ -80,43 +102,56 @@ padding: 4px 8px; .el-input { - width: 45px; + width: 42px; margin-top: 3px; } - .el-radio-group { + .#{bem('split-layout-container-trigger-con-radio-group')} { + display: flex; flex-direction: column; + align-items: center; + width: 100%; margin-top: 4px; - .el-radio-button:first-child { - .el-radio-button__inner { - width: 45px; - border-right: var(--el-border); - border-radius: var(--el-border-radius-base) - var(--el-border-radius-base) 0 0; - box-shadow: none; + .#{bem('split-layout-container-trigger-con-radio-group-item')} { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 32px; + cursor: pointer; + border: 1px solid #dcdfe6; + + &:first-child { + align-items: flex-start; + padding-top: 4px; + border-bottom: none; + border-radius: 4px 4px 0 0; } - .el-radio-button__original-radio:checked + .el-radio-button__inner { - color: var(--el-radio-button-checked-text-color); - background-color: var(--el-radio-button-checked-bg-color); - border-color: var(--el-radio-button-checked-border-color); - border-bottom: var(--el-border); - border-bottom-color: var(--el-radio-button-checked-border-color); + + &:last-child { + border-radius: 0 0 4px 4px; } - } - .el-radio-button:last-child { - .el-radio-button__inner { - width: 45px; - border-left: var(--el-border); - border-radius: 0 0 var(--el-border-radius-base) - var(--el-border-radius-base); - box-shadow: none; + &:hover { + color: #409eff; } - .el-radio-button__original-radio:checked + .el-radio-button__inner { - color: var(--el-radio-button-checked-text-color); - background-color: var(--el-radio-button-checked-bg-color); - border-color: var(--el-radio-button-checked-border-color); + + @include when(disabled) { + color: #c0c4cc; + border-color: #ebeef5; + } + + @include when(active) { + color: #c0c4cc; + background-color: #ffe9c0; + border: 1px solid orange; + + &:first-child { + + .#{bem('split-layout-container-trigger-con-radio-group-item')} { + border-top: none; + } + } } } } diff --git a/packages/view-design/src/components/panel-item-widgets/widgets/split-layout-container/split-layout-container.tsx b/packages/view-design/src/components/panel-item-widgets/widgets/split-layout-container/split-layout-container.tsx index 79fba3e9b1970535dfa59cd883be56d4fda50e8a..d94d96f9b006fca5e6da23b6344d0d7b56073c68 100644 --- a/packages/view-design/src/components/panel-item-widgets/widgets/split-layout-container/split-layout-container.tsx +++ b/packages/view-design/src/components/panel-item-widgets/widgets/split-layout-container/split-layout-container.tsx @@ -76,6 +76,8 @@ export default defineComponent({ } else { splitStyle.value = `width:${splitValue.value}%`; } + } else { + splitStyle.value = ''; } } else if (splitValue.value) { if (label === 'PX') { @@ -83,6 +85,28 @@ export default defineComponent({ } else { splitStyle.value = `height:${splitValue.value}%`; } + } else { + splitStyle.value = ''; + } + }; + + // 创建子容器 + const generateChild = async () => { + const defaultChildParam = { + itemtype: 'CONTAINER', + showcaption: 0, + layoutmode: 'TABLE_24COL', + playoutmode: 'SIMPLEFLEX', + caption: '面板容器', + }; + const items = [{ ...defaultChildParam }, { ...defaultChildParam }]; + if (props.provider.createBatch) { + const results = await props.provider.createBatch( + props.context, + props.data, + items, + ); + children.value = results as PanelLayoutDataProvider[]; } }; @@ -90,6 +114,7 @@ export default defineComponent({ () => children.value, () => { if (children.value.length === 0) { + generateChild(); return; } if (splitMode.value === 'horizontal') { @@ -113,6 +138,44 @@ export default defineComponent({ }, ); + // 更新容器分割信息 + const update = (type: string, data?: string) => { + const child = children.value[0] as PanelLayoutDataProvider; + if (!child || !child.data) { + return; + } + if (type === 'split') { + if (splitMode.value === 'horizontal') { + Object.assign(child.data, { width: Number(data) || null }); + } else { + Object.assign(child.data, { height: Number(data) || null }); + } + } else if (type === 'select') { + if (splitMode.value === 'horizontal') { + Object.assign(child.data, { widthmode: data }); + } else { + Object.assign(child.data, { heightmode: data }); + } + } + getStyle(); + props.provider.updateItem(props.context, props.data, child); + }; + + // 设置分割值 + const setSplitValue = (value: string) => { + let data = value; + if (value === '0') { + data = ''; + splitValue.value = ''; + } + update('split', data); + }; + + // 选择分割值单位 + const changeSelectValue = (value: string) => { + update('select', selectLabelMap.get(value)); + }; + return { ns, splitMode, @@ -122,6 +185,8 @@ export default defineComponent({ selectValue, selectOption, children, + setSplitValue, + changeSelectValue, }; }, render() { @@ -162,12 +227,26 @@ export default defineComponent({ class={this.ns.b('trigger-con-input')} v-model={this.splitValue} disabled={!this.active} + onChange={this.setSplitValue} > - +
{this.selectOption.map(item => { - return ; + return ( +
{ + this.changeSelectValue(item.value); + }} + > + {item.value} +
+ ); })} - +
{this.children[1] && ( | null, item: PanelDataProvider, ): Promise | null> { - throw new Error('method not implement'); - // const res = await this.service.UpdateTemp(context, item.data); - // if (res.ok) { - // item.data = res.data; - // return item; - // } - // Vue.prototype.$throw(res.statusText); - - // return item; + const res = await this.service.updateTemp(context, item.data); + if (res.ok && res.data) { + this.sendDataChangeMessage('update', res.data as AppDataEntity); + return new PanelItemDataProvider(res.data as AppDataEntity); + } + throw new RuntimeError('项更新失败'); } /** @@ -340,7 +337,7 @@ export class PanelItemProviderImpl implements PanelItemProvider { newIndex: number, ): Promise | null> { const data = item.data; - if (!to) { + if (!to || to.type === 'PSDEVIEWBASE') { data.ppssysviewpanelitemid = undefined; data.ppssysviewpanelitemname = undefined; } else if ((!from && to) || from.id !== to.id) {