柚见十三期(优化)

前端优化

加载匹配功能与加载骨架特效

骨架屏 : vant-skeleton

index.vue中

/**  * 加载数据  */  
const loadData = async () => {  let userListData;  loading.value = true;  //心动模式  if (isMatchMode.value){  const  num = 10;//推荐人数  userListData = await myAxios.get('user/match',{  params: {  num,  },  })  .then(function (response) {  console.log('/user/match succeed',response);  return response?.data;  })  .catch(function (error) {  console.log('/user/match error',error);  showFailToast('请求失败');  });  }  else {  //不开启推荐模式,默认全部查询  //普通用户使用分页查询todo 并没有实现  userListData = await  myAxios.get('/user/recommend',{  params: {  pageSize: 8,  pageNum: 1,  },  })  .then(function (response) {  console.log('/user/recommend succeed', response);  return response?.data?.records;  })  .catch(function (error) {  console.log('/user/recommends error',error);  showFailToast('请求失败');  });  }  if (userListData){  userListData.forEach((user: userType) =>{  if (user.tags){  user.tags = JSON.parse(user.tags);  }  })  userList.value = userListData;  }  loading.value = false;  
}  //`watchEffect`函数用于在响应式数据发生变化时执行副作用(side effect)操作。在这个例子中,当数据发生变化时,会调用`loadData()`函数来加载数据。watchEffect(() =>{  loadData();  
})

在这里插入图片描述

前端导航标题

router :控制路由跳转
route: 获取路由信息

解决:使用 router.beforeEach,根据要跳转页面的 url 路径 匹配 config/routes 配置的 title 字段

在这里插入图片描述

在这里插入图片描述

//标题  
const DEFAULT_TITLE='柚见'  
const title=ref(DEFAULT_TITLE);  
/**  * 根据路由切换标题  */  
router.beforeEach((to,from)=>{  const toPath=to.path;  const route=routes.find((r)=>{  return toPath==r.path;  })  title.value=route.title ?? DEFAULT_TITLE;  })

重定向到登录页

根据后端返回的未登录的错误码,重定向

全局响应拦截器中修改
在这里插入图片描述

发现页面一直不能正常跳转
切换hash模式
不同的历史模式 | Vue Router (vuejs.org)
在这里插入图片描述

登录成功后自动跳转回之前页面

在这里插入图片描述

添加创建队伍按钮

添加样式

在这里插入图片描述

引入样式

在这里插入图片描述

在这里插入图片描述

换成vant库中的+
在这里插入图片描述

<!--  创建队伍按钮-->  <van-button type="primary" @click="doJoinTeam" class="add-button" icon="plus">  </van-button>

队伍操作按钮权限控制

更新队伍:仅创建人可见

 v-if="team.userId === currentUser?.id"

解散队伍:仅创建人可见

 v-if="team.userId === currentUser?.id"

加入队伍: 仅非队伍创建人、且未加入队伍的人可见
退出队伍:创建人不可见,仅已加入队伍的人可见

后端修改

仅加入队伍和创建队伍的人能看到队伍操作按钮(listTeam 接口要能获取我加入的队伍状态) ✔

方案 1:前端查询我加入了哪些队伍列表,然后判断每个队伍 id 是否在列表中(前端要多发一次请求)

方案 2:在后端去做上述事情(推荐)

加密队伍与公开队伍的展示

前端修改

Tab 标签页 - Vant 4 (gitee.io)

<van-tabs v-model:active="activeName">  <van-tab title="公开队伍" name="public"></van-tab>  <van-tab title="加密队伍" name="secret"></van-tab>  
</van-tabs>

在这里插入图片描述

const onTabChange=(name)=>{  if(name==='public')  {  //查询公开队伍  listTeam()  }else if(name === 'secret'){  //查询加密队伍  listTeam(' ',2);  }  console.log(name)  }
//只有管理员和本人才能查看非私有的房间  
if (!isManager && !statusEnum.equals(TeamStatusEnum.PUBLIC) && !statusEnum.equals(TeamStatusEnum.SECRET)) {  
throw new BusinessException(ErrorCode.NO_AUTH);  
}

点击加入加密队伍后,弹窗密码跳出

在这里插入图片描述

<script setup lang="ts">  
import {TeamType} from "../models/team";  
import {teamStatusEnum} from "../constants/team.ts";  
import myAxios from "../plungins/myAxios.js";  
import {showFailToast, showSuccessToast} from "vant";  
import {onMounted, ref} from "vue";  
import {getCurrentUser} from "../services/user.ts";  
import {useRouter} from "vue-router";  const  router=useRouter();  
const password=ref('');  
//加入该队伍的id  
const joinId=ref(0)  
const showPwd=ref(false)  
interface TeamCardListProps{  teamList: TeamType[];  
}  const props= withDefaults(defineProps<TeamCardListProps>(),{  //@ts-ignore  teamList: [] as TeamType[]  
});  //获得当前用户  
const currentUser=ref()  
onMounted(async ()=>{  const res=await getCurrentUser()  currentUser.value=res  
})  //加入队伍之前---------------------------------------------------------------------  
const preJoinTeam=(team : TeamType)=>{  joinId.value=team.id;  if(team.status === 2)  {  //打开加密弹窗  showPwd.value=true;  }  else if(team.status === 0)  {  doJoinTeam();  }  
}  
const doJoinCancel = () => {  joinId.value = 0;  password.value = '';  
}  
//点击公开队伍,加入队伍------------------------------------------------------------  
const doJoinTeam=async()=>{  if(!joinId.value)  {  return ;  }  const res=await myAxios.post('/team/join',{  teamId:joinId.value,  password:password.value  })  if(res?.code===0){  showSuccessToast("加入成功")  doJoinCancel();  }else{  showFailToast("加入失败\n"+(res.description ? `${res.description}`: ''))  }  
}  
//跳转到更新页---------------------------------------------------------------------  
const doUpdateTeam=({id}: { id: any })=>{  router.push({  path:'/team/update',  query:{  id  }  })  
}  
//点击退出队伍-------------------------------------------------------------------  
const doQuitTeam=async (id)=>{  const res=await myAxios.post('/team/quit',{  teamId:id  })  if(res?.code===0){  showSuccessToast("操作成功")  }else{  showFailToast("操作失败\n"+(res.description ? `${res.description}`: ''))  }  }  
//点击解散队伍-------------------------------------------------------------------------  
const doDeleteTeam=async ({id}: { id: any })=>{  const res=await myAxios.post('/team/delete',{  id  })  if(res?.code===0){  showSuccessToast("操作成功")  }else{  showFailToast("操作失败\n"+(res.description ? `${res.description}`: ''))  }  
}  </script>

已加入队伍人数

<div>  {{ `已加入人数 : ${team.hasJoinNum} / ` + team.maxNum }}  
</div>

后端优化

队伍按钮权限控制

在这里插入图片描述

在这里插入图片描述

@GetMapping("/list")  
public BaseResponse<List<TeamUserVO>> listTeams(TeamQuery teamQuery,HttpServletRequest request) {  if (teamQuery == null) {  
throw new BusinessException(ErrorCode.PARAMS_ERROR);  
}  
boolean isManager = userService.isManager(request);  
User loginUser = userService.getLoginUser(request);  // 1、查询队伍列表  
List<TeamUserVO> teamList = teamService.listTeams(teamQuery, isManager);  
final List<Integer> teamIdList = teamList.stream().map(TeamUserVO::getId).collect(Collectors.toList());  
// 2、判断当前用户是否已加入队伍  
QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();  
try {  
userTeamQueryWrapper.eq("userId", loginUser.getId());  
userTeamQueryWrapper.in("teamId", teamIdList);  
List<UserTeam> userTeamList = userTeamService.list(userTeamQueryWrapper);  
// 已加入的队伍 id 集合  
Set<Integer> hasJoinTeamIdSet = userTeamList.stream().map(UserTeam::getTeamId).collect(Collectors.toSet());  
teamList.forEach(team -> {  
boolean hasJoin = hasJoinTeamIdSet.contains(team.getId());  
team.setHasJoin(hasJoin);  
});  
} catch (Exception e) {}  return ResultUtils.success(teamList);  
}

查询已加入队伍的人数

//3.查询已加入队伍数  
QueryWrapper<UserTeam> userTeamQueryWrapper2 = new QueryWrapper<>();  
userTeamQueryWrapper.in("teamId", teamIdList);  
List<UserTeam> userTeamList2 = userTeamService.list(userTeamQueryWrapper2);  //队伍id => userId集合  
Map<Integer, List<UserTeam>> teamIdUserTeamList = userTeamList2.stream().collect(Collectors.groupingBy(UserTeam::getTeamId));  
teamList.forEach(team ->  
team.setHasJoinNum(teamIdUserTeamList.getOrDefault(team.getId(), new ArrayList<>()).size())  
);

多次快速点击加入队伍,重复加入队伍

只要我们点的足够快,就可以在同一时间内往数据库插入多条同样的数据,所以这里我们使用分布式锁(推荐)使用两把锁,一把锁锁队伍,一把锁锁用户(实现较难,不推荐)

之前的代码

//该用户已加入的队伍数量不能超过5个  
int userId = loginUser.getId();  
// 只有一个线程能获取到锁  
RLock lock = redissonClient.getLock("youjian:join_team");  QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();  
userTeamQueryWrapper.eq("userId",userId);  
int hasJoinNum = (int) userTeamService.count(userTeamQueryWrapper);  
if (hasJoinNum >= 5){  
throw new BusinessException(ErrorCode.PARAMS_ERROR,"最多创建和加入5个队伍");  
}  //不能重复加入已加入的队伍  
userTeamQueryWrapper = new QueryWrapper<>();  
userTeamQueryWrapper.eq("userId",userId);  
userTeamQueryWrapper.eq("teamId",teamId);  
int hasUserJoinTeam = (int) userTeamService.count(userTeamQueryWrapper);  
if (hasUserJoinTeam > 0){  
throw new BusinessException(ErrorCode.PARAMS_ERROR,"用户已加入该队伍");  
}  
//不能加入已满队伍  
userTeamQueryWrapper = new QueryWrapper<>();  
userTeamQueryWrapper.eq("teamId",teamId);  
int teamHasJoinNum = (int) userTeamService.count(userTeamQueryWrapper);  
if (teamHasJoinNum >= team.getMaxNum()){  
throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍已满");  
}  //3.加入,修改队伍信息  
UserTeam userTeam = new UserTeam();  
userTeam.setUserId(userId);  
userTeam.setTeamId(teamId);  
userTeam.setJoinTime(new Date());  
return userTeamService.save(userTeam);

修改后

@Resource  
private RedissonClient redissonClient;

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

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

相关文章

软件应用,台球管理系统怎么收费,球房计时计费系统的安装方法,计时灯控器连接

软件应用&#xff0c;台球管理系统怎么收费&#xff0c;球房计时计费系统的安装方法&#xff0c;计时灯控器连接 一、前言 以下软件操作教程以 佳易王桌球计时计费管理系统软件V17.9为例说明 件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 1、软件的安装…

【Spring Boot】创建你的第一个 Spring Boot 应用

创建你的第一个 Spring Boot 应用 1.环境配置2.步骤详解3.项目结构分析3.1 入口类 DemoApplication3.2 控制器 PathVariableController3.3 控制器 BasicController3.4 模型 User 4.运行 Spring Boot 目前已经成为了 Java 开发领域的框架范式。本篇博客&#xff0c;我将带领大家…

Vision Pro的初体验

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

redis学习-redis介绍以及一些通用命令

目录 1.redis介绍 2.redis常用命令&#xff08;可以在官网的命令中查看redis的所有命令&#xff09; 2.1终端命令 2.2 redis通用命令 1.redis介绍 redis即远程字典服务&#xff0c;是当下最热门的NoSQL&#xff08;非关系型数据库&#xff09;技术之一&#xff0c;采用…

中文编程入门(Lua5.4.6中文版)第四章 Lua 循环

在游戏开发的奇幻世界中&#xff0c;循环机制就像一位执着的冒险者&#xff0c;在特定规则&#xff08;条件&#xff09;的指引下&#xff0c;会不断重复执行一系列精心设计的游戏动作。在 Lua 这款强大而灵活的游戏引擎中&#xff0c;我们有几种独特的“游戏回合”来实现这一规…

C++算法学习心得八.动态规划算法(4)

1.零钱兑换&#xff08;322题&#xff09; 题目描述&#xff1a; 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额&#xff0c;返回 -1。 你可以认为每种硬币的数量是无限的。…

ArkTs的资源Resource类型怎么转为string

使用ResourceManager同步转换 请参看&#xff1a;ResourceManager.getStringSync9 例子&#xff1a; try { let testStr: string this.context.resourceManager.getStringSync($r(app.string.test).id); } catch (error) { console.error(getStringSync failed, error code…

@RequestParam、@PathVariable、@RequestBody

1、中文翻译 RequestParam-请求参数、PathVariable-路径变量、RequestBody请求体 2、作用&#xff1a; Controller中获取前端传递的参数 3、从注解本身角度分析 3.1、PathVariable&#xff1a;路径变量 通过 PathVariable 可以将URL中占位符参数{xxx}绑定到处理器类的方法形…

基于单片机的智能小车泊车系统设计

摘 要:随着信息技术的进步,汽车逐渐朝着安全、智能方向发展,智能泊车系统的出现不仅能帮助人们更加快速、安全地完成泊车操作,而且适用于狭小空间的泊车操作,降低驾驶员泊车负担,减轻泊车交通事故发生率。文章基于单片机设计自动泊车系统,以单片机为核心来实现信息收集及…

Java程序OOM自动生成.hprof堆文件并使用jvisualvm分析

Java程序OOM自动生成.hprof堆文件并使用jvisualvm分析 1.示例代码2.编译两份源代码3.带jvm参数启动当内存溢出后&#xff0c;命令会出现4.启动jvisualvm5.导入堆文件 1.示例代码 同目录下准备两个.java源文件 StudentOne.java public class StudentOne {private String id;pri…

【Python】成功解决NameError: name ‘cv2‘ is not defined

【Python】成功解决NameError: name ‘cv2’ is not defined &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希望得到您…

Rabbit MQ详解

写在前面,由于Rabbit MQ涉及的内容较多&#xff0c;赶在春招我个人先按照我认为重要的内容进行一定总结&#xff0c;也算是个学习笔记吧。主要参考官方文档、其他优秀文章、大模型问答。自己边学习边总结。后面有时间我会慢慢把所有内容补全&#xff0c;分享出来也是希望可以给…

ARM和AMD介绍

一、介绍 ARM 和 AMD 都是计算机领域中的知名公司&#xff0c;它们在不同方面具有重要的影响和地位。 ARM&#xff08;Advanced RISC Machine&#xff09;&#xff1a;ARM 公司是一家总部位于英国的公司&#xff0c;专注于设计低功耗、高性能的处理器架构。ARM 架构以其精简指…

20240313-2-search

search bfs 和 dfs的相关的题目 1. 全排列 题目: 给定一个数字列表&#xff0c;返回其所有可能的排列。 // premute(ans, nums, 0) void permute(vector<vector<int> > &ans, vector<int> &nums, int k){if(knums.size()-1){ans.push_back(nums);…

算法打卡day19|二叉树篇08|Leetcode 235. 二叉搜索树的最近公共祖先、701.二叉搜索树中的插入操作、450.删除二叉搜索树中的节点

算法题 Leetcode 235. 二叉搜索树的最近公共祖先 题目链接:235. 二叉搜索树的最近公共祖先 大佬视频讲解&#xff1a;二叉搜索树的最近公共祖先视频讲解 个人思路 昨天做过一道二叉树的最近公共祖先&#xff0c;而这道是二叉搜索树&#xff0c;那就要好好利用这个有序的特点…

zed2i相机驱动的安装(2)

安装完sdk和wrapper&#xff0c;启动时显示缺少标定文件&#xff0c;第一反应是运行自带的标定程序 但是此时运行ZED tools里的标定程序也会出问题 打开 On Linux : /usr/local/zed/settings/On Windows : C:\ProgramData\Stereolabs\settings 查看里面是否是空的&#xff…

Android系统签名的制作与使用

目录 1. &#x1f4c2; 背景 2. &#x1f531; 制作Android系统签名 步骤一&#xff1a;找到platform.pk8和platform.x509.pem签名文件 步骤二&#xff1a;下载keytool-importkeypair签名工具 步骤三&#xff1a;使用签名文件和签名工具生成.jks签名文件 3. ✅ 使用Andro…

嵌入空间(Embedding Space)

摘要&#xff1a; 嵌入空间&#xff08;Embedding Space&#xff09;是一种在数学、机器学习和自然语言处理等领域广泛应用的概念。它指的是将原本复杂、离散或者高维的数据结构转换为一个连续的、低维向量空间的过程&#xff0c;使得这些数据能够在新的空间中以向量的形式表示…

AI_寻路系统_修改寻路网格体__下

虚幻引擎的 寻路系统&#xff08;Navigation System&#xff09; 向人工智能代理提供了寻路功能。为了能够找到开始位置和目的地之间的路径&#xff0c;从世界的碰撞几何结构生成了寻路网格体。 默认设置将寻路网格体细分为图块&#xff0c;以允许重建寻路网格体的本地化部件。…

FFplay播放参数详解决及示例

1. -version 查看版本 2. -buildconf 查看编译配置 3. -formats 显示所有支持的媒体格式 4. -muxers 查看所有的封装 5. -demuxers 查看所有支持的解封装