roymondchen ffcc734102 feat(editor): 查看/编辑时自动切换 sidebar 并定位到对应配置
点击代码块、数据源字段或方法的选择器编辑/查看按钮时,自动切换到对应 sidebar tab,并打开详情中的目标字段或方法配置。
2026-06-15 17:43:09 +08:00

236 lines
7.0 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 && !isCompare"
: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 isCompare = computed(() => Boolean(mForm?.isCompare));
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 dataSourceSidePanel = computed(() =>
uiService.get('sideBarItems').find((item) => item.$key === SideItemKey.DATA_SOURCE),
);
const hasDataSourceSidePanel = computed(() => dataSourceSidePanel.value);
const editHandler = (id: string) => {
const sideBarItem = dataSourceSidePanel.value;
if (sideBarItem) {
uiService.set('sideBarActiveTabName', sideBarItem.text || sideBarItem.$key || SideItemKey.DATA_SOURCE);
}
const dataSourceId = removeDataSourceFieldPrefix(id);
const fieldPath = selectFieldsId.value;
if (fieldPath.length) {
eventBus?.emit('edit-data-source-field', dataSourceId, [...fieldPath]);
} else {
eventBus?.emit('edit-data-source', dataSourceId);
}
};
</script>