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

news2024/10/2 20:37:03

聊天中的通知及好友申请:
如下图效果在这里插入图片描述
在这里插入图片描述

聊天的效果:
在这里插入图片描述在这里插入图片描述

在这里插入图片描述
这里的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">
        <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>
        <uni-list>
          <uni-list-chat
            v-for="(friend, index) in friendsInv"
            :key="index"
            :title="friend.group ? friend.group.name : friend.user.username"
            :avatar-circle="true"
            :note="formatDate(friend.created_at) + ' 邀请'"
            badge-position="left"
            badge-text="邀请"
            showArrow
            link
            @tap="aggrees(friend.id)"
            :avatar="friend.group ? friend.group.avatar_url : friend.user.avatar_url"
          ></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="friend.type === 'group' ? friend.count + '人' : ''"
            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>
    <uni-popup ref="popupAggreeBag" type="center">
      <view class="bagDetail">
        <view class="title flex align-center justify-content-between">
          <view class="flex-sub">邀请操作</view>
        </view>
        <uni-list :border="true">
          <uni-list-item title="同意" note="同意后加入" badge-position="right" badge-text="dot" link @tap="aggree(1)"></uni-list-item>
          <uni-list-item title="拒绝" note="拒绝后则不再显示" badge-position="right" badge-text="dot" link @tap="aggree(0)"></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: '',
      friends: [],
      friendsInv: [],
      aggreeid: '',
    };
  },

  computed: {
    ...mapState(['token', 'lastMessages']),
    isLoggedIn() {
      return !!this.token;
    },
  },

  onLoad() {
    this.invateFriends();
		this.loadFriends();
  },

  mounted() {
    this.socket = io(config.apiBaseUrl, {
      query: {
        token: this.token,
      },
    });
    this.socket.on('connect', () => {
      console.log('Socket connected:', this.socket.id);
    });
    this.socket.on('disconnect', () => {
      console.log('Socket disconnected');
    });

		this.socket.on('message', (msg) => {
			console.log('Received message:', msg);
    this.friends = this.friends.map((friend) => {
        if (friend.id == msg.group_name || (friend.group && friend.group.id == msg.group_name.replace("g_", ""))) {
          friend.message = msg.content;
        }
        return friend;
      });
			
			console.log(this.friends)
		});
		
		
  },
  beforeDestroy() {
    if (this.socket) {
      this.socket.off('message', this.handleNewMessage);
      this.socket.disconnect();
    }
  },
  methods: {
    ...mapActions(['fetchFriends']),
    handleNewMessage(msg) {
      this.friends = this.friends.map((friend) => {
        if (friend.id == msg.group_name || (friend.group && friend.group.id == msg.group_name.replace("g_", ""))) {
          friend.message = msg.content;
        }
        return friend;
      });
			console.log(this.friends)
			
    },
    async aggrees(id) {
      this.aggreeid = id;
      this.$refs.popupAggreeBag.open();
    },
    async aggree(agid) {
      try {
        this.$refs.popupAggreeBag.close();
        const token = uni.getStorageSync('token');
        const [error, response] = await uni.request({
          url: `${config.apiBaseUrl}/agree`,
          method: 'POST',
          header: {
            Authorization: `Bearer ${token}`,
            'Content-Type': 'application/json',
          },
          data: {
            id: this.aggreeid,
            status: agid === 1 ? 'accepted' : 'declined',
          },
        });
        if (response.data.code === 0) {
          uni.showToast({
            title: response.data.message,
            duration: 2000,
          });
          this.loadFriends();
          this.invateFriends();
        } else {
          uni.showToast({
            title: response.data.message,
          });
        }
      } catch (error) {
        console.error(error);
      }
    },
    formatDate(date) {
      const d = new Date(date);
      const year = d.getFullYear();
      const month = (d.getMonth() + 1).toString().padStart(2, '0');
      const day = d.getDate().toString().padStart(2, '0');
      const hours = d.getHours().toString().padStart(2, '0');
      const minutes = d.getMinutes().toString().padStart(2, '0');
      return `${year}年${month}月${day}日 ${hours}:${minutes}`;
    },
    async loadFriends() {
      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}`,
          },
        });
        if (response.data) {
          this.friends = response.data.data;
        }
      } catch (error) {
        console.error(error);
      }
    },
    toChat(item) {
      const url = item.type === 'user'
        ? `/pages/index/chat?id=${item.id}&type=${item.type}&tid=${item.group_friend_id}`
        : `/pages/index/chat?id=g_${item.group_friend_id}&type=${item.type}&tid=${item.group_friend_id}`;
      uni.navigateTo({ url });
    },
    loadMoreFriends() {
      this.page++;
      this.loadFriends();
    },
    async invateFriends() {
      try {
        const token = uni.getStorageSync('token');
        const [error, response] = await uni.request({
          url: `${config.apiBaseUrl}/invateFriends`,
          method: 'GET',
          header: {
            Authorization: `Bearer ${token}`,
            'Content-Type': 'application/json',
          },
        });
        if (response.data.code === 0) {
          this.friendsInv = response.data.data.map(friend => ({
            ...friend,
            id: friend.id.toString(),
          }));
        } else {
          this.friendsInv = [];
        }
      } catch (error) {
        console.error(error);
      }
    },
    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();
    },
  },
};
</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.coloradmin.cn/o/1979119.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【资治通鉴】“ 将欲取之、必先予之 “ 策略 ① ( 魏桓子 割让土地 | 资治通鉴原文分析 | 道德经、周书、吕氏春秋、六韬 中的相似策略 )

文章目录 一、" 将欲取之、必先予之 " 策略1、魏桓子 割让土地2、资治通鉴原文分析 二、" 将欲取之、必先予之 " 类似的原理1、将欲败之&#xff0c;必姑辅之&#xff1b;将欲取之&#xff0c;必姑与之 - 周书2、将欲歙之&#xff0c;必固张之&#xff0c;…

24年第五届“华数杯”数学建模竞赛浅析

需要完整资料&#xff0c;请关注WX&#xff1a;“小何数模”&#xff01; 本次华数杯数模比赛的赛题已正式出炉&#xff0c;无论是赛题难度还是认可度&#xff0c;该比赛都是仅次于数模国赛的独一档&#xff0c;可以用于国赛前的练手训练。考虑到大家解题实属不易&#xff0c;…

24/8/4算法笔记 线性回归

在简单或多元线性回归中&#xff0c;最小二乘法用来估计模型参数&#xff0c;使得预测值与实际值之间的差异&#xff08;残差&#xff09;的平方和最小。 最小二乘法 导入代码包 import numpy as np Xnp.array([[1,1],[2,1]]) X 构建矩阵 ynp.array([14,10]) y linalg是线…

电子电气架构 --- 域控制器在新架构中的功能承担

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

五. TensorRT API的基本使用-build-trt-module

目录 前言0. 简述1. 案例运行2. 代码分析2.1 main.cpp2.2 model.cpp2.3 network.hpp 3. 案例3.1 sample_cbr3.2 sample_resBlock3.3 sample_convBNSiLU3.4 sample_c2f 总结下载链接参考 前言 自动驾驶之心推出的 《CUDA与TensorRT部署实战课程》&#xff0c;链接。记录下个人学…

力扣第五十二题——N皇后II

内容介绍 n 皇后问题 研究的是如何将 n 个皇后放置在 n n 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回 n 皇后问题 不同的解决方案的数量。 示例 1&#xff1a; 输入&#xff1a;n 4 输出&#xff1a;2 解释&#xff1a;如上图所…

C# ADC数据波形显示

1. 串口显示汉字的程序设计 using System; using System.Text; using System.Windows.Forms;namespace 汉字显示 {public partial class Form1 : Form{public Form1(){InitializeComponent();}private byte[] StringToBytes(string TheString){Encoding FromEncoding Encodin…

鼻咽癌综述

小罗碎碎念 本期推文主题&#xff1a;鼻咽癌综述 这篇文章提供了一个全面的综述&#xff0c;探讨了鼻咽癌&#xff08;NPC&#xff09;的关键研究进展&#xff0c;包括病理机制、治疗、筛查和生物标志物的发展。 文章首先强调了NPC在特定地理区域的流行情况&#xff0c;并讨论了…

微分方程的数值解法——Runge-Kutta (RK4)

Runge-Kutta (RK4)   The Runge-Kutta (RK4) methods are used to solve the solution of the non-liner ordinary differential equation. Here, we will simply summary this method.   Assume the Intial Value Piont (IVP) is satisfied: y ′ f ( t , y ) , y ( t 0 )…

深入底层源码,剖析AQS的来龙去脉!

这里写目录标题 回顾前缀知识一、Condition的概念二、Condition底层结构三、Condition源码解析3.1 newCondition()3.2 await() 总结主要方法&#xff1a; 回顾 如果你还没熟悉 AQS 中的独占锁&#xff0c;可以先看这篇文章的前导篇。上一篇文章是以ReentrantLock 里面的加锁、解…

【2024年华数杯C题老外游中国】(完整题解+代码+完整参考论文)

请问 352 个城市中所有 35200 个景点评分的最高分&#xff08;Best Score&#xff0c;简称 BS&#xff09;是多少&#xff1f;全国有多少个景点获评了这个最高评分&#xff08;BS&#xff09;&#xff1f;获评了这个最高评分&#xff08;BS&#xff09;景点最多的城市有哪些&am…

2024带你轻松玩转Parallels Desktop19虚拟机!让你在Mac电脑上运行Windows系统

大家好&#xff0c;今天我要给大家安利一款神奇的软件——Parallels Desktop 19虚拟机。这款软件不仅可以让你在Mac电脑上运行Windows系统&#xff0c;还能轻松切换两个操作系统之间的文件和应用程序&#xff0c;让你的工作效率翻倍&#xff01; 让我来介绍一下Parallels Desk…

【口语】基础英语之疑问句 | 描述一个认为音乐很重要的人

文章目录 一、基础英语之疑问句二、口语题&#xff1a;描述一个认为音乐很重要并且喜欢音乐的人 一、基础英语之疑问句 英语中的疑问句可以根据结构和用途被分为几种主要类型&#xff1a; 一般疑问句&#xff08;General Questions&#xff09;: 结构&#xff1a;助动词 主语…

Learn ComputeShader 03 Passing data to shader

这次我们想要在一个平面中生成随机运动的圆形。之前传递数据都是通过setInt&#xff0c;setVector等方法进行的&#xff0c;但是这些方法并不能一下传递大量数据&#xff0c;比如一个结构体数组&#xff0c;一个数据块。所以这次的主要内容就是通过buffer传递大量数据。 首先是…

Android 本地化、多语言切换:Localization

目录 1&#xff09;如何实现多语言切换、如何实现跟随手机语言切换而切换app语言 2&#xff09;Localization是什么 3&#xff09;不管手机语言如何&#xff0c;根据用户在App选择的语言&#xff0c;只切换App语言 4&#xff09;文字长短不一样&#xff0c;怎么办呢? 一、Lo…

积分的简介

积分的简介 集成是一种添加切片以找到整体的方法。积分可用于查找区域、体积、中心点和许多有用的东西。但是&#xff0c;最简单的方法是从找到函数和 x 轴之间的区域开始&#xff0c;如下所示&#xff1a; 1.面积是什么&#xff1f;是片 我们可以在几个点上计算函数&#xf…

Error in importing environment OpenAI Gym

题意&#xff1a;尝试导入OpenAI Gym库中的某个环境时发生了错误 问题背景&#xff1a; I am trying to run an OpenAI Gym environment however I get the following error: 我正在尝试运行一个OpenAI Gym环境&#xff0c;但是我遇到了以下错误&#xff1a; import gym env…

Spring Boot整合MyBatis-Flex

说明&#xff1a;MyBatis-Flex&#xff08;官网地址&#xff1a;https://mybatis-flex.com/&#xff09;&#xff0c;是一款数据访问层框架&#xff0c;可实现项目中对数据库的访问&#xff0c;类比MyBatis-Plus。本文介绍&#xff0c;在Spring Boot项目整合MyBatis-Flex。 创…

专业解析:U盘打不开的应对与数据恢复策略

一、U盘打不开的困境解析 在日常的数据存储与传输中&#xff0c;U盘作为便携的存储媒介&#xff0c;其重要性不言而喻。然而&#xff0c;当您急需使用U盘时&#xff0c;却遭遇“U盘打不开”的尴尬境地&#xff0c;这无疑会给工作和学习带来极大的不便。U盘打不开的原因多种多样…

Javase--Date

1.Date简介 Date的学习: 1. java.util包下的类 2.用于日期、时间的描述 3. 实际上时距离一个固定时间点1970年1月1日00:00:00的毫秒数 4.我们常用的是格林威治时间:GMT UTC:世界调整时间 5.固定时间点:说的其实是本初子午线的时间。因此北京时间是1970年1月1日8:00:…