diff --git a/packages/code-editor/components/code-editor/src/composition/editor-core/file.ts b/packages/code-editor/components/code-editor/src/composition/editor-core/file.ts index 687cd2140d315054d58375be82e2630af4443b2e..aae825860512d92b1988df033814ec21bf165366 100644 --- a/packages/code-editor/components/code-editor/src/composition/editor-core/file.ts +++ b/packages/code-editor/components/code-editor/src/composition/editor-core/file.ts @@ -201,7 +201,10 @@ export abstract class CodeFile implements ICodeFile { } const _monaco = await this.editor.monacoPromise; const model = await this.model; - this.instance = _monaco.editor.create(element, { model }); + this.instance = _monaco.editor.create(element, { + model, + mouseWheelZoom: true + }); this._rendered = true; this.addActions(); // 初始化后加载文件依赖的包 diff --git a/packages/code-editor/components/command-code-view/src/composition/controller/ts.config.ts b/packages/code-editor/components/command-code-view/src/composition/controller/ts.config.ts index e2bd1dfcdce805a2db81d37c51286ab626de7167..b6c0deec96bbc109a92df2f1181199c3cc8e2d2e 100644 --- a/packages/code-editor/components/command-code-view/src/composition/controller/ts.config.ts +++ b/packages/code-editor/components/command-code-view/src/composition/controller/ts.config.ts @@ -13,11 +13,11 @@ import { ProcessEditController } from "./process-edit"; * @returns ts文件路径 */ function getTsFilePath(path: string): string { - const idx = path.lastIndexOf('.'); - if (idx > 0) { - return path.substring(0, idx) + '.ts'; - } - return path; + const idx = path.lastIndexOf('.'); + if (idx > 0) { + return path.substring(0, idx) + '.ts'; + } + return path; } /** 新增方法的编号前缀 */ @@ -28,32 +28,32 @@ export const NEW_METHOD_NAME_PREFIX = '方法'; /** 前端构件设计器组件 */ interface CmpEditorComponent { - /** 切换按钮点击事件 */ - // switchMode: EventEmitter; + /** 切换按钮点击事件 */ + // switchMode: EventEmitter; - /** 跳转到ts并定位代码 */ - jumpToCode: Ref; + /** 跳转到ts并定位代码 */ + jumpToCode: Ref; - /** 初始化构件设计器 */ - init(fullPath: string): void; + /** 初始化构件设计器 */ + init(fullPath: string): void; - /** 保存 */ - save(tscode?: string, classes?: any[]): Promise; + /** 保存 */ + save(tscode?: string, classes?: any[]): Promise; - /** 切换当前编辑内容 */ - setDisplayMode(type: 'webcmp' | 'webcmd'): void; + /** 切换当前编辑内容 */ + setDisplayMode(type: 'webcmp' | 'webcmd'): void; - /** 注册设计器元数据文件变更事件 */ - onFileChanged(handler: (changed: boolean) => void): void; + /** 注册设计器元数据文件变更事件 */ + onFileChanged(handler: (changed: boolean) => void): void; - /** 是否显示保存按钮 */ - showSaveButton: boolean; + /** 是否显示保存按钮 */ + showSaveButton: boolean; - /** 保存按钮被点击事件 */ - // saveRequest: EventEmitter; + /** 保存按钮被点击事件 */ + // saveRequest: EventEmitter; - // /** 添加webcmd编排方法事件 */ - handleAddNewCmdMethod(eventPayload: any): Promise; + // /** 添加webcmd编排方法事件 */ + handleAddNewCmdMethod(eventPayload: any): Promise; } @@ -61,18 +61,34 @@ interface CmpEditorComponent { * 触发新增构件编排方法(webcmd方法) */ async function handleAddNewCmdMethod(controller: CommandCodeViewController, eventPayload: any): Promise { + const cmpEditor = controller.designerComponent?.value.instance as CmpEditorComponent; + if (cmpEditor) { + return new Promise((resolve, reject) => { + cmpEditor.handleAddNewCmdMethod(eventPayload).then(result => { + resolve(result); + }), error => { + reject(null); + }; + }); + } +} - const cmpEditor = controller.designerComponent?.value.instance as CmpEditorComponent; - if (cmpEditor) { - return new Promise((resolve, reject) => { - cmpEditor.handleAddNewCmdMethod(eventPayload).then(result => { - resolve(result); - }), error => { - reject(null); - }; - }); - - } +async function handleGotoMethod(controller: CommandCodeViewController, eventPayload: any): Promise { + const codeEditorInstance = controller.editorPanel.codeEditor; + const codeAnalysisResult = await codeEditorInstance.value.resolve(controller.path, true); + const classes = codeAnalysisResult.classes || []; + let targetClass: IClass | null = null; + // 找到第一个导出的类 + for (const _class of classes) { + if (_class.exported) { + targetClass = _class; + break; + } + } + if (!targetClass) { + return null; + } + codeEditorInstance.value?.position(controller.path, targetClass.code, eventPayload.methodCode); } @@ -82,245 +98,246 @@ async function handleAddNewCmdMethod(controller: CommandCodeViewController, even * @return 序号字符串(如果前缀本身就不重复,则返回空字符串) */ function getNewMethodNumber(_class: IClass, codePrefix: string): string { - const isRepeat = (_class?.methods || []).findIndex(method => method.code === codePrefix) >= 0; - if (!isRepeat && NEW_METHOD_CODE_PREFIX !== codePrefix) { - return ""; - } - let counter = 2; - if (NEW_METHOD_CODE_PREFIX === codePrefix) { - counter = 1; - } - while (true) { - const newCode = codePrefix + counter; - const repeatMethod = (_class?.methods || []).find(method => method.code === newCode); - if (!repeatMethod) { - return counter + ""; - } - ++counter; - } + const isRepeat = (_class?.methods || []).findIndex(method => method.code === codePrefix) >= 0; + if (!isRepeat && NEW_METHOD_CODE_PREFIX !== codePrefix) { + return ""; + } + let counter = 2; + if (NEW_METHOD_CODE_PREFIX === codePrefix) { + counter = 1; + } + while (true) { + const newCode = codePrefix + counter; + const repeatMethod = (_class?.methods || []).find(method => method.code === newCode); + if (!repeatMethod) { + return counter + ""; + } + ++counter; + } } /** * 处理新增前端ts代码方法片段通知 */ async function handleAddNewMethod(controller: CommandCodeViewController, eventPayload: any): Promise { - if (!controller.hasCodeEditor) { - return null; - } - const{codeEditor }= controller.editorPanel; - const codeAnalysisResult = await codeEditor.value.resolve(controller.path, true); - const classes = codeAnalysisResult.classes || []; - let targetClass: IClass | null = null; - // 找到第一个导出的类 - for (const _class of classes) { - if (_class.exported) { - targetClass = _class; - break; - } - } - if (!targetClass) { - return null; - } - const methodCodePrefix: string = eventPayload && eventPayload.methodCode || NEW_METHOD_CODE_PREFIX; - const methodNamePrefix: string = eventPayload && eventPayload.methodName || NEW_METHOD_NAME_PREFIX; - const methodNumber = getNewMethodNumber(targetClass, methodCodePrefix); - const methodCode = methodCodePrefix + methodNumber; - const methodName = methodNamePrefix + methodNumber; - const method: IMethod = { - code: methodCode, - accessibility: 'public', - kind: 'method', - name: methodName, - type: 'any', - returns: '', - description: '', - params: [] - }; - await codeEditor.value.addMethod(controller.path, method, targetClass.code); - // 由于自动的代码结构变更检测具有防抖时延,通过resolve方法手动触发类结构更新 - await codeEditor.value.resolve(controller.path, true); - await codeEditor.value.position(controller.path, targetClass.code, methodCode); - return { methodCode, methodName }; + if (!controller.hasCodeEditor) { + return null; + } + const { codeEditor } = controller.editorPanel; + const codeAnalysisResult = await codeEditor.value.resolve(controller.path, true); + const classes = codeAnalysisResult.classes || []; + let targetClass: IClass | null = null; + // 找到第一个导出的类 + for (const _class of classes) { + if (_class.exported) { + targetClass = _class; + break; + } + } + if (!targetClass) { + return null; + } + const methodCodePrefix: string = eventPayload && eventPayload.methodCode || NEW_METHOD_CODE_PREFIX; + const methodNamePrefix: string = eventPayload && eventPayload.methodName || NEW_METHOD_NAME_PREFIX; + const methodNumber = getNewMethodNumber(targetClass, methodCodePrefix); + const methodCode = methodCodePrefix + methodNumber; + const methodName = methodNamePrefix + methodNumber; + const method: IMethod = { + code: methodCode, + accessibility: 'public', + kind: 'method', + name: methodName, + type: 'any', + returns: '', + description: '', + params: [] + }; + await codeEditor.value.addMethod(controller.path, method, targetClass.code); + // 由于自动的代码结构变更检测具有防抖时延,通过resolve方法手动触发类结构更新 + await codeEditor.value.resolve(controller.path, true); + await codeEditor.value.position(controller.path, targetClass.code, methodCode); + return { methodCode, methodName }; } /** * 前端构件设计器 */ export class TSConfig implements OpenWithConfig { - needCodeEditor = true; - - suffix = [".ts", ".webcmp", ".webcmd"]; - - designerComponentUrl = "./designers/controller-cmp-designer.js"; - - customGlobalCssStyle = "html, body { overflow: hidden; }"; - - toolButtons = [] as any; - private fileSrv: FileService; - constructor() { - this.fileSrv = new FileService(); - this.toolButtons = [{ - label: "通过VSCode打开", - title: "通过VSCode打开", - handleClick: (controller: CommandCodeViewController) => { - this.handleOpenWithVSCode(controller); - } - }]; - } - - getFile(path: string): Promise { - const tsFilePath = getTsFilePath(path); - return this.fileSrv.getFile(tsFilePath); - } - - saveFile(path: string, content: string): Promise { - // 保存代码文件的任务也由构件设计器执行 - return new Promise((resolve, reject) => { - resolve(true); - }); - } - - initDesignerComponent(designerComponent: Ref, controller: CommandCodeViewController) { - // const processController=new ProcessEditController(); - designerComponent.value.init(controller.path); - // const cmpEditor = componentRef.value as CmpEditorComponent; - // if (!cmpEditor) { - // return; - // } - // // 进行构件初始化 - // cmpEditor.init(controller.path); - // // 在代码视图外打开构件设计器,需要让设计器显示自己的保存按钮 - // // if (!controller.fromCodeView) { - // // cmpEditor.showSaveButton = true; - // // cmpEditor.saveRequest.subscribe(() => { - // // controller.throttledDirectlySave && controller.throttledDirectlySave(); - // // }); - // // } - // // // 绑定切换按钮点击事件 - // // const switchModeEmitter = cmpEditor.switchMode; - // // if (!!switchModeEmitter) { - // // switchModeEmitter.subscribe(() => { - // // controller.switchMode(); - // // }); - // // } - // // 注册文件变更事件回调函数 - // cmpEditor.onFileChanged && cmpEditor.onFileChanged(controller.emitDesignerChanged); - // // 绑定ts跳转定位事件 - // const jumpToCodeEmitter = cmpEditor.jumpToCode; - // if (jumpToCodeEmitter) { - // watch(jumpToCodeEmitter, (methodCode: string) => { - // controller.switchMode(true); - // const {codeEditor} = controller.editorPanel; - // if (!codeEditor) { - // return; - // } - // // 获取导出的类的类名 - // let className = null; - // // ========================================此方法是异步 - // codeEditor.resolve(controller.path).then((codeRes) => { - // for (const _class of codeRes.classes) { - // if (_class && _class.exported) { - // className = _class.code; - // break; - // } - // } - // if (!className || !methodCode) { - // return; - // } - // setTimeout(() => { - // codeEditor.position(controller.path, className, methodCode); - // }, 15); - // }); - // }); - // } - } - - switchRelevantFile(path: string, designerComponent: Ref, controller: CommandCodeViewController) { - if (path.endsWith(".ts")) { - controller.switchMode(true); - } else { - const cmpEditor = designerComponent.value as CmpEditorComponent; - // 判断应该显示webcmp还是webcmd - if (path.endsWith(".webcmp")) { - cmpEditor.setDisplayMode('webcmp'); - } else if (path.endsWith(".webcmd")) { - cmpEditor.setDisplayMode('webcmd'); - } - controller.switchMode(false); - } - } - - save(designerComponent: Ref, result: CodeAnalysisResult, controller: CommandCodeViewController, editorSaveSuccess: boolean): Promise { - if (result && result.hasFatalError) { - return new Promise((resolve, reject) => { - // 你可以在这里执行异步操作,比如保存文件 - // 这里的例子中直接返回一个成功的 Promise - resolve("代码中包含不可忽视的语法错误,请修改后再保存"); - }); - } - const cmpEditor = designerComponent.value as CmpEditorComponent; - return cmpEditor.save(result.content, result.classes); - } - - beforeInit(controller: CommandCodeViewController): Promise { - // 如果代码文件不存在,则不显示代码编辑器,只显示构件设计器 - const {path} = controller; - const tsFilePath = getTsFilePath(path); - return new Promise((resolve, reject) => { - this.fileSrv.isFileExist(tsFilePath).then((exist: boolean) => { - if (!exist) { - this.needCodeEditor = false; - controller.editorPanel.hasCodeEditor = false; - } - resolve(); - }) - .catch(error => { - // 处理错误情况 - reject(error); - }); - }); - } - - /** 通过本地的VSCode打开代码文件 */ - async handleOpenWithVSCode(controller: CommandCodeViewController): Promise { - // 判断是否为本地环境,不支持打开远程环境上的文件 - const hostname = window.top && window.top.location.hostname; - if (hostname !== 'localhost' && hostname !== '127.0.0.1') { - controller.requestNotifyMessage({ - type: 'info', - msg: '仅支持打开本地环境上的文件', - timeout: 3500, - showClose: true - }); - return; - } - // 获取文件路径并触发链接 - const filePath = controller.path; - const rootPath = await this.fileSrv.getRootPath(); - if (!rootPath || !filePath) { - return; - } - const path = rootPath + filePath; - const encodedPath = encodeURIComponent(path); - const vscodePath = `vscode://file/${encodedPath}`; - const a = document.createElement("a"); - a.target = "_blank"; - a.href = vscodePath; - a.click(); - } - - outerNotificationHandlers = { - "AddNewMethod": handleAddNewMethod, - "AddNewCmdMethod": handleAddNewCmdMethod - }; - - - async getCodeFilePath(path: string): Promise { - return getTsFilePath(path); - } - - correctPath(path: string): string { - return getTsFilePath(path); - } + needCodeEditor = true; + + suffix = [".ts", ".webcmp", ".webcmd"]; + + designerComponentUrl = "./designers/controller-cmp-designer.js"; + + customGlobalCssStyle = "html, body { overflow: hidden; }"; + + toolButtons = [] as any; + private fileSrv: FileService; + constructor() { + this.fileSrv = new FileService(); + this.toolButtons = [{ + label: "通过VSCode打开", + title: "通过VSCode打开", + handleClick: (controller: CommandCodeViewController) => { + this.handleOpenWithVSCode(controller); + } + }]; + } + + getFile(path: string): Promise { + const tsFilePath = getTsFilePath(path); + return this.fileSrv.getFile(tsFilePath); + } + + saveFile(path: string, content: string): Promise { + // 保存代码文件的任务也由构件设计器执行 + return new Promise((resolve, reject) => { + resolve(true); + }); + } + + initDesignerComponent(designerComponent: Ref, controller: CommandCodeViewController) { + // const processController=new ProcessEditController(); + designerComponent.value.init(controller.path); + // const cmpEditor = componentRef.value as CmpEditorComponent; + // if (!cmpEditor) { + // return; + // } + // // 进行构件初始化 + // cmpEditor.init(controller.path); + // // 在代码视图外打开构件设计器,需要让设计器显示自己的保存按钮 + // // if (!controller.fromCodeView) { + // // cmpEditor.showSaveButton = true; + // // cmpEditor.saveRequest.subscribe(() => { + // // controller.throttledDirectlySave && controller.throttledDirectlySave(); + // // }); + // // } + // // // 绑定切换按钮点击事件 + // // const switchModeEmitter = cmpEditor.switchMode; + // // if (!!switchModeEmitter) { + // // switchModeEmitter.subscribe(() => { + // // controller.switchMode(); + // // }); + // // } + // // 注册文件变更事件回调函数 + // cmpEditor.onFileChanged && cmpEditor.onFileChanged(controller.emitDesignerChanged); + // // 绑定ts跳转定位事件 + // const jumpToCodeEmitter = cmpEditor.jumpToCode; + // if (jumpToCodeEmitter) { + // watch(jumpToCodeEmitter, (methodCode: string) => { + // controller.switchMode(true); + // const {codeEditor} = controller.editorPanel; + // if (!codeEditor) { + // return; + // } + // // 获取导出的类的类名 + // let className = null; + // // ========================================此方法是异步 + // codeEditor.resolve(controller.path).then((codeRes) => { + // for (const _class of codeRes.classes) { + // if (_class && _class.exported) { + // className = _class.code; + // break; + // } + // } + // if (!className || !methodCode) { + // return; + // } + // setTimeout(() => { + // codeEditor.position(controller.path, className, methodCode); + // }, 15); + // }); + // }); + // } + } + + switchRelevantFile(path: string, designerComponent: Ref, controller: CommandCodeViewController) { + if (path.endsWith(".ts")) { + controller.switchMode(true); + } else { + const cmpEditor = designerComponent.value as CmpEditorComponent; + // 判断应该显示webcmp还是webcmd + if (path.endsWith(".webcmp")) { + cmpEditor.setDisplayMode('webcmp'); + } else if (path.endsWith(".webcmd")) { + cmpEditor.setDisplayMode('webcmd'); + } + controller.switchMode(false); + } + } + + save(designerComponent: Ref, result: CodeAnalysisResult, controller: CommandCodeViewController, editorSaveSuccess: boolean): Promise { + if (result && result.hasFatalError) { + return new Promise((resolve, reject) => { + // 你可以在这里执行异步操作,比如保存文件 + // 这里的例子中直接返回一个成功的 Promise + resolve("代码中包含不可忽视的语法错误,请修改后再保存"); + }); + } + const cmpEditor = designerComponent.value as CmpEditorComponent; + return cmpEditor.save(result.content, result.classes); + } + + beforeInit(controller: CommandCodeViewController): Promise { + // 如果代码文件不存在,则不显示代码编辑器,只显示构件设计器 + const { path } = controller; + const tsFilePath = getTsFilePath(path); + return new Promise((resolve, reject) => { + this.fileSrv.isFileExist(tsFilePath).then((exist: boolean) => { + if (!exist) { + this.needCodeEditor = false; + controller.editorPanel.hasCodeEditor = false; + } + resolve(); + }) + .catch(error => { + // 处理错误情况 + reject(error); + }); + }); + } + + /** 通过本地的VSCode打开代码文件 */ + async handleOpenWithVSCode(controller: CommandCodeViewController): Promise { + // 判断是否为本地环境,不支持打开远程环境上的文件 + const hostname = window.top && window.top.location.hostname; + if (hostname !== 'localhost' && hostname !== '127.0.0.1') { + controller.requestNotifyMessage({ + type: 'info', + msg: '仅支持打开本地环境上的文件', + timeout: 3500, + showClose: true + }); + return; + } + // 获取文件路径并触发链接 + const filePath = controller.path; + const rootPath = await this.fileSrv.getRootPath(); + if (!rootPath || !filePath) { + return; + } + const path = rootPath + filePath; + const encodedPath = encodeURIComponent(path); + const vscodePath = `vscode://file/${encodedPath}`; + const a = document.createElement("a"); + a.target = "_blank"; + a.href = vscodePath; + a.click(); + } + + outerNotificationHandlers = { + "AddNewMethod": handleAddNewMethod, + "AddNewCmdMethod": handleAddNewCmdMethod, + "GotoMethod": handleGotoMethod, + }; + + + async getCodeFilePath(path: string): Promise { + return getTsFilePath(path); + } + + correctPath(path: string): string { + return getTsFilePath(path); + } } diff --git a/packages/designer/src/components/components/code-view/components/code-view.component.tsx b/packages/designer/src/components/components/code-view/components/code-view.component.tsx index 7b434ac9f4c19445d6bca81cbe374374042a3d2a..9385b0f1caa5e6947ced1322b80531232349258e 100644 --- a/packages/designer/src/components/components/code-view/components/code-view.component.tsx +++ b/packages/designer/src/components/components/code-view/components/code-view.component.tsx @@ -143,6 +143,39 @@ export default defineComponent({ editorController.openFile(path); filePath.value = path; } + + const flattenTreeData = (items, result = []) => { + items = items || []; + return items.reduce((resultObject, current) => { + resultObject.push(current); + if (current.children && current.children.length) { + flattenTreeData(current.children, resultObject); + } + return resultObject; + }, result); + }; + + + function openAndGoTo(data: any) { + // console.log('打开并跳转', data, fileTreeInstance.value.controllers()); + const { command, controller } = data; + if (controller) { + const controllerFileName = controller.label + '.ts'; + const controllerFiles = flattenTreeData(fileTreeInstance.value.controllers() || []); + + const controllFile = controllerFiles.find(file => { + return file.data.name === controllerFileName; + }); + + if (controllFile?.path) { + editorController.openFile(controllFile.path); + filePath.value = controllFile.path; + + editorController.sendNotification(controllFile.path, {eventName: 'GotoMethod', eventPayload: { methodCode: command.handlerName, methodName: command.name } }); + } + } + } + /** * 点击文件导航树 * @param data @@ -227,7 +260,7 @@ export default defineComponent({ */} ; } - context.expose({ refreshNavTree, open, sendNotification }); + context.expose({ refreshNavTree, open, sendNotification, openAndGoTo }); return () => { return ( diff --git a/packages/designer/src/components/components/code-view/components/nav-tree.component.tsx b/packages/designer/src/components/components/code-view/components/nav-tree.component.tsx index d99969770ca969ce5a0b69268a52486b9ea5dc46..996db29a02ae33b22e5d37ccd9a5ef93ab59af8e 100644 --- a/packages/designer/src/components/components/code-view/components/nav-tree.component.tsx +++ b/packages/designer/src/components/components/code-view/components/nav-tree.component.tsx @@ -182,7 +182,9 @@ export default defineComponent({ } - context.expose({ reloadTreeData, setDataService,selectByPath }); + context.expose({ reloadTreeData, setDataService,selectByPath, controllers: () => { + return treeData; + } }); return () => { return ( diff --git a/packages/designer/src/components/components/form-designer/components/custom-class-editor/custom-class-editor.component.tsx b/packages/designer/src/components/components/form-designer/components/custom-class-editor/custom-class-editor.component.tsx index d582aaba6951dedda3a219f009a622314584ce95..c8bf1eaa8c9c951304d7a5375984def4a9f186c4 100644 --- a/packages/designer/src/components/components/form-designer/components/custom-class-editor/custom-class-editor.component.tsx +++ b/packages/designer/src/components/components/form-designer/components/custom-class-editor/custom-class-editor.component.tsx @@ -25,10 +25,12 @@ export default defineComponent({ /** 视图模型导航数据 */ const viewModelNavgationData = ref(viewModelNavigation?.viewModelTabs); - const monacoEditorRef = ref(); + const cssEditorRef = ref(); /** 自定义样式Dom */ formSchema.module.customClass ??= {}; + const cssFilePath = ref(formSchema.module.customStyleFile || ''); + const { customClass } = formSchema.module; /** 当前编辑器内的样式代码 */ const currentClassCode = ref(); @@ -64,7 +66,7 @@ export default defineComponent({ } const oldClassCode = customClass[currentComponentId]; - const newClassCode = monacoEditorRef.value?.getContent(); + const newClassCode = cssEditorRef.value?.getContent(); // 样式变化后,进行更新 if (oldClassCode !== newClassCode) { customClass[currentComponentId] = newClassCode; @@ -80,6 +82,7 @@ export default defineComponent({ return; } currentClassCode.value = customClass[currentComponentId]; + cssEditorRef.value?.setContent(currentClassCode.value || ''); } /** @@ -106,19 +109,31 @@ export default defineComponent({ function customStyleFilePath() { const {code:formCode, projectName} = formSchema.module; - const basePath = getUrlParam('id').split('/').slice(0,3).join('/'); - return `/apps${basePath}/web/${projectName}/${formCode}/${formCode}.css`; + const basePath = getUrlParam('id').split('/').filter(name => name); + const app = basePath[0]; + const su = basePath[1]; + return `${app}/${su}/web/${projectName}/${formCode}/css/${formCode}.css`; + } + + function resetCssFilePath() { + formSchema.module.customStyleFile = customStyleFilePath(); + cssFilePath.value = formSchema.module.customStyleFile; } onBeforeMount(() => { updateCurrentClassCode(); if (!formSchema.module.customStyleFile) { - formSchema.module.customStyleFile = customStyleFilePath(); + resetCssFilePath(); } }); + const updateWheelZoom = (isEnabled) => { + cssEditorRef.value?.updateWheelZoom(isEnabled); + }; + context.expose({ - saveCustomClass + saveCustomClass, + updateWheelZoom }); /** @@ -154,13 +169,14 @@ export default defineComponent({ {renderViewModelNavgation()} - - + + -
+
部署路径: - + +
); diff --git a/packages/designer/src/components/components/form-designer/components/custom-class-editor/custom-class-editor.props.ts b/packages/designer/src/components/components/form-designer/components/custom-class-editor/custom-class-editor.props.ts index f819c3e1c70ff7b759db8bc4896be8b2e4c493c6..a89c775a769e194710cc5d37291ab8f9930a8ccd 100644 --- a/packages/designer/src/components/components/form-designer/components/custom-class-editor/custom-class-editor.props.ts +++ b/packages/designer/src/components/components/form-designer/components/custom-class-editor/custom-class-editor.props.ts @@ -1,6 +1,7 @@ import { ExtractPropTypes } from "vue"; export const customClassEditorProps = { + isActive: { type: Boolean, default: false }, } as Record; export type CustomClassEditorProps = ExtractPropTypes; 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 47709119228cb6788ae4414ce470ff498c22d13f..08d668ec80147c477c7ec5152714fd974779c9b5 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 @@ -278,7 +278,7 @@ export default defineComponent({ @@ -287,8 +287,8 @@ export default defineComponent({ class={formDesignerViewClass.value}>可视化设计器
onChangeDesignerView('formDesignerCode')} class={formDesignerCodeViewClass.value}>设计时代码
- {/* {
onChangeDesignerView('customClassEditor')} - class={customClassEditorClass.value}>自定义样式
} */} + {/*
onChangeDesignerView('customClassEditor')} + class={customClassEditorClass.value}>自定义样式
*/}
diff --git a/packages/designer/src/components/components/monaco-editor/monaco-editor.component.tsx b/packages/designer/src/components/components/monaco-editor/monaco-editor.component.tsx index 66bc48595045bf76ca6f5748d519ac482cdde398..fd37bd04b2ee6cf5a37c0fb8056acfa7e2672c73 100644 --- a/packages/designer/src/components/components/monaco-editor/monaco-editor.component.tsx +++ b/packages/designer/src/components/components/monaco-editor/monaco-editor.component.tsx @@ -1,4 +1,4 @@ -import { defineComponent, watch, onBeforeUnmount, onMounted, ref } from "vue"; +import { defineComponent, watch, onBeforeUnmount, onMounted, ref, computed } from "vue"; import loader from "@monaco-editor/loader"; import { MonacoEditorProps, monacoEditorProps } from "./monaco-editor.props"; @@ -16,6 +16,16 @@ export default defineComponent({ }); } + const editorOptions = { + value: codeValues.value, + language: props.language, + theme: props.theme, + folding: true, + readOnly: props.readOnly, + automaticLayout: true, + mouseWheelZoom: true // 启用滚轮缩放 + }; + async function initMonacoEditor() { if (editorContainer.value && !editorInstance) { const config = await getMonacoEditorConfig(); @@ -24,21 +34,27 @@ export default defineComponent({ loader.config({ "vs/nls": { availableLanguages: { "*": "zh-cn" } } }); loader.init().then((monaco) => { - editorInstance = monaco.editor.create(editorContainer.value, { - value: codeValues.value, - language: props.language, - theme: props.theme, - folding: true, - readOnly: props.readOnly - }); + editorInstance = monaco.editor.create(editorContainer.value, {...editorOptions}); + // 添加编辑器激活状态监听 + // editorInstance.onDidFocusEditorWidget(() => { + // editorInstance.updateOptions({ mouseWheelZoom: true }); + // }); + // editorInstance.onDidBlurEditorWidget(() => { + // editorInstance.updateOptions({ mouseWheelZoom: false }); + // }); }); } } + watch(() => props.isActive, (newValue) => { + editorInstance?.updateOptions({ mouseWheelZoom: newValue }); + }); + watch(() => props.modelValue, (newValue) => { codeValues.value = newValue; editorInstance?.setValue(newValue); }); + const resizeObserver = new ResizeObserver(entries => { editorInstance?.layout(); editorInstance?.updateOptions({ theme: 'vs-dark' }); @@ -84,9 +100,22 @@ export default defineComponent({ } }; + const setContent = (content: string) => { + if (!editorInstance) { + return; + } + editorInstance.setValue(content); + }; + + const updateWheelZoom = (isEnabled) => { + editorInstance?.updateOptions({ mouseWheelZoom: isEnabled }); + }; + context.expose({ getContent, - setPosition + setContent, + setPosition, + updateWheelZoom }); return () => { diff --git a/packages/designer/src/components/components/monaco-editor/monaco-editor.props.ts b/packages/designer/src/components/components/monaco-editor/monaco-editor.props.ts index d5bbd94c85a1d6ea62b5fe51ae21b12a4e12a845..72d62896a2b625506d0897d5e22f24fe1d711f86 100644 --- a/packages/designer/src/components/components/monaco-editor/monaco-editor.props.ts +++ b/packages/designer/src/components/components/monaco-editor/monaco-editor.props.ts @@ -4,7 +4,8 @@ export const monacoEditorProps = { modelValue: { type: String, default: '' }, language: { type: String, default: 'typescript' }, theme: { type: String, default: 'vs-dark' }, - readOnly: { type: Boolean, default: false } + readOnly: { type: Boolean, default: false }, + isActive: { type: Boolean, default: false }, }; export type MonacoEditorProps = ExtractPropTypes; diff --git a/packages/designer/src/components/components/view-model-designer/method-manager/components/method-list/method-list.component.tsx b/packages/designer/src/components/components/view-model-designer/method-manager/components/method-list/method-list.component.tsx index 55f2de974fe2a25512c01bdda367d7d9a63776e3..00269647ac8102e7bc1bc28b123c9346e5c841e5 100644 --- a/packages/designer/src/components/components/view-model-designer/method-manager/components/method-list/method-list.component.tsx +++ b/packages/designer/src/components/components/view-model-designer/method-manager/components/method-list/method-list.component.tsx @@ -17,7 +17,7 @@ import { DesignerMode, UseDesignerContext } from "../../../../../types/designer- export default defineComponent({ name: 'FMethodList', props: methodListProps, - emits: [], + emits: ['viewSource'], setup(props: MethodListProps, context) { const designerContext = inject('designerContext') as UseDesignerContext; const useFormSchemaComposition = inject('useFormSchema')!; @@ -106,6 +106,23 @@ export default defineComponent({ treeGridRef.value.reassignRowData(selectedTreeNode.value.id, commandData); } + function onViewMethodSource($event) { + if ($event.node && $event.node.hasChildren) { + return; + } + + const { controller } = $event.node.data; + if (controller && controller.isCommon) { + return; + } + + const {data} = $event.node; + context.emit('viewSource', data); + } + + const isCustomMethod = computed(() => { + return !selectedTreeNode.value?.data.controller?.isCommon; + }); onMounted(() => { }); @@ -170,6 +187,7 @@ export default defineComponent({

构件

+ {isCustomMethod.value && }
@@ -286,6 +304,7 @@ export default defineComponent({ }} columnOption={{ fitColumns: true }} row-number={rowNumberOption} + onDblclickNode={onViewMethodSource} onSelectionChange={onChangeSelectedCommand} row-option={rowOption}> {{ 'cellTemplate': ({ cell, row }) => { diff --git a/packages/designer/src/components/components/view-model-designer/method-manager/entity/command.ts b/packages/designer/src/components/components/view-model-designer/method-manager/entity/command.ts index 5d280e9321578f38044aa9275eb51a0d37213c27..0ff878c6cbc987a542d43df891c0e4519239d38e 100644 --- a/packages/designer/src/components/components/view-model-designer/method-manager/entity/command.ts +++ b/packages/designer/src/components/components/view-model-designer/method-manager/entity/command.ts @@ -64,6 +64,13 @@ export class Command implements ITreeNode { /** 命令在构件中的名称 */ handlerShowName?: string; + /** 控制器信息 */ + controller?: { + label: string; + name: string; + isCommon: boolean; + }; + get children(): ITreeNode[] { return this.handlers || []; } @@ -137,6 +144,12 @@ export class Command implements ITreeNode { this.controllerName = webCmd.Name; this.isFromPresetController = !webCmd.Code.includes('_frm_'); + + this.controller = { + label: webCmd.Code, + name: webCmd.Name, + isCommon: webCmd.Extends.IsCommon + }; } this.handlers = new OperationCollection(); @@ -208,6 +221,7 @@ export class Command implements ITreeNode { handler.postExtendable = methodItem.IsAfterExpansion; handler.root = this; + handler.controller = this.controller; // 处理扩展 const extensions = extensionMap.get(handler.id); diff --git a/packages/designer/src/components/components/view-model-designer/method-manager/entity/execute-node.ts b/packages/designer/src/components/components/view-model-designer/method-manager/entity/execute-node.ts index f3c309b09389fa63b248a3bab629c9d3aadaab2a..5372d45c43ad80bcd41bf77096ceb7aac2598cd4 100644 --- a/packages/designer/src/components/components/view-model-designer/method-manager/entity/execute-node.ts +++ b/packages/designer/src/components/components/view-model-designer/method-manager/entity/execute-node.ts @@ -93,6 +93,8 @@ export class ExecuteNode implements ITreeNode, IOperationNode { /** 操作所属的构件名称 */ componentName = ''; + controller?: { label: string; name: string; isCommon: boolean }; + constructor(executeNodeJson?: any, extension?: Extension, parent?: ITreeNode) { if (executeNodeJson) { this.parse(executeNodeJson, extension); @@ -112,6 +114,7 @@ export class ExecuteNode implements ITreeNode, IOperationNode { this.extendLevel = executeNodeJson.extendLevel || OperationExtendLevel.Form; this.componentCode = executeNodeJson.componentCode; this.componentName = executeNodeJson.componentName; + this.controller = executeNodeJson.controller; if (extension) { this.belongedExt = extension; diff --git a/packages/designer/src/components/components/view-model-designer/method-manager/method-manager.component.tsx b/packages/designer/src/components/components/view-model-designer/method-manager/method-manager.component.tsx index 03af6c0f919ec062ba2ab03d34e20b0757aaaa92..8b5ad20ca0b03c05dfc8c7757e7a7e1fba6ac347 100644 --- a/packages/designer/src/components/components/view-model-designer/method-manager/method-manager.component.tsx +++ b/packages/designer/src/components/components/view-model-designer/method-manager/method-manager.component.tsx @@ -187,6 +187,11 @@ export default defineComponent({ 'disable': methodListRef.value?.selectedTreeNode && methodListRef.value?.isCommandNodeSelected ? false : true }; }); + + function onViewSource($event: any) { + context.emit('viewSource', $event); + } + return () => { return (
@@ -217,6 +222,7 @@ export default defineComponent({
onViewSource($event)} commandsData={commandsTreeData.value} activeViewModel={activeViewModel.value}>
diff --git a/packages/designer/src/components/components/view-model-designer/view-model-designer.component.tsx b/packages/designer/src/components/components/view-model-designer/view-model-designer.component.tsx index f55ab1da28b2a6cc6659210586d9e45c938d8558..9975b07ede684914d0fae453871847faeaa565cd 100644 --- a/packages/designer/src/components/components/view-model-designer/view-model-designer.component.tsx +++ b/packages/designer/src/components/components/view-model-designer/view-model-designer.component.tsx @@ -10,7 +10,7 @@ import { DesignerMode, UseDesignerContext } from "../../types/designer-context"; export default defineComponent({ name: 'FViewModelDesigner', props: viewModelDesignerProps, - emits: [], + emits: ['viewSource'], setup(props: ViewModelDesignerProps, context) { const methodManagerRef = ref(); const variableManagerRef = ref(); @@ -54,7 +54,7 @@ export default defineComponent({ - + diff --git a/packages/designer/src/components/composition/command-builder.service.ts b/packages/designer/src/components/composition/command-builder.service.ts index 14bfc476fd283cb6f8ede9df92dc7e000645096e..2601b8bb5ecb92f515b1bd74ef0a4e99a108a560 100644 --- a/packages/designer/src/components/composition/command-builder.service.ts +++ b/packages/designer/src/components/composition/command-builder.service.ts @@ -215,5 +215,9 @@ export function useCommandBuilderService(formSchemaService: UseFormSchema): UseC return buildInfo; } - return { eventBetweenDesignerAndCodeView, addControllerMethod, addWebCommandMethod, getBuildInfo }; + function jumpToCodeView(param: any) { + eventBetweenDesignerAndCodeView.value = { eventName: 'jumpToCodeView', eventValue: param }; + } + + return { eventBetweenDesignerAndCodeView, addControllerMethod, addWebCommandMethod, getBuildInfo, jumpToCodeView }; } diff --git a/packages/designer/src/components/composition/command.service.ts b/packages/designer/src/components/composition/command.service.ts index ac0318edb4d90549f98dd7a7c878a815964898eb..162daeef35fe495e5b49ed493d35931aecb4aacf 100644 --- a/packages/designer/src/components/composition/command.service.ts +++ b/packages/designer/src/components/composition/command.service.ts @@ -34,6 +34,7 @@ export function useFormCommandService(formSchemaService: UseFormSchema, useFormS label: '', name: '', id: '', + isCommon: false }, controllerList: [ ] as any @@ -210,6 +211,8 @@ export function useFormCommandService(formSchemaService: UseFormSchema, useFormS label: controller.Code, name: controller.Name, id: controller.Id, + isCommon: controller.Extends.IsCommon + }; if (!controller.Commands) { controller['Commands'] = []; @@ -1347,6 +1350,6 @@ export function useFormCommandService(formSchemaService: UseFormSchema, useFormS return { checkCommands, commandsChanged, generateInternalCommandList, viewModelDisplay, findParamtersPosition, addControllerMethod, viewModelDomChanged, getCommands, bindNewMethodToControl, getInternalControllerFromControllerMetadata, getSupportedControllerMetadata, - getEventParameterGeneralData + getEventParameterGeneralData, webCmpBuilderService }; } diff --git a/packages/designer/src/components/composition/control-creator/use-pc-control-creator.service.ts b/packages/designer/src/components/composition/control-creator/use-pc-control-creator.service.ts index 11804ce264ef466175a5941ae1256dc7f2d41c92..89be721bdc9a0f3ca9b158a2a00d2feaebaae04f 100644 --- a/packages/designer/src/components/composition/control-creator/use-pc-control-creator.service.ts +++ b/packages/designer/src/components/composition/control-creator/use-pc-control-creator.service.ts @@ -54,12 +54,21 @@ export function usePCControlCreator(schemaService: UseSchemaService): UseControl // 数字类型 if (fieldTypeInSchema === 'NumericType') { - formEditor.precision = field.type.precision; - formEditor.nullable = true; + if (formEditor.type === 'check-box' || formEditor.type === 'switch') { + formEditor.trueValue = 1; + formEditor.falseValue = 0; + } else { + formEditor.precision = field.type.precision; + formEditor.nullable = true; + } } // 数字、字符串、备注 :设置最大长度 if (['NumericType', 'StringType', 'TextType'].includes(fieldTypeInSchema)) { formEditor.maxLength = field.type.length; + if (fieldTypeInSchema === 'StringType' && (formEditor.type ==='check-box' || formEditor.type === 'switch')) { + formEditor.trueValue = 'true'; + formEditor.falseValue = 'false'; + } } // 日期类型 if (formEditor.type === 'date-picker') { diff --git a/packages/designer/src/components/composition/events-editor-utils.ts b/packages/designer/src/components/composition/events-editor-utils.ts index 6e61bd41dd9b9c87eaac768f6a45c1274d9bbe62..ba8316a5e8ed67de36c14a4846d1e390450ca4c3 100644 --- a/packages/designer/src/components/composition/events-editor-utils.ts +++ b/packages/designer/src/components/composition/events-editor-utils.ts @@ -473,5 +473,10 @@ export function useEventsEditorUtils(commandService: UseFormCommandService, form boundEventsList }; } - return { formProperties, saveRelatedParameters }; + + function jumpToMethod(command) { + commandService.webCmpBuilderService.jumpToCodeView(command); + } + + return { formProperties, saveRelatedParameters, jumpToMethod }; } diff --git a/packages/designer/src/components/designer.component.tsx b/packages/designer/src/components/designer.component.tsx index 8612a133b77d451b26ea35895fa6c6168d661f9a..49a3237425c0f4909c85de47efb87b0801ae66ec 100644 --- a/packages/designer/src/components/designer.component.tsx +++ b/packages/designer/src/components/designer.component.tsx @@ -237,6 +237,14 @@ export default defineComponent({ } } + function jumpToCodeView(data: any) { + if (data) { + codeViewComponent.value.openAndGoTo(data); + // 切换到代码视图 + currentViewType.value = 'codeEditor'; + } + } + watch(eventBetweenDesignerAndCodeView, (newData) => { if (!newData || !newData.eventName) { return; @@ -250,8 +258,24 @@ export default defineComponent({ addNewMethodToWebCmd(newData.eventValue); break; } + case 'jumpToCodeView': { + jumpToCodeView(newData.eventValue); + break; + } } }); + + function onViewMethodSource(data: any) { + if (!data) { + return; + } + const { controller } = data; + jumpToCodeView({ command: { + handlerName: data.code, + name: data.name + }, controller }); + } + return () => { return ( metadataLoaded.value ? @@ -273,7 +297,7 @@ export default defineComponent({ {/* */} - +
diff --git a/packages/designer/src/components/types/command.ts b/packages/designer/src/components/types/command.ts index 751eba546c5aa5a84748739f260879e057ebf283..dd106f02dc09167b0c697889c135e4b1f34eb701 100644 --- a/packages/designer/src/components/types/command.ts +++ b/packages/designer/src/components/types/command.ts @@ -1,5 +1,12 @@ import { WebCommand } from "../components/view-model-designer/method-manager/entity/web-command"; import { Ref } from "vue"; +export interface UseCommandBuilderService { + addControllerMethod: (methodCode: string, methodName: string) => void; + addWebCommandMethod: (command: WebCommand, targetWebCmd?: { controllerCode: string, controllerName: string }) => void; + eventBetweenDesignerAndCodeView: Ref<{ eventName: string, eventValue: any }>; + getBuildInfo: () => any; + jumpToCodeView: (param: {tsFilePathName: string, command: WebCommand}) => void; +} export interface UseFormCommandService { checkCommands: () => Promise; commandsChanged: (newController) => void; @@ -12,15 +19,10 @@ export interface UseFormCommandService { bindNewMethodToControl: (methodCode: string, methodName: string) => void; getInternalControllerFromControllerMetadata: (controller: any, code: string, nameSpace: string) => any; getSupportedControllerMetadata: (controller: any) => Promise; - getEventParameterGeneralData: () => any; + webCmpBuilderService: UseCommandBuilderService; } -export interface UseCommandBuilderService { - addControllerMethod: (methodCode: string, methodName: string) => void; - addWebCommandMethod: (command: WebCommand, targetWebCmd?: { controllerCode: string, controllerName: string }) => void; - eventBetweenDesignerAndCodeView: Ref<{ eventName: string, eventValue: any }>; - getBuildInfo: () => any; -} + /** 构件操作参数 */ export declare class Parameter { diff --git a/packages/designer/src/components/types/events-editor.ts b/packages/designer/src/components/types/events-editor.ts index 22908e746e10304c3531ea92fa4bfa18cff3d760..7cc3e94423dffbcb26ccd67350711dcc5e500a87 100644 --- a/packages/designer/src/components/types/events-editor.ts +++ b/packages/designer/src/components/types/events-editor.ts @@ -54,6 +54,7 @@ export interface ControllerListItem { export interface UseEventsEditorUtils { formProperties: (eventEditorService, formBasicService, domService, webCmdService, propertyData, viewModelId, eventList, switchEvents?: (propertyData, eventList) => object) => void; saveRelatedParameters: (eventEditorService, domService, webCmdService, propertyData: any, viewModelId: string, eventList, parameters: any) => void; + jumpToMethod: (command: any) => void; } export interface Node { diff --git a/packages/ui-vue/components/checkbox/src/checkbox.props.ts b/packages/ui-vue/components/checkbox/src/checkbox.props.ts index b17e78e6ab2f543fe015bc25bf3ba9b20c1f8a1e..3610db9b1cd2d3d59a5f90b97ee3878e73d88b5a 100644 --- a/packages/ui-vue/components/checkbox/src/checkbox.props.ts +++ b/packages/ui-vue/components/checkbox/src/checkbox.props.ts @@ -42,7 +42,9 @@ export const checkboxProps = { /** 标识是否被选中 */ checked: { type: Boolean, default: false }, /** 显示文本标签 */ - label: { type: String, default: '' } + label: { type: String, default: '' }, + trueValue: { type: [String, Number, Boolean], default: true }, + falseValue: { type: [String, Number, Boolean], default: false } } as Record; export type CheckboxProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/checkbox/src/property-config/checkbox.property-config.ts b/packages/ui-vue/components/checkbox/src/property-config/checkbox.property-config.ts index 1a9ff370047f89dace0367013bf2bae4eb92a23c..a509f562262c70022fabbaf7d61091157870197a 100644 --- a/packages/ui-vue/components/checkbox/src/property-config/checkbox.property-config.ts +++ b/packages/ui-vue/components/checkbox/src/property-config/checkbox.property-config.ts @@ -1,10 +1,11 @@ -import { InputBaseProperty } from "../../../property-panel/src/composition/entity/input-base-property"; +import { InputBaseProperty } from "@farris/ui-vue/components/property-panel"; export class CheckBoxProperty extends InputBaseProperty { constructor(componentId: string, designerHostService: any) { super(componentId, designerHostService); } + getEditorProperties(propertyData) { return this.getComponentConfig(propertyData, { "type": "check-box" },{ placeholder: { @@ -17,6 +18,24 @@ export class CheckBoxProperty extends InputBaseProperty { description: "", title: "名称", type: "string" + }, + trueValue: { + description: "选中值", + title: "选中时的值", + type: this.getBindingDataType(), + visible: this.designViewModelField.type.name !== 'Boolean', + refreshPanelAfterChanged: true, + editor: this.getEditor(), + $converter: this.getTrueValueConverter() + }, + falseValue: { + description: "未选中值", + title: "未选中时的值", + type: this.getBindingDataType(), + visible: this.designViewModelField.type.name !== 'Boolean', + refreshPanelAfterChanged: true, + editor: this.getEditor(), + $converter: this.getFalseValueConverter() } // name: { // description: "控件名称,提交时的唯一标识,可以为空但不能与其他控件重复", diff --git a/packages/ui-vue/components/checkbox/src/schema/checkbox.schema.json b/packages/ui-vue/components/checkbox/src/schema/checkbox.schema.json index daee0e5d36fdc77ce2cbe89a5a387563fa2ff47c..1a3e4c5a51f2932c9da7f48cf3827c7358b27fa4 100644 --- a/packages/ui-vue/components/checkbox/src/schema/checkbox.schema.json +++ b/packages/ui-vue/components/checkbox/src/schema/checkbox.schema.json @@ -80,6 +80,16 @@ "description": "", "type": "string", "default": "" + }, + "trueValue": { + "description": "", + "type": "boolean", + "default": true + }, + "falseValue": { + "description": "", + "type": "boolean", + "default": false } }, "required": [ diff --git a/packages/ui-vue/components/combo-tree/src/combo-tree.component.tsx b/packages/ui-vue/components/combo-tree/src/combo-tree.component.tsx index aca52805067beff4ce049d72f1a862d46c5ab74c..5ab2f6e4ae2339b26648a849175f77d9338d782b 100644 --- a/packages/ui-vue/components/combo-tree/src/combo-tree.component.tsx +++ b/packages/ui-vue/components/combo-tree/src/combo-tree.component.tsx @@ -3,18 +3,22 @@ import FButtonEdit from '@farris/ui-vue/components/button-edit'; import { ComboTreeProps, comboTreeProps, Option } from "./combo-tree.props"; import ComboTreeContainer from './components/tree-container.component'; import { useDataSource } from "./composition/use-data-source"; -import { cloneDeep } from "lodash-es"; +import { cloneDeep, debounce } from "lodash-es"; export default defineComponent({ name: 'FComboTree', props: comboTreeProps, - emits: ['clear', 'update:modelValue', 'change'], + emits: ['clear', 'update:modelValue', 'change', 'search'], setup(props: ComboTreeProps, context: SetupContext) { const comboEditorRef: Ref = ref(); const disable = ref(props.disabled); const enableClear = ref(props.enableClear); const enableSearch = ref(props.enableSearch); const readonly = ref(props.readonly); + const searchFields = ref(props.searchFields || [props.textField]); + const originalValue = ref(); + const comboTreeRef = ref(); + const { dataSource, displayText, editable, modelValue, getSelectedItemsByDisplayText } = useDataSource(props); const isMultiSelect = computed(() => props.multiSelect); @@ -23,6 +27,14 @@ export default defineComponent({ return comboEditorRef.value ? (comboEditorRef.value.elementRef as HTMLElement).getBoundingClientRect().width : 0; }); + const showPopover = computed(() => { + const popoverInstance = comboEditorRef.value?.popoverRef; + if (popoverInstance) { + return popoverInstance.shown; + } + return false; + }); + function tryHidePopupOnSelect() { const shouldHidePopupOnSelect = !isMultiSelect.value; if (shouldHidePopupOnSelect && comboEditorRef.value) { @@ -40,10 +52,77 @@ export default defineComponent({ function onClear($event: Event) { modelValue.value = ''; + if (showPopover.value) { + // comboTreeRef.value?.treeInstance?.clearSelection(); + comboEditorRef.value?.hidePopup(); + } context.emit('update:modelValue', ''); context.emit('clear'); } + + function searchTree(node: any, keyword: string): any { + // 先处理子节点(深度优先) + const filteredChildren = node.children + .map(child => searchTree(child, keyword)) + .filter(child => child !== null) as any[]; + + // 判断当前节点是否匹配 + const isMatch = searchFields.value.some((field: string) => { + return node.data[field]?.toString().toLowerCase().includes(keyword.toLowerCase()); + }); + + // 构造返回条件 + if (isMatch) { + // 当前节点匹配时,保留完整子结构 + return { + data: { ...node.data }, + children: node.children // 注意这里保留原始子节点 + }; + } else if (filteredChildren.length > 0) { + // 子节点有匹配时,返回过滤后的子结构 + return { + data: { ...node.data }, + children: filteredChildren + }; + } + + return null; + } + + + function searchDataSource(searchValue: string) { + if (!showPopover.value) { + comboEditorRef.value?.showPopup(); + } + + if (!searchValue) { + originalValue.value = cloneDeep(dataSource.value); + return; + } + + const searchResult = dataSource.value.map(root => searchTree(root, searchValue)) + .filter(node => node !== null) as any[]; + + originalValue.value = cloneDeep(searchResult); + } + + const handleChangeDebounce = debounce(($event) => { + const searchText = ($event.target as HTMLInputElement)?.value; + searchDataSource(searchText); + }, 200); + + function onValueChange($event: any) { + let searchText = ($event.target as HTMLInputElement)?.value; + if (searchText !== '') { + searchText = searchText.trim(); + } + + if ($event.target['_value'] !== searchText) { + handleChangeDebounce($event); + } + } + function onDisplayTextChange(displayText: string) { const selectedItems = getSelectedItemsByDisplayText(displayText); onSelectionChange(selectedItems); @@ -60,20 +139,10 @@ export default defineComponent({ } ); - const originalValue = ref(); - const onBeforeOpen = () => { originalValue.value = cloneDeep(dataSource.value); }; - const showPopover = computed(() => { - const popoverInstance = comboEditorRef.value?.popoverRef; - if (popoverInstance) { - return popoverInstance.shown; - } - return false; - }); - return () => { return {showPopover.value && ; export type ComboTreeProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/combo-tree/src/components/tree-container.component.tsx b/packages/ui-vue/components/combo-tree/src/components/tree-container.component.tsx index 910d9b484169f5bdfe72d5a50df65476a30f0a36..0725f72caa84de9f3b458a91c1176d28e70fa510 100644 --- a/packages/ui-vue/components/combo-tree/src/components/tree-container.component.tsx +++ b/packages/ui-vue/components/combo-tree/src/components/tree-container.component.tsx @@ -1,4 +1,4 @@ -import { SetupContext, computed, defineComponent, inject, onMounted, ref } from "vue"; +import { SetupContext, computed, defineComponent, inject, onMounted, ref, watch } from "vue"; import FTreeView from '@farris/ui-vue/components/tree-view'; import { ComboTreeHttpService } from "../combo-tree.props"; import { TreeContainerProps, treeContainerProps } from "./tree-container.props"; @@ -20,6 +20,12 @@ export default defineComponent({ customRowStatus: props.customRowStatus }; + watch(() => props.data, (newValue) => { + dataSource.value = newValue; + treeViewRef.value?.updateDataSource(newValue); + }); + + const selectOptions = { enableSelectRow: true, multiSelect: props.multiSelect, @@ -54,6 +60,11 @@ export default defineComponent({ styleObject.maxHeight = `${maxHeight.value}px`; styleObject.overflow = 'auto'; } + + if (dataSource.value.length === 0) { + styleObject.height = '200px'; + } + return styleObject; }); @@ -75,6 +86,8 @@ export default defineComponent({ } }); + context.expose({ treeInstance: treeViewRef }); + return () => { return (
diff --git a/packages/ui-vue/components/combo-tree/src/schema/combo-tree.schema.json b/packages/ui-vue/components/combo-tree/src/schema/combo-tree.schema.json index 4eddbe962b7846f3e41d5c79551f5303b24de309..26ff02e8b7a16427736bf52394abab49417a6c68 100644 --- a/packages/ui-vue/components/combo-tree/src/schema/combo-tree.schema.json +++ b/packages/ui-vue/components/combo-tree/src/schema/combo-tree.schema.json @@ -137,6 +137,16 @@ "description": "", "type": "boolean", "default": false + }, + "viewType": { + "description": "", + "type": "string", + "default": "text" + }, + "searchFields": { + "description": "", + "type": "array", + "default": [] } }, "required": [ diff --git a/packages/ui-vue/components/common/radio-checkbox/use-check.ts b/packages/ui-vue/components/common/radio-checkbox/use-check.ts index ab16d3249fdd60e3caafa55fac305154bd1c8dfc..78570a8ae9400b53d7822b5b2b8e17ca93b95597 100644 --- a/packages/ui-vue/components/common/radio-checkbox/use-check.ts +++ b/packages/ui-vue/components/common/radio-checkbox/use-check.ts @@ -22,7 +22,8 @@ export function useCheck( // 如果是group const checked = computed(() => parentProps ? parentProps.modelValue === props.value || parentProps.modelValue.includes(props.value) : - !!props.checked || !!props.modelValue); + props.trueValue != null ? props.modelValue === props.trueValue: !!props.modelValue + ); // 按钮样式 const buttonClass = computed(() => { @@ -85,8 +86,8 @@ export function useCheck( } } else { context.emit('update:checked', !checked.value); - context.emit('update:modelValue', !checked.value); - context.emit('changeValue', !checked.value); + context.emit('update:modelValue', !checked.value? props.trueValue : props.falseValue); + context.emit('changeValue', !checked.value? props.trueValue : props.falseValue); context.emit('change', { originalEvent: e, checked: !checked.value }); } }; diff --git a/packages/ui-vue/components/data-grid/src/property-config/data-grid.property-config.ts b/packages/ui-vue/components/data-grid/src/property-config/data-grid.property-config.ts index d194efe246f95f2c36d6abee0f45944239fd6486..949fc26a93cef9c9af33beb087293c3976365c48 100644 --- a/packages/ui-vue/components/data-grid/src/property-config/data-grid.property-config.ts +++ b/packages/ui-vue/components/data-grid/src/property-config/data-grid.property-config.ts @@ -274,7 +274,10 @@ export class DataGridProperty extends BaseControlProperty { properties[self.viewModelId] = { type: 'events-editor', editor: { - initialData + initialData, + viewSourceHandle: (commandInfo: any) => { + self.eventsEditorUtils.jumpToMethod(commandInfo); + } } }; this.propertyConfig.categories['eventsEditor'] = { diff --git a/packages/ui-vue/components/events-editor/src/components/interaction-item/interaction-item.component.tsx b/packages/ui-vue/components/events-editor/src/components/interaction-item/interaction-item.component.tsx index 9db1d1f9fd26eb9c31936a4cb4751a923b539ebc..c227d7ead710081af4723156481eb41661087126 100644 --- a/packages/ui-vue/components/events-editor/src/components/interaction-item/interaction-item.component.tsx +++ b/packages/ui-vue/components/events-editor/src/components/interaction-item/interaction-item.component.tsx @@ -268,6 +268,15 @@ export default defineComponent({ interaction.value.command = cloneDeep(changedCommand); emitFinalState(false, null); } + + function onViewSource($event) { + const {controller} = interaction.value; + if (controller.isCommon) { + return; + } + props.viewSourceHandle?.({...$event, ...interaction.value }); + } + function renderMethodInfo() { return onParameterEditorConfirmHandler()} onChange={(event) => onParameterEditorChangeHandler(event)} onTargetChange={(event) => onParameterEditorTargetChangeHandler(event)} + onViewSource={($event) => onViewSource($event)} >; } diff --git a/packages/ui-vue/components/events-editor/src/components/interaction-item/interaction-item.props.ts b/packages/ui-vue/components/events-editor/src/components/interaction-item/interaction-item.props.ts index e75849cbfb03297709e00f9f3f32dc755da561ae..874b827f1b0860619049f300a3bf980382d6aaa1 100644 --- a/packages/ui-vue/components/events-editor/src/components/interaction-item/interaction-item.props.ts +++ b/packages/ui-vue/components/events-editor/src/components/interaction-item/interaction-item.props.ts @@ -7,6 +7,7 @@ export const interactionItemProps = { interaction: { type: Object as PropType, default: {} }, controllers: { type: Array, default: [] }, sourceCommunication: { type: Object, default: {} }, + viewSourceHandle: {type: Function as PropType<() => void>, default: () => { }} } as Record; export type InteractionItemProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/events-editor/src/components/parameter-editor/parameter-editor.component.tsx b/packages/ui-vue/components/events-editor/src/components/parameter-editor/parameter-editor.component.tsx index e13e3dac2a84abd6e0656d5730eeb4164166bf04..3beac15c0465617cfcce5cf61b1d961ba715ab38 100644 --- a/packages/ui-vue/components/events-editor/src/components/parameter-editor/parameter-editor.component.tsx +++ b/packages/ui-vue/components/events-editor/src/components/parameter-editor/parameter-editor.component.tsx @@ -20,7 +20,7 @@ import { cloneDeep } from "lodash-es"; export default defineComponent({ name: 'FParameterEditor', props: parameterEditorProps, - emits: ['confirm', 'change', 'targetChange'] as (string[] & ThisType) | undefined, + emits: ['confirm', 'change', 'targetChange', 'viewSource'] as (string[] & ThisType) | undefined, setup(props: ParameterEditorProps, context: SetupContext) { const notifyService = new NotifyService(); const command = ref(cloneDeep(props.command)); @@ -229,6 +229,12 @@ export default defineComponent({ {renderParameterEditors()}
; } + + function onViewSource($event: MouseEvent) { + $event.stopPropagation(); + context.emit('viewSource', {command: command.value, event: $event}); + } + /** * 显示方法名称和编辑按钮 * 显示参数列表 @@ -240,7 +246,8 @@ export default defineComponent({
{/* 参数编辑第一行-方法名称 */}
- {command.value.name} + {command.value.name} +
{showAbandonedIcon.value && renderAbandonedIcon()} {showDeletedIcon.value && renderDeletedIcon()} diff --git a/packages/ui-vue/components/events-editor/src/events-editor.component.tsx b/packages/ui-vue/components/events-editor/src/events-editor.component.tsx index 01a2c32df1158b9d60eacd0bf7300b3349fe877b..eb509830eb9e4cd6b464a4e9b85910d79cdbbf10 100644 --- a/packages/ui-vue/components/events-editor/src/events-editor.component.tsx +++ b/packages/ui-vue/components/events-editor/src/events-editor.component.tsx @@ -71,7 +71,8 @@ export default defineComponent({ collapsed={collapsed} onDelete={onDeleteInteraction} sourceCommunication={initialData.value.sourceCommunication} - > + viewSourceHandle={props.viewSourceHandle} + > ); } diff --git a/packages/ui-vue/components/events-editor/src/events-editor.props.ts b/packages/ui-vue/components/events-editor/src/events-editor.props.ts index 9f255bbfd14d2703a73eff92cab2312404adc1cb..8ba507b48039b5cad5ad74802802d6c39da474f7 100644 --- a/packages/ui-vue/components/events-editor/src/events-editor.props.ts +++ b/packages/ui-vue/components/events-editor/src/events-editor.props.ts @@ -8,7 +8,8 @@ import propertyConfig from './property-config/events-editor.property-config.json import { initialData } from '../src/data/initial-data'; export const eventsEditorProps = { - initialData: { type: Object as PropType, default: initialData } + initialData: { type: Object as PropType, default: initialData }, + viewSourceHandle: {type: Function as PropType<() => void>, default: () => { }} } as Record; export type EventsEditorProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/events-editor/src/schema/events-editor.schema.json b/packages/ui-vue/components/events-editor/src/schema/events-editor.schema.json index ae6254393b7f5762ac7d0397ff842c12da495c73..c5f92905004c64dbd4d2601e8c86e7b5a034a9dc 100644 --- a/packages/ui-vue/components/events-editor/src/schema/events-editor.schema.json +++ b/packages/ui-vue/components/events-editor/src/schema/events-editor.schema.json @@ -18,7 +18,12 @@ "description": "", "type": "object", "default": {} - } + }, + "viewSourceHandle": { + "description": "", + "type": "function", + "default": null + } }, "required": [ "id", diff --git a/packages/ui-vue/components/events-editor/src/types.ts b/packages/ui-vue/components/events-editor/src/types.ts index b84288e6686c5397f5c77111523ba5d982f8dcde..bde84623f1cb0a22d5506a27a6217f5f5351346e 100644 --- a/packages/ui-vue/components/events-editor/src/types.ts +++ b/packages/ui-vue/components/events-editor/src/types.ts @@ -55,6 +55,8 @@ export interface ControllerName { label: string; /** 控制器中文名 */ name: string; + /** 是否为内置控制器 */ + isCommon?: boolean; } /** 内置构件对应的单个控制器方法列表 */ diff --git a/packages/ui-vue/components/lookup/src/lookup.component.tsx b/packages/ui-vue/components/lookup/src/lookup.component.tsx index 7d6e1e9d773e085bfb1b5a6d9335c7892608f29c..b2b994d70a3759c43ab658d842bfaea86c714833 100644 --- a/packages/ui-vue/components/lookup/src/lookup.component.tsx +++ b/packages/ui-vue/components/lookup/src/lookup.component.tsx @@ -33,7 +33,7 @@ import { useUserData } from './composition/use-user-data'; import { useSelections } from './composition/use-selections'; import { useContext } from './composition/use-context'; import { usePopup } from './composition/use-popup'; -import { debounce, isEmpty, throttle } from 'lodash-es'; +import { debounce } from 'lodash-es'; export default defineComponent({ diff --git a/packages/ui-vue/components/lookup/src/lookup.props.ts b/packages/ui-vue/components/lookup/src/lookup.props.ts index 70267ccae72378d29cb45eddfc49ee5dd15a277e..bf3f7aa5175ce134225622b1e21a8e93dee16b6e 100644 --- a/packages/ui-vue/components/lookup/src/lookup.props.ts +++ b/packages/ui-vue/components/lookup/src/lookup.props.ts @@ -56,7 +56,7 @@ export const lookupProps = { enableMultiFieldSearch: { type: Boolean, default: false }, treeToList: { type: Boolean, default: false }, navTreeToList: { type: Boolean, default: false }, - loadTreeDataType:{ type: String as PropType, default: LoadTreeDataType.all }, + loadTreeDataType:{ type: String as PropType, default: LoadTreeDataType.default }, enableToSelect: { type: Boolean, default: true }, customData: { type: Object, default: null }, modelValue: { type: String, default: '' }, diff --git a/packages/ui-vue/components/lookup/src/property-config/lookup.property-config.ts b/packages/ui-vue/components/lookup/src/property-config/lookup.property-config.ts index 742f5bb775a275a74016f1d8f45514dd97488dfd..eb765d52db65d44ac749db01098c3bd198fdb226 100644 --- a/packages/ui-vue/components/lookup/src/property-config/lookup.property-config.ts +++ b/packages/ui-vue/components/lookup/src/property-config/lookup.property-config.ts @@ -12,7 +12,6 @@ import { DesignerComponentInstance } from "@farris/ui-vue/components/designer-ca import { LookupEvents } from "./lookup-events"; import { cascadeItems } from "../composition/types"; import { ExpressionProperty } from "@farris/ui-vue/components/property-panel"; -import { isUndefined } from "util"; export const LookupSchemaRepositoryToken = Symbol('schema_repository_token'); @@ -423,19 +422,22 @@ export class LookupPropertyConfig extends InputBaseProperty { formBasicInfo: this.formSchemaUtils.getFormMetadataBasicInfo() }, fromData: { - editable: false, + editable: true, formatter: (cell, data) => { return `${data.raw['name']} [${data.raw['bindingPath']}]`; }, idField: 'bindingPath', textField: 'name', valueField: 'bindingPath', + searchFields: ['name', 'bindingPath'], repositoryToken: FieldSelectorRepositoryToken }, toData: { + editable: false, idField: 'bindingPath', textField: 'name', valueField: 'bindingPath', + searchFields: ['name', 'bindingPath'], dataSource: () => { const fields = this.designViewModelUtils.getAllFields2TreeByVMId(this.viewModelId); const primaryField = this.schemaService.getPrimaryField(); @@ -551,8 +553,8 @@ export class LookupPropertyConfig extends InputBaseProperty { editor: { ...this.comboListEditor, data: [ - { text: '弹出窗口', value: 'Modal' }, - { text: '下拉面板', value: 'Popup' }, + { text: '弹出窗口', value: 'Modal'}, + { text: '下拉面板', value: 'Popup'}, ], valueField: 'value', textField: 'text', @@ -785,8 +787,7 @@ export class LookupPropertyConfig extends InputBaseProperty { textField: 'text', valueField: 'value' }, - // visible: this.showLoadType(editorOptions), - visible: false + visible: this.showLoadType(editorOptions) }, enableFullTree: { description: "启用构造完整树", @@ -808,30 +809,27 @@ export class LookupPropertyConfig extends InputBaseProperty { refreshPanelAfterChanged: true, title: "启用级联选择", type: "boolean", - // visible: this.isTree(editorOptions) && !!editorOptions.multiSelect, - visible: false + visible: this.isTree(editorOptions) && !!editorOptions.multiSelect }, showCascadeControl: { description: "显示级联选择控件", $converter: lookupDefaultConverter, title: "显示级联选择控件", type: "boolean", - // visible: !!editorOptions.enableCascade && this.isTree(editorOptions), - visible: false + visible: !!editorOptions.enableCascade && this.isTree(editorOptions) }, cascadeValue: { description: "级联选择默认状态", $converter: lookupDefaultConverter, title: "级联状态", type: "string", - // visible: !!editorOptions.enableCascade && this.isTree(editorOptions), + visible: !!editorOptions.enableCascade && this.isTree(editorOptions), editor: { ...this.comboListEditor, data: cascadeItems, textField: 'text', valueField: 'value' - }, - visible: false + } }, expandLevel: { description: "默认展开层级: 0: 不展开; -1: 全部展开;>0: 展开到指定级数 ", @@ -860,7 +858,10 @@ export class LookupPropertyConfig extends InputBaseProperty { properties[self.viewModelId] = { type: 'events-editor', editor: { - initialData + initialData, + viewSourceHandle: (commandInfo: any) => { + self.eventsEditorUtils.jumpToMethod(commandInfo); + } } }; return { diff --git a/packages/ui-vue/components/mapping-editor/src/mapping-editor.component.tsx b/packages/ui-vue/components/mapping-editor/src/mapping-editor.component.tsx index e7ad529a8ea2e5e25eaf6219e7c7c4e3f99ba59e..8b32dca26c5c1e5e94f00a7ebe8af880ef0c1255 100644 --- a/packages/ui-vue/components/mapping-editor/src/mapping-editor.component.tsx +++ b/packages/ui-vue/components/mapping-editor/src/mapping-editor.component.tsx @@ -63,8 +63,9 @@ export default defineComponent({ valueField: props.fromData.valueField || 'id', formatter: props.fromData.formatter, editorParams: props.fromData.editorParams, - editable: false, - customRowStatus: treeNodeStatus + editable: props.fromData.editable || false, + searchFields: props.fromData.searchFields || [], + customRowStatus: treeNodeStatus, },formatter: (cell, row) => { return flattenFromDataSource.value.find(item => item[props.fromData.valueField] === row.raw.sourceField)?.[props.fromData.textField || 'name'] || ''; } @@ -78,8 +79,9 @@ export default defineComponent({ valueField: props.toData.valueField || 'id', formatter: props.toData.formatter, editorParams: props.toData.editorParams, - multiSelect: false, - editable: false, + multiSelect: true, + editable: props.toData.editable || false, + searchFields: props.toData.searchFields || [], customRowStatus: treeNodeStatus }, formatter: (cell, row) => { if (row.raw.targetField) { @@ -280,10 +282,10 @@ export default defineComponent({ } function onBeforeEditCell(event: any) { - const { column } = event; + const { column, rawData } = event; const { field, editor } = column; if (field === 'sourceField' && editor) { - const disabledFields = mappingFieldList.value.map((item: any) => { + const disabledFields = mappingFieldList.value.filter((item: any) => item.sourceField !== rawData.sourceField).map((item: any) => { return item.sourceField; }); diff --git a/packages/ui-vue/components/number-spinner/src/composition/use-text-box.ts b/packages/ui-vue/components/number-spinner/src/composition/use-text-box.ts index 81f2714d4a1f65c75b9c93c79c6b5c4d2f4391f0..108e059aa795b6ab2fbfb5e9c67fdf0e45faea24 100644 --- a/packages/ui-vue/components/number-spinner/src/composition/use-text-box.ts +++ b/packages/ui-vue/components/number-spinner/src/composition/use-text-box.ts @@ -29,7 +29,9 @@ export function useTextBox( } const textValue = cleanFormat(inputValue); displayValue.value = format(getRealValue(textValue)); - onNumberValueChanged(getRealValue(textValue)); + if (props.updateOn === 'blur') { + onNumberValueChanged(getRealValue(textValue)); + } context.emit('blur', { event: $event, formatted: displayValue.value, value: modelValue.value }); } @@ -56,8 +58,11 @@ export function useTextBox( inputValue = inputValue || 0; } const textValue = cleanFormat(inputValue); - displayValue.value = textValue; - onNumberValueChanged(getRealValue(textValue)); + if (props.updateOn === 'change') { + displayValue.value = textValue; + onNumberValueChanged(getRealValue(textValue)); + } + context.emit('input', getRealValue(textValue)); } diff --git a/packages/ui-vue/components/number-spinner/src/number-spinner.props.ts b/packages/ui-vue/components/number-spinner/src/number-spinner.props.ts index 8bf68c6fcddd80658937871f2e8eac57cd3a19aa..5a434617cfdc6556c754b5ae7e1d4d50a89b9cd6 100644 --- a/packages/ui-vue/components/number-spinner/src/number-spinner.props.ts +++ b/packages/ui-vue/components/number-spinner/src/number-spinner.props.ts @@ -134,7 +134,10 @@ export const numberSpinnerProps = { /** * 是否启用大数 */ - bigNumber: { type: Boolean, default: false } + bigNumber: { type: Boolean, default: false }, + + /** 更新方式 blur | change */ + updateOn: { type: String, default: 'change' }, } as Record; export type NumberSpinnerProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/number-spinner/src/schema/number-spinner.schema.json b/packages/ui-vue/components/number-spinner/src/schema/number-spinner.schema.json index b1e4dfdf30d69c01c273bf5853528c2ef21b6119..a76e4e50ab16b65db14611ed186e62b1010dfc9d 100644 --- a/packages/ui-vue/components/number-spinner/src/schema/number-spinner.schema.json +++ b/packages/ui-vue/components/number-spinner/src/schema/number-spinner.schema.json @@ -124,6 +124,16 @@ "description": "", "type": "boolean", "default": false + }, + "updateOn": { + "description": "", + "type": "string", + "default": "blur" + }, + "showButton":{ + "description": "", + "type": "boolean", + "default": true } }, "required": [ diff --git a/packages/ui-vue/components/property-panel/src/composition/entity/input-base-property.ts b/packages/ui-vue/components/property-panel/src/composition/entity/input-base-property.ts index b6d9671d2a485f72d2a0dcfa992640b002cd004d..d0b3f0643edb8955daf3552579d191df276ccf7e 100644 --- a/packages/ui-vue/components/property-panel/src/composition/entity/input-base-property.ts +++ b/packages/ui-vue/components/property-panel/src/composition/entity/input-base-property.ts @@ -581,7 +581,10 @@ export class InputBaseProperty extends BaseControlProperty { properties[self.viewModelId] = { type: 'events-editor', editor: { - initialData + initialData, + viewSourceHandle: (commandInfo: any) => { + self.eventsEditorUtils.jumpToMethod(commandInfo); + } } }; const eventsEditorConfig = { @@ -623,4 +626,60 @@ export class InputBaseProperty extends BaseControlProperty { propertyData, type, expressionTypes, associationCallBack); } + + protected getBindingDataType() { + return this.designViewModelField.type.name === 'Number'? 'number': this.designViewModelField.type.name === 'String'? 'string': 'boolean'; + } + + private numberEditorOptions = { + type: "number-spinner", + useThousands: false, + keyboard: false, + showButton: false + }; + + protected getEditor() { + return this.getBindingDataType() === 'number'? this.numberEditorOptions: {}; + } + + protected getTrueValueConverter() { + return { + convertFrom: (schema: Record, propertyKey: string) => { + return schema.editor[propertyKey]; + }, + convertTo: (schema: Record, propertyKey: string, propertyValue: any) => { + let newValue = propertyValue; + const dataType = this.getBindingDataType(); + if (dataType === 'string') { + newValue = (newValue || '').trim() || 'true'; + } + + if (schema.editor['falseValue'] === newValue) { + newValue = newValue + 1; + } + schema.editor[propertyKey] = newValue; + } + }; + } + + protected getFalseValueConverter() { + return { + convertFrom: (schema: Record, propertyKey: string) => { + return schema.editor[propertyKey]; + }, + convertTo: (schema: Record, propertyKey: string, propertyValue: any) => { + let newValue = propertyValue; + const dataType = this.getBindingDataType(); + if (dataType === 'string') { + newValue = (newValue || '').trim() || 'false'; + } + if (schema.editor['trueValue'] === newValue) { + newValue = newValue + 1; + } + schema.editor[propertyKey] = newValue; + } + }; + } + + } diff --git a/packages/ui-vue/components/property-panel/src/composition/entity/schema-dom-mapping.ts b/packages/ui-vue/components/property-panel/src/composition/entity/schema-dom-mapping.ts index 7900e2ea6a161cb7b2ea5233d9765414a53ab46a..1cacf76d8f5e073181ce40de4328e557fe16c7ed 100644 --- a/packages/ui-vue/components/property-panel/src/composition/entity/schema-dom-mapping.ts +++ b/packages/ui-vue/components/property-panel/src/composition/entity/schema-dom-mapping.ts @@ -12,6 +12,8 @@ export class SchemaDOMMapping { { key: DgControl['input-group'].type, value: DgControl['input-group'].name }, { key: DgControl['lookup'].type, value: DgControl['lookup'].name }, { key: DgControl['date-picker'].type, value: DgControl['date-picker'].name }, + { key: DgControl['switch'].type, value: DgControl['switch'].name }, + { key: DgControl['check-box'].type, value: DgControl['check-box'].name }, { key: DgControl['check-group'].type, value: DgControl['check-group'].name }, { key: DgControl['radio-group'].type, value: DgControl['radio-group'].name }, { key: DgControl['combo-list'].type, value: DgControl['combo-list'].name }, @@ -29,7 +31,9 @@ export class SchemaDOMMapping { { key: DgControl['number-spinner'].type, value: DgControl['number-spinner'].name } ], Number: [ - { key: DgControl['number-spinner'].type, value: DgControl['number-spinner'].name } + { key: DgControl['number-spinner'].type, value: DgControl['number-spinner'].name }, + { key: DgControl['switch'].type, value: DgControl['switch'].name }, + { key: DgControl['check-box'].type, value: DgControl['check-box'].name } ], BigNumber: [ { key: DgControl['number-spinner'].type, value: DgControl['number-spinner'].name } diff --git a/packages/ui-vue/components/switch/src/property-config/switch.property-config.ts b/packages/ui-vue/components/switch/src/property-config/switch.property-config.ts index c2479dc9f489f85ac0b42dbb5be8ae733c3ba945..77947a3711353e9a76a894acac597d87f03840eb 100644 --- a/packages/ui-vue/components/switch/src/property-config/switch.property-config.ts +++ b/packages/ui-vue/components/switch/src/property-config/switch.property-config.ts @@ -7,7 +7,9 @@ export class SwitchProperty extends InputBaseProperty { } getEditorProperties(propertyData: any) { - return this.getComponentConfig(propertyData, { type:"switch" }, { + return this.getComponentConfig(propertyData, { + type:"switch" + }, { disabled: { visible:false }, @@ -16,22 +18,22 @@ export class SwitchProperty extends InputBaseProperty { }, onLabel: { description: "", - title: "打开时标签", + title: "打开标签", type: "string" }, offLabel: { description: "", - title: "关闭时标签", + title: "关闭标签", type: "string" }, onBackground: { description: "值可以是颜色或者16进制颜色字符串,比如:blue或者#2A87FF", - title: "打开时背景色", + title: "打开背景色", type: "string" }, offBackground: { description: "值可以是颜色或者16进制颜色字符串,比如:gray或者#D9DEE7", - title: "关闭时背景色", + title: "关闭背景色", type: "string" }, size:{ @@ -54,6 +56,24 @@ export class SwitchProperty extends InputBaseProperty { } ] } + }, + trueValue: { + description: "打开值", + title: "打开时的值", + type: this.getBindingDataType(), + visible: this.designViewModelField.type.name !== 'Boolean', + refreshPanelAfterChanged: true, + editor: this.getEditor(), + $converter: this.getTrueValueConverter() + }, + falseValue: { + description: "关闭值", + title: "关闭时的值", + type: this.getBindingDataType(), + visible: this.designViewModelField.type.name !== 'Boolean', + refreshPanelAfterChanged: true, + editor: this.getEditor(), + $converter: this.getFalseValueConverter() } }); } diff --git a/packages/ui-vue/components/switch/src/schema/switch.schema.json b/packages/ui-vue/components/switch/src/schema/switch.schema.json index 5bc93c801074b203c30e4fa20b0c6faff3a343ed..8558ca4a7680c354f4159d6d0639772436c6a7ed 100644 --- a/packages/ui-vue/components/switch/src/schema/switch.schema.json +++ b/packages/ui-vue/components/switch/src/schema/switch.schema.json @@ -80,6 +80,16 @@ "description": "", "type": "boolean", "default": false + }, + "trueValue": { + "description": "", + "type": "boolean", + "default": true + }, + "falseValue": { + "description": "", + "type": "boolean", + "default": false } }, "required": [ diff --git a/packages/ui-vue/components/switch/src/switch.component.tsx b/packages/ui-vue/components/switch/src/switch.component.tsx index 4f9aba59a15e67aba9585d76cd032d6b922a0065..fb8970c20ee31f6989d55eeffc1ee246983a57df 100644 --- a/packages/ui-vue/components/switch/src/switch.component.tsx +++ b/packages/ui-vue/components/switch/src/switch.component.tsx @@ -23,9 +23,12 @@ export default defineComponent({ emits: ['update:modelValue', 'modelValueChanged'] as (string[] & ThisType) | undefined, setup(props: SwitchProps, context: SetupContext) { const { disabled, size, onLabel, offLabel, onBackground, offBackground, onColor, offColor, readonly } = toRefs(props); - const modelValue = ref(!!props.modelValue); + const modelValue = ref(props.modelValue); const switchRef = ref(); const titleText =ref(""); + + const checked = computed(() => props.trueValue != null ? modelValue.value === props.trueValue: !!modelValue.value); + function getSwitchColor() { return ''; } @@ -33,7 +36,7 @@ export default defineComponent({ const switchContainerClass = computed(() => ({ switch: true, 'f-cmp-switch': true, - checked: modelValue.value, + checked: checked.value, disabled: readonly.value || disabled.value, 'switch-large': size.value === 'large', 'switch-medium': size.value === 'medium', @@ -41,7 +44,7 @@ export default defineComponent({ })); const switchContainerStyle = computed(() => { - if (modelValue.value) { + if (checked.value) { return { outline: 'none', background: onBackground.value, @@ -55,7 +58,7 @@ export default defineComponent({ }); const smallStyle = computed(() => { - if (modelValue.value) { + if (checked.value) { return { background: onColor.value, }; @@ -92,7 +95,7 @@ export default defineComponent({ if (readonly.value || disabled.value) { return; } - modelValue.value = !modelValue.value; + modelValue.value = !checked.value ? props.trueValue: props.falseValue; context.emit('update:modelValue', modelValue.value); changeTitleText(); // context.emit('modelValueChanged', modelValue.value); diff --git a/packages/ui-vue/components/switch/src/switch.props.ts b/packages/ui-vue/components/switch/src/switch.props.ts index dbf1414cab3ffb091b2113e07f98299f12dae8d4..fe679ed35e4c0812aa3f030a7caf9fa85ca30af2 100644 --- a/packages/ui-vue/components/switch/src/switch.props.ts +++ b/packages/ui-vue/components/switch/src/switch.props.ts @@ -22,7 +22,7 @@ import { schemaResolver } from './schema/schema-resolver'; export type SwitchType = 'small' | 'medium' | 'large'; export const switchProps = { - modelValue: { type: Boolean, default: false }, + modelValue: { type: [String, Number, Boolean], default: false }, /** 禁用 */ disabled: { type: Boolean, default: false }, /** @@ -39,7 +39,9 @@ export const switchProps = { size: { type: String as PropType, default: 'medium' }, /** 开关值变化事件 */ onModelValueChanged: { type: Function, default: () => { } }, - focusOnCreated: { type: Boolean, default: false } + focusOnCreated: { type: Boolean, default: false }, + trueValue: { type: [String, Number, Boolean], default: true }, + falseValue: { type: [String, Number, Boolean], default: false } } as Record; export type SwitchProps = ExtractPropTypes; diff --git a/packages/ui-vue/demos/checkbox/checkbox.vue b/packages/ui-vue/demos/checkbox/checkbox.vue index 887801950225acb40d6b91b9ece17c8e49dd7166..779b22952901b7d9dfb1c0d444134cb646283b4d 100644 --- a/packages/ui-vue/demos/checkbox/checkbox.vue +++ b/packages/ui-vue/demos/checkbox/checkbox.vue @@ -1,7 +1,8 @@ @@ -10,10 +11,13 @@ const disabled = ref(false);
禁用:
- + 单个复选框 + +

Value: {{ value }}; Checkd: {{ checkedValue }}

+ 中间值状态 diff --git a/packages/ui-vue/demos/switch/text.vue b/packages/ui-vue/demos/switch/text.vue index 3ce7ef767771c209b5492af8581826b903b7d37a..69f532066c31f3406c2d7a34d012c39e1404ef59 100644 --- a/packages/ui-vue/demos/switch/text.vue +++ b/packages/ui-vue/demos/switch/text.vue @@ -1,7 +1,7 @@