feat: extract address tool into standalone project

This commit is contained in:
kory
2026-10-12 08:21:40 +08:00
commit 2fb416ac33
6 changed files with 1158 additions and 0 deletions
+802
View File
@@ -0,0 +1,802 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>快速地址与身份生成器</title>
<style>
:root {
--bg: #090d16;
--card-bg: rgba(22, 30, 49, 0.72);
--card-border: rgba(255, 255, 255, 0.12);
--card-inner-border: rgba(255, 255, 255, 0.08);
--primary: #3b82f6;
--primary-hover: #2563eb;
--btn-disabled-bg: #1e293b;
--btn-disabled-text: #475569;
--text: #f8fafc;
--text-muted: #94a3b8;
--text-secondary: #cbd5e1;
--success: #10b981;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background: radial-gradient(circle at 50% 0%, #172554 0%, var(--bg) 100%);
color: var(--text);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 24px 16px;
-webkit-font-smoothing: antialiased;
}
.container {
width: 100%;
max-width: 520px;
background: var(--card-bg);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid var(--card-border);
border-radius: 24px;
padding: 28px 22px;
box-shadow:
0 4px 6px -1px rgba(0, 0, 0, 0.2),
0 24px 48px -12px rgba(0, 0, 0, 0.5),
inset 0 1px 0 0 rgba(255, 255, 255, 0.15);
transition: max-width 0.3s ease;
}
.container.wide-mode {
max-width: 760px;
}
.header {
text-align: center;
margin-bottom: 20px;
}
.header h1 {
font-size: 22px;
font-weight: 700;
letter-spacing: -0.5px;
margin-bottom: 6px;
background: linear-gradient(180deg, #ffffff 0%, #94a3b8 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.header p {
font-size: 13px;
color: var(--text-muted);
}
/* 模式切换器 */
.mode-switch {
display: flex;
background: rgba(15, 23, 42, 0.6);
border: 1px solid var(--card-inner-border);
border-radius: 12px;
padding: 4px;
margin-bottom: 16px;
gap: 4px;
}
.mode-btn {
flex: 1;
padding: 8px 12px;
border: none;
background: transparent;
color: var(--text-muted);
font-size: 13px;
font-weight: 600;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
}
.mode-btn.active {
background: rgba(59, 130, 246, 0.2);
color: #60a5fa;
border: 1px solid rgba(59, 130, 246, 0.4);
}
.controls {
display: flex;
gap: 10px;
margin-bottom: 16px;
}
.controls input {
flex: 1;
background: rgba(15, 23, 42, 0.6);
border: 1px solid var(--card-inner-border);
border-radius: 12px;
padding: 12px 14px;
color: var(--text);
font-size: 14px;
outline: none;
transition: all 0.2s;
}
.controls input:focus {
border-color: var(--primary);
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
}
.cap-wrap {
display: flex;
justify-content: center;
margin-bottom: 16px;
width: 100%;
}
#capWidget {
width: 100%;
--cap-widget-width: 100%;
--cap-widget-height: 56px;
--cap-border-radius: 14px;
--cap-background: rgba(15, 23, 42, 0.65);
--cap-border-color: rgba(255, 255, 255, 0.14);
--cap-color: #f8fafc;
--cap-checkbox-background: rgba(255, 255, 255, 0.08);
--cap-checkbox-border: 1.5px solid rgba(255, 255, 255, 0.22);
--cap-checkbox-border-radius: 8px;
--cap-spinner-background-color: rgba(255, 255, 255, 0.12);
--cap-spinner-color: #3b82f6;
--cap-focus-ring: rgba(59, 130, 246, 0.45);
--cap-troubleshoot-color: #60a5fa;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
#capWidget:hover {
--cap-border-color: rgba(255, 255, 255, 0.24);
--cap-background: rgba(15, 23, 42, 0.8);
}
.btn-main {
width: 100%;
background: var(--primary);
border: none;
border-radius: 12px;
padding: 13px;
color: #fff;
font-size: 14px;
font-weight: 600;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: all 0.2s;
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);
}
.btn-main:hover:not(:disabled) {
background: var(--primary-hover);
transform: translateY(-1px);
}
.btn-main:active:not(:disabled) {
transform: translateY(0);
}
.btn-main:disabled {
background: var(--btn-disabled-bg);
color: var(--btn-disabled-text);
cursor: not-allowed;
box-shadow: none;
}
.result-box {
margin-top: 20px;
display: flex;
flex-direction: column;
gap: 10px;
}
/* 完整模式网格 */
.grid-2 {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
}
@media (max-width: 600px) {
.grid-2 {
grid-template-columns: 1fr;
}
}
.icon {
width: 14px;
height: 14px;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
fill: none;
vertical-align: -2px;
flex-shrink: 0;
display: inline-block;
margin-right: 4px;
}
.section-title {
font-size: 12px;
font-weight: 700;
color: #60a5fa;
text-transform: uppercase;
letter-spacing: 0.8px;
margin: 12px 0 4px 2px;
display: flex;
align-items: center;
gap: 6px;
}
.field-card {
background: rgba(15, 23, 42, 0.45);
border: 1px solid var(--card-inner-border);
border-radius: 12px;
padding: 11px 14px;
cursor: pointer;
transition: all 0.15s ease;
position: relative;
}
.field-card:hover {
background: rgba(30, 41, 59, 0.6);
border-color: rgba(255, 255, 255, 0.18);
transform: translateY(-1px);
}
.field-card:active {
transform: translateY(0);
}
.field-card .label {
font-size: 11px;
color: var(--text-muted);
margin-bottom: 4px;
display: flex;
justify-content: space-between;
align-items: center;
}
.field-card .copy-tip {
font-size: 10px;
color: #3b82f6;
opacity: 0;
transition: opacity 0.15s;
}
.field-card:hover .copy-tip {
opacity: 1;
}
.field-card .val {
font-size: 13px;
color: var(--text-secondary);
font-weight: 500;
word-break: break-all;
user-select: all;
}
.field-card .val.highlight {
color: #f8fafc;
font-weight: 600;
}
.btn-copy-all {
margin-top: 10px;
background: rgba(255, 255, 255, 0.06);
border: 1px solid var(--card-border);
border-radius: 12px;
padding: 12px;
color: var(--text);
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.btn-copy-all:hover {
background: rgba(255, 255, 255, 0.12);
border-color: rgba(255, 255, 255, 0.25);
}
.toast {
position: fixed;
bottom: 24px;
left: 50%;
transform: translateX(-50%) translateY(20px);
background: #0f172a;
border: 1px solid var(--card-border);
color: var(--text);
padding: 10px 18px;
border-radius: 30px;
font-size: 13px;
font-weight: 500;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5);
opacity: 0;
pointer-events: none;
transition: all 0.25s cubic-bezier(0.16, 1, 0.3, 1);
z-index: 9999;
display: flex;
align-items: center;
gap: 8px;
}
.toast.show {
transform: translateX(-50%) translateY(0);
opacity: 1;
}
.spinner {
width: 16px;
height: 16px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-top-color: #ffffff;
border-radius: 50%;
animation: spin 0.7s linear infinite;
display: none;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>
<script src="https://cdn.jsdelivr.net/npm/@cap.js/[email protected]"></script>
</head>
<body>
<div class="container" id="mainContainer">
<div class="header">
<h1>快速地址与身份生成</h1>
<p>点击一键生成身份、地址与安全信息</p>
</div>
<!-- 模式切换 Tab -->
<div class="mode-switch">
<button class="mode-btn active" id="tabSimple" onclick="switchMode('simple')">
<svg class="icon" viewBox="0 0 24 24"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" fill="currentColor" stroke="none"/></svg><span>精简模式</span>
</button>
<button class="mode-btn" id="tabFull" onclick="switchMode('full')">
<svg class="icon" viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg><span>完整档案</span>
</button>
</div>
<div class="controls">
<input type="text" id="cityInput" placeholder="输入地区/州(例如 California, TX, NY)" value="Virginia Beach" />
</div>
<div class="cap-wrap">
<cap-widget id="capWidget" data-cap-lang="zh-cn" data-cap-api-endpoint="https://cap.kory.kdns.fr/0817b51627/" data-cap-onsolve="onCapSolve()" onsolve="onCapSolve()"></cap-widget>
</div>
<button id="genBtn" class="btn-main" disabled>
<div id="spinner" class="spinner"></div>
<span id="btnText">点击一键生成</span>
</button>
<!-- 模式一:精简日常结果容器 -->
<div class="result-box" id="simpleResultBox" style="display: none;">
<div class="field-card" onclick="copyText(document.getElementById('s_fullName').innerText, '全名已复制')">
<div class="label">
<span>全名 (Full Name)</span>
<span class="copy-tip">点击复制</span>
</div>
<div class="val highlight" id="s_fullName">-</div>
</div>
<div class="field-card" onclick="copyText(document.getElementById('s_fullAddr').innerText, '完整地址已复制')">
<div class="label">
<span>完整地址 (Address)</span>
<span class="copy-tip">点击复制</span>
</div>
<div class="val highlight" id="s_fullAddr">-</div>
</div>
<div class="field-card" onclick="copyText(document.getElementById('s_phone').innerText, '电话号码已复制')">
<div class="label">
<span>电话号码 (Phone)</span>
<span class="copy-tip">点击复制</span>
</div>
<div class="val" id="s_phone">-</div>
</div>
<div class="field-card" onclick="copyText(document.getElementById('s_password').innerText, '强随机密码已复制')">
<div class="label">
<span>强随机密码 (Password)</span>
<span class="copy-tip">点击复制</span>
</div>
<div class="val highlight" id="s_password">-</div>
</div>
<div class="field-card" onclick="copyText(document.getElementById('s_birthday').innerText, '生日已复制')">
<div class="label">
<span>生日 (Birthday)</span>
<span class="copy-tip">点击复制</span>
</div>
<div class="val" id="s_birthday">-</div>
</div>
<div class="field-card" onclick="copyText(document.getElementById('s_secAnswer').innerText, '密保答案已复制')">
<div class="label">
<span id="s_secQuestion">密保问题 / 答案</span>
<span class="copy-tip">点击复制答案</span>
</div>
<div class="val" id="s_secAnswer">-</div>
</div>
<button class="btn-copy-all" onclick="copySimpleAll()">一键复制精简内容</button>
</div>
<!-- 模式二:完整档案结果容器 -->
<div class="result-box" id="fullResultBox" style="display: none;">
<div class="section-title"><svg class="icon" viewBox="0 0 24 24"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg> 个人身份与账户</div>
<div class="grid-2">
<div class="field-card" onclick="copyText(document.getElementById('f_fullName').innerText, '全名已复制')">
<div class="label"><span>全名 (Full Name)</span><span class="copy-tip">复制</span></div>
<div class="val highlight" id="f_fullName">-</div>
</div>
<div class="field-card" onclick="copyText(document.getElementById('f_splitName').innerText, '姓/名已复制')">
<div class="label"><span>名 / 姓 (First / Last)</span><span class="copy-tip">复制</span></div>
<div class="val" id="f_splitName">-</div>
</div>
<div class="field-card" onclick="copyText(document.getElementById('f_genderAge').innerText, '性别与生日已复制')">
<div class="label"><span>性别 / 生日 (Birthday)</span><span class="copy-tip">复制</span></div>
<div class="val" id="f_genderAge">-</div>
</div>
<div class="field-card" onclick="copyText(document.getElementById('f_ssn').innerText, 'SSN已复制')">
<div class="label"><span>社会安全号 (SSN)</span><span class="copy-tip">复制</span></div>
<div class="val highlight" id="f_ssn">-</div>
</div>
<div class="field-card" onclick="copyText(document.getElementById('f_email').innerText, '临时邮箱已复制')">
<div class="label"><span>邮箱 (Email)</span><span class="copy-tip">复制</span></div>
<div class="val" id="f_email">-</div>
</div>
<div class="field-card" onclick="copyText(document.getElementById('f_username').innerText, '用户名已复制')">
<div class="label"><span>推荐用户名 (Username)</span><span class="copy-tip">复制</span></div>
<div class="val" id="f_username">-</div>
</div>
</div>
<div class="section-title"><svg class="icon" viewBox="0 0 24 24"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg> 地址详细拆解</div>
<div class="field-card" onclick="copyText(document.getElementById('f_street').innerText, '街道地址已复制')">
<div class="label"><span>街道地址 (Street Address)</span><span class="copy-tip">复制</span></div>
<div class="val highlight" id="f_street">-</div>
</div>
<div class="grid-2">
<div class="field-card" onclick="copyText(document.getElementById('f_city').innerText, '城市已复制')">
<div class="label"><span>城市 (City)</span><span class="copy-tip">复制</span></div>
<div class="val" id="f_city">-</div>
</div>
<div class="field-card" onclick="copyText(document.getElementById('f_stateZip').innerText, '州与邮编已复制')">
<div class="label"><span>州 / 邮编 (State / Zip)</span><span class="copy-tip">复制</span></div>
<div class="val" id="f_stateZip">-</div>
</div>
</div>
<div class="field-card" onclick="copyText(document.getElementById('f_fullAddr').innerText, '单行完整地址已复制')">
<div class="label"><span>单行完整地址 (Full Line Address)</span><span class="copy-tip">复制</span></div>
<div class="val" id="f_fullAddr">-</div>
</div>
<div class="section-title"><svg class="icon" viewBox="0 0 24 24"><rect x="1" y="4" width="22" height="16" rx="2" ry="2"/><line x1="1" y1="10" x2="23" y2="10"/></svg> 虚拟支付卡信息</div>
<div class="field-card" onclick="copyText(document.getElementById('f_cardNumber').innerText, '信用卡号已复制')">
<div class="label"><span id="f_cardType">信用卡号 (Card Number)</span><span class="copy-tip">复制</span></div>
<div class="val highlight" id="f_cardNumber">-</div>
</div>
<div class="grid-2">
<div class="field-card" onclick="copyText(document.getElementById('f_cardExp').innerText, '到期时间已复制')">
<div class="label"><span>有效期 (Expires)</span><span class="copy-tip">复制</span></div>
<div class="val" id="f_cardExp">-</div>
</div>
<div class="field-card" onclick="copyText(document.getElementById('f_cardCvv').innerText, 'CVV安全码已复制')">
<div class="label"><span>安全码 (CVV)</span><span class="copy-tip">复制</span></div>
<div class="val highlight" id="f_cardCvv">-</div>
</div>
</div>
<div class="section-title"><svg class="icon" viewBox="0 0 24 24"><rect x="2" y="7" width="20" height="14" rx="2" ry="2"/><path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"/></svg> 辅助与职业资料</div>
<div class="grid-2">
<div class="field-card" onclick="copyText(document.getElementById('f_job').innerText, '公司与职业已复制')">
<div class="label"><span>职业 / 公司 (Job / Co.)</span><span class="copy-tip">复制</span></div>
<div class="val" id="f_job">-</div>
</div>
<div class="field-card" onclick="copyText(document.getElementById('f_phone').innerText, '电话已复制')">
<div class="label"><span>电话号码 (Phone)</span><span class="copy-tip">复制</span></div>
<div class="val" id="f_phone">-</div>
</div>
<div class="field-card" onclick="copyText(document.getElementById('f_password').innerText, '随机强密码已复制')">
<div class="label"><span>强随机密码 (Password)</span><span class="copy-tip">复制</span></div>
<div class="val highlight" id="f_password">-</div>
</div>
<div class="field-card" onclick="copyText(document.getElementById('f_physical').innerText, '体貌特征已复制')">
<div class="label"><span>身高 / 体重 / 血型</span><span class="copy-tip">复制</span></div>
<div class="val" id="f_physical">-</div>
</div>
</div>
<button class="btn-copy-all" onclick="copyFullAll()">一键复制完整档案 (TXT 格式)</button>
</div>
</div>
<div id="toast" class="toast">已复制到剪贴板</div>
<script>
const API_URL = "/api/v1/dz";
let isCapSolved = false;
let currentMode = localStorage.getItem("addr_mode") || "simple";
let currentData = null;
const mainContainer = document.getElementById("mainContainer");
const genBtn = document.getElementById("genBtn");
const btnText = document.getElementById("btnText");
const spinner = document.getElementById("spinner");
const cityInput = document.getElementById("cityInput");
const capWidget = document.getElementById("capWidget");
const toast = document.getElementById("toast");
const simpleResultBox = document.getElementById("simpleResultBox");
const fullResultBox = document.getElementById("fullResultBox");
const tabSimple = document.getElementById("tabSimple");
const tabFull = document.getElementById("tabFull");
// 初始化模式
switchMode(currentMode);
function switchMode(mode) {
currentMode = mode;
localStorage.setItem("addr_mode", mode);
if (mode === "full") {
tabFull.classList.add("active");
tabSimple.classList.remove("active");
mainContainer.classList.add("wide-mode");
if (currentData) {
fullResultBox.style.display = "flex";
simpleResultBox.style.display = "none";
}
} else {
tabSimple.classList.add("active");
tabFull.classList.remove("active");
mainContainer.classList.remove("wide-mode");
if (currentData) {
simpleResultBox.style.display = "flex";
fullResultBox.style.display = "none";
}
}
}
function onCapSolve() {
isCapSolved = true;
genBtn.disabled = false;
}
let toastTimer = null;
function showToast(msg, type = "success") {
toast.innerText = msg;
if (type === "warn") toast.style.borderColor = "#f59e0b";
else if (type === "error") toast.style.borderColor = "#ef4444";
else toast.style.borderColor = "#10b981";
toast.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(() => {
toast.classList.remove("show");
}, 2000);
}
function copyText(text, successMsg) {
if (!text || text === "-") return;
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(text).then(() => {
showToast(successMsg || "已复制到剪贴板");
}).catch(() => fallbackCopy(text, successMsg));
} else {
fallbackCopy(text, successMsg);
}
}
function fallbackCopy(text, successMsg) {
const textarea = document.createElement("textarea");
textarea.value = text;
textarea.style.position = "fixed";
textarea.style.left = "-9999px";
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand("copy");
showToast(successMsg || "已复制到剪贴板");
} catch (err) {
showToast("复制失败,请手动选择复制", "error");
}
document.body.removeChild(textarea);
}
function formatAddress(addr) {
const street = (addr.Address || "").trim();
const city = (addr.City || "").trim();
const state = (addr.State || "").trim();
const zip = (addr.Zip_Code || "").trim();
return `${street}, ${city}, ${state} ${zip}`.replace(/^, /, "").trim();
}
function renderData(addr) {
currentData = addr;
// 1. 渲染精简模式数据
document.getElementById("s_fullName").innerText = addr.Full_Name || "-";
document.getElementById("s_fullAddr").innerText = formatAddress(addr) || "-";
document.getElementById("s_phone").innerText = addr.Telephone || "-";
document.getElementById("s_password").innerText = addr.Generated_Password || addr.Password || "-";
document.getElementById("s_birthday").innerText = addr.Birthday ? `${addr.Birthday} (${addr.Gender || ""})` : "-";
document.getElementById("s_secQuestion").innerText = addr.Security_Question || "Mother's Maiden Name (密保)";
document.getElementById("s_secAnswer").innerText = addr.Security_Answer || "Smith";
// 2. 渲染完整模式数据
document.getElementById("f_fullName").innerText = addr.Full_Name || "-";
document.getElementById("f_splitName").innerText = `${addr.First_Name || ""} / ${addr.Last_Name || ""}`.trim() || "-";
document.getElementById("f_genderAge").innerText = `${addr.Gender || "N/A"}, ${addr.Birthday || "N/A"}`;
document.getElementById("f_ssn").innerText = addr.Social_Security_Number || "N/A";
document.getElementById("f_email").innerText = addr.Temporary_mail || "-";
document.getElementById("f_username").innerText = addr.Username || "-";
document.getElementById("f_street").innerText = addr.Address || "-";
document.getElementById("f_city").innerText = addr.City || "-";
document.getElementById("f_stateZip").innerText = `${addr.State || ""} ${addr.Zip_Code || ""}`.trim() || "-";
document.getElementById("f_fullAddr").innerText = formatAddress(addr) || "-";
document.getElementById("f_cardType").innerText = `信用卡 (${addr.Credit_Card_Type || "Card"})`;
document.getElementById("f_cardNumber").innerText = addr.Credit_Card_Number || "-";
document.getElementById("f_cardExp").innerText = addr.Expires || "-";
document.getElementById("f_cardCvv").innerText = addr.CVV2 || "-";
const co = addr.Company_Name ? ` @ ${addr.Company_Name}` : "";
document.getElementById("f_job").innerText = (addr.Occupation || "N/A") + co;
document.getElementById("f_phone").innerText = addr.Telephone || "-";
document.getElementById("f_password").innerText = addr.Generated_Password || addr.Password || "-";
const phy = [];
if (addr.Height) phy.push(addr.Height);
if (addr.Weight) phy.push(addr.Weight);
if (addr.Blood_Type) phy.push(`Blood: ${addr.Blood_Type}`);
document.getElementById("f_physical").innerText = phy.join(" | ") || "N/A";
// 显示对应盒子
if (currentMode === "full") {
fullResultBox.style.display = "flex";
simpleResultBox.style.display = "none";
} else {
simpleResultBox.style.display = "flex";
fullResultBox.style.display = "none";
}
}
function copySimpleAll() {
if (!currentData) return;
const d = currentData;
const text = [
`全名: ${d.Full_Name || ""}`,
`完整地址: ${formatAddress(d)}`,
`电话号码: ${d.Telephone || ""}`,
`强随机密码: ${d.Generated_Password || d.Password || ""}`,
`生日: ${d.Birthday || ""}`,
`密保问题: ${d.Security_Question || "Mother's Maiden Name"}`,
`密保答案: ${d.Security_Answer || "Smith"}`
].join("\n");
copyText(text, "已复制全部精简信息");
}
function copyFullAll() {
if (!currentData) return;
const d = currentData;
const text = [
`=== 个人身份 ===`,
`全名: ${d.Full_Name || ""}`,
`First Name: ${d.First_Name || ""}`,
`Last Name: ${d.Last_Name || ""}`,
`性别/生日: ${d.Gender || ""}, ${d.Birthday || ""}`,
`SSN: ${d.Social_Security_Number || ""}`,
`邮箱: ${d.Temporary_mail || ""}`,
`用户名: ${d.Username || ""}`,
`密码: ${d.Generated_Password || d.Password || ""}`,
``,
`=== 详细地址 ===`,
`街道: ${d.Address || ""}`,
`城市: ${d.City || ""}`,
`州: ${d.State || ""}`,
`邮编: ${d.Zip_Code || ""}`,
`完整地址: ${formatAddress(d)}`,
``,
`=== 支付信息 ===`,
`卡类型: ${d.Credit_Card_Type || ""}`,
`卡号: ${d.Credit_Card_Number || ""}`,
`到期日: ${d.Expires || ""}`,
`CVV: ${d.CVV2 || ""}`,
``,
`=== 辅助资料 ===`,
`电话: ${d.Telephone || ""}`,
`职业/公司: ${d.Occupation || ""} (${d.Company_Name || ""})`,
`身高/体重: ${d.Height || ""} / ${d.Weight || ""}`,
`血型: ${d.Blood_Type || ""}`
].join("\n");
copyText(text, "已复制完整档案 (TXT 格式)");
}
async function fetchAddress() {
if (!isCapSolved) {
showToast("请先完成验证", "warn");
return;
}
const city = cityInput.value.trim();
genBtn.disabled = true;
spinner.style.display = "inline-block";
btnText.innerText = "生成中...";
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 11000);
const res = await fetch(API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
city: city,
path: "/",
method: "refresh"
}),
signal: controller.signal
});
clearTimeout(timeoutId);
const data = await res.json();
if (res.ok && data && data.status === "ok" && data.address) {
renderData(data.address);
} else {
const errMsg = (data && data.message) ? data.message : "未获取到数据,请重试";
showToast(errMsg, "error");
}
} catch (err) {
console.error(err);
if (err.name === "AbortError") {
showToast("请求超时,请稍后重试", "error");
} else {
showToast("网络连接异常,请重试", "error");
}
} finally {
spinner.style.display = "none";
btnText.innerText = "点击一键生成";
isCapSolved = false;
genBtn.disabled = true;
if (capWidget && typeof capWidget.reset === "function") {
capWidget.reset();
}
}
}
genBtn.addEventListener("click", fetchAddress);
cityInput.addEventListener("keydown", (e) => {
if (e.key === "Enter" && !genBtn.disabled) fetchAddress();
});
</script>
</body>
</html>