mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-09-22 23:20:00 +08:00
parent
3e78a0809b
commit
3673d6016d
@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<TMagicDialog
|
<TMagicDialog
|
||||||
v-model="isShowCodeBlockEditor"
|
:model-value="true"
|
||||||
class="code-editor-dialog"
|
class="code-editor-dialog"
|
||||||
:title="currentTitle"
|
:title="currentTitle"
|
||||||
:fullscreen="true"
|
:fullscreen="true"
|
||||||
:before-close="saveAndClose"
|
:before-close="close"
|
||||||
:append-to-body="true"
|
:append-to-body="true"
|
||||||
>
|
>
|
||||||
<Layout v-model:left="left" :min-left="45" class="code-editor-layout">
|
<Layout v-model:left="left" :min-left="45" class="code-editor-layout">
|
||||||
@ -58,7 +58,7 @@
|
|||||||
ref="codeEditor"
|
ref="codeEditor"
|
||||||
class="m-editor-container"
|
class="m-editor-container"
|
||||||
:init-values="`${codeConfig.content}`"
|
:init-values="`${codeConfig.content}`"
|
||||||
@save="saveCode"
|
@save="saveCodeDraft"
|
||||||
:options="{
|
:options="{
|
||||||
tabSize: 2,
|
tabSize: 2,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
@ -68,10 +68,10 @@
|
|||||||
></MagicCodeEditor>
|
></MagicCodeEditor>
|
||||||
<div class="m-editor-content-bottom" v-if="editable">
|
<div class="m-editor-content-bottom" v-if="editable">
|
||||||
<TMagicButton type="primary" class="button" @click="saveCode">保存</TMagicButton>
|
<TMagicButton type="primary" class="button" @click="saveCode">保存</TMagicButton>
|
||||||
<TMagicButton type="primary" class="button" @click="saveAndClose">关闭</TMagicButton>
|
<TMagicButton type="primary" class="button" @click="close">关闭</TMagicButton>
|
||||||
</div>
|
</div>
|
||||||
<div class="m-editor-content-bottom" v-else>
|
<div class="m-editor-content-bottom" v-else>
|
||||||
<TMagicButton type="primary" class="button" @click="saveAndClose">关闭</TMagicButton>
|
<TMagicButton type="primary" class="button" @click="close">关闭</TMagicButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</TMagicCard>
|
</TMagicCard>
|
||||||
@ -83,14 +83,24 @@
|
|||||||
|
|
||||||
<script lang="ts" setup name="MEditorCodeBlockEditor">
|
<script lang="ts" setup name="MEditorCodeBlockEditor">
|
||||||
import { computed, inject, reactive, ref, watchEffect } from 'vue';
|
import { computed, inject, reactive, ref, watchEffect } from 'vue';
|
||||||
import { forIn, isEmpty } from 'lodash-es';
|
import { cloneDeep, forIn, isEmpty } from 'lodash-es';
|
||||||
import type * as monaco from 'monaco-editor';
|
import type * as monaco from 'monaco-editor';
|
||||||
|
|
||||||
import { TMagicButton, TMagicCard, TMagicDialog, TMagicInput, tMagicMessage, TMagicTree } from '@tmagic/design';
|
import {
|
||||||
|
TMagicButton,
|
||||||
|
TMagicCard,
|
||||||
|
TMagicDialog,
|
||||||
|
TMagicInput,
|
||||||
|
tMagicMessage,
|
||||||
|
tMagicMessageBox,
|
||||||
|
TMagicTree,
|
||||||
|
} from '@tmagic/design';
|
||||||
|
import { datetimeFormatter } from '@tmagic/utils';
|
||||||
|
|
||||||
import Layout from '../../../components/Layout.vue';
|
import Layout from '../../../components/Layout.vue';
|
||||||
import type { CodeBlockContent, CodeDslList, ListState, Services } from '../../../type';
|
import type { CodeBlockContent, CodeDslList, ListState, Services } from '../../../type';
|
||||||
import { CodeEditorMode } from '../../../type';
|
import { CodeEditorMode } from '../../../type';
|
||||||
|
import { serializeConfig } from '../../../utils/editor';
|
||||||
import MagicCodeEditor from '../../CodeEditor.vue';
|
import MagicCodeEditor from '../../CodeEditor.vue';
|
||||||
|
|
||||||
const services = inject<Services>('services');
|
const services = inject<Services>('services');
|
||||||
@ -100,15 +110,13 @@ const left = ref(200);
|
|||||||
const currentTitle = ref('');
|
const currentTitle = ref('');
|
||||||
// 编辑器当前需展示的代码块内容
|
// 编辑器当前需展示的代码块内容
|
||||||
const codeConfig = ref<CodeBlockContent | null>(null);
|
const codeConfig = ref<CodeBlockContent | null>(null);
|
||||||
|
// 原始代码内容
|
||||||
|
const originCodeContent = ref<string | null>(null);
|
||||||
// select选择的内容(ListState)
|
// select选择的内容(ListState)
|
||||||
const state = reactive<ListState>({
|
const state = reactive<ListState>({
|
||||||
codeList: [],
|
codeList: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
// 是否展示代码编辑区
|
|
||||||
const isShowCodeBlockEditor = computed(
|
|
||||||
() => (codeConfig.value && services?.codeBlockService.getCodeEditorShowStatus()) || false,
|
|
||||||
);
|
|
||||||
const mode = computed(() => services?.codeBlockService.getMode());
|
const mode = computed(() => services?.codeBlockService.getMode());
|
||||||
const id = computed(() => services?.codeBlockService.getId() || '');
|
const id = computed(() => services?.codeBlockService.getId() || '');
|
||||||
const editable = computed(() => services?.codeBlockService.getEditStatus());
|
const editable = computed(() => services?.codeBlockService.getEditStatus());
|
||||||
@ -116,7 +124,17 @@ const editable = computed(() => services?.codeBlockService.getEditStatus());
|
|||||||
const selectedIds = computed(() => services?.codeBlockService.getCombineIds() || []);
|
const selectedIds = computed(() => services?.codeBlockService.getCombineIds() || []);
|
||||||
|
|
||||||
watchEffect(async () => {
|
watchEffect(async () => {
|
||||||
codeConfig.value = (await services?.codeBlockService.getCodeContentById(id.value)) || null;
|
codeConfig.value = cloneDeep(await services?.codeBlockService.getCodeContentById(id.value)) || null;
|
||||||
|
if (!codeConfig.value) return;
|
||||||
|
if (!originCodeContent.value) {
|
||||||
|
// 暂存原始的代码内容
|
||||||
|
originCodeContent.value = serializeConfig(codeConfig.value.content);
|
||||||
|
}
|
||||||
|
// 有草稿时展示上次保存的草稿内容
|
||||||
|
const codeDraft = services?.codeBlockService.getCodeDraft(id.value);
|
||||||
|
if (codeDraft) {
|
||||||
|
codeConfig.value.content = codeDraft;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
watchEffect(async () => {
|
watchEffect(async () => {
|
||||||
@ -132,6 +150,18 @@ watchEffect(async () => {
|
|||||||
currentTitle.value = state.codeList[0]?.name || '';
|
currentTitle.value = state.codeList[0]?.name || '';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 保存草稿
|
||||||
|
const saveCodeDraft = (codeValue: string) => {
|
||||||
|
if (!codeEditor.value) return;
|
||||||
|
if (originCodeContent.value === codeValue) {
|
||||||
|
// 没修改或改回原样 有草稿的话删除草稿
|
||||||
|
services?.codeBlockService.removeCodeDraft(id.value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
services?.codeBlockService.setCodeDraft(id.value, codeValue);
|
||||||
|
tMagicMessage.success(`代码草稿保存成功 ${datetimeFormatter(new Date())}`);
|
||||||
|
};
|
||||||
|
|
||||||
// 保存代码
|
// 保存代码
|
||||||
const saveCode = async (): Promise<boolean> => {
|
const saveCode = async (): Promise<boolean> => {
|
||||||
if (!codeEditor.value || !codeConfig.value || !editable.value) return true;
|
if (!codeEditor.value || !codeConfig.value || !editable.value) return true;
|
||||||
@ -151,14 +181,33 @@ const saveCode = async (): Promise<boolean> => {
|
|||||||
content: codeConfig.value.content,
|
content: codeConfig.value.content,
|
||||||
});
|
});
|
||||||
tMagicMessage.success('代码保存成功');
|
tMagicMessage.success('代码保存成功');
|
||||||
|
// 删除草稿
|
||||||
|
services?.codeBlockService.removeCodeDraft(id.value);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 保存并关闭
|
// 关闭弹窗
|
||||||
const saveAndClose = async () => {
|
const close = async () => {
|
||||||
const saveRes = await saveCode();
|
const codeDraft = services?.codeBlockService.getCodeDraft(id.value);
|
||||||
if (saveRes) {
|
let shouldClose = true;
|
||||||
await services?.codeBlockService.setCodeEditorShowStatus(false);
|
if (codeDraft) {
|
||||||
|
await tMagicMessageBox
|
||||||
|
.confirm('您有代码修改未保存,是否保存后再关闭?', '提示', {
|
||||||
|
confirmButtonText: '确认',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
// 保存之后再关闭
|
||||||
|
shouldClose = await saveCode();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// 删除草稿 直接关闭
|
||||||
|
services?.codeBlockService.removeCodeDraft(id.value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (shouldClose) {
|
||||||
|
services?.codeBlockService.setCodeEditorShowStatus(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@
|
|||||||
</TMagicTree>
|
</TMagicTree>
|
||||||
|
|
||||||
<!-- 代码块编辑区 -->
|
<!-- 代码块编辑区 -->
|
||||||
<code-block-editor>
|
<code-block-editor v-if="isShowCodeBlockEditor">
|
||||||
<template #code-block-edit-panel-header="{ id }">
|
<template #code-block-edit-panel-header="{ id }">
|
||||||
<slot name="code-block-edit-panel-header" :id="id"></slot>
|
<slot name="code-block-edit-panel-header" :id="id"></slot>
|
||||||
</template>
|
</template>
|
||||||
@ -122,6 +122,9 @@ const state = reactive<ListRelationState>({
|
|||||||
|
|
||||||
const editable = computed(() => services?.codeBlockService.getEditStatus());
|
const editable = computed(() => services?.codeBlockService.getEditStatus());
|
||||||
|
|
||||||
|
// 是否展示代码编辑区
|
||||||
|
const isShowCodeBlockEditor = computed(() => services?.codeBlockService.getCodeEditorShowStatus() || false);
|
||||||
|
|
||||||
// 根据代码块ID获取其绑定的组件信息
|
// 根据代码块ID获取其绑定的组件信息
|
||||||
const getBindCompsByCodeId = (codeId: string, codeBlockContent: CodeBlockContent) => {
|
const getBindCompsByCodeId = (codeId: string, codeBlockContent: CodeBlockContent) => {
|
||||||
if (isEmpty(codeBlockContent) || isEmpty(codeBlockContent.comps)) {
|
if (isEmpty(codeBlockContent) || isEmpty(codeBlockContent.comps)) {
|
||||||
|
@ -23,7 +23,7 @@ import { Id, MNode } from '@tmagic/schema';
|
|||||||
|
|
||||||
import editorService from '../services/editor';
|
import editorService from '../services/editor';
|
||||||
import type { CodeBlockContent, CodeBlockDSL, CodeState } from '../type';
|
import type { CodeBlockContent, CodeBlockDSL, CodeState } from '../type';
|
||||||
import { CodeEditorMode, CodeSelectOp } from '../type';
|
import { CODE_DRAFT_STORAGE_KEY, CodeEditorMode, CodeSelectOp } from '../type';
|
||||||
import { error, info } from '../utils/logger';
|
import { error, info } from '../utils/logger';
|
||||||
|
|
||||||
import BaseService from './BaseService';
|
import BaseService from './BaseService';
|
||||||
@ -321,6 +321,27 @@ class CodeBlock extends BaseService {
|
|||||||
this.state.undeletableList = codeIds;
|
this.state.undeletableList = codeIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置代码草稿
|
||||||
|
*/
|
||||||
|
public setCodeDraft(codeId: string, content: string): void {
|
||||||
|
globalThis.localStorage.setItem(`${CODE_DRAFT_STORAGE_KEY}_${codeId}`, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取代码草稿
|
||||||
|
*/
|
||||||
|
public getCodeDraft(codeId: string): string | null {
|
||||||
|
return globalThis.localStorage.getItem(`${CODE_DRAFT_STORAGE_KEY}_${codeId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除代码草稿
|
||||||
|
*/
|
||||||
|
public removeCodeDraft(codeId: string): void {
|
||||||
|
globalThis.localStorage.removeItem(`${CODE_DRAFT_STORAGE_KEY}_${codeId}`);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在dsl数据源中删除指定id的代码块
|
* 在dsl数据源中删除指定id的代码块
|
||||||
* @param {string[]} codeIds 需要删除的代码块id数组
|
* @param {string[]} codeIds 需要删除的代码块id数组
|
||||||
@ -346,33 +367,6 @@ class CodeBlock extends BaseService {
|
|||||||
return await this.getUniqueId();
|
return await this.getUniqueId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 一对一解除绑定关系 功能隐藏,暂时注释
|
|
||||||
* @param {string} compId 组件id
|
|
||||||
* @param {string} codeId 代码块id
|
|
||||||
* @param {string[]} codeHooks 代码块挂载hook名称
|
|
||||||
* @returns {boolean} 结果
|
|
||||||
*/
|
|
||||||
// public async unbind(compId: Id, codeId: string, codeHooks: string[]): Promise<boolean> {
|
|
||||||
// const nodeInfo = editorService.getNodeById(compId);
|
|
||||||
// if (!nodeInfo) return false;
|
|
||||||
// // 更新node节点信息
|
|
||||||
// codeHooks.forEach((hook) => {
|
|
||||||
// if (!isEmpty(nodeInfo[hook])) {
|
|
||||||
// const newHookInfo = nodeInfo[hook].filter((item: string) => item !== codeId);
|
|
||||||
// nodeInfo[hook] = newHookInfo;
|
|
||||||
// editorService.update(nodeInfo);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// // 更新绑定关系
|
|
||||||
// const oldRelation = await this.getCompRelation();
|
|
||||||
// const oldCodeIds = oldRelation[compId];
|
|
||||||
// // 不能直接splice修改原数组,会导致绑定关系更新错乱
|
|
||||||
// const newCodeIds = oldCodeIds.filter((item) => item !== codeId);
|
|
||||||
// this.setCompRelation(compId, newCodeIds);
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过组件id解除绑定关系(删除组件)
|
* 通过组件id解除绑定关系(删除组件)
|
||||||
* @param {MNode} compId 组件节点
|
* @param {MNode} compId 组件节点
|
||||||
|
@ -392,3 +392,6 @@ export enum CodeSelectOp {
|
|||||||
/** 单选修改 */
|
/** 单选修改 */
|
||||||
CHANGE = 'change',
|
CHANGE = 'change',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 代码块草稿localStorage key
|
||||||
|
export const CODE_DRAFT_STORAGE_KEY = 'magicCodeDraft';
|
||||||
|
@ -16,13 +16,14 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import serialize from 'serialize-javascript';
|
||||||
|
|
||||||
import type { MApp, MContainer, MNode, MPage } from '@tmagic/schema';
|
import type { MApp, MContainer, MNode, MPage } from '@tmagic/schema';
|
||||||
import { NodeType } from '@tmagic/schema';
|
import { NodeType } from '@tmagic/schema';
|
||||||
import type StageCore from '@tmagic/stage';
|
import type StageCore from '@tmagic/stage';
|
||||||
import { getNodePath, isNumber, isPage, isPop } from '@tmagic/utils';
|
import { getNodePath, isNumber, isPage, isPop } from '@tmagic/utils';
|
||||||
|
|
||||||
import { Layout } from '../type';
|
import { Layout } from '../type';
|
||||||
|
|
||||||
export const COPY_STORAGE_KEY = '$MagicEditorCopyData';
|
export const COPY_STORAGE_KEY = '$MagicEditorCopyData';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -237,3 +238,10 @@ export const fixNodePosition = (config: MNode, parent: MContainer, stage: StageC
|
|||||||
left: fixNodeLeft(config, parent, stage?.renderer.contentWindow?.document),
|
left: fixNodeLeft(config, parent, stage?.renderer.contentWindow?.document),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 序列化配置
|
||||||
|
export const serializeConfig = (config: any) =>
|
||||||
|
serialize(config, {
|
||||||
|
space: 2,
|
||||||
|
unsafe: true,
|
||||||
|
}).replace(/"(\w+)":\s/g, '$1: ');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user