diff --git a/packages/designer/src/components/composition/control-creator.service.ts b/packages/designer/src/components/composition/control-creator.service.ts index d980e6cf1cf4a6473f02348ae6a986405efbfc9e..887861411d6b260ae70dd01451b1b3e4c81881e2 100644 --- a/packages/designer/src/components/composition/control-creator.service.ts +++ b/packages/designer/src/components/composition/control-creator.service.ts @@ -242,6 +242,8 @@ export function useControlCreator(): UseControlCreator { return { setFormFieldProperty, setGridFieldProperty, - createFormGroupWithoutField + createFormGroupWithoutField, + mapControlType2GridFieldType, + setGridFieldFormatter }; } diff --git a/packages/designer/src/components/designer.component.tsx b/packages/designer/src/components/designer.component.tsx index fa26d04287b1427c992314c8501fecd7ae5c6b44..8c3676cdf2853564e52ffe425e0091df93c7df3c 100644 --- a/packages/designer/src/components/designer.component.tsx +++ b/packages/designer/src/components/designer.component.tsx @@ -150,12 +150,22 @@ export default defineComponent({ loadingInstance.value.close(); }); } + + /** 复制部署路径 */ + async function publishMenu() { + const metadataId = useFormSchemaComposition.getFormMetadataBasicInfo()?.id; + const publishUrl = `/platform/common/web/renderer/index.html#/?metadataId=${metadataId}`; + await navigator.clipboard.writeText(publishUrl); + + notifyService.success({ message: '表单部署路径已复制到剪贴板,请前往框架【菜单】页面发布菜单。' }); + } /** * 工具栏配置 */ const designerToolbarItems = [ { id: 'save', text: '保存', onClick: () => saveFormMetadata(false), class: 'btn-primary' }, - { id: 'run', text: '运行', onClick: () => saveFormMetadata(true) } + { id: 'run', text: '运行', onClick: () => saveFormMetadata(true) }, + { id: 'publish', text: '复制部署路径', onClick: () => publishMenu() } ]; /** * 切换设计器视图与代码视图 diff --git a/packages/designer/src/components/types/metadata.ts b/packages/designer/src/components/types/metadata.ts index 41d7973dbf66dc0c017a58495a0a7ecc32bd6473..897993a63841dcbb635f423d311d61abe9af6fec 100644 --- a/packages/designer/src/components/types/metadata.ts +++ b/packages/designer/src/components/types/metadata.ts @@ -253,6 +253,8 @@ export interface UseControlCreator { setFormFieldProperty: (field: FormSchemaEntityField, editorType: string, controlClass: string) => any; setGridFieldProperty: (gridType: string, field: FormSchemaEntityField, metadata: any, needInlineEditor: false) => any; createFormGroupWithoutField: (editorType: string, controlClass: string) => any; + setGridFieldFormatter: (gridFieldType: string, metadata: any, schemaField: any) => void; + mapControlType2GridFieldType: (field: FormSchemaEntityField) => string; } export interface UseFormStateMachine { diff --git a/packages/ui-vue/components/binding-selector/src/components/binding-selector-container.component.tsx b/packages/ui-vue/components/binding-selector/src/components/binding-selector-container.component.tsx index 489af512029ca4f290f48a06ded2f525bfa22432..ef974ddd82030e82ce35115428de3c4e5ddd6262 100644 --- a/packages/ui-vue/components/binding-selector/src/components/binding-selector-container.component.tsx +++ b/packages/ui-vue/components/binding-selector/src/components/binding-selector-container.component.tsx @@ -19,8 +19,9 @@ export default defineComponent({ const treegridRef = ref(); const occupiedFieldset = ref(); const availableFieldTypes = ref(); - const { getTreeDataSource, initTreeData, resolveOccupiedFields, resolveFieldTypesByEditorType } = useFieldSelection(props); + const { getTreeDataSource, initTreeData, resolveOccupiedFields, resolveFieldTypesByEditorType, getOriginalFieldType } = useFieldSelection(props); const columns = ref([]); + const { originalFieldType, isOriginalBindComplexField } = getOriginalFieldType(); onBeforeMount(() => { initTreeData(); dataSource.value = getTreeDataSource(bindingType.value); @@ -32,6 +33,7 @@ export default defineComponent({ columns.value = bindingType.value === 'Form' ? fieldColumns : variableColumns; }); + onMounted(() => { const bindingFieldId = props.editorParams?.componentSchema?.binding?.field; if (bindingFieldId) { @@ -89,9 +91,17 @@ export default defineComponent({ visualData.disabled = true; return visualData; } - // 与当前控件类型不匹配的字段,不可选 const fieldTypeNode = bindingType.value === 'Form' ? visualData.raw.type?.name : visualData.raw.type; - if (fieldTypeNode && availableFieldTypes.value && !availableFieldTypes.value.includes(fieldTypeNode)) { + + // 1、由复杂类型字段切换成其他字段类型:新字段适配控件类型即可 + // 2、原字段被删除或原本没有绑定信息:新字段适配控件类型即可 + if (isOriginalBindComplexField || !originalFieldType) { + if (fieldTypeNode && availableFieldTypes.value?.length && !availableFieldTypes.value.includes(fieldTypeNode)) { + visualData.disabled = true; + return visualData; + } + } else if (originalFieldType && originalFieldType !== fieldTypeNode) { + // 3、简单字段之间的切换:字段类型必须一致 visualData.disabled = true; return visualData; } diff --git a/packages/ui-vue/components/binding-selector/src/composition/use-field-selection.ts b/packages/ui-vue/components/binding-selector/src/composition/use-field-selection.ts index e091e10caf810d089c401516fd1ec37c08faea3e..54ed88eadc1843e0e51a5d45faeeb472c7844c5f 100644 --- a/packages/ui-vue/components/binding-selector/src/composition/use-field-selection.ts +++ b/packages/ui-vue/components/binding-selector/src/composition/use-field-selection.ts @@ -69,7 +69,7 @@ export function useFieldSelection(props: BindingSelectorProps) { // 向视图模型中添加新字段 dgViewModel.addField(currentSelectedField); if (props.editorParams.componentSchema?.editor?.type) { - dgViewModel.changeField(currentSelectedField.id, { editor: props.editorParams.componentSchema.editor.type }); + dgViewModel.changeField(currentSelectedField.id, { editor: currentSelectedField.editor }); } } @@ -277,24 +277,21 @@ export function useFieldSelection(props: BindingSelectorProps) { return { fieldType, fieldTypeName }; } - function checkFieldValiation(bindingData: any, bindingType: string) { - if (!bindingData?.length) { - notifyService.warning({ message: '请先选择数据' }); - return false; - } - - let result = true; - let fieldTypeChanged = false; + /** + * 获取当前已有的绑定类型,以及是否绑定复杂类型字段 + * @returns + */ + function getOriginalFieldType(): { originalFieldType: string, isOriginalBindComplexField: boolean } { const originalComponentSchema = props.editorParams?.componentSchema; const originalBinding = originalComponentSchema?.binding; if (!originalBinding) { - return true; + return { originalFieldType: '', isOriginalBindComplexField: false }; } // 旧绑定字段类型 let originalFieldType; // 旧绑定类型为复杂字段:场景为由简单字段变为关联字段或udt的场景 - let isOldBindComplexField = false; + let isOriginalBindComplexField = false; if (originalBinding.type === FormBindingType.Form) { const schemaField = schemaService.getFieldByID(originalBinding.field); @@ -302,7 +299,7 @@ export function useFieldSelection(props: BindingSelectorProps) { const oldField = getFieldType(schemaField, FormBindingType.Form); originalFieldType = oldField.fieldType; if (schemaField.$type === FormSchemaEntityField$Type.ComplexField) { - isOldBindComplexField = true; + isOriginalBindComplexField = true; } } @@ -312,6 +309,22 @@ export function useFieldSelection(props: BindingSelectorProps) { originalFieldType = varField.type; } } + return { originalFieldType, isOriginalBindComplexField }; + } + function checkFieldValiation(bindingData: any, bindingType: string) { + if (!bindingData?.length) { + notifyService.warning({ message: '请先选择数据' }); + return false; + } + const originalComponentSchema = props.editorParams?.componentSchema; + const originalBinding = originalComponentSchema?.binding; + if (!originalBinding) { + return true; + } + let result = true; + let fieldTypeChanged = false; + + const { originalFieldType, isOriginalBindComplexField } = getOriginalFieldType(); // 新绑定类型 const selectedData = bindingData[0]; @@ -322,11 +335,11 @@ export function useFieldSelection(props: BindingSelectorProps) { // 1、由复杂类型字段切换成其他字段类型:新字段适配控件类型即可 // 2、原字段被删除:新字段适配控件类型即可 - if (isOldBindComplexField || !originalFieldType) { + if (isOriginalBindComplexField || !originalFieldType) { fieldTypeChanged = true; const allowedBindingFieldTypes = resolveFieldTypesByEditorType(props.editorParams?.componentSchema?.editor?.type); - if (allowedBindingFieldTypes && !allowedBindingFieldTypes.includes(newFieldType)) { + if (allowedBindingFieldTypes?.length && !allowedBindingFieldTypes.includes(newFieldType)) { notifyService.warning(`${cannotChangeTo}${newFieldTypeName}${reselect}`); result = false; } @@ -345,6 +358,7 @@ export function useFieldSelection(props: BindingSelectorProps) { } return result; } + return { initTreeData, getTreeDataSource, @@ -353,6 +367,7 @@ export function useFieldSelection(props: BindingSelectorProps) { resolveOccupiedFields, resolveFieldTypesByEditorType, checkFieldValiation, - clearExpresssionConfig + clearExpresssionConfig, + getOriginalFieldType }; } diff --git a/packages/ui-vue/components/data-grid/src/property-config/data-grid-column.property-config.ts b/packages/ui-vue/components/data-grid/src/property-config/data-grid-column.property-config.ts index d09aa30547d6ef8dfafebeea35a9eb48893541ce..c436907b71cfe124ae1a7bd132a93dca373e3f01 100644 --- a/packages/ui-vue/components/data-grid/src/property-config/data-grid-column.property-config.ts +++ b/packages/ui-vue/components/data-grid/src/property-config/data-grid-column.property-config.ts @@ -35,6 +35,7 @@ export class DataGriColumnProperty extends BaseControlProperty { } getBasicPropConfig(propertyData: any) { + const self = this; this.propertyConfig.categories['basic'] = { description: 'Basic Information', title: '基本信息', @@ -49,6 +50,36 @@ export class DataGriColumnProperty extends BaseControlProperty { description: '标题', title: '标题', type: 'string' + }, + binding: { + description: "绑定的表单字段", + title: "绑定", + editor: { + type: "binding-selector", + bindingType: { "enable": false }, + editorParams: { + componentSchema: propertyData, + needSyncToViewModel: true, + viewModelId: this.viewModelId, + designerHostService: this.designerHostService, + disableOccupiedFields: true + }, + textField: 'bindingField' + }, + refreshPanelAfterChanged: true + } + }, + setPropertyRelates(changeObject, prop, paramters: any) { + if (!changeObject) { + return; + } + switch (changeObject && changeObject.propertyID) { + case 'binding': { + self.afterChangeBindingType(propertyData, paramters); + // 刷新实体树 + changeObject.needRefreshEntityTree = true; + break; + } } } }; @@ -322,4 +353,15 @@ export class DataGriColumnProperty extends BaseControlProperty { } return { canChangeControlType, editorTypeList }; } + /** + * 切换绑定后,变更了字段类型,则需要更新列上记录的dataType和格式化属性 + */ + private afterChangeBindingType(propertyData: any, schemaField: any) { + if (!propertyData || !schemaField) { + return; + } + propertyData.dataType = this.controlCreatorUtils.mapControlType2GridFieldType(schemaField); + this.controlCreatorUtils.setGridFieldFormatter(propertyData.dataType, propertyData, schemaField); + } + } diff --git a/packages/ui-vue/components/data-grid/src/schema/data-grid-column.schema.json b/packages/ui-vue/components/data-grid/src/schema/data-grid-column.schema.json index 693f936a4998e1c5d3d95026fcfb72925ada305f..ec078ae4b3da1ac1a85839c71cbbe6447176cb8c 100644 --- a/packages/ui-vue/components/data-grid/src/schema/data-grid-column.schema.json +++ b/packages/ui-vue/components/data-grid/src/schema/data-grid-column.schema.json @@ -15,6 +15,11 @@ "type": "string", "default": "data-grid-column" }, + "binding": { + "description": "", + "type": "object", + "default": {} + }, "allowGrouping": { "description": "", "type": "boolean", diff --git a/packages/ui-vue/components/radio-group/src/property-config/radio-group.property-config.ts b/packages/ui-vue/components/radio-group/src/property-config/radio-group.property-config.ts index 030e0b0c2302b96163191051a76b04dddc812b5c..b82bf8ce7bec0c8b0399c966b76f56a8cf8a3af8 100644 --- a/packages/ui-vue/components/radio-group/src/property-config/radio-group.property-config.ts +++ b/packages/ui-vue/components/radio-group/src/property-config/radio-group.property-config.ts @@ -1,4 +1,7 @@ import { InputBaseProperty } from "../../../property-panel/src/composition/entity/input-base-property"; +import { FormPropertyChangeObject } from "../../../property-panel"; +import { FormSchemaEntityFieldType$Type } from "@farris/ui-vue/components/common"; + export class RadioGroupProperty extends InputBaseProperty { constructor(componentId: string, designerHostService: any) { @@ -91,5 +94,19 @@ export class RadioGroupProperty extends InputBaseProperty { } } } + + /** + * 切换绑定属性 + */ + public changeBindingField(propertyData: any, changeObject: FormPropertyChangeObject, parameters?: any): any { + super.changeBindingField(propertyData, changeObject); + + // 更新枚举数据 + const newBindingField = parameters; + if (propertyData.editor && newBindingField?.type?.$type === FormSchemaEntityFieldType$Type.EnumType) { + propertyData.editor.data = newBindingField.type.enumValues || []; + + } + } }; diff --git a/packages/ui-vue/components/tabs/src/composition/tab-header-dragula.ts b/packages/ui-vue/components/tabs/src/composition/tab-header-dragula.ts index ad6e57d52b38591f00c8a9c9bf855e565dfd21ce..988fa1c90cce1eb5d38afe0a3d1f9f8580bd2a0c 100644 --- a/packages/ui-vue/components/tabs/src/composition/tab-header-dragula.ts +++ b/packages/ui-vue/components/tabs/src/composition/tab-header-dragula.ts @@ -9,7 +9,22 @@ export function tabHeaderItemDragula(designItemContext: DesignerItemContext, tab let dragulaInstance: any; + function hideAddIconDuringDrag(target: DesignerHTMLElement) { + const navItemList = Array.from(target.children); + const addNavItemElement = navItemList.find(navItem => navItem.className.includes('add-nav-item')); + if (addNavItemElement && !addNavItemElement.className?.includes('d-none')) { + addNavItemElement.className += ' d-none'; + } + } + function showAddIconAfterDrop(target: DesignerHTMLElement) { + const navItemList = Array.from(target.children); + const addNavItemElement = navItemList.find(navItem => navItem.className.includes('add-nav-item')); + if (addNavItemElement && addNavItemElement.className?.includes(' d-none')) { + addNavItemElement.className = addNavItemElement.className.replace(' d-none', ''); + } + } function onDrop(element: DesignerHTMLElement, target: DesignerHTMLElement, sibling: DesignerHTMLElement) { + showAddIconAfterDrop(target); const tabSchema = designItemContext.schema; if (!tabSchema?.contents) { return; @@ -76,6 +91,7 @@ export function tabHeaderItemDragula(designItemContext: DesignerItemContext, tab return element.innerText; }, accepts(el: HTMLElement, target: DesignerHTMLElement, source: HTMLElement, sibling: DesignerHTMLElement): boolean { + hideAddIconDuringDrag(target); return sibling && sibling.className.includes('nav-item'); } diff --git a/packages/ui-vue/components/tabs/src/composition/types.ts b/packages/ui-vue/components/tabs/src/composition/types.ts index 34bed3365fb3580b3294e40d015a25169c52e188..9e6ade11d73cedd5b29e4a5b601714bb3f842e7a 100644 --- a/packages/ui-vue/components/tabs/src/composition/types.ts +++ b/packages/ui-vue/components/tabs/src/composition/types.ts @@ -101,7 +101,7 @@ export interface UseDesignTabs { hasInHeadClass: Ref; emitSelectionChange: (tabId: string, componentInstance: DesignerComponentInstance) => void; - resetPositionForSelectedElement: (selectElement) => void; + resetPositionForSelectedElement: (selectElement?: HTMLElement, containerElement?: HTMLElement) => void; setTabsRef: (parentRef) => void; setScrollToSelectedTabFunction: (scrollFunction: any) => void; } diff --git a/packages/ui-vue/components/tabs/src/designer/tab-header-item.design.component.tsx b/packages/ui-vue/components/tabs/src/designer/tab-header-item.design.component.tsx index 9422654ebdb84eca9bcbedcb892c20832f10a889..d2338e4e34385f6d371f67b393057b7258d8eace 100644 --- a/packages/ui-vue/components/tabs/src/designer/tab-header-item.design.component.tsx +++ b/packages/ui-vue/components/tabs/src/designer/tab-header-item.design.component.tsx @@ -92,13 +92,13 @@ export default function ( watch(() => props.toolbarPosition, () => { // 属性面板更新此值,会存在多次更新 nextTick(() => { - resetPositionForSelectedElement(null); + resetPositionForSelectedElement(); }); }); watch(() => props.tabWidth, () => { // 属性面板更新此值,会存在多次更新 nextTick(() => { - resetPositionForSelectedElement(null); + resetPositionForSelectedElement(); }); }); function renderDeleteButton() { diff --git a/packages/ui-vue/components/tabs/src/designer/tab-header.design.component.tsx b/packages/ui-vue/components/tabs/src/designer/tab-header.design.component.tsx index 456b36efd19afab7de137e8c509f6e203154edb9..8352ddf2770e30a5193b08df8d65445a7df050dc 100644 --- a/packages/ui-vue/components/tabs/src/designer/tab-header.design.component.tsx +++ b/packages/ui-vue/components/tabs/src/designer/tab-header.design.component.tsx @@ -79,7 +79,7 @@ export default function ( tabPages.value.map((tabPage: TabPageContext) => renderTabHeaderItem(props, tabPage.props, tabPage, useOnePageComposition, useTabsComposition, componentInstance)) } -