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

Laravel 8+Livewire私聊实现新消息未读角标点击隐藏

Laravel + Livewire 私聊未读消息角标实现方案

1. 数据库字段调整

首先需要给消息表新增已读状态标记,区分已读/未读消息:

  • 执行命令生成迁移文件:
php artisan make:migration add_is_read_to_messages_table --table=messages
  • 在迁移文件的up方法中添加字段:
public function up()
{
    Schema::table('messages', function (Blueprint $table) {
        $table->boolean('is_read')->default(false)->after('body');
    });
}
  • 执行迁移生效:
php artisan migrate

2. 调整会话列表查询逻辑

原有用户列表查询需要追加两个核心数据:和当前用户的最后一条会话消息、对方发给当前用户的未读消息总数,注意未读计数只统计「对方发送、当前用户接收、未标记已读」的消息,避免把自己发的消息算成未读。
在Livewire组件的渲染逻辑中替换原有$users查询:

use Illuminate\Support\Facades\DB;

// 在render方法或者mount方法中添加
$currentUserId = auth()->id();
$users = User::where('id', '!=', $currentUserId)
    ->with([
        // 预加载和当前用户的最后一条会话消息
        'messages' => function($query) use ($currentUserId) {
            $query->where(function($q) use ($currentUserId) {
                $q->where('sender_id', $currentUserId)
                  ->where('receiver_id', DB::raw('users.id'));
            })->orWhere(function($q) use ($currentUserId) {
                $q->where('receiver_id', $currentUserId)
                  ->where('sender_id', DB::raw('users.id'));
            })->latest()->take(1);
        }
    ])
    // 统计每个会话的未读消息数
    ->withCount([
        'messages as unread_count' => function($query) use ($currentUserId) {
            $query->where('sender_id', DB::raw('users.id'))
                  ->where('receiver_id', $currentUserId)
                  ->where('is_read', false);
        }
    ])
    ->get();

请提前确认User模型中已经定义好和Message模型的一对多关联:public function messages() { return $this->hasMany(Message::class); }


3. 打开会话时自动标记消息已读

修改原有viewMessages方法,在选中会话用户时,将该会话下所有对方发给当前用户的未读消息批量标记为已读:

public function viewMessages($userId)
{
    $this->selectedUser = User::findOrFail($userId);
    // 批量标记未读消息为已读
    Message::where('sender_id', $userId)
        ->where('receiver_id', auth()->id())
        ->where('is_read', false)
        ->update(['is_read' => true]);
    
    // 保留你原有加载会话消息的逻辑即可
    $this->messages = Message::where(function($query) use ($userId) {
        $query->where('sender_id', auth()->id())->where('receiver_id', $userId);
    })->orWhere(function($query) use ($userId) {
        $query->where('sender_id', $userId)->where('receiver_id', auth()->id());
    })->latest()->get();
}

4. 修改视图按条件渲染角标

替换原有视图中写死的角标代码,根据未读计数判断是否显示角标,同时修正最后一条消息的前缀显示逻辑(只有自己发的消息才显示You:前缀):

@if(count($users) > 0)
  @foreach ($users as $user)
    <a href="#" class="user"  style="color:#CB6F53"  wire:click.prevent="viewMessages({{ $user->id }} )">
      <div class="user-card rounded {{ $user->id === $selectedUser->id ? 'bg-dark' : '' }} bg-opacity-10 mb-1  rounded mb-1">
        <div class="mx-2">
          <div class="d-flex pt-3">
            @if($user->avator != Null)
              <img class="rounded-circle" width="48px" height="48px" src="{{Config('wfh.file').$user->avator}}" alt="">
            @else
              <img class="rounded-circle" width="48px" height="48px" src="{{url('')}}/uploads/images/php55DB.tmp.png" alt="">
            @endif

            <div class="notification-text ms-3 w-100">
              <span class="username fw-bold">{{$user->full_name}}</span>
              @if($user->messages->last()->created_at ?? null)
                @php
                  $newDate = date("d-m-Y", strtotime($user->messages->last()->created_at));
                @endphp
                <span class="float-end small">{{ $newDate }}</span>
              @endif
              @if($user->messages->last()->body ?? null)
                <p class="mt-1 text-muted">
                  @if($user->messages->last()->sender_id === auth()->id())
                    You:
                  @endif
                  {{$user->messages->last()->body}}
                  {{-- 未读数大于0时才显示角标 --}}
                  @if($user->unread_count > 0)
                    <span class="badge bg-danger float-end mt-1">{{$user->unread_count}}</span>
                  @endif
                </p>
              @else
                <p class="mt-1 text-muted">暂无聊天记录</p>
              @endif
            </div>
          </div>
        </div>
      </div>
    </a>
  @endforeach
@else
  <p class="text-center"><b>User not found !</b></p>
@endif

5. 实时更新角标(可选)

如果需要新消息到达时无需刷新页面自动显示角标,可以用Livewire事件实现:

  • 在发送消息的方法中,消息保存成功后触发全局事件:
// 消息入库逻辑执行完成后添加
$this->dispatch('newMessageNotify', receiverId: $this->selectedUser->id);
  • 在会话列表对应的Livewire组件中添加事件监听,收到事件时自动刷新组件数据:
protected $listeners = ['newMessageNotify' => '$refresh'];

内容的提问来源于stack exchange,提问作者zubair syslab

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 12:01:04