mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-08-28 10:29:31 +08:00
feat(core,editor,data-source,form,schema): 新增数据源方法配置,支持事件联动数据源方法
This commit is contained in:
parent
1a546c326c
commit
2a0680c707
@ -31,6 +31,7 @@ import {
|
|||||||
CodeBlockDSL,
|
CodeBlockDSL,
|
||||||
CodeItemConfig,
|
CodeItemConfig,
|
||||||
CompItemConfig,
|
CompItemConfig,
|
||||||
|
DataSourceItemConfig,
|
||||||
DeprecatedEventConfig,
|
DeprecatedEventConfig,
|
||||||
EventConfig,
|
EventConfig,
|
||||||
Id,
|
Id,
|
||||||
@ -321,6 +322,28 @@ class App extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async dataSourceActionHandler(eventConfig: DataSourceItemConfig) {
|
||||||
|
const { dataSourceMethod = [], params = {} } = eventConfig;
|
||||||
|
|
||||||
|
const [id, methodName] = dataSourceMethod;
|
||||||
|
|
||||||
|
if (!id || !methodName) return;
|
||||||
|
|
||||||
|
const dataSource = this.dataSourceManager?.get(id);
|
||||||
|
|
||||||
|
if (!dataSource) return;
|
||||||
|
|
||||||
|
const methods = dataSource.getMethods() || [];
|
||||||
|
|
||||||
|
const method = methods.find((item) => item.name === methodName);
|
||||||
|
|
||||||
|
if (!method) return;
|
||||||
|
|
||||||
|
if (typeof method.content === 'function') {
|
||||||
|
await method.content({ app: this, params, dataSource });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public compiledNode(node: MNode, content: DataSourceManagerData, sourceId?: Id) {
|
public compiledNode(node: MNode, content: DataSourceManagerData, sourceId?: Id) {
|
||||||
return compiledNode(
|
return compiledNode(
|
||||||
(value: any) => {
|
(value: any) => {
|
||||||
@ -364,6 +387,8 @@ class App extends EventEmitter {
|
|||||||
} else if (actionItem.actionType === ActionType.CODE) {
|
} else if (actionItem.actionType === ActionType.CODE) {
|
||||||
// 执行代码块
|
// 执行代码块
|
||||||
await this.codeActionHandler(actionItem as CodeItemConfig);
|
await this.codeActionHandler(actionItem as CodeItemConfig);
|
||||||
|
} else if (actionItem.actionType === ActionType.DATA_SOURCE) {
|
||||||
|
await this.dataSourceActionHandler(actionItem as DataSourceItemConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -102,8 +102,11 @@ class Node extends EventEmitter {
|
|||||||
if (this.data[hook]?.hookType !== HookType.CODE || isEmpty(this.app.codeDsl)) return;
|
if (this.data[hook]?.hookType !== HookType.CODE || isEmpty(this.app.codeDsl)) return;
|
||||||
for (const item of this.data[hook].hookData) {
|
for (const item of this.data[hook].hookData) {
|
||||||
const { codeId, params = {} } = item;
|
const { codeId, params = {} } = item;
|
||||||
if (this.app.codeDsl![codeId] && typeof this.app.codeDsl![codeId]?.content === 'function') {
|
|
||||||
await this.app.codeDsl![codeId].content({ app: this.app, params });
|
const functionContent = this.app.codeDsl?.[codeId]?.content;
|
||||||
|
|
||||||
|
if (typeof functionContent === 'function') {
|
||||||
|
await functionContent({ app: this.app, params });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
|
|
||||||
import type { DataSchema } from '@tmagic/schema';
|
import type { CodeBlockContent, DataSchema } from '@tmagic/schema';
|
||||||
|
|
||||||
import type { DataSourceOptions } from '@data-source/types';
|
import type { DataSourceOptions } from '@data-source/types';
|
||||||
import { getDefaultValueFromFields } from '@data-source/util';
|
import { getDefaultValueFromFields } from '@data-source/util';
|
||||||
@ -35,12 +35,14 @@ export default class DataSource extends EventEmitter {
|
|||||||
public data: Record<string, any> = {};
|
public data: Record<string, any> = {};
|
||||||
|
|
||||||
private fields: DataSchema[] = [];
|
private fields: DataSchema[] = [];
|
||||||
|
private methods: CodeBlockContent[] = [];
|
||||||
|
|
||||||
constructor(options: DataSourceOptions) {
|
constructor(options: DataSourceOptions) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.id = options.schema.id;
|
this.id = options.schema.id;
|
||||||
this.setFields(options.schema.fields);
|
this.setFields(options.schema.fields);
|
||||||
|
this.setMethods(options.schema.methods || []);
|
||||||
|
|
||||||
this.updateDefaultData();
|
this.updateDefaultData();
|
||||||
}
|
}
|
||||||
@ -49,6 +51,14 @@ export default class DataSource extends EventEmitter {
|
|||||||
this.fields = fields;
|
this.fields = fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setMethods(methods: CodeBlockContent[]) {
|
||||||
|
this.methods = methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getMethods() {
|
||||||
|
return this.methods;
|
||||||
|
}
|
||||||
|
|
||||||
public setData(data: Record<string, any>) {
|
public setData(data: Record<string, any>) {
|
||||||
// todo: 校验数据,看是否符合 schema
|
// todo: 校验数据,看是否符合 schema
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
@ -40,10 +40,6 @@
|
|||||||
<template #code-block-panel-tool="{ id, data }">
|
<template #code-block-panel-tool="{ id, data }">
|
||||||
<slot name="code-block-panel-tool" :id="id" :data="data"></slot>
|
<slot name="code-block-panel-tool" :id="id" :data="data"></slot>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #code-block-edit-panel-header="{ id }">
|
|
||||||
<slot name="code-block-edit-panel-header" :id="id"></slot>
|
|
||||||
</template>
|
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
</slot>
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
|
141
packages/editor/src/components/CodeBlockEditor.vue
Normal file
141
packages/editor/src/components/CodeBlockEditor.vue
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
<template>
|
||||||
|
<MFormDrawer
|
||||||
|
ref="fomDrawer"
|
||||||
|
label-width="80px"
|
||||||
|
:title="content.name"
|
||||||
|
:width="size"
|
||||||
|
:config="functionConfig"
|
||||||
|
:values="content"
|
||||||
|
:disabled="disabled"
|
||||||
|
@submit="submitForm"
|
||||||
|
@error="errorHandler"
|
||||||
|
></MFormDrawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, inject, ref } from 'vue';
|
||||||
|
|
||||||
|
import { tMagicMessage } from '@tmagic/design';
|
||||||
|
import { ColumnConfig, FormState, MFormDrawer } from '@tmagic/form';
|
||||||
|
import type { CodeBlockContent } from '@tmagic/schema';
|
||||||
|
|
||||||
|
import type { Services } from '@editor/type';
|
||||||
|
import { getConfig } from '@editor/utils/config';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'MEditorCodeBlockEditor',
|
||||||
|
});
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
content: CodeBlockContent;
|
||||||
|
disabled?: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [values: CodeBlockContent];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const services = inject<Services>('services');
|
||||||
|
|
||||||
|
const size = computed(() => globalThis.document.body.clientWidth - (services?.uiService.get('columnWidth').left || 0));
|
||||||
|
|
||||||
|
const defaultParamColConfig: ColumnConfig = {
|
||||||
|
type: 'row',
|
||||||
|
label: '参数类型',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
text: '参数类型',
|
||||||
|
labelWidth: '70px',
|
||||||
|
type: 'select',
|
||||||
|
name: 'type',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
text: '数字',
|
||||||
|
label: '数字',
|
||||||
|
value: 'number',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '字符串',
|
||||||
|
label: '字符串',
|
||||||
|
value: 'text',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '组件',
|
||||||
|
label: '组件',
|
||||||
|
value: 'ui-select',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const functionConfig = computed(() => [
|
||||||
|
{
|
||||||
|
text: '名称',
|
||||||
|
name: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '注释',
|
||||||
|
name: 'desc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'table',
|
||||||
|
border: true,
|
||||||
|
text: '参数',
|
||||||
|
enableFullscreen: false,
|
||||||
|
name: 'params',
|
||||||
|
maxHeight: '300px',
|
||||||
|
dropSort: false,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
label: '参数名',
|
||||||
|
name: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
label: '注释',
|
||||||
|
name: 'extra',
|
||||||
|
},
|
||||||
|
services?.codeBlockService.getParamsColConfig() || defaultParamColConfig,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'content',
|
||||||
|
type: 'vs-code',
|
||||||
|
options: inject('codeOptions', {}),
|
||||||
|
onChange: (formState: FormState | undefined, code: string) => {
|
||||||
|
try {
|
||||||
|
// 检测js代码是否存在语法错误
|
||||||
|
getConfig('parseDSL')(code);
|
||||||
|
|
||||||
|
return code;
|
||||||
|
} catch (error: any) {
|
||||||
|
tMagicMessage.error(error.message);
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const submitForm = async (values: CodeBlockContent) => {
|
||||||
|
emit('submit', values);
|
||||||
|
};
|
||||||
|
|
||||||
|
const errorHandler = (error: any) => {
|
||||||
|
tMagicMessage.error(error.message);
|
||||||
|
};
|
||||||
|
|
||||||
|
const fomDrawer = ref<InstanceType<typeof MFormDrawer>>();
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
show() {
|
||||||
|
fomDrawer.value?.show();
|
||||||
|
},
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
fomDrawer.value?.hide();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
@ -1,148 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="m-editor-wrapper" :class="isFullScreen ? 'fullScreen' : 'normal'">
|
|
||||||
<magic-code-editor
|
|
||||||
ref="codeEditor"
|
|
||||||
class="m-editor-container"
|
|
||||||
:init-values="`${codeContent}`"
|
|
||||||
@save="saveCodeDraft"
|
|
||||||
:language="language"
|
|
||||||
:options="codeOptions"
|
|
||||||
></magic-code-editor>
|
|
||||||
<div class="m-editor-content-bottom" v-if="editable">
|
|
||||||
<TMagicButton type="primary" class="button" @click="toggleFullScreen">
|
|
||||||
{{ isFullScreen ? '退出全屏' : '全屏' }}</TMagicButton
|
|
||||||
>
|
|
||||||
<TMagicButton type="primary" class="button" @click="saveAndClose">确认</TMagicButton>
|
|
||||||
<TMagicButton class="button" @click="close">关闭</TMagicButton>
|
|
||||||
</div>
|
|
||||||
<div class="m-editor-content-bottom" v-else>
|
|
||||||
<TMagicButton type="primary" class="button" @click="toggleFullScreen">
|
|
||||||
{{ isFullScreen ? '退出全屏' : '全屏' }}</TMagicButton
|
|
||||||
>
|
|
||||||
<TMagicButton class="button" @click="close">关闭</TMagicButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { computed, inject, ref, watchEffect } from 'vue';
|
|
||||||
import type * as monaco from 'monaco-editor';
|
|
||||||
|
|
||||||
import { TMagicButton, tMagicMessage, tMagicMessageBox } from '@tmagic/design';
|
|
||||||
import { Id } from '@tmagic/schema';
|
|
||||||
import { datetimeFormatter } from '@tmagic/utils';
|
|
||||||
|
|
||||||
import MagicCodeEditor from '@editor/layouts/CodeEditor.vue';
|
|
||||||
import type { Services } from '@editor/type';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'MEditorCodeDraftEditor',
|
|
||||||
});
|
|
||||||
|
|
||||||
const props = withDefaults(
|
|
||||||
defineProps<{
|
|
||||||
/** 代码id */
|
|
||||||
id: Id;
|
|
||||||
/** 代码内容 */
|
|
||||||
content: string;
|
|
||||||
/** 是否可编辑 */
|
|
||||||
editable?: boolean;
|
|
||||||
/** 是否自动保存草稿 */
|
|
||||||
autoSaveDraft?: boolean;
|
|
||||||
/** 编辑器参数 */
|
|
||||||
codeOptions?: Object;
|
|
||||||
/** 编辑器语言 */
|
|
||||||
language?: string;
|
|
||||||
}>(),
|
|
||||||
{
|
|
||||||
editable: true,
|
|
||||||
autoSaveDraft: true,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
const emit = defineEmits(['close', 'saveAndClose']);
|
|
||||||
|
|
||||||
const services = inject<Services>('services');
|
|
||||||
|
|
||||||
const codeContent = ref<string>('');
|
|
||||||
const editorContent = ref<string>('');
|
|
||||||
const codeEditor = ref<InstanceType<typeof MagicCodeEditor>>();
|
|
||||||
// 原始代码内容
|
|
||||||
const originCodeContent = ref<string>('');
|
|
||||||
const isFullScreen = ref<boolean>(false);
|
|
||||||
|
|
||||||
const codeOptions = computed(() => ({
|
|
||||||
...props.codeOptions,
|
|
||||||
readOnly: !props.editable,
|
|
||||||
}));
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
codeContent.value = props.content;
|
|
||||||
if (!originCodeContent.value) {
|
|
||||||
// 暂存原始的代码内容
|
|
||||||
originCodeContent.value = codeContent.value;
|
|
||||||
}
|
|
||||||
// 有草稿时展示上次保存的草稿内容
|
|
||||||
const codeDraft = services?.codeBlockService.getCodeDraft(props.id);
|
|
||||||
if (codeDraft) {
|
|
||||||
codeContent.value = codeDraft;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 保存草稿
|
|
||||||
const saveCodeDraft = async (codeValue: string) => {
|
|
||||||
if (!props.autoSaveDraft) return;
|
|
||||||
if (originCodeContent.value === codeValue) {
|
|
||||||
// 没修改或改回原样 有草稿的话删除草稿
|
|
||||||
services?.codeBlockService.removeCodeDraft(props.id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
services?.codeBlockService.setCodeDraft(props.id, codeValue);
|
|
||||||
tMagicMessage.success(`代码草稿成功保存到本地 ${datetimeFormatter(new Date())}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 保存并关闭
|
|
||||||
const saveAndClose = (): void => {
|
|
||||||
if (!codeEditor.value || !props.editable) return;
|
|
||||||
// 代码内容
|
|
||||||
editorContent.value = (codeEditor.value.getEditor() as monaco.editor.IStandaloneCodeEditor)?.getValue();
|
|
||||||
emit('saveAndClose', editorContent.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 关闭弹窗
|
|
||||||
const close = async (): Promise<void> => {
|
|
||||||
const codeDraft = services?.codeBlockService.getCodeDraft(props.id);
|
|
||||||
if (codeDraft) {
|
|
||||||
try {
|
|
||||||
await tMagicMessageBox.confirm('您有代码修改未保存,是否保存后再关闭?', '提示', {
|
|
||||||
confirmButtonText: '保存并关闭',
|
|
||||||
cancelButtonText: '直接关闭',
|
|
||||||
type: 'warning',
|
|
||||||
distinguishCancelAndClose: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 保存之后再关闭
|
|
||||||
saveAndClose();
|
|
||||||
} catch (action: any) {
|
|
||||||
if (action === 'cancel') {
|
|
||||||
// 删除草稿 直接关闭
|
|
||||||
services?.codeBlockService.removeCodeDraft(props.id);
|
|
||||||
emit('close');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
emit('close');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 切换全屏
|
|
||||||
const toggleFullScreen = (): void => {
|
|
||||||
isFullScreen.value = !isFullScreen.value;
|
|
||||||
if (codeEditor.value) {
|
|
||||||
codeEditor.value.focus();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
saveAndClose,
|
|
||||||
close,
|
|
||||||
});
|
|
||||||
</script>
|
|
@ -51,7 +51,6 @@ const codeParamsConfig = computed(() => getFormConfig(props.paramsConfig));
|
|||||||
const onParamsChangeHandler = async () => {
|
const onParamsChangeHandler = async () => {
|
||||||
try {
|
try {
|
||||||
const value = await form.value?.submitForm(true);
|
const value = await form.value?.submitForm(true);
|
||||||
console.log(value);
|
|
||||||
emit('change', value);
|
emit('change', value);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
|
@ -1,198 +0,0 @@
|
|||||||
<template>
|
|
||||||
<TMagicCard shadow="never">
|
|
||||||
<template #header>
|
|
||||||
<div class="code-name-wrapper">
|
|
||||||
<div class="code-name-label">代码块名称</div>
|
|
||||||
<TMagicInput class="code-name-input" v-model="codeName" :disabled="!editable" />
|
|
||||||
</div>
|
|
||||||
<div class="code-name-wrapper">
|
|
||||||
<div class="code-name-label">参数</div>
|
|
||||||
<m-form-table
|
|
||||||
style="width: 800px"
|
|
||||||
:config="tableConfig"
|
|
||||||
:model="tableModel"
|
|
||||||
:enableToggleMode="false"
|
|
||||||
:disabled="!editable"
|
|
||||||
name="params"
|
|
||||||
prop="params"
|
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
</m-form-table>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<CodeDraftEditor
|
|
||||||
ref="codeDraftEditor"
|
|
||||||
:id="id"
|
|
||||||
:content="codeContent"
|
|
||||||
:editable="editable"
|
|
||||||
:autoSaveDraft="autoSaveDraft"
|
|
||||||
:codeOptions="codeOptions"
|
|
||||||
language="javascript"
|
|
||||||
@saveAndClose="saveAndClose"
|
|
||||||
@close="close"
|
|
||||||
></CodeDraftEditor>
|
|
||||||
</TMagicCard>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
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 '@editor/type';
|
|
||||||
import { getConfig } from '@editor/utils/config';
|
|
||||||
|
|
||||||
import CodeDraftEditor from './CodeDraftEditor.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'MEditorFunctionEditor',
|
|
||||||
});
|
|
||||||
|
|
||||||
provide('mForm', null);
|
|
||||||
|
|
||||||
const defaultParamColConfig: ColumnConfig = {
|
|
||||||
type: 'row',
|
|
||||||
label: '参数类型',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
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,
|
|
||||||
autoSaveDraft: true,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
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: 'extra',
|
|
||||||
width: 200,
|
|
||||||
},
|
|
||||||
paramsColConfig,
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const services = inject<Services>('services');
|
|
||||||
|
|
||||||
const codeName = ref<string>('');
|
|
||||||
const codeContent = ref<string>('');
|
|
||||||
const evalRes = ref(true);
|
|
||||||
|
|
||||||
const tableModel = ref<{ params: CodeParam[] }>();
|
|
||||||
watchEffect(() => {
|
|
||||||
codeName.value = props.name;
|
|
||||||
codeContent.value = props.content;
|
|
||||||
});
|
|
||||||
|
|
||||||
const initTableModel = (): void => {
|
|
||||||
const codeDsl = cloneDeep(services?.codeBlockService.getCodeDsl());
|
|
||||||
if (!codeDsl) return;
|
|
||||||
tableModel.value = {
|
|
||||||
params: codeDsl[props.id]?.params || [],
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
initTableModel();
|
|
||||||
|
|
||||||
// 保存前钩子
|
|
||||||
const beforeSave = (codeValue: string): boolean => {
|
|
||||||
try {
|
|
||||||
// 检测js代码是否存在语法错误
|
|
||||||
getConfig('parseDSL')(codeValue);
|
|
||||||
return true;
|
|
||||||
} catch (e: any) {
|
|
||||||
tMagicMessage.error(e.stack);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 保存代码
|
|
||||||
const saveCode = async (codeValue: string): Promise<void> => {
|
|
||||||
if (!props.editable) return;
|
|
||||||
evalRes.value = beforeSave(codeValue);
|
|
||||||
if (evalRes.value) {
|
|
||||||
// 存入dsl
|
|
||||||
await services?.codeBlockService.setCodeDslById(props.id, {
|
|
||||||
name: codeName.value,
|
|
||||||
content: codeValue,
|
|
||||||
params: tableModel.value?.params || [],
|
|
||||||
});
|
|
||||||
tMagicMessage.success('代码成功保存到本地');
|
|
||||||
// 删除草稿
|
|
||||||
services?.codeBlockService.removeCodeDraft(props.id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 保存并关闭
|
|
||||||
const saveAndClose = async (codeValue: string): Promise<void> => {
|
|
||||||
await saveCode(codeValue);
|
|
||||||
if (evalRes.value) {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 关闭弹窗
|
|
||||||
const close = (): void => {
|
|
||||||
services?.codeBlockService.setCodeEditorShowStatus(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const codeDraftEditor = ref<InstanceType<typeof CodeDraftEditor>>();
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
codeDraftEditor,
|
|
||||||
});
|
|
||||||
</script>
|
|
@ -1,32 +1,45 @@
|
|||||||
<template>
|
<template>
|
||||||
<magic-code-editor
|
<MagicCodeEditor
|
||||||
:style="`height: ${height}`"
|
:height="height"
|
||||||
:init-values="model[name]"
|
:init-values="model[name]"
|
||||||
:language="language"
|
:language="language"
|
||||||
:options="config.options"
|
:options="{
|
||||||
|
...config.options,
|
||||||
|
readOnly: disabled,
|
||||||
|
}"
|
||||||
@save="save"
|
@save="save"
|
||||||
></magic-code-editor>
|
></MagicCodeEditor>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
import MagicCodeEditor from '@editor/layouts/CodeEditor.vue';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'MEditorCode',
|
name: 'MEditorCode',
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['change']);
|
const emit = defineEmits(['change']);
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = withDefaults(
|
||||||
model: any;
|
defineProps<{
|
||||||
name: string;
|
config: {
|
||||||
config: {
|
language?: string;
|
||||||
language?: string;
|
options?: Object;
|
||||||
options?: Object;
|
height?: string;
|
||||||
height?: string;
|
};
|
||||||
};
|
model: any;
|
||||||
prop: string;
|
name: string;
|
||||||
}>();
|
prop: string;
|
||||||
|
lastValues?: any;
|
||||||
|
disabled?: boolean;
|
||||||
|
size?: 'small' | 'default' | 'large';
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const language = computed(() => props.config.language || 'javascript');
|
const language = computed(() => props.config.language || 'javascript');
|
||||||
const height = computed(() => props.config.height || `${document.body.clientHeight - 168}px`);
|
const height = computed(() => props.config.height || `${document.body.clientHeight - 168}px`);
|
||||||
|
@ -9,18 +9,26 @@
|
|||||||
@change="onParamsChangeHandler"
|
@change="onParamsChangeHandler"
|
||||||
></m-form-container>
|
></m-form-container>
|
||||||
<!-- 查看/编辑按钮 -->
|
<!-- 查看/编辑按钮 -->
|
||||||
<Icon v-if="model[name]" class="icon" :icon="!disabled ? Edit : View" @click="editCode"></Icon>
|
<Icon v-if="model[name]" class="icon" :icon="!disabled ? Edit : View" @click="editCode(model[name])"></Icon>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 参数填写框 -->
|
<!-- 参数填写框 -->
|
||||||
<CodeParams
|
<CodeParams
|
||||||
v-if="paramsConfig.length"
|
v-if="paramsConfig.length"
|
||||||
name="params"
|
name="params"
|
||||||
:model="model"
|
:model="model"
|
||||||
:size="size"
|
:size="size"
|
||||||
:disabled="disabled"
|
|
||||||
:params-config="paramsConfig"
|
:params-config="paramsConfig"
|
||||||
@change="onParamsChangeHandler"
|
@change="onParamsChangeHandler"
|
||||||
></CodeParams>
|
></CodeParams>
|
||||||
|
|
||||||
|
<CodeBlockEditor
|
||||||
|
ref="codeBlockEditor"
|
||||||
|
v-if="codeConfig"
|
||||||
|
:disabled="disabled"
|
||||||
|
:content="codeConfig"
|
||||||
|
@submit="submitCodeBlockHandler"
|
||||||
|
></CodeBlockEditor>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -32,9 +40,11 @@ import { isEmpty, map } from 'lodash-es';
|
|||||||
import { createValues } from '@tmagic/form';
|
import { createValues } from '@tmagic/form';
|
||||||
import type { Id } from '@tmagic/schema';
|
import type { Id } from '@tmagic/schema';
|
||||||
|
|
||||||
|
import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
|
||||||
import CodeParams from '@editor/components/CodeParams.vue';
|
import CodeParams from '@editor/components/CodeParams.vue';
|
||||||
import Icon from '@editor/components/Icon.vue';
|
import Icon from '@editor/components/Icon.vue';
|
||||||
import type { CodeParamStatement, CodeSelectColConfig, Services } from '@editor/type';
|
import type { CodeParamStatement, CodeSelectColConfig, Services } from '@editor/type';
|
||||||
|
import { useCodeBlockEdit } from '@editor/utils/use-code-block-edit';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'MEditorCodeSelectCol',
|
name: 'MEditorCodeSelectCol',
|
||||||
@ -53,7 +63,9 @@ const props = withDefaults(
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
size: 'small' | 'default' | 'large';
|
size: 'small' | 'default' | 'large';
|
||||||
}>(),
|
}>(),
|
||||||
{},
|
{
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -114,8 +126,5 @@ const onParamsChangeHandler = (value: any) => {
|
|||||||
emit('change', props.model);
|
emit('change', props.model);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 打开代码编辑框
|
const { codeBlockEditor, codeConfig, editCode, submitCodeBlockHandler } = useCodeBlockEdit(services?.codeBlockService);
|
||||||
const editCode = () => {
|
|
||||||
services?.codeBlockService.setCodeEditorContent(true, props.model[props.name]);
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="m-editor-data-source-fields">
|
<div class="m-editor-data-source-fields">
|
||||||
<MagicTable :data="model[name]" :columns="filedColumns"></MagicTable>
|
<MagicTable :data="model[name]" :columns="fieldColumns"></MagicTable>
|
||||||
|
|
||||||
<div class="m-editor-data-source-fields-footer">
|
<div class="m-editor-data-source-fields-footer">
|
||||||
<TMagicButton size="small" type="primary" :disabled="disabled" plain @click="newHandler()">添加</TMagicButton>
|
<TMagicButton size="small" type="primary" :disabled="disabled" plain @click="newHandler()">添加</TMagicButton>
|
||||||
@ -12,6 +12,7 @@
|
|||||||
:config="dataSourceFieldsConfig"
|
:config="dataSourceFieldsConfig"
|
||||||
:values="fieldValues"
|
:values="fieldValues"
|
||||||
:parentValues="model[name]"
|
:parentValues="model[name]"
|
||||||
|
:disabled="disabled"
|
||||||
@submit="fieldChange"
|
@submit="fieldChange"
|
||||||
></MFormDialog>
|
></MFormDialog>
|
||||||
</div>
|
</div>
|
||||||
@ -35,6 +36,7 @@ const props = withDefaults(
|
|||||||
};
|
};
|
||||||
model: any;
|
model: any;
|
||||||
prop: string;
|
prop: string;
|
||||||
|
lastValues?: any;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
}>(),
|
}>(),
|
||||||
@ -50,27 +52,24 @@ const fieldValues = ref<Record<string, any>>({});
|
|||||||
const filedTitle = ref('');
|
const filedTitle = ref('');
|
||||||
|
|
||||||
const newHandler = () => {
|
const newHandler = () => {
|
||||||
if (!addDialog.value) return;
|
|
||||||
fieldValues.value = {};
|
fieldValues.value = {};
|
||||||
filedTitle.value = '新增属性';
|
filedTitle.value = '新增属性';
|
||||||
addDialog.value.dialogVisible = true;
|
addDialog.value?.show();
|
||||||
};
|
};
|
||||||
|
|
||||||
const fieldChange = ({ index, ...value }: Record<string, any>) => {
|
const fieldChange = ({ index, ...value }: Record<string, any>) => {
|
||||||
if (!addDialog.value) return;
|
|
||||||
|
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
props.model[props.name][index] = value;
|
props.model[props.name][index] = value;
|
||||||
} else {
|
} else {
|
||||||
props.model[props.name].push(value);
|
props.model[props.name].push(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
addDialog.value.dialogVisible = false;
|
addDialog.value?.hide();
|
||||||
|
|
||||||
emit('change', props.model[props.name]);
|
emit('change', props.model[props.name]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const filedColumns = [
|
const fieldColumns = [
|
||||||
{
|
{
|
||||||
label: '属性名称',
|
label: '属性名称',
|
||||||
prop: 'title',
|
prop: 'title',
|
||||||
@ -90,13 +89,12 @@ const filedColumns = [
|
|||||||
{
|
{
|
||||||
text: '编辑',
|
text: '编辑',
|
||||||
handler: (row: Record<string, any>, index: number) => {
|
handler: (row: Record<string, any>, index: number) => {
|
||||||
if (!addDialog.value) return;
|
|
||||||
fieldValues.value = {
|
fieldValues.value = {
|
||||||
...row,
|
...row,
|
||||||
index,
|
index,
|
||||||
};
|
};
|
||||||
filedTitle.value = `编辑${row.title}`;
|
filedTitle.value = `编辑${row.title}`;
|
||||||
addDialog.value.dialogVisible = true;
|
addDialog.value?.show();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
147
packages/editor/src/fields/DataSourceMethodSelect.vue
Normal file
147
packages/editor/src/fields/DataSourceMethodSelect.vue
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<template>
|
||||||
|
<div class="m-fields-data-source-method-select">
|
||||||
|
<div class="data-source-method-select-container">
|
||||||
|
<m-form-container
|
||||||
|
class="select"
|
||||||
|
:config="cascaderConfig"
|
||||||
|
:model="model"
|
||||||
|
@change="onChangeHandler"
|
||||||
|
></m-form-container>
|
||||||
|
<Icon v-if="model[name]" class="icon" :icon="!disabled ? Edit : View" @click="editCodeHandler"></Icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<CodeParams
|
||||||
|
v-if="paramsConfig.length"
|
||||||
|
name="params"
|
||||||
|
:model="model"
|
||||||
|
:size="size"
|
||||||
|
:disabled="disabled"
|
||||||
|
:params-config="paramsConfig"
|
||||||
|
@change="onChangeHandler"
|
||||||
|
></CodeParams>
|
||||||
|
|
||||||
|
<CodeBlockEditor
|
||||||
|
ref="codeBlockEditor"
|
||||||
|
v-if="codeConfig"
|
||||||
|
:disabled="disabled"
|
||||||
|
:content="codeConfig"
|
||||||
|
@submit="submitCodeBlockHandler"
|
||||||
|
></CodeBlockEditor>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup name="">
|
||||||
|
import { computed, inject, ref } from 'vue';
|
||||||
|
import { Edit, View } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
|
import { createValues } from '@tmagic/form';
|
||||||
|
import type { CodeBlockContent, Id } from '@tmagic/schema';
|
||||||
|
|
||||||
|
import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
|
||||||
|
import CodeParams from '@editor/components/CodeParams.vue';
|
||||||
|
import Icon from '@editor/components/Icon.vue';
|
||||||
|
import type { CodeParamStatement, DataSourceMethodSelectConfig, Services } from '@editor/type';
|
||||||
|
import { useDataSourceMethod } from '@editor/utils/use-data-source-method';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'MEditorDataSourceMethodSelect',
|
||||||
|
});
|
||||||
|
|
||||||
|
const services = inject<Services>('services');
|
||||||
|
const emit = defineEmits(['change']);
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
config: DataSourceMethodSelectConfig;
|
||||||
|
model: any;
|
||||||
|
lastValues?: any;
|
||||||
|
prop: string;
|
||||||
|
name: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
size: 'small' | 'default' | 'large';
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const dataSources = computed(() => services?.dataSourceService.get('dataSources'));
|
||||||
|
|
||||||
|
const getParamItemsConfig = ([dataSourceId, medthodName]: [Id, string] = ['', '']): CodeParamStatement[] => {
|
||||||
|
if (!dataSourceId) return [];
|
||||||
|
|
||||||
|
const paramStatements = dataSources.value
|
||||||
|
?.find((item) => item.id === dataSourceId)
|
||||||
|
?.methods?.find((item) => item.name === medthodName)?.params;
|
||||||
|
|
||||||
|
if (!paramStatements) return [];
|
||||||
|
|
||||||
|
return paramStatements.map((paramState: CodeParamStatement) => ({
|
||||||
|
labelWidth: '100px',
|
||||||
|
text: paramState.name,
|
||||||
|
...paramState,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const paramsConfig = ref<CodeParamStatement[]>(getParamItemsConfig(props.model.dataSourceMethod));
|
||||||
|
|
||||||
|
const setParamsConfig = (dataSourceMethod: [Id, string], formState: any = {}) => {
|
||||||
|
// 通过下拉框选择的codeId变化后修正model的值,避免写入其他codeId的params
|
||||||
|
paramsConfig.value = dataSourceMethod ? getParamItemsConfig(dataSourceMethod) : [];
|
||||||
|
|
||||||
|
if (paramsConfig.value.length) {
|
||||||
|
props.model.params = createValues(formState, paramsConfig.value, {}, props.model.params);
|
||||||
|
} else {
|
||||||
|
props.model.params = {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const cascaderConfig = {
|
||||||
|
type: 'cascader',
|
||||||
|
text: '数据源方法',
|
||||||
|
name: props.name,
|
||||||
|
labelWidth: '80px',
|
||||||
|
options: () =>
|
||||||
|
dataSources.value
|
||||||
|
?.filter((ds) => ds.methods?.length)
|
||||||
|
?.map((ds) => ({
|
||||||
|
label: `${ds.title}(${ds.id})`,
|
||||||
|
value: ds.id,
|
||||||
|
children: ds.methods?.map((method) => ({
|
||||||
|
label: method.name,
|
||||||
|
value: method.name,
|
||||||
|
})),
|
||||||
|
})) || [],
|
||||||
|
onChange: (formState: any, dataSourceMethod: [Id, string]) => {
|
||||||
|
setParamsConfig(dataSourceMethod, formState);
|
||||||
|
|
||||||
|
return dataSourceMethod;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数值修改更新
|
||||||
|
*/
|
||||||
|
const onChangeHandler = (value: any) => {
|
||||||
|
props.model.params = value.params;
|
||||||
|
emit('change', props.model);
|
||||||
|
};
|
||||||
|
|
||||||
|
const { codeBlockEditor, codeConfig, editCode, submitCode } = useDataSourceMethod();
|
||||||
|
|
||||||
|
const editCodeHandler = () => {
|
||||||
|
const [id, name] = props.model[props.name];
|
||||||
|
|
||||||
|
const dataSource = services?.dataSourceService.getDataSourceById(id);
|
||||||
|
|
||||||
|
if (!dataSource) return;
|
||||||
|
|
||||||
|
editCode(dataSource, name);
|
||||||
|
|
||||||
|
setParamsConfig([id, name]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitCodeBlockHandler = (value: CodeBlockContent) => {
|
||||||
|
submitCode(value);
|
||||||
|
};
|
||||||
|
</script>
|
103
packages/editor/src/fields/DataSourceMethods.vue
Normal file
103
packages/editor/src/fields/DataSourceMethods.vue
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
<template>
|
||||||
|
<div class="m-editor-data-source-methods">
|
||||||
|
<MagicTable :data="model[name]" :columns="methodColumns"></MagicTable>
|
||||||
|
|
||||||
|
<div class="m-editor-data-source-methods-footer">
|
||||||
|
<TMagicButton size="small" type="primary" :disabled="disabled" plain @click="createCodeHandler"
|
||||||
|
>添加</TMagicButton
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<CodeBlockEditor
|
||||||
|
ref="codeBlockEditor"
|
||||||
|
v-if="codeConfig"
|
||||||
|
:disabled="disabled"
|
||||||
|
:content="codeConfig"
|
||||||
|
@submit="submitCodeHandler"
|
||||||
|
></CodeBlockEditor>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { TMagicButton } from '@tmagic/design';
|
||||||
|
import type { CodeBlockContent } from '@tmagic/schema';
|
||||||
|
import { MagicTable } from '@tmagic/table';
|
||||||
|
|
||||||
|
import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
|
||||||
|
import { useDataSourceMethod } from '@editor/utils/use-data-source-method';
|
||||||
|
|
||||||
|
import { CodeParamStatement } from '..';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'MEditorDataSourceMethods',
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
config: {
|
||||||
|
type: 'data-source-methods';
|
||||||
|
};
|
||||||
|
model: any;
|
||||||
|
prop: string;
|
||||||
|
lastValues?: any;
|
||||||
|
disabled: boolean;
|
||||||
|
name: string;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits(['change']);
|
||||||
|
|
||||||
|
const { codeConfig, codeBlockEditor, createCode, editCode, deleteCode, submitCode } = useDataSourceMethod();
|
||||||
|
|
||||||
|
const methodColumns = [
|
||||||
|
{
|
||||||
|
label: '名称',
|
||||||
|
prop: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '注释',
|
||||||
|
prop: 'desc',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '参数',
|
||||||
|
prop: 'params',
|
||||||
|
formatter: (params: CodeParamStatement[]) => params.map((item) => item.name).join(', '),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '操作',
|
||||||
|
fixed: 'right',
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
text: '编辑',
|
||||||
|
handler: (row: CodeBlockContent) => {
|
||||||
|
editCode(props.model, row.name);
|
||||||
|
emit('change', props.model[props.name]);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '删除',
|
||||||
|
buttonType: 'danger',
|
||||||
|
handler: (row: CodeBlockContent) => {
|
||||||
|
deleteCode(props.model, row.name);
|
||||||
|
emit('change', props.model[props.name]);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const createCodeHandler = () => {
|
||||||
|
createCode(props.model);
|
||||||
|
|
||||||
|
emit('change', props.model[props.name]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitCodeHandler = (values: CodeBlockContent) => {
|
||||||
|
submitCode(values);
|
||||||
|
|
||||||
|
emit('change', props.model[props.name]);
|
||||||
|
};
|
||||||
|
</script>
|
@ -42,7 +42,7 @@ import { TMagicButton } from '@tmagic/design';
|
|||||||
import { FormState } from '@tmagic/form';
|
import { FormState } from '@tmagic/form';
|
||||||
import { ActionType } from '@tmagic/schema';
|
import { ActionType } from '@tmagic/schema';
|
||||||
|
|
||||||
import type { EventSelectConfig, Services } from '@editor/type';
|
import type { CodeSelectColConfig, DataSourceMethodSelectConfig, EventSelectConfig, Services } from '@editor/type';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'MEditorEventSelect',
|
name: 'MEditorEventSelect',
|
||||||
@ -81,7 +81,7 @@ const actionTypeConfig = computed(() => {
|
|||||||
const defaultActionTypeConfig = {
|
const defaultActionTypeConfig = {
|
||||||
name: 'actionType',
|
name: 'actionType',
|
||||||
text: '联动类型',
|
text: '联动类型',
|
||||||
labelWidth: '70px',
|
labelWidth: '80px',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
defaultValue: ActionType.COMP,
|
defaultValue: ActionType.COMP,
|
||||||
options: () => [
|
options: () => [
|
||||||
@ -93,8 +93,15 @@ const actionTypeConfig = computed(() => {
|
|||||||
{
|
{
|
||||||
text: '代码',
|
text: '代码',
|
||||||
label: '代码',
|
label: '代码',
|
||||||
|
disabled: !Object.keys(services?.codeBlockService.getCodeDsl() || {}).length,
|
||||||
value: ActionType.CODE,
|
value: ActionType.CODE,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: '数据源',
|
||||||
|
label: '数据源',
|
||||||
|
disabled: !services?.dataSourceService.get('dataSources')?.filter((ds) => ds.methods?.length).length,
|
||||||
|
value: ActionType.DATA_SOURCE,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
return { ...defaultActionTypeConfig, ...props.config.actionTypeConfig };
|
return { ...defaultActionTypeConfig, ...props.config.actionTypeConfig };
|
||||||
@ -105,7 +112,7 @@ const targetCompConfig = computed(() => {
|
|||||||
const defaultTargetCompConfig = {
|
const defaultTargetCompConfig = {
|
||||||
name: 'to',
|
name: 'to',
|
||||||
text: '联动组件',
|
text: '联动组件',
|
||||||
labelWidth: '70px',
|
labelWidth: '80px',
|
||||||
type: 'ui-select',
|
type: 'ui-select',
|
||||||
display: (mForm: FormState, { model }: { model: Record<any, any> }) => model.actionType === ActionType.COMP,
|
display: (mForm: FormState, { model }: { model: Record<any, any> }) => model.actionType === ActionType.COMP,
|
||||||
};
|
};
|
||||||
@ -117,7 +124,7 @@ const compActionConfig = computed(() => {
|
|||||||
const defaultCompActionConfig = {
|
const defaultCompActionConfig = {
|
||||||
name: 'method',
|
name: 'method',
|
||||||
text: '动作',
|
text: '动作',
|
||||||
labelWidth: '70px',
|
labelWidth: '80px',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
display: (mForm: FormState, { model }: { model: Record<any, any> }) => model.actionType === ActionType.COMP,
|
display: (mForm: FormState, { model }: { model: Record<any, any> }) => model.actionType === ActionType.COMP,
|
||||||
options: (mForm: FormState, { model }: any) => {
|
options: (mForm: FormState, { model }: any) => {
|
||||||
@ -135,14 +142,27 @@ const compActionConfig = computed(() => {
|
|||||||
|
|
||||||
// 代码联动配置
|
// 代码联动配置
|
||||||
const codeActionConfig = computed(() => {
|
const codeActionConfig = computed(() => {
|
||||||
const defaultCodeActionConfig = {
|
const defaultCodeActionConfig: CodeSelectColConfig = {
|
||||||
type: 'code-select-col',
|
type: 'code-select-col',
|
||||||
labelWidth: 0,
|
labelWidth: 0,
|
||||||
display: (mForm: FormState, { model }: { model: Record<any, any> }) => model.actionType === ActionType.CODE,
|
name: 'codeId',
|
||||||
|
disabled: () => !services?.codeBlockService.getEditStatus(),
|
||||||
|
display: (mForm, { model }) => model.actionType === ActionType.CODE,
|
||||||
};
|
};
|
||||||
return { ...defaultCodeActionConfig, ...props.config.codeActionConfig };
|
return { ...defaultCodeActionConfig, ...props.config.codeActionConfig };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 数据源联动配置
|
||||||
|
const dataSourceActionConfig = computed(() => {
|
||||||
|
const defaultDataSourceActionConfig: DataSourceMethodSelectConfig = {
|
||||||
|
type: 'data-source-method-select',
|
||||||
|
name: 'dataSourceMethod',
|
||||||
|
labelWidth: 0,
|
||||||
|
display: (mForm, { model }) => model.actionType === ActionType.DATA_SOURCE,
|
||||||
|
};
|
||||||
|
return { ...defaultDataSourceActionConfig, ...props.config.dataSourceActionConfig };
|
||||||
|
});
|
||||||
|
|
||||||
// 兼容旧的数据格式
|
// 兼容旧的数据格式
|
||||||
const tableConfig = computed(() => ({
|
const tableConfig = computed(() => ({
|
||||||
type: 'table',
|
type: 'table',
|
||||||
@ -188,7 +208,13 @@ const actionsConfig = computed(() => ({
|
|||||||
name: 'actions',
|
name: 'actions',
|
||||||
expandAll: true,
|
expandAll: true,
|
||||||
enableToggleMode: false,
|
enableToggleMode: false,
|
||||||
items: [actionTypeConfig.value, targetCompConfig.value, compActionConfig.value, codeActionConfig.value],
|
items: [
|
||||||
|
actionTypeConfig.value,
|
||||||
|
targetCompConfig.value,
|
||||||
|
compActionConfig.value,
|
||||||
|
codeActionConfig.value,
|
||||||
|
dataSourceActionConfig.value,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}));
|
}));
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- 代码icon,cdn链接:https://cloudcache.tencent-cloud.com/qcloud/ui/static/government/0d463ed5-6407-4498-8865-3d05b5e70115.svg -->
|
|
||||||
<svg viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
<svg viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
<!-- Generator: Sketch 60.1 (88133) - https://sketch.com -->
|
|
||||||
<defs><rect id="path-1" x="0" y="0" width="32" height="32"></rect></defs>
|
<defs><rect id="path-1" x="0" y="0" width="32" height="32"></rect></defs>
|
||||||
<g id="组件规范" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
<g id="组件规范" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
<g id="03图标" transform="translate(-561.000000, -2356.000000)">
|
<g id="03图标" transform="translate(-561.000000, -2356.000000)">
|
||||||
|
@ -23,6 +23,8 @@ import CodeSelect from './fields/CodeSelect.vue';
|
|||||||
import CodeSelectCol from './fields/CodeSelectCol.vue';
|
import CodeSelectCol from './fields/CodeSelectCol.vue';
|
||||||
import DataSourceFields from './fields/DataSourceFields.vue';
|
import DataSourceFields from './fields/DataSourceFields.vue';
|
||||||
import DataSourceInput from './fields/DataSourceInput.vue';
|
import DataSourceInput from './fields/DataSourceInput.vue';
|
||||||
|
import DataSourceMethods from './fields/DataSourceMethods.vue';
|
||||||
|
import DataSourceMethodSelect from './fields/DataSourceMethodSelect.vue';
|
||||||
import DataSourceSelect from './fields/DataSourceSelect.vue';
|
import DataSourceSelect from './fields/DataSourceSelect.vue';
|
||||||
import EventSelect from './fields/EventSelect.vue';
|
import EventSelect from './fields/EventSelect.vue';
|
||||||
import KeyValue from './fields/KeyValue.vue';
|
import KeyValue from './fields/KeyValue.vue';
|
||||||
@ -53,8 +55,10 @@ export { default as LayerPanel } from './layouts/sidebar/LayerPanel.vue';
|
|||||||
export { default as CodeSelect } from './fields/CodeSelect.vue';
|
export { default as CodeSelect } from './fields/CodeSelect.vue';
|
||||||
export { default as CodeSelectCol } from './fields/CodeSelectCol.vue';
|
export { default as CodeSelectCol } from './fields/CodeSelectCol.vue';
|
||||||
export { default as DataSourceFields } from './fields/DataSourceFields.vue';
|
export { default as DataSourceFields } from './fields/DataSourceFields.vue';
|
||||||
|
export { default as DataSourceMethods } from './fields/DataSourceMethods.vue';
|
||||||
export { default as DataSourceInput } from './fields/DataSourceInput.vue';
|
export { default as DataSourceInput } from './fields/DataSourceInput.vue';
|
||||||
export { default as DataSourceSelect } from './fields/DataSourceSelect.vue';
|
export { default as DataSourceSelect } from './fields/DataSourceSelect.vue';
|
||||||
|
export { default as DataSourceMethodSelect } from './fields/DataSourceMethodSelect.vue';
|
||||||
export { default as EventSelect } from './fields/EventSelect.vue';
|
export { default as EventSelect } from './fields/EventSelect.vue';
|
||||||
export { default as KeyValue } from './fields/KeyValue.vue';
|
export { default as KeyValue } from './fields/KeyValue.vue';
|
||||||
export { default as CodeBlockList } from './layouts/sidebar/code-block/CodeBlockList.vue';
|
export { default as CodeBlockList } from './layouts/sidebar/code-block/CodeBlockList.vue';
|
||||||
@ -65,6 +69,7 @@ export { default as Icon } from './components/Icon.vue';
|
|||||||
export { default as LayoutContainer } from './components/SplitView.vue';
|
export { default as LayoutContainer } from './components/SplitView.vue';
|
||||||
export { default as SplitView } from './components/SplitView.vue';
|
export { default as SplitView } from './components/SplitView.vue';
|
||||||
export { default as Resizer } from './components/Resizer.vue';
|
export { default as Resizer } from './components/Resizer.vue';
|
||||||
|
export { default as CodeBlockEditor } from './components/CodeBlockEditor.vue';
|
||||||
|
|
||||||
const defaultInstallOpt: InstallOptions = {
|
const defaultInstallOpt: InstallOptions = {
|
||||||
// eslint-disable-next-line no-eval
|
// eslint-disable-next-line no-eval
|
||||||
@ -90,5 +95,7 @@ export default {
|
|||||||
app.component('m-fields-key-value', KeyValue);
|
app.component('m-fields-key-value', KeyValue);
|
||||||
app.component('m-fields-data-source-input', DataSourceInput);
|
app.component('m-fields-data-source-input', DataSourceInput);
|
||||||
app.component('m-fields-data-source-select', DataSourceSelect);
|
app.component('m-fields-data-source-select', DataSourceSelect);
|
||||||
|
app.component('m-fields-data-source-methods', DataSourceMethods);
|
||||||
|
app.component('m-fields-data-source-method-select', DataSourceMethodSelect);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,13 +1,32 @@
|
|||||||
<template>
|
<template>
|
||||||
<div ref="codeEditor" class="magic-code-editor"></div>
|
<div :class="`magic-code-editor`">
|
||||||
|
<Teleport to="body" :disabled="!fullScreen">
|
||||||
|
<div
|
||||||
|
:class="`magic-code-editor-wrapper${fullScreen ? ' full-screen' : ''}`"
|
||||||
|
:style="!fullScreen && height ? `height: ${height}` : ''"
|
||||||
|
>
|
||||||
|
<TMagicButton
|
||||||
|
class="magic-code-editor-full-screen-icon"
|
||||||
|
circle
|
||||||
|
size="small"
|
||||||
|
:icon="FullScreen"
|
||||||
|
@click="fullScreenHandler"
|
||||||
|
></TMagicButton>
|
||||||
|
<div ref="codeEditor" class="magic-code-editor-content"></div>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
||||||
|
import { FullScreen } from '@element-plus/icons-vue';
|
||||||
import { throttle } from 'lodash-es';
|
import { throttle } from 'lodash-es';
|
||||||
import * as monaco from 'monaco-editor';
|
import * as monaco from 'monaco-editor';
|
||||||
import serialize from 'serialize-javascript';
|
import serialize from 'serialize-javascript';
|
||||||
|
|
||||||
|
import { TMagicButton } from '@tmagic/design';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'MEditorCodeEditor',
|
name: 'MEditorCodeEditor',
|
||||||
});
|
});
|
||||||
@ -21,6 +40,7 @@ const props = withDefaults(
|
|||||||
options?: {
|
options?: {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
|
height?: string;
|
||||||
autoSave?: boolean;
|
autoSave?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
@ -155,6 +175,17 @@ onUnmounted(() => {
|
|||||||
resizeObserver.disconnect();
|
resizeObserver.disconnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const fullScreen = ref(false);
|
||||||
|
const fullScreenHandler = () => {
|
||||||
|
fullScreen.value = !fullScreen.value;
|
||||||
|
setTimeout(() => {
|
||||||
|
vsEditor?.focus();
|
||||||
|
vsEditor?.layout();
|
||||||
|
vsDiffEditor?.focus();
|
||||||
|
vsDiffEditor?.layout();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
values,
|
values,
|
||||||
|
|
||||||
|
@ -58,14 +58,6 @@
|
|||||||
<component v-else-if="config.slots?.codeBlockPanelTool" :is="config.slots.codeBlockPanelTool" />
|
<component v-else-if="config.slots?.codeBlockPanelTool" :is="config.slots.codeBlockPanelTool" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template
|
|
||||||
#code-block-edit-panel-header="{ id }"
|
|
||||||
v-if="config.$key === 'code-block' || config.slots?.codeBlockEditPanelHeader"
|
|
||||||
>
|
|
||||||
<slot v-if="config.$key === 'code-block'" name="code-block-edit-panel-header" :id="id"></slot>
|
|
||||||
<component v-else-if="config.slots?.codeBlockEditPanelHeader" :is="config.slots.codeBlockEditPanelHeader" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template
|
<template
|
||||||
#layer-node-content="{ data: nodeData, node }"
|
#layer-node-content="{ data: nodeData, node }"
|
||||||
v-if="config.$key === 'layer' || config.slots?.layerNodeContent"
|
v-if="config.$key === 'layer' || config.slots?.layerNodeContent"
|
||||||
@ -93,7 +85,7 @@ import MIcon from '@editor/components/Icon.vue';
|
|||||||
import type { MenuButton, MenuComponent, SideComponent, SideItem } from '@editor/type';
|
import type { MenuButton, MenuComponent, SideComponent, SideItem } from '@editor/type';
|
||||||
import { SideBarData } from '@editor/type';
|
import { SideBarData } from '@editor/type';
|
||||||
|
|
||||||
import CodeBlockList from './code-block/CodeBlockList.vue';
|
import CodeBlockListPanel from './code-block/CodeBlockListPanel.vue';
|
||||||
import DataSourceListPanel from './data-source/DataSourceListPanel.vue';
|
import DataSourceListPanel from './data-source/DataSourceListPanel.vue';
|
||||||
import ComponentListPanel from './ComponentListPanel.vue';
|
import ComponentListPanel from './ComponentListPanel.vue';
|
||||||
import LayerPanel from './LayerPanel.vue';
|
import LayerPanel from './LayerPanel.vue';
|
||||||
@ -143,7 +135,7 @@ const getItemConfig = (data: SideItem): SideComponent => {
|
|||||||
type: 'component',
|
type: 'component',
|
||||||
icon: EditPen,
|
icon: EditPen,
|
||||||
text: '代码编辑',
|
text: '代码编辑',
|
||||||
component: CodeBlockList,
|
component: CodeBlockListPanel,
|
||||||
slots: {},
|
slots: {},
|
||||||
},
|
},
|
||||||
'data-source': {
|
'data-source': {
|
||||||
|
@ -1,100 +0,0 @@
|
|||||||
<template>
|
|
||||||
<TMagicDrawer
|
|
||||||
class="code-editor-dialog"
|
|
||||||
:model-value="true"
|
|
||||||
:title="currentTitle"
|
|
||||||
:close-on-press-escape="true"
|
|
||||||
:append-to-body="true"
|
|
||||||
:show-close="false"
|
|
||||||
:close-on-click-modal="true"
|
|
||||||
:size="size"
|
|
||||||
:before-close="handleClose"
|
|
||||||
>
|
|
||||||
<SplitView v-model:left="left" :min-left="45" class="code-editor-layout">
|
|
||||||
<!-- 右侧区域 -->
|
|
||||||
<template #center>
|
|
||||||
<div v-if="!isEmpty(codeConfig)" class="m-editor-code-block-editor-panel">
|
|
||||||
<slot name="code-block-edit-panel-header" :id="id"></slot>
|
|
||||||
<FunctionEditor
|
|
||||||
ref="functionEditor"
|
|
||||||
v-if="codeConfig"
|
|
||||||
:id="id"
|
|
||||||
:name="codeConfig.name"
|
|
||||||
:content="codeConfig.content"
|
|
||||||
:paramsColConfig="paramsColConfig"
|
|
||||||
:editable="!!editable"
|
|
||||||
:autoSaveDraft="true"
|
|
||||||
:codeOptions="codeOptions"
|
|
||||||
></FunctionEditor>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</SplitView>
|
|
||||||
</TMagicDrawer>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { computed, inject, reactive, ref, watchEffect } from 'vue';
|
|
||||||
import { cloneDeep, forIn, isEmpty } from 'lodash-es';
|
|
||||||
|
|
||||||
import { TMagicDrawer } from '@tmagic/design';
|
|
||||||
import { ColumnConfig } from '@tmagic/form';
|
|
||||||
import { CodeBlockContent } from '@tmagic/schema';
|
|
||||||
|
|
||||||
import FunctionEditor from '@editor/components/FunctionEditor.vue';
|
|
||||||
import SplitView from '@editor/components/SplitView.vue';
|
|
||||||
import type { ListState, Services } from '@editor/type';
|
|
||||||
import { serializeConfig } from '@editor/utils/editor';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'MEditorCodeBlockEditor',
|
|
||||||
});
|
|
||||||
|
|
||||||
const services = inject<Services>('services');
|
|
||||||
const codeOptions = inject('codeOptions', {});
|
|
||||||
|
|
||||||
defineProps<{
|
|
||||||
paramsColConfig?: ColumnConfig;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const size = computed(() => globalThis.document.body.clientWidth - (services?.uiService.get('columnWidth').left || 0));
|
|
||||||
|
|
||||||
const left = ref(200);
|
|
||||||
const currentTitle = ref('');
|
|
||||||
// 编辑器当前需展示的代码块内容
|
|
||||||
const codeConfig = ref<CodeBlockContent | null>(null);
|
|
||||||
// select选择的内容(ListState)
|
|
||||||
const state = reactive<ListState>({
|
|
||||||
codeList: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
const id = computed(() => services?.codeBlockService.getId() || '');
|
|
||||||
const editable = computed(() => services?.codeBlockService.getEditStatus());
|
|
||||||
// 当前选中组件绑定的代码块id数组
|
|
||||||
const selectedIds = computed(() => services?.codeBlockService.getCombineIds() || []);
|
|
||||||
|
|
||||||
watchEffect(async () => {
|
|
||||||
codeConfig.value = cloneDeep(await services?.codeBlockService.getCodeContentById(id.value)) || null;
|
|
||||||
if (!codeConfig.value) return;
|
|
||||||
codeConfig.value.content = serializeConfig(codeConfig.value.content);
|
|
||||||
});
|
|
||||||
|
|
||||||
watchEffect(async () => {
|
|
||||||
const codeDsl = (await services?.codeBlockService.getCodeDslByIds(selectedIds.value)) || null;
|
|
||||||
if (!codeDsl) return;
|
|
||||||
state.codeList = [];
|
|
||||||
forIn(codeDsl, (value: CodeBlockContent, key: string) => {
|
|
||||||
state.codeList.push({
|
|
||||||
id: key,
|
|
||||||
name: value.name,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
currentTitle.value = state.codeList[0]?.name || '';
|
|
||||||
});
|
|
||||||
|
|
||||||
const functionEditor = ref<InstanceType<typeof FunctionEditor>>();
|
|
||||||
|
|
||||||
const handleClose = async () => {
|
|
||||||
// 触发codeDraftEditor组件关闭事件
|
|
||||||
await functionEditor.value?.codeDraftEditor?.close();
|
|
||||||
};
|
|
||||||
</script>
|
|
@ -1,97 +1,69 @@
|
|||||||
<template>
|
<template>
|
||||||
<TMagicScrollbar class="m-editor-code-block-list m-editor-dep-list-panel">
|
<TMagicTree
|
||||||
<slot name="code-block-panel-header">
|
ref="tree"
|
||||||
<div class="search-wrapper">
|
class="magic-editor-layer-tree"
|
||||||
<SearchInput @search="filterTextChangeHandler"></SearchInput>
|
node-key="id"
|
||||||
<TMagicButton class="create-code-button" type="primary" size="small" @click="createCodeBlock" v-if="editable"
|
empty-text="暂无代码块"
|
||||||
>新增</TMagicButton
|
:default-expanded-keys="expandedKeys"
|
||||||
>
|
:expand-on-click-node="false"
|
||||||
</div>
|
:data="codeList"
|
||||||
</slot>
|
:props="{
|
||||||
|
children: 'children',
|
||||||
<!-- 代码块列表 -->
|
label: 'name',
|
||||||
<TMagicTree
|
value: 'id',
|
||||||
ref="tree"
|
}"
|
||||||
class="magic-editor-layer-tree"
|
:highlight-current="true"
|
||||||
node-key="id"
|
:filter-node-method="filterNode"
|
||||||
empty-text="暂无代码块"
|
@node-click="clickHandler"
|
||||||
:default-expanded-keys="expandedKeys"
|
>
|
||||||
:expand-on-click-node="false"
|
<template #default="{ data }">
|
||||||
:data="codeList"
|
<div :id="data.id" class="list-container">
|
||||||
:props="treeProps"
|
<div class="list-item">
|
||||||
:highlight-current="true"
|
<CodeIcon v-if="data.type === 'code'" class="codeIcon"></CodeIcon>
|
||||||
:filter-node-method="filterNode"
|
<AppManageIcon v-if="data.type === 'node'" class="compIcon"></AppManageIcon>
|
||||||
@node-click="clickHandler"
|
<span class="name" :class="{ code: data.type === 'code', hook: data.type === 'key' }"
|
||||||
>
|
>{{ data.name }} ({{ data.id }})</span
|
||||||
<template #default="{ data }">
|
>
|
||||||
<div :id="data.id" class="list-container">
|
<!-- 右侧工具栏 -->
|
||||||
<div class="list-item">
|
<div class="right-tool" v-if="data.type === 'code'">
|
||||||
<CodeIcon v-if="data.type === 'code'" class="codeIcon"></CodeIcon>
|
<TMagicTooltip effect="dark" :content="editable ? '编辑' : '查看'" placement="bottom">
|
||||||
<AppManageIcon v-if="data.type === 'node'" class="compIcon"></AppManageIcon>
|
<Icon :icon="editable ? Edit : View" class="edit-icon" @click.stop="editCode(data.id)"></Icon>
|
||||||
<span class="name" :class="{ code: data.type === 'code', hook: data.type === 'key' }"
|
</TMagicTooltip>
|
||||||
>{{ data.name }} ({{ data.id }})</span
|
<TMagicTooltip v-if="editable" effect="dark" content="删除" placement="bottom">
|
||||||
>
|
<Icon :icon="Close" class="edit-icon" @click.stop="deleteCode(`${data.id}`)"></Icon>
|
||||||
<!-- 右侧工具栏 -->
|
</TMagicTooltip>
|
||||||
<div class="right-tool" v-if="data.type === 'code'">
|
<slot name="code-block-panel-tool" :id="data.id" :data="data.codeBlockContent"></slot>
|
||||||
<TMagicTooltip effect="dark" :content="editable ? '编辑' : '查看'" placement="bottom">
|
|
||||||
<Icon :icon="editable ? Edit : View" class="edit-icon" @click.stop="editCode(`${data.id}`)"></Icon>
|
|
||||||
</TMagicTooltip>
|
|
||||||
<TMagicTooltip effect="dark" content="删除" placement="bottom" v-if="editable">
|
|
||||||
<Icon :icon="Close" class="edit-icon" @click.stop="deleteCode(`${data.id}`)"></Icon>
|
|
||||||
</TMagicTooltip>
|
|
||||||
<slot name="code-block-panel-tool" :id="data.id" :data="data.codeBlockContent"></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</div>
|
||||||
</TMagicTree>
|
</template>
|
||||||
|
</TMagicTree>
|
||||||
<!-- 代码块编辑区 -->
|
|
||||||
<CodeBlockEditor v-if="isShowCodeBlockEditor" :paramsColConfig="paramsColConfig">
|
|
||||||
<template #code-block-edit-panel-header="{ id }">
|
|
||||||
<slot name="code-block-edit-panel-header" :id="id"></slot>
|
|
||||||
</template>
|
|
||||||
</CodeBlockEditor>
|
|
||||||
</TMagicScrollbar>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
import { computed, inject, ref } from 'vue';
|
import { computed, inject, ref } from 'vue';
|
||||||
import { Close, Edit, View } from '@element-plus/icons-vue';
|
import { Close, Edit, View } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
import {
|
import { tMagicMessage, tMagicMessageBox, TMagicTooltip, TMagicTree } from '@tmagic/design';
|
||||||
TMagicButton,
|
import type { Id } from '@tmagic/schema';
|
||||||
tMagicMessage,
|
|
||||||
tMagicMessageBox,
|
|
||||||
TMagicScrollbar,
|
|
||||||
TMagicTooltip,
|
|
||||||
TMagicTree,
|
|
||||||
} from '@tmagic/design';
|
|
||||||
import { ColumnConfig } from '@tmagic/form';
|
|
||||||
import { CodeBlockContent, Id } from '@tmagic/schema';
|
|
||||||
|
|
||||||
import Icon from '@editor/components/Icon.vue';
|
import Icon from '@editor/components/Icon.vue';
|
||||||
import SearchInput from '@editor/components/SearchInput.vue';
|
|
||||||
import AppManageIcon from '@editor/icons/AppManageIcon.vue';
|
import AppManageIcon from '@editor/icons/AppManageIcon.vue';
|
||||||
import CodeIcon from '@editor/icons/CodeIcon.vue';
|
import CodeIcon from '@editor/icons/CodeIcon.vue';
|
||||||
import { CodeDeleteErrorType, CodeDslItem, Services } from '@editor/type';
|
import { CodeDeleteErrorType, CodeDslItem, Services } from '@editor/type';
|
||||||
|
|
||||||
import CodeBlockEditor from './CodeBlockEditor.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'MEditorCodeBlockList',
|
name: 'MEditorCodeBlockList',
|
||||||
});
|
});
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
customError?: (id: Id, errorType: CodeDeleteErrorType) => any;
|
customError?: (id: Id, errorType: CodeDeleteErrorType) => any;
|
||||||
paramsColConfig?: ColumnConfig;
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const treeProps = {
|
const emit = defineEmits<{
|
||||||
children: 'children',
|
edit: [id: string];
|
||||||
label: 'name',
|
remove: [id: string];
|
||||||
value: 'id',
|
}>();
|
||||||
};
|
|
||||||
|
|
||||||
const { codeBlockService, depService, editorService } = inject<Services>('services') || {};
|
const { codeBlockService, depService, editorService } = inject<Services>('services') || {};
|
||||||
|
|
||||||
@ -114,64 +86,10 @@ const codeList = computed(() =>
|
|||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 默认展开组件层级的节点
|
// 默认展开组件层级的节点
|
||||||
const expandedKeys = computed(() => codeList.value.map((item) => item.id));
|
const expandedKeys = computed(() => codeList.value.map((item) => item.id));
|
||||||
|
|
||||||
const editable = computed(() => codeBlockService?.getEditStatus());
|
const editable = computed(() => codeBlockService?.getEditStatus());
|
||||||
|
|
||||||
// 是否展示代码编辑区
|
|
||||||
const isShowCodeBlockEditor = computed(() => codeBlockService?.getCodeEditorShowStatus() || false);
|
|
||||||
|
|
||||||
// 新增代码块
|
|
||||||
const createCodeBlock = async () => {
|
|
||||||
if (!codeBlockService) {
|
|
||||||
tMagicMessage.error('新增代码块失败');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const codeConfig: CodeBlockContent = {
|
|
||||||
name: '代码块',
|
|
||||||
content: `({app, params}) => {\n // place your code here\n}`,
|
|
||||||
params: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const id = await codeBlockService.getUniqueId();
|
|
||||||
|
|
||||||
await codeBlockService.setCodeDslById(id, codeConfig);
|
|
||||||
codeBlockService.setCodeEditorContent(true, id);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 编辑代码块
|
|
||||||
const editCode = async (key: Id) => {
|
|
||||||
codeBlockService?.setCodeEditorContent(true, key);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 删除代码块
|
|
||||||
const deleteCode = async (key: Id) => {
|
|
||||||
const currentCode = codeList.value.find((codeItem) => codeItem.id === key);
|
|
||||||
const existBinds = Boolean(currentCode?.children.length);
|
|
||||||
const undeleteableList = codeBlockService?.getUndeletableList() || [];
|
|
||||||
if (!existBinds && !undeleteableList.includes(key)) {
|
|
||||||
await tMagicMessageBox.confirm('确定删除该代码块吗?', '提示', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning',
|
|
||||||
});
|
|
||||||
|
|
||||||
// 无绑定关系,且不在不可删除列表中
|
|
||||||
codeBlockService?.deleteCodeDslByIds([key]);
|
|
||||||
} else {
|
|
||||||
if (typeof props.customError === 'function') {
|
|
||||||
props.customError(key, existBinds ? CodeDeleteErrorType.BIND : CodeDeleteErrorType.UNDELETEABLE);
|
|
||||||
} else {
|
|
||||||
tMagicMessage.error('代码块删除失败');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const tree = ref<InstanceType<typeof TMagicTree>>();
|
|
||||||
|
|
||||||
const filterNode = (value: string, data: CodeDslItem): boolean => {
|
const filterNode = (value: string, data: CodeDslItem): boolean => {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return true;
|
return true;
|
||||||
@ -179,10 +97,6 @@ const filterNode = (value: string, data: CodeDslItem): boolean => {
|
|||||||
return `${data.name}${data.id}`.toLocaleLowerCase().indexOf(value.toLocaleLowerCase()) !== -1;
|
return `${data.name}${data.id}`.toLocaleLowerCase().indexOf(value.toLocaleLowerCase()) !== -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
const filterTextChangeHandler = (val: string) => {
|
|
||||||
tree.value?.filter(val);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 选中组件
|
// 选中组件
|
||||||
const selectComp = (compId: Id) => {
|
const selectComp = (compId: Id) => {
|
||||||
const stage = editorService?.get('stage');
|
const stage = editorService?.get('stage');
|
||||||
@ -197,4 +111,39 @@ const clickHandler = (data: any, node: any) => {
|
|||||||
selectComp(node.parent.data.id);
|
selectComp(node.parent.data.id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 编辑代码块
|
||||||
|
const editCode = (id: string) => {
|
||||||
|
emit('edit', id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteCode = async (id: string) => {
|
||||||
|
const currentCode = codeList.value.find((codeItem) => codeItem.id === id);
|
||||||
|
const existBinds = Boolean(currentCode?.children.length);
|
||||||
|
const undeleteableList = codeBlockService?.getUndeletableList() || [];
|
||||||
|
if (!existBinds && !undeleteableList.includes(id)) {
|
||||||
|
await tMagicMessageBox.confirm('确定删除该代码块吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
});
|
||||||
|
|
||||||
|
// 无绑定关系,且不在不可删除列表中
|
||||||
|
emit('remove', id);
|
||||||
|
} else {
|
||||||
|
if (typeof props.customError === 'function') {
|
||||||
|
props.customError(id, existBinds ? CodeDeleteErrorType.BIND : CodeDeleteErrorType.UNDELETEABLE);
|
||||||
|
} else {
|
||||||
|
tMagicMessage.error('代码块删除失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const tree = ref<InstanceType<typeof TMagicTree>>();
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
filter(val: string) {
|
||||||
|
tree.value?.filter(val);
|
||||||
|
},
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
<template>
|
||||||
|
<TMagicScrollbar class="m-editor-code-block-list m-editor-dep-list-panel">
|
||||||
|
<slot name="code-block-panel-header">
|
||||||
|
<div class="search-wrapper">
|
||||||
|
<SearchInput @search="filterTextChangeHandler"></SearchInput>
|
||||||
|
<TMagicButton v-if="editable" class="create-code-button" type="primary" size="small" @click="createCodeBlock"
|
||||||
|
>新增</TMagicButton
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
|
|
||||||
|
<!-- 代码块列表 -->
|
||||||
|
<CodeBlockList
|
||||||
|
ref="codeBlockList"
|
||||||
|
:custom-error="customError"
|
||||||
|
@edit="editCode"
|
||||||
|
@remove="deleteCode"
|
||||||
|
></CodeBlockList>
|
||||||
|
|
||||||
|
<!-- 代码块编辑区 -->
|
||||||
|
<CodeBlockEditor
|
||||||
|
v-if="codeConfig"
|
||||||
|
ref="codeBlockEditor"
|
||||||
|
:disabled="!editable"
|
||||||
|
:content="codeConfig"
|
||||||
|
@submit="submitCodeBlockHandler"
|
||||||
|
></CodeBlockEditor>
|
||||||
|
</TMagicScrollbar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, inject, ref } from 'vue';
|
||||||
|
|
||||||
|
import { TMagicButton, TMagicScrollbar } from '@tmagic/design';
|
||||||
|
import type { Id } from '@tmagic/schema';
|
||||||
|
|
||||||
|
import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
|
||||||
|
import SearchInput from '@editor/components/SearchInput.vue';
|
||||||
|
import type { CodeDeleteErrorType, Services } from '@editor/type';
|
||||||
|
import { useCodeBlockEdit } from '@editor/utils/use-code-block-edit';
|
||||||
|
|
||||||
|
import CodeBlockList from './CodeBlockList.vue';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'MEditorCodeBlockListPanel',
|
||||||
|
});
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
customError?: (id: Id, errorType: CodeDeleteErrorType) => any;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { codeBlockService } = inject<Services>('services') || {};
|
||||||
|
|
||||||
|
const editable = computed(() => codeBlockService?.getEditStatus());
|
||||||
|
|
||||||
|
const { codeBlockEditor, codeConfig, editCode, deleteCode, createCodeBlock, submitCodeBlockHandler } =
|
||||||
|
useCodeBlockEdit(codeBlockService);
|
||||||
|
|
||||||
|
const codeBlockList = ref<InstanceType<typeof CodeBlockList>>();
|
||||||
|
|
||||||
|
const filterTextChangeHandler = (val: string) => {
|
||||||
|
codeBlockList.value?.filter(val);
|
||||||
|
};
|
||||||
|
</script>
|
@ -1,29 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<TMagicDrawer
|
<MFormDrawer
|
||||||
v-model="visible"
|
ref="fomDrawer"
|
||||||
|
label-width="80px"
|
||||||
:title="title"
|
:title="title"
|
||||||
:close-on-press-escape="true"
|
:width="size"
|
||||||
:append-to-body="true"
|
:config="dataSourceConfig"
|
||||||
:show-close="true"
|
:values="initValues"
|
||||||
:close-on-click-modal="true"
|
:disabled="disabled"
|
||||||
:size="size"
|
@change="changeHandler"
|
||||||
>
|
@submit="submitHandler"
|
||||||
<MForm ref="form" :config="dataSourceConfig" :init-values="initValues" @change="changeHandler"></MForm>
|
@error="errorHandler"
|
||||||
|
></MFormDrawer>
|
||||||
<template #footer>
|
|
||||||
<div>
|
|
||||||
<TMagicButton type="primary" @click="submitHandler">确定</TMagicButton>
|
|
||||||
<TMagicButton @click="hide">关闭</TMagicButton>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</TMagicDrawer>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, inject, ref, watchEffect } from 'vue';
|
import { computed, inject, ref, watchEffect } from 'vue';
|
||||||
|
|
||||||
import { TMagicButton, TMagicDrawer, tMagicMessage } from '@tmagic/design';
|
import { tMagicMessage } from '@tmagic/design';
|
||||||
import { MForm } from '@tmagic/form';
|
import { MFormDrawer } from '@tmagic/form';
|
||||||
|
|
||||||
import type { Services } from '@editor/type';
|
import type { Services } from '@editor/type';
|
||||||
|
|
||||||
@ -34,6 +28,7 @@ defineOptions({
|
|||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
title?: string;
|
title?: string;
|
||||||
values: any;
|
values: any;
|
||||||
|
disabled: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const type = ref('base');
|
const type = ref('base');
|
||||||
@ -46,7 +41,7 @@ const size = computed(() => globalThis.document.body.clientWidth - (services?.ui
|
|||||||
|
|
||||||
const dataSourceConfig = computed(() => services?.dataSourceService.getFormConfig(type.value) || []);
|
const dataSourceConfig = computed(() => services?.dataSourceService.getFormConfig(type.value) || []);
|
||||||
|
|
||||||
const form = ref<InstanceType<typeof MForm>>();
|
const fomDrawer = ref<InstanceType<typeof MFormDrawer>>();
|
||||||
|
|
||||||
const initValues = ref({});
|
const initValues = ref({});
|
||||||
|
|
||||||
@ -63,26 +58,21 @@ const changeHandler = (value: Record<string, any>) => {
|
|||||||
initValues.value = value;
|
initValues.value = value;
|
||||||
};
|
};
|
||||||
|
|
||||||
const submitHandler = async () => {
|
const submitHandler = (values: any) => {
|
||||||
try {
|
emit('submit', values);
|
||||||
const values = await form.value?.submitForm();
|
|
||||||
emit('submit', values);
|
|
||||||
} catch (error: any) {
|
|
||||||
tMagicMessage.error(error.message);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const visible = ref(false);
|
const errorHandler = (error: any) => {
|
||||||
|
tMagicMessage.error(error.message);
|
||||||
const hide = () => {
|
|
||||||
visible.value = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
show() {
|
show() {
|
||||||
visible.value = true;
|
fomDrawer.value?.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
hide,
|
hide() {
|
||||||
|
fomDrawer.value?.hide();
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -0,0 +1,111 @@
|
|||||||
|
<template>
|
||||||
|
<TMagicTree
|
||||||
|
ref="tree"
|
||||||
|
class="magic-editor-layer-tree"
|
||||||
|
node-key="id"
|
||||||
|
empty-text="暂无代码块"
|
||||||
|
default-expand-all
|
||||||
|
:expand-on-click-node="false"
|
||||||
|
:data="list"
|
||||||
|
:highlight-current="true"
|
||||||
|
@node-click="clickHandler"
|
||||||
|
>
|
||||||
|
<template #default="{ data }">
|
||||||
|
<div :id="data.id" class="list-container">
|
||||||
|
<div class="list-item">
|
||||||
|
<Icon v-if="data.type === 'code'" class="codeIcon" :icon="Coin"></Icon>
|
||||||
|
<Icon v-if="data.type === 'node'" class="compIcon" :icon="Aim"></Icon>
|
||||||
|
<span class="name" :class="{ code: data.type === 'code', hook: data.type === 'key' }"
|
||||||
|
>{{ data.name }}({{ data.id }})</span
|
||||||
|
>
|
||||||
|
<!-- 右侧工具栏 -->
|
||||||
|
<div class="right-tool" v-if="data.type === 'code'">
|
||||||
|
<TMagicTooltip effect="dark" :content="editable ? '编辑' : '查看'" placement="bottom">
|
||||||
|
<Icon :icon="editable ? Edit : View" class="edit-icon" @click.stop="editHandler(`${data.id}`)"></Icon>
|
||||||
|
</TMagicTooltip>
|
||||||
|
<TMagicTooltip v-if="editable" effect="dark" content="删除" placement="bottom">
|
||||||
|
<Icon :icon="Close" class="edit-icon" @click.stop="removeHandler(`${data.id}`)"></Icon>
|
||||||
|
</TMagicTooltip>
|
||||||
|
<slot name="data-source-panel-tool" :id="data.id" :data="data.codeBlockContent"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</TMagicTree>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, inject, ref } from 'vue';
|
||||||
|
import { Aim, Close, Coin, Edit, View } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
|
import { tMagicMessageBox, TMagicTooltip, TMagicTree } from '@tmagic/design';
|
||||||
|
import { Id } from '@tmagic/schema';
|
||||||
|
|
||||||
|
import Icon from '@editor/components/Icon.vue';
|
||||||
|
import type { Services } from '@editor/type';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'MEditorDataSourceList',
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
edit: [id: string];
|
||||||
|
remove: [id: string];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { depService, editorService, dataSourceService } = inject<Services>('services') || {};
|
||||||
|
|
||||||
|
const editable = computed(() => dataSourceService?.get('editable') ?? true);
|
||||||
|
|
||||||
|
const list = computed(() =>
|
||||||
|
Object.values(depService?.targets['data-source'] || {}).map((target) => ({
|
||||||
|
id: target.id,
|
||||||
|
name: target.name,
|
||||||
|
type: 'code',
|
||||||
|
children: Object.entries(target.deps).map(([id, dep]) => ({
|
||||||
|
name: dep.name,
|
||||||
|
type: 'node',
|
||||||
|
id,
|
||||||
|
children: dep.keys.map((key) => ({ name: key, id: key, type: 'key' })),
|
||||||
|
})),
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
|
const editHandler = (id: string) => {
|
||||||
|
emit('edit', id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeHandler = async (id: string) => {
|
||||||
|
await tMagicMessageBox.confirm('确定删除?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
});
|
||||||
|
|
||||||
|
emit('remove', id);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 选中组件
|
||||||
|
const selectComp = (compId: Id) => {
|
||||||
|
const stage = editorService?.get('stage');
|
||||||
|
editorService?.select(compId);
|
||||||
|
stage?.select(compId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const clickHandler = (data: any, node: any) => {
|
||||||
|
if (data.type === 'node') {
|
||||||
|
selectComp(data.id);
|
||||||
|
} else if (data.type === 'key') {
|
||||||
|
selectComp(node.parent.data.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const tree = ref<InstanceType<typeof TMagicTree>>();
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
filter(val: string) {
|
||||||
|
debugger;
|
||||||
|
tree.value?.filter(val);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
@ -2,46 +2,15 @@
|
|||||||
<TMagicScrollbar class="data-source-list-panel m-editor-dep-list-panel">
|
<TMagicScrollbar class="data-source-list-panel m-editor-dep-list-panel">
|
||||||
<div class="search-wrapper">
|
<div class="search-wrapper">
|
||||||
<SearchInput @search="filterTextChangeHandler"></SearchInput>
|
<SearchInput @search="filterTextChangeHandler"></SearchInput>
|
||||||
<TMagicButton type="primary" size="small" @click="addHandler">新增</TMagicButton>
|
<TMagicButton v-if="editable" type="primary" size="small" @click="addHandler">新增</TMagicButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 数据源列表 -->
|
<!-- 数据源列表 -->
|
||||||
<TMagicTree
|
<DataSourceList @edit="editHandler" @remove="removeHandler"></DataSourceList>
|
||||||
ref="tree"
|
|
||||||
class="magic-editor-layer-tree"
|
|
||||||
node-key="id"
|
|
||||||
empty-text="暂无代码块"
|
|
||||||
default-expand-all
|
|
||||||
:expand-on-click-node="false"
|
|
||||||
:data="list"
|
|
||||||
:highlight-current="true"
|
|
||||||
@node-click="clickHandler"
|
|
||||||
>
|
|
||||||
<template #default="{ data }">
|
|
||||||
<div :id="data.id" class="list-container">
|
|
||||||
<div class="list-item">
|
|
||||||
<Icon v-if="data.type === 'code'" class="codeIcon" :icon="Coin"></Icon>
|
|
||||||
<Icon v-if="data.type === 'node'" class="compIcon" :icon="Aim"></Icon>
|
|
||||||
<span class="name" :class="{ code: data.type === 'code', hook: data.type === 'key' }"
|
|
||||||
>{{ data.name }}({{ data.id }})</span
|
|
||||||
>
|
|
||||||
<!-- 右侧工具栏 -->
|
|
||||||
<div class="right-tool" v-if="data.type === 'code'">
|
|
||||||
<TMagicTooltip effect="dark" content="编辑" placement="bottom">
|
|
||||||
<Icon class="edit-icon" :icon="Edit" @click.stop="editHandler(`${data.id}`)"></Icon>
|
|
||||||
</TMagicTooltip>
|
|
||||||
<TMagicTooltip effect="dark" content="删除" placement="bottom">
|
|
||||||
<Icon :icon="Close" class="edit-icon" @click.stop="removeHandler(`${data.id}`)"></Icon>
|
|
||||||
</TMagicTooltip>
|
|
||||||
<slot name="data-source-panel-tool" :id="data.id" :data="data.codeBlockContent"></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</TMagicTree>
|
|
||||||
|
|
||||||
<DataSourceConfigPanel
|
<DataSourceConfigPanel
|
||||||
ref="editDialog"
|
ref="editDialog"
|
||||||
|
:disabled="!editable"
|
||||||
:values="dataSourceValues"
|
:values="dataSourceValues"
|
||||||
:title="typeof dataSourceValues.id !== 'undefined' ? `编辑${dataSourceValues.title}` : '新增'"
|
:title="typeof dataSourceValues.id !== 'undefined' ? `编辑${dataSourceValues.title}` : '新增'"
|
||||||
@submit="submitDataSourceHandler"
|
@submit="submitDataSourceHandler"
|
||||||
@ -51,42 +20,28 @@
|
|||||||
|
|
||||||
<script setup lang="ts" name="MEditorDataSourceListPanel">
|
<script setup lang="ts" name="MEditorDataSourceListPanel">
|
||||||
import { computed, inject, ref } from 'vue';
|
import { computed, inject, ref } from 'vue';
|
||||||
import { Aim, Close, Coin, Edit } from '@element-plus/icons-vue';
|
|
||||||
|
|
||||||
import { TMagicButton, tMagicMessageBox, TMagicScrollbar, TMagicTooltip, TMagicTree } from '@tmagic/design';
|
import { TMagicButton, TMagicScrollbar } from '@tmagic/design';
|
||||||
import { DataSourceSchema, Id } from '@tmagic/schema';
|
import type { DataSourceSchema } from '@tmagic/schema';
|
||||||
|
|
||||||
import Icon from '@editor/components/Icon.vue';
|
|
||||||
import SearchInput from '@editor/components/SearchInput.vue';
|
import SearchInput from '@editor/components/SearchInput.vue';
|
||||||
import type { Services } from '@editor/type';
|
import type { Services } from '@editor/type';
|
||||||
|
|
||||||
import DataSourceConfigPanel from './DataSourceConfigPanel.vue';
|
import DataSourceConfigPanel from './DataSourceConfigPanel.vue';
|
||||||
|
import DataSourceList from './DataSourceList.vue';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'MEditorDataSourceListPanel',
|
name: 'MEditorDataSourceListPanel',
|
||||||
});
|
});
|
||||||
|
|
||||||
const services = inject<Partial<Services>>('services', {});
|
const { dataSourceService } = inject<Services>('services') || {};
|
||||||
const { dataSourceService, depService, editorService } = inject<Services>('services') || {};
|
|
||||||
|
|
||||||
const list = computed(() =>
|
|
||||||
Object.values(depService?.targets['data-source'] || {}).map((target) => ({
|
|
||||||
id: target.id,
|
|
||||||
name: target.name,
|
|
||||||
type: 'code',
|
|
||||||
children: Object.entries(target.deps).map(([id, dep]) => ({
|
|
||||||
name: dep.name,
|
|
||||||
type: 'node',
|
|
||||||
id,
|
|
||||||
children: dep.keys.map((key) => ({ name: key, id: key, type: 'key' })),
|
|
||||||
})),
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
|
|
||||||
const editDialog = ref<InstanceType<typeof DataSourceConfigPanel>>();
|
const editDialog = ref<InstanceType<typeof DataSourceConfigPanel>>();
|
||||||
|
|
||||||
const dataSourceValues = ref<Record<string, any>>({});
|
const dataSourceValues = ref<Record<string, any>>({});
|
||||||
|
|
||||||
|
const editable = computed(() => dataSourceService?.get('editable') ?? true);
|
||||||
|
|
||||||
const addHandler = () => {
|
const addHandler = () => {
|
||||||
if (!editDialog.value) return;
|
if (!editDialog.value) return;
|
||||||
|
|
||||||
@ -96,7 +51,7 @@ const addHandler = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const editHandler = (id: string) => {
|
const editHandler = (id: string) => {
|
||||||
if (!editDialog.value || !services) return;
|
if (!editDialog.value) return;
|
||||||
|
|
||||||
dataSourceValues.value = {
|
dataSourceValues.value = {
|
||||||
...dataSourceService?.getDataSourceById(id),
|
...dataSourceService?.getDataSourceById(id),
|
||||||
@ -105,19 +60,11 @@ const editHandler = (id: string) => {
|
|||||||
editDialog.value.show();
|
editDialog.value.show();
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeHandler = async (id: string) => {
|
const removeHandler = (id: string) => {
|
||||||
await tMagicMessageBox.confirm('确定删除?', '提示', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning',
|
|
||||||
});
|
|
||||||
|
|
||||||
dataSourceService?.remove(id);
|
dataSourceService?.remove(id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const submitDataSourceHandler = (value: DataSourceSchema) => {
|
const submitDataSourceHandler = (value: DataSourceSchema) => {
|
||||||
if (!services) return;
|
|
||||||
|
|
||||||
if (value.id) {
|
if (value.id) {
|
||||||
dataSourceService?.update(value);
|
dataSourceService?.update(value);
|
||||||
} else {
|
} else {
|
||||||
@ -127,24 +74,9 @@ const submitDataSourceHandler = (value: DataSourceSchema) => {
|
|||||||
editDialog.value?.hide();
|
editDialog.value?.hide();
|
||||||
};
|
};
|
||||||
|
|
||||||
const tree = ref<InstanceType<typeof TMagicTree>>();
|
const dataSourceList = ref<InstanceType<typeof DataSourceList>>();
|
||||||
|
|
||||||
const filterTextChangeHandler = (val: string) => {
|
const filterTextChangeHandler = (val: string) => {
|
||||||
tree.value?.filter(val);
|
dataSourceList.value?.filter(val);
|
||||||
};
|
|
||||||
|
|
||||||
// 选中组件
|
|
||||||
const selectComp = (compId: Id) => {
|
|
||||||
const stage = editorService?.get('stage');
|
|
||||||
editorService?.select(compId);
|
|
||||||
stage?.select(compId);
|
|
||||||
};
|
|
||||||
|
|
||||||
const clickHandler = (data: any, node: any) => {
|
|
||||||
if (data.type === 'node') {
|
|
||||||
selectComp(data.id);
|
|
||||||
} else if (data.type === 'key') {
|
|
||||||
selectComp(node.parent.data.id);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
import { reactive } from 'vue';
|
import { reactive } from 'vue';
|
||||||
import { keys, pick } from 'lodash-es';
|
import { keys, pick } from 'lodash-es';
|
||||||
|
|
||||||
import { CodeBlockContent, CodeBlockDSL, Id } from '@tmagic/schema';
|
import type { ColumnConfig } from '@tmagic/form';
|
||||||
|
import type { CodeBlockContent, CodeBlockDSL, Id } from '@tmagic/schema';
|
||||||
|
|
||||||
import type { CodeState } from '@editor/type';
|
import type { CodeState } from '@editor/type';
|
||||||
import { CODE_DRAFT_STORAGE_KEY } from '@editor/type';
|
import { CODE_DRAFT_STORAGE_KEY } from '@editor/type';
|
||||||
@ -29,23 +30,15 @@ import BaseService from './BaseService';
|
|||||||
|
|
||||||
class CodeBlock extends BaseService {
|
class CodeBlock extends BaseService {
|
||||||
private state = reactive<CodeState>({
|
private state = reactive<CodeState>({
|
||||||
isShowCodeEditor: false,
|
|
||||||
codeDsl: null,
|
codeDsl: null,
|
||||||
id: '',
|
|
||||||
editable: true,
|
editable: true,
|
||||||
combineIds: [],
|
combineIds: [],
|
||||||
undeletableList: [],
|
undeletableList: [],
|
||||||
|
paramsColConfig: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super([
|
super(['setCodeDslById', 'setEditStatus', 'setCombineIds', 'setUndeleteableList', 'deleteCodeDslByIds']);
|
||||||
'setCodeDslById',
|
|
||||||
'setCodeEditorShowStatus',
|
|
||||||
'setEditStatus',
|
|
||||||
'setCombineIds',
|
|
||||||
'setUndeleteableList',
|
|
||||||
'deleteCodeDslByIds',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,7 +90,10 @@ class CodeBlock extends BaseService {
|
|||||||
if (codeConfig.content) {
|
if (codeConfig.content) {
|
||||||
// 在保存的时候转换代码内容
|
// 在保存的时候转换代码内容
|
||||||
const parseDSL = getConfig('parseDSL');
|
const parseDSL = getConfig('parseDSL');
|
||||||
codeConfigProcessed.content = parseDSL(codeConfig.content);
|
if (typeof codeConfig.content === 'string') {
|
||||||
|
codeConfig.content = parseDSL<(...args: any[]) => any>(codeConfig.content);
|
||||||
|
}
|
||||||
|
codeConfigProcessed.content = codeConfig.content;
|
||||||
}
|
}
|
||||||
|
|
||||||
const existContent = codeDsl[id] || {};
|
const existContent = codeDsl[id] || {};
|
||||||
@ -120,43 +116,6 @@ class CodeBlock extends BaseService {
|
|||||||
return pick(codeDsl, ids) as CodeBlockDSL;
|
return pick(codeDsl, ids) as CodeBlockDSL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置代码编辑面板展示状态
|
|
||||||
* @param {boolean} status 是否展示代码编辑面板
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
public async setCodeEditorShowStatus(status: boolean): Promise<void> {
|
|
||||||
this.state.isShowCodeEditor = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取代码编辑面板展示状态
|
|
||||||
* @returns {boolean} 是否展示代码编辑面板
|
|
||||||
*/
|
|
||||||
public getCodeEditorShowStatus(): boolean {
|
|
||||||
return this.state.isShowCodeEditor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置代码编辑面板展示状态及展示内容
|
|
||||||
* @param {boolean} status 是否展示代码编辑面板
|
|
||||||
* @param {Id} id 代码块id
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
public setCodeEditorContent(status: boolean, id: Id): void {
|
|
||||||
if (!id) return;
|
|
||||||
this.setId(id);
|
|
||||||
this.state.isShowCodeEditor = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前选中的代码块内容
|
|
||||||
* @returns {CodeBlockContent | null}
|
|
||||||
*/
|
|
||||||
public getCurrentDsl(): CodeBlockContent | null {
|
|
||||||
return this.getCodeContentById(this.state.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取编辑状态
|
* 获取编辑状态
|
||||||
* @returns {boolean} 是否可编辑
|
* @returns {boolean} 是否可编辑
|
||||||
@ -174,24 +133,6 @@ class CodeBlock extends BaseService {
|
|||||||
this.state.editable = status;
|
this.state.editable = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置当前选中的代码块ID
|
|
||||||
* @param {Id} id 代码块id
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
public setId(id: Id): void {
|
|
||||||
if (!id) return;
|
|
||||||
this.state.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前选中的代码块ID
|
|
||||||
* @returns {Id} id 代码块id
|
|
||||||
*/
|
|
||||||
public getId(): Id {
|
|
||||||
return this.state.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置当前选中组件已关联绑定的代码块id数组
|
* 设置当前选中组件已关联绑定的代码块id数组
|
||||||
* @param {string[]} ids 代码块id数组
|
* @param {string[]} ids 代码块id数组
|
||||||
@ -263,11 +204,19 @@ class CodeBlock extends BaseService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setParamsColConfig(config: ColumnConfig): void {
|
||||||
|
this.state.paramsColConfig = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getParamsColConfig(): ColumnConfig | undefined {
|
||||||
|
return this.state.paramsColConfig;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成代码块唯一id
|
* 生成代码块唯一id
|
||||||
* @returns {Id} 代码块唯一id
|
* @returns {Id} 代码块唯一id
|
||||||
*/
|
*/
|
||||||
public async getUniqueId(): Promise<Id> {
|
public async getUniqueId(): Promise<string> {
|
||||||
const newId = `code_${Math.random().toString(10).substring(2).substring(0, 4)}`;
|
const newId = `code_${Math.random().toString(10).substring(2).substring(0, 4)}`;
|
||||||
// 判断是否重复
|
// 判断是否重复
|
||||||
const dsl = await this.getCodeDsl();
|
const dsl = await this.getCodeDsl();
|
||||||
@ -277,9 +226,7 @@ class CodeBlock extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public resetState() {
|
public resetState() {
|
||||||
this.state.isShowCodeEditor = false;
|
|
||||||
this.state.codeDsl = null;
|
this.state.codeDsl = null;
|
||||||
this.state.id = '';
|
|
||||||
this.state.editable = true;
|
this.state.editable = true;
|
||||||
this.state.combineIds = [];
|
this.state.combineIds = [];
|
||||||
this.state.undeletableList = [];
|
this.state.undeletableList = [];
|
||||||
|
@ -11,6 +11,7 @@ import BaseService from './BaseService';
|
|||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
dataSources: DataSourceSchema[];
|
dataSources: DataSourceSchema[];
|
||||||
|
editable: boolean;
|
||||||
configs: Record<string, FormConfig>;
|
configs: Record<string, FormConfig>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ type StateKey = keyof State;
|
|||||||
class DataSource extends BaseService {
|
class DataSource extends BaseService {
|
||||||
private state = reactive<State>({
|
private state = reactive<State>({
|
||||||
dataSources: [],
|
dataSources: [],
|
||||||
|
editable: true,
|
||||||
configs: {},
|
configs: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -24,125 +24,3 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-dialog.is-fullscreen.code-editor-dialog {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-editor-dialog {
|
|
||||||
.tmagic-design-card {
|
|
||||||
.t-card__header {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-dialog__body,
|
|
||||||
.t-dialog__body {
|
|
||||||
height: 90%;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
.tmagic-design-card {
|
|
||||||
height: 100%;
|
|
||||||
background: #fff;
|
|
||||||
.el-card__body,
|
|
||||||
.t-card__body {
|
|
||||||
height: 100%;
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-editor-layout {
|
|
||||||
height: 100%;
|
|
||||||
border: 1px solid #e4e7ed;
|
|
||||||
|
|
||||||
.side-tree {
|
|
||||||
height: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
.el-tree-node__label {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-container {
|
|
||||||
width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
margin-left: -10px;
|
|
||||||
.list-item {
|
|
||||||
width: 100%;
|
|
||||||
margin: 10px 0;
|
|
||||||
line-height: 30px;
|
|
||||||
.code-name {
|
|
||||||
width: 100%;
|
|
||||||
font-size: 14px;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-name-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 16px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
.code-name-label {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
.code-name-input {
|
|
||||||
width: 300px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.m-editor-code-block-editor-panel-list-mode {
|
|
||||||
height: 100%;
|
|
||||||
z-index: 10;
|
|
||||||
background: #fff;
|
|
||||||
.el-card {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.m-editor-code-block-editor-panel {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 4px;
|
|
||||||
width: calc(100% - 9px);
|
|
||||||
height: 100%;
|
|
||||||
z-index: 10;
|
|
||||||
background: #fff;
|
|
||||||
.el-card {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.m-editor-wrapper {
|
|
||||||
height: 100%;
|
|
||||||
&.fullScreen {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
.m-editor-container {
|
|
||||||
height: calc(100% - 45px);
|
|
||||||
}
|
|
||||||
.m-editor-content-bottom {
|
|
||||||
height: 45px;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: end;
|
|
||||||
background: #fff;
|
|
||||||
position: absolute;
|
|
||||||
right: 20px;
|
|
||||||
bottom: 0;
|
|
||||||
> button {
|
|
||||||
height: 30px;
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,7 +1,32 @@
|
|||||||
.magic-code-editor {
|
.magic-code-editor {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.margin {
|
.magic-code-editor-wrapper {
|
||||||
margin: 0;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.full-screen {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 10000;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.magic-code-editor-content {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.margin {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.magic-code-editor-full-screen-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
packages/editor/src/theme/data-source-methods.scss
Normal file
13
packages/editor/src/theme/data-source-methods.scss
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
.m-editor-data-source-methods {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.tmagic-design-table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.m-editor-data-source-methods-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
}
|
@ -19,18 +19,21 @@
|
|||||||
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.12);
|
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.12);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.m-fields-code-select-col {
|
.m-fields-code-select-col,
|
||||||
|
.m-fields-data-source-method-select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
.code-select-container {
|
}
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
.code-select-container,
|
||||||
.select {
|
.data-source-method-select-container {
|
||||||
flex: 10 0 100px;
|
display: flex;
|
||||||
}
|
align-items: center;
|
||||||
.icon {
|
.select {
|
||||||
flex: 1 0 20px;
|
flex: 10 0 100px;
|
||||||
cursor: pointer;
|
}
|
||||||
margin-right: 5px;
|
.icon {
|
||||||
}
|
flex: 1 0 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,5 +19,6 @@
|
|||||||
@import "./dep-list.scss";
|
@import "./dep-list.scss";
|
||||||
@import "./data-source.scss";
|
@import "./data-source.scss";
|
||||||
@import "./data-source-fields.scss";
|
@import "./data-source-fields.scss";
|
||||||
|
@import "./data-source-methods.scss";
|
||||||
@import "./data-source-input.scss";
|
@import "./data-source-input.scss";
|
||||||
@import "./key-value.scss";
|
@import "./key-value.scss";
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
import type { Component } from 'vue';
|
import type { Component } from 'vue';
|
||||||
|
|
||||||
import type { FilterFunction, FormConfig, FormItem } from '@tmagic/form';
|
import type { ColumnConfig, FilterFunction, FormConfig, FormItem } from '@tmagic/form';
|
||||||
import type { CodeBlockContent, CodeBlockDSL, Id, MApp, MContainer, MNode, MPage } from '@tmagic/schema';
|
import type { CodeBlockContent, CodeBlockDSL, Id, MApp, MContainer, MNode, MPage } from '@tmagic/schema';
|
||||||
import type StageCore from '@tmagic/stage';
|
import type StageCore from '@tmagic/stage';
|
||||||
import type {
|
import type {
|
||||||
@ -45,7 +45,7 @@ export type BeforeAdd = (config: MNode, parent: MContainer) => Promise<MNode> |
|
|||||||
export type GetConfig = (config: FormConfig) => Promise<FormConfig> | FormConfig;
|
export type GetConfig = (config: FormConfig) => Promise<FormConfig> | FormConfig;
|
||||||
|
|
||||||
export interface InstallOptions {
|
export interface InstallOptions {
|
||||||
parseDSL: (dsl: string) => MApp;
|
parseDSL: <T = any>(dsl: string) => T;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,18 +334,15 @@ export interface ScrollViewerEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type CodeState = {
|
export type CodeState = {
|
||||||
/** 是否展示代码块编辑区 */
|
|
||||||
isShowCodeEditor: boolean;
|
|
||||||
/** 代码块DSL数据源 */
|
/** 代码块DSL数据源 */
|
||||||
codeDsl: CodeBlockDSL | null;
|
codeDsl: CodeBlockDSL | null;
|
||||||
/** 当前选中的代码块id */
|
|
||||||
id: Id;
|
|
||||||
/** 代码块是否可编辑 */
|
/** 代码块是否可编辑 */
|
||||||
editable: boolean;
|
editable: boolean;
|
||||||
/** list模式下左侧展示的代码列表 */
|
/** list模式下左侧展示的代码列表 */
|
||||||
combineIds: string[];
|
combineIds: string[];
|
||||||
/** 为业务逻辑预留的不可删除的代码块列表,由业务逻辑维护(如代码块上线后不可删除) */
|
/** 为业务逻辑预留的不可删除的代码块列表,由业务逻辑维护(如代码块上线后不可删除) */
|
||||||
undeletableList: Id[];
|
undeletableList: Id[];
|
||||||
|
paramsColConfig?: ColumnConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type HookData = {
|
export type HookData = {
|
||||||
@ -429,6 +426,8 @@ export interface EventSelectConfig {
|
|||||||
compActionConfig?: FormItem;
|
compActionConfig?: FormItem;
|
||||||
/** 联动代码配置 */
|
/** 联动代码配置 */
|
||||||
codeActionConfig?: FormItem;
|
codeActionConfig?: FormItem;
|
||||||
|
/** 联动数据源配置 */
|
||||||
|
dataSourceActionConfig?: FormItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum KeyBindingCommand {
|
export enum KeyBindingCommand {
|
||||||
@ -493,3 +492,11 @@ export interface CodeSelectColConfig {
|
|||||||
disabled?: boolean | FilterFunction;
|
disabled?: boolean | FilterFunction;
|
||||||
display?: boolean | FilterFunction;
|
display?: boolean | FilterFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DataSourceMethodSelectConfig {
|
||||||
|
type: 'data-source-method-select';
|
||||||
|
name: string;
|
||||||
|
labelWidth?: number | string;
|
||||||
|
disabled?: boolean | FilterFunction;
|
||||||
|
display?: boolean | FilterFunction;
|
||||||
|
}
|
||||||
|
@ -17,6 +17,17 @@ const fillConfig = (config: FormConfig): FormConfig => [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'panel',
|
||||||
|
title: '方法定义',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'methods',
|
||||||
|
type: 'data-source-methods',
|
||||||
|
defaultValue: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const getFormConfig = (type: string, configs: Record<string, FormConfig>): FormConfig => {
|
export const getFormConfig = (type: string, configs: Record<string, FormConfig>): FormConfig => {
|
||||||
|
84
packages/editor/src/utils/use-code-block-edit.ts
Normal file
84
packages/editor/src/utils/use-code-block-edit.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import { nextTick, ref } from 'vue';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
|
import { tMagicMessage } from '@tmagic/design';
|
||||||
|
import type { CodeBlockContent } from '@tmagic/schema';
|
||||||
|
|
||||||
|
import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
|
||||||
|
import type { CodeBlockService } from '@editor/services/codeBlock';
|
||||||
|
|
||||||
|
export const useCodeBlockEdit = (codeBlockService?: CodeBlockService) => {
|
||||||
|
const codeConfig = ref<CodeBlockContent>();
|
||||||
|
const codeId = ref<string>();
|
||||||
|
const codeBlockEditor = ref<InstanceType<typeof CodeBlockEditor>>();
|
||||||
|
|
||||||
|
// 新增代码块
|
||||||
|
const createCodeBlock = async () => {
|
||||||
|
if (!codeBlockService) {
|
||||||
|
tMagicMessage.error('新增代码块失败');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
codeConfig.value = {
|
||||||
|
name: '代码块',
|
||||||
|
content: `({app, params}) => {\n // place your code here\n}`,
|
||||||
|
params: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
codeId.value = await codeBlockService.getUniqueId();
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
codeBlockEditor.value?.show();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 编辑代码块
|
||||||
|
const editCode = async (id: string) => {
|
||||||
|
const codeBlock = await codeBlockService?.getCodeContentById(id);
|
||||||
|
|
||||||
|
if (!codeBlock) {
|
||||||
|
tMagicMessage.error('获取代码块内容失败');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let codeContent = codeBlock.content;
|
||||||
|
|
||||||
|
if (typeof codeContent !== 'string') {
|
||||||
|
codeContent = codeContent.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
codeConfig.value = {
|
||||||
|
...cloneDeep(codeBlock),
|
||||||
|
content: codeContent,
|
||||||
|
};
|
||||||
|
codeId.value = id;
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
codeBlockEditor.value?.show();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除代码块
|
||||||
|
const deleteCode = async (key: string) => {
|
||||||
|
codeBlockService?.deleteCodeDslByIds([key]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitCodeBlockHandler = async (values: CodeBlockContent) => {
|
||||||
|
if (!codeId.value) return;
|
||||||
|
|
||||||
|
await codeBlockService?.setCodeDslById(codeId.value, values);
|
||||||
|
|
||||||
|
codeBlockEditor.value?.hide();
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
codeId,
|
||||||
|
codeConfig,
|
||||||
|
codeBlockEditor,
|
||||||
|
|
||||||
|
createCodeBlock,
|
||||||
|
editCode,
|
||||||
|
deleteCode,
|
||||||
|
submitCodeBlockHandler,
|
||||||
|
};
|
||||||
|
};
|
101
packages/editor/src/utils/use-data-source-method.ts
Normal file
101
packages/editor/src/utils/use-data-source-method.ts
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import { nextTick, ref } from 'vue';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
|
import { tMagicMessage } from '@tmagic/design';
|
||||||
|
import type { CodeBlockContent, DataSourceSchema } from '@tmagic/schema';
|
||||||
|
|
||||||
|
import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
|
||||||
|
|
||||||
|
import { getConfig } from './config';
|
||||||
|
|
||||||
|
export const useDataSourceMethod = () => {
|
||||||
|
const codeConfig = ref<CodeBlockContent>();
|
||||||
|
const codeBlockEditor = ref<InstanceType<typeof CodeBlockEditor>>();
|
||||||
|
|
||||||
|
const dataSource = ref<DataSourceSchema>();
|
||||||
|
const dataSourceMethod = ref('');
|
||||||
|
|
||||||
|
return {
|
||||||
|
codeConfig,
|
||||||
|
codeBlockEditor,
|
||||||
|
|
||||||
|
createCode: async (model: DataSourceSchema) => {
|
||||||
|
codeConfig.value = {
|
||||||
|
name: '',
|
||||||
|
content: `({ params, dataSource, app }) => {\n // place your code here\n}`,
|
||||||
|
params: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
dataSource.value = model;
|
||||||
|
dataSourceMethod.value = '';
|
||||||
|
|
||||||
|
codeBlockEditor.value?.show();
|
||||||
|
},
|
||||||
|
|
||||||
|
editCode: async (model: DataSourceSchema, methodName: string) => {
|
||||||
|
const method = model.methods?.find((method) => method.name === methodName);
|
||||||
|
|
||||||
|
if (!method) {
|
||||||
|
tMagicMessage.error('获取数据源方法失败');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let codeContent = method.content;
|
||||||
|
|
||||||
|
if (typeof codeContent !== 'string') {
|
||||||
|
codeContent = codeContent.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
codeConfig.value = {
|
||||||
|
...cloneDeep(method),
|
||||||
|
content: codeContent,
|
||||||
|
};
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
dataSource.value = model;
|
||||||
|
dataSourceMethod.value = methodName;
|
||||||
|
|
||||||
|
codeBlockEditor.value?.show();
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteCode: async (model: DataSourceSchema, methodName: string) => {
|
||||||
|
if (!model.methods) return;
|
||||||
|
|
||||||
|
const index = model.methods.findIndex((method) => method.name === methodName);
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
model.methods.splice(index, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
submitCode: (values: CodeBlockContent) => {
|
||||||
|
if (!dataSource.value) return;
|
||||||
|
|
||||||
|
if (!dataSource.value.methods) {
|
||||||
|
dataSource.value.methods = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values.content) {
|
||||||
|
// 在保存的时候转换代码内容
|
||||||
|
const parseDSL = getConfig('parseDSL');
|
||||||
|
if (typeof values.content === 'string') {
|
||||||
|
values.content = parseDSL<(...args: any[]) => any>(values.content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataSourceMethod.value) {
|
||||||
|
const index = dataSource.value.methods.findIndex((method) => method.name === dataSourceMethod.value);
|
||||||
|
dataSource.value.methods.splice(index, 1, values);
|
||||||
|
} else {
|
||||||
|
dataSource.value.methods.push(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
codeBlockEditor.value?.hide();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
@ -118,10 +118,6 @@ const hasStep = computed(() => {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
const cancel = () => {
|
|
||||||
dialogVisible.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const closeHandler = () => {
|
const closeHandler = () => {
|
||||||
stepActive.value = 1;
|
stepActive.value = 1;
|
||||||
emit('close');
|
emit('close');
|
||||||
@ -148,6 +144,18 @@ const changeHandler = (value: any) => {
|
|||||||
emit('change', value);
|
emit('change', value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const show = () => {
|
||||||
|
dialogVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hide = () => {
|
||||||
|
dialogVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const cancel = () => {
|
||||||
|
hide();
|
||||||
|
};
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
form,
|
form,
|
||||||
saveFetch,
|
saveFetch,
|
||||||
@ -155,5 +163,7 @@ defineExpose({
|
|||||||
|
|
||||||
cancel,
|
cancel,
|
||||||
save,
|
save,
|
||||||
|
show,
|
||||||
|
hide,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
116
packages/form/src/FormDrawer.vue
Normal file
116
packages/form/src/FormDrawer.vue
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<template>
|
||||||
|
<TMagicDrawer
|
||||||
|
class="m-form-drawer"
|
||||||
|
v-model="visible"
|
||||||
|
:title="title"
|
||||||
|
:close-on-press-escape="true"
|
||||||
|
:append-to-body="true"
|
||||||
|
:show-close="true"
|
||||||
|
:close-on-click-modal="true"
|
||||||
|
:size="width"
|
||||||
|
:zIndex="zIndex"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="visible"
|
||||||
|
class="m-drawer-body"
|
||||||
|
:style="`max-height: ${bodyHeight}; overflow-y: auto; overflow-x: hidden;`"
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
ref="form"
|
||||||
|
:size="size"
|
||||||
|
:disabled="disabled"
|
||||||
|
:config="config"
|
||||||
|
:init-values="values"
|
||||||
|
:parent-values="parentValues"
|
||||||
|
:label-width="labelWidth"
|
||||||
|
@change="changeHandler"
|
||||||
|
></Form>
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<TMagicRow class="dialog-footer">
|
||||||
|
<TMagicCol :span="12" style="text-align: left">
|
||||||
|
<div style="min-height: 1px">
|
||||||
|
<slot name="left"></slot>
|
||||||
|
</div>
|
||||||
|
</TMagicCol>
|
||||||
|
<TMagicCol :span="12">
|
||||||
|
<slot name="footer">
|
||||||
|
<TMagicButton @click="hide">关闭</TMagicButton>
|
||||||
|
<TMagicButton type="primary" @click="submitHandler" :loading="saveFetch">{{ confirmText }}</TMagicButton>
|
||||||
|
</slot>
|
||||||
|
</TMagicCol>
|
||||||
|
</TMagicRow>
|
||||||
|
</template>
|
||||||
|
</TMagicDrawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import { TMagicButton, TMagicCol, TMagicDrawer, TMagicRow } from '@tmagic/design';
|
||||||
|
|
||||||
|
import Form from './Form.vue';
|
||||||
|
import type { FormConfig } from './schema';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'MFormDialog',
|
||||||
|
});
|
||||||
|
|
||||||
|
withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
config?: FormConfig;
|
||||||
|
values?: Object;
|
||||||
|
parentValues?: Object;
|
||||||
|
width?: string | number;
|
||||||
|
labelWidth?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
title?: string;
|
||||||
|
zIndex?: number;
|
||||||
|
size?: 'small' | 'default' | 'large';
|
||||||
|
confirmText?: string;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
config: () => [],
|
||||||
|
values: () => ({}),
|
||||||
|
confirmText: '确定',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits(['close', 'submit', 'error', 'change']);
|
||||||
|
|
||||||
|
const form = ref<InstanceType<typeof Form>>();
|
||||||
|
const visible = ref(false);
|
||||||
|
const saveFetch = ref(false);
|
||||||
|
const bodyHeight = ref(`${document.body.clientHeight - 194}px`);
|
||||||
|
|
||||||
|
const submitHandler = async () => {
|
||||||
|
try {
|
||||||
|
const values = await form.value?.submitForm();
|
||||||
|
emit('submit', values);
|
||||||
|
} catch (e) {
|
||||||
|
emit('error', e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeHandler = (value: any) => {
|
||||||
|
emit('change', value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const show = () => {
|
||||||
|
visible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hide = () => {
|
||||||
|
visible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
form,
|
||||||
|
saveFetch,
|
||||||
|
|
||||||
|
show,
|
||||||
|
hide,
|
||||||
|
});
|
||||||
|
</script>
|
@ -2,8 +2,7 @@
|
|||||||
<div
|
<div
|
||||||
v-if="config"
|
v-if="config"
|
||||||
:style="config.tip ? 'display: flex;align-items: baseline;' : ''"
|
:style="config.tip ? 'display: flex;align-items: baseline;' : ''"
|
||||||
:class="config.className"
|
:class="`m-form-container m-container-${type || ''} ${config.className || ''}`"
|
||||||
class="m-form-container"
|
|
||||||
>
|
>
|
||||||
<m-fields-hidden
|
<m-fields-hidden
|
||||||
v-if="type === 'hidden'"
|
v-if="type === 'hidden'"
|
||||||
|
@ -56,6 +56,7 @@ export * from './utils/useAddField';
|
|||||||
|
|
||||||
export { default as MForm } from './Form.vue';
|
export { default as MForm } from './Form.vue';
|
||||||
export { default as MFormDialog } from './FormDialog.vue';
|
export { default as MFormDialog } from './FormDialog.vue';
|
||||||
|
export { default as MFormDrawer } from './FormDrawer.vue';
|
||||||
export { default as MContainer } from './containers/Container.vue';
|
export { default as MContainer } from './containers/Container.vue';
|
||||||
export { default as MFieldset } from './containers/Fieldset.vue';
|
export { default as MFieldset } from './containers/Fieldset.vue';
|
||||||
export { default as MPanel } from './containers/Panel.vue';
|
export { default as MPanel } from './containers/Panel.vue';
|
||||||
|
5
packages/form/src/theme/form-drawer.scss
Normal file
5
packages/form/src/theme/form-drawer.scss
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.m-form-drawer {
|
||||||
|
.el-drawer__header {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
@use "./form-dialog.scss";
|
@use "./form-dialog.scss";
|
||||||
|
@use "./form-drawer.scss";
|
||||||
@use "./form.scss";
|
@use "./form.scss";
|
||||||
@use "./date-time.scss";
|
@use "./date-time.scss";
|
||||||
@use "./link.scss";
|
@use "./link.scss";
|
||||||
|
@ -16,3 +16,9 @@
|
|||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.m-container-panel {
|
||||||
|
&:not(:last-of-type) {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -30,6 +30,8 @@ export enum ActionType {
|
|||||||
COMP = 'comp',
|
COMP = 'comp',
|
||||||
/** 联动代码 */
|
/** 联动代码 */
|
||||||
CODE = 'code',
|
CODE = 'code',
|
||||||
|
/** 数据源 */
|
||||||
|
DATA_SOURCE = 'data-source',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DataSourceDeps {
|
export interface DataSourceDeps {
|
||||||
@ -71,7 +73,16 @@ export interface CompItemConfig {
|
|||||||
method: string;
|
method: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EventActionItem = CompItemConfig | CodeItemConfig;
|
export interface DataSourceItemConfig {
|
||||||
|
/** 动作类型 */
|
||||||
|
actionType: ActionType;
|
||||||
|
/** [数据源id, 方法] */
|
||||||
|
dataSourceMethod: [string, string];
|
||||||
|
/** 代码参数 */
|
||||||
|
params?: object;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EventActionItem = CompItemConfig | CodeItemConfig | DataSourceItemConfig;
|
||||||
|
|
||||||
export interface MComponent {
|
export interface MComponent {
|
||||||
/** 组件ID,默认为${type}_${number}}形式, 如:page_123 */
|
/** 组件ID,默认为${type}_${number}}形式, 如:page_123 */
|
||||||
@ -124,9 +135,11 @@ export interface CodeBlockContent {
|
|||||||
/** 代码块名称 */
|
/** 代码块名称 */
|
||||||
name: string;
|
name: string;
|
||||||
/** 代码块内容 */
|
/** 代码块内容 */
|
||||||
content: any;
|
content: ((...args: any[]) => any) | string;
|
||||||
/** 参数定义 */
|
/** 参数定义 */
|
||||||
params: CodeParam[] | [];
|
params: CodeParam[] | [];
|
||||||
|
/** 注释 */
|
||||||
|
desc?: string;
|
||||||
/** 扩展字段 */
|
/** 扩展字段 */
|
||||||
[propName: string]: any;
|
[propName: string]: any;
|
||||||
}
|
}
|
||||||
@ -137,6 +150,7 @@ export interface CodeParam {
|
|||||||
/** 扩展字段 */
|
/** 扩展字段 */
|
||||||
[propName: string]: any;
|
[propName: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PastePosition {
|
export interface PastePosition {
|
||||||
left?: number;
|
left?: number;
|
||||||
top?: number;
|
top?: number;
|
||||||
@ -176,6 +190,8 @@ export interface DataSourceSchema {
|
|||||||
description?: string;
|
description?: string;
|
||||||
/** 字段列表 */
|
/** 字段列表 */
|
||||||
fields: DataSchema[];
|
fields: DataSchema[];
|
||||||
|
/** 方法列表 */
|
||||||
|
methods?: CodeBlockContent[];
|
||||||
/** 扩展字段 */
|
/** 扩展字段 */
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user