vue脱敏组件封装(适用于多种场景)——超详细手把手一步步教你使用

开发过程中,经常遇到一些数据需要加密,主要体现为身份证号码和手机号码,因此,在这给大家分享一个自己封装的脱敏组件:

效果图

含输入框效果

输入框也做了可编辑的功能。

下面是其代码:

text-desensitize.vue

vue 复制代码
<template>
  <div>
    <div v-if="!isInput" class="text-desensitize-container">
      <!-- 数据展示 -->
      <div class="text-box">{{ text }}</div>
      <!-- 脱敏状态切换按钮 -->
      <div v-if="needEye" class="icon-box" @click="changeSecretState">
        <img :src="isEyeClose ? eyeCloseIcon : eyeOpenIcon" width="100" height="100" alt="">
      </div>
    </div>
    <!-- 输入框模式 -->
    <!-- 要编辑时,要选用v-model绑定数据 -->
    <div v-else class="input-container">
      <el-input
        ref="input"
        :value="isEyeClose ? desensitizedText : value"
        @input="$emit('input', $event)"
        :disabled="!editable" 
        @focus="isEyeClose = false"
        :placeholder="placeholder"
      > 
        <template #suffix>
          <div v-if="needEye" class="icon-box" @click="changeSecretState">
            <img :src="isEyeClose ? eyeCloseIcon : eyeOpenIcon" width="20" height="20" alt="">
          </div>
        </template>
      </el-input>
    </div>
  </div>
</template>

<script>
import desensitizeUtil, { desensitizeTypeList } from '@/utils/util.desensitize'
import eye from '@/assets/icon/eye.png'
import eyeClose from '@/assets/icon/eye-close.png'
  export default {
    name: 'text-desensitize',
    props: {
      // 源数据
      value: {
        type: String,
        default: ''
      },
      // 脱敏类型:idCard 证件号、phone 手机号等,根据定义的脱敏工具类中的类型
      type: {
        type: String,
        default: 'idCard',
        validator: (val) => desensitizeTypeList.includes(val)
      },
      // 是否以input标签包裹
      isInput: {
        type: Boolean,
        default: false
      },
      // 是否可编辑
      editable: {
        type: Boolean,
        default: false
      },
      placeholder: {
        type: String,
        default: ''
      }
    },
    data() {
      return {
        // 当前是否展示脱敏数据
        isEyeClose: true,
        // 切换脱敏的图标
        eyeOpenIcon: eye,
        eyeCloseIcon: eyeClose
      }
    },
    computed: {
      // 是否需要眼睛按钮
      needEye() {
        return this.value && !this.value.includes('****')
      },
      // 脱敏后的数据
      desensitizedText() {
        // return desensitizeUtil.idCard(this.value)
        if (this.value) {
          const desensitizeMethod = desensitizeUtil[this.type]
          return desensitizeMethod
            ? desensitizeMethod(this.value)
            : ''.padEnd(this.value.length, '*')
        }
        return this.value || ''
      },
      // 展示的数据
      text() {
        if (this.isEyeClose) {
          return this.desensitizedText
        }
        return this.value || ''
      }
    },
    methods: {
      // 切换当前脱敏状态
      changeSecretState() {
        this.isEyeClose = !this.isEyeClose
      },
      focusInput() {
        this.$refs.input.focus()
      }
    }
  }
</script>

<style lang="scss" scoped>
  .text-desensitize-container {
    .text-box {
      display: inline-block;
      vertical-align: middle;
    }

    .icon-box {
      display: inline-block;
      vertical-align: middle;
      width: 20px;
      height: 20px;
      margin-left: 10px;
      font-weight: bold;
      fill: #333;
      cursor: pointer;
      img {
        width: 20px;
        height: 20px;
      }
    }
  }
  .input-container {
  position: relative;
}

.transparent-input {
  ::v-deep .el-input__inner {
    color: transparent;
    text-shadow: 0 0 0 #666; /* 保留文字占位空间 */
  }
}

.icon-box {
  cursor: pointer;
  display: flex;
  align-items: center;
  height: 100%;
  padding-right: 8px;
}
</style>
index.js 复制代码
import TextDesensitize from './text-desensitize.vue'

export default TextDesensitize

组件结构如图;

需要用时直接import TextDesensitize from "@/components/text-desensitize"; 具体位置看封装的位置

接下来是使用的示例:

1.el-table里使用

vue 复制代码
<el-table-column
  align="center"
  label="身份证号码"
  prop="zjhm"
  width="150"
  fixed
>
  <template slot-scope="scope">
    <text-desensitize :value="scope.row.zjhm"></text-desensitize>
  </template>
</el-table-column>

<el-table-column
  align="center"
  label="联系电话"
  prop="lxdh"
  width="120"
>
  <template slot-scope="scope">
    <text-desensitize :value="scope.row.lxdh" type="phone"></text-desensitize>
  </template>
</el-table-column>

2.正常使用:

vue 复制代码
// 无输入框
<span class="infos">身份证号:<text-desensitize :value="zjhm"></text-desensitize></span>
<span class="infos">手机号:<text-desensitize :value="lxdh" type="phone"></text-desensitize></span>
// 含输入框
<text-desensitize
  :value="info[item.prop]"
  :isInput="true"
/>
// 可编辑输入框
<text-desensitize
  v-model="form.sqrzjdm"
  :isInput="true"
  :editable="true"
  placeholder="请输入内容"
/>

3.对于封装后的el-table或el-form,不能直接加<text-desensitize>只能在数据中做处理 ,如:el-table里的data、el-form里的model,这种情况可以使用下面这个组件来实现单项数据中插槽以及自定义展示

scoped-slots.mixin.vue

vue 复制代码
<script>
export default {
  name: 'scoped-slots-mixin',
  methods: {
    /**
     * 返回插槽
     * @param {Object} item
     * @param {String} slotName 插槽名称
     * @return {*}
     */
    getScopedSlot(item, slotName = 'default') {
      const { customRender, customRenderLabel, scopedSlots } = item
      let content = {}
      if (scopedSlots) {
        Object.keys(scopedSlots).forEach((key) => {
          let slotKey = key.replace('customRender', '').toLowerCase()
          if (!slotKey) {
            slotKey = 'default'
          }
          const slotValue = scopedSlots[key]
          if (slotValue && this.$scopedSlots[slotValue]) {
            content[slotKey] = this.getSlotFunc(this.$scopedSlots[slotValue])
          }
        })
        // if (scopedSlots.customRender) {
        //   content[slotName] = this.getSlotFunc(this.$scopedSlots[scopedSlots.customRender])
        // }
        // if (scopedSlots.customRenderLabel) {
        //   content['label'] = this.getSlotFunc(this.$scopedSlots[scopedSlots.customRenderLabel])
        // }
      }
      if (!content[slotName] && customRender) {
        content[slotName] = this.getSlotFunc(customRender)
      }
      if (!content['label'] && customRenderLabel) {
        content['label'] = this.getSlotFunc(customRenderLabel)
      }

      if (Object.keys(content).length) {
        return content
      }
      return null
    },
    /**
     * 返回插槽
     * @param {Function} newCustomRender
     * @return {Function}
     */
    getSlotFunc(newCustomRender) {
      return (scope) => newCustomRender(scope)
    }
  }
}
</script>

将这个组件引入到封装的table或者form表单里,

javascript 复制代码
import ScopedSlotsMixin from '../mixins/scoped-slots.mixin.vue'
...
mixins: [ScopedSlotsMixin],

这样就可以实现单项数据中插槽以及自定义展示,这时候的使用方式为:

vue 复制代码
// el-form的model
{
  ...
  tagName: 'text-desensitize', // 使用自定义组件
  tagProps: {
    isInput: true,     // 显示为输入框
    editable: true,    // 允许编辑
  }
},

// el-table的data
{
  ...
  customRender: ({ row }) => <text-desensitize value={row.cardNum} />
},

以上内容,涵盖了大多数脱敏组件使用的情景,同时提供了一个实现单项数据中插槽以及自定义展示的代码,读者可根据项目情况进行处理。

tips:

  1. 本项目使用的是vue2,element-ui,vue3和element-plus的项目也可简单修改使用;
  2. 使用的是scss,没有过多处理,less和sass的项目可直接使用;

如需eye图标,可访问我的github链接自行获取:

相关推荐
weixin-a153003083161 小时前
【playwright篇】教程(十七)[html元素知识]
java·前端·html
ai小鬼头1 小时前
AIStarter最新版怎么卸载AI项目?一键删除操作指南(附路径设置技巧)
前端·后端·github
wen's1 小时前
React Native 0.79.4 中 [RCTView setColor:] 崩溃问题完整解决方案
javascript·react native·react.js
一只叫煤球的猫2 小时前
普通程序员,从开发到管理岗,为什么我越升职越痛苦?
前端·后端·全栈
vvilkim2 小时前
Electron 自动更新机制详解:实现无缝应用升级
前端·javascript·electron
vvilkim2 小时前
Electron 应用中的内容安全策略 (CSP) 全面指南
前端·javascript·electron
aha-凯心2 小时前
vben 之 axios 封装
前端·javascript·学习
漫谈网络2 小时前
WebSocket 在前后端的完整使用流程
javascript·python·websocket
遗憾随她而去.2 小时前
uniapp 中使用路由导航守卫,进行登录鉴权
前端·uni-app
xjt_09013 小时前
浅析Web存储系统
前端