613 lines
18 KiB
HTML
613 lines
18 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, viewport-fit=cover" />
|
|
<title>Recharge Agency</title>
|
|
<style>
|
|
:root {
|
|
--page-width: 512px;
|
|
--brown-990: #290e00;
|
|
--panel-brown: #5e3513;
|
|
--gold-line: #f7dd92;
|
|
}
|
|
|
|
* {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
html,
|
|
body {
|
|
margin: 0;
|
|
min-height: 100%;
|
|
background: var(--brown-990);
|
|
font-family:
|
|
-apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial,
|
|
sans-serif;
|
|
}
|
|
|
|
body {
|
|
color: #fff7df;
|
|
padding-bottom: env(safe-area-inset-bottom);
|
|
}
|
|
|
|
.page {
|
|
width: min(100%, var(--page-width));
|
|
aspect-ratio: 1080 / 2410;
|
|
margin: 0 auto;
|
|
overflow: visible;
|
|
background: var(--brown-990);
|
|
}
|
|
|
|
.canvas {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: var(--brown-990);
|
|
overflow: visible;
|
|
}
|
|
|
|
.sr-only {
|
|
position: absolute;
|
|
width: 1px;
|
|
height: 1px;
|
|
padding: 0;
|
|
margin: -1px;
|
|
overflow: hidden;
|
|
clip: rect(0, 0, 0, 0);
|
|
white-space: nowrap;
|
|
border: 0;
|
|
}
|
|
|
|
.hero-scene {
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
width: 100%;
|
|
height: 70.9959%;
|
|
overflow: hidden;
|
|
-webkit-mask: url("../../assets/recharge-agency-list/mask.svg") 0 0 / 100% 100% no-repeat;
|
|
mask: url("../../assets/recharge-agency-list/mask.svg") 0 0 / 100% 100% no-repeat;
|
|
}
|
|
|
|
.hero-layer {
|
|
position: absolute;
|
|
display: block;
|
|
max-width: none;
|
|
pointer-events: none;
|
|
user-select: none;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.hero-layer.top-glow {
|
|
left: -16.3889%;
|
|
top: -2.9807%;
|
|
width: 132.7778%;
|
|
height: 45.7043%;
|
|
}
|
|
|
|
.hero-layer.palace {
|
|
left: 0;
|
|
top: -30.0994%;
|
|
width: 100%;
|
|
height: 133.2554%;
|
|
}
|
|
|
|
.hero-layer.figure {
|
|
left: 0;
|
|
top: -12.858%;
|
|
width: 100%;
|
|
height: 104.0327%;
|
|
}
|
|
|
|
.title-ribbon {
|
|
position: absolute;
|
|
left: 1.8519%;
|
|
top: 56.722%;
|
|
width: 96.2037%;
|
|
height: 4.6888%;
|
|
display: block;
|
|
max-width: none;
|
|
pointer-events: none;
|
|
user-select: none;
|
|
}
|
|
|
|
.content-panel {
|
|
position: absolute;
|
|
left: 5.3704%;
|
|
top: 63.0705%;
|
|
width: 89.1667%;
|
|
height: auto;
|
|
overflow: visible;
|
|
}
|
|
|
|
.agency-list {
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
overflow: visible;
|
|
}
|
|
|
|
.agency-row {
|
|
width: 100%;
|
|
aspect-ratio: 963 / 275;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 0 24px;
|
|
border: 1px solid var(--gold-line);
|
|
border-radius: 2.9076% / 10.1818%;
|
|
background: var(--panel-brown);
|
|
box-shadow:
|
|
inset 0 0 0 1px rgba(255, 214, 109, 0.16),
|
|
0 6px 18px rgba(38, 11, 0, 0.35);
|
|
}
|
|
|
|
.agency-avatar {
|
|
width: 52px;
|
|
height: 52px;
|
|
flex: 0 0 52px;
|
|
border-radius: 50%;
|
|
border: 1px solid rgba(255, 220, 136, 0.78);
|
|
background: rgba(46, 18, 0, 0.55);
|
|
object-fit: cover;
|
|
}
|
|
|
|
.agency-main {
|
|
min-width: 0;
|
|
flex: 1;
|
|
}
|
|
|
|
.agency-name {
|
|
display: block;
|
|
color: #fff3cf;
|
|
font-size: 13px;
|
|
font-weight: 700;
|
|
line-height: 1.15;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.agency-meta {
|
|
margin-top: 4px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
color: rgba(255, 238, 195, 0.74);
|
|
font-size: 10px;
|
|
line-height: 1.15;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.agency-flags {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 3px;
|
|
flex: 0 0 auto;
|
|
}
|
|
|
|
.agency-flag {
|
|
width: 16px;
|
|
height: 16px;
|
|
border-radius: 50%;
|
|
border: 1px solid rgba(255, 238, 195, 0.62);
|
|
object-fit: cover;
|
|
background: rgba(46, 18, 0, 0.6);
|
|
}
|
|
|
|
.agency-actions {
|
|
flex: 0 0 78px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
justify-content: center;
|
|
gap: 5px;
|
|
}
|
|
|
|
.agency-action {
|
|
appearance: none;
|
|
width: 78px;
|
|
height: 24px;
|
|
margin: 0;
|
|
padding: 0 7px;
|
|
border: 1px solid rgba(255, 222, 126, 0.72);
|
|
border-radius: 999px;
|
|
font-size: 10px;
|
|
font-weight: 800;
|
|
line-height: 1;
|
|
letter-spacing: 0;
|
|
white-space: nowrap;
|
|
cursor: pointer;
|
|
-webkit-tap-highlight-color: transparent;
|
|
box-shadow: 0 2px 8px rgba(38, 11, 0, 0.3);
|
|
}
|
|
|
|
.agency-action:active {
|
|
transform: translateY(1px);
|
|
}
|
|
|
|
.agency-action.whatsapp {
|
|
border-color: rgba(159, 255, 185, 0.84);
|
|
background: linear-gradient(180deg, #27c66a, #168a49);
|
|
color: #ffffff;
|
|
}
|
|
|
|
.agency-action.profile {
|
|
background: linear-gradient(180deg, #fff2a8, #efb43d);
|
|
color: #552300;
|
|
}
|
|
|
|
.agency-state {
|
|
min-height: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: rgba(255, 238, 195, 0.78);
|
|
font-size: 13px;
|
|
font-weight: 650;
|
|
text-align: center;
|
|
padding: 0 18px;
|
|
aspect-ratio: 963 / 275;
|
|
border: 1px solid var(--gold-line);
|
|
border-radius: 2.9076% / 10.1818%;
|
|
background: var(--panel-brown);
|
|
}
|
|
|
|
.bottom-frame {
|
|
position: absolute;
|
|
left: 1.8056%;
|
|
top: var(--bottom-frame-top, 91%);
|
|
width: 96.1574%;
|
|
height: 2.4066%;
|
|
display: block;
|
|
max-width: none;
|
|
pointer-events: none;
|
|
user-select: none;
|
|
transform: scaleY(-1);
|
|
transform-origin: center;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<main class="page" aria-labelledby="page-title">
|
|
<div class="canvas" data-node-id="220:1781">
|
|
<h1 id="page-title" class="sr-only">Recharge Agency</h1>
|
|
|
|
<div class="hero-scene" data-node-id="220:1782">
|
|
<img class="hero-layer top-glow" src="../../assets/recharge-agency-list/top-glow.png" alt="" />
|
|
<img class="hero-layer palace" src="../../assets/recharge-agency-list/palace-hall.png" alt="" />
|
|
<img class="hero-layer figure" src="../../assets/recharge-agency-list/title-figure.png" alt="" />
|
|
</div>
|
|
|
|
<img class="title-ribbon" src="../../assets/recharge-agency-list/title-ribbon.png" alt="Recharge Agency" />
|
|
|
|
<section class="content-panel" aria-label="Recharge agency list">
|
|
<div class="agency-list" data-agency-list>
|
|
<div class="agency-state">Loading agencies...</div>
|
|
</div>
|
|
</section>
|
|
|
|
<svg class="bottom-frame" aria-hidden="true" viewBox="0 0 1038.5 58" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
<path d="M0 58L9 49V28L34 8H193.5L200 17.5H840.5L845.5 8H1005.5L1031 28.5V49L1038.5 58V25.5L1010 0H840.5L834.5 11.5H204L197 0H30L0 25.5V58Z" fill="url(#bottom-frame-gradient)" />
|
|
<defs>
|
|
<linearGradient id="bottom-frame-gradient" x1="0" y1="29" x2="1038.5" y2="29" gradientUnits="userSpaceOnUse">
|
|
<stop stop-color="#FFCC5A" />
|
|
<stop offset="0.1342" stop-color="#FAE47E" />
|
|
<stop offset="0.721154" stop-color="#FAE47E" />
|
|
<stop offset="1" stop-color="#FFCC5A" />
|
|
</linearGradient>
|
|
</defs>
|
|
</svg>
|
|
</div>
|
|
</main>
|
|
<script>
|
|
(function () {
|
|
const DEFAULT_API_BASE = "https://jvapi.haiyihy.com/";
|
|
const DEFAULT_API_PREFIX = "/go";
|
|
const ENDPOINT = "/app/h5/recharge-agency-list/sellers";
|
|
const DEFAULT_AVATAR = "../../assets/defaultAvatar-CdxWBK1k.png";
|
|
const query = new URLSearchParams(window.location.search || "");
|
|
const hashQuery = new URLSearchParams((window.location.hash.split("?")[1] || "").split("#")[0]);
|
|
const listNode = document.querySelector("[data-agency-list]");
|
|
|
|
function trimValue(value) {
|
|
return String(value || "").trim();
|
|
}
|
|
|
|
function readParam(names) {
|
|
for (const name of names) {
|
|
const fromQuery = trimValue(query.get(name));
|
|
if (fromQuery) return fromQuery;
|
|
const fromHash = trimValue(hashQuery.get(name));
|
|
if (fromHash) return fromHash;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function normalizeBaseURL(base) {
|
|
const value = trimValue(base) || DEFAULT_API_BASE;
|
|
return value.endsWith("/") ? value : value + "/";
|
|
}
|
|
|
|
function resolveAPIBase() {
|
|
const override = readParam(["apiBase", "api_base"]);
|
|
if (override) return normalizeBaseURL(override);
|
|
if (typeof window.__RECHARGE_AGENCY_API_BASE__ === "string") {
|
|
return normalizeBaseURL(window.__RECHARGE_AGENCY_API_BASE__);
|
|
}
|
|
return normalizeBaseURL(DEFAULT_API_BASE);
|
|
}
|
|
|
|
function resolveAPIPrefix(apiBase) {
|
|
const override = readParam(["apiPrefix", "api_prefix"]);
|
|
if (override) return override;
|
|
if (typeof window.__RECHARGE_AGENCY_API_PREFIX__ === "string") {
|
|
return trimValue(window.__RECHARGE_AGENCY_API_PREFIX__);
|
|
}
|
|
try {
|
|
return new URL(apiBase).origin === new URL(DEFAULT_API_BASE).origin ? DEFAULT_API_PREFIX : "";
|
|
} catch (_) {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
function joinPath() {
|
|
return "/" + Array.prototype.slice.call(arguments)
|
|
.map((part) => trimValue(part).replace(/^\/+|\/+$/g, ""))
|
|
.filter(Boolean)
|
|
.join("/");
|
|
}
|
|
|
|
function buildURL(path) {
|
|
const apiBase = resolveAPIBase();
|
|
const apiPrefix = resolveAPIPrefix(apiBase);
|
|
const url = new URL(joinPath(apiPrefix, path).replace(/^\/+/, ""), apiBase);
|
|
url.searchParams.set("cursor", readParam(["cursor"]) || "1");
|
|
url.searchParams.set("limit", readParam(["limit"]) || "20");
|
|
return url;
|
|
}
|
|
|
|
function normalizeAuthorization(value) {
|
|
const token = trimValue(value);
|
|
if (!token) return "";
|
|
return /^bearer\s+/i.test(token) ? token : "Bearer " + token;
|
|
}
|
|
|
|
function readAuthorization() {
|
|
const direct = readParam(["Authorization", "authorization"]);
|
|
if (direct) return normalizeAuthorization(direct);
|
|
const token = readParam(["token", "accessToken", "access_token"]);
|
|
if (token) return normalizeAuthorization(token);
|
|
if (typeof window.__RECHARGE_AGENCY_AUTHORIZATION__ === "string") {
|
|
return normalizeAuthorization(window.__RECHARGE_AGENCY_AUTHORIZATION__);
|
|
}
|
|
try {
|
|
return normalizeAuthorization(
|
|
window.localStorage.getItem("Authorization") ||
|
|
window.localStorage.getItem("authorization") ||
|
|
window.localStorage.getItem("token") ||
|
|
window.localStorage.getItem("accessToken")
|
|
);
|
|
} catch (_) {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
function clearList() {
|
|
while (listNode.firstChild) listNode.removeChild(listNode.firstChild);
|
|
}
|
|
|
|
function syncListLayout(count) {
|
|
const safeCount = Math.max(1, Number(count) || 1);
|
|
const listTop = 63.0705;
|
|
const cardHeight = 11.4108;
|
|
const cardGap = 1.16;
|
|
const bottomGap = 3.95;
|
|
const listHeight = safeCount * cardHeight + Math.max(0, safeCount - 1) * cardGap;
|
|
const bottomTop = Math.max(91, listTop + listHeight + bottomGap);
|
|
document.documentElement.style.setProperty("--bottom-frame-top", bottomTop.toFixed(4) + "%");
|
|
}
|
|
|
|
function renderState(message) {
|
|
syncListLayout(1);
|
|
clearList();
|
|
const node = document.createElement("div");
|
|
node.className = "agency-state";
|
|
node.textContent = message;
|
|
listNode.appendChild(node);
|
|
}
|
|
|
|
function compactNumber(value) {
|
|
const number = Number(value || 0);
|
|
if (!Number.isFinite(number) || number <= 0) return "";
|
|
if (number >= 1000000) return (number / 1000000).toFixed(1).replace(/\.0$/, "") + "M";
|
|
if (number >= 1000) return (number / 1000).toFixed(1).replace(/\.0$/, "") + "K";
|
|
return String(Math.trunc(number));
|
|
}
|
|
|
|
function createImage(className, src, fallback) {
|
|
const img = document.createElement("img");
|
|
img.className = className;
|
|
img.alt = "";
|
|
img.decoding = "async";
|
|
img.loading = "lazy";
|
|
img.src = trimValue(src) || fallback || "";
|
|
img.onerror = function () {
|
|
if (fallback && img.src.indexOf(fallback) === -1) {
|
|
img.src = fallback;
|
|
}
|
|
};
|
|
return img;
|
|
}
|
|
|
|
function isInAppWebView() {
|
|
return !!(
|
|
window.FlutterPageControl &&
|
|
typeof window.FlutterPageControl.postMessage === "function"
|
|
);
|
|
}
|
|
|
|
function sendToApp(message) {
|
|
if (!isInAppWebView()) {
|
|
console.warn("[AppBridge] FlutterPageControl is not available", message);
|
|
return false;
|
|
}
|
|
window.FlutterPageControl.postMessage(String(message));
|
|
return true;
|
|
}
|
|
|
|
function openUserProfile(uid) {
|
|
const safeUID = trimValue(uid).replace(/:/g, "");
|
|
if (!safeUID) return false;
|
|
return sendToApp("view_user_info:" + safeUID);
|
|
}
|
|
|
|
function normalizeWhatsapp(value) {
|
|
const raw = trimValue(value);
|
|
if (!raw) return "";
|
|
const linkMatch = raw.match(/(?:wa\.me\/|phone=)(\+?\d[\d\s().-]*)/i);
|
|
const phoneSource = linkMatch ? linkMatch[1] : ((raw.match(/\+?\d[\d\s().-]{5,}\d/) || [raw])[0]);
|
|
const digits = phoneSource.replace(/\D/g, "");
|
|
return digits.length >= 6 ? digits : "";
|
|
}
|
|
|
|
function buildWhatsappURL(value) {
|
|
const raw = trimValue(value);
|
|
if (/^https?:\/\/(?:wa\.me|api\.whatsapp\.com|www\.whatsapp\.com)\//i.test(raw)) {
|
|
return raw;
|
|
}
|
|
const phone = normalizeWhatsapp(raw);
|
|
return phone ? "https://wa.me/" + encodeURIComponent(phone) : "";
|
|
}
|
|
|
|
function openWhatsapp(value) {
|
|
const url = buildWhatsappURL(value);
|
|
if (!url) return false;
|
|
let opened = null;
|
|
if (typeof window.open === "function") {
|
|
opened = window.open(url, "_blank", "noopener");
|
|
}
|
|
if (!opened) {
|
|
window.location.href = url;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function createRow(item) {
|
|
const row = document.createElement("article");
|
|
row.className = "agency-row";
|
|
row.dataset.userId = trimValue(item.userId);
|
|
|
|
row.appendChild(createImage("agency-avatar", item.userAvatar, DEFAULT_AVATAR));
|
|
|
|
const main = document.createElement("div");
|
|
main.className = "agency-main";
|
|
|
|
const name = document.createElement("span");
|
|
name.className = "agency-name";
|
|
name.textContent = item.userNickname || item.account || ("ID " + item.userId);
|
|
main.appendChild(name);
|
|
|
|
const meta = document.createElement("div");
|
|
meta.className = "agency-meta";
|
|
const place = [item.countryName, item.regionCode].filter(Boolean).join(" / ");
|
|
const count = compactNumber(item.transactionCount);
|
|
const metaParts = [];
|
|
if (place) metaParts.push(place);
|
|
metaParts.push("ID " + (item.account || item.userId));
|
|
if (count) metaParts.push(count + " orders");
|
|
meta.textContent = metaParts.join(" · ");
|
|
main.appendChild(meta);
|
|
row.appendChild(main);
|
|
|
|
const flags = document.createElement("div");
|
|
flags.className = "agency-flags";
|
|
const flagValues = (Array.isArray(item.nationalFlag) ? item.nationalFlag : []).slice(0, 3).filter(Boolean);
|
|
flagValues.forEach(function (flag) {
|
|
flags.appendChild(createImage("agency-flag", flag, ""));
|
|
});
|
|
if (flagValues.length) row.appendChild(flags);
|
|
|
|
const actions = document.createElement("div");
|
|
actions.className = "agency-actions";
|
|
const whatsapp = item.whatsapp || item.whatsApp || item.contactInfo;
|
|
if (normalizeWhatsapp(whatsapp)) {
|
|
const whatsappButton = document.createElement("button");
|
|
whatsappButton.type = "button";
|
|
whatsappButton.className = "agency-action whatsapp";
|
|
whatsappButton.textContent = "WhatsApp";
|
|
whatsappButton.setAttribute("aria-label", "Open WhatsApp");
|
|
whatsappButton.addEventListener("click", function (event) {
|
|
event.stopPropagation();
|
|
openWhatsapp(whatsapp);
|
|
});
|
|
actions.appendChild(whatsappButton);
|
|
}
|
|
|
|
const profileButton = document.createElement("button");
|
|
profileButton.type = "button";
|
|
profileButton.className = "agency-action profile";
|
|
profileButton.textContent = "Profile";
|
|
profileButton.setAttribute("aria-label", "Open user profile");
|
|
profileButton.addEventListener("click", function (event) {
|
|
event.stopPropagation();
|
|
openUserProfile(item.userId);
|
|
});
|
|
actions.appendChild(profileButton);
|
|
row.appendChild(actions);
|
|
|
|
return row;
|
|
}
|
|
|
|
function normalizePayload(json) {
|
|
const body = json && (json.body || json.data || json);
|
|
return body && Array.isArray(body.records) ? body.records : [];
|
|
}
|
|
|
|
async function fetchAgencies() {
|
|
if (!listNode) return;
|
|
const authorization = readAuthorization();
|
|
if (!authorization) {
|
|
renderState("Open in app to view agencies.");
|
|
return;
|
|
}
|
|
try {
|
|
const response = await fetch(buildURL(ENDPOINT).toString(), {
|
|
cache: "no-store",
|
|
headers: {
|
|
Accept: "application/json",
|
|
Authorization: authorization,
|
|
},
|
|
});
|
|
const json = await response.json().catch(function () { return {}; });
|
|
if (!response.ok || json.status === false) {
|
|
throw new Error(json.errorMsg || json.message || "Unable to load agencies.");
|
|
}
|
|
const records = normalizePayload(json);
|
|
if (!records.length) {
|
|
renderState("No agencies available.");
|
|
return;
|
|
}
|
|
syncListLayout(records.length);
|
|
clearList();
|
|
records.forEach(function (item) {
|
|
listNode.appendChild(createRow(item || {}));
|
|
});
|
|
} catch (error) {
|
|
renderState(error && error.message ? error.message : "Unable to load agencies.");
|
|
}
|
|
}
|
|
|
|
fetchAgencies();
|
|
})();
|
|
</script>
|
|
</body>
|
|
|
|
</html>
|