柚见十三期(优化)
前端优化
加载匹配功能与加载骨架特效
骨架屏 : 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;相关文章:
柚见十三期(优化)
前端优化 加载匹配功能与加载骨架特效 骨架屏 : vant-skeleton index.vue中 /** * 加载数据 */ const loadData async () > { let userListData; loading.value true; //心动模式 if (isMatchMode.value){ const num 10;//推荐人数 userListData await myA…...
Node.js常用命令:了解Node.js的核心命令和用法
学习目标: 理解Node.js和npm的概念及其在开发中的作用;掌握Node.js的核心命令,包括node、npm、npx等;学会使用node命令来执行JavaScript文件和模块;熟悉npm命令,包括安装、更新、卸载依赖包等操作…...
QT 驾校系统界面布局编写
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow) {ui->setupUi(this);this->resize(ui->label_img->width(),ui->label_img->height());//图片自适应窗口大小ui->label_img->setScaledContents(true);//图片置…...
【Auth Proxy】为你的 Web 服务上把锁
Auth Proxy 一个极简的用于 Web 服务鉴权的反向代理服务 极其简约的 UI对你的真实服务无任何侵入性支持容器部署,Docker Image 优化到不能再小(不到 9MB)GitHub:https://github.com/wengchaoxi/auth-proxy 效果 我在 http://lo…...
Docker 从0安装 nacos集群
前提条件 Docker支持一下的CentOs版本 Centos7(64-bit),系统内核版本为 3.10 以上Centos6.5(64-bit) 或者更高版本,系统内核版本为 2.6.32-431 或者更高版本 安装步骤 使用 yum 安装(CentOS 7下) 通过 uname -r 命令查看你当…...
keithley2612A数字源表
181/2461/8938产品概述: Keithley 2612A源表既可用作台式I-V表征工具,也可用作多通道I-V测试系统的构建模块组件。对于台式使用,吉时利2612ASourceMeter具有嵌入式TSP Express软件工具,允许用户快速轻松地执行常见的I-V测试&…...
AI助手 - 月之暗面 Kimi.ai
前言 这是 AI工具专栏 下的第四篇,这一篇所介绍的AI,也许是截至今天(204-03-19)国内可访问的实用性最强的一款。 今年年初,一直看到有人推荐 Kimi,不过面对雨后春笋般的各类品质的AI,说实话也有…...
《计算机考研精炼1000题》为你考研之路保驾护航
创作背景 在这个充满挑战与竞争的时代,每一位考生在备战研究生考试的过程中,都希望通过更多符合考纲要求的练习题来提高自己的知识和技能。为了满足这一需求,我们精心策划和编辑了这本《计算机考研精炼1000题》。在考研政治和考研数学领域&a…...
el-input添加keyup事件无响应
<el-input type"password" v-model"formData.password" keyup.enter"onSubmit"></el-input>使用 .native修饰符 有时,Vue 组件内部可能会处理某些事件,导致你不能直接在组件上监听这些事件。 在这种情况下&am…...
错误1075:依存服务不存在, 或已标记为删除的解决方法
错误1075:依存服务不存在, 或已标记为删除的解决方法 运行 sc config spooler depend rpcss 注意"depend “而不是"depend”。...
【Python】使用selenium对Poe批量模拟注册脚本
配置好接码api即可实现自动化注册登录试用一体。 运行后会注册账号并绑定邮箱与手机号进行登录试用。 测试结果30秒一个号 import re import time import requests from bs4 import BeautifulSoup from selenium import webdriver from selenium.webdriver.chrome.options imp…...
【Linux】编译器-gcc/g++的使用(预处理、编译、汇编、连接)
目录 01.预处理(宏替换) 02.编译(生成汇编) 03.汇编(生成机器可识别码) 04.连接(生成可执行文件或库文件) 05.选项 编译器在编译代码时包含以下四个步骤:1.预处理 2…...
【Linux】Linux安装软件---软件包管理器 yum
主页:醋溜马桶圈-CSDN博客 专栏:Linux_醋溜马桶圈的博客-CSDN博客 gitee:mnxcc (mnxcc) - Gitee.com 目录 1.Linux中安装软件 1.1 源代码安装 1.2 rpm包安装 1.3 yum安装 1.3.1 举例 1.3.2 图示yum下载安装 2.Linux系统的生态 如何选…...
QT网络编程之获取本机网络信息
一.概述 查询一个主机的MAC地址或者IP地址是网络应用中常用到的功能,Qt提供了QHostInfo和QNetworkInterface 类可以用于此类信息的查询 1.QHostInfo 类(显示和查找本地的信息) 2.QNetworkInterface 类(获得应用程序上所在主机的…...
离线安装docker、docker-compose、Mysql镜像
离线安装docker docker-compose mysql镜像 一、下载docker docker-compose mysql 镜像文件 1、首先下载docker镜像 博主所用文件版本号: docker-23.0.6.tgz 下载docker 地址 :https://blog.csdn.net/xiaohanshasha/article/details/135489623?spm1001…...
Redis系列学习文章分享---第九篇(Redis快速入门之好友关注--关注和取关 -共同关注 -Feed流实现方案分析 -推送到粉丝收件箱 -滚动分页查询)
Redis的实战篇-好友关注 目录 好友关注-关注和取关好友关注-共同关注好友关注-Feed流实现方案分析好友关注-推送到粉丝收件箱好友关注-滚动分页查询收件箱的思路好友关注-实现滚动分页查询 1. 好友关注-关注和取关 1.1 概述 在好友关注系统中,用户可以关注其他用…...
数据库基本介绍及编译安装mysql
目录 数据库介绍 数据库类型 数据库管理系统(DBMS) 数据库系统 DBMS的工作模式 关系型数据库的优缺点 编译安装mysql 数据库介绍 数据:描述事物的的符号纪录称为数据(Data) 表:以行和列的形式组成…...
代码随想录算法训练营第55天 | 583. 两个字符串的删除操作, 72. 编辑距离
动态规划章节理论基础: https://programmercarl.com/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html 583. 两个字符串的删除操作 题目链接:https://leetcode.cn/problems/delete-operation-for-two-strings/descrip…...
Guava之EventBus源码分析
简介 事件总线。 有助于深入理解代码的功能和实现细节。 可以了解代码背后的逻辑、算法、数据结构和设计模式等方面,从而更好地理解代码的作用和功能。 可以学习到业界的最佳实践和设计模式。 这有助于提高自己的编程水平,使你能够编写更高质量、可…...
Spark on Yarn安装配置
目录 前言 初了解spark Standalone模式 Yarn模式 前言 今天我们讲解Spark的安装配置,spark的部署分为两种,一种是Standalone模式,另一种就是on yarn 模式,我们这一节着重讲解on yarn 模式,因为符合生产活动&#…...
计算机网络习题及答案
仅供参考第一章 概述1、计算机网络可以向用户提供哪些服务?答:基于互联网的连通性和共享,计算机网络可以向用户提供:①信息交换服务,如电子邮件(收发信息)、文件传输(上传和下载大文…...
安卓逆向效率翻倍:用NP管理器3.0.18的Dex混淆与资源加密,给你的APK加把“锁”
安卓应用安全加固实战:NP管理器3.0高级混淆与加密技术解析 在移动应用开发领域,安全防护始终是开发者面临的核心挑战之一。随着安卓生态的开放特性,APK文件被反编译、篡改的风险与日俱增。对于金融、游戏等高价值应用而言,一套完善…...
【Dify 2026边缘部署权威指南】:20年架构师亲授7步极简落地法,错过再等三年
第一章:Dify 2026边缘部署的战略定位与架构演进全景Dify 2026将边缘智能从“能力延伸”升维为“决策原生”,其战略内核在于构建轻量、自治、可编排的AI推理闭环,使模型服务在离数据源头50ms延迟圈内完成感知—推理—响应全链路。这一范式迁移…...
SQL触发器实现自动生成流水号_配合序列对象实现递增逻辑
触发器中调用NEXTVAL报错主因是语法误用:Oracle应使用赋值语句:NEW.id : seq_name.NEXTVAL而非SELECT INTO;PostgreSQL须用nextval(seq_name);MySQL无原生序列,需借AUTO_INCREMENT与LAST_INSERT_ID()模拟。触发器里调用 NEXTVAL 为…...
如何实现typed.js动画模块的按需加载:提升网页性能的完整指南
如何实现typed.js动画模块的按需加载:提升网页性能的完整指南 【免费下载链接】typed.js A JavaScript Typing Animation Library 项目地址: https://gitcode.com/gh_mirrors/ty/typed.js typed.js是一款轻量级的JavaScript打字动画库,能够为网页…...
终极Outline数据备份策略:保护团队知识库的完整指南
终极Outline数据备份策略:保护团队知识库的完整指南 【免费下载链接】outline The fastest knowledge base for growing teams. Beautiful, realtime collaborative, feature packed, and markdown compatible. 项目地址: https://gitcode.com/GitHub_Trending/ou…...
Opengl笔记之颜色混合
混合是为了实现绘制半透明物体...
从零构建B站数据生态:Python异步API架构深度解析
从零构建B站数据生态:Python异步API架构深度解析 【免费下载链接】bilibili-api 哔哩哔哩常用API调用。支持视频、番剧、用户、频道、音频等功能。原仓库地址:https://github.com/MoyuScript/bilibili-api 项目地址: https://gitcode.com/gh_mirrors/b…...
从MATLAB仿真到FPGA上板:一个8Mbps通信系统的成形滤波器全链路实现
从MATLAB仿真到FPGA上板:一个8Mbps通信系统的成形滤波器全链路实现 在数字通信系统的基带处理环节中,成形滤波器扮演着至关重要的角色。它负责将离散的数字信号转换为适合在信道中传输的连续波形,同时有效控制信号带宽。对于采用FPGA实现通信…...
