Files
education/frontend/node_modules/.cache/vue-loader/4b8e41ed8ba18efea48daff672f1c0d8.json
T

1 line
18 KiB
JSON

{"remainingRequest":"C:\\Users\\Admin\\Desktop\\education\\frontend\\node_modules\\vue-loader\\lib\\index.js??vue-loader-options!C:\\Users\\Admin\\Desktop\\education\\frontend\\src\\views\\lock.vue?vue&type=script&lang=js","dependencies":[{"path":"C:\\Users\\Admin\\Desktop\\education\\frontend\\src\\views\\lock.vue","mtime":1787567871616},{"path":"C:\\Users\\Admin\\Desktop\\education\\frontend\\node_modules\\cache-loader\\dist\\cjs.js","mtime":1787309791955},{"path":"C:\\Users\\Admin\\Desktop\\education\\frontend\\node_modules\\babel-loader\\lib\\index.js","mtime":1787309793142},{"path":"C:\\Users\\Admin\\Desktop\\education\\frontend\\node_modules\\cache-loader\\dist\\cjs.js","mtime":1787309791955},{"path":"C:\\Users\\Admin\\Desktop\\education\\frontend\\node_modules\\vue-loader\\lib\\index.js","mtime":1787309793790}],"contextDependencies":[],"result":[{"type":"Buffer","data":"base64:Ly8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KDQppbXBvcnQgeyBtYXBHZXR0ZXJzIH0gZnJvbSAndnVleCcNCmltcG9ydCB7IHVubG9ja1NjcmVlbiB9IGZyb20gJ0AvYXBpL2xvZ2luJw0KaW1wb3J0IGRlZkF2YSBmcm9tICdAL2Fzc2V0cy9pbWFnZXMvcHJvZmlsZS5qcGcnDQoNCmV4cG9ydCBkZWZhdWx0IHsNCiAgbmFtZTogJ0xvY2tTY3JlZW4nLA0KICBkYXRhKCkgew0KICAgIHJldHVybiB7DQogICAgICBwYXNzd29yZDogJycsDQogICAgICBsb2FkaW5nOiBmYWxzZSwNCiAgICAgIGVycm9yTXNnOiAnJywNCiAgICAgIGlzU2hha2luZzogZmFsc2UsDQogICAgICBjdXJyZW50VGltZTogJycsDQogICAgICBjdXJyZW50RGF0ZTogJycsDQogICAgICB0aW1lcjogbnVsbCwNCiAgICAgIGFuaW1hdGlvbklkOiBudWxsLA0KICAgICAgcGFydGljbGVzOiBbXQ0KICAgIH0NCiAgfSwNCiAgY29tcHV0ZWQ6IHsNCiAgICAuLi5tYXBHZXR0ZXJzKFsnYXZhdGFyJywgJ25pY2tOYW1lJ10pDQogIH0sDQogIG1vdW50ZWQoKSB7DQogICAgdGhpcy5zdGFydENsb2NrKCkNCiAgICB0aGlzLmluaXRQYXJ0aWNsZXMoKQ0KICAgIHRoaXMuJG5leHRUaWNrKCgpID0+IHsNCiAgICAgIHRoaXMuJHJlZnMucGFzc3dvcmRJbnB1dCAmJiB0aGlzLiRyZWZzLnBhc3N3b3JkSW5wdXQuZm9jdXMoKQ0KICAgIH0pDQogIH0sDQogIGJlZm9yZURlc3Ryb3koKSB7DQogICAgY2xlYXJJbnRlcnZhbCh0aGlzLnRpbWVyKQ0KICAgIGNhbmNlbEFuaW1hdGlvbkZyYW1lKHRoaXMuYW5pbWF0aW9uSWQpDQogIH0sDQogIG1ldGhvZHM6IHsNCiAgICBvbkF2YXRhckVycm9yKGUpIHsNCiAgICAgIGUudGFyZ2V0LnNyYyA9IGRlZkF2YQ0KICAgIH0sDQogICAgc3RhcnRDbG9jaygpIHsNCiAgICAgIGNvbnN0IHVwZGF0ZSA9ICgpID0+IHsNCiAgICAgICAgY29uc3Qgbm93ID0gbmV3IERhdGUoKQ0KICAgICAgICBjb25zdCBoID0gU3RyaW5nKG5vdy5nZXRIb3VycygpKS5wYWRTdGFydCgyLCAnMCcpDQogICAgICAgIGNvbnN0IG0gPSBTdHJpbmcobm93LmdldE1pbnV0ZXMoKSkucGFkU3RhcnQoMiwgJzAnKQ0KICAgICAgICBjb25zdCBzID0gU3RyaW5nKG5vdy5nZXRTZWNvbmRzKCkpLnBhZFN0YXJ0KDIsICcwJykNCiAgICAgICAgdGhpcy5jdXJyZW50VGltZSA9IGAke2h9OiR7bX06JHtzfWANCiAgICAgICAgY29uc3QgZGF5cyA9IFsn5pif5pyf5pelJywn5pif5pyf5LiAJywn5pif5pyf5LqMJywn5pif5pyf5LiJJywn5pif5pyf5ZubJywn5pif5pyf5LqUJywn5pif5pyf5YWtJ10NCiAgICAgICAgdGhpcy5jdXJyZW50RGF0ZSA9IGAke25vdy5nZXRGdWxsWWVhcigpfeW5tCR7bm93LmdldE1vbnRoKCkrMX3mnIgke25vdy5nZXREYXRlKCl95pelICR7ZGF5c1tub3cuZ2V0RGF5KCldfWANCiAgICAgIH0NCiAgICAgIHVwZGF0ZSgpDQogICAgICB0aGlzLnRpbWVyID0gc2V0SW50ZXJ2YWwodXBkYXRlLCAxMDAwKQ0KICAgIH0sDQogICAgYXN5bmMgaGFuZGxlVW5sb2NrKCkgew0KICAgICAgaWYgKCF0aGlzLnBhc3N3b3JkKSB7DQogICAgICAgIHRoaXMuc2hvd0Vycm9yKCfor7fovpPlhaXlr4bnoIEnKQ0KICAgICAgICByZXR1cm4NCiAgICAgIH0NCiAgICAgIHRoaXMubG9hZGluZyA9IHRydWUNCiAgICAgIHRoaXMuZXJyb3JNc2cgPSAnJw0KICAgICAgdHJ5IHsNCiAgICAgICAgYXdhaXQgdW5sb2NrU2NyZWVuKHRoaXMucGFzc3dvcmQpDQogICAgICAgIGNvbnN0IGxvY2tQYXRoID0gdGhpcy4kc3RvcmUuZ2V0dGVycy5sb2NrUGF0aCAgLy8g5Y+W6ZSB5bGP5YmN55qE6Lev5b6EDQogICAgICAgIGF3YWl0IHRoaXMuJHN0b3JlLmRpc3BhdGNoKCdsb2NrL3VubG9ja1NjcmVlbicpDQogICAgICAgIHRoaXMuJHJvdXRlci5yZXBsYWNlKGxvY2tQYXRoKQ0KICAgICAgfSBjYXRjaCAoZXJyKSB7DQogICAgICAgIGNvbnN0IG1zZyA9IGVyci5tZXNzYWdlIHx8IGVyci50b1N0cmluZygpDQogICAgICAgIHRoaXMuc2hvd0Vycm9yKG1zZykNCiAgICAgICAgdGhpcy5wYXNzd29yZCA9ICcnDQogICAgICAgIHRoaXMuJHJlZnMucGFzc3dvcmRJbnB1dCAmJiB0aGlzLiRyZWZzLnBhc3N3b3JkSW5wdXQuZm9jdXMoKQ0KICAgICAgfSBmaW5hbGx5IHsNCiAgICAgICAgdGhpcy5sb2FkaW5nID0gZmFsc2UNCiAgICAgIH0NCiAgICB9LA0KICAgIHNob3dFcnJvcihtc2cpIHsNCiAgICAgIHRoaXMuZXJyb3JNc2cgPSBtc2cNCiAgICAgIHRoaXMuaXNTaGFraW5nID0gdHJ1ZQ0KICAgICAgc2V0VGltZW91dCgoKSA9PiB7IHRoaXMuaXNTaGFraW5nID0gZmFsc2UgfSwgNjAwKQ0KICAgIH0sDQogICAgZ29Mb2dpbigpIHsNCiAgICAgIHRoaXMuJHN0b3JlLmRpc3BhdGNoKCdsb2NrL3VubG9ja1NjcmVlbicpDQogICAgICB0aGlzLiRzdG9yZS5kaXNwYXRjaCgnTG9nT3V0JykudGhlbigoKSA9PiB7DQogICAgICAgIHRoaXMuJHJvdXRlci5wdXNoKCcvbG9naW4nKQ0KICAgICAgfSkNCiAgICB9LA0KICAgIC8vIOeykuWtkOiDjOaZrw0KICAgIGluaXRQYXJ0aWNsZXMoKSB7DQogICAgICBjb25zdCBjYW52YXMgPSB0aGlzLiRyZWZzLnBhcnRpY2xlQ2FudmFzDQogICAgICBpZiAoIWNhbnZhcykgcmV0dXJuDQogICAgICBjb25zdCBjdHggPSBjYW52YXMuZ2V0Q29udGV4dCgnMmQnKQ0KICAgICAgY29uc3QgcmVzaXplID0gKCkgPT4gew0KICAgICAgICBjYW52YXMud2lkdGggPSB3aW5kb3cuaW5uZXJXaWR0aA0KICAgICAgICBjYW52YXMuaGVpZ2h0ID0gd2luZG93LmlubmVySGVpZ2h0DQogICAgICB9DQogICAgICByZXNpemUoKQ0KICAgICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoJ3Jlc2l6ZScsIHJlc2l6ZSkNCiAgICAgIGNvbnN0IGNvdW50ID0gODANCiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgY291bnQ7IGkrKykgew0KICAgICAgICB0aGlzLnBhcnRpY2xlcy5wdXNoKHsNCiAgICAgICAgICB4OiBNYXRoLnJhbmRvbSgpICogY2FudmFzLndpZHRoLA0KICAgICAgICAgIHk6IE1hdGgucmFuZG9tKCkgKiBjYW52YXMuaGVpZ2h0LA0KICAgICAgICAgIHI6IE1hdGgucmFuZG9tKCkgKiAyICsgMSwNCiAgICAgICAgICBkeDogKE1hdGgucmFuZG9tKCkgLSAwLjUpICogMC42LA0KICAgICAgICAgIGR5OiAoTWF0aC5yYW5kb20oKSAtIDAuNSkgKiAwLjYsDQogICAgICAgICAgYWxwaGE6IE1hdGgucmFuZG9tKCkgKiAwLjUgKyAwLjINCiAgICAgICAgfSkNCiAgICAgIH0NCiAgICAgIGNvbnN0IGRyYXcgPSAoKSA9PiB7DQogICAgICAgIGN0eC5jbGVhclJlY3QoMCwgMCwgY2FudmFzLndpZHRoLCBjYW52YXMuaGVpZ2h0KQ0KICAgICAgICB0aGlzLnBhcnRpY2xlcy5mb3JFYWNoKHAgPT4gew0KICAgICAgICAgIGN0eC5iZWdpblBhdGgoKQ0KICAgICAgICAgIGN0eC5hcmMocC54LCBwLnksIHAuciwgMCwgTWF0aC5QSSAqIDIpDQogICAgICAgICAgY3R4LmZpbGxTdHlsZSA9IGByZ2JhKDI1NSwyNTUsMjU1LCR7cC5hbHBoYX0pYA0KICAgICAgICAgIGN0eC5maWxsKCkNCiAgICAgICAgICBwLnggKz0gcC5keA0KICAgICAgICAgIHAueSArPSBwLmR5DQogICAgICAgICAgaWYgKHAueCA8IDAgfHwgcC54ID4gY2FudmFzLndpZHRoKSBwLmR4ICo9IC0xDQogICAgICAgICAgaWYgKHAueSA8IDAgfHwgcC55ID4gY2FudmFzLmhlaWdodCkgcC5keSAqPSAtMQ0KICAgICAgICB9KQ0KICAgICAgICAvLyDov57nur8NCiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLnBhcnRpY2xlcy5sZW5ndGg7IGkrKykgew0KICAgICAgICAgIGZvciAobGV0IGogPSBpICsgMTsgaiA8IHRoaXMucGFydGljbGVzLmxlbmd0aDsgaisrKSB7DQogICAgICAgICAgICBjb25zdCBhID0gdGhpcy5wYXJ0aWNsZXNbaV0sIGIgPSB0aGlzLnBhcnRpY2xlc1tqXQ0KICAgICAgICAgICAgY29uc3QgZGlzdCA9IE1hdGguaHlwb3QoYS54IC0gYi54LCBhLnkgLSBiLnkpDQogICAgICAgICAgICBpZiAoZGlzdCA8IDEyMCkgew0KICAgICAgICAgICAgICBjdHguYmVnaW5QYXRoKCkNCiAgICAgICAgICAgICAgY3R4Lm1vdmVUbyhhLngsIGEueSkNCiAgICAgICAgICAgICAgY3R4LmxpbmVUbyhiLngsIGIueSkNCiAgICAgICAgICAgICAgY3R4LnN0cm9rZVN0eWxlID0gYHJnYmEoMjU1LDI1NSwyNTUsJHswLjE1ICogKDEgLSBkaXN0IC8gMTIwKX0pYA0KICAgICAgICAgICAgICBjdHgubGluZVdpZHRoID0gMC41DQogICAgICAgICAgICAgIGN0eC5zdHJva2UoKQ0KICAgICAgICAgICAgfQ0KICAgICAgICAgIH0NCiAgICAgICAgfQ0KICAgICAgICB0aGlzLmFuaW1hdGlvbklkID0gcmVxdWVzdEFuaW1hdGlvbkZyYW1lKGRyYXcpDQogICAgICB9DQogICAgICBkcmF3KCkNCiAgICB9DQogIH0NCn0NCg=="},{"version":3,"sources":["lock.vue"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"lock.vue","sourceRoot":"src/views","sourcesContent":["<template>\r\n <div class=\"lock-container\">\r\n <!-- 动态粒子背景 -->\r\n <canvas ref=\"particleCanvas\" class=\"particle-bg\"></canvas>\r\n\r\n <!-- 时钟 -->\r\n <div class=\"lock-time\">{{ currentTime }}</div>\r\n <div class=\"lock-date\">{{ currentDate }}</div>\r\n\r\n <!-- 锁屏卡片 -->\r\n <div class=\"lock-card\">\r\n <div class=\"avatar-wrap\">\r\n <img :src=\"avatar\" class=\"lock-avatar\" @error=\"onAvatarError\" />\r\n <div class=\"lock-icon\">🔒</div>\r\n </div>\r\n <div class=\"lock-username\">{{ nickName }}</div>\r\n <div class=\"lock-hint\">系统已锁定,请输入密码解锁</div>\r\n\r\n <div class=\"input-wrap\" :class=\"{ shake: isShaking }\">\r\n <input ref=\"passwordInput\" v-model=\"password\" type=\"password\" placeholder=\"请输入登录密码\" class=\"lock-input\" @keydown.enter=\"handleUnlock\" autocomplete=\"off\" />\r\n <button class=\"unlock-btn\" @click=\"handleUnlock\" :disabled=\"loading\">\r\n <span v-if=\"!loading\">→</span>\r\n <span v-else class=\"loading-dot\">···</span>\r\n </button>\r\n </div>\r\n\r\n <div v-if=\"errorMsg\" class=\"error-msg\">{{ errorMsg }}</div>\r\n\r\n <div class=\"lock-footer\">\r\n <a href=\"/login\" @click.prevent=\"goLogin\">退出重新登录</a>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script>\r\nimport { mapGetters } from 'vuex'\r\nimport { unlockScreen } from '@/api/login'\r\nimport defAva from '@/assets/images/profile.jpg'\r\n\r\nexport default {\r\n name: 'LockScreen',\r\n data() {\r\n return {\r\n password: '',\r\n loading: false,\r\n errorMsg: '',\r\n isShaking: false,\r\n currentTime: '',\r\n currentDate: '',\r\n timer: null,\r\n animationId: null,\r\n particles: []\r\n }\r\n },\r\n computed: {\r\n ...mapGetters(['avatar', 'nickName'])\r\n },\r\n mounted() {\r\n this.startClock()\r\n this.initParticles()\r\n this.$nextTick(() => {\r\n this.$refs.passwordInput && this.$refs.passwordInput.focus()\r\n })\r\n },\r\n beforeDestroy() {\r\n clearInterval(this.timer)\r\n cancelAnimationFrame(this.animationId)\r\n },\r\n methods: {\r\n onAvatarError(e) {\r\n e.target.src = defAva\r\n },\r\n startClock() {\r\n const update = () => {\r\n const now = new Date()\r\n const h = String(now.getHours()).padStart(2, '0')\r\n const m = String(now.getMinutes()).padStart(2, '0')\r\n const s = String(now.getSeconds()).padStart(2, '0')\r\n this.currentTime = `${h}:${m}:${s}`\r\n const days = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六']\r\n this.currentDate = `${now.getFullYear()}年${now.getMonth()+1}月${now.getDate()}日 ${days[now.getDay()]}`\r\n }\r\n update()\r\n this.timer = setInterval(update, 1000)\r\n },\r\n async handleUnlock() {\r\n if (!this.password) {\r\n this.showError('请输入密码')\r\n return\r\n }\r\n this.loading = true\r\n this.errorMsg = ''\r\n try {\r\n await unlockScreen(this.password)\r\n const lockPath = this.$store.getters.lockPath // 取锁屏前的路径\r\n await this.$store.dispatch('lock/unlockScreen')\r\n this.$router.replace(lockPath)\r\n } catch (err) {\r\n const msg = err.message || err.toString()\r\n this.showError(msg)\r\n this.password = ''\r\n this.$refs.passwordInput && this.$refs.passwordInput.focus()\r\n } finally {\r\n this.loading = false\r\n }\r\n },\r\n showError(msg) {\r\n this.errorMsg = msg\r\n this.isShaking = true\r\n setTimeout(() => { this.isShaking = false }, 600)\r\n },\r\n goLogin() {\r\n this.$store.dispatch('lock/unlockScreen')\r\n this.$store.dispatch('LogOut').then(() => {\r\n this.$router.push('/login')\r\n })\r\n },\r\n // 粒子背景\r\n initParticles() {\r\n const canvas = this.$refs.particleCanvas\r\n if (!canvas) return\r\n const ctx = canvas.getContext('2d')\r\n const resize = () => {\r\n canvas.width = window.innerWidth\r\n canvas.height = window.innerHeight\r\n }\r\n resize()\r\n window.addEventListener('resize', resize)\r\n const count = 80\r\n for (let i = 0; i < count; i++) {\r\n this.particles.push({\r\n x: Math.random() * canvas.width,\r\n y: Math.random() * canvas.height,\r\n r: Math.random() * 2 + 1,\r\n dx: (Math.random() - 0.5) * 0.6,\r\n dy: (Math.random() - 0.5) * 0.6,\r\n alpha: Math.random() * 0.5 + 0.2\r\n })\r\n }\r\n const draw = () => {\r\n ctx.clearRect(0, 0, canvas.width, canvas.height)\r\n this.particles.forEach(p => {\r\n ctx.beginPath()\r\n ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2)\r\n ctx.fillStyle = `rgba(255,255,255,${p.alpha})`\r\n ctx.fill()\r\n p.x += p.dx\r\n p.y += p.dy\r\n if (p.x < 0 || p.x > canvas.width) p.dx *= -1\r\n if (p.y < 0 || p.y > canvas.height) p.dy *= -1\r\n })\r\n // 连线\r\n for (let i = 0; i < this.particles.length; i++) {\r\n for (let j = i + 1; j < this.particles.length; j++) {\r\n const a = this.particles[i], b = this.particles[j]\r\n const dist = Math.hypot(a.x - b.x, a.y - b.y)\r\n if (dist < 120) {\r\n ctx.beginPath()\r\n ctx.moveTo(a.x, a.y)\r\n ctx.lineTo(b.x, b.y)\r\n ctx.strokeStyle = `rgba(255,255,255,${0.15 * (1 - dist / 120)})`\r\n ctx.lineWidth = 0.5\r\n ctx.stroke()\r\n }\r\n }\r\n }\r\n this.animationId = requestAnimationFrame(draw)\r\n }\r\n draw()\r\n }\r\n }\r\n}\r\n</script>\r\n\r\n<style scoped>\r\n.lock-container {\r\n position: fixed;\r\n inset: 0;\r\n background: linear-gradient(135deg, #0f0c29, #302b63, #24243e);\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n z-index: 9999;\r\n font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;\r\n overflow: hidden;\r\n}\r\n\r\n.particle-bg {\r\n position: absolute;\r\n inset: 0;\r\n z-index: 0;\r\n}\r\n\r\n.lock-time {\r\n position: relative;\r\n z-index: 1;\r\n font-size: 72px;\r\n font-weight: 200;\r\n color: #fff;\r\n letter-spacing: 4px;\r\n text-shadow: 0 0 40px rgba(255,255,255,0.3);\r\n margin-bottom: 8px;\r\n font-variant-numeric: tabular-nums;\r\n}\r\n\r\n.lock-date {\r\n position: relative;\r\n z-index: 1;\r\n font-size: 15px;\r\n color: rgba(255,255,255,0.6);\r\n margin-bottom: 48px;\r\n letter-spacing: 2px;\r\n}\r\n\r\n.lock-card {\r\n position: relative;\r\n z-index: 1;\r\n background: rgba(255, 255, 255, 0.08);\r\n backdrop-filter: blur(20px);\r\n -webkit-backdrop-filter: blur(20px);\r\n border: 1px solid rgba(255, 255, 255, 0.15);\r\n border-radius: 24px;\r\n padding: 40px 48px;\r\n width: 360px;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n box-shadow: 0 25px 60px rgba(0,0,0,0.4);\r\n}\r\n\r\n.avatar-wrap {\r\n position: relative;\r\n margin-bottom: 16px;\r\n}\r\n\r\n.lock-avatar {\r\n width: 80px;\r\n height: 80px;\r\n border-radius: 50%;\r\n border: 3px solid rgba(255,255,255,0.3);\r\n object-fit: cover;\r\n display: block;\r\n}\r\n\r\n.lock-icon {\r\n position: absolute;\r\n bottom: -4px;\r\n right: -4px;\r\n background: rgba(255,255,255,0.15);\r\n border-radius: 50%;\r\n width: 26px;\r\n height: 26px;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: 13px;\r\n backdrop-filter: blur(8px);\r\n}\r\n\r\n.lock-username {\r\n color: #fff;\r\n font-size: 18px;\r\n font-weight: 600;\r\n margin-bottom: 6px;\r\n letter-spacing: 1px;\r\n}\r\n\r\n.lock-hint {\r\n color: rgba(255,255,255,0.5);\r\n font-size: 13px;\r\n margin-bottom: 28px;\r\n}\r\n\r\n.input-wrap {\r\n width: 100%;\r\n display: flex;\r\n align-items: center;\r\n background: rgba(255,255,255,0.1);\r\n border: 1px solid rgba(255,255,255,0.2);\r\n border-radius: 50px;\r\n padding: 4px 4px 4px 20px;\r\n transition: border-color 0.3s;\r\n}\r\n\r\n.input-wrap:focus-within {\r\n border-color: rgba(255,255,255,0.6);\r\n background: rgba(255,255,255,0.13);\r\n}\r\n\r\n.input-wrap.shake {\r\n animation: shake 0.5s ease;\r\n}\r\n\r\n@keyframes shake {\r\n 0%, 100% { transform: translateX(0); }\r\n 20% { transform: translateX(-8px); }\r\n 40% { transform: translateX(8px); }\r\n 60% { transform: translateX(-6px); }\r\n 80% { transform: translateX(6px); }\r\n}\r\n\r\n.lock-input {\r\n flex: 1;\r\n background: transparent;\r\n border: none;\r\n outline: none;\r\n color: #fff;\r\n font-size: 15px;\r\n padding: 10px 0;\r\n}\r\n\r\n.lock-input::placeholder {\r\n color: rgba(255,255,255,0.35);\r\n}\r\n\r\n.unlock-btn {\r\n width: 42px;\r\n height: 42px;\r\n border-radius: 50%;\r\n background: linear-gradient(135deg, #667eea, #764ba2);\r\n border: none;\r\n color: #fff;\r\n font-size: 18px;\r\n cursor: pointer;\r\n transition: transform 0.2s, opacity 0.2s;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex-shrink: 0;\r\n}\r\n\r\n.unlock-btn:hover:not(:disabled) {\r\n transform: scale(1.08);\r\n}\r\n\r\n.unlock-btn:disabled {\r\n opacity: 0.6;\r\n cursor: not-allowed;\r\n}\r\n\r\n.loading-dot {\r\n font-size: 13px;\r\n letter-spacing: 1px;\r\n}\r\n\r\n.error-msg {\r\n margin-top: 14px;\r\n color: #ff7675;\r\n font-size: 13px;\r\n text-align: center;\r\n animation: fadeIn 0.3s ease;\r\n}\r\n\r\n@keyframes fadeIn {\r\n from { opacity: 0; transform: translateY(-4px); }\r\n to { opacity: 1; transform: translateY(0); }\r\n}\r\n\r\n.lock-footer {\r\n margin-top: 24px;\r\n}\r\n\r\n.lock-footer a {\r\n color: rgba(255,255,255,0.4);\r\n font-size: 13px;\r\n text-decoration: none;\r\n transition: color 0.2s;\r\n}\r\n\r\n.lock-footer a:hover {\r\n color: rgba(255,255,255,0.8);\r\n}\r\n</style>\r\n"]}]}