ConversationList.vue 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. <script setup lang="ts">
  2. import { computed } from 'vue';
  3. import { type Conversation } from '../../api/dto';
  4. interface Props {
  5. conversations: Conversation[];
  6. currentId: string | number;
  7. }
  8. interface Emits {
  9. (e: 'select', conv: Conversation): void;
  10. }
  11. const props = defineProps<Props>();
  12. defineEmits<Emits>();
  13. // 分离固定会话和普通会话
  14. const permanentConversations = computed(() => props.conversations.filter((conv) => conv.isPermanent));
  15. const regularConversations = computed(() => props.conversations.filter((conv) => !conv.isPermanent));
  16. </script>
  17. <template>
  18. <!-- 固定会话按钮 -->
  19. <div
  20. v-for="conv in permanentConversations"
  21. :key="conv.id"
  22. class="conversation-item permanent"
  23. :class="{ active: conv.id === props.currentId }"
  24. @click="$emit('select', conv)"
  25. >
  26. <div class="conversation-title">
  27. <span v-if="conv.icon" class="conversation-icon">{{ conv.icon }}</span>
  28. {{ conv.title }}
  29. </div>
  30. </div>
  31. <div class="section-header">
  32. <span class="section-title">对话</span>
  33. <span class="section-count">{{ regularConversations.length }}</span>
  34. </div>
  35. <!-- 普通会话 -->
  36. <div class="conversation-list">
  37. <div v-if="regularConversations.length > 0" class="conversations-section">
  38. <div
  39. v-for="conv in regularConversations"
  40. :key="conv.id"
  41. class="conversation-item"
  42. :class="{ active: conv.id === props.currentId }"
  43. @click="$emit('select', conv)"
  44. >
  45. <div class="conversation-title">
  46. <span v-if="conv.icon" class="conversation-icon">{{ conv.icon }}</span>
  47. {{ conv.title }}
  48. </div>
  49. <div class="conversation-time">{{ conv.timestamps }}</div>
  50. </div>
  51. </div>
  52. <!-- 空状态 -->
  53. <div v-if="props.conversations.length === 0" class="empty-conversations">
  54. <span>暂无会话</span>
  55. </div>
  56. </div>
  57. </template>
  58. <style scoped lang="less">
  59. .conversation-list {
  60. flex: 1;
  61. overflow-y: auto;
  62. padding: 8px;
  63. display: flex;
  64. flex-direction: column;
  65. gap: 12px;
  66. }
  67. .conversations-section {
  68. display: flex;
  69. flex-direction: column;
  70. gap: 4px;
  71. }
  72. .section-header {
  73. display: flex;
  74. align-items: center;
  75. justify-content: space-between;
  76. padding: 4px 16px;
  77. }
  78. .section-title {
  79. font-size: 12px;
  80. font-weight: 600;
  81. color: @cb-text-secondary;
  82. text-transform: uppercase;
  83. letter-spacing: 0.5px;
  84. }
  85. .section-count {
  86. font-size: 12px;
  87. color: @cb-text-secondary;
  88. background-color: @cb-vscode-input-background;
  89. padding: 2px 8px;
  90. border-radius: 10px;
  91. }
  92. .conversation-item {
  93. padding: 12px 16px;
  94. border-radius: 6px;
  95. cursor: pointer;
  96. transition: background-color 0.2s;
  97. }
  98. .conversation-item:hover {
  99. background-color: @cb-vscode-list-hoverBackground;
  100. }
  101. .conversation-item.active {
  102. background-color: @cb-vscode-list-activeSelectionBackground;
  103. }
  104. /* 固定会话按钮样式 */
  105. .conversation-item.permanent {
  106. background-color: @cb-vscode-input-background;
  107. border: 1px solid @cb-vscode-widget-border;
  108. border-radius: 6px;
  109. padding: 10px 12px;
  110. display: flex;
  111. align-items: center;
  112. gap: 8px;
  113. margin: 10px;
  114. transition: all 0.2s;
  115. cursor: pointer;
  116. box-sizing: border-box;
  117. }
  118. .conversation-item.permanent:hover {
  119. background-color: @cb-vscode-list-hoverBackground;
  120. border-color: @cb-accent;
  121. }
  122. .conversation-item.permanent.active {
  123. background-color: @cb-vscode-list-hoverBackground;
  124. border-color: @cb-accent;
  125. }
  126. .conversation-item.permanent .conversation-title {
  127. display: flex;
  128. align-items: center;
  129. gap: 8px;
  130. font-size: 13px;
  131. font-weight: 500;
  132. color: @cb-text-primary;
  133. margin-bottom: 0;
  134. }
  135. .conversation-item.permanent .conversation-icon {
  136. font-size: 16px;
  137. flex-shrink: 0;
  138. }
  139. .conversation-title {
  140. font-size: 14px;
  141. font-weight: 500;
  142. margin-bottom: 4px;
  143. display: flex;
  144. align-items: center;
  145. gap: 8px;
  146. color: @cb-text-primary;
  147. overflow: hidden;
  148. text-overflow: ellipsis;
  149. white-space: nowrap;
  150. }
  151. .conversation-icon {
  152. font-size: 16px;
  153. flex-shrink: 0;
  154. }
  155. .conversation-time {
  156. font-size: 12px;
  157. color: @cb-text-secondary;
  158. }
  159. .empty-conversations {
  160. display: flex;
  161. flex-direction: column;
  162. align-items: center;
  163. justify-content: center;
  164. padding: 32px 16px;
  165. color: @cb-text-secondary;
  166. font-size: 13px;
  167. text-align: center;
  168. }
  169. .empty-conversations span {
  170. opacity: 0.7;
  171. }
  172. </style>