57 lines
2.0 KiB
Vue
57 lines
2.0 KiB
Vue
<template>
|
|
<div class="list">
|
|
<div v-for="m in messages" :key="m.id" class="row" :class="m.role">
|
|
<div class="avatar" :class="m.role">
|
|
<AssistantAvatar v-if="m.role === 'assistant'" />
|
|
<template v-else>🧑</template>
|
|
</div>
|
|
<div class="col">
|
|
<div class="meta">{{ m.role === "user" ? "你" : "小鑫" }}</div>
|
|
<MessageBubble :msg="m" @approve="(p: any) => $emit('approve', p)" @reject="(p: any) => $emit('reject', p)" />
|
|
<div v-if="m.loading" class="typing"><span></span><span></span><span></span></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import MessageBubble from "./MessageBubble.vue";
|
|
import AssistantAvatar from "./AssistantAvatar.vue";
|
|
|
|
interface Msg {
|
|
id: number;
|
|
role: "user" | "assistant" | "system";
|
|
content: string;
|
|
loading?: boolean;
|
|
approval?: any;
|
|
tools?: any[];
|
|
}
|
|
|
|
defineProps<{ messages: Msg[] }>();
|
|
defineEmits<{ approve: [payload: any]; reject: [payload: any] }>();
|
|
</script>
|
|
|
|
<style scoped>
|
|
.list { display: flex; flex-direction: column; gap: 18px; }
|
|
.row { display: flex; gap: 12px; align-items: flex-start; }
|
|
.row.user { flex-direction: row-reverse; }
|
|
.avatar {
|
|
width: 36px; height: 36px; border-radius: 10px; flex-shrink: 0;
|
|
display: grid; place-items: center; font-size: 18px;
|
|
background: var(--surface-2); border: 1px solid var(--border);
|
|
}
|
|
.avatar.assistant { background: transparent; border: none; overflow: hidden; padding: 0; }
|
|
.col { display: flex; flex-direction: column; gap: 4px; max-width: 78%; }
|
|
.row.user .col { align-items: flex-end; }
|
|
.meta { font-size: 11px; color: var(--text-dim); padding: 0 4px; }
|
|
|
|
.typing { display: flex; gap: 4px; padding: 6px 4px; }
|
|
.typing span {
|
|
width: 7px; height: 7px; border-radius: 50%; background: var(--text-dim);
|
|
animation: blink 1.2s infinite ease-in-out;
|
|
}
|
|
.typing span:nth-child(2) { animation-delay: 0.2s; }
|
|
.typing span:nth-child(3) { animation-delay: 0.4s; }
|
|
@keyframes blink { 0%, 60%, 100% { opacity: 0.25; } 30% { opacity: 1; } }
|
|
</style>
|