界面如下:
<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>