mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-06-27 20:59:20 +08:00
feat(editor,form,schema): 优化代码
Squash merge branch 'feature/parisma_881986193' into 'master' 1、扩展参数配置能力,支持参数类型定义,支持参数注释 2、修复代码块嵌套多层时绑定关系展示不正确的问题 3、支持在组件绑定位置编辑查看代码块
This commit is contained in:
parent
e42aee84bc
commit
0b537f5bff
@ -12,7 +12,7 @@
|
||||
<TMagicButton type="primary" class="button" @click="toggleFullScreen">
|
||||
{{ isFullScreen ? '退出全屏' : '全屏' }}</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 class="m-editor-content-bottom" v-else>
|
||||
@ -54,7 +54,7 @@ const props = withDefaults(
|
||||
autoSaveDraft: true,
|
||||
},
|
||||
);
|
||||
const emit = defineEmits(['save', 'close', 'saveAndClose']);
|
||||
const emit = defineEmits(['close', 'saveAndClose']);
|
||||
|
||||
const services = inject<Services>('services');
|
||||
|
||||
@ -95,14 +95,6 @@ const saveCodeDraft = async (codeValue: string) => {
|
||||
tMagicMessage.success(`代码草稿保存成功 ${datetimeFormatter(new Date())}`);
|
||||
};
|
||||
|
||||
// 保存代码
|
||||
const saveCode = (): void => {
|
||||
if (!codeEditor.value || !props.editable) return;
|
||||
// 代码内容
|
||||
editorContent.value = (codeEditor.value.getEditor() as monaco.editor.IStandaloneCodeEditor)?.getValue();
|
||||
emit('save', editorContent.value);
|
||||
};
|
||||
|
||||
// 保存并关闭
|
||||
const saveAndClose = (): void => {
|
||||
if (!codeEditor.value || !props.editable) return;
|
||||
|
@ -6,9 +6,9 @@
|
||||
<TMagicInput class="code-name-input" v-model="codeName" :disabled="!editable" />
|
||||
</div>
|
||||
<div class="code-name-wrapper">
|
||||
<div class="code-name-label">参数定义</div>
|
||||
<div class="code-name-label">参数</div>
|
||||
<m-form-table
|
||||
style="width: 320px"
|
||||
style="width: 800px"
|
||||
:config="tableConfig"
|
||||
:model="tableModel"
|
||||
:enableToggleMode="false"
|
||||
@ -26,7 +26,6 @@
|
||||
:autoSaveDraft="autoSaveDraft"
|
||||
:codeOptions="codeOptions"
|
||||
language="javascript"
|
||||
@save="saveCode"
|
||||
@saveAndClose="saveAndClose"
|
||||
@close="close"
|
||||
></CodeDraftEditor>
|
||||
@ -34,36 +33,56 @@
|
||||
</template>
|
||||
<script lang="ts" setup name="MEditorFunctionEditor">
|
||||
import { inject, provide, ref, watchEffect } from 'vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
import { TMagicCard, TMagicInput, tMagicMessage } from '@tmagic/design';
|
||||
import { ColumnConfig, TableConfig } from '@tmagic/form';
|
||||
import { CodeParam, Id } from '@tmagic/schema';
|
||||
|
||||
import type { Services } from '../type';
|
||||
|
||||
import CodeDraftEditor from './CodeDraftEditor.vue';
|
||||
|
||||
const tableConfig = {
|
||||
border: true,
|
||||
enableFullscreen: false,
|
||||
name: 'params',
|
||||
maxHeight: '150px',
|
||||
const defaultParamColConfig: ColumnConfig = {
|
||||
type: 'row',
|
||||
label: '参数类型',
|
||||
items: [
|
||||
{
|
||||
type: 'text',
|
||||
label: '参数名',
|
||||
name: 'name',
|
||||
text: '参数类型',
|
||||
labelWidth: '70px',
|
||||
type: 'select',
|
||||
name: 'type',
|
||||
options: [
|
||||
{
|
||||
text: '数字',
|
||||
label: '数字',
|
||||
value: 'number',
|
||||
},
|
||||
{
|
||||
text: '字符串',
|
||||
label: '字符串',
|
||||
value: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
/** 代码块id */
|
||||
id: Id;
|
||||
/** 代码块名称 */
|
||||
name: string;
|
||||
/** 代码内容 */
|
||||
content: string;
|
||||
/** 是否可编辑 */
|
||||
editable?: boolean;
|
||||
/** 是否自动保存草稿 */
|
||||
autoSaveDraft?: boolean;
|
||||
/** 编辑器扩展参数 */
|
||||
codeOptions?: object;
|
||||
/** 代码参数扩展配置 */
|
||||
paramsColConfig?: ColumnConfig;
|
||||
}>(),
|
||||
{
|
||||
editable: true,
|
||||
@ -71,6 +90,32 @@ const props = withDefaults(
|
||||
},
|
||||
);
|
||||
|
||||
const paramsColConfig = props.paramsColConfig || defaultParamColConfig;
|
||||
|
||||
const tableConfig: TableConfig = {
|
||||
type: 'table',
|
||||
border: true,
|
||||
enableFullscreen: false,
|
||||
name: 'params',
|
||||
maxHeight: '300px',
|
||||
dropSort: false,
|
||||
items: [
|
||||
{
|
||||
type: 'text',
|
||||
label: '参数名',
|
||||
name: 'name',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
label: '参数注释',
|
||||
name: 'tip',
|
||||
width: 200,
|
||||
},
|
||||
paramsColConfig,
|
||||
],
|
||||
};
|
||||
|
||||
const emit = defineEmits(['change', 'field-input']);
|
||||
|
||||
const services = inject<Services>('services');
|
||||
@ -91,7 +136,7 @@ watchEffect(() => {
|
||||
});
|
||||
|
||||
const initTableModel = (): void => {
|
||||
const codeDsl = services?.codeBlockService.getCodeDslSync();
|
||||
const codeDsl = cloneDeep(services?.codeBlockService.getCodeDslSync());
|
||||
if (!codeDsl) return;
|
||||
tableModel.value = {
|
||||
params: codeDsl[props.id]?.params || [],
|
||||
|
@ -9,17 +9,27 @@
|
||||
:size="size"
|
||||
@change="changeHandler"
|
||||
>
|
||||
<template #operateCol="{ scope }">
|
||||
<Icon
|
||||
v-if="scope.row.codeId"
|
||||
:icon="editable ? Edit : View"
|
||||
class="edit-icon"
|
||||
@click="editCode(scope.row.codeId)"
|
||||
></Icon>
|
||||
</template>
|
||||
</m-form-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="MEditorCodeSelect">
|
||||
import { computed, defineEmits, defineProps, inject, watch } from 'vue';
|
||||
import { Edit, View } from '@element-plus/icons-vue';
|
||||
import { isEmpty, map } from 'lodash-es';
|
||||
|
||||
import { createValues, FormItem, FormState, TableConfig } from '@tmagic/form';
|
||||
import { HookType, Id } from '@tmagic/schema';
|
||||
|
||||
import Icon from '../components/Icon.vue';
|
||||
import { CodeParamStatement, HookData, Services } from '../type';
|
||||
const services = inject<Services>('services');
|
||||
const mForm = inject<FormState>('mForm');
|
||||
@ -38,9 +48,10 @@ const codeDsl = computed(() => services?.codeBlockService.getCodeDslSync());
|
||||
|
||||
const tableConfig = computed<FormItem>(() => {
|
||||
const defaultConfig = {
|
||||
dropSort: true,
|
||||
dropSort: false,
|
||||
enableFullscreen: false,
|
||||
border: true,
|
||||
operateColWidth: 60,
|
||||
items: [
|
||||
{
|
||||
type: 'select',
|
||||
@ -68,8 +79,8 @@ const tableConfig = computed<FormItem>(() => {
|
||||
defaultValue: {},
|
||||
itemsFunction: (row: HookData) => {
|
||||
const paramsConfig = getParamsConfig(row.codeId);
|
||||
if (!row.params) row.params = {};
|
||||
if (isEmpty(row.params)) {
|
||||
// 如果参数没有填值,则使用createValues补全空值
|
||||
if (isEmpty(row.params) || !row.params) {
|
||||
createValues(mForm, paramsConfig, {}, row.params);
|
||||
}
|
||||
return paramsConfig;
|
||||
@ -83,6 +94,8 @@ const tableConfig = computed<FormItem>(() => {
|
||||
};
|
||||
});
|
||||
|
||||
const editable = computed(() => services?.codeBlockService.getEditStatus());
|
||||
|
||||
watch(
|
||||
() => props.model[props.name],
|
||||
(value) => {
|
||||
@ -104,15 +117,18 @@ const changeHandler = async () => {
|
||||
emit('change', props.model[props.name]);
|
||||
};
|
||||
|
||||
const getParamsConfig = (codeId: Id) => {
|
||||
const getParamsConfig = (codeId: Id): CodeParamStatement[] => {
|
||||
if (!codeDsl.value) return [];
|
||||
const paramStatements = codeDsl.value[codeId]?.params;
|
||||
if (isEmpty(paramStatements)) return [];
|
||||
return paramStatements.map((paramState: CodeParamStatement) => ({
|
||||
name: paramState.name,
|
||||
text: paramState.name,
|
||||
labelWidth: '100px',
|
||||
type: 'text',
|
||||
text: paramState.name,
|
||||
...paramState,
|
||||
}));
|
||||
};
|
||||
|
||||
const editCode = (codeId: Id) => {
|
||||
services?.codeBlockService.setCodeEditorContent(true, codeId);
|
||||
};
|
||||
</script>
|
||||
|
@ -9,45 +9,18 @@
|
||||
:show-close="false"
|
||||
>
|
||||
<Layout v-model:left="left" :min-left="45" class="code-editor-layout">
|
||||
<!-- 左侧列表 -->
|
||||
<template #left v-if="mode === CodeEditorMode.LIST">
|
||||
<TMagicTree
|
||||
v-if="!isEmpty(state.codeList)"
|
||||
class="side-tree"
|
||||
node-key="id"
|
||||
empty-text="暂无代码块"
|
||||
:data="state.codeList"
|
||||
:highlight-current="true"
|
||||
:current-node-key="state.codeList[0].id"
|
||||
@node-click="selectHandler"
|
||||
>
|
||||
<template #default="{ data }">
|
||||
<div :id="data.id" class="list-container">
|
||||
<div class="list-item">
|
||||
<div class="code-name">{{ data.name }}({{ data.id }})</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</TMagicTree>
|
||||
</template>
|
||||
<!-- 右侧区域 -->
|
||||
<template #center>
|
||||
<div
|
||||
v-if="!isEmpty(codeConfig)"
|
||||
:class="[
|
||||
mode === CodeEditorMode.LIST
|
||||
? 'm-editor-code-block-editor-panel-list-mode'
|
||||
: 'm-editor-code-block-editor-panel',
|
||||
]"
|
||||
>
|
||||
<div v-if="!isEmpty(codeConfig)" class="m-editor-code-block-editor-panel">
|
||||
<slot name="code-block-edit-panel-header" :id="id"></slot>
|
||||
<FunctionEditor
|
||||
v-if="codeConfig"
|
||||
:id="id"
|
||||
:name="codeConfig.name"
|
||||
:content="codeConfig.content"
|
||||
:paramsColConfig="paramsColConfig"
|
||||
:editable="!!editable"
|
||||
:autoSaveDraft="mode === CodeEditorMode.EDITOR"
|
||||
:autoSaveDraft="true"
|
||||
:codeOptions="codeOptions"
|
||||
></FunctionEditor>
|
||||
</div>
|
||||
@ -60,18 +33,22 @@
|
||||
import { computed, inject, reactive, ref, watchEffect } from 'vue';
|
||||
import { cloneDeep, forIn, isEmpty } from 'lodash-es';
|
||||
|
||||
import { TMagicDialog, TMagicTree } from '@tmagic/design';
|
||||
import { TMagicDialog } from '@tmagic/design';
|
||||
import { ColumnConfig } from '@tmagic/form';
|
||||
import { CodeBlockContent } from '@tmagic/schema';
|
||||
|
||||
import FunctionEditor from '../../../components/FunctionEditor.vue';
|
||||
import Layout from '../../../components/Layout.vue';
|
||||
import type { CodeDslItem, ListState, Services } from '../../../type';
|
||||
import { CodeEditorMode } from '../../../type';
|
||||
import type { ListState, Services } from '../../../type';
|
||||
import { serializeConfig } from '../../../utils/editor';
|
||||
|
||||
const services = inject<Services>('services');
|
||||
const codeOptions = inject('codeOptions', {});
|
||||
|
||||
defineProps<{
|
||||
paramsColConfig?: ColumnConfig;
|
||||
}>();
|
||||
|
||||
const left = ref(200);
|
||||
const currentTitle = ref('');
|
||||
// 编辑器当前需展示的代码块内容
|
||||
@ -81,7 +58,6 @@ const state = reactive<ListState>({
|
||||
codeList: [],
|
||||
});
|
||||
|
||||
const mode = computed(() => services?.codeBlockService.getMode());
|
||||
const id = computed(() => services?.codeBlockService.getId() || '');
|
||||
const editable = computed(() => services?.codeBlockService.getEditStatus());
|
||||
// 当前选中组件绑定的代码块id数组
|
||||
@ -105,9 +81,4 @@ watchEffect(async () => {
|
||||
});
|
||||
currentTitle.value = state.codeList[0]?.name || '';
|
||||
});
|
||||
|
||||
const selectHandler = (data: CodeDslItem) => {
|
||||
services?.codeBlockService.setId(data.id);
|
||||
currentTitle.value = data.name;
|
||||
};
|
||||
</script>
|
||||
|
@ -78,7 +78,7 @@
|
||||
</TMagicScrollbar>
|
||||
|
||||
<!-- 代码块编辑区 -->
|
||||
<code-block-editor v-if="isShowCodeBlockEditor">
|
||||
<code-block-editor v-if="isShowCodeBlockEditor" :paramsColConfig="paramsColConfig">
|
||||
<template #code-block-edit-panel-header="{ id }">
|
||||
<slot name="code-block-edit-panel-header" :id="id"></slot>
|
||||
</template>
|
||||
@ -92,16 +92,18 @@ import { Close, Edit, Link, View } from '@element-plus/icons-vue';
|
||||
import { cloneDeep, forIn, isEmpty } from 'lodash-es';
|
||||
|
||||
import { TMagicButton, TMagicInput, tMagicMessage, TMagicScrollbar, TMagicTooltip, TMagicTree } from '@tmagic/design';
|
||||
import { ColumnConfig } from '@tmagic/form';
|
||||
import { CodeBlockContent, Id } from '@tmagic/schema';
|
||||
|
||||
import Icon from '../../../components/Icon.vue';
|
||||
import type { CodeRelation, Services } from '../../../type';
|
||||
import { CodeDeleteErrorType, CodeDslItem, CodeEditorMode, ListState } from '../../../type';
|
||||
import { CodeDeleteErrorType, CodeDslItem, ListState } from '../../../type';
|
||||
|
||||
import codeBlockEditor from './CodeBlockEditor.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
customError?: (id: Id, errorType: CodeDeleteErrorType) => any;
|
||||
paramsColConfig?: ColumnConfig;
|
||||
}>();
|
||||
|
||||
const services = inject<Services>('services');
|
||||
@ -168,7 +170,6 @@ const createCodeBlock = async () => {
|
||||
content: `({app, params}) => {\n // place your code here\n}`,
|
||||
params: [],
|
||||
};
|
||||
await codeBlockService.setMode(CodeEditorMode.EDITOR);
|
||||
const id = await codeBlockService.getUniqueId();
|
||||
await codeBlockService.setCodeDslById(id, codeConfig);
|
||||
codeBlockService.setCodeEditorContent(true, id);
|
||||
@ -176,7 +177,6 @@ const createCodeBlock = async () => {
|
||||
|
||||
// 编辑代码块
|
||||
const editCode = async (key: Id) => {
|
||||
await services?.codeBlockService.setMode(CodeEditorMode.EDITOR);
|
||||
services?.codeBlockService.setCodeEditorContent(true, key);
|
||||
};
|
||||
|
||||
|
@ -23,7 +23,7 @@ import { CodeBlockContent, CodeBlockDSL, HookType, Id, MNode } from '@tmagic/sch
|
||||
|
||||
import editorService from '../services/editor';
|
||||
import type { CodeRelation, CodeState, HookData } from '../type';
|
||||
import { CODE_DRAFT_STORAGE_KEY, CodeEditorMode } from '../type';
|
||||
import { CODE_DRAFT_STORAGE_KEY } from '../type';
|
||||
import { info } from '../utils/logger';
|
||||
|
||||
import BaseService from './BaseService';
|
||||
@ -34,7 +34,6 @@ class CodeBlock extends BaseService {
|
||||
codeDsl: null,
|
||||
id: '',
|
||||
editable: true,
|
||||
mode: CodeEditorMode.EDITOR,
|
||||
combineIds: [],
|
||||
undeletableList: [],
|
||||
relations: {},
|
||||
@ -50,7 +49,6 @@ class CodeBlock extends BaseService {
|
||||
'setCodeDslById',
|
||||
'setCodeEditorShowStatus',
|
||||
'setEditStatus',
|
||||
'setMode',
|
||||
'setCombineIds',
|
||||
'setUndeleteableList',
|
||||
'deleteCodeDslByIds',
|
||||
@ -214,23 +212,6 @@ class CodeBlock extends BaseService {
|
||||
return this.state.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前模式
|
||||
* @returns {CodeEditorMode}
|
||||
*/
|
||||
public getMode(): CodeEditorMode {
|
||||
return this.state.mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前模式
|
||||
* @param {CodeEditorMode} mode 模式
|
||||
* @returns {void}
|
||||
*/
|
||||
public async setMode(mode: CodeEditorMode): Promise<void> {
|
||||
this.state.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前选中组件已关联绑定的代码块id数组
|
||||
* @param {string[]} ids 代码块id数组
|
||||
@ -349,7 +330,6 @@ class CodeBlock extends BaseService {
|
||||
this.state.codeDsl = null;
|
||||
this.state.id = '';
|
||||
this.state.editable = true;
|
||||
this.state.mode = CodeEditorMode.EDITOR;
|
||||
this.state.combineIds = [];
|
||||
this.state.undeletableList = [];
|
||||
}
|
||||
@ -386,6 +366,7 @@ class CodeBlock extends BaseService {
|
||||
*/
|
||||
private recurseMNode(node: MNode, relations: CodeRelation): void {
|
||||
forIn(node, (value, key) => {
|
||||
let unConfirmedValue: MNode = { id: node.id };
|
||||
if (value?.hookType === HookType.CODE && !isEmpty(value.hookData)) {
|
||||
value.hookData.forEach((relationItem: HookData) => {
|
||||
// continue
|
||||
@ -399,6 +380,16 @@ class CodeBlock extends BaseService {
|
||||
}
|
||||
codeItem[node.id].push(key);
|
||||
});
|
||||
// continue
|
||||
return;
|
||||
}
|
||||
if (typeof value === 'object') {
|
||||
// 检查value内部是否有嵌套
|
||||
unConfirmedValue = {
|
||||
...unConfirmedValue,
|
||||
...value,
|
||||
};
|
||||
this.recurseMNode(unConfirmedValue, relations);
|
||||
}
|
||||
});
|
||||
if (!isEmpty(node.items)) {
|
||||
|
@ -78,6 +78,10 @@
|
||||
|
||||
.m-fields-code-select {
|
||||
width: 100%;
|
||||
.edit-icon {
|
||||
cursor: pointer;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-dialog.is-fullscreen.code-editor-dialog {
|
||||
|
@ -335,8 +335,6 @@ export type CodeState = {
|
||||
id: Id;
|
||||
/** 代码块是否可编辑 */
|
||||
editable: boolean;
|
||||
/** 代码编辑面板的展示模式 */
|
||||
mode: CodeEditorMode;
|
||||
/** list模式下左侧展示的代码列表 */
|
||||
combineIds: string[];
|
||||
/** 为业务逻辑预留的不可删除的代码块列表,由业务逻辑维护(如代码块上线后不可删除) */
|
||||
@ -359,13 +357,6 @@ export type CodeRelation = {
|
||||
};
|
||||
};
|
||||
|
||||
export enum CodeEditorMode {
|
||||
/** 左侧菜单,右侧代码 */
|
||||
LIST = 'list',
|
||||
/** 全屏代码 */
|
||||
EDITOR = 'editor',
|
||||
}
|
||||
|
||||
export interface CodeDslItem {
|
||||
/** 代码块id */
|
||||
id: Id;
|
||||
@ -406,6 +397,7 @@ export interface CodeParamStatement {
|
||||
name: string;
|
||||
/** 参数类型 */
|
||||
type?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface StepValue {
|
||||
|
@ -24,11 +24,12 @@
|
||||
|
||||
<TMagicTableColumn
|
||||
label="操作"
|
||||
width="55"
|
||||
:width="config.operateColWidth || 55"
|
||||
align="center"
|
||||
:fixed="config.fixed === false ? undefined : 'left'"
|
||||
>
|
||||
<template v-slot="scope">
|
||||
<slot name="operateCol" :scope="scope"></slot>
|
||||
<TMagicIcon
|
||||
v-show="showDelete(scope.$index + 1 + pagecontext * pagesize - 1)"
|
||||
class="m-table-delete-icon"
|
||||
|
@ -598,11 +598,11 @@ export interface PanelConfig extends FormItem, ContainerCommonConfig {
|
||||
schematic?: string;
|
||||
}
|
||||
|
||||
export interface ColumnConfig extends FormItem, ContainerCommonConfig {
|
||||
name: string;
|
||||
export interface ColumnConfig extends FormItem {
|
||||
name?: string;
|
||||
label: string;
|
||||
width: string | number;
|
||||
sortable: boolean;
|
||||
width?: string | number;
|
||||
sortable?: boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@ -622,6 +622,8 @@ export interface TableConfig extends FormItem {
|
||||
border?: boolean;
|
||||
/** 显示行号 */
|
||||
showIndex?: boolean;
|
||||
/** 操作栏宽度 */
|
||||
operateColWidth?: number | string;
|
||||
pagination?: boolean;
|
||||
enum?: any[];
|
||||
/** 是否显示添加按钮 */
|
||||
@ -642,7 +644,7 @@ export interface TableConfig extends FormItem {
|
||||
enableFullscreen?: boolean;
|
||||
fixed?: boolean;
|
||||
itemExtra?: string | FilterFunction;
|
||||
rowKey: string;
|
||||
rowKey?: string;
|
||||
}
|
||||
|
||||
export interface GroupListConfig extends FormItem {
|
||||
|
@ -90,6 +90,8 @@ export interface CodeBlockContent {
|
||||
export interface CodeParam {
|
||||
/** 参数名 */
|
||||
name: string;
|
||||
/** 扩展字段 */
|
||||
[propName: string]: any;
|
||||
}
|
||||
export interface PastePosition {
|
||||
left?: number;
|
||||
|
Loading…
x
Reference in New Issue
Block a user