完善个人信息配置

This commit is contained in:
龙运模
2024-06-28 13:56:53 +08:00
parent f71caee898
commit 211e4c70b1
15 changed files with 818 additions and 73 deletions
+26 -30
View File
@@ -7,7 +7,7 @@
<el-avatar :size="70" src="img/avatar.jpg"></el-avatar>
<div class="userView">
<h2 class="name">{{ user.userName }}</h2>
<p><el-tag effect="dark" round size="large" disable-transitions>{{ user.role }}</el-tag></p>
<p><el-tag round effect="plain" size="small" disable-transitions>{{ user.role }}</el-tag></p>
</div>
</div>
</el-header>
@@ -50,65 +50,61 @@
password: defineAsyncComponent(() => import('./user/password')),
space: defineAsyncComponent(() => import('./user/space')),
logs: defineAsyncComponent(() => import('./user/logs')),
upToEnterprise: defineAsyncComponent(() => import('./user/upToEnterprise'))
upToEnterprise: defineAsyncComponent(() => import('./user/upToEnterprise')),
bind: defineAsyncComponent(() => import('./user/bind')),
},
data() {
return {
menu: [
{
groupName: "基本设置",
groupName: "账号设置",
list: [
{
icon: "el-icon-postcard",
icon: "sc-icon-Account",
title: "账号信息",
component: "account"
},
{
icon: "el-icon-operation",
title: "个人设置",
component: "seting"
},
{
icon: "el-icon-lock",
icon: "sc-icon-Password",
title: "密码",
component: "password"
},
{
icon: "el-icon-bell",
icon: "sc-icon-Notice",
title: "通知设置",
component: "pushSettings"
}
},
{
icon: "sc-icon-Secure",
title: "账户安全",
component: "bind"
},
]
},
{
groupName: "数据管理",
list: [
{
icon: "el-icon-coin",
title: "存储空间信息",
component: "space"
},
{
icon: "el-icon-clock",
icon: "sc-icon-UserLog",
title: "操作日志",
component: "logs"
}
]
},
{
groupName: "账号升级",
list: [
{
icon: "el-icon-office-building",
title: "升级为企业账号",
component: "upToEnterprise"
}
]
}
// {
// groupName: "账号升级",
// list: [
// {
// icon: "el-icon-office-building",
// title: "升级为企业账号",
// component: "upToEnterprise"
// }
// ]
// }
],
user: {
userName: "Sakuya",
role: "超级管理员",
userName: "龙隆",
role: "管理员",
},
page: "account"
}
+10 -12
View File
@@ -1,21 +1,18 @@
<template>
<el-alert title="异步组件动态加载使用了正处于试验阶段的<Suspense>组件, 其API和使用方式可能会改变. <Suspense> is an experimental feature and its API will likely change." type="warning" show-icon style="margin-bottom: 15px;"/>
<el-card shadow="never" header="个人信息">
<el-form ref="form" :model="form" label-width="120px" style="margin-top:20px;">
<el-card shadow="never" header="账户信息">
<el-form ref="form" :model="form" label-width="90px" style="margin-top:10px;width: 518px;">
<el-form-item label="账号">
<el-input v-model="form.user" disabled></el-input>
<div class="el-form-item-msg">账号信息用于登录,系统不允许修改</div>
</el-form-item>
<el-form-item label="姓名">
<el-form-item label="手机号码">
<el-input v-model="form.mobile"></el-input>
</el-form-item>
<el-form-item label="真实姓名">
<el-input v-model="form.name"></el-input>
</el-form-item>
<el-form-item label="性别">
<el-select v-model="form.sex" placeholder="请选择">
<el-option label="保密" value="0"></el-option>
<el-option label="男" value="1"></el-option>
<el-option label="女" value="2"></el-option>
</el-select>
<el-form-item label="邮箱地址">
<el-input v-model="form.email"></el-input>
</el-form-item>
<el-form-item label="个性签名">
<el-input v-model="form.about" type="textarea"></el-input>
@@ -34,7 +31,8 @@
form: {
user: "administrator@scuiadmin.com",
name: "Sakuya",
sex: "0",
mobile:'18828382888',
email:'',
about: "正所谓富贵险中求"
}
}
+398
View File
@@ -0,0 +1,398 @@
<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" round v-if="wechat.open_id==''" @click="bindWechat">绑定</el-button>
<el-button type="primary" :size="size" round 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" round v-if="dingTalk.open_id==''" @click="bindDingTalk">绑定</el-button>
<el-button type="primary" :size="size" round 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="title">OA能力</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="title">使用Passkey</div>
<div class="boxView boxViewCenter">
<div class="nameBox passkeyView">
<el-button type="primary" round :size="size" @click="createPasskey">添加指纹</el-button>
<div class="msg">借助 Passkey,你可以使用自己的指纹、面孔、屏锁设置或实体安全密钥登录你的账号。请仅在你自有的设备上设置 Passkey。</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,
userInfo:{},
}
},
created() {
// 监听缓存变化 addEventListener
window.addEventListener('storage',this.wechatStorageChange);
},
mounted() {
const userInfo = this.$TOOL.data.get("USER_INFO");
this.userInfo = userInfo;
// this.getUserInfo();
// this.getSso();
// 获取新消息
// this.$socketApi.getSock(this.getWsResult);
},
beforeUnmount() {
window.removeEventListener('storage', this.wechatStorageChange);
},
watch:{
},
methods:{
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 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);
console.log(res,55)
// this.$message.success('通行秘钥创建成功');
},
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;
}
}
}
.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>
+24 -6
View File
@@ -1,15 +1,33 @@
<template>
<el-card shadow="never" header="修改密码">
<el-alert title="密码更新成功后,您将被重定向到登录页面,您可以使用新密码重新登录。" type="info" show-icon style="margin-bottom: 15px;"/>
<el-form ref="form" :model="form" :rules="rules" label-width="120px" style="margin-top:20px;">
<el-alert title="密码更新成功后,您将被重定向到登录页面,您可以使用新密码重新登录。" type="error" show-icon style="margin-bottom: 15px;"/>
<el-form ref="form" :model="form" :rules="rules" label-width="120px" style="margin-top:20px;width: 518px;">
<el-form-item label="当前密码" prop="userPassword">
<el-input v-model="form.userPassword" type="password" show-password placeholder="请输入当前密码"></el-input>
<div class="el-form-item-msg">必须提供当前登录用户密码才能进行更改</div>
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<el-input v-model="form.newPassword" type="password" show-password placeholder="请输入新密码"></el-input>
<sc-password-strength v-model="form.newPassword"></sc-password-strength>
<div class="el-form-item-msg">请输入包含英文、数字的8位以上密码</div>
<el-popover
class="popoverPassword"
placement="right"
title=""
:width="360"
trigger="click"
content=""
>
<template #reference>
<el-input v-model="form.newPassword" type="password" show-password placeholder="请输入新密码"></el-input>
</template>
<template #default>
<div class="passwordView">
<div class="title">
<div class="name">安全程度等级:</div>
<div class="strength"><sc-password-strength v-model="form.newPassword"></sc-password-strength></div>
</div>
<div class="text"><div class="icon"></div>6-20位字符</div>
<div class="text"><div class="icon"></div>只能包含大小写字母、数字和标点符号(除空格)</div>
</div>
</template>
</el-popover>
</el-form-item>
<el-form-item label="确认新密码" prop="confirmNewPassword">
<el-input v-model="form.confirmNewPassword" type="password" show-password placeholder="请再次输入新密码"></el-input>
+181 -19
View File
@@ -1,30 +1,192 @@
<template>
<el-card shadow="never" header="事务待办">
<el-form ref="form" label-width="180px" label-position="left" style="margin-top:20px;">
<el-form-item label="有新的待办">
<el-checkbox v-model="form.new">短信推送</el-checkbox>
<el-checkbox v-model="form.new_wx">微信推送</el-checkbox>
</el-form-item>
<el-form-item label="待办有效时剩24小时">
<el-checkbox v-model="form.timeout">短信推送</el-checkbox>
<el-checkbox v-model="form.timeout_wx">微信推送</el-checkbox>
</el-form-item>
</el-form>
<el-card shadow="never" header="">
<template #header>
<div class="card-header-box">
<div class="card-header">
<span>事务待办通知设置</span>
</div>
<div class="card-btn">
<el-button size="small" type="primary" @click="save">保存</el-button>
</div>
</div>
</template>
<div class="pushSetting" v-loading="loading" element-loading-text="努力加载中...">
<div class="headerNav">
<div class="item">通知事项</div>
<div class="item center" v-for="(item,index) in subscription_items" :key="index">
{{item.label}}
<template v-if="item.value==1">
<el-tooltip
class="box-item"
effect="dark"
content=""
placement="top-start"
>
<template #content>
<div>一、第一步账户安全里绑定微信号</div>
<div>二、第二步勾选想要接收的通知</div>
</template>
<span> <i class="sc-icon"><el-icon-QuestionFilled /></i></span>
</el-tooltip>
</template>
<template v-else-if="item.value==3">
<el-tooltip
class="box-item"
effect="dark"
content=""
placement="top-start"
>
<template #content>
<div>一、账号信息里绑定手机号</div>
<div>二、第二步勾选想要接收的通知</div>
</template>
<span><i class="sc-icon"><el-icon-QuestionFilled /></i></span>
</el-tooltip>
</template>
</div>
</div>
<div class="middleView">
<el-scrollbar height="100%">
<div class="listView" v-for="(item,index) in check_msg_temp" :key="index">
<div class="item">{{item.value}}</div>
<div class="item center" v-for="(em,ind) in item.subscription_items" :key="ind">
<el-checkbox :value="em.value" v-model="em.subscribed"></el-checkbox>
</div>
</div>
</el-scrollbar>
</div>
</div>
</el-card>
</template>
<script>
export default {
data() {
return {
form: {
new: true,
timeout: true
}
export default {
data() {
return {
subscription_items:[],
check_msg_temp:[],
loading:false,
};
},
mounted() {
// this.getList(true);
},
beforeUnmount() {
},
methods: {
async getList(isShow) {
if(isShow){
this.loading = true;
}
const res = await this.$API.user.subscribe.list.post();
if(res.code == 200){
this.subscription_items = res.data.subscription_items;
this.check_msg_temp = res.data.check_msg_temp;
this.loading = false;
}
},
async save() {
let params = {
temps:this.setList()
};
const res = await this.$API.user.subscribe.set.post(params);
if (res.code == 200) {
this.$message.success('保存成功');
await this.getList();
}
},
setList(){
let list = JSON.parse(JSON.stringify(this.check_msg_temp));
let temps = [];
if(list.length>0){
list.forEach(item=>{
let obj = {
notice_item:item.notice_item,
value:item.value,
subscription_items:[]
}
if(item.subscription_items.length>0){
item.subscription_items.forEach(em=>{
if(em.subscribed){
obj.subscription_items.push(em.value);
}
})
}
if(obj.subscription_items.length>0){
temps.push(obj)
}
})
}
return temps
}
}
};
</script>
<style>
<style scoped lang="scss">
.card-header-box{
display: flex;
align-items: center;
justify-content: space-between;
}
.pushSetting{
padding: 0 0 15px 0;
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
.headerNav{
padding: 10px 10px;
display: flex;
background: #F5F7FA;
.item{
flex: 1;
}
.center{
display: flex;
justify-content: center;
cursor: pointer;
.el-tooltip__trigger{
display: flex;
align-items: center;
}
}
.sc-icon{
display: inline-block;
width: 16px;
height: 16px;
margin-left: 6px;
color: var(--el-color-warning);
}
}
.middleView{
display: flex;
flex-direction: column;
padding: 10px 10px;
flex: 1;
overflow: hidden;
.listView{
display: flex;
}
.item{
flex: 1;
padding: 0 0;
display: flex;
align-items: center;
}
.center{
display: flex;
justify-content: center;
}
}
}
.dark{
.pushSetting .headerNav{
background: #313131;
}
}
</style>
+1 -2
View File
@@ -16,8 +16,7 @@
<div class="el-form-item-msg">{{ $t('user.language_msg') }}</div>
</el-form-item>
</el-form>
</el-card>
<el-card shadow="never" header="个人设置" style="margin-top:20px;">
<el-form ref="form" label-width="120px" style="margin-top:20px;">
<el-form-item label="自动登出">
<el-select v-model="config.autoExit">