512 lines
15 KiB
JavaScript
512 lines
15 KiB
JavaScript
import { _ as helpIcon } from "../js/help-BgsLqEJs-1776148658686.js";
|
|
|
|
const API_BASE_URL = "https://jvapi.haiyihy.com";
|
|
|
|
const labels = {
|
|
en: {
|
|
apply_join_team: "Apply to join the team",
|
|
apply_reminder: "Reminder before application:",
|
|
apply_reminder_detail:
|
|
"Enter the ID of your agent, and you will become the host after the host apply is approved. After becoming an hoster,your income will be entrusted to your agent",
|
|
apply_id: "Apply ID:",
|
|
enter_agent_id: "Please enter the agent ID",
|
|
apply: "Apply",
|
|
application_records: "Application records",
|
|
pending: "Pending",
|
|
cancel: "Cancel",
|
|
application_help: "Application help",
|
|
application_help_detail: "Enter your agent ID and wait for approval.",
|
|
got_it: "Got it",
|
|
user_id_prefix: "ID:",
|
|
application_submitted: "Application submitted",
|
|
application_cancelled: "Application cancelled",
|
|
cancellation_failed: "Cancellation failed",
|
|
application_record_not_found: "Application record not found",
|
|
application_processed_cannot_cancel: "Application processed, cannot cancel",
|
|
network_error: "Network error",
|
|
cancellation_failed_try_again: "Cancellation failed, please try again"
|
|
},
|
|
ar: {},
|
|
tr: {},
|
|
bn: {}
|
|
};
|
|
|
|
class ApiError extends Error {
|
|
constructor(message, options = {}) {
|
|
super(message);
|
|
this.name = "ApiError";
|
|
this.type = options.type || "unknown";
|
|
this.status = options.status || null;
|
|
this.errorCode = options.errorCode || null;
|
|
this.errorCodeName = options.errorCodeName || null;
|
|
this.errorMsg = options.errorMsg || null;
|
|
this.response = options.response || null;
|
|
}
|
|
}
|
|
|
|
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(),
|
|
agencyId: "",
|
|
inlineError: "",
|
|
loading: false,
|
|
records: [],
|
|
selectedTeam: null,
|
|
helpOpen: false
|
|
};
|
|
|
|
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.querySelectorAll("[data-i18n]").forEach((node) => {
|
|
node.textContent = t(node.dataset.i18n);
|
|
});
|
|
document.querySelectorAll("[data-i18n-placeholder]").forEach((node) => {
|
|
node.setAttribute("placeholder", t(node.dataset.i18nPlaceholder));
|
|
});
|
|
}
|
|
|
|
function setIcons() {
|
|
document.getElementById("helpIcon").src = helpIcon;
|
|
}
|
|
|
|
function isMobileDevice() {
|
|
return /Android|iPad|iPhone|iPod/.test(navigator.userAgent);
|
|
}
|
|
|
|
function isAppEnvironment() {
|
|
return Boolean(window.app || window.webkit || window.FlutterPageControl);
|
|
}
|
|
|
|
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) {
|
|
try {
|
|
const data = JSON.parse(raw);
|
|
const appIntel = splitHeaderPairs(data["Req-App-Intel"]);
|
|
const sysOrigin = splitHeaderPairs(data["Req-Sys-Origin"]);
|
|
|
|
return {
|
|
success: true,
|
|
data: {
|
|
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
|
|
}
|
|
};
|
|
} catch (error) {
|
|
console.error("Failed to parse access origin:", error);
|
|
return { success: false, error: "accessOrigin Error.", data: null };
|
|
}
|
|
}
|
|
|
|
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 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()) {
|
|
poll();
|
|
return;
|
|
}
|
|
resolve(null);
|
|
});
|
|
}
|
|
|
|
async function connectToApp() {
|
|
const raw = await requestAccessOrigin();
|
|
if (!raw) return { success: true, environment: "browser" };
|
|
const parsed = parseAccessOrigin(raw);
|
|
if (!parsed.success) {
|
|
navigateTo(`/not_app?message=${encodeURIComponent(parsed.error)}`);
|
|
return { success: false, error: parsed.error };
|
|
}
|
|
setHeadersFromApp(parsed.data);
|
|
return { success: true, environment: "app" };
|
|
}
|
|
|
|
async function apiRequest(path, options = {}) {
|
|
const response = await fetch(`${API_BASE_URL}${path}`, {
|
|
method: options.method || "GET",
|
|
headers: {
|
|
...commonHeaders,
|
|
...(options.body ? { "Content-Type": "application/json" } : {})
|
|
},
|
|
body: options.body ? JSON.stringify(options.body) : undefined
|
|
});
|
|
|
|
let data = {};
|
|
try {
|
|
data = await response.json();
|
|
} catch {
|
|
data = {};
|
|
}
|
|
|
|
if (!response.ok) {
|
|
throw new ApiError(`HTTP Error: ${response.status} ${response.statusText}`, {
|
|
type: "http",
|
|
status: response.status,
|
|
response: data,
|
|
errorCode: data.errorCode,
|
|
errorCodeName: data.errorCodeName,
|
|
errorMsg: data.errorMsg
|
|
});
|
|
}
|
|
|
|
if (data.status === false) {
|
|
throw new ApiError(data.errorMsg || data.message || `API Error: ${data.errorCode}`, {
|
|
type: "business",
|
|
status: response.status,
|
|
response: data,
|
|
errorCode: data.errorCode,
|
|
errorCodeName: data.errorCodeName,
|
|
errorMsg: data.errorMsg
|
|
});
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
function apiGet(path) {
|
|
return apiRequest(path);
|
|
}
|
|
|
|
function apiPost(path, body) {
|
|
return apiRequest(path, { method: "POST", body });
|
|
}
|
|
|
|
async function fetchUserProfile() {
|
|
const result = await apiGet("/team/member/profile");
|
|
if (result.status && result.body) {
|
|
localStorage.setItem("userProfile", JSON.stringify(result.body));
|
|
}
|
|
}
|
|
|
|
async function fetchApplicationRecord() {
|
|
try {
|
|
const result = await apiGet("/team/wait-apply/profile");
|
|
state.records = result.status && result.body ? [result.body] : [];
|
|
} catch (error) {
|
|
console.error("获取申请记录失败:", error);
|
|
state.records = [];
|
|
showToast(error.errorMsg || error.message || "");
|
|
}
|
|
render();
|
|
}
|
|
|
|
async function cancelApplication(record) {
|
|
try {
|
|
const result = await apiPost("/team/user/join-apply-cancel", { id: record.messageId });
|
|
if (result.status) {
|
|
showToast(t("application_cancelled"));
|
|
state.records = [];
|
|
await fetchApplicationRecord();
|
|
} else {
|
|
showToast(result.message || t("cancellation_failed"));
|
|
}
|
|
} catch (error) {
|
|
console.error("撤销申请失败:", error);
|
|
if (error instanceof ApiError) {
|
|
if (error.type === "business") {
|
|
switch (error.errorCode) {
|
|
case 6404:
|
|
showToast(t("application_record_not_found"));
|
|
break;
|
|
case 6003:
|
|
showToast(t("application_processed_cannot_cancel"));
|
|
break;
|
|
default:
|
|
showToast(error.errorMsg || error.message || "Cancellation failed, please try again");
|
|
}
|
|
} else {
|
|
showToast(error.type === "http" ? t("network_error") : t("cancellation_failed"));
|
|
}
|
|
return;
|
|
}
|
|
showToast(t("cancellation_failed_try_again"));
|
|
}
|
|
}
|
|
|
|
async function searchTeamAccount(account) {
|
|
try {
|
|
console.debug("🔍 Searching team account:", account);
|
|
const result = await apiGet(`/team/search-account?account=${encodeURIComponent(account)}`);
|
|
if (result.status && result.body && result.body.teamProfile) {
|
|
state.selectedTeam = result.body;
|
|
console.debug("✅ Team found:", result.body.teamProfile);
|
|
return result.body.teamProfile.id;
|
|
}
|
|
state.selectedTeam = null;
|
|
showToast(result.errorMsg || "");
|
|
} catch (error) {
|
|
state.selectedTeam = null;
|
|
showToast(error.errorMsg || error.message || "");
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async function submitApplication() {
|
|
if (!state.agencyId.trim()) {
|
|
state.inlineError = "Please enter agency ID";
|
|
return;
|
|
}
|
|
|
|
state.loading = true;
|
|
state.inlineError = "";
|
|
render();
|
|
|
|
try {
|
|
const teamId = await searchTeamAccount(state.agencyId.trim());
|
|
if (!teamId) {
|
|
state.inlineError = "Account not found or not an agency";
|
|
return;
|
|
}
|
|
|
|
console.debug("✅ Team ID found:", teamId);
|
|
console.debug("📝 Step 2: Submitting application...");
|
|
const result = await apiPost("/team/user/apply", { teamId, reason: "JOIN" });
|
|
if (result.status) {
|
|
showToast(t("application_submitted"));
|
|
await fetchApplicationRecord();
|
|
state.agencyId = "";
|
|
state.selectedTeam = null;
|
|
} else {
|
|
state.inlineError = result.message || "Application failed. Please try again.";
|
|
}
|
|
} catch (error) {
|
|
if (error instanceof ApiError) {
|
|
if (error.type === "business") {
|
|
showToast(`business error!${error.errorMsg || ""}`);
|
|
} else if (error.type === "http") {
|
|
showToast(error.status === 406 ? error.errorMsg : error.message || "Unknown error, please try again");
|
|
} else {
|
|
showToast("Unknown error, please try again");
|
|
}
|
|
}
|
|
} finally {
|
|
state.loading = false;
|
|
render();
|
|
}
|
|
}
|
|
|
|
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 avatarText(profile) {
|
|
const name = profile?.userNickname || profile?.account || "U";
|
|
return name.trim().slice(0, 1).toUpperCase() || "U";
|
|
}
|
|
|
|
function userAccount(profile) {
|
|
return profile?.ownSpecialId?.account || profile?.account || "";
|
|
}
|
|
|
|
function renderRecords() {
|
|
const recordsSection = document.getElementById("recordsSection");
|
|
const recordList = document.getElementById("recordList");
|
|
recordList.innerHTML = "";
|
|
recordsSection.hidden = state.records.length === 0;
|
|
document.querySelector("[data-action='apply']").hidden = state.records.length > 0;
|
|
|
|
state.records.forEach((record) => {
|
|
const profile = record.ownUserProfile || {};
|
|
const card = document.createElement("article");
|
|
card.className = "surface-card record-card";
|
|
card.innerHTML = `
|
|
<div class="record-status"></div>
|
|
<div class="record-user">
|
|
<div class="record-avatar"><img alt=""><span></span></div>
|
|
<div class="record-info">
|
|
<div class="record-name"></div>
|
|
<div class="record-id"></div>
|
|
</div>
|
|
</div>
|
|
<button class="record-cancel" type="button"></button>
|
|
`;
|
|
|
|
card.querySelector(".record-status").textContent = t("pending");
|
|
const img = card.querySelector(".record-avatar img");
|
|
const fallback = card.querySelector(".record-avatar span");
|
|
img.src = profile.userAvatar || "";
|
|
fallback.textContent = avatarText(profile);
|
|
img.addEventListener("error", () => {
|
|
img.hidden = true;
|
|
fallback.hidden = false;
|
|
});
|
|
if (!profile.userAvatar) {
|
|
img.hidden = true;
|
|
fallback.hidden = false;
|
|
} else {
|
|
fallback.hidden = true;
|
|
}
|
|
card.querySelector(".record-name").textContent = profile.userNickname || "";
|
|
card.querySelector(".record-id").textContent = `${t("user_id_prefix")} ${userAccount(profile)}`;
|
|
const cancelButton = card.querySelector(".record-cancel");
|
|
cancelButton.textContent = t("cancel");
|
|
cancelButton.addEventListener("click", () => cancelApplication(record));
|
|
recordList.appendChild(card);
|
|
});
|
|
}
|
|
|
|
function render() {
|
|
const input = document.getElementById("agencyInput");
|
|
if (document.activeElement !== input) input.value = state.agencyId;
|
|
document.querySelector("[data-action='apply']").textContent = state.loading ? "..." : t("apply");
|
|
document.getElementById("helpModal").hidden = !state.helpOpen;
|
|
renderRecords();
|
|
}
|
|
|
|
function showToast(message) {
|
|
if (!message) return;
|
|
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() {
|
|
const input = document.getElementById("agencyInput");
|
|
input.addEventListener("input", () => {
|
|
state.agencyId = input.value;
|
|
render();
|
|
});
|
|
|
|
document.querySelector("[data-action='apply']").addEventListener("click", submitApplication);
|
|
document.querySelector("[data-action='help']").addEventListener("click", () => {
|
|
state.helpOpen = true;
|
|
render();
|
|
});
|
|
document.querySelector("[data-action='close-help']").addEventListener("click", () => {
|
|
state.helpOpen = false;
|
|
render();
|
|
});
|
|
document.getElementById("helpModal").addEventListener("click", (event) => {
|
|
if (event.target.id === "helpModal") {
|
|
state.helpOpen = false;
|
|
render();
|
|
}
|
|
});
|
|
}
|
|
|
|
async function initializePageData() {
|
|
try {
|
|
await fetchUserProfile();
|
|
} catch (error) {
|
|
console.error("❌ Data initialization failed:", error);
|
|
}
|
|
await fetchApplicationRecord();
|
|
}
|
|
|
|
async function init() {
|
|
setLanguage();
|
|
setIcons();
|
|
bindEvents();
|
|
render();
|
|
|
|
try {
|
|
const connection = await connectToApp();
|
|
if (connection.success) await initializePageData();
|
|
} catch (error) {
|
|
console.error("❌ Page initialization failed:", error);
|
|
}
|
|
}
|
|
|
|
init();
|