316 lines
19 KiB
HTML
316 lines
19 KiB
HTML
<!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>
|
||
<button type="button" id="cancelEditBtn" class="btn-edit" data-i18n="cancelBtn" hidden>取消</button>
|
||
</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 行,在 <input> 或 <button> 标签上看 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">
|
||
<h2 data-i18n="configuredDomainsTitle">已配置域名</h2>
|
||
<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>
|
||
<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>
|
||
<input type="file" id="importFile" accept="application/json,.json" hidden>
|
||
</div>
|
||
</div>
|
||
<div id="searchBox" class="search-box" hidden>
|
||
<input type="text" id="searchInput" data-i18n-placeholder="searchPlaceholder" placeholder="搜索域名 / 用户名 / 别名">
|
||
<button type="button" id="searchClear" class="search-clear">×</button>
|
||
</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" data-i18n="poweredBy">power by chenxuan</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>
|
||
|
||
<script src="utils.js"></script>
|
||
<script src="env-store.js"></script>
|
||
<script src="crypto-store.js"></script>
|
||
<script src="popup.js"></script>
|
||
</body>
|
||
</html>
|