用uniapp 及socket.io做一个简单聊天app 4

界面如下:
在这里插入图片描述

<template><view class="container"><input v-model="username" placeholder="用户名" /><input v-model="password" type="password" placeholder="密码" /><button @click="handleLogin">登录</button><text v-if="errorMessage" class="error">{{ errorMessage }}</text><view class="link"><text>没有帐号?</text><button @click="goreg">注册</button></view></view>
</template><script>
import { mapActions } from 'vuex';export default {data() {return {username: '',password: '',errorMessage: ''};},methods: {...mapActions(['login', 'fetchUser']),validateInput() {const usernameRegex = /^[a-zA-Z0-9]{6,12}$/;const passwordRegex = /^[a-zA-Z0-9]{6,12}$/;if (!usernameRegex.test(this.username)) {this.errorMessage = '用户名必须是6到12位的字母或数字';return false;}if (!passwordRegex.test(this.password)) {this.errorMessage = '密码必须是6到12位的字母或数字';return false;}return true;},async handleLogin() {if (!this.validateInput()) {return;}try {//用户名 6~12位  密码 6~12位  console.log('Attempting login...');await this.login({ username: this.username, password: this.password });} catch (error) {this.errorMessage = '登陆失败';}},goreg() {uni.navigateTo({url: '/pages/index/register'});}},async mounted() {const token = uni.getStorageSync('token');if (token) {try {// Attempt to fetch user data with the tokenawait this.fetchUser();// Redirect to the friends page if the user is authenticateduni.redirectTo({url: '/pages/index/friends'});} catch (error) {console.error('Failed to fetch user:', error);this.errorMessage = '自动登录失败,请重新登录';}}}
};
</script><style>
.container {padding: 20px;
}
input {display: block;margin: 10px 0;
}
button {display: block;margin: 10px 0;
}
.error {color: red;
}
.link {margin-top: 20px;text-align: center;
}
.link button {background-color: #007aff;color: white;border: none;padding: 10px;border-radius: 5px;
}
</style>

注册页:
在这里插入图片描述

<template><view class="container"><input v-model="username" placeholder="用户名" /><input v-model="password" type="password" placeholder="密码" /><button @click="register">注册</button><text v-if="errorMessage" class="error">{{ errorMessage }}</text><view class="link"><text>已有帐号?</text><button @click="goToLogin">登录</button></view></view>
</template><script>
import config from '@/config/config.js';export default {data() {return {username: '',password: '',errorMessage: ''};},methods: {validateInput() {const usernameRegex = /^[a-zA-Z0-9]{6,12}$/;const passwordRegex = /^[a-zA-Z0-9]{6,12}$/;if (!usernameRegex.test(this.username)) {this.errorMessage = '用户名必须是6到12位的字母或数字';return false;}if (!passwordRegex.test(this.password)) {this.errorMessage = '密码必须是6到12位的字母或数字';return false;}return true;},async register() {if (!this.validateInput()) {return;}try {const [error, response] = await uni.request({url: config.apiBaseUrl + '/register',method: 'POST',data: {username: this.username,password: this.password}});if (response.data.success) {uni.navigateTo({url: '/pages/index/login'});this.errorMessage = ''; // 清除任何以前的错误消息} else {this.errorMessage = response.data.error;}} catch (error) {console.error(error);this.errorMessage = '发生错误';}},goToLogin() {uni.navigateTo({url: '/pages/index/login'});}}
};
</script><style>
.container {padding: 20px;
}
input {display: block;margin: 10px 0;
}
button {display: block;margin: 10px 0;
}
.error {color: red;
}
.link {margin-top: 20px;text-align: center;
}
.link button {display: block;background-color: #007aff;color: white;border: none;padding: 10px;border-radius: 5px;
}
</style>

在store加入index.js:

import Vue from 'vue';
import Vuex from 'vuex';
import config from '@/config/config.js';
Vue.use(Vuex);
export default new Vuex.Store({state: {token: uni.getStorageSync('token') || '', // 从本地存储中获取 tokenuser: null, // 用户信息friends: [], // 好友列表groups: [],lastMessages: {}, // 新增状态,用于存储最后一条消息hasMoreFriends: true // 新增状态用于跟踪是否有更多好友},mutations: {SET_TOKEN(state, token) {state.token = token;uni.setStorageSync('token', token); // 保存到本地存储},CLEAR_TOKEN(state) {state.token = '';uni.removeStorageSync('token'); // 从本地存储中删除},SET_USER(state, user) {state.user = user;},SET_FRIENDS(state, {friends,hasMoreFriends}) {state.friends = friends;state.hasMoreFriends = hasMoreFriends;},SET_GROUPS(state, groups) {state.groups = groups;},SET_LAST_MESSAGE(state, {id,message}) {Vue.set(state.lastMessages, id, message); // 动态设置最后一条消息}},actions: {async fetchGroups({commit}) {const token = uni.getStorageSync('token');const [error, response] = await uni.request({url: `${config.apiBaseUrl}/groups`,method: 'GET',header: {'Authorization': `Bearer ${token}`}});if (!error && response.data.code == 0) {commit('SET_GROUPS', response.data.data);}logoutpub(response, commit);},async createGroup({state}, {name,description,avatar_url}) {try {const token = uni.getStorageSync('token');const [error, response] = await uni.request({url: `${config.apiBaseUrl}/groups`,method: 'POST',header: {'Authorization': `Bearer ${token}`,'Content-Type': 'application/json'},data: {name,description,avatar_url}});logoutpub(response, commit);return response.data;} catch (error) {throw error;}},async login({commit}, {username,password}) {try {const [error, response] = await uni.request({url: `${config.apiBaseUrl}/login`,method: 'POST',data: {username,password}});if (error) {throw new Error(`Request failed with error: ${error}`);}response.data = response.data.data;if (response.data.token) {commit('SET_TOKEN', response.data.token);uni.redirectTo({url: '/pages/index/friends'});} else {throw new Error('Invalid credentials');}} catch (error) {console.error('Login error:', error);throw error;}},async fetchUser({commit}) {const token = uni.getStorageSync('token');if (!token) return;try {const [error, response] = await uni.request({url: `${config.apiBaseUrl}/user`,method: 'GET',header: {'Authorization': `Bearer ${token}`}});logoutpub(response, commit);if (error) {throw new Error(`Request failed with error: ${error}`);}if (response.statusCode === 200) {const userData = response.data.data || response.data;commit('SET_USER', userData);} else {throw new Error('Failed to fetch user data');}} catch (error) {console.error('Failed to fetch user:', error);}},async fetchFriends({commit}, {page = 1,perPage = 20}) {const token = uni.getStorageSync('token');if (!token) return;try {const [error, response] = await uni.request({url: `${config.apiBaseUrl}/friends`,method: 'GET',header: {'Authorization': `Bearer ${token}`},data: {page,perPage}});console.log("friends",response)logoutpub(response, commit);if (error) {throw new Error(`Request failed with error: ${error}`);}if (response.data) {commit('SET_FRIENDS', {friends: response.data.data,hasMoreFriends: response.data.hasMoreFriends});}} catch (error) {console.error(error);}},async handleNewMessage({commit}, {id,message}) {try {// 假设这是接收消息的逻辑commit('SET_LAST_MESSAGE', {id,message});} catch (error) {console.error('Failed to handle new message:', error);}},logout({commit}) {commit('CLEAR_TOKEN');commit('SET_USER', null);commit('SET_FRIENDS', {friends: [],hasMoreFriends: true});uni.redirectTo({url: '/pages/index/login' // 跳转到登录页面});}}
});// Helper function for handling token expiration
function logoutpub(response, commit) {if (response.data && response.data.code === -1 && response.data.message === 'expire') {commit('CLEAR_TOKEN');commit('SET_USER', null);commit('SET_FRIENDS', {friends: [],hasMoreFriends: true});uni.redirectTo({url: '/pages/index/login' // 跳转到登录页面});}
}

在config创建config.js:

const config = {apiBaseUrl: 'http://localhost:3000'
};
export default config;

用户登陆后进入到friends页。

在这里插入图片描述
界面代码为:

<template><view class="friends-container"><view v-if="!isLoggedIn" class="not-logged-in"><text>您尚未登录。请先登录以查看好友列表。</text><button @click="goToLogin">去登录</button></view><view><view v-if="friends.length === 0"><text>您还没有添加任何好友。</text><uni-list><uni-list-chat :avatar-circle="true" title="增加好友/群" note="输入用户帐号或群号" :avatar="'../../static/addfriend.png'" showArrow link @click="gotadd()"></uni-list-chat></uni-list></view><view  v-if="friends.length > 0"><uni-list><uni-list-chat :avatar-circle="true" title="增加好友/群" note="输入用户帐号或群号" :avatar="'../../static/addfriend.png'" showArrow link @click="gotadd()"></uni-list-chat></uni-list><uni-list><uni-list-chatv-for="(friend, index) in friends":key="index":title="friend.type === 'group' ? ('[群]'+friend.group.name) : friend.user.username":avatar-circle="true":avatar="friend.type === 'group' ? friend.group.avatar_url : friend.user.avatar_url":note="friend.message || '暂无信息'":time="friend.time"badge-position="left"badge-text="188"showArrowlink@click="toChat(friend)"></uni-list-chat></uni-list></view><button @click="loadMoreFriends" v-if="hasMoreFriends">加载更多</button></view><uni-popup ref="popupBag" type="center"><view class="bagDetail"><view class="title flex align-center justify-content-between"><view class="flex-sub">添加好友</view><view class="close-button" style="font-size: 22px;" @tap="closepopupBag">×</view></view><uni-list :border="true"><uni-list-item title="增加好友或群" note="请输入正确的帐号或群号" badge-position="right" badge-text="dot" link @tap="goaddurl"></uni-list-item><uni-list-item title="创建自己的群" note="群号创建后不能修改" badge-position="right" badge-text="dot" link @tap="gogroupurl"></uni-list-item></uni-list></view></uni-popup><button @click="myself()">我的信息</button></view>
</template><script>
import { mapState, mapActions } from 'vuex';
import io from 'socket.io-client';
import config from '@/config/config.js';
export default {data() {return {page: 1,perPage: 20,loading: false,hasMoreFriends: false,message:'',friendlist:[]};},computed: {...mapState(['friends', 'token', 'lastMessages']),isLoggedIn() {return !!this.token;},},methods: {...mapActions(['fetchFriends']),async getmsg() {this.socket.on('message', (msg) => {this.friends.forEach((friend, index) => {if (friend.id == msg.group_name) {this.$set(this.friends, index, { ...friend, message: msg.content,type:msg.type });}});});},async loadFriends() {if (!this.isLoggedIn) return;this.loading = true;try {await this.fetchFriends({ page: this.page, perPage: this.perPage });this.page++;} catch (error) {console.error('Failed to load friends:', error);this.loading = false;} finally {this.loading = false;}},toChat(item) {console.log(":::::item.type::::",item.type)if(item.type=='user'){uni.navigateTo({url: '/pages/index/chat?id=' + item.id + '&type=' + item.type + '&tid='+item.group_friend_id});}else{uni.navigateTo({url: '/pages/index/chat?id=' + "g_"+item.group_friend_id + '&type=' + item.type + '&tid='+item.group_friend_id});}},loadMoreFriends() {this.page++;this.loadFriends();},goToLogin() {uni.navigateTo({url: '/pages/index/login'});},gotadd() {this.$refs.popupBag.open();},goaddurl() {this.closepopupBag();uni.navigateTo({url: '/pages/index/addfriend'});},gogroupurl() {this.closepopupBag();uni.navigateTo({url: '/pages/index/addgroup'});},myself() {uni.navigateTo({url: '/pages/index/profile'});},closepopupBag() {this.$refs.popupBag.close();},getLastMessage(id) {console.log('Getting last message for ID:', id);return this.lastMessages && this.lastMessages[id] ? this.lastMessages[id] : '暂无信息';}},mounted() {if (this.isLoggedIn) {this.loadFriends();}this.socket = io('http://127.0.0.1:3000');this.socket.on('connect', () => {console.log('Socket connected:', this.socket.id);});this.socket.on('disconnect', () => {console.log('Socket disconnected');});this.getmsg();}
};
</script>
<style>
.container {padding: 20px;
}.bagDetail {padding:10px;width: 100%;height: 30%;position: fixed;background-color: #ffffff;left: 0;display: flex;flex-direction: column;}#messages {height: 300px;overflow-y: scroll;border: 1px solid #ccc;margin-bottom: 10px;
}input {display: block;margin: 10px 0;
}button {display: block;margin: 10px 0;
}.user-list {margin-top: 20px;border: 1px solid #ccc;padding: 10px;
}.title {display: flex;justify-content: space-between;align-items: center;width: 100%;padding: 10px;
}.close-button {font-size: 22px;cursor: pointer;
}
</style>

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

comfyUI-MuseTalk的参数设置

comfyUI-MuseTalk的参数设置 目录 comfyUI-MuseTalk的参数设置 一、ComfyUI-VideoHelperSuite 二、comfyUI-MuseV合成的参考视频 2.1、什么时候会用到MuseV&#xff1f; 2.2、MuseV特别消耗系统内存 2.2.1、测试图片序列的像素比 2.2.2、影响运动范围和生成结果的参数 …

Yarn:一个快速、可靠且安全的JavaScript包管理工具

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;还请三连支持一波哇ヾ(&#xff20;^∇^&#xff20;)ノ&#xff09; 目录 一、Yarn简介 二、Yarn的安装 1. 使用npm安装Yarn 2. 在macOS上…

基于Springboot的个人博客系统

文章目录 介绍访问地址一、功能展示1.前台首页归档相册留言关于我登陆注册 2.后台管理系统登陆页面首页文章管理相册管理写博客访客统计 介绍 基于Java&#xff08;Springboot&#xff09;可以用做毕业设计的个人博客系统&#xff0c;包括网站前台和后台管理系统两部分。网站前…

C++中const关键字的用法

C语言和C中const的不同 首先我们需要区分一下C语言中的const和C中的const&#xff0c;C语言中的const修饰的变量可以不初始化&#xff0c;但如果将一个变量定位为const类型还不初始化&#xff0c;那么之后就不能对这个变量直接赋值了。 如果我们使用C语言中的const定义的变量指…

c++ 21 指针

*像一把钥匙 通过钥匙去找内存空间 间接修改内存空间的值 不停的给指针赋值 等于不停的更改指针的指向 指针也是一种数据类型 指针做函数参数怎么看都不可以 指针也是一个数据类型 是指它指向空间的数据类习惯 作业 野指针 向null空间地址copy数据 不断改变指针指向 …

将本地微服务发布到docker镜像二:

上一篇文章我们介绍了如何将一个简单的springboot服务发布到docker镜像中&#xff0c;这一篇我们将介绍如何将一个复杂的微服务&#xff08;关联mysql、redis&#xff09;发布到docker镜像。 我们将使用以下两种不同的方式来实现此功能。 redis、mysql、springboot微服务分开…

Redis未授权访问漏洞 *

#安装redis apt-get install redis #redis链接 redis-cli -h 192.168.4.176 -p 6379 #redis常见命令 &#xff08;1&#xff09;查看信息&#xff1a;info &#xff08;2&#xff09;删除所有数据库内容&#xff1a;flushall &#xff08;3&#xff09;刷新数据库&#xff1a;f…

平衡二叉树 - 力扣(LeetCode) C语言

110. 平衡二叉树 - 力扣&#xff08;LeetCode&#xff09;&#xff08;点击前面链接即可查看题目&#xff09; 一、题目 给定一个二叉树&#xff0c;判断它是否是 平衡二叉树 是指该树所有节点的左右子树的深度相差不超过 1。 示例 1&#xff1a; 输入&#xff1a;root […

unity拖拽物品遇到的bug及解决思路

记录一下拖拽实现过程中遇到的bug RectTransform 专门用在UI中transform 判断点击是否在UI中 使用这个函数就可以判断点击的是否是UI面板&#xff0c;返回true表明在UI面板中 EventSystem.current.IsPointerOverGameObject()值得一提的是&#xff0c;如果发现了有UI穿透效…

【Material-UI】多选模式(Multiple values)与Autocomplete的高级用法解析

文章目录 一、多选模式的基本用法二、高级用法1. 固定选项&#xff08;Fixed options&#xff09;2. 复选框&#xff08;Checkboxes&#xff09;3. 限制标签显示数量&#xff08;Limit tags&#xff09; 三、性能优化与最佳实践1. 筛选已选项&#xff08;filterSelectedOptions…

乡村振兴建设顶层规划建设方案

数字乡村振兴平台顶层规划方案摘要 1. 政策背景与总体要求 政策背景&#xff1a;党的十九大明确提出实施乡村振兴战略&#xff0c;总体要求包括产业兴旺、生态宜居、乡风文明、治理有效和生活富裕。随后&#xff0c;中央一号文件、乡村振兴战略规划以及十四五规划和2035年远景…

C# 设计模式之装饰器模式

总目录 前言 装饰器模式的主要作用就是扩展一个类的功能&#xff0c;或给一个类添加多个变化的情况。学习面向对象的都知道&#xff0c;如果想单纯的给某个类增加一些功能&#xff0c;可以直接继承该类生成一个子类就可以。应对一些简单的业务场景继承也就够了&#xff0c;但是…

共享`pexlinux`数据文件的网络服务

实验环境准备&#xff1a; 1.红帽7主机 2.要全图形安装 3.配置网络为手动&#xff0c;配置网络可用 4.关闭vmware DHCP功能 一、kickstart自动安装脚本制作 1.安装图形化生成kickstart自动脚本安装工具 2.启动图形制作工具 3.图形配置脚本 这里使用的共享方式是http&#xff0…

2024靠这份软件测试面试题宝典已成功上岸,跳槽成功

上月很多朋友靠这份面试宝典拿到大厂的office&#xff0c;跳槽成功&#xff0c;面试找工作的小白和要跳槽进阶都很适合&#xff0c;没有一点准备怎么能上岸成功呢&#xff1f; 这份面试题宝库&#xff0c;包含了很多部分&#xff1a;测试理论&#xff0c;Linux基础&#xff0c…

每日OJ_牛客WY15 幸运的袋子

目录 牛客HJ62 查找输入整数二进制中1的个数 解析代码 牛客HJ62 查找输入整数二进制中1的个数 查找输入整数二进制中1的个数_牛客题霸_牛客网 解析代码 本题是计算一个数二进制表示中1的个数&#xff0c;通过&#xff08;n >> i) & 1可以获取第i位的二进制值&…

Linux中安装C#的.net,创建运行后端或控制台项目

安装脚本命令&#xff1a; 创建一个sh文件并将该文件更改权限运行 sudo apt update wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb sudo dpkg -i packages-microsoft-prod.deb sudo apt-get upd…

【文件系统】抽象磁盘的存储结构 CHS寻址法 | sector数组 | LAB数组

目录 1.为什么要抽象 2.逻辑抽象_版本1 2.1sector数组 ​2.2index转化CHS 3.逻辑抽象_版本2 3.1LBA数组 3.2LAB下标转化sector下标 文件其实就是在磁盘中占有几个扇区的问题❗文件是很多个sector的数组下标❗文件是有很多块构成的❗❗文件由很多扇区构成------>文件…

PXE+Kickstart自动化安装RHEL7.9

准备环境 1. 一台RHEL7.9主机 2. 开启主机图形 如果是7.9的主机是图形化界面了 就输入命令init 5 开启图形 如果主机一开始没装图形化界面&#xff0c;可以使用以下命令安装 # yum group install "Server with GUI" -y 3. 配置网络可用 4. 关闭VMware dhcp功…

音视频入门基础:WAV专题(5)——FFmpeg源码中解码WAV Header的实现

音视频入门基础&#xff1a;WAV专题系列文章&#xff1a; 音视频入门基础&#xff1a;WAV专题&#xff08;1&#xff09;——使用FFmpeg命令生成WAV音频文件 音视频入门基础&#xff1a;WAV专题&#xff08;2&#xff09;——WAV格式简介 音视频入门基础&#xff1a;WAV专题…

IJCAI 2024 | 时空数据(Spatial-Temporal)论文总结

2024 IJCAI&#xff08;International Joint Conference on Artificial Intelligence, 国际人工智能联合会议&#xff09;在2024年8月3日-9日在韩国济州岛举行。 本文总结了IJCAI2024有关时空数据(Spatial-temporal) 的相关论文&#xff0c;如有疏漏&#xff0c;欢迎大家补充。…