【前后端的那些事】评论功能实现

文章目录

    • 聊天模块
      • 1. 数据库表
      • 2. 后端初始化
        • 2.1 controller
        • 2.2 service
        • 2.3 dao
        • 2.4 mapper
      • 3. 前端初始化
        • 3.1 路由创建
        • 3.2 目录创建
        • 3.3 tailwindCSS安装
      • 4. tailwindUI
      • 5. 前端代码编写

前言:最近写项目,发现了一些很有意思的功能,想写文章,录视频把这些内容记录下。但这些功能太零碎,如果为每个功能都单独搭建一个项目,这明显不合适。于是我想,就搭建一个项目,把那些我想将的小功能全部整合到一起。实现 搭一次环境,处处使用。

本文主要实现以下功能

  1. 评论功能

环境搭建
文章链接

已录制视频
视频链接

仓库地址
https://github.com/xuhuafeifei/fgbg-font-and-back.git

聊天模块

效果展示

在这里插入图片描述

1. 数据库表

CREATE TABLE `communicate` (`id` int NOT NULL AUTO_INCREMENT,`content` varchar(255) COLLATE utf8mb4_croatian_ci DEFAULT NULL,`create_time` datetime DEFAULT NULL,`pid` int DEFAULT NULL,`user_id` int DEFAULT NULL,`reply_user_id` int DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_croatian_ci;

2. 后端初始化

2.1 controller

CommunicateController

package com.fgbg.demo.controller;import com.fgbg.common.utils.R;
import com.fgbg.demo.entity.Communicate;
import com.fgbg.demo.service.CommunicateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@RequestMapping("comm")
@RestController
public class CommunicateController {@Autowiredprivate CommunicateService service;/*** 返回树形结构评论数据*/@RequestMapping("/list")public R list() {List<Communicate> list = service.listTree();return R.ok().put("data", list);}/*** 保存评论*/@RequestMapping("/save")public R save(@RequestBody Communicate entity) {service.save(entity);return R.ok();}
}
2.2 service

CommunicateServiceImpl

package com.fgbg.demo.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fgbg.demo.dao.CommunicateDao;
import com.fgbg.demo.entity.Communicate;
import com.fgbg.demo.service.CommunicateService;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;/****/
@Service
public class CommunicateServiceImpl extends ServiceImpl<CommunicateDao, Communicate>implements CommunicateService{/*** 返回树形评论数据** @return*/@Overridepublic List<Communicate> listTree() {List<Communicate> list = this.list();// 映射id->indexHashMap<Integer, Integer> map = new HashMap<>();for (int index = 0; index < list.size(); index++) {map.put(list.get(index).getId(), index);}// 遍历寻找父节点for (Communicate communicate : list) {Integer pid = communicate.getPid();// 有父节点if (pid != null) {// 获取父节点idInteger indexFather = map.get(pid);Communicate father = list.get(indexFather);if (father.getChildren() == null) {father.setChildren(new ArrayList<>());}// 在父节点上添加当前节点father.getChildren().add(communicate);}}// 过滤出一级节点List<Communicate> ans = list.stream().filter(child -> child.getPid() == null).collect(Collectors.toList());return ans;}
}
2.3 dao

CommunicateDao

package com.fgbg.demo.dao;import com.fgbg.demo.entity.Communicate;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;/*** @Entity com.fgbg.demo.entity.Communicate*/
@Mapper
public interface CommunicateDao extends BaseMapper<Communicate> {}
2.4 mapper

CommunicateMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fgbg.demo.dao.CommunicateDao"><resultMap id="BaseResultMap" type="com.fgbg.demo.entity.Communicate"><id property="id" column="id" jdbcType="INTEGER"/><result property="content" column="content" jdbcType="VARCHAR"/><result property="createTime" column="create_time" jdbcType="TIMESTAMP"/><result property="pid" column="pid" jdbcType="INTEGER"/><result property="userId" column="user_id" jdbcType="INTEGER"/><result property="replyUserId" column="reply_user_id" jdbcType="INTEGER"/></resultMap><sql id="Base_Column_List">id,content,create_time,pid,user_id</sql>
</mapper>

3. 前端初始化

3.1 路由创建

/src/router/modules/communicate.ts

const { VITE_HIDE_HOME } = import.meta.env;
const Layout = () => import("@/layout/index.vue");export default {path: "/communicate",name: "communicate",component: Layout,redirect: "/communicate",meta: {icon: "homeFilled",title: "沟通",rank: 0},children: [{path: "/communicate",name: "communicate",component: () => import("@/views/communicate/communicate.vue"),meta: {title: "评论",showLink: VITE_HIDE_HOME === "true" ? false : true}}]
} as RouteConfigsTable;
3.2 目录创建

/src/views/communicate/communicate.vue

3.3 tailwindCSS安装
  • 安装

    pnpm install -D tailwindcss postcss autoprefixer
    
  • 输入命令初始化tailwind和postcss配置文件

    npx tailwindcss init -p
    
  • 打开vue项目,在src目录下新建一个css文件:index.css,在文件中写入

    @tailwind base;@tailwind components;@tailwind utilities;
    
  • main.ts中引入

    import './index.css'
    

检查tailwind.config.js。我的项目中,文件代码为

/** @type {import('tailwindcss').Config} */
module.exports = {darkMode: "class",corePlugins: {preflight: false},content: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],theme: {extend: {colors: {bg_color: "var(--el-bg-color)",primary: "var(--el-color-primary)",text_color_primary: "var(--el-text-color-primary)",text_color_regular: "var(--el-text-color-regular)"}}}
};

stylelint.config.js

module.exports = {root: true,extends: ["stylelint-config-standard","stylelint-config-html/vue","stylelint-config-recess-order"],plugins: ["stylelint-order", "stylelint-prettier", "stylelint-scss"],overrides: [{files: ["**/*.(css|html|vue)"],customSyntax: "postcss-html"},{files: ["*.scss", "**/*.scss"],customSyntax: "postcss-scss",extends: ["stylelint-config-standard-scss","stylelint-config-recommended-vue/scss"]}],rules: {"selector-class-pattern": null,"no-descending-specificity": null,"scss/dollar-variable-pattern": null,"selector-pseudo-class-no-unknown": [true,{ignorePseudoClasses: ["deep", "global"]}],"selector-pseudo-element-no-unknown": [true,{ignorePseudoElements: ["v-deep", "v-global", "v-slotted"]}],"at-rule-no-unknown": [true,{ignoreAtRules: ["tailwind","apply","variants","responsive","screen","function","if","each","include","mixin","use"]}],"rule-empty-line-before": ["always",{ignore: ["after-comment", "first-nested"]}],"unit-no-unknown": [true, { ignoreUnits: ["rpx"] }],"order/order": [["dollar-variables","custom-properties","at-rules","declarations",{type: "at-rule",name: "supports"},{type: "at-rule",name: "media"},"rules"],{ severity: "warning" }]},ignoreFiles: ["**/*.js", "**/*.ts", "**/*.jsx", "**/*.tsx"]
};
  • 测试是否引入成功

    <p class=" text-2xl font-bold">Hello Tailwind!</p>
    

    如果出现css样式,则表明引入成功

4. tailwindUI

聊天框样式涉及参考了这个大神的UI设计

网址:[comment聊天](comment | Cards (tailwindcomponents.com))

源代码

<div class="flex justify-center relative top-1/3"><!-- This is an example component -->
<div class="relative grid grid-cols-1 gap-4 p-4 mb-8 border rounded-lg bg-white shadow-lg"><div class="relative flex gap-4"><img src="https://icons.iconarchive.com/icons/diversity-avatars/avatars/256/charlie-chaplin-icon.png" class="relative rounded-lg -top-8 -mb-4 bg-white border h-20 w-20" alt="" loading="lazy"><div class="flex flex-col w-full"><div class="flex flex-row justify-between"><p class="relative text-xl whitespace-nowrap truncate overflow-hidden">COMMENTOR</p><a class="text-gray-500 text-xl" href="#"><i class="fa-solid fa-trash"></i></a></div><p class="text-gray-400 text-sm">20 April 2022, at 14:88 PM</p></div></div><p class="-mt-4 text-gray-500">Lorem ipsum dolor sit amet consectetur adipisicing elit. <br>Maxime quisquam vero adipisci beatae voluptas dolor ame.</p>
</div></div>

在这里插入图片描述

tip: 本项目编写时,对其代码进行一定程度的调整

5. 前端代码编写

笔者考虑篇幅问题,没有封装组件。读者可以尝试着将聊天代码封装为组件,一级评论一个样式,回复评论一个样式。通过这样的方式实现代码复用。

<script setup lang="ts">
import { CommunicateEntity, list, save } from "/src/api/communicate.ts";
import { ElMessage } from "element-plus";
import { ref, onMounted } from "vue";const input = ref("");const replyInput = ref("");const chatList = ref<Array<CommunicateEntity>>();const submit = (replyUserId?: Number, pid?: Number) => {const entity = new CommunicateEntity();entity.replyUserId = replyUserId;entity.content = input.value;entity.pid = pid;console.log(entity);save(entity).then(res => {if (res.code === 0) {ElMessage.success("提交成功");getData();} else {ElMessage.error("提交失败: " + res.msg);}});
};onMounted(() => {getData();
});const getData = () => {list().then(res => {console.log(res);chatList.value = res.data;});
};// 模拟获取用户信息(一般用户信息会在登陆时, 存储在浏览器本地)
const getUser = (userId: Number) => {return "测试人员";
};
</script><template><div style="border: 1px solid #ccc; margin-top: 10px"><el-input v-model="input" textarea style="height: 200px" /><el-button @click="submit()">提交</el-button><el-divider /><div v-for="item in chatList" :key="item.id"><!-- This is an example component --><div class="relative gap-4 p-6 rounded-lg mb-8 bg-white border"><div class="relative flex gap-4"><imgsrc="https://cube.elemecdn.com/e/fd/0fc7d20532fdaf769a25683617711png.png"class="relative rounded-lg -top-8 -mb-4 bg-white border h-20 w-20"alt=""loading="lazy"/><div class="flex flex-col w-full"><div class="flex flex-row justify-between"><pclass="relative text-xl whitespace-nowrap truncate overflow-hidden">{{ getUser(item.id) }}</p><a class="text-gray-500 text-xl" href="#"><i class="fa-solid fa-trash"/></a></div><p class="text-gray-400 text-sm">{{ item.createTime }}</p></div></div><p class="-mt-4 text-gray-500">{{ item.content }}</p><!-- 回复按钮 --><div><el-popover placement="bottom-start" trigger="click" :width="200"><template #reference><el-button style="float: right" link type="primary">回复</el-button></template><el-input v-model="input" /><el-button @click="submit(item.userId, item.id)" style="width: 100%">确定</el-button></el-popover></div></div><!-- 回复 --><div v-for="subItem in item.children" :key="subItem.id"><divclass="relative gap-4 p-6 rounded-lg mb-8 bg-white border"style="margin-left: 50px"><div class="relative flex gap-4"><imgsrc="https://cube.elemecdn.com/e/fd/0fc7d20532fdaf769a25683617711png.png"class="relative rounded-lg -top-8 -mb-4 bg-white border h-20 w-20"alt=""loading="lazy"/><div class="flex flex-col w-full"><div class="flex flex-row justify-between"><pclass="relative text-xl whitespace-nowrap truncate overflow-hidden">{{ getUser(subItem.userId) }} 回复<span style="color: cornflowerblue">@{{ getUser(subItem.replyUserId) }}</span></p><a class="text-gray-500 text-xl" href="#"><i class="fa-solid fa-trash"/></a></div><p class="text-gray-400 text-sm">{{ item.createTime }}</p></div></div><p class="-mt-4 text-gray-500">{{ subItem.content }}</p><!-- 回复按钮 --><div><el-popover placement="bottom-start" trigger="click" :width="200"><template #reference><el-button style="float: right" link type="primary">回复</el-button></template><el-input v-model="input" /><el-button@click="submit(item.userId, item.id)"style="width: 100%">确定</el-button></el-popover></div></div></div></div></div>
</template><style lang="scss" scoped></style>

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JVM知识总结

1.概述 JVM指的是Java虚拟机&#xff0c;本质上是一个运行在计算机上的程序&#xff0c;他的职责是运行Java字节码文件&#xff0c;作用是为了支持跨平台特性。 功能&#xff1a; 装载字节码&#xff0c;解释/编译为机器码 管理数据存储和垃圾回收 优化热点代码提升效率 …

Pytest应用PO设计模式

Pytest应用PO设计模式 本篇内容主要涉及在软件测试中实现PO设计模式的应用 包含PO思想、PO原则、PO使用方法&#xff0c;最后会写一个实际模板供大家参考。 一、PO思想 ​ PO(PageObject)&#xff0c;在UI页面测试时&#xff0c;通常会存在大量的页面元素和各种点击操作&#…

华为欧拉操作系统结合内网穿透实现固定公网地址SSH远程连接

文章目录 1. 本地SSH连接测试2. openEuler安装Cpolar3. 配置 SSH公网地址4. 公网远程SSH连接5. 固定连接SSH公网地址6. SSH固定地址连接测试 欧拉操作系统(openEuler, 简称“欧拉”)是面向数字基础设施的操作系统,支持服务器、云计算、边缘openEuler是面向数字基础设施的操作系…

【开源】基于JAVA的停车场收费系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 停车位模块2.2 车辆模块2.3 停车收费模块2.4 IC卡模块2.5 IC卡挂失模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 停车场表3.2.2 车辆表3.2.3 停车收费表3.2.4 IC 卡表3.2.5 IC 卡挂失表 四、系统实现五、核心代码…

【QT+QGIS跨平台编译】之二:【zlib+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、zlib介绍二、文件下载三、文件分析四、pro文件五、编译实践 一、zlib介绍 zlib是一套通用的解压缩开源库&#xff0c;提供了内存&#xff08;in-memory&#xff09;压缩和解压函数。zlib是一套通用的解压缩开源库&#xff0c;提供了内存&#xff08;in-memory&am…

#vue3 实现前端下载excel文件模板功能

一、需求&#xff1a; 前端无需通过后端接口&#xff0c;即可实现模板下载功能。 通过构造一个 JSON 对象&#xff0c;使用前端常用的第三方库 xlsx&#xff0c;可以直接将该 JSON 对象转换成 Excel 文件&#xff0c;让用户下载模板 二、效果&#xff1a; 三、源码如下&…

【Docker】部署和运行青龙面板:一个支持python3、javaScript、shell、typescript 的定时任务管理面板

引言 青龙面板是一个支持python3、javaScript、shell、typescript 的定时任务管理面板。 步骤 拉取镜像 从 Docker Hub 上拉取最新的 “qinglong” 镜像。 docker pull whyour/qinglong:latest启动容器 使用刚刚拉取的镜像来启动一个新的 Docker 容器。 docker run -dit \-v…

Linux中的软件包管理器yum

目录 1.什么是软件包 2.关于 rzsz 3.查看软件包 4.如何安装软件 5.如何卸载软件 1.什么是软件包 ● 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序. ● 但是这样太麻烦了, 于是有些人把一些常用的软件提前编译好, 做成软件包(可以理…

IN操作符

目录 IN NOT IN Oracle从入门到总裁:https://blog.csdn.net/weixin_67859959/article/details/135209645 IN IN 指的是根据一个指定的范围进行数据查询 1.查询出员工编号是 7369、7566、7788、9999 的员工信息 利用前面学的知识,得出: SQL> set linesize 250 SQL>…

寒假思维训练计划day11

每日一题&#xff0c;这两天有事&#xff0c;断更了一天&#xff0c;今天补上&#xff0c;感觉状态也不太好&#xff0c;来道1500的题压压惊。 宣传一下我总结的几个构造题模型&#xff0c;一点个人的浅薄见解&#xff1a; 1、前后缀贪心&#xff0c;比如说观察前后缀的sum&…

Spring Boot 整合 Camunda 实现工作流

工作流是我们开发企业应用几乎必备的一项功能&#xff0c;工作流引擎发展至今已经有非常多的产品。最近正好在接触Camunda&#xff0c;所以来做个简单的入门整合介绍。如果您也刚好在调研或者刚开始计划接入&#xff0c;希望本文对您有所帮助。如果您是一名Java开发或Spring框架…

CS8370错误,这是由于使用了C# 7.3中不支持的功能

目录 背景: 第一种方法: 第二种办法: 背景: 在敲代码的时候&#xff0c;程序提示报错消息提示:CS8370错误&#xff0c;那么这是什么原因导致的&#xff0c;这是由于使用了C# 7.3中不支持的功能&#xff0c;不支持该功能&#xff0c;那就是版本太低我们就需要升级更高的版本&…

Spring框架面试题

目录 1.Spring中bean的生命周期 2.Spring中bean的循环依赖 3.SpringMVC执行流程 4.Springboot自动装配原理 5.Spring框架常见注解(Spring、Springboot、SpringMVC) 6.mybatis执行流程 7.mybatis延迟加载使用及原理 8.mybatis一级、二级缓存 1.Spring中bean的生命周期 2.…

11- OpenCV:自定义线性滤波(卷积,卷积边缘)

目录 一、卷积 1、卷积概念 2、卷积如何工作 3、常见算子&#xff08;卷积核 Kenel&#xff09; 4、自定义卷积模糊 5、代码演示 二、卷积边缘 1、卷积边缘问题 2、处理边缘 3、相关的API说明 4、代码演示 一、卷积 1、卷积概念 &#xff08;1&#xff09;在OpenC…

[MySQL]基础的增删改查

目录 1.前置介绍 2.数据库操作 2.1显示当前数据库 2.2创建数据库 2.3 使用数据库 2.4 删除数据库 3.常用数据类型 3.1整型和浮点型 3.2字符串类型 4.表的操作 4.1查看表结构 4.2创建表 4.3删除表 5.重点 5.1操作数据库 5.2常用数据类型 5.3操作表 1.前置介绍 …

课题学习(十九)----Allan方差:陀螺仪噪声分析

一、介绍 Allan方差是一种分析时域数据序列的方法&#xff0c;用于测量振荡器的频率稳定性。该方法还可用于确定系统中作为平均时间函数的本征噪声。该方法易于计算和理解&#xff0c;是目前最流行的识别和量化惯性传感器数据中存在的不同噪声项的方法之一。该方法的结果与适用…

双指针算法专题

前言 双指针算法入门&#xff0c;干就完了 下面的题目都是来自灵神的基础算法精讲&#xff0c;有思路不清晰的地方&#xff0c;可以去看讲解。 灵茶山艾府的个人空间-灵茶山艾府个人主页-哔哩哔哩视频 (bilibili.com) 相向双指针 1.两数之和 题目链接&#xff1a;167. 两数之…

VSCode使用Makefile Tools插件开发C/C++程序

提起Makefile&#xff0c;可能有人会觉得它已经过时了&#xff0c;毕竟现在有比它更好的工具&#xff0c;比如CMake&#xff0c;XMake&#xff0c;Meson等等&#xff0c;但是在Linux下很多C/C源码都是直接或者间接使用Makefile文件来编译项目的&#xff0c;可以说Makefile是基石…

YARN节点故障的容错方案

YARN节点故障的容错方案 1. RM高可用1.1 选主和HA切换逻辑 2. NM高可用2.1 感知NM节点异常2.2 异常NM上的任务处理 4. 疑问和思考4,1 RM感知NM异常需要10min&#xff0c;对于app来说是否太长了&#xff1f; 5. 参考文档 本文主要探讨yarn集群的高可用容错方案和容错能力的探讨。…

查找局域网树莓派raspberry的mac地址和ip

依赖python库&#xff1a; pip install socket pip install scapy运行代码&#xff1a; import socket from scapy.layers.l2 import ARP, Ether, srpdef get_hostname(ip_address):try:return socket.gethostbyaddr(ip_address)[0]except socket.herror:# 未能解析主机名ret…