mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-06-14 08:49:23 +08:00
feat(editor): 代码块service封装完成
This commit is contained in:
parent
452c80d829
commit
7640c06ccb
@ -27,8 +27,12 @@
|
||||
<slot name="code-block-panel-header"></slot>
|
||||
</template>
|
||||
|
||||
<template #code-block-panel-tool>
|
||||
<slot name="code-block-panel-tool"></slot>
|
||||
<template #code-block-panel-tool="{ id }">
|
||||
<slot name="code-block-panel-tool" :id="id"></slot>
|
||||
</template>
|
||||
|
||||
<template #code-block-edit-panel-header="{ id }">
|
||||
<slot name="code-block-edit-panel-header" :id="id"></slot>
|
||||
</template>
|
||||
</sidebar>
|
||||
</slot>
|
||||
@ -73,6 +77,7 @@ import NavMenu from './layouts/NavMenu.vue';
|
||||
import PropsPanel from './layouts/PropsPanel.vue';
|
||||
import Sidebar from './layouts/sidebar/Sidebar.vue';
|
||||
import Workspace from './layouts/workspace/Workspace.vue';
|
||||
import codeBlockService from './services/codeBlock';
|
||||
import componentListService from './services/componentList';
|
||||
import editorService from './services/editor';
|
||||
import eventsService from './services/events';
|
||||
@ -296,6 +301,7 @@ export default defineComponent({
|
||||
uiService.destroy();
|
||||
componentListService.destroy();
|
||||
storageService.destroy();
|
||||
codeBlockService.destroy();
|
||||
});
|
||||
|
||||
const services: Services = {
|
||||
@ -306,6 +312,7 @@ export default defineComponent({
|
||||
editorService,
|
||||
uiService,
|
||||
storageService,
|
||||
codeBlockService,
|
||||
};
|
||||
|
||||
provide('services', services);
|
||||
|
@ -27,8 +27,12 @@
|
||||
<slot name="code-block-panel-header"></slot>
|
||||
</template>
|
||||
|
||||
<template #code-block-panel-tool v-if="item === 'code-block'">
|
||||
<slot name="code-block-panel-tool"></slot>
|
||||
<template #code-block-panel-tool="{ id }" v-if="item === 'code-block'">
|
||||
<slot name="code-block-panel-tool" :id="id"></slot>
|
||||
</template>
|
||||
|
||||
<template #code-block-edit-panel-header="{ id }" v-if="item === 'code-block'">
|
||||
<slot name="code-block-edit-panel-header" :id="id"></slot>
|
||||
</template>
|
||||
</tab-pane>
|
||||
</el-tabs>
|
||||
|
@ -35,11 +35,19 @@
|
||||
<component v-else-if="config.slots?.codeBlockPanelHeader" :is="config.slots.codeBlockPanelHeader" />
|
||||
</template>
|
||||
|
||||
<template #code-block-panel-tool v-if="data === 'code-block' || config.slots?.codeBlockPanelTool">
|
||||
<slot v-if="data === 'code-block'" name="code-block-panel-tool"></slot>
|
||||
<template #code-block-panel-tool="{ id }" v-if="data === 'code-block' || config.slots?.codeBlockPanelTool">
|
||||
<slot v-if="data === 'code-block'" name="code-block-panel-tool" :id="id"></slot>
|
||||
<component v-else-if="config.slots?.codeBlockPanelTool" :is="config.slots.codeBlockPanelTool" />
|
||||
</template>
|
||||
|
||||
<template
|
||||
#code-block-edit-panel-header="{ id }"
|
||||
v-if="data === 'code-block' || config.slots?.codeBlockEditPanelHeader"
|
||||
>
|
||||
<slot v-if="data === 'code-block'" name="code-block-edit-panel-header" :id="id"></slot>
|
||||
<component v-else-if="config.slots?.codeBlockEditPanelHeader" :is="config.slots.codeBlockEditPanelHeader" />
|
||||
</template>
|
||||
|
||||
<template
|
||||
#layer-node-content="{ data: nodeData, node }"
|
||||
v-if="data === 'layer' || config.slots?.layerNodeContent"
|
||||
|
@ -1,19 +1,20 @@
|
||||
<template>
|
||||
<el-dialog v-model="state.isShowCodeBlockEditor" title="代码块编辑面板" :fullscreen="true">
|
||||
<el-dialog v-model="isShowCodeBlockEditor" title="代码块编辑面板" :fullscreen="true">
|
||||
<div class="m-editor-code-block-editor-panel">
|
||||
<slot name="code-block-edit-panel-header" :id="id"></slot>
|
||||
<el-row class="code-name-wrapper" justify="start">
|
||||
<el-col :span="2">
|
||||
<span>代码块名称</span>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-input size="small" v-model="state.codeConfig.name" />
|
||||
<el-input size="small" v-model="codeConfig.name" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="m-editor-content">
|
||||
<magic-code-editor
|
||||
ref="codeEditor"
|
||||
class="m-editor-content"
|
||||
:init-values="state.codeConfig.content"
|
||||
:init-values="codeConfig.content"
|
||||
@save="saveCode"
|
||||
:options="{
|
||||
tabSize: 2,
|
||||
@ -31,56 +32,51 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineEmits, defineProps, inject, ref } from 'vue';
|
||||
import { computed, defineProps, inject, ref, watchEffect } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
import type { Services, State } from '../../../type';
|
||||
import type { CodeBlockContent, Services } from '../../../type';
|
||||
|
||||
const props = defineProps<{
|
||||
id: string;
|
||||
}>();
|
||||
const services = inject<Services>('services');
|
||||
|
||||
const codeEditor = ref<any | null>(null);
|
||||
const services = inject<Services>('services');
|
||||
const props = defineProps<{
|
||||
state: State;
|
||||
}>();
|
||||
// 编辑器当前需展示的代码块内容
|
||||
const codeConfig = ref<CodeBlockContent | null>(null);
|
||||
|
||||
const emit = defineEmits(['close']);
|
||||
// 是否展示代码编辑区
|
||||
const isShowCodeBlockEditor = computed(() => codeConfig.value && services?.codeBlockService.getCodeEditorShowStatus());
|
||||
|
||||
watchEffect(() => {
|
||||
codeConfig.value = services?.codeBlockService.getCodeDslById(props.id) ?? null;
|
||||
});
|
||||
|
||||
// 保存代码
|
||||
const saveCode = () => {
|
||||
if (!codeEditor.value || !codeConfig.value) return;
|
||||
|
||||
try {
|
||||
// 代码内容
|
||||
const codeContent = codeEditor.value.getEditor().getValue();
|
||||
/* eslint no-eval: "off" */
|
||||
codeConfig.value.content = eval(codeContent);
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e.stack);
|
||||
return;
|
||||
}
|
||||
// 存入dsl
|
||||
services?.codeBlockService.setCodeDslById(props.id, {
|
||||
name: codeConfig.value.name,
|
||||
content: codeConfig.value.content,
|
||||
});
|
||||
ElMessage.success('代码保存成功');
|
||||
};
|
||||
|
||||
// 保存并关闭
|
||||
const saveAndClose = () => {
|
||||
saveCode();
|
||||
emit('close');
|
||||
};
|
||||
|
||||
// 保存代码
|
||||
const saveCode = () => {
|
||||
if (!codeEditor.value || !props.state.codeConfig) return;
|
||||
|
||||
try {
|
||||
const codeContent = codeEditor.value.getEditor().getValue();
|
||||
/* eslint no-eval: "off" */
|
||||
props.state.codeConfig.content = eval(codeContent);
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e.stack);
|
||||
}
|
||||
const { id, ...codeConfig } = props.state.codeConfig;
|
||||
const { editorService } = services || {};
|
||||
if (!editorService) return;
|
||||
const root = editorService.get('root');
|
||||
|
||||
// 代码块id作为key
|
||||
let codeBlockMap = root?.method || {};
|
||||
codeBlockMap = {
|
||||
...codeBlockMap,
|
||||
...{
|
||||
[props.state.codeConfig.id]: codeConfig,
|
||||
},
|
||||
};
|
||||
|
||||
// 写入dsl
|
||||
editorService.set('root', {
|
||||
...root,
|
||||
method: codeBlockMap,
|
||||
});
|
||||
|
||||
ElMessage.success('代码保存成功');
|
||||
services?.codeBlockService.setCodeEditorShowStatus(false);
|
||||
};
|
||||
</script>
|
||||
|
@ -8,81 +8,63 @@
|
||||
</slot>
|
||||
|
||||
<!-- 代码块列表 -->
|
||||
<div class="list-container" v-if="state.codeBlockMap">
|
||||
<div v-for="(value, key) in state.codeBlockMap" :key="key">
|
||||
<div class="list-container" v-if="codeList">
|
||||
<div v-for="(value, key) in codeList" :key="key">
|
||||
<div class="list-item">
|
||||
<div class="code-name">{{ value.name }}({{ key }})</div>
|
||||
<div class="right-tool">
|
||||
<el-tooltip effect="dark" content="编辑代码" placement="top">
|
||||
<el-icon class="edit-icon" @click="editCode(key)"><Edit /></el-icon>
|
||||
</el-tooltip>
|
||||
<slot name="code-block-panel-tool"></slot>
|
||||
<slot name="code-block-panel-tool" :id="key"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 代码块编辑区 -->
|
||||
<code-block-editor :state="state" @close="closePanel"></code-block-editor>
|
||||
<code-block-editor :id="codeId">
|
||||
<template #code-block-edit-panel-header="{ id }">
|
||||
<slot name="code-block-edit-panel-header" :id="id"></slot>
|
||||
</template>
|
||||
</code-block-editor>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { inject, reactive, watchEffect } from 'vue';
|
||||
import { computed, inject, ref } from 'vue';
|
||||
import { Edit } from '@element-plus/icons-vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
import type { CodeBlockConfig, Services, State } from '../../../type';
|
||||
import type { CodeBlockContent, Services } from '../../../type';
|
||||
|
||||
import codeBlockEditor from './CodeBlockEditor.vue';
|
||||
|
||||
const state = reactive<State>({
|
||||
isShowCodeBlockEditor: false,
|
||||
codeConfig: null,
|
||||
codeBlockMap: null,
|
||||
});
|
||||
const services = inject<Services>('services');
|
||||
const codeId = ref('');
|
||||
|
||||
// 代码块列表
|
||||
const codeList = computed(() => services?.codeBlockService.getCodeDsl());
|
||||
|
||||
// 新增代码块
|
||||
const createCodeBlock = () => {
|
||||
const config: CodeBlockConfig = {
|
||||
id: getUniqueId(),
|
||||
const { codeBlockService } = services || {};
|
||||
if (!codeBlockService) {
|
||||
ElMessage.error('新增代码块失败');
|
||||
return;
|
||||
}
|
||||
codeId.value = codeBlockService.getUniqueId();
|
||||
const codeConfig: CodeBlockContent = {
|
||||
name: '代码块',
|
||||
content: `(app) => {\n // place your code here\n}`,
|
||||
content: `() => {\n // place your code here\n}`,
|
||||
};
|
||||
showCodeEditor(config);
|
||||
codeBlockService.setCodeDslById(codeId.value, codeConfig);
|
||||
codeBlockService.setCodeEditorShowStatus(true);
|
||||
};
|
||||
|
||||
// 展示代码编辑区
|
||||
const showCodeEditor = (config: CodeBlockConfig) => {
|
||||
state.codeConfig = config;
|
||||
showPanel();
|
||||
};
|
||||
|
||||
// 打开代码块面板
|
||||
const showPanel = () => (state.isShowCodeBlockEditor = true);
|
||||
|
||||
// 关闭代码块面板
|
||||
const closePanel = () => (state.isShowCodeBlockEditor = false);
|
||||
|
||||
// 生成代码块唯一id
|
||||
const getUniqueId = () => (Date.now().toString(36) + Math.random().toString(36).substring(2)).padEnd(19, '0');
|
||||
|
||||
// 编辑代码块
|
||||
const editCode = (key: string) => {
|
||||
if (!state.codeBlockMap) return;
|
||||
// 获取对应Id的代码块
|
||||
const currentCode = state.codeBlockMap[key];
|
||||
const config: CodeBlockConfig = {
|
||||
id: key,
|
||||
...currentCode,
|
||||
};
|
||||
showCodeEditor(config);
|
||||
codeId.value = key;
|
||||
services?.codeBlockService.setCodeEditorShowStatus(true);
|
||||
};
|
||||
|
||||
watchEffect(() => {
|
||||
const { editorService } = services || {};
|
||||
if (!editorService) return;
|
||||
const root = editorService.get('root');
|
||||
state.codeBlockMap = root?.method || null;
|
||||
});
|
||||
</script>
|
||||
|
114
packages/editor/src/services/codeBlock.ts
Normal file
114
packages/editor/src/services/codeBlock.ts
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { reactive } from 'vue';
|
||||
|
||||
import type { CodeBlockContent, CodeBlockDSL, CodeState } from '../type';
|
||||
import { info } from '../utils/logger';
|
||||
|
||||
import BaseService from './BaseService';
|
||||
|
||||
class CodeBlock extends BaseService {
|
||||
private state = reactive<CodeState>({
|
||||
isShowCodeEditor: false,
|
||||
codeDsl: null,
|
||||
});
|
||||
|
||||
constructor() {
|
||||
super([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置活动的代码块dsl数据源
|
||||
* @param {CodeBlockDSL} codeDsl 代码DSL
|
||||
* @returns {void}
|
||||
*/
|
||||
public setCodeDsl(codeDsl: CodeBlockDSL): void {
|
||||
this.state.codeDsl = codeDsl;
|
||||
info('[code-block]:code-dsl-change', this.state.codeDsl);
|
||||
this.emit('code-dsl-change', this.state.codeDsl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活动的代码块dsl数据源
|
||||
* @returns {CodeBlockDSL | null}
|
||||
*/
|
||||
public getCodeDsl(): CodeBlockDSL | null {
|
||||
return this.state.codeDsl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据代码块id获取代码块内容
|
||||
* @param {string} id 代码块id
|
||||
* @returns {CodeBlockContent | null}
|
||||
*/
|
||||
public getCodeDslById(id: string): CodeBlockContent | null {
|
||||
const totalCodeDsl = this.getCodeDsl();
|
||||
if (!totalCodeDsl) return null;
|
||||
return totalCodeDsl[id] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据代码块id设置代码块内容
|
||||
* @param {string} id 代码块id
|
||||
* @param {CodeBlockContent} codeConfig 代码块内容配置信息
|
||||
* @returns {void}
|
||||
*/
|
||||
public setCodeDslById(id: string, codeConfig: CodeBlockContent): void {
|
||||
let codeDsl = this.getCodeDsl();
|
||||
codeDsl = {
|
||||
...codeDsl,
|
||||
[id]: codeConfig,
|
||||
};
|
||||
this.setCodeDsl(codeDsl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置代码编辑面板展示状态
|
||||
* @param {boolean} status 是否展示代码编辑面板
|
||||
* @returns {void}
|
||||
*/
|
||||
public setCodeEditorShowStatus(status: boolean): void {
|
||||
this.state.isShowCodeEditor = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代码编辑面板展示状态
|
||||
* @returns {boolean} 是否展示代码编辑面板
|
||||
*/
|
||||
public getCodeEditorShowStatus(): boolean {
|
||||
return this.state.isShowCodeEditor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成代码块唯一id
|
||||
* @returns {string} 代码块唯一id
|
||||
*/
|
||||
public getUniqueId(): string {
|
||||
return (Date.now().toString(36) + Math.random().toString(36).substring(2)).padEnd(19, '0');
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
this.state.isShowCodeEditor = false;
|
||||
this.state.codeDsl = null;
|
||||
}
|
||||
}
|
||||
|
||||
export type CodeBlockService = CodeBlock;
|
||||
|
||||
export default new CodeBlock();
|
@ -23,6 +23,7 @@ import type { Id, MApp, MContainer, MNode, MPage } from '@tmagic/schema';
|
||||
import type StageCore from '@tmagic/stage';
|
||||
import type { ContainerHighlightType, MoveableOptions } from '@tmagic/stage';
|
||||
|
||||
import type { CodeBlockService } from './services/codeBlock';
|
||||
import type { ComponentListService } from './services/componentList';
|
||||
import type { EditorService } from './services/editor';
|
||||
import type { EventsService } from './services/events';
|
||||
@ -46,6 +47,7 @@ export interface Services {
|
||||
propsService: PropsService;
|
||||
componentListService: ComponentListService;
|
||||
uiService: UiService;
|
||||
codeBlockService: CodeBlockService;
|
||||
}
|
||||
|
||||
export interface StageOptions {
|
||||
@ -306,16 +308,7 @@ export interface ScrollViewerEvent {
|
||||
scrollWidth: number;
|
||||
}
|
||||
|
||||
export interface CodeBlockConfig {
|
||||
/** 代码块唯一id */
|
||||
id: string;
|
||||
/** 代码块名称 */
|
||||
name: string;
|
||||
/** 代码块内容 */
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface CodeBlockMap {
|
||||
export interface CodeBlockDSL {
|
||||
[id: string]: CodeBlockContent;
|
||||
}
|
||||
|
||||
@ -325,11 +318,10 @@ export interface CodeBlockContent {
|
||||
/** 代码块内容 */
|
||||
content: string;
|
||||
}
|
||||
export type State = {
|
||||
|
||||
export type CodeState = {
|
||||
/** 是否展示代码块编辑区 */
|
||||
isShowCodeBlockEditor: boolean;
|
||||
/** 代码块配置 */
|
||||
codeConfig: CodeBlockConfig | null;
|
||||
/** 代码块列表 */
|
||||
codeBlockMap: CodeBlockMap | null;
|
||||
isShowCodeEditor: boolean;
|
||||
/** 代码块DSL */
|
||||
codeDsl: CodeBlockDSL | null;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user