gaoluyang
2026-06-24 712aa51536236d43e87273e4ce45ac5691dffad8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
<!-- 秒杀活动橱窗组件:用于展示和选择秒杀活动 -->
<script lang="ts" setup>
import type { MallSeckillActivityApi } from '#/api/mall/promotion/seckill/seckillActivity';
 
import { computed, ref, watch } from 'vue';
 
import { IconifyIcon } from '..\..\..\..\..\packages\icons\src';
 
import { Image, Tooltip } from 'ant-design-vue';
 
import { getSeckillActivityListByIds } from '#/api/mall/promotion/seckill/seckillActivity';
 
import SeckillTableSelect from './table-select.vue';
 
interface SeckillShowcaseProps {
  modelValue?: number | number[];
  limit?: number;
  disabled?: boolean;
}
 
const props = withDefaults(defineProps<SeckillShowcaseProps>(), {
  modelValue: undefined,
  limit: Number.MAX_VALUE,
  disabled: false,
});
 
const emit = defineEmits(['update:modelValue', 'change']);
 
const activityList = ref<MallSeckillActivityApi.SeckillActivity[]>([]); // 已选择的活动列表
const seckillTableSelectRef = ref<InstanceType<typeof SeckillTableSelect>>(); // 活动选择表格组件引用
const isMultiple = computed(() => props.limit !== 1); // 是否为多选模式
 
/** 计算是否可以添加 */
const canAdd = computed(() => {
  if (props.disabled) {
    return false;
  }
  if (!props.limit) {
    return true;
  }
  return activityList.value.length < props.limit;
});
 
/** 监听 modelValue 变化,加载活动详情 */
watch(
  () => props.modelValue,
  async (newValue) => {
    // eslint-disable-next-line unicorn/no-nested-ternary
    const ids = Array.isArray(newValue) ? newValue : newValue ? [newValue] : [];
    if (ids.length === 0) {
      activityList.value = [];
      return;
    }
    // 只有活动发生变化时才重新查询
    if (
      activityList.value.length === 0 ||
      activityList.value.some((activity) => !ids.includes(activity.id!))
    ) {
      activityList.value = await getSeckillActivityListByIds(ids as number[]);
    }
  },
  { immediate: true },
);
 
/** 打开活动选择对话框 */
function handleOpenActivitySelect() {
  seckillTableSelectRef.value?.open(activityList.value);
}
 
/** 选择活动后触发 */
function handleActivitySelected(
  activities:
    | MallSeckillActivityApi.SeckillActivity
    | MallSeckillActivityApi.SeckillActivity[],
) {
  activityList.value = Array.isArray(activities) ? activities : [activities];
  emitActivityChange();
}
 
/** 删除活动 */
function handleRemoveActivity(index: number) {
  activityList.value.splice(index, 1);
  emitActivityChange();
}
 
/** 触发变更事件 */
function emitActivityChange() {
  if (props.limit === 1) {
    const activity =
      activityList.value.length > 0 ? activityList.value[0] : null;
    emit('update:modelValue', activity?.id || 0);
    emit('change', activity);
  } else {
    emit(
      'update:modelValue',
      activityList.value.map((activity) => activity.id!),
    );
    emit('change', activityList.value);
  }
}
</script>
 
<template>
  <div class="flex flex-wrap items-center gap-2">
    <!-- 已选活动列表 -->
    <div
      v-for="(activity, index) in activityList"
      :key="activity.id"
      class="relative h-[60px] w-[60px] overflow-hidden rounded-lg border border-dashed border-gray-300"
    >
      <Tooltip :title="activity.name">
        <div class="relative h-full w-full">
          <Image
            :preview="true"
            :src="activity.picUrl"
            class="h-full w-full rounded-lg object-cover"
          />
          <!-- 删除按钮 -->
          <!-- TODO @芋艿:等待和 /Users/yunai/Java/yudao-ui-admin-vben-v5/apps/web-antd/src/views/mall/product/spu/components/spu-showcase.vue 进一步统一 -->
          <IconifyIcon
            v-if="!disabled"
            icon="lucide:x"
            class="absolute -right-2 -top-2 z-10 h-5 w-5 cursor-pointer text-red-500 hover:text-red-600"
            @click="handleRemoveActivity(index)"
          />
        </div>
      </Tooltip>
    </div>
 
    <!-- 添加活动按钮 -->
    <Tooltip v-if="canAdd" title="选择活动">
      <div
        class="flex h-[60px] w-[60px] cursor-pointer items-center justify-center rounded-lg border border-dashed border-gray-300 hover:border-blue-400"
        @click="handleOpenActivitySelect"
      >
        <IconifyIcon icon="lucide:plus" class="text-xl text-gray-400" />
      </div>
    </Tooltip>
  </div>
 
  <!-- 活动选择对话框 -->
  <SeckillTableSelect
    ref="seckillTableSelectRef"
    :multiple="isMultiple"
    @change="handleActivitySelected"
  />
</template>