From 089bf5d2378b3c4a61d795b2a92bede2c193b771 Mon Sep 17 00:00:00 2001
From: admin <344137771@qq.com>
Date: Tue, 06 Jan 2026 11:22:58 +0800
Subject: [PATCH] 1
---
src/components/chat/TalkSearchRecord.vue | 622 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 622 insertions(+), 0 deletions(-)
diff --git a/src/components/chat/TalkSearchRecord.vue b/src/components/chat/TalkSearchRecord.vue
new file mode 100644
index 0000000..76ad8b3
--- /dev/null
+++ b/src/components/chat/TalkSearchRecord.vue
@@ -0,0 +1,622 @@
+<template>
+ <div class="lum-dialog-mask">
+ <el-container class="lum-dialog-box" :class="{ 'full-screen': fullscreen }">
+ <el-header height="60px" class="header">
+ <p>消息管理器</p>
+ <p class="title">
+ <span>{{ query.talk_type == 1 ? '好友' : '群' }}【{{ title }}】</span>
+ </p>
+ <p class="tools">
+ <i
+ class="iconfont"
+ style="transform: scale(0.85)"
+ :class="fullscreen ? 'icon-tuichuquanping' : 'icon-quanping'"
+ @click="fullscreen = !fullscreen"
+ />
+ <i class="el-icon-close" @click="$emit('close')" />
+ </p>
+ </el-header>
+
+ <el-header height="38px" class="sub-header">
+ <i
+ class="iconfont pointer"
+ :class="{ 'icon-shouqi2': broadside, 'icon-zhankai': !broadside }"
+ @click="triggerBroadside"
+ />
+ <div class="search-box no-select">
+ <i class="el-icon-search" />
+ <input
+ v-model="search.keyword"
+ type="text"
+ maxlength="30"
+ placeholder="搜索"
+ @keyup.enter="searchText($event)"
+ />
+ </div>
+ </el-header>
+
+ <el-container class="full-height ov-hidden">
+ <el-aside width="200px" class="broadside" v-show="broadside">
+ <el-container class="full-height">
+ <el-header height="40px" class="aside-header">
+ <div
+ class="item"
+ :class="{ selected: contacts.show == 'friends' }"
+ @click="contacts.show = 'friends'"
+ >
+ 我的好友({{ contacts.friends.length }})
+ </div>
+ <div class="item-shuxian">|</div>
+ <div
+ class="item"
+ :class="{ selected: contacts.show == 'groups' }"
+ @click="contacts.show = 'groups'"
+ >
+ 我的群组({{ contacts.groups.length }})
+ </div>
+ </el-header>
+ <el-main class="no-padding">
+ <el-scrollbar class="full-height" tag="section" :native="false">
+ <div
+ v-for="item in contacts[contacts.show]"
+ class="contacts-item pointer"
+ :class="{
+ selected:
+ query.talk_type == item.type &&
+ query.receiver_id == item.id,
+ }"
+ :key="item.id"
+ @click="triggerMenuItem(item)"
+ >
+ <div class="avatar">
+ <el-avatar :size="20" :src="item.avatar" shape="square">
+ <img src="~@/assets/image/detault-avatar.jpg" />
+ </el-avatar>
+ </div>
+ <div class="content" v-text="item.name"></div>
+ </div>
+ </el-scrollbar>
+ </el-main>
+ </el-container>
+ </el-aside>
+
+ <!-- 聊天记录阅览 -->
+ <el-main v-show="showBox == 0" class="no-padding">
+ <el-container class="full-height">
+ <el-header height="40px" class="type-items">
+ <span
+ v-for="tab in tabType"
+ :class="{ active: query.msg_type == tab.type }"
+ @click="triggerLoadType(tab.type)"
+ >{{ tab.name }}
+ </span>
+ </el-header>
+
+ <el-main
+ v-if="records.isEmpty"
+ class="history-record animated fadeIn"
+ >
+ <div class="empty-records">
+ <img src="~@/assets/image/chat-search-no-message.png" />
+ <p>暂无聊天记录</p>
+ </div>
+ </el-main>
+
+ <el-main v-else class="history-record">
+ <el-scrollbar class="full-height" tag="section" :native="false">
+ <div
+ v-for="record in records.items"
+ :key="record.id"
+ class="message-group"
+ >
+ <div class="left-box">
+ <el-avatar
+ shape="square"
+ fit="contain"
+ :size="30"
+ :src="record.avatar"
+ />
+ </div>
+
+ <div class="right-box">
+ <div class="msg-header">
+ <span class="name">
+ {{
+ record.nickname_remarks
+ ? record.nickname_remarks
+ : record.nickname
+ }}
+ </span>
+ <el-divider direction="vertical" />
+ <span class="time">{{ record.created_at }}</span>
+ </div>
+
+ <!-- 文本消息 -->
+ <text-message
+ v-if="record.msg_type == 1"
+ :content="record.content"
+ />
+
+ <!-- 文件 - 图片消息 -->
+ <image-message
+ v-else-if="
+ record.msg_type == 2 && record.file.type == 1
+ "
+ :src="record.file.url"
+ />
+
+ <!-- 文件 - 音频消息 -->
+ <audio-message
+ v-else-if="
+ record.msg_type == 2 && record.file.type == 2
+ "
+ :src="record.file.url"
+ />
+
+ <!-- 文件 - 视频消息 -->
+ <video-message
+ v-else-if="
+ record.msg_type == 2 && record.file.type == 3
+ "
+ :src="record.file.url"
+ />
+
+ <!-- 文件 - 其它格式文件 -->
+ <file-message
+ v-else-if="
+ record.msg_type == 2 && record.file.type == 4
+ "
+ :file="record.file"
+ :record_id="record.id"
+ />
+
+ <!-- 会话记录消息 -->
+ <forward-message
+ v-else-if="record.msg_type == 3"
+ :forward="record.forward"
+ :record_id="record.id"
+ />
+
+ <!-- 代码块消息 -->
+ <code-message
+ v-else-if="record.msg_type == 4"
+ :code="record.code_block.code"
+ :lang="record.code_block.lang"
+ />
+
+ <!-- 投票消息 -->
+ <vote-message
+ v-else-if="record.msg_type == 5"
+ :record_id="record.id"
+ :vote="record.vote"
+ />
+
+ <div v-else class="other-message">未知消息类型</div>
+ </div>
+ </div>
+
+ <!-- 数据加载栏 -->
+ <div v-show="records.loadStatus == 1" class="load-button blue">
+ <i class="el-icon-loading" />
+ <span>加载数据中...</span>
+ </div>
+ <div v-show="records.loadStatus == 0" class="load-button">
+ <i class="el-icon-arrow-down" />
+ <span @click="loadChatRecord">加载更多...</span>
+ </div>
+ </el-scrollbar>
+ </el-main>
+ </el-container>
+ </el-main>
+ </el-container>
+ </el-container>
+ </div>
+</template>
+<script>
+import { ServeGetContacts } from '@/api/contacts'
+import { ServeGetGroups } from '@/api/group'
+import { ServeFindTalkRecords } from '@/api/chat'
+import { formateSize as renderSize, download, imgZoom } from '@/utils/functions'
+
+export default {
+ name: 'TalkSearchRecord',
+ props: {
+ params: {
+ type: Object,
+ default: () => {
+ return {
+ talk_type: 0,
+ receiver_id: 0,
+ title: '',
+ }
+ },
+ },
+ },
+ data() {
+ return {
+ fullscreen: false,
+
+ user_id: this.$store.state.user.uid,
+ title: '',
+
+ // 侧边栏相关信息
+ broadside: false,
+ contacts: {
+ show: 'friends',
+ friends: [],
+ groups: [],
+ },
+
+ query: {
+ talk_type: 0,
+ receiver_id: 0,
+ msg_type: 0,
+ },
+
+ // 用户聊天记录
+ records: {
+ record_id: 0,
+ items: [],
+ isEmpty: false,
+ loadStatus: 0,
+ },
+
+ showBox: 0,
+
+ tabType: [
+ { name: '全部', type: 0 },
+ { name: '图片/视频/文件', type: 2 },
+ { name: '会话记录', type: 3 },
+ { name: '代码块', type: 4 },
+ { name: '群投票', type: 5 },
+ ],
+
+ search: {
+ keyword: '', // 关键字查询
+ date: '', // 时间查询
+ page: 1, // 当前分页
+ totalPage: 50, // 总分页
+ items: [], // 数据列表
+ isShowDate: false,
+ },
+ }
+ },
+ mounted() {
+ this.title = this.params.title
+ this.query = {
+ talk_type: this.params.talk_type,
+ receiver_id: this.params.receiver_id,
+ msg_type: 0,
+ }
+
+ this.loadChatRecord(0)
+ },
+ created() {
+ this.loadFriends()
+ this.loadGroups()
+ },
+ methods: {
+ download,
+ renderSize,
+
+ // 获取图片信息
+ getImgStyle(url) {
+ return imgZoom(url, 200)
+ },
+
+ // 获取会话记录消息名称
+ getForwardTitle(item) {
+ let arr = [...new Set(item.map(v => v.nickname))]
+ return arr.join('、') + '的会话记录'
+ },
+
+ // 获取好友列表
+ loadFriends() {
+ ServeGetContacts().then(({ code, data }) => {
+ if (code == 200) {
+ this.contacts.friends = data.map(item => {
+ return {
+ id: item.id,
+ type: 1,
+ avatar: item.avatar,
+ name: item.friend_remark ? item.friend_remark : item.nickname,
+ }
+ })
+ }
+ })
+ },
+
+ // 获取群聊列表
+ loadGroups() {
+ ServeGetGroups().then(({ code, data }) => {
+ if (code == 200) {
+ this.contacts.groups = data.rows.map(item => {
+ return {
+ id: item.id,
+ type: 2,
+ name: item.group_name,
+ avatar: item.avatar,
+ }
+ })
+ }
+ })
+ },
+
+ // 左侧联系人菜单点击事件
+ triggerMenuItem(item) {
+ this.title = item.name
+ this.query.talk_type = item.type
+ this.query.receiver_id = item.id
+ this.showBox = 0
+
+ this.triggerLoadType(0)
+ },
+
+ // 加载历史记录
+ loadChatRecord() {
+ let data = {
+ talk_type: this.query.talk_type,
+ receiver_id: this.query.receiver_id,
+ record_id: this.records.record_id,
+ msg_type: this.query.msg_type,
+ limit: 30,
+ }
+
+ if (this.records.loadStatus == 1) return
+
+ this.records.loadStatus = 1
+ ServeFindTalkRecords(data)
+ .then(res => {
+ if (res.code != 200) return
+
+ let records = data.record_id == 0 ? [] : this.records.items
+
+ records.push(...res.data.rows)
+
+ this.records.items = records
+ this.records.loadStatus =
+ res.data.rows.length < res.data.limit ? 2 : 0
+
+ if (this.records.items.length == 0) {
+ this.records.isEmpty = true
+ } else {
+ this.records.record_id = this.records.items[
+ this.records.items.length - 1
+ ].id
+ }
+ })
+ .catch(() => {
+ this.records.loadStatus = 0
+ })
+ },
+
+ triggerLoadType(type) {
+ this.records.record_id = 0
+ this.query.msg_type = type
+ this.records.isEmpty = false
+ this.records.items = []
+
+ this.loadChatRecord()
+ },
+
+ searchText() {
+ if (this.search.keyword == '') {
+ this.showBox = 0
+ return false
+ }
+
+ this.$notify.info({
+ title: '消息',
+ message: '查询功能正在开发中...',
+ })
+ },
+
+ triggerBroadside() {
+ this.broadside = !this.broadside
+ },
+ },
+}
+</script>
+<style lang="less" scoped>
+/deep/.el-scrollbar__wrap {
+ overflow-x: hidden;
+}
+
+.lum-dialog-mask {
+ z-index: 1;
+}
+
+.lum-dialog-box {
+ width: 100%;
+ height: 600px;
+ max-width: 800px;
+ transition: 1s ease;
+
+ &.full-screen {
+ width: 100%;
+ height: 100%;
+ max-width: unset;
+ margin: 0;
+ border-radius: 0px;
+ }
+
+ .sub-header {
+ height: 38px;
+ line-height: 38px;
+ font-size: 12px;
+ border-bottom: 1px solid #f9f4f4;
+ margin-top: 10px;
+ padding: 0 10px;
+ position: relative;
+
+ i {
+ font-size: 22px;
+ color: #6f6a6a;
+ }
+
+ .search-box {
+ position: absolute;
+ width: 230px;
+ height: 32px;
+ top: 2px;
+ right: 10px;
+ background: #f9f4f4;
+ border-radius: 5px;
+
+ i {
+ position: absolute;
+ left: 10px;
+ top: 8px;
+ font-size: 16px;
+ }
+
+ input {
+ position: absolute;
+ left: 35px;
+ top: 3px;
+ height: 25px;
+ width: 184px;
+ color: #7d7171;
+ background: #f9f4f4;
+ }
+ }
+ }
+
+ .broadside {
+ @border: 1px solid #f9f9f9;
+ border-right: @border;
+ user-select: none;
+ transition: 3s ease;
+
+ .aside-header {
+ display: flex;
+ flex-direction: row;
+ height: 100%;
+ border-bottom: @border;
+ padding: 0;
+
+ > div {
+ text-align: center;
+ line-height: 40px;
+ font-size: 13px;
+ font-weight: 400;
+ }
+
+ .item {
+ flex: 1;
+ cursor: pointer;
+
+ &.selected {
+ color: #66b1ff;
+ }
+ }
+
+ .item-shuxian {
+ flex-basis: 1px;
+ flex-shrink: 0;
+ color: rgb(232 224 224);
+ }
+ }
+
+ .contacts-item {
+ height: 35px;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ padding-left: 10px;
+ position: relative;
+
+ .avatar {
+ flex-basis: 40px;
+ flex-shrink: 0;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+
+ .content {
+ flex: 1 1;
+ height: 100%;
+ line-height: 35px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ font-size: 13px;
+ padding-right: 10px;
+ }
+
+ &:hover,
+ &.selected {
+ background-color: #f5f5f5;
+ }
+ }
+ }
+}
+
+/* first box */
+.type-items {
+ padding: 0 0 0 10px;
+ line-height: 40px;
+ user-select: none;
+ border-bottom: 1px solid #f9f4f4;
+
+ .active {
+ color: #03a9f4;
+ font-weight: 500;
+ }
+
+ span {
+ height: 40px;
+ width: 45px;
+ text-align: center;
+ cursor: pointer;
+ margin: 0 10px;
+ font-size: 12px;
+ font-weight: 400;
+ }
+}
+
+.history-record {
+ padding: 10px 0;
+}
+
+.load-button {
+ width: 100%;
+ height: 35px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: row;
+
+ &.blue {
+ color: #51b2ff;
+ }
+
+ span {
+ margin-left: 5px;
+ font-size: 13px;
+ cursor: pointer;
+ user-select: none;
+ }
+}
+
+.empty-records {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ color: #cccccc;
+ font-weight: 300;
+ font-size: 14px;
+
+ img {
+ width: 100px;
+ }
+}
+
+@import '~@/assets/css/talk/talk-records.less';
+</style>
--
Gitblit v1.9.3