From 5ba3c427cae9dd75e507e654e22ad0413aac639e Mon Sep 17 00:00:00 2001 From: lijisanxiong <1518062161@qq.com> Date: Sun, 28 Apr 2024 17:36:02 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E7=BC=96=E8=BE=91=E5=99=A8=E7=BC=96=E8=BE=91=E6=80=81?= =?UTF-8?q?=E5=92=8C=E5=8F=AA=E8=AF=BB=E6=80=81=E5=88=87=E6=8D=A2=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E3=80=81=E5=85=A8=E5=B1=8F=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E5=9D=87=E7=94=B1=E7=BC=96=E8=BE=91=E5=99=A8=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../code/monaco-editor/monaco-editor.scss | 136 ++++++++- .../code/monaco-editor/monaco-editor.tsx | 258 +++++++++++++++++- src/locale/en/index.ts | 11 +- src/locale/zh-CN/index.ts | 10 +- 4 files changed, 400 insertions(+), 15 deletions(-) diff --git a/src/editor/code/monaco-editor/monaco-editor.scss b/src/editor/code/monaco-editor/monaco-editor.scss index c246aac2..906fb26b 100644 --- a/src/editor/code/monaco-editor/monaco-editor.scss +++ b/src/editor/code/monaco-editor/monaco-editor.scss @@ -1,6 +1,138 @@ @include b(code) { - display: inline-block; + display: flex; + flex-direction: column; width: 100%; height: 100%; - min-height: 200px; + @include e(box) { + width: 100%; + height: 100%; + min-height: 200px; + overflow: hidden; + } +} + +@include b('code-toolbar') { + display: flex; + align-items: center; + justify-content: end; + width: 100%; + min-height: 32px; + padding-right: getCssVar(spacing, base); + font-size: getCssVar(font-size, header-6); + + &>*+* { + margin-left: getCssVar(spacing, base-loose); + } + + i { + cursor: pointer; + + &:hover { + color: #{getCssVar(color, primary)}; + } + } +} + +@include b('code-footer') { + display: flex; + align-items: center; + justify-content: end; + width: 100%; + min-height: 36px; + margin-top: getCssVar(spacing, base-tight); + margin-right: getCssVar(spacing, tight); + + &>*+* { + margin-left: getCssVar(spacing, base);; + } + + + @include e('cancel') { + height: 36px; + line-height: 36px; + color: #{getCssVar(color, text, 1)}; + cursor: pointer; + opacity: 0.7; + + &:hover { + color: #{getCssVar(color, primary)}; + opacity: 1; + } + } + + @include e('save') { + width: 96px; + height: 36px; + line-height: 36px; + color: #{getCssVar(color, primary, active, text)}; + text-align: center; + cursor: pointer; + background-color: #{getCssVar(color, primary)}; + border-radius: 5px; + + &:hover { + box-shadow: 0 2px 5px 1px #{getCssVar(color, primary)}; + } + } +} + +@include b('code-message') { + width: 500px; + max-width: unset; + + @include e('message-content') { + @include m('message-tip') { + color: #{getCssVar(color, text, 3)}; + } + } + + @include e('message-cancel') { + color: #{getCssVar(color, text, 1)}; + background-color: transparent; + + &:hover { + color: #{getCssVar(color, primary)}; + background-color: transparent; + } + } + + @include e('message-comfire') { + background-color: #{getCssVar(color, danger)} !important; + + &:hover { + box-shadow: 0 2px 5px 1px #{getCssVar(color, danger)}; + } + } +} + +@include b('code-editor-enable') { + .#{bem(code__box)} { + // 减去工具栏高度与底部操作按钮高度 + height: calc(100% - (32px + 36px)); + } +} + +@include b('code-dialog-full-screen') { + height: 80% !important; + + @include b('code') { + gap: 0; + padding: 0 getCssVar(spacing, extra-loose); + + --w-e-toolbar-bg-color: #{getCssVar(color, bg, 0)}; + + .#{bem(code__box)} { + // 减去工具栏高度与底部操作按钮高度 + height: calc(100% - (56px + 68px)); + } + + .#{bem(code-toolbar)} { + min-height: 56px; + } + } +} + +@include b('code-footer-dialog') { + min-height: 68px; + margin-top: 0; } diff --git a/src/editor/code/monaco-editor/monaco-editor.tsx b/src/editor/code/monaco-editor/monaco-editor.tsx index 208ca3ec..fbfa3788 100644 --- a/src/editor/code/monaco-editor/monaco-editor.tsx +++ b/src/editor/code/monaco-editor/monaco-editor.tsx @@ -13,6 +13,7 @@ import { useNamespace, useUIStore, } from '@ibiz-template/vue3-util'; +import { ElMessageBox } from 'element-plus'; import * as monaco from 'monaco-editor'; import loader from '@monaco-editor/loader'; import { CodeEditorController } from '../code-editor.controller'; @@ -24,9 +25,43 @@ export const IBizCode = defineComponent({ setup(props, { emit }) { const ns = useNamespace('code'); + const c = props.controller!; + const currentVal = ref(''); - let editor: monaco.editor.IStandaloneCodeEditor; + // 允许编辑 + const enableEdit = ref(true); + + // 是否存在编辑器参数enableEdit + const hasEnableEdit = ref(false); + + // 只读状态 + const readonlyState = ref(false); + + // 允许全屏打开 + const enableFullScreen = ref(false); + + // 是否全屏 + const isFullScreen = ref(false); + + const editorModel = c.model; + if (editorModel.editorParams) { + if (editorModel.editorParams.enableEdit) { + hasEnableEdit.value = true; + readonlyState.value = true; + enableEdit.value = + c.toBoolean(editorModel.editorParams.enableEdit) && + !props.readonly && + !props.disabled; + } + if (editorModel.editorParams.enableFullScreen) { + enableFullScreen.value = c.toBoolean( + editorModel.editorParams.enableFullScreen, + ); + } + } + + let editor: monaco.editor.IStandaloneCodeEditor | null; let monacoEditor: typeof monaco.editor; const { UIStore } = useUIStore(); @@ -58,7 +93,15 @@ export const IBizCode = defineComponent({ if (!editor) { return; } - editor.updateOptions({ readOnly: props.readonly || props.disabled }); + if (props.readonly || props.disabled) { + hasEnableEdit.value = false; + readonlyState.value = true; + } + editor.updateOptions({ + readOnly: hasEnableEdit.value + ? readonlyState.value + : props.readonly || props.disabled, + }); }; watch(() => props.readonly, updateEditorOptions, { immediate: true }); @@ -87,31 +130,179 @@ export const IBizCode = defineComponent({ minimap: { enabled: true, }, - readOnly: props.readonly || props.disabled, // 只读 - readOnlyMessage: { value: '当前为只读模式,不可编辑' }, + readOnly: hasEnableEdit.value + ? readonlyState.value + : props.readonly || props.disabled, // 只读 + readOnlyMessage: { + value: ibiz.i18n.t('editor.code.readOnlyPrompt'), + }, fontSize: 16, // 字体大小 scrollBeyondLastLine: false, // 取消代码后面一大段空白 overviewRulerBorder: false, // 不要滚动条的边框 }); } setTimeout(() => { - editor.layout(); - editor.setValue(currentVal.value); + editor!.layout(); + editor!.setValue(currentVal.value); }); // 监听值的变化 editor.onDidChangeModelContent(() => { - currentVal.value = editor.getValue(); - emit('change', currentVal.value); + if (!hasEnableEdit.value) { + currentVal.value = editor!.getValue(); + emit('change', currentVal.value); + } }); window.addEventListener('resize', () => { - editor.layout(); + editor!.layout(); }); }); }); }; + // 更改编辑状态 + const changeEditState = () => { + readonlyState.value = !readonlyState.value; + if (!editor) return; + if (!readonlyState.value) { + editor.updateOptions({ + readOnly: false, + }); + } else { + editor.updateOptions({ + readOnly: true, + }); + } + }; + + // 更新全屏状态 + const changeFullScreenState = async () => { + currentVal.value = String(editor?.getValue()); + editor?.dispose(); + editor = null; + isFullScreen.value = !isFullScreen.value; + editorInit(); + }; + + // 绘制全屏图标 + const isAllowRenderFullScreen = () => { + if (enableFullScreen.value) { + if (isFullScreen.value) { + return ( + + ); + } + return ( + + ); + } + return null; + }; + + // 绘制取消消息盒子 + const renderCancelMessage = () => { + return ( +
+

{ibiz.i18n.t('editor.code.confirmCancelPrompt')}

+

+ {ibiz.i18n.t('editor.code.cancelEditPrompt')} +

+
+ ); + }; + + // 取消编辑 + const cancelEdit = () => { + if (props.value !== editor?.getValue()) { + ElMessageBox({ + title: ibiz.i18n.t('editor.code.confirmCancel'), + type: 'warning', + customClass: ns.b('message'), + message: renderCancelMessage(), + showCancelButton: true, + cancelButtonClass: ns.be('message', 'message-cancel'), + confirmButtonClass: ns.be('message', 'message-comfire'), + }) + .then(() => { + editor?.setValue(String(props.value || '')); + changeEditState(); + }) + .catch(() => { + // 重新聚焦 + editor?.focus(); + }); + } else { + changeEditState(); + } + }; + + // 确认保存 + const save = () => { + changeEditState(); + if (editor) { + currentVal.value = editor!.getValue(); + emit('change', currentVal.value); + } + if (isFullScreen.value) { + changeFullScreenState(); + } + }; + + // 绘制底部取消确认按钮 + const renderFooter = () => { + if (hasEnableEdit.value) { + return ( +
+
cancelEdit()}> + {ibiz.i18n.t('app.cancel')} +
+
save()}> + {ibiz.i18n.t('app.save')} +
+
+ ); + } + return null; + }; + + // 绘制头部工具栏 + const renderHeaderToolbar = () => { + if (hasEnableEdit.value || enableFullScreen.value) { + return ( +
+ {hasEnableEdit.value && enableEdit.value && readonlyState.value ? ( + + ) : null} + {isAllowRenderFullScreen()} +
+ ); + } + return null; + }; + + // 绘制代码框内容 + const renderCodeContent = () => { + return
; + }; + onMounted(() => { editorInit(); }); @@ -120,9 +311,54 @@ export const IBizCode = defineComponent({ editor?.dispose(); }); - return { ns, currentVal, codeEditBox }; + return { + ns, + currentVal, + codeEditBox, + isFullScreen, + hasEnableEdit, + readonlyState, + renderFooter, + renderHeaderToolbar, + renderCodeContent, + changeFullScreenState, + }; }, render() { - return
; + return !this.isFullScreen ? ( +
+ {this.renderHeaderToolbar()} + {this.renderCodeContent()} + {this.hasEnableEdit && !this.readonlyState ? this.renderFooter() : null} +
+ ) : ( + this.changeFullScreenState()} + > +
+ {this.renderHeaderToolbar()} + {this.renderCodeContent()} + {this.hasEnableEdit && !this.readonlyState + ? this.renderFooter() + : null} +
+
+ ); }, }); diff --git a/src/locale/en/index.ts b/src/locale/en/index.ts index 50594272..bf694197 100644 --- a/src/locale/en/index.ts +++ b/src/locale/en/index.ts @@ -12,6 +12,7 @@ export default { noSupport: 'Not supported currently', add: 'Add', delete: 'Delete', + save: 'Save', }, // 视图 view: {}, @@ -112,5 +113,13 @@ export default { }, }, // 编辑器 - editor: {}, + editor: { + code: { + readOnlyPrompt: 'Currently in read-only mode, not editable', + confirmCancelPrompt: 'Are you sure you want to cancel editing?', + cancelEditPrompt: + 'Canceling editing will prevent the modified content from being saved and cannot be retrieved.', + confirmCancel: 'Confirm cancel', + }, + }, }; diff --git a/src/locale/zh-CN/index.ts b/src/locale/zh-CN/index.ts index 141a0cd7..dafdcc76 100644 --- a/src/locale/zh-CN/index.ts +++ b/src/locale/zh-CN/index.ts @@ -12,6 +12,7 @@ export default { noSupport: '暂未支持', add: '添加', delete: '删除', + save: '保存', }, // 视图 view: {}, @@ -110,5 +111,12 @@ export default { }, }, // 编辑器 - editor: {}, + editor: { + code: { + readOnlyPrompt: '当前为只读模式,不可编辑', + confirmCancelPrompt: '确定要取消编辑吗?', + cancelEditPrompt: '取消编辑将无法保存修改的内容,且不能找回。', + confirmCancel: '确认取消', + }, + }, }; -- Gitee From 73b4525227bd87a199074bce50addd3ac97ae926 Mon Sep 17 00:00:00 2001 From: lijisanxiong <1518062161@qq.com> Date: Sun, 28 Apr 2024 19:40:30 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=20=E5=B7=A5=E5=85=B7=E6=A0=8F?= =?UTF-8?q?=E5=88=86=E7=BB=84=E5=8F=8A=E5=88=86=E7=BB=84=E9=A1=B9=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=8C=89=E9=92=AE=E6=A0=B7=E5=BC=8F=E4=B8=8E=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E7=95=8C=E9=9D=A2=E6=A0=B7=E5=BC=8F=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/control/toolbar/toolbar.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/control/toolbar/toolbar.tsx b/src/control/toolbar/toolbar.tsx index 944a0eda..008e9367 100644 --- a/src/control/toolbar/toolbar.tsx +++ b/src/control/toolbar/toolbar.tsx @@ -89,6 +89,7 @@ export const ToolbarControl = defineComponent({ popper-class={[ ns.b('submenu-popper'), ns.bm('submenu-popper', toolbarStyle), + (item as IDETBGroupItem)?.sysCss?.cssName, ]} > {{ @@ -116,10 +117,17 @@ export const ToolbarControl = defineComponent({ ); } if (item2.itemType === 'DEUIACTION') { + const buttonType = ( + item2 as IDETBUIActionItem + ).buttonStyle?.toLowerCase(); if (actionId === 'exportexcel') { return (