| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- <template>
- <!-- 会话引用选择弹层:由父组件用 # 触发,多选上限 5;已选状态与高亮项都由父组件传入 -->
- <div ref="rootEl" class="session-popup" @click.stop @pointerdown.stop>
- <div class="session-popup-header">
- <span>选择会话</span>
- <span class="session-count">{{ selectedCount }}/5</span>
- </div>
- <div class="session-list">
- <div
- v-for="(task, index) in list"
- :key="task.id"
- class="session-item"
- :class="{
- active: index === activeIndex,
- selected: isSelected(task.id),
- disabled: (selectedCount >= 5 && !isSelected(task.id)) || task.id === currentTaskId,
- }"
- @click="emit('select', task)"
- >
- <div class="session-checkbox" :class="{ checked: isSelected(task.id) }">
- <span v-if="isSelected(task.id)" class="check-mark">✓</span>
- </div>
- <div class="session-item-icon"></div>
- <span class="session-item-name">{{ task.name }}</span>
- </div>
- <div v-if="list.length === 0" class="session-empty">暂无匹配会话</div>
- </div>
- <div class="session-popup-footer">
- <span v-if="selectedCount >= 5" class="max-hint">最多选择5个会话</span>
- <span v-else>点击选择会话(最多5个)</span>
- </div>
- </div>
- </template>
- <script setup lang="ts">
- import { computed, ref } from 'vue';
- import { onClickOutside } from '@vueuse/core';
- interface SessionOption {
- id: string;
- name: string;
- }
- const props = defineProps<{
- list: SessionOption[];
- activeIndex: number;
- selectedIds: string[];
- currentTaskId: string;
- }>();
- const emit = defineEmits<{ (e: 'select', task: SessionOption): void; (e: 'close'): void }>();
- const selectedCount = computed(() => props.selectedIds.length);
- const isSelected = (id: string) => props.selectedIds.includes(id);
- // 组件自己持有根元素,外部点击由这里判定后回调父组件
- const rootEl = ref<HTMLElement>();
- onClickOutside(rootEl, () => emit('close'));
- </script>
- <style scoped lang="less">
- .session-popup {
- position: absolute;
- bottom: 100%;
- left: 0;
- right: 0;
- margin-bottom: 8px;
- background: #0a2a3f;
- border: 1px solid #1a4a6f;
- border-radius: 8px;
- max-height: 300px;
- display: flex;
- flex-direction: column;
- z-index: 1000;
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
- .session-popup-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 10px 14px 8px;
- font-size: 13px;
- color: #e0e6ed;
- border-bottom: 1px solid rgba(63, 80, 106, 0.3);
- flex-shrink: 0;
- .session-count {
- font-size: 12px;
- color: #3a8fd4;
- font-weight: 500;
- }
- }
- .session-list {
- flex: 1;
- overflow-y: auto;
- padding: 6px;
- .session-item {
- display: flex;
- align-items: center;
- gap: 10px;
- padding: 8px 10px;
- cursor: pointer;
- border-radius: 6px;
- transition: background 0.2s;
- &:hover {
- background: rgba(10, 132, 255, 0.15);
- }
- &.active {
- background: rgba(10, 132, 255, 0.2);
- }
- &.selected {
- background: rgba(10, 132, 255, 0.12);
- }
- &.disabled {
- opacity: 0.4;
- cursor: not-allowed;
- &:hover {
- background: transparent;
- }
- }
- .session-checkbox {
- width: 16px;
- height: 16px;
- border: 1.5px solid rgba(255, 255, 255, 0.25);
- border-radius: 3px;
- flex-shrink: 0;
- display: flex;
- align-items: center;
- justify-content: center;
- transition: all 0.2s;
- &.checked {
- background: #3a8fd4;
- border-color: #3a8fd4;
- }
- .check-mark {
- font-size: 11px;
- color: #fff;
- line-height: 1;
- }
- }
- .session-item-icon {
- width: 18px;
- height: 18px;
- background-image: var(--img-chat-popup-session-icon);
- background-repeat: no-repeat;
- background-size: 100% 100%;
- flex-shrink: 0;
- }
- .session-item-name {
- font-size: 13px;
- color: #e0e6ed;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- }
- .session-empty {
- text-align: center;
- color: #8b949e;
- font-size: 13px;
- padding: 20px 0;
- }
- }
- .session-popup-footer {
- padding: 8px 14px;
- font-size: 12px;
- color: #8b949e;
- text-align: center;
- border-top: 1px solid rgba(63, 80, 106, 0.3);
- flex-shrink: 0;
- .max-hint {
- color: #faad14;
- }
- }
- }
- </style>
|