黑词分析与可疑对话挖掘组件的设计与实现

继上一篇https://blog.csdn.net/whmdsb5201314/article/details/157685283?spm=1001.2014.3001.5501

一、项目背景

在内容安全与舆情分析领域,如何高效地从海量对话数据中识别敏感信息、挖掘可疑对话,是许多企业面临的共性挑战。本文将深入剖析一个基于Vue + Element UI构建的黑词分析与可疑对话挖掘组件的核心实现,该组件通过黑词管理、可疑对话组、上下文查看等功能,为用户提供完整的内容安全解决方案。

二、组件概览

技术栈

前端框架:Vue 2

UI组件库:Element UI

状态管理:组件内状态管理

HTTP客户端:Axios

特色组件:自定义弹窗(AbyDialog)、分页组件

核心功能模块

黑词管理模块 - 黑词列表展示、搜索、批量操作

可疑对话组模块 - 私聊/群聊切换、对话展示、上下文查看

关键词管理模块 - 私有关键词/公有关键词添加

话题总结模块 - 对话上下文分析与话题挖掘

三、核心组件设计

1. 双面板布局设计

组件采用左右分栏的布局设计,左侧为黑词管理区,右侧为可疑对话展示区。这种布局符合用户"先选择黑词,后查看对话"的操作体验。

html 复制代码
<div class="black-word-analysis">
  <!-- 左侧:黑词列表 -->
  <div class="left-panel">
    <div class="panel-header">
      <div class="panel-icon"></div>
      <h2 class="panel-title">黑词列表</h2>
    </div>
    <!-- 表格区域 -->
  </div>
  
  <!-- 右侧:可疑对话组 -->
  <div class="right-panel">
    <div class="panel-header">
      <div class="panel-icon"></div>
      <h2 class="panel-title">可疑对话组</h2>
    </div>
    <!-- 对话列表 -->
  </div>
</div>

2. 黑词管理模块

黑词模块包含搜索、表格展示、批量操作三大核心功能。特别值得注意的是,我们通过el-dropdown实现了批量操作的优雅交互:

html 复制代码
<div class="search_word row">
  <div class="left-search row">
    <div class="search-con row">
      <div class="black_word">黑词:</div>
      <el-input v-model="searchKeyword" placeholder="请输入黑词" 
        @keyup.enter.native="handleSearch"></el-input>
    </div>
    <div class="search_btn_box">
      <el-button type="primary" size="small" @click="handleSearch">搜索</el-button>
      <el-button size="small" @click="handleReset">重置</el-button>
    </div>
  </div>
  
  <!-- 批量操作下拉菜单 -->
  <el-dropdown @command="handleCommand">
    <div class="right-box">
      <img src="@/assets/blackWord/ic_batch.png" alt="">
    </div>
    <el-dropdown-menu slot="dropdown">
      <el-dropdown-item command="2">批量加入私有关键词</el-dropdown-item>
      <el-dropdown-item command="3">批量加入公有关键词</el-dropdown-item>
    </el-dropdown-menu>
  </el-dropdown>
</div>

关键技术点el-dropdowncommand模式可以优雅地处理多个批量操作指令,避免编写重复的事件处理逻辑。

3. 分页管理

组件针对左侧黑词和右侧对话分别维护了两套独立的分页逻辑,但采用了统一的设计模式:

javascript 复制代码
// 左侧黑词分页
handleSizeChange(val) {
  this.pageSize = val;
  this.currentPage = 1;
  this.updateTableData();
},

handleCurrentChange(val) {
  this.currentPage = val;
  this.updateTableData();
},

// 根据当前页码和搜索条件更新表格数据
updateTableData() {
  let filteredData = this.allTableData;
  if (this.searchKeyword) {
    filteredData = this.allTableData.filter(item =>
      item.blackWord.includes(this.searchKeyword)
    );
  }
  this.tableTotal = filteredData.length;
  const start = (this.currentPage - 1) * this.pageSize;
  const end = start + this.pageSize;
  this.tableData = filteredData.slice(start, end);
}

设计亮点 :采用前端分页+过滤的策略,避免对后端的频繁请求。allTableData存储完整数据,tableData存储当前页展示数据,两者分离维护,性能优异。

4. 可疑对话展示

对话展示是组件的核心交互模块,我们设计了差异化的展示逻辑:

html 复制代码
<div class="table_item_title">
  <!-- 发送者账号(所有类型都显示) -->
  <img src="@/assets/caseScreen/wx.png" alt="" class="wechat_icon">
  <div class="accout_name">
    {{ item.account1_number }}{{ item.nickname1 ? '(' + item.nickname1 + ')' : '' }}
  </div>

  <!-- 只有私聊才显示箭头和接收者账号 -->
  <template v-if="item.chat_type == 1">
    <div class="accout_icon">
      <i class="el-icon-sort"></i>
    </div>
    <img src="@/assets/caseScreen/wx.png" alt="" class="wechat_icon">
    <div class="accout_name">
      {{ item.account2_number }}{{ item.nickname2 ? '(' + item.nickname2 + ')' : '' }}
    </div>
  </template>
</div>

设计思考:私聊需要展示双向关系,群聊只需展示发送者,这种差异化展示大大提升了用户体验和信息密度。

5. 上下文消息展示与查看完整的对话上下文

对话消息展示区域采用了三段式设计:时间轴、发送者、消息内容。特别设计了消息内容的三行截断功能:

html 复制代码
.content-sty {
  /* 多行文本截断 - 显示3行 */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  max-height: 4.5em; /* 3行 * 1.5行高 */
  overflow: hidden;
}
javascript 复制代码
// 查看上下文
    async viewContext(dialogItem) {
      try {
        // 显示加载状态
        this.currentSelectedDialog = {
          ...dialogItem,
          loading: true
        }

        // 构建请求参数
        const params = {
          recordId: this.taskUuid || '',  // 修改这里:使用 recordId
          chatType: dialogItem.chat_type || this.activeChatType || 1,  // 聊天类型
          pageNum: 1,
          pageSize: 20
        }

        // 重要:添加groupID参数(如果是群聊)
        if (dialogItem.groupID) {
          params.groupID = dialogItem.groupID;
        }

        // 如果是私聊,需要添加账号信息
        if (dialogItem.chat_type == 1) {
          // 根据您的数据结构,可能需要传递发送方和接收方账号
          if (dialogItem.account1_number) {
            params.account1Number = dialogItem.account1_number;
          }
          if (dialogItem.account2_number) {
            params.account2Number = dialogItem.account2_number;
          }
        }


        // 调用获取上下文和话题总结的接口
        const res = await suspiciousDialogueGroup(params);

        if (res.data.code === 1) {
          const responseData = res.data.data;

          let contextList = [];
          let topicSummary = '';

          // 根据实际数据结构处理
          if (Array.isArray(responseData) && responseData.length > 0) {
            // 如果是数组,取第一个元素
            const firstItem = responseData[0];
            contextList = firstItem.contextList || [];

            // 如果没有topicSummary,可以生成一个简单的总结
            topicSummary = firstItem.topicSummary || firstItem.summary ||
              `对话涉及 ${contextList.length} 条消息`;
          } else if (responseData && typeof responseData === 'object') {
            // 如果是对象,直接使用
            contextList = responseData.contextList || [];
            topicSummary = responseData.topicSummary || responseData.summary || '';
          }

          // 更新当前选中的对话数据,添加上下文信息
          this.currentSelectedDialog = {
            ...dialogItem,
            taskUuid: this.taskUuid,  // 保留原 taskUuid(如果需要)
            recordId: this.taskUuid,  // 添加 recordId
            keywordsName: this.keywordsName,  // 添加keywordsName
            chat_type: dialogItem.chat_type || this.activeChatType || 1,  // 确保有chat_type
            groupID: dialogItem.groupID || '',  // 确保有groupID
            account1_number: dialogItem.account1_number || dialogItem.sendUser || '',
            account2_number: dialogItem.account2_number || dialogItem.recvUser || '',
            contextList: contextList,  // 上下文消息列表
            topicSummary: topicSummary,  // 话题总结
            totalCount: contextList.length,  // 总条数
            currentPage: params.pageNum,
            pageSize: params.pageSize,
            loading: false
          };

          // 显示话题总结弹窗
          this.topSummaryVisible = true;
        } else {
          this.$message.error(res.data.message || '获取上下文失败');
          this.currentSelectedDialog.loading = false;
        }
      } catch (error) {
        console.error('获取上下文失败:', error);
        this.$message.error('获取上下文失败');
        this.currentSelectedDialog.loading = false;
      }
    },

配合"查看上下文"按钮,实现了"查看+详情"的优雅交互。

6. 私聊-群聊 账号显示逻辑

组件需要处理私聊、群聊不同场景下的账号显示规则,我们封装了专用的计算逻辑:

javascript 复制代码
getAccountDisplayName(item) {
  // 群聊且是接收方时,显示群成员的账号
  if (item.chat_type == 2) {
    if (item.send_or_accept === "2") {
      // 接收方消息,显示被@的账号
      return item.group_involved_account || item.account2_number || '未知账号';
    } else {
      // 发送方消息,显示发送者账号
      return item.account1_number || '未知账号';
    }
  }
  
  // 私聊逻辑:发送方显示account1,接收方显示account2
  const isSender = item.send_or_accept === "1";
  return isSender ? item.account1_number : item.account2_number || '未知账号';
}

技术价值:将复杂的业务逻辑封装在方法中,模板保持简洁,易于维护和测试。

7. 弹窗组件复用

组件实现了弹窗组件的统一管理,通过v-if动态创建/销毁,避免内存泄漏:

html 复制代码
<!-- 主弹窗 -->
<aby-dialog v-if="dialogVisible" @closed="dialogClose" :dialogData="dialogData">
  <!-- 内容区域 -->
</aby-dialog>

<!-- 话题总结弹窗 -->
<aby-dialog v-if="topSummaryVisible" @closed="closeTopicSummary">
  <TopSumary ref="topicSummaryRef" :currentDialog="currentSelectedDialog" />
</aby-dialog>

<!-- 关键词添加弹窗 -->
<aby-dialog v-if="keywordVisible" @closed="keywordDialogClose">
  <!-- 表单内容 -->
</aby-dialog>

这种设计使弹窗职责单一,互不干扰,并通过ref实现子组件方法调用。

8. 批量操作

系统支持批量选择黑词、批量选择对话组两种批量场景

javascript 复制代码
// 黑词单选/全选回调
selectWords(selection) {
  this.selectWordsList = selection.map(item => item.blackWord);
},

selectAllWords(selection) {
  this.selectWordsList = selection.map(item => item.blackWord);
},

// 批量账号选择
handleAccount() {
  this.accountPairs = this.dialogTableData.filter(e => e.checked);
  if (this.accountPairs.length > 0) {
    this.$refs.group_visible.open();
  } else {
    this.$message.warning("请选择需要批量添加的账号");
  }
}

设计要点:批量操作前必须进行空值校验,并提供明确的用户反馈。

9. 下拉框远程搜索与懒加载

关键词类型选择是一个典型的"大数据量+搜索"场景,我们实现了下拉框的懒加载和远程搜索:

javascript 复制代码
// 滚动加载更多
loadMore(n) {
  if (this.isFirstLoadType) {
    this.rangeNumber += 5;
    this.keywordTypeList = this.caseTypeAllList.slice(0, this.rangeNumber);
  }
},

// 远程搜索
selectRemote(query) {
  if (query === "") {
    this.isFirstLoadType = true;
    this.rangeNumber = 10;
    this.keywordTypeList = this.caseTypeAllList.slice(0, 10);
    return;
  }
  
  this.isFirstLoadType = false;
  this.caseTypeSelectList = this.caseTypeAllList.filter(item => 
    item.item.indexOf(query) > -1
  );
  
  // 分段加载搜索结果
  clearInterval(this.timer);
  this.loadNum = 10;
  this.timer = setInterval(() => {
    this.keywordTypeList = this.caseTypeSelectList.slice(0, this.loadNum);
    this.loadNum += 10;
  });
}

性能优化:采用增量加载策略,避免一次性渲染千条下拉选项导致的页面卡顿。

四、项目总结

本文通过Vue + Element UI构建了一个功能完善、交互流畅的黑词分析与对话挖掘组件。核心价值体现在:

模块解耦:黑词管理、对话列表、关键词维护各自独立

交互友好:批量操作、上下文预览、差异化展示

性能优化:前端分页、懒加载、定时器清理

扩展性:弹窗复用、批量操作统一处理

该组件已在实际项目中稳定运行,可快速适配各类内容安全分析场景。希望本文对正在构建类似系统的开发者有所启发。

相关推荐
anOnion1 小时前
构建无障碍组件之Disclosure Pattern
前端·html·交互设计
threerocks1 小时前
前端将死,Agent 永生
前端·人工智能·ai编程
问道飞鱼2 小时前
【前端知识】Vite用法从入门到实战
前端·vite·项目构建
爱上妖精的尾巴2 小时前
8-10 WPS JSA 正则表达式:贪婪匹配
服务器·前端·javascript·正则表达式·wps·jsa
Aliex_git4 小时前
浏览器 API 兼容性解决方案
前端·笔记·学习
独泪了无痕4 小时前
useStorage:本地数据持久化利器
前端·vue.js
程序员林北北4 小时前
【前端进阶之旅】JavaScript 一些常用的简写技巧
开发语言·前端·javascript
全栈前端老曹4 小时前
【Redis】Redis 持久化机制 RDB 与 AOF
前端·javascript·数据库·redis·缓存·node.js·全栈
NEXT064 小时前
受控与非受控组件
前端·javascript·react.js