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