From b9bd29a1a81f6f7de479e3cc3fdfe3d85fc660bf Mon Sep 17 00:00:00 2001 From: he wei <858544502@qq.com> Date: 星期三, 23 四月 2025 13:35:06 +0800 Subject: [PATCH] UA 整理提交 --- src/views/login/index.vue | 701 ++++++++++++++++++++++++++++++++-------------------------- 1 files changed, 386 insertions(+), 315 deletions(-) diff --git a/src/views/login/index.vue b/src/views/login/index.vue index e028f40..f4e1962 100644 --- a/src/views/login/index.vue +++ b/src/views/login/index.vue @@ -1,39 +1,81 @@ <template> <div class="login-container"> - <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" autocomplete="on" - label-position="left"> - - <div class="title-container"> - <h3 class="title">楦胯挋鏅鸿兘鐢靛瓙閿佺鐞嗗钩鍙�</h3> - </div> - - <el-form-item prop="username"> - <span class="svg-container"> - <svg-icon icon-class="user" /> - </span> - <el-input ref="username" v-model="loginForm.username" placeholder="Username" name="username" type="text" - tabindex="1" autocomplete="on" /> - </el-form-item> - - <el-tooltip v-model="capsTooltip" :content="passwordType === 'password' ?'鏄剧ず瀵嗙爜':'闅愯棌瀵嗙爜'" placement="right" manual> - <el-form-item prop="password"> + <div class="site-title"> + <svg-icon icon-class="lock1"></svg-icon>楦胯挋鏅鸿兘鐢靛瓙閿佺鐞嗗钩鍙� + </div> + <div class="img-hm"></div> + <div class="login-wrap"> + <el-form + ref="loginFormRef" + :model="loginForm" + :rules="loginRules" + class="login-form" + autocomplete="on" + label-position="left" + > + <div class="title-container"> + <h3 class="title">鐢ㄦ埛鐧诲綍</h3> + </div> + <el-form-item class="input-item" prop="username"> <span class="svg-container"> - <svg-icon icon-class="password" /> + <svg-icon icon-class="user" /> </span> - <el-input :key="passwordType" ref="password" v-model="loginForm.password" :type="passwordType" - placeholder="Password" name="password" tabindex="2" autocomplete="on" @keyup="checkCapslock" - @blur="capsTooltip = false" @keyup.enter="handleLogin" /> - <span class="show-pwd" @click="showPwd"> - <svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" /> - </span> + <el-input + ref="username" + v-model="loginForm.username" + placeholder="Username" + name="username" + type="text" + tabindex="1" + autocomplete="on" + /> </el-form-item> - </el-tooltip> - - <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:40px;" - @click.prevent="handleLogin">鐧诲綍</el-button> - </el-form> + <el-tooltip + v-model="capsTooltip" + :content="passwordType === 'password' ?'鏄剧ず瀵嗙爜':'闅愯棌瀵嗙爜'" + placement="right" + manual + > + <el-form-item class="input-item" prop="password"> + <span class="svg-container"> + <svg-icon icon-class="password" /> + </span> + <el-input + :key="passwordType" + ref="password" + v-model="loginForm.password" + :type="passwordType" + placeholder="Password" + name="password" + tabindex="2" + autocomplete="on" + @keyup="checkCapslock" + @blur="capsTooltip = false" + @keyup.enter="handleLogin" + /> + <span class="show-pwd" @click="showPwd"> + <svg-icon + :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" + /> + </span> + </el-form-item> + </el-tooltip> + <el-button + :loading="loading" + type="primary" + style="width:100%; height:50px; margin-bottom:40px;" + @click.prevent="handleLogin" + >鐧诲綍</el-button + > + </el-form> + </div> <div class="tools-container"> - <div class="tools-item" :class="uKeyState" v-if="uKey" @click="uKeyVisible = true"> + <div + class="tools-item" + :class="uKeyState" + v-if="uKey" + @click="uKeyVisible = true" + > <span class="ukey"></span> </div> <div class="tools-item" v-if="faceCfg" @click="faceVisible = true"> @@ -41,295 +83,287 @@ </div> </div> <!-- uKey鐨勯獙璇� --> - <el-dialog title="uKey缁戝畾" width="750px" v-model="uKeyVisible" :close-on-click-modal="false" top="0" - class="dialog-center" :modal-append-to-body="false"> + <el-dialog + title="uKey缁戝畾" + width="750px" + v-model="uKeyVisible" + :close-on-click-modal="false" + top="0" + class="dialog-center" + :modal-append-to-body="false" + > <ukey-bind v-if="uKeyVisible" v-model:visible="uKeyVisible"></ukey-bind> </el-dialog> - + <!-- 浜鸿劯鐧婚檰 --> - <el-dialog title="浜鸿劯鐧婚檰" width="480px" v-model="faceVisible" :close-on-click-modal="false" top="0" - class="dialog-center" :modal-append-to-body="false"> - <face-login v-if="faceVisible" ></face-login> + <el-dialog + title="浜鸿劯鐧婚檰" + width="480px" + v-model="faceVisible" + :close-on-click-modal="false" + top="0" + class="dialog-center" + :modal-append-to-body="false" + > + <face-login v-if="faceVisible"></face-login> </el-dialog> </div> </template> -<script lang="ts"> - import { defineComponent } from 'vue'; - import { ElMessage, type FormItemRule } from 'element-plus'; - import type { IForm } from '@/types/element-plus'; - import store from '@/store'; - import { mapState } from 'pinia'; - import config from '@/utils/config.js'; - import UkeyBind from './components/UKeyBind/index.vue'; - import useElement from '@/hooks/useElement.js'; - import { uKeyLogin } from '@/api/user'; - import FaceLogin from './components/face/FaceLogin.vue'; - import { getToken, removeToken, setToken, getUname, setUname, removeUname, getUrole, setUrole, removeUrole } from '@/utils/auth'; +<script setup name="Login"> +import { ref, computed, reactive, watch, onMounted, nextTick } from 'vue'; +import { ElMessage } from 'element-plus'; - const { $loading, $confirm, $message } = useElement(); - const { ukey, face } = config; +import { mapState } from 'pinia'; +import config from '@/utils/config.js'; +import UkeyBind from './components/UKeyBind/index.vue'; +import useElement from '@/hooks/useElement.js'; +import { uKeyLogin } from '@/api/user'; +import FaceLogin from './components/face/FaceLogin.vue'; +import { getToken, removeToken, setToken, getUname, setUname, removeUname, getUrole, setUrole, removeUrole } from '@/utils/auth'; +import { useUserStore } from '@/store/user'; +import { useSettingsStore } from '@/store/settings'; +import { useUKeyStore } from '@/store/ukey'; +import { storeToRefs } from 'pinia'; +import { useRoute, useRouter } from 'vue-router'; - interface QueryType { - // 鑷畾涔塳ey 浠绘剰瀛楃涓� - [propname: string]: string - } +const route = useRoute(); +const router = useRouter(); - export default defineComponent({ - name: 'Login', - data() { - const validateUsername: FormItemRule['validator'] = (_rule, value, callback) => { - if (!value.trim()) { - callback(new Error('璇疯緭鍏ョ敤鎴峰悕')); - } else { - callback(); - } - }; - const validatePassword: FormItemRule['validator'] = (_rule, value, callback) => { - if (value.length < 1) { - callback(new Error('璇疯緭鍏ュ瘑鐮侊紝鑷冲皯1浣�')); - } else { - callback(); - } - }; - return { - uKey: ukey, - faceCfg: face, - uKeyVisible: false, - faceVisible: false, - loginForm: { - username: '', - password: '' - }, - loginRules: { - username: [{ required: true, trigger: 'blur', validator: validateUsername }], - password: [{ required: true, trigger: 'blur', validator: validatePassword }] - }, - passwordType: 'password', - capsTooltip: false, - loading: false, - showDialog: false, - redirect: undefined, - otherQuery: {} - }; - }, - components: { - UkeyBind, - FaceLogin, - }, - computed: { +const userStore = useUserStore(); +const settingsStore = useSettingsStore(); +const ukeyStore = useUKeyStore(); - uKeyState() { - let state = -1; - let uKey = store.ukey(); - let cls = "state-error"; - // 璁剧疆uKey鐨勭姸鎬� - if (uKey.connect) { - if (uKey.isIn) { - state = 1; - } else { - state = 0; - } - } else { - state = -1; - } - // 鏍规嵁鐘舵�佺‘瀹歶Key鍥捐〃鐨勯鑹� - switch (state) { - case 0: - cls = "state-default"; - break; - case 1: - cls = ""; - break; - default: - cls = "state-error"; - break; - } - return cls; - }, - ...mapState(store.ukey, ['isIn', 'connect', 'id']) - }, - watch: { - $route: { - handler: function (route) { - const query = route.query; - if (query) { - this.redirect = query.redirect; - this.otherQuery = this.getOtherQuery(query); - } - }, - immediate: true - } - }, - created() { - // window.addEventListener('storage', this.afterQRScan) - }, - mounted() { - if (this.loginForm.username === '') { - (this.$refs.username as HTMLElement).focus(); - } else if (this.loginForm.password === '') { - (this.$refs.password as HTMLElement).focus(); - } - this.themeChange('blue-theme'); - }, - unmounted() { - // window.removeEventListener('storage', this.afterQRScan) - }, - methods: { - themeChange(val) { - store.settings().changeSetting({ - key: 'theme', - value: val - }); - }, - checkCapslock(e) { - const { key } = e; - this.capsTooltip = key && key.length === 1 && (key >= 'A' && key <= 'Z'); - }, - showPwd() { - if (this.passwordType === 'password') { - this.passwordType = ''; - } else { - this.passwordType = 'password'; - } - this.$nextTick(() => { - (this.$refs.password as HTMLElement).focus(); - }); - }, - handleLogin() { - if (ukey) { - this.uKeyLoginFn(); - } else { - this.normalLogin(); - } - }, - uKeyLoginFn() { - // - if (!this.connect) { - $message.error("璇峰厛瀹夎uKey瀹㈡埛绔湇鍔�"); - return; - } - if (!this.isIn) { - $message.warning("璇峰厛鎻掑叆uKey"); - return; - } +const { isIn, connect, id } = storeToRefs(ukeyStore); +const { login } = userStore; +const { changeSetting } = settingsStore; - // 鏍¢獙鐢ㄦ埛 - if (this.loginForm.username && this.loginForm.password) { - // 寮�鍚瓑寰呮 - this.loading = true; - uKeyLogin( - this.loginForm.username, - this.loginForm.password, - this.id - ) - .then((res) => { - // 瀵圭粨鏋滆繘琛屽鐞� - console.log('res', res, '============='); - let { code, data, data2, msg } = res; - this.loading = false; - if (code && data) { - setUname(data2.uname); - setToken('admin'); - setUrole(data2.urole); - ElMessage({ - message: msg, - type: 'success' - }); - this.$router.push({ path: this.redirect || '/', query: this.otherQuery }); - } else { - ElMessage({ - message: msg, - type: 'error' - }); - } +const { $loading, $confirm, $message } = useElement(); +const { ukey: _ukey, face } = config; - }) - .catch((error) => { - // 鍏抽棴绛夊緟 - this.loading = false; - console.log(error); - $message.error("缃戠粶寮傚父"); - }); - } else { - $message.error("鐢ㄦ埛鍚嶆垨瀵嗙爜涓嶈兘涓虹┖"); - } - }, - normalLogin() { - (this.$refs.loginForm as IForm).validate(valid => { - return new Promise((resolve, reject) => { - if (valid) { - this.loading = true; - store.user().login(this.loginForm) - .then((res) => { - this.loading = false; - let { code, data, msg } = res; - if (code && data) { - ElMessage({ - message: msg, - type: 'success' - }); - this.$router.push({ path: this.redirect || '/', query: this.otherQuery }); - } else { - ElMessage({ - message: msg, - type: 'error' - }); - } - }).catch((error) => { - this.loading = false; - // 璇锋眰澶辫触锛屽鐞嗛敊璇� - if (error.message === 'Network Error') { - // 濡傛灉鏄綉缁滈敊璇紝鏄剧ず涓枃娑堟伅 - ElMessage({ - message: '缃戠粶閿欒锛岃妫�鏌ユ偍鐨勭綉缁滆繛鎺ユ垨鏈嶅姟鍣ㄧ姸鎬併��', - type: 'error' - }); - } else { - // 鍏朵粬绫诲瀷鐨勯敊璇紝鍙互鏍规嵁闇�瑕佸鐞� - ElMessage({ - message: error, - type: 'warning' - }); - } - }).finally(() => { - resolve(); - }); - } else { - console.log('error submit!!'); - reject(); - } - }); - }); - }, - getOtherQuery(query: QueryType) { - return Object.keys(query).reduce((acc: QueryType, cur) => { - if (cur !== 'redirect') { - acc[cur] = query[cur]; - } - return acc; - }, {}); - } - // afterQRScan() { - // if (e.key === 'x-admin-oauth-code') { - // const code = getQueryObject(e.newValue) - // const codeMap = { - // wechat: 'code', - // tencent: 'code' - // } - // const type = codeMap[this.auth_type] - // const codeName = code[type] - // if (codeName) { - // store.user().LoginByThirdparty(codeName).then(() => { - // this.$router.push({ path: this.redirect || '/' }) - // }) - // } else { - // alert('绗笁鏂圭櫥褰曞け璐�') - // } - // } - // } +const uKeyState = computed(() => { + let state = -1; + let cls = "state-error"; + // 璁剧疆uKey鐨勭姸鎬� + if (connect.value) { + if (isIn.value) { + state = 1; + } else { + state = 0; } + } else { + state = -1; + } + // 鏍规嵁鐘舵�佺‘瀹歶Key鍥捐〃鐨勯鑹� + switch (state) { + case 0: + cls = "state-default"; + break; + case 1: + cls = ""; + break; + default: + cls = "state-error"; + break; + } + return cls; +}); + +const validateUsername = (_rule, value, callback) => { + if (!value.trim()) { + callback(new Error('璇疯緭鍏ョ敤鎴峰悕')); + } else { + callback(); + } +}; +const validatePassword = (_rule, value, callback) => { + if (value.length < 1) { + callback(new Error('璇疯緭鍏ュ瘑鐮侊紝鑷冲皯1浣�')); + } else { + callback(); + } +}; + +const uKey = ref(_ukey); +const faceCfg = ref(face); + +const uKeyVisible = ref(false); +const faceVisible = ref(false); +const loginFormRef = ref(); +const username = ref(); +const password = ref(); +const loginForm = reactive({ + username: '', + password: '' +}); +const loginRules = reactive({ + username: [{ required: true, trigger: 'blur', validator: validateUsername }], + password: [{ required: true, trigger: 'blur', validator: validatePassword }] +}); + +const passwordType = ref('password'); +const capsTooltip = ref(false); +const loading = ref(false); +const showDialog = ref(false); +const redirect = ref(undefined); +const otherQuery = ref({}); + +watch( + () => route.path, + () => { + let query = route.query; + if (query) { + redirect.value = query.redirect; + otherQuery.value = getOtherQuery(query); + } + }, + { + immediate: true + } +); + +onMounted(() => { + if (loginForm.username === '') { + username.value.focus(); + } else if (loginForm.password === '') { + password.value.focus(); + } + themeChange('blue-theme'); +}); + +function themeChange(val) { + changeSetting({ + key: 'theme', + value: val }); +} + +function checkCapslock(e) { + const { key } = e; + capsTooltip.value = key && key.length === 1 && (key >= 'A' && key <= 'Z'); +} + +function showPwd() { + if (passwordType.value === 'password') { + passwordType.value = ''; + } else { + passwordType.value = 'password'; + } + nextTick(() => { + password.value.focus(); + }) +} + +function handleLogin() { + if (uKey.value) { + uKeyLoginFn(); + } else { + normalLogin(); + } +} + +function uKeyLoginFn() { + if (!connect.value) { + $message.error("璇峰厛瀹夎uKey瀹㈡埛绔湇鍔�"); + return; + } + if (!isIn.value) { + $message.warning("璇峰厛鎻掑叆uKey"); + return; + } + // 鏍¢獙鐢ㄦ埛 + if (loginForm.username && loginForm.password) { + // 寮�鍚瓑寰呮 + loading.value = true; + uKeyLogin( + loginForm.username, + loginForm.password, + id.value + ) + .then((res) => { + let { code, data, data2, msg } = res; + loading.value = false; + if (code && data) { + setUname(data2.uname); + setToken('admin'); + setUrole(data2.urole); + ElMessage({ + message: msg, + type: 'success' + }); + router.push({ path: redirect.value || '/', query: otherQuery.value }); + } else { + ElMessage({ + message: msg, + type: 'error' + }); + } + }).catch((error) => { + loading.value = false; + console.log(error); + $message.error("缃戠粶寮傚父"); + }) + } else { + $message.error("鐢ㄦ埛鍚嶆垨瀵嗙爜涓嶈兘涓虹┖"); + } +} + +function normalLogin() { + loginFormRef.value.validate(valid => { + return new Promise((resolve, reject) => { + if (valid) { + loading.value = true; + login(loginForm) + .then((res) => { + loading.value = false; + let { code, data, msg } = res; + if (code && data) { + ElMessage({ + message: msg, + type: 'success' + }); + router.push({ path: redirect.value || '/', query: otherQuery.value }); + } else { + ElMessage({ + message: msg, + type: 'error' + }); + } + }).catch((error) => { + loading.value = false; + console.log('error', error, '============='); + + if (error?.message === 'Network Error') { + // 濡傛灉鏄綉缁滈敊璇紝鏄剧ず涓枃娑堟伅 + ElMessage({ + message: '缃戠粶閿欒锛岃妫�鏌ユ偍鐨勭綉缁滆繛鎺ユ垨鏈嶅姟鍣ㄧ姸鎬併��', + type: 'error' + }); + } else { + ElMessage({ + message: error, + type: 'warning' + }); + } + }).finally(() => { + loading.value = false; + resolve(); + }); + } else { + reject(); + } + }) + }); +} + +function getOtherQuery(query) { + return Object.keys(query).reduce((acc, cur) => { + if (cur !== 'redirect') { + acc[cur] = query[cur]; + } + return acc; + }, {}); +} </script> <style lang="scss"> @@ -338,7 +372,9 @@ $bg: #283443; $light_gray: #fff; -$cursor: #fff; +$cursor: #999; +$input_bg: rgba(0, 0, 0, 0.1); +$input_color: #000; @supports (-webkit-mask: none) and (not (cater-color: $cursor)) { .login-container .el-input input { @@ -363,13 +399,13 @@ input { padding: 12px 5px 12px 15px; - color: $light_gray; + color: $input_color; height: 47px; caret-color: $cursor; &:-webkit-autofill { - box-shadow: 0 0 0px 1000px $bg inset !important; - -webkit-text-fill-color: $cursor !important; + box-shadow: 0 0 0px 1000px $input_bg inset !important; + -webkit-text-fill-color: $input_color !important; } } } @@ -391,19 +427,43 @@ .login-container { min-height: 100%; width: 100%; - background-image: url("./images/login_bg.jpg"); + background-image: url("@/assets/images/login-bg.jpg"); background-size: 100% 100%; background-repeat: no-repeat; overflow: hidden; - .login-form { + .login-wrap { + // display: none; position: relative; width: 520px; max-width: 100%; - padding: 36px 35px 36px; + // margin: 260px 10% 0 auto; margin: 260px auto 0; + transform: translate(clamp(0px, calc(36vw - 50%), 50vw), 0); + padding: 8px; overflow: hidden; - background-color: #1d4167; + // background-color: #1d4167; + background: rgba(255, 255, 255, 0.2); + border: 2px solid #0d3c68; + + .login-form { + background: #fff; + padding: 36px 70px 36px; + border: 2px solid #064ec3; + } + .input-item { + margin-bottom: 28px; + } + } + + .site-title { + font-size: 30px; + font-weight: 700; + color: #07fcff; + position: absolute; + left: 0; + top: 12%; + transform: translate(clamp(0px, calc(18vw - 50%), 50vw), -50%); } .tips { @@ -418,6 +478,16 @@ } } + .img-hm { + position: absolute; + width: clamp(500px, 50vw, 800px); + height: clamp(300px, 80vh, 80vh); + background: url(@/assets/images/img-hm.png) center center / contain no-repeat; + left: 0; + top: 20%; + transform: translate(clamp(0px, calc(24vw - 50%), 50vw), 0); + } + .svg-container { padding: 6px 5px 6px 15px; color: $dark_gray; @@ -430,10 +500,10 @@ position: relative; .title { - font-size: 26px; - color: $light_gray; + font-size: 18px; + color: #0296f3; margin: 0px auto 40px auto; - text-align: center; + // text-align: center; font-weight: bold; } } @@ -506,6 +576,7 @@ .tools-container .state-error .iconfont { color: #ff0000; } + :deep(.dialog-center) { box-sizing: content-box; } -- Gitblit v1.9.1