1.结合项目本身制定思路
- 根据需求脱敏规则制定正则表达式
- 复制 、呼叫、查看功能均可内置,所需必须参数为要脱敏的原文
- 脱敏位置(表单脱敏、文本脱敏)
- 功能鉴权通过自定义指令配合达到效果
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; // 王五哈哈显示为王**哈
}
}