<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">自动密码输入器 · 安全工具</title>
<style>
* {
box-sizing: border-box;
font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
}
body {
background: #f0f3f8;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
padding: 20px;
}
.card {
background: #ffffff;
border-radius: 28px;
box-shadow: 0 20px 40px -12px rgba(0, 20, 40, 0.25);
max-width: 600px;
width: 100%;
padding: 30px 28px;
transition: box-shadow 0.2s;
}
.card:hover {
box-shadow: 0 24px 48px -12px rgba(0, 20, 40, 0.35);
}
h1 {
font-size: 1.9rem;
font-weight: 600;
letter-spacing: -0.5px;
color: #0b1a2f;
display: flex;
align-items: center;
gap: 12px;
margin-top: 0;
margin-bottom: 0.25rem;
}
.subhead {
color: #4a5b6e;
font-size: 0.95rem;
margin-bottom: 1.8rem;
border-left: 4px solid #2b6fde;
padding-left: 14px;
background: #f6f9fc;
border-radius: 0 12px 12px 0;
line-height: 1.4;
}
.settings {
background: #f8fafd;
border-radius: 20px;
padding: 18px 18px 12px;
margin-bottom: 24px;
}
.label-flex {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 6px;
}
.label-flex label {
font-weight: 500;
color: #1a2c40;
font-size: 0.9rem;
}
.badge {
background: #dee6f0;
color: #1a2c40;
font-size: 0.7rem;
font-weight: 500;
padding: 2px 10px;
border-radius: 30px;
letter-spacing: 0.2px;
}
input[type="password"], input[type="text"] {
width: 100%;
padding: 12px 16px;
border: 1.5px solid #d9e1ec;
border-radius: 60px;
font-size: 1rem;
background: white;
transition: 0.15s;
outline: none;
}
input[type="password"]:focus, input[type="text"]:focus {
border-color: #2b6fde;
box-shadow: 0 0 0 3px rgba(43, 111, 222, 0.2);
}
.input-group {
margin-bottom: 18px;
position: relative;
}
.reveal-btn {
position: absolute;
right: 6px;
top: 50%;
transform: translateY(-50%);
background: transparent;
border: none;
font-size: 0.9rem;
color: #2b6fde;
font-weight: 500;
cursor: pointer;
padding: 8px 14px;
border-radius: 40px;
transition: background 0.1s;
}
.reveal-btn:hover {
background: #eaf0fa;
}
.hotkey-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 14px;
margin: 10px 0 6px;
}
.hotkey-box {
display: flex;
align-items: center;
gap: 6px;
background: #edf2f9;
padding: 4px 14px 4px 10px;
border-radius: 40px;
font-size: 0.85rem;
font-weight: 500;
color: #1a2c40;
}
kbd {
background: #ffffff;
border: 1px solid #bcc9db;
box-shadow: 0 1px 2px rgba(0,0,0,0.06);
padding: 0 8px;
line-height: 1.8;
border-radius: 6px;
font-size: 0.8rem;
font-weight: 600;
color: #0a1a2a;
display: inline-block;
}
.action-bar {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
gap: 12px;
margin: 18px 0 12px;
}
.btn {
background: white;
border: 1.5px solid #d0dbe9;
padding: 10px 22px;
border-radius: 60px;
font-weight: 500;
font-size: 0.9rem;
color: #1a2c40;
cursor: pointer;
transition: all 0.12s;
display: inline-flex;
align-items: center;
gap: 6px;
box-shadow: 0 2px 4px rgba(0,0,0,0.02);
}
.btn-primary {
background: #1a2c40;
border-color: #1a2c40;
color: white;
box-shadow: 0 4px 10px rgba(26, 44, 64, 0.15);
}
.btn-primary:hover {
background: #2b4058;
transform: scale(0.97);
}
.btn-primary:active {
background: #0f1f30;
transform: scale(0.95);
}
.btn-outline {
background: transparent;
border-color: #b9c9dd;
}
.btn-outline:hover {
background: #eef4fc;
border-color: #8da3c2;
}
.btn-success {
background: #1f8b4c;
border-color: #1f8b4c;
color: white;
}
.btn-success:hover {
background: #16703b;
transform: scale(0.97);
}
.status {
padding: 12px 16px;
background: #f2f6fe;
border-radius: 60px;
font-size: 0.9rem;
color: #1a2c40;
margin: 12px 0 0;
display: flex;
align-items: center;
gap: 8px;
border: 1px solid #dfe9f5;
transition: 0.15s;
}
.status .icon {
font-size: 1.2rem;
}
.status.success {
background: #e2f3e9;
border-color: #a7d8b9;
}
.status.waiting {
background: #fff4dd;
border-color: #fadc9c;
}
.footer-note {
font-size: 0.75rem;
color: #6f829a;
text-align: center;
margin-top: 20px;
border-top: 1px solid #e6edf6;
padding-top: 16px;
}
.footer-note a {
color: #1a2c40;
text-decoration: none;
font-weight: 500;
}
.flex-grow {
flex: 1;
}
@media (max-width: 480px) {
.card { padding: 20px 16px; }
.action-bar { flex-direction: column; align-items: stretch; }
.btn { justify-content: center; }
}
</style>
</head>
<body>
<div class="card" role="main">
<h1>
<span>🔐</span> 自动密码输入器
</h1>
<div class="subhead">
安全保存密码 · 一键填入任何输入框<br>
<span style="font-size:0.85rem; opacity:0.8;">全局快捷键 <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd> (可自定义)</span>
</div>
<!-- 密码存储区 -->
<div class="settings">
<div class="label-flex">
<label for="masterPassword">🔑 主密码 (加密存储)</label>
<span class="badge">仅本地</span>
</div>
<div class="input-group">
<input type="password" id="masterPassword" placeholder="输入要保存的密码..." autocomplete="off">
<button class="reveal-btn" id="toggleMasterPass" aria-label="显示/隐藏密码">显示</button>
</div>
<div class="label-flex" style="margin-top:12px;">
<label for="hotkeyInput">⌨️ 快捷键设置</label>
<span class="badge">全局</span>
</div>
<div class="input-group" style="margin-bottom:4px;">
<input type="text" id="hotkeyInput" value="Ctrl+Shift+P" placeholder="Ctrl+Shift+P">
</div>
<div class="hotkey-row">
<span class="hotkey-box">🖱️ 点击填入</span>
<span class="hotkey-box"><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd></span>
</div>
</div>
<!-- 操作栏 -->
<div class="action-bar">
<button class="btn btn-primary" id="savePasswordBtn">💾 保存密码</button>
<button class="btn btn-outline" id="clearPasswordBtn">🗑️ 清除</button>
<button class="btn btn-success" id="pastePasswordBtn">📋 填入当前输入框</button>
</div>
<!-- 状态 / 反馈 -->
<div class="status waiting" id="statusIndicator">
<span class="icon">⏳</span>
<span id="statusMessage">等待保存密码 …</span>
</div>
<!-- 模拟测试输入框 (演示) -->
<div style="margin: 28px 0 6px; background:#f6fafe; border-radius: 60px; padding: 8px 8px 8px 20px; display: flex; align-items: center; border:1px solid #e2ecf9;">
<span style="color:#2b4d72; font-size:0.9rem;">🧪 测试焦点输入框:</span>
<input type="text" id="demoInput" placeholder="点击这里, 然后按快捷键" style="border:none; background:transparent; flex:1; padding:8px 8px; outline:none; font-size:0.95rem;">
</div>
<div class="footer-note">
⚡ 密码仅保存在浏览器内存中 · 关闭页面自动清除 ·
<a href="#" id="lockBtn" style="cursor:pointer;">🔒 手动锁定</a>
</div>
</div>
<script>
(function() {
"use strict";
// DOM 元素
const masterPassInput = document.getElementById('masterPassword');
const hotkeyInput = document.getElementById('hotkeyInput');
const saveBtn = document.getElementById('savePasswordBtn');
const clearBtn = document.getElementById('clearPasswordBtn');
const pasteBtn = document.getElementById('pastePasswordBtn');
const statusIndicator = document.getElementById('statusIndicator');
const statusMessage = document.getElementById('statusMessage');
const togglePassBtn = document.getElementById('toggleMasterPass');
const demoInput = document.getElementById('demoInput');
const lockBtn = document.getElementById('lockBtn');
// ----- 状态 -----
let savedPassword = ''; // 内存中保存的明文密码
let isLocked = false; // 手动锁定 (不可填入)
let currentPassword = ''; // 用于显示/隐藏
// ----- 辅助函数 更新状态UI -----
function updateStatus(type, msg) {
statusIndicator.className = 'status';
const iconSpan = statusIndicator.querySelector('.icon');
if (type === 'success') {
statusIndicator.classList.add('success');
iconSpan.textContent = '✅';
} else if (type === 'waiting') {
statusIndicator.classList.add('waiting');
iconSpan.textContent = '⏳';
} else if (type === 'error') {
statusIndicator.style.background = '#fee9e7';
statusIndicator.style.borderColor = '#f5b8b1';
iconSpan.textContent = '❌';
} else if (type === 'info') {
statusIndicator.style.background = '#e8f0fe';
statusIndicator.style.borderColor = '#b6cceb';
iconSpan.textContent = 'ℹ️';
}
statusMessage.textContent = msg;
// 重置额外样式 (error时)
if (type !== 'error') {
statusIndicator.style.background = '';
statusIndicator.style.borderColor = '';
}
}
// 刷新: 显示当前是否保存密码
function refreshStatus() {
if (isLocked) {
updateStatus('info', '🔒 已手动锁定,无法填入密码');
return;
}
if (savedPassword && savedPassword.length > 0) {
const masked = savedPassword.substring(0, 2) + '••••' + (savedPassword.length > 4 ? savedPassword.slice(-2) : '');
updateStatus('success', `密码已保存 (${masked}) · 就绪`);
} else {
updateStatus('waiting', '尚未保存密码 · 请先输入并保存');
}
}
// ----- 核心: 填入密码到当前聚焦输入框 -----
function fillPasswordToActiveElement() {
// 锁定或密码为空
if (isLocked) {
updateStatus('error', '🔒 已锁定,请先解锁 (点击 "手动锁定" 解除)');
return false;
}
if (!savedPassword || savedPassword.length === 0) {
updateStatus('error', '❌ 没有已保存的密码,请先保存');
return false;
}
const activeEl = document.activeElement;
// 必须是一个输入框 (input 或 textarea)
if (!activeEl) {
updateStatus('error', '❌ 没有焦点元素');
return false;
}
const tag = activeEl.tagName.toLowerCase();
const validInput = (tag === 'input' || tag === 'textarea');
// 如果是 input 但 type 不是 text/search/email/password/tel/url 等可输入的排除? 但密码框也是password, 允许
const inputType = (activeEl.type || '').toLowerCase();
const allowedTypes = ['text', 'password', 'search', 'email', 'tel', 'url', 'number', 'textarea', ''];
if (!validInput || (tag === 'input' && !allowedTypes.includes(inputType) && inputType !== 'password')) {
updateStatus('error', '❌ 焦点不在可输入框上 (点击输入框再试)');
return false;
}
// 使用 setValue 模拟输入 (触发 input/change 事件)
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype, 'value'
)?.set || Object.getOwnPropertyDescriptor(
window.HTMLTextAreaElement.prototype, 'value'
)?.set;
if (nativeInputValueSetter) {
nativeInputValueSetter.call(activeEl, savedPassword);
} else {
// fallback
activeEl.value = savedPassword;
}
// 触发必要的事件 (让站点/框架检测到)
activeEl.dispatchEvent(new Event('input', { bubbles: true }));
activeEl.dispatchEvent(new Event('change', { bubbles: true }));
// 如果焦点在demoInput上,做特殊高亮
if (activeEl === demoInput) {
demoInput.style.background = '#e2f0e0';
setTimeout(() => { demoInput.style.background = ''; }, 300);
}
updateStatus('success', `✅ 密码已填入 “${activeEl.name || activeEl.id || activeEl.placeholder || '当前输入框'}”`);
return true;
}
// ----- 保存密码 -----
function savePassword() {
const pass = masterPassInput.value;
if (!pass || pass.trim() === '') {
updateStatus('error', '❌ 密码不能为空');
return;
}
savedPassword = pass;
// 清除手动锁定
if (isLocked) {
isLocked = false;
}
refreshStatus();
// 清空输入框 (安全)
masterPassInput.value = '';
updateStatus('success', '🔐 密码已安全保存 (仅内存)');
}
// ----- 清除密码 -----
function clearPassword() {
if (savedPassword === '' && !isLocked) {
updateStatus('info', 'ℹ️ 没有已保存的密码');
return;
}
savedPassword = '';
isLocked = false;
masterPassInput.value = '';
refreshStatus();
updateStatus('waiting', '🗑️ 密码已清除');
}
// ----- 锁定/解锁切换 -----
function toggleLock() {
if (isLocked) {
isLocked = false;
lockBtn.innerHTML = '🔒 手动锁定';
refreshStatus();
} else {
isLocked = true;
lockBtn.innerHTML = '🔓 解除锁定';
updateStatus('info', '🔒 已锁定,无法填入密码 (点击解除)');
}
}
// ----- 显示/隐藏主密码输入框 -----
let passVisible = false;
togglePassBtn.addEventListener('click', function() {
if (!passVisible) {
masterPassInput.type = 'text';
togglePassBtn.textContent = '隐藏';
passVisible = true;
} else {
masterPassInput.type = 'password';
togglePassBtn.textContent = '显示';
passVisible = false;
}
});
// ----- 快捷键解析与注册 -----
let currentHotkey = 'Ctrl+Shift+P'; // 默认
let registeredCallback = null;
function parseHotkey(hotkeyStr) {
const parts = hotkeyStr.split('+').map(s => s.trim().toLowerCase());
const keys = {
ctrl: false,
shift: false,
alt: false,
meta: false,
key: ''
};
for (const part of parts) {
if (part === 'ctrl' || part === 'control') keys.ctrl = true;
else if (part === 'shift') keys.shift = true;
else if (part === 'alt') keys.alt = true;
else if (part === 'meta' || part === 'win' || part === 'cmd') keys.meta = true;
else if (part.length === 1) keys.key = part.toUpperCase();
else if (part.length > 1) keys.key = part.charAt(0).toUpperCase() + part.slice(1); // e.g. "P"
else keys.key = part.toUpperCase();
}
// 如果key为空, 默认 'P'
if (!keys.key) keys.key = 'P';
return keys;
}
function registerHotkey(hotkeyStr) {
// 移除旧监听 (通过保存的handler)
if (registeredCallback) {
document.removeEventListener('keydown', registeredCallback);
registeredCallback = null;
}
const keys = parseHotkey(hotkeyStr);
const handler = function(e) {
// 检查修饰键
const ctrlOK = keys.ctrl ? (e.ctrlKey || e.metaKey) : true; // 如果要求ctrl, 则必须按下ctrl或meta
const shiftOK = keys.shift ? e.shiftKey : true;
const altOK = keys.alt ? e.altKey : true;
// 特例: 如果用户设置单纯 "P" 无修饰, 则避免与其他输入冲突, 但保留
const metaOK = keys.meta ? (e.metaKey || e.ctrlKey) : true; // 宽松
// 排除输入框内输入字符 (如果无修饰, 不在输入框触发)
if (!keys.ctrl && !keys.shift && !keys.alt && !keys.meta) {
// 无修饰键: 只在非输入框中生效? 但密码填入需要聚焦输入框, 造成无限循环?
// 避免: 如果无修饰且焦点在输入框, 不触发快捷键 (防止输入字符)
const tag = (e.target.tagName || '').toLowerCase();
if (tag === 'input' || tag === 'textarea' || e.target.isContentEditable) {
return;
}
}
// 检查key
const pressedKey = e.key.toUpperCase();
if (pressedKey !== keys.key.toUpperCase()) return;
// 修饰键组合检查
if (keys.ctrl && !e.ctrlKey && !e.metaKey) return;
if (keys.shift && !e.shiftKey) return;
if (keys.alt && !e.altKey) return;
if (keys.meta && !e.metaKey && !e.ctrlKey) return;
// 如果无修饰键但上面已经过滤了输入框, 则这里可以执行
// 额外: 如果只按了一个字母且没有修饰, 并且在输入框中, 已经return, 安全。
// 防止默认行为 (如ctrl+shift+P可能打开打印)
e.preventDefault();
e.stopPropagation();
// 执行填入
fillPasswordToActiveElement();
};
document.addEventListener('keydown', handler);
registeredCallback = handler;
currentHotkey = hotkeyStr;
}
// ----- 更新快捷键设置 -----
function applyHotkeyFromInput() {
let raw = hotkeyInput.value.trim();
if (!raw || raw.length < 2) {
raw = 'Ctrl+Shift+P';
hotkeyInput.value = raw;
}
// 简单格式化: 首字母大写等
registerHotkey(raw);
updateStatus('info', `⌨️ 快捷键已更新: ${raw}`);
// 刷新状态
setTimeout(refreshStatus, 100);
}
// ----- 初始化 -----
function init() {
// 默认注册
registerHotkey('Ctrl+Shift+P');
// 如果有保存过? 无
refreshStatus();
// 事件绑定
saveBtn.addEventListener('click', savePassword);
clearBtn.addEventListener('click', clearPassword);
pasteBtn.addEventListener('click', function() {
fillPasswordToActiveElement();
});
lockBtn.addEventListener('click', function(e) {
e.preventDefault();
toggleLock();
});
// 快捷键修改
hotkeyInput.addEventListener('change', applyHotkeyFromInput);
hotkeyInput.addEventListener('blur', function() {
// 如果为空恢复默认
if (!hotkeyInput.value.trim()) {
hotkeyInput.value = 'Ctrl+Shift+P';
applyHotkeyFromInput();
}
});
// 回车保存密码 (在密码输入框)
masterPassInput.addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
savePassword();
}
});
// demo输入框演示提示
demoInput.addEventListener('focus', function() {
if (savedPassword) {
updateStatus('info', '💡 现在按快捷键 (或点"填入") 自动填充密码');
} else {
updateStatus('waiting', '💡 先保存密码,再按快捷键填充');
}
});
// 额外: 如果用户在输入框输入快捷键, 友好的示例
console.log('🔐 自动密码输入器已启动 | 快捷键填充');
}
init();
})();
</script>
</body>
</html>
核心功能与操作路径
您可以通过以下方式快速使用这个工具,它旨在简化重复输入密码的流程。

- 保存密码:在“主密码”输入框中输入您的密码,点击“保存密码”按钮即可安全地存储在浏览器内存中。
- 填入密码:点击任何需要输入密码的输入框使其获得焦点,然后按下您设置的快捷键(默认
Ctrl+Shift+P),密码会自动填入。 - 管理与安全:您可以随时清除已保存的密码或点击“手动锁定”来防止误填入,状态栏会清晰显示当前密码的保存状态和操作反馈。