Files
Wx_WxCheck_Prod/openspec/changes/optimize-conversation-user-management/implementation.md

11 KiB
Raw Blame History

会话管理和用户管理页面优化 - 技术实现

1. 工具函数实现

1.1 创建工具函数文件

创建了 src/utils/formatters.js 文件,用于存放通用的格式化工具函数:

// 手机号脱敏处理
export const formatPhoneNumber = (phoneNumber, showFull = false) => {
  if (!phoneNumber) return ''
  const phone = phoneNumber.toString()
  if (phone.length !== 11) return phone
  
  if (showFull) {
    return phone
  } else {
    return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
  }
}

// 时间格式转换将T替换为空格
export const formatDateTime = (dateTime) => {
  if (!dateTime) return ''
  return dateTime.toString().replace('T', ' ')
}

// 时间戳转换为格式化日期
export const formatTimestamp = (timestamp) => {
  if (!timestamp) return ''
  const date = new Date(timestamp)
  return date.toISOString().replace('T', ' ').slice(0, 19)
}

1.2 工具函数设计思路

  • 手机号脱敏使用正则表达式将中间4位替换为"****",支持参数控制是否显示完整号码
  • 时间格式转换:简单高效地将"T"替换为空格,保持原有时间格式不变
  • 时间戳转换:将时间戳转换为标准的"YYYY-MM-DD HH:mm:ss"格式

2. 会话管理页面优化

2.1 发送方式Tag标签实现

<el-descriptions-item label="发送方式">
  <el-tag :type="item.sendMethod === '文字' ? 'success' : ''">
    {{ item.sendMethod === '文字' ? 'text' : 'voice' }}
  </el-tag>
</el-descriptions-item>

2.2 手机号脱敏与点击交互

<el-descriptions-item label="手机号">
  <span 
    class="phone-number" 
    @click="item.showFullPhone = !item.showFullPhone" 
    :title="'点击' + (item.showFullPhone ? '隐藏' : '显示') + '完整号码'"
  >
    {{ formatPhoneNumber(item.phoneNumber, item.showFullPhone) }}
  </span>
</el-descriptions-item>

2.3 时间格式转换

<el-descriptions-item label="记录时间">
  {{ formatDateTime(item.recordTime) }}
</el-descriptions-item>

2.4 Descriptions描述列表实现

2.4.1 模板修改

<!-- 会话记录列表 -->
<el-card class="table-card">
  <el-scrollbar height="500px" @scroll="handleScroll">
    <div v-loading="loading" class="descriptions-container">
      <div v-for="(item, index) in conversationList" :key="index" class="descriptions-item">
        <!-- 主要信息3列显示 -->
        <el-descriptions :column="3" border>
          <el-descriptions-item label="记录时间">{{ formatDateTime(item.recordTime) }}</el-descriptions-item>
          <el-descriptions-item label="用户名">{{ item.userName }}</el-descriptions-item>
          <el-descriptions-item label="微信名">{{ item.weChatName }}</el-descriptions-item>
          <el-descriptions-item label="手机号">
            <span 
              class="phone-number" 
              @click="item.showFullPhone = !item.showFullPhone" 
              :title="'点击' + (item.showFullPhone ? '隐藏' : '显示') + '完整号码'"
            >
              {{ formatPhoneNumber(item.phoneNumber, item.showFullPhone) }}
            </span>
          </el-descriptions-item>
          <el-descriptions-item label="部门">{{ item.department }}</el-descriptions-item>
          <el-descriptions-item label="消息类型">
            <el-tag :type="item.messageType === 1 ? 'success' : ''">
              {{ item.messageType === 1 ? '公有' : '私有' }}
            </el-tag>
          </el-descriptions-item>
          <el-descriptions-item label="发送方式">
            <el-tag :type="item.sendMethod === '文字' ? 'success' : ''">
              {{ item.sendMethod === '文字' ? 'text' : 'voice' }}
            </el-tag>
          </el-descriptions-item>
          <el-descriptions-item label="语音时长">{{ item.speakingTime ? item.speakingTime + '秒' : '-' }}</el-descriptions-item>
          <el-descriptions-item label="位置">{{ item.userLocation }}</el-descriptions-item>
        </el-descriptions>
        
        <!-- 会话内容单独一行显示在最下方 -->
        <el-descriptions :column="1" border class="conversation-content">
          <el-descriptions-item label="会话内容">
            {{ item.conversationContent }}
          </el-descriptions-item>
        </el-descriptions>
      </div>
      
      <!-- 加载更多提示 -->
      <div v-if="loadingMore" class="loading-more">
        <el-icon class="is-loading"><Loading /></el-icon>
        <span>正在加载中...</span>
      </div>
      <div v-else-if="!hasMoreData" class="no-more-data">
        <span>已加载全部数据</span>
      </div>
    </div>
  </el-scrollbar>
</el-card>

2.4.2 滚动处理逻辑

// 滚动处理
const handleScroll = (event) => {
  const { scrollTop, scrollHeight, clientHeight } = event.target
  // 当滚动到底部100px以内时加载下一页
  if (scrollHeight - scrollTop - clientHeight < 100 && !loadingMore.value && hasMoreData.value) {
    loadNextPage()
  }
}

// 加载下一页数据
const loadNextPage = () => {
  if (loadingMore.value || !hasMoreData.value) return
  
  loadingMore.value = true
  currentPage.value++
  fetchConversations(true) // 传入true表示加载更多
}

2.4.3 分页请求逻辑

// 获取会话记录
const fetchConversations = async (isLoadMore = false) => {
  if (isLoadMore) {
    loadingMore.value = true
  } else {
    loading.value = true
    conversationList.value = [] // 非加载更多时清空列表
    hasMoreData.value = true // 重置加载状态
    currentPage.value = 1 // 重置当前页
  }
  
  try {
    // 构建请求参数,包含分页信息
    const params = {
      // 其他参数
      page: currentPage.value,
      pageSize: pageSize.value // 固定为20条/页
    }
    
    // 调用API获取会话记录
    const data = await request.post('/Admin/QueryConversations', JSON.stringify(params))
    
    // 处理返回数据
    const rawData = Array.isArray(data) ? data : data.data || []
    const sortedData = rawData.sort((a, b) => {
      return b.recordTimeUTCStamp - a.recordTimeUTCStamp
    })
    
    // 如果是加载更多,则追加数据,否则替换数据
    if (isLoadMore) {
      conversationList.value = [...conversationList.value, ...sortedData]
    } else {
      conversationList.value = sortedData
    }
    
    // 判断是否已加载全部数据
    if (sortedData.length < pageSize.value) {
      hasMoreData.value = false
    }
  } catch (error) {
    ElMessage.error('获取会话记录失败:' + error.message)
    if (!isLoadMore) {
      conversationList.value = []
    }
  } finally {
    loading.value = false
    loadingMore.value = false
  }
}

2.5 样式优化

/* Descriptions容器样式 */
.descriptions-container {
  width: 100%;
}

/* 每个Descriptions项的样式 */
.descriptions-item {
  margin-bottom: 20px;
  border-radius: 4px;
  overflow: hidden;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}

/* 会话内容样式 */
.conversation-content {
  margin-top: 10px;
}

/* 响应式调整Descriptions列数 */
@media (max-width: 767px) {
  .el-descriptions {
    :deep(.el-descriptions__table) {
      width: 100%;
    }
    
    :deep(.el-descriptions__cell) {
      width: 50%;
    }
  }
}

3. 用户管理页面优化

3.1 移除分页控件

移除了以下内容:

  • el-pagination组件
  • 分页数据变量currentPage, pageSize, total
  • 分页变化处理函数handlePageChange

3.2 应用时间格式转换和手机号脱敏

与会话管理页面类似,为时间列和手机号列应用了相同的格式化处理。

4. 样式优化

4.1 手机号样式

/* 手机号样式 */
.phone-number {
  cursor: pointer;
  color: #409eff;
  text-decoration: underline;
}

.phone-number:hover {
  color: #66b1ff;
}

4.2 加载更多样式

/* 加载更多样式 */
.loading-more,
.no-more-data {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 16px;
  color: #909399;
  font-size: 14px;
}

.loading-more .el-icon {
  margin-right: 8px;
}

5. 最佳实践

5.1 代码组织

  • 将通用工具函数抽离到单独的文件中,提高复用性和可维护性
  • 使用Composition API和script setup语法简化组件代码
  • 遵循单一职责原则,每个组件只负责一个功能模块

5.2 性能优化

  • 无限滚动加载减少了初始加载时间,提高了用户体验
  • 固定分页大小为20条/页,平衡了加载性能和用户体验
  • 添加了加载状态提示和重复请求防止机制

5.3 用户体验

  • 手机号脱敏保护了用户隐私
  • 点击交互支持查看完整手机号,方便用户操作
  • 时间格式优化提高了可读性
  • 无限滚动减少了用户的操作步骤
  • Descriptions描述列表提供了更清晰的数据展示方式

5.4 代码可读性

  • 使用清晰的变量和函数命名
  • 添加了必要的注释
  • 遵循Vue 3的最佳实践

6. 技术选型理由

  1. Element Plus组件库提供了丰富的UI组件包括tag标签、descriptions描述列表、scrollbar滚动条等简化了开发工作

  2. Descriptions描述列表:相比表格,描述列表更适合展示一行数据的详细信息,提供了更好的视觉层次

  3. 无限滚动分页:相比传统分页,无限滚动提供了更流畅的用户体验,减少了用户的操作步骤

  4. 工具函数设计:将通用功能抽离为工具函数,提高了代码的复用性和可维护性

  5. 手机号脱敏:保护用户隐私,符合数据安全规范

  6. 时间格式优化:提高了数据的可读性,符合用户的阅读习惯

7. 影响范围

  1. 会话管理页面

    • 模板结构变化表格替换为Descriptions描述列表
    • 数据处理逻辑变化
    • 交互方式变化
    • 样式变化
  2. 用户管理页面

    • 模板结构变化:移除了分页控件
    • 数据处理逻辑变化
    • 交互方式变化
  3. API调用

    • 会话记录API添加了分页参数
  4. 新增文件

    • src/utils/formatters.js:工具函数文件
  5. 文档

    • 更新了详细的修改记录文档

8. 后续优化建议

  1. 添加搜索功能:为会话记录和用户管理页面添加更强大的搜索功能

  2. 支持导出功能支持将数据导出为Excel或CSV格式

  3. 添加筛选条件记忆功能:记住用户的筛选条件,提高用户体验

  4. 优化移动端体验:进一步优化移动端的显示效果和交互方式

  5. 添加数据统计功能:在页面顶部添加数据统计卡片,展示关键指标

  6. 支持自定义列:允许用户自定义显示哪些列,提高灵活性

  7. 添加批量操作功能:支持批量删除或修改数据,提高操作效率

9. 总结

本次优化实现了会话管理和用户管理页面的多项改进包括发送方式tag标签显示、手机号脱敏、时间格式优化、表格替换为Descriptions描述列表、无限滚动分页等。这些改进提高了系统的用户体验、数据安全性和性能表现。

所有修改都遵循了openspec开发规范并更新了详细的修改记录文档便于后续维护和参考。