Files
auto-login/popup.html
T
2026-09-16 17:31:26 +08:00

341 lines
21 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="popup.css">
<title>Auto Login Manager</title>
</head>
<body>
<!-- 锁定/设置本机密码覆盖层 -->
<div id="lockScreen" class="lock-screen">
<div class="lock-card">
<img src="icons/icon128.png" alt="logo" class="lock-logo">
<h2 id="lockTitle" data-i18n="lockSetupTitle">设置本机密码</h2>
<p id="lockHint" class="lock-hint" data-i18n="lockSetupHint">本机密码用于加密存储所有配置,忘记将无法恢复</p>
<div class="form-group">
<input type="password" id="lockPassword" data-i18n-placeholder="lockPasswordPlaceholder" placeholder="请输入本机密码" autocomplete="off">
</div>
<div class="form-group" id="lockConfirmGroup">
<label for="lockPasswordConfirm" data-i18n="lockPasswordConfirmLabel">确认本机密码</label>
<input type="password" id="lockPasswordConfirm" data-i18n-placeholder="lockPasswordConfirmPlaceholder" placeholder="再次输入本机密码" autocomplete="off">
</div>
<div class="lock-warn" id="lockWarn"></div>
<button type="button" id="lockSubmitBtn" class="btn-primary lock-submit" data-i18n="lockSetupBtn">设置本机密码并进入</button>
<p class="lock-footnote" data-i18n="lockFootnote">本机密码仅用于本地加密,不会上传到任何服务器</p>
</div>
</div>
<div class="container" id="mainContainer" style="display:none;">
<header>
<button type="button" id="lockBtn" class="btn-lock" data-i18n-title="lockBtnTitle" title="锁定(清除内存中的明文)">🔒</button>
<div class="header-title">
<img src="icons/icon128.png" alt="logo" class="header-logo">
<h1 data-i18n="appTitle">Auto Login Manager</h1>
</div>
<div class="env-bar">
<label class="env-label" data-i18n="currentEnvLabel">当前环境</label>
<div class="env-controls">
<select id="envSelect" class="env-select"></select>
<button type="button" id="envManageBtn" class="btn-env" data-i18n-title="envManageBtnTitle" title="管理环境(新建 / 重命名 / 删除)">⚙</button>
</div>
</div>
</header>
<section class="add-section" id="addSection">
<button type="button" id="addEntryBtn" class="btn-primary" data-i18n="addConfigTitle">添加域名配置</button>
<div class="add-section-header">
<h2 id="addConfigTitle" data-i18n="addConfigTitle">添加域名配置</h2>
</div>
<div class="form-group">
<label for="domain" data-i18n="domainLabel">域名 / URL 匹配规则</label>
<div class="domain-wrapper">
<input type="text" id="domain" data-i18n-placeholder="domainPlaceholder" placeholder="example.com 或 *.example.com 或 site.com/admin/*">
<button type="button" id="fillDomain" class="btn-fill-domain" data-i18n="fillDomainBtn" data-i18n-title="fillDomainBtnTitle" title="填入当前标签页域名">填入当前域名</button>
</div>
<small data-i18n="domainHelp">支持通配符 *。带 / 时按 hostname+path 匹配,如 oa.com/login/*;不带 / 时只匹配域名</small>
</div>
<div class="form-group">
<label for="username" data-i18n="usernameLabel">用户名</label>
<input type="text" id="username" data-i18n-placeholder="usernamePlaceholder" placeholder="输入用户名">
</div>
<div class="form-group">
<label for="password" data-i18n="passwordLabel">密码</label>
<div class="password-wrapper">
<input type="password" id="password" data-i18n-placeholder="passwordPlaceholder" placeholder="输入密码">
<button class="toggle-pwd eye-open" type="button" data-target="password" data-i18n-aria="togglePwdAria" aria-label="切换密码显示">👁</button>
</div>
</div>
<div class="form-group">
<label for="alias" data-i18n="aliasLabel">别名(可选)</label>
<input type="text" id="alias" data-i18n-placeholder="aliasPlaceholder" placeholder="例如: 公司OA、GitLab、邮箱">
<small data-i18n="aliasHelp">方便识别该配置,留空则显示域名</small>
</div>
<details class="advanced-options">
<summary data-i18n="advancedOptionsSummary">高级选项(选填,默认即可)</summary>
<div class="advanced-options-body">
<div class="form-group">
<label for="autoSubmit" data-i18n="autoSubmitLabel">自动提交登录</label>
<select id="autoSubmit">
<option value="true" data-i18n="autoSubmitYes">是 - 自动点击登录按钮</option>
<option value="false" data-i18n="autoSubmitNo">否 - 仅填充不提交</option>
</select>
</div>
<div class="form-group">
<label for="enabled" data-i18n="enabledLabel">启用 / 禁用</label>
<label class="toggle-switch">
<input type="checkbox" id="enabled" checked>
<span class="toggle-slider"></span>
</label>
<small data-i18n="enabledHelp">禁用后该条配置将不会生效</small>
</div>
<div class="form-group">
<label for="delayMs" data-i18n="delayMsLabel">填充延迟(毫秒,可选)</label>
<input type="number" id="delayMs" min="0" max="10000" step="100" data-i18n-placeholder="delayMsPlaceholder" placeholder="留空=默认 300ms,可填 500-2000 用于 SPA 页面">
<small data-i18n="delayMsHelp">某些页面(Ant Design、ASP.NET)需 JS 初始化,立即填充会失效</small>
</div>
<div class="selector-tutorial">
<p class="help-tip" data-i18n="selectorHelpTip">下面三项通常<strong>留空即可</strong>,插件会自动找到登录框。只有当自动填充失败、或填错位置时才需要填。</p>
<p class="help-step" data-i18n="selectorHelpStep1">1. 在登录页面按 <kbd>F12</kbd> 打开开发者工具,切到"Elements(元素)"标签</p>
<p class="help-step" data-i18n="selectorHelpStep2">2. 点开发者工具左上角的 <span class="icon-arrow">箭头图标</span>,再去点页面上的输入框/按钮</p>
<p class="help-step" data-i18n="selectorHelpStep3">3. 工具会跳到对应的 HTML 行,在 &lt;input&gt; 或 &lt;button&gt; 标签上看 id、name、class、type 属性</p>
<p class="help-step" data-i18n="selectorHelpStep4">4. 按 #id、.class、[name=xxx]、tag[属性=值] 规则组合填写即可</p>
</div>
<div class="form-group">
<label data-i18n="usernameSelectorLabel">用户名输入框位置</label>
<input type="text" id="usernameSelector" data-i18n-placeholder="usernameSelectorPlaceholder" placeholder="留空=自动查找,如 #username 或 input[name=account]">
<small data-i18n="usernameSelectorHelp">留空即可。填错会导致用户名填不进框。</small>
</div>
<div class="form-group">
<label data-i18n="passwordSelectorLabel">密码输入框位置</label>
<input type="text" id="passwordSelector" data-i18n-placeholder="passwordSelectorPlaceholder" placeholder="留空=自动查找,如 #password 或 input[type=password]">
<small data-i18n="passwordSelectorHelp">留空即可。填错会导致密码填不进框。</small>
</div>
<div class="form-group">
<label data-i18n="submitSelectorLabel">登录按钮位置</label>
<input type="text" id="submitSelector" data-i18n-placeholder="submitSelectorPlaceholder" placeholder="留空=自动查找,如 #loginBtn 或 button[type=submit]">
<small data-i18n="submitSelectorHelp">留空即可。填错会导致不会自动点击登录。</small>
</div>
</div>
</details>
<button id="addBtn" class="btn-primary" data-i18n="addConfigBtn">添加配置</button>
</section>
<section class="list-section" id="listSection">
<div class="list-header">
<div class="list-actions">
<button type="button" id="searchBtn" class="btn-io btn-icon" data-i18n-title="searchBtnTitle" title="搜索">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
</button>
<div id="searchBox" class="search-box-inline" hidden>
<input type="text" id="searchInput" data-i18n-placeholder="searchPlaceholder" placeholder="搜索域名/用户名/别名">
<button type="button" id="searchClear" class="search-clear" title="清除" hidden>×</button>
<button type="button" id="searchToggleBtn" class="btn-io btn-icon" data-i18n-title="searchBtnTitle" title="搜索">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
</button>
</div>
<button type="button" id="exportBtn" class="btn-io btn-icon" data-i18n-title="exportBtn" title="导出">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v12"/><path d="M7 8l5-5 5 5"/><path d="M5 17v2a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-2"/></svg>
</button>
<button type="button" id="importBtn" class="btn-io btn-icon" data-i18n-title="importBtn" title="导入">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 21V9"/><path d="M7 16l5 5 5-5"/><path d="M5 7V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v2"/></svg>
</button>
<button type="button" id="importBookmarkBtn" class="btn-io btn-icon" data-i18n-title="importBookmarkBtnTitle" title="从书签导入">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>
</button>
<input type="file" id="importFile" accept="application/json,.json" hidden>
</div>
</div>
<div id="configList" class="config-list">
</div>
</section>
<section class="current-section">
<button id="fillCurrent" class="btn-secondary" data-i18n="fillCurrentBtn">在当前页面填充</button>
</section>
<footer class="footer">
<p class="powered-by"><span data-i18n="poweredBy">power by chenxuan</span> <span id="versionLabel">v1.0.0</span></p>
</footer>
</div>
<div id="toast" class="toast"></div>
<!-- 环境管理对话框 -->
<div id="envManageModal" class="modal">
<div class="modal-content">
<h3 data-i18n="envManageTitle">环境管理</h3>
<div id="envManageList" class="env-manage-list"></div>
<div class="modal-warn" id="envManageWarn" style="display:none;"></div>
<div class="modal-actions">
<button type="button" id="envManageClose" class="btn-edit" data-i18n="closeBtn">关闭</button>
<button type="button" id="envManageAdd" class="btn-primary" data-i18n="createEnvBtn">+ 新建环境</button>
</div>
</div>
</div>
<div id="envModal" class="modal">
<div class="modal-content">
<h3 id="envModalTitle" data-i18n="envCreateTitle">新建环境</h3>
<div class="form-group">
<label for="envNameInput" data-i18n="envNameLabel">环境名称</label>
<input type="text" id="envNameInput" data-i18n-placeholder="envNamePlaceholder" placeholder="例如: 个人 / 测试 / 生产 / 客户A">
</div>
<div class="preset-env-tags" id="presetEnvTags">
<span class="preset-tag" data-name="个人" data-i18n="presetPersonal">个人</span>
<span class="preset-tag" data-name="测试" data-i18n="presetTest">测试</span>
<span class="preset-tag" data-name="生产" data-i18n="presetProd">生产</span>
<span class="preset-tag" data-name="开发" data-i18n="presetDev">开发</span>
<span class="preset-tag" data-name="预发" data-i18n="presetStaging">预发</span>
</div>
<div class="modal-warn" id="envModalWarn" style="display:none;"></div>
<div class="modal-actions">
<button type="button" id="envModalCancel" class="btn-edit" data-i18n="cancelBtn">取消</button>
<button type="button" id="envModalConfirm" class="btn-primary" data-i18n="confirmBtn">确认</button>
</div>
</div>
</div>
<div id="pwdModal" class="modal">
<div class="modal-content">
<h3 id="modalTitle" data-i18n="pwdModalExportTitle">加密导出</h3>
<div id="encryptToggleGroup" class="form-group modal-toggle">
<label class="toggle-label">
<input type="checkbox" id="encryptToggle" checked>
<span data-i18n="encryptExportLabel">加密导出文件</span>
</label>
<small data-i18n="encryptExportHelp">勾选后用文件密码加密;不勾选则导出明文 JSON</small>
</div>
<div id="pwdFields" class="modal-fields">
<div class="form-group">
<label for="modalPwd" data-i18n="filePasswordLabel">文件密码</label>
<div class="password-wrapper">
<input type="password" id="modalPwd" data-i18n-placeholder="filePasswordPlaceholder" placeholder="输入文件密码">
<button class="toggle-pwd eye-open" type="button" data-target="modalPwd" data-i18n-aria="togglePwdAria" aria-label="切换密码显示">👁</button>
</div>
</div>
<div class="form-group" id="confirmGroup">
<label for="modalPwdConfirm" data-i18n="filePasswordConfirmLabel">确认文件密码</label>
<div class="password-wrapper">
<input type="password" id="modalPwdConfirm" data-i18n-placeholder="filePasswordConfirmPlaceholder" placeholder="再次输入文件密码">
<button class="toggle-pwd eye-open" type="button" data-target="modalPwdConfirm" data-i18n-aria="togglePwdAria" aria-label="切换密码显示">👁</button>
</div>
</div>
<div class="modal-warn" id="modalWarn" data-i18n="pwdModalExportWarn">文件密码将用于 AES-GCM 加密,无法找回,请妥善保存</div>
<div class="strength-block" id="strengthBlock">
<div class="strength-bar-bg">
<div class="strength-bar-fg" id="strengthBar"></div>
</div>
<div class="strength-info">
<span id="strengthLabel" data-i18n="strengthEmpty">尚未输入</span>
<span id="crackTime"></span>
</div>
</div>
</div>
<div class="modal-actions">
<button type="button" id="modalCancel" class="btn-edit" data-i18n="cancelBtn">取消</button>
<button type="button" id="modalConfirm" class="btn-primary" data-i18n="confirmBtn">确认</button>
</div>
</div>
</div>
<!-- 编辑域名配置对话框 -->
<div id="editConfigModal" class="modal">
<div class="modal-content edit-config-content">
<h3 id="editConfigModalTitle" data-i18n="editConfigTitle">编辑域名配置</h3>
<div class="form-group">
<label for="editDomain" data-i18n="domainLabel">域名 / URL 匹配规则</label>
<div class="domain-wrapper">
<input type="text" id="editDomain" data-i18n-placeholder="domainPlaceholder" placeholder="example.com 或 *.example.com 或 site.com/admin/*">
<button type="button" id="editFillDomain" class="btn-fill-domain" data-i18n="fillDomainBtn" data-i18n-title="fillDomainBtnTitle" title="填入当前标签页域名">填入当前域名</button>
</div>
<small data-i18n="domainHelp">支持通配符 *。带 / 时按 hostname+path 匹配,如 oa.com/login/*;不带 / 时只匹配域名</small>
</div>
<div class="form-group">
<label for="editUsername" data-i18n="usernameLabel">用户名</label>
<input type="text" id="editUsername" data-i18n-placeholder="usernamePlaceholder" placeholder="输入用户名">
</div>
<div class="form-group">
<label for="editPassword" data-i18n="passwordLabel">密码</label>
<div class="password-wrapper">
<input type="password" id="editPassword" data-i18n-placeholder="passwordPlaceholder" placeholder="输入密码">
<button class="toggle-pwd eye-open" type="button" data-target="editPassword" data-i18n-aria="togglePwdAria" aria-label="切换密码显示">👁</button>
</div>
</div>
<div class="form-group">
<label for="editAlias" data-i18n="aliasLabel">别名(可选)</label>
<input type="text" id="editAlias" data-i18n-placeholder="aliasPlaceholder" placeholder="例如: 公司OA、GitLab、邮箱">
<small data-i18n="aliasHelp">方便识别该配置,留空则显示域名</small>
</div>
<details class="advanced-options">
<summary data-i18n="advancedOptionsSummary">高级选项(选填,默认即可)</summary>
<div class="advanced-options-body">
<div class="form-group">
<label for="editAutoSubmit" data-i18n="autoSubmitLabel">自动提交登录</label>
<select id="editAutoSubmit">
<option value="true" data-i18n="autoSubmitYes">是 - 自动点击登录按钮</option>
<option value="false" data-i18n="autoSubmitNo">否 - 仅填充不提交</option>
</select>
</div>
<div class="form-group">
<label for="editEnabled" data-i18n="enabledLabel">启用 / 禁用</label>
<label class="toggle-switch">
<input type="checkbox" id="editEnabled" checked>
<span class="toggle-slider"></span>
</label>
<small data-i18n="enabledHelp">禁用后该条配置将不会生效</small>
</div>
<div class="form-group">
<label for="editDelayMs" data-i18n="delayMsLabel">填充延迟(毫秒,可选)</label>
<input type="number" id="editDelayMs" min="0" max="10000" step="100" data-i18n-placeholder="delayMsPlaceholder" placeholder="留空=默认 300ms,可填 500-2000 用于 SPA 页面">
<small data-i18n="delayMsHelp">某些页面(Ant Design、ASP.NET)需 JS 初始化,立即填充会失效</small>
</div>
<div class="form-group">
<label data-i18n="usernameSelectorLabel">用户名输入框位置</label>
<input type="text" id="editUsernameSelector" data-i18n-placeholder="usernameSelectorPlaceholder" placeholder="留空=自动查找,如 #username 或 input[name=account]">
</div>
<div class="form-group">
<label data-i18n="passwordSelectorLabel">密码输入框位置</label>
<input type="text" id="editPasswordSelector" data-i18n-placeholder="passwordSelectorPlaceholder" placeholder="留空=自动查找,如 #password 或 input[type=password]">
</div>
<div class="form-group">
<label data-i18n="submitSelectorLabel">登录按钮位置</label>
<input type="text" id="editSubmitSelector" data-i18n-placeholder="submitSelectorPlaceholder" placeholder="留空=自动查找,如 #loginBtn 或 button[type=submit]">
</div>
</div>
</details>
<div class="modal-actions">
<button type="button" id="editConfigCancel" class="btn-edit" data-i18n="cancelBtn">取消</button>
<button type="button" id="editConfigSave" class="btn-primary" data-i18n="updateConfigBtn">更新配置</button>
</div>
</div>
</div>
<!-- 从书签导入对话框 -->
<div id="bookmarkModal" class="modal">
<div class="modal-content bookmark-modal-content">
<h3 data-i18n="bookmarkImportTitle">从书签导入</h3>
<div class="bookmark-toolbar">
<label class="bookmark-select-all">
<input type="checkbox" id="bookmarkSelectAll">
<span data-i18n="bookmarkSelectAll">全选</span>
</label>
<input type="text" id="bookmarkSearch" data-i18n-placeholder="bookmarkSearchPlaceholder" placeholder="搜索书签">
</div>
<div id="bookmarkList" class="bookmark-list"></div>
<div class="modal-actions">
<button type="button" id="bookmarkCancel" class="btn-edit" data-i18n="cancelBtn">取消</button>
<button type="button" id="bookmarkConfirm" class="btn-primary" data-i18n="bookmarkImportBtn">导入选中</button>
</div>
</div>
</div>
<script src="utils.js"></script>
<script src="env-store.js"></script>
<script src="crypto-store.js"></script>
<script src="popup.js"></script>
</body>
</html>