feat: implement account archiving system and brand logo upgrade

This commit is contained in:
jason
2026-03-04 00:09:01 +08:00
parent fc4898a9ee
commit 734c99849d
3 changed files with 174 additions and 46 deletions

View File

@@ -34,14 +34,26 @@ body {
border-bottom: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);
} }
h1 { .logo {
font-size: 16px; display: flex;
font-weight: 600; align-items: center;
margin: 0; justify-content: center;
background: linear-gradient(90deg, #2563eb, #3b82f6); filter: drop-shadow(0 2px 4px rgba(59, 130, 246, 0.2));
-webkit-background-clip: text; transition: transform 0.3s ease;
background-clip: text; }
-webkit-text-fill-color: transparent;
.logo:hover {
transform: scale(1.05) rotate(2deg);
}
#archive-toggle {
color: var(--text-secondary);
opacity: 0.7;
}
#archive-toggle:hover {
opacity: 1;
background: rgba(100, 116, 139, 0.1);
} }
.icon-btn { .icon-btn {

View File

@@ -11,7 +11,29 @@
<body> <body>
<div id="app-view"> <div id="app-view">
<div class="header"> <div class="header">
<h1>清速登</h1> <div style="display: flex; align-items: center; gap: 8px;">
<button id="archive-toggle" class="icon-btn" title="查看已归档项目" style="padding: 4px;">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 8v13H3V8M1 3h22v5H1V3zm10 8h2" />
</svg>
</button>
<div class="logo">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2L2 7L12 12L22 7L12 2Z" fill="url(#logo-grad)" stroke="white" stroke-width="1.5"
stroke-linejoin="round" />
<path d="M2 17L12 22L22 17" stroke="url(#logo-grad)" stroke-width="1.5" stroke-linecap="round"
stroke-linejoin="round" />
<path d="M2 12L12 17L22 12" stroke="url(#logo-grad)" stroke-width="1.5" stroke-linecap="round"
stroke-linejoin="round" />
<defs>
<linearGradient id="logo-grad" x1="2" y1="2" x2="22" y2="22" gradientUnits="userSpaceOnUse">
<stop stop-color="#3b82f6" />
<stop offset="1" stop-color="#8b5cf6" />
</linearGradient>
</defs>
</svg>
</div>
</div>
<div> <div>
<a href="#" id="logout-btn" <a href="#" id="logout-btn"
style="font-size: 11px; color: var(--text-secondary); text-decoration: none; margin-right: 8px;">切换账号</a> style="font-size: 11px; color: var(--text-secondary); text-decoration: none; margin-right: 8px;">切换账号</a>
@@ -22,36 +44,51 @@
</div> </div>
</div> </div>
<div id="add-form" class="hidden"> <div id="main-view-content">
<h2 id="form-title">添加配置</h2> <div id="add-form" class="hidden">
<input type="hidden" id="editing-id" /> <h2 id="form-title">添加配置</h2>
<div class="form-group"> <input type="hidden" id="editing-id" />
<select id="env-select"> <div class="form-group">
<option value="DD_PP">DD PP</option> <select id="env-select">
<option value="DD_ATS">DD ATS</option> <option value="DD_PP">DD PP</option>
<option value="STD_PP">STD PP</option> <option value="DD_ATS">DD ATS</option>
<option value="STD_ATS">STD ATS</option> <option value="STD_PP">STD PP</option>
</select> <option value="STD_ATS">STD ATS</option>
</select>
</div>
<div class="form-group">
<input type="text" id="client-input" placeholder="客户名称 (例如: 某某测试)" autocomplete="off" />
</div>
<div class="form-group">
<input type="text" id="username-input" placeholder="账号" autocomplete="off" />
</div>
<div class="form-group">
<input type="password" id="password-input" placeholder="密码" autocomplete="new-password" />
</div>
<div class="form-actions">
<button id="cancel-btn" class="secondary-btn">取消</button>
<button id="save-btn" class="primary-btn">保存配置</button>
</div>
</div> </div>
<div class="form-group">
<input type="text" id="client-input" placeholder="客户名称 (例如: 某某测试)" autocomplete="off" /> <div class="rules-container">
</div> <ul id="rules-list">
<div class="form-group"> <!-- Rules will be dynamically inserted here by JS -->
<input type="text" id="username-input" placeholder="账号" autocomplete="off" /> </ul>
</div>
<div class="form-group">
<input type="password" id="password-input" placeholder="密码" autocomplete="new-password" />
</div>
<div class="form-actions">
<button id="cancel-btn" class="secondary-btn">取消</button>
<button id="save-btn" class="primary-btn">保存配置</button>
</div> </div>
</div> </div>
<div class="rules-container"> <div id="archive-view-content" class="hidden">
<ul id="rules-list"> <div
<!-- Rules will be dynamically inserted here by JS --> style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; border-bottom: 1px dashed var(--border-color); padding-bottom: 8px;">
</ul> <h2 style="font-size: 13px; margin: 0; color: var(--text-secondary); font-weight: 500;">已归档账号</h2>
<button id="back-to-main" class="secondary-btn" style="padding: 2px 8px; font-size: 11px;">返回列表</button>
</div>
<div class="rules-container">
<ul id="archive-list">
<!-- Archived rules will be inserted here -->
</ul>
</div>
</div> </div>
</div> </div>

101
popup.js
View File

@@ -21,6 +21,11 @@ document.addEventListener('DOMContentLoaded', () => {
const goToRegister = document.getElementById('go-to-register'); const goToRegister = document.getElementById('go-to-register');
const goToLogin = document.getElementById('go-to-login'); const goToLogin = document.getElementById('go-to-login');
const logoutBtn = document.getElementById('logout-btn'); const logoutBtn = document.getElementById('logout-btn');
const archiveToggle = document.getElementById('archive-toggle');
const backToMain = document.getElementById('back-to-main');
const mainViewContent = document.getElementById('main-view-content');
const archiveViewContent = document.getElementById('archive-view-content');
const archiveList = document.getElementById('archive-list');
if (logoutBtn) { if (logoutBtn) {
logoutBtn.addEventListener('click', (e) => { logoutBtn.addEventListener('click', (e) => {
@@ -294,8 +299,9 @@ document.addEventListener('DOMContentLoaded', () => {
function renderRules() { function renderRules() {
rulesList.innerHTML = ''; rulesList.innerHTML = '';
const activeRules = rules.filter(r => !r.archived);
if (rules.length === 0) { if (activeRules.length === 0) {
rulesList.innerHTML = ` rulesList.innerHTML = `
<div class="empty-state"> <div class="empty-state">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
@@ -308,7 +314,7 @@ document.addEventListener('DOMContentLoaded', () => {
return; return;
} }
rules.forEach((rule, index) => { activeRules.forEach((rule, index) => {
const li = document.createElement('li'); const li = document.createElement('li');
li.className = 'rule-item'; li.className = 'rule-item';
li.setAttribute('draggable', 'true'); li.setAttribute('draggable', 'true');
@@ -331,8 +337,10 @@ document.addEventListener('DOMContentLoaded', () => {
<path stroke-linecap="round" stroke-linejoin="round" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg> </svg>
</button> </button>
<button class="icon-btn btn-delete" data-id="${rule.id}" title="删除此配置"> <button class="icon-btn btn-delete" data-id="${rule.id}" title="归档此配置">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg> <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M20 7l-8 8-4-4m12 4v5a2 2 0 01-2 2H6a2 2 0 01-2-2V9a2 2 0 012-2h5" />
</svg>
</button> </button>
</div> </div>
`; `;
@@ -341,21 +349,75 @@ document.addEventListener('DOMContentLoaded', () => {
rulesList.appendChild(li); rulesList.appendChild(li);
}); });
attachRuleEvents();
}
function renderArchive() {
archiveList.innerHTML = '';
const archivedRules = rules.filter(r => r.archived);
if (archivedRules.length === 0) {
archiveList.innerHTML = '<div class="empty-state" style="padding: 20px 0;"><span>暂无归档内容</span></div>';
return;
}
archivedRules.forEach(rule => {
const li = document.createElement('li');
li.className = 'rule-item';
li.innerHTML = `
<div class="rule-info">
<div class="rule-domain">${rule.clientName} <span class="rule-env env-${rule.env.toLowerCase().replace('_', '-')}">${rule.env.replace('_', ' ')}</span></div>
<div class="rule-acc">帐号: ${rule.username}</div>
</div>
<div class="rule-actions">
<button class="icon-btn btn-restore" data-id="${rule.id}" title="恢复到主列表">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>
</button>
<button class="icon-btn btn-delete-perm" data-id="${rule.id}" title="彻底删除">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg>
</button>
</div>
`;
archiveList.appendChild(li);
});
document.querySelectorAll('.btn-restore').forEach(btn => {
btn.addEventListener('click', (e) => {
const id = e.currentTarget.getAttribute('data-id');
const index = rules.findIndex(r => r.id === id);
if (index !== -1) {
rules[index].archived = false;
saveRules();
renderArchive();
}
});
});
document.querySelectorAll('.btn-delete-perm').forEach(btn => {
btn.addEventListener('click', (e) => {
const id = e.currentTarget.getAttribute('data-id');
if (confirm('确定要彻底删除此配置吗?此操作不可撤销。')) {
rules = rules.filter(r => r.id !== id);
saveRules();
renderArchive();
}
});
});
}
function attachRuleEvents() {
document.querySelectorAll('.rule-info').forEach(infoArea => { document.querySelectorAll('.rule-info').forEach(infoArea => {
infoArea.addEventListener('click', (e) => { infoArea.addEventListener('click', (e) => {
const id = e.currentTarget.getAttribute('data-id'); const id = e.currentTarget.getAttribute('data-id');
const rule = rules.find(r => r.id === id); const rule = rules.find(r => r.id === id);
if (rule) { if (rule) executePurgeAndLogin(rule, e.currentTarget);
executePurgeAndLogin(rule, e.currentTarget);
}
}); });
}); });
document.querySelectorAll('.btn-edit').forEach(btn => { document.querySelectorAll('.btn-edit').forEach(btn => {
btn.addEventListener('click', (e) => { btn.addEventListener('click', (e) => {
e.stopPropagation(); e.stopPropagation();
const id = e.currentTarget.getAttribute('data-id'); toggleAddForm('edit', e.currentTarget.getAttribute('data-id'));
toggleAddForm('edit', id);
}); });
}); });
@@ -363,14 +425,31 @@ document.addEventListener('DOMContentLoaded', () => {
btn.addEventListener('click', (e) => { btn.addEventListener('click', (e) => {
e.stopPropagation(); e.stopPropagation();
const id = e.currentTarget.getAttribute('data-id'); const id = e.currentTarget.getAttribute('data-id');
if (confirm('确定要删除此配置吗?')) { const index = rules.findIndex(r => r.id === id);
rules = rules.filter(r => r.id !== id); if (index !== -1) {
rules[index].archived = true;
saveRules(); saveRules();
} }
}); });
}); });
} }
if (archiveToggle) {
archiveToggle.addEventListener('click', () => {
mainViewContent.classList.add('hidden');
archiveViewContent.classList.remove('hidden');
renderArchive();
});
}
if (backToMain) {
backToMain.addEventListener('click', () => {
archiveViewContent.classList.add('hidden');
mainViewContent.classList.remove('hidden');
renderRules();
});
}
// --- Drag and Drop functionality --- // --- Drag and Drop functionality ---
function addDragEvents(item) { function addDragEvents(item) {
item.addEventListener('dragstart', (e) => { item.addEventListener('dragstart', (e) => {