前端:vite+vue3+ts+elementplus+less
后端:springboot2.7.13+mybatisplus
最终效果大致如下:
后端:
引入pom依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
运行sql
/*
Navicat Premium Data Transfer
Source Server : MyDemo
Source Server Type : MySQL
Source Server Version : 80027
Source Host : 192.168.157.134:3306
Source Schema : giveALike
Target Server Type : MySQL
Target Server Version : 80027
File Encoding : 65001
Date: 21/11/2022 23:50:34
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for article
-- ----------------------------
DROP TABLE IF EXISTS `article`;
CREATE TABLE `article` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '编号',
`content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '内容',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of article
-- ----------------------------
INSERT INTO `article` VALUES (1, '11');
INSERT INTO `article` VALUES (2, '666');
INSERT INTO `article` VALUES (3, '777');
INSERT INTO `article` VALUES (4, '999');
-- ----------------------------
-- Table structure for giveALike
-- ----------------------------
DROP TABLE IF EXISTS `giveALike`;
CREATE TABLE `giveALike` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '编号',
`user_id` int NULL DEFAULT NULL COMMENT '用户编号',
`article_id` int NULL DEFAULT NULL COMMENT '文章编号',
`is_like` int NULL DEFAULT NULL COMMENT '是否点赞(0表示未点赞,1表示点赞)',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 935456769 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of giveALike
-- ----------------------------
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '编号',
`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户名',
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '密码',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, '11', '11');
SET FOREIGN_KEY_CHECKS = 1;
项目结构
yml配置,数据库改成你自己的数据库
server:
port: 5000
spring:
application:
name: service-user
datasource:
url: jdbc:mysql://192.168.157.134:3306/giveALike?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
entity下的三个实体类
Article:
@Data
public class Article {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;//编号
private String content;//内容
@TableField(exist = false)
private int isLike;//是否点赞(0表示未点赞,1表示点赞)
}
GiveALike:
@Data
@TableName("giveALike")
public class GiveALike {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private Integer articleId;//文章编号
private Integer userId;//用户编号
private int isLike;//是否点赞(0表示未点赞,1表示点赞)
}
User:
@Data
public class User {
@TableId(value = "id", type = IdType.AUTO)
private Integer id;//用户id
private String username;//用户名
private String password;//密码
}
mapper
ArticleMapper:
@Mapper
public interface ArticleMapper extends BaseMapper<Article> {
}
GiveALikeMapper:
@Mapper
public interface GiveALikeMapper extends BaseMapper<GiveALike> {
}
UserMapper:
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
controller
@Slf4j
@CrossOrigin
@RestController
@RequestMapping("/giveALike")
public class GiveALikeController {
@Resource
private UserMapper userMapper;
@Resource
private ArticleMapper articleMapper;
@Resource
private GiveALikeMapper giveALikeMapper;
//登录
@PostMapping("/login")
public User login(@RequestBody User user) {
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.eq("username", user.getUsername());
User vo = userMapper.selectOne(userQueryWrapper);
if (vo != null && Objects.equals(vo.getPassword(), user.getPassword())) {
return vo;
} else {
return null;
}
}
//获取所有文章数据
//TODO 优化建议:使用分页,减少并发
@GetMapping("/getList")
public List<Article> getList() {
List<Article> articles = articleMapper.selectList(null);
List<Article> list = new ArrayList<>();
for (Article item : articles) {
QueryWrapper<GiveALike> wrapper = new QueryWrapper<>();
wrapper.eq("article_id", item.getId());
GiveALike giveALike = giveALikeMapper.selectOne(wrapper);
if (giveALike == null || giveALike.getIsLike() == 0) {
item.setIsLike(0);
} else {
item.setIsLike(1);
}
list.add(item);
}
return list;
}
//点赞
@PostMapping("/saveUserLike")
@Transactional
public GiveALike saveUserLike(@RequestBody GiveALike giveALike) {
QueryWrapper<GiveALike> wrapper = new QueryWrapper<>();
wrapper.eq("article_id", giveALike.getArticleId()).eq("user_id", giveALike.getUserId());
GiveALike vo = giveALikeMapper.selectOne(wrapper);
if (vo != null) {
if (vo.getIsLike() == 0) {
vo.setIsLike(1);
} else {
vo.setIsLike(0);
}
giveALikeMapper.updateById(vo);
return vo;
} else {
giveALike.setIsLike(1);
giveALikeMapper.insert(giveALike);
return giveALike;
}
}
}
前端:
相关依赖
目录结构
main.ts页面
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import router from "./router";
createApp(App).use(router).use(ElementPlus).mount('#app')
App.vue页面
<template>
<div id="app">
<router-view/>
</div>
</template>
<style>
</style>
路由router.ts
import {createRouter, createWebHistory, RouteRecordRaw} from "vue-router";
const routes: Array<RouteRecordRaw> = [
{
path: "/home",
name: '首页',
component: () => import('../page/home.vue')
},
{
path: "/",
name: '登录',
component: () => import('../page/login.vue')
},
]
const router = createRouter({
history: createWebHistory(),
routes: routes,
})
export default router
登录页面login.vue
<template>
<div class="main">
<div class="main_username">
<input v-model="user.username" class="username" type="text" placeholder="请输入用户名">
</div>
<div class="main_password">
<input v-model="user.password" class="password" type="password" placeholder="请输入密码">
</div>
<el-button @click="login" type="primary" class="main_login">登录</el-button>
</div>
</template>
<script setup lang="ts">
import {ElMessage} from 'element-plus'
import {reactive} from "vue";
import axios from "axios";
import router from "../router";
interface User {
username: string,
password: string
}
//登录所需的信息
const user = reactive<User>({
username: '11',
password: '11'
})
//登录
const login = () => {
axios.post("http://localhost:5000/giveALike/login", user).then((res) => {
if (res.data != '') {
ElMessage({
message: '登录成功',
type: 'success',
})
//跳转页面
router.push("/home")
//将用户信息存到session
window.sessionStorage.setItem("user", JSON.stringify(res.data))
} else {
ElMessage.error('登录失败')
}
})
}
</script>
<style lang="less" scoped>
.main {
.main_username {
margin-bottom: 10px;
input {
font-size: 30px;
}
}
.main_password {
input {
font-size: 30px;
}
}
.main_login {
font-size: 30px;
margin-top: 10px;
}
}
</style>
home.vue页面
<template>
<div>
<div v-for="item in data.articleList" :key="item.id">
<div class="item">
<div class="item_id">编号:{
{ item.id }}</div>
<div class="item_content">内容:{
{ item.content }}</div>
<div>{
{ item }}</div>
<img v-if="item.isLike===0" @click="handleLike(item.id)" src="../assets/未点赞.png" class="item_img" alt="图片">
<img v-else @click="handleLike(item.id)" src="../assets/点赞.png" class="item_img" alt="图片">
</div>
</div>
</div>
</template>
<script setup lang="ts">
import axios from "axios";
import {reactive} from "vue";
import {ElMessage} from "element-plus";
const data = reactive({
articleList: [] as Article[],//所有内容列表
userLikeList: [] as any[]//当前用户点赞表的数据
})
interface GiveALike {
articleId: number,
userId: number
}
interface Article {
id: number,
content: string,
isLike?: number
}
//点赞所需的参数
const giveALikeParam = reactive<GiveALike>({
articleId: 0,//文章编号
userId: 0//用户编号
})
//获取所有内容
const getList = () => {
axios.get("http://localhost:5000/giveALike/getList").then((res) => {
data.articleList = res.data
console.log(res.data)
})
}
//点赞
const handleLike = (row: number) => {
//从session中获取用户信息
const user = JSON.parse(window.sessionStorage.getItem("user") ?? '')
//设置用户编号
giveALikeParam.userId = user.id
//设置文章编号
giveALikeParam.articleId = row
axios.post("http://localhost:5000/giveALike/saveUserLike", giveALikeParam).then((res) => {
data.articleList[row - 1].isLike = data.articleList[row - 1].isLike === 1 ? 0 : 1;
if (res.data.isLike == 1) {
ElMessage({
message: '点赞成功',
type: 'success',
})
} else {
ElMessage({
message: '取消点赞',
type: 'warning',
})
}
})
}
getList()
</script>
<style lang="less" scoped>
.item {
display: flex;
.item_id {
margin-right: 10px;
}
.item_content {
width: 100px;
}
.item_img {
width: 100px;
height: 100px;
cursor: pointer;
}
}
</style>
Gitee仓库地址:Gitee
上述代码如有问题,欢迎提出来,博主看到了会第一时间解决