<script lang="ts" setup>
|
import type { MesMdProductBomApi } from '#/api/mes/md/item/productBom';
|
|
import { computed, ref, useAttrs, watch } from 'vue';
|
|
import { IconifyIcon } from '@vben/icons';
|
|
import { Input, Tooltip } from 'ant-design-vue';
|
|
import { getProductBomListByItemId } from '#/api/mes/md/item/productBom';
|
|
import MdProductBomSelectDialog from './product-bom-select-dialog.vue';
|
|
defineOptions({ name: 'MdProductBomSelect', inheritAttrs: false });
|
|
const props = withDefaults(
|
defineProps<{
|
allowClear?: boolean;
|
disabled?: boolean;
|
itemId?: number;
|
modelValue?: number | number[];
|
multiple?: boolean;
|
placeholder?: string;
|
}>(),
|
{
|
allowClear: true,
|
disabled: false,
|
itemId: undefined,
|
modelValue: undefined,
|
multiple: false,
|
placeholder: '请选择 BOM 物料',
|
},
|
);
|
const emit = defineEmits<{
|
change: [
|
bom: MesMdProductBomApi.ProductBom[] | MesMdProductBomApi.ProductBom | undefined,
|
];
|
'update:modelValue': [value: number[] | number | undefined];
|
}>();
|
const attrs = useAttrs(); // 透传属性
|
const dialogRef = ref<InstanceType<typeof MdProductBomSelectDialog>>(); // BOM 物料选择弹窗
|
const hovering = ref(false); // 是否悬停
|
const selectedBoms = ref<MesMdProductBomApi.ProductBom[]>([]); // 已选 BOM 物料(单选时最多一项)
|
|
const selectedBom = computed(() => selectedBoms.value[0]); // 单选时的当前项
|
const displayLabel = computed(() => {
|
// 多选展示「名称、名称」,单选展示单个名称
|
if (props.multiple) {
|
return selectedBoms.value
|
.map((bom) => bom.bomItemName)
|
.filter(Boolean)
|
.join('、');
|
}
|
return selectedBom.value?.bomItemName ?? '';
|
}); // 选择器展示名称
|
const showClear = computed(() => {
|
if (!props.allowClear || props.disabled || !hovering.value) {
|
return false;
|
}
|
if (props.multiple) {
|
return Array.isArray(props.modelValue) && props.modelValue.length > 0;
|
}
|
return props.modelValue !== undefined && props.modelValue !== null;
|
});
|
|
/** 从 modelValue 解析出 BOM 物料编号列表 */
|
function getModelIds() {
|
if (props.multiple) {
|
return Array.isArray(props.modelValue)
|
? props.modelValue.filter((id): id is number => id != null)
|
: [];
|
}
|
return typeof props.modelValue === 'number' ? [props.modelValue] : [];
|
}
|
|
/** 根据 BOM 物料编号回显选择器 */
|
async function resolveBomsById(ids: number[]) {
|
if (ids.length === 0 || props.itemId === undefined || props.itemId === null) {
|
selectedBoms.value = [];
|
return;
|
}
|
const list = await getProductBomListByItemId(props.itemId as number);
|
selectedBoms.value = list.filter(
|
(item) => item.bomItemId != null && ids.includes(item.bomItemId),
|
);
|
}
|
|
watch(
|
() => props.modelValue,
|
() => {
|
resolveBomsById(getModelIds());
|
},
|
{ immediate: true },
|
);
|
|
watch(
|
() => props.itemId,
|
() => {
|
selectedBoms.value = [];
|
emit('update:modelValue', props.multiple ? [] : undefined);
|
emit('change', props.multiple ? [] : undefined);
|
},
|
);
|
|
/** 清空已选 BOM 物料 */
|
function clearSelected() {
|
selectedBoms.value = [];
|
emit('update:modelValue', props.multiple ? [] : undefined);
|
emit('change', props.multiple ? [] : undefined);
|
}
|
|
/** 打开 BOM 物料选择弹窗 */
|
function handleClick(event: MouseEvent) {
|
if (props.disabled || props.itemId === undefined || props.itemId === null) {
|
return;
|
}
|
const target = event.target as HTMLElement;
|
if (showClear.value && target.closest('.ant-input-suffix')) {
|
event.stopPropagation();
|
clearSelected();
|
return;
|
}
|
const ids = getModelIds();
|
dialogRef.value?.open(props.itemId as number, props.multiple ? ids : ids[0]);
|
}
|
|
/** 回填选中的 BOM 物料 */
|
function handleSelected(rows: MesMdProductBomApi.ProductBom[]) {
|
selectedBoms.value = rows;
|
emit(
|
'update:modelValue',
|
props.multiple
|
? rows
|
.map((row) => row.bomItemId)
|
.filter((id): id is number => id != null)
|
: rows[0]?.bomItemId,
|
);
|
emit('change', props.multiple ? rows : rows[0]);
|
}
|
</script>
|
|
<template>
|
<div
|
v-bind="attrs"
|
class="w-full"
|
:class="disabled ? 'cursor-not-allowed' : 'cursor-pointer'"
|
@click="handleClick"
|
@mouseenter="hovering = true"
|
@mouseleave="hovering = false"
|
>
|
<Tooltip :mouse-enter-delay="0.5" :open="selectedBoms.length ? undefined : false">
|
<template #title>
|
<div v-if="multiple" class="leading-6">
|
<div v-for="bom in selectedBoms" :key="bom.bomItemId">
|
{{ bom.bomItemName || '-' }}{{ bom.bomItemCode ? `(${bom.bomItemCode})` : '' }}
|
</div>
|
</div>
|
<div v-else-if="selectedBom" class="leading-6">
|
<div>编码:{{ selectedBom.bomItemCode || '-' }}</div>
|
<div>名称:{{ selectedBom.bomItemName || '-' }}</div>
|
<div>规格:{{ selectedBom.bomItemSpecification || '-' }}</div>
|
<div>单位:{{ selectedBom.unitMeasureName || '-' }}</div>
|
<div>用量比例:{{ selectedBom.quantity ?? '-' }}</div>
|
</div>
|
</template>
|
<Input
|
:disabled="disabled"
|
:placeholder="placeholder"
|
:value="displayLabel"
|
readonly
|
>
|
<template #suffix>
|
<IconifyIcon
|
class="size-4"
|
:icon="showClear ? 'lucide:circle-x' : 'lucide:search'"
|
/>
|
</template>
|
</Input>
|
</Tooltip>
|
</div>
|
<MdProductBomSelectDialog
|
ref="dialogRef"
|
:multiple="multiple"
|
@selected="handleSelected"
|
/>
|
</template>
|