Skip to content

Commit

Permalink
feat(editor): 优化数据源字段选择器交互
Browse files Browse the repository at this point in the history
  • Loading branch information
roymondchen committed Jun 27, 2024
1 parent 77f13fa commit 0ffc223
Show file tree
Hide file tree
Showing 10 changed files with 273 additions and 78 deletions.
152 changes: 152 additions & 0 deletions packages/editor/src/fields/DataSourceFieldSelect/FieldSelect.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<template>
<div class="m-editor-data-source-field-select">
<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>
<TMagicButton
v-if="selectDataSourceId && hasDataSourceSidePanel"
class="m-fields-select-action-button"
:size="size"
@click="editHandler(selectDataSourceId)"
><MIcon :icon="!notEditable ? Edit : View"></MIcon
></TMagicButton>
</div>
</template>
<script lang="ts" setup>
import { computed, inject, ref, watch } from 'vue';
import { Edit, View } from '@element-plus/icons-vue';
import { getConfig as getDesignConfig, TMagicButton, TMagicCascader, TMagicSelect } from '@tmagic/design';
import { type FilterFunction, filterFunction, type FormState, type SelectOption } from '@tmagic/form';
import { DataSourceFieldType } from '@tmagic/schema';
import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX } from '@tmagic/utils';
import MIcon from '@editor/components/Icon.vue';
import { type EventBus, type Services, SideItemKey } from '@editor/type';
import { getCascaderOptionsFromFields, removeDataSourceFieldPrefix } 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;
}>();
const emit = defineEmits<{
change: [v: string[]];
}>();
const modelValue = defineModel<string[] | any>('modelValue', { default: [] });
const optionComponent = getDesignConfig('components')?.option;
const services = inject<Services>('services');
const mForm = inject<FormState | undefined>('mForm');
const eventBus = inject<EventBus>('eventBus');
const dataSources = computed(() => services?.dataSourceService.get('dataSources') || []);
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 (Array.isArray(value)) {
const [dsId, ...fields] = value;
selectDataSourceId.value = dsId;
selectFieldsId.value = fields;
} else {
selectDataSourceId.value = '';
selectFieldsId.value = [];
}
},
{
immediate: true,
},
);
const fieldsOptions = computed(() => {
const ds = dataSources.value.find((ds) => ds.id === removeDataSourceFieldPrefix(selectDataSourceId.value));
if (!ds) return [];
return getCascaderOptionsFromFields(ds.fields, props.dataSourceFieldType);
});
const dsChangeHandler = (v: string) => {
modelValue.value = [v];
emit('change', modelValue.value);
};
const fieldChangeHandler = (v: string[]) => {
modelValue.value = [selectDataSourceId.value, ...v];
emit('change', modelValue.value);
};
const hasDataSourceSidePanel = computed(() =>
(services?.uiService.get('sideBarItems') || []).find((item) => item.$key === SideItemKey.DATA_SOURCE),
);
const editHandler = (id: string) => {
eventBus?.emit('edit-data-source', removeDataSourceFieldPrefix(id));
};
</script>
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
<template>
<div class="m-fields-data-source-field-select">
<FieldSelect
v-if="showDataSourceFieldSelect || !config.fieldConfig"
:model-value="model[name]"
:disabled="disabled"
:size="size"
:value="config.value"
:checkStrictly="checkStrictly"
:dataSourceFieldType="config.dataSourceFieldType"
@change="onChangeHandler"
></FieldSelect>

<component
v-else
:is="tagName"
:config="showDataSourceFieldSelect || !config.fieldConfig ? cascaderConfig : config.fieldConfig"
:config="config.fieldConfig"
:model="model"
:name="name"
:disabled="disabled"
Expand All @@ -14,14 +26,6 @@
@change="onChangeHandler"
></component>

<TMagicButton
v-if="(showDataSourceFieldSelect || !config.fieldConfig) && selectedDataSourceId && hasDataSourceSidePanel"
class="m-fields-select-action-button"
:size="size"
@click="editHandler(selectedDataSourceId)"
><MIcon :icon="!notEditable ? Edit : View"></MIcon
></TMagicButton>

<TMagicButton
v-if="config.fieldConfig"
style="margin-left: 5px"
Expand All @@ -35,66 +39,29 @@

<script setup lang="ts">
import { computed, inject, ref, resolveComponent, watch } from 'vue';
import { Coin, Edit, View } from '@element-plus/icons-vue';
import { Coin } from '@element-plus/icons-vue';
import { TMagicButton } from '@tmagic/design';
import type { CascaderConfig, FieldProps, FormState } from '@tmagic/form';
import { filterFunction, MCascader } from '@tmagic/form';
import { TMagicButton, tMagicMessage } from '@tmagic/design';
import type { FieldProps, FormState } from '@tmagic/form';
import { DataSchema } from '@tmagic/schema';
import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX } from '@tmagic/utils';
import MIcon from '@editor/components/Icon.vue';
import type { DataSourceFieldSelectConfig, EventBus, Services } from '@editor/type';
import { SideItemKey } from '@editor/type';
import { getCascaderOptionsFromFields } from '@editor/utils';
import type { DataSourceFieldSelectConfig, Services } from '@editor/type';
import { removeDataSourceFieldPrefix } from '@editor/utils';
import FieldSelect from './FieldSelect.vue';
defineOptions({
name: 'MFieldsDataSourceFieldSelect',
});
const services = inject<Services>('services');
const eventBus = inject<EventBus>('eventBus');
const emit = defineEmits(['change']);
const props = withDefaults(defineProps<FieldProps<DataSourceFieldSelectConfig>>(), {
disabled: false,
});
const notEditable = computed(() => filterFunction(mForm, props.config.notEditable, props));
const hasDataSourceSidePanel = computed(() =>
(services?.uiService.get('sideBarItems') || []).find((item) => item.$key === SideItemKey.DATA_SOURCE),
);
const selectedDataSourceId = computed(() => {
const value = props.model[props.name];
if (!Array.isArray(value) || !value.length) {
return '';
}
return value[0].replace(DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX, '');
});
const dataSources = computed(() => services?.dataSourceService.get('dataSources'));
const cascaderConfig = computed<CascaderConfig>(() => {
const valueIsKey = props.config.value === 'key';
return {
type: 'cascader',
checkStrictly: props.config.checkStrictly ?? !valueIsKey,
popperClass: 'm-editor-data-source-field-select-popper',
options: () => {
const options =
dataSources.value?.map((ds) => ({
label: ds.title || ds.id,
value: valueIsKey ? ds.id : `${DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX}${ds.id}`,
children: getCascaderOptionsFromFields(ds.fields, props.config.dataSourceFieldType),
})) || [];
return options.filter((option) => option.children.length);
},
};
});
const showDataSourceFieldSelect = ref(false);
watch(
Expand All @@ -115,8 +82,11 @@ watch(
},
);
const services = inject<Services>('services');
const mForm = inject<FormState | undefined>('mForm');
const dataSources = computed(() => services?.dataSourceService.get('dataSources') || []);
const type = computed((): string => {
let type = props.config.fieldConfig?.type;
if (typeof type === 'function') {
Expand All @@ -130,20 +100,60 @@ const type = computed((): string => {
});
const tagName = computed(() => {
if (showDataSourceFieldSelect.value || !props.config.fieldConfig) {
return MCascader;
}
const component = resolveComponent(`m-${props.config.items ? 'form' : 'fields'}-${type.value}`);
if (typeof component !== 'string') return component;
return 'm-fields-text';
});
const onChangeHandler = (value: any) => {
emit('change', value);
};
const checkStrictly = computed(() => {
let value: boolean | undefined;
if (typeof props.config.checkStrictly !== 'function') {
value = props.config.checkStrictly;
} else {
const dsId = removeDataSourceFieldPrefix(props.model[0]);
const dataSource = dataSources.value.find((ds) => ds.id === dsId);
value = props.config.checkStrictly(mForm, {
values: mForm?.initValues || {},
model: props.model,
parent: mForm?.parentValues || {},
formValue: mForm?.values || props.model,
prop: props.prop,
config: props.config,
dataSource,
});
}
return value ?? props.config.value === 'key';
});
const onChangeHandler = (value: string[]) => {
const [dsId, ...keys] = value;
const dataSource = dataSources.value.find((ds) => ds.id === removeDataSourceFieldPrefix(dsId));
const editHandler = (id: string) => {
eventBus?.emit('edit-data-source', id);
let fields = dataSource?.fields || [];
let field: DataSchema | undefined;
(keys || []).forEach((key) => {
field = fields.find((f) => f.name === key);
fields = field?.fields || [];
});
const dataSourceFieldType = props.config.dataSourceFieldType || ['any'];
if (!dataSourceFieldType.length) {
dataSourceFieldType.push('any');
}
if (
!dsId ||
!keys.length ||
(field?.type &&
(field.type === 'any' || dataSourceFieldType.includes('any') || dataSourceFieldType.includes(field.type)))
) {
emit('change', value);
} else {
tMagicMessage.error(`请选择类型为${dataSourceFieldType.join('')}的字段`);
emit('change', [dsId]);
}
};
</script>
4 changes: 2 additions & 2 deletions packages/editor/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import CodeLink from './fields/CodeLink.vue';
import CodeSelect from './fields/CodeSelect.vue';
import CodeSelectCol from './fields/CodeSelectCol.vue';
import DataSourceFields from './fields/DataSourceFields.vue';
import DataSourceFieldSelect from './fields/DataSourceFieldSelect.vue';
import DataSourceFieldSelect from './fields/DataSourceFieldSelect/Index.vue';
import DataSourceInput from './fields/DataSourceInput.vue';
import DataSourceMethods from './fields/DataSourceMethods.vue';
import DataSourceMethodSelect from './fields/DataSourceMethodSelect.vue';
Expand Down Expand Up @@ -68,7 +68,7 @@ export { default as DataSourceMethods } from './fields/DataSourceMethods.vue';
export { default as DataSourceInput } from './fields/DataSourceInput.vue';
export { default as DataSourceSelect } from './fields/DataSourceSelect.vue';
export { default as DataSourceMethodSelect } from './fields/DataSourceMethodSelect.vue';
export { default as DataSourceFieldSelect } from './fields/DataSourceFieldSelect.vue';
export { default as DataSourceFieldSelect } from './fields/DataSourceFieldSelect/Index.vue';
export { default as EventSelect } from './fields/EventSelect.vue';
export { default as KeyValue } from './fields/KeyValue.vue';
export { default as CodeBlockList } from './layouts/sidebar/code-block/CodeBlockList.vue';
Expand Down
16 changes: 16 additions & 0 deletions packages/editor/src/theme/data-source-field-select.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.m-fields-data-source-field-select {
width: 100%;
.m-editor-data-source-field-select {
display: flex;
width: 100%;

.tmagic-design-select {
flex: 1;
margin-right: 10px;
}

.tmagic-design-cascader {
flex: 2;
}
}
}
1 change: 1 addition & 0 deletions packages/editor/src/theme/theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@
@import "./floating-box.scss";
@import "./page-fragment-select.scss";
@import "./data-source-field.scss";
@import "./data-source-field-select.scss";
Loading

0 comments on commit 0ffc223

Please sign in to comment.