roymondchen f583c7daec feat(editor,data-source): 数据源支持内置"设置数据"方法
支持通过事件调用数据源的 setData 方法,可以选择数据源字段并根据字段类型动态设置数据;
重构 CodeParams 参数配置支持动态类型; DataSourceFieldSelect 支持指定数据源ID;
常量抽取到 utils/const.ts

Made-with: Cursor
2026-04-07 18:25:35 +08:00

216 lines
6.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="m-editor-data-source-field-select">
<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
:model-value="selectDataSourceId"
clearable
filterable
:size="size"
:disabled="disabled"
@change="dsChangeHandler"
>
<component
v-for="option in dataSourcesOptions"
class="tmagic-design-option"
:key="option.value"
:is="optionComponent?.component || 'el-option'"
v-bind="
optionComponent?.props({
label: option.text,
value: option.value,
disabled: option.disabled,
}) || {
label: option.text,
value: option.value,
disabled: option.disabled,
}
"
>
</component>
</TMagicSelect>
<TMagicCascader
:model-value="selectFieldsId"
clearable
filterable
:size="size"
:disabled="disabled"
:options="fieldsOptions"
:props="{
checkStrictly,
}"
@change="fieldChangeHandler"
></TMagicCascader>
</template>
<TMagicCascader
v-else
clearable
filterable
:model-value="modelValue"
:disabled="disabled"
:size="size"
:options="cascaderOptions"
:props="{
checkStrictly,
}"
@change="onChangeHandler"
></TMagicCascader>
<TMagicTooltip v-if="selectDataSourceId && hasDataSourceSidePanel" :content="notEditable ? '查看' : '编辑'">
<TMagicButton class="m-fields-select-action-button" :size="size" @click="editHandler(selectDataSourceId)"
><MIcon :icon="!notEditable ? Edit : View"></MIcon
></TMagicButton>
</TMagicTooltip>
</div>
</template>
<script lang="ts" setup>
import { computed, inject, ref, watch } from 'vue';
import { Edit, View } from '@element-plus/icons-vue';
import type { DataSourceFieldType } from '@tmagic/core';
import { getDesignConfig, TMagicButton, TMagicCascader, TMagicSelect, TMagicTooltip } from '@tmagic/design';
import { type FilterFunction, filterFunction, type FormState, type SelectOption } from '@tmagic/form';
import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX, removeDataSourceFieldPrefix } from '@tmagic/utils';
import MIcon from '@editor/components/Icon.vue';
import { useServices } from '@editor/hooks/use-services';
import { type EventBus, SideItemKey } from '@editor/type';
import { getCascaderOptionsFromFields } from '@editor/utils';
const props = defineProps<{
/**
* 是否要编译成数据源的data。
* key: 不编译就是要数据源id和field name;
* value: 要编译数据源data[`${filed}`]
* */
value?: 'key' | 'value';
disabled?: boolean;
checkStrictly?: boolean;
size?: 'large' | 'default' | 'small';
dataSourceFieldType?: DataSourceFieldType[];
/** 是否可以编辑数据源disable表示的是是否可以选择数据源 */
notEditable?: boolean | FilterFunction;
/** 指定数据源ID限定只能选择该数据源的字段 */
dataSourceId?: string;
}>();
const emit = defineEmits<{
change: [v: string[]];
}>();
const modelValue = defineModel<string[] | any>('modelValue', { default: [] });
const optionComponent = getDesignConfig('components')?.option;
const { dataSourceService, uiService } = useServices();
const mForm = inject<FormState | undefined>('mForm');
const eventBus = inject<EventBus>('eventBus');
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 notEditable = computed(() => filterFunction(mForm, props.notEditable, props));
const dataSourcesOptions = computed<SelectOption[]>(() =>
dataSources.value.map((ds) => ({
text: ds.title || ds.id,
value: valueIsKey.value ? ds.id : `${DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX}${ds.id}`,
})),
);
const selectDataSourceId = ref('');
const selectFieldsId = ref<string[]>([]);
watch(
modelValue,
(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;
selectDataSourceId.value = dsId;
selectFieldsId.value = fields;
} else {
selectDataSourceId.value = '';
selectFieldsId.value = [];
}
},
{
immediate: true,
},
);
const fieldsOptions = computed(() => {
const ds = allDataSources.value.find((ds) => ds.id === removeDataSourceFieldPrefix(selectDataSourceId.value));
if (!ds) return [];
return getCascaderOptionsFromFields(ds.fields, props.dataSourceFieldType);
});
const cascaderOptions = computed(() => {
const options =
dataSources.value?.map((ds) => ({
label: ds.title || ds.id,
value: valueIsKey.value ? ds.id : `${DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX}${ds.id}`,
children: getCascaderOptionsFromFields(ds.fields, props.dataSourceFieldType),
})) || [];
return options.filter((option) => option.children.length);
});
const dsChangeHandler = (v: string) => {
modelValue.value = [v];
emit('change', modelValue.value);
};
const fieldChangeHandler = (v: string[] = []) => {
if (props.dataSourceId) {
modelValue.value = v;
emit('change', v);
} else {
modelValue.value = [selectDataSourceId.value, ...v];
emit('change', modelValue.value);
}
};
const onChangeHandler = (v: string[] = []) => {
modelValue.value = v;
emit('change', v);
};
const hasDataSourceSidePanel = computed(() =>
uiService.get('sideBarItems').find((item) => item.$key === SideItemKey.DATA_SOURCE),
);
const editHandler = (id: string) => {
eventBus?.emit('edit-data-source', removeDataSourceFieldPrefix(id));
};
</script>