mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2026-04-23 10:18:55 +00:00
feat(editor,data-source): 数据源支持内置"设置数据"方法
支持通过事件调用数据源的 setData 方法,可以选择数据源字段并根据字段类型动态设置数据; 重构 CodeParams 参数配置支持动态类型; DataSourceFieldSelect 支持指定数据源ID; 常量抽取到 utils/const.ts Made-with: Cursor
This commit is contained in:
parent
172a7a1c92
commit
f583c7daec
@ -297,7 +297,7 @@ class App extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (typeof dataSource[methodName] === 'function') {
|
if (typeof dataSource[methodName] === 'function') {
|
||||||
return await dataSource[methodName]();
|
return await dataSource[methodName]({ params });
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (this.errorHandler) {
|
if (this.errorHandler) {
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import EventEmitter from 'events';
|
|||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
import type { CodeBlockContent, DataSchema, DataSourceSchema, default as TMagicApp } from '@tmagic/core';
|
import type { CodeBlockContent, DataSchema, DataSourceSchema, default as TMagicApp } from '@tmagic/core';
|
||||||
import { getDefaultValueFromFields } from '@tmagic/core';
|
import { DATA_SOURCE_SET_DATA_METHOD_NAME, getDefaultValueFromFields } from '@tmagic/core';
|
||||||
|
|
||||||
import { ObservedData } from '@data-source/observed-data/ObservedData';
|
import { ObservedData } from '@data-source/observed-data/ObservedData';
|
||||||
import { SimpleObservedData } from '@data-source/observed-data/SimpleObservedData';
|
import { SimpleObservedData } from '@data-source/observed-data/SimpleObservedData';
|
||||||
@ -51,6 +51,7 @@ export default class DataSource<T extends DataSourceSchema = DataSourceSchema> e
|
|||||||
super();
|
super();
|
||||||
|
|
||||||
this.#id = options.schema.id;
|
this.#id = options.schema.id;
|
||||||
|
this.#type = options.schema.type;
|
||||||
this.#schema = options.schema;
|
this.#schema = options.schema;
|
||||||
|
|
||||||
this.app = options.app;
|
this.app = options.app;
|
||||||
@ -58,6 +59,11 @@ export default class DataSource<T extends DataSourceSchema = DataSourceSchema> e
|
|||||||
this.setFields(options.schema.fields);
|
this.setFields(options.schema.fields);
|
||||||
this.setMethods(options.schema.methods || []);
|
this.setMethods(options.schema.methods || []);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
this[DATA_SOURCE_SET_DATA_METHOD_NAME] = ({ params }: { params: { field?: string[]; data: any } }) => {
|
||||||
|
this.setData(params.data, params.field?.join('.'));
|
||||||
|
};
|
||||||
|
|
||||||
let data = options.initialData;
|
let data = options.initialData;
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
const ObservedDataClass = options.ObservedDataClass || SimpleObservedData;
|
const ObservedDataClass = options.ObservedDataClass || SimpleObservedData;
|
||||||
|
|||||||
@ -46,13 +46,29 @@ const getFormConfig = (items: FormItemConfig[] = []) => [
|
|||||||
|
|
||||||
const codeParamsConfig = computed(() =>
|
const codeParamsConfig = computed(() =>
|
||||||
getFormConfig(
|
getFormConfig(
|
||||||
props.paramsConfig.map(({ name, text, extra, ...config }) => ({
|
props.paramsConfig.map(({ name, text, extra, ...config }) => {
|
||||||
type: 'data-source-field-select',
|
let { type } = config;
|
||||||
|
if (typeof type === 'function') {
|
||||||
|
type = type(undefined, {
|
||||||
|
model: props.model[props.name],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (type && ['data-source-field-select', 'vs-code'].includes(type)) {
|
||||||
|
return {
|
||||||
|
...config,
|
||||||
name,
|
name,
|
||||||
text,
|
text,
|
||||||
extra,
|
extra,
|
||||||
fieldConfig: config as FormItemConfig,
|
};
|
||||||
})),
|
}
|
||||||
|
return {
|
||||||
|
type: 'data-source-field-select' as const,
|
||||||
|
name,
|
||||||
|
text,
|
||||||
|
extra,
|
||||||
|
fieldConfig: config,
|
||||||
|
};
|
||||||
|
}) as FormItemConfig[],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="m-editor-data-source-field-select">
|
<div class="m-editor-data-source-field-select">
|
||||||
<template v-if="checkStrictly">
|
<template v-if="dataSourceId">
|
||||||
|
<TMagicCascader
|
||||||
|
:model-value="selectFieldsId"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
:size="size"
|
||||||
|
:disabled="disabled"
|
||||||
|
:options="fieldsOptions"
|
||||||
|
:props="{
|
||||||
|
checkStrictly,
|
||||||
|
}"
|
||||||
|
@change="fieldChangeHandler"
|
||||||
|
></TMagicCascader>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else-if="checkStrictly">
|
||||||
<TMagicSelect
|
<TMagicSelect
|
||||||
:model-value="selectDataSourceId"
|
:model-value="selectDataSourceId"
|
||||||
clearable
|
clearable
|
||||||
@ -92,6 +107,8 @@ const props = defineProps<{
|
|||||||
dataSourceFieldType?: DataSourceFieldType[];
|
dataSourceFieldType?: DataSourceFieldType[];
|
||||||
/** 是否可以编辑数据源,disable表示的是是否可以选择数据源 */
|
/** 是否可以编辑数据源,disable表示的是是否可以选择数据源 */
|
||||||
notEditable?: boolean | FilterFunction;
|
notEditable?: boolean | FilterFunction;
|
||||||
|
/** 指定数据源ID,限定只能选择该数据源的字段 */
|
||||||
|
dataSourceId?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@ -106,7 +123,12 @@ const { dataSourceService, uiService } = useServices();
|
|||||||
const mForm = inject<FormState | undefined>('mForm');
|
const mForm = inject<FormState | undefined>('mForm');
|
||||||
const eventBus = inject<EventBus>('eventBus');
|
const eventBus = inject<EventBus>('eventBus');
|
||||||
|
|
||||||
const dataSources = computed(() => dataSourceService.get('dataSources') || []);
|
const allDataSources = computed(() => dataSourceService.get('dataSources') || []);
|
||||||
|
|
||||||
|
const dataSources = computed(() => {
|
||||||
|
if (!props.dataSourceId) return allDataSources.value;
|
||||||
|
return allDataSources.value.filter((ds) => ds.id === props.dataSourceId);
|
||||||
|
});
|
||||||
|
|
||||||
const valueIsKey = computed(() => props.value === 'key');
|
const valueIsKey = computed(() => props.value === 'key');
|
||||||
const notEditable = computed(() => filterFunction(mForm, props.notEditable, props));
|
const notEditable = computed(() => filterFunction(mForm, props.notEditable, props));
|
||||||
@ -125,7 +147,13 @@ const selectFieldsId = ref<string[]>([]);
|
|||||||
watch(
|
watch(
|
||||||
modelValue,
|
modelValue,
|
||||||
(value) => {
|
(value) => {
|
||||||
if (Array.isArray(value)) {
|
if (props.dataSourceId) {
|
||||||
|
const dsIdValue = valueIsKey.value
|
||||||
|
? props.dataSourceId
|
||||||
|
: `${DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX}${props.dataSourceId}`;
|
||||||
|
selectDataSourceId.value = dsIdValue;
|
||||||
|
selectFieldsId.value = Array.isArray(value) ? value : [];
|
||||||
|
} else if (Array.isArray(value) && value.length) {
|
||||||
const [dsId, ...fields] = value;
|
const [dsId, ...fields] = value;
|
||||||
selectDataSourceId.value = dsId;
|
selectDataSourceId.value = dsId;
|
||||||
selectFieldsId.value = fields;
|
selectFieldsId.value = fields;
|
||||||
@ -140,7 +168,7 @@ watch(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const fieldsOptions = computed(() => {
|
const fieldsOptions = computed(() => {
|
||||||
const ds = dataSources.value.find((ds) => ds.id === removeDataSourceFieldPrefix(selectDataSourceId.value));
|
const ds = allDataSources.value.find((ds) => ds.id === removeDataSourceFieldPrefix(selectDataSourceId.value));
|
||||||
|
|
||||||
if (!ds) return [];
|
if (!ds) return [];
|
||||||
|
|
||||||
@ -163,8 +191,13 @@ const dsChangeHandler = (v: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const fieldChangeHandler = (v: string[] = []) => {
|
const fieldChangeHandler = (v: string[] = []) => {
|
||||||
|
if (props.dataSourceId) {
|
||||||
|
modelValue.value = v;
|
||||||
|
emit('change', v);
|
||||||
|
} else {
|
||||||
modelValue.value = [selectDataSourceId.value, ...v];
|
modelValue.value = [selectDataSourceId.value, ...v];
|
||||||
emit('change', modelValue.value);
|
emit('change', modelValue.value);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onChangeHandler = (v: string[] = []) => {
|
const onChangeHandler = (v: string[] = []) => {
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
:value="config.value"
|
:value="config.value"
|
||||||
:checkStrictly="checkStrictly"
|
:checkStrictly="checkStrictly"
|
||||||
:dataSourceFieldType="config.dataSourceFieldType"
|
:dataSourceFieldType="config.dataSourceFieldType"
|
||||||
|
:dataSourceId="config.dataSourceId"
|
||||||
@change="onChangeHandler"
|
@change="onChangeHandler"
|
||||||
></FieldSelect>
|
></FieldSelect>
|
||||||
|
|
||||||
|
|||||||
@ -52,12 +52,14 @@ import {
|
|||||||
type FormState,
|
type FormState,
|
||||||
MCascader,
|
MCascader,
|
||||||
} from '@tmagic/form';
|
} from '@tmagic/form';
|
||||||
|
import { DATA_SOURCE_SET_DATA_METHOD_NAME } from '@tmagic/utils';
|
||||||
|
|
||||||
import CodeParams from '@editor/components/CodeParams.vue';
|
import CodeParams from '@editor/components/CodeParams.vue';
|
||||||
import MIcon from '@editor/components/Icon.vue';
|
import MIcon from '@editor/components/Icon.vue';
|
||||||
import { useServices } from '@editor/hooks/use-services';
|
import { useServices } from '@editor/hooks/use-services';
|
||||||
import type { CodeParamStatement, EventBus } from '@editor/type';
|
import type { CodeParamStatement, EventBus } from '@editor/type';
|
||||||
import { SideItemKey } from '@editor/type';
|
import { SideItemKey } from '@editor/type';
|
||||||
|
import { getFieldType } from '@editor/utils';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'MFieldsDataSourceMethodSelect',
|
name: 'MFieldsDataSourceMethodSelect',
|
||||||
@ -92,6 +94,42 @@ const isCustomMethod = computed(() => {
|
|||||||
const getParamItemsConfig = ([dataSourceId, methodName]: [Id, string] = ['', '']): CodeParamStatement[] => {
|
const getParamItemsConfig = ([dataSourceId, methodName]: [Id, string] = ['', '']): CodeParamStatement[] => {
|
||||||
if (!dataSourceId) return [];
|
if (!dataSourceId) return [];
|
||||||
|
|
||||||
|
if (methodName === DATA_SOURCE_SET_DATA_METHOD_NAME) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'field',
|
||||||
|
text: '字段',
|
||||||
|
type: 'data-source-field-select',
|
||||||
|
dataSourceId,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'data',
|
||||||
|
text: '数据',
|
||||||
|
type: (_formState, { model }) => {
|
||||||
|
const fieldType = getFieldType(dataSourceService.getDataSourceById(`${dataSourceId}`), model.field);
|
||||||
|
|
||||||
|
let type = 'vs-code';
|
||||||
|
|
||||||
|
if (fieldType === 'number') {
|
||||||
|
type = 'number';
|
||||||
|
} else if (fieldType === 'string') {
|
||||||
|
type = 'text';
|
||||||
|
} else if (fieldType === 'boolean') {
|
||||||
|
type = 'switch';
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
},
|
||||||
|
language: 'javascript',
|
||||||
|
options: inject('codeOptions', {}),
|
||||||
|
autosize: {
|
||||||
|
minRows: 1,
|
||||||
|
maxRows: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
const paramStatements = dataSources.value
|
const paramStatements = dataSources.value
|
||||||
?.find((item) => item.id === dataSourceId)
|
?.find((item) => item.id === dataSourceId)
|
||||||
?.methods?.find((item) => item.name === methodName)?.params;
|
?.methods?.find((item) => item.name === methodName)?.params;
|
||||||
@ -114,6 +152,10 @@ const methodsOptions = computed(
|
|||||||
label: ds.title || ds.id,
|
label: ds.title || ds.id,
|
||||||
value: ds.id,
|
value: ds.id,
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
label: '设置数据',
|
||||||
|
value: DATA_SOURCE_SET_DATA_METHOD_NAME,
|
||||||
|
},
|
||||||
...(dataSourceService?.getFormMethod(ds.type) || []),
|
...(dataSourceService?.getFormMethod(ds.type) || []),
|
||||||
...(ds.methods || []).map((method) => ({
|
...(ds.methods || []).map((method) => ({
|
||||||
label: method.name,
|
label: method.name,
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import type { default as Sortable, Options, SortableEvent } from 'sortablejs';
|
|||||||
import type { PascalCasedProperties, Writable } from 'type-fest';
|
import type { PascalCasedProperties, Writable } from 'type-fest';
|
||||||
|
|
||||||
import type { CodeBlockContent, CodeBlockDSL, Id, MApp, MContainer, MNode, MPage, MPageFragment } from '@tmagic/core';
|
import type { CodeBlockContent, CodeBlockDSL, Id, MApp, MContainer, MNode, MPage, MPageFragment } from '@tmagic/core';
|
||||||
import type { ChangeRecord, FormConfig, TableColumnConfig } from '@tmagic/form';
|
import type { ChangeRecord, FormConfig, TableColumnConfig, TypeFunction } from '@tmagic/form';
|
||||||
import type StageCore from '@tmagic/stage';
|
import type StageCore from '@tmagic/stage';
|
||||||
import type {
|
import type {
|
||||||
ContainerHighlightType,
|
ContainerHighlightType,
|
||||||
@ -541,7 +541,7 @@ export interface CodeParamStatement {
|
|||||||
/** 参数名称 */
|
/** 参数名称 */
|
||||||
name: string;
|
name: string;
|
||||||
/** 参数类型 */
|
/** 参数类型 */
|
||||||
type?: string;
|
type?: string | TypeFunction<string>;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -29,6 +29,8 @@ export interface DataSourceFieldSelectConfig<T = never> extends FormItem {
|
|||||||
fieldConfig?: FormItemConfig<T>;
|
fieldConfig?: FormItemConfig<T>;
|
||||||
/** 是否可以编辑数据源,disable表示的是是否可以选择数据源 */
|
/** 是否可以编辑数据源,disable表示的是是否可以选择数据源 */
|
||||||
notEditable?: boolean | FilterFunction;
|
notEditable?: boolean | FilterFunction;
|
||||||
|
|
||||||
|
dataSourceId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CodeConfig extends FormItem {
|
export interface CodeConfig extends FormItem {
|
||||||
|
|||||||
5
packages/utils/src/const.ts
Normal file
5
packages/utils/src/const.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export const DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX = 'ds-field::';
|
||||||
|
|
||||||
|
export const DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX = 'ds-field-changed';
|
||||||
|
|
||||||
|
export const DATA_SOURCE_SET_DATA_METHOD_NAME = 'setDataFromEvent';
|
||||||
@ -35,8 +35,12 @@ import { NodeType } from '@tmagic/schema';
|
|||||||
|
|
||||||
import type { EditorNodeInfo } from '@editor/type';
|
import type { EditorNodeInfo } from '@editor/type';
|
||||||
|
|
||||||
|
import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX } from './const';
|
||||||
|
|
||||||
export * from './dom';
|
export * from './dom';
|
||||||
|
|
||||||
|
export * from './const';
|
||||||
|
|
||||||
// for typeof global checks without @types/node
|
// for typeof global checks without @types/node
|
||||||
declare let global: {};
|
declare let global: {};
|
||||||
|
|
||||||
@ -542,10 +546,6 @@ export const getDefaultValueFromFields = (fields: DataSchema[]) => {
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX = 'ds-field::';
|
|
||||||
|
|
||||||
export const DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX = 'ds-field-changed';
|
|
||||||
|
|
||||||
export const getKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;
|
export const getKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;
|
||||||
|
|
||||||
export const calculatePercentage = (value: number, percentageStr: string) => {
|
export const calculatePercentage = (value: number, percentageStr: string) => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user