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

文章目录

    • 聊天模块
      • 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.mzph.cn/news/632618.shtml

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

相关文章

最新 生成pdf文字和表格

生成pdf文字和表格 先看效果 介绍 java项目&#xff0c;使用apache的pdfbox工具&#xff0c;可分页&#xff0c;自定义列 依赖 <dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.22<…

css 前端实现通过css动画实现进度条动态加载效果

效果图 代码 CommonProcess.vue 进度条动态加载组件代码 <!-- 进度条组件 --> <template><div class"common_process"><div v-for"(item, index) in dataList" :key"processType index" class"common_process_item…

SPI传感器接口设计与优化:基于STM32的实践

SPI&#xff08;串行外设接口&#xff09;是一种常用的串行通信协议&#xff0c;用于在微控制器和外部设备之间进行全双工的高速数据传输。在本文中&#xff0c;我们将探讨如何基于STM32微控制器设计和优化SPI传感器接口&#xff0c;并提供相应的代码示例。 1. SPI传感器接口设…

首届PolarDB开发者大会在京举办,阿里云李飞飞:云数据库加速迈向智能化

1月17日&#xff0c;阿里云PolarDB开发者大会在京举办&#xff0c;中国首款自研云原生数据库PolarDB发布“三层分离”新版本&#xff0c;基于智能决策实现查询性能10倍提升、节省50%成本。此外&#xff0c;阿里云全新推出数据库场景体验馆、训练营等系列新举措&#xff0c;广大…

【openwrt】【overlayfs】Openwrt系统overlayfs挂载流程

overlayfs是一种叠加文件系统&#xff0c;在openwrt和安卓系统中都有很广泛的应用&#xff0c;overlayfs通常用于将只读根文件系统(rootfs)和可写文件系统(jffs2)进行叠加后形成一个新的文件系统&#xff0c;这个新的文件系统“看起来”是可读写的&#xff0c;这种做法的好处是…

汽车美容行业研究:预计2029年将达到127亿美元

车体保养又习惯称汽车美容。主要目的是清除车体外和车体内的各种氧化和腐蚀&#xff0c;污染物等。然后加以保护&#xff0c;尽量突出车的“美”。它主要包括&#xff1a;车漆保养&#xff0c;内饰保养&#xff0c;电镀加工保养&#xff0c;皮革塑料保养&#xff0c;轮胎、轮毂…

UE C++打印文本的两种方式

UE C打印文本的两种方式 第一种方式UE_LOG打印打印出的内容在**输出日志**中显示 第二种方式GEngine->AddOnScreenDebugMessage打印&#xff08;常用&#xff09;打印出的内容在**控制台**中显示 第一种方式UE_LOG打印 官方文档&#xff1a; https://docs.unrealengine.com…

1、node.js安装

文章目录 node.js下载及安装node.js安装验证node执行js代码 node.js下载及安装 https://nodejs.org/en 访问官网&#xff0c;下载LTS版本 下载完成后&#xff0c;双击安装&#xff0c;安装过程基本不用动什么&#xff0c;包括盘符也尽量不要改。 node.js安装验证 cmd运行nod…

大师学SwiftUI第6章 - 声明式用户界面 Part 4

步进器视图 ​​Stepper​​视图创建一个带递增和递减按钮的控件。该结构体提供了多个初始化方法&#xff0c;包含不同的配置参数组合。以下是最常用的一部分。 Stepper(String, value: Binding, in: Range, step: Float, onEditingChanged: Closure)&#xff1a;此初始化方法…

Tuxera NTFS2024下载使用详细操作教程

你是否还在为Mac不能正常读写NTFS格式分区而感到苦恼呢&#xff1f;想要适合Mac系统使用来回转换磁盘格式又十分麻烦&#xff0c;这该怎么办呢&#xff0c;有了这款软件Tuxera ntfs就能马上帮你解决目前遇到的问题。 Tuxera NTFS2024最新免费版下载如下&#xff1a; https://…

如何给新华网投稿发稿?新华网的媒体发稿方法步骤

现如今&#xff0c;互联网已经成为了人们获取信息的主要途径&#xff0c;各大媒体网站也成为了发布自己作品的首选平台。其中&#xff0c;新华网作为中国最具影响力的新闻媒体之一&#xff0c;其内容覆盖面广、触及人群众多&#xff0c;因此&#xff0c;能够在新华网上发表文章…

Vue-26、Vue内置指令v-cloak与v-once以及v-pre

1、v-cloak 本质上是一个特殊属性&#xff0c;Vue实例创建完毕并接管容器后&#xff0c;会删掉v-cloak属性使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF…

ocker ps -a 要求只显示自己想要的信息

在使用 docker ps -a 要求只显示这下面这几个字段&#xff0c;不显示其他的内容&#xff0c;方便查看哪些容器在运行&#xff0c;那些已经挂起。 例子&#xff1a; 我只想想显示这几个字段&#xff1a; CONTAINER ID STATUS NAMES docker ps -a --format "table {{.ID}…

Pure-admin框架 Pure-table中获取所选中的内容的信息

最近在尝试使用Pure-admin框架来进行开发&#xff0c;正好遇到了多选表格需要获取选中项的id的情况&#xff0c;因为平台介绍说是二次封装 element-plus 的 Table &#xff0c;直接拿el-table的方法来试 在table上设置属性ref"multipleTableRef" let idArr [];mult…

数据结构实验6:图的应用

目录 一、实验目的 1. 邻接矩阵 2. 邻接矩阵表示图的结构定义 3. 图的初始化 4. 边的添加 5. 边的删除 6. Dijkstra算法 三、实验内容 实验内容 代码 截图 分析 一、实验目的 1&#xff0e;掌握图的邻接矩阵的存储定义&#xff1b; 2&#xff0e;掌握图的最短路径…

华为网络设备文件传输FTP配置

R2配置 ftp server enable aaa local-user ftp-client password cipher Huawei123local-user ftp-client privilege level 15local-user ftp-client ftp-directory flash:/local-user ftp-client service-type ftpinterface GigabitEthernet0/0/0ip address 10.0.12.2 255.255.…

vue3 知识

vue3介绍 Vue3的变化: 1、vue3完全兼容vue2&#xff0c;但是vue3不建议用vue2的写法 2、拥抱TypeScript&#xff0c;ts完全兼容js 3、组合式API和配置项API vue2 是配置项api vue3 组合式api vue3项目创建和启动 # 创建vue3项目&a…

TDengine 企业级功能:存储引擎对多表低频场景优化工作分享

在去年 8 月份发布的 3.1.0.0 版本中&#xff0c;TDengine 进行了一系列重要的企业级功能更新&#xff0c;其中包括对多表低频场景写入性能的大幅优化。这一优化工作为有此需求的用户提供了更大的便捷性和易用性。在本文中&#xff0c;TDengine 的资深研发将对此次优化工作进行…

鸿蒙开发之状态管理

State 组件内状态 State装饰的变量&#xff0c;会和自定义组件的渲染绑定起来。当状态改变时&#xff0c;UI会发生对应的渲染改变。在状态变量相关装饰器中&#xff0c;State是最基础的&#xff0c;使变量拥有状态属性的装饰器&#xff0c;它也是大部分状态变量的数据源。 装…

wiadefui.dll文件丢失导致程序无法运行,怎么办?文件下载

很多用户在启动游戏或扫描仪的时候&#xff0c;电脑会报错提示“wiadefui.dll文件找到不到&#xff0c;程序无法启动”&#xff0c;这是怎么回事呢&#xff1f; 首先&#xff0c;我们先来了解wiadefui.dll文件是什么&#xff1f;有什么作用&#xff1f; wiadefui.dll是Windows…