初始化
This commit is contained in:
249
frontend/static/js/websites.js
Normal file
249
frontend/static/js/websites.js
Normal file
@@ -0,0 +1,249 @@
|
||||
/**
|
||||
* 网站管理页面交互逻辑
|
||||
*/
|
||||
var currentSiteId = null;
|
||||
var parsedImportData = null;
|
||||
|
||||
// ===== 网站选择 =====
|
||||
function selectWebsite(siteId, el) {
|
||||
currentSiteId = siteId;
|
||||
document.querySelector("#currentSiteId").value = siteId;
|
||||
document.querySelectorAll(".site-item").forEach(function(s) { s.classList.remove("active"); });
|
||||
if (el) el.classList.add("active");
|
||||
fetch("/api/websites/" + siteId).then(function(r) { return r.json(); }).then(renderSiteDetail);
|
||||
}
|
||||
|
||||
function renderSiteDetail(site) {
|
||||
var fName = getFolderName(site.folder_id);
|
||||
document.querySelector("#siteHeaderInfo").innerHTML =
|
||||
'<span style="font-size:11px;color:var(--text-tertiary);">' + fName + ' / </span>' +
|
||||
'<span style="font-size:15px;font-weight:600;">' + site.name + '</span>' +
|
||||
'<a href="' + site.url + '" target="_blank" style="font-size:11px;color:var(--brand);margin-left:8px;text-decoration:none;">' + site.url + '</a>';
|
||||
document.querySelector("#editSiteBtn").classList.remove("hidden");
|
||||
document.querySelector("#addAccountBtn").classList.remove("hidden");
|
||||
|
||||
var cards = document.querySelector("#accountCards");
|
||||
var accounts = site.accounts || [];
|
||||
if (!accounts.length) {
|
||||
cards.innerHTML = '<div style="text-align:center;padding:80px 20px;color:var(--text-placeholder);"><p>暂无账号</p></div>';
|
||||
return;
|
||||
}
|
||||
cards.innerHTML = accounts.map(function(a) {
|
||||
return '<div class="card" style="margin-bottom:12px;">' +
|
||||
'<div class="card-header">' +
|
||||
'<span class="card-title">👤 ' + (a.remark || '账号') + '</span>' +
|
||||
'<div class="card-actions">' +
|
||||
'<button class="card-btn edit" onclick="editAccount(' + a.id + ')">✏️ 编辑</button>' +
|
||||
'<button class="card-btn danger" onclick="deleteAccount(' + a.id + ')">🗑 删除</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="card-body">' +
|
||||
'<span class="label">账号</span><span class="value">' + esc(a.username) + '</span>' +
|
||||
'<button class="card-btn" onclick="copyText(\'' + esc(a.username) + '\')">复制</button>' +
|
||||
'<span class="label">密码</span><span class="value pw-hidden" id="pw-' + a.id + '" data-pw="' + esc(a.password) + '">••••••••</span>' +
|
||||
'<div style="display:flex;gap:4px;">' +
|
||||
'<button class="card-btn" onclick="togglePassword(' + a.id + ')">显示</button>' +
|
||||
'<button class="card-btn" onclick="copyText(\'' + esc(a.password) + '\')">复制</button>' +
|
||||
'</div>' +
|
||||
'<span class="label">说明</span><span style="color:var(--text-secondary);font-size:12px;">' + (a.remark || '-') + '</span><span></span>' +
|
||||
'</div></div>';
|
||||
}).join("");
|
||||
}
|
||||
|
||||
function esc(s) { return (s||"").replace(/'/g,"\\'"); }
|
||||
|
||||
function togglePassword(id) {
|
||||
var el = document.querySelector("#pw-" + id);
|
||||
if (!el) return;
|
||||
el.textContent = el.textContent === "••••••••" ? el.dataset.pw : "••••••••";
|
||||
}
|
||||
|
||||
function copyText(t) {
|
||||
navigator.clipboard.writeText(t).then(function() {
|
||||
var toast = document.createElement("div");
|
||||
toast.className = "toast";
|
||||
toast.textContent = "已复制";
|
||||
document.body.appendChild(toast);
|
||||
setTimeout(function() { toast.remove(); }, 1500);
|
||||
});
|
||||
}
|
||||
|
||||
function getFolderName(fid) {
|
||||
if (!fid) return "未分类";
|
||||
var el = document.querySelector('.folder-group[data-folder-id="' + fid + '"] .folder-item span:nth-child(2)');
|
||||
return el ? el.textContent.replace(/📁\s*/, "").trim() : "未分类";
|
||||
}
|
||||
|
||||
// ===== 文件夹 =====
|
||||
function toggleFolder(el) {
|
||||
var ch = el.parentElement.querySelector(".folder-children");
|
||||
var arrow = el.querySelector(".folder-arrow");
|
||||
if (!ch) return;
|
||||
if (ch.style.display === "none") { ch.style.display = "block"; arrow.textContent = "▼"; }
|
||||
else { ch.style.display = "none"; arrow.textContent = "▶"; }
|
||||
}
|
||||
|
||||
function toggleFolderMenu(btn) {
|
||||
document.querySelectorAll(".folder-menu").forEach(function(m) { if (m !== btn.nextElementSibling) m.classList.add("hidden"); });
|
||||
var menu = btn.nextElementSibling;
|
||||
if (menu) menu.classList.toggle("hidden");
|
||||
setTimeout(function() {
|
||||
document.addEventListener("click", function c() { if (menu) menu.classList.add("hidden"); document.removeEventListener("click", c); }, { once: true });
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function renameFolder(id) { var n = prompt("新名称:"); if (n) fetch("/api/folders/" + id, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: n }) }).then(function() { window.location.reload(); }); }
|
||||
function deleteFolder(id) { if (confirm("确定删除?内容将移至「未分类」")) fetch("/api/folders/" + id, { method: "DELETE" }).then(function() { window.location.reload(); }); }
|
||||
function showAddFolder() { var n = prompt("文件夹名:"); if (n) fetch("/api/folders", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: n, type: "website" }) }).then(function() { window.location.reload(); }); }
|
||||
|
||||
// ===== 智能导入 =====
|
||||
function showImportModal() {
|
||||
document.querySelector("#importModal").classList.remove("hidden");
|
||||
document.querySelector("#parseResult").classList.add("hidden");
|
||||
document.querySelector("#importText").value = "";
|
||||
document.querySelector("#importNewFolder").value = "";
|
||||
}
|
||||
function hideImportModal() { document.querySelector("#importModal").classList.add("hidden"); }
|
||||
|
||||
function parseImport() {
|
||||
var text = document.querySelector("#importText").value.trim();
|
||||
if (!text) return alert("请先粘贴文本");
|
||||
fetch("/api/import/parse", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ text: text }) })
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(d) {
|
||||
if (!d.ok) return alert(d.error);
|
||||
parsedImportData = d;
|
||||
document.querySelector("#parseResult").classList.remove("hidden");
|
||||
document.querySelector("#parseGrid").innerHTML =
|
||||
'<span style="color:var(--text-secondary);">网址</span><span>' + d.url + '</span>' +
|
||||
'<span style="color:var(--text-secondary);">网站名</span><span>' + d.site_name + '</span>' +
|
||||
'<span style="color:var(--text-secondary);">账号</span><span style="font-family:monospace;">' + d.username + '</span>' +
|
||||
'<span style="color:var(--text-secondary);">密码</span><span style="font-family:monospace;">' + d.password + '</span>' +
|
||||
'<span style="color:var(--text-secondary);">备注</span><span>' + (d.remark || '(无)') + '</span>';
|
||||
var alert = document.querySelector("#duplicateAlert");
|
||||
if (d.action === "append") {
|
||||
alert.classList.remove("hidden");
|
||||
alert.innerHTML = '⚠️ <strong>' + d.matched_site.name + '</strong> 已存在(' + d.matched_site.account_count + '个账号),将追加进去';
|
||||
if (d.matched_site.folder_id) document.querySelector("#importFolderSelect").value = d.matched_site.folder_id;
|
||||
} else { alert.classList.add("hidden"); }
|
||||
});
|
||||
}
|
||||
|
||||
function confirmImport() {
|
||||
if (!parsedImportData) return;
|
||||
var d = parsedImportData;
|
||||
var fid = document.querySelector("#importFolderSelect").value || null;
|
||||
var nf = document.querySelector("#importNewFolder").value.trim();
|
||||
if (nf) {
|
||||
fetch("/api/folders", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: nf, type: "website" }) })
|
||||
.then(function(r) { return r.json(); }).then(function(f) { doImport(d, f.id); });
|
||||
} else { doImport(d, fid); }
|
||||
}
|
||||
|
||||
function doImport(d, fid) {
|
||||
fetch("/api/websites").then(function(r) { return r.json(); }).then(function(sites) {
|
||||
var s = sites.find(function(x) { return x.url === d.url; });
|
||||
if (s) { addAccountToSite(s.id, d); }
|
||||
else {
|
||||
fetch("/api/websites", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: d.site_name, url: d.url, folder_id: fid }) })
|
||||
.then(function(r) { return r.json(); }).then(function(ns) { addAccountToSite(ns.id, d); });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addAccountToSite(sid, d) {
|
||||
fetch("/api/websites/" + sid + "/accounts", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username: d.username, password: d.password, remark: d.remark }) })
|
||||
.then(function() { hideImportModal(); window.location.reload(); });
|
||||
}
|
||||
|
||||
// ===== 网站弹窗 =====
|
||||
function showAddWebsite() {
|
||||
document.querySelector("#websiteModalTitle").textContent = "添加网站";
|
||||
document.querySelector("#websiteName").value = "";
|
||||
document.querySelector("#websiteUrl").value = "";
|
||||
document.querySelector("#websiteFolder").value = "";
|
||||
document.querySelector("#editWebsiteId").value = "";
|
||||
document.querySelector("#websiteModal").classList.remove("hidden");
|
||||
}
|
||||
function hideWebsiteModal() { document.querySelector("#websiteModal").classList.add("hidden"); }
|
||||
|
||||
function editWebsite() {
|
||||
if (!currentSiteId) return;
|
||||
document.querySelector("#websiteModalTitle").textContent = "编辑网站";
|
||||
document.querySelector("#editWebsiteId").value = currentSiteId;
|
||||
fetch("/api/websites/" + currentSiteId).then(function(r) { return r.json(); }).then(function(s) {
|
||||
document.querySelector("#websiteName").value = s.name;
|
||||
document.querySelector("#websiteUrl").value = s.url;
|
||||
document.querySelector("#websiteFolder").value = s.folder_id || "";
|
||||
document.querySelector("#websiteModal").classList.remove("hidden");
|
||||
});
|
||||
}
|
||||
|
||||
function saveWebsite() {
|
||||
var id = document.querySelector("#editWebsiteId").value;
|
||||
var data = { name: document.querySelector("#websiteName").value, url: document.querySelector("#websiteUrl").value, folder_id: document.querySelector("#websiteFolder").value || null };
|
||||
fetch(id ? "/api/websites/" + id : "/api/websites", { method: id ? "PUT" : "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) })
|
||||
.then(function() { hideWebsiteModal(); window.location.reload(); });
|
||||
}
|
||||
|
||||
// ===== 账号弹窗 =====
|
||||
function showAddAccount() {
|
||||
if (!currentSiteId) return alert("请先选择网站");
|
||||
document.querySelector("#accountModalTitle").textContent = "添加账号";
|
||||
document.querySelector("#accountUsername").value = "";
|
||||
document.querySelector("#accountPassword").value = "";
|
||||
document.querySelector("#accountRemark").value = "";
|
||||
document.querySelector("#editAccountId").value = "";
|
||||
document.querySelector("#accountPassword").type = "password";
|
||||
document.querySelector("#accountModal").classList.remove("hidden");
|
||||
}
|
||||
|
||||
function hideAccountModal() { document.querySelector("#accountModal").classList.add("hidden"); }
|
||||
|
||||
function editAccount(accId) {
|
||||
document.querySelector("#accountModalTitle").textContent = "编辑账号";
|
||||
document.querySelector("#editAccountId").value = accId;
|
||||
document.querySelector("#accountPassword").type = "password";
|
||||
fetch("/api/accounts/" + accId).then(function(r) { return r.json(); }).then(function(a) {
|
||||
document.querySelector("#accountUsername").value = a.username || "";
|
||||
document.querySelector("#accountPassword").value = a.password || "";
|
||||
document.querySelector("#accountRemark").value = a.remark || "";
|
||||
document.querySelector("#accountModal").classList.remove("hidden");
|
||||
});
|
||||
}
|
||||
|
||||
function togglePwVisibility() {
|
||||
var pw = document.querySelector("#accountPassword");
|
||||
var btn = pw.parentElement.querySelector("button");
|
||||
if (pw.type === "password") { pw.type = "text"; btn.textContent = "隐藏"; }
|
||||
else { pw.type = "password"; btn.textContent = "显示"; }
|
||||
}
|
||||
|
||||
function saveAccount() {
|
||||
var sid = document.querySelector("#currentSiteId").value;
|
||||
var aid = document.querySelector("#editAccountId").value;
|
||||
var data = { username: document.querySelector("#accountUsername").value, password: document.querySelector("#accountPassword").value, remark: document.querySelector("#accountRemark").value };
|
||||
if (aid) {
|
||||
fetch("/api/accounts/" + aid, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) })
|
||||
.then(function() { hideAccountModal(); selectWebsite(sid); });
|
||||
} else {
|
||||
fetch("/api/websites/" + sid + "/accounts", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) })
|
||||
.then(function() { hideAccountModal(); selectWebsite(sid); });
|
||||
}
|
||||
}
|
||||
|
||||
function deleteAccount(accId) {
|
||||
if (!confirm("确定删除?")) return;
|
||||
fetch("/api/accounts/" + accId, { method: "DELETE" }).then(function() { selectWebsite(currentSiteId); });
|
||||
}
|
||||
|
||||
// ===== 搜索 =====
|
||||
function filterSites() {
|
||||
var q = document.querySelector("#siteSearch").value.toLowerCase().trim();
|
||||
document.querySelectorAll(".site-item").forEach(function(item) {
|
||||
if (!q) { item.style.display = "flex"; return; }
|
||||
var name = (item.querySelector(".site-name")?.textContent || "").toLowerCase();
|
||||
var url = (item.dataset.siteUrl || "").toLowerCase();
|
||||
item.style.display = (name.indexOf(q) !== -1 || url.indexOf(q) !== -1) ? "flex" : "none";
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user