chatapp-h5/h5/bd-leader-center/bd-leader-center.js
2026-04-25 14:38:02 +08:00

565 lines
19 KiB
JavaScript

import { historyMoreUrl } from "../bd-static/shared.js";
const API_BASE_URL = "https://jvapi.haiyihy.com";
const labels = {
en: {
bd_leader_center: "BD Leader Center",
my_available_salary: "My available salary",
my_salary_link: "Details",
bd_policy_link: "BD policy",
agency_list_link: "Agency list",
bd_list_link: "BD List",
invite_become_agent: "Invite to become agent",
invite_become_BD: "Invite to become BD",
bank_cards_link: "Bank cards",
send_welcome_gift: "Send welcome gift",
bd_salary: "BD salary",
bd_leader_history: "BD Leader history",
total_income: "Total income",
previous_period_income: "Previous period income",
agency_number: "Agency number",
bd_number: "BD number",
team_salary: "Team salary",
team_top_up: "Team top up",
bd_income_salary: "BD income salary",
bd_income_top_up: "BD income top up",
user_id_prefix: "ID: ",
salary: "Salary",
recharge: "Recharge",
host: "Host",
agencies: "Agencies",
total_team_salary: "Total team salary",
more: "More",
in_progress: "In Progress",
pending: "Pending",
completed: "Completed",
out_of_account: "Out of account",
failed_to_load_balance: "Failed to load balance. Please try again.",
failed_to_load_history: "Failed to load history. Please try again.",
request_not_available: "Request failed. Please try again."
},
ar: {},
tr: {},
bn: {}
};
["ar", "tr", "bn"].forEach((lang) => {
labels[lang] = { ...labels.en };
});
let commonHeaders = {
authorization: "Bearer undefined",
"req-app-intel": "version=1.0.0;build=1;model=SM-G9550;sysVersion=9;channel=Google",
"req-client": "Android",
"req-imei": "8fa54d728ab449e04f9292329ed44bda",
"req-lang": "id-ID",
"req-sys-origin": "origin=ATYOU;originChild=ATYOU",
"req-version": "V2",
"req-zone": "Asia/Shanghai"
};
const state = {
lang: resolveLanguage(),
balanceTotal: {},
userInfo: null,
teamInfo: null,
identityInfo: null,
identity: "BD_LEADER",
modalMode: "",
historyType: "bd",
bdHistory: {},
bdLeaderHistory: {},
historyMore: {}
};
function resolveLanguage() {
const params = new URLSearchParams(window.location.search);
const hashQuery = window.location.hash.includes("?") ? window.location.hash.split("?")[1] : "";
const hashParams = new URLSearchParams(hashQuery);
const lang = params.get("lang") || hashParams.get("lang") || navigator.language || "en";
if (lang.startsWith("ar")) return "ar";
if (lang.startsWith("tr")) return "tr";
if (lang.startsWith("bn")) return "bn";
return "en";
}
function t(key) {
return labels[state.lang]?.[key] || labels.en[key] || key;
}
function setLanguage() {
document.documentElement.lang = state.lang;
document.documentElement.dir = state.lang === "ar" ? "rtl" : "ltr";
document.querySelector("[data-action='language']").textContent = state.lang.toUpperCase();
document.querySelectorAll("[data-i18n]").forEach((node) => {
node.textContent = t(node.dataset.i18n);
});
}
function setIcons() {
const icon = (body) => `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(`
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none">
${body}
</svg>
`)}`;
const iconMap = {
policy: icon(`
<path d="M13 8h15l7 7v25H13V8Z" fill="#E9F8EF" stroke="#22945D" stroke-width="3" stroke-linejoin="round"/>
<path d="M28 8v8h7" fill="#BDEBD0" stroke="#22945D" stroke-width="3" stroke-linejoin="round"/>
<path d="M18 32h4l2-9 4 14 2-5h4" stroke="#1766C2" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
`),
bank: icon(`
<path d="M8 20h32L24 10 8 20Z" fill="#FFF2C7" stroke="#C08212" stroke-width="3" stroke-linejoin="round"/>
<path d="M12 22v14M20 22v14M28 22v14M36 22v14" stroke="#2C7A4B" stroke-width="3" stroke-linecap="round"/>
<path d="M9 38h30" stroke="#C08212" stroke-width="3" stroke-linecap="round"/>
`),
gift: icon(`
<path d="M11 22h26v18H11V22Z" fill="#FFF0B8" stroke="#C47B13" stroke-width="3" stroke-linejoin="round"/>
<path d="M8 15h32v7H8v-7Z" fill="#FFE4E8" stroke="#D94B63" stroke-width="3" stroke-linejoin="round"/>
<path d="M24 15v25" stroke="#D94B63" stroke-width="3" stroke-linecap="round"/>
<path d="M24 15c-7-1-10-3-10-6 0-2 2-4 4-4 4 0 6 6 6 10ZM24 15c7-1 10-3 10-6 0-2-2-4-4-4-4 0-6 6-6 10Z" fill="#FFFFFF" stroke="#D94B63" stroke-width="3" stroke-linejoin="round"/>
`),
invite: icon(`
<circle cx="18" cy="17" r="6" fill="#E8F3FF" stroke="#2878D8" stroke-width="3"/>
<path d="M7 37c1.5-7 6-11 11-11 2.8 0 5.4 1.2 7.3 3.4" stroke="#2878D8" stroke-width="3" stroke-linecap="round"/>
<path d="M34 19v14M27 26h14" stroke="#2C9B63" stroke-width="3" stroke-linecap="round"/>
`),
arrow: icon(`
<path d="M19 12l12 12-12 12" stroke="#62A87C" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
`),
salary: icon(`
<circle cx="24" cy="24" r="17" fill="#E8F8EF" stroke="#2C9B63" stroke-width="3"/>
<path d="M24 13v22" stroke="#2C9B63" stroke-width="3" stroke-linecap="round"/>
<path d="M30 17.5c-1.4-1.4-3.4-2.2-6-2.2-3.5 0-6 1.8-6 4.7 0 6.7 12 2.8 12 9.1 0 2.7-2.5 4.6-6.2 4.6-2.8 0-5.3-.9-7-2.5" stroke="#D69E17" stroke-width="3" stroke-linecap="round"/>
`)
};
document.querySelectorAll("[data-icon]").forEach((img) => {
img.src = iconMap[img.dataset.icon] || "";
});
}
function splitHeaderPairs(value) {
const result = {};
if (!value) return result;
value.split(";").forEach((item) => {
const index = item.indexOf("=");
if (index > -1) result[item.slice(0, index)] = item.slice(index + 1);
});
return result;
}
function parseAccessOrigin(raw) {
const data = JSON.parse(raw);
const appIntel = splitHeaderPairs(data["Req-App-Intel"]);
const sysOrigin = splitHeaderPairs(data["Req-Sys-Origin"]);
return {
reqLang: data["Req-Lang"],
authorization: data.Authorization,
buildVersion: appIntel.build,
appVersion: appIntel.version,
appChannel: appIntel.channel,
reqImei: appIntel["Req-Imei"],
sysOrigin: sysOrigin.origin,
sysOriginChild: sysOrigin.child
};
}
function setHeadersFromApp(headers) {
const next = {};
if (headers.authorization) next.authorization = headers.authorization;
if (headers.reqLang) next["req-lang"] = headers.reqLang;
if (headers.appVersion || headers.buildVersion || headers.appChannel) {
const intel = [];
if (headers.appVersion) intel.push(`version=${headers.appVersion}`);
if (headers.buildVersion) intel.push(`build=${headers.buildVersion}`);
if (headers.appChannel) intel.push(`channel=${headers.appChannel}`);
next["req-app-intel"] = intel.join(";");
}
if (headers.reqImei) next["req-imei"] = headers.reqImei;
if (headers.sysOrigin) {
const origin = [`origin=${headers.sysOrigin}`];
if (headers.sysOriginChild) origin.push(`child=${headers.sysOriginChild}`);
next["req-sys-origin"] = origin.join(";");
}
commonHeaders = { ...commonHeaders, ...next };
}
function isMobileDevice() {
return /Android|iPad|iPhone|iPod/.test(navigator.userAgent);
}
function isAppEnvironment() {
return Boolean(window.app || window.webkit || window.FlutterPageControl || window.FlutterApp);
}
function requestAccessOrigin() {
return new Promise((resolve) => {
window.renderData = resolve;
window.getIosAccessOriginParam = resolve;
if (!isAppEnvironment()) {
setHeadersFromApp({
authorization: "",
reqLang: state.lang,
appVersion: "1.0.0",
buildVersion: "1",
appChannel: "Web",
reqImei: "H5-STATIC",
sysOrigin: "LIKEI",
sysOriginChild: "LIKEI"
});
resolve(null);
return;
}
let attempt = 0;
const poll = () => {
attempt += 1;
if (window.app && typeof window.app.getAccessOrigin === "function") {
resolve(window.app.getAccessOrigin());
return;
}
if (attempt < 50) {
window.setTimeout(poll, 100);
return;
}
if (window.FlutterApp && typeof window.FlutterApp.postMessage === "function") {
window.FlutterApp.postMessage("requestAccessOrigin");
}
};
if (isMobileDevice() || isAppEnvironment()) {
poll();
} else {
resolve(null);
}
});
}
async function connectToApp() {
const raw = await requestAccessOrigin();
if (!raw) return { success: true, environment: "browser" };
setHeadersFromApp(parseAccessOrigin(raw));
return { success: true, environment: "app" };
}
async function apiGet(path) {
const response = await fetch(`${API_BASE_URL}${path}`, {
method: "GET",
headers: commonHeaders
});
const data = await response.json().catch(() => ({}));
if (!response.ok || data.status === false) {
const error = new Error(data.errorMsg || data.message || `Request failed: ${response.status}`);
error.response = data;
throw error;
}
return data;
}
async function fetchUserProfile() {
const result = await apiGet("/team/member/profile");
if (result.status && result.body) state.userInfo = result.body;
}
async function fetchTeamInfo() {
try {
const result = await apiGet("/team/entry");
if (result.status && result.body) state.teamInfo = result.body.teamProfile || result.body;
} catch (error) {
state.teamInfo = null;
console.debug("BD leader team info unavailable:", error);
}
}
async function fetchIdentity() {
const result = await apiGet("/app/h5/identity");
if (result.status && result.body) state.identityInfo = result.body;
}
async function fetchBalanceTotal() {
const result = await apiGet("/wallet/salary-account/balance/total");
if (result.status && result.body) state.balanceTotal = result.body || {};
}
async function fetchBDHistoryMore(billBelong) {
const endpoint = historyMoreUrl("/team/bd/history/more", billBelong);
if (!endpoint) throw new Error(t("request_not_available"));
const result = await apiGet(endpoint);
if (result.status && result.body) state.historyMore = result.body || {};
}
async function fetchBDLeaderHistoryMore(billBelong) {
const endpoint = historyMoreUrl("/team/bd/leader/history/more", billBelong);
if (!endpoint) throw new Error(t("request_not_available"));
const result = await apiGet(endpoint);
if (result.status && result.body) state.historyMore = result.body || {};
}
function setIdentity(identity) {
state.identity = identity;
localStorage.setItem("identity", JSON.stringify({ identity }));
}
function clearPayee() {
try {
sessionStorage.removeItem("payee");
localStorage.removeItem("payee");
} catch (error) {
console.debug("Payee cache clear skipped:", error);
}
}
function formatMoney(value) {
if (value === null || value === undefined || value === "") return "0";
const number = Number(value);
if (Number.isNaN(number)) return String(value);
return Number.isInteger(number) ? String(number) : number.toFixed(2);
}
function normalizeStatus(status) {
if (status === "UNPAID" || status === "In Progress" || status === t("in_progress")) return t("in_progress");
if (status === "Pending" || status === t("pending")) return t("pending");
if (status === "SETTLED" || status === "PAY_OUT" || status === "Completed" || status === t("completed")) return t("completed");
if (status === "HANG_UP" || status === "Out of account" || status === "Out Of Account" || status === t("out_of_account")) return t("out_of_account");
return status || "";
}
function statusClass(status) {
const value = normalizeStatus(status);
if (value === t("completed")) return "is-completed";
if (value === t("pending")) return "is-pending";
if (value === t("out_of_account")) return "is-out";
return "is-progress";
}
function navigateTo(target) {
const url = new URL(target, window.location.origin);
const currentLang = new URLSearchParams(window.location.search).get("lang");
if (currentLang && !url.searchParams.has("lang")) url.searchParams.set("lang", currentLang);
window.location.href = `${url.pathname}${url.search}${url.hash}`;
}
function closePage() {
if (window.history.length > 1) {
window.history.back();
return;
}
if (window.app && typeof window.app.closePage === "function") {
window.app.closePage();
return;
}
if (window.FlutterPageControl && typeof window.FlutterPageControl.postMessage === "function") {
window.FlutterPageControl.postMessage("close_page");
}
}
function renderBalance() {
document.getElementById("availableBalance").textContent = `$${formatMoney(state.balanceTotal.availableBalance || 0)}`;
}
function historySource() {
return state.historyType === "bd" ? state.bdHistory : state.bdLeaderHistory;
}
function historyTitle() {
return state.historyType === "bd" ? t("bd_salary") : t("bd_leader_history");
}
function renderHistorySheet() {
const data = historySource();
document.getElementById("historyTitle").textContent = historyTitle();
document.getElementById("historyTotalIncome").textContent = formatMoney(data.totalIncome || 0);
document.getElementById("historyPreviousIncome").textContent = formatMoney(data.previousPeriodIncome || 0);
const list = document.getElementById("historyList");
list.innerHTML = "";
const items = data.incomeDetails || [];
items.forEach((item) => list.appendChild(createHistoryItem(item)));
}
function createHistoryItem(item) {
const card = document.createElement("article");
card.className = `salary-item ${statusClass(item.statusText)}`;
const status = document.createElement("div");
status.className = "status-background";
status.textContent = normalizeStatus(item.statusText);
card.appendChild(status);
const content = document.createElement("div");
content.className = "salary-item-content";
if (state.historyType === "bd") {
content.append(
textLine(item.billTitle || ""),
textLine(`${t("agency_number")}: ${item.agencyNumber || 0}`),
pairLine(`${t("team_salary")}: $${item.teamSalaryAmount || 0}`, `${t("bd_income_salary")}: $${item.bdIncomeSalary || item.settlementSalary || 0} (${item.bdRatioSalary || item.commissionRate || 0}%)`),
pairLine(`${t("team_top_up")}: $${item.teamRechargeAmount || 0}`, `${t("bd_income_top_up")}: $${item.bdIncomeRecharge || item.settlementSalary || 0} (${item.bdRatioRecharge || item.commissionRate || 0}%)`)
);
} else {
content.append(
textLine(item.billTitle || ""),
pairLine(`${t("bd_number")}: ${item.bdNumber || 0}`, `${t("team_salary")}: $${item.teamSalaryAmount || 0}`)
);
}
card.appendChild(content);
if (normalizeStatus(item.statusText) === t("completed")) {
const more = document.createElement("button");
more.type = "button";
more.className = "more-bt";
more.textContent = t("more");
more.addEventListener("click", () => showHistoryMore(item.billBelong));
card.appendChild(more);
}
return card;
}
function textLine(text) {
const node = document.createElement("div");
node.textContent = text;
return node;
}
function pairLine(left, right) {
const node = document.createElement("div");
node.className = "pair-line";
const leftNode = document.createElement("div");
const rightNode = document.createElement("div");
leftNode.textContent = left;
rightNode.textContent = right;
node.append(leftNode, rightNode);
return node;
}
async function showHistoryMore(billBelong) {
try {
if (state.historyType === "bd") {
await fetchBDHistoryMore(billBelong);
} else {
await fetchBDLeaderHistoryMore(billBelong);
}
state.modalMode = "details";
renderModal();
} catch (error) {
console.warn("BD leader history more failed:", error);
showToast(error.response?.errorMsg || error.message || t("failed_to_load_history"));
}
}
function renderDetailsSheet() {
const data = state.historyMore || {};
document.getElementById("detailsPeriod").textContent = data.period || "";
const summary = document.getElementById("detailsSummary");
summary.innerHTML = "";
if (state.historyType === "bd") {
summary.append(
textLine(`${t("team_top_up")}: $${data.teamTotalRecharge || 0}`),
textLine(`${t("total_team_salary")}: $${data.teamTotalSalary || 0}`)
);
} else {
summary.append(
textLine(`${t("team_salary")}: $${data.teamSalary || 0}`),
textLine(`${t("bd_number")}: ${data.bdNumber || 0}`)
);
}
const list = document.getElementById("memberDetails");
list.innerHTML = "";
(data.memberDetails || []).forEach((item) => {
const card = document.createElement("article");
card.className = "member-item";
const img = document.createElement("img");
img.src = state.historyType === "bd" ? item.userAvatar || "" : item.bdUserAvatar || "";
img.alt = "";
img.addEventListener("error", () => {
img.removeAttribute("src");
});
const info = document.createElement("div");
info.className = "member-info";
info.append(
textLine(state.historyType === "bd" ? item.userName || "" : item.bdUserName || ""),
textLine(`${t("user_id_prefix")}${item.account || ""}`)
);
const metrics = document.createElement("div");
metrics.className = "member-metrics";
metrics.append(textLine(`${t("salary")}: $${item.salary || 0}`));
if (state.historyType === "bd") {
metrics.append(textLine(`${t("recharge")}: $${item.recharge || 0}`), textLine(`${t("host")}: ${item.hostCount || 0}`));
} else {
metrics.append(textLine(`${t("agencies")}: ${item.agencyCount || 0}`));
}
card.append(img, info, metrics);
list.appendChild(card);
});
}
function renderModal() {
const mask = document.getElementById("maskLayer");
const history = document.getElementById("historySheet");
const details = document.getElementById("detailsSheet");
const showHistory = state.modalMode === "history";
const showDetails = state.modalMode === "details";
mask.hidden = !showHistory && !showDetails;
history.hidden = !showHistory;
details.hidden = !showDetails;
if (showHistory) renderHistorySheet();
if (showDetails) renderDetailsSheet();
}
function closeModal() {
state.modalMode = "";
state.historyMore = {};
renderModal();
}
function showToast(message) {
const toast = document.getElementById("toast");
toast.textContent = message;
toast.hidden = false;
window.clearTimeout(showToast.timer);
showToast.timer = window.setTimeout(() => {
toast.hidden = true;
}, 2500);
}
function bindEvents() {
document.querySelector("[data-action='back']").addEventListener("click", closePage);
document.querySelector("[data-action='language']").addEventListener("click", () => navigateTo("/language"));
document.querySelectorAll("[data-route]").forEach((node) => {
node.addEventListener("click", () => navigateTo(node.dataset.route));
});
document.querySelector("[data-action='sendGift']").addEventListener("click", () => navigateTo("/bd-item-distribution"));
document.querySelector("[data-action='closeModal']").addEventListener("click", closeModal);
}
async function initializePageData() {
const tasks = [fetchUserProfile(), fetchTeamInfo(), fetchIdentity()];
await Promise.allSettled(tasks);
}
async function init() {
setLanguage();
setIcons();
bindEvents();
renderBalance();
clearPayee();
setIdentity("BD_LEADER");
try {
await connectToApp();
await initializePageData();
await fetchBalanceTotal();
} catch (error) {
console.warn("BD leader center initialization failed:", error);
showToast(error.response?.errorMsg || error.message || t("failed_to_load_balance"));
} finally {
renderBalance();
}
}
init();