You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Vuetify的简易聊天布局:实现聊天记录自下而上增长

Vuetify 极简聊天布局(自下而上消息流)

我太懂这种被CSS布局折腾到心态爆炸的感觉了!下面这个方案完全贴合你的需求——几乎全用Vuetify原生组件和指令,只加了一丢丢必要的自定义CSS,完美实现自下而上增长的聊天记录,还包含你要的三主列布局+底部输入栏。

<template>
  <v-app>
    <!-- 全屏主容器 -->
    <v-container fluid class="pa-0" style="height: 100vh;">
      <v-row no-gutters class="h-100">
        <!-- 左侧联系人侧边栏 -->
        <v-col cols="3" class="bg-gray-light">
          <v-card class="h-100">
            <v-card-title class="text-h6">联系人</v-card-title>
            <v-list dense>
              <v-list-item active>
                <v-list-item-title>张三</v-list-item-title>
              </v-list-item>
              <v-list-item>
                <v-list-item-title>李四</v-list-item-title>
              </v-list-item>
            </v-list>
          </v-card>
        </v-col>

        <!-- 中间核心聊天区域 -->
        <v-col cols="6" class="d-flex flex-column">
          <!-- 聊天头部 -->
          <v-card class="mb-0">
            <v-card-title>
              <span class="text-h6">与张三聊天</span>
            </v-card-title>
          </v-card>

          <!-- 聊天记录容器(自下而上核心) -->
          <v-card-text
            class="chat-messages-container flex-grow-1 overflow-auto"
            ref="chatContainer"
          >
            <div class="chat-messages">
              <!-- 静态示例消息 -->
              <v-chip class="message mb-2" color="blue-lighten-3" text-color="black">
                今天天气不错啊!
              </v-chip>
              <v-chip class="message mb-2 self" color="green-lighten-3" text-color="black">
                确实~ 终于把聊天布局搞定了😭
              </v-chip>
              
              <!-- 动态消息列表(替换静态示例) -->
              <!-- <v-chip 
                v-for="msg in messages" 
                :key="msg.id"
                class="message mb-2"
                :class="{ self: msg.isSelf }"
                :color="msg.isSelf ? 'green-lighten-3' : 'blue-lighten-3'"
                text-color="black"
              >
                {{ msg.content }}
              </v-chip> -->
            </div>
          </v-card-text>

          <!-- 底部输入栏 -->
          <v-card class="mt-0">
            <v-container fluid class="pa-2">
              <v-row align="center">
                <v-col cols="10">
                  <v-text-field
                    v-model="newMessage"
                    placeholder="输入消息..."
                    outlined
                    dense
                    @keyup.enter="sendMessage"
                  ></v-text-field>
                </v-col>
                <v-col cols="2">
                  <v-btn color="primary" block @click="sendMessage">发送</v-btn>
                </v-col>
              </v-row>
            </v-container>
          </v-card>
        </v-col>

        <!-- 右侧用户资料侧边栏 -->
        <v-col cols="3" class="bg-gray-light">
          <v-card class="h-100">
            <v-card-title class="text-h6">张三的资料</v-card-title>
            <v-card-text>
              <p>昵称:张三</p>
              <p>在线状态:在线</p>
            </v-card-text>
          </v-card>
        </v-col>
      </v-row>
    </v-container>
  </v-app>
</template>

<script>
export default {
  data() {
    return {
      newMessage: '',
      messages: [
        { id: 1, content: '今天天气不错啊!', isSelf: false },
        { id: 2, content: '确实~ 终于把聊天布局搞定了😭', isSelf: true }
      ]
    }
  },
  methods: {
    sendMessage() {
      if (!this.newMessage.trim()) return
      
      // 添加自己的消息
      this.messages.push({
        id: Date.now(),
        content: this.newMessage.trim(),
        isSelf: true
      })
      
      // 模拟对方回复(可选)
      setTimeout(() => {
        this.messages.push({
          id: Date.now() + 1,
          content: '哈哈,CSS布局确实坑!',
          isSelf: false
        })
        this.$nextTick(() => this.$refs.chatContainer.scrollTop = 0)
      }, 1000)
      
      this.newMessage = ''
      // 发送后自动滚动到最新消息
      this.$nextTick(() => this.$refs.chatContainer.scrollTop = 0)
    }
  },
  mounted() {
    // 初始化时滚动到最新消息
    this.$nextTick(() => this.$refs.chatContainer.scrollTop = 0)
  }
}
</script>

<style scoped>
/* 仅需这几行自定义CSS实现核心需求 */
.chat-messages-container {
  display: flex;
  flex-direction: column-reverse;
}

.chat-messages {
  display: flex;
  flex-direction: column;
}

/* 消息对齐样式:对方左对齐,自己右对齐 */
.message {
  max-width: 70%;
  align-self: flex-start;
}

.message.self {
  align-self: flex-end;
}
</style>

核心设计亮点

  1. 自下而上消息流:通过flex-direction: column-reverse让聊天容器的内容从底部开始堆叠,新消息添加到DOM末尾后,视觉上自动出现在底部,完美解决“自下而上增长”的需求,比手动计算滚动高度简单100倍。
  2. 最大化复用Vuetify组件:全程用v-app、v-container、v-row、v-col实现响应式三列布局,v-card、v-text-field、v-btn构建聊天界面,几乎没有自定义组件,保持Vuetify风格一致性。
  3. 自动滚动到最新消息:利用Vue的$nextTick确保DOM更新完成后,将滚动条设置到容器顶部(因为column-reverse布局,顶部对应视觉上的最新消息位置),避免了复杂的滚动监听逻辑。
  4. 最小化自定义CSS:只写了8行样式——实现reverse布局、消息对齐,其余全靠Vuetify内置类(比如flex-grow-1让聊天区域占满剩余高度,overflow-auto提供滚动能力)。

可扩展优化建议

  • 若需要更精致的消息气泡,可以把v-chip换成v-card,调整border-radius和padding即可。
  • 侧边栏可以替换为v-navigation-drawer组件,实现可折叠的侧边栏效果。
  • 当消息数量过多时,推荐使用Vuetify的v-virtual-scroll组件实现虚拟滚动,提升页面性能。

内容的提问来源于stack exchange,提问作者Lucien Chardon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 09:42:37