mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-06 03:57:56 +08:00
feat(editor,form,core,schema): 事件支持触发代码块
This commit is contained in:
parent
cfd2a6eee3
commit
39468f3b95
@ -18,7 +18,18 @@
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
import type { CodeBlockDSL, EventItemConfig, Id, MApp } from '@tmagic/schema';
|
||||
import { has, isEmpty } from 'lodash-es';
|
||||
|
||||
import {
|
||||
ActionType,
|
||||
CodeBlockDSL,
|
||||
CodeItemConfig,
|
||||
CompItemConfig,
|
||||
DeprecatedEventConfig,
|
||||
EventConfig,
|
||||
Id,
|
||||
MApp,
|
||||
} from '@tmagic/schema';
|
||||
|
||||
import Env from './Env';
|
||||
import { bindCommonEventListener, isCommonMethod, triggerCommonMethod } from './events';
|
||||
@ -37,7 +48,7 @@ interface AppOptionsConfig {
|
||||
}
|
||||
|
||||
interface EventCache {
|
||||
eventConfig: EventItemConfig;
|
||||
eventConfig: CompItemConfig | DeprecatedEventConfig;
|
||||
fromCpt: any;
|
||||
args: any[];
|
||||
}
|
||||
@ -221,10 +232,10 @@ class App extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
public bindEvent(event: EventItemConfig, id: string) {
|
||||
public bindEvent(event: EventConfig | DeprecatedEventConfig, id: string) {
|
||||
const { name } = event;
|
||||
this.on(`${name}_${id}`, (fromCpt: Node, ...args) => {
|
||||
this.eventHandler(event, fromCpt, args);
|
||||
this.on(`${name}_${id}`, async (fromCpt: Node, ...args) => {
|
||||
await this.eventHandler(event, fromCpt, args);
|
||||
});
|
||||
}
|
||||
|
||||
@ -235,11 +246,53 @@ class App extends EventEmitter {
|
||||
return super.emit(name, node, ...args);
|
||||
}
|
||||
|
||||
public eventHandler(eventConfig: EventItemConfig, fromCpt: any, args: any[]) {
|
||||
/**
|
||||
* 事件联动处理函数
|
||||
* @param eventConfig 事件配置
|
||||
* @param fromCpt 触发事件的组件
|
||||
* @param args 事件参数
|
||||
*/
|
||||
public async eventHandler(eventConfig: EventConfig | DeprecatedEventConfig, fromCpt: any, args: any[]) {
|
||||
if (has(eventConfig, 'actions')) {
|
||||
// EventConfig类型
|
||||
const { actions } = eventConfig as EventConfig;
|
||||
for (const actionItem of actions) {
|
||||
if (actionItem.actionType === ActionType.COMP) {
|
||||
// 组件动作
|
||||
await this.compActionHandler(actionItem as CompItemConfig, fromCpt, args);
|
||||
} else if (actionItem.actionType === ActionType.CODE) {
|
||||
// 执行代码块
|
||||
await this.codeActionHandler(actionItem as CodeItemConfig);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 兼容DeprecatedEventConfig类型 组件动作
|
||||
await this.compActionHandler(eventConfig as DeprecatedEventConfig, fromCpt, args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行代码块动作
|
||||
* @param eventConfig 代码动作的配置
|
||||
* @returns void
|
||||
*/
|
||||
public async codeActionHandler(eventConfig: CodeItemConfig) {
|
||||
const { codeId = '', params = {} } = eventConfig;
|
||||
if (!codeId || isEmpty(this.codeDsl)) return;
|
||||
if (this.codeDsl![codeId] && typeof this.codeDsl![codeId]?.content === 'function') {
|
||||
await this.codeDsl![codeId].content({ app: this, params });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行联动组件动作
|
||||
* @param eventConfig 联动组件的配置
|
||||
* @returns void
|
||||
*/
|
||||
public async compActionHandler(eventConfig: CompItemConfig | DeprecatedEventConfig, fromCpt: any, args: any[]) {
|
||||
if (!this.page) throw new Error('当前没有页面');
|
||||
|
||||
const { method: methodName, to } = eventConfig;
|
||||
|
||||
const toNode = this.page.getNode(to);
|
||||
if (!toNode) throw `ID为${to}的组件不存在`;
|
||||
|
||||
@ -249,7 +302,7 @@ class App extends EventEmitter {
|
||||
|
||||
if (toNode.instance) {
|
||||
if (typeof toNode.instance[methodName] === 'function') {
|
||||
toNode.instance[methodName](fromCpt, ...args);
|
||||
await toNode.instance[methodName](fromCpt, ...args);
|
||||
}
|
||||
} else {
|
||||
this.addEventToMap({
|
||||
|
@ -20,7 +20,7 @@ import { EventEmitter } from 'events';
|
||||
|
||||
import { isEmpty } from 'lodash-es';
|
||||
|
||||
import { EventItemConfig, HookType, MComponent, MContainer, MPage } from '@tmagic/schema';
|
||||
import { DeprecatedEventConfig, EventConfig, HookType, MComponent, MContainer, MPage } from '@tmagic/schema';
|
||||
|
||||
import type App from './App';
|
||||
import type Page from './Page';
|
||||
@ -37,7 +37,7 @@ class Node extends EventEmitter {
|
||||
public style?: {
|
||||
[key: string]: any;
|
||||
};
|
||||
public events?: EventItemConfig[];
|
||||
public events?: DeprecatedEventConfig[] | EventConfig[];
|
||||
public instance?: any;
|
||||
public page?: Page;
|
||||
public parent?: Node;
|
||||
@ -86,7 +86,7 @@ class Node extends EventEmitter {
|
||||
const eventConfigQueue = this.app.eventQueueMap[instance.config.id] || [];
|
||||
|
||||
for (let eventConfig = eventConfigQueue.shift(); eventConfig; eventConfig = eventConfigQueue.shift()) {
|
||||
this.app.eventHandler(eventConfig.eventConfig, eventConfig.fromCpt, eventConfig.args);
|
||||
this.app.compActionHandler(eventConfig.eventConfig, eventConfig.fromCpt, eventConfig.args);
|
||||
}
|
||||
|
||||
await this.runCodeBlock('mounted');
|
||||
@ -99,11 +99,11 @@ class Node extends EventEmitter {
|
||||
await this.data[hook](this);
|
||||
return;
|
||||
}
|
||||
if (this.data[hook]?.hookType !== HookType.CODE || !this.app.codeDsl || 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) {
|
||||
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 });
|
||||
if (this.app.codeDsl![codeId] && typeof this.app.codeDsl![codeId]?.content === 'function') {
|
||||
await this.app.codeDsl![codeId].content({ app: this.app, params });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
</template>
|
||||
<script lang="ts" setup name="MEditorCodeDraftEditor">
|
||||
import { computed, inject, ref, watchEffect } from 'vue';
|
||||
import type { Action } from 'element-plus';
|
||||
import type * as monaco from 'monaco-editor';
|
||||
|
||||
import { TMagicButton, tMagicMessage, tMagicMessageBox } from '@tmagic/design';
|
||||
@ -109,18 +110,21 @@ const close = async (): Promise<void> => {
|
||||
if (codeDraft) {
|
||||
tMagicMessageBox
|
||||
.confirm('您有代码修改未保存,是否保存后再关闭?', '提示', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
confirmButtonText: '保存并关闭',
|
||||
cancelButtonText: '直接关闭',
|
||||
type: 'warning',
|
||||
distinguishCancelAndClose: true,
|
||||
})
|
||||
.then(async () => {
|
||||
// 保存之后再关闭
|
||||
saveAndClose();
|
||||
})
|
||||
.catch(() => {
|
||||
// 删除草稿 直接关闭
|
||||
services?.codeBlockService.removeCodeDraft(props.id);
|
||||
emit('close');
|
||||
.catch((action: Action) => {
|
||||
if (action === 'cancel') {
|
||||
// 删除草稿 直接关闭
|
||||
services?.codeBlockService.removeCodeDraft(props.id);
|
||||
emit('close');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
emit('close');
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="m-fields-code-select">
|
||||
<div class="m-fields-code-select" :class="config.className">
|
||||
<m-form-table
|
||||
:config="tableConfig"
|
||||
:model="model[name]"
|
||||
@ -11,7 +11,7 @@
|
||||
>
|
||||
<template #operateCol="{ scope }">
|
||||
<Icon
|
||||
v-if="scope.row.codeId"
|
||||
v-if="scope.row.codeId && config.editable"
|
||||
:icon="editable ? Edit : View"
|
||||
class="edit-icon"
|
||||
@click="editCode(scope.row.codeId)"
|
||||
@ -35,15 +35,25 @@ const services = inject<Services>('services');
|
||||
const mForm = inject<FormState>('mForm');
|
||||
const emit = defineEmits(['change']);
|
||||
|
||||
const props = defineProps<{
|
||||
config: {
|
||||
tableConfig?: TableConfig;
|
||||
};
|
||||
model: any;
|
||||
prop: string;
|
||||
name: string;
|
||||
size: 'mini' | 'small' | 'medium';
|
||||
}>();
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
config: {
|
||||
tableConfig?: TableConfig;
|
||||
className?: string;
|
||||
editable?: boolean;
|
||||
};
|
||||
model: any;
|
||||
prop: string;
|
||||
name: string;
|
||||
size: 'small' | 'default' | 'large';
|
||||
}>(),
|
||||
{
|
||||
config: () => ({
|
||||
editable: true,
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
const codeDsl = computed(() => services?.codeBlockService.getCodeDsl());
|
||||
|
||||
const tableConfig = computed<FormItem>(() => {
|
||||
@ -80,7 +90,7 @@ const tableConfig = computed<FormItem>(() => {
|
||||
itemsFunction: (row: HookData) => {
|
||||
const paramsConfig = getParamsConfig(row.codeId);
|
||||
// 如果参数没有填值,则使用createValues补全空值
|
||||
if (isEmpty(row.params) || !row.params) {
|
||||
if (!row.params || isEmpty(row.params)) {
|
||||
createValues(mForm, paramsConfig, {}, row.params);
|
||||
}
|
||||
return paramsConfig;
|
||||
@ -124,6 +134,7 @@ const getParamsConfig = (codeId: Id): CodeParamStatement[] => {
|
||||
return paramStatements.map((paramState: CodeParamStatement) => ({
|
||||
labelWidth: '100px',
|
||||
text: paramState.name,
|
||||
inline: true,
|
||||
...paramState,
|
||||
}));
|
||||
};
|
||||
|
117
packages/editor/src/fields/CodeSelectCol.vue
Normal file
117
packages/editor/src/fields/CodeSelectCol.vue
Normal file
@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<div class="m-fields-code-select-col">
|
||||
<!-- 代码块下拉框 -->
|
||||
<m-form-container :config="selectConfig" :model="model" @change="onParamsChangeHandler"></m-form-container>
|
||||
<!-- 参数填写框 -->
|
||||
<m-form-container :config="codeParamsConfig" :model="model" @change="onParamsChangeHandler"></m-form-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="MEditorCodeSelectCol">
|
||||
import { computed, defineEmits, defineProps, inject, ref, watch } from 'vue';
|
||||
import { isEmpty, map } from 'lodash-es';
|
||||
|
||||
import { createValues, FieldsetConfig, FormState } from '@tmagic/form';
|
||||
import { Id } from '@tmagic/schema';
|
||||
|
||||
import { CodeParamStatement, Services } from '../type';
|
||||
const services = inject<Services>('services');
|
||||
const mForm = inject<FormState>('mForm');
|
||||
const emit = defineEmits(['change']);
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
config: any;
|
||||
model: any;
|
||||
prop: string;
|
||||
name: string;
|
||||
size: 'small' | 'default' | 'large';
|
||||
}>(),
|
||||
{},
|
||||
);
|
||||
const codeDsl = computed(() => services?.codeBlockService.getCodeDsl());
|
||||
const codeParamsConfig = ref<FieldsetConfig>({
|
||||
type: 'fieldset',
|
||||
items: [],
|
||||
legend: '参数',
|
||||
labelWidth: '70px',
|
||||
name: 'params',
|
||||
display: false,
|
||||
});
|
||||
const selectConfig = {
|
||||
type: 'select',
|
||||
text: '代码块',
|
||||
name: 'codeId',
|
||||
labelWidth: '70px',
|
||||
options: () => {
|
||||
if (codeDsl.value) {
|
||||
return map(codeDsl.value, (value, key) => ({
|
||||
text: `${value.name}(${key})`,
|
||||
label: `${value.name}(${key})`,
|
||||
value: key,
|
||||
}));
|
||||
}
|
||||
return [];
|
||||
},
|
||||
onChange: (formState: any, codeId: Id, { model }: any) => {
|
||||
// 通过下拉框选择的codeId变化后修正model的值,避免写入其他codeId的params
|
||||
model.params = {};
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据代码块id获取代码块参数配置
|
||||
* @param codeId 代码块ID
|
||||
*/
|
||||
const getParamItemsConfig = (codeId: Id): CodeParamStatement[] => {
|
||||
if (!codeDsl.value) return [];
|
||||
const paramStatements = codeDsl.value[codeId]?.params;
|
||||
if (isEmpty(paramStatements)) return [];
|
||||
return paramStatements.map((paramState: CodeParamStatement) => ({
|
||||
labelWidth: '100px',
|
||||
text: paramState.name,
|
||||
...paramState,
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据代码块id获取参数fieldset表单配置
|
||||
* @param codeId 代码块ID
|
||||
*/
|
||||
const getCodeParamsConfig = (codeId: Id) => {
|
||||
const paramsConfig = getParamItemsConfig(codeId);
|
||||
// 如果参数没有填值,则使用createValues补全空值
|
||||
if (!props.model.params || isEmpty(props.model.params)) {
|
||||
props.model.params = {};
|
||||
createValues(mForm, paramsConfig, {}, props.model.params);
|
||||
}
|
||||
codeParamsConfig.value = {
|
||||
type: 'fieldset',
|
||||
items: paramsConfig,
|
||||
legend: '参数',
|
||||
labelWidth: '70px',
|
||||
name: 'params',
|
||||
display: !isEmpty(paramsConfig),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 参数值修改更新
|
||||
*/
|
||||
const onParamsChangeHandler = () => {
|
||||
emit('change', props.model);
|
||||
};
|
||||
|
||||
// 监听代码块顺序变化以及下拉选择的变化,并更新参数配置
|
||||
// TODO onchange在watch之前触发
|
||||
watch(
|
||||
() => props.model.codeId,
|
||||
(codeId: Id) => {
|
||||
if (!codeId) return;
|
||||
getCodeParamsConfig(codeId);
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
</script>
|
216
packages/editor/src/fields/EventSelect.vue
Normal file
216
packages/editor/src/fields/EventSelect.vue
Normal file
@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<div class="m-fields-event-select">
|
||||
<m-form-container
|
||||
v-if="isOldVersion"
|
||||
ref="eventForm"
|
||||
:size="props.size"
|
||||
:model="model"
|
||||
:config="tableConfig"
|
||||
@change="onChangeHandler"
|
||||
></m-form-container>
|
||||
|
||||
<div v-else class="fullWidth">
|
||||
<TMagicButton class="create-button" type="primary" size="small" @click="addEvent()">添加事件</TMagicButton>
|
||||
<m-form-panel
|
||||
v-for="(cardItem, index) in model[name]"
|
||||
:key="index"
|
||||
:config="actionsConfig"
|
||||
:model="cardItem"
|
||||
@change="onChangeHandler"
|
||||
>
|
||||
<template #header>
|
||||
<m-form-container
|
||||
class="fullWidth"
|
||||
:config="eventNameConfig"
|
||||
:model="cardItem"
|
||||
@change="onChangeHandler"
|
||||
></m-form-container>
|
||||
<TMagicButton style="color: #f56c6c" text :icon="Delete" @click="removeEvent(index)"></TMagicButton>
|
||||
</template>
|
||||
</m-form-panel>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="MEditorEventSelect">
|
||||
import { computed, defineProps, inject } from 'vue';
|
||||
import { Delete } from '@element-plus/icons-vue';
|
||||
import { has } from 'lodash-es';
|
||||
|
||||
import { TMagicButton } from '@tmagic/design';
|
||||
import { FormState } from '@tmagic/form';
|
||||
import { ActionType } from '@tmagic/schema';
|
||||
|
||||
import { EventSelectConfig, Services } from '../type';
|
||||
const services = inject<Services>('services');
|
||||
|
||||
const props = defineProps<{
|
||||
config: EventSelectConfig;
|
||||
model: any;
|
||||
prop: string;
|
||||
name: string;
|
||||
size: 'small' | 'default' | 'large';
|
||||
}>();
|
||||
const emit = defineEmits(['change']);
|
||||
|
||||
// 事件名称下拉框表单配置
|
||||
const eventNameConfig = computed(() => {
|
||||
const defaultEventNameConfig = {
|
||||
name: 'name',
|
||||
text: '事件',
|
||||
type: 'select',
|
||||
labelWidth: '40px',
|
||||
options: (mForm: FormState, { formValue }: any) =>
|
||||
services?.eventsService.getEvent(formValue.type).map((option: any) => ({
|
||||
text: option.label,
|
||||
value: option.value,
|
||||
})),
|
||||
};
|
||||
return { ...defaultEventNameConfig, ...props.config.eventNameConfig };
|
||||
});
|
||||
|
||||
// 联动类型
|
||||
const actionTypeConfig = computed(() => {
|
||||
const defaultActionTypeConfig = {
|
||||
name: 'actionType',
|
||||
text: '联动类型',
|
||||
labelWidth: '70px',
|
||||
type: 'select',
|
||||
defaultValue: ActionType.COMP,
|
||||
options: () => [
|
||||
{
|
||||
text: '组件',
|
||||
label: '组件',
|
||||
value: ActionType.COMP,
|
||||
},
|
||||
{
|
||||
text: '代码',
|
||||
label: '代码',
|
||||
value: ActionType.CODE,
|
||||
},
|
||||
],
|
||||
};
|
||||
return { ...defaultActionTypeConfig, ...props.config.actionTypeConfig };
|
||||
});
|
||||
|
||||
// 联动组件配置
|
||||
const targetCompConfig = computed(() => {
|
||||
const defaultTargetCompConfig = {
|
||||
name: 'to',
|
||||
text: '联动组件',
|
||||
labelWidth: '70px',
|
||||
type: 'ui-select',
|
||||
display: (mForm: FormState, { model }: { model: Record<any, any> }) => model.actionType === ActionType.COMP,
|
||||
};
|
||||
return { ...defaultTargetCompConfig, ...props.config.targetCompConfig };
|
||||
});
|
||||
|
||||
// 联动组件动作配置
|
||||
const compActionConfig = computed(() => {
|
||||
const defaultCompActionConfig = {
|
||||
name: 'method',
|
||||
text: '动作',
|
||||
labelWidth: '70px',
|
||||
type: 'select',
|
||||
display: (mForm: FormState, { model }: { model: Record<any, any> }) => model.actionType === ActionType.COMP,
|
||||
options: (mForm: FormState, { model }: any) => {
|
||||
const node = services?.editorService.getNodeById(model.to);
|
||||
if (!node?.type) return [];
|
||||
|
||||
return services?.eventsService.getMethod(node.type).map((option: any) => ({
|
||||
text: option.label,
|
||||
value: option.value,
|
||||
}));
|
||||
},
|
||||
};
|
||||
return { ...defaultCompActionConfig, ...props.config.compActionConfig };
|
||||
});
|
||||
|
||||
// 代码联动配置
|
||||
const codeActionConfig = computed(() => {
|
||||
const defaultCodeActionConfig = {
|
||||
type: 'code-select-col',
|
||||
display: (mForm: FormState, { model }: { model: Record<any, any> }) => model.actionType === ActionType.CODE,
|
||||
};
|
||||
return { ...defaultCodeActionConfig, ...props.config.codeActionConfig };
|
||||
});
|
||||
|
||||
// 兼容旧的数据格式
|
||||
const tableConfig = computed(() => ({
|
||||
type: 'table',
|
||||
name: 'events',
|
||||
items: [
|
||||
{
|
||||
name: 'name',
|
||||
label: '事件名',
|
||||
type: eventNameConfig.value.type,
|
||||
options: (mForm: FormState, { formValue }: any) =>
|
||||
services?.eventsService.getEvent(formValue.type).map((option: any) => ({
|
||||
text: option.label,
|
||||
value: option.value,
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: 'to',
|
||||
label: '联动组件',
|
||||
type: 'ui-select',
|
||||
},
|
||||
{
|
||||
name: 'method',
|
||||
label: '动作',
|
||||
type: compActionConfig.value.type,
|
||||
options: (mForm: FormState, { model }: any) => {
|
||||
const node = services?.editorService.getNodeById(model.to);
|
||||
if (!node?.type) return [];
|
||||
|
||||
return services?.eventsService.getMethod(node.type).map((option: any) => ({
|
||||
text: option.label,
|
||||
value: option.value,
|
||||
}));
|
||||
},
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
// 组件动作组表单配置
|
||||
const actionsConfig = computed(() => ({
|
||||
items: [
|
||||
{
|
||||
type: 'group-list',
|
||||
name: 'actions',
|
||||
enableToggleMode: false,
|
||||
items: [actionTypeConfig.value, targetCompConfig.value, compActionConfig.value, codeActionConfig.value],
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
// 是否为旧的数据格式
|
||||
const isOldVersion = computed(() => {
|
||||
if (props.model[props.name].length === 0) return false;
|
||||
return !has(props.model[props.name][0], 'actions');
|
||||
});
|
||||
|
||||
// 添加事件
|
||||
const addEvent = () => {
|
||||
const defaultEvent = {
|
||||
name: '',
|
||||
actions: [],
|
||||
};
|
||||
if (!props.model[props.name]) {
|
||||
props.model[props.name] = [];
|
||||
}
|
||||
props.model[props.name].push(defaultEvent);
|
||||
onChangeHandler();
|
||||
};
|
||||
|
||||
// 删除事件
|
||||
const removeEvent = (index: number) => {
|
||||
if (!props.name) return;
|
||||
props.model[props.name].splice(index, 1);
|
||||
onChangeHandler();
|
||||
};
|
||||
|
||||
const onChangeHandler = () => {
|
||||
emit('change', props.model);
|
||||
};
|
||||
</script>
|
@ -20,6 +20,8 @@ import { App } from 'vue';
|
||||
import Code from './fields/Code.vue';
|
||||
import CodeLink from './fields/CodeLink.vue';
|
||||
import CodeSelect from './fields/CodeSelect.vue';
|
||||
import CodeSelectCol from './fields/CodeSelectCol.vue';
|
||||
import EventSelect from './fields/EventSelect.vue';
|
||||
import uiSelect from './fields/UISelect.vue';
|
||||
import CodeEditor from './layouts/CodeEditor.vue';
|
||||
import { setConfig } from './utils/config';
|
||||
@ -44,6 +46,8 @@ export { default as depService } from './services/dep';
|
||||
export { default as ComponentListPanel } from './layouts/sidebar/ComponentListPanel.vue';
|
||||
export { default as LayerPanel } from './layouts/sidebar/LayerPanel.vue';
|
||||
export { default as CodeSelect } from './fields/CodeSelect.vue';
|
||||
export { default as CodeSelectCol } from './fields/CodeSelectCol.vue';
|
||||
export { default as EventSelect } from './fields/EventSelect.vue';
|
||||
export { default as CodeBlockList } from './layouts/sidebar/code-block/CodeBlockList.vue';
|
||||
export { default as PropsPanel } from './layouts/PropsPanel.vue';
|
||||
export { default as ToolButton } from './components/ToolButton.vue';
|
||||
@ -68,5 +72,7 @@ export default {
|
||||
app.component('m-fields-vs-code', Code);
|
||||
app.component('magic-code-editor', CodeEditor);
|
||||
app.component('m-fields-code-select', CodeSelect);
|
||||
app.component('m-fields-code-select-col', CodeSelectCol);
|
||||
app.component('m-fields-event-select', EventSelect);
|
||||
},
|
||||
};
|
||||
|
@ -171,7 +171,7 @@ class CodeBlock extends BaseService {
|
||||
|
||||
/**
|
||||
* 设置编辑状态
|
||||
* @param {boolean} 是否可编辑
|
||||
* @param {boolean} status 是否可编辑
|
||||
* @returns {void}
|
||||
*/
|
||||
public async setEditStatus(status: boolean): Promise<void> {
|
||||
|
24
packages/editor/src/theme/event.scss
Normal file
24
packages/editor/src/theme/event.scss
Normal file
@ -0,0 +1,24 @@
|
||||
.m-fields-event-select {
|
||||
width: 100%;
|
||||
.fullWidth {
|
||||
width: 100%;
|
||||
}
|
||||
.m-form-panel .el-card__body {
|
||||
padding: 10px 25px;
|
||||
}
|
||||
|
||||
.event-select-code {
|
||||
margin-left: 20px;
|
||||
width: auto;
|
||||
}
|
||||
.m-form-panel {
|
||||
margin: 10px 0px;
|
||||
}
|
||||
|
||||
.el-card.is-always-shadow {
|
||||
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
}
|
||||
.m-fields-code-select-col {
|
||||
width: 100%;
|
||||
}
|
@ -12,5 +12,6 @@
|
||||
@import "./code-editor.scss";
|
||||
@import "./icon.scss";
|
||||
@import "./code-block.scss";
|
||||
@import "./event.scss";
|
||||
@import "./layout.scss";
|
||||
@import "./breadcrumb.scss";
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
import type { Component } from 'vue';
|
||||
|
||||
import type { FormConfig } from '@tmagic/form';
|
||||
import type { FormConfig, FormItem } from '@tmagic/form';
|
||||
import type { CodeBlockContent, CodeBlockDSL, Id, MApp, MContainer, MNode, MPage } from '@tmagic/schema';
|
||||
import type StageCore from '@tmagic/stage';
|
||||
import type {
|
||||
@ -410,3 +410,18 @@ export interface HistoryState {
|
||||
canRedo: boolean;
|
||||
canUndo: boolean;
|
||||
}
|
||||
|
||||
export interface EventSelectConfig {
|
||||
name: string;
|
||||
type: 'event-select';
|
||||
/** 事件名称表单配置 */
|
||||
eventNameConfig?: FormItem;
|
||||
/** 动作类型配置 */
|
||||
actionTypeConfig?: FormItem;
|
||||
/** 联动组件配置 */
|
||||
targetCompConfig?: FormItem;
|
||||
/** 联动组件动作配置 */
|
||||
compActionConfig?: FormItem;
|
||||
/** 联动代码配置 */
|
||||
codeActionConfig?: FormItem;
|
||||
}
|
||||
|
@ -18,9 +18,6 @@
|
||||
|
||||
import { FormConfig, FormState } from '@tmagic/form';
|
||||
|
||||
import editorService from '../services/editor';
|
||||
import eventsService from '../services/events';
|
||||
|
||||
/**
|
||||
* 统一为组件属性表单加上事件、高级、样式配置
|
||||
* @param config 组件属性配置
|
||||
@ -183,39 +180,8 @@ export const fillConfig = (config: FormConfig = []) => [
|
||||
title: '事件',
|
||||
items: [
|
||||
{
|
||||
type: 'table',
|
||||
name: 'events',
|
||||
items: [
|
||||
{
|
||||
name: 'name',
|
||||
label: '事件名',
|
||||
type: 'select',
|
||||
options: (mForm: FormState, { formValue }: any) =>
|
||||
eventsService.getEvent(formValue.type).map((option) => ({
|
||||
text: option.label,
|
||||
value: option.value,
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: 'to',
|
||||
label: '联动组件',
|
||||
type: 'ui-select',
|
||||
},
|
||||
{
|
||||
name: 'method',
|
||||
label: '动作',
|
||||
type: 'select',
|
||||
options: (mForm: FormState, { model }: any) => {
|
||||
const node = editorService.getNodeById(model.to);
|
||||
if (!node?.type) return [];
|
||||
|
||||
return eventsService.getMethod(node.type).map((option) => ({
|
||||
text: option.label,
|
||||
value: option.value,
|
||||
}));
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'event-select',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -1,11 +1,9 @@
|
||||
<template>
|
||||
<div class="m-fields-group-list-item">
|
||||
<div>
|
||||
<TMagicIcon style="margin-right: 7px" @click="expandHandler"
|
||||
><CaretBottom v-if="expand" /><CaretRight v-else
|
||||
/></TMagicIcon>
|
||||
|
||||
<TMagicButton text :disabled="disabled" @click="expandHandler">{{ title }}</TMagicButton>
|
||||
<TMagicButton text :disabled="disabled" :icon="expand ? CaretBottom : CaretRight" @click="expandHandler">{{
|
||||
title
|
||||
}}</TMagicButton>
|
||||
|
||||
<TMagicButton
|
||||
v-show="showDelete(parseInt(String(index)))"
|
||||
|
@ -5,11 +5,11 @@
|
||||
:body-style="{ display: expand ? 'block' : 'none' }"
|
||||
>
|
||||
<template #header>
|
||||
<div class="clearfix">
|
||||
<a href="javascript:" style="width: 100%; display: block" @click="expand = !expand">
|
||||
<TMagicIcon><CaretBottom v-if="expand" /><CaretRight v-else /></TMagicIcon> {{ filter(config.title) }}
|
||||
<span v-if="config && config.extra" v-html="config.extra" class="m-form-tip"></span>
|
||||
</a>
|
||||
<div style="width: 100%; display: flex; align-items: center">
|
||||
<TMagicButton style="padding: 0" text :icon="expand ? CaretBottom : CaretRight" @click="expand = !expand">
|
||||
</TMagicButton>
|
||||
<span v-if="config && config.extra" v-html="config.extra" class="m-form-tip"></span>
|
||||
<slot name="header">{{ filter(config.title) }}</slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -61,7 +61,7 @@
|
||||
import { computed, inject, ref } from 'vue';
|
||||
import { CaretBottom, CaretRight } from '@element-plus/icons-vue';
|
||||
|
||||
import { TMagicCard, TMagicIcon } from '@tmagic/design';
|
||||
import { TMagicButton, TMagicCard } from '@tmagic/design';
|
||||
|
||||
import { FormState, PanelConfig } from '../schema';
|
||||
import { filterFunction } from '../utils/form';
|
||||
@ -73,7 +73,7 @@ const props = defineProps<{
|
||||
lastValues?: any;
|
||||
isCompare?: boolean;
|
||||
config: PanelConfig;
|
||||
name: string;
|
||||
name?: string;
|
||||
labelWidth?: string;
|
||||
prop?: string;
|
||||
size?: string;
|
||||
|
@ -24,15 +24,51 @@ export enum NodeType {
|
||||
|
||||
export type Id = string | number;
|
||||
|
||||
export interface EventItemConfig {
|
||||
// 事件联动的动作类型
|
||||
export enum ActionType {
|
||||
/** 联动组件 */
|
||||
COMP = 'comp',
|
||||
/** 联动代码 */
|
||||
CODE = 'code',
|
||||
}
|
||||
|
||||
/** 事件类型(已废弃,后续不建议继续使用) */
|
||||
export interface DeprecatedEventConfig {
|
||||
/** 待触发的事件名称 */
|
||||
name: string;
|
||||
/** 被选中组件ID */
|
||||
to: Id;
|
||||
/** 被选中组件名称 */
|
||||
name: string;
|
||||
/** 触发事件后执行被选中组件的方法 */
|
||||
method: string;
|
||||
}
|
||||
|
||||
export interface EventConfig {
|
||||
/** 待触发的事件名称 */
|
||||
name: string;
|
||||
/** 动作响应配置 */
|
||||
actions: EventActionItem[];
|
||||
}
|
||||
|
||||
export interface CodeItemConfig {
|
||||
/** 动作类型 */
|
||||
actionType: ActionType;
|
||||
/** 代码ID */
|
||||
codeId: Id;
|
||||
/** 代码参数 */
|
||||
params?: object;
|
||||
}
|
||||
|
||||
export interface CompItemConfig {
|
||||
/** 动作类型 */
|
||||
actionType: ActionType;
|
||||
/** 被选中组件ID */
|
||||
to: Id;
|
||||
/** 触发事件后执行被选中组件的方法 */
|
||||
method: string;
|
||||
}
|
||||
|
||||
export type EventActionItem = CompItemConfig | CodeItemConfig;
|
||||
|
||||
export interface MComponent {
|
||||
/** 组件ID,默认为${type}_${number}}形式, 如:page_123 */
|
||||
id: Id;
|
||||
@ -43,7 +79,7 @@ export interface MComponent {
|
||||
/** 组件根Dom上的class */
|
||||
className?: string;
|
||||
/* 关联事件集合 */
|
||||
events?: EventItemConfig[];
|
||||
events?: EventConfig[] | DeprecatedEventConfig[];
|
||||
/** 组件根Dom的style */
|
||||
style?: {
|
||||
[key: string]: any;
|
||||
|
@ -28,9 +28,11 @@ export default {
|
||||
params: [
|
||||
{
|
||||
name: 'age',
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
name: 'studentName',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -64,7 +66,35 @@ export default {
|
||||
fontSize: '',
|
||||
fontWeight: '',
|
||||
},
|
||||
events: [],
|
||||
events: [
|
||||
{
|
||||
name: 'magic:common:events:click', // 事件名
|
||||
actions: [
|
||||
{
|
||||
actionType: 'code', // 联动动作类型
|
||||
codeId: 'code_5336', // 代码块id
|
||||
params: {
|
||||
age: 12,
|
||||
}, // 参数
|
||||
},
|
||||
{
|
||||
actionType: 'comp',
|
||||
to: 'overlay_2159', // 联动组件id
|
||||
method: 'openOverlay', // 联动组件方法
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'magic:common:events:click', // 事件名
|
||||
actions: [
|
||||
{
|
||||
actionType: 'code', // 联动动作类型
|
||||
codeId: 'code_5316', // 代码块id
|
||||
params: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
created: {
|
||||
hookType: 'code',
|
||||
hookData: [
|
||||
|
Loading…
x
Reference in New Issue
Block a user