feat(editor): 代码块service封装完成

This commit is contained in:
parisma 2022-09-08 15:12:11 +08:00 committed by jia000
parent 452c80d829
commit 7640c06ccb
7 changed files with 215 additions and 112 deletions

View File

@ -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);

View File

@ -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>

View File

@ -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"

View File

@ -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');
// idkey
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>

View File

@ -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>

View 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();

View File

@ -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;
};