259 lines
7.3 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>
<component
v-model="activeTabName"
v-bind="
tabsComponent?.props({
type: config.tabType,
editable: config.editable || false,
tabPosition: config.tabPosition || 'top',
}) || {}
"
:is="tabsComponent?.component || 'el-tabs'"
:class="`tmagic-design-tabs ${config.dynamic ? 'magic-form-dynamic-tab' : 'magic-form-tab'}`"
@tab-click="tabClickHandler"
@tab-add="onTabAdd"
@tab-remove="onTabRemove"
>
<component
v-for="(tab, tabIndex) in tabs"
:is="tabPaneComponent?.component || 'el-tab-pane'"
:key="tab[mForm?.keyProp || '__key'] ?? tabIndex"
v-bind="
tabPaneComponent?.props({ name: filter(tab.status) || tabIndex.toString(), lazy: tab.lazy || false }) || {}
"
>
<template #label>
<span>
{{ filter(tab.title)
}}<TMagicBadge
:hidden="!diffCount[tabIndex]"
:value="diffCount[tabIndex]"
class="diff-count-badge"
></TMagicBadge>
</span>
</template>
<Container
v-for="item in tabItems(tab)"
:key="(item as Record<string, any>)[mForm?.keyProp || '__key']"
:config="item"
:disabled="disabled"
:model="
config.dynamic
? (name ? model[name] : model)[tabIndex]
: tab.name
? (name ? model[name] : model)[tab.name]
: name
? model[name]
: model
"
:last-values="
isEmpty(lastValues)
? {}
: config.dynamic
? (name ? lastValues[name] : lastValues)[tabIndex]
: tab.name
? (name ? lastValues[name] : lastValues)[tab.name]
: name
? lastValues[name]
: lastValues
"
:is-compare="isCompare"
:prop="
config.dynamic
? `${prop}${prop ? '.' : ''}${String(tabIndex)}`
: tab.name
? `${prop}${prop ? '.' : ''}${tab.name}`
: prop
"
:size="size"
:label-width="tab.labelWidth || labelWidth"
:expand-more="expandMore"
@change="changeHandler"
@addDiffCount="onAddDiffCount(tabIndex)"
></Container>
</component>
</component>
</template>
<script setup lang="ts">
import { computed, inject, ref, watchEffect } from 'vue';
import { isEmpty } from 'lodash-es';
import { getDesignConfig, TMagicBadge } from '@tmagic/design';
import type { ContainerChangeEventData, FormState, TabConfig, TabPaneConfig } from '../schema';
import { display as displayFunc, filterFunction, initValue } from '../utils/form';
import Container from './Container.vue';
defineOptions({
name: 'MFormTabs',
});
type DiffCount = {
[tabIndex: number]: number;
};
const props = withDefaults(
defineProps<{
model: any;
lastValues?: any;
isCompare?: boolean;
config: TabConfig;
name: string;
size?: string;
labelWidth?: string;
prop?: string;
expandMore?: boolean;
disabled?: boolean;
}>(),
{
lastValues: () => ({}),
isCompare: false,
prop: '',
},
);
const tabPaneComponent = getDesignConfig('components')?.tabPane;
const tabsComponent = getDesignConfig('components')?.tabs;
const getActive = (mForm: FormState | undefined, props: any, activeTabName: string) => {
const { config, model, prop } = props;
const { active } = config;
if (typeof active === 'function') return active(mForm, { model, formValue: mForm?.values, prop });
if (+activeTabName >= props.config.items.length) return '0';
if (typeof active !== 'undefined') return active;
return '0';
};
const tabClick = (mForm: FormState | undefined, tab: any, props: any) => {
const { config, model, prop } = props;
// 兼容vue2的element-ui
tab.name = tab.paneName;
if (typeof config.onTabClick === 'function') {
config.onTabClick(mForm, tab, { model, formValue: mForm?.values, prop, config });
}
const tabConfig = config.items.find((item: TabPaneConfig) => tab.name === item.status);
if (tabConfig && typeof tabConfig.onTabClick === 'function') {
tabConfig.onTabClick(mForm, tab, { model, formValue: mForm?.values, prop, config });
}
};
const emit = defineEmits<{
change: [v: any, eventData?: ContainerChangeEventData];
addDiffCount: [];
}>();
const mForm = inject<FormState | undefined>('mForm');
const activeTabName = ref(getActive(mForm, props, ''));
const diffCount = ref<DiffCount>({});
const tabs = computed(() => {
if (props.config.dynamic) {
if (!props.config.name) throw new Error('dynamic tab 必须配置name');
return props.model[props.config.name] || [];
}
return props.config.items.filter((item) => displayFunc(mForm, item.display, props));
});
const filter = (config: any) => filterFunction(mForm, config, props);
watchEffect(() => {
if (typeof props.config.activeChange === 'function') {
props.config.activeChange(mForm, activeTabName.value, {
model: props.model,
prop: props.prop,
});
}
});
const tabItems = (tab: TabPaneConfig) => (props.config.dynamic ? props.config.items : tab.items);
const tabClickHandler = (tab: any) => {
if (typeof tab === 'object') {
tabClick(mForm, tab, props);
} else {
let item = tabs.value.find((tab: any) => tab.status === tab);
if (!item) {
item = tabs.value[tab];
}
tabClick(mForm, item, props);
}
};
const onTabAdd = async () => {
if (!props.name) throw new Error('dynamic tab 必须配置name');
if (typeof props.config.onTabAdd === 'function') {
props.config.onTabAdd(mForm, {
model: props.model,
prop: props.prop,
config: props.config,
});
emit('change', props.model);
} else {
const newObj = await initValue(mForm, {
config: props.config.items,
initValues: {},
});
newObj.title = `标签${tabs.value.length + 1}`;
props.model[props.name].push(newObj);
emit('change', props.model[props.name], {
changeRecords: [
{
propPath: `${props.prop}.${props.model[props.name].length - 1}`,
value: newObj,
},
],
});
}
mForm?.$emit('field-change', props.prop, props.model[props.name]);
};
const onTabRemove = (tabName: string) => {
if (!props.name) throw new Error('dynamic tab 必须配置name');
if (typeof props.config.onTabRemove === 'function') {
props.config.onTabRemove(mForm, tabName, {
model: props.model,
prop: props.prop,
config: props.config,
});
} else {
props.model[props.name].splice(+tabName, 1);
// 防止删除后没有选中的问题
if (tabName < activeTabName.value || activeTabName.value >= props.model[props.name].length) {
activeTabName.value = (+activeTabName.value - 1).toString();
tabClick(mForm, { name: activeTabName.value }, props);
}
}
emit('change', props.model);
mForm?.$emit('field-change', props.prop, props.model[props.name]);
};
const changeHandler = (v: any, eventData: ContainerChangeEventData) => {
emit('change', props.model, eventData);
};
// 在tabs组件中收集事件触发次数即该tab下的差异数
const onAddDiffCount = (tabIndex: number) => {
if (!diffCount.value[tabIndex]) {
diffCount.value[tabIndex] = 1;
} else {
diffCount.value[tabIndex] += 1;
}
// 继续抛出给更高层级的组件
emit('addDiffCount');
};
</script>