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

avatar
作者
猴君
阅读量:0

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

<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 token         await this.fetchUser();         // Redirect to the friends page if the user is authenticated         uni.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') || '', // 从本地存储中获取 token 		user: 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-chat             v-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"             showArrow             link             @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> 

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!