diff --git a/packages/designer/src/components/components/form-designer/components/external-component-panel/components/external-component-selector/external-component-selector.component.tsx b/packages/designer/src/components/components/form-designer/components/external-component-panel/components/external-component-selector/external-component-selector.component.tsx index b8cd2d6bffbb82330a2a9391480ebd67408d9d2f..1ca522ae9972cdbb8ade229482f95d5d408f5d4c 100644 --- a/packages/designer/src/components/components/form-designer/components/external-component-panel/components/external-component-selector/external-component-selector.component.tsx +++ b/packages/designer/src/components/components/form-designer/components/external-component-panel/components/external-component-selector/external-component-selector.component.tsx @@ -1,6 +1,11 @@ import { computed, defineComponent, Ref, ref } from "vue"; import { externalComponentSelectorProps, ExternalComponentSelectorProps } from "./external-component-selector.props"; -import { LookupSchemaRepositoryToken,FormSchemaRepositorySymbol,FSchemaSelector, SchemaItem,FNotifyService } from "@farris/ui-vue/components"; +import { + LookupSchemaRepositoryToken, FormSchemaRepositorySymbol, FSchemaSelector, + SchemaItem, FNotifyService, FMessageBoxService, +} from "@farris/ui-vue/components"; +import { MetadataService } from "../../../../../../../components/composition/metadata.service"; +import { FormMetadaDataDom } from "../../../../../../../components/types"; export default defineComponent({ name: 'FExternalComponentSelector', @@ -8,10 +13,12 @@ export default defineComponent({ emits: ['close', 'submit'], setup(props: ExternalComponentSelectorProps, context) { const selectedComponent: Ref = ref(); - const { externalComponentType,formSchemaUtils } = props; + const { externalComponentType, formSchemaUtils } = props; const notifyService = new FNotifyService(); notifyService.globalConfig = { position: 'top-center' }; + const metadataService = new MetadataService(); + const formBasicInfo = formSchemaUtils.getFormMetadataBasicInfo(); const editorParams = { formBasicInfo }; const viewOptions = [ @@ -20,13 +27,13 @@ export default defineComponent({ ]; const injectSymbolToken = computed(() => { return externalComponentType === 'Independence' ? FormSchemaRepositorySymbol : LookupSchemaRepositoryToken; - }) + }); /** * 选择外部组件 - * @param $event + * @param $event 已选数据 */ - function onSelectionChange($event) { + function onSelectionChange($event: any[]) { if (externalComponentType === 'Lookup') { selectedComponent.value = $event[0].data; } else { @@ -34,18 +41,78 @@ export default defineComponent({ } } - /** - * 校验外部组件数据 - * @returns - */ - async function validate() { - if (!selectedComponent.value) { + function queryControlByPredicate(formMetadataDom: FormMetadaDataDom, predicate: (control: any) => boolean): any { + const controlsToScan = [...formMetadataDom.module.components]; + while (controlsToScan.length) { + const control = controlsToScan.pop(); + if (predicate(control)) { + return control; + } + if (Array.isArray(control?.contents)) { + controlsToScan.push(...control.contents); + } + } + } + + function containsNestedForm(formMetadataDom: FormMetadaDataDom): boolean { + const nestedControlTypes = ['modal', 'external-container']; + const externalComponents = formMetadataDom.module.externalComponents || []; + for (const externalComponent of externalComponents) { + const controlType = externalComponent?.type; + if (nestedControlTypes.includes(controlType)) { + return true; + } + } + const predicate = (control: any) => { + return nestedControlTypes.includes(control?.type); + }; + if (queryControlByPredicate(formMetadataDom, predicate)) { + return true; + } + return false; + } + + async function validateFormSchema(formSchema?: SchemaItem): Promise { + if (!formSchema) { notifyService.warning('请选择表单'); return false; } + return metadataService.getPickMetadata(formBasicInfo.relativePath, formSchema).then((result: any) => { + const metadata = JSON.parse(result?.metadata.content); + const formMetadataDom = metadata.Contents as FormMetadaDataDom; + const targetFormId = formMetadataDom.module.id; + const isCurrentFormSelected = targetFormId === formBasicInfo.id; + if (isCurrentFormSelected || containsNestedForm(formMetadataDom)) { + const message = `表单【${formSchema.name}】嵌套了外部表单,不支持作为弹窗使用。`; + FMessageBoxService.warning(message, ''); + return false; + } + return true; + }).catch((error) => { + FMessageBoxService.error(error?.error || `查询表单【${formSchema.name}】失败`, ''); + return false; + }); + } + + async function validateLookupSchema(lookupSchema?: SchemaItem): Promise { + if (!lookupSchema) { + notifyService.warning('请选择帮助'); + return false; + } return true; } + /** + * 校验外部组件数据 + */ + function validate(): Promise { + if (externalComponentType === 'Independence') { + return validateFormSchema(selectedComponent.value); + } else { + return validateLookupSchema(selectedComponent.value); + } + } + function onSubmit() { context.emit('submit', selectedComponent.value); } @@ -66,10 +133,10 @@ export default defineComponent({ onSelectionChange={onSelectionChange} validateFunction={validate} onSubmit={onSubmit} - onCancel={onCancel} > - - ) - } + onCancel={onCancel} + > + ); + }; } -}) +}); diff --git a/packages/designer/src/components/components/form-designer/components/external-component-panel/components/external-component-selector/external-component-selector.props.ts b/packages/designer/src/components/components/form-designer/components/external-component-panel/components/external-component-selector/external-component-selector.props.ts index 0e11227dae7f66b3c675aa13d333ae41b5412162..7abcaf02b34f4aa0a0ebf7189b047e5e024d2b45 100644 --- a/packages/designer/src/components/components/form-designer/components/external-component-panel/components/external-component-selector/external-component-selector.props.ts +++ b/packages/designer/src/components/components/form-designer/components/external-component-panel/components/external-component-selector/external-component-selector.props.ts @@ -1,13 +1,12 @@ import { ExtractPropTypes, PropType } from "vue"; import { ExternalComponentType } from "../../composition/types"; +import { UseFormSchema } from "../../../../../../../components/types"; export const externalComponentSelectorProps = { id: { type: String, default: '' }, modelValue: { type: Object }, - formSchemaUtils: { type: String, Object: {} }, + formSchemaUtils: { type: Object as PropType, default: {} }, externalComponentType: { type: String as PropType, default: 'Independence' }, - -} as Record; +}; export type ExternalComponentSelectorProps = ExtractPropTypes; - diff --git a/packages/designer/src/components/components/form-designer/components/external-component-panel/components/external-component/external-component.component.tsx b/packages/designer/src/components/components/form-designer/components/external-component-panel/components/external-component/external-component.component.tsx index 00f2ff713e2a35492ab903e73944ade8720d0f53..eb836db9aeea8b9c138ae9812b0f43bbb085ab2a 100644 --- a/packages/designer/src/components/components/form-designer/components/external-component-panel/components/external-component/external-component.component.tsx +++ b/packages/designer/src/components/components/form-designer/components/external-component-panel/components/external-component/external-component.component.tsx @@ -1,4 +1,4 @@ -import { computed, defineComponent, onMounted, ref, watch } from "vue"; +import { computed, defineComponent, ref, watch } from "vue"; import { FNotifyService } from "@farris/ui-vue/components"; import { externalComponentProps, ExternalComponentProps } from "./external-component.props"; import { ExternalComponentSchema } from "../../composition/types"; @@ -12,7 +12,7 @@ export default defineComponent({ notifyService.globalConfig = { position: 'top-center' }; /** 外部组件的Schema */ const externalComponentSchema: ExternalComponentSchema = props.modelValue; - const { name, componentType } = externalComponentSchema; + const { componentType } = externalComponentSchema; /** 当前组件是否选中 */ const isSelected = ref(props.isSelected); @@ -24,11 +24,12 @@ export default defineComponent({ 'position': isSelected.value ? 'relative' : '', 'display': isSelected.value ? 'flex' : 'none' }; - }) + }); + /** 图片路径 */ const imagePath = computed(() => { return componentType === 'Independence' ? './assets/images/independence-component.svg' : './assets/images/lookup-component.svg'; - }) + }); /** * 点击外部组件 @@ -44,9 +45,6 @@ export default defineComponent({ context.emit('delete', externalComponentSchema); } - onMounted(() => { - }); - watch( () => props.isSelected, () => { @@ -71,6 +69,8 @@ export default defineComponent({ } function renderHidenComponent() { + const { name } = externalComponentSchema; + return (
-
{name}
+
{name}
@@ -95,7 +95,7 @@ export default defineComponent({ {renderHidenComponent()} ); - } + }; } -}) +}); diff --git a/packages/designer/src/components/components/form-designer/components/external-component-panel/composition/drag-resolve.tsx b/packages/designer/src/components/components/form-designer/components/external-component-panel/composition/drag-resolve.tsx index 3fcaca3061aa956043a5535c998041ea7dd11917..72d9daef76644c0431d4a55ce5b6c3ea7433b8f1 100644 --- a/packages/designer/src/components/components/form-designer/components/external-component-panel/composition/drag-resolve.tsx +++ b/packages/designer/src/components/components/form-designer/components/external-component-panel/composition/drag-resolve.tsx @@ -38,10 +38,14 @@ export function dragResolveService(externalComponentComposition: UseExternalComp function renderComponentSelector(externalComponentType: ExternalComponentType) { return () => { return ( - { onSubmit(selectedComponent, externalComponentType) }} - onClose={closeModal} externalComponentType={externalComponentType}> - ) - } + onSubmit(selectedComponent, externalComponentType)} + onClose={closeModal} + externalComponentType={externalComponentType} + > + ); + }; } /** diff --git a/packages/designer/src/components/components/form-designer/components/external-component-panel/composition/use-external-component-property.ts b/packages/designer/src/components/components/form-designer/components/external-component-panel/composition/use-external-component-property.ts index 6319e284e940c7dd2263066ce39b4b10aaf4c11e..1e5864660e967497d944fe2386795ff90bac5520 100644 --- a/packages/designer/src/components/components/form-designer/components/external-component-panel/composition/use-external-component-property.ts +++ b/packages/designer/src/components/components/form-designer/components/external-component-panel/composition/use-external-component-property.ts @@ -4,18 +4,15 @@ import { ModalProperty } from "@farris/ui-vue/components"; export function useExternalComponentProperty(designerHostService: DesignerHostService, propertyData: ExternalComponentSchema) { const componentId = 'root-component'; - const externalLookupPropertyConfig = new ExternalLookupPropertyConfig(componentId,designerHostService); - let convertedPropertyData ; + const externalLookupPropertyConfig = new ExternalLookupPropertyConfig(componentId, designerHostService); + let convertedPropertyData: any; /** * 获取弹窗的属性 - * @param propertyData - * @returns */ function getModalPropertyConfig(): any { - const propertyConfig = new ModalProperty(componentId,designerHostService); + const propertyConfig = new ModalProperty(componentId, designerHostService); return propertyConfig.getPropertyConfig(propertyData); - } function getBasePropertyConfig(): any { @@ -31,33 +28,39 @@ export function useExternalComponentProperty(designerHostService: DesignerHostSe name: { title: '名称', type: 'string', - readonly: true + readonly: false } } }; } function getConvertedPropertyData(): any { - if(convertedPropertyData){ + if (convertedPropertyData) { return convertedPropertyData; } if (propertyData.type === 'lookup') { convertedPropertyData = { id: propertyData.id, - name: propertyData.name, + get name(): string { + return propertyData.name; + }, + set name(value: string) { + propertyData.name = value; + }, type: 'form-group', editor: propertyData }; externalLookupPropertyConfig.events.forEach(event => { convertedPropertyData[event.label] = propertyData[event.label]; }); - }else{ + } else { convertedPropertyData = propertyData; } return convertedPropertyData; } + function getPropConfig(componentId: string) { let propertyConfig: any; @@ -69,14 +72,12 @@ export function useExternalComponentProperty(designerHostService: DesignerHostSe } const basic = getBasePropertyConfig(); - propertyConfig.categories = { basic, ...propertyConfig.categories } + propertyConfig.categories = { basic, ...propertyConfig.categories }; return propertyConfig; - } return { getPropConfig, getConvertedPropertyData }; - } diff --git a/packages/designer/src/components/components/form-designer/components/external-component-panel/composition/use-external-component.ts b/packages/designer/src/components/components/form-designer/components/external-component-panel/composition/use-external-component.ts index 7058a4e1ed9ec5391e84361cd813df59a2d5b481..fed8f4e9beac478f7ade1b51b78ed95224652b53 100644 --- a/packages/designer/src/components/components/form-designer/components/external-component-panel/composition/use-external-component.ts +++ b/packages/designer/src/components/components/form-designer/components/external-component-panel/composition/use-external-component.ts @@ -21,17 +21,42 @@ export default function (): UseExternalComponent { return externalComponents; } + function isPropertyValueUnique(propertyName: string, propertyValue: any): boolean { + return externalComponents.value.findIndex((externalComponent) => { + return externalComponent[propertyName] === propertyValue; + }) < 0; + } + + function getNewIdAndName(idPrefix: string, namePrefix: string): { newId: string, newName: string } { + const DEFAULT_PREFIX = 'external-component'; + idPrefix = idPrefix || DEFAULT_PREFIX; + namePrefix = namePrefix || DEFAULT_PREFIX; + const needNameSuffix = isPropertyValueUnique('name', namePrefix) === false; + while (true) { + const randomSuffix = Math.random().toString(36).slice(2, 6); + const newId = `${idPrefix}-${randomSuffix}`; + const newName = needNameSuffix ? `${namePrefix}-${randomSuffix}` : namePrefix; + if (isPropertyValueUnique('id', newId) === false) { + continue; + } + if (needNameSuffix && isPropertyValueUnique('name', newName) === false) { + continue; + } + return { newId, newName }; + } + } + /** * 获取帮助属性 - * @param component - * @returns + * @param component 帮助元数据 */ function getLookupPropertyValue(component: SchemaItem): Promise { const formInfo = formSchemaUtils.getFormMetadataBasicInfo(); const helpPropertyValue = getSchemaByType('lookup') || {}; + helpPropertyValue.enableToSelect = false; const propertyValue = { editor: helpPropertyValue - } + }; return metadataService.getPickMetadata(formInfo.relativePath, component) .then((result: any) => { const metadataContent = JSON.parse(result?.metadata.content); @@ -40,19 +65,27 @@ export default function (): UseExternalComponent { code: metadataContent.code, name: metadataContent.name, metadataContent - } + }; lookupDataSourceConverter.convertTo(propertyValue, 'dataSource', [metadata]); helpPropertyValue.dialog ??= {}; helpPropertyValue.dialog.title = metadataContent.name; - propertyValue.editor.name = metadataContent.name; + const { newId, newName } = getNewIdAndName(metadataContent.code, metadataContent.name); + propertyValue.editor.id = newId; + propertyValue.editor.name = newName; return propertyValue; }); } + /** + * 获取弹窗属性 + * @param component 表单元数据 + */ function getModalPropertyValue(component: SchemaItem): any { const propertyValue = getSchemaByType('modal') || {}; + const { newId, newName } = getNewIdAndName(component.code, component.name); + propertyValue.id = newId; + propertyValue.name = newName; propertyValue.title = component.name; - propertyValue.name = component.name; const idSuffix = idService.uuid().slice(5, 10); const externalContainerId = `external-container-${idSuffix}`; @@ -72,15 +105,14 @@ export default function (): UseExternalComponent { }, onCommunication: "" } - ] - + ]; return propertyValue; } /** * 添加外部组件 - * @param component - * @param externalComponentType + * @param component 目标元数据 + * @param externalComponentType 外部组件类型 */ function addComponent(component: SchemaItem, externalComponentType: ExternalComponentType) { const idSuffix = idService.uuid().slice(5, 10); @@ -99,7 +131,7 @@ export default function (): UseExternalComponent { /** * 删除外部组件 - * @param externalComponentSchema + * @param externalComponentSchema 待删除的外部组件 */ function deleteComponent(externalComponentSchema: ExternalComponentSchema) { const componentIndex = externalComponents.value.findIndex(component => component.id === externalComponentSchema.id); @@ -113,5 +145,5 @@ export default function (): UseExternalComponent { getComponents, addComponent, deleteComponent, - } + }; } diff --git a/packages/designer/src/components/components/form-designer/components/external-component-panel/external-component-panel.scss b/packages/designer/src/components/components/form-designer/components/external-component-panel/external-component-panel.scss index 1abe5098a21a8f44bc7eafdaa036a57ed2e8bb01..64eb6ee16301650ebebc8f522b72bedb2d767098 100644 --- a/packages/designer/src/components/components/form-designer/components/external-component-panel/external-component-panel.scss +++ b/packages/designer/src/components/components/form-designer/components/external-component-panel/external-component-panel.scss @@ -68,6 +68,12 @@ display: flex; align-items: center; } +.f-external-component-template-text-container div { + margin: auto; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} /* 左侧外部组件 */ .f-external-component-container { diff --git a/packages/designer/src/components/components/form-designer/form-designer.component.tsx b/packages/designer/src/components/components/form-designer/form-designer.component.tsx index 6b01a9e4d2720db836bb9b5915a014f297f36ebd..de638d771394d54f5d5ed9ab0805e9a752d51f67 100644 --- a/packages/designer/src/components/components/form-designer/form-designer.component.tsx +++ b/packages/designer/src/components/components/form-designer/form-designer.component.tsx @@ -1,4 +1,4 @@ -import { Ref, computed, defineComponent, inject, onMounted, provide, ref, nextTick, onUnmounted } from "vue"; +import { computed, defineComponent, inject, onMounted, provide, ref, nextTick, onUnmounted } from "vue"; import { ComponentSchema, DesignerComponentInstance, FDesignerCanvas, FTabs, FTabPage, propertyConfigSchemaMapForDesigner, FSplitter, FSplitterPane, FDesignerToolbox, FPropertyPanel, FDesignerOutline } from "@farris/ui-vue/components"; import { FormDesignerProps, formDesignerProps } from "./form-designer.props"; import { useComponentSchemaService } from '../../composition/component-schema.service'; @@ -13,6 +13,7 @@ import FExternalComponentPanel from "./components/external-component-panel/exter import { useExternalComponentProperty } from "./components/external-component-panel/composition/use-external-component-property"; import { ExternalComponentSchema } from "./components/external-component-panel/composition/types"; import { UseFormCommandService } from "../../../components/types/command"; +import { DesignerMode } from "../../types/designer-context"; export default defineComponent({ name: 'FFormDesigner', @@ -155,6 +156,17 @@ export default defineComponent({ propertyConfigSchemaMapForDesigner['Module'] = modulePropertyConfig; + const shouldRenderExternalComponentPanel = designerMode !== DesignerMode.PC_RTC; + + function renderExternalComponentPanel() { + return shouldRenderExternalComponentPanel && ( + + + ); + } + /** * 切换不同的页面 * @param selectionNode @@ -192,7 +204,6 @@ export default defineComponent({ // 切换了页面 changePageComponent(selectionNode); - } function reloadPropertyPanel() { @@ -274,10 +285,7 @@ export default defineComponent({ components={componentsToRegister} canvasMode={designerMode} > - - + {renderExternalComponentPanel()} { newPropertyData[event.label] = propertyData[event.label]; - }) + }); } }; } - }