---
title: "Chat 消息列表"
description: "AI 对话记录的消息列表与消息行"
category: AiChat
status: beta
since: 0.3.4
tags: [chat, ai, markdown]
syncStatus: reviewed
verified: true
---

## 用法

### 基础
:::TuffDemoWrapper{demo="ChatChatListDemo" code-lang="vue"}
---
code: |
  <script setup lang="ts">
  import type { ChatMessageModel } from '@talex-touch/tuffex/chat'
  import { ref } from 'vue'

  const messages = ref<ChatMessageModel[]>([
    { id: 'system-1', role: 'system', content: 'Markdown 已启用。', createdAt: 1_705_000_000_000 },
    {
      id: 'user-1',
      role: 'user',
      content: '请审阅这张发布封面。',
      attachments: [{ type: 'image', url: '/cover.svg', name: 'cover.svg' }],
    },
    { id: 'assistant-1', role: 'assistant', content: '可以进入发布说明。\n\n```ts\nconst status = "ready"\n```' },
  ])
  </script>

  <template>
    <TxChatList :messages="messages" @image-click="openPreview" />
  </template>
---
:::

### 最佳实践

- `id` 使用对话存储中的稳定 id，不用数组下标。
- 在数据边界归一化图片 URL；组件不校验或改写附件地址。
- 虚拟列表，或流式输出中会重排消息时，关闭 `stagger`。
- 在宿主处理 `imageClick`；组件没有内置预览。
- 需要改写单行渲染时，直接使用 `TxChatMessage` 及其插槽。

## API 参考

### TxChatList

#### 属性

| 属性名 | 类型 | 默认值 | 说明 |
|------|------|---------|------|
| `messages` | `ChatMessageModel[]` | 必填 | 消息数组，按顺序渲染。 |
| `markdown` | `boolean` | `true` | 用 `TxMarkdownView` 渲染正文；关闭时渲染纯文本。 |
| `stagger` | `boolean` | `true` | 用 `TxStagger` 播放消息入场动画。 |
| `attachmentLabel` | `string` | - | 转发给每条消息：无 `name` 缩略图的可访问名称。 |

#### 事件

| 事件名 | 参数 | 说明 |
|------|------|------|
| `imageClick` | `{ url: string, name?: string, messageId: string }` | 转发 `TxChatMessage` 的 `imageClick`。 |

### TxChatMessage

#### 属性

| 属性名 | 类型 | 默认值 | 说明 |
|------|------|---------|------|
| `message` | `ChatMessageModel` | 必填 | 单条消息数据。 |
| `markdown` | `boolean` | `true` | 以 Markdown 或纯文本渲染正文。 |
| `attachmentLabel` | `string` | `'Open image attachment'` | 无 `name` 的图片缩略图的可访问名称。 |

#### 事件

| 事件名 | 参数 | 说明 |
|------|------|------|
| `imageClick` | `{ url: string, name?: string, messageId: string }` | 点击图片缩略图时触发。 |

#### 插槽

| 插槽名 | Props | 说明 |
|------|------|------|
| `avatar` | `{ message: ChatMessageModel }` | 替换头像。 |
| `header` | `{ message: ChatMessageModel }` | 替换时间头部。 |
| `content` | `{ message: ChatMessageModel }` | 替换正文渲染。 |

### 类型

#### ChatMessageModel

| 字段 | 类型 | 默认值 | 说明 |
|------|------|---------|------|
| `id` | `string` | 必填 | 消息的稳定 key。 |
| `role` | `'user' \| 'assistant' \| 'system'` | 必填 | 消息角色，决定行的对齐。 |
| `content` | `string` | 必填 | Markdown 或纯文本正文。 |
| `createdAt` | `number` | - | epoch 时间戳，挂载后渲染为 `HH:mm`。 |
| `avatarUrl` | `string` | - | 头像图片地址。 |
| `attachments` | `ChatMessageAttachment[]` | - | 以缩略图渲染的图片附件。 |

#### ChatMessageAttachment

| 字段 | 类型 | 默认值 | 说明 |
|------|------|---------|------|
| `type` | `'image'` | 必填 | 附件类型，目前只有图片。 |
| `url` | `string` | 必填 | 缩略图地址，随 `imageClick` 发出。 |
| `name` | `string` | - | 缩略图按钮的可访问名称，随 `imageClick` 发出。 |

## 概述

- `TxChatList` 按 `messages` 顺序渲染 `TxChatMessage`，以 `id` 作 key。
- 图片附件是原生 `<button type="button">` 缩略图，图片懒加载；可访问名称在按钮上，`<img>` 只作装饰。
- `createdAt` 挂载后才格式化，SSR 与客户端输出一致。

## 技术实现

- 源码：`packages/tuffex/packages/components/src/chat/`（`TxChatList.vue`、`TxChatMessage.vue`、`types.ts`）。

<TuffDocSourceLink />

## 相关组件

- [ChatComposer](./chat-composer.zh.mdc)：文本输入、附件与发送。
- [TypingIndicator](./typing-indicator.zh.mdc)：助手输入中状态。
- [AI Elements](./ai-elements.zh.mdc)：需要 `tool` 消息或流式状态时优先使用。
