GitBucket
4.6.0
Toggle navigation
Sign in
Files
Branches
1
Tags
Issues
Pull Requests
Labels
Milestones
Wiki
08335
/
hivui-platform-template
hivui平台项目模板
Browse code
@中文描述: 登录配置化优化
@版本号: @升级登记:1 @同步后端:0 @同步数据库:0 @同步配置:0 @向下兼容:1
master
1 parent
f3215d6
commit
33e793bedde29391f7877b36241202f8d1f8cfcd
ganyi
authored
8 days ago
Showing
1 changed file
project/hivuiLogin/components/LoginPanel.vue
Ignore Space
Show notes
View
project/hivuiLogin/components/LoginPanel.vue
<template> <div :class="'login_form' + ((captchaObj&&captchaObj.captchaId)?' showCaptcha':'')" v-if="config.isPwdLogin || config.isScan"> <!-- 用户名密码窗口 --> <div id="showQr" class="loginMainBox" :hidden="!(config.isPwdLogin && currLoginType == 'com')"> <img @click="changeQr" class="icon-qrcode" :src="scanIcon" :hidden="!config.isScan" /> <div class="login-tip" :hidden="!config.isScan"> <div class="poptip"> <div class="poptip-arrow"> <em></em> <span></span> </div> <div class="poptip-content">{{$t('hivuiLogin_main_scan_login')}}</div> </div> </div> <div class="login_form_bd"> <div class="error_tips"></div> <input type="hidden" name="directAction" value="" /> <input type="hidden" name="refererurl" value="" /> <div class="login_field"> <label>{{$t('hivuiLogin_main_user')}}</label> <input class="login_field_text" v-model="username" type="text" id="txtUserName" name="userName" :placeholder="$t('hivuiLogin_main_user_ph')" autofocus required autocomplete="off" @keyup.enter="loginFunc" :readonly="usernameReadonly" /> <i class="fa fa-user"></i> </div> <div class="login_field"> <label>{{$t('hivuiLogin_main_pw')}}</label> <input class="login_field_text" v-model="password" type="text" onfocus="this.type='password'" id="txtPassword" name="password" :placeholder="$t('hivuiLogin_main_pw_ph')" required autocomplete="off" @keyup.enter="loginFunc" /> <i class="fa fa-unlock-alt"></i> </div> <div class="login_field captcha_field" v-if="captchaObj&&captchaObj.captchaId"> <label>{{$t('hivuiLogin_main_captcha')}}</label> <div style="display:flex;align-items:center;"> <input class="login_field_text" v-model="loginCaptcha" type="text" id="loginCaptcha" name="captcha" :placeholder="$t('hivuiLogin_main_captcha_ph')" autofocus required autocomplete="off" @keyup.enter="loginFunc" style="width:50%;" /> <img :src="captchaObj.imageData" @click="getCaptchaPicObj"> </div> </div> <div class="saveCheckbox_box" v-if="!miniLoginInfo"> <label for="saveCheckbox" ><i :class=" 'fa ' + (isCheckRu ? 'fa-check-square-o' : 'fa-square-o') " ></i >{{$t('hivuiLogin_main_save_user')}}</label > <input type="checkbox" id="saveCheckbox" style="display: none" v-model="isCheckRu" @change="rememberUserEvent" /> <span class="forgetPw" @click="showResetPw" ><i class="fa fa-lock"></i ><span style="margin-left: 5px">{{$t('hivuiLogin_main_forget_user')}}</span></span > </div> <div class="login_form_btn"> <div class="login_btn_area"> <button :style="btnStyle" :class="'loginBtn' + (loginLoading ? ' disabled' : '')" @click="loginFunc" > {{$t('hivuiLogin_main_login_btn')}}<i :hidden="!loginLoading" class="el-icon-loading" ></i> </button> </div> </div> <div class="login_form_bottom" v-if="!miniLoginInfo"> <div class="register_tips" :hidden="config.hideRegister"> {{$t('hivuiLogin_main_register1')}} <span @click="goRegisterPage">{{$t('hivuiLogin_main_register2')}}</span> </div> <el-dropdown @command="changeLang" class="langDropdown" :hidden="!config.showChangeLangBtn"> <span class="el-dropdown-link"> {{currLangDesc}}<i class="el-icon-arrow-down el-icon--right"></i> </span> <el-dropdown-menu slot="dropdown"> <template v-for="(item,index) in langList"> <el-dropdown-item :key="'langList'+index" :command="item.name+','+item.key">{{item.name}}</el-dropdown-item> </template> </el-dropdown-menu> </el-dropdown> </div> <el-popover v-if="config.appQRcode && !miniLoginInfo" class="login_form_appqr" placement="bottom" width="210" popper-class="appQRPopper"> <div ref="appQRcode" class="login_download_app"> <canvas v-if="!config.appQRcode.startsWith('imgUrl:')"></canvas> <img v-else :src="config.appQRcode.replace('imgUrl:','')"> </div> <span slot="reference" class="login_form_app" :title="$t('hivuiLogin_main_appdownload')"><i class="miniIcon" data-type="app"></i>{{$t('hivuiLogin_main_download')}}</span> </el-popover> </div> </div> <!-- 扫码窗口 --> <div id="showCom" class="loginMainBox" :hidden="!(config.isScan && currLoginType == 'scan')" > <div class="login_form_hd"> <h3>{{$t('hivuiLogin_main_scan_login')}}</h3> <img @click="changeCom" class="icon-qrcode" :src="comIcon" alt="" :hidden="!config.isPwdLogin" /> <div class="login-tip" :hidden="!config.isPwdLogin"> <div class="poptip"> <div class="poptip-arrow"> <em></em> <span></span> </div> <div class="poptip-content">{{$t('hivuiLogin_main_pw_text')}}</div> </div> </div> </div> <!-- 扫码界面 --> <div class="scan-page"> <!-- 初始状态 --> <div class="state default" v-if="scanState ==-1"> <div class="qr"> <img ref="scanQrCode" :src="scanQrCode" :load="scanLoading"/> </div> <div class="scan-info"> <div class="countdown"> <span>{{countdown}}</span> s </div> <div class="scan-desc"> <div> <img :src="saomaIcon"/> </div> <div class="right"> <span> {{$t('hivuiLogin_main_app_text1')}} <a style="color: #06c">{{config.appName}}</a><br /> {{$t('hivuiLogin_main_app_text3')}} </span> </div> </div> </div> </div> <!-- 扫码中 --> <div class="state scanning" v-else-if="scanState == 0"> <div class="qr"> <img ref="scanQrCode" :src="scanQrCode" :load="scanLoading" /> <div class="loading-bg" v-loading="true"> </div> </div> <div class="scan-info"> <div class="scan-desc"> <div style="text-align:center;"> <span style="font-size:16px;color: #06c;">正在扫码...</span><br /> <span>请在手机上确认登录</span> </div> </div> </div> </div> <!-- 二维码错误 --> <div class="state expired" v-else> <div class="qr"> <img ref="scanQrCode" :src="scanQrCode" :load="scanLoading" /> <div class="loading-bg opacity"> <div class="loading-text"> <i class="el-icon-warning"></i> <span >{{scanErrorMsg}}</span> <el-button type="text" @click="refreshScanQrCode">点击刷新</el-button> </div> </div> </div> <div class="scan-info"> <div class="scan-desc"> <div> <img :src="saomaIcon"/> </div> <div class="right"> <span> {{$t('hivuiLogin_main_app_text1')}} <a style="color: #06c">{{config.appName}}</a><br /> {{$t('hivuiLogin_main_app_text3')}} </span> </div> </div> </div> </div> </div> </div> <div id="qrcode"></div> <!-- 重置密码 --> <el-dialog :title="$t('hivuiLogin_main_forget_title')" :visible.sync="isShowResetPw" width="400px" :close-on-click-modal="false" @close="cancelResetPw" :modal-append-to-body="false"> <resetPw res="resetPw" @resetCb="cancelResetPw"></resetPw> </el-dialog> <!--选择岗位 --> <el-dialog :title="$t('hivuiLogin_main_bz_title')" :visible.sync="isShowUserBz" width="300px" top="40vh" :close-on-click-modal="false" @close="cancelUserBz" :modal-append-to-body="false"> <div class="seldUserBzDialog"> <el-select v-model="seldUserBz" :placeholder="$t('hivuiLogin_main_bz_placeholder')" @change="changeUserBz"> <el-option v-for="item in userBzList" :key="item.fbzid" :label="item.fbzname+'/'+item.fbzid" :value="item.fbzid"> </el-option> </el-select> </div> </el-dialog> <!-- 管理员解锁 --> <el-dialog :title="$t('hivuiLogin_main_admin_forget_title')" :visible.sync="isShowAdminResetPw" width="430px" :close-on-click-modal="false" @close="cancelAdminResetPw" :modal-append-to-body="false"> <div class="unlock_field"> <label>{{$t('hivuiLogin_main_pw')}}</label> <div class="unlock_field_input"> <input ref="aaaa" class="unlock_field_text" v-model="unlockPassword" type="password" name="unlockPassword" :placeholder="$t('hivuiLogin_main_pw_ph')" autocomplete="off" @keyup.enter="adminUnlock" /> <i class="fa fa-unlock-alt"></i> </div> </div> <div class="unlock_field" v-if="captchaObj2&&captchaObj2.captchaId"> <label>{{$t('hivuiLogin_main_captcha')}}</label> <div class="unlock_field_input"> <input class="unlock_field_text" v-model="unlockCaptcha" type="text" name="unlockCaptcha" :placeholder="$t('hivuiLogin_main_captcha_ph')" autocomplete="off" @keyup.enter="adminUnlock" style="width:50%;" /> <i class="fa fa-unlock-alt"></i> <img :src="captchaObj2.imageData" @click="getCaptchaPicObj(2)"> </div> </div> <span slot="footer" class="dialog-footer"> <el-button size="small" type="primary" @click="adminUnlock">{{$t('hivuiLogin_main_admin_unlock')}}</el-button> </span> </el-dialog> </div> </template> <script> import { baseLogin,getScanQrCode,adminUnlock } from "../api/login"; import { getBzList,changeBz,getCaptchaPic } from "../api/user"; import md5 from "js-md5"; import Cookies from 'js-cookie'; import QRCode from 'qrcode'; import { utils } from "hi-ui"; import { projectName } from "../config"; import WebScoketManager from '../utils/websocket.js' import "font-awesome/css/font-awesome.css"; import comIcon from "../assets/computer2.png" import scanIcon from "../assets/qrcode2.png" import saomaIcon from "../assets/saoma2.png" import _loginLogoImg from "../assets/Logo1.png" import _loginBgImg from "../assets/background.png" import { getToken, setToken, removeToken, getUserId, setUserId, removeUserId, } from "../utils/auth.js"; import {setUrlValue,getUrlValue} from "../utils/index.js"; import resetPw from "../components/resetPw.vue"; export default { name: "LoginPanel", components: {resetPw}, props: { miniLoginInfo:{ type: Object, default: null, } }, data() { return { comIcon: comIcon, scanIcon: scanIcon, saomaIcon: saomaIcon, username: "", usernameReadonly: false, password: "", //登录输入验证码 loginCaptcha:"", config: { title: window.customSysCofig?.loginTitle||this.$t('hivuiLogin_main_def_title'), loginLogoImg: window.customSysCofig?.loginLogo||_loginLogoImg, loginLogoImg_height: window.customSysCofig?.loginLogo_h||56, loginLogoImg_width: window.customSysCofig?.loginLogo_w||206, loginBgImg: window.customSysCofig?.loginBgImg||_loginBgImg, copyright: window.customSysCofig?.copyright||this.$t('hivuiLogin_main_def_copyright'), isScan: window.customSysCofig?.isScan||false, isPwdLogin: window.customSysCofig?.isPwdLogin === false ? false : true, mainColor:window.customSysCofig?.mainColor||"#06c", appQRcode:window.customSysCofig?.appQRcode||"", hideRegister:window.customSysCofig?.hideRegister||false, showChangeLangBtn:window.customSysCofig?.showChangeLangBtn||false, appName:window.customSysCofig?.appName||this.$t('hivuiLogin_main_app_text2'), }, currLoginType: "com", rememberUserId: getUserId(), loginLoading: false, isShowResetPw:false, isShowAdminResetPw:false, //是否显示选择登录岗位弹窗 isShowUserBz:false, //已选用户岗位 seldUserBz:"", //用户岗位数据列表 userBzList:[], //扫码Socket scanWS:null, //扫码UUID scanUUID:"", //登录二维码 scanQrCode:"", //二维码loading scanLoading:false, //扫码报错信息 scanErrorMsg:"", //当前扫码登录状态 二维码状态(type):-1-待机 0-扫码中 1-扫码取消 2-登录中 3-已失效 4-已使用 5-登录成功 6-登录失败 scanState:-1, //解锁密码 unlockPassword:"", //解锁输入验证码 unlockCaptcha:"", //验证码对象 captchaObj:null, //解锁验证码对象 captchaObj2:null, // 倒计时相关 countdown: 60, countdownTimer: null, isCounting: false }; }, computed: { btnStyle(){ return { "background-color": this.config.mainColor, }; }, isCheckRu() { return !!this.rememberUserId; }, currLangDesc(){ return window.localStorage.getItem("locale")?(JSON.parse(window.localStorage.getItem("locale")).desc):"中文"; }, langList(){ let arr=[ { name:"中文", key:"zh-CN", }, { name:"English", key:"en", }, ] if(window.customSysCofig?.addLangList?.length){ window.customSysCofig?.addLangList.forEach((item,index)=>{ arr.push({ name:item.name, key:item.key, }); }); } return arr; } }, async created() { let me=this; if(!me.config.isPwdLogin && me.config.isScan){ //获取扫码登录二维码 this.changeQr(); } //获取验证码图片 await me.getCaptchaPicObj(); }, mounted() { if(this.miniLoginInfo){ this.username=this.miniLoginInfo.username||""; this.usernameReadonly= !!(this.miniLoginInfo.questType=='ajax'&&this.miniLoginInfo.oldUserName); }else{ if (this.isCheckRu) { this.username = this.rememberUserId; } if(!window.localStorage.getItem("locale")){ window.localStorage.setItem("locale",JSON.stringify({ desc:"中文", name:"zh-CN", })); Cookies.set("locale","zh-CN"); } this.$refs.scanQrCode.onload = function() { // 清理创建的临时链接 //URL.revokeObjectURL(imageUrl); } //生成二维码 if(this.config.appQRcode){ let isIMG=this.config.appQRcode.startsWith('imgUrl:'); if(this.config.appQRcode.indexOf('http')==-1){ this.config.appQRcode=(isIMG?'imgUrl:':'')+location.origin+this.config.appQRcode.replace('imgUrl:',''); } if(!isIMG){ let targetUrl=this.config.appQRcode; if(!window._global){//正式环境 targetUrl=utils.string.setUrlValue(targetUrl,"pn",window.HIVUI_SETTING.projectName); } console.log("测试str",targetUrl); this.createDLQrCode(this.$refs.appQRcode.querySelector("canvas"),targetUrl); } } } }, beforeDestroy() { // 组件销毁前清除定时器 this.stopCountdown(); }, methods: { // 开始倒计时 startCountdown() { // 如果已经在倒计时,则不重复启动 if (this.isCounting) return; this.isCounting = true; this.countdown = 60; // 重置时间为60秒 this.countdownTimer = setInterval(() => { this.countdown--; // 当倒计时结束 if (this.countdown <= 0) { this.finishCountdown(); } }, 1000); }, // 倒计时结束处理 finishCountdown() { this.stopCountdown(); // 设置为失效状态 this.setScanState({type:3,msg:"二维码已失效"}) }, // 停止倒计时 stopCountdown() { if (this.countdownTimer) { clearInterval(this.countdownTimer); this.countdownTimer = null; } this.isCounting = false; }, // 重置倒计时 resetCountdown() { this.stopCountdown(); this.countdown = 60; this.$nextTick(() => { this.startCountdown(); }); }, loginFunc() { this.defaultLogin(); }, defaultLogin() { let me=this; if(me.captchaObj && me.captchaObj.captchaId && !me.loginCaptcha.trim()){ me.$message.error(me.$t('hivuiLogin_main_valid_captcha')); return; }else if(!me.username.trim()){ me.$message.error(me.$t('hivuiLogin_main_valid_user')); return; }else if(!me.password){ me.$message.error(me.$t('hivuiLogin_main_valid_pw')); return; } removeToken(); me.loginLoading = true; let _params={ username: me.username.trim(), password: md5(me.password), }; if(me.loginCaptcha && me.captchaObj && me.captchaObj.captchaId){ _params.captchaId=me.captchaObj.captchaId; _params.captcha=me.loginCaptcha; } baseLogin(_params) .then((response) => { const data = response; setToken(data.token); if (me.isCheckRu) { setUserId(me.username); } else { removeUserId(); } if(this.miniLoginInfo){ me.$emit("loginSuccess", data); }else{ if(data.isAuthorize===false){ if(data.authorizeMsg){ me.$message.warning(data.authorizeMsg); } this.$router.push("/authorize"); }else if(window.customSysCofig?.isSelectUserBz){ getBzList().then((res)=>{ me.userBzList=res.dataPack; me.isShowUserBz=true; }); }else{ me.$emit("loginSuccess", data); } } }) .catch((error) => { me.loginLoading = false; if(me.captchaObj && me.captchaObj.captchaId && error.status==500){ me.getCaptchaPicObj(); } if(me.username.trim()=="admin" && error&&error.dataPack&&error.dataPack.lock){ me.isShowAdminResetPw=true; //获取验证码图片 me.getCaptchaPicObj(2); } }); }, //关闭选岗弹窗 cancelUserBz(){ this.isShowUserBz=false; this.loginLoading=false; }, //选择岗位 changeUserBz(val){ changeBz({ bzid:val }).then((res)=>{ this.$emit("loginSuccess"); }); }, changeQr() { this.currLoginType = "scan"; this.getScanQrCode(); }, changeCom() { this.currLoginType = "com"; }, showResetPw() { this.isShowResetPw=true; }, cancelResetPw() { this.isShowResetPw=false; }, cancelAdminResetPw(){ this.isShowAdminResetPw=false; }, rememberUserEvent() { if (this.isCheckRu) { this.rememberUserId = ""; } else { this.rememberUserId = true; } }, goRegisterPage(){ this.$router.push("/register"); }, changeLang(command){ let dataArr=command.split(","); window.localStorage.setItem("locale",JSON.stringify({ desc:dataArr[0], name:dataArr[1], })); Cookies.set("locale",dataArr[1]); window.location.reload(); }, //生成下载二维码 createDLQrCode(target,text){ QRCode.toCanvas(target, text, function (error) { // 将文本转化为二维码并渲染到canvas上 if (error) { console.error(error); } }); }, //解锁 adminUnlock(){ let me=this; if(!me.unlockPassword){ me.$message.error(me.$t('hivuiLogin_main_valid_pw')); return; } let _params={ userId: me.username.trim(), password: md5(me.unlockPassword) }; if(me.unlockCaptcha && me.captchaObj2 && me.captchaObj2.captchaId){ _params.captchaId=me.captchaObj2.captchaId; _params.captcha=me.unlockCaptcha; } adminUnlock(_params).then((response)=>{ if(response.status==200){ me.isShowAdminResetPw=false; if(!response.msg){ me.$message.error(me.$t('hivuiLogin_main_unlock_success')); } me.password=me.unlockPassword; if(!me.captchaObj || !me.captchaObj.captchaId){ me.loginFunc(); }else{ me.loginCaptcha=""; } } }).catch((err)=>{ if(me.captchaObj2 && me.captchaObj2.captchaId && err.status==500){ me.getCaptchaPicObj(2); }else{ me.isShowAdminResetPw=false; } }); }, //获取验证码 getCaptchaPicObj(num){//num=2:管理员解锁时验证码 let me=this; getCaptchaPic({ type:"login" }).then((res)=>{ if(res&&res.dataPack){ if(num==2){ me.captchaObj2=res.dataPack; }else{ me.captchaObj=res.dataPack; } } }); }, scanInit(url,params) { let me=this; return getScanQrCode(url,params).then((res)=>{ me.scanLoading=false; if(res.headers.uuid){ me.scanUUID=atob(res.headers.uuid); console.log("uuid:",me.scanUUID); } const blob = new Blob([res.data], { type: 'image/jpeg' }); const imageUrl = URL.createObjectURL(blob); me.scanQrCode=imageUrl; me.resetCountdown(); if(!url){ me.createScanSocket(); } return Promise.resolve(res); }).catch((error)=>{ me.scanLoading=false; this.setScanState({ type:6, msg:"获取二维码失败" }); return Promise.reject(error); }); }, //获取扫码登录的二维码 getScanQrCode(){ let me=this; me.scanLoading=true; me.scanState=-1; if(me.scanWS){ me.scanWS.end(); } const scanInit = window.customSysCofig?.loginView?.scanInit; if(scanInit){ scanInit({ setScanQrCode:this.scanInit, setToken:setToken, setScanState:this.setScanState, createScanSocket:this.createScanSocket, }) }else{ me.scanInit(null,{"type":"qrLogin"}) } }, setScanState(state){ this.scanState = (state?.type !== undefined && state?.type !== null) ? state.type : 9; this.scanErrorMsg = state?.msg || ""; if(this.scanState != -1){ this.stopCountdown(); } }, //刷新二维码 refreshScanQrCode(){ let me=this; me.getScanQrCode(); }, // 建立扫码登录socket createScanSocket(url, callback) { let me = this; return new Promise((resolve, reject) => { if ("WebSocket" in window) { try { // 构造 WebSocket 路径 let socketHost = window.HIVUI_SETTING?.serverUrl.replace("http", "ws"); let path = socketHost + "/ws/qr/login/pc/" + window.HIVUI_SETTING?.projectName + "/" + me.scanUUID; if (url) { path = url; } // 创建 WebSocket 实例 me.scanWS = new WebScoketManager({ link: path, //"reConnect":true, }); // 初始化 WebSocket 事件 me.initWSEvent(url, callback); // 监听连接错误 me.scanWS.on("onerror", (error) => { reject(new Error("WebSocket 链接错误: " + error.message)); }); // 监听连接关闭 me.scanWS.on("onclose", () => { reject(new Error("WebSocket 连接已关闭")); }); // 监听连接成功 me.scanWS.on("onopen", () => { resolve(me.scanWS); }); } catch (error) { console.error("WebSocket 初始化失败:", error); reject(new Error("WebSocket 初始化失败: " + error.message)); } } else { // 浏览器不支持 WebSocket alert("您的浏览器不支持 WebSocket!"); reject(new Error("浏览器不支持 WebSocket")); } }); }, //初始化WS事件 initWSEvent(url, callback) { let me=this; let _ws = me.scanWS; if (!_ws) { console.log("WebSocket 实例未初始化"); return; } console.log('开始连接websocket'); //建立连接 _ws.on("onopen", () => { console.log('数据发送中...'); }); //连接失败 _ws.on("onclose", () => { console.log('连接已关闭...'); }); //超时关闭 _ws.on("overtime", () => { console.log('服务器连接超时'); }); _ws.on("reConnect", () => { console.log('重新连接websocket中'); }); _ws.on("endReConnect", () => { console.log('重新连接次数超出上限,终止连接'); }); //接受消息 _ws.on("onmessage", (data) => { if(url && callback){ callback(data); return; }else{ if(data.status + "" =="500"){ me.setScanState({type:6,msg:data.msg}) return; }else if(data.status + "" =="200"){ if(data.token){//登录成功 setToken(data.token); if(window.customSysCofig?.isSelectUserBz){ getBzList().then((res)=>{ me.userBzList=res.dataPack; me.isShowUserBz=true; }); }else{ me.$emit("loginSuccess"); } return; } } if(typeof(data.type)!="undefined"){ this.setScanState(data); switch (data.type + "") {//二维码状态(type):-1-待机 0-扫码中 1-扫码取消 2-登录中 3-已失效 4-已使用 5-登录成功 6-登录失败 case "0": break; case "1": me.scanErrorMsg="扫码已取消"; break; case "2": break; case "3": me.scanErrorMsg="二维码已失效"; me.stopCountdown(); break; case "4": me.scanErrorMsg="二维码已使用"; me.stopCountdown(); break; default: me.scanErrorMsg="扫码登录异常"; break; } } } }); }, }, }; </script> <style lang="scss" scoped> .login_form { // width: 370px; // height: 380px; // margin: auto; // position: absolute; // top: 0; // left: 40%; // right: 0; // bottom: 0; // z-index: 100; // background-color: #fff; // padding: 18px 20px 0; // box-shadow: 0 0 5px 1px rgba(0, 0, 0, 0.5); .login_form_hd{ position: relative; box-sizing: border-box; padding-left:30px; width: 100%; h3{ font-size: 24px; text-align: left; line-height: 44px; color: #333; font-weight: normal; } } .login_form_bd { min-height: 231px; height: 100%; position: relative; padding: 24px 20px 0; .login_form_appqr { position: absolute; bottom: 0; left: 15px; .login_form_app{ color: #409eff; cursor: pointer; display: flex; align-items: center; &:hover { color: #0f406c; &i { color: #0f406c; } } } .miniIcon { display: inline-block; height: 20px; width: 20px; background-position: center center; background-size: 100%; margin-right:5px; &[data-type="app"] { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAbdJREFUWEftV0tOwzAQfUMoa24ARwA1XQMnoJyAVKLZUk7Q9AR0nSJRbhBOQPdtBUcoN2DdD0aTNiKOk9ZJYyGheBXF43nPz57xDCE5nPdjHC27ADUBnCrzxX7MABFgftjD8Pwr7oIUf+3xCEQXxXB2rgrg2zfZBO7Glzigt51u9jH4Fld4aowiF7IC7rQDiEfJv8ArSHwUwhR0BsK1vJYe4Nf7GQTGHkDd3wWycSESyqZED37D0yOQkKsQAeVY/xWBtbwz+HaQqY5RBdyJCIF9Ww3niFFFoFKgVAXakz4I9xDUwqA+RPwStqcOSDwDcpyjVAJuLFMyiRAQCAlF30YJMJg7GQK4zYj7F/i2I82VqkDkOZ2ECs72RgioSqSDGyXAzvlS8hjYnb9JxTrPo7Ej0AE3fgQ6JPZSAAgwt1rJSlYHN7QJK+wV5wqusDcjT0GijZTHsCJQlgJcri+sddqtLQP9ZqYsAvGKOVdDs5VASmOSeb9iPUNaQ6OzDoBcTObaCT/D4JeRvcgv4Lag2NqahfndYHPK92Zgx3JCUoEoedRW3Do1QTjJE+GZtgKf4KS2sLxkUvsBGpxuMP/zizEAAAAASUVORK5CYII="); } &[data-type="desk"] { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJGRUFBRDNFOTc2NTExRUI5OTdGOTE0QkVGODlDRDIwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJGRUFBRDNGOTc2NTExRUI5OTdGOTE0QkVGODlDRDIwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkZFQUFEM0M5NzY1MTFFQjk5N0Y5MTRCRUY4OUNEMjAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkZFQUFEM0Q5NzY1MTFFQjk5N0Y5MTRCRUY4OUNEMjAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7jOkn1AAABSElEQVR42mL8//8/AymApXzd08fvfv36+58Rh4o///7LCrLZqPCEmQgCuYwMaWeINPv/TGOQDUC8OEkx2kzoHw63MTMxrjv/Yf/Nz1AnATE/JzMjIwMIYwO7rn3iZGWcHCGL0PDt1z9czrj7+qf7xNtAxslKDTMFbiCDCY+jP/3469B7C8J27L318O0vfBpuvfwhUXLpyftfEC7QFQpVl888/AZyEjsLFtdffvodGJRff/07dvcLkCvGy6IrzXnkDpCddmbN2Xf/cYD9Nz+Bwj3tTNOWZxARkA0hM+8JcDH//osSrEBuiau4uSI3hOupww8NpTJ3iXuvf37//Y8J1V3AuNeQ4AA6DMjWk+E0keeCxR9ewJJxFuiei4+/wUVwhtKbL39su28yMTGsz1QG2oCUQnCAE/e+1G58+vrzbzRxRlKTN0CAAQB1Gu32zyWjpAAAAABJRU5ErkJggg=="); } } } .error_tips { position: absolute; line-height: 30px; font-size: 12px; color: #f60000; text-align: center; top: 0; left: 0; right: 0; width: 100%; margin: auto; } .login_field { position: relative; padding-bottom: 20px; label { line-height: 35px; display: block; height: 35px; font-size: 16px; } i { font-size: 20px; color: #aaa; width: 35px; text-align: center; line-height: 40px; height: 40px; position: absolute; top: 35px; left: 0; margin: auto; } .login_field_text { height: 40px; width: 100%; line-height: 40px; border: 1px solid #ccc; border-radius: 5px; background-color: #fff; font-size: 16px; color: #000; padding-left: 35px; box-sizing: border-box; font-family: "Microsoft YaHei", proxima-nova, "Helvetica Neue", arial, helvetica, clean, sans-serif; &[readonly]{ background-color: #eee; color: #666; } &::input-placeholder { color: #ccc; } } img{ width:calc(50% - 10px); margin-left:10px; cursor: pointer; } } .saveCheckbox_box { height: 35px; line-height: 35px; padding-left: 30px; position: relative; top: 0px; label { cursor: pointer; user-select: none; i { font-size: 24px; position: absolute; top: 0; left: 2px; bottom: 0; margin: auto; height: 25px; width: 25px; color: #888; cursor: pointer; text-align: center; } } .occasion { float: right; &:after { content: ""; background: no-repeat; width: 14px; height: 18px; position: absolute; top: 30%; pointer-events: none; right: 1px; } #selectStyle { border: none; outline: none; width: 62px; appearance: none; -webkit-appearance: none; -moz-appearance: none; } } } .forgetPw { float: right; color: #409eff; cursor: pointer; margin-left: 5px; &:hover { color: #0f406c; i { color: #0f406c; } } } .login_form_btn { font-size: 13px; margin-top: 20px; .loginBtn { /* background-color: #135189; */ width: 100%; height: 40px; border: none; border-radius: 5px; color: #fff; font-size: 16px; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; i { margin-left: 5px; } &:hover { filter: brightness(0.8); } &.disabled { background-color: #ccc!important; &:hover { filter: brightness(1); } } } } /*二维码*/ .qrCodeImg { /* position: absolute; */ height: 139px; width: 139px; /* left:40% !important; right:0 !important; bottom:12%; */ margin-top: 10px; margin-left: 85px; z-index: 999; } .login_form_bottom{ padding-top: 15px; min-height:36px; .register_tips{ font-size: 14px; text-align: center; span{ color:#409eff; cursor: pointer; } } .langDropdown{ position: absolute; bottom:0; right:0; padding:0 20px; } } } .login_form_google { width: 280px; position: absolute; top: 317px; left: 0; bottom: 0; color: #fff; line-height: 20px; text-align: center; font-weight: bold; font-size: 13px; cursor: pointer; user-select: none; & > a { color: #fff; } } .loginMainBox { position: relative; .login-title { height: 18px; line-height: 18px; font-size: 16px; color: #889aa4; margin-top: 12px; margin-left: 12px; padding-bottom: 8px; font-weight: 700; } .login-tip{ position: absolute; top: 8px; right: 52px; display: block; .poptip{ background-color: #F2F7FC; line-height: 16px; position: relative; z-index: 99; border: 1px solid #DAE9F7; padding: 5px 10px; border-radius: 4px; .poptip-arrow { position: absolute; z-index: 10; top: 8px; right: 0; span{ position: absolute; width: 0; height: 0; border-color: hsla(0,0%,100%,0); border-style: solid; overflow: hidden; top: 0; left: -1px; border-width: 6px 0 6px 6px; border-left-color: #F2F7FC; } em { position: absolute; width: 0; height: 0; border-color: hsla(0,0%,100%,0); border-style: solid; overflow: hidden; top: 0; left: 0; border-width: 6px 0 6px 6px; border-left-color: #DAE9F7; border-right-color: #DAE9F7; } } .poptip-content { font-size: 12px; font-weight: 400; color: #06c; } } } .qrDesc { font-size: 12px; color: #666; width: 160px; margin: auto; overflow: hidden; line-height: 17px; margin-top: 230px; .qrDesc_right { position: relative; left: 50px; top: -35px; } } .scanMain { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); .qrcode-img { position: relative; margin: 20px auto; font-size: 14px; -webkit-box-shadow: 0 0 8px #ddd; box-shadow: 0 0 8px #ddd; opacity: 1; width: 140px; height: 140px; .qrcode-error { background: hsla(0, 0%, 100%, 0.95); position: absolute; left: 0; top: 0; z-index: 99; width: 100%; height: 100%; p { font-weight: 700; color: #222; margin-top: 38px; margin-bottom: 8px; text-align: center; } .refresh { background: #f40; width: 110px; height: 34px; line-height: 34px; text-align: center; margin: 0 auto; background: #ff9000; border-color: #ff9000; display: block; color: #fff; border-radius: 3px; font-size: 14px; cursor: pointer; } } canvas { margin: 5px; } } .login-error { text-align: center; color: #222; font-weight: 700; } } .scanMask{ position: absolute; top: 0; left: 0; height: 100%; width: 100%; background-color: rgba(255,255,255,0.7); display: flex; justify-content: center; align-items: center; .scanMaskMsg{ padding: 0 10px; box-shadow: #000 0 0 10px 1px; background-color: #fff; line-height: 32px; border-radius: 5px; color: #06c; font-weight: bold; } } /*二维码切换登录*/ .icon-qrcode { position: absolute; right: 0px; top: 0px; cursor: pointer; width: 44px; z-index: 99; } .login-content { width: 100%; margin: 0 auto; padding-top: 55px; .qrcode-success { text-align: center; margin-top: 20px; .iconfont { color: #c5c5c5; font-size: 36px; } h4, p { margin-top: 10px; font-size: 14px; } } } // 扫码界面 .scan-page{ width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; .state{ .qr{ position: relative; img{ width: 200px; height: 200px; } .loading-bg{ position: absolute; top: 0; left: 0; width: 100%; height: 100%; &.opacity{ background-color: rgba(255,255,255,0.9); } .loading-text{ width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; i{ font-size: 34px; color: red; } } } } .scan-info{ display: flex; flex-direction: column; gap: 5px; position: relative; top: -10px; z-index: 2001; .countdown{ text-align: center; } .scan-desc{ display: flex; justify-content: center; align-items: center; gap: 15px; font-size: 12px; color: #666; margin: auto; } } } } } &.showCaptcha{ height:440px; .login_form_bd { .login_field{ padding-bottom:10px; } .captcha_field{ input{ padding-left:10px; } } } } } .appQRPopper{ text-align: center; .login_download_app{ display:flex; justify-content:center; canvas{ width:180px!important; height:180px!important; } img{ max-width:180px; } } } .seldUserBzDialog{ display: flex; justify-content: center; padding-bottom:10px; } .unlock_field { display:flex; padding-top:10px; label { line-height: 35px; display: block; height: 35px; width:80px; font-size: 14px; padding-left:20px; } .unlock_field_input{ position: relative; margin-left:20px; flex: 0 0 calc(100% - 150px); display:flex; align-items: center; i { font-size: 20px; color: #aaa; width: 35px; text-align: center; line-height: 35px; height: 35px; position: absolute; top: 0; left: 0; margin: auto; } .unlock_field_text { height: 35px; width: 100%; line-height: 35px; border: 1px solid #ccc; border-radius: 5px; background-color: #fff; font-size: 14px; color: #000; padding-left: 35px; box-sizing: border-box; font-family: "Microsoft YaHei", proxima-nova, "Helvetica Neue", arial, helvetica, clean, sans-serif; &::input-placeholder { color: #ccc; } } img{ width:calc(50% - 10px); margin-left:10px; cursor: pointer; } } } @supports (display:none) { dot { display: inline-block; width: 3ch; text-indent: -1ch; vertical-align: bottom; overflow: hidden; animation: dot 2s infinite step-start both; font-family: Consolas, Monaco, monospace; } } @keyframes dot { 33% { text-indent: 0; } 66% { text-indent: -2ch; } } </style>
<template> <div :class="'login_form' + ((captchaObj&&captchaObj.captchaId)?' showCaptcha':'')" v-if="config.isPwdLogin || config.isScan"> <!-- 用户名密码窗口 --> <div id="showQr" class="loginMainBox" :hidden="!(config.isPwdLogin && currLoginType == 'com')"> <img @click="changeQr" class="icon-qrcode" :src="scanIcon" :hidden="!config.isScan" /> <div class="login-tip" :hidden="!config.isScan"> <div class="poptip"> <div class="poptip-arrow"> <em></em> <span></span> </div> <div class="poptip-content">{{$t('hivuiLogin_main_scan_login')}}</div> </div> </div> <div class="login_form_bd"> <div class="error_tips"></div> <input type="hidden" name="directAction" value="" /> <input type="hidden" name="refererurl" value="" /> <div class="login_field"> <label>{{$t('hivuiLogin_main_user')}}</label> <input class="login_field_text" v-model="username" type="text" id="txtUserName" name="userName" :placeholder="$t('hivuiLogin_main_user_ph')" autofocus required autocomplete="off" @keyup.enter="loginFunc" :readonly="usernameReadonly" /> <i class="fa fa-user"></i> </div> <div class="login_field"> <label>{{$t('hivuiLogin_main_pw')}}</label> <input class="login_field_text" v-model="password" type="text" onfocus="this.type='password'" id="txtPassword" name="password" :placeholder="$t('hivuiLogin_main_pw_ph')" required autocomplete="off" @keyup.enter="loginFunc" /> <i class="fa fa-unlock-alt"></i> </div> <div class="login_field captcha_field" v-if="captchaObj&&captchaObj.captchaId"> <label>{{$t('hivuiLogin_main_captcha')}}</label> <div style="display:flex;align-items:center;"> <input class="login_field_text" v-model="loginCaptcha" type="text" id="loginCaptcha" name="captcha" :placeholder="$t('hivuiLogin_main_captcha_ph')" autofocus required autocomplete="off" @keyup.enter="loginFunc" style="width:50%;" /> <img :src="captchaObj.imageData" @click="getCaptchaPicObj"> </div> </div> <div class="saveCheckbox_box" v-if="!miniLoginInfo"> <label for="saveCheckbox" ><i :class=" 'fa ' + (isCheckRu ? 'fa-check-square-o' : 'fa-square-o') " ></i >{{$t('hivuiLogin_main_save_user')}}</label > <input type="checkbox" id="saveCheckbox" style="display: none" v-model="isCheckRu" @change="rememberUserEvent" /> <span class="forgetPw" @click="showResetPw" ><i class="fa fa-lock"></i ><span style="margin-left: 5px">{{$t('hivuiLogin_main_forget_user')}}</span></span > </div> <div class="login_form_btn"> <div class="login_btn_area"> <button :style="btnStyle" :class="'loginBtn' + (loginLoading ? ' disabled' : '')" @click="loginFunc" > {{$t('hivuiLogin_main_login_btn')}}<i :hidden="!loginLoading" class="el-icon-loading" ></i> </button> </div> </div> <div class="login_form_bottom" v-if="!miniLoginInfo"> <div class="register_tips" :hidden="config.hideRegister"> {{$t('hivuiLogin_main_register1')}} <span @click="goRegisterPage">{{$t('hivuiLogin_main_register2')}}</span> </div> <el-dropdown @command="changeLang" class="langDropdown" :hidden="!config.showChangeLangBtn"> <span class="el-dropdown-link"> {{currLangDesc}}<i class="el-icon-arrow-down el-icon--right"></i> </span> <el-dropdown-menu slot="dropdown"> <template v-for="(item,index) in langList"> <el-dropdown-item :key="'langList'+index" :command="item.name+','+item.key">{{item.name}}</el-dropdown-item> </template> </el-dropdown-menu> </el-dropdown> </div> <el-popover v-if="config.appQRcode && !miniLoginInfo" class="login_form_appqr" placement="bottom" width="210" popper-class="appQRPopper"> <div ref="appQRcode" class="login_download_app"> <canvas v-if="!config.appQRcode.startsWith('imgUrl:')"></canvas> <img v-else :src="config.appQRcode.replace('imgUrl:','')"> </div> <span slot="reference" class="login_form_app" :title="$t('hivuiLogin_main_appdownload')"><i class="miniIcon" data-type="app"></i>{{$t('hivuiLogin_main_download')}}</span> </el-popover> </div> </div> <!-- 扫码窗口 --> <div id="showCom" class="loginMainBox" :hidden="!(config.isScan && currLoginType == 'scan')" > <div class="login_form_hd"> <h3>{{$t('hivuiLogin_main_scan_login')}}</h3> <img @click="changeCom" class="icon-qrcode" :src="comIcon" alt="" :hidden="!config.isPwdLogin" /> <div class="login-tip" :hidden="!config.isPwdLogin"> <div class="poptip"> <div class="poptip-arrow"> <em></em> <span></span> </div> <div class="poptip-content">{{$t('hivuiLogin_main_pw_text')}}</div> </div> </div> </div> <!-- 扫码界面 --> <div class="scan-page"> <!-- 初始状态 --> <div class="state default" v-if="scanState ==-1"> <div class="qr"> <img ref="scanQrCode" :src="scanQrCode" :load="scanLoading"/> </div> <div class="scan-info"> <div class="countdown"> <span>{{countdown}}</span> s </div> <div class="scan-desc"> <div> <img :src="saomaIcon"/> </div> <div class="right"> <span> {{$t('hivuiLogin_main_app_text1')}} <a style="color: #06c">{{config.appName}}</a><br /> {{$t('hivuiLogin_main_app_text3')}} </span> </div> </div> </div> </div> <!-- 扫码中 --> <div class="state scanning" v-else-if="scanState == 0"> <div class="qr"> <img ref="scanQrCode" :src="scanQrCode" :load="scanLoading" /> <div class="loading-bg" v-loading="true"> </div> </div> <div class="scan-info"> <div class="scan-desc"> <div style="text-align:center;"> <span style="font-size:16px;color: #06c;">正在扫码...</span><br /> <span>请在手机上确认登录</span> </div> </div> </div> </div> <!-- 二维码失效 --> <div class="state expired" v-else-if="scanState !== -1 || scanState == 0"> <div class="qr"> <img ref="scanQrCode" :src="scanQrCode" :load="scanLoading" /> <div class="loading-bg opacity"> <div class="loading-text"> <i class="el-icon-warning"></i> <span >{{scanErrorMsg}}</span> <el-button type="text" @click="refreshScanQrCode">点击刷新</el-button> </div> </div> </div> <div class="scan-info"> <div class="scan-desc"> <div> <img :src="saomaIcon"/> </div> <div class="right"> <span> {{$t('hivuiLogin_main_app_text1')}} <a style="color: #06c">{{config.appName}}</a><br /> {{$t('hivuiLogin_main_app_text3')}} </span> </div> </div> </div> </div> </div> </div> <div id="qrcode"></div> <!-- 重置密码 --> <el-dialog :title="$t('hivuiLogin_main_forget_title')" :visible.sync="isShowResetPw" width="400px" :close-on-click-modal="false" @close="cancelResetPw" :modal-append-to-body="false"> <resetPw res="resetPw" @resetCb="cancelResetPw"></resetPw> </el-dialog> <!--选择岗位 --> <el-dialog :title="$t('hivuiLogin_main_bz_title')" :visible.sync="isShowUserBz" width="300px" top="40vh" :close-on-click-modal="false" @close="cancelUserBz" :modal-append-to-body="false"> <div class="seldUserBzDialog"> <el-select v-model="seldUserBz" :placeholder="$t('hivuiLogin_main_bz_placeholder')" @change="changeUserBz"> <el-option v-for="item in userBzList" :key="item.fbzid" :label="item.fbzname+'/'+item.fbzid" :value="item.fbzid"> </el-option> </el-select> </div> </el-dialog> <!-- 管理员解锁 --> <el-dialog :title="$t('hivuiLogin_main_admin_forget_title')" :visible.sync="isShowAdminResetPw" width="430px" :close-on-click-modal="false" @close="cancelAdminResetPw" :modal-append-to-body="false"> <div class="unlock_field"> <label>{{$t('hivuiLogin_main_pw')}}</label> <div class="unlock_field_input"> <input ref="aaaa" class="unlock_field_text" v-model="unlockPassword" type="password" name="unlockPassword" :placeholder="$t('hivuiLogin_main_pw_ph')" autocomplete="off" @keyup.enter="adminUnlock" /> <i class="fa fa-unlock-alt"></i> </div> </div> <div class="unlock_field" v-if="captchaObj2&&captchaObj2.captchaId"> <label>{{$t('hivuiLogin_main_captcha')}}</label> <div class="unlock_field_input"> <input class="unlock_field_text" v-model="unlockCaptcha" type="text" name="unlockCaptcha" :placeholder="$t('hivuiLogin_main_captcha_ph')" autocomplete="off" @keyup.enter="adminUnlock" style="width:50%;" /> <i class="fa fa-unlock-alt"></i> <img :src="captchaObj2.imageData" @click="getCaptchaPicObj(2)"> </div> </div> <span slot="footer" class="dialog-footer"> <el-button size="small" type="primary" @click="adminUnlock">{{$t('hivuiLogin_main_admin_unlock')}}</el-button> </span> </el-dialog> </div> </template> <script> import { baseLogin,getScanQrCode,adminUnlock } from "../api/login"; import { getBzList,changeBz,getCaptchaPic } from "../api/user"; import md5 from "js-md5"; import Cookies from 'js-cookie'; import QRCode from 'qrcode'; import { utils } from "hi-ui"; import { projectName } from "../config"; import WebScoketManager from '../utils/websocket.js' import "font-awesome/css/font-awesome.css"; import comIcon from "../assets/computer2.png" import scanIcon from "../assets/qrcode2.png" import saomaIcon from "../assets/saoma2.png" import _loginLogoImg from "../assets/Logo1.png" import _loginBgImg from "../assets/background.png" import { getToken, setToken, removeToken, getUserId, setUserId, removeUserId, } from "../utils/auth.js"; import {setUrlValue,getUrlValue} from "../utils/index.js"; import resetPw from "../components/resetPw.vue"; export default { name: "LoginPanel", components: {resetPw}, props: { miniLoginInfo:{ type: Object, default: null, } }, data() { return { comIcon: comIcon, scanIcon: scanIcon, saomaIcon: saomaIcon, username: "", usernameReadonly: false, password: "", //登录输入验证码 loginCaptcha:"", config: { title: window.customSysCofig?.loginTitle||this.$t('hivuiLogin_main_def_title'), loginLogoImg: window.customSysCofig?.loginLogo||_loginLogoImg, loginLogoImg_height: window.customSysCofig?.loginLogo_h||56, loginLogoImg_width: window.customSysCofig?.loginLogo_w||206, loginBgImg: window.customSysCofig?.loginBgImg||_loginBgImg, copyright: window.customSysCofig?.copyright||this.$t('hivuiLogin_main_def_copyright'), isScan: window.customSysCofig?.isScan||false, isPwdLogin: window.customSysCofig?.isPwdLogin === false ? false : true, mainColor:window.customSysCofig?.mainColor||"#06c", appQRcode:window.customSysCofig?.appQRcode||"", hideRegister:window.customSysCofig?.hideRegister||false, showChangeLangBtn:window.customSysCofig?.showChangeLangBtn||false, appName:window.customSysCofig?.appName||this.$t('hivuiLogin_main_app_text2'), }, currLoginType: "com", rememberUserId: getUserId(), loginLoading: false, isShowResetPw:false, isShowAdminResetPw:false, //是否显示选择登录岗位弹窗 isShowUserBz:false, //已选用户岗位 seldUserBz:"", //用户岗位数据列表 userBzList:[], //扫码Socket scanWS:null, //扫码UUID scanUUID:"", //登录二维码 scanQrCode:"", //二维码loading scanLoading:false, //扫码报错信息 scanErrorMsg:"", //当前扫码登录状态 二维码状态(type):-1-待机 0-扫码中 1-扫码取消 2-登录中 3-已失效 4-已使用 5-登录成功 6-登录失败 scanState:-1, //解锁密码 unlockPassword:"", //解锁输入验证码 unlockCaptcha:"", //验证码对象 captchaObj:null, //解锁验证码对象 captchaObj2:null, // 倒计时相关 countdown: 60, countdownTimer: null, isCounting: false }; }, computed: { btnStyle(){ return { "background-color": this.config.mainColor, }; }, isCheckRu() { return !!this.rememberUserId; }, currLangDesc(){ return window.localStorage.getItem("locale")?(JSON.parse(window.localStorage.getItem("locale")).desc):"中文"; }, langList(){ let arr=[ { name:"中文", key:"zh-CN", }, { name:"English", key:"en", }, ] if(window.customSysCofig?.addLangList?.length){ window.customSysCofig?.addLangList.forEach((item,index)=>{ arr.push({ name:item.name, key:item.key, }); }); } return arr; } }, async created() { let me=this; if(!me.config.isPwdLogin && me.config.isScan){ //获取扫码登录二维码 this.changeQr(); } //获取验证码图片 await me.getCaptchaPicObj(); }, mounted() { if(this.miniLoginInfo){ this.username=this.miniLoginInfo.username||""; this.usernameReadonly= !!(this.miniLoginInfo.questType=='ajax'&&this.miniLoginInfo.oldUserName); }else{ if (this.isCheckRu) { this.username = this.rememberUserId; } if(!window.localStorage.getItem("locale")){ window.localStorage.setItem("locale",JSON.stringify({ desc:"中文", name:"zh-CN", })); Cookies.set("locale","zh-CN"); } this.$refs.scanQrCode.onload = function() { // 清理创建的临时链接 //URL.revokeObjectURL(imageUrl); } //生成二维码 if(this.config.appQRcode){ let isIMG=this.config.appQRcode.startsWith('imgUrl:'); if(this.config.appQRcode.indexOf('http')==-1){ this.config.appQRcode=(isIMG?'imgUrl:':'')+location.origin+this.config.appQRcode.replace('imgUrl:',''); } if(!isIMG){ let targetUrl=this.config.appQRcode; if(!window._global){//正式环境 targetUrl=utils.string.setUrlValue(targetUrl,"pn",window.HIVUI_SETTING.projectName); } console.log("测试str",targetUrl); this.createDLQrCode(this.$refs.appQRcode.querySelector("canvas"),targetUrl); } } } }, beforeDestroy() { // 组件销毁前清除定时器 this.stopCountdown(); }, methods: { // 开始倒计时 startCountdown() { // 如果已经在倒计时,则不重复启动 if (this.isCounting) return; this.isCounting = true; this.countdown = 60; // 重置时间为60秒 this.countdownTimer = setInterval(() => { this.countdown--; // 当倒计时结束 if (this.countdown <= 0) { this.finishCountdown(); } }, 1000); }, // 倒计时结束处理 finishCountdown() { this.stopCountdown(); // 设置为失效状态 this.setScanState({type:3,msg:"二维码已失效"}) }, // 停止倒计时 stopCountdown() { if (this.countdownTimer) { clearInterval(this.countdownTimer); this.countdownTimer = null; } this.isCounting = false; }, // 重置倒计时 resetCountdown() { this.stopCountdown(); this.countdown = 60; this.$nextTick(() => { this.startCountdown(); }); }, loginFunc() { this.defaultLogin(); }, defaultLogin() { let me=this; if(me.captchaObj && me.captchaObj.captchaId && !me.loginCaptcha.trim()){ me.$message.error(me.$t('hivuiLogin_main_valid_captcha')); return; }else if(!me.username.trim()){ me.$message.error(me.$t('hivuiLogin_main_valid_user')); return; }else if(!me.password){ me.$message.error(me.$t('hivuiLogin_main_valid_pw')); return; } removeToken(); me.loginLoading = true; let _params={ username: me.username.trim(), password: md5(me.password), }; if(me.loginCaptcha && me.captchaObj && me.captchaObj.captchaId){ _params.captchaId=me.captchaObj.captchaId; _params.captcha=me.loginCaptcha; } baseLogin(_params) .then((response) => { const data = response; setToken(data.token); if (me.isCheckRu) { setUserId(me.username); } else { removeUserId(); } if(this.miniLoginInfo){ me.$emit("loginSuccess", data); }else{ if(data.isAuthorize===false){ if(data.authorizeMsg){ me.$message.warning(data.authorizeMsg); } this.$router.push("/authorize"); }else if(window.customSysCofig?.isSelectUserBz){ getBzList().then((res)=>{ me.userBzList=res.dataPack; me.isShowUserBz=true; }); }else{ me.$emit("loginSuccess", data); } } }) .catch((error) => { me.loginLoading = false; if(me.captchaObj && me.captchaObj.captchaId && error.status==500){ me.getCaptchaPicObj(); } if(me.username.trim()=="admin" && error&&error.dataPack&&error.dataPack.lock){ me.isShowAdminResetPw=true; //获取验证码图片 me.getCaptchaPicObj(2); } }); }, //关闭选岗弹窗 cancelUserBz(){ this.isShowUserBz=false; this.loginLoading=false; }, //选择岗位 changeUserBz(val){ changeBz({ bzid:val }).then((res)=>{ this.$emit("loginSuccess"); }); }, changeQr() { this.currLoginType = "scan"; this.getScanQrCode(); }, changeCom() { this.currLoginType = "com"; }, showResetPw() { this.isShowResetPw=true; }, cancelResetPw() { this.isShowResetPw=false; }, cancelAdminResetPw(){ this.isShowAdminResetPw=false; }, rememberUserEvent() { if (this.isCheckRu) { this.rememberUserId = ""; } else { this.rememberUserId = true; } }, goRegisterPage(){ this.$router.push("/register"); }, changeLang(command){ let dataArr=command.split(","); window.localStorage.setItem("locale",JSON.stringify({ desc:dataArr[0], name:dataArr[1], })); Cookies.set("locale",dataArr[1]); window.location.reload(); }, //生成下载二维码 createDLQrCode(target,text){ QRCode.toCanvas(target, text, function (error) { // 将文本转化为二维码并渲染到canvas上 if (error) { console.error(error); } }); }, //解锁 adminUnlock(){ let me=this; if(!me.unlockPassword){ me.$message.error(me.$t('hivuiLogin_main_valid_pw')); return; } let _params={ userId: me.username.trim(), password: md5(me.unlockPassword) }; if(me.unlockCaptcha && me.captchaObj2 && me.captchaObj2.captchaId){ _params.captchaId=me.captchaObj2.captchaId; _params.captcha=me.unlockCaptcha; } adminUnlock(_params).then((response)=>{ if(response.status==200){ me.isShowAdminResetPw=false; if(!response.msg){ me.$message.error(me.$t('hivuiLogin_main_unlock_success')); } me.password=me.unlockPassword; if(!me.captchaObj || !me.captchaObj.captchaId){ me.loginFunc(); }else{ me.loginCaptcha=""; } } }).catch((err)=>{ if(me.captchaObj2 && me.captchaObj2.captchaId && err.status==500){ me.getCaptchaPicObj(2); }else{ me.isShowAdminResetPw=false; } }); }, //获取验证码 getCaptchaPicObj(num){//num=2:管理员解锁时验证码 let me=this; getCaptchaPic({ type:"login" }).then((res)=>{ if(res&&res.dataPack){ if(num==2){ me.captchaObj2=res.dataPack; }else{ me.captchaObj=res.dataPack; } } }); }, scanInit(url,params) { let me=this; return getScanQrCode(url,params).then((res)=>{ me.scanLoading=false; if(res.headers.uuid){ me.scanUUID=atob(res.headers.uuid); console.log("uuid:",me.scanUUID); } const blob = new Blob([res.data], { type: 'image/jpeg' }); const imageUrl = URL.createObjectURL(blob); me.scanQrCode=imageUrl; me.resetCountdown(); if(!url){ me.createScanSocket(); } return Promise.resolve(res); }).catch((error)=>{ me.scanLoading=false; this.setScanState({ type:6, msg:"获取二维码失败" }); return Promise.reject(error); }); }, //获取扫码登录的二维码 getScanQrCode(){ let me=this; me.scanLoading=true; me.scanState=-1; if(me.scanWS){ me.scanWS.end(); } const scanInit = window.customSysCofig?.loginView?.scanInit; if(scanInit){ scanInit({ setScanQrCode:this.scanInit, setToken:setToken, setScanState:this.setScanState, createScanSocket:this.createScanSocket, }) }else{ me.scanInit(null,{"type":"qrLogin"}) } }, setScanState(state){ this.scanState = (state?.type !== undefined && state?.type !== null) ? state.type : 9; this.scanErrorMsg = state?.msg || "未知错误"; }, //刷新二维码 refreshScanQrCode(){ let me=this; me.getScanQrCode(); }, // 建立扫码登录socket createScanSocket(url, callback) { let me = this; return new Promise((resolve, reject) => { if ("WebSocket" in window) { try { // 构造 WebSocket 路径 let socketHost = window.HIVUI_SETTING?.serverUrl.replace("http", "ws"); let path = socketHost + "/ws/qr/login/pc/" + window.HIVUI_SETTING?.projectName + "/" + me.scanUUID; if (url) { path = url; } // 创建 WebSocket 实例 me.scanWS = new WebScoketManager({ link: path, //"reConnect":true, }); // 初始化 WebSocket 事件 me.initWSEvent(url, callback); // 监听连接错误 me.scanWS.on("onerror", (error) => { reject(new Error("WebSocket 链接错误: " + error.message)); }); // 监听连接关闭 me.scanWS.on("onclose", () => { reject(new Error("WebSocket 连接已关闭")); }); // 监听连接成功 me.scanWS.on("onopen", () => { resolve(me.scanWS); }); } catch (error) { console.error("WebSocket 初始化失败:", error); reject(new Error("WebSocket 初始化失败: " + error.message)); } } else { // 浏览器不支持 WebSocket alert("您的浏览器不支持 WebSocket!"); reject(new Error("浏览器不支持 WebSocket")); } }); }, //初始化WS事件 initWSEvent(url, callback) { let me=this; let _ws = me.scanWS; if (!_ws) { console.log("WebSocket 实例未初始化"); return; } console.log('开始连接websocket'); //建立连接 _ws.on("onopen", () => { console.log('数据发送中...'); }); //连接失败 _ws.on("onclose", () => { console.log('连接已关闭...'); }); //超时关闭 _ws.on("overtime", () => { console.log('服务器连接超时'); }); _ws.on("reConnect", () => { console.log('重新连接websocket中'); }); _ws.on("endReConnect", () => { console.log('重新连接次数超出上限,终止连接'); }); //接受消息 _ws.on("onmessage", (data) => { if(url && callback){ callback(data); return; }else{ if(data.status + "" =="500"){ me.setScanState({type:6,msg:data.msg}) return; }else if(data.status + "" =="200"){ if(data.token){//登录成功 setToken(data.token); if(window.customSysCofig?.isSelectUserBz){ getBzList().then((res)=>{ me.userBzList=res.dataPack; me.isShowUserBz=true; }); }else{ me.$emit("loginSuccess"); } return; } } if(typeof(data.type)!="undefined"){ me.scanState=data.type; switch (data.type + "") {//二维码状态(type):-1-待机 0-扫码中 1-扫码取消 2-登录中 3-已失效 4-已使用 5-登录成功 6-登录失败 case "0": break; case "1": me.scanErrorMsg="扫码已取消"; break; case "2": break; case "3": me.scanErrorMsg="二维码已失效"; me.stopCountdown(); break; case "4": me.scanErrorMsg="二维码已使用"; me.stopCountdown(); break; default: me.scanErrorMsg="扫码登录异常"; break; } } } }); }, }, }; </script> <style lang="scss" scoped> .login_form { // width: 370px; // height: 380px; // margin: auto; // position: absolute; // top: 0; // left: 40%; // right: 0; // bottom: 0; // z-index: 100; // background-color: #fff; // padding: 18px 20px 0; // box-shadow: 0 0 5px 1px rgba(0, 0, 0, 0.5); .login_form_hd{ position: relative; box-sizing: border-box; padding-left:30px; width: 100%; h3{ font-size: 24px; text-align: left; line-height: 44px; color: #333; font-weight: normal; } } .login_form_bd { min-height: 231px; height: 100%; position: relative; padding: 24px 20px 0; .login_form_appqr { position: absolute; bottom: 0; left: 15px; .login_form_app{ color: #409eff; cursor: pointer; display: flex; align-items: center; &:hover { color: #0f406c; &i { color: #0f406c; } } } .miniIcon { display: inline-block; height: 20px; width: 20px; background-position: center center; background-size: 100%; margin-right:5px; &[data-type="app"] { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAbdJREFUWEftV0tOwzAQfUMoa24ARwA1XQMnoJyAVKLZUk7Q9AR0nSJRbhBOQPdtBUcoN2DdD0aTNiKOk9ZJYyGheBXF43nPz57xDCE5nPdjHC27ADUBnCrzxX7MABFgftjD8Pwr7oIUf+3xCEQXxXB2rgrg2zfZBO7Glzigt51u9jH4Fld4aowiF7IC7rQDiEfJv8ArSHwUwhR0BsK1vJYe4Nf7GQTGHkDd3wWycSESyqZED37D0yOQkKsQAeVY/xWBtbwz+HaQqY5RBdyJCIF9Ww3niFFFoFKgVAXakz4I9xDUwqA+RPwStqcOSDwDcpyjVAJuLFMyiRAQCAlF30YJMJg7GQK4zYj7F/i2I82VqkDkOZ2ECs72RgioSqSDGyXAzvlS8hjYnb9JxTrPo7Ej0AE3fgQ6JPZSAAgwt1rJSlYHN7QJK+wV5wqusDcjT0GijZTHsCJQlgJcri+sddqtLQP9ZqYsAvGKOVdDs5VASmOSeb9iPUNaQ6OzDoBcTObaCT/D4JeRvcgv4Lag2NqahfndYHPK92Zgx3JCUoEoedRW3Do1QTjJE+GZtgKf4KS2sLxkUvsBGpxuMP/zizEAAAAASUVORK5CYII="); } &[data-type="desk"] { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjJGRUFBRDNFOTc2NTExRUI5OTdGOTE0QkVGODlDRDIwIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjJGRUFBRDNGOTc2NTExRUI5OTdGOTE0QkVGODlDRDIwIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MkZFQUFEM0M5NzY1MTFFQjk5N0Y5MTRCRUY4OUNEMjAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MkZFQUFEM0Q5NzY1MTFFQjk5N0Y5MTRCRUY4OUNEMjAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7jOkn1AAABSElEQVR42mL8//8/AymApXzd08fvfv36+58Rh4o///7LCrLZqPCEmQgCuYwMaWeINPv/TGOQDUC8OEkx2kzoHw63MTMxrjv/Yf/Nz1AnATE/JzMjIwMIYwO7rn3iZGWcHCGL0PDt1z9czrj7+qf7xNtAxslKDTMFbiCDCY+jP/3469B7C8J27L318O0vfBpuvfwhUXLpyftfEC7QFQpVl888/AZyEjsLFtdffvodGJRff/07dvcLkCvGy6IrzXnkDpCddmbN2Xf/cYD9Nz+Bwj3tTNOWZxARkA0hM+8JcDH//osSrEBuiau4uSI3hOupww8NpTJ3iXuvf37//Y8J1V3AuNeQ4AA6DMjWk+E0keeCxR9ewJJxFuiei4+/wUVwhtKbL39su28yMTGsz1QG2oCUQnCAE/e+1G58+vrzbzRxRlKTN0CAAQB1Gu32zyWjpAAAAABJRU5ErkJggg=="); } } } .error_tips { position: absolute; line-height: 30px; font-size: 12px; color: #f60000; text-align: center; top: 0; left: 0; right: 0; width: 100%; margin: auto; } .login_field { position: relative; padding-bottom: 20px; label { line-height: 35px; display: block; height: 35px; font-size: 16px; } i { font-size: 20px; color: #aaa; width: 35px; text-align: center; line-height: 40px; height: 40px; position: absolute; top: 35px; left: 0; margin: auto; } .login_field_text { height: 40px; width: 100%; line-height: 40px; border: 1px solid #ccc; border-radius: 5px; background-color: #fff; font-size: 16px; color: #000; padding-left: 35px; box-sizing: border-box; font-family: "Microsoft YaHei", proxima-nova, "Helvetica Neue", arial, helvetica, clean, sans-serif; &[readonly]{ background-color: #eee; color: #666; } &::input-placeholder { color: #ccc; } } img{ width:calc(50% - 10px); margin-left:10px; cursor: pointer; } } .saveCheckbox_box { height: 35px; line-height: 35px; padding-left: 30px; position: relative; top: 0px; label { cursor: pointer; user-select: none; i { font-size: 24px; position: absolute; top: 0; left: 2px; bottom: 0; margin: auto; height: 25px; width: 25px; color: #888; cursor: pointer; text-align: center; } } .occasion { float: right; &:after { content: ""; background: no-repeat; width: 14px; height: 18px; position: absolute; top: 30%; pointer-events: none; right: 1px; } #selectStyle { border: none; outline: none; width: 62px; appearance: none; -webkit-appearance: none; -moz-appearance: none; } } } .forgetPw { float: right; color: #409eff; cursor: pointer; margin-left: 5px; &:hover { color: #0f406c; i { color: #0f406c; } } } .login_form_btn { font-size: 13px; margin-top: 20px; .loginBtn { /* background-color: #135189; */ width: 100%; height: 40px; border: none; border-radius: 5px; color: #fff; font-size: 16px; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; i { margin-left: 5px; } &:hover { filter: brightness(0.8); } &.disabled { background-color: #ccc!important; &:hover { filter: brightness(1); } } } } /*二维码*/ .qrCodeImg { /* position: absolute; */ height: 139px; width: 139px; /* left:40% !important; right:0 !important; bottom:12%; */ margin-top: 10px; margin-left: 85px; z-index: 999; } .login_form_bottom{ padding-top: 15px; min-height:36px; .register_tips{ font-size: 14px; text-align: center; span{ color:#409eff; cursor: pointer; } } .langDropdown{ position: absolute; bottom:0; right:0; padding:0 20px; } } } .login_form_google { width: 280px; position: absolute; top: 317px; left: 0; bottom: 0; color: #fff; line-height: 20px; text-align: center; font-weight: bold; font-size: 13px; cursor: pointer; user-select: none; & > a { color: #fff; } } .loginMainBox { position: relative; .login-title { height: 18px; line-height: 18px; font-size: 16px; color: #889aa4; margin-top: 12px; margin-left: 12px; padding-bottom: 8px; font-weight: 700; } .login-tip{ position: absolute; top: 8px; right: 52px; display: block; .poptip{ background-color: #F2F7FC; line-height: 16px; position: relative; z-index: 99; border: 1px solid #DAE9F7; padding: 5px 10px; border-radius: 4px; .poptip-arrow { position: absolute; z-index: 10; top: 8px; right: 0; span{ position: absolute; width: 0; height: 0; border-color: hsla(0,0%,100%,0); border-style: solid; overflow: hidden; top: 0; left: -1px; border-width: 6px 0 6px 6px; border-left-color: #F2F7FC; } em { position: absolute; width: 0; height: 0; border-color: hsla(0,0%,100%,0); border-style: solid; overflow: hidden; top: 0; left: 0; border-width: 6px 0 6px 6px; border-left-color: #DAE9F7; border-right-color: #DAE9F7; } } .poptip-content { font-size: 12px; font-weight: 400; color: #06c; } } } .qrDesc { font-size: 12px; color: #666; width: 160px; margin: auto; overflow: hidden; line-height: 17px; margin-top: 230px; .qrDesc_right { position: relative; left: 50px; top: -35px; } } .scanMain { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); .qrcode-img { position: relative; margin: 20px auto; font-size: 14px; -webkit-box-shadow: 0 0 8px #ddd; box-shadow: 0 0 8px #ddd; opacity: 1; width: 140px; height: 140px; .qrcode-error { background: hsla(0, 0%, 100%, 0.95); position: absolute; left: 0; top: 0; z-index: 99; width: 100%; height: 100%; p { font-weight: 700; color: #222; margin-top: 38px; margin-bottom: 8px; text-align: center; } .refresh { background: #f40; width: 110px; height: 34px; line-height: 34px; text-align: center; margin: 0 auto; background: #ff9000; border-color: #ff9000; display: block; color: #fff; border-radius: 3px; font-size: 14px; cursor: pointer; } } canvas { margin: 5px; } } .login-error { text-align: center; color: #222; font-weight: 700; } } .scanMask{ position: absolute; top: 0; left: 0; height: 100%; width: 100%; background-color: rgba(255,255,255,0.7); display: flex; justify-content: center; align-items: center; .scanMaskMsg{ padding: 0 10px; box-shadow: #000 0 0 10px 1px; background-color: #fff; line-height: 32px; border-radius: 5px; color: #06c; font-weight: bold; } } /*二维码切换登录*/ .icon-qrcode { position: absolute; right: 0px; top: 0px; cursor: pointer; width: 44px; z-index: 99; } .login-content { width: 100%; margin: 0 auto; padding-top: 55px; .qrcode-success { text-align: center; margin-top: 20px; .iconfont { color: #c5c5c5; font-size: 36px; } h4, p { margin-top: 10px; font-size: 14px; } } } // 扫码界面 .scan-page{ width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; .state{ .qr{ position: relative; img{ width: 200px; height: 200px; } .loading-bg{ position: absolute; top: 0; left: 0; width: 100%; height: 100%; &.opacity{ background-color: rgba(255,255,255,0.9); } .loading-text{ width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; i{ font-size: 34px; color: red; } } } } .scan-info{ display: flex; flex-direction: column; gap: 5px; position: relative; top: -10px; z-index: 2001; .countdown{ text-align: center; } .scan-desc{ display: flex; justify-content: center; align-items: center; gap: 15px; font-size: 12px; color: #666; margin: auto; } } } } } &.showCaptcha{ height:440px; .login_form_bd { .login_field{ padding-bottom:10px; } .captcha_field{ input{ padding-left:10px; } } } } } .appQRPopper{ text-align: center; .login_download_app{ display:flex; justify-content:center; canvas{ width:180px!important; height:180px!important; } img{ max-width:180px; } } } .seldUserBzDialog{ display: flex; justify-content: center; padding-bottom:10px; } .unlock_field { display:flex; padding-top:10px; label { line-height: 35px; display: block; height: 35px; width:80px; font-size: 14px; padding-left:20px; } .unlock_field_input{ position: relative; margin-left:20px; flex: 0 0 calc(100% - 150px); display:flex; align-items: center; i { font-size: 20px; color: #aaa; width: 35px; text-align: center; line-height: 35px; height: 35px; position: absolute; top: 0; left: 0; margin: auto; } .unlock_field_text { height: 35px; width: 100%; line-height: 35px; border: 1px solid #ccc; border-radius: 5px; background-color: #fff; font-size: 14px; color: #000; padding-left: 35px; box-sizing: border-box; font-family: "Microsoft YaHei", proxima-nova, "Helvetica Neue", arial, helvetica, clean, sans-serif; &::input-placeholder { color: #ccc; } } img{ width:calc(50% - 10px); margin-left:10px; cursor: pointer; } } } @supports (display:none) { dot { display: inline-block; width: 3ch; text-indent: -1ch; vertical-align: bottom; overflow: hidden; animation: dot 2s infinite step-start both; font-family: Consolas, Monaco, monospace; } } @keyframes dot { 33% { text-indent: 0; } 66% { text-indent: -2ch; } } </style>
Show line notes below