引言

在企业级应用开发中,数据管理和文件处理是常见的需求场景。本文将详细介绍一个基于Vue.js和Element UI实现的号码申诉管理系统,涵盖大文件分块上传SM4国密加密多文件打包下载等核心功能。该系统展示了如何优雅地处理企业级应用中的复杂交互和数据安全需求。

系统架构概览

技术栈

前端框架:Vue.js 2.x

UI组件库:Element UI

HTTP客户端:Axios

文件处理:JSZip + FileSaver

加密算法:SM4国密算法

项目结构

// 核心组件结构
├── components/
│   ├── addDialog.vue      // 添加申诉对话框
│   ├── reviewDialog.vue   // 资料审核对话框
│   ├── imageDialog.vue    // 图片预览对话框
│   ├── downDialog.vue     // 下载预览对话框
│   └── importDialog.vue   // 导入失败号码对话框

核心功能实现详解

1.大文件分片上传

在处理大文件上传时,传统的单次上传方式存在超时和网络不稳定的风险。本系统实现了智能分块上传机制:

// 分块上传核心逻辑
async handleFileUpload(e) {
  const file = e.file;
  const CHUNK_SIZE = 5 * 1024 * 1024; // 5MB分块
  const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
  
  for (let currentChunk = 0; currentChunk < totalChunks; currentChunk++) {
    const start = currentChunk * CHUNK_SIZE;
    const end = Math.min(file.size, start + CHUNK_SIZE);
    const chunk = file.slice(start, end);
    
    const formData = new FormData();
    formData.append("file", chunk);
    formData.append("fileName", file.name);
    formData.append("chunkIndex", currentChunk);
    formData.append("totalChunks", totalChunks);
    
    // 上传单个分块
    const chunkRes = await uploadBlock(formData);
    
    // 最后一个分块上传完成后,触发文件合并
    if (currentChunk === totalChunks - 1) {
      const uploadFileRes = await uploadFileFn(formData);
      // 根据文件类型存储到对应字段
    }
  }
}

技术优势

断点续传:网络中断后可从断点继续上传

并行上传:多个分块可并行上传提高效率

进度可控:可实时显示上传进度

2.SM4国密加密实现

系统采用SM4算法对敏感数据进行加密,确保数据传输安全:

// SM4加密工具类
export function encryptSM4(data) {
  try {
    let dataToEncrypt = data;
    if (typeof dataToEncrypt === "string") {
      dataToEncrypt = removeQuotes(dataToEncrypt);
    } else {
      dataToEncrypt = JSON.stringify(dataToEncrypt);
    }
    // 使用固定的32位密钥进行ECB模式加密
    const encryptedData = SM4.encrypt(dataToEncrypt, key);
    return arrayBufferToHex(encryptedData);
  } catch (error) {
    this.$message.error("加密失败");
  }
}

// 解密函数
export function decryptSM4(data) {
  try {
    // 将十六进制字符串转换为ArrayBuffer
    data = hexToArrayBuffer(data);
    const decryptedArrayBuffer = SM4.decrypt(data, key);
    let decryptedString = arrayBuffer2String(decryptedArrayBuffer);
    decryptedString = removeQuotes(decryptedString);
    
    // 尝试解析JSON,失败则返回原始字符串
    try {
      return JSON.parse(decryptedString);
    } catch (e) {
      return decryptedString;
    }
  } catch (error) {
    this.$message.error("解密失败");
    return null;
  }
}

3.多文件打包下载

系统实现了将多个图片文件打包为ZIP下载的功能:

// 多文件打包下载
handleDownloadPictures(row) {
  this.dialogVisibleDownloadPicturePreview = true;
  
  // 收集所有需要下载的图片
  const downloadList = [];
  if (row.business !== "") downloadList.push(...row.business);
  if (row.company_certificate !== "") downloadList.push(...row.company_certificate);
  if (row.number_ownership !== "") downloadList.push(...row.number_ownership);
  
  // 异步下载并打包
  this.asyncDownload(downloadList);
}

async asyncDownload(selectImgList) {
  const zip = new JSZip();
  const promises = selectImgList.map(item => {
    return this.getFile(item).then(data => {
      const arr_name = item.split("/");
      let file_name = arr_name[arr_name.length - 1];
      if (!file_name.includes(".png")) file_name += ".png";
      zip.file(file_name, data, { binary: true });
    });
  });
  
  Promise.all(promises).then(() => {
    zip.generateAsync({ type: "blob" }).then(content => {
      FileSaver.saveAs(content, "图片.zip");
    });
  });
}

响应式表格设计与优化

表格列渲染优化

<el-table-column prop="data_status" label="资料审核" width="150">
  <template slot-scope="scope">
    <!-- 使用tag组件显示状态 -->
    <el-tag size="small" type="warning" v-if="scope.row.data_status === '1'">
      未审核
    </el-tag>
    <!-- 其他状态显示... -->
  </template>
</el-table-column>

数据脱敏处理

// 电话号码脱敏显示
maskInput(value) {
  if (value != null) {
    let length = value.length;
    if (length > 6) {
      let frontChars = value.substring(0, 3);
      let backChars = value.substring(length - 3, length);
      let middleChars = value.substring(3, length - 3);
      let maskedMiddle = "*".repeat(middleChars.length);
      return frontChars + maskedMiddle + backChars;
    }
    // 其他长度处理...
  }
  return "";
}

组件化设计实践

1.对话框组件拆封组件

系统采用组件化设计,将不同功能的弹窗拆分为独立组件:

<!-- 主组件中引入 -->
<addDialog v-model="dialogVisibleAddNumber" @on-add="getNumberAppeal" />
<reviewDialog v-model="dialogVisibleReview" :ruleForm="ruleForm" />
<imageDialog v-model="dialogVisiblePrevenImage" />

2.权限控制集成

<!-- 使用指令控制按钮权限 -->
<el-button 
  v-permission="'number/appeal'"
  @click="addNumber"
>
  添加号码申诉
</el-button>

性能优化

1.表格虚拟滚动

<el-table
  :data="tableData"
  :max-height="tableHeight"
  :row-style="{ height: '50px' }"
  size="small"
>
  <!-- 表格内容 -->
</el-table>

2.分页数据懒加载

// 分页处理
handleSizeChange(val) {
  this.formInline.num = val;
  this.getNumberAppeal();
}
handleCurrentChange(val) {
  this.formInline.page = val;
  this.getNumberAppeal();
}

错误处理与用户体验

1.上传失败处理

try {
  const chunkRes = await uploadBlock(formData);
  if (chunkRes.data.returnCode === "000") {
    // 上传成功
  } else if (chunkRes.data.returnCode === "111") {
    this.$message.info("文件上传中");
  } else {
    this.$message.error("分块上传失败");
    break; // 中断上传
  }
} catch (error) {
  console.error("分块上传请求失败", error);
  this.$message.error("分块上传请求失败");
  break;
}

2.表单验证

rulesAdd: {
  contacts: [
    { required: true, message: "请输入联系人", trigger: "blur" },
    { min: 2, max: 10, message: "长度在2到10个字符", trigger: "blur" }
  ],
  email: [
    { required: true, message: "请输入邮箱地址", trigger: "blur" },
    { type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }
  ]
}

总结

本文详细解析了一个基于Vue和Element UI的企业级号码申诉管理系统的实现。该系统展示了以下关键技术实践:

大文件处理:采用分块上传机制,提升大文件上传的可靠性和用户体验

数据安全:集成SM4国密算法,确保敏感数据的安全性

组件化开发:通过组件化设计提高代码复用性和可维护性

用户体验优化:包括虚拟滚动、数据脱敏、实时反馈等

错误处理:完善的异常处理机制,保障系统稳定性

这些实践为开发类似的企业级管理系统提供了有价值的参考,特别是在处理文件上传、数据安全和复杂交互等方面。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐