敏感数据加密和查看密文

This commit is contained in:
ZZM 2025-09-09 17:45:58 +08:00
parent 461eba8f93
commit dda013c4a9
7 changed files with 662 additions and 344 deletions

View File

@ -274,3 +274,18 @@ export function crmContactsExitTeamAPI(data) {
method: 'post'
})
}
/**
* 获取明文
* @param data
* @returns {*}
*/
export function filedGetPlaintextAPI(data) {
return request({
url: 'crmContacts/getPlaintext',
method: 'post',
data: data,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
}

View File

@ -711,3 +711,35 @@ export function crmCustomerInvoiceInfoAPI(data) {
}
})
}
/**
* 获取明文
* @param data
* @returns {*}
*/
export function filedGetPlaintextAPI(data) {
return request({
url: 'crmCustomer/getPlaintext',
method: 'post',
data: data,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
}
/**
* 验证企业画像的密码
* @param data
* @returns {*}
*/
export function verifyPasswordAPI(data) {
return request({
url: 'crmCustomer/verifyPassword',
method: 'post',
data: data,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
}

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1757056872697" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1347" width="16" height="16" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M512.048 768a224 224 0 1 1 0-448 224 224 0 0 1 0 448z m0-64a160 160 0 1 0 0-320 160 160 0 0 0 0 320z" p-id="1348" fill="#8a8a8a"></path><path d="M674.992 706.944Q742.448 639.36 742.448 544q0-95.424-67.456-162.944Q607.408 313.6 512.048 313.6q-95.424 0-162.944 67.456Q281.648 448.64 281.648 544q0 95.424 67.456 162.944Q416.688 774.4 512.048 774.4q95.424 0 162.944-67.456z m-9.088-316.8q63.744 63.744 63.744 153.856t-63.744 153.856Q602.16 761.6 512.048 761.6t-153.856-63.744Q294.448 634.112 294.448 544t63.744-153.856Q421.936 326.4 512.048 326.4t153.856 63.744z m-271.488 36.16Q345.648 475.072 345.648 544t48.704 117.632Q443.184 710.4 512.048 710.4t117.632-48.768q48.768-48.704 48.768-117.632T629.68 426.24Q580.976 377.6 512.048 377.6t-117.632 48.704z m8.96 226.304Q358.448 607.616 358.448 544q0-63.616 44.992-108.608Q448.432 390.4 512.048 390.4q63.616 0 108.608 44.992 44.992 44.992 44.992 108.608 0 63.616-44.992 108.608Q575.664 697.6 512.048 697.6q-63.616 0-108.608-44.992z" p-id="1349" fill="#8a8a8a"></path><path d="M512.048 896C323.248 896 154.224 783.232 5.488 561.856a32 32 0 0 1 0-35.712C154.288 304.704 323.248 192 512.048 192s357.824 112.704 506.56 334.144a32 32 0 0 1 0 35.712c-148.736 221.44-317.76 334.08-506.56 334.08z m0-64c159.872 0 306.752-94.784 441.216-288C818.8 350.72 671.92 256 512.048 256 352.24 256 205.36 350.784 70.832 544 205.36 737.216 352.24 832 512.048 832z" p-id="1350" fill="#8a8a8a"></path><path d="M512.048 896C323.248 896 154.224 783.232 5.488 561.856a32 32 0 0 1 0-35.712C154.288 304.704 323.248 192 512.048 192s357.824 112.704 506.56 334.144a32 32 0 0 1 0 35.712c-148.736 221.44-317.76 334.08-506.56 334.08z m0-12.8q275.328 0 495.936-328.512 7.168-10.688 0-21.44Q787.376 204.8 512.048 204.8T16.112 533.312q-7.232 10.688 0 21.376Q236.72 883.2 512.048 883.2z m0-38.4q-247.424 0-451.776-293.504L55.152 544l5.12-7.296Q264.624 243.2 512.048 243.2t451.776 293.504l5.12 7.296-5.12 7.296Q759.472 844.8 512.048 844.8z m0-12.8c159.872 0 306.752-94.784 441.216-288C818.8 350.72 671.92 256 512.048 256 352.24 256 205.36 350.784 70.832 544 205.36 737.216 352.24 832 512.048 832z" p-id="1351" fill="#8a8a8a"></path></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 427 KiB

After

Width:  |  Height:  |  Size: 426 KiB

View File

@ -28,7 +28,7 @@
type="primary"
icon="wk wk-transfer"
@click.native="handleTypeClick('transfer')">转移</el-button>
<el-button v-if="crmType === 'customer'" type="primary" icon="el-icon-s-opportunity" @click="showQyHx">企业画像</el-button>
<el-button v-if="crmType === 'customer'" type="primary" icon="el-icon-s-opportunity" @click="popPasswordBox">企业画像</el-button>
<el-button
v-if="showEdit"
class="head-handle-button xr-btn--green"
@ -118,6 +118,28 @@
:id="id"
:pool-id="poolId"
/>
<!-- 密码验证对话框 -->
<el-dialog
:visible.sync="showPasswordDialog"
title="验证密码"
width="400px"
append-to-body>
<el-form>
<el-form-item label="密码">
<el-input
v-model="passwordValue"
type="password"
placeholder="请输入验证密码"
@keyup.enter.native="verifyPassword"
/>
</el-form-item>
</el-form>
<p v-if="errorMessage" class="error-message">{{ errorMessage }}</p>
<span slot="footer" class="dialog-footer">
<el-button @click="showPasswordDialog = false">取消</el-button>
<el-button :loading="verifying" type="primary" @click="verifyPassword">验证</el-button>
</span>
</el-dialog>
</div>
</template>
<script type="text/javascript">
@ -150,7 +172,8 @@ import TransferHandle from './SelectionHandle/TransferHandle' // 转移
import AllocHandle from './SelectionHandle/AllocHandle' //
import DealStatusHandle from './SelectionHandle/DealStatusHandle' //
import PutPoolHandle from './SelectionHandle/PutPoolHandle'
import EnterpriseProfile from './EnterpriseProfile.vue' //
import EnterpriseProfile from './EnterpriseProfile.vue'
import { verifyPasswordAPI } from '../../../api/crm/customer' //
export default {
name: 'CRMDetailHead',
@ -205,7 +228,10 @@ export default {
allocDialogShow: false, //
dealStatusShow: false, //
putPoolShow: false, //
EnterpriseProfileShow: false //
EnterpriseProfileShow: false, //
showPasswordDialog: false, //
errorMessage: '', //
passwordValue: '' //
}
},
computed: {
@ -694,6 +720,32 @@ export default {
return true
},
/**
* 弹出密码框
*/
popPasswordBox() {
this.passwordValue = ''
this.errorMessage = ''
this.showPasswordDialog = true
},
/**
* 验证密码
*/
verifyPassword() {
const data = {
password: this.passwordValue
}
verifyPasswordAPI(data)
.then(res => {
this.showPasswordDialog = false
this.showQyHx()
})
.catch(error => {
console.error('验证请求失败:', error)
this.errorMessage = error.msg
this.passwordValue = ''
})
},
showQyHx() {
console.log(this.EnterpriseProfileShow)
this.EnterpriseProfileShow = true
@ -818,4 +870,10 @@ export default {
margin-right: 5px;
}
}
.error-message {
color: #f56c6c;
font-size: 12px;
margin-top: 8px;
}
</style>

View File

@ -144,6 +144,13 @@
</template>
</wk-field-view>
<!-- 添加眼睛图标 -->
<img
v-if="triggerEyeField(item.fieldName)"
:src="eyeClosedSrc"
class="eye-icon"
@click="changeEyeState(item)"
>
<i v-if="getEditAuth(item)" class="wk wk-edit form-item__edit" @click.stop="editClick(item, index)" />
</flexbox>
</template>
@ -162,6 +169,47 @@
:visible.sync="showFullDetail"
:id="fullDetailId"
:crm-type="fullDetailType"/>
<!-- 密码验证对话框 -->
<el-dialog
:visible.sync="showPasswordDialog"
title="验证密码"
width="400px"
append-to-body>
<el-form>
<el-form-item label="密码">
<el-input
v-model="passwordValue"
type="password"
placeholder="请输入验证密码"
@keyup.enter.native="verifyPasswordAndGetPlaintext"
/>
</el-form-item>
</el-form>
<p v-if="errorMessage" class="error-message">{{ errorMessage }}</p>
<span slot="footer" class="dialog-footer">
<el-button @click="showPasswordDialog = false">取消</el-button>
<el-button :loading="verifying" type="primary" @click="verifyPasswordAndGetPlaintext">验证</el-button>
</span>
</el-dialog>
<!-- 明文弹框 -->
<el-dialog
:visible.sync="showPlaintext"
title="明文"
width="500px"
append-to-body>
<div class="plaintext-content">
<el-input
:autosize="{ minRows: 3, maxRows: 6 }"
:value="plaintextValues"
type="textarea"
readonly
class="plaintext-field"
/>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="showPlaintext = false">确定</el-button>
</span>
</el-dialog>
</div>
</template>
@ -185,6 +233,7 @@ import { isArray, isObject, isEmpty } from '@/utils/types'
import { mapGetters } from 'vuex'
import { getFormFieldShowName } from '@/components/NewCom/WkForm/utils'
import CustomFieldsMixin from '@/mixins/CustomFields'
import { filedGetPlaintextAPI } from '../../../api/crm/customer'
export default {
//
@ -268,7 +317,14 @@ export default {
}, {
name: '收据',
value: 5
}]
}],
eyeClosedSrc: require('@/assets/img/crm/eye-open.svg'), //
showPasswordDialog: false, //
currentField: null, //
plaintextValues: null, //
showPlaintext: false, //
passwordValue: '', //
errorMessage: '' //
}
},
inject: ['rootTabs'],
@ -728,6 +784,46 @@ export default {
return dataValue ? dataValue.name : ''
}
return ''
},
/**
* 触发眼睛的字段
*/
triggerEyeField(fieldName) {
return ['mobile', 'email', 'fieldCbiasz', 'fliedKjhmgc', 'name', 'telephone', 'address'].includes(fieldName)
},
/**
* 切换眼睛
*/
changeEyeState(item) {
console.log(item)
this.passwordValue = ''
this.errorMessage = ''
this.plaintextValues = null
this.showPasswordDialog = true
this.currentField = item.fieldName
},
/**
* 验证密码并获取明文
*/
verifyPasswordAndGetPlaintext() {
const data = {
id: this.id,
field: this.currentField,
password: this.passwordValue,
poolId: this.poolId,
crmType: this.crmType
}
filedGetPlaintextAPI(data)
.then(res => {
this.plaintextValues = res.data
this.showPasswordDialog = false
this.showPlaintext = true
})
.catch(error => {
console.error('验证请求失败:', error)
this.errorMessage = error.msg
this.passwordValue = ''
})
}
}
}
@ -795,6 +891,9 @@ export default {
.form-item__edit{
display: inline;
}
.eye-icon{
display: inline;
}
}
&.is-desc_text {
@ -859,4 +958,21 @@ export default {
word-break: break-all;
}
}
.eye-icon {
cursor: pointer; /* 将鼠标光标变为手指形状 */
transition: filter 0.3s ease; /* 添加颜色变化的过渡动画,使效果更柔和 */
display: none;
}
.eye-icon:hover {
filter: brightness(1.2) saturate(150%) hue-rotate(380deg);
}
.error-message {
color: #f56c6c;
font-size: 12px;
margin-top: 8px;
}
.plaintext-field {
border-radius: 12px; /* 更大的圆角 */
overflow: hidden; /* 防止内容溢出圆角 */
}
</style>

View File

@ -70,18 +70,25 @@
show-overflow-tooltip />
<el-table-column
label="操作"
width="100">
width="260">
<template slot-scope="scope">
<flexbox justify="center">
<flexbox>
<span
v-if="contactsId == scope.row.contactsId"
v-if="contactsId === scope.row.contactsId"
class="chief">
<i class="wk wk-success" />首要联系人</span>
<i class="wk wk-success" />首要联系人
</span>
<el-button
v-else-if="!isSeas"
class="set-chief-btn"
type="text"
@click.native="setChieflyContacts(scope)">设为首要联系人</el-button>
@click.native="setChieflyContacts(scope)">设为首要联系人
</el-button>
<el-button
class="set-chief-btn"
type="text"
@click.native="viewPlaintext(scope.row.contactsId)">查看明文
</el-button>
</flexbox>
</template>
</el-table-column>
@ -97,6 +104,48 @@
crm-type="contacts"
@save-success="createSaveSuccess"
@close="isCreate=false" />
<!-- 密码验证对话框 -->
<el-dialog
:visible.sync="showPasswordDialog"
title="验证密码"
width="400px"
append-to-body>
<el-form>
<el-form-item label="密码">
<el-input
v-model="passwordValue"
type="password"
placeholder="请输入验证密码"
@keyup.enter.native="verifyPasswordAndGetPlaintext"
/>
</el-form-item>
</el-form>
<p v-if="errorMessage" class="error-message">{{ errorMessage }}</p>
<span slot="footer" class="dialog-footer">
<el-button @click="showPasswordDialog = false">取消</el-button>
<el-button :loading="verifying" type="primary" @click="verifyPasswordAndGetPlaintext">验证</el-button>
</span>
</el-dialog>
<!-- 明文弹框 -->
<el-dialog
:visible.sync="showPlaintext"
title="明文"
width="500px"
append-to-body>
<div class="plaintext-content">
<el-input
:autosize="{ minRows: 3, maxRows: 6 }"
:value="plaintextValues"
type="textarea"
readonly
class="plaintext-field"
/>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="showPlaintext = false">确定</el-button>
</span>
</el-dialog>
</div>
</template>
@ -114,6 +163,7 @@ import {
} from '@/api/crm/business'
import CrmRelative from '@/components/CreateCom/CrmRelative'
import { getPermissionByKey } from '@/utils'
import { filedGetPlaintextAPI } from '../../../api/crm/contacts'
export default {
name: 'RelativeContacts', //
@ -128,6 +178,8 @@ export default {
id: [String, Number],
// ID
contactsId: [String, Number],
// ID
selectedContactsId: [String, Number],
//
crmType: {
type: String,
@ -162,7 +214,12 @@ export default {
createActionInfo: { type: 'relative', crmType: this.crmType, data: {}},
//
showRelativeView: false,
selectionList: []
selectionList: [],
showPasswordDialog: false, //
errorMessage: '', //
plaintextValues: null, //
showPlaintext: false, //
passwordValue: '' //
}
},
inject: ['rootTabs'],
@ -362,6 +419,37 @@ export default {
this.$bus.emit('crm-tab-num-update')
})
}
},
/**
* 点击查看明文
*/
viewPlaintext(selectedContactsId) {
this.passwordValue = ''
this.errorMessage = ''
this.plaintextValues = null
this.showPasswordDialog = true
this.selectedContactsId = selectedContactsId
},
/**
* 验证密码并获取明文
*/
verifyPasswordAndGetPlaintext() {
const data = {
id: this.selectedContactsId,
password: this.passwordValue
}
console.log(data)
filedGetPlaintextAPI(data)
.then(res => {
this.plaintextValues = res.data
this.showPasswordDialog = false
this.showPlaintext = true
})
.catch(error => {
console.error('验证请求失败:', error)
this.errorMessage = error.msg
this.passwordValue = ''
})
}
}
}
@ -371,16 +459,24 @@ export default {
.set-chief-btn {
font-size: 12px;
padding: 4px 12px;
padding: 2px 10px;
width: 150px;
}
.chief {
font-size: 12px;
color: #333;
padding: 2px 10px;
i {
font-size: 14px;
color: #389e0b;
margin-right: 3px;
margin-right: 2px;
}
width: 150px;
}
.error-message {
color: #f56c6c;
font-size: 12px;
margin-top: 8px;
}
</style>