基于 springboot+vue 进行多条件查询历史聊天记录

news/2024/10/18 2:27:38/

查询历史聊天记录

多条件查询记录是经常需要进行编写的功能,这里就以多条件查询历史聊天记录为例子来介绍如何进行基于关键字、日期、聊天记录类型 三种条件进行模糊查询、日期拼接、条件拼接查询。

在这里插入图片描述

前端

抽屉管理

首先我们控制 element ui 的 抽屉的打开与关闭

这里可能会出现父子组件传值的错误,详情请看这篇文章

https://blog.csdn.net/qq_45803593/article/details/125546395

由于 element ui 的 el-date-picker 组件也存在父子组件关系,所以这个还会报错,但是不影响使用了

日期查询配置

日期查询我们就使用 element ui 的 date-picker 组件

  • format:选中日期后显示在页面时的格式
  • value-format: 选中日期后传值时的格式
  • picker-options:这里我们绑定一个选中日期不可超过今天的规定
  • change:在我们更换日期后,检测到立刻重新请求聊天记录
<el-date-pickerv-model="searchDate"type="date"placeholder="选择日期"size="mini"format="yyyy 年 MM 月 dd 日"value-format="yyyy-MM-dd"@change="searchDateChange":picker-options="pickerOptions"/>

必须晚于今天

      pickerOptions: { // 必须晚于今天disabledDate (time) {return time.getTime() > Date.now()}},

首次加载数据

首次加载时,我们首先将起始的页码设置为了 1,首次应该加载第 0 页,所以我们直接将 pageIndex 进行 -1 操作,此时的分页组件上就会显示第一页的高亮了,而且访问到的数据也是真正的第一页

pageIndex: this.pageIndex - 1,

每次切换加载的记录

当切换的时候组件没有进行销毁,所以历史记录所在的抽屉的数据不会变化,因此我们每次加载时切换会话,每个会话都有唯一的 roomId,我们将其作为 key 进行属性的绑定,此时组件就会重新生成了

虚拟DOM中key的作用

  • key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】

  • 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下

对比规则:

  • 就虚拟DOM中找到了与新虚拟DOM相同的key

  • 若虚拟DOM中内容没变,直接使用之前的真实DOM!

  • 若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM

  • 就虚拟DOM中未找到与新虚拟DOM相同的key创建新的真实DOM,随后渲染到页面。

      <history-message@changeHistoryDrawerVisible="changeHistoryDrawerVisible":historyDrawer="historyDrawer":roomId="currentConversation.roomId":key="currentConversation.roomId":conversationType="currentConversation.type"></history-message>

前端全部代码

MyMain.vue

<template><div class="chat-area"><div class="main"><div class="message-list-container"><!-- 聊天列表 --><message-list ref='messagelist'@load-message="loadmessage":messagelist="messages":hasmore="hasMore":scrollbottom="scrollBottom"></message-list></div></div><div class="message-edit-container"><div class="tool"><span class="tool-item"><i class="item iconfont icon-emoji" @click.stop="showEmojiCom = !showEmojiCom"></i></span><span class="tool-item"><label for="upImg"><i class="item el-icon-picture"></i></label></span><span class="tool-item"><i class="item el-icon-folder"/></span><span class="tool-item"><i class="item iconfont icon-huaban"/></span><span class="tool-item"><i class="item iconfont icon-shipin"/></span><span class="tool-item"><i class="item el-icon-phone-outline"/></span><span class="tool-item" ><i class="item el-icon-caret-bottom" @click="changeHistoryDrawerVisible(true)">历史记录</i></span></div><div class="operation"><el-button @click="send" type="success" size="small" plain>发送</el-button></div><textarea ref="chatInp" class="textarea" v-model="messageText" maxlength="200" @input="scrollBottom = true"@keydown.enter="send($event)"></textarea><custom-emoji v-if="showEmojiCom" class="emoji-component" @addemoji="addEmoji"/><history-message@changeHistoryDrawerVisible="changeHistoryDrawerVisible":historyDrawer="historyDrawer":roomId="currentConversation.roomId":key="currentConversation.roomId":conversationType="currentConversation.type"></history-message></div></div>
</template>
<script>
import './../../../static/iconfont/iconfont.css'
import customEmoji from '@/components/customEmoji'
import historyMessage from '@/views/chat/HistoryMessage'
import messageList from '../chat/MessageList'
import {conversationTypes} from '@/const'
import singleMessageApi from '@/api/modules/friend'
import groupMessageApi from '@/api/modules/group'
import {fromatTime} from '@/utils'
export default {name: 'MyMain',components: {customEmoji,historyMessage,messageList},props: ['currentConversation'],data () {return {messageText: '', // 当前编辑消息内容messages: [], // 消息列表showEmojiCom: false, // 表情栏显示historyDrawer: false, // 历史记录显示pageIndex: 1, // 消息页数pageSize: 10, // 一次加载消息量hasMore: true, // 更多消息scrollBottom: true // 滚到底部}},computed: {userInfo () {return this.$store.state.user.userInfo}},watch: {currentConversation (newVal, oldVal) {if (newVal && newVal.id) {this.chatInpAutoFocus()this.pageIndex = 1this.messageText = ''this.scrollBottom = truethis.messages = []this.hasMore = truethis.getRecentMessages()}},deep: true,immediate: true},created () {this.getRecentMessages()document.addEventListener('click', this.handlerShowEmoji)},beforeDestroy () {// console.log('chatArea BeforeDestroy')document.removeEventListener('click', this.handlerShowEmoji)},sockets: {receiveMessage (news) {// 收到消息if (news.roomId === this.currentConversation.roomId) {// 是自己正所处的房间就添加消息并更新会话列表this.messages = [...this.messages, news]// 进来设置该房间未读消息数为0setTimeout(() => {this.$store.dispatch('conversation/SET_UNREAD_NUM', {type: 'clear', data: news})}, 0)}}},methods: {// 控制历史记录抽屉changeHistoryDrawerVisible (val) {this.historyDrawer = val},// 控制表情面板handlerShowEmoji () {this.showEmojiCom = false// this.showUpFileCom = false},// 加载更多消息loadmessage () {if (this.hasMore) {this.scrollBottom = falseif (this.hasMore) {this.getRecentMessages()}}},// 添加表情addEmoji (emoji = '') {this.messageText += emoji},// 发送消息send (e) {e.preventDefault()if (!this.messageText) {return}const common = this.generatorMessageCommon()const newMessage = {...common,message: this.messageText,messageType: 0}let selfMessage = {...newMessage,createTime: fromatTime(new Date())}this.messages = [...this.messages, selfMessage]this.$socket.emit('sendNewMessage', newMessage)this.messageText = ''this.showEmojiCom = false// 发完消息更新自己会话列表的最新消息if (selfMessage.conversationType === conversationTypes.group) selfMessage.message = selfMessage.senderNickname + ':' + selfMessage.messagethis.$store.dispatch('conversation/SET_UNREAD_NUM', {type: 'clear', data: selfMessage})},// 生成发送消息部分字段generatorMessageCommon () {return {roomId: this.currentConversation.roomId,senderId: this.userInfo.id,senderName: this.userInfo.username,senderNickname: this.userInfo.nickname,senderAvatar: this.userInfo.avatar,conversationType: this.currentConversation.type}},// 进入聊天信息页面,清除消息未读getRecentMessages () {const {roomId, type} = this.currentConversationconst params = {roomId,pageIndex: this.pageIndex,pageSize: this.pageSize}// 私聊信息if (type === conversationTypes.single) {singleMessageApi.getRecentSingleMessage(params).then(res => {if (res.code === 2000) {// reverse() 会改变原数组,并且当前作用域的对象都会改变res.data.recentMessage.reverse()this.messages = [...res.data.recentMessage, ...this.messages]if (res.data.recentMessage.length < this.pageSize) {this.hasMore = falsethis.pageIndex = 2return}this.pageIndex++}})} else if (type === conversationTypes.group) {groupMessageApi.getRecentGroupMessage(params).then(res => {if (res.code === 2000) {// reverse() 会改变原数组,并且当前作用域的对象都会改变res.data.recentMessage.reverse()this.messages = [...res.data.recentMessage, ...this.messages]if (res.data.recentMessage.length < this.pageSize) {this.hasMore = falsethis.pageIndex = 2return}this.pageIndex++}})console.log('获取群聊信息')}this.chatInpAutoFocus()},/** 聊天内容输入框自动聚焦 */chatInpAutoFocus () {this.$nextTick(() => {this.$refs.chatInp.focus()})}}
}
</script>
<style scoped>
.chat-area {position: relative;height: 100%;
}
.main {display: flex;position: relative;height: calc(100% - 160px);width: 100%;
}
.main .message-list-container {position: relative;height: 100%;width: 75%;flex: 1;
}
.message-edit-container {box-sizing: border-box;position: relative;height: 150px;border-top: 1px solid #cccccc;
}
.message-edit-container .tool {width: 100%;height: 28px;line-height: 28px;text-align: left;background-color: rgba(233, 235, 238, 0.5);padding: 0 10px;box-sizing: border-box;
}
.message-edit-container .tool .tool-item {cursor: pointer;display: inline-block;height: 100%;position: relative;}
.message-edit-container .tool .tool-item i {padding: 0 5px;
}
.message-edit-container .tool .tool-item .emoji-container {width: 400px;height: 260px;position: absolute;bottom: 30px;left: 0;z-index: 10;transition: all 0.2s;
}.message-edit-container .tool .tool-item input {position: absolute;left: 0;top: 0;width: 100%;height: 100%;opacity: 0;
}
.message-edit-container .tool .tool-item:hover {background-color: rgba(255, 255, 255, 0.3);
}.message-edit-container .tool .tool-item.active {background-color: rgba(255, 255, 255, 0.3);
}.message-edit-container .tool .tool-item.active .emoji-container {transform: scaleX(1);opacity: 1;
}
.message-edit-container .tool i {margin: 0;
}
.message-edit-container .operation {position: absolute;bottom: 10px;right: 20px;
}
.message-edit-container .textarea {overflow-x: hidden;box-sizing: border-box;height: calc(100% - 30px);width: 100%;outline: none;border: none;padding: 0 10px;border: 0;border-radius: 5px;background-color: #e9ebee;padding: 10px;resize: none;
}
.message-edit-container .emoji-component {position: absolute;bottom: 101%;
}
</style>

HistoryMessage.vue

<template><div><el-drawertitle="历史记录":visible.sync="historyDrawerVisible"v-if="historyDrawerVisible"><div class="history-msg-cmp" v-loading="isLoading"><div class="search"><el-inputplaceholder="请输入搜索内容"prefix-icon="el-icon-search"size="mini"v-model="searchWord"@keydown.native="searchWordChange"/></div><div class="type"><el-radio-group v-model="searchType" size="mini" @change="searchTypeChange"><el-radio-button value="all" label="全部"></el-radio-button><el-radio-button value="img" label="图片"></el-radio-button><el-radio-button value="file" label="文件"></el-radio-button></el-radio-group><el-date-pickerv-model="searchDate"type="date"placeholder="选择日期"size="mini"format="yyyy 年 MM 月 dd 日"value-format="yyyy-MM-dd"@change="searchDateChange":picker-options="pickerOptions"/></div><div class="history-msg__body"><div class="msg-list-container" v-if="historyMessageList.length"><div class="msg-item" v-for="item in historyMessageList" :key="item.id"><historyMessageItem :msg-item="item"/></div><div class="history-msg__footer"><el-paginationbackgroundhide-on-single-page@current-change="handleCurrentChange":current-page="pageIndex":page-size="pageSize":total="total"layout="prev, pager, next"/></div></div><div class="no-data" v-else><p class="text">没有数据~</p><empty width="150" heigth="150"></empty></div></div></div></el-drawer></div>
</template>
<script>
import {conversationTypes} from '@/const'
import groupApi from '@/api/modules/group'
import friendApi from '@/api/modules/friend'
const typeTextToValue = {'全部': 'all','图片': 'img','文件': 'file'
}
export default {name: 'HistoryMessage',components: {empty: () => import('@/SVGComponents/empty'),historyMessageItem: () => import('@/views/chat/HistoryMessageItem')},props: {historyDrawer: {type: Boolean,default: false},roomId: {type: String},conversationType: {type: Number}},mounted () {this.getHistoryMsg()},data () {return {historyMessageList: [],searchType: '全部', // 消息类型searchWord: '', // 搜索关键字searchDate: '', // 搜索日期pageIndex: 1, // 页码pageSize: 10, // 页大小total: 0, // 总记录pickerOptions: { // 必须晚于今天disabledDate (time) {return time.getTime() > Date.now()}},isLoading: false}},computed: {historyDrawerVisible: {get () {return this.historyDrawer},set (val) {this.$emit('changeHistoryDrawerVisible', val)}}},methods: {// 关闭重置handleReset () {this.historyMessageList = []this.searchType = '全部'this.searchWord = ''this.searchDate = ''this.pageIndex = 1this.pageSize = 10this.total = 0this.isLoading = false},// 切换页码handleCurrentChange (currentPage) {this.pageIndex = currentPagethis.getHistoryMsg()},// 更换搜索关键字searchWordChange () {this.pageIndex = 1this.getHistoryMsg()},// 更换搜索消息记录类型searchTypeChange () {this.pageIndex = 1this.getHistoryMsg()},// 更换搜索日期searchDateChange () {this.pageIndex = 1this.getHistoryMsg()},// 得到历史记录getHistoryMsg () {if (this.isLoading) returnthis.isLoading = trueconst params = {roomId: this.roomId,type: typeTextToValue[this.searchType],keyword: this.searchWord,date: this.searchDate,pageIndex: this.pageIndex - 1,pageSize: this.pageSize}console.log('历史记录params:', params)let fetch = this.conversationType === conversationTypes.group ? groupApi : friendApifetch.getHistoryMessages(params).then(res => {// console.log('历史消息:', res)this.historyMessageList = res.data.historyMessageList.recordsthis.total = res.data.historyMessageList.totalthis.isLoading = false})}}
}
</script>
<style>
.history-msg-cmp {display: flex;flex-direction: column;padding: 10px 10px 5px;align-items: center;height: 100%;
}
.history-msg-cmp .search {width: 89%;margin-bottom: 10px;
}
.history-msg__body {margin: 0 25px;flex: 1;overflow-x: hidden;
}
.msg-list-container {margin-top: 10px;
}
.msg-list-container .msg-item {border-top: 1px solid #ededed;
}
.history-msg__footer {text-align: center;
}
.no-data {position: relative;top: 50%;left: 50%;transform: translate(-50%, -50%)
}
.no-data .text {color: #909399;font-size: 20px;text-align: center;
}
</style>

HistoryMessageItem

<template><div class="historyMsgItem"><div class="historyMsgItem-avatar"><el-avatar shape="square" :size="40" :src="msgItem.senderAvatar"></el-avatar></div><div class="historyMsgItem-content"><div class="historyMsgItem-header"><span class="secondary-font">{{ msgItem.senderNickname }}</span><span class="secondary-font">{{ msgItem.createTime | fromatTime }}</span></div><div class="historyMsgItem-text"><span>{{ msgItem.message }}</span></div></div></div>
</template>
<script>
import {fromatTime} from '@/utils/index'
export default {name: 'HistoryMessageItem',props: [ 'msgItem' ],filters: {fromatTime (val) {return fromatTime(val)}}
}
</script>
<style>
.historyMsgItem {display: flex;flex-direction: row;justify-content: flex-start;padding: 10px 0;
}
.historyMsgItem-avatar {margin-right: 10px;
}
.historyMsgItem-content {display: flex;flex-direction: column;
}
.historyMsgItem-header {display: flex;flex-direction: row;justify-content: space-between;width: 320px;margin-bottom: 4px;
}
</style>

后端

分页信息

首先我们将页码与页面大小取出,这里我们将起始页码进行 + 1 操作,是因为 element ui 的分页组件默认第一页为 0,而我们的 mybatis-plus 默认的第一页为 1,所以我们需要为其进行调整

Page<GroupMessage> page = new Page<>(historyMsgRequestVo.getPageIndex() + 1, historyMsgRequestVo.getPageSize());

查询指定日期记录

由于我们使用 mybatis-plus,所以在查询时我们直接按照官方提供的函数来进行 sql 语句的拼接

apply

apply(String applySql, Object... params)
apply(boolean condition, String applySql, Object... params)
  • 拼接 sql

注意事项:

该方法可用于数据库函数 动态入参的params对应前面applySql内部的{index}部分.这样是不会有sql注入风险的,反之会有!

  • 例: apply("id = 1")—>id = 1

  • 例: apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")—>date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")

  • 例: apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2008-08-08")—>date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")

        if(StringUtils.isNotEmpty(historyMsgRequestVo.getDate())){wrapper.apply("date_format(create_time,'%Y-%m-%d') = '" + historyMsgRequestVo.getDate() + "'");}

模糊查询

最后就是我们要进行模糊查询了,我们的思路是

  • 如果为全部聊天记录
    • 如果关键字不为空,就对聊天文本内容 message 和聊天文件原名 file_original_name 进行查询
    • 注意我们使用了 .or() 来进行查询
  • 如果不为全部内容
    • 判断是否为文件或者图片类型,然后我们对其原文件名进行模糊查询
        // 如果为文件或者图片if(!historyMsgRequestVo.getType().equals(ConstValueEnum.MESSAGE_TYPE_ALL)){wrapper.eq(historyMsgRequestVo.getType().equals(ConstValueEnum.MESSAGE_TYPE_IMG), "message_type", 2).eq(historyMsgRequestVo.getType().equals(ConstValueEnum.MESSAGE_TYPE_FILE), "message_type", 3).like(StringUtils.isNotEmpty(historyMsgRequestVo.getKeyword()), "file_original_name", historyMsgRequestVo.getKeyword());}else {wrapper.like(StringUtils.isNotEmpty(historyMsgRequestVo.getKeyword()), "message", historyMsgRequestVo.getKeyword()).or().like(StringUtils.isNotEmpty(historyMsgRequestVo.getKeyword()), "file_original_name", historyMsgRequestVo.getKeyword());}

后端全部代码

    /*** 获取分页历史聊天记录* @param historyMsgRequestVo 包括模糊关键字* @return*/@Overridepublic IPage<GroupMessage> getGroupHistoryMessages(HistoryMsgRequestVo historyMsgRequestVo) {Page<GroupMessage> page = new Page<>(historyMsgRequestVo.getPageIndex() + 1, historyMsgRequestVo.getPageSize());QueryWrapper<GroupMessage> wrapper = new QueryWrapper<>();wrapper.eq("room_id", historyMsgRequestVo.getRoomId());// 日期条件存在if(StringUtils.isNotEmpty(historyMsgRequestVo.getDate())){wrapper.apply("date_format(create_time,'%Y-%m-%d') = '" + historyMsgRequestVo.getDate() + "'");}// 如果为文件或者图片if(!historyMsgRequestVo.getType().equals(ConstValueEnum.MESSAGE_TYPE_ALL)){wrapper.eq(historyMsgRequestVo.getType().equals(ConstValueEnum.MESSAGE_TYPE_IMG), "message_type", 2).eq(historyMsgRequestVo.getType().equals(ConstValueEnum.MESSAGE_TYPE_FILE), "message_type", 3).like(StringUtils.isNotEmpty(historyMsgRequestVo.getKeyword()), "file_original_name", historyMsgRequestVo.getKeyword());}else {wrapper.like(StringUtils.isNotEmpty(historyMsgRequestVo.getKeyword()), "message", historyMsgRequestVo.getKeyword()).or().like(StringUtils.isNotEmpty(historyMsgRequestVo.getKeyword()), "file_original_name", historyMsgRequestVo.getKeyword());}Page<GroupMessage> groupMessagePage = baseMapper.selectPage(page, wrapper);return groupMessagePage;}

http://www.ppmy.cn/news/581959.html

相关文章

Vue3版本生命周期详解

介绍 vue3和vue2的生命周期改动不大,下面以图来展现两个版本的周期钩子 使用示例 配置项写法 vue3可以使用vue2版本的周期配置 准备一个HelloWord组件 使用App组件嵌套HelloWorld组件,并进行v-if判断是否卸载该组件,以此查看vue3的卸载钩子 测试: 可以看到当页面刷新后执行…

python qq群发消息_Python版QQ群发消息

PyQQ已经发布&#xff0c;利用web2qq协议&#xff0c;目前支持批量添加好友&#xff0c;批量群发消息等功能。 我的初衷是以PyQt为GUI做一个跨平台的QQ机器人&#xff0c;在win平台下不需要安装Python环境也能运行&#xff0c;能根据收到的消息自动回复。目前已经测试通过的只有…

JAVA 仿QQ聊天程序(附源码)

个人推荐: &#x1f4e2;&#x1f4e2;&#x1f4e2; 前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下 "通俗易懂&#xff0c;风趣幽默"&#xff0c;感觉非常有意思,忍不住分享一下给大家。点击跳转到教程。 前言: 第一次通过java编写完了一个模拟QQ的C/S…

java qq通信,Java通信-仿QQ聊天项目

前后历时一个多月的Java实现聊天通信项目-仿QQ聊天室基本告一段落,期间面对了很多问题,也有不同的解决方案,重写了几次核心代码,等等问题。现在在项目的结束之时,给自己做一个总结,算是一个回顾,算是一次提升,也是一次新的开始。 Github链接 https://github.com/SocraH…

QQ聊天室

这是整个QQ聊天室的思维导图&#xff0c;由QQ聊天室进一步发展到登录&#xff0c;这个登录&#xff0c;用到密码加密&#xff08;*&#xff09;。 然后就是功能分布。 这就是密码加密的部份代码&#xff0c;在这里&#xff0c;我用的是多文件编译 以下截图是功能图&#xff0c;…

用Python把QQ聊天记录文件转成WORD并排版

和女票在一起五年了&#xff0c;保留了几年的QQ聊天记录&#xff0c;偶然翻到&#xff0c;感觉很温暖&#xff0c;就想把这些文字做成一本属于我们的书&#xff0c;应该会很有纪念意义。然而qq备份的聊天记录是txt格式&#xff0c;网上找了半天也没有合适的排版工具&#xff0c…

Python-QQ聊天记录分析-jieba+wordcloud

QQ聊天记录简单分析 0. Description 从QQ导出了和好友从2016-08-25到2017-11-18的消息记录&#xff0c;85874行&#xff0c;也算不少。于是就有了大致分析、可视化一下。步骤大致如下&#xff1a; 消息记录文件预处理使用jieba分词使用wordcloud生成词云生成简单图表 结果大致…

Nonebot QQ机器人插件九:qq群聊天记录词云图

QQ机器人插件九&#xff1a;qq群聊天记录词云图 1. 将实时聊天记录保存到数据库 安装数据库可自行到网上查找&#xff0c;本文仅使用mysql数据库为例。linux下mysql数据库安装可参考(59条消息) Linux安装mysql 5.7_qq_43784519的博客-CSDN博客或者(59条消息) Linux下载安装my…