mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-06 03:57:56 +08:00
feat(editor,schema): 支持配置数据源mock
This commit is contained in:
parent
2b2a9c6706
commit
d4a8b89e6f
@ -7,6 +7,7 @@
|
|||||||
...config.options,
|
...config.options,
|
||||||
readOnly: disabled,
|
readOnly: disabled,
|
||||||
}"
|
}"
|
||||||
|
:parse="config.parse"
|
||||||
@save="save"
|
@save="save"
|
||||||
></MagicCodeEditor>
|
></MagicCodeEditor>
|
||||||
</template>
|
</template>
|
||||||
@ -20,14 +21,19 @@ defineOptions({
|
|||||||
name: 'MEditorCode',
|
name: 'MEditorCode',
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['change']);
|
const emit = defineEmits<{
|
||||||
|
change: [value: string | any];
|
||||||
|
}>();
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<
|
defineProps<
|
||||||
FieldProps<{
|
FieldProps<{
|
||||||
language?: string;
|
language?: string;
|
||||||
options?: Object;
|
options?: {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
height?: string;
|
height?: string;
|
||||||
|
parse?: boolean;
|
||||||
}>
|
}>
|
||||||
>(),
|
>(),
|
||||||
{
|
{
|
||||||
@ -35,7 +41,7 @@ const props = withDefaults(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const save = (v: string) => {
|
const save = (v: string | any) => {
|
||||||
props.model[props.name] = v;
|
props.model[props.name] = v;
|
||||||
emit('change', v);
|
emit('change', v);
|
||||||
};
|
};
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<MFormDrawer
|
<MFormDrawer
|
||||||
ref="addDialog"
|
ref="addDialog"
|
||||||
label-width="80px"
|
label-width="80px"
|
||||||
:title="filedTitle"
|
:title="fieldTitle"
|
||||||
:config="dataSourceFieldsConfig"
|
:config="dataSourceFieldsConfig"
|
||||||
:values="fieldValues"
|
:values="fieldValues"
|
||||||
:parentValues="model[name]"
|
:parentValues="model[name]"
|
||||||
@ -50,13 +50,13 @@ const services = inject<Services>('services');
|
|||||||
|
|
||||||
const addDialog = ref<InstanceType<typeof MFormDrawer>>();
|
const addDialog = ref<InstanceType<typeof MFormDrawer>>();
|
||||||
const fieldValues = ref<Record<string, any>>({});
|
const fieldValues = ref<Record<string, any>>({});
|
||||||
const filedTitle = ref('');
|
const fieldTitle = ref('');
|
||||||
|
|
||||||
const width = computed(() => globalThis.document.body.clientWidth - (services?.uiService.get('columnWidth').left || 0));
|
const width = computed(() => globalThis.document.body.clientWidth - (services?.uiService.get('columnWidth').left || 0));
|
||||||
|
|
||||||
const newHandler = () => {
|
const newHandler = () => {
|
||||||
fieldValues.value = {};
|
fieldValues.value = {};
|
||||||
filedTitle.value = '新增属性';
|
fieldTitle.value = '新增属性';
|
||||||
addDialog.value?.show();
|
addDialog.value?.show();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ const fieldColumns = [
|
|||||||
...row,
|
...row,
|
||||||
index,
|
index,
|
||||||
};
|
};
|
||||||
filedTitle.value = `编辑${row.title}`;
|
fieldTitle.value = `编辑${row.title}`;
|
||||||
addDialog.value?.show();
|
addDialog.value?.show();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
187
packages/editor/src/fields/DataSourceMocks.vue
Normal file
187
packages/editor/src/fields/DataSourceMocks.vue
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
<template>
|
||||||
|
<div class="m-editor-data-source-fields">
|
||||||
|
<MagicTable :data="model[name]" :columns="columns"></MagicTable>
|
||||||
|
|
||||||
|
<div class="m-editor-data-source-fields-footer">
|
||||||
|
<TMagicButton size="small" type="primary" :disabled="disabled" plain @click="newHandler()">添加</TMagicButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<MFormDrawer
|
||||||
|
ref="addDialog"
|
||||||
|
label-width="80px"
|
||||||
|
:title="drawerTitle"
|
||||||
|
:config="formConfig"
|
||||||
|
:values="formValues"
|
||||||
|
:parentValues="model[name]"
|
||||||
|
:disabled="disabled"
|
||||||
|
:width="width"
|
||||||
|
@submit="formChangeHandler"
|
||||||
|
></MFormDrawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, inject, ref } from 'vue';
|
||||||
|
|
||||||
|
import { TMagicButton, tMagicMessageBox, TMagicSwitch } from '@tmagic/design';
|
||||||
|
import { type FieldProps, type FormConfig, type FormState, MFormDrawer } from '@tmagic/form';
|
||||||
|
import type { MockSchema } from '@tmagic/schema';
|
||||||
|
import { MagicTable } from '@tmagic/table';
|
||||||
|
|
||||||
|
import { Services } from '@editor/type';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'MEditorDataSourceMocks',
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<
|
||||||
|
FieldProps<{
|
||||||
|
type: 'data-source-mocks';
|
||||||
|
}>
|
||||||
|
>(),
|
||||||
|
{
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits(['change']);
|
||||||
|
|
||||||
|
const services = inject<Services>('services');
|
||||||
|
const width = computed(() => globalThis.document.body.clientWidth - (services?.uiService.get('columnWidth').left || 0));
|
||||||
|
|
||||||
|
const addDialog = ref<InstanceType<typeof MFormDrawer>>();
|
||||||
|
const drawerTitle = ref('');
|
||||||
|
const formValues = ref<Record<string, any>>({});
|
||||||
|
|
||||||
|
const formConfig: FormConfig = [
|
||||||
|
{ name: 'index', type: 'hidden', filter: 'number', defaultValue: -1 },
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
text: '名称',
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入字段名称',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入名称',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
text: '描述',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'enable',
|
||||||
|
text: '启用',
|
||||||
|
type: 'switch',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'data',
|
||||||
|
text: 'mock数据',
|
||||||
|
type: 'vs-code',
|
||||||
|
language: 'json',
|
||||||
|
options: inject('codeOptions', {}),
|
||||||
|
defaultValue: '{}',
|
||||||
|
height: '400px',
|
||||||
|
onChange: (formState: FormState | undefined, v: string | any) => {
|
||||||
|
if (typeof v !== 'string') return v;
|
||||||
|
return JSON.parse(v);
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
validator: ({ value, callback }) => {
|
||||||
|
if (typeof value !== 'string') return callback();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 检测json是否存在语法错误
|
||||||
|
JSON.parse(value);
|
||||||
|
|
||||||
|
callback();
|
||||||
|
} catch (error: any) {
|
||||||
|
callback(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
label: '名称',
|
||||||
|
prop: 'title',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '描述',
|
||||||
|
prop: 'description',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '是否启用',
|
||||||
|
prop: 'enable',
|
||||||
|
type: 'component',
|
||||||
|
component: TMagicSwitch,
|
||||||
|
props: (row: MockSchema) => ({
|
||||||
|
modelValue: row.enable,
|
||||||
|
activeValue: true,
|
||||||
|
inactiveValue: false,
|
||||||
|
}),
|
||||||
|
listeners: (row: MockSchema, index: number) => ({
|
||||||
|
'update:modelValue': (v: boolean) => {
|
||||||
|
formChangeHandler({
|
||||||
|
...row,
|
||||||
|
enable: v,
|
||||||
|
index,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '操作',
|
||||||
|
fixed: 'right',
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
text: '编辑',
|
||||||
|
handler: (row: MockSchema, index: number) => {
|
||||||
|
formValues.value = {
|
||||||
|
...row,
|
||||||
|
index,
|
||||||
|
};
|
||||||
|
drawerTitle.value = `编辑${row.title}`;
|
||||||
|
addDialog.value?.show();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '删除',
|
||||||
|
buttonType: 'danger',
|
||||||
|
handler: async (row: MockSchema, index: number) => {
|
||||||
|
await tMagicMessageBox.confirm(`确定删除${row.title}?`, '提示');
|
||||||
|
props.model[props.name].splice(index, 1);
|
||||||
|
emit('change', props.model[props.name]);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const newHandler = () => {
|
||||||
|
formValues.value = {};
|
||||||
|
drawerTitle.value = '新增Mock';
|
||||||
|
addDialog.value?.show();
|
||||||
|
};
|
||||||
|
|
||||||
|
const formChangeHandler = ({ index, ...value }: Record<string, any>) => {
|
||||||
|
if (index > -1) {
|
||||||
|
props.model[props.name][index] = value;
|
||||||
|
} else {
|
||||||
|
props.model[props.name].push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
addDialog.value?.hide();
|
||||||
|
|
||||||
|
emit('change', props.model[props.name]);
|
||||||
|
};
|
||||||
|
</script>
|
@ -26,6 +26,7 @@ import DataSourceFieldSelect from './fields/DataSourceFieldSelect.vue';
|
|||||||
import DataSourceInput from './fields/DataSourceInput.vue';
|
import DataSourceInput from './fields/DataSourceInput.vue';
|
||||||
import DataSourceMethods from './fields/DataSourceMethods.vue';
|
import DataSourceMethods from './fields/DataSourceMethods.vue';
|
||||||
import DataSourceMethodSelect from './fields/DataSourceMethodSelect.vue';
|
import DataSourceMethodSelect from './fields/DataSourceMethodSelect.vue';
|
||||||
|
import DataSourceMocks from './fields/DataSourceMocks.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';
|
||||||
@ -59,6 +60,7 @@ 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 DataSourceMocks } from './fields/DataSourceMocks.vue';
|
||||||
export { default as DataSourceMethods } from './fields/DataSourceMethods.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';
|
||||||
@ -98,6 +100,7 @@ export default {
|
|||||||
app.component('m-fields-code-select-col', CodeSelectCol);
|
app.component('m-fields-code-select-col', CodeSelectCol);
|
||||||
app.component('m-fields-event-select', EventSelect);
|
app.component('m-fields-event-select', EventSelect);
|
||||||
app.component('m-fields-data-source-fields', DataSourceFields);
|
app.component('m-fields-data-source-fields', DataSourceFields);
|
||||||
|
app.component('m-fields-data-source-mocks', DataSourceMocks);
|
||||||
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);
|
||||||
|
@ -27,6 +27,8 @@ import serialize from 'serialize-javascript';
|
|||||||
|
|
||||||
import { TMagicButton } from '@tmagic/design';
|
import { TMagicButton } from '@tmagic/design';
|
||||||
|
|
||||||
|
import { getConfig } from '@editor/utils/config';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'MEditorCodeEditor',
|
name: 'MEditorCodeEditor',
|
||||||
});
|
});
|
||||||
@ -42,6 +44,7 @@ const props = withDefaults(
|
|||||||
};
|
};
|
||||||
height?: string;
|
height?: string;
|
||||||
autoSave?: boolean;
|
autoSave?: boolean;
|
||||||
|
parse?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
autoSave: true,
|
autoSave: true,
|
||||||
@ -49,6 +52,7 @@ const props = withDefaults(
|
|||||||
options: () => ({
|
options: () => ({
|
||||||
tabSize: 2,
|
tabSize: 2,
|
||||||
}),
|
}),
|
||||||
|
parse: false,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -57,7 +61,7 @@ const emit = defineEmits(['initd', 'save']);
|
|||||||
const toString = (v: string | any, language: string): string => {
|
const toString = (v: string | any, language: string): string => {
|
||||||
let value = '';
|
let value = '';
|
||||||
if (typeof v !== 'string') {
|
if (typeof v !== 'string') {
|
||||||
if (props.language.toLocaleLowerCase() === 'json') {
|
if (language === 'json') {
|
||||||
value = JSON.stringify(v, null, 2);
|
value = JSON.stringify(v, null, 2);
|
||||||
} else {
|
} else {
|
||||||
value = serialize(v, {
|
value = serialize(v, {
|
||||||
@ -74,6 +78,18 @@ const toString = (v: string | any, language: string): string => {
|
|||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const parse = (v: string | any, language: string): any => {
|
||||||
|
if (typeof v !== 'string') {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (language === 'json') {
|
||||||
|
return JSON.parse(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getConfig('parseDSL')(v);
|
||||||
|
};
|
||||||
|
|
||||||
let vsEditor: monaco.editor.IStandaloneCodeEditor | null = null;
|
let vsEditor: monaco.editor.IStandaloneCodeEditor | null = null;
|
||||||
let vsDiffEditor: monaco.editor.IStandaloneDiffEditor | null = null;
|
let vsDiffEditor: monaco.editor.IStandaloneDiffEditor | null = null;
|
||||||
|
|
||||||
@ -89,7 +105,7 @@ const resizeObserver = new globalThis.ResizeObserver(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const setEditorValue = (v: string | any, m: string | any) => {
|
const setEditorValue = (v: string | any, m: string | any) => {
|
||||||
values.value = toString(v, props.language);
|
values.value = toString(v, props.language.toLocaleLowerCase());
|
||||||
|
|
||||||
if (props.type === 'diff') {
|
if (props.type === 'diff') {
|
||||||
const originalModel = monaco.editor.createModel(values.value, 'text/javascript');
|
const originalModel = monaco.editor.createModel(values.value, 'text/javascript');
|
||||||
@ -135,7 +151,7 @@ const init = async () => {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const newValue = getEditorValue();
|
const newValue = getEditorValue();
|
||||||
values.value = newValue;
|
values.value = newValue;
|
||||||
emit('save', newValue);
|
emit('save', props.parse ? parse(newValue, props.language) : newValue);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -144,7 +160,7 @@ const init = async () => {
|
|||||||
const newValue = getEditorValue();
|
const newValue = getEditorValue();
|
||||||
if (values.value !== newValue) {
|
if (values.value !== newValue) {
|
||||||
values.value = newValue;
|
values.value = newValue;
|
||||||
emit('save', newValue);
|
emit('save', props.parse ? parse(newValue, props.language) : newValue);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,17 @@ const fillConfig = (config: FormConfig): FormConfig => [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'panel',
|
||||||
|
title: 'mock数据',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'mocks',
|
||||||
|
type: 'data-source-mocks',
|
||||||
|
defaultValue: () => [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const getFormConfig = (type: string, configs: Record<string, FormConfig>): FormConfig => {
|
export const getFormConfig = (type: string, configs: Record<string, FormConfig>): FormConfig => {
|
||||||
|
@ -200,6 +200,13 @@ export interface DataSchema {
|
|||||||
fields?: DataSchema[];
|
fields?: DataSchema[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MockSchema {
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
enable: boolean;
|
||||||
|
data: Record<string | number, any>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface DataSourceSchema {
|
export interface DataSourceSchema {
|
||||||
/** 数据源类型,根据类型来实例化;例如http则使用new HttpDataSource */
|
/** 数据源类型,根据类型来实例化;例如http则使用new HttpDataSource */
|
||||||
type: string;
|
type: string;
|
||||||
@ -213,6 +220,8 @@ export interface DataSourceSchema {
|
|||||||
fields: DataSchema[];
|
fields: DataSchema[];
|
||||||
/** 方法列表 */
|
/** 方法列表 */
|
||||||
methods: CodeBlockContent[];
|
methods: CodeBlockContent[];
|
||||||
|
/** mock数据 */
|
||||||
|
mocks?: MockSchema[];
|
||||||
/** 扩展字段 */
|
/** 扩展字段 */
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,11 @@
|
|||||||
:prop="config.prop"
|
:prop="config.prop"
|
||||||
>
|
>
|
||||||
<template v-slot="scope">
|
<template v-slot="scope">
|
||||||
<component :is="config.component" v-bind="componentProps(scope.row)"></component>
|
<component
|
||||||
|
:is="config.component"
|
||||||
|
v-bind="componentProps(scope.row, scope.$index)"
|
||||||
|
v-on="componentListeners(scope.row, scope.$index)"
|
||||||
|
></component>
|
||||||
</template>
|
</template>
|
||||||
</TMagicTableColumn>
|
</TMagicTableColumn>
|
||||||
</template>
|
</template>
|
||||||
@ -31,10 +35,17 @@ const props = withDefaults(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const componentProps = (row: any) => {
|
const componentProps = (row: any, index: number) => {
|
||||||
if (typeof props.config.props === 'function') {
|
if (typeof props.config.props === 'function') {
|
||||||
return props.config.props(row) || {};
|
return props.config.props(row, index) || {};
|
||||||
}
|
}
|
||||||
return props.config.props || {};
|
return props.config.props || {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const componentListeners = (row: any, index: number) => {
|
||||||
|
if (typeof props.config.listeners === 'function') {
|
||||||
|
return props.config.listeners(row, index) || {};
|
||||||
|
}
|
||||||
|
return props.config.listeners || {};
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -62,8 +62,10 @@ export type ColumnConfig = {
|
|||||||
expandContent?: (row: any, prop?: string) => string;
|
expandContent?: (row: any, prop?: string) => string;
|
||||||
/** 当type为expand时,展开为vue组件;当type为component时显示的组件 */
|
/** 当type为expand时,展开为vue组件;当type为component时显示的组件 */
|
||||||
component?: any;
|
component?: any;
|
||||||
/** 当type为expand时有效,展开的vue组件props */
|
/** 当type为expand时,展开的vue组件props;当type为component时显示的组件的props */
|
||||||
props?: any;
|
props?: any;
|
||||||
|
/** 当type为component时显示的组件的事件监听 */
|
||||||
|
listeners?: any;
|
||||||
/** 当type为tip时有效,显示文案 */
|
/** 当type为tip时有效,显示文案 */
|
||||||
buttonText?: string;
|
buttonText?: string;
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user