he wei
2025-04-23 b9bd29a1a81f6f7de479e3cc3fdfe3d85fc660bf
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 class="site-title">
      <svg-icon icon-class="lock1"></svg-icon>鸿蒙智能电子锁管理平台
      </div>
      <el-form-item prop="username">
    <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="user" />
        </span>
        <el-input ref="username" v-model="loginForm.username" placeholder="Username" name="username" type="text"
          tabindex="1" autocomplete="on" />
          <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">
        <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" />
            <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'" />
              <svg-icon
                :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'"
              />
          </span>
        </el-form-item>
      </el-tooltip>
      <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:40px;"
        @click.prevent="handleLogin">登录</el-button>
        <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,24 +83,37 @@
      </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">
    <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';
<script setup name="Login">
import { ref, computed, reactive, watch, onMounted, nextTick } from 'vue';
import { ElMessage } from 'element-plus';
   import { mapState } from 'pinia';
   import config from '@/utils/config.js';
   import UkeyBind from './components/UKeyBind/index.vue';
@@ -66,66 +121,32 @@
   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';
const route = useRoute();
const router = useRouter();
const userStore = useUserStore();
const settingsStore = useSettingsStore();
const ukeyStore = useUKeyStore();
const { isIn, connect, id } = storeToRefs(ukeyStore);
const { login } = userStore;
const { changeSetting } = settingsStore;
   const { $loading, $confirm, $message } = useElement();
   const { ukey, face } = config;
const { ukey: _ukey, face } = config;
   interface QueryType {
      // 自定义key 任意字符串
      [propname: string]: string
   }
   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: {
         uKeyState() {
const uKeyState = computed(() => {
            let state = -1;
            let uKey = store.ukey();
            let cls = "state-error";
            // 设置uKey的状态
            if (uKey.connect) {
               if (uKey.isIn) {
   if (connect.value) {
      if (isIn.value) {
                  state = 1;
               } else {
                  state = 0;
@@ -146,88 +167,122 @@
                  break;
            }
            return cls;
         },
         ...mapState(store.ukey, ['isIn', 'connect', 'id'])
      },
      watch: {
         $route: {
            handler: function (route) {
               const query = route.query;
});
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) {
                  this.redirect = query.redirect;
                  this.otherQuery = this.getOtherQuery(query);
         redirect.value = query.redirect;
         otherQuery.value = 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();
);
onMounted(() => {
   if (loginForm.username === '') {
      username.value.focus();
   } else if (loginForm.password === '') {
      password.value.focus();
         }
         this.themeChange('blue-theme');
      },
      unmounted() {
         // window.removeEventListener('storage', this.afterQRScan)
      },
      methods: {
         themeChange(val) {
            store.settings().changeSetting({
   themeChange('blue-theme');
});
function themeChange(val) {
   changeSetting({
               key: 'theme',
               value: val
            });
         },
         checkCapslock(e) {
}
function 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';
   capsTooltip.value = key && key.length === 1 && (key >= 'A' && key <= 'Z');
            }
            this.$nextTick(() => {
               (this.$refs.password as HTMLElement).focus();
            });
         },
         handleLogin() {
            if (ukey) {
               this.uKeyLoginFn();
function showPwd() {
   if (passwordType.value === 'password') {
      passwordType.value = '';
            } else {
               this.normalLogin();
      passwordType.value = 'password';
            }
         },
         uKeyLoginFn() {
            //
            if (!this.connect) {
   nextTick(() => {
      password.value.focus();
   })
}
function handleLogin() {
   if (uKey.value) {
      uKeyLoginFn();
   } else {
      normalLogin();
   }
}
function uKeyLoginFn() {
   if (!connect.value) {
               $message.error("请先安装uKey客户端服务");
               return;
            }
            if (!this.isIn) {
   if (!isIn.value) {
               $message.warning("请先插入uKey");
               return;
            }
            // 校验用户
            if (this.loginForm.username && this.loginForm.password) {
   if (loginForm.username && loginForm.password) {
               // 开启等待框
               this.loading = true;
      loading.value = true;
               uKeyLogin(
                  this.loginForm.username,
                  this.loginForm.password,
                  this.id
         loginForm.username,
         loginForm.password,
         id.value
               )
                  .then((res) => {
                     // 对结果进行处理
                     console.log('res', res, '=============');
                     let { code, data, data2, msg } = res;
                     this.loading = false;
            loading.value = false;
                     if (code && data) {
                        setUname(data2.uname);
                        setToken('admin');
@@ -236,40 +291,7 @@
                           message: msg,
                           type: 'success'
                        });
                        this.$router.push({ path: this.redirect || '/', query: this.otherQuery });
                     } else {
                        ElMessage({
                           message: msg,
                           type: 'error'
                        });
                     }
                  })
                  .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 });
               router.push({ path: redirect.value || '/', query: otherQuery.value });
                           } else {
                              ElMessage({
                                 message: msg,
@@ -277,59 +299,71 @@
                              });
                           }
                        }).catch((error) => {
                           this.loading = false;
                           // 请求失败,处理错误
                           if (error.message === 'Network 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 {
                     console.log('error submit!!');
                     reject();
                  }
      })
               });
            });
         },
         getOtherQuery(query: QueryType) {
            return Object.keys(query).reduce((acc: QueryType, cur) => {
}
function getOtherQuery(query) {
   return Object.keys(query).reduce((acc, 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('第三方登录失败')
         //     }
         //   }
         // }
      }
   });
</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;
  }