【Java项目-轻聊】07-实现主页面模块

✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨

🎯 你正在阅读「Java项目-轻聊」系列文章 🎯

✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨

🔥 弹简特 个人主页

❄️ 个人专栏直通车:

靠热爱去书写自己,靠勇敢去书写生活!

✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨


🌟 博主简介:


本期实现我们的登录成功之后跳转到主页面的前端实现

文章目录:


主页面模块

1、页面原型

1.1 初始登录原型图

用户登录成功之后会显示的页面如下:

  • 左侧默认选中的是会话列表

1.2 好友列表原型图

1.3 聊天框原型图

1.4 搜索好友请求项列表

1)未搜索到结果
2)搜索到结果

1.4 好友请求弹窗

1)发送方
2)接收方

2、实现页面基本框架-1

2.1 代码实现

新建 client.html 以及 client.css,并引入全站公共样式 common.css(清除默认 margin、html/body 高度 100%)。

填入初始 html 代码:

html 复制代码
<!DOCTYPE html> 
<html lang="zh-CN"> 
<head>
    <meta charset="UTF-8"> 
    <meta name="viewport" content="width=device-width, initial-scale=1.0"> 
    <title>轻聊</title> 
    <link rel="stylesheet" href="css/common.css"> <!-- 全局重置与基础布局 -->
    <link rel="stylesheet" href="css/client.css"> <!-- 类微信双栏聊天界面样式 -->
</head>
<body> 

<div class="cover"></div> <!-- 全屏背景图(app-bg.jpg),衬在聊天窗口后方 -->

<div class="client-container"> <!-- 外层居中容器,使主窗口浮于背景中央 -->
    <div class="main"> <!-- 1000×740 主面板:左栏 + 右栏 -->
        <div class="left"></div> <!-- 左侧:当前用户、搜索、Tab、会话/好友列表 -->
        <div class="right"></div> <!-- 右侧:标题 + 聊天区 或 搜索结果区 -->
    </div>
</div>
</body>
</html>

填入初始 css 代码:

css 复制代码
/* 聊天主界面样式:类微信双栏,色调与登录注册页统一 */

/* .client-container:整页居中包裹,使 .main 浮于背景中央 */
.client-container {
    height: 100%; /* 继承 html/body 满高 */
    display: flex;
    justify-content: center; /* 水平居中主窗口 */
    align-items: center; /* 垂直居中 */
}

/* .main:固定尺寸聊天主窗口(左栏+右栏) */
.main {
    width: 1000px;
    height: 740px;
    display: flex; /* 左右分栏 */
    border-radius: 14px;
    overflow: hidden; /* 圆角裁剪子元素 */
    z-index: 2; /* 浮在 .cover 之上 */
    background: #f7f8fa;
}

/* .main .left:会话/好友列表侧栏,固定宽度 */
.main .left {
    width: 280px;
    height: 100%;
    background-color: #2f3542; /* 左栏深灰背景 */
}

.main .right {
    flex: 1; /* 占据主窗口剩余宽度 */
    min-width: 0;
}

2.2 效果图


3、实现页面基本框架-2【背景与主窗口层叠】

3.1 代码实现

思路:.cover 铺满窗口当背景图;.main 用更大的 z-index 叠在上面,形成「中间一块聊天窗口、四周是背景图」的效果。

1、.cover 与窗口同大,绝对定位:

css 复制代码
/* .cover:全屏背景图,位于主窗口下层 */
.cover {
    position: absolute; /* 相对 body 定位铺满 */
    inset: 0;
    z-index: 1; /* 低于 .main 的 z-index:2 */
}

2、背景图放在 img/app-bg.jpg(与登录页风格统一):

css 复制代码
.cover {
    background-image: url(../img/app-bg.jpg); /* 应用主背景 */
    background-color: #6b8cae; /* 图片加载前兜底色 */
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}

3、主窗口压在上面:

css 复制代码
.main {
    z-index: 2; /* 浮在 .cover 之上 */
    box-shadow: 0 12px 40px rgba(30, 50, 80, 0.12); /* 主窗口投影 */
    border: 1px solid rgba(255, 255, 255, 0.65); /* 浅色描边 */
}

3.2 效果图

说明:本项目的 client.css 没有 对背景做 filter: blur 模糊,而是清晰背景 + 中间卡片;若需要朦胧感可自行给 .coverfilter: blur(20px)


4、实现顶栏:头像、用户名、退出

4.1 代码实现

添加头像图标


登录成功后,client.js 会调 /userInfo 把用户名写到 .user-name,头像可走 /avatars/xxx/user/getAvatar

html 复制代码
<div class="left"> <!-- 左侧:当前用户、搜索、Tab、会话/好友列表 -->
    <div class="user" id="user-bar"> <!-- 顶栏:头像、昵称、退出;JS 写入 user-id -->
        <label class="avatar-upload-label" title="点击更换头像"> <!-- 点击触发隐藏 file 选择头像 -->
            <img class="avatar-img avatar-lg" id="my-avatar" src="/img/default-avatar.svg" alt="我的头像"> <!-- 当前用户头像 -->
            <input type="file" id="avatar-file-input" accept=".jpg,.jpeg,.png,image/jpeg,image/png" hidden> <!-- 仅 jpg/png -->
        </label>
        <span class="user-name">李四</span> <!-- 登录用户名,getUserInfo 填充 -->
        <button type="button" class="logout-btn" id="logout-btn" title="退出登录">退出</button> <!-- 调用 /logout -->
    </div>
</div>
css 复制代码
.main .left .user {
    height: 68px;
    padding: 0 16px 0 20px;
    display: flex;
    align-items: center;
    gap: 10px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

.avatar-img {
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}

.avatar-lg { width: 44px; height: 44px; }

.avatar-upload-label {
    cursor: pointer;
    display: flex;
}

.avatar-upload-label .avatar-lg {
    border: 2px solid rgba(110, 231, 183, 0.55);
}

.main .left .user .user-name {
    flex: 1;
    min-width: 0;
    font-size: 17px;
    font-weight: 600;
    color: #fff;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.main .left .user .logout-btn {
    padding: 5px 12px;
    font-size: 12px;
    color: rgba(255, 255, 255, 0.88);
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.22);
    border-radius: 6px;
    cursor: pointer;
}

4.2 效果图


5、实现搜索框【加好友入口】

5.1 代码实现

输入用户名点搜索,右侧会切到「好友查询结果」面板(见后文);图标用 img/搜索.png

html 复制代码
<div class="search">
    <input type="text" id="search-input" placeholder="搜索用户名">
    <button id="search-btn" type="button" title="搜索用户"></button>
</div>
css 复制代码
.main .left .search {
    padding: 14px 16px 10px;
    display: flex;
    gap: 8px;
}

.main .left .search input {
    flex: 1;
    height: 38px;
    padding: 0 14px;
    font-size: 13px;
    color: #eee;
    background: rgba(0, 0, 0, 0.2);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 10px;
    outline: none;
}

.main .left .search button {
    width: 38px;
    height: 38px;
    border: none;
    border-radius: 10px;
    background: rgba(0, 0, 0, 0.2) url(../img/搜索.png) center / 18px no-repeat;
    cursor: pointer;
}

5.2 效果图


6、实现会话和好友标签页按钮

6.1 代码实现

img 目录准备两套图:选中 / 未选中 (对话.png、对话2.png、用户.png、用户2.png)。点击切换由 client.jsinitSwitchTab()background-image 和列表的 hide 类。

html 复制代码
<div class="tab">
    <div class="tab-session"></div>
    <div class="tab-friend"></div>
</div>
css 复制代码
.main .left .tab {
    height: 50px;
    display: flex;
    padding: 0 12px;
    gap: 4px;
    align-items: center;
}

.main .left .tab .tab-session,
.main .left .tab .tab-friend {
    flex: 1;
    height: 40px;
    cursor: pointer;
    border-radius: 10px;
    background-repeat: no-repeat;
    background-position: center;
    background-size: 24px;
    opacity: 0.5;
}

.main .left .tab .tab-session {
    background-image: url(../img/对话.png);
}

.main .left .tab .tab-friend {
    background-image: url(../img/用户2.png);
}

6.2 效果图


7、实现会话列表

7.1 代码实现

会话项 不写死 在 html 里,只留空 ul,(但是此处为了方便效果的演示我们先将他写死),登录后由 JS 根据 /sessionList 渲染。结构上预留:头像 + 昵称 + 消息预览 + 未读角标

html 复制代码
<ul class="list" id="session-list"></ul>

JS 拼出的单行大致结构(便于理解 css 选择器):

html 复制代码
<li message-session-id="1" data-friend-id="2">
    <div class="session-item-inner">
        <img class="avatar-img avatar-md" src="..." alt="">
        <div class="session-main">
            <div class="session-row-top">
                <span class="session-name">李四</span>
            </div>
            <p class="session-preview">晚上吃什么</p>
        </div>
        <span class="unread-badge hide">3</span>
    </div>
</li>

写死的话,是如下所示:

左栏用 flex 纵向布局 ,列表区 flex: 1 自动占满剩余高度:

css 复制代码
.main .left {
    display: flex;
    flex-direction: column;
}

.main .left .list {
    flex: 1;
    overflow-y: auto;
    list-style: none;
    padding: 6px 8px;
}

.main .left .list li {
    padding: 10px 12px;
    margin-bottom: 4px;
    color: #e8eaed;
    border-radius: 10px;
    cursor: pointer;
}

.main .left .list li .session-item-inner {
    display: flex;
    align-items: center;
    gap: 10px;
}

.main .left .list li.selected {
    background: #3d4654;
    border-color: rgba(91, 141, 239, 0.25);
}

/* 红色未读角标,纯前端 unreadCounts 驱动 */
.main .left .list .unread-badge {
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    font-size: 12px;
    line-height: 20px;
    text-align: center;
    color: #fff;
    background: #fa5151;
    border-radius: 10px;
}

.main .left .list .unread-badge.hide {
    display: none;
}

7.2 效果图


8、实现好友列表

8.1 代码实现

与「会话」互斥显示:默认给好友列表加 hide

html 复制代码
<ul class="list hide" id="friend-list"></ul>
css 复制代码
.main .left .list li .list-row-with-avatar {
    display: flex;
    align-items: center;
    gap: 10px;
}

.main .left .list li h4 {
    font-size: 15px;
    color: #e8eaed;
    font-weight: 500;
}

.hide {
    display: none !important;
}

9、实现好友请求置顶

别人发来好友申请时,会在 #session-list 即会话列表的最上面 插入带 friend-request-itemli,样式偏橙黄,和普通会话区分开。

css 复制代码
.main .left .list li.friend-request-item {
    display: flex;
    align-items: center;
    gap: 10px;
    background: rgba(250, 173, 20, 0.1);
    border-color: rgba(250, 173, 20, 0.2);
}

.main .left .list li.friend-request-item h3::before {
    content: "请求 · ";
    color: #e8b339;
    font-size: 12px;
}

10、美化滚动条

本项目没有把整个滚动条藏掉,而是改成 6px 细条,左栏用半透明白色 thumb,更贴合深色侧栏。

css 复制代码
::-webkit-scrollbar {
    width: 6px;
}

::-webkit-scrollbar-thumb {
    background: rgba(0, 0, 0, 0.15);
    border-radius: 3px;
}

.main .left .list::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.15);
}

11、实现标签页切换(引入 client.js)

11.1 代码实现

  1. 新建 js/client.js

  2. client.html 底部引入 jQuery 与业务脚本:

html 复制代码
<script src="https://libs.baidu.com/jquery/2.0.3/jquery.min.js"></script>
<script src="js/client.js"></script>
  1. client.js 写入(含 showChatPanel,避免第 12 节还没写 HTML 就报错)
javascript 复制代码
// 显示右侧聊天区,隐藏搜索结果区(第 12 节写 HTML 后生效)
function showChatPanel() {
    let chatPanel = document.querySelector('.right .chat-panel');
    let searchPanel = document.querySelector('.right .search-result-panel');
    if (chatPanel) chatPanel.classList.remove('hide');
    if (searchPanel) searchPanel.classList.add('hide');
}

// 初始化左侧「会话 / 好友」Tab 切换
function initSwitchTab() {
    let tabSession = document.querySelector('.tab .tab-session');
    let tabFriend = document.querySelector('.tab .tab-friend');
    let lists = document.querySelectorAll('.list');

    tabSession.onclick = function() {
        tabSession.style.backgroundImage = 'url(img/对话.png)';
        tabFriend.style.backgroundImage = 'url(img/用户2.png)';
        lists[0].classList = 'list';
        lists[1].classList = 'list hide';
        let selectedLi = document.querySelector('#session-list .selected');
        if (selectedLi && !selectedLi.classList.contains('friend-request-item')) {
            showChatPanel();
        }
    };

    tabFriend.onclick = function() {
        tabSession.style.backgroundImage = 'url(img/对话2.png)';
        tabFriend.style.backgroundImage = 'url(img/用户.png)';
        lists[0].classList = 'list hide';
        lists[1].classList = 'list';
        let chatPanel = document.querySelector('.right .chat-panel');
        if (chatPanel) chatPanel.classList.add('hide');
    };
}

// DOM 就绪后初始化各模块 -- 记得调用函数否则不生效
$(document).ready(function() {
    initSwitchTab(); // 会话/好友 标签Tab切换
});

路径以 html 所在目录 为基准,故写 url(img/对话.png)

记得调用函数

11.2 效果图

11.3 说明

  • 此时右侧还没写,点「好友 Tab」只会藏聊天区(找不到元素也不报错)。
  • 第 12 节写完 .right,Tab 切换才完整生效。

12、实现右侧区域-1【聊天面板】

首先在顶部放置以下样式

css 复制代码
/* :root:CSS 变量,侧边栏/强调色/右侧背景等主题 token */
:root {
    --sidebar-bg: #2f3542; /* 左栏深灰背景 */
    --sidebar-hover: rgba(255, 255, 255, 0.07); /* 列表项悬停高亮 */
    --sidebar-active: #3d4654; /* 当前选中会话背景 */
    --accent: #5b8def; /* 主强调蓝(按钮、边框) */
    --right-bg: #f7f8fa; /* 右侧区域浅灰底 */
    --card-shadow: 0 12px 40px rgba(30, 50, 80, 0.12); /* 主窗口投影 */
}

12.1 代码实现

右侧拆成两块:聊天 chat-panel(默认显示)和 搜索结果 search-result-panel(搜索时显示),同一时刻只露一个。

html 复制代码
<div class="right">
    <div class="title"></div>

    <div class="chat-panel">
        <div class="message-show"></div>
        <textarea class="message-input"></textarea>
        <div class="ctrl">
            <button type="button">发送</button>
        </div>
    </div>

    <div class="search-result-panel hide">
        <div class="search-result-hint">好友查询结果</div>
        <ul id="search-result-list"></ul>
    </div>
</div>
css 复制代码
/********************聊天-begin***********************/

.main .right {
    flex: 1;
    display: flex;
    flex-direction: column;
    background: var(--right-bg);
    min-width: 0;
}

.main .right .title {
    height: 58px;
    line-height: 58px;
    padding: 0 24px;
    font-size: 16px;
    font-weight: 600;
    color: #2c3e50;
    text-align: center;
    background: #fff;
    border-bottom: 1px solid #e8ecf1;
    flex-shrink: 0;
    box-shadow: 0 1px 0 rgba(0, 0, 0, 0.03);
}

.main .right .title.title-with-avatar {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    line-height: normal;
}

.main .right .title .title-name {
    font-size: 16px;
    font-weight: 600;
}

.main .right .chat-panel {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
    background: #fff;
    margin: 0;
}

.main .right .chat-panel.hide {
    display: none;
}

.main .right .search-result-panel {
    flex: 1;
    overflow-y: auto;
    padding: 16px 22px 24px;
    background: var(--right-bg);
}

.main .right .search-result-panel.hide {
    display: none;
}

/********************聊天-end***********************/

12.2 效果图


13、实现右侧区域-2【聊天框中每一条消息气泡与头像】

13.1 代码实现

对方消息靠左、自己靠右;每条消息 头像 + 气泡,不再单独写 h4 名字(名字在列表里已有)。

静态调试可先写两条:

html 复制代码
<div class="message message-left">
    <img class="avatar-img avatar-sm" src="/img/default-avatar.svg" alt="">
    <div class="bubble">
        <p class="bubble-text">今晚出去玩?</p>
    </div>
</div>
<div class="message message-right">
    <div class="bubble">
        <p class="bubble-text">不太想去</p>
    </div>
    <img class="avatar-img avatar-sm" src="/img/default-avatar.svg" alt="">
</div>

添加聊天背景图

css 追加 (与工程 client.css 一致):

css 复制代码
/********************消息气泡与头像-begin***********************/

.main .right .message-show {
    flex: 1;
    overflow-y: auto;
    padding: 22px 26px;
    background-color: #f0f2f5;
    background-image: url(../img/chat-bg.jpg);
    background-size: cover;
    background-position: center;
}

.main .right .message-show .message {
    display: flex;
    align-items: flex-end;
    gap: 10px;
    margin-bottom: 16px;
    clear: both;
}

.main .right .message-show .message-left {
    justify-content: flex-start;
}

.main .right .message-show .message-right {
    justify-content: flex-end;
}

.main .right .message-show .message .avatar-sm {
    width: 36px;
    height: 36px;
    margin-bottom: 2px;
}

.main .right .message-show .message .bubble {
    max-width: min(68%, 420px);
    padding: 10px 14px;
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
}

.main .right .message-show .message-left .bubble {
    background: #fff;
    border: 1px solid #e8ecf1;
    border-radius: 4px 14px 14px 14px;
}

.main .right .message-show .message-right .bubble {
    background: #95ec69;
    border: 1px solid rgba(7, 193, 96, 0.15);
    border-radius: 14px 4px 14px 14px;
}

.main .right .message-show .message .bubble-text {
    margin: 0;
    font-size: 14px;
    line-height: 1.55;
    color: #2c3e50;
    word-break: break-word;
}

.main .right .message-show .message-right .bubble-text {
    color: #1a2e1a;
}

/********************消息气泡与头像-end***********************/

13.2 效果图

左白泡、右绿泡,中间聊天背景图。


14、实现右侧区域-3【输入框与发送】

14.1 代码实现

html 第 12 节已写好,本节只追加 css

css 复制代码
/********************输入框与发送-begin***********************/

.main .right .message-input {
    height: 96px;
    padding: 14px 18px;
    font-size: 14px;
    line-height: 1.55;
    border: none;
    border-top: 1px solid #e8ecf1;
    outline: none;
    resize: none;
    background: #fafbfc;
    font-family: inherit;
    color: #2c3e50;
    display: block;
    width: 100%;
    box-sizing: border-box;
    flex-shrink: 0;
}

.main .right .message-input:focus {
    background: #fff;
}

.main .right .ctrl {
    height: 54px;
    padding: 0 20px 14px;
    display: flex;
    justify-content: flex-end;
    align-items: center;
    background: #fafbfc;
    border-top: 1px solid #eef1f5;
    flex-shrink: 0;
}

.main .right .ctrl button {
    height: 38px;
    padding: 0 32px;
    font-size: 14px;
    font-weight: 500;
    color: #fff;
    background: #5b8def;
    border: none;
    border-radius: 10px;
    cursor: pointer;
    transition: background 0.2s, box-shadow 0.2s;
    box-shadow: 0 2px 8px rgba(91, 141, 239, 0.35);
}

.main .right .ctrl button:hover {
    background: #4a7de0;
}

.main .right .ctrl button:active {
    background: #3d6ed4;
    box-shadow: none;
}

/********************输入框与发送-end***********************/

14.2 说明

display:block; width:100%; box-sizing:border-box; flex-shrink:0 这三行很重要,少了输入框会撑破 flex 布局。

14.3 效果图


15、实现好友搜索结果面板

15.1 代码实现

html 第 12 节已写好。本节 css 只写列表样式 ,不要再写 .search-result-panel(第 12 节已有)。

css 复制代码
/********************好友搜索结果-begin***********************/

.main .right .search-result-hint {
    height: 44px;
    line-height: 44px;
    font-size: 14px;
    font-weight: 600;
    color: #5c6b7a;
    padding-left: 4px;
    margin-bottom: 8px;
}

#search-result-list {
    list-style: none;
    padding: 0;
    margin: 0;
}

#search-result-list li {
    padding: 18px 16px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
    background: #fff;
    border: 1px solid #e8ecf1;
    border-radius: 12px;
    margin-bottom: 12px;
    box-shadow: 0 2px 8px rgba(30, 50, 80, 0.04);
    transition: box-shadow 0.2s, border-color 0.2s;
}

#search-result-list li:hover {
    border-color: #d0daf0;
    box-shadow: 0 4px 12px rgba(91, 141, 239, 0.08);
}

#search-result-list .search-user-name {
    font-size: 16px;
    font-weight: 600;
    color: #2c3e50;
    min-width: 72px;
}

#search-result-list .search-reason-input {
    flex: 1;
    min-width: 160px;
    height: 38px;
    padding: 0 14px;
    font-size: 14px;
    border: 1px solid #e0e6ed;
    border-radius: 10px;
    background: #fafbfc;
    outline: none;
    transition: border-color 0.2s;
}

#search-result-list .search-reason-input:focus {
    border-color: #5b8def;
    background: #fff;
}

#search-result-list .search-add-btn {
    height: 38px;
    padding: 0 20px;
    font-size: 13px;
    font-weight: 500;
    border: none;
    border-radius: 10px;
    background: #5b8def;
    color: #fff;
    cursor: pointer;
    transition: background 0.2s;
}

#search-result-list .search-add-btn:hover:not(:disabled) {
    background: #4a7de0;
}

#search-result-list .search-add-btn:disabled {
    background: #c5cdd8;
    cursor: not-allowed;
}

#search-result-list .search-empty {
    padding: 48px 20px;
    text-align: center;
    color: #9aa3b0;
    font-size: 14px;
    background: #fff;
    border-radius: 12px;
    border: 1px dashed #e0e6ed;
}

/********************好友搜索结果-end***********************/

16、实现好友请求弹窗

16.1 代码实现

html (写在 </div><!-- client-container --> 后面、</body> 前面):

html 复制代码
<div id="friend-request-modal" class="friend-request-modal hide">
    <div class="modal-box">
        <h3>好友请求</h3>
        <p class="modal-from"></p>
        <p class="modal-reason"></p>
        <div class="modal-actions">
            <button type="button" id="accept-friend-btn">接受</button>
            <button type="button" id="reject-friend-btn">拒绝</button>
        </div>
    </div>
</div>

css 追加

css 复制代码
/********************好友请求模态框-begin***********************/

.friend-request-modal {
    position: fixed;
    inset: 0;
    z-index: 100;
    background: rgba(44, 62, 80, 0.4);
    display: flex;
    align-items: center;
    justify-content: center;
}

.friend-request-modal.hide {
    display: none;
}

.friend-request-modal .modal-box {
    width: 400px;
    max-width: 90vw;
    padding: 28px 28px 24px;
    background: #fff;
    border-radius: 14px;
    border: 1px solid #e8ecf1;
    box-shadow: var(--card-shadow);
}

.friend-request-modal .modal-box h3 {
    font-size: 18px;
    font-weight: 600;
    color: #2c3e50;
    margin-bottom: 16px;
}

.friend-request-modal .modal-from,
.friend-request-modal .modal-reason {
    font-size: 14px;
    color: #5c6b7a;
    line-height: 1.6;
    margin-bottom: 8px;
}

.friend-request-modal .modal-actions {
    margin-top: 22px;
    display: flex;
    justify-content: flex-end;
    gap: 10px;
}

.friend-request-modal .modal-actions button {
    height: 38px;
    padding: 0 22px;
    font-size: 14px;
    border-radius: 10px;
    border: 1px solid #e0e6ed;
    background: #fff;
    color: #5c6b7a;
    cursor: pointer;
    transition: background 0.2s;
}

.friend-request-modal #accept-friend-btn {
    background: #5b8def;
    color: #fff;
    border-color: #5b8def;
}

.friend-request-modal #accept-friend-btn:hover {
    background: #4a7de0;
}

/********************好友请求模态框-end***********************/

17、完整代码

17.1 完整HTML代码

html 复制代码
<!DOCTYPE html> <!-- HTML5 文档类型 -->
<html lang="zh-CN"> <!-- 聊天主界面,简体中文 -->
<head>
    <meta charset="UTF-8"> <!-- UTF-8 支持中文消息与用户名 -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- 移动端适配 -->
    <title>轻聊</title> <!-- 应用标题 -->
    <link rel="stylesheet" href="css/common.css"> <!-- 全局重置与基础布局 -->
    <link rel="stylesheet" href="css/client.css"> <!-- 类微信双栏聊天界面样式 -->
</head>
<body> <!-- 主逻辑由 js/client.js 驱动,本页仅提供 DOM 骨架 -->

    <div class="cover"></div> <!-- 全屏背景图(app-bg.jpg),衬在聊天窗口后方 -->

    <div class="client-container"> <!-- 外层居中容器,使主窗口浮于背景中央 -->
        <div class="main"> <!-- 1000×740 主面板:左栏 + 右栏 -->

            <div class="left"> <!-- 左侧:当前用户、搜索、Tab、会话/好友列表 -->
                <div class="user" id="user-bar"> <!-- 顶栏:头像、昵称、退出;JS 写入 user-id -->
                    <label class="avatar-upload-label" title="点击更换头像"> <!-- 点击触发隐藏 file 选择头像 -->
                        <img class="avatar-img avatar-lg" id="my-avatar" src="/img/default-avatar.svg" alt="我的头像"> <!-- 当前用户头像,可被 uploadAvatar 更新 -->
                        <input type="file" id="avatar-file-input" accept=".jpg,.jpeg,.png,image/jpeg,image/png" hidden> <!-- 隐藏文件选择,仅 jpg/png -->
                    </label>
                    <span class="user-name"></span> <!-- 登录用户名,getUserInfo 填充 -->
                    <button type="button" class="logout-btn" id="logout-btn" title="退出登录">退出</button> <!-- 调用 /logout 并关闭 WebSocket -->
                </div>
                <div class="search"> <!-- 按用户名搜索陌生人以添加好友 -->
                    <input type="text" id="search-input" placeholder="搜索用户名"> <!-- 搜索关键词 -->
                    <button id="search-btn" type="button" title="搜索用户"></button> <!-- 图标按钮,请求 /search/user -->
                </div>

                <div class="tab"> <!-- 会话 / 好友 两个 Tab 切换 -->
                    <div class="tab-session"></div> <!-- 会话列表 Tab(对话图标) -->
                    <div class="tab-friend"></div> <!-- 好友列表 Tab(用户图标) -->
                </div>

                <ul class="list" id="session-list"></ul> <!-- 会话列表:含聊天会话与置顶的好友请求项,JS 动态渲染 -->
                <ul class="list hide" id="friend-list"></ul> <!-- 好友列表,默认 hide,切 Tab 时显示 -->
            </div>

            <div class="right"> <!-- 右侧:标题 + 聊天区 或 搜索结果区 -->
                <div class="title"></div> <!-- 当前会话对方名称(含头像),或「好友查询结果」 -->

                <!-- 聊天区域:消息历史 + 输入 + 发送 -->
                <div class="chat-panel">
                    <div class="message-show"></div> <!-- 消息气泡滚动区,WebSocket 与历史消息渲染于此 -->
                    <textarea class="message-input"></textarea> <!-- 多行输入框 -->
                    <div class="ctrl">
                        <button type="button">发送</button> <!-- 经 WebSocket 发送 type=message JSON -->
                    </div>
                </div>

                <!-- 好友搜索结果区域:搜索时显示,与 chat-panel 互斥 -->
                <div class="search-result-panel hide">
                    <div class="search-result-hint">好友查询结果</div> <!-- 结果区标题 -->
                    <ul id="search-result-list"></ul> <!-- 用户列表:用户名、添加理由、添加按钮 -->
                </div>
            </div>
        </div>
    </div>

    <!-- 好友请求弹窗:WebSocket FRIEND_REQUEST 或点击列表项时展示 -->
    <div id="friend-request-modal" class="friend-request-modal hide">
        <div class="modal-box">
            <h3>好友请求</h3> <!-- 弹窗标题 -->
            <p class="modal-from"></p> <!-- 请求发起人,JS 填充 -->
            <p class="modal-reason"></p> <!-- 添加理由 -->
            <div class="modal-actions">
                <button type="button" id="accept-friend-btn">接受</button> <!-- POST /friend/handle action=accept -->
                <button type="button" id="reject-friend-btn">拒绝</button> <!-- POST /friend/handle action=reject -->
            </div>
        </div>
    </div>

    <script src="https://libs.baidu.com/jquery/2.0.3/jquery.min.js"></script> <!-- AJAX:userInfo、sessionList、friendList 等 -->
    <script src="js/client.js"></script> <!-- 聊天室核心业务:WebSocket、未读、会话、好友 -->
</body>
</html>

17.2 完整CSS代码

css 复制代码
/* 聊天主界面样式:类微信双栏,色调与登录注册页统一 */

/* :root:CSS 变量,侧边栏/强调色/右侧背景等主题 token */
:root {
    --sidebar-bg: #2f3542; /* 左栏深灰背景 */
    --sidebar-hover: rgba(255, 255, 255, 0.07); /* 列表项悬停高亮 */
    --sidebar-active: #3d4654; /* 当前选中会话背景 */
    --accent: #5b8def; /* 主强调蓝(按钮、边框) */
    --accent-soft: rgba(91, 141, 239, 0.15); /* 浅色强调(未直接使用可扩展) */
    --right-bg: #f7f8fa; /* 右侧区域浅灰底 */
    --card-shadow: 0 12px 40px rgba(30, 50, 80, 0.12); /* 主窗口投影 */
}

/* .client-container:整页居中包裹,使 .main 浮于背景中央 */
.client-container {
    height: 100%; /* 继承 html/body 满高 */
    display: flex;
    justify-content: center; /* 水平居中主窗口 */
    align-items: center; /* 垂直居中 */
    font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
}

/* .cover:全屏背景图,位于主窗口下层 */
.cover {
    position: absolute; /* 相对 body 定位铺满 */
    inset: 0;
    z-index: 1; /* 低于 .main 的 z-index:2 */
    background-image: url(../img/app-bg.jpg); /* 应用主背景 */
    background-color: #6b8cae; /* 图片加载前兜底色 */
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}

/* .main:固定尺寸聊天主窗口(左栏+右栏) */
.main {
    width: 1000px;
    height: 740px;
    display: flex; /* 左右分栏 */
    border-radius: 14px;
    overflow: hidden; /* 圆角裁剪子元素 */
    box-shadow: var(--card-shadow);
    border: 1px solid rgba(255, 255, 255, 0.65); /* 浅色描边 */
    z-index: 2; /* 浮在 .cover 之上 */
    background: var(--right-bg);
}

/* ========== 左侧栏 ========== */

/* .main .left:会话/好友列表侧栏,固定宽度 */
.main .left {
    width: 280px;
    height: 100%;
    background: var(--sidebar-bg);
    display: flex;
    flex-direction: column; /* 上到下:用户栏、搜索、Tab、列表 */
}

/* .user:顶部当前用户区(头像、昵称、退出) */
.main .left .user {
    height: 68px;
    padding: 0 16px 0 20px;
    display: flex;
    align-items: center;
    gap: 10px; /* 头像、名字、按钮间距 */
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.04) 0%, transparent 100%); /* 顶栏微亮 */
}

/* ---------- 头像通用:圆形裁剪 ---------- */
.avatar-img {
    border-radius: 50%; /* 圆形头像 */
    object-fit: cover; /* 裁剪填充不变形 */
    background: rgba(255, 255, 255, 0.12); /* 加载前占位色 */
    flex-shrink: 0; /* 不被 flex 挤压变形 */
}

.avatar-sm { width: 32px; height: 32px; } /* 消息气泡旁小头像 */
.avatar-md { width: 40px; height: 40px; } /* 列表项中等头像 */
.avatar-lg { width: 44px; height: 44px; } /* 顶栏「我的头像」 */

/* .avatar-upload-label:点击上传头像的可点击区域 */
.avatar-upload-label {
    cursor: pointer;
    display: flex;
    flex-shrink: 0;
    position: relative;
}

/* 顶栏大头像绿色描边,提示可点击更换 */
.avatar-upload-label .avatar-lg {
    border: 2px solid rgba(110, 231, 183, 0.55);
    transition: opacity 0.15s ease, box-shadow 0.15s ease;
}

.avatar-upload-label:hover .avatar-lg {
    opacity: 0.9;
    box-shadow: 0 0 0 3px rgba(91, 141, 239, 0.35); /* 悬停蓝色光晕 */
}

/* .user-name:当前登录用户名,过长省略号 */
.main .left .user .user-name {
    flex: 1; /* 占据中间剩余空间 */
    min-width: 0; /* 允许 flex 子项收缩以触发 ellipsis */
    font-size: 17px;
    font-weight: 600;
    color: #fff;
    letter-spacing: 0.3px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* .logout-btn:退出登录,描边轻按钮 */
.main .left .user .logout-btn {
    flex-shrink: 0;
    padding: 5px 12px;
    font-size: 12px;
    font-weight: 500;
    color: rgba(255, 255, 255, 0.88);
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.22);
    border-radius: 6px;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.main .left .user .logout-btn:hover {
    color: #fff;
    background: rgba(255, 255, 255, 0.1);
    border-color: rgba(255, 255, 255, 0.38);
}

.main .left .user .logout-btn:active {
    background: rgba(255, 255, 255, 0.06);
}

/* .search:用户名搜索行(输入+图标按钮) */
.main .left .search {
    padding: 14px 16px 10px;
    display: flex;
    gap: 8px;
}

.main .left .search input {
    flex: 1; /* 输入框占满剩余宽 */
    height: 38px;
    padding: 0 14px;
    font-size: 13px;
    color: #eee;
    background: rgba(0, 0, 0, 0.2);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 10px;
    outline: none;
    transition: border-color 0.2s, background 0.2s;
}

.main .left .search input::placeholder {
    color: #8b929e; /* 占位符灰色 */
}

.main .left .search input:focus {
    border-color: rgba(91, 141, 239, 0.5);
    background: rgba(0, 0, 0, 0.28);
}

/* 搜索按钮:图标背景,无文字 */
.main .left .search button {
    width: 38px;
    height: 38px;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 10px;
    background: rgba(0, 0, 0, 0.2) url(../img/搜索.png) center / 18px no-repeat;
    cursor: pointer;
    flex-shrink: 0;
    transition: background-color 0.2s;
}

.main .left .search button:hover {
    background-color: rgba(255, 255, 255, 0.1);
}

/* .tab:会话 / 好友 切换条 */
.main .left .tab {
    height: 50px;
    display: flex;
    padding: 0 12px;
    gap: 4px;
    align-items: center;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

/* Tab 项共用:图标居中、半透明、可点击 */
.main .left .tab .tab-session,
.main .left .tab .tab-friend {
    flex: 1;
    height: 40px;
    cursor: pointer;
    border-radius: 10px;
    background-repeat: no-repeat;
    background-position: center;
    background-size: 24px;
    opacity: 0.5; /* 未选中态较淡 */
    transition: opacity 0.2s, background-color 0.2s;
}

.main .left .tab .tab-session {
    background-image: url(../img/对话.png); /* 会话图标 */
}

.main .left .tab .tab-friend {
    background-image: url(../img/用户2.png); /* 好友图标 */
}

.main .left .tab .tab-session:hover,
.main .left .tab .tab-friend:hover {
    opacity: 0.85;
    background-color: var(--sidebar-hover);
}

/* .list:会话或好友 UL,可纵向滚动 */
.main .left .list {
    flex: 1; /* 占满左栏剩余高度 */
    overflow-y: auto;
    list-style: none;
    padding: 6px 8px;
}

/* 列表每一项 li:可点击会话/好友/好友请求 */
.main .left .list li {
    position: relative;
    padding: 10px 12px;
    margin-bottom: 4px;
    color: #e8eaed;
    border-radius: 10px;
    cursor: pointer;
    transition: background 0.15s;
    border: 1px solid transparent;
}

/* .session-item-inner:会话行内 flex(头像+文字+角标) */
.main .left .list li .session-item-inner {
    display: flex;
    align-items: center;
    gap: 10px;
}

.main .left .list li .session-main {
    flex: 1;
    min-width: 0; /* 预览文字可省略 */
}

.main .left .list li:hover {
    background: var(--sidebar-hover);
}

/* .selected:当前打开的会话 */
.main .left .list li.selected {
    background: var(--sidebar-active);
    border-color: rgba(91, 141, 239, 0.25);
}

.main .left .list .session-row-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 5px;
}

.main .left .list .session-name {
    font-size: 15px;
    font-weight: 500;
    color: #fff;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1;
    min-width: 0;
}

/* .unread-badge:微信式红色未读数字角标 */
.main .left .list .unread-badge {
    flex-shrink: 0;
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    font-size: 12px;
    font-weight: 600;
    line-height: 20px;
    text-align: center;
    color: #fff;
    background: #fa5151; /* 微信红 */
    border-radius: 10px;
    box-shadow: 0 1px 4px rgba(250, 81, 81, 0.45);
}

.main .left .list .unread-badge.hide {
    display: none; /* JS 无未读时添加 hide */
}

/* 有未读时会话名加粗、预览略亮 */
.main .left .list li.has-unread .session-name {
    font-weight: 600;
}

.main .left .list li.has-unread .session-preview {
    color: #c8cdd3;
}

/* 最后一条消息预览 / 好友项副文本 */
.main .left .list .session-preview,
.main .left .list li > p {
    font-size: 12px;
    color: #9aa3b0;
    line-height: 1.45;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* 好友列表行:头像+昵称横排 */
.main .left .list li .list-row-with-avatar {
    display: flex;
    align-items: center;
    gap: 10px;
}

.main .left .list li .list-row-with-avatar h4 {
    flex: 1;
    min-width: 0;
    margin: 0;
}

.main .left .list li h4 {
    font-size: 15px;
    line-height: 1.4;
    padding: 8px 0;
    color: #e8eaed;
    font-weight: 500;
}

/* .friend-request-item:置顶的好友请求条目,橙黄提示 */
.main .left .list li.friend-request-item {
    display: flex;
    align-items: center;
    gap: 10px;
}

.main .left .list li.friend-request-item .friend-request-text {
    flex: 1;
    min-width: 0;
}

.main .left .list li.friend-request-item {
    background: rgba(250, 173, 20, 0.1);
    border-color: rgba(250, 173, 20, 0.2);
}

/* 好友请求前缀「请求 · 」伪元素 */
.main .left .list li.friend-request-item .session-name::before,
.main .left .list li.friend-request-item h3::before {
    content: "请求 · ";
    color: #e8b339;
    font-size: 12px;
    font-weight: 600;
}

/* .hide:通用隐藏(Tab 切换、面板切换、角标) */
.hide {
    display: none !important;
}

/* ========== 右侧聊天 / 搜索区 ========== */

.main .right {
    flex: 1; /* 占据主窗口剩余宽度 */
    display: flex;
    flex-direction: column;
    background: var(--right-bg);
    min-width: 0;
}

/* .title:右侧顶栏,显示当前聊天对象或搜索结果标题 */
.main .right .title {
    height: 58px;
    line-height: 58px;
    padding: 0 24px;
    font-size: 16px;
    font-weight: 600;
    color: #2c3e50;
    text-align: center;
    background: #fff;
    border-bottom: 1px solid #e8ecf1;
    flex-shrink: 0;
    box-shadow: 0 1px 0 rgba(0, 0, 0, 0.03);
}

/* 带头像的标题行(进入会话后 JS 添加 title-with-avatar) */
.main .right .title.title-with-avatar {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    line-height: normal;
}

/* .chat-panel:消息区+输入+发送,默认显示 */
.main .right .chat-panel {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0; /* flex 子项可正确收缩滚动 */
    background: #fff;
    margin: 0;
}

.main .right .chat-panel.hide {
    display: none; /* 切到搜索页时隐藏 */
}

/* .message-show:历史与实时消息滚动容器 */
.main .right .message-show {
    flex: 1;
    overflow-y: auto;
    padding: 22px 26px;
    background-color: #f0f2f5;
    background-image: url(../img/chat-bg.jpg); /* 聊天区壁纸 */
    background-size: cover;
    background-position: center;
}

/* .message:单条消息行(头像+气泡) */
.main .right .message-show .message {
    display: flex;
    align-items: flex-end; /* 头像与气泡底对齐 */
    gap: 10px;
    margin-bottom: 16px;
    clear: both;
}

.main .right .message-show .message-left {
    justify-content: flex-start; /* 对方消息靠左 */
}

.main .right .message-show .message-right {
    justify-content: flex-end; /* 自己消息靠右 */
}

.main .right .message-show .message .avatar-sm {
    width: 36px;
    height: 36px;
    margin-bottom: 2px;
}

/* .bubble:消息文本气泡 */
.main .right .message-show .message .bubble {
    max-width: min(68%, 420px); /* 限制气泡最大宽 */
    padding: 10px 14px;
    border-radius: 12px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
}

/* 左侧(对方)白底气泡,左上尖角 */
.main .right .message-show .message-left .bubble {
    background: #fff;
    border: 1px solid #e8ecf1;
    border-radius: 4px 14px 14px 14px;
}

/* 右侧(自己)绿色气泡,右上尖角,类微信 */
.main .right .message-show .message-right .bubble {
    background: #95ec69;
    border: 1px solid rgba(7, 193, 96, 0.15);
    border-radius: 14px 4px 14px 14px;
}

.main .right .message-show .message .bubble-text {
    margin: 0;
    font-size: 14px;
    line-height: 1.55;
    color: #2c3e50;
    word-break: break-word; /* 长英文/URL 换行 */
}

.main .right .message-show .message-right .bubble-text {
    color: #1a2e1a; /* 绿泡上深绿字 */
}

.main .right .title .title-name {
    font-size: 16px;
    font-weight: 600;
}

/* .message-input:底部多行输入 */
.main .right .message-input {
    height: 96px;
    padding: 14px 18px;
    font-size: 14px;
    line-height: 1.55;
    border: none;
    border-top: 1px solid #e8ecf1;
    outline: none;
    resize: none; /* 禁止拖拽改变高度 */
    background: #fafbfc;
    font-family: inherit;
    color: #2c3e50;
}

.main .right .message-input:focus {
    background: #fff;
}

/* .ctrl:发送按钮行 */
.main .right .ctrl {
    height: 54px;
    padding: 0 20px 14px;
    display: flex;
    justify-content: flex-end; /* 按钮靠右 */
    align-items: center;
    background: #fafbfc;
    border-top: 1px solid #eef1f5;
}

.main .right .ctrl button {
    height: 38px;
    padding: 0 32px;
    font-size: 14px;
    font-weight: 500;
    color: #fff;
    background: #5b8def;
    border: none;
    border-radius: 10px;
    cursor: pointer;
    transition: background 0.2s, box-shadow 0.2s;
    box-shadow: 0 2px 8px rgba(91, 141, 239, 0.35);
}

.main .right .ctrl button:hover {
    background: #4a7de0;
}

.main .right .ctrl button:active {
    background: #3d6ed4;
    box-shadow: none;
}

/* ========== 好友搜索结果面板 ========== */

.main .right .search-result-panel {
    flex: 1;
    overflow-y: auto;
    padding: 16px 22px 24px;
    background: var(--right-bg);
}

.main .right .search-result-panel.hide {
    display: none;
}

.main .right .search-result-hint {
    height: 44px;
    line-height: 44px;
    font-size: 14px;
    font-weight: 600;
    color: #5c6b7a;
    padding-left: 4px;
    margin-bottom: 8px;
    border-bottom: none;
}

/* #search-result-list:搜索结果 UL */
#search-result-list {
    list-style: none;
    padding: 0;
    margin: 0;
}

/* 每个候选用户卡片 */
#search-result-list li {
    padding: 18px 16px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
    background: #fff;
    border: 1px solid #e8ecf1;
    border-radius: 12px;
    margin-bottom: 12px;
    box-shadow: 0 2px 8px rgba(30, 50, 80, 0.04);
    transition: box-shadow 0.2s, border-color 0.2s;
}

#search-result-list li:hover {
    border-color: #d0daf0;
    box-shadow: 0 4px 12px rgba(91, 141, 239, 0.08);
}

#search-result-list .search-user-name {
    font-size: 16px;
    font-weight: 600;
    color: #2c3e50;
    min-width: 72px;
}

#search-result-list .search-reason-input {
    flex: 1;
    min-width: 160px;
    height: 38px;
    padding: 0 14px;
    font-size: 14px;
    border: 1px solid #e0e6ed;
    border-radius: 10px;
    background: #fafbfc;
    outline: none;
    transition: border-color 0.2s;
}

#search-result-list .search-reason-input:focus {
    border-color: #5b8def;
    background: #fff;
}

#search-result-list .search-add-btn {
    height: 38px;
    padding: 0 20px;
    font-size: 13px;
    font-weight: 500;
    border: none;
    border-radius: 10px;
    background: #5b8def;
    color: #fff;
    cursor: pointer;
    transition: background 0.2s;
}

#search-result-list .search-add-btn:hover:not(:disabled) {
    background: #4a7de0;
}

#search-result-list .search-add-btn:disabled {
    background: #c5cdd8; /* 已发送请求后禁用 */
    cursor: not-allowed;
}

#search-result-list .search-empty {
    padding: 48px 20px;
    text-align: center;
    color: #9aa3b0;
    font-size: 14px;
    background: #fff;
    border-radius: 12px;
    border: 1px dashed #e0e6ed;
}

/* ========== 好友请求模态框 ========== */

.friend-request-modal {
    position: fixed;
    inset: 0;
    z-index: 100; /* 盖住主界面 */
    background: rgba(44, 62, 80, 0.4); /* 半透明遮罩 */
    display: flex;
    align-items: center;
    justify-content: center;
}

.friend-request-modal.hide {
    display: none;
}

.friend-request-modal .modal-box {
    width: 400px;
    max-width: 90vw; /* 小屏不溢出 */
    padding: 28px 28px 24px;
    background: #fff;
    border-radius: 14px;
    border: 1px solid #e8ecf1;
    box-shadow: var(--card-shadow);
}

.friend-request-modal .modal-box h3 {
    font-size: 18px;
    font-weight: 600;
    color: #2c3e50;
    margin-bottom: 16px;
}

.friend-request-modal .modal-from,
.friend-request-modal .modal-reason {
    font-size: 14px;
    color: #5c6b7a;
    line-height: 1.6;
    margin-bottom: 8px;
}

.friend-request-modal .modal-actions {
    margin-top: 22px;
    display: flex;
    justify-content: flex-end;
    gap: 10px;
}

.friend-request-modal .modal-actions button {
    height: 38px;
    padding: 0 22px;
    font-size: 14px;
    border-radius: 10px;
    border: 1px solid #e0e6ed;
    background: #fff;
    color: #5c6b7a;
    cursor: pointer;
    transition: background 0.2s;
}

.friend-request-modal #accept-friend-btn {
    background: #5b8def;
    color: #fff;
    border-color: #5b8def;
}

.friend-request-modal #accept-friend-btn:hover {
    background: #4a7de0;
}

/* 全局细滚动条(WebKit) */
::-webkit-scrollbar {
    width: 6px;
}

::-webkit-scrollbar-thumb {
    background: rgba(0, 0, 0, 0.15);
    border-radius: 3px;
}

/* 左栏列表滚动条 thumb 略亮,适配深色底 */
.main .left .list::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.15);
}

搞定【主页面静态框架】🎉!左栏能切会话/好友,右栏能切聊天/搜索结果,并预留头像、加好友、未读角标、好友请求弹窗。🚀

下期开始实现我们用户管理中的获取用户信息等等🖥️!

干货持续更新,记得点赞👍关注🌟收藏⭐,追更不迷路~

相关推荐
wuminyu3 小时前
Java锁机制之轻量级锁判断与尝试逻辑源码剖析
java·linux·c语言·jvm·c++
Thecozzy4 小时前
写文档教 AI 用代码
开发语言·python
Misnearch4 小时前
1、数组/字符串
java·数据结构·算法
☆cwlulu4 小时前
Linux系统调用与C库I/O的底层奥秘
java·spring boot·spring
Hanniel4 小时前
装饰器 (中): 进阶篇,解锁框架级玩法
开发语言·python
于先生吖4 小时前
前后端分离人事招聘项目,校招宣讲预约+社招双向撮合功能架构设计教程
java·开发语言·uni-app
user_admin_god4 小时前
Claude Code 安装与配置指南:兼容国产模型,禁止自动更新
java·人工智能
一只鹿鹿鹿4 小时前
网络安全评估方案
java·大数据·运维·物联网·web安全
川冰ICE4 小时前
JavaScript进阶④|Symbol与元编程,对象的隐藏身份
开发语言·javascript·ecmascript