/ src / renderer / components / common / ChatCard.vue
ChatCard.vue
  1  <script setup lang="tsx">
  2  import { ref } from 'vue'
  3  import { useSnackbarStore } from '@/renderer/store/snackbar'
  4  
  5  interface MessageContentItem {
  6    type: string
  7    text: string
  8  }
  9  
 10  interface Message {
 11    content: string | MessageContentItem[]
 12  }
 13  
 14  const props = defineProps({
 15    index: { type: Number, required: true },
 16    range: { type: Number, default: 1 },
 17    messages: { type: Object, required: true },
 18    showContent: { type: Boolean, default: false },
 19    showDelete: { type: Boolean, default: true },
 20    showReduce: { type: Boolean, default: true },
 21    showModify: { type: Boolean, default: false },
 22    showCopy: { type: Boolean, default: true }
 23  })
 24  
 25  const emit = defineEmits<{
 26    (_e: 'delete-messages', _payload: { index: number; range: number }): void
 27  }>()
 28  
 29  const emitDeleteMessage = () => {
 30    emit('delete-messages', {
 31      index: props.index,
 32      range: props.range
 33    })
 34  }
 35  
 36  const emitDeleteMessageRange = () => {
 37    emit('delete-messages', {
 38      index: 0,
 39      range: props.index
 40    })
 41  }
 42  
 43  const showcontent = ref(false)
 44  const showmodify = ref(false)
 45  const snackbarStore = useSnackbarStore()
 46  
 47  const copyToClipboard = async (msg: Message) => {
 48    let textToCopy = ''
 49  
 50    try {
 51      if (typeof msg.content === 'string') {
 52        textToCopy = msg.content
 53      } else if (Array.isArray(msg.content)) {
 54        for (const item of msg.content) {
 55          if (item.type === 'text' && typeof item.text === 'string') {
 56            textToCopy = item.text
 57            break // 只取第一个文本内容
 58          }
 59        }
 60      }
 61  
 62      await navigator.clipboard.writeText(textToCopy)
 63      snackbarStore.showSuccessMessage('snackbar.copied')
 64    } catch (err) {
 65      snackbarStore.showErrorMessage(err instanceof Error ? err.message : String(err))
 66    }
 67  }
 68  </script>
 69  <template>
 70    <v-hover open-delay="100">
 71      <template #default="{ isHovering, props: hoverProps }">
 72        <v-card v-bind="hoverProps" :elevation="isHovering ? 4 : 2" width="100vw" max-width="100%">
 73          <slot :showcontent="showcontent" :showmodify="showmodify" />
 74          <v-expand-transition>
 75            <div v-if="isHovering">
 76              <v-divider />
 77              <v-card-actions>
 78                <!-- Copy Button -->
 79                <v-btn
 80                  v-if="showCopy"
 81                  color="primary"
 82                  icon="mdi-content-copy"
 83                  size="x-small"
 84                  variant="plain"
 85                  @click="copyToClipboard(messages[index])"
 86                />
 87  
 88                <!-- Modify Button -->
 89                <v-btn
 90                  v-if="showModify"
 91                  color="primary"
 92                  :icon="showmodify ? 'mdi-check-bold' : 'mdi-lead-pencil'"
 93                  size="x-small"
 94                  variant="plain"
 95                  @click="showmodify = !showmodify"
 96                />
 97  
 98                <!-- Content Toggle Button -->
 99                <v-btn
100                  v-if="showContent"
101                  color="primary"
102                  :icon="showcontent ? 'mdi-eye-remove' : 'mdi-eye'"
103                  size="x-small"
104                  variant="plain"
105                  @click="showcontent = !showcontent"
106                />
107  
108                <v-spacer />
109  
110                <!-- Reduce Button -->
111                <v-btn
112                  v-if="showReduce && index > 0"
113                  v-tooltip:top="$t('chat.reduce')"
114                  color="error"
115                  icon="mdi-format-align-top"
116                  size="x-small"
117                  variant="plain"
118                  @click="emitDeleteMessageRange"
119                />
120  
121                <!-- Delete Button -->
122                <v-btn
123                  v-if="showDelete"
124                  color="error"
125                  icon="mdi-delete-outline"
126                  size="x-small"
127                  variant="plain"
128                  @click="emitDeleteMessage"
129                />
130              </v-card-actions>
131            </div>
132          </v-expand-transition>
133        </v-card>
134      </template>
135    </v-hover>
136  </template>