2026-08-04 49c9eff61882f1f7d519397ccfdd90cdd8735cb8
src/views/mes/md/item/components/select.vue
@@ -17,7 +17,8 @@
  defineProps<{
    allowClear?: boolean;
    disabled?: boolean;
    modelValue?: number;
    modelValue?: number | number[];
    multiple?: boolean;
    placeholder?: string;
    productIds?: number[];
  }>(),
@@ -25,53 +26,70 @@
    allowClear: true,
    disabled: false,
    modelValue: undefined,
    multiple: false,
    placeholder: '请选择产品物料',
    productIds: () => [],
  },
);
const emit = defineEmits<{
  change: [item: MesMdItemApi.Item | undefined];
  'update:modelValue': [value: number | undefined];
  change: [items: MesMdItemApi.Item | MesMdItemApi.Item[] | undefined];
  'update:modelValue': [value: number | number[] | undefined];
}>();
const attrs = useAttrs(); // 透传属性
const dialogRef = ref<InstanceType<typeof MdItemSelectDialog>>(); // 物料选择弹窗
const hovering = ref(false); // 是否悬停
const selectedItem = ref<MesMdItemApi.Item>(); // 当前选中物料
const attrs = useAttrs();
const dialogRef = ref<InstanceType<typeof MdItemSelectDialog>>();
const hovering = ref(false);
const selectedItems = ref<MesMdItemApi.Item[]>([]);
const displayLabel = computed(() => selectedItem.value?.name ?? ''); // 选择器展示名称
const displayLabel = computed(() => {
  if (selectedItems.value.length === 0) return '';
  return selectedItems.value.map(item => item.name ?? '').join('、');
});
const showClear = computed(
  () =>
    props.allowClear &&
    !props.disabled &&
    hovering.value &&
    props.modelValue !== null,
    (props.multiple
      ? Array.isArray(props.modelValue) && props.modelValue.length > 0
      : props.modelValue != null),
);
/** 根据物料编号回显选择器 */
async function resolveItemById(id: number | undefined) {
  if (!id) {
    selectedItem.value = undefined;
async function resolveItemsById(values: number | number[] | undefined) {
  if (values == null || (Array.isArray(values) && values.length === 0)) {
    selectedItems.value = [];
    return;
  }
  if (selectedItem.value?.id === id) {
  const ids = Array.isArray(values) ? values : [values];
  // 已全部回显则跳过
  if (
    ids.length === selectedItems.value.length &&
    ids.every((id, i) => selectedItems.value[i]?.id === id)
  ) {
    return;
  }
  selectedItem.value = await getItem(id);
  const items = await Promise.all(ids.map(id => getItem(id)));
  selectedItems.value = items.filter(Boolean) as MesMdItemApi.Item[];
}
watch(
  () => props.modelValue,
  (value) => {
    resolveItemById(value);
    resolveItemsById(value);
  },
  { immediate: true },
);
/** 清空已选物料 */
function clearSelected() {
  selectedItem.value = undefined;
  emit('update:modelValue', undefined);
  emit('change', undefined);
  selectedItems.value = [];
  if (props.multiple) {
    emit('update:modelValue', []);
    emit('change', []);
  } else {
    emit('update:modelValue', undefined);
    emit('change', undefined);
  }
}
/** 打开物料选择弹窗 */
@@ -85,24 +103,33 @@
    clearSelected();
    return;
  }
  const selectedIds = (
    props.modelValue === null ? [] : [props.modelValue]
  ) as number[];
  const selectedIds: number[] = props.multiple
    ? (props.modelValue as number[] | undefined) ?? []
    : props.modelValue != null
      ? [props.modelValue as number]
      : [];
  dialogRef.value?.open(selectedIds, {
    multiple: false,
    multiple: props.multiple,
    productIds: props.productIds,
  });
}
/** 回填选中的物料 */
function handleSelected(rows: MesMdItemApi.Item[]) {
  const item = rows[0];
  if (!item) {
    return;
  if (props.multiple) {
    selectedItems.value = rows;
    const ids = rows.map(r => r.id).filter(Boolean) as number[];
    emit('update:modelValue', ids);
    emit('change', rows);
  } else {
    const item = rows[0];
    if (!item) {
      return;
    }
    selectedItems.value = [item];
    emit('update:modelValue', item.id);
    emit('change', item);
  }
  selectedItem.value = item;
  emit('update:modelValue', item.id);
  emit('change', item);
}
</script>
@@ -115,13 +142,21 @@
    @mouseenter="hovering = true"
    @mouseleave="hovering = false"
  >
    <Tooltip :mouse-enter-delay="0.5" :open="selectedItem ? undefined : false">
    <Tooltip :mouse-enter-delay="0.5" :open="selectedItems.length > 0 ? undefined : false">
      <template #title>
        <div v-if="selectedItem" class="leading-6">
          <div>编码:{{ selectedItem.code || '-' }}</div>
          <div>名称:{{ selectedItem.name || '-' }}</div>
          <div>规格:{{ selectedItem.specification || '-' }}</div>
          <div>单位:{{ selectedItem.unitMeasureName || '-' }}</div>
        <div v-if="selectedItems.length === 1" class="leading-6">
          <div>编码:{{ selectedItems[0].code || '-' }}</div>
          <div>名称:{{ selectedItems[0].name || '-' }}</div>
          <div>规格:{{ selectedItems[0].specification || '-' }}</div>
          <div>单位:{{ selectedItems[0].unitMeasureName || '-' }}</div>
        </div>
        <div v-else-if="selectedItems.length > 1" class="leading-6">
          <div v-for="item in selectedItems" :key="item.id" class="mb-2 border-b border-gray-100 pb-1 last:border-b-0 last:pb-0">
            <div>编码:{{ item.code || '-' }}</div>
            <div>名称:{{ item.name || '-' }}</div>
            <div>规格:{{ item.specification || '-' }}</div>
            <div>单位:{{ item.unitMeasureName || '-' }}</div>
          </div>
        </div>
      </template>
      <Input