一、项目简介
本项目基于纯前端(移动端)技术开发一个聊天系统,界面美观大方,采用Nodejs+Vue+ElemenetUI开发实现,主要包含:登录注册,修改个人资料,更改头像,发送消息,单对单聊天等。
二、环境介绍
语言环境:nodejs
数据库:MySQL
应用服务器:nodejs
开发工具:vscode
开发技术:nodejs+vue+elementUI
三、系统展示
四、视频功能展示
基于Vue+nodejs+Element-ui的聊天系统项目
五、前端核心代码展示
- 头部导航功能代码
<template> <div> <el-tabs v-model="activeName" type="card" @tab-click="handleClick"> <el-tab-pane label="用户" name="user"><User></User></el-tab-pane> <el-tab-pane label="我的" name="mine"><Mine></Mine></el-tab-pane> </el-tabs> </div> </template> <script> import User from "@/views/user/index.vue"; import Mine from "@/views/mine/index.vue"; export default { components: { User, Mine, }, data() { return { activeName: "Home", }; }, mounted() { this.activeName = this.$route.name; }, methods: { handleClick(tab, event) { // console.log(this.activeName); this.$router.push({ name: this.activeName, }); }, }, }; </script> <style></style>
- 登录注册页居中旋转功能代码
<template> <div class="box" :class="{ box_rolling: isRolling }"> <div class="box_register"><register></register></div> <div class="box_login"><login></login></div> </div> </template> <script> import login from "@/views/login/login.vue"; import register from "@/views/login/register.vue"; import { mapState } from "vuex"; export default { components: { login, register, }, computed: { ...mapState(["isRolling"]), }, }; </script> <style lang="scss" scoped> .box { &_register, &_login { transform-style: preserve-3d; //表示所有子元素在3D空间中呈现 backface-visibility: hidden; //元素背面向屏幕时是否可见 transition-duration: 0.5s; transition-timing-function: "ease-in"; background: #008080; } &_login { transform: rotateY(180deg); visibility: hidden; //元素不可见,但占据空间 position: absolute; top: 0; bottom: 0; right: 0; left: 0; } } .box_rolling { .box_register { transform: rotateY(180deg); visibility: hidden; } .box_login { transform: rotateY(360deg); visibility: visible; } } </style>
- 登录页代码
<template> <div class="box"> <p class="title">欢迎登录</p> <input type="text" class="name" placeholder="请输入账号" v-model="username" /> <input type="password" class="password" placeholder="请输入密码" v-model="password" /> <button @click="add">登录</button> <p class="login"> <span @click="addlogin">点击跳转到注册页</span> </p> </div> </template> <script> export default { data() { return { username: "", password: "", }; }, methods: { addlogin() { this.$store.commit("addlogin"); }, add() { this.$axios({ url: "/api/api/login", method: "POST", data: { username: this.username, password: this.password, }, }).then(({ data }) => { console.log(data); localStorage.setItem("token", data.token); location.href = "/"; }); }, }, }; </script> <style lang="scss" scoped> .box { width: 100%; height: 100vh; padding: 0 0.5rem; overflow: hidden; text-align: center; background-color: #f7f7f7; .title { font-size: 0.4rem; text-align: center; letter-spacing: 0.2rem; margin: 0.2rem 0 0.5rem; color: #7c7c7c; } .name, .password, .user { width: 100%; padding-left: 0.1rem; height: 0.5rem; border: 2px solid #cccccc; border-radius: 0.2rem; font-size: 0.15rem; } input::-webkit-input-placeholder { color: #97040b; } .password { margin-top: 0.75rem; } button { width: 0.6rem; height: 0.6rem; border-radius: 50%; background-color: #0b24fb; border: none; color: #fff; font-size: 0.1rem; margin-top: 0.5rem; } .login { display: flex; justify-content: flex-end; margin-top: 0.3rem; span { font-size: 0.1rem; color: #000; font-weight: 400; } } } </style>
- 注册页代码
<template> <div class="box"> <p class="title">欢迎注册</p> <input type="text" class="name" placeholder="请输入账号" v-model="username" /> <input type="password" class="password" placeholder="请输入密码" v-model="password" /> <button @click="add">注册</button> <p class="login"> <span @click="addlogin">点击跳转到登录页</span> </p> </div> </template> <script> export default { data() { return { username: "", password: "", }; }, methods: { addlogin() { this.$store.commit("addlogin"); }, add() { this.$axios({ url: "/api/api/reguser", method: "POST", data: { username: this.username, password: this.password, }, }).then(({ data }) => { console.log(data); }); }, }, }; </script> <style lang="scss" scoped> .box { width: 100%; height: 100vh; padding: 0 0.5rem; overflow: hidden; text-align: center; background-color: #f7f7f7; .title { font-size: 0.4rem; text-align: center; letter-spacing: 0.2rem; margin: 0.2rem 0 0.5rem; color: #7c7c7c; } .name, .password, .user { width: 100%; padding-left: 0.1rem; height: 0.5rem; border: 2px solid #cccccc; border-radius: 0.2rem; font-size: 0.15rem; } input::-webkit-input-placeholder { color: #97040b; } .password { margin-top: 0.75rem; } button { width: 0.6rem; height: 0.6rem; border-radius: 50%; background-color: #0b24fb; border: none; color: #fff; font-size: 0.1rem; margin-top: 0.5rem; } .login { display: flex; justify-content: flex-end; margin-top: 0.3rem; span { font-size: 0.1rem; color: #000; font-weight: 400; } } } </style>
- 我的主页代码
<template> <div class="box"> <div class="top"> <img :src="imageUrl" class="avatar" /> <p> {{ list.username }} <br /> <router-link :to="{ name: 'setMessage' }">修改用户基本信息</router-link> <span @click="tuichu">退出</span> </p> </div> </div> </template> <script> export default { data() { return { list: [], imageUrl: "", }; }, mounted() { this.list = this.$store.state.userInfo; this.imageUrl = this.list.image; }, methods: { handleAvatarSuccess(res, file) { this.imageUrl = URL.createObjectURL(file.raw); }, beforeAvatarUpload(file) { const isJPG = file.type === "image/jpeg"; const isLt2M = file.size / 1024 / 1024 < 2; if (!isJPG) { this.$message.error("上传头像图片只能是 JPG 格式!"); } if (!isLt2M) { this.$message.error("上传头像图片大小不能超过 2MB!"); } return isJPG && isLt2M; }, tuichu() { localStorage.removeItem("token"); location.href = "/"; }, }, }; </script> <style lang="scss" scoped> .box { .top { display: flex; .avatar { width: 0.8rem; height: 0.8rem; display: block; } p { margin-left: 0.2rem; font-size: 0.25rem; a { text-decoration: none; font-size: 0.1rem; color: gray; margin-right: 0.2rem; } } } } </style>
- 修改个人信息代码
<template> <div class="box"> <div> <el-upload class="avatar-uploader" action="/api/api/upload" :show-file-list="false" :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload" > <img v-if="imageUrl" :src="imageUrl" class="avatar" /> <i v-else class="el-icon-plus avatar-uploader-icon"></i> </el-upload> <p> 用户名: <el-input v-model="input" :placeholder="list.username"> </el-input> </p> <p> 个人简介: <el-input type="textarea" :rows="2" placeholder="请输入内容" v-model="textarea" > </el-input> </p> <el-button type="primary" @click="add">提交</el-button> </div> </div> </template> <script> export default { data() { return { list: [], imageUrl: "", input: "", textarea: "", image: "", }; }, mounted() { this.list = this.$store.state.userInfo; this.imageUrl = this.list.image; this.input = this.list.username; this.textarea = this.list.brief; }, methods: { handleAvatarSuccess(res, file) { // console.log(res, file.name); this.image = file.name; this.imageUrl = URL.createObjectURL(file.raw); }, beforeAvatarUpload(file) { const isJPG = file.type === "image/jpeg"; const isLt2M = file.size / 1024 / 1024 < 2; if (!isJPG) { this.$message.error("上传头像图片只能是 JPG 格式!"); } if (!isLt2M) { this.$message.error("上传头像图片大小不能超过 2MB!"); } return isJPG && isLt2M; }, add() { this.$axios({ url: `/api/my/setmessages`, method: "post", data: { image: this.image ? "http://127.0.0.1:830/api/uploads?img=image_" + this.image : this.list.image, username: this.input, brief: this.textarea, }, headers: { Authorization: localStorage.getItem("token"), }, }).then(({ data }) => { console.log(data); location.href = "/footer/mine"; }); }, }, }; </script> <style lang="scss" scoped> .box { div { .avatar-uploader .el-upload { border: 1px dashed #d9d9d9; border-radius: 6px; cursor: pointer; position: relative; overflow: hidden; } .avatar-uploader .el-upload:hover { border-color: #409eff; } .avatar-uploader-icon { font-size: 28px; color: #8c939d; width: 178px; height: 178px; line-height: 178px; text-align: center; } .avatar { width: 178px; height: 178px; display: block; } } } </style>