xw_admin/src/views/userCenter/user/bind.vue
2024-09-02 23:32:40 +08:00

526 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<el-card shadow="never" header="账户安全">
<div class="bindBxo">
<div class="title" style="margin-top: 5px;">账号绑定</div>
<div class="boxView">
<div class="leftImg">
<div class="leftIcon wechat" v-if="wechat.open_id=='' || wechat.avatar==''"><i class="el-icon"><sc-icon-WechartRound/></i></div>
<div class="leftIcon" v-else>
<el-avatar :size="38" :src="wechat.avatar" />
</div>
<div class="nameBox">
<div class="titleName nameRed" v-if="wechat.open_id==''">绑定微信</div>
<div class="titleName" v-else>{{wechat.nick_name==""?'-':wechat.nick_name}}</div>
<div class="msg">绑定微信,用于账号登录,客服咨询及其它消息提醒。</div>
</div>
</div>
<div class="rightBtn">
<el-button type="primary" :size="size" v-if="wechat.open_id==''" @click="bindWechat">绑 定</el-button>
<el-button type="primary" :size="size" v-else plain @click="secureDingTalk(1)">解 除</el-button>
</div>
</div>
<div class="boxView">
<div class="leftImg">
<div class="leftIcon alipay" v-if="dingTalk.open_id=='' || dingTalk.avatar==''"><i class="el-icon"><sc-icon-DingTalk/></i></div>
<div class="leftIcon" v-else>
<el-avatar :size="38" :src="dingTalk.avatar" />
</div>
<div class="nameBox">
<div class="titleName nameRed" v-if="dingTalk.open_id==''">绑定钉钉</div>
<div class="titleName" v-else>{{dingTalk.nick_name==""?'-':dingTalk.nick_name}}</div>
<div class="msg">绑定钉钉,用于账号登录。</div>
</div>
</div>
<div class="rightBtn">
<el-button type="primary" :size="size" v-if="dingTalk.open_id==''" @click="bindDingTalk">绑 定</el-button>
<el-button type="primary" :size="size" v-else plain @click="secureDingTalk(2)">解 除</el-button>
</div>
</div>
<div class="title">安全登录</div>
<div class="boxView boxViewCenter">
<div class="leftBox">允许多地登录</div>
<div class="rightBox">
<el-radio-group v-model="multipleLocation" @change="multipleLocationSet">
<el-radio :label="true">允许多地</el-radio>
<el-radio :label="false">限制单设备</el-radio>
</el-radio-group>
</div>
</div>
<div class="boxView boxViewCenter bandTime">
<div class="leftBox">系统超时安全自动退出时间</div>
<div class="rightBox">
<div class="boxInput">
<el-input onkeyup="value=value.replace(/[^\d]/g,'')" @change="loginTime" controls-position="right" v-model="timeout" placeholder="请输入时间" style="width: 260px">
<template #suffix>分钟</template>
</el-input>
</div>
<div class="tip">光标移出自动更新退出时间。</div>
</div>
</div>
<div class="title">OA能力</div>
<div class="boxView boxViewCenter">
<div class="leftBox">启用阿里企业邮箱功能</div>
<div class="rightBox">
<!-- v-model="multipleLocation" @change="multipleLocationSet"-->
<el-radio-group>
<el-radio :label="true">启用</el-radio>
<el-radio :label="false">禁用</el-radio>
</el-radio-group>
</div>
</div>
<div class="title">使用Passkey</div>
<div class="boxView passKeyView boxViewCenter">
<div class="nameBox passkeyView">
<!-- <el-button type="primary" :size="size" @click="createPasskey">添加passKey</el-button>-->
<div class="msg">借助 Passkey你可以使用自己的指纹、面孔、屏锁设置或实体安全密钥登录你的账号。请仅在你自有的设备上设置 Passkey。</div>
</div>
</div>
<div class="boxView passKeyView boxViewCenter">
<div class="itemMain">
<div class="boxCom" v-for="item in passKeyList" :key="item">
<i class="icon"><sc-icon-Fingerprint /></i>
<span class="name">
<el-input class="nameInput" v-model="item.alias" @change="passKeyAlias(item)" placeholder="请输入"></el-input>
</span>
</div>
<div class="boxCom" @click="createPasskey">
<span class="iconBack">
<i class="icon"><el-icon-Plus/></i>
</span>
<span class="name">添加</span>
</div>
</div>
</div>
</div>
</el-card>
<el-dialog
v-model="showWechatLogin"
title="微信绑定"
:width="500"
destroy-on-close
>
<div class="qrCodeLogin">
<div class="code_container">
<object :data="WechatLoginCode" width="430" height="430" type="text/html"></object>
</div>
<p class="error" v-if="bind_wechat_error!=''">{{bind_wechat_error}},请先解绑原账号绑定</p>
<p class="msg">
请使用微信扫一扫绑定
</p>
<div class="qrCodeLogin-result" v-if="isWechatLoginResult">
<el-result
icon="success"
title="绑定成功"
sub-title="您可以使用微信扫码登录了"
></el-result>
</div>
</div>
</el-dialog>
</template>
<script>
export default {
data() {
return {
size:'small',
wechat:{
avatar:'',
nick_name:'',
open_id:''
},
dingTalk:{
avatar:'',
nick_name:'',
open_id:'',
},
WechatLoginCode:'',
showWechatLogin:false,
isWechatLoginResult: false,
bind_wechat_error:'',
multipleLocation:false,
timeout:'10',
userInfo:{},
passKeyList:[],
}
},
created() {
// 监听缓存变化 addEventListener
window.addEventListener('storage',this.wechatStorageChange);
},
mounted() {
const userInfo = this.$TOOL.data.get("USER_INFO");
this.userInfo = userInfo;
this.getUserInfo();
this.getSso();
this.getTimeOut();
this.getPassKeyList();
// 获取新消息
// this.$socketApi.getSock(this.getWsResult);
},
beforeUnmount() {
window.removeEventListener('storage', this.wechatStorageChange);
},
watch:{
},
methods:{
async getTimeOut() {
const res = await this.$API.user.timeoutGet.post();
if (res.code == 200) {
this.timeout = res.data.options && res.data.options.timeout?res.data.options.timeout:this.timeout;
}
},
async loginTime(e) {
const res = await this.$API.user.timeoutConfig.post({timeout:e});
if (res.code == 200) {
this.$message.success('设置成功');
}
},
async getSso(){
const res = await this.$API.system.sso.get.post();
if(res.code == 200){
if(res.data.options){
this.multipleLocation = res.data.options.is_sso_login;
}
}
},
async multipleLocationSet(){
const res = await this.$API.system.sso.setup.post({is_sso_login:this.multipleLocation});
if(res.code == 200){
this.$message.success('配置成功');
}
},
getWsResult(res){
if(res.type == 7){
this.isWechatLoginResult = true;
setTimeout(()=>{
this.showWechatLogin = false;
this.getUserInfo();
},2000)
}
if(res.type == 12){
this.bind_wechat_error = res.msg;
}
},
async bindWechat(){
this.isWechatLoginResult = false;
const res = await this.$API.auth.bindWechat.post();
if(res.code == 200){
const url = JSON.parse(JSON.stringify(res.data.qrcode));
localStorage.setItem('bindWechat','2');
this.WechatLoginCode = url;
this.showWechatLogin = true;
}
},
async bindDingTalk(){
const res = await this.$API.auth.bindDingTalk.post();
if(res.code == 200){
const url = JSON.parse(JSON.stringify(res.data.redirect));
localStorage.setItem('bindDingTalk','2');
window.open(url);
}
},
wechatStorageChange(e){
let dingTalk = localStorage.getItem('bindDingTalk');
let wechat = localStorage.getItem('bindWechat');
if(e.key == 'DINGTALK_LOGIN_MESSAGE'){
if(dingTalk == 2 && typeof e.newValue =="string" && e.newValue!=''){
this.dingTalkBind(e.newValue)
}
localStorage.removeItem("DINGTALK_LOGIN_MESSAGE");
}
if(e.key == 'WECHAT_LOGIN_MESSAGE'){
if(wechat == 2 && typeof e.newValue =="string" && e.newValue!=''){
this.wechatBind(e.newValue);
}
localStorage.removeItem("WECHAT_LOGIN_MESSAGE");
}
},
wechatBind(){
},
async dingTalkBind(e){
let item = JSON.parse(e);
let params = {code:item.code,state:item.state}
const res = await this.$API.auth.bindDingTalkUser.post(params);
if(res.code == 200){
await this.getUserInfo()
}
},
async secureDingTalk(num){
let params = {app_type:num}
const res = await this.$API.auth.unbindUser.post(params);
if(res.code == 200){
await this.getUserInfo();
}
},
async getUserInfo(){
const res = await this.$API.user.getInfo.post();
if(res.code == 200){
this.wechat={
avatar:'',
nick_name:'',
open_id:''
}
this.dingTalk={
avatar:'',
nick_name:'',
open_id:''
}
if(res.data && res.data.length){
res.data.forEach(em=>{
if(em.app_type == 1){
this.wechat = em;
}
if(em.app_type ==2){
this.dingTalk = em;
}
})
}
}
},
// 获取指纹列表
async getPassKeyList() {
const res = await this.$API.system.user.publishList.post();
if(res.code == 200){
this.passKeyList = res.data;
}
},
// 修改指纹名称
async passKeyAlias(em) {
let params = {
id:em.id,
alias:em.alias
}
await this.$API.system.user.renameAlias.post(params);
},
// 创建通行秘钥
async createPasskey() {
try {
const res = await this.$API.system.user.generateRegistration.post();
const publicKey= {
challenge:Uint8Array.from(res.data.challenge),
rp:{
name:res.data.rp.name,
id:res.data.rp.id
},
user:{
id:Uint8Array.from(res.data.user.id),
name:res.data.user.name,
displayName:res.data.user.displayName,
},
pubKeyCredParams:res.data.pubKeyCredParams,
authenticatorSelection:{
authenticatorAttachment:'',
userVerification:res.data.authenticatorSelection.userVerification,
},
timeout:res.data.timeout,
attestation:res.data.attestation
};
const credential = await navigator.credentials.create({ publicKey });
await this.storeCredential(credential);
} catch (error) {
this.$message.warning('创建通行秘钥失败');
}
},
async storeCredential(credential){
const params = {
id: credential.id,
rawId: this.bufferToBase64URL(credential.rawId),
type: credential.type,
response: {
clientDataJSON: this.bufferToBase64URL(credential.response.clientDataJSON),
attestationObject: this.bufferToBase64URL(credential.response.attestationObject),
},
authenticatorAttachment: credential.authenticatorAttachment,
}
const res = await this.$API.system.user.verifyResponse.post(params);
if(res.code == 200){
await this.getPassKeyList();
}
},
bufferToBase64URL(buffer) {
return btoa(String.fromCharCode.apply(null, new Uint8Array(buffer)))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");
},
}
}
</script>
<style lang="scss" scoped>
.bindBxo{
padding: 0 10px;
.title{
font-size: 14px;
font-weight: 500;
margin: 25px 0 10px 0;
}
}
.boxView{
width: 460px;
display: flex;
align-items: end;
justify-content: space-between;
margin-top: 10px;
margin-left: 5px;
.leftImg{
display: flex;
align-items: center;
.leftIcon{
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-items: center;
margin-right: 10px;
.el-icon{
font-size: 34px;
}
}
.titleName{
margin-bottom: 5px;
font-size: var(--el-font-size-base);
}
.nameRed{
color: var(--el-color-danger);
}
.msg{
font-size: var(--el-font-size-extra-small);
color: #837e7e;
}
}
}
.passKeyView{
width: 100%;
.itemMain{
width: 100%;
display: flex;
flex-direction: row;
align-items: flex-start;
margin-top: 10px;
.boxCom{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 80px;
cursor: pointer;
.icon{
text-align: center;
width: 40px;
height: 40px;
}
.iconBack{
width: 40px;
height: 40px;
border-radius: 50%;
background: #E9E9E9;
font-size: 14px;
display: flow;
align-items: center;
justify-content: center;
padding: 10px;
.icon{
width: 14px;height: 14px;font-size: 12px;
}
}
.name{
height: 30px;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
color: var(--el-input-text-color);
}
.nameInput{
text-align: center;
}
.el-input{
text-align: center;
border: 0;
::v-deep .el-input__wrapper{
border: 0;
padding: 0;
box-shadow:none;
}
::v-deep .el-input__inner{
text-align: center;
}
}
.el-input:hover ::v-deep .el-input__wrapper{
//border-bottom: 1px solid var(--el-input-border-color);
border-radius: 0;
}
}
}
}
.boxViewCenter{
align-items: center;
.leftBox{
font-size: var(--el-font-size-base);
}
}
.bandTime{
align-items: baseline;
.tip{
margin-top: 6px;
color: #999;
}
}
.qrCodeLogin {
text-align: center;
position: relative;
padding: 0 0 20px 0;
}
.code_container{
width: 430px;
height:430px;
margin: 0 auto;
}
.qrCodeLogin img.qrCode {
background: #fff;
padding: 20px;
border-radius: 10px;
}
.qrCodeLogin .error{
color: var(--el-color-error);
}
.qrCodeLogin p.msg {
margin-top: 15px;
}
.qrCodeLogin .qrCodeLogin-result {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
text-align: center;
background: var(--el-mask-color);
display: flex;
flex-direction: column;
justify-content: center;
}
.passkeyView{
.msg{
margin-top: 10px;
color: var(--el-color-warning);
}
}
</style>