柚见十三期(优化)
前端优化
加载匹配功能与加载骨架特效
骨架屏 : 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 模式,因为符合生产活动&#…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...

【Linux】使用1Panel 面板让服务器定时自动执行任务
服务器就是一台24小时开机的主机,相比自己家中不定时开关机的主机更适合完成定时任务,例如下载资源、备份上传,或者登录某个网站执行一些操作,只需要编写 脚本,然后让服务器定时来执行这个脚本就可以。 有很多方法实现…...

Qt/C++学习系列之列表使用记录
Qt/C学习系列之列表使用记录 前言列表的初始化界面初始化设置名称获取简单设置 单元格存储总结 前言 列表的使用主要基于QTableWidget控件,同步使用QTableWidgetItem进行单元格的设置,最后可以使用QAxObject进行单元格的数据读出将数据进行存储。接下来…...
VUE3 ref 和 useTemplateRef
使用ref来绑定和获取 页面 <headerNav ref"headerNavRef"></headerNav><div click"showRef" ref"buttonRef">refbutton</div>使用ref方法const后面的命名需要跟页面的ref值一样 const buttonRef ref(buttonRef) cons…...