从零开始用react + tailwindcss + express + mongodb实现一个聊天程序(六) 导航栏 和 个人信息设置

news2025/5/12 23:17:17

1.导航栏(navbar)

 在components下面 创建NavBar.jsx

import { MessageSquare,Settings,User,LogOut} from "lucide-react"

import {Link} from "react-router-dom"

import { useAuthStore } from "../store/useAuthStore"

const NavBar = () => {

    const {authUser,logout} = useAuthStore()

  return (

    <header className="bg-base-100 border-b border-base-300 fixed w-full top-0 z-40

        backdrop-blur-lg bg-base-100/80">

        <div className="container mx-auto px-4 h-16">

            <div className="flex items-center justify-between h-full">

                <div className="flex items-center gap-8">

                    <Link to="/" className="flex items-center gap-2.5 hover:opacity-80 transition-all duration-200">

                        <div className="size-9 rounded-lg bg-primary/10 flex items-center justify-center">

                            <MessageSquare className="w-5 h-5 text-primary"/>

                        </div>

                        <h1 className="text-lg font-bold">HamburgerChat</h1>

                    </Link>

                </div>

                <div className="flex items-center gap-2">

                    <Link to="/settings" className={`btn btn-sm gap-2 transition-colors`}>

                        <Settings className="size-5"></Settings>

                        <span className="hidden sm:inline">设置</span>

                    </Link>

                        // 只有登录了 才会显示 个人资料和退出按钮

                    {authUser && (

                        <>

                            <Link to="/profile" className={`btn btn-sm gap-2 transition-colors`}>

                                <User className="size-5"></User>

                                <span className="hidden sm:inline">个人资料</span>

                            </Link>

                            <button className="flex items-center gap-2" onClick={logout}>

                                <LogOut className="size-5"></LogOut>

                                <span className="hidden sm:inline">退出</span>

                            </button>

                        </>

                    )}

                </div>

               

            </div>

        </div>

    </header>

  )

}

export default NavBar

 然后再App.jsx引入 NavBar.jsx组件

import NavBar from '@/components/NavBar' 

并使用组件 加在Routes配置上面

         <NavBar />

      <Routes>

        <Route path="/" element={authUser?<HomePage />: <Navigate to="/login" />} />

        <Route path="/signup" element={!authUser ? <SignUpPage />:<Navigate to="/" />} />

        <Route path="/login" element={!authUser ?<LoginPage /> : <Navigate to="/" />} />

        <Route path="/settings" element={<SettingsPage /> } />

        <Route path="/profile" element={authUser ? <ProfilePage />:<Navigate to="/login" />} />

      </Routes>

效果如图

点击退出按钮 会跳转到 登录页面  导航栏这就完成了。

2.个人信息设置

 修改ProfilePage内容    Profile最主要的内容就是 点击头像 上的小相机图标 我们可以上传更换头像。上传功能我们是使用cloudinary (类似七牛云 阿里oss)。网址:https://cloudinary.com/

我们在官方申请账号

 1.后端接口

在server 下 .env配置

在lib下新建cloudinary.js  使用我们申请的key

import {v2 as cloudinary} from 'cloudinary'
import  {config} from "dotenv"

config()

cloudinary.config({
    cloud_name: process.env.CLOUDINARY_NAME,
    api_key: process.env.CLOUDINARY_API_KEY,
    api_secret: process.env.CLOUDINARY_API_SECRET
})


export default cloudinary

接下来在后端实现 保存个人信息接口

在server auth.route.js 增加保存信息路由

先引入updateProfile方法

import {signUp, login,logout,checkAuth,updateProfile} from "../controllers/auth.controller.js"

router.put('/update-profile', protectRoute, updateProfile) 

在auth.controller.js 增加UpdateProfile方法

2.前端页面 

  在useAuthStore中增加  保存信息loading状态 和请求方法  

  isUpdatingProfile: false, // 是否更新中

updateProfile: async(data) => {

        set({isUpdatingProfile: true})

        try {

            const res = await axiosInstance.put('/auth/update-profile', data)

            set({authUser: res.data})   // 请求成功后 更新当前user的信息

            toast.success("资料更新成功")

        } catch (error) {

            console.log("useAuthStore updateProfile error",error.message)

            toast.error(error.response.data.message)

        } finally {

            set({isUpdatingProfile: false})

        }

    },

修改ProfilePage.jsx

import { useState } from "react";
import { useAuthStore } from "../store/useAuthStore";
import {Camera,User,Mail} from "lucide-react";
const ProfilePage = () => {

  const [selectedImage, setSelectedImage] = useState(null)
  const { authUser, isUpdatingProfile,updateProfile } = useAuthStore();
  const handlerImageUpload = async (e) => {
    const file = e.target.files[0];
    if (!file) return;
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = async () => {
      const base64Image = reader.result;
      await updateProfile({ profilePic: base64Image });
    }
  }
    
  return (
    <div className="h-screen pt-20">
        <div className="max-w-2xl mx-auto py-8">
           <div className="bg-base-300 rounded-xl p-6 space-y-8">
              <div className="text-center">
                <h1 className="text-2xl font-semibold">个人资料</h1>
                <p className="mt-2">你的个人信息</p>
              </div>

              {/* 头像 */}
              <div className="flex flex-col items-center gap-4">
                <div className="relative">
                  <img src={selectedImage || authUser.profilePic || "https://placehold.co/128x128"} alt="profile"
                    className="size-32 rounded-full object-cover border-4"
                  ></img>
                  <label 
                    htmlFor="avatar-upload"
                    className={`absolute bottom-0 right-0 bg-base-content hover:scale-105
                      p-2 rounded-full cursor-pointer transition-all duration-300
                      ${isUpdatingProfile ? 'animate-pulse pointer-events-none' : ''}
                    `}
                  >
                    <Camera className="size-5 text-base-200" />
                    <input
                      type="file"
                      id="avatar-upload"
                      className="hidden"
                      accept="image/*"
                      onChange={handlerImageUpload}
                      disabled={isUpdatingProfile}
                    />
                  </label>
                </div>
                <p className="text-sm text-zinc-400">
                   {isUpdatingProfile ? "上传中..." : "点击相机上传头像"}
                </p>
              </div>

              {/* 用户信息 */}
              <div className="space-y-6">
                <div className="space-y-1.5">
                  <div className="text-sm text-zinc-400 flex items-center gap-2">
                    <User className="size-4"/>
                    用户名
                  </div>
                  <p className="px-4 py-2.5 bg-base-200 rounded-lg border">{authUser?.userName}</p>
                </div>

                <div className="space-y-1.5">
                  <div className="text-sm text-zinc-400 flex items-center gap-2">
                    <Mail className="size-4"/>
                    邮箱地址
                  </div>
                  <p className="px-4 py-2.5 bg-base-200 rounded-lg border">{authUser?.email}</p>
                </div>
              </div>

              {/* 账号状态 */}
              <div className="mt-6 bg-base-300 rounded-xl p-6">
                <h2 className="text-lg font-medium mb-4">账号信息</h2>
                <div className="spacy-y-3 text-sm">
                  <div className="flex items-center justify-between py-2
                    border-b border-zinc-700"
                  >
                    <span>账号注册时间</span>
                    <span>{authUser.createdAt?.split("T")[0]}</span>
                  </div>
                  <div className="flex items-center justify-between py-2
                    border-b border-zinc-700"
                  >
                    <span>账号状态</span>
                    <span className="text-green-500">正常</span>
                  </div>
                </div>
              </div>
           </div>
        </div>
    </div>
  )
}

export default ProfilePage

效果如图

点击头像小相机 选择一张小于50kb的图片上传更新 头像 

这就是今天内容 下篇实现主题设置  欢迎评论区留言 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2308614.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据库MySQL,在终端输入后,提示不是内部命令等

【解决问题】mysql提示不是内部或外部命令&#xff0c;也不是可运行的程序 一般这种问题是因为没有在系统变量里面添加MySQL的可执行路径 以下是添加可执行路径的方法&#xff1a; 第一步&#xff1a;winR输入services.msc 然后找到MySQL&#xff0c;右击属性并复制MySQL的可执…

C语言生成二维码

1. 效果 2. 需要的代码&#xff08;QRCode&#xff09; qrcode.cqrcode.h 代码 3. 代码 #include <stdio.h> #include "qrcode.h"int main() {//拓展编码SetConsoleOutputCP(437);QRCode qrcode;uint8_t qrcodeBytes[qrcode_getBufferSize(3)];qrcode_initT…

[Web 安全] PHP 反序列化漏洞 —— POP 链构造思路

关注这个专栏的其他相关笔记&#xff1a;[Web 安全] 反序列化漏洞 - 学习笔记-CSDN博客 0x01&#xff1a;什么是 POP 链&#xff1f; POP 链&#xff08;Payload On Purpose Chain&#xff09;是一种利用 PHP 中的魔法方法进行多次跳转以获取敏感数据的技术。它通常出现在 CTF…

深度探索推理新境界:DeepSeek-R1如何用“自学”让AI更聪明?

今天我们要聊从1月初火到现在的AI模型——DeepSeek-R1。它就像一个“自学成材的学霸”&#xff0c;不用老师手把手教&#xff0c;就能在数学、编程、逻辑推理等领域大显身手&#xff01;仔细阅读了深度求索发表的R1论文&#xff0c;发现它不仅揭秘了它的成长秘籍&#xff0c;还…

2025春新生培训数据结构(树,图)

教学目标&#xff1a; 1&#xff0c;清楚什么是树和图&#xff0c;了解基本概念&#xff0c;并且理解其应用场景 2&#xff0c;掌握一种建图&#xff08;树&#xff09;方法 3&#xff0c;掌握图的dfs和树的前中后序遍历 例题与习题 2025NENU新生培训&#xff08;树&#…

keil主题(vscode风格)

#修改global.prop文件&#xff0c;重新打开keil即可 # Keil uVision Global Properties File # This file is used to customize the appearance of the editor# Editor Font editor.font.nameConsolas editor.font.size10 editor.font.style0# Editor Colors editor.backgro…

使用Hydra进行AI项目的动态配置管理

引言:机器学习中的超参数调优挑战 在机器学习领域,超参数调优是决定模型性能的关键环节。不同的模型架构,如神经网络中的层数、节点数,决策树中的最大深度、最小样本分割数等;以及各种训练相关的超参数,像学习率、优化器类型、批量大小等,其取值的选择对最终模型的效果…

深入了解 K-Means 聚类算法:原理与应用

引言 在数据科学和机器学习的世界中&#xff0c;聚类是一项非常重要的技术&#xff0c;它帮助我们根据数据的相似性将数据划分为不同的组或簇。聚类算法在许多领域中得到了广泛的应用&#xff0c;如图像处理、市场细分、基因研究等。K-Means 聚类算法作为最常见的无监督学习算…

永磁同步电机无速度算法--反电动势观测器

一、原理介绍 在众多无位置传感器控制方法中&#xff0c;低通滤波反电势观测器结构简单&#xff0c;参数整定容易&#xff0c;易于编程实现。但是该方法估计出的反电势会产生相位滞后&#xff0c;需要在估计永磁同步电机转子位置时进行了相位补偿。 二、仿真模型 在MATLAB/si…

【Linux】命令行参数 | 环境变量(四)

目录 前言&#xff1a; 一、命令行参数&#xff1a; 1.main函数参数 2.为什么有它&#xff1f; 二、环境变量&#xff1a; 1.main函数第三个参数 2.查看shell本身环境变量 3.PATH环境变量 4.修改PATH环境变量配置文件 5.HOME环境变量 6.SHELL环境变量 7.PWD环境变…

java高级(IO流多线程)

file 递归 字符集 编码 乱码gbk&#xff0c;a我m&#xff0c;utf-8 缓冲流 冒泡排序 //冒泡排序 public static void bubbleSort(int[] arr) {int n arr.length;for (int i 0; i < n - 1; i) { // 外层循环控制排序轮数for (int j 0; j < n -i - 1; j) { // 内层循环…

深度剖析数据分析职业成长阶梯

一、数据分析岗位剖析 目前&#xff0c;数据分析领域主要有以下几类岗位&#xff1a;业务数据分析师、商业数据分析师、数据运营、数据产品经理、数据工程师、数据科学家等&#xff0c;按照工作侧重点不同&#xff0c;本文将上述岗位分为偏业务和偏技术两大类&#xff0c;并对…

Web3.py 入门笔记

Web3.py 学习笔记 &#x1f4da; 1. Web3.py 简介 &#x1f31f; Web3.py 是一个 Python 库&#xff0c;用于与以太坊区块链进行交互。它就像是连接 Python 程序和以太坊网络的桥梁。 官方文档 1.1 主要功能 查询区块链数据&#xff08;余额、交易等&#xff09;发送交易与…

NFC拉起微信小程序申请URL scheme 汇总

NFC拉起微信小程序&#xff0c;需要在微信小程序开发里边申请 URL scheme &#xff0c;审核通过后才可以使用NFC标签碰一碰拉起微信小程序 有不少人被难住了&#xff0c;从微信小程序开发社区汇总了以下信息&#xff0c;供大家参考 第一&#xff0c;NFC标签打开小程序 https://…

《Python实战进阶》No 8:部署 Flask/Django 应用到云平台(以Aliyun为例)

第8集&#xff1a;部署 Flask/Django 应用到云平台&#xff08;以Aliyun为例&#xff09; 2025年3月1日更新 增加了 Ubuntu服务器安装Python详细教程链接。 引言 在现代 Web 开发中&#xff0c;开发一个功能强大的应用只是第一步。为了让用户能够访问你的应用&#xff0c;你需…

【JAVA SE基础】抽象类和接口

目录 一、前言 二、抽象类 2.1 抽象类的概念 2.2 抽象类语法 2.3 抽象类特性 2.4 抽象类的作用 三、接口 3.1 什么是接口 3.2 语法规则 3.3 接口使用 3.4 接口特性 3.5 实现多接口 3.6 接口间的继承 四、Object类 4.1 获取对象信息&#xff08; toString() &…

530 Login fail. A secure connection is requiered(such as ssl)-java发送QQ邮箱(简单配置)

由于cs的csdN许多文章关于这方面的都是vip文章&#xff0c;而本文是免费的&#xff0c;希望广大网友觉得有帮助的可以多点赞和关注&#xff01; QQ邮箱授权码到这里去开启 授权码是16位的字母&#xff0c;填入下面的mail.setting里面的pass里面 # 邮件服务器的SMTP地址 host…

计算机毕业设计SpringBoot+Vue.js体育馆使用预约平台(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

MyBatis-Plus 为简化开发而生【核心功能】

文章目录 一、前言二、快速入门1. 入门案例2. 常见注解3. 常见配置 三、核心功能1. 条件构造器2. 自定义 SQL3. Service 接口3.1 基本使用3.2 复杂条件 一、前言 顾名思义&#xff0c;MyBatis-Plus 其实是 MyBatis 的一个加强版&#xff0c;它可以帮助我们快速高效地编写数据库…

【MySQL】(2) 库的操作

SQL 关键字&#xff0c;大小写不敏感。 一、查询数据库 show databases; 注意加分号&#xff0c;才算一句结束。 二、创建数据库 {} 表示必选项&#xff0c;[] 表示可选项&#xff0c;| 表示任选其一。 示例&#xff1a;建议加上 if not exists 选项。 三、字符集编码和排序…