vue2脱敏手机号、身份证号、客户名称、车牌号等脱敏组件封装

1.结合项目本身制定思路

  1.  根据需求脱敏规则制定正则表达式
  2. 复制 、呼叫、查看功能均可内置,所需必须参数为要脱敏的原文
  3. 脱敏位置(表单脱敏、文本脱敏)
  4. 功能鉴权通过自定义指令配合达到效果

2.组件所需文件创建

2.1在components文件夹内创建我们组件文件夹Desensitization(自己认识即可)创建组件 index.vue 和 enum/index.js文件枚举存储

3.组件封装

3.1 props定制 

 props:{
    // 是否禁用
    disabledInput:{
      type:Boolean || Number,
      default:true
    },
    /**
     * @ * 默认全部显示
     * @Array ['look','copy','call']
     *          查看   复制    拨打
     */
    isPerM:{
      type : String || Array ,
      default : '*'
    },

    /** 显示模式
     *  @body 文本
     *  @input 表单
     */
    showType:{
      type : String,
      default : 'body'
    },
    /**
     * @FormateType 格式化类型
     */
    formatValue:{
      type : String,
      default : 'phone'
    },
    // 原数据,默认明文
    value:{
      require: true,
      type : String || Number,
      default : ''
    }
  },

3.2 data 所需要的变量声明

data(){
    return {
      isCall :[ 'phone', 'fixPhone'], // 可以使用软电话外呼的类型
      isDes: true, // 当前是否脱敏标识
      cacheValue:'' , // 缓存Value 用于控制显隐状态
// 权限列表,可根据自己情况进行控制,我们这边是可以根据两个角色分别控制,不需要可删除使用props的isPerM
      rolePer :['role253563339844767744','role253563339844767755'] 
    }
  },

3.3 template 模版搭建 , :value数据绑定 , v-if v-hasPermis指令进行功能的控制

<template>
  <div class="des_container">
    <!-- 脱敏组件封装 -->
    <!-- 1. 表单类型 -->
    <div class="des_input" v-if="showType == 'input'">
      <el-input :value="cacheValue" :disabled="disabledInput"></el-input>
    </div>
    <!-- 2. 文本类型 -->
    <div class="des_body" v-else-if="showType == 'body'">
      <span >{{ cacheValue }}</span>
    </div>
    <!-- 3.操作功能 -->
    <div class="des_handle">
      <span class="des_handle_btn" v-if="isDes" v-hasPermis="rolePer[0]" @click="ShowValue">查看</span>
      <span class="des_handle_btn" v-else v-hasPermis="rolePer[0]" @click="HideValue">隐藏</span>
      <span class="des_handle_btn" v-if="isCall.includes(formatValue)" v-hasPermis="rolePer" @click="handleCall">呼叫</span>
      <span class="des_handle_btn copy"  v-hasPermis="rolePer" @click="handleCopy(value)" :data-clipboard-text="value">复制</span>
    </div>
  </div>
</template>

 3.4 导入所需组件和工具类

import RequestUtils from "@/util/RequestUtils.js"; // 封装好的axios请求工具类
import UrlMapping from "@/UrlMapping.js"; // 接口地址映射类
import { FormatType, SaveValue } from "./enum/index.js"; // 格式化类型和脱敏保存类型
import { decryptData_ECB, encryptData_ECB } from "@/script/sm4.js"; // sm4js加密解密方法
import Clipboard from "clipboard";  // 复制插件

3.5 javascript Methods事件

  methods: {
    // 显示明文
    ShowValue() {
      // 1.解密标识
      this.isDes = false;
      // 2.解密赋值原文
      this.cacheValue = this.value;
      // 3.解密时需要对操作进行存储,方便追责(根据实际情况情况进行调用)
      RequestUtils.request(
        { url: "http://xxxxxx.com/decrypt", method: "postjson" },
        {
          decryptData: this.sourceMsg, // 解密数据(电话号码、证件号)
          decryptOperationUi: window.location.pathname.split("/")[2], // 解密操作界面
          decryptUserCode: sessionStorage.getItem("csId"), // 解密工号
        }
      ).then((res) => {});
    },
    // 加密处理
    HideValue() {
      // 1.解密标识
      this.isDes = true;
      // 2.加密赋值正则表达式替换成的密文(这边userName需要单独处理)
      if (this.formatValue == "userName") {
        this.cacheValue = this.value.replace(
          FormatType[this.formatValue],
          SaveValue[this.formatValue](this.value)
        );
      } else {
        this.cacheValue = this.value.replace(
          FormatType[this.formatValue],
          SaveValue[this.formatValue]
        );
      }
    },
    // 执行拨打
    handleCall() {
      // 1.判断是否可以拨打,以下外呼系统是我们项目之前封装好的方法
      if (this.formatValue == "phone") {
        window.phone.phonenum = this.value;
        window.phone.phoneCallType = "phoneCallType";
        window.phone.okCall("queryCallbacktask"); //调用软电话的外呼方法
      }
    },
    // 执行复制
    handleCopy(val) {
      // 1.获取原数据,使用sm4js进行加密处理,不需要可以删除此操作
      const encryptedText = encryptData_ECB(val);
      // 2.使用clipboard插件进行复制,注意按钮上要绑定data-clipboard-text属性:值为原文或密文,还需要绑定class为copy的类名(类名可自定义),还需要引入clipboard.js插件
      const clipboard = new Clipboard(".copy", {
        text: function () {
          return encryptedText;
        },
      });
      // 3.监听复制成功和失败
      clipboard.on("success", () => {
        this.$message.success("复制成功");
        // 清理clipboard实例
        clipboard.destroy();
      });

      clipboard.on("error", () => {
        this.$message.error("复制失败");
        // 清理clipboard实例
        clipboard.destroy();
      });
    },
  },

3.6 鉴权自定义指令封装,因为此指令别的地方用不到,所以可以直接局部注册指令

directives: {
    hasPermis: {
      inserted(el, binding) {
        const checkPermission = (value, permission) => {
          if (typeof value == "string") {
            return !!permission.find((it) => it.roleCode == value);
          } else if (typeof value == "object") {
            var isRole = false;
            for (const item of value) {
              if (permission.find((it) => it.roleCode == item)) {
                isRole = true;
                break;
              }
            }
            return isRole;
          }
        };
        let roleInfo = JSON.parse(sessionStorage.getItem("roleInfo"));
        // el为绑定的元素,binding为绑定的信息对象
        // 实现根据checkPermission函数的结果,动态移除或隐藏元素
        if (!checkPermission(binding.value, roleInfo)) {
          el.parentNode && el.parentNode.removeChild(el);
        }
      },
    },
  },

3.7 css部分,我这里写的是demo,样式可自己进行调整

<style lang="scss" scoped>
.des_container {
  width: 100%;
  height: 100%;
  display: flex;
  .des_input,
  .des_body {
    flex: 1;
  }
  .des_handle {
    display: flex;
    align-items: center;
    .des_handle_btn {
      font-size: 12px;
      color: #409eff;
      width: 28px;
      padding: 0 3px;
      cursor: pointer;
    }
  }
}
</style>

3.8  枚举类定制,格式化类型和格式化需要保存的格式 enum/index.js


/**
 * @phone 手机号码
 * @idCard 身份号码
 * @carId 车牌号码
 * @sex 性别
 * @userName 名称
 * @fixPhone 带区号的座机
 * @birdate 生日或包含日期等
 * @email 邮箱
 * @address 地址
 * @VIN 车架号
 * @EngineNo 发动机号
 * @bindNo 银行卡号,信用卡号
 */
export const FormatType = {
    Landline:'',
    phone:/(\d{3})\d{4}(\d{4})/,
    idCard:/(\d{0})\d{14}(\d{4})/ ,
    carId:/^(.{2})(?:\d+)(.{2})$/,
    sex:/男|女/,
    userName:/(^.{1})(.+)$/g,
    fixPhone:/(\d{4}-)\d{4}(\d{4})/,
    birdate :/\d/g,
    email :/^(.{2})(.*)@(.+)$/,
    address :/省.*/,
    VIN :/^.*(?=.{6}$)/, 
    EngineNo:/./g, 
    bindNo:/^(.{6})(?:\d+)(.{4})$/,  
}

// 保存格式
export const SaveValue = {
    Landline:"$1****$2",
    phone:"$1****$2",
    idCard:'$1********$2',
    carId: '$1***$2',
    sex: '*',
    userName: makedName,
    fixPhone:'$1****$2',
    birdate :'*',
    email :'$1*****@$3',
    address :'省*******',
    VIN :'*******',
    EngineNo:'*',
    bindNo:'$1******$2',
}

/**
 * 脱敏特殊处理
 * @param {*} name 
 * @returns  
 */
// 姓名
const makedName =  (name)=> {
    console.log('args',name)
    if (name.length == 2) {
        name = name.substring(0, 1) + '*'; // 截取name 字符串截取第一个字符,
        return name; // 张三显示为张*
    } else if (name.length == 3) {
        name = name.substring(0, 1) + '*' + name.substring(2, 3); // 截取第一个和第三个字符
        return name; // 李思思显示为李*思
    } else if (name.length > 3) {
        name = name.substring(0, 1) + '*' + '*' + name.substring(3, name.length); // 截取第一个和大于第4个字符
        return name; // 王五哈哈显示为王**哈
    }

}

4. 组件使用样例

/**
 * @使用说明
 * @注 组件内包含 '查看隐藏' 、 '软电话外呼' 、 '复制号码(密文)'
 * 1.局部或者全局导入组件
 * 2.属性传入
 *      2.1 v-model : 挂载原数据
 *      2.2 formatValue 格式化类型 默认:phone
 *      2.3 showType 显示类型 默认为body
 *      2.3 disabledInput 当showType为input时,是否禁用input
 *      2.4 isPerM 权限认证 (当前版本废弃,当前版本通过角色区分,若使用则可传入对应功能码值)
 *
 */

4.1 组件导入,进行局部注册;或者全局注册此组件

效果:

1.示例1   formatValue ="bindNo" 银行卡号

2.示例1   formatValue ="phone" 手机号码

复制结果为加密内容:

 组件完整代码

1.index.vue

<template>
  <div class="des_container">
    <!-- 脱敏组件封装 -->
    <!-- 1. 表单类型 -->
    <div class="des_input" v-if="showType == 'input'">
      <el-input :value="cacheValue" :disabled="disabledInput"></el-input>
    </div>
    <!-- 2. 文本类型 -->
    <div class="des_body" v-else-if="showType == 'body'">
      <span>{{ cacheValue }}</span>
    </div>
    <!-- 3.操作功能 -->
    <div class="des_handle">
      <span
        class="des_handle_btn"
        v-if="isDes"
        v-hasPermis="rolePer[0]"
        @click="ShowValue"
        >查看</span
      >
      <span
        class="des_handle_btn"
        v-else
        v-hasPermis="rolePer[0]"
        @click="HideValue"
        >隐藏</span
      >
      <span
        class="des_handle_btn"
        v-if="isCall.includes(formatValue)"
        v-hasPermis="rolePer"
        @click="handleCall"
        >呼叫</span
      >
      <span
        class="des_handle_btn copy"
        v-hasPermis="rolePer"
        @click="handleCopy(value)"
        :data-clipboard-text="value"
        >复制</span
      >
    </div>
  </div>
</template>


<script>
import RequestUtils from "@/util/RequestUtils.js"; // 封装好的axios请求工具类
import UrlMapping from "@/UrlMapping.js"; // 接口地址映射
import { FormatType, SaveValue } from "./types/enum.js"; // 格式化类型和脱敏保存类型
import { decryptData_ECB, encryptData_ECB } from "@/script/sm4.js"; // sm4js加密解密方法
import Clipboard from "clipboard";  // 复制插件
/**
 * @使用说明
 * @注 组件内包含 '查看隐藏' 、 '软电话外呼' 、 '复制号码(密文)'
 * 1.局部或者全局导入组件
 * 2.属性传入
 *      2.1 v-model : 挂载原数据
 *      2.2 formatValue 格式化类型 默认:phone
 *      2.3 showType 显示类型 默认为body
 *      2.3 disabledInput 当showType为input时,是否禁用input
 *      2.4 isPerM 权限认证 (当前版本废弃,当前版本通过角色区分,若使用则可传入对应功能码值)
 *
 */

export default {
  name: "DjbxSensitiveData", // Sensitive Data
  props: {
    // 是否禁用
    disabledInput: {
      type: Boolean || Number,
      default: true,
    },
    /**
     * @ * 默认全部显示
     * @Array ['look','copy','call']
     *          查看   复制    拨打
     */
    isPerM: {
      type: String || Array,
      default: "*",
    },

    /** 显示模式
     *  @body 文本
     *  @input 表单
     */
    showType: {
      type: String,
      default: "body",
    },
    /**
     * @FormateType 格式化类型
     */
    formatValue: {
      type: String,
      default: "phone",
    },
    // 原数据,默认明文
    value: {
      require: true,
      type: String || Number,
      default: "",
    },
  },
  data() {
    return {
      isCall: ["phone", "fixPhone"], // 可以使用软电话外呼的类型
      isDes: true, // 当前是否脱敏标识
      cacheValue: "", // 缓存Value 用于控制显隐状态
      rolePer: ["role253563339844767744", "role253563339844767755"], // 权限列表,可根据自己情况进行控制,我们这边是可以根据两个角色分别控制,不需要可删除使用props的isPerM
    };
  },
  created() {
    this.cacheValue = this.value.replace(
      FormatType[this.formatValue],
      SaveValue[this.formatValue]
    );
  },
  methods: {
    // 显示明文
    ShowValue() {
      // 1.解密标识
      this.isDes = false;
      // 2.解密赋值原文
      this.cacheValue = this.value;
      // 3.解密时需要对操作进行存储,方便追责(根据实际情况情况进行调用)
      RequestUtils.request(
        { url: "http://xxxxxx.com/decrypt", method: "postjson" },
        {
          decryptData: this.sourceMsg, // 解密数据(电话号码、证件号)
          decryptOperationUi: window.location.pathname.split("/")[2], // 解密操作界面
          decryptUserCode: sessionStorage.getItem("csId"), // 解密工号
        }
      ).then((res) => {});
    },
    // 加密处理
    HideValue() {
      // 1.解密标识
      this.isDes = true;
      // 2.加密赋值正则表达式替换成的密文(这边userName需要单独处理)
      if (this.formatValue == "userName") {
        this.cacheValue = this.value.replace(
          FormatType[this.formatValue],
          SaveValue[this.formatValue](this.value)
        );
      } else {
        this.cacheValue = this.value.replace(
          FormatType[this.formatValue],
          SaveValue[this.formatValue]
        );
      }
    },
    // 执行拨打
    handleCall() {
      // 1.判断是否可以拨打,以下外呼系统是我们项目之前封装好的方法
      if (this.formatValue == "phone") {
        window.phone.phonenum = this.value;
        window.phone.phoneCallType = "phoneCallType";
        window.phone.okCall("queryCallbacktask"); //调用软电话的外呼方法
      }
    },
    // 执行复制
    handleCopy(val) {
      // 1.获取原数据,使用sm4js进行加密处理,不需要可以删除此操作
      const encryptedText = encryptData_ECB(val);
      // 2.使用clipboard插件进行复制,注意按钮上要绑定data-clipboard-text属性:值为原文或密文,还需要绑定class为copy的类名(类名可自定义),还需要引入clipboard.js插件
      const clipboard = new Clipboard(".copy", {
        text: function () {
          return encryptedText;
        },
      });
      // 3.监听复制成功和失败
      clipboard.on("success", () => {
        this.$message.success("复制成功");
        // 清理clipboard实例
        clipboard.destroy();
      });

      clipboard.on("error", () => {
        this.$message.error("复制失败");
        // 清理clipboard实例
        clipboard.destroy();
      });
    },
  },
  directives: {
    hasPermis: {
      inserted(el, binding) {
        // 权限校验函数
        const checkPermission = (value, permission) => {
          if (typeof value == "string") {
            return !!permission.find((it) => it.roleCode == value);
          } else if (typeof value == "object") {
            var isRole = false;
            for (const item of value) {
              if (permission.find((it) => it.roleCode == item)) {
                isRole = true;
                break;
              }
            }
            return isRole;
          }
        };
        // 获取当前用户角色信息
        let roleInfo = JSON.parse(sessionStorage.getItem("roleInfo"));
        // el为绑定的元素,binding为绑定的信息对象
        // 实现根据checkPermission函数的结果,动态移除或隐藏元素
        if (!checkPermission(binding.value, roleInfo)) {
          el.parentNode && el.parentNode.removeChild(el);
        }
      },
    },
  },
};
</script>

<style lang="scss" scoped>
.des_container {
  width: 100%;
  height: 100%;
  display: flex;
  .des_input,
  .des_body {
    flex: 1;
  }
  .des_handle {
    display: flex;
    align-items: center;
    .des_handle_btn {
      font-size: 12px;
      color: #409eff;
      width: 28px;
      padding: 0 3px;
      cursor: pointer;
    }
  }
}
</style>

2.enum/index.js


/**
 * @phone 手机号码
 * @idCard 身份号码
 * @carId 车牌号码
 * @sex 性别
 * @userName 名称
 * @fixPhone 带区号的座机
 * @birdate 生日或包含日期等
 * @email 邮箱
 * @address 地址
 * @VIN 车架号
 * @EngineNo 发动机号
 * @bindNo 银行卡号,信用卡号
 */
export const FormatType = {
    Landline:'',
    phone:/(\d{3})\d{4}(\d{4})/,
    idCard:/(\d{0})\d{14}(\d{4})/ ,
    carId:/^(.{2})(?:\d+)(.{2})$/,
    sex:/男|女/,
    userName:/(^.{1})(.+)$/g,
    fixPhone:/(\d{4}-)\d{4}(\d{4})/,
    birdate :/\d/g,
    email :/^(.{2})(.*)@(.+)$/,
    address :/省.*/,
    VIN :/^.*(?=.{6}$)/, 
    EngineNo:/./g, 
    bindNo:/^(.{6})(?:\d+)(.{4})$/,  
}

// 保存格式
export const SaveValue = {
    Landline:"$1****$2",
    phone:"$1****$2",
    idCard:'$1********$2',
    carId: '$1***$2',
    sex: '*',
    userName: makedName,
    fixPhone:'$1****$2',
    birdate :'*',
    email :'$1*****@$3',
    address :'省*******',
    VIN :'*******',
    EngineNo:'*',
    bindNo:'$1******$2',
}

/**
 * 脱敏特殊处理
 * @param {*} name 
 * @returns  
 */
// 姓名
const makedName =  (name)=> {
    console.log('args',name)
    if (name.length == 2) {
        name = name.substring(0, 1) + '*'; // 截取name 字符串截取第一个字符,
        return name; // 张三显示为张*
    } else if (name.length == 3) {
        name = name.substring(0, 1) + '*' + name.substring(2, 3); // 截取第一个和第三个字符
        return name; // 李思思显示为李*思
    } else if (name.length > 3) {
        name = name.substring(0, 1) + '*' + '*' + name.substring(3, name.length); // 截取第一个和大于第4个字符
        return name; // 王五哈哈显示为王**哈
    }

}

此上就是这个组件结合需求来封装的一个简单的组件,脱敏需求很常见希望帮到大家!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值