mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-06-02 22:19:19 +08:00
feat(editor): 代码块支持传递参数 (merge request !9)
Squash merge branch 'feature/parisma_codeDraft' into 'master' 1、 table支持items为函数 2、代码块支持传递参数
This commit is contained in:
parent
cc21c47829
commit
16f671cd8f
@ -17,7 +17,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup name="MEditorCodeDraftEditor">
|
||||||
import { computed, inject, ref, watchEffect } from 'vue';
|
import { computed, inject, ref, watchEffect } from 'vue';
|
||||||
import type * as monaco from 'monaco-editor';
|
import type * as monaco from 'monaco-editor';
|
||||||
|
|
||||||
|
@ -5,6 +5,19 @@
|
|||||||
<div class="code-name-label">代码块名称</div>
|
<div class="code-name-label">代码块名称</div>
|
||||||
<TMagicInput class="code-name-input" v-model="codeName" :disabled="!editable" />
|
<TMagicInput class="code-name-input" v-model="codeName" :disabled="!editable" />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="code-name-wrapper">
|
||||||
|
<div class="code-name-label">参数定义</div>
|
||||||
|
<m-form-table
|
||||||
|
style="width: 320px"
|
||||||
|
:config="tableConfig"
|
||||||
|
:model="tableModel"
|
||||||
|
:enableToggleMode="false"
|
||||||
|
name="params"
|
||||||
|
prop="params"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
</m-form-table>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<CodeDraftEditor
|
<CodeDraftEditor
|
||||||
:id="id"
|
:id="id"
|
||||||
@ -19,16 +32,29 @@
|
|||||||
></CodeDraftEditor>
|
></CodeDraftEditor>
|
||||||
</TMagicCard>
|
</TMagicCard>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup name="MEditorFunctionEditor">
|
||||||
import { inject, ref, watchEffect } from 'vue';
|
import { inject, provide, ref, watchEffect } from 'vue';
|
||||||
|
|
||||||
import { TMagicCard, TMagicInput, tMagicMessage } from '@tmagic/design';
|
import { TMagicCard, TMagicInput, tMagicMessage } from '@tmagic/design';
|
||||||
import { Id } from '@tmagic/schema';
|
import { CodeParam, Id } from '@tmagic/schema';
|
||||||
|
|
||||||
import type { Services } from '../type';
|
import type { Services } from '../type';
|
||||||
|
|
||||||
import CodeDraftEditor from './CodeDraftEditor.vue';
|
import CodeDraftEditor from './CodeDraftEditor.vue';
|
||||||
|
|
||||||
|
const tableConfig = {
|
||||||
|
border: true,
|
||||||
|
enableFullscreen: false,
|
||||||
|
name: 'params',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
label: '参数名',
|
||||||
|
name: 'name',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
id: Id;
|
id: Id;
|
||||||
@ -44,20 +70,39 @@ const props = withDefaults(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits(['change', 'field-input']);
|
||||||
|
|
||||||
const services = inject<Services>('services');
|
const services = inject<Services>('services');
|
||||||
|
|
||||||
const codeName = ref<string>('');
|
const codeName = ref<string>('');
|
||||||
const codeContent = ref<string>('');
|
const codeContent = ref<string>('');
|
||||||
const evalRes = ref(true);
|
const evalRes = ref(true);
|
||||||
|
|
||||||
|
provide('mForm', {
|
||||||
|
$emit: emit,
|
||||||
|
setField: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const tableModel = ref<{ params: CodeParam[] }>();
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
codeName.value = props.name;
|
codeName.value = props.name;
|
||||||
codeContent.value = props.content;
|
codeContent.value = props.content;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const initTableModel = () => {
|
||||||
|
const codeDsl = services?.codeBlockService.getCodeDslSync();
|
||||||
|
if (!codeDsl) return;
|
||||||
|
tableModel.value = {
|
||||||
|
params: codeDsl[props.id]?.params || [],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
initTableModel();
|
||||||
|
|
||||||
// 保存前钩子
|
// 保存前钩子
|
||||||
const beforeSave = (codeValue: string): boolean => {
|
const beforeSave = (codeValue: string): boolean => {
|
||||||
try {
|
try {
|
||||||
|
// eval检测js代码是否存在语法错误
|
||||||
// eslint-disable-next-line no-eval
|
// eslint-disable-next-line no-eval
|
||||||
eval(codeValue);
|
eval(codeValue);
|
||||||
return true;
|
return true;
|
||||||
@ -76,6 +121,7 @@ const saveCode = async (codeValue: string): Promise<void> => {
|
|||||||
await services?.codeBlockService.setCodeDslById(props.id, {
|
await services?.codeBlockService.setCodeDslById(props.id, {
|
||||||
name: codeName.value,
|
name: codeName.value,
|
||||||
content: codeValue,
|
content: codeValue,
|
||||||
|
params: tableModel.value?.params || [],
|
||||||
});
|
});
|
||||||
tMagicMessage.success('代码保存成功');
|
tMagicMessage.success('代码保存成功');
|
||||||
// 删除草稿
|
// 删除草稿
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
:config="tableConfig"
|
:config="tableConfig"
|
||||||
:model="model[name]"
|
:model="model[name]"
|
||||||
name="hookData"
|
name="hookData"
|
||||||
|
:enableToggleMode="false"
|
||||||
:prop="prop"
|
:prop="prop"
|
||||||
:size="size"
|
:size="size"
|
||||||
@change="changeHandler"
|
@change="changeHandler"
|
||||||
@ -17,9 +18,9 @@ import { computed, defineEmits, defineProps, inject, watch } from 'vue';
|
|||||||
import { isEmpty, map } from 'lodash-es';
|
import { isEmpty, map } from 'lodash-es';
|
||||||
|
|
||||||
import { FormItem, TableConfig } from '@tmagic/form';
|
import { FormItem, TableConfig } from '@tmagic/form';
|
||||||
import { HookType } from '@tmagic/schema';
|
import { HookType, Id } from '@tmagic/schema';
|
||||||
|
|
||||||
import { Services } from '../type';
|
import { CodeParamStatement, HookData, Services } from '../type';
|
||||||
const services = inject<Services>('services');
|
const services = inject<Services>('services');
|
||||||
const emit = defineEmits(['change']);
|
const emit = defineEmits(['change']);
|
||||||
|
|
||||||
@ -32,19 +33,22 @@ const props = defineProps<{
|
|||||||
name: string;
|
name: string;
|
||||||
size: 'mini' | 'small' | 'medium';
|
size: 'mini' | 'small' | 'medium';
|
||||||
}>();
|
}>();
|
||||||
|
const codeDsl = computed(() => services?.codeBlockService.getCodeDslSync());
|
||||||
|
|
||||||
const tableConfig = computed<FormItem>(() => {
|
const tableConfig = computed<FormItem>(() => {
|
||||||
const defaultConfig = {
|
const defaultConfig = {
|
||||||
dropSort: true,
|
dropSort: true,
|
||||||
|
enableFullscreen: false,
|
||||||
|
border: true,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
type: 'select',
|
type: 'select',
|
||||||
label: '代码块',
|
label: '代码块',
|
||||||
name: 'codeId',
|
name: 'codeId',
|
||||||
options: async () => {
|
width: '200px',
|
||||||
const codeDsl = await services?.codeBlockService.getCodeDsl();
|
options: () => {
|
||||||
if (codeDsl) {
|
if (codeDsl.value) {
|
||||||
return map(codeDsl, (value, key) => ({
|
return map(codeDsl.value, (value, key) => ({
|
||||||
text: `${value.name}(${key})`,
|
text: `${value.name}(${key})`,
|
||||||
label: `${value.name}(${key})`,
|
label: `${value.name}(${key})`,
|
||||||
value: key,
|
value: key,
|
||||||
@ -52,6 +56,16 @@ const tableConfig = computed<FormItem>(() => {
|
|||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
},
|
},
|
||||||
|
onChange: (formState: any, codeId: Id, { model }: any) => {
|
||||||
|
// 参数的items是根据函数生成的,当codeId变化后修正model的值,避免写入其他codeId的params
|
||||||
|
model.params = {};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'params',
|
||||||
|
label: '参数',
|
||||||
|
defaultValue: {},
|
||||||
|
itemsFunction: (row: HookData) => getParamsConfig(row.codeId),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@ -71,15 +85,6 @@ watch(
|
|||||||
hookType: HookType.CODE,
|
hookType: HookType.CODE,
|
||||||
hookData: [],
|
hookData: [],
|
||||||
};
|
};
|
||||||
} else if (Array.isArray(value) && value.length > 0) {
|
|
||||||
// 兼容旧的数据结构 ['code1','code2']
|
|
||||||
const hookData = value.map((codeId) => ({
|
|
||||||
codeId,
|
|
||||||
}));
|
|
||||||
props.model[props.name] = {
|
|
||||||
hookType: HookType.CODE,
|
|
||||||
hookData,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -90,4 +95,16 @@ watch(
|
|||||||
const changeHandler = async () => {
|
const changeHandler = async () => {
|
||||||
emit('change', props.model[props.name]);
|
emit('change', props.model[props.name]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getParamsConfig = (codeId: Id) => {
|
||||||
|
if (!codeDsl.value) return [];
|
||||||
|
const paramStatements = codeDsl.value[codeId]?.params;
|
||||||
|
if (isEmpty(paramStatements)) return [];
|
||||||
|
return paramStatements.map((paramState: CodeParamStatement) => ({
|
||||||
|
name: paramState.name,
|
||||||
|
text: paramState.name,
|
||||||
|
labelWidth: '100px',
|
||||||
|
type: 'text',
|
||||||
|
}));
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -145,7 +145,7 @@ const initList = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
[() => services?.codeBlockService.getCodeDsl(), () => services?.codeBlockService.refreshCombineInfo()],
|
[() => services?.codeBlockService.getCodeDslSync(), () => services?.codeBlockService.refreshCombineInfo()],
|
||||||
() => {
|
() => {
|
||||||
initList();
|
initList();
|
||||||
},
|
},
|
||||||
@ -165,6 +165,7 @@ const createCodeBlock = async () => {
|
|||||||
const codeConfig: CodeBlockContent = {
|
const codeConfig: CodeBlockContent = {
|
||||||
name: '代码块',
|
name: '代码块',
|
||||||
content: `() => {\n // place your code here\n}`,
|
content: `() => {\n // place your code here\n}`,
|
||||||
|
params: [],
|
||||||
};
|
};
|
||||||
await codeBlockService.setMode(CodeEditorMode.EDITOR);
|
await codeBlockService.setMode(CodeEditorMode.EDITOR);
|
||||||
const id = await codeBlockService.getUniqueId();
|
const id = await codeBlockService.getUniqueId();
|
||||||
|
@ -71,12 +71,17 @@ class CodeBlock extends BaseService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取活动的代码块dsl数据源(默认从dsl中的codeBlocks字段读取)
|
* 获取活动的代码块dsl数据源(默认从dsl中的codeBlocks字段读取)
|
||||||
|
* 方法要支持钩子添加扩展,会被重写为异步方法,因此这里显示写为异步以提醒调用者需以异步形式调用
|
||||||
* @param {boolean} forceRefresh 是否强制从活动dsl拉取刷新
|
* @param {boolean} forceRefresh 是否强制从活动dsl拉取刷新
|
||||||
* @returns {CodeBlockDSL | null}
|
* @returns {CodeBlockDSL | null}
|
||||||
*/
|
*/
|
||||||
public async getCodeDsl(forceRefresh = false): Promise<CodeBlockDSL | null> {
|
public async getCodeDsl(forceRefresh = false): Promise<CodeBlockDSL | null> {
|
||||||
|
return this.getCodeDslSync(forceRefresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCodeDslSync(forceRefresh = false): CodeBlockDSL | null {
|
||||||
if (!this.state.codeDsl || forceRefresh) {
|
if (!this.state.codeDsl || forceRefresh) {
|
||||||
this.state.codeDsl = await editorService.getCodeDsl();
|
this.state.codeDsl = editorService.getCodeDslSync();
|
||||||
}
|
}
|
||||||
return this.state.codeDsl;
|
return this.state.codeDsl;
|
||||||
}
|
}
|
||||||
|
@ -788,6 +788,12 @@ class Editor extends BaseService {
|
|||||||
return root.codeBlocks || null;
|
return root.codeBlocks || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getCodeDslSync(): CodeBlockDSL | null {
|
||||||
|
const root = this.get<MApp | null>('root');
|
||||||
|
if (!root) return null;
|
||||||
|
return root.codeBlocks || null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置代码块到dsl的codeBlocks字段
|
* 设置代码块到dsl的codeBlocks字段
|
||||||
* @param {CodeBlockDSL} codeDsl 代码DSL
|
* @param {CodeBlockDSL} codeDsl 代码DSL
|
||||||
|
@ -87,7 +87,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
.el-card__body {
|
.el-card__body {
|
||||||
height: calc(100% - 80px);
|
height: 100%;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,6 +127,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
margin-bottom: 10px;
|
||||||
.code-name-label {
|
.code-name-label {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
@ -148,7 +149,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 4px;
|
left: 4px;
|
||||||
width: calc(100% - 9px);
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@ -160,14 +161,17 @@
|
|||||||
.m-editor-wrapper {
|
.m-editor-wrapper {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
.m-editor-container {
|
.m-editor-container {
|
||||||
height: calc(100% - 70px);
|
height: calc(100% - 65px);
|
||||||
}
|
}
|
||||||
.m-editor-content-bottom {
|
.m-editor-content-bottom {
|
||||||
height: 40px;
|
height: 45px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
position: absolute;
|
||||||
|
right: 20px;
|
||||||
|
bottom: 0;
|
||||||
> button {
|
> button {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
|
@ -383,3 +383,10 @@ export enum CodeDeleteErrorType {
|
|||||||
|
|
||||||
// 代码块草稿localStorage key
|
// 代码块草稿localStorage key
|
||||||
export const CODE_DRAFT_STORAGE_KEY = 'magicCodeDraft';
|
export const CODE_DRAFT_STORAGE_KEY = 'magicCodeDraft';
|
||||||
|
|
||||||
|
export interface CodeParamStatement {
|
||||||
|
/** 参数名称 */
|
||||||
|
name: string;
|
||||||
|
/** 参数类型 */
|
||||||
|
type?: string;
|
||||||
|
}
|
||||||
|
@ -101,7 +101,7 @@
|
|||||||
labelWidth="0"
|
labelWidth="0"
|
||||||
:prop="getProp(scope.$index)"
|
:prop="getProp(scope.$index)"
|
||||||
:rules="column.rules"
|
:rules="column.rules"
|
||||||
:config="makeConfig(column)"
|
:config="makeConfig(column, scope.row)"
|
||||||
:model="scope.row"
|
:model="scope.row"
|
||||||
:size="size"
|
:size="size"
|
||||||
@change="$emit('change', model[modelName])"
|
@change="$emit('change', model[modelName])"
|
||||||
@ -432,8 +432,11 @@ const toggleRowSelection = (row: any, selected: boolean) => {
|
|||||||
tMagicTable.value?.toggleRowSelection.call(tMagicTable.value, row, selected);
|
tMagicTable.value?.toggleRowSelection.call(tMagicTable.value, row, selected);
|
||||||
};
|
};
|
||||||
|
|
||||||
const makeConfig = (config: ColumnConfig) => {
|
const makeConfig = (config: ColumnConfig, row: any) => {
|
||||||
const newConfig = cloneDeep(config);
|
const newConfig = cloneDeep(config);
|
||||||
|
if (typeof config.itemsFunction === 'function') {
|
||||||
|
newConfig.items = config.itemsFunction(row);
|
||||||
|
}
|
||||||
delete newConfig.display;
|
delete newConfig.display;
|
||||||
return newConfig;
|
return newConfig;
|
||||||
};
|
};
|
||||||
|
@ -81,10 +81,16 @@ export interface CodeBlockContent {
|
|||||||
name: string;
|
name: string;
|
||||||
/** 代码块内容 */
|
/** 代码块内容 */
|
||||||
content: any;
|
content: any;
|
||||||
|
/** 参数定义 */
|
||||||
|
params: CodeParam[] | [];
|
||||||
/** 扩展字段 */
|
/** 扩展字段 */
|
||||||
[propName: string]: any;
|
[propName: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CodeParam {
|
||||||
|
/** 参数名 */
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
export interface PastePosition {
|
export interface PastePosition {
|
||||||
left?: number;
|
left?: number;
|
||||||
top?: number;
|
top?: number;
|
||||||
|
@ -24,13 +24,21 @@ export default {
|
|||||||
code_5336: {
|
code_5336: {
|
||||||
name: 'getData',
|
name: 'getData',
|
||||||
// eslint-disable-next-line no-eval
|
// eslint-disable-next-line no-eval
|
||||||
content: eval(`(vm, params) => {\n console.log("this is getData function",vm,params)\n}`),
|
content: eval(`(vm, params) => {\n console.log("this is getData function",params)\n}`),
|
||||||
params: ['name', 'age'],
|
params: [
|
||||||
|
{
|
||||||
|
name: 'age',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'studentName',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
code_5316: {
|
code_5316: {
|
||||||
name: 'getList',
|
name: 'getList',
|
||||||
// eslint-disable-next-line no-eval
|
// eslint-disable-next-line no-eval
|
||||||
content: eval(`(vm) => {\n console.log("this is getList function")\n}`),
|
content: eval(`(vm) => {\n console.log("this is getList function")\n}`),
|
||||||
|
params: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
items: [
|
items: [
|
||||||
@ -63,20 +71,13 @@ export default {
|
|||||||
{
|
{
|
||||||
codeId: 'code_5336',
|
codeId: 'code_5336',
|
||||||
params: {
|
params: {
|
||||||
name: 'lisa',
|
studentName: 'lisa',
|
||||||
age: 12,
|
age: 14,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
codeId: 'code_5316',
|
codeId: 'code_5316',
|
||||||
},
|
params: {},
|
||||||
],
|
|
||||||
},
|
|
||||||
mounted: {
|
|
||||||
hookType: 'code',
|
|
||||||
hookData: [
|
|
||||||
{
|
|
||||||
codeId: 'code_5316',
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -104,14 +105,6 @@ export default {
|
|||||||
text: 'Tmagic editor 营销活动编辑器',
|
text: 'Tmagic editor 营销活动编辑器',
|
||||||
multiple: true,
|
multiple: true,
|
||||||
events: [],
|
events: [],
|
||||||
created: {
|
|
||||||
hookType: 'code',
|
|
||||||
hookData: [
|
|
||||||
{
|
|
||||||
codeId: 'code_5316',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'qrcode',
|
type: 'qrcode',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user