From 45792b3776cda2e1ada31755ffc226a663f90b48 Mon Sep 17 00:00:00 2001
From: zouyu <2723363702@qq.com>
Date: 星期三, 19 三月 2025 15:32:23 +0800
Subject: [PATCH] 锁屏功能

---
 src/layout/components/lock/index.vue |  121 +++++++++++++
 src/permission.js                    |   10 
 src/views/login.vue                  |    6 
 src/utils/store.js                   |  103 +++++++++++
 src/layout/components/Navbar.vue     |   78 ++++----
 src/layout/components/top-lock.vue   |   73 ++++++++
 src/store/getters.js                 |    2 
 src/store/modules/app.js             |   56 +++++
 src/assets/images/lock_bg.png        |    0 
 src/const/website.js                 |   25 ++
 src/router/index.js                  |    5 
 src/utils/validate.js                |   32 +++
 12 files changed, 459 insertions(+), 52 deletions(-)

diff --git a/src/assets/images/lock_bg.png b/src/assets/images/lock_bg.png
new file mode 100644
index 0000000..eb92779
--- /dev/null
+++ b/src/assets/images/lock_bg.png
Binary files differ
diff --git a/src/const/website.js b/src/const/website.js
new file mode 100644
index 0000000..4832ead
--- /dev/null
+++ b/src/const/website.js
@@ -0,0 +1,25 @@
+export default {
+  title: 'ZTTC-LIMS',
+  copyright: 'Copyright 漏 2021 chinaztt.com. All rights reserved.',
+  isFirstPage: true, // 閰嶇疆棣栭〉涓嶅彲鍏抽棴
+  key: 'ztt', // 閰嶇疆涓婚敭,鐩墠鐢ㄤ簬瀛樺偍
+  whiteList: ['/login', '/404', '/401', '/lock'], // 閰嶇疆鏃犳潈闄愬彲浠ヨ闂殑椤甸潰
+  whiteTagList: ['/login', '/404', '/401', '/lock'], // 閰嶇疆涓嶆坊鍔爐ags椤甸潰 锛�'/advanced-router/mutative-detail/*'鈥斺��*涓洪�氶厤绗︼級
+  fistPage: {
+    label: '棣栭〉',
+    value: '/wel/index',
+    params: {},
+    query: {},
+    group: [],
+    close: false
+  },
+  // 閰嶇疆鑿滃崟鐨勫睘鎬�
+  menu: {
+    props: {
+      label: 'label',
+      path: 'path',
+      icon: 'icon',
+      children: 'children'
+    }
+  }
+}
diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue
index 79b01c4..f5f4cb5 100644
--- a/src/layout/components/Navbar.vue
+++ b/src/layout/components/Navbar.vue
@@ -9,8 +9,7 @@
       <span class="label">LIMS瀹為獙瀹ょ鐞嗙郴缁�</span>
     </div>
     <div class="right-menu">
-      <div class="avatar-wrapper">
-        <!-- <img :src="avatar" class="user-avatar" /> -->
+      <!-- <div class="avatar-wrapper">
         <el-avatar :size="28">{{ nickName.substring(0, 1) }}</el-avatar>
         <span class="userName">{{ nickName }}</span>
         <img
@@ -19,54 +18,51 @@
           @click="logout"
           title="閫�鍑鸿处鍙�"
         />
-      </div>
-      <!--      <template v-if="device!=='mobile'">-->
-      <!--        <search id="header-search" class="right-menu-item" />-->
+      </div> -->
+      <template v-if="device !== 'mobile'">
+        <el-tooltip
+          class="right-menu-item"
+          effect="dark"
+          content="閿佸睆"
+          placement="bottom"
+        >
+          <top-lock />
+        </el-tooltip>
+      </template>
 
-      <!--        <el-tooltip content="婧愮爜鍦板潃" effect="dark" placement="bottom">-->
-      <!--          <ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />-->
-      <!--        </el-tooltip>-->
-
-      <!--        <el-tooltip content="鏂囨。鍦板潃" effect="dark" placement="bottom">-->
-      <!--          <ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />-->
-      <!--        </el-tooltip>-->
-
-      <!--        <screenfull id="screenfull" class="right-menu-item hover-effect" />-->
-
-      <!--        <el-tooltip content="甯冨眬澶у皬" effect="dark" placement="bottom">-->
-      <!--          <size-select id="size-select" class="right-menu-item hover-effect" />-->
-      <!--        </el-tooltip>-->
-
-      <!--      </template>-->
-
-      <!--      <div class="avatar-container">-->
-      <!--        <el-dropdown-menu slot="dropdown">-->
-      <!--          <router-link to="/user/profile">-->
-      <!--            <el-dropdown-item>涓汉涓績</el-dropdown-item>-->
-      <!--          </router-link>-->
-      <!--          <el-dropdown-item @click.native="setting = true">-->
-      <!--            <span>甯冨眬璁剧疆</span>-->
-      <!--          </el-dropdown-item>-->
-      <!--          <el-dropdown-item divided @click.native="logout">-->
-      <!--            <span>閫�鍑虹櫥褰�</span>-->
-      <!--          </el-dropdown-item>-->
-      <!--        </el-dropdown-menu>-->
-      <!--      </div>-->
+      <el-dropdown
+        class="avatar-container right-menu-item hover-effect"
+        trigger="click"
+      >
+        <div class="avatar-wrapper">
+          <img :src="avatar" class="user-avatar" />
+          <i class="el-icon-caret-bottom" />
+        </div>
+        <el-dropdown-menu slot="dropdown">
+          <router-link to="/user/profile">
+            <el-dropdown-item>涓汉涓績</el-dropdown-item>
+          </router-link>
+          <el-dropdown-item @click.native="setting = true">
+            <span>甯冨眬璁剧疆</span>
+          </el-dropdown-item>
+          <el-dropdown-item divided @click.native="logout">
+            <span>閫�鍑虹櫥褰�</span>
+          </el-dropdown-item>
+        </el-dropdown-menu>
+      </el-dropdown>
     </div>
   </div>
 </template>
 
 <script>
-import { mapGetters } from "vuex";
+import { mapGetters, mapState } from "vuex";
 import Breadcrumb from "@/components/Breadcrumb";
 import TopNav from "@/components/TopNav";
 import Hamburger from "@/components/Hamburger";
 import Screenfull from "@/components/Screenfull";
 import SizeSelect from "@/components/SizeSelect";
 import Search from "@/components/HeaderSearch";
-import RuoYiGit from "@/components/RuoYi/Git";
-import RuoYiDoc from "@/components/RuoYi/Doc";
-
+import topLock from "./top-lock";
 export default {
   components: {
     Breadcrumb,
@@ -75,10 +71,12 @@
     Screenfull,
     SizeSelect,
     Search,
-    RuoYiGit,
-    RuoYiDoc,
+    topLock,
   },
   computed: {
+    ...mapState({
+      showLock: (state) => state.app.showLock,
+    }),
     ...mapGetters(["avatar", "device", "nickName"]),
     setting: {
       get() {
diff --git a/src/layout/components/lock/index.vue b/src/layout/components/lock/index.vue
new file mode 100644
index 0000000..9a345fb
--- /dev/null
+++ b/src/layout/components/lock/index.vue
@@ -0,0 +1,121 @@
+<template>
+  <div class="lock-container">
+    <div class="lock-form animated bounceInDown">
+      <div class="animated" :class="{ shake: passwdError, bounceOut: pass }">
+        <h3 class="title">{{ loginUserInfo.nickName }}</h3>
+        <el-input
+          placeholder="璇疯緭鍏ョ櫥褰曞瘑鐮�"
+          type="password"
+          class="input-with-select animated"
+          v-model="passwd"
+          @keyup.enter.native="handleLogin"
+        >
+          <el-button
+            slot="append"
+            icon="el-icon-unlock"
+            @click="handleLogin"
+          ></el-button>
+          <el-button
+            slot="append"
+            icon="el-icon-switch-button"
+            @click="handleLogout"
+          ></el-button>
+        </el-input>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import { mapGetters } from "vuex";
+export default {
+  name: "lock",
+  data() {
+    return {
+      passwd: "",
+      passwdError: false,
+      pass: false,
+    };
+  },
+  created() {},
+  mounted() {},
+  computed: {
+    ...mapGetters([
+      "visitedViews",
+      "iframeViews",
+      "cachedViews",
+      "lockPasswd",
+      "loginUserInfo",
+    ]),
+  },
+  props: [],
+  methods: {
+    handleLogout() {
+      this.$confirm("鏄惁閫�鍑虹郴缁�, 鏄惁缁х画?", "鎻愮ず", {
+        confirmButtonText: "纭畾",
+        cancelButtonText: "鍙栨秷",
+        closeOnClickModal: false,
+        type: "warning",
+      }).then(() => {
+        this.$store.dispatch("LogOut").then(() => {
+          this.$router.push({ path: "/login" });
+        });
+      });
+    },
+    handleLogin() {
+      if (this.passwd != this.lockPasswd) {
+        this.passwd = "";
+        this.$message({
+          message: "瑙i攣瀵嗙爜閿欒,璇烽噸鏂拌緭鍏�",
+          type: "error",
+        });
+        this.passwdError = true;
+        setTimeout(() => {
+          this.passwdError = false;
+        }, 1000);
+        return;
+      }
+      this.pass = true;
+      setTimeout(() => {
+        this.$store.dispatch("app/clearLock");
+        //閲嶅畾鍚戣矾鐢辫繕闇�璋冩暣
+        let path =
+          this.visitedViews && this.visitedViews.length > 0
+            ? this.visitedViews.pop().fullPath
+            : "/index";
+        this.$router.push({
+          path: path,
+        });
+      }, 1000);
+    },
+  },
+  components: {},
+};
+</script>
+
+<style lang="scss">
+.lock-container {
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  position: relative;
+  .title {
+    text-align: center;
+    margin-bottom: 8px;
+  }
+}
+.lock-container::before {
+  z-index: -999;
+  content: "";
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  background-image: url("~@/assets/images/lock_bg.png");
+  background-size: cover;
+}
+.lock-form {
+  width: 300px;
+}
+</style>
diff --git a/src/layout/components/top-lock.vue b/src/layout/components/top-lock.vue
new file mode 100644
index 0000000..90acb03
--- /dev/null
+++ b/src/layout/components/top-lock.vue
@@ -0,0 +1,73 @@
+<template>
+  <span>
+    <i class="fa fa-lock" @click="handleLock"></i>
+    <el-dialog
+      title="璁剧疆閿佸睆瀵嗙爜"
+      :visible.sync="box"
+      width="30%"
+      append-to-body
+    >
+      <el-form :model="form" ref="form" label-width="80px">
+        <el-form-item
+          label="閿佸睆瀵嗙爜"
+          prop="passwd"
+          :rules="[{ required: true, message: '閿佸睆瀵嗙爜涓嶈兘涓虹┖' }]"
+        >
+          <el-input
+            v-model="form.passwd"
+            placeholder="璇疯緭鍏ラ攣灞忓瘑鐮�"
+          ></el-input>
+        </el-form-item>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="handleSetLock">纭� 瀹�</el-button>
+      </span>
+    </el-dialog>
+  </span>
+</template>
+
+<script>
+import { validatenull } from "@/utils/validate";
+import { mapGetters } from "vuex";
+export default {
+  name: "top-lock",
+  data() {
+    return {
+      box: false,
+      form: {
+        passwd: "",
+      },
+    };
+  },
+  created() {},
+  mounted() {},
+  computed: {
+    ...mapGetters(["lockPasswd"]),
+  },
+  props: [],
+  methods: {
+    handleSetLock() {
+      this.$refs["form"].validate((valid) => {
+        if (valid) {
+          this.$store.dispatch("app/setLockPasswd", this.form.passwd);
+          // this.$store.commit("SET_LOCK_PASSWD", this.form.passwd);
+          this.handleLock();
+        }
+      });
+    },
+    handleLock() {
+      if (validatenull(this.lockPasswd)) {
+        this.box = true;
+        return;
+      }
+      this.$store.dispatch("app/setLock");
+      setTimeout(() => {
+        this.$router.push({ path: "/lock" });
+      }, 100);
+    },
+  },
+  components: {},
+};
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/permission.js b/src/permission.js
index b63bc64..aeb591e 100644
--- a/src/permission.js
+++ b/src/permission.js
@@ -26,7 +26,7 @@
     } else if (isWhiteList(to.path)) {
       next()
     } else {
-      if (store.getters.roles.length === 0) {
+        if (store.getters.roles.length === 0) {
         isRelogin.show = true
         // 鍒ゆ柇褰撳墠鐢ㄦ埛鏄惁宸叉媺鍙栧畬user_info淇℃伅
         store.dispatch('GetInfo').then(() => {
@@ -42,8 +42,12 @@
               next({ path: '/' })
             })
           })
-      } else {
-        next()
+        } else {
+          if (store.getters.isLock && to.path !=='/lock') {
+            next('/lock')
+          } else {
+            next()
+          }
       }
     }
   } else {
diff --git a/src/router/index.js b/src/router/index.js
index 0817afc..a476b13 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -74,6 +74,11 @@
     hidden: true,
   },
   {
+    path: "/lock",
+    component: () => import("@/layout/components/lock/index"),
+    hidden: true,
+  },
+  {
     path: "",
     component: Layout,
     redirect: "index",
diff --git a/src/store/getters.js b/src/store/getters.js
index 099391b..f44d244 100644
--- a/src/store/getters.js
+++ b/src/store/getters.js
@@ -19,5 +19,7 @@
   topbarRouters: (state) => state.permission.topbarRouters,
   defaultRoutes: (state) => state.permission.defaultRoutes,
   sidebarRouters: (state) => state.permission.sidebarRouters,
+  isLock: (state) => state.app.isLock,
+  lockPasswd: (state) => state.app.lockPasswd,
 };
 export default getters;
diff --git a/src/store/modules/app.js b/src/store/modules/app.js
index 3e22d1c..cdd6139 100644
--- a/src/store/modules/app.js
+++ b/src/store/modules/app.js
@@ -1,5 +1,6 @@
 import Cookies from 'js-cookie'
-
+import website from '@/const/website'
+import { getStore, removeStore, setStore } from '@/utils/store'
 const state = {
   sidebar: {
     opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true,
@@ -7,7 +8,11 @@
     hide: false
   },
   device: 'desktop',
-  size: Cookies.get('size') || 'medium'
+  size: Cookies.get('size') || 'medium',
+  lockPasswd: getStore({ name: 'lockPasswd' }) || '',
+  isLock: getStore({ name: 'isLock' }) || false,
+  website: website,
+  showLock: getStore({ name: 'showLock' }),
 }
 
 const mutations = {
@@ -37,7 +42,41 @@
   },
   SET_SIDEBAR_HIDE: (state, status) => {
     state.sidebar.hide = status
-  }
+  },
+  SET_LOCK_PASSWD: (state, lockPasswd) => {
+    state.lockPasswd = lockPasswd
+    setStore({
+      name: 'lockPasswd',
+      content: state.lockPasswd,
+      type: 'session'
+    })
+  },
+  CLEAR_LOCK: (state) => {
+    state.isLock = false
+    state.lockPasswd = ''
+    removeStore({
+      name: 'lockPasswd'
+    })
+    removeStore({
+      name: 'isLock',
+      type: 'session'
+    })
+  },
+  SET_LOCK: (state) => {
+    state.isLock = true
+    setStore({
+      name: 'isLock',
+      content: state.isLock,
+      type: 'session'
+    })
+  },
+  SET_SHOW_LOCK: (state, active) => {
+    state.showLock = active
+    setStore({
+      name: 'showLock',
+      content: state.showLock
+    })
+  },
 }
 
 const actions = {
@@ -55,7 +94,16 @@
   },
   toggleSideBarHide({ commit }, status) {
     commit('SET_SIDEBAR_HIDE', status)
-  }
+  },
+  setLockPasswd({ commit }, lockPasswd) {
+    commit('SET_LOCK_PASSWD', lockPasswd)
+  },
+  setLock({ commit }) {
+    commit('SET_LOCK')
+  },
+  clearLock({ commit }) {
+    commit('CLEAR_LOCK')
+  },
 }
 
 export default {
diff --git a/src/utils/store.js b/src/utils/store.js
new file mode 100644
index 0000000..c5b6880
--- /dev/null
+++ b/src/utils/store.js
@@ -0,0 +1,103 @@
+import { validatenull } from '@/utils/validate'
+import website from '@/const/website'
+
+const keyName = website.key + '-'
+/**
+ * 瀛樺偍localStorage
+ */
+export const setStore = (params = {}) => {
+  let { name, content, type } = params
+  name = keyName + name
+  const obj = {
+    dataType: typeof content,
+    content: content,
+    type: type,
+    datetime: new Date().getTime()
+  }
+  if (type) window.sessionStorage.setItem(name, JSON.stringify(obj))
+  else window.localStorage.setItem(name, JSON.stringify(obj))
+}
+/**
+ * 鑾峰彇localStorage
+ */
+
+export const getStore = (params = {}) => {
+  let { name, debug } = params
+  name = keyName + name
+  let obj = {}
+  let content
+  obj = window.sessionStorage.getItem(name)
+  if (validatenull(obj)) obj = window.localStorage.getItem(name)
+  if (validatenull(obj)) return
+  try {
+    obj = JSON.parse(obj)
+  } catch (e) {
+    return obj
+  }
+  if (debug) {
+    return obj
+  }
+  if (obj.dataType === 'string') {
+    content = obj.content
+  } else if (obj.dataType === 'number') {
+    content = Number(obj.content)
+  } else if (obj.dataType === 'boolean') {
+    content = eval(obj.content)
+  } else if (obj.dataType === 'object') {
+    content = obj.content
+  }
+  return content
+}
+/**
+ * 鍒犻櫎localStorage
+ */
+export const removeStore = (params = {}) => {
+  let { name, type } = params
+  name = keyName + name
+  if (type) {
+    window.sessionStorage.removeItem(name)
+  } else {
+    window.localStorage.removeItem(name)
+  }
+}
+
+/**
+ * 鑾峰彇鍏ㄩ儴localStorage
+ */
+export const getAllStore = (params = {}) => {
+  const list = []
+  const { type } = params
+  if (type) {
+    for (let i = 0; i <= window.sessionStorage.length; i++) {
+      list.push({
+        name: window.sessionStorage.key(i),
+        content: getStore({
+          name: window.sessionStorage.key(i),
+          type: 'session'
+        })
+      })
+    }
+  } else {
+    for (let i = 0; i <= window.localStorage.length; i++) {
+      list.push({
+        name: window.localStorage.key(i),
+        content: getStore({
+          name: window.localStorage.key(i)
+        })
+      })
+    }
+  }
+  return list
+}
+
+/**
+ * 娓呯┖鍏ㄩ儴localStorage
+ */
+export const clearStore = (params = {}) => {
+  const { type } = params
+  if (type) {
+    window.sessionStorage.clear()
+  } else {
+    window.localStorage.clear()
+  }
+}
diff --git a/src/utils/validate.js b/src/utils/validate.js
index 6a4c0c5..ca8d579 100644
--- a/src/utils/validate.js
+++ b/src/utils/validate.js
@@ -11,7 +11,7 @@
 }
 
 /**
- * 鍒ゆ柇value瀛楃涓叉槸鍚︿负绌� 
+ * 鍒ゆ柇value瀛楃涓叉槸鍚︿负绌�
  * @param {string} value
  * @returns {Boolean}
  */
@@ -23,7 +23,7 @@
 }
 
 /**
- * 鍒ゆ柇url鏄惁鏄痟ttp鎴杊ttps 
+ * 鍒ゆ柇url鏄惁鏄痟ttp鎴杊ttps
  * @param {string} url
  * @returns {Boolean}
  */
@@ -112,3 +112,31 @@
   }
   return Array.isArray(arg)
 }
+
+/**
+ * 鍒ゆ柇鏄惁涓虹┖
+ */
+export function validatenull(val) {
+  if (typeof val === 'boolean') {
+    return false
+  }
+  if (typeof val === 'number') {
+    return false
+  }
+  if (val instanceof Array) {
+    if (val.length === 0) return true
+  } else if (val instanceof Object) {
+    if (JSON.stringify(val) === '{}') return true
+  } else {
+    if (
+      val === 'null' ||
+      val == null ||
+      val === 'undefined' ||
+      val === undefined ||
+      val === ''
+    )
+      return true
+    return false
+  }
+  return false
+}
diff --git a/src/views/login.vue b/src/views/login.vue
index a67d503..1656428 100644
--- a/src/views/login.vue
+++ b/src/views/login.vue
@@ -41,7 +41,7 @@
             />
           </el-input>
         </el-form-item>
-        <el-form-item prop="code" v-if="captchaEnabled">
+        <!-- <el-form-item prop="code" v-if="captchaEnabled">
           <el-input
             v-model="loginForm.code"
             auto-complete="off"
@@ -58,7 +58,7 @@
           <div class="login-code">
             <img :src="codeUrl" @click="getCode" class="login-code-img" />
           </div>
-        </el-form-item>
+        </el-form-item> -->
         <el-checkbox
           v-model="loginForm.rememberMe"
           style="margin: 0px 0px 25px 0px"
@@ -121,7 +121,7 @@
       },
       loading: false,
       // 楠岃瘉鐮佸紑鍏�
-      captchaEnabled: true,
+      captchaEnabled: false,
       // 娉ㄥ唽寮�鍏�
       register: false,
       redirect: undefined,

--
Gitblit v1.9.3