diff --git a/h5/app-invite/app.js b/h5/app-invite/app.js new file mode 100644 index 0000000..02397f2 --- /dev/null +++ b/h5/app-invite/app.js @@ -0,0 +1,794 @@ +const query = new URLSearchParams(window.location.search); + +const DEFAULT_INVITE_API_BASE = "https://jvapi.haiyihy.com/"; +const DEFAULT_AVATAR = "./assets/avatar-sample.png"; +const DEFAULT_TASK_NOTES = { + transport: "(Only the highest-level bonuses may be awarded)", + quota: "(You'll receive a reward upon reaching each level)", +}; + +const DEFAULTS = { + view: "invite", + inviteLink: "http://yourappbalbalanal", + inviteCode: "123455678", + invitees: "168", + eligibleVoters: "01", + rechargeTotal: "01", + myRewards: "12354", + friendName: "Name", + downloadUrl: "https://example.com/download", + endAt: "", + inviterBindReward: 2000, + rechargeReward: 1000, + giftReward: 1000, +}; + +const FALLBACK_TASKS = { + transport: [ + { + title: "Milestone 1", + desc: "Total invitee recharge reaches 49,999 coins", + buttonLabel: "Incomplete", + disabled: true, + claimed: false, + achieved: false, + ruleId: 0, + }, + { + title: "Milestone 2", + desc: "Total invitee recharge reaches 99,999 coins", + buttonLabel: "Incomplete", + disabled: true, + claimed: false, + achieved: false, + ruleId: 0, + }, + { + title: "Milestone 3", + desc: "Total invitee recharge reaches 149,999 coins", + buttonLabel: "Incomplete", + disabled: true, + claimed: false, + achieved: false, + ruleId: 0, + }, + ], + quota: [ + { + title: "Milestone 1", + desc: "Invite 10 friends who have already made a payment", + buttonLabel: "Claim", + disabled: false, + claimed: false, + achieved: true, + ruleId: 0, + }, + { + title: "Milestone 2", + desc: "Invite 20 friends who have already made a payment", + buttonLabel: "Incomplete", + disabled: true, + claimed: false, + achieved: false, + ruleId: 0, + }, + { + title: "Milestone 3", + desc: "Invite 30 friends who have already made a payment", + buttonLabel: "Claim", + disabled: false, + claimed: false, + achieved: true, + ruleId: 0, + }, + ], +}; + +const DEFAULT_RANKS = Array.from({ length: 30 }, (_, index) => ({ + rank: index + 1, + name: + [ + "hanneem", + "layan", + "amira", + "sami", + "noura", + "huda", + "rami", + "selim", + "mira", + "yael", + ][index % 10], + score: `${123 - Math.floor(index / 3)} people`, +})); + +const state = { + view: normalizeView(query.get("view") || DEFAULTS.view), + apiBase: resolveAPIBase(), + authorization: resolveAuthorization(), + landingCode: trimValue(query.get("code") || query.get("inviteCode")), + inviteLink: query.get("inviteLink") || DEFAULTS.inviteLink, + inviteCode: query.get("inviteCode") || DEFAULTS.inviteCode, + invitees: query.get("invitees") || DEFAULTS.invitees, + eligibleVoters: query.get("eligible") || DEFAULTS.eligibleVoters, + rechargeTotal: query.get("recharge") || DEFAULTS.rechargeTotal, + myRewards: query.get("rewards") || DEFAULTS.myRewards, + friendName: query.get("friendName") || DEFAULTS.friendName, + inviterAvatar: DEFAULT_AVATAR, + downloadUrl: query.get("downloadUrl") || DEFAULTS.downloadUrl, + inviterBindReward: query.get("inviterReward") || DEFAULTS.inviterBindReward, + rechargeReward: query.get("rechargeReward") || DEFAULTS.rechargeReward, + giftReward: query.get("giftReward") || DEFAULTS.giftReward, + taskSet: normalizeTaskSet(query.get("taskSet") || "quota"), + tasks: { + transport: cloneTasks(FALLBACK_TASKS.transport), + quota: cloneTasks(FALLBACK_TASKS.quota), + }, + countdownTimer: null, + pendingClaimRuleId: 0, +}; + +const els = { + views: [...document.querySelectorAll("[data-view]")], + inviteLinkText: document.getElementById("inviteLinkText"), + inviteCodeText: document.getElementById("inviteCodeText"), + statInvitees: document.getElementById("statInvitees"), + statEligible: document.getElementById("statEligible"), + statRecharge: document.getElementById("statRecharge"), + statRewards: document.getElementById("statRewards"), + taskList: document.getElementById("taskList"), + taskNote: document.getElementById("taskNote"), + taskTabs: [...document.querySelectorAll("[data-task-set]")], + navButtons: [...document.querySelectorAll("[data-nav-view]")], + copyButtons: [...document.querySelectorAll("[data-copy-target]")], + rankList: document.getElementById("rankList"), + giftTitle: document.getElementById("giftTitle"), + giftAvatarImage: document.getElementById("giftAvatarImage"), + downloadButton: document.getElementById("downloadButton"), + inviteRewardAmounts: [ + ...document.querySelectorAll('.reward-panel .reward-amount'), + ], + giftRewardAmount: document.querySelector('.gift-panel .reward-amount'), + toast: document.getElementById("pageToast"), + countdown: { + days: document.querySelector('[data-countdown="days"]'), + hours: document.querySelector('[data-countdown="hours"]'), + minutes: document.querySelector('[data-countdown="minutes"]'), + seconds: document.querySelector('[data-countdown="seconds"]'), + }, +}; + +function normalizeView(value) { + if (value === "rank" || value === "gift") { + return value; + } + + return "invite"; +} + +function normalizeTaskSet(value) { + return value === "transport" ? "transport" : "quota"; +} + +function trimValue(value) { + return value == null ? "" : String(value).trim(); +} + +function normalizeBaseURL(base) { + const value = trimValue(base); + if (!value) { + return DEFAULT_INVITE_API_BASE; + } + + return value.endsWith("/") ? value : `${value}/`; +} + +function resolveAPIBase() { + const queryBase = trimValue(query.get("apiBase")); + if (queryBase) { + return normalizeBaseURL(queryBase); + } + + if (typeof window.__APP_INVITE_API_BASE__ === "string") { + return normalizeBaseURL(window.__APP_INVITE_API_BASE__); + } + + return normalizeBaseURL(DEFAULT_INVITE_API_BASE); +} + +function resolveAuthorization() { + const candidates = [ + query.get("authorization"), + query.get("Authorization"), + query.get("token"), + query.get("auth"), + typeof window.__APP_INVITE_AUTHORIZATION__ === "string" + ? window.__APP_INVITE_AUTHORIZATION__ + : "", + typeof window.__APP_INVITE_TOKEN__ === "string" ? window.__APP_INVITE_TOKEN__ : "", + ]; + + const value = candidates.find((item) => trimValue(item)); + if (!value) { + return ""; + } + + const normalized = trimValue(value); + if (/^bearer\s+/i.test(normalized)) { + return normalized; + } + + return `Bearer ${normalized}`; +} + +function escapeHtml(value) { + return String(value).replace(/[&<>"']/g, (match) => { + const entityMap = { + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'", + }; + return entityMap[match] || match; + }); +} + +function cloneTasks(list) { + return list.map((item) => ({ ...item })); +} + +function showToast(text) { + if (!text) { + return; + } + + els.toast.textContent = text; + els.toast.classList.add("show"); + + window.clearTimeout(showToast.timer); + showToast.timer = window.setTimeout(() => { + els.toast.classList.remove("show"); + }, 1800); +} + +function loadImage(image) { + if (!image || image.getAttribute("src")) { + return; + } + + const src = image.getAttribute("data-src"); + if (src) { + image.setAttribute("src", src); + } +} + +function setImageSource(image, src, fallbackSrc = "") { + if (!(image instanceof HTMLImageElement)) { + return; + } + + const nextSrc = trimValue(src) || fallbackSrc; + if (!nextSrc) { + return; + } + + image.setAttribute("src", nextSrc); + image.setAttribute("data-src", nextSrc); +} + +function hydrateViewAssets(view) { + const targets = { + invite: [ + document.getElementById("inviteHeroImage"), + ...document.querySelectorAll('.view[data-view="invite"] .lazy-image'), + ], + rank: [ + ...document.querySelectorAll('.view[data-view="rank"] [data-badge-src]'), + ...document.querySelectorAll('.view[data-view="rank"] [data-avatar-src]'), + ], + gift: [ + document.getElementById("giftBgImage"), + document.getElementById("giftAvatarImage"), + document.getElementById("phoneHomeImage"), + document.getElementById("phoneGiftImage"), + ...document.querySelectorAll('.view[data-view="gift"] .lazy-image'), + ], + }; + + (targets[view] || []).forEach((item) => { + if (item instanceof HTMLImageElement) { + loadImage(item); + } + }); +} + +function setView(view) { + state.view = normalizeView(view); + els.views.forEach((section) => { + section.classList.toggle("active", section.getAttribute("data-view") === state.view); + }); + + hydrateViewAssets(state.view); + + const url = new URL(window.location.href); + if (state.view === DEFAULTS.view) { + url.searchParams.delete("view"); + } else { + url.searchParams.set("view", state.view); + } + window.history.replaceState({}, "", url.toString()); +} + +function formatMetric(value) { + const raw = trimValue(value); + if (!raw) { + return "--"; + } + + if (/^-?\d+(\.\d+)?$/.test(raw)) { + return new Intl.NumberFormat("en-US").format(Number(raw)); + } + + return raw; +} + +function formatSignedReward(value) { + const raw = trimValue(value); + if (!raw) { + return "+0"; + } + + if (raw.startsWith("+")) { + return raw; + } + + return `+${formatMetric(raw)}`; +} + +function renderStats() { + els.inviteLinkText.textContent = trimValue(state.inviteLink) || "--"; + els.inviteCodeText.textContent = trimValue(state.inviteCode) || "--"; + els.statInvitees.textContent = formatMetric(state.invitees); + els.statEligible.textContent = formatMetric(state.eligibleVoters); + els.statRecharge.textContent = formatMetric(state.rechargeTotal); + els.statRewards.textContent = formatMetric(state.myRewards); + els.giftTitle.innerHTML = `Your friend [${escapeHtml(state.friendName)}]
has invited you to join`; + setImageSource(els.giftAvatarImage, state.inviterAvatar, DEFAULT_AVATAR); + + if (els.inviteRewardAmounts[0]) { + els.inviteRewardAmounts[0].textContent = formatSignedReward(state.inviterBindReward); + } + if (els.inviteRewardAmounts[1]) { + els.inviteRewardAmounts[1].textContent = formatSignedReward(state.rechargeReward); + } + if (els.giftRewardAmount) { + els.giftRewardAmount.textContent = formatSignedReward(state.giftReward); + } +} + +function renderTasks() { + const current = state.tasks[state.taskSet] || cloneTasks(FALLBACK_TASKS[state.taskSet]); + els.taskTabs.forEach((tab) => { + const active = tab.getAttribute("data-task-set") === state.taskSet; + tab.classList.toggle("active", active); + tab.classList.toggle("passive", !active); + }); + + els.taskList.innerHTML = current + .map((item) => { + const isLoading = Number(item.ruleId) > 0 && state.pendingClaimRuleId === Number(item.ruleId); + const disabled = item.disabled || isLoading || Number(item.ruleId) <= 0; + const buttonLabel = isLoading ? "Loading..." : item.buttonLabel; + let buttonStateClass = " is-pending"; + + if (isLoading) { + buttonStateClass = " is-loading"; + } else if (item.claimed) { + buttonStateClass = " is-claimed"; + } else if (item.achieved && !item.claimed) { + buttonStateClass = " is-claimable"; + } + + return ` +
+
+

${escapeHtml(item.title)}

+

${escapeHtml(item.desc)}

+
+ +
+ `; + }) + .join(""); + els.taskNote.textContent = DEFAULT_TASK_NOTES[state.taskSet] || ""; +} + +function renderRankList() { + els.rankList.innerHTML = DEFAULT_RANKS.map((item) => { + const badgeMap = { + 1: "./assets/rank-top1.png", + 2: "./assets/rank-top2.png", + 3: "./assets/rank-top3.png", + }; + + const badge = badgeMap[item.rank]; + return ` +
+
+ ${ + badge + ? `Rank ${item.rank}` + : escapeHtml(String(item.rank)) + } +
+
+ ${escapeHtml(item.name)} +
+
${escapeHtml(item.name)}
+
${escapeHtml(item.score)}
+
+ `; + }).join(""); +} + +function parseEndAt() { + const raw = query.get("endAt") || DEFAULTS.endAt; + if (!raw) { + return null; + } + + const timestamp = Date.parse(raw); + if (Number.isNaN(timestamp)) { + return null; + } + + return timestamp; +} + +function pad(number) { + return String(number).padStart(2, "0"); +} + +function updateCountdown() { + const endAt = parseEndAt(); + if (!endAt) { + return; + } + + const diff = Math.max(0, endAt - Date.now()); + const seconds = Math.floor(diff / 1000); + const days = Math.floor(seconds / 86400); + const hours = Math.floor((seconds % 86400) / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + const secs = seconds % 60; + + els.countdown.days.textContent = pad(days); + els.countdown.hours.textContent = pad(hours); + els.countdown.minutes.textContent = pad(minutes); + els.countdown.seconds.textContent = pad(secs); +} + +function setupCountdown() { + const endAt = parseEndAt(); + if (!endAt) { + return; + } + + updateCountdown(); + state.countdownTimer = window.setInterval(updateCountdown, 1000); +} + +async function copyText(text) { + if (navigator.clipboard?.writeText) { + await navigator.clipboard.writeText(text); + return; + } + + const input = document.createElement("textarea"); + input.value = text; + input.setAttribute("readonly", ""); + input.style.position = "absolute"; + input.style.left = "-9999px"; + document.body.appendChild(input); + input.select(); + document.execCommand("copy"); + document.body.removeChild(input); +} + +function extractErrorMessage(payload) { + const candidates = [ + payload?.errorMsg, + payload?.message, + payload?.errorCodeName, + payload?.code, + ]; + + return candidates.find((item) => trimValue(item)) || ""; +} + +async function requestJSON(path, options = {}) { + const { method = "GET", headers = {}, body, searchParams } = options; + const url = new URL(path, state.apiBase); + + Object.entries(searchParams || {}).forEach(([key, value]) => { + const raw = trimValue(value); + if (raw) { + url.searchParams.set(key, raw); + } + }); + + const response = await fetch(url.toString(), { + method, + headers: { + Accept: "application/json", + ...headers, + }, + body, + }); + + const contentType = response.headers.get("content-type") || ""; + const payload = contentType.includes("application/json") ? await response.json() : null; + + if (!response.ok || payload?.status === false) { + const message = extractErrorMessage(payload) || `Request failed (${response.status})`; + throw new Error(message); + } + + return payload?.body || payload?.data || payload || null; +} + +function normalizeTaskList(list, taskSet) { + if (!Array.isArray(list) || list.length === 0) { + return cloneTasks(FALLBACK_TASKS[taskSet]); + } + + return list.map((item, index) => { + const threshold = Number(item?.threshold || 0); + const goldAmount = Number(item?.goldAmount || 0); + const achieved = Boolean(item?.achieved); + const claimed = Boolean(item?.claimed); + const desc = + taskSet === "quota" + ? `Invite ${formatMetric(threshold)} valid friends to claim ${formatMetric(goldAmount)} coins` + : `Reach ${formatMetric(threshold)} recharge coins to claim ${formatMetric(goldAmount)} coins`; + + return { + title: `Milestone ${index + 1}`, + desc, + buttonLabel: claimed ? "Claimed" : achieved ? "Claim" : "Incomplete", + disabled: claimed || !achieved, + claimed, + achieved, + ruleId: Number(item?.ruleId) || 0, + }; + }); +} + +function markTaskClaimed(ruleId) { + ["quota", "transport"].forEach((taskSet) => { + state.tasks[taskSet] = (state.tasks[taskSet] || []).map((item) => { + if (Number(item.ruleId) !== Number(ruleId)) { + return item; + } + + return { + ...item, + claimed: true, + disabled: true, + buttonLabel: "Claimed", + }; + }); + }); +} + +function applyHomeData(body) { + if (!body || typeof body !== "object") { + return; + } + + const stats = body.stats || {}; + state.inviteLink = trimValue(body.inviteLink) || state.inviteLink; + state.inviteCode = trimValue(body.inviteCode) || state.inviteCode; + state.downloadUrl = trimValue(body.downloadUrl) || state.downloadUrl; + state.invitees = stats.totalInvitedUsers ?? state.invitees; + state.eligibleVoters = stats.totalValidUsers ?? state.eligibleVoters; + state.rechargeTotal = stats.totalRechargeCoins ?? state.rechargeTotal; + state.myRewards = stats.rewardGoldCoins ?? state.myRewards; + + const inviterBindReward = body.inviterBindReward?.goldAmount; + if (inviterBindReward != null) { + state.inviterBindReward = inviterBindReward; + } + + const rechargeReward = + body.inviterTotalRechargeTasks?.[0]?.goldAmount ?? + body.inviteeRechargeRules?.[0]?.goldAmount; + if (rechargeReward != null) { + state.rechargeReward = rechargeReward; + } + + state.tasks.quota = normalizeTaskList(body.inviterValidCountTasks, "quota"); + state.tasks.transport = normalizeTaskList(body.inviterTotalRechargeTasks, "transport"); + + const shareTitle = trimValue(body.shareTitle); + if (shareTitle) { + document.title = shareTitle; + } +} + +function applyLandingData(body) { + if (!body || typeof body !== "object") { + return; + } + + state.friendName = trimValue(body.inviterNickname) || state.friendName; + state.inviterAvatar = trimValue(body.inviterAvatar) || DEFAULT_AVATAR; + state.downloadUrl = trimValue(body.downloadUrl) || state.downloadUrl; + state.giftReward = body.inviteeReward?.goldAmount ?? state.giftReward; + + const inviteCode = trimValue(body.inviteCode); + if (inviteCode) { + state.landingCode = inviteCode; + } + + const shareTitle = trimValue(body.shareTitle); + if (shareTitle) { + document.title = shareTitle; + } +} + +async function fetchHomeData() { + if (!state.authorization) { + return false; + } + + const body = await requestJSON("/go/app/h5/invite-campaign/home", { + headers: { + Authorization: state.authorization, + }, + }); + applyHomeData(body); + return true; +} + +async function fetchLandingData() { + if (!state.landingCode) { + return false; + } + + const body = await requestJSON("/go/public/h5/invite-campaign/landing", { + searchParams: { + code: state.landingCode, + }, + }); + applyLandingData(body); + return true; +} + +async function refreshPageData() { + try { + if (state.view === "gift") { + await fetchLandingData(); + } else { + await fetchHomeData(); + } + } catch (error) { + const message = trimValue(error?.message); + if (message) { + showToast(message); + } + } finally { + renderStats(); + renderTasks(); + } +} + +async function claimTask(ruleId) { + if (!ruleId) { + return; + } + + if (!state.authorization) { + showToast("Token is missing"); + return; + } + + state.pendingClaimRuleId = ruleId; + renderTasks(); + + try { + const body = await requestJSON("/go/app/h5/invite-campaign/tasks/claim", { + method: "POST", + headers: { + Authorization: state.authorization, + "Content-Type": "application/json", + }, + body: JSON.stringify({ ruleId }), + }); + + markTaskClaimed(ruleId); + showToast(body?.alreadyClaimed ? "Already claimed" : "Claim succeeded"); + await fetchHomeData().catch(() => {}); + } catch (error) { + showToast(trimValue(error?.message) || "Claim failed"); + } finally { + state.pendingClaimRuleId = 0; + renderStats(); + renderTasks(); + } +} + +function bindEvents() { + els.taskTabs.forEach((button) => { + button.addEventListener("click", () => { + state.taskSet = normalizeTaskSet(button.getAttribute("data-task-set")); + renderTasks(); + }); + }); + + els.navButtons.forEach((button) => { + button.addEventListener("click", () => { + setView(button.getAttribute("data-nav-view") || "invite"); + }); + }); + + els.copyButtons.forEach((button) => { + button.addEventListener("click", async () => { + const target = button.getAttribute("data-copy-target"); + const value = target === "invite-code" ? state.inviteCode : state.inviteLink; + + try { + await copyText(trimValue(value)); + showToast(target === "invite-code" ? "Invitation code copied" : "Invitation link copied"); + } catch (error) { + showToast("Copy failed"); + } + }); + }); + + els.taskList.addEventListener("click", (event) => { + const button = event.target.closest("[data-rule-id]"); + if (!(button instanceof HTMLButtonElement)) { + return; + } + + const ruleId = Number(button.getAttribute("data-rule-id") || 0); + if (!ruleId || button.disabled) { + return; + } + + claimTask(ruleId); + }); + + els.downloadButton.addEventListener("click", () => { + if (!state.downloadUrl) { + showToast("Download link is not configured"); + return; + } + + window.location.href = state.downloadUrl; + }); +} + +async function init() { + renderStats(); + renderTasks(); + renderRankList(); + bindEvents(); + setView(state.view); + setupCountdown(); + await refreshPageData(); +} + +init(); diff --git a/h5/app-invite/assets/avatar-sample.png b/h5/app-invite/assets/avatar-sample.png new file mode 100644 index 0000000..6d0410b Binary files /dev/null and b/h5/app-invite/assets/avatar-sample.png differ diff --git a/h5/app-invite/assets/button-active-bg.png b/h5/app-invite/assets/button-active-bg.png new file mode 100644 index 0000000..07ac42a Binary files /dev/null and b/h5/app-invite/assets/button-active-bg.png differ diff --git a/h5/app-invite/assets/button-inactive-bg.png b/h5/app-invite/assets/button-inactive-bg.png new file mode 100644 index 0000000..b5bb466 Binary files /dev/null and b/h5/app-invite/assets/button-inactive-bg.png differ diff --git a/h5/app-invite/assets/claim-active-art.png b/h5/app-invite/assets/claim-active-art.png new file mode 100644 index 0000000..e90fa13 Binary files /dev/null and b/h5/app-invite/assets/claim-active-art.png differ diff --git a/h5/app-invite/assets/claim-active-core.png b/h5/app-invite/assets/claim-active-core.png new file mode 100644 index 0000000..409dd50 Binary files /dev/null and b/h5/app-invite/assets/claim-active-core.png differ diff --git a/h5/app-invite/assets/claim-active-mask.svg b/h5/app-invite/assets/claim-active-mask.svg new file mode 100644 index 0000000..9250ace --- /dev/null +++ b/h5/app-invite/assets/claim-active-mask.svg @@ -0,0 +1,3 @@ + + + diff --git a/h5/app-invite/assets/claim-active.png b/h5/app-invite/assets/claim-active.png new file mode 100644 index 0000000..d34e8bb Binary files /dev/null and b/h5/app-invite/assets/claim-active.png differ diff --git a/h5/app-invite/assets/claim-inactive.png b/h5/app-invite/assets/claim-inactive.png new file mode 100644 index 0000000..d34e8bb Binary files /dev/null and b/h5/app-invite/assets/claim-inactive.png differ diff --git a/h5/app-invite/assets/copy-button.png b/h5/app-invite/assets/copy-button.png new file mode 100644 index 0000000..55bb4db Binary files /dev/null and b/h5/app-invite/assets/copy-button.png differ diff --git a/h5/app-invite/assets/copy-core.png b/h5/app-invite/assets/copy-core.png new file mode 100644 index 0000000..409dd50 Binary files /dev/null and b/h5/app-invite/assets/copy-core.png differ diff --git a/h5/app-invite/assets/copy-mask.svg b/h5/app-invite/assets/copy-mask.svg new file mode 100644 index 0000000..a94f823 --- /dev/null +++ b/h5/app-invite/assets/copy-mask.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/h5/app-invite/assets/gift-bg.png b/h5/app-invite/assets/gift-bg.png new file mode 100644 index 0000000..55386de Binary files /dev/null and b/h5/app-invite/assets/gift-bg.png differ diff --git a/h5/app-invite/assets/gift-cta-core.png b/h5/app-invite/assets/gift-cta-core.png new file mode 100644 index 0000000..409dd50 Binary files /dev/null and b/h5/app-invite/assets/gift-cta-core.png differ diff --git a/h5/app-invite/assets/gift-cta-mask.svg b/h5/app-invite/assets/gift-cta-mask.svg new file mode 100644 index 0000000..c1da41a --- /dev/null +++ b/h5/app-invite/assets/gift-cta-mask.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/h5/app-invite/assets/gift-cta.png b/h5/app-invite/assets/gift-cta.png new file mode 100644 index 0000000..0adfc8c Binary files /dev/null and b/h5/app-invite/assets/gift-cta.png differ diff --git a/h5/app-invite/assets/hero-title-combined.png b/h5/app-invite/assets/hero-title-combined.png new file mode 100644 index 0000000..e7c6474 Binary files /dev/null and b/h5/app-invite/assets/hero-title-combined.png differ diff --git a/h5/app-invite/assets/hero-title-full.png b/h5/app-invite/assets/hero-title-full.png new file mode 100644 index 0000000..fcce241 Binary files /dev/null and b/h5/app-invite/assets/hero-title-full.png differ diff --git a/h5/app-invite/assets/hero-title-top-shift4.png b/h5/app-invite/assets/hero-title-top-shift4.png new file mode 100644 index 0000000..5e37547 Binary files /dev/null and b/h5/app-invite/assets/hero-title-top-shift4.png differ diff --git a/h5/app-invite/assets/hero-title-top.png b/h5/app-invite/assets/hero-title-top.png new file mode 100644 index 0000000..fccbdaf Binary files /dev/null and b/h5/app-invite/assets/hero-title-top.png differ diff --git a/h5/app-invite/assets/invite-hero-bg.png b/h5/app-invite/assets/invite-hero-bg.png new file mode 100644 index 0000000..55386de Binary files /dev/null and b/h5/app-invite/assets/invite-hero-bg.png differ diff --git a/h5/app-invite/assets/monthly-tasks-button.png b/h5/app-invite/assets/monthly-tasks-button.png new file mode 100644 index 0000000..5dca0c8 Binary files /dev/null and b/h5/app-invite/assets/monthly-tasks-button.png differ diff --git a/h5/app-invite/assets/panel-bottom.png b/h5/app-invite/assets/panel-bottom.png new file mode 100644 index 0000000..1ed2000 Binary files /dev/null and b/h5/app-invite/assets/panel-bottom.png differ diff --git a/h5/app-invite/assets/panel-middle.png b/h5/app-invite/assets/panel-middle.png new file mode 100644 index 0000000..c3f9e50 Binary files /dev/null and b/h5/app-invite/assets/panel-middle.png differ diff --git a/h5/app-invite/assets/panel-top.png b/h5/app-invite/assets/panel-top.png new file mode 100644 index 0000000..3444635 Binary files /dev/null and b/h5/app-invite/assets/panel-top.png differ diff --git a/h5/app-invite/assets/phone-gift.png b/h5/app-invite/assets/phone-gift.png new file mode 100644 index 0000000..7e621a9 Binary files /dev/null and b/h5/app-invite/assets/phone-gift.png differ diff --git a/h5/app-invite/assets/phone-home.png b/h5/app-invite/assets/phone-home.png new file mode 100644 index 0000000..bbb7523 Binary files /dev/null and b/h5/app-invite/assets/phone-home.png differ diff --git a/h5/app-invite/assets/rank-button.png b/h5/app-invite/assets/rank-button.png new file mode 100644 index 0000000..6a19075 Binary files /dev/null and b/h5/app-invite/assets/rank-button.png differ diff --git a/h5/app-invite/assets/rank-row.svg b/h5/app-invite/assets/rank-row.svg new file mode 100644 index 0000000..c77d5be --- /dev/null +++ b/h5/app-invite/assets/rank-row.svg @@ -0,0 +1,3 @@ + + + diff --git a/h5/app-invite/assets/rank-title-button.png b/h5/app-invite/assets/rank-title-button.png new file mode 100644 index 0000000..14ea6bd Binary files /dev/null and b/h5/app-invite/assets/rank-title-button.png differ diff --git a/h5/app-invite/assets/rank-top1.png b/h5/app-invite/assets/rank-top1.png new file mode 100644 index 0000000..df7b345 Binary files /dev/null and b/h5/app-invite/assets/rank-top1.png differ diff --git a/h5/app-invite/assets/rank-top2.png b/h5/app-invite/assets/rank-top2.png new file mode 100644 index 0000000..67ad74d Binary files /dev/null and b/h5/app-invite/assets/rank-top2.png differ diff --git a/h5/app-invite/assets/rank-top3.png b/h5/app-invite/assets/rank-top3.png new file mode 100644 index 0000000..5ee50f6 Binary files /dev/null and b/h5/app-invite/assets/rank-top3.png differ diff --git a/h5/app-invite/assets/reward-bag.png b/h5/app-invite/assets/reward-bag.png new file mode 100644 index 0000000..44e5294 Binary files /dev/null and b/h5/app-invite/assets/reward-bag.png differ diff --git a/h5/app-invite/assets/tab-active-core.png b/h5/app-invite/assets/tab-active-core.png new file mode 100644 index 0000000..409dd50 Binary files /dev/null and b/h5/app-invite/assets/tab-active-core.png differ diff --git a/h5/app-invite/assets/tab-active-mask.png b/h5/app-invite/assets/tab-active-mask.png new file mode 100644 index 0000000..dcf6876 --- /dev/null +++ b/h5/app-invite/assets/tab-active-mask.png @@ -0,0 +1,3 @@ + + + diff --git a/h5/app-invite/assets/tab-inactive-core.png b/h5/app-invite/assets/tab-inactive-core.png new file mode 100644 index 0000000..dcf6876 --- /dev/null +++ b/h5/app-invite/assets/tab-inactive-core.png @@ -0,0 +1,3 @@ + + + diff --git a/h5/app-invite/assets/tab-quota-active.png b/h5/app-invite/assets/tab-quota-active.png new file mode 100644 index 0000000..8b48731 Binary files /dev/null and b/h5/app-invite/assets/tab-quota-active.png differ diff --git a/h5/app-invite/assets/tab-quota-inactive.png b/h5/app-invite/assets/tab-quota-inactive.png new file mode 100644 index 0000000..9983733 Binary files /dev/null and b/h5/app-invite/assets/tab-quota-inactive.png differ diff --git a/h5/app-invite/assets/tab-transport-active.png b/h5/app-invite/assets/tab-transport-active.png new file mode 100644 index 0000000..0634916 Binary files /dev/null and b/h5/app-invite/assets/tab-transport-active.png differ diff --git a/h5/app-invite/assets/tab-transport-inactive.png b/h5/app-invite/assets/tab-transport-inactive.png new file mode 100644 index 0000000..a4f0d2f Binary files /dev/null and b/h5/app-invite/assets/tab-transport-inactive.png differ diff --git a/h5/app-invite/assets/task-row-bg.svg b/h5/app-invite/assets/task-row-bg.svg new file mode 100644 index 0000000..4e0391e --- /dev/null +++ b/h5/app-invite/assets/task-row-bg.svg @@ -0,0 +1,3 @@ + + + diff --git a/h5/app-invite/assets/title-image.png b/h5/app-invite/assets/title-image.png new file mode 100644 index 0000000..707104a Binary files /dev/null and b/h5/app-invite/assets/title-image.png differ diff --git a/h5/app-invite/assets/title-top-core.png b/h5/app-invite/assets/title-top-core.png new file mode 100644 index 0000000..a85badf Binary files /dev/null and b/h5/app-invite/assets/title-top-core.png differ diff --git a/h5/app-invite/assets/title-top-mask.svg b/h5/app-invite/assets/title-top-mask.svg new file mode 100644 index 0000000..8383326 --- /dev/null +++ b/h5/app-invite/assets/title-top-mask.svg @@ -0,0 +1,3 @@ + + + diff --git a/h5/app-invite/assets/任务标题背景按钮.png b/h5/app-invite/assets/任务标题背景按钮.png new file mode 100644 index 0000000..7ca5fa2 Binary files /dev/null and b/h5/app-invite/assets/任务标题背景按钮.png differ diff --git a/h5/app-invite/assets/按钮未选中.png b/h5/app-invite/assets/按钮未选中.png new file mode 100644 index 0000000..efde023 Binary files /dev/null and b/h5/app-invite/assets/按钮未选中.png differ diff --git a/h5/app-invite/assets/按钮选中.png b/h5/app-invite/assets/按钮选中.png new file mode 100644 index 0000000..3bcd7c1 Binary files /dev/null and b/h5/app-invite/assets/按钮选中.png differ diff --git a/h5/app-invite/dev-test-server.mjs b/h5/app-invite/dev-test-server.mjs new file mode 100644 index 0000000..af776a5 --- /dev/null +++ b/h5/app-invite/dev-test-server.mjs @@ -0,0 +1,96 @@ +import { createServer } from "node:http"; +import { createReadStream, statSync } from "node:fs"; +import { extname, join, normalize } from "node:path"; +import { fileURLToPath } from "node:url"; + +const rootDir = fileURLToPath(new URL(".", import.meta.url)); +const port = Number(process.env.INVITE_TEST_PORT || 65248); +const apiTarget = String(process.env.INVITE_API_TARGET || "http://127.0.0.1:2900").replace(/\/$/, ""); + +const mimeTypes = { + ".html": "text/html; charset=utf-8", + ".js": "text/javascript; charset=utf-8", + ".css": "text/css; charset=utf-8", + ".json": "application/json; charset=utf-8", + ".png": "image/png", + ".jpg": "image/jpeg", + ".jpeg": "image/jpeg", + ".svg": "image/svg+xml", +}; + +function sendText(response, status, text) { + response.writeHead(status, { "content-type": "text/plain; charset=utf-8" }); + response.end(text); +} + +function serveStatic(request, response) { + const requestPath = new URL(request.url, "http://localhost").pathname; + const pathname = requestPath === "/" ? "/dev-test.html" : requestPath; + const filePath = normalize(join(rootDir, pathname)); + if (!filePath.startsWith(rootDir)) { + sendText(response, 403, "Forbidden"); + return; + } + + try { + const stat = statSync(filePath); + if (!stat.isFile()) { + sendText(response, 404, "Not found"); + return; + } + response.writeHead(200, { + "content-type": mimeTypes[extname(filePath)] || "application/octet-stream", + "content-length": stat.size, + }); + createReadStream(filePath).pipe(response); + } catch { + sendText(response, 404, "Not found"); + } +} + +async function proxyAPI(request, response) { + const incomingURL = new URL(request.url, "http://localhost"); + const targetURL = new URL(incomingURL.pathname.replace(/^\/api/, "") + incomingURL.search, apiTarget); + const headers = new Headers(); + for (const [key, value] of Object.entries(request.headers)) { + if (value !== undefined && key.toLowerCase() !== "host") { + headers.set(key, Array.isArray(value) ? value.join(",") : value); + } + } + + try { + const upstream = await fetch(targetURL, { + method: request.method, + headers, + body: request.method === "GET" || request.method === "HEAD" ? undefined : request, + duplex: "half", + }); + response.writeHead(upstream.status, { + "content-type": upstream.headers.get("content-type") || "application/octet-stream", + }); + if (upstream.body) { + for await (const chunk of upstream.body) { + response.write(chunk); + } + } + response.end(); + } catch (error) { + response.writeHead(502, { "content-type": "application/json; charset=utf-8" }); + response.end(JSON.stringify({ + status: false, + code: "proxy_error", + message: error instanceof Error ? error.message : "proxy error", + })); + } +} + +createServer((request, response) => { + if (request.url?.startsWith("/api/") || request.url === "/api") { + proxyAPI(request, response); + return; + } + serveStatic(request, response); +}).listen(port, "127.0.0.1", () => { + console.log(`Invite dev test page: http://127.0.0.1:${port}/dev-test.html`); + console.log(`Proxy target: ${apiTarget}`); +}); diff --git a/h5/app-invite/dev-test.html b/h5/app-invite/dev-test.html new file mode 100644 index 0000000..d78ba8b --- /dev/null +++ b/h5/app-invite/dev-test.html @@ -0,0 +1,799 @@ + + + + + + 邀请活动本地接口测试 + + + +
+
+
+

邀请活动本地接口测试

+

+ 默认通过本页同源的 /api 代理到本地 Go 服务,避免浏览器 CORS 限制。 +

+
+
等待操作
+
+ +
+ + +
+
+

流程操作

+
+ + + + + + + +
+

+ 推荐顺序:填邀请人 token -> 查首页 -> 填被邀请人 token 和 userId -> 绑码 -> 充值回调 -> 刷新 -> 点任务 Claim。 +

+
+ +
+

首页结果

+
+
+ +
月度倒计时未加载
+
+
+ +
+

任务领取

+
+
+ +
+

排行榜

+
+
+ +
+

接口日志

+
+
+
+
+
+ + + + diff --git a/h5/app-invite/index.html b/h5/app-invite/index.html new file mode 100644 index 0000000..c06f98d --- /dev/null +++ b/h5/app-invite/index.html @@ -0,0 +1,1033 @@ + + + + + + App Invite + + + +
+
+
+ Invite activity hero +
+ Invite friends Make money +
+
+ +
+
+
+
+
01
+
Days
+
+
+
01
+
Hours
+
+
+
01
+
Minutes
+
+
+
01
+
Seconds
+
+
+
+ +
+

Your Reward

+

(Earn for every friend you invite)

+
+
+
+2000
+
+ Invitation reward +
+
Invitation received
+
+
+
+
+
+1000
+
+ Recharge reward +
+
Top up via friends
+
+
+
+ +
+

Start inviting people now

+

(Copy your invitation link and share it with your friends)

+
+
+
+ Invitation link: + +
+ +
+
+
+ Invitation code: + +
+ +
+
+
+ +
+
+
+
168
+
Total number of invitees
+
+
+
01
+
Number of eligible voters
+
+
+
01
+
Total amount recharged by invitees
+
+
+
12354
+
My Rewards
+
+
+
+ +
+ Monthly Tasks +
+ +
+ + +
+ +
+
+

+
+ + +
+
+ +
+
+
+ +
+ Rank +
+
+
+
+
+ +
+
+ Invite gift background +
+
+
+ Inviter avatar +
+

+

Join him to receive an exclusive welcome gift

+ +
+

Your Personal Gift

+

(This is a special gift from your friend)

+
+
+1000
+
+ Personal gift +
+
+

+ (Available only through this invitation link! Don't miss out!) +

+
+ +
+
+ App home preview +
+
+ Gift preview +
+
+ + +
+
+
+ +
+ + + + diff --git a/h5/assets/loading-ranking.gif b/h5/assets/loading-ranking.gif new file mode 100644 index 0000000..e1656fd Binary files /dev/null and b/h5/assets/loading-ranking.gif differ diff --git a/h5/assets/yumi-ranking/Ranking/Overall/bgCharm.png b/h5/assets/yumi-ranking/Ranking/Overall/bgCharm.png index 0b7a8cc..afc2828 100644 Binary files a/h5/assets/yumi-ranking/Ranking/Overall/bgCharm.png and b/h5/assets/yumi-ranking/Ranking/Overall/bgCharm.png differ diff --git a/h5/assets/yumi-ranking/Ranking/Overall/bgRoom.png b/h5/assets/yumi-ranking/Ranking/Overall/bgRoom.png index 52cbe41..afc2828 100644 Binary files a/h5/assets/yumi-ranking/Ranking/Overall/bgRoom.png and b/h5/assets/yumi-ranking/Ranking/Overall/bgRoom.png differ diff --git a/h5/assets/yumi-ranking/Ranking/Overall/bgWealth.png b/h5/assets/yumi-ranking/Ranking/Overall/bgWealth.png index 0114a72..afc2828 100644 Binary files a/h5/assets/yumi-ranking/Ranking/Overall/bgWealth.png and b/h5/assets/yumi-ranking/Ranking/Overall/bgWealth.png differ diff --git a/h5/js/Ranking-xafVKSSF-1776148658686.js b/h5/js/Ranking-xafVKSSF-1776148658686.js index a59d965..19ea82b 100644 --- a/h5/js/Ranking-xafVKSSF-1776148658686.js +++ b/h5/js/Ranking-xafVKSSF-1776148658686.js @@ -1 +1 @@ -import{L as H,_ as ce,w as be,j as A,c as O,b as e,D as b,n as h,q as s,Q as U,t as c,v as X,S as _,an as ve,k as Ae,r as n,a$ as _e,o as Te,i as xe,Y as J,l as C,ac as Ie,ay as ie,F as Re,x as Ce,G as ne,m as re,J as Be,X as se,b0 as Oe,a_ as Se}from"./index-CIAVq8iD-1776148658686.js";import{_ as K}from"./coin-Dqu2_ND9-1776148658686.js";import{c as Ue}from"./appConnector-B5Pbaops-1776148658686.js";import{p as Fe}from"./imagePreloader-C_QM-Nv8-1776148658686.js";import{g as Ee}from"./imagePaths-Bi1Bwe4Z-1776148658686.js";import{h as B}from"./imageHandler-DstQGL3Z-1776148658686.js";import{B as ue}from"./BackgroundLayer-DJYogygx-1776148658686.js";import{i as de}from"./itemCenter-DveKthYv-1776148658686.js";const De="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAACE4AAAhOAFFljFgAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAADxSURBVHgB7dvBDYJAFEXRHyuhBEuzA+3EWAmlUIJ2MDJCNATy5m1I1H9PwoZhww3Mgh8iAAAAAACllG48joGlOUxfPoZ6LvCOM5S1PrITcV4is1acuhY7O8SXmveX+gp14rJbZGQ8OdUlMiKOQByBOAJxBOIIxBGIIxBHII5AHIE4AnEE4gjEEYgjEKeBOMJ488d/irPHN+lHYz33JKIqywHflnNkZm7SRCJSA5EMRDIQyUAkA5EMRDIQyUAkA5EMRDIQyUAkA5EMRDIQyWBGOkVmRqR7ZNeKFJCRroHJRqS+8DvUWpkGk10AAAAAwO96AiOznp1YXpZWAAAAAElFTkSuQmCC",Me=async()=>{try{return await H("/activity/leaderboard/gifts-send")}catch(i){throw console.error("Failed to fetch get wealth ranking:",i),console.error("error:"+i.response.errorMsg),i}},We=async()=>{try{return await H("/activity/leaderboard/gifts-received")}catch(i){throw console.error("Failed to fetch get charm ranking:",i),console.error("error:"+i.response.errorMsg),i}},$e=async()=>{try{return await H("/activity/leaderboard/room-gifts")}catch(i){throw console.error("Failed to fetch get room ranking:",i),console.error("error:"+i.response.errorMsg),i}},je=async(i,a)=>{try{return await H(`/activity/leaderboard/my-rank?type=${i}&dateType=${a}`)}catch(v){throw console.error("Failed to fetch get my ranking:",v),console.error("error:"+v.response.errorMsg),v}},He={style:{width:"100%"}},Ve=["src"],ze=["src"],Ge={style:{position:"absolute",bottom:"0",left:"0",right:"0","z-index":"3",display:"flex","flex-direction":"column","justify-content":"flex-end","align-items":"center",gap:"1vw"}},Ne={style:{display:"flex","justify-content":"center","align-items":"center",gap:"2px",padding:"3px 5px"}},Pe={src:K,alt:"",style:{display:"block",width:"1.3em","object-fit":"cover"}},qe={__name:"topUser",props:{Type:{type:Boolean,default:""},isTopOne:{type:Boolean,default:!1},avatarUrl:{type:String,default:"/src/assets/icon/Atu/defaultAvatar.png"},BorderImgUrl:{type:String,required:!0},name:{type:String,default:""},distributionValue:{type:String,default:""}},setup(i){const a=i;be(()=>a.Type,(d,g)=>{console.log("Type 变化 =>","新值:",d,"旧值:",g)});const v=_(()=>{if(a.Type=="Wealth")return a.isTopOne?"76.5vw":"44vw";if(a.Type=="Charm")return a.isTopOne?"80vw":"44vw";if(a.Type=="Room")return a.isTopOne?"76vw":"52vw"}),Z=_(()=>{if(a.Type=="Wealth")return a.isTopOne?"height:110%":"height:72%";if(a.Type=="Charm")return a.isTopOne?"height:98%":"height:74%";if(a.Type=="Room")return a.isTopOne?"height:110%":"height:80%"}),o=_(()=>{if(a.Type=="Wealth")return a.isTopOne?"31%":"45%";if(a.Type=="Charm")return a.isTopOne?"30%":"45%";if(a.Type=="Room")return a.isTopOne?"30%":"50%"}),F=_(()=>{if(a.Type=="Wealth")return a.isTopOne?"height: 8vw;width: 30%;":"height: 5vw;width: 50%;";if(a.Type=="Charm")return a.isTopOne?"height: 10vw;width: 40%;":"height: 5vw;width: 40%;";if(a.Type=="Room")return a.isTopOne?"height: 7vw;width: 28%;":"height: 4.5vw;width: 40%;"}),r=_(()=>{if(a.Type=="Wealth")return a.isTopOne?"30%":"50%";if(a.Type=="Charm")return a.isTopOne?"35%":"50%";if(a.Type=="Room")return a.isTopOne?"30%":"50%"});return(d,g)=>{const f=ve("smart-img");return A(),O("div",He,[e("div",{style:h([{position:"relative"},{minHeight:v.value}])},[b((A(),O("img",{src:i.BorderImgUrl,key:i.BorderImgUrl,alt:"",width:"100%",style:{display:"block",position:"relative","z-index":"2"}},null,8,Ve)),[[f]]),e("div",{style:h([{position:"absolute",top:"0",left:"0",right:"0","z-index":"1",display:"flex","justify-content":"center","align-items":"center"},Z.value])},[e("div",{style:h({width:o.value})},[e("img",{src:i.avatarUrl||"",alt:"",width:"100%",style:{display:"block","aspect-ratio":"1/1","border-radius":"50%","object-fit":"cover"},onError:g[0]||(g[0]=(...p)=>s(B)&&s(B)(...p))},null,40,ze)],4)],4),e("div",Ge,[e("div",{style:h([{display:"flex","justify-content":"center","align-items":"center"},F.value])},[e("div",{style:{"font-weight":"860"},class:U(["showText",i.isTopOne?"top1Text":"text"])},c(i.name),3)],4),i.distributionValue?(A(),O("div",{key:0,style:h([{position:"relative","border-radius":"80px",border:"1px solid #ffdf36",background:"linear-gradient(270deg, #000 0%, #3b3b3b 47.45%, #000 100%)","box-shadow":"0 0 4px 0 rgba(0, 0, 0, 0.4) inset"},{width:r.value}])},[e("div",Ne,[b(e("img",Pe,null,512),[[f]]),e("div",{style:{"font-weight":"700",background:"linear-gradient(180deg, #ffe656 0%, #fff 100%)","background-clip":"text","-webkit-background-clip":"text","-webkit-text-fill-color":"transparent"},class:U(["showText",i.isTopOne?"top1Text":"text"])},c(i.distributionValue),3)])],4)):X("",!0)])],4)])}}},Q=ce(qe,[["__scopeId","data-v-7e43213b"]]),Le={class:"loading-container"},Ye={style:{position:"relative","z-index":"4"}},Je={style:{padding:"4px 8%"}},Qe={style:{display:"flex","justify-content":"space-around",position:"relative"}},Xe={style:{margin:"1vw 6vw 4vw",padding:"4px","border-radius":"32px",border:"1px solid #b2a67d",background:"linear-gradient(90deg, #0d0d0d 0%, #232323 100%)","backdrop-filter":"blur(32px)",display:"grid","grid-template-columns":"repeat(3, 1fr)"}},Ke={style:{}},Ze={style:{margin:"0 10px"}},et={style:{display:"flex","justify-content":"center"}},tt={style:{display:"flex","justify-content":"space-between","margin-top":"0"}},at={style:{padding:"3vw 3vw 0",display:"flex","flex-direction":"column",gap:"2vw"}},lt={style:{flex:"1","min-width":"0","align-self":"stretch",display:"flex","justify-content":"space-around","align-items":"center",gap:"8px"}},ot={style:{width:"auto","min-width":"0","align-self":"stretch",display:"flex","justify-content":"center","align-items":"center"}},it={style:{"font-weight":"700"}},nt=["src"],rt={style:{flex:"1","min-width":"0",padding:"0 4px","font-weight":"700",overflow:"hidden","white-space":"nowrap","text-overflow":"ellipsis"}},st={style:{width:"auto","min-width":"0","align-self":"stretch",display:"flex","align-items":"center"}},ut={src:K,alt:"",style:{display:"block",width:"1.3em","aspect-ratio":"1/1"}},dt={style:{"font-weight":"500",color:"rgba(248, 182, 45, 1)"}},ct={key:0,style:{height:"26vw"}},vt={style:{flex:"1","min-width":"0","align-self":"stretch",display:"flex","justify-content":"space-around","align-items":"center",gap:"8px"}},gt={style:{width:"auto","min-width":"0","align-self":"stretch",display:"flex","justify-content":"center","align-items":"center"}},pt={style:{"font-weight":"700"}},yt=["src"],mt={style:{flex:"1","min-width":"0","font-weight":"700",padding:"0 4px",overflow:"hidden","white-space":"nowrap","text-overflow":"ellipsis"}},ht={style:{width:"auto","min-width":"0","align-self":"stretch",display:"flex","align-items":"center"}},ft={src:K,alt:"",style:{display:"block",width:"1.3em","aspect-ratio":"1/1"}},kt={style:{"font-weight":"500",color:"rgba(248, 182, 45, 1)"}},wt={__name:"Ranking",setup(i){const a=Be(),{t:v,locale:Z}=Ae(),o=t=>Ee("Ranking/Overall/",t),F=n(!1),r=n(a.query.first||"Wealth"),d=n("Daily"),g=n("GIFTS_SEND"),f=n("DAY"),p=n({}),y=n([]),k=n({}),S=n(!0),V=n(null),x=n(!0),T=n(10),ee=_(()=>r.value=="Wealth"?o("bgWealth"):r.value=="Charm"?o("bgCharm"):r.value=="Room"?o("bgRoom"):""),ge=async()=>{const t=[o("bgWealth"),o("bgCharm"),o("bgRoom"),o("top1-wealth"),o("top1-charm"),o("top1-room"),o("top2"),o("top3"),o("top2-room"),o("top3-room")];await Fe(t)},te=()=>{console.log("updateTimeRanking()//筛选展示榜单"),d.value=="Hourly"&&p.value.hourly?(y.value=p.value.hourly,console.log("showRanking.value:",y.value)):d.value=="Daily"&&p.value.daily?y.value=p.value.daily:d.value=="Weekly"&&p.value.weekly?y.value=p.value.weekly:d.value=="Monthly"&&p.value.monthly&&(y.value=p.value.monthly),T.value=10,x.value=!0},z=se(t=>{r.value!=t&&(console.log("改变rankingType"),r.value=t,d.value="Daily",f.value="DAY",t=="Wealth"?g.value="GIFTS_SEND":t=="Charm"?g.value="GIFTS_RECEIVED":t=="Room"&&(g.value="ROOM_GIFTS"),p.value={},y.value=[],k.value={},T.value=10,x.value=!0,le(),Y())},1e3),G=se(t=>{d.value!=t&&(console.log("改变rankingType"),d.value=t,t=="Hourly"?f.value="HOUR":t=="Daily"?f.value="DAY":t=="Weekly"?f.value="WEEK":t=="Monthly"&&(f.value="MONTH"),y.value=[],k.value={},te(),Y())},500),m=_(()=>{console.log("刷新RankingTop3");let t=[...y.value];if(y.value.length<3){let l=Array.from({length:3-y.value.length},()=>({avatar:"",nickname:"",quantityFormat:""}));t.push(...l)}return t}),pe=_(()=>y.value.filter((l,w)=>w>=3).slice(0,T.value)),ye=n(""),N=n(""),P=n(""),I=n(""),q=n(""),L=n(""),E=n(""),D=n(""),M=n(""),ae=n(null),W=n(null);_e(()=>{r.value=="Wealth"&&(E.value=o("top1-wealth"),D.value=o("top2"),M.value=o("top3"),I.value=o("tabSelectedBg"),N.value="1px solid rgba(221, 114, 0, 0.39)",P.value="rgba(252, 194, 147, 0.20)",q.value="linear-gradient(97deg, rgba(50, 34, 21, 0.60) 12.03%, rgba(99, 52, 33, 0.60) 100.37%)",L.value="solid #FFA166",g.value="GIFTS_SEND"),r.value=="Charm"&&(E.value=o("top1-charm"),D.value=o("top2"),M.value=o("top3"),I.value=o("tabSelectedBg"),N.value="1px solid rgba(0, 221, 74, 0.39)",P.value="rgba(147, 252, 166, 0.20)",q.value="linear-gradient(97deg, rgba(21, 50, 22, 0.60) 12.03%, rgba(33, 99, 40, 0.60) 100.37%)",L.value="solid #66FF82",g.value="GIFTS_RECEIVED"),r.value=="Room"&&(E.value=o("top1-room"),D.value=o("top2-room"),M.value=o("top3-room"),I.value=o("tabSelectedBg"),N.value="1px solid rgba(155, 0, 221, 0.39)",P.value="rgba(219, 147, 252, 0.20)",q.value="linear-gradient(97deg, rgba(33, 21, 50, 0.60) 12.03%, rgba(60, 33, 99, 0.60) 100.37%)",L.value="solid #A366FF",g.value="ROOM_GIFTS")});const me=new IntersectionObserver(t=>{t.forEach(l=>{l.isIntersecting?W.value.classList.remove("stuck"):(console.log("哨兵元素离开视口"),W.value?(W.value.classList.add("stuck"),console.log("添加stuck类")):console.log("元素未找到!"))})}),he=new IntersectionObserver(t=>{t.forEach(l=>{if(l.isIntersecting&&x.value){console.log("触发上拉加载更多");const w=y.value.filter((u,j)=>j>=3).length,R=T.value;R=w&&(x.value=!1,console.log("没有更多数据了"))):(x.value=!1,console.log("没有更多数据了"))}})},{rootMargin:"100px"}),$=t=>{F.value&&(r.value=="Room"?Oe(t):Se(t))},le=async()=>{console.log("获取榜单");let t={};r.value=="Wealth"?t=await Me():r.value=="Charm"?t=await We():r.value=="Room"&&(t=await $e()),t.status&&t.body&&(p.value=t.body,te())},Y=async()=>{console.log("获取我的排名");const t=await je(g.value,f.value);t.status&&t.body?k.value=t.body:k.value={}},fe=async()=>{await Promise.all([le(),Y()])},ke=async()=>{try{await Promise.all([fe(),ge()])}catch(t){console.error("预加载过程中发生错误:",t)}finally{S.value=!1}},we=async()=>{await Ue(()=>{ke()})};return Te(()=>{we(),F.value=xe(),me.observe(ae.value),V.value&&he.observe(V.value)}),(t,l)=>{const w=Ie,R=ve("smart-img");return A(),O("div",{class:"fullPage",style:h({backgroundColor:ye.value})},[b(e("div",Le,[C(ue,{backgroundImages:[ee.value]},null,8,["backgroundImages"]),C(w),e("p",null,c(t.$t("loading"))+"...",1)],512),[[J,S.value]]),b(e("div",Ye,[C(ue,{backgroundImages:[ee.value],layerStyle:{zIndex:-1}},null,8,["backgroundImages"]),e("div",{ref_key:"sentinel",ref:ae},null,512),e("div",{ref_key:"stickyHeader",ref:W,style:{position:"sticky",top:"0","z-index":"999","border-radius":"0 0 12px 12px",transition:"all 0.3s",height:"35vw"}},[l[12]||(l[12]=e("div",{style:{height:"13vw"}},null,-1)),e("div",Je,[e("div",Qe,[e("div",{class:"tab",onClick:l[0]||(l[0]=u=>s(z)("Wealth")),style:h([{},{color:r.value=="Wealth"?"#FF9500":"#fff"}])},c(s(v)("ranking_wealth")),5),e("div",{class:"tab",onClick:l[1]||(l[1]=u=>s(z)("Charm")),style:h([{},{color:r.value=="Charm"?"#FF9500":"#fff"}])},c(s(v)("ranking_charm")),5),e("div",{class:"tab",onClick:l[2]||(l[2]=u=>s(z)("Room")),style:h([{},{color:r.value=="Room"?"#FF9500":"#fff"}])},c(s(v)("ranking_room")),5),b(e("img",{src:De,alt:"",style:{position:"absolute",left:"-4%",top:"45%",transform:"translateY(-50%)"},width:"6%",onClick:l[3]||(l[3]=(...u)=>s(ie)&&s(ie)(...u))},null,512),[[R]])])]),e("div",Xe,[e("div",{class:"tabItem",style:h(d.value=="Daily"?{backgroundImage:`url(${I.value})`,backgroundSize:"100% 100%",backgroundRepeat:"no-repeat",backgroundPosition:"center"}:{})},[e("div",{class:U(["tag",{tagActive:d.value=="Daily"}]),onClick:l[4]||(l[4]=u=>s(G)("Daily"))},c(s(v)("ranking_daily")),3)],4),e("div",{class:"tabItem",style:h(d.value=="Weekly"?{backgroundImage:`url(${I.value})`,backgroundSize:"100% 100%",backgroundRepeat:"no-repeat",backgroundPosition:"center"}:{})},[e("div",{class:U(["tag",{tagActive:d.value=="Weekly"}]),onClick:l[5]||(l[5]=u=>s(G)("Weekly"))},c(s(v)("ranking_weekly")),3)],4),e("div",{class:"tabItem",style:h(d.value=="Monthly"?{backgroundImage:`url(${I.value})`,backgroundSize:"100% 100%",backgroundRepeat:"no-repeat",backgroundPosition:"center"}:{})},[e("div",{class:U(["tag",{tagActive:d.value=="Monthly"}]),onClick:l[6]||(l[6]=u=>s(G)("Monthly"))},c(s(v)("ranking_monthly")),3)],4)])],512),e("div",Ke,[e("div",Ze,[e("div",et,[C(Q,{Type:r.value,isTopOne:!0,BorderImgUrl:E.value,avatarUrl:m.value[0].avatar||"",name:m.value[0].nickname||"",distributionValue:m.value[0].quantityFormat||"",style:{width:"85%"},onClick:l[7]||(l[7]=u=>$(m.value[0].id))},null,8,["Type","BorderImgUrl","avatarUrl","name","distributionValue"])]),e("div",tt,[C(Q,{Type:r.value,BorderImgUrl:D.value,avatarUrl:m.value[1].avatar||"",name:m.value[1].nickname||"",distributionValue:m.value[1].quantityFormat||"",style:{width:"43%"},onClick:l[8]||(l[8]=u=>$(m.value[1].id))},null,8,["Type","BorderImgUrl","avatarUrl","name","distributionValue"]),C(Q,{Type:r.value,BorderImgUrl:M.value,avatarUrl:m.value[2].avatar||"",name:m.value[2].nickname||"",distributionValue:m.value[2].quantityFormat||"",style:{width:"43%"},onClick:l[9]||(l[9]=u=>$(m.value[2].id))},null,8,["Type","BorderImgUrl","avatarUrl","name","distributionValue"])])]),e("div",at,[(A(!0),O(Re,null,Ce(pe.value,(u,j)=>(A(),ne(de,{style:{"min-height":"19vw"},key:u.id,imgUrl:o("RankingItem"),contentStyle:"padding: 3vw;justify-content: space-between;gap: 8px;",lazy:!0,immediate:j<10,onClick:oe=>$(u.id)},{default:re(()=>[e("div",lt,[e("div",ot,[e("div",it,c(j+4),1)]),e("img",{src:u.avatar||"",alt:"",style:{display:"block",width:"3em","aspect-ratio":"1/1","border-radius":"50%","object-fit":"cover"},onError:l[10]||(l[10]=(...oe)=>s(B)&&s(B)(...oe))},null,40,nt),e("div",rt,c(u.nickname),1)]),e("div",st,[b(e("img",ut,null,512),[[R]]),e("div",dt,c(u.quantityFormat),1)])]),_:2},1032,["imgUrl","immediate","onClick"]))),128)),b(e("div",{ref_key:"RankingLoadmore",ref:V},null,512),[[J,x.value]])])])],512),[[J,!S.value]]),S.value?X("",!0):(A(),O("div",ct)),S.value?X("",!0):(A(),ne(de,{key:1,style:{"min-height":"25vw",position:"fixed",bottom:"0","z-index":"999",padding:"0 1px"},imgUrl:o("myRankingBg"),contentStyle:"padding:4vw 4vw 0;justify-content: space-between;gap: 8px;"},{default:re(()=>[e("div",vt,[e("div",gt,[e("div",pt,c(k.value.rank||999),1)]),e("img",{src:k.value.avatar||"",alt:"",style:{display:"block",width:"3em","aspect-ratio":"1/1","border-radius":"50%","object-fit":"cover"},onError:l[11]||(l[11]=(...u)=>s(B)&&s(B)(...u))},null,40,yt),e("div",mt,c(k.value.nickname||""),1)]),e("div",ht,[b(e("img",ft,null,512),[[R]]),e("div",kt,c(k.value.quantityFormat||0),1)])]),_:1},8,["imgUrl"]))],4)}}},St=ce(wt,[["__scopeId","data-v-93c53aed"]]);export{St as default}; +import{L as H,_ as ce,w as be,j as A,c as O,b as e,D as b,n as h,q as s,Q as U,t as c,v as X,S as _,an as ve,k as Ae,r as n,a$ as _e,o as Te,i as xe,Y as J,l as C,ac as Ie,ay as ie,F as Re,x as Ce,G as ne,m as re,J as Be,X as se,b0 as Oe,a_ as Se}from"./index-CIAVq8iD-1776148658686.js";import{_ as K}from"./coin-Dqu2_ND9-1776148658686.js";import{c as Ue}from"./appConnector-B5Pbaops-1776148658686.js";import{p as Fe}from"./imagePreloader-C_QM-Nv8-1776148658686.js";import{g as Ee}from"./imagePaths-Bi1Bwe4Z-1776148658686.js";import{h as B}from"./imageHandler-DstQGL3Z-1776148658686.js";import{B as ue}from"./BackgroundLayer-DJYogygx-1776148658686.js";import{i as de}from"./itemCenter-DveKthYv-1776148658686.js";const De="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAACE4AAAhOAFFljFgAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAADxSURBVHgB7dvBDYJAFEXRHyuhBEuzA+3EWAmlUIJ2MDJCNATy5m1I1H9PwoZhww3Mgh8iAAAAAACllG48joGlOUxfPoZ6LvCOM5S1PrITcV4is1acuhY7O8SXmveX+gp14rJbZGQ8OdUlMiKOQByBOAJxBOIIxBGIIxBHII5AHIE4AnEE4gjEEYgjEKeBOMJ488d/irPHN+lHYz33JKIqywHflnNkZm7SRCJSA5EMRDIQyUAkA5EMRDIQyUAkA5EMRDIQyUAkA5EMRDIQyWBGOkVmRqR7ZNeKFJCRroHJRqS+8DvUWpkGk10AAAAAwO96AiOznp1YXpZWAAAAAElFTkSuQmCC",Me=async()=>{try{return await H("/activity/leaderboard/gifts-send")}catch(i){throw console.error("Failed to fetch get wealth ranking:",i),console.error("error:"+i.response.errorMsg),i}},We=async()=>{try{return await H("/activity/leaderboard/gifts-received")}catch(i){throw console.error("Failed to fetch get charm ranking:",i),console.error("error:"+i.response.errorMsg),i}},$e=async()=>{try{return await H("/activity/leaderboard/room-gifts")}catch(i){throw console.error("Failed to fetch get room ranking:",i),console.error("error:"+i.response.errorMsg),i}},je=async(i,a)=>{try{return await H(`/activity/leaderboard/my-rank?type=${i}&dateType=${a}`)}catch(v){throw console.error("Failed to fetch get my ranking:",v),console.error("error:"+v.response.errorMsg),v}},He={style:{width:"100%"}},Ve=["src"],ze=["src"],Ge={style:{position:"absolute",bottom:"0",left:"0",right:"0","z-index":"3",display:"flex","flex-direction":"column","justify-content":"flex-end","align-items":"center",gap:"1vw"}},Ne={style:{display:"flex","justify-content":"center","align-items":"center",gap:"2px",padding:"3px 5px"}},Pe={src:K,alt:"",style:{display:"block",width:"1.3em","object-fit":"cover"}},qe={__name:"topUser",props:{Type:{type:Boolean,default:""},isTopOne:{type:Boolean,default:!1},avatarUrl:{type:String,default:"/src/assets/icon/Atu/defaultAvatar.png"},BorderImgUrl:{type:String,required:!0},name:{type:String,default:""},distributionValue:{type:String,default:""}},setup(i){const a=i;be(()=>a.Type,(d,g)=>{console.log("Type 变化 =>","新值:",d,"旧值:",g)});const v=_(()=>{if(a.Type=="Wealth")return a.isTopOne?"76.5vw":"44vw";if(a.Type=="Charm")return a.isTopOne?"80vw":"44vw";if(a.Type=="Room")return a.isTopOne?"76vw":"52vw"}),Z=_(()=>{if(a.Type=="Wealth")return a.isTopOne?"height:110%":"height:72%";if(a.Type=="Charm")return a.isTopOne?"height:98%":"height:74%";if(a.Type=="Room")return a.isTopOne?"height:110%":"height:80%"}),o=_(()=>{if(a.Type=="Wealth")return a.isTopOne?"31%":"45%";if(a.Type=="Charm")return a.isTopOne?"30%":"45%";if(a.Type=="Room")return a.isTopOne?"30%":"50%"}),F=_(()=>{if(a.Type=="Wealth")return a.isTopOne?"height: 8vw;width: 30%;":"height: 5vw;width: 50%;";if(a.Type=="Charm")return a.isTopOne?"height: 10vw;width: 40%;":"height: 5vw;width: 40%;";if(a.Type=="Room")return a.isTopOne?"height: 7vw;width: 28%;":"height: 4.5vw;width: 40%;"}),r=_(()=>{if(a.Type=="Wealth")return a.isTopOne?"30%":"50%";if(a.Type=="Charm")return a.isTopOne?"35%":"50%";if(a.Type=="Room")return a.isTopOne?"30%":"50%"});return(d,g)=>{const f=ve("smart-img");return A(),O("div",He,[e("div",{style:h([{position:"relative"},{minHeight:v.value}])},[b((A(),O("img",{src:i.BorderImgUrl,key:i.BorderImgUrl,alt:"",width:"100%",style:{display:"block",position:"relative","z-index":"2"}},null,8,Ve)),[[f]]),e("div",{style:h([{position:"absolute",top:"0",left:"0",right:"0","z-index":"1",display:"flex","justify-content":"center","align-items":"center"},Z.value])},[e("div",{style:h({width:o.value})},[e("img",{src:i.avatarUrl||"",alt:"",width:"100%",style:{display:"block","aspect-ratio":"1/1","border-radius":"50%","object-fit":"cover"},onError:g[0]||(g[0]=(...p)=>s(B)&&s(B)(...p))},null,40,ze)],4)],4),e("div",Ge,[e("div",{style:h([{display:"flex","justify-content":"center","align-items":"center"},F.value])},[e("div",{style:{"font-weight":"860"},class:U(["showText",i.isTopOne?"top1Text":"text"])},c(i.name),3)],4),i.distributionValue?(A(),O("div",{key:0,style:h([{position:"relative","border-radius":"80px",border:"1px solid #ffdf36",background:"linear-gradient(270deg, #000 0%, #3b3b3b 47.45%, #000 100%)","box-shadow":"0 0 4px 0 rgba(0, 0, 0, 0.4) inset"},{width:r.value}])},[e("div",Ne,[b(e("img",Pe,null,512),[[f]]),e("div",{style:{"font-weight":"700",background:"linear-gradient(180deg, #ffe656 0%, #fff 100%)","background-clip":"text","-webkit-background-clip":"text","-webkit-text-fill-color":"transparent"},class:U(["showText",i.isTopOne?"top1Text":"text"])},c(i.distributionValue),3)])],4)):X("",!0)])],4)])}}},Q=ce(qe,[["__scopeId","data-v-7e43213b"]]),Le={class:"loading-container"},Ye={style:{position:"relative","z-index":"4"}},Je={style:{padding:"4px 8%"}},Qe={style:{display:"flex","justify-content":"space-around",position:"relative"}},Xe={style:{margin:"1vw 6vw 4vw",padding:"4px","border-radius":"32px",border:"1px solid #b2a67d",background:"linear-gradient(90deg, #0d0d0d 0%, #232323 100%)","backdrop-filter":"blur(32px)",display:"grid","grid-template-columns":"repeat(3, 1fr)"}},Ke={style:{}},Ze={style:{margin:"0 10px"}},et={style:{display:"flex","justify-content":"center"}},tt={style:{display:"flex","justify-content":"space-between","margin-top":"0"}},at={style:{padding:"3vw 3vw 0",display:"flex","flex-direction":"column",gap:"2vw"}},lt={style:{flex:"1","min-width":"0","align-self":"stretch",display:"flex","justify-content":"space-around","align-items":"center",gap:"8px"}},ot={style:{width:"auto","min-width":"0","align-self":"stretch",display:"flex","justify-content":"center","align-items":"center"}},it={style:{"font-weight":"700"}},nt=["src"],rt={style:{flex:"1","min-width":"0",padding:"0 4px","font-weight":"700",overflow:"hidden","white-space":"nowrap","text-overflow":"ellipsis"}},st={style:{width:"auto","min-width":"0","align-self":"stretch",display:"flex","align-items":"center"}},ut={src:K,alt:"",style:{display:"block",width:"1.3em","aspect-ratio":"1/1"}},dt={style:{"font-weight":"500",color:"rgba(248, 182, 45, 1)"}},ct={key:0,style:{height:"26vw"}},vt={style:{flex:"1","min-width":"0","align-self":"stretch",display:"flex","justify-content":"space-around","align-items":"center",gap:"8px"}},gt={style:{width:"auto","min-width":"0","align-self":"stretch",display:"flex","justify-content":"center","align-items":"center"}},pt={style:{"font-weight":"700"}},yt=["src"],mt={style:{flex:"1","min-width":"0","font-weight":"700",padding:"0 4px",overflow:"hidden","white-space":"nowrap","text-overflow":"ellipsis"}},ht={style:{width:"auto","min-width":"0","align-self":"stretch",display:"flex","align-items":"center"}},ft={src:K,alt:"",style:{display:"block",width:"1.3em","aspect-ratio":"1/1"}},kt={style:{"font-weight":"500",color:"rgba(248, 182, 45, 1)"}},wt={__name:"Ranking",setup(i){const a=Be(),{t:v,locale:Z}=Ae(),o=t=>Ee("Ranking/Overall/",t),F=n(!1),r=n(a.query.first||"Wealth"),d=n("Daily"),g=n("GIFTS_SEND"),f=n("DAY"),p=n({}),y=n([]),k=n({}),S=n(!0),V=n(null),x=n(!0),T=n(10),ee=_(()=>r.value=="Wealth"?o("bgWealth")+"?v=20260424c":r.value=="Charm"?o("bgCharm")+"?v=20260424c":r.value=="Room"?o("bgRoom")+"?v=20260424c":""),ge=async()=>{const t=[o("bgWealth")+"?v=20260424c",o("bgCharm")+"?v=20260424c",o("bgRoom")+"?v=20260424c",o("top1-wealth"),o("top1-charm"),o("top1-room"),o("top2"),o("top3"),o("top2-room"),o("top3-room")];await Fe(t)},te=()=>{console.log("updateTimeRanking()//筛选展示榜单"),d.value=="Hourly"&&p.value.hourly?(y.value=p.value.hourly,console.log("showRanking.value:",y.value)):d.value=="Daily"&&p.value.daily?y.value=p.value.daily:d.value=="Weekly"&&p.value.weekly?y.value=p.value.weekly:d.value=="Monthly"&&p.value.monthly&&(y.value=p.value.monthly),T.value=10,x.value=!0},z=se(t=>{r.value!=t&&(console.log("改变rankingType"),r.value=t,d.value="Daily",f.value="DAY",t=="Wealth"?g.value="GIFTS_SEND":t=="Charm"?g.value="GIFTS_RECEIVED":t=="Room"&&(g.value="ROOM_GIFTS"),p.value={},y.value=[],k.value={},T.value=10,x.value=!0,le(),Y())},1e3),G=se(t=>{d.value!=t&&(console.log("改变rankingType"),d.value=t,t=="Hourly"?f.value="HOUR":t=="Daily"?f.value="DAY":t=="Weekly"?f.value="WEEK":t=="Monthly"&&(f.value="MONTH"),y.value=[],k.value={},te(),Y())},500),m=_(()=>{console.log("刷新RankingTop3");let t=[...y.value];if(y.value.length<3){let l=Array.from({length:3-y.value.length},()=>({avatar:"",nickname:"",quantityFormat:""}));t.push(...l)}return t}),pe=_(()=>y.value.filter((l,w)=>w>=3).slice(0,T.value)),ye=n(""),N=n(""),P=n(""),I=n(""),q=n(""),L=n(""),E=n(""),D=n(""),M=n(""),ae=n(null),W=n(null);_e(()=>{r.value=="Wealth"&&(E.value=o("top1-wealth"),D.value=o("top2"),M.value=o("top3"),I.value=o("tabSelectedBg"),N.value="1px solid rgba(221, 114, 0, 0.39)",P.value="rgba(252, 194, 147, 0.20)",q.value="linear-gradient(97deg, rgba(50, 34, 21, 0.60) 12.03%, rgba(99, 52, 33, 0.60) 100.37%)",L.value="solid #FFA166",g.value="GIFTS_SEND"),r.value=="Charm"&&(E.value=o("top1-charm"),D.value=o("top2"),M.value=o("top3"),I.value=o("tabSelectedBg"),N.value="1px solid rgba(0, 221, 74, 0.39)",P.value="rgba(147, 252, 166, 0.20)",q.value="linear-gradient(97deg, rgba(21, 50, 22, 0.60) 12.03%, rgba(33, 99, 40, 0.60) 100.37%)",L.value="solid #66FF82",g.value="GIFTS_RECEIVED"),r.value=="Room"&&(E.value=o("top1-room"),D.value=o("top2-room"),M.value=o("top3-room"),I.value=o("tabSelectedBg"),N.value="1px solid rgba(155, 0, 221, 0.39)",P.value="rgba(219, 147, 252, 0.20)",q.value="linear-gradient(97deg, rgba(33, 21, 50, 0.60) 12.03%, rgba(60, 33, 99, 0.60) 100.37%)",L.value="solid #A366FF",g.value="ROOM_GIFTS")});const me=new IntersectionObserver(t=>{t.forEach(l=>{l.isIntersecting?W.value.classList.remove("stuck"):(console.log("哨兵元素离开视口"),W.value?(W.value.classList.add("stuck"),console.log("添加stuck类")):console.log("元素未找到!"))})}),he=new IntersectionObserver(t=>{t.forEach(l=>{if(l.isIntersecting&&x.value){console.log("触发上拉加载更多");const w=y.value.filter((u,j)=>j>=3).length,R=T.value;R=w&&(x.value=!1,console.log("没有更多数据了"))):(x.value=!1,console.log("没有更多数据了"))}})},{rootMargin:"100px"}),$=t=>{F.value&&(r.value=="Room"?Oe(t):Se(t))},le=async()=>{console.log("获取榜单");let t={};r.value=="Wealth"?t=await Me():r.value=="Charm"?t=await We():r.value=="Room"&&(t=await $e()),t.status&&t.body&&(p.value=t.body,te())},Y=async()=>{console.log("获取我的排名");const t=await je(g.value,f.value);t.status&&t.body?k.value=t.body:k.value={}},fe=async()=>{await Promise.all([le(),Y()])},ke=async()=>{try{await Promise.all([fe(),ge()])}catch(t){console.error("预加载过程中发生错误:",t)}finally{S.value=!1}},we=async()=>{await Ue(()=>{ke()})};return Te(()=>{we(),F.value=xe(),me.observe(ae.value),V.value&&he.observe(V.value)}),(t,l)=>{const w=Ie,R=ve("smart-img");return A(),O("div",{class:"fullPage",style:h({backgroundColor:ye.value})},[b(e("div",Le,[C(ue,{backgroundImages:[ee.value]},null,8,["backgroundImages"]),e("img",{src:"/assets/loading-ranking.gif",alt:"",class:"ranking-loading-image",style:{display:"block",width:"180px",maxWidth:"70vw",height:"auto",margin:"0 auto 12px","object-fit":"contain"}},null,-1),e("p",null,c(t.$t("loading"))+"...",1)],512),[[J,S.value]]),b(e("div",Ye,[C(ue,{backgroundImages:[ee.value],layerStyle:{zIndex:-1}},null,8,["backgroundImages"]),e("div",{ref_key:"sentinel",ref:ae},null,512),e("div",{ref_key:"stickyHeader",ref:W,style:{position:"sticky",top:"0","z-index":"999","border-radius":"0 0 12px 12px",transition:"all 0.3s",height:"35vw"}},[l[12]||(l[12]=e("div",{style:{height:"13vw"}},null,-1)),e("div",Je,[e("div",Qe,[e("div",{class:"tab",onClick:l[0]||(l[0]=u=>s(z)("Wealth")),style:h([{},{color:r.value=="Wealth"?"#FF9500":"#fff"}])},c(s(v)("ranking_wealth")),5),e("div",{class:"tab",onClick:l[1]||(l[1]=u=>s(z)("Charm")),style:h([{},{color:r.value=="Charm"?"#FF9500":"#fff"}])},c(s(v)("ranking_charm")),5),e("div",{class:"tab",onClick:l[2]||(l[2]=u=>s(z)("Room")),style:h([{},{color:r.value=="Room"?"#FF9500":"#fff"}])},c(s(v)("ranking_room")),5),b(e("img",{src:De,alt:"",style:{position:"absolute",left:"-4%",top:"45%",transform:"translateY(-50%)"},width:"6%",onClick:l[3]||(l[3]=(...u)=>s(ie)&&s(ie)(...u))},null,512),[[R]])])]),e("div",Xe,[e("div",{class:"tabItem",style:h(d.value=="Daily"?{backgroundImage:`url(${I.value})`,backgroundSize:"100% 100%",backgroundRepeat:"no-repeat",backgroundPosition:"center"}:{})},[e("div",{class:U(["tag",{tagActive:d.value=="Daily"}]),onClick:l[4]||(l[4]=u=>s(G)("Daily"))},c(s(v)("ranking_daily")),3)],4),e("div",{class:"tabItem",style:h(d.value=="Weekly"?{backgroundImage:`url(${I.value})`,backgroundSize:"100% 100%",backgroundRepeat:"no-repeat",backgroundPosition:"center"}:{})},[e("div",{class:U(["tag",{tagActive:d.value=="Weekly"}]),onClick:l[5]||(l[5]=u=>s(G)("Weekly"))},c(s(v)("ranking_weekly")),3)],4),e("div",{class:"tabItem",style:h(d.value=="Monthly"?{backgroundImage:`url(${I.value})`,backgroundSize:"100% 100%",backgroundRepeat:"no-repeat",backgroundPosition:"center"}:{})},[e("div",{class:U(["tag",{tagActive:d.value=="Monthly"}]),onClick:l[6]||(l[6]=u=>s(G)("Monthly"))},c(s(v)("ranking_monthly")),3)],4)])],512),e("div",Ke,[e("div",Ze,[e("div",et,[C(Q,{Type:r.value,isTopOne:!0,BorderImgUrl:E.value,avatarUrl:m.value[0].avatar||"",name:m.value[0].nickname||"",distributionValue:m.value[0].quantityFormat||"",style:{width:"85%"},onClick:l[7]||(l[7]=u=>$(m.value[0].id))},null,8,["Type","BorderImgUrl","avatarUrl","name","distributionValue"])]),e("div",tt,[C(Q,{Type:r.value,BorderImgUrl:D.value,avatarUrl:m.value[1].avatar||"",name:m.value[1].nickname||"",distributionValue:m.value[1].quantityFormat||"",style:{width:"43%"},onClick:l[8]||(l[8]=u=>$(m.value[1].id))},null,8,["Type","BorderImgUrl","avatarUrl","name","distributionValue"]),C(Q,{Type:r.value,BorderImgUrl:M.value,avatarUrl:m.value[2].avatar||"",name:m.value[2].nickname||"",distributionValue:m.value[2].quantityFormat||"",style:{width:"43%"},onClick:l[9]||(l[9]=u=>$(m.value[2].id))},null,8,["Type","BorderImgUrl","avatarUrl","name","distributionValue"])])]),e("div",at,[(A(!0),O(Re,null,Ce(pe.value,(u,j)=>(A(),ne(de,{style:{"min-height":"19vw"},key:u.id,imgUrl:o("RankingItem"),contentStyle:"padding: 3vw;justify-content: space-between;gap: 8px;",lazy:!0,immediate:j<10,onClick:oe=>$(u.id)},{default:re(()=>[e("div",lt,[e("div",ot,[e("div",it,c(j+4),1)]),e("img",{src:u.avatar||"",alt:"",style:{display:"block",width:"3em","aspect-ratio":"1/1","border-radius":"50%","object-fit":"cover"},onError:l[10]||(l[10]=(...oe)=>s(B)&&s(B)(...oe))},null,40,nt),e("div",rt,c(u.nickname),1)]),e("div",st,[b(e("img",ut,null,512),[[R]]),e("div",dt,c(u.quantityFormat),1)])]),_:2},1032,["imgUrl","immediate","onClick"]))),128)),b(e("div",{ref_key:"RankingLoadmore",ref:V},null,512),[[J,x.value]])])])],512),[[J,!S.value]]),S.value?X("",!0):(A(),O("div",ct)),S.value?X("",!0):(A(),ne(de,{key:1,style:{"min-height":"25vw",position:"fixed",bottom:"0","z-index":"999",padding:"0 1px"},imgUrl:o("myRankingBg"),contentStyle:"padding:4vw 4vw 0;justify-content: space-between;gap: 8px;"},{default:re(()=>[e("div",vt,[e("div",gt,[e("div",pt,c(k.value.rank||999),1)]),e("img",{src:k.value.avatar||"",alt:"",style:{display:"block",width:"3em","aspect-ratio":"1/1","border-radius":"50%","object-fit":"cover"},onError:l[11]||(l[11]=(...u)=>s(B)&&s(B)(...u))},null,40,yt),e("div",mt,c(k.value.nickname||""),1)]),e("div",ht,[b(e("img",ft,null,512),[[R]]),e("div",kt,c(k.value.quantityFormat||0),1)])]),_:1},8,["imgUrl"]))],4)}}},St=ce(wt,[["__scopeId","data-v-93c53aed"]]);export{St as default}; diff --git a/h5/mifa-pay/app.js b/h5/mifa-pay/app.js new file mode 100644 index 0000000..9af609e --- /dev/null +++ b/h5/mifa-pay/app.js @@ -0,0 +1,407 @@ +const query = new URLSearchParams(window.location.search); +const storedAppId = window.localStorage.getItem("mifa-pay-application-id") || ""; +const LOGO_SRC = "./mifapay-logo.png"; + +function getPathApplicationId() { + const segments = window.location.pathname.split("/").filter(Boolean); + const pageIndex = segments.lastIndexOf("mifa-pay"); + if (pageIndex === -1) { + return ""; + } + + return segments[pageIndex + 1] || ""; +} + +const defaults = { + applicationId: + getPathApplicationId() || + query.get("applicationId") || + query.get("appId") || + storedAppId || + "2048200000000000701", + productType: query.get("type") || "GOLD", +}; + +const deps = { + getApi: null, + postApi: null, + fetchCurrentUser: null, + getCachedUserProfile: null, + connectApp: null, +}; + +const state = { + applicationId: defaults.applicationId, + productType: defaults.productType, + userProfile: null, + countries: [], + selectedCountryId: "", + commodityCard: null, + selectedGoodsId: "", + selectedGoods: null, + pendingChannelCode: "", + dependencyPromise: null, +}; + +const els = { + applicationId: document.getElementById("applicationId"), + productList: document.getElementById("productList"), + channelList: document.getElementById("channelList"), + pageStatus: document.getElementById("pageStatus"), +}; + +function setStatus(text = "") { + if (!text) { + els.pageStatus.textContent = ""; + els.pageStatus.className = "status"; + return; + } + + els.pageStatus.textContent = text; + els.pageStatus.className = "status show"; +} + +function persistSettings() { + window.localStorage.setItem("mifa-pay-application-id", state.applicationId); +} + +function extractErrorMessage(error) { + return ( + error?.response?.errorMsg || + error?.response?.body?.errorMsg || + error?.errorMsg || + error?.message || + "Unknown error" + ); +} + +function readStoredUserProfile() { + const raw = window.localStorage.getItem("userProfile"); + if (!raw) { + return null; + } + + try { + const parsed = JSON.parse(raw); + return parsed && typeof parsed === "object" ? parsed : null; + } catch (error) { + return null; + } +} + +function resolveUserProfile(response = null) { + if (response?.body && typeof response.body === "object") { + return response.body; + } + + if (typeof deps.getCachedUserProfile === "function") { + const cached = deps.getCachedUserProfile(); + if (cached && typeof cached === "object") { + return cached; + } + } + + return readStoredUserProfile(); +} + +function currentUserId() { + return String(state.userProfile?.id || "").trim(); +} + +function currentCountryId() { + return state.selectedCountryId; +} + +function getCountryLabel(item) { + return ( + item?.label || + item?.countryName || + item?.country?.aliasName || + item?.country?.countryName || + String(item?.id || "") + ); +} + +function getGoodsList() { + return state.commodityCard?.commodity || []; +} + +function getChannelList() { + return state.commodityCard?.channels || []; +} + +function getChannelCode(item) { + return item?.details?.channelCode || item?.channel?.channelCode || ""; +} + +function getAmountText(item) { + const content = item?.content || item?.obtainGoldsQuantity || "-"; + return `${content} ${item?.type || ""}`.trim(); +} + +function getPriceText(item) { + const currency = item?.currency || state.commodityCard?.payCountry?.currency || "USD"; + return `${item?.amount || item?.amountUsd || "-"} ${currency}`; +} + +function ensureSelectedGoods() { + const goods = getGoodsList(); + if (!goods.length) { + state.selectedGoodsId = ""; + state.selectedGoods = null; + return; + } + + const matched = + goods.find((item) => String(item.id) === state.selectedGoodsId) || + goods.find((item) => item.type === state.productType) || + goods[0]; + state.selectedGoods = matched; + state.selectedGoodsId = String(matched.id); +} + +function renderProducts() { + const goods = getGoodsList(); + ensureSelectedGoods(); + + if (!goods.length) { + els.productList.innerHTML = '
No amount available.
'; + return; + } + + els.productList.innerHTML = goods + .map((item) => { + const selected = String(item.id) === state.selectedGoodsId; + return ` + + `; + }) + .join(""); + + els.productList.querySelectorAll("[data-goods-id]").forEach((button) => { + button.addEventListener("click", () => { + const goodsId = button.getAttribute("data-goods-id"); + state.selectedGoodsId = goodsId; + state.selectedGoods = + getGoodsList().find((item) => String(item.id) === goodsId) || null; + renderProducts(); + }); + }); +} + +function renderChannels() { + const channels = getChannelList(); + if (!channels.length) { + els.channelList.innerHTML = '
No payment method available.
'; + return; + } + + els.channelList.innerHTML = channels + .map((item) => { + const code = getChannelCode(item); + const pending = state.pendingChannelCode === code; + return ` + + `; + }) + .join(""); + + els.channelList.querySelectorAll("[data-channel-code]").forEach((button) => { + button.addEventListener("click", async () => { + const channelCode = button.getAttribute("data-channel-code"); + try { + await createOrder(channelCode); + } catch (error) { + state.pendingChannelCode = ""; + renderChannels(); + setStatus(extractErrorMessage(error)); + } + }); + }); +} + +function selectCountryByProfile() { + const userCountryCode = state.userProfile?.countryCode; + if (!userCountryCode) { + return false; + } + + const matched = state.countries.find((item) => + getCountryLabel(item).toUpperCase().includes(userCountryCode.toUpperCase()) + ); + + if (!matched) { + return false; + } + + state.selectedCountryId = String(matched.id); + return true; +} + +async function ensureDependencies() { + if (!state.dependencyPromise) { + state.dependencyPromise = Promise.all([ + import("../js/index-CIAVq8iD-1776148658686.js"), + import("../js/appConnector-B5Pbaops-1776148658686.js"), + ]).then(([indexModule, connectorModule]) => { + deps.getApi = indexModule.L; + deps.postApi = indexModule.W; + deps.fetchCurrentUser = indexModule.au; + deps.getCachedUserProfile = indexModule.at; + deps.connectApp = connectorModule.c; + return deps; + }); + } + + return state.dependencyPromise; +} + +async function connectSession() { + await ensureDependencies(); + + const connection = await deps.connectApp(); + if (!connection?.success) { + throw new Error(connection?.error || "Failed to initialize app session"); + } + + const cachedUserProfile = resolveUserProfile(); + if (cachedUserProfile?.id) { + state.userProfile = cachedUserProfile; + return; + } + + const response = await deps.fetchCurrentUser(); + const userProfile = resolveUserProfile(response); + if (!userProfile?.id) { + throw new Error("Please open this page inside the app"); + } + + state.userProfile = userProfile; +} + +async function refreshMeta() { + if (!state.applicationId) { + throw new Error("Missing applicationId"); + } + + persistSettings(); + els.applicationId.value = state.applicationId; + + const countriesResp = await deps.getApi("/order/web/pay/country"); + state.countries = Array.isArray(countriesResp?.body) ? countriesResp.body : []; + if (!state.countries.length) { + state.selectedCountryId = ""; + state.commodityCard = null; + renderProducts(); + renderChannels(); + throw new Error("No supported country"); + } + + if ( + !state.selectedCountryId || + !state.countries.some((item) => String(item.id) === String(state.selectedCountryId)) + ) { + if (!selectCountryByProfile()) { + state.selectedCountryId = String(state.countries[0].id); + } + } + + const commodityResp = await deps.getApi( + `/order/web/pay/commodity?applicationId=${encodeURIComponent( + state.applicationId + )}&payCountryId=${encodeURIComponent(currentCountryId())}&type=${encodeURIComponent( + state.productType + )}` + ); + + state.commodityCard = commodityResp?.body || {}; + renderProducts(); + renderChannels(); + setStatus(""); +} + +function safeDecodeUrl(url) { + if (!url) { + return ""; + } + + try { + return decodeURIComponent(url); + } catch (error) { + return url; + } +} + +async function createOrder(channelCode) { + if (!state.selectedGoods) { + throw new Error("Select an amount first"); + } + + if (!currentUserId()) { + throw new Error("Account not available"); + } + + state.pendingChannelCode = channelCode; + setStatus(""); + renderChannels(); + + const payload = { + applicationId: Number(state.applicationId), + goodsId: Number(state.selectedGoodsId), + payCountryId: Number(currentCountryId()), + userId: Number(currentUserId()), + channelCode, + newVersion: true, + }; + + const response = await deps.postApi("/order/web/pay/recharge", payload); + const requestUrl = safeDecodeUrl(response?.body?.requestUrl || ""); + if (!requestUrl) { + throw new Error("Missing payment url"); + } + + window.location.href = requestUrl; +} + +async function reloadPageData() { + await ensureDependencies(); + await connectSession(); + await refreshMeta(); +} + +async function init() { + renderProducts(); + renderChannels(); + + try { + await reloadPageData(); + } catch (error) { + setStatus(extractErrorMessage(error)); + } +} + +init(); diff --git a/h5/mifa-pay/index.html b/h5/mifa-pay/index.html new file mode 100644 index 0000000..86c94ae --- /dev/null +++ b/h5/mifa-pay/index.html @@ -0,0 +1,249 @@ + + + + + + Recharge + + + +
+

Recharge

+
+
+
+
+ + + + + + diff --git a/h5/mifa-pay/mifapay-logo.png b/h5/mifa-pay/mifapay-logo.png new file mode 100644 index 0000000..eded9ca Binary files /dev/null and b/h5/mifa-pay/mifapay-logo.png differ diff --git a/h5/yumi-invite-agency/index.html b/h5/yumi-invite-agency/index.html new file mode 100644 index 0000000..8a8a08c --- /dev/null +++ b/h5/yumi-invite-agency/index.html @@ -0,0 +1,120 @@ + + + + + + Invite User To Become Agent + + + +
+
+
+
+ +
+

Invite User To Become Agent

+
+
+
English
+
+
+
+ +
+
+
+
+
+ + +
+
+
+ +
+ +
+
please input user id
+
+
+ + + + + + diff --git a/h5/yumi-invite-agency/yumi-invite-agency.css b/h5/yumi-invite-agency/yumi-invite-agency.css new file mode 100644 index 0000000..9d8eff2 --- /dev/null +++ b/h5/yumi-invite-agency/yumi-invite-agency.css @@ -0,0 +1,456 @@ +html { + -webkit-text-size-adjust: 100%; + -webkit-overflow-scrolling: touch; +} + +body { + margin: 0; + min-height: 100%; + font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif; +} + +* { + box-sizing: border-box; +} + +button, +input { + font: inherit; +} + +[hidden] { + display: none !important; +} + +.fullPage { + width: 100vw; + min-height: 100vh; + background-color: #fff; + background-image: url("../assets/secondBg-CSqvFWr0-1776148661447.png"); + background-size: 100% auto; + background-repeat: no-repeat; + position: relative; + color: rgba(0, 0, 0, 0.8); +} + +.fullPage input::placeholder { + font-weight: 700; + color: rgba(0, 0, 0, 0.4); +} + +.invite-btn { + padding: 4px 12px; + border-radius: 32px; + background: linear-gradient(269deg, #feb219 7.04%, #ff9326 96.78%); + color: #fff; + border: none; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; +} + +.invite-btn:disabled { + background: #d1d5db; + cursor: not-allowed; +} + +.no-data { + text-align: center; + padding: 40px 20px; + color: #666; +} + +.user-card { + cursor: default; +} + +.result-action-btn { + padding: 4px 10px; + border-radius: 32px; + background: linear-gradient(269deg, #feb219 7.04%, #ff9326 96.78%); + color: #fff; + border: none; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; + white-space: nowrap; +} + +.result-action-btn:disabled { + background: #d1d5db; + cursor: not-allowed; +} + +.status-bar-placeholder { + display: none; + height: 30px; +} + +.status-bar-placeholder.is-visible { + display: block; +} + +.header-container { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px 16px; + height: 60px; + font-size: 16px; +} + +.side-area, +.actions-area { + min-width: 1.5em; + display: flex; + align-items: center; +} + +.actions-area { + justify-content: flex-end; + gap: 8px; +} + +.header-title { + flex: 1; + margin: 0; + font-size: 1.2em; + font-weight: 600; + text-align: center; + letter-spacing: 0.3px; +} + +.back-btn, +.help-btn { + width: 1.5em; + border: none; + background: transparent; + transition: all 0.2s ease; + padding: 0; +} + +.back-btn:active, +.help-btn:active { + transform: scale(0.9); +} + +.language-entry { + display: flex; + align-items: center; + color: #f59e0b !important; + cursor: pointer; +} + +.language-name { + font-weight: 700; +} + +.gradient-border-wrapper { + position: relative; +} + +.gradient-border-wrapper::before { + content: ""; + position: absolute; + inset: 0; + border-radius: inherit; + padding: var(--gradient-border-width, 1.5px); + background: linear-gradient( + var(--gradient-direction, to bottom), + var(--gradient-start-color, #ffc4c4), + var(--gradient-end-color, #fff6a6) + ); + -webkit-mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; +} + +.gradient-border-content { + position: relative; + border-radius: inherit; + width: 100%; + height: 100%; + background: var( + --gradient-background, + linear-gradient(120deg, rgba(255, 216, 0, 0.1) 39.13%, rgba(255, 149, 0, 0.1) 119.28%) + ); +} + +.modal-overlay { + position: fixed; + inset: 0; + background-color: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; + padding: 20px; +} + +.modal { + background: #fff; + border-radius: 16px; + box-shadow: + 0 20px 25px rgba(0, 0, 0, 0.1), + 0 10px 10px rgba(0, 0, 0, 0.04); + width: 100%; + max-width: 320px; + padding: 24px 20px 20px; + text-align: center; + position: relative; +} + +.modal-icon { + margin-bottom: 16px; + display: flex; + justify-content: center; +} + +.icon-circle { + width: 56px; + height: 56px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + animation: iconPulse 0.6s ease-out; + border: 3px solid; +} + +.icon-symbol { + font-size: 24px; + font-weight: 700; + line-height: 1; +} + +.modal-title { + font-size: 18px; + font-weight: 600; + color: #1f2937; + margin-bottom: 12px; + line-height: 1.4; +} + +.modal-message { + font-size: 14px; + color: #6b7280; + line-height: 1.5; + margin-bottom: 20px; + word-wrap: break-word; +} + +.modal-actions { + display: flex; + justify-content: center; +} + +.modal-btn { + border: none; + border-radius: 8px; + padding: 10px 32px; + font-size: 14px; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; + min-width: 80px; + color: #fff; +} + +.modal-btn:active { + transform: scale(0.98); +} + +.modal-error .icon-circle-error { + background-color: #fee2e2; + border-color: #fecaca; +} + +.modal-error .icon-symbol-error { + color: #dc2626; +} + +.modal-error .modal-btn-error { + background-color: #dc2626; +} + +.modal-warning .icon-circle-warning { + background-color: #fef3c7; + border-color: #fde68a; +} + +.modal-warning .icon-symbol-warning { + color: #d97706; +} + +.modal-warning .modal-btn-warning { + background-color: #d97706; +} + +.modal-success .icon-circle-success { + background-color: #d1fae5; + border-color: #a7f3d0; +} + +.modal-success .icon-symbol-success { + color: #059669; +} + +.modal-success .modal-btn-success { + background-color: #059669; +} + +.modal-info .icon-circle-info { + background-color: #dbeafe; + border-color: #bfdbfe; +} + +.modal-info .icon-symbol-info { + color: #2563eb; +} + +.modal-info .modal-btn-info { + background-color: #2563eb; +} + +.modal-simple .modal-message { + font-size: 15px; + color: #374151; + margin-bottom: 24px; +} + +.modal-simple .modal-btn { + background-color: #2563eb; +} + +.modal-enter { + animation: modalEnter 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); +} + +.modal-leave { + animation: modalLeave 0.2s ease-in; +} + +[dir="rtl"] .flip-img { + transform: scaleX(-1); +} + +[dir="rtl"] .avatar { + margin-right: 0; + margin-left: 12px; +} + +[dir="rtl"] .user-info { + text-align: right; +} + +[dir="rtl"] .language-entry, +[dir="rtl"] .header-title { + direction: rtl; +} + +@keyframes modalEnter { + 0% { + opacity: 0; + transform: scale(0.7) translateY(-10px); + } + + 100% { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +@keyframes modalLeave { + 0% { + opacity: 1; + transform: scale(1) translateY(0); + } + + 100% { + opacity: 0; + transform: scale(0.9) translateY(-5px); + } +} + +@keyframes iconPulse { + 0% { + transform: scale(0.8); + opacity: 0.5; + } + + 50% { + transform: scale(1.1); + } + + 100% { + transform: scale(1); + opacity: 1; + } +} + +@media screen and (max-width: 360px) { + .fullPage { + font-size: 10px; + } + + .header-container { + font-size: 10px; + } +} + +@media screen and (min-width: 360px) { + .fullPage { + font-size: 12px; + } + + .header-container { + font-size: 16px; + } +} + +@media screen and (min-width: 768px) { + .fullPage { + font-size: 24px; + } + + .header-container { + font-size: 24px; + } +} + +@media screen and (min-width: 1024px) { + .fullPage { + font-size: 32px; + } + + .header-container { + font-size: 32px; + } +} + +@media (max-width: 480px) { + .modal { + max-width: 280px; + padding: 20px 16px 16px; + } + + .icon-circle { + width: 48px; + height: 48px; + } + + .icon-symbol { + font-size: 20px; + } + + .modal-title { + font-size: 16px; + } + + .modal-message { + font-size: 13px; + } +} diff --git a/h5/yumi-invite-agency/yumi-invite-agency.js b/h5/yumi-invite-agency/yumi-invite-agency.js new file mode 100644 index 0000000..49c6dad --- /dev/null +++ b/h5/yumi-invite-agency/yumi-invite-agency.js @@ -0,0 +1,694 @@ +(function () { + const API_BASE = "https://jvapi.haiyihy.com"; + const BACK_ICON = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAACE4AAAhOAFFljFgAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAGXSURBVHgB7dvBTcNAFIThB5xTQFwADVBBmqEnUgZQAAXAjRqgACgAj2wfECbJZXZnrPmkVRT5lF9ODn4vVRERERERETEaxnNb8cd+PA/jeZ3PY02xqG7KA+Ic6/eds5vfPxfRdelb4uxXrt0VmXqgU3Hgo8iUA52LA9SvF1yVpkviHOdDpRhIJg6oBZKKA0qB5OKASiDJOKAQSDYO9A4kHQd6BpKPA70CWcSBHoFs4kDrQFZxoGUguzjQKpBlHGgRyDYOsANZxwFmIPs4wAq0iTjACvRUG4gDjEeumDRsIg4wAn3VhjDmYt81jWOGf64vo5q3MsAaHOLDH2oa7q2xicQKhLvopTYQiTl63kQk9mzePlKL5QXrSK22O2wjtVx/sYzUej/ILlKPBSqrSL02zGwi9VzBs4jUe0dRPpLCEqd0JJUtV9lISmvAkpHU9qTlIikukktFUt20vzQSHu++F5HqGvDi3PgIgQ5FpL5p/zme+/l1za7IHP6rcSoSfdPeIRCsRcIPtM18rSUMJoeKiIiIiIjw9QNKh3OJhFQBoAAAAABJRU5ErkJggg=="; + const DEFAULT_AVATAR = "../assets/defaultAvatar-CdxWBK1k-1776148661448.png"; + + const TEXT = { + pageTitle: "Invite User To Become Agent", + language: "English", + placeholder: "Enter User ID", + search: "Search", + invite: "Invite", + idlePrompt: "please input user id", + noUsersFound: "No users found", + information: "Information:", + userIdPrefix: "User ID:", + pending: "Pending", + success: "Success", + cancel: "Cancel", + invitationSubmitted: "Invitation submitted", + applicationCancelled: "Application cancelled", + somethingWentWrong: "Something went wrong", + cancellationFailed: "Cancellation failed, try again", + informationTitle: "Information", + successTitle: "Success", + errorTitle: "Error", + ok: "OK", + }; + + const state = { + query: "", + invitedList: [], + searchResults: [], + showSearchResults: false, + searching: false, + hasSearched: false, + }; + + const dom = { + statusBarPlaceholder: document.getElementById("statusBarPlaceholder"), + backButton: document.getElementById("backButton"), + backButtonIcon: document.getElementById("backButtonIcon"), + pageTitle: document.getElementById("pageTitle"), + languageEntry: document.getElementById("languageEntry"), + languageName: document.getElementById("languageName"), + searchInput: document.getElementById("searchInput"), + clearButton: document.getElementById("clearButton"), + searchButton: document.getElementById("searchButton"), + resultList: document.getElementById("resultList"), + noData: document.getElementById("noData"), + userCardTemplate: document.getElementById("userCardTemplate"), + }; + + init(); + + function init() { + document.title = TEXT.pageTitle; + document.documentElement.lang = "en"; + document.documentElement.dir = resolveDocumentDirection(); + + dom.backButtonIcon.src = BACK_ICON; + dom.pageTitle.textContent = TEXT.pageTitle; + dom.languageName.textContent = TEXT.language; + dom.searchInput.placeholder = TEXT.placeholder; + dom.searchButton.textContent = TEXT.search; + dom.noData.textContent = TEXT.idlePrompt; + + syncStatusBarPlaceholder(); + bindEvents(); + render(); + loadInvitedList(); + } + + function bindEvents() { + dom.backButton.addEventListener("click", handleBack); + dom.languageEntry.addEventListener("click", handleLanguageClick); + dom.searchInput.addEventListener("input", handleSearchInput); + dom.clearButton.addEventListener("click", clearSearch); + dom.searchButton.addEventListener("click", triggerSearchNow); + } + + function resolveDocumentDirection() { + const lang = (new URLSearchParams(window.location.search).get("lang") || "").toLowerCase(); + return lang.startsWith("ar") ? "rtl" : "ltr"; + } + + function syncStatusBarPlaceholder() { + const hasAppBridge = + Boolean(window.app) || + Boolean(window.FlutterPageControl) || + Boolean(window.webkit && window.webkit.messageHandlers); + const isIOS = /iphone|ipad|ipod/i.test(window.navigator.userAgent || ""); + dom.statusBarPlaceholder.classList.toggle("is-visible", hasAppBridge && isIOS); + } + + function handleBack() { + if (window.history.length > 1) { + window.history.back(); + return; + } + + window.location.href = "/"; + } + + function handleLanguageClick() { + window.location.href = "/language"; + } + + function handleSearchInput(event) { + state.query = event.target.value; + dom.clearButton.hidden = !state.query; + state.showSearchResults = false; + state.hasSearched = false; + render(); + } + + function clearSearch() { + state.query = ""; + state.searchResults = []; + state.showSearchResults = false; + state.hasSearched = false; + dom.searchInput.value = ""; + dom.clearButton.hidden = true; + render(); + } + + async function triggerSearchNow() { + if (state.searching) { + return; + } + + const query = state.query.trim(); + + if (!query) { + state.showSearchResults = false; + state.hasSearched = false; + render(); + return; + } + + state.searching = true; + state.showSearchResults = true; + state.hasSearched = true; + state.searchResults = []; + render(); + + try { + const payload = await searchUser(query); + state.searchResults = payload ? [mergeWithInvitationStatus(payload.userProfile)] : []; + } catch (error) { + state.searchResults = []; + showToast(extractErrorMessage(error), { + title: TEXT.errorTitle, + type: "error", + }); + } finally { + state.searching = false; + render(); + } + } + + async function loadInvitedList() { + try { + const response = await request("GET", "/team/bd/invite-message-own"); + state.invitedList = response && response.status && Array.isArray(response.body) ? response.body : []; + state.searchResults = state.searchResults.map(function (item) { + return mergeWithInvitationStatus(item.userProfile || {}); + }); + render(); + } catch (error) { + state.invitedList = []; + render(); + } + } + + async function searchUser(query) { + try { + const response = await request( + "GET", + "/user/user-profile/open-search?account=" + encodeURIComponent(query) + ); + + if (response && response.status && response.body) { + return { userProfile: response.body }; + } + } catch (error) { + if (!/^\d+$/.test(query)) { + throw error; + } + } + + if (!/^\d+$/.test(query)) { + return null; + } + + const fallbackResponse = await request( + "GET", + "/user/user-profile?userId=" + encodeURIComponent(query) + ); + + if (fallbackResponse && fallbackResponse.status && fallbackResponse.body) { + return { userProfile: fallbackResponse.body }; + } + + return null; + } + + function mergeWithInvitationStatus(userProfile) { + const matchedInvitation = state.invitedList.find(function (item) { + return ( + item && + item.userProfile && + userProfile && + item.userProfile.id === userProfile.id + ); + }); + + return Object.assign({}, matchedInvitation || {}, { + userProfile: userProfile || {}, + }); + } + + async function inviteUser(item) { + if (!item || !item.userProfile || !item.userProfile.id) { + return; + } + + try { + const response = await request("POST", "/team/bd/invite-agent", { + body: { + inviteUserId: item.userProfile.id, + }, + }); + + if (response && response.status) { + showToast(TEXT.invitationSubmitted, { + title: TEXT.successTitle, + type: "success", + }); + await loadInvitedList(); + } + } catch (error) { + showToast(extractErrorMessage(error, TEXT.somethingWentWrong), { + title: TEXT.errorTitle, + type: "error", + }); + } + } + + async function cancelInvitation(invitationId) { + try { + const response = await request("POST", "/team/bd/invite-agent-cancel/" + invitationId); + + if (response && response.status) { + showToast(TEXT.applicationCancelled, { + title: TEXT.informationTitle, + type: "info", + }); + await loadInvitedList(); + } + } catch (error) { + showToast(extractErrorMessage(error, TEXT.cancellationFailed), { + title: TEXT.errorTitle, + type: "error", + }); + } + } + + function render() { + renderSearchButton(); + renderList(); + } + + function renderSearchButton() { + dom.searchButton.disabled = !state.query.trim() || state.searching; + } + + function renderList() { + const currentList = state.showSearchResults ? state.searchResults : state.invitedList; + dom.resultList.innerHTML = ""; + + currentList.forEach(function (item) { + dom.resultList.appendChild(createUserCard(item, state.showSearchResults ? "search" : "invited")); + }); + + if (currentList.length > 0 || (state.searching && state.showSearchResults)) { + dom.noData.hidden = true; + return; + } + + dom.noData.hidden = false; + dom.noData.textContent = + state.showSearchResults && state.hasSearched ? TEXT.noUsersFound : TEXT.idlePrompt; + } + + function createUserCard(item, mode) { + const fragment = dom.userCardTemplate.content.cloneNode(true); + const statusBadge = fragment.querySelector(".status-badge"); + const avatarImage = fragment.querySelector(".avatar-image"); + const nickname = fragment.querySelector(".nickname"); + const account = fragment.querySelector(".account"); + const pendingActions = fragment.querySelector(".pending-actions"); + const userProfile = item.userProfile || {}; + + avatarImage.src = userProfile.userAvatar || DEFAULT_AVATAR; + avatarImage.alt = userProfile.userNickname || ""; + avatarImage.onerror = handleAvatarError; + nickname.textContent = userProfile.userNickname || ""; + account.textContent = TEXT.userIdPrefix + " " + (userProfile.account || userProfile.id || ""); + + if (item.status === 0) { + statusBadge.innerHTML = + '
' + + TEXT.pending + + "
"; + } else if (item.status === 1) { + statusBadge.innerHTML = + '
' + + TEXT.success + + "
"; + } + + if (mode === "search") { + const inviteButton = document.createElement("button"); + inviteButton.type = "button"; + inviteButton.className = "result-action-btn"; + inviteButton.textContent = TEXT.invite; + inviteButton.disabled = item.status === 0 || item.status === 1; + inviteButton.addEventListener("click", function (event) { + event.stopPropagation(); + inviteUser(item); + }); + pendingActions.appendChild(inviteButton); + } else if (item.status === 0) { + const cancelButton = document.createElement("div"); + cancelButton.textContent = TEXT.cancel; + cancelButton.style.padding = "4px 8px"; + cancelButton.style.borderRadius = "32px"; + cancelButton.style.backgroundColor = "red"; + cancelButton.style.color = "white"; + cancelButton.style.fontWeight = "600"; + cancelButton.style.zIndex = "5"; + cancelButton.addEventListener("click", function (event) { + event.stopPropagation(); + cancelInvitation(item.id); + }); + pendingActions.appendChild(cancelButton); + } + + return fragment; + } + + function handleAvatarError(event) { + event.target.onerror = null; + event.target.src = DEFAULT_AVATAR; + } + + function extractErrorMessage(error, fallback) { + return ( + (error && + error.response && + (error.response.errorMsg || error.response.message || error.response.error)) || + (error && error.message) || + fallback || + TEXT.somethingWentWrong + ); + } + + function showToast(message, options) { + const config = Object.assign( + { + title: TEXT.informationTitle, + type: "info", + confirmText: TEXT.ok, + }, + options || {} + ); + + const iconMap = { + error: "✕", + warning: "⚠", + success: "✓", + info: "i", + simple: "", + }; + + const overlay = document.createElement("div"); + overlay.className = "modal-overlay"; + overlay.innerHTML = + '"; + + const modal = overlay.querySelector(".modal"); + const closeButton = overlay.querySelector(".modal-btn"); + const close = function () { + modal.classList.remove("modal-enter"); + modal.classList.add("modal-leave"); + window.setTimeout(function () { + overlay.remove(); + }, 200); + }; + + closeButton.addEventListener("click", close); + overlay.addEventListener("click", function (event) { + if (event.target === overlay) { + close(); + } + }); + + document.body.appendChild(overlay); + } + + function escapeHtml(value) { + return String(value) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + } + + async function request(method, path, options) { + const config = options || {}; + const headers = buildRequestHeaders(Boolean(config.body), config.headers || {}); + const response = await fetch(API_BASE + path, { + method: method, + credentials: "include", + headers: headers, + body: config.body ? JSON.stringify(config.body) : undefined, + }); + const text = await response.text(); + let payload = {}; + + if (text) { + try { + payload = JSON.parse(text); + } catch (error) { + payload = { + status: response.ok, + body: text, + }; + } + } + + if (!response.ok || payload.status === false) { + const requestError = new Error( + payload.errorMsg || payload.message || response.statusText || TEXT.somethingWentWrong + ); + requestError.response = payload; + throw requestError; + } + + return payload; + } + + function buildRequestHeaders(hasBody, overrideHeaders) { + const discovered = discoverAppHeaders(); + const headers = Object.assign({}, overrideHeaders || {}); + + if (hasBody) { + headers["Content-Type"] = headers["Content-Type"] || "application/json"; + } + + headers.Accept = headers.Accept || "application/json, text/plain, */*"; + + if (discovered.authorization) { + headers.Authorization = headers.Authorization || discovered.authorization; + } + + if (discovered.reqLang) { + headers["Req-Lang"] = headers["Req-Lang"] || discovered.reqLang; + } + + if (discovered.appVersion) { + headers["App-Version"] = headers["App-Version"] || discovered.appVersion; + } + + if (discovered.buildVersion) { + headers["Build-Version"] = headers["Build-Version"] || discovered.buildVersion; + } + + if (discovered.appChannel) { + headers["App-Channel"] = headers["App-Channel"] || discovered.appChannel; + } + + if (discovered.reqImei) { + headers["Req-Imei"] = headers["Req-Imei"] || discovered.reqImei; + } + + if (discovered.sysOrigin) { + headers["Sys-Origin"] = headers["Sys-Origin"] || discovered.sysOrigin; + } + + if (discovered.sysOriginChild) { + headers["Sys-Origin-Child"] = + headers["Sys-Origin-Child"] || discovered.sysOriginChild; + } + + return headers; + } + + function discoverAppHeaders() { + const query = new URLSearchParams(window.location.search); + const collected = []; + + collectCandidate(collected, window.__APP_HEADERS__); + collectCandidate(collected, window.__HEADER_INFO__); + collectCandidate(collected, window.headerInfo); + collectCandidate(collected, parseHeaderObjectFromQuery(query)); + + collectFromStorage(collected, window.localStorage); + collectFromStorage(collected, window.sessionStorage); + + return collected.reduce(function (result, candidate) { + return Object.assign(result, candidate); + }, { + reqLang: query.get("lang") || document.documentElement.lang || "en", + }); + } + + function parseHeaderObjectFromQuery(query) { + const candidate = {}; + mapHeaderValue(candidate, "authorization", query.get("authorization")); + mapHeaderValue(candidate, "authorization", query.get("token")); + mapHeaderValue(candidate, "reqLang", query.get("lang")); + mapHeaderValue(candidate, "appVersion", query.get("appVersion")); + mapHeaderValue(candidate, "buildVersion", query.get("buildVersion")); + mapHeaderValue(candidate, "appChannel", query.get("appChannel")); + mapHeaderValue(candidate, "reqImei", query.get("reqImei")); + mapHeaderValue(candidate, "sysOrigin", query.get("sysOrigin")); + mapHeaderValue(candidate, "sysOriginChild", query.get("sysOriginChild")); + return candidate; + } + + function collectFromStorage(collected, storage) { + if (!storage) { + return; + } + + const preferredKeys = [ + "authorization", + "Authorization", + "token", + "accessToken", + "headerInfo", + "headersFromApp", + "appHeaders", + "persist:root", + "user", + "userInfo", + ]; + + preferredKeys.forEach(function (key) { + try { + collectCandidate(collected, storage.getItem(key)); + } catch (error) { + return; + } + }); + + for (let index = 0; index < storage.length; index += 1) { + try { + const key = storage.key(index); + collectCandidate(collected, storage.getItem(key)); + } catch (error) { + return; + } + } + } + + function collectCandidate(collected, value) { + const normalized = normalizeHeaderCandidate(value, 0); + if (normalized && Object.keys(normalized).length > 0) { + collected.push(normalized); + } + } + + function normalizeHeaderCandidate(value, depth) { + if (depth > 4 || value == null) { + return null; + } + + if (typeof value === "string") { + const trimmed = value.trim(); + + if (!trimmed) { + return null; + } + + if (trimmed[0] === "{" || trimmed[0] === "[") { + try { + return normalizeHeaderCandidate(JSON.parse(trimmed), depth + 1); + } catch (error) { + return null; + } + } + + if (/bearer\s+/i.test(trimmed)) { + return { + authorization: trimmed, + }; + } + + return null; + } + + if (Array.isArray(value)) { + return value.reduce(function (result, entry) { + const normalized = normalizeHeaderCandidate(entry, depth + 1); + return normalized ? Object.assign(result, normalized) : result; + }, {}); + } + + if (typeof value !== "object") { + return null; + } + + const normalized = {}; + const aliasMap = { + authorization: ["authorization", "Authorization", "token", "accessToken"], + reqLang: ["reqLang", "Req-Lang", "lang", "language"], + appVersion: ["appVersion", "App-Version"], + buildVersion: ["buildVersion", "Build-Version"], + appChannel: ["appChannel", "App-Channel"], + reqImei: ["reqImei", "Req-Imei"], + sysOrigin: ["sysOrigin", "Sys-Origin"], + sysOriginChild: ["sysOriginChild", "Sys-Origin-Child"], + }; + + Object.keys(aliasMap).forEach(function (targetKey) { + aliasMap[targetKey].forEach(function (sourceKey) { + mapHeaderValue(normalized, targetKey, value[sourceKey]); + }); + }); + + ["headerInfo", "headers", "auth", "data", "state"].forEach(function (nestedKey) { + const nestedValue = normalizeHeaderCandidate(value[nestedKey], depth + 1); + if (nestedValue) { + Object.assign(normalized, nestedValue); + } + }); + + Object.keys(value).forEach(function (key) { + if ( + key === "headerInfo" || + key === "headers" || + key === "auth" || + key === "data" || + key === "state" + ) { + return; + } + + if (typeof value[key] === "object") { + const nestedValue = normalizeHeaderCandidate(value[key], depth + 1); + if (nestedValue) { + Object.assign(normalized, nestedValue); + } + } + }); + + return normalized; + } + + function mapHeaderValue(target, key, value) { + if (value == null || value === "" || target[key]) { + return; + } + + target[key] = String(value); + } +})(); diff --git a/h5/yumi-invite-bd-leader/index.html b/h5/yumi-invite-bd-leader/index.html new file mode 100644 index 0000000..96b0a2c --- /dev/null +++ b/h5/yumi-invite-bd-leader/index.html @@ -0,0 +1,119 @@ + + + + + + Invite User To Become BD Leader + + + +
+
+
+
+ +
+

Invite User To Become BD Leader

+
+
+
English
+
+
+
+ +
+
+
+
+
+ + +
+
+
+ +
+ +
+
please input user id
+
+
+ + + + + + diff --git a/h5/yumi-invite-bd-leader/yumi-invite-bd-leader.css b/h5/yumi-invite-bd-leader/yumi-invite-bd-leader.css new file mode 100644 index 0000000..9d8eff2 --- /dev/null +++ b/h5/yumi-invite-bd-leader/yumi-invite-bd-leader.css @@ -0,0 +1,456 @@ +html { + -webkit-text-size-adjust: 100%; + -webkit-overflow-scrolling: touch; +} + +body { + margin: 0; + min-height: 100%; + font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif; +} + +* { + box-sizing: border-box; +} + +button, +input { + font: inherit; +} + +[hidden] { + display: none !important; +} + +.fullPage { + width: 100vw; + min-height: 100vh; + background-color: #fff; + background-image: url("../assets/secondBg-CSqvFWr0-1776148661447.png"); + background-size: 100% auto; + background-repeat: no-repeat; + position: relative; + color: rgba(0, 0, 0, 0.8); +} + +.fullPage input::placeholder { + font-weight: 700; + color: rgba(0, 0, 0, 0.4); +} + +.invite-btn { + padding: 4px 12px; + border-radius: 32px; + background: linear-gradient(269deg, #feb219 7.04%, #ff9326 96.78%); + color: #fff; + border: none; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; +} + +.invite-btn:disabled { + background: #d1d5db; + cursor: not-allowed; +} + +.no-data { + text-align: center; + padding: 40px 20px; + color: #666; +} + +.user-card { + cursor: default; +} + +.result-action-btn { + padding: 4px 10px; + border-radius: 32px; + background: linear-gradient(269deg, #feb219 7.04%, #ff9326 96.78%); + color: #fff; + border: none; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; + white-space: nowrap; +} + +.result-action-btn:disabled { + background: #d1d5db; + cursor: not-allowed; +} + +.status-bar-placeholder { + display: none; + height: 30px; +} + +.status-bar-placeholder.is-visible { + display: block; +} + +.header-container { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px 16px; + height: 60px; + font-size: 16px; +} + +.side-area, +.actions-area { + min-width: 1.5em; + display: flex; + align-items: center; +} + +.actions-area { + justify-content: flex-end; + gap: 8px; +} + +.header-title { + flex: 1; + margin: 0; + font-size: 1.2em; + font-weight: 600; + text-align: center; + letter-spacing: 0.3px; +} + +.back-btn, +.help-btn { + width: 1.5em; + border: none; + background: transparent; + transition: all 0.2s ease; + padding: 0; +} + +.back-btn:active, +.help-btn:active { + transform: scale(0.9); +} + +.language-entry { + display: flex; + align-items: center; + color: #f59e0b !important; + cursor: pointer; +} + +.language-name { + font-weight: 700; +} + +.gradient-border-wrapper { + position: relative; +} + +.gradient-border-wrapper::before { + content: ""; + position: absolute; + inset: 0; + border-radius: inherit; + padding: var(--gradient-border-width, 1.5px); + background: linear-gradient( + var(--gradient-direction, to bottom), + var(--gradient-start-color, #ffc4c4), + var(--gradient-end-color, #fff6a6) + ); + -webkit-mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; +} + +.gradient-border-content { + position: relative; + border-radius: inherit; + width: 100%; + height: 100%; + background: var( + --gradient-background, + linear-gradient(120deg, rgba(255, 216, 0, 0.1) 39.13%, rgba(255, 149, 0, 0.1) 119.28%) + ); +} + +.modal-overlay { + position: fixed; + inset: 0; + background-color: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; + padding: 20px; +} + +.modal { + background: #fff; + border-radius: 16px; + box-shadow: + 0 20px 25px rgba(0, 0, 0, 0.1), + 0 10px 10px rgba(0, 0, 0, 0.04); + width: 100%; + max-width: 320px; + padding: 24px 20px 20px; + text-align: center; + position: relative; +} + +.modal-icon { + margin-bottom: 16px; + display: flex; + justify-content: center; +} + +.icon-circle { + width: 56px; + height: 56px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + animation: iconPulse 0.6s ease-out; + border: 3px solid; +} + +.icon-symbol { + font-size: 24px; + font-weight: 700; + line-height: 1; +} + +.modal-title { + font-size: 18px; + font-weight: 600; + color: #1f2937; + margin-bottom: 12px; + line-height: 1.4; +} + +.modal-message { + font-size: 14px; + color: #6b7280; + line-height: 1.5; + margin-bottom: 20px; + word-wrap: break-word; +} + +.modal-actions { + display: flex; + justify-content: center; +} + +.modal-btn { + border: none; + border-radius: 8px; + padding: 10px 32px; + font-size: 14px; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; + min-width: 80px; + color: #fff; +} + +.modal-btn:active { + transform: scale(0.98); +} + +.modal-error .icon-circle-error { + background-color: #fee2e2; + border-color: #fecaca; +} + +.modal-error .icon-symbol-error { + color: #dc2626; +} + +.modal-error .modal-btn-error { + background-color: #dc2626; +} + +.modal-warning .icon-circle-warning { + background-color: #fef3c7; + border-color: #fde68a; +} + +.modal-warning .icon-symbol-warning { + color: #d97706; +} + +.modal-warning .modal-btn-warning { + background-color: #d97706; +} + +.modal-success .icon-circle-success { + background-color: #d1fae5; + border-color: #a7f3d0; +} + +.modal-success .icon-symbol-success { + color: #059669; +} + +.modal-success .modal-btn-success { + background-color: #059669; +} + +.modal-info .icon-circle-info { + background-color: #dbeafe; + border-color: #bfdbfe; +} + +.modal-info .icon-symbol-info { + color: #2563eb; +} + +.modal-info .modal-btn-info { + background-color: #2563eb; +} + +.modal-simple .modal-message { + font-size: 15px; + color: #374151; + margin-bottom: 24px; +} + +.modal-simple .modal-btn { + background-color: #2563eb; +} + +.modal-enter { + animation: modalEnter 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); +} + +.modal-leave { + animation: modalLeave 0.2s ease-in; +} + +[dir="rtl"] .flip-img { + transform: scaleX(-1); +} + +[dir="rtl"] .avatar { + margin-right: 0; + margin-left: 12px; +} + +[dir="rtl"] .user-info { + text-align: right; +} + +[dir="rtl"] .language-entry, +[dir="rtl"] .header-title { + direction: rtl; +} + +@keyframes modalEnter { + 0% { + opacity: 0; + transform: scale(0.7) translateY(-10px); + } + + 100% { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +@keyframes modalLeave { + 0% { + opacity: 1; + transform: scale(1) translateY(0); + } + + 100% { + opacity: 0; + transform: scale(0.9) translateY(-5px); + } +} + +@keyframes iconPulse { + 0% { + transform: scale(0.8); + opacity: 0.5; + } + + 50% { + transform: scale(1.1); + } + + 100% { + transform: scale(1); + opacity: 1; + } +} + +@media screen and (max-width: 360px) { + .fullPage { + font-size: 10px; + } + + .header-container { + font-size: 10px; + } +} + +@media screen and (min-width: 360px) { + .fullPage { + font-size: 12px; + } + + .header-container { + font-size: 16px; + } +} + +@media screen and (min-width: 768px) { + .fullPage { + font-size: 24px; + } + + .header-container { + font-size: 24px; + } +} + +@media screen and (min-width: 1024px) { + .fullPage { + font-size: 32px; + } + + .header-container { + font-size: 32px; + } +} + +@media (max-width: 480px) { + .modal { + max-width: 280px; + padding: 20px 16px 16px; + } + + .icon-circle { + width: 48px; + height: 48px; + } + + .icon-symbol { + font-size: 20px; + } + + .modal-title { + font-size: 16px; + } + + .modal-message { + font-size: 13px; + } +} diff --git a/h5/yumi-invite-bd-leader/yumi-invite-bd-leader.js b/h5/yumi-invite-bd-leader/yumi-invite-bd-leader.js new file mode 100644 index 0000000..8437fba --- /dev/null +++ b/h5/yumi-invite-bd-leader/yumi-invite-bd-leader.js @@ -0,0 +1,699 @@ +(function () { + const API_BASE = "https://jvapi.haiyihy.com"; + const BACK_ICON = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAACE4AAAhOAFFljFgAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAGXSURBVHgB7dvBTcNAFIThB5xTQFwADVBBmqEnUgZQAAXAjRqgACgAj2wfECbJZXZnrPmkVRT5lF9ODn4vVRERERERETEaxnNb8cd+PA/jeZ3PY02xqG7KA+Ic6/eds5vfPxfRdelb4uxXrt0VmXqgU3Hgo8iUA52LA9SvF1yVpkviHOdDpRhIJg6oBZKKA0qB5OKASiDJOKAQSDYO9A4kHQd6BpKPA70CWcSBHoFs4kDrQFZxoGUguzjQKpBlHGgRyDYOsANZxwFmIPs4wAq0iTjACvRUG4gDjEeumDRsIg4wAn3VhjDmYt81jWOGf64vo5q3MsAaHOLDH2oa7q2xicQKhLvopTYQiTl63kQk9mzePlKL5QXrSK22O2wjtVx/sYzUej/ILlKPBSqrSL02zGwi9VzBs4jUe0dRPpLCEqd0JJUtV9lISmvAkpHU9qTlIikukktFUt20vzQSHu++F5HqGvDi3PgIgQ5FpL5p/zme+/l1za7IHP6rcSoSfdPeIRCsRcIPtM18rSUMJoeKiIiIiIjw9QNKh3OJhFQBoAAAAABJRU5ErkJggg=="; + const DEFAULT_AVATAR = "../assets/defaultAvatar-CdxWBK1k-1776148661448.png"; + const ENDPOINTS = { + invitedList: "/team/bd/invite-bdleader-message-own", + invite: "/team/bd/invite-bdleader", + cancelPrefix: "/team/bd/invite-bdleader-cancel/", + }; + + const TEXT = { + pageTitle: "Invite User To Become BD Leader", + language: "English", + placeholder: "Enter User ID", + search: "Search", + invite: "Invite", + idlePrompt: "please input user id", + noUsersFound: "No users found", + information: "Information:", + userIdPrefix: "User ID:", + pending: "Pending", + success: "Success", + cancel: "Cancel", + invitationSubmitted: "Invitation submitted", + applicationCancelled: "Application cancelled", + somethingWentWrong: "Something went wrong", + cancellationFailed: "Cancellation failed, try again", + informationTitle: "Information", + successTitle: "Success", + errorTitle: "Error", + ok: "OK", + }; + + const state = { + query: "", + invitedList: [], + searchResults: [], + showSearchResults: false, + searching: false, + hasSearched: false, + }; + + const dom = { + statusBarPlaceholder: document.getElementById("statusBarPlaceholder"), + backButton: document.getElementById("backButton"), + backButtonIcon: document.getElementById("backButtonIcon"), + pageTitle: document.getElementById("pageTitle"), + languageEntry: document.getElementById("languageEntry"), + languageName: document.getElementById("languageName"), + searchInput: document.getElementById("searchInput"), + clearButton: document.getElementById("clearButton"), + searchButton: document.getElementById("searchButton"), + resultList: document.getElementById("resultList"), + noData: document.getElementById("noData"), + userCardTemplate: document.getElementById("userCardTemplate"), + }; + + init(); + + function init() { + document.title = TEXT.pageTitle; + document.documentElement.lang = "en"; + document.documentElement.dir = resolveDocumentDirection(); + + dom.backButtonIcon.src = BACK_ICON; + dom.pageTitle.textContent = TEXT.pageTitle; + dom.languageName.textContent = TEXT.language; + dom.searchInput.placeholder = TEXT.placeholder; + dom.searchButton.textContent = TEXT.search; + dom.noData.textContent = TEXT.idlePrompt; + + syncStatusBarPlaceholder(); + bindEvents(); + render(); + loadInvitedList(); + } + + function bindEvents() { + dom.backButton.addEventListener("click", handleBack); + dom.languageEntry.addEventListener("click", handleLanguageClick); + dom.searchInput.addEventListener("input", handleSearchInput); + dom.clearButton.addEventListener("click", clearSearch); + dom.searchButton.addEventListener("click", triggerSearchNow); + } + + function resolveDocumentDirection() { + const lang = (new URLSearchParams(window.location.search).get("lang") || "").toLowerCase(); + return lang.startsWith("ar") ? "rtl" : "ltr"; + } + + function syncStatusBarPlaceholder() { + const hasAppBridge = + Boolean(window.app) || + Boolean(window.FlutterPageControl) || + Boolean(window.webkit && window.webkit.messageHandlers); + const isIOS = /iphone|ipad|ipod/i.test(window.navigator.userAgent || ""); + dom.statusBarPlaceholder.classList.toggle("is-visible", hasAppBridge && isIOS); + } + + function handleBack() { + if (window.history.length > 1) { + window.history.back(); + return; + } + + window.location.href = "/"; + } + + function handleLanguageClick() { + window.location.href = "/language"; + } + + function handleSearchInput(event) { + state.query = event.target.value; + dom.clearButton.hidden = !state.query; + state.showSearchResults = false; + state.hasSearched = false; + render(); + } + + function clearSearch() { + state.query = ""; + state.searchResults = []; + state.showSearchResults = false; + state.hasSearched = false; + dom.searchInput.value = ""; + dom.clearButton.hidden = true; + render(); + } + + async function triggerSearchNow() { + if (state.searching) { + return; + } + + const query = state.query.trim(); + + if (!query) { + state.showSearchResults = false; + state.hasSearched = false; + render(); + return; + } + + state.searching = true; + state.showSearchResults = true; + state.hasSearched = true; + state.searchResults = []; + render(); + + try { + const payload = await searchUser(query); + state.searchResults = payload ? [mergeWithInvitationStatus(payload.userProfile)] : []; + } catch (error) { + state.searchResults = []; + showToast(extractErrorMessage(error), { + title: TEXT.errorTitle, + type: "error", + }); + } finally { + state.searching = false; + render(); + } + } + + async function loadInvitedList() { + try { + const response = await request("GET", ENDPOINTS.invitedList); + state.invitedList = response && response.status && Array.isArray(response.body) ? response.body : []; + state.searchResults = state.searchResults.map(function (item) { + return mergeWithInvitationStatus(item.userProfile || {}); + }); + render(); + } catch (error) { + state.invitedList = []; + render(); + } + } + + async function searchUser(query) { + try { + const response = await request( + "GET", + "/user/user-profile/open-search?account=" + encodeURIComponent(query) + ); + + if (response && response.status && response.body) { + return { userProfile: response.body }; + } + } catch (error) { + if (!/^\d+$/.test(query)) { + throw error; + } + } + + if (!/^\d+$/.test(query)) { + return null; + } + + const fallbackResponse = await request( + "GET", + "/user/user-profile?userId=" + encodeURIComponent(query) + ); + + if (fallbackResponse && fallbackResponse.status && fallbackResponse.body) { + return { userProfile: fallbackResponse.body }; + } + + return null; + } + + function mergeWithInvitationStatus(userProfile) { + const matchedInvitation = state.invitedList.find(function (item) { + return ( + item && + item.userProfile && + userProfile && + item.userProfile.id === userProfile.id + ); + }); + + return Object.assign({}, matchedInvitation || {}, { + userProfile: userProfile || {}, + }); + } + + async function inviteUser(item) { + if (!item || !item.userProfile || !item.userProfile.id) { + return; + } + + try { + const response = await request("POST", ENDPOINTS.invite, { + body: { + inviteUserId: item.userProfile.id, + }, + }); + + if (response && response.status) { + showToast(TEXT.invitationSubmitted, { + title: TEXT.successTitle, + type: "success", + }); + await loadInvitedList(); + } + } catch (error) { + showToast(extractErrorMessage(error, TEXT.somethingWentWrong), { + title: TEXT.errorTitle, + type: "error", + }); + } + } + + async function cancelInvitation(invitationId) { + try { + const response = await request("POST", ENDPOINTS.cancelPrefix + invitationId); + + if (response && response.status) { + showToast(TEXT.applicationCancelled, { + title: TEXT.informationTitle, + type: "info", + }); + await loadInvitedList(); + } + } catch (error) { + showToast(extractErrorMessage(error, TEXT.cancellationFailed), { + title: TEXT.errorTitle, + type: "error", + }); + } + } + + function render() { + renderSearchButton(); + renderList(); + } + + function renderSearchButton() { + dom.searchButton.disabled = !state.query.trim() || state.searching; + } + + function renderList() { + const currentList = state.showSearchResults ? state.searchResults : state.invitedList; + dom.resultList.innerHTML = ""; + + currentList.forEach(function (item) { + dom.resultList.appendChild(createUserCard(item, state.showSearchResults ? "search" : "invited")); + }); + + if (currentList.length > 0 || (state.searching && state.showSearchResults)) { + dom.noData.hidden = true; + return; + } + + dom.noData.hidden = false; + dom.noData.textContent = + state.showSearchResults && state.hasSearched ? TEXT.noUsersFound : TEXT.idlePrompt; + } + + function createUserCard(item, mode) { + const fragment = dom.userCardTemplate.content.cloneNode(true); + const statusBadge = fragment.querySelector(".status-badge"); + const avatarImage = fragment.querySelector(".avatar-image"); + const nickname = fragment.querySelector(".nickname"); + const account = fragment.querySelector(".account"); + const pendingActions = fragment.querySelector(".pending-actions"); + const userProfile = item.userProfile || {}; + + avatarImage.src = userProfile.userAvatar || DEFAULT_AVATAR; + avatarImage.alt = userProfile.userNickname || ""; + avatarImage.onerror = handleAvatarError; + nickname.textContent = userProfile.userNickname || ""; + account.textContent = TEXT.userIdPrefix + " " + (userProfile.account || userProfile.id || ""); + + if (item.status === 0) { + statusBadge.innerHTML = + '
' + + TEXT.pending + + "
"; + } else if (item.status === 1) { + statusBadge.innerHTML = + '
' + + TEXT.success + + "
"; + } + + if (mode === "search") { + const inviteButton = document.createElement("button"); + inviteButton.type = "button"; + inviteButton.className = "result-action-btn"; + inviteButton.textContent = TEXT.invite; + inviteButton.disabled = item.status === 0 || item.status === 1; + inviteButton.addEventListener("click", function (event) { + event.stopPropagation(); + inviteUser(item); + }); + pendingActions.appendChild(inviteButton); + } else if (item.status === 0) { + const cancelButton = document.createElement("div"); + cancelButton.textContent = TEXT.cancel; + cancelButton.style.padding = "4px 8px"; + cancelButton.style.borderRadius = "32px"; + cancelButton.style.backgroundColor = "red"; + cancelButton.style.color = "white"; + cancelButton.style.fontWeight = "600"; + cancelButton.style.zIndex = "5"; + cancelButton.addEventListener("click", function (event) { + event.stopPropagation(); + cancelInvitation(item.id); + }); + pendingActions.appendChild(cancelButton); + } + + return fragment; + } + + function handleAvatarError(event) { + event.target.onerror = null; + event.target.src = DEFAULT_AVATAR; + } + + function extractErrorMessage(error, fallback) { + return ( + (error && + error.response && + (error.response.errorMsg || error.response.message || error.response.error)) || + (error && error.message) || + fallback || + TEXT.somethingWentWrong + ); + } + + function showToast(message, options) { + const config = Object.assign( + { + title: TEXT.informationTitle, + type: "info", + confirmText: TEXT.ok, + }, + options || {} + ); + + const iconMap = { + error: "✕", + warning: "⚠", + success: "✓", + info: "i", + simple: "", + }; + + const overlay = document.createElement("div"); + overlay.className = "modal-overlay"; + overlay.innerHTML = + '"; + + const modal = overlay.querySelector(".modal"); + const closeButton = overlay.querySelector(".modal-btn"); + const close = function () { + modal.classList.remove("modal-enter"); + modal.classList.add("modal-leave"); + window.setTimeout(function () { + overlay.remove(); + }, 200); + }; + + closeButton.addEventListener("click", close); + overlay.addEventListener("click", function (event) { + if (event.target === overlay) { + close(); + } + }); + + document.body.appendChild(overlay); + } + + function escapeHtml(value) { + return String(value) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + } + + async function request(method, path, options) { + const config = options || {}; + const headers = buildRequestHeaders(Boolean(config.body), config.headers || {}); + const response = await fetch(API_BASE + path, { + method: method, + credentials: "include", + headers: headers, + body: config.body ? JSON.stringify(config.body) : undefined, + }); + const text = await response.text(); + let payload = {}; + + if (text) { + try { + payload = JSON.parse(text); + } catch (error) { + payload = { + status: response.ok, + body: text, + }; + } + } + + if (!response.ok || payload.status === false) { + const requestError = new Error( + payload.errorMsg || payload.message || response.statusText || TEXT.somethingWentWrong + ); + requestError.response = payload; + throw requestError; + } + + return payload; + } + + function buildRequestHeaders(hasBody, overrideHeaders) { + const discovered = discoverAppHeaders(); + const headers = Object.assign({}, overrideHeaders || {}); + + if (hasBody) { + headers["Content-Type"] = headers["Content-Type"] || "application/json"; + } + + headers.Accept = headers.Accept || "application/json, text/plain, */*"; + + if (discovered.authorization) { + headers.Authorization = headers.Authorization || discovered.authorization; + } + + if (discovered.reqLang) { + headers["Req-Lang"] = headers["Req-Lang"] || discovered.reqLang; + } + + if (discovered.appVersion) { + headers["App-Version"] = headers["App-Version"] || discovered.appVersion; + } + + if (discovered.buildVersion) { + headers["Build-Version"] = headers["Build-Version"] || discovered.buildVersion; + } + + if (discovered.appChannel) { + headers["App-Channel"] = headers["App-Channel"] || discovered.appChannel; + } + + if (discovered.reqImei) { + headers["Req-Imei"] = headers["Req-Imei"] || discovered.reqImei; + } + + if (discovered.sysOrigin) { + headers["Sys-Origin"] = headers["Sys-Origin"] || discovered.sysOrigin; + } + + if (discovered.sysOriginChild) { + headers["Sys-Origin-Child"] = + headers["Sys-Origin-Child"] || discovered.sysOriginChild; + } + + return headers; + } + + function discoverAppHeaders() { + const query = new URLSearchParams(window.location.search); + const collected = []; + + collectCandidate(collected, window.__APP_HEADERS__); + collectCandidate(collected, window.__HEADER_INFO__); + collectCandidate(collected, window.headerInfo); + collectCandidate(collected, parseHeaderObjectFromQuery(query)); + + collectFromStorage(collected, window.localStorage); + collectFromStorage(collected, window.sessionStorage); + + return collected.reduce(function (result, candidate) { + return Object.assign(result, candidate); + }, { + reqLang: query.get("lang") || document.documentElement.lang || "en", + }); + } + + function parseHeaderObjectFromQuery(query) { + const candidate = {}; + mapHeaderValue(candidate, "authorization", query.get("authorization")); + mapHeaderValue(candidate, "authorization", query.get("token")); + mapHeaderValue(candidate, "reqLang", query.get("lang")); + mapHeaderValue(candidate, "appVersion", query.get("appVersion")); + mapHeaderValue(candidate, "buildVersion", query.get("buildVersion")); + mapHeaderValue(candidate, "appChannel", query.get("appChannel")); + mapHeaderValue(candidate, "reqImei", query.get("reqImei")); + mapHeaderValue(candidate, "sysOrigin", query.get("sysOrigin")); + mapHeaderValue(candidate, "sysOriginChild", query.get("sysOriginChild")); + return candidate; + } + + function collectFromStorage(collected, storage) { + if (!storage) { + return; + } + + const preferredKeys = [ + "authorization", + "Authorization", + "token", + "accessToken", + "headerInfo", + "headersFromApp", + "appHeaders", + "persist:root", + "user", + "userInfo", + ]; + + preferredKeys.forEach(function (key) { + try { + collectCandidate(collected, storage.getItem(key)); + } catch (error) { + return; + } + }); + + for (let index = 0; index < storage.length; index += 1) { + try { + const key = storage.key(index); + collectCandidate(collected, storage.getItem(key)); + } catch (error) { + return; + } + } + } + + function collectCandidate(collected, value) { + const normalized = normalizeHeaderCandidate(value, 0); + if (normalized && Object.keys(normalized).length > 0) { + collected.push(normalized); + } + } + + function normalizeHeaderCandidate(value, depth) { + if (depth > 4 || value == null) { + return null; + } + + if (typeof value === "string") { + const trimmed = value.trim(); + + if (!trimmed) { + return null; + } + + if (trimmed[0] === "{" || trimmed[0] === "[") { + try { + return normalizeHeaderCandidate(JSON.parse(trimmed), depth + 1); + } catch (error) { + return null; + } + } + + if (/bearer\\s+/i.test(trimmed)) { + return { + authorization: trimmed, + }; + } + + return null; + } + + if (Array.isArray(value)) { + return value.reduce(function (result, entry) { + const normalized = normalizeHeaderCandidate(entry, depth + 1); + return normalized ? Object.assign(result, normalized) : result; + }, {}); + } + + if (typeof value !== "object") { + return null; + } + + const normalized = {}; + const aliasMap = { + authorization: ["authorization", "Authorization", "token", "accessToken"], + reqLang: ["reqLang", "Req-Lang", "lang", "language"], + appVersion: ["appVersion", "App-Version"], + buildVersion: ["buildVersion", "Build-Version"], + appChannel: ["appChannel", "App-Channel"], + reqImei: ["reqImei", "Req-Imei"], + sysOrigin: ["sysOrigin", "Sys-Origin"], + sysOriginChild: ["sysOriginChild", "Sys-Origin-Child"], + }; + + Object.keys(aliasMap).forEach(function (targetKey) { + aliasMap[targetKey].forEach(function (sourceKey) { + mapHeaderValue(normalized, targetKey, value[sourceKey]); + }); + }); + + ["headerInfo", "headers", "auth", "data", "state"].forEach(function (nestedKey) { + const nestedValue = normalizeHeaderCandidate(value[nestedKey], depth + 1); + if (nestedValue) { + Object.assign(normalized, nestedValue); + } + }); + + Object.keys(value).forEach(function (key) { + if ( + key === "headerInfo" || + key === "headers" || + key === "auth" || + key === "data" || + key === "state" + ) { + return; + } + + if (typeof value[key] === "object") { + const nestedValue = normalizeHeaderCandidate(value[key], depth + 1); + if (nestedValue) { + Object.assign(normalized, nestedValue); + } + } + }); + + return normalized; + } + + function mapHeaderValue(target, key, value) { + if (value == null || value === "" || target[key]) { + return; + } + + target[key] = String(value); + } +})(); diff --git a/h5/yumi-invite-bd/index.html b/h5/yumi-invite-bd/index.html new file mode 100644 index 0000000..31e3471 --- /dev/null +++ b/h5/yumi-invite-bd/index.html @@ -0,0 +1,119 @@ + + + + + + Invite User To Become BD + + + +
+
+
+
+ +
+

Invite User To Become BD

+
+
+
English
+
+
+
+ +
+
+
+
+
+ + +
+
+
+ +
+ +
+
please input user id
+
+
+ + + + + + diff --git a/h5/yumi-invite-bd/yumi-invite-bd.css b/h5/yumi-invite-bd/yumi-invite-bd.css new file mode 100644 index 0000000..9d8eff2 --- /dev/null +++ b/h5/yumi-invite-bd/yumi-invite-bd.css @@ -0,0 +1,456 @@ +html { + -webkit-text-size-adjust: 100%; + -webkit-overflow-scrolling: touch; +} + +body { + margin: 0; + min-height: 100%; + font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif; +} + +* { + box-sizing: border-box; +} + +button, +input { + font: inherit; +} + +[hidden] { + display: none !important; +} + +.fullPage { + width: 100vw; + min-height: 100vh; + background-color: #fff; + background-image: url("../assets/secondBg-CSqvFWr0-1776148661447.png"); + background-size: 100% auto; + background-repeat: no-repeat; + position: relative; + color: rgba(0, 0, 0, 0.8); +} + +.fullPage input::placeholder { + font-weight: 700; + color: rgba(0, 0, 0, 0.4); +} + +.invite-btn { + padding: 4px 12px; + border-radius: 32px; + background: linear-gradient(269deg, #feb219 7.04%, #ff9326 96.78%); + color: #fff; + border: none; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; +} + +.invite-btn:disabled { + background: #d1d5db; + cursor: not-allowed; +} + +.no-data { + text-align: center; + padding: 40px 20px; + color: #666; +} + +.user-card { + cursor: default; +} + +.result-action-btn { + padding: 4px 10px; + border-radius: 32px; + background: linear-gradient(269deg, #feb219 7.04%, #ff9326 96.78%); + color: #fff; + border: none; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; + white-space: nowrap; +} + +.result-action-btn:disabled { + background: #d1d5db; + cursor: not-allowed; +} + +.status-bar-placeholder { + display: none; + height: 30px; +} + +.status-bar-placeholder.is-visible { + display: block; +} + +.header-container { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px 16px; + height: 60px; + font-size: 16px; +} + +.side-area, +.actions-area { + min-width: 1.5em; + display: flex; + align-items: center; +} + +.actions-area { + justify-content: flex-end; + gap: 8px; +} + +.header-title { + flex: 1; + margin: 0; + font-size: 1.2em; + font-weight: 600; + text-align: center; + letter-spacing: 0.3px; +} + +.back-btn, +.help-btn { + width: 1.5em; + border: none; + background: transparent; + transition: all 0.2s ease; + padding: 0; +} + +.back-btn:active, +.help-btn:active { + transform: scale(0.9); +} + +.language-entry { + display: flex; + align-items: center; + color: #f59e0b !important; + cursor: pointer; +} + +.language-name { + font-weight: 700; +} + +.gradient-border-wrapper { + position: relative; +} + +.gradient-border-wrapper::before { + content: ""; + position: absolute; + inset: 0; + border-radius: inherit; + padding: var(--gradient-border-width, 1.5px); + background: linear-gradient( + var(--gradient-direction, to bottom), + var(--gradient-start-color, #ffc4c4), + var(--gradient-end-color, #fff6a6) + ); + -webkit-mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; +} + +.gradient-border-content { + position: relative; + border-radius: inherit; + width: 100%; + height: 100%; + background: var( + --gradient-background, + linear-gradient(120deg, rgba(255, 216, 0, 0.1) 39.13%, rgba(255, 149, 0, 0.1) 119.28%) + ); +} + +.modal-overlay { + position: fixed; + inset: 0; + background-color: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; + padding: 20px; +} + +.modal { + background: #fff; + border-radius: 16px; + box-shadow: + 0 20px 25px rgba(0, 0, 0, 0.1), + 0 10px 10px rgba(0, 0, 0, 0.04); + width: 100%; + max-width: 320px; + padding: 24px 20px 20px; + text-align: center; + position: relative; +} + +.modal-icon { + margin-bottom: 16px; + display: flex; + justify-content: center; +} + +.icon-circle { + width: 56px; + height: 56px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + animation: iconPulse 0.6s ease-out; + border: 3px solid; +} + +.icon-symbol { + font-size: 24px; + font-weight: 700; + line-height: 1; +} + +.modal-title { + font-size: 18px; + font-weight: 600; + color: #1f2937; + margin-bottom: 12px; + line-height: 1.4; +} + +.modal-message { + font-size: 14px; + color: #6b7280; + line-height: 1.5; + margin-bottom: 20px; + word-wrap: break-word; +} + +.modal-actions { + display: flex; + justify-content: center; +} + +.modal-btn { + border: none; + border-radius: 8px; + padding: 10px 32px; + font-size: 14px; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; + min-width: 80px; + color: #fff; +} + +.modal-btn:active { + transform: scale(0.98); +} + +.modal-error .icon-circle-error { + background-color: #fee2e2; + border-color: #fecaca; +} + +.modal-error .icon-symbol-error { + color: #dc2626; +} + +.modal-error .modal-btn-error { + background-color: #dc2626; +} + +.modal-warning .icon-circle-warning { + background-color: #fef3c7; + border-color: #fde68a; +} + +.modal-warning .icon-symbol-warning { + color: #d97706; +} + +.modal-warning .modal-btn-warning { + background-color: #d97706; +} + +.modal-success .icon-circle-success { + background-color: #d1fae5; + border-color: #a7f3d0; +} + +.modal-success .icon-symbol-success { + color: #059669; +} + +.modal-success .modal-btn-success { + background-color: #059669; +} + +.modal-info .icon-circle-info { + background-color: #dbeafe; + border-color: #bfdbfe; +} + +.modal-info .icon-symbol-info { + color: #2563eb; +} + +.modal-info .modal-btn-info { + background-color: #2563eb; +} + +.modal-simple .modal-message { + font-size: 15px; + color: #374151; + margin-bottom: 24px; +} + +.modal-simple .modal-btn { + background-color: #2563eb; +} + +.modal-enter { + animation: modalEnter 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); +} + +.modal-leave { + animation: modalLeave 0.2s ease-in; +} + +[dir="rtl"] .flip-img { + transform: scaleX(-1); +} + +[dir="rtl"] .avatar { + margin-right: 0; + margin-left: 12px; +} + +[dir="rtl"] .user-info { + text-align: right; +} + +[dir="rtl"] .language-entry, +[dir="rtl"] .header-title { + direction: rtl; +} + +@keyframes modalEnter { + 0% { + opacity: 0; + transform: scale(0.7) translateY(-10px); + } + + 100% { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +@keyframes modalLeave { + 0% { + opacity: 1; + transform: scale(1) translateY(0); + } + + 100% { + opacity: 0; + transform: scale(0.9) translateY(-5px); + } +} + +@keyframes iconPulse { + 0% { + transform: scale(0.8); + opacity: 0.5; + } + + 50% { + transform: scale(1.1); + } + + 100% { + transform: scale(1); + opacity: 1; + } +} + +@media screen and (max-width: 360px) { + .fullPage { + font-size: 10px; + } + + .header-container { + font-size: 10px; + } +} + +@media screen and (min-width: 360px) { + .fullPage { + font-size: 12px; + } + + .header-container { + font-size: 16px; + } +} + +@media screen and (min-width: 768px) { + .fullPage { + font-size: 24px; + } + + .header-container { + font-size: 24px; + } +} + +@media screen and (min-width: 1024px) { + .fullPage { + font-size: 32px; + } + + .header-container { + font-size: 32px; + } +} + +@media (max-width: 480px) { + .modal { + max-width: 280px; + padding: 20px 16px 16px; + } + + .icon-circle { + width: 48px; + height: 48px; + } + + .icon-symbol { + font-size: 20px; + } + + .modal-title { + font-size: 16px; + } + + .modal-message { + font-size: 13px; + } +} diff --git a/h5/yumi-invite-bd/yumi-invite-bd.js b/h5/yumi-invite-bd/yumi-invite-bd.js new file mode 100644 index 0000000..763787b --- /dev/null +++ b/h5/yumi-invite-bd/yumi-invite-bd.js @@ -0,0 +1,699 @@ +(function () { + const API_BASE = "https://jvapi.haiyihy.com"; + const BACK_ICON = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAACE4AAAhOAFFljFgAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAGXSURBVHgB7dvBTcNAFIThB5xTQFwADVBBmqEnUgZQAAXAjRqgACgAj2wfECbJZXZnrPmkVRT5lF9ODn4vVRERERERETEaxnNb8cd+PA/jeZ3PY02xqG7KA+Ic6/eds5vfPxfRdelb4uxXrt0VmXqgU3Hgo8iUA52LA9SvF1yVpkviHOdDpRhIJg6oBZKKA0qB5OKASiDJOKAQSDYO9A4kHQd6BpKPA70CWcSBHoFs4kDrQFZxoGUguzjQKpBlHGgRyDYOsANZxwFmIPs4wAq0iTjACvRUG4gDjEeumDRsIg4wAn3VhjDmYt81jWOGf64vo5q3MsAaHOLDH2oa7q2xicQKhLvopTYQiTl63kQk9mzePlKL5QXrSK22O2wjtVx/sYzUej/ILlKPBSqrSL02zGwi9VzBs4jUe0dRPpLCEqd0JJUtV9lISmvAkpHU9qTlIikukktFUt20vzQSHu++F5HqGvDi3PgIgQ5FpL5p/zme+/l1za7IHP6rcSoSfdPeIRCsRcIPtM18rSUMJoeKiIiIiIjw9QNKh3OJhFQBoAAAAABJRU5ErkJggg=="; + const DEFAULT_AVATAR = "../assets/defaultAvatar-CdxWBK1k-1776148661448.png"; + const ENDPOINTS = { + invitedList: "/team/bd/invite-bd-message-own", + invite: "/team/bd/invite-bd", + cancelPrefix: "/team/bd/invite-bd-cancel/", + }; + + const TEXT = { + pageTitle: "Invite User To Become BD", + language: "English", + placeholder: "Enter User ID", + search: "Search", + invite: "Invite", + idlePrompt: "please input user id", + noUsersFound: "No users found", + information: "Information:", + userIdPrefix: "User ID:", + pending: "Pending", + success: "Success", + cancel: "Cancel", + invitationSubmitted: "Invitation submitted", + applicationCancelled: "Application cancelled", + somethingWentWrong: "Something went wrong", + cancellationFailed: "Cancellation failed, try again", + informationTitle: "Information", + successTitle: "Success", + errorTitle: "Error", + ok: "OK", + }; + + const state = { + query: "", + invitedList: [], + searchResults: [], + showSearchResults: false, + searching: false, + hasSearched: false, + }; + + const dom = { + statusBarPlaceholder: document.getElementById("statusBarPlaceholder"), + backButton: document.getElementById("backButton"), + backButtonIcon: document.getElementById("backButtonIcon"), + pageTitle: document.getElementById("pageTitle"), + languageEntry: document.getElementById("languageEntry"), + languageName: document.getElementById("languageName"), + searchInput: document.getElementById("searchInput"), + clearButton: document.getElementById("clearButton"), + searchButton: document.getElementById("searchButton"), + resultList: document.getElementById("resultList"), + noData: document.getElementById("noData"), + userCardTemplate: document.getElementById("userCardTemplate"), + }; + + init(); + + function init() { + document.title = TEXT.pageTitle; + document.documentElement.lang = "en"; + document.documentElement.dir = resolveDocumentDirection(); + + dom.backButtonIcon.src = BACK_ICON; + dom.pageTitle.textContent = TEXT.pageTitle; + dom.languageName.textContent = TEXT.language; + dom.searchInput.placeholder = TEXT.placeholder; + dom.searchButton.textContent = TEXT.search; + dom.noData.textContent = TEXT.idlePrompt; + + syncStatusBarPlaceholder(); + bindEvents(); + render(); + loadInvitedList(); + } + + function bindEvents() { + dom.backButton.addEventListener("click", handleBack); + dom.languageEntry.addEventListener("click", handleLanguageClick); + dom.searchInput.addEventListener("input", handleSearchInput); + dom.clearButton.addEventListener("click", clearSearch); + dom.searchButton.addEventListener("click", triggerSearchNow); + } + + function resolveDocumentDirection() { + const lang = (new URLSearchParams(window.location.search).get("lang") || "").toLowerCase(); + return lang.startsWith("ar") ? "rtl" : "ltr"; + } + + function syncStatusBarPlaceholder() { + const hasAppBridge = + Boolean(window.app) || + Boolean(window.FlutterPageControl) || + Boolean(window.webkit && window.webkit.messageHandlers); + const isIOS = /iphone|ipad|ipod/i.test(window.navigator.userAgent || ""); + dom.statusBarPlaceholder.classList.toggle("is-visible", hasAppBridge && isIOS); + } + + function handleBack() { + if (window.history.length > 1) { + window.history.back(); + return; + } + + window.location.href = "/"; + } + + function handleLanguageClick() { + window.location.href = "/language"; + } + + function handleSearchInput(event) { + state.query = event.target.value; + dom.clearButton.hidden = !state.query; + state.showSearchResults = false; + state.hasSearched = false; + render(); + } + + function clearSearch() { + state.query = ""; + state.searchResults = []; + state.showSearchResults = false; + state.hasSearched = false; + dom.searchInput.value = ""; + dom.clearButton.hidden = true; + render(); + } + + async function triggerSearchNow() { + if (state.searching) { + return; + } + + const query = state.query.trim(); + + if (!query) { + state.showSearchResults = false; + state.hasSearched = false; + render(); + return; + } + + state.searching = true; + state.showSearchResults = true; + state.hasSearched = true; + state.searchResults = []; + render(); + + try { + const payload = await searchUser(query); + state.searchResults = payload ? [mergeWithInvitationStatus(payload.userProfile)] : []; + } catch (error) { + state.searchResults = []; + showToast(extractErrorMessage(error), { + title: TEXT.errorTitle, + type: "error", + }); + } finally { + state.searching = false; + render(); + } + } + + async function loadInvitedList() { + try { + const response = await request("GET", ENDPOINTS.invitedList); + state.invitedList = response && response.status && Array.isArray(response.body) ? response.body : []; + state.searchResults = state.searchResults.map(function (item) { + return mergeWithInvitationStatus(item.userProfile || {}); + }); + render(); + } catch (error) { + state.invitedList = []; + render(); + } + } + + async function searchUser(query) { + try { + const response = await request( + "GET", + "/user/user-profile/open-search?account=" + encodeURIComponent(query) + ); + + if (response && response.status && response.body) { + return { userProfile: response.body }; + } + } catch (error) { + if (!/^\d+$/.test(query)) { + throw error; + } + } + + if (!/^\d+$/.test(query)) { + return null; + } + + const fallbackResponse = await request( + "GET", + "/user/user-profile?userId=" + encodeURIComponent(query) + ); + + if (fallbackResponse && fallbackResponse.status && fallbackResponse.body) { + return { userProfile: fallbackResponse.body }; + } + + return null; + } + + function mergeWithInvitationStatus(userProfile) { + const matchedInvitation = state.invitedList.find(function (item) { + return ( + item && + item.userProfile && + userProfile && + item.userProfile.id === userProfile.id + ); + }); + + return Object.assign({}, matchedInvitation || {}, { + userProfile: userProfile || {}, + }); + } + + async function inviteUser(item) { + if (!item || !item.userProfile || !item.userProfile.id) { + return; + } + + try { + const response = await request("POST", ENDPOINTS.invite, { + body: { + inviteUserId: item.userProfile.id, + }, + }); + + if (response && response.status) { + showToast(TEXT.invitationSubmitted, { + title: TEXT.successTitle, + type: "success", + }); + await loadInvitedList(); + } + } catch (error) { + showToast(extractErrorMessage(error, TEXT.somethingWentWrong), { + title: TEXT.errorTitle, + type: "error", + }); + } + } + + async function cancelInvitation(invitationId) { + try { + const response = await request("POST", ENDPOINTS.cancelPrefix + invitationId); + + if (response && response.status) { + showToast(TEXT.applicationCancelled, { + title: TEXT.informationTitle, + type: "info", + }); + await loadInvitedList(); + } + } catch (error) { + showToast(extractErrorMessage(error, TEXT.cancellationFailed), { + title: TEXT.errorTitle, + type: "error", + }); + } + } + + function render() { + renderSearchButton(); + renderList(); + } + + function renderSearchButton() { + dom.searchButton.disabled = !state.query.trim() || state.searching; + } + + function renderList() { + const currentList = state.showSearchResults ? state.searchResults : state.invitedList; + dom.resultList.innerHTML = ""; + + currentList.forEach(function (item) { + dom.resultList.appendChild(createUserCard(item, state.showSearchResults ? "search" : "invited")); + }); + + if (currentList.length > 0 || (state.searching && state.showSearchResults)) { + dom.noData.hidden = true; + return; + } + + dom.noData.hidden = false; + dom.noData.textContent = + state.showSearchResults && state.hasSearched ? TEXT.noUsersFound : TEXT.idlePrompt; + } + + function createUserCard(item, mode) { + const fragment = dom.userCardTemplate.content.cloneNode(true); + const statusBadge = fragment.querySelector(".status-badge"); + const avatarImage = fragment.querySelector(".avatar-image"); + const nickname = fragment.querySelector(".nickname"); + const account = fragment.querySelector(".account"); + const pendingActions = fragment.querySelector(".pending-actions"); + const userProfile = item.userProfile || {}; + + avatarImage.src = userProfile.userAvatar || DEFAULT_AVATAR; + avatarImage.alt = userProfile.userNickname || ""; + avatarImage.onerror = handleAvatarError; + nickname.textContent = userProfile.userNickname || ""; + account.textContent = TEXT.userIdPrefix + " " + (userProfile.account || userProfile.id || ""); + + if (item.status === 0) { + statusBadge.innerHTML = + '
' + + TEXT.pending + + "
"; + } else if (item.status === 1) { + statusBadge.innerHTML = + '
' + + TEXT.success + + "
"; + } + + if (mode === "search") { + const inviteButton = document.createElement("button"); + inviteButton.type = "button"; + inviteButton.className = "result-action-btn"; + inviteButton.textContent = TEXT.invite; + inviteButton.disabled = item.status === 0 || item.status === 1; + inviteButton.addEventListener("click", function (event) { + event.stopPropagation(); + inviteUser(item); + }); + pendingActions.appendChild(inviteButton); + } else if (item.status === 0) { + const cancelButton = document.createElement("div"); + cancelButton.textContent = TEXT.cancel; + cancelButton.style.padding = "4px 8px"; + cancelButton.style.borderRadius = "32px"; + cancelButton.style.backgroundColor = "red"; + cancelButton.style.color = "white"; + cancelButton.style.fontWeight = "600"; + cancelButton.style.zIndex = "5"; + cancelButton.addEventListener("click", function (event) { + event.stopPropagation(); + cancelInvitation(item.id); + }); + pendingActions.appendChild(cancelButton); + } + + return fragment; + } + + function handleAvatarError(event) { + event.target.onerror = null; + event.target.src = DEFAULT_AVATAR; + } + + function extractErrorMessage(error, fallback) { + return ( + (error && + error.response && + (error.response.errorMsg || error.response.message || error.response.error)) || + (error && error.message) || + fallback || + TEXT.somethingWentWrong + ); + } + + function showToast(message, options) { + const config = Object.assign( + { + title: TEXT.informationTitle, + type: "info", + confirmText: TEXT.ok, + }, + options || {} + ); + + const iconMap = { + error: "✕", + warning: "⚠", + success: "✓", + info: "i", + simple: "", + }; + + const overlay = document.createElement("div"); + overlay.className = "modal-overlay"; + overlay.innerHTML = + '"; + + const modal = overlay.querySelector(".modal"); + const closeButton = overlay.querySelector(".modal-btn"); + const close = function () { + modal.classList.remove("modal-enter"); + modal.classList.add("modal-leave"); + window.setTimeout(function () { + overlay.remove(); + }, 200); + }; + + closeButton.addEventListener("click", close); + overlay.addEventListener("click", function (event) { + if (event.target === overlay) { + close(); + } + }); + + document.body.appendChild(overlay); + } + + function escapeHtml(value) { + return String(value) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + } + + async function request(method, path, options) { + const config = options || {}; + const headers = buildRequestHeaders(Boolean(config.body), config.headers || {}); + const response = await fetch(API_BASE + path, { + method: method, + credentials: "include", + headers: headers, + body: config.body ? JSON.stringify(config.body) : undefined, + }); + const text = await response.text(); + let payload = {}; + + if (text) { + try { + payload = JSON.parse(text); + } catch (error) { + payload = { + status: response.ok, + body: text, + }; + } + } + + if (!response.ok || payload.status === false) { + const requestError = new Error( + payload.errorMsg || payload.message || response.statusText || TEXT.somethingWentWrong + ); + requestError.response = payload; + throw requestError; + } + + return payload; + } + + function buildRequestHeaders(hasBody, overrideHeaders) { + const discovered = discoverAppHeaders(); + const headers = Object.assign({}, overrideHeaders || {}); + + if (hasBody) { + headers["Content-Type"] = headers["Content-Type"] || "application/json"; + } + + headers.Accept = headers.Accept || "application/json, text/plain, */*"; + + if (discovered.authorization) { + headers.Authorization = headers.Authorization || discovered.authorization; + } + + if (discovered.reqLang) { + headers["Req-Lang"] = headers["Req-Lang"] || discovered.reqLang; + } + + if (discovered.appVersion) { + headers["App-Version"] = headers["App-Version"] || discovered.appVersion; + } + + if (discovered.buildVersion) { + headers["Build-Version"] = headers["Build-Version"] || discovered.buildVersion; + } + + if (discovered.appChannel) { + headers["App-Channel"] = headers["App-Channel"] || discovered.appChannel; + } + + if (discovered.reqImei) { + headers["Req-Imei"] = headers["Req-Imei"] || discovered.reqImei; + } + + if (discovered.sysOrigin) { + headers["Sys-Origin"] = headers["Sys-Origin"] || discovered.sysOrigin; + } + + if (discovered.sysOriginChild) { + headers["Sys-Origin-Child"] = + headers["Sys-Origin-Child"] || discovered.sysOriginChild; + } + + return headers; + } + + function discoverAppHeaders() { + const query = new URLSearchParams(window.location.search); + const collected = []; + + collectCandidate(collected, window.__APP_HEADERS__); + collectCandidate(collected, window.__HEADER_INFO__); + collectCandidate(collected, window.headerInfo); + collectCandidate(collected, parseHeaderObjectFromQuery(query)); + + collectFromStorage(collected, window.localStorage); + collectFromStorage(collected, window.sessionStorage); + + return collected.reduce(function (result, candidate) { + return Object.assign(result, candidate); + }, { + reqLang: query.get("lang") || document.documentElement.lang || "en", + }); + } + + function parseHeaderObjectFromQuery(query) { + const candidate = {}; + mapHeaderValue(candidate, "authorization", query.get("authorization")); + mapHeaderValue(candidate, "authorization", query.get("token")); + mapHeaderValue(candidate, "reqLang", query.get("lang")); + mapHeaderValue(candidate, "appVersion", query.get("appVersion")); + mapHeaderValue(candidate, "buildVersion", query.get("buildVersion")); + mapHeaderValue(candidate, "appChannel", query.get("appChannel")); + mapHeaderValue(candidate, "reqImei", query.get("reqImei")); + mapHeaderValue(candidate, "sysOrigin", query.get("sysOrigin")); + mapHeaderValue(candidate, "sysOriginChild", query.get("sysOriginChild")); + return candidate; + } + + function collectFromStorage(collected, storage) { + if (!storage) { + return; + } + + const preferredKeys = [ + "authorization", + "Authorization", + "token", + "accessToken", + "headerInfo", + "headersFromApp", + "appHeaders", + "persist:root", + "user", + "userInfo", + ]; + + preferredKeys.forEach(function (key) { + try { + collectCandidate(collected, storage.getItem(key)); + } catch (error) { + return; + } + }); + + for (let index = 0; index < storage.length; index += 1) { + try { + const key = storage.key(index); + collectCandidate(collected, storage.getItem(key)); + } catch (error) { + return; + } + } + } + + function collectCandidate(collected, value) { + const normalized = normalizeHeaderCandidate(value, 0); + if (normalized && Object.keys(normalized).length > 0) { + collected.push(normalized); + } + } + + function normalizeHeaderCandidate(value, depth) { + if (depth > 4 || value == null) { + return null; + } + + if (typeof value === "string") { + const trimmed = value.trim(); + + if (!trimmed) { + return null; + } + + if (trimmed[0] === "{" || trimmed[0] === "[") { + try { + return normalizeHeaderCandidate(JSON.parse(trimmed), depth + 1); + } catch (error) { + return null; + } + } + + if (/bearer\\s+/i.test(trimmed)) { + return { + authorization: trimmed, + }; + } + + return null; + } + + if (Array.isArray(value)) { + return value.reduce(function (result, entry) { + const normalized = normalizeHeaderCandidate(entry, depth + 1); + return normalized ? Object.assign(result, normalized) : result; + }, {}); + } + + if (typeof value !== "object") { + return null; + } + + const normalized = {}; + const aliasMap = { + authorization: ["authorization", "Authorization", "token", "accessToken"], + reqLang: ["reqLang", "Req-Lang", "lang", "language"], + appVersion: ["appVersion", "App-Version"], + buildVersion: ["buildVersion", "Build-Version"], + appChannel: ["appChannel", "App-Channel"], + reqImei: ["reqImei", "Req-Imei"], + sysOrigin: ["sysOrigin", "Sys-Origin"], + sysOriginChild: ["sysOriginChild", "Sys-Origin-Child"], + }; + + Object.keys(aliasMap).forEach(function (targetKey) { + aliasMap[targetKey].forEach(function (sourceKey) { + mapHeaderValue(normalized, targetKey, value[sourceKey]); + }); + }); + + ["headerInfo", "headers", "auth", "data", "state"].forEach(function (nestedKey) { + const nestedValue = normalizeHeaderCandidate(value[nestedKey], depth + 1); + if (nestedValue) { + Object.assign(normalized, nestedValue); + } + }); + + Object.keys(value).forEach(function (key) { + if ( + key === "headerInfo" || + key === "headers" || + key === "auth" || + key === "data" || + key === "state" + ) { + return; + } + + if (typeof value[key] === "object") { + const nestedValue = normalizeHeaderCandidate(value[key], depth + 1); + if (nestedValue) { + Object.assign(normalized, nestedValue); + } + } + }); + + return normalized; + } + + function mapHeaderValue(target, key, value) { + if (value == null || value === "" || target[key]) { + return; + } + + target[key] = String(value); + } +})(); diff --git a/h5/yumi-invite-recharge-agency/index.html b/h5/yumi-invite-recharge-agency/index.html new file mode 100644 index 0000000..8cc7a8e --- /dev/null +++ b/h5/yumi-invite-recharge-agency/index.html @@ -0,0 +1,119 @@ + + + + + + Invite User To Become Recharge Agent + + + +
+
+
+
+ +
+

Invite User To Become Recharge Agent

+
+
+
English
+
+
+
+ +
+
+
+
+
+ + +
+
+
+ +
+ +
+
please input user id
+
+
+ + + + + + diff --git a/h5/yumi-invite-recharge-agency/yumi-invite-recharge-agency.css b/h5/yumi-invite-recharge-agency/yumi-invite-recharge-agency.css new file mode 100644 index 0000000..9d8eff2 --- /dev/null +++ b/h5/yumi-invite-recharge-agency/yumi-invite-recharge-agency.css @@ -0,0 +1,456 @@ +html { + -webkit-text-size-adjust: 100%; + -webkit-overflow-scrolling: touch; +} + +body { + margin: 0; + min-height: 100%; + font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Helvetica Neue", Arial, sans-serif; +} + +* { + box-sizing: border-box; +} + +button, +input { + font: inherit; +} + +[hidden] { + display: none !important; +} + +.fullPage { + width: 100vw; + min-height: 100vh; + background-color: #fff; + background-image: url("../assets/secondBg-CSqvFWr0-1776148661447.png"); + background-size: 100% auto; + background-repeat: no-repeat; + position: relative; + color: rgba(0, 0, 0, 0.8); +} + +.fullPage input::placeholder { + font-weight: 700; + color: rgba(0, 0, 0, 0.4); +} + +.invite-btn { + padding: 4px 12px; + border-radius: 32px; + background: linear-gradient(269deg, #feb219 7.04%, #ff9326 96.78%); + color: #fff; + border: none; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; +} + +.invite-btn:disabled { + background: #d1d5db; + cursor: not-allowed; +} + +.no-data { + text-align: center; + padding: 40px 20px; + color: #666; +} + +.user-card { + cursor: default; +} + +.result-action-btn { + padding: 4px 10px; + border-radius: 32px; + background: linear-gradient(269deg, #feb219 7.04%, #ff9326 96.78%); + color: #fff; + border: none; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; + white-space: nowrap; +} + +.result-action-btn:disabled { + background: #d1d5db; + cursor: not-allowed; +} + +.status-bar-placeholder { + display: none; + height: 30px; +} + +.status-bar-placeholder.is-visible { + display: block; +} + +.header-container { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px 16px; + height: 60px; + font-size: 16px; +} + +.side-area, +.actions-area { + min-width: 1.5em; + display: flex; + align-items: center; +} + +.actions-area { + justify-content: flex-end; + gap: 8px; +} + +.header-title { + flex: 1; + margin: 0; + font-size: 1.2em; + font-weight: 600; + text-align: center; + letter-spacing: 0.3px; +} + +.back-btn, +.help-btn { + width: 1.5em; + border: none; + background: transparent; + transition: all 0.2s ease; + padding: 0; +} + +.back-btn:active, +.help-btn:active { + transform: scale(0.9); +} + +.language-entry { + display: flex; + align-items: center; + color: #f59e0b !important; + cursor: pointer; +} + +.language-name { + font-weight: 700; +} + +.gradient-border-wrapper { + position: relative; +} + +.gradient-border-wrapper::before { + content: ""; + position: absolute; + inset: 0; + border-radius: inherit; + padding: var(--gradient-border-width, 1.5px); + background: linear-gradient( + var(--gradient-direction, to bottom), + var(--gradient-start-color, #ffc4c4), + var(--gradient-end-color, #fff6a6) + ); + -webkit-mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + pointer-events: none; +} + +.gradient-border-content { + position: relative; + border-radius: inherit; + width: 100%; + height: 100%; + background: var( + --gradient-background, + linear-gradient(120deg, rgba(255, 216, 0, 0.1) 39.13%, rgba(255, 149, 0, 0.1) 119.28%) + ); +} + +.modal-overlay { + position: fixed; + inset: 0; + background-color: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; + padding: 20px; +} + +.modal { + background: #fff; + border-radius: 16px; + box-shadow: + 0 20px 25px rgba(0, 0, 0, 0.1), + 0 10px 10px rgba(0, 0, 0, 0.04); + width: 100%; + max-width: 320px; + padding: 24px 20px 20px; + text-align: center; + position: relative; +} + +.modal-icon { + margin-bottom: 16px; + display: flex; + justify-content: center; +} + +.icon-circle { + width: 56px; + height: 56px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + animation: iconPulse 0.6s ease-out; + border: 3px solid; +} + +.icon-symbol { + font-size: 24px; + font-weight: 700; + line-height: 1; +} + +.modal-title { + font-size: 18px; + font-weight: 600; + color: #1f2937; + margin-bottom: 12px; + line-height: 1.4; +} + +.modal-message { + font-size: 14px; + color: #6b7280; + line-height: 1.5; + margin-bottom: 20px; + word-wrap: break-word; +} + +.modal-actions { + display: flex; + justify-content: center; +} + +.modal-btn { + border: none; + border-radius: 8px; + padding: 10px 32px; + font-size: 14px; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; + min-width: 80px; + color: #fff; +} + +.modal-btn:active { + transform: scale(0.98); +} + +.modal-error .icon-circle-error { + background-color: #fee2e2; + border-color: #fecaca; +} + +.modal-error .icon-symbol-error { + color: #dc2626; +} + +.modal-error .modal-btn-error { + background-color: #dc2626; +} + +.modal-warning .icon-circle-warning { + background-color: #fef3c7; + border-color: #fde68a; +} + +.modal-warning .icon-symbol-warning { + color: #d97706; +} + +.modal-warning .modal-btn-warning { + background-color: #d97706; +} + +.modal-success .icon-circle-success { + background-color: #d1fae5; + border-color: #a7f3d0; +} + +.modal-success .icon-symbol-success { + color: #059669; +} + +.modal-success .modal-btn-success { + background-color: #059669; +} + +.modal-info .icon-circle-info { + background-color: #dbeafe; + border-color: #bfdbfe; +} + +.modal-info .icon-symbol-info { + color: #2563eb; +} + +.modal-info .modal-btn-info { + background-color: #2563eb; +} + +.modal-simple .modal-message { + font-size: 15px; + color: #374151; + margin-bottom: 24px; +} + +.modal-simple .modal-btn { + background-color: #2563eb; +} + +.modal-enter { + animation: modalEnter 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); +} + +.modal-leave { + animation: modalLeave 0.2s ease-in; +} + +[dir="rtl"] .flip-img { + transform: scaleX(-1); +} + +[dir="rtl"] .avatar { + margin-right: 0; + margin-left: 12px; +} + +[dir="rtl"] .user-info { + text-align: right; +} + +[dir="rtl"] .language-entry, +[dir="rtl"] .header-title { + direction: rtl; +} + +@keyframes modalEnter { + 0% { + opacity: 0; + transform: scale(0.7) translateY(-10px); + } + + 100% { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +@keyframes modalLeave { + 0% { + opacity: 1; + transform: scale(1) translateY(0); + } + + 100% { + opacity: 0; + transform: scale(0.9) translateY(-5px); + } +} + +@keyframes iconPulse { + 0% { + transform: scale(0.8); + opacity: 0.5; + } + + 50% { + transform: scale(1.1); + } + + 100% { + transform: scale(1); + opacity: 1; + } +} + +@media screen and (max-width: 360px) { + .fullPage { + font-size: 10px; + } + + .header-container { + font-size: 10px; + } +} + +@media screen and (min-width: 360px) { + .fullPage { + font-size: 12px; + } + + .header-container { + font-size: 16px; + } +} + +@media screen and (min-width: 768px) { + .fullPage { + font-size: 24px; + } + + .header-container { + font-size: 24px; + } +} + +@media screen and (min-width: 1024px) { + .fullPage { + font-size: 32px; + } + + .header-container { + font-size: 32px; + } +} + +@media (max-width: 480px) { + .modal { + max-width: 280px; + padding: 20px 16px 16px; + } + + .icon-circle { + width: 48px; + height: 48px; + } + + .icon-symbol { + font-size: 20px; + } + + .modal-title { + font-size: 16px; + } + + .modal-message { + font-size: 13px; + } +} diff --git a/h5/yumi-invite-recharge-agency/yumi-invite-recharge-agency.js b/h5/yumi-invite-recharge-agency/yumi-invite-recharge-agency.js new file mode 100644 index 0000000..64453ca --- /dev/null +++ b/h5/yumi-invite-recharge-agency/yumi-invite-recharge-agency.js @@ -0,0 +1,699 @@ +(function () { + const API_BASE = "https://jvapi.haiyihy.com"; + const BACK_ICON = + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAACE4AAAhOAFFljFgAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAGXSURBVHgB7dvBTcNAFIThB5xTQFwADVBBmqEnUgZQAAXAjRqgACgAj2wfECbJZXZnrPmkVRT5lF9ODn4vVRERERERETEaxnNb8cd+PA/jeZ3PY02xqG7KA+Ic6/eds5vfPxfRdelb4uxXrt0VmXqgU3Hgo8iUA52LA9SvF1yVpkviHOdDpRhIJg6oBZKKA0qB5OKASiDJOKAQSDYO9A4kHQd6BpKPA70CWcSBHoFs4kDrQFZxoGUguzjQKpBlHGgRyDYOsANZxwFmIPs4wAq0iTjACvRUG4gDjEeumDRsIg4wAn3VhjDmYt81jWOGf64vo5q3MsAaHOLDH2oa7q2xicQKhLvopTYQiTl63kQk9mzePlKL5QXrSK22O2wjtVx/sYzUej/ILlKPBSqrSL02zGwi9VzBs4jUe0dRPpLCEqd0JJUtV9lISmvAkpHU9qTlIikukktFUt20vzQSHu++F5HqGvDi3PgIgQ5FpL5p/zme+/l1za7IHP6rcSoSfdPeIRCsRcIPtM18rSUMJoeKiIiIiIjw9QNKh3OJhFQBoAAAAABJRU5ErkJggg=="; + const DEFAULT_AVATAR = "../assets/defaultAvatar-CdxWBK1k-1776148661448.png"; + const ENDPOINTS = { + invitedList: "/team/bd/invite-recharge-agent-message-own", + invite: "/team/bd/invite-recharge-agent", + cancelPrefix: "/team/bd/invite-recharge-agent-cancel/", + }; + + const TEXT = { + pageTitle: "Invite User To Become Recharge Agent", + language: "English", + placeholder: "Enter User ID", + search: "Search", + invite: "Invite", + idlePrompt: "please input user id", + noUsersFound: "No users found", + information: "Information:", + userIdPrefix: "User ID:", + pending: "Pending", + success: "Success", + cancel: "Cancel", + invitationSubmitted: "Invitation submitted", + applicationCancelled: "Application cancelled", + somethingWentWrong: "Something went wrong", + cancellationFailed: "Cancellation failed, try again", + informationTitle: "Information", + successTitle: "Success", + errorTitle: "Error", + ok: "OK", + }; + + const state = { + query: "", + invitedList: [], + searchResults: [], + showSearchResults: false, + searching: false, + hasSearched: false, + }; + + const dom = { + statusBarPlaceholder: document.getElementById("statusBarPlaceholder"), + backButton: document.getElementById("backButton"), + backButtonIcon: document.getElementById("backButtonIcon"), + pageTitle: document.getElementById("pageTitle"), + languageEntry: document.getElementById("languageEntry"), + languageName: document.getElementById("languageName"), + searchInput: document.getElementById("searchInput"), + clearButton: document.getElementById("clearButton"), + searchButton: document.getElementById("searchButton"), + resultList: document.getElementById("resultList"), + noData: document.getElementById("noData"), + userCardTemplate: document.getElementById("userCardTemplate"), + }; + + init(); + + function init() { + document.title = TEXT.pageTitle; + document.documentElement.lang = "en"; + document.documentElement.dir = resolveDocumentDirection(); + + dom.backButtonIcon.src = BACK_ICON; + dom.pageTitle.textContent = TEXT.pageTitle; + dom.languageName.textContent = TEXT.language; + dom.searchInput.placeholder = TEXT.placeholder; + dom.searchButton.textContent = TEXT.search; + dom.noData.textContent = TEXT.idlePrompt; + + syncStatusBarPlaceholder(); + bindEvents(); + render(); + loadInvitedList(); + } + + function bindEvents() { + dom.backButton.addEventListener("click", handleBack); + dom.languageEntry.addEventListener("click", handleLanguageClick); + dom.searchInput.addEventListener("input", handleSearchInput); + dom.clearButton.addEventListener("click", clearSearch); + dom.searchButton.addEventListener("click", triggerSearchNow); + } + + function resolveDocumentDirection() { + const lang = (new URLSearchParams(window.location.search).get("lang") || "").toLowerCase(); + return lang.startsWith("ar") ? "rtl" : "ltr"; + } + + function syncStatusBarPlaceholder() { + const hasAppBridge = + Boolean(window.app) || + Boolean(window.FlutterPageControl) || + Boolean(window.webkit && window.webkit.messageHandlers); + const isIOS = /iphone|ipad|ipod/i.test(window.navigator.userAgent || ""); + dom.statusBarPlaceholder.classList.toggle("is-visible", hasAppBridge && isIOS); + } + + function handleBack() { + if (window.history.length > 1) { + window.history.back(); + return; + } + + window.location.href = "/"; + } + + function handleLanguageClick() { + window.location.href = "/language"; + } + + function handleSearchInput(event) { + state.query = event.target.value; + dom.clearButton.hidden = !state.query; + state.showSearchResults = false; + state.hasSearched = false; + render(); + } + + function clearSearch() { + state.query = ""; + state.searchResults = []; + state.showSearchResults = false; + state.hasSearched = false; + dom.searchInput.value = ""; + dom.clearButton.hidden = true; + render(); + } + + async function triggerSearchNow() { + if (state.searching) { + return; + } + + const query = state.query.trim(); + + if (!query) { + state.showSearchResults = false; + state.hasSearched = false; + render(); + return; + } + + state.searching = true; + state.showSearchResults = true; + state.hasSearched = true; + state.searchResults = []; + render(); + + try { + const payload = await searchUser(query); + state.searchResults = payload ? [mergeWithInvitationStatus(payload.userProfile)] : []; + } catch (error) { + state.searchResults = []; + showToast(extractErrorMessage(error), { + title: TEXT.errorTitle, + type: "error", + }); + } finally { + state.searching = false; + render(); + } + } + + async function loadInvitedList() { + try { + const response = await request("GET", ENDPOINTS.invitedList); + state.invitedList = response && response.status && Array.isArray(response.body) ? response.body : []; + state.searchResults = state.searchResults.map(function (item) { + return mergeWithInvitationStatus(item.userProfile || {}); + }); + render(); + } catch (error) { + state.invitedList = []; + render(); + } + } + + async function searchUser(query) { + try { + const response = await request( + "GET", + "/user/user-profile/open-search?account=" + encodeURIComponent(query) + ); + + if (response && response.status && response.body) { + return { userProfile: response.body }; + } + } catch (error) { + if (!/^\d+$/.test(query)) { + throw error; + } + } + + if (!/^\d+$/.test(query)) { + return null; + } + + const fallbackResponse = await request( + "GET", + "/user/user-profile?userId=" + encodeURIComponent(query) + ); + + if (fallbackResponse && fallbackResponse.status && fallbackResponse.body) { + return { userProfile: fallbackResponse.body }; + } + + return null; + } + + function mergeWithInvitationStatus(userProfile) { + const matchedInvitation = state.invitedList.find(function (item) { + return ( + item && + item.userProfile && + userProfile && + item.userProfile.id === userProfile.id + ); + }); + + return Object.assign({}, matchedInvitation || {}, { + userProfile: userProfile || {}, + }); + } + + async function inviteUser(item) { + if (!item || !item.userProfile || !item.userProfile.id) { + return; + } + + try { + const response = await request("POST", ENDPOINTS.invite, { + body: { + inviteUserId: item.userProfile.id, + }, + }); + + if (response && response.status) { + showToast(TEXT.invitationSubmitted, { + title: TEXT.successTitle, + type: "success", + }); + await loadInvitedList(); + } + } catch (error) { + showToast(extractErrorMessage(error, TEXT.somethingWentWrong), { + title: TEXT.errorTitle, + type: "error", + }); + } + } + + async function cancelInvitation(invitationId) { + try { + const response = await request("POST", ENDPOINTS.cancelPrefix + invitationId); + + if (response && response.status) { + showToast(TEXT.applicationCancelled, { + title: TEXT.informationTitle, + type: "info", + }); + await loadInvitedList(); + } + } catch (error) { + showToast(extractErrorMessage(error, TEXT.cancellationFailed), { + title: TEXT.errorTitle, + type: "error", + }); + } + } + + function render() { + renderSearchButton(); + renderList(); + } + + function renderSearchButton() { + dom.searchButton.disabled = !state.query.trim() || state.searching; + } + + function renderList() { + const currentList = state.showSearchResults ? state.searchResults : state.invitedList; + dom.resultList.innerHTML = ""; + + currentList.forEach(function (item) { + dom.resultList.appendChild(createUserCard(item, state.showSearchResults ? "search" : "invited")); + }); + + if (currentList.length > 0 || (state.searching && state.showSearchResults)) { + dom.noData.hidden = true; + return; + } + + dom.noData.hidden = false; + dom.noData.textContent = + state.showSearchResults && state.hasSearched ? TEXT.noUsersFound : TEXT.idlePrompt; + } + + function createUserCard(item, mode) { + const fragment = dom.userCardTemplate.content.cloneNode(true); + const statusBadge = fragment.querySelector(".status-badge"); + const avatarImage = fragment.querySelector(".avatar-image"); + const nickname = fragment.querySelector(".nickname"); + const account = fragment.querySelector(".account"); + const pendingActions = fragment.querySelector(".pending-actions"); + const userProfile = item.userProfile || {}; + + avatarImage.src = userProfile.userAvatar || DEFAULT_AVATAR; + avatarImage.alt = userProfile.userNickname || ""; + avatarImage.onerror = handleAvatarError; + nickname.textContent = userProfile.userNickname || ""; + account.textContent = TEXT.userIdPrefix + " " + (userProfile.account || userProfile.id || ""); + + if (item.status === 0) { + statusBadge.innerHTML = + '
' + + TEXT.pending + + "
"; + } else if (item.status === 1) { + statusBadge.innerHTML = + '
' + + TEXT.success + + "
"; + } + + if (mode === "search") { + const inviteButton = document.createElement("button"); + inviteButton.type = "button"; + inviteButton.className = "result-action-btn"; + inviteButton.textContent = TEXT.invite; + inviteButton.disabled = item.status === 0 || item.status === 1; + inviteButton.addEventListener("click", function (event) { + event.stopPropagation(); + inviteUser(item); + }); + pendingActions.appendChild(inviteButton); + } else if (item.status === 0) { + const cancelButton = document.createElement("div"); + cancelButton.textContent = TEXT.cancel; + cancelButton.style.padding = "4px 8px"; + cancelButton.style.borderRadius = "32px"; + cancelButton.style.backgroundColor = "red"; + cancelButton.style.color = "white"; + cancelButton.style.fontWeight = "600"; + cancelButton.style.zIndex = "5"; + cancelButton.addEventListener("click", function (event) { + event.stopPropagation(); + cancelInvitation(item.id); + }); + pendingActions.appendChild(cancelButton); + } + + return fragment; + } + + function handleAvatarError(event) { + event.target.onerror = null; + event.target.src = DEFAULT_AVATAR; + } + + function extractErrorMessage(error, fallback) { + return ( + (error && + error.response && + (error.response.errorMsg || error.response.message || error.response.error)) || + (error && error.message) || + fallback || + TEXT.somethingWentWrong + ); + } + + function showToast(message, options) { + const config = Object.assign( + { + title: TEXT.informationTitle, + type: "info", + confirmText: TEXT.ok, + }, + options || {} + ); + + const iconMap = { + error: "✕", + warning: "⚠", + success: "✓", + info: "i", + simple: "", + }; + + const overlay = document.createElement("div"); + overlay.className = "modal-overlay"; + overlay.innerHTML = + '"; + + const modal = overlay.querySelector(".modal"); + const closeButton = overlay.querySelector(".modal-btn"); + const close = function () { + modal.classList.remove("modal-enter"); + modal.classList.add("modal-leave"); + window.setTimeout(function () { + overlay.remove(); + }, 200); + }; + + closeButton.addEventListener("click", close); + overlay.addEventListener("click", function (event) { + if (event.target === overlay) { + close(); + } + }); + + document.body.appendChild(overlay); + } + + function escapeHtml(value) { + return String(value) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + } + + async function request(method, path, options) { + const config = options || {}; + const headers = buildRequestHeaders(Boolean(config.body), config.headers || {}); + const response = await fetch(API_BASE + path, { + method: method, + credentials: "include", + headers: headers, + body: config.body ? JSON.stringify(config.body) : undefined, + }); + const text = await response.text(); + let payload = {}; + + if (text) { + try { + payload = JSON.parse(text); + } catch (error) { + payload = { + status: response.ok, + body: text, + }; + } + } + + if (!response.ok || payload.status === false) { + const requestError = new Error( + payload.errorMsg || payload.message || response.statusText || TEXT.somethingWentWrong + ); + requestError.response = payload; + throw requestError; + } + + return payload; + } + + function buildRequestHeaders(hasBody, overrideHeaders) { + const discovered = discoverAppHeaders(); + const headers = Object.assign({}, overrideHeaders || {}); + + if (hasBody) { + headers["Content-Type"] = headers["Content-Type"] || "application/json"; + } + + headers.Accept = headers.Accept || "application/json, text/plain, */*"; + + if (discovered.authorization) { + headers.Authorization = headers.Authorization || discovered.authorization; + } + + if (discovered.reqLang) { + headers["Req-Lang"] = headers["Req-Lang"] || discovered.reqLang; + } + + if (discovered.appVersion) { + headers["App-Version"] = headers["App-Version"] || discovered.appVersion; + } + + if (discovered.buildVersion) { + headers["Build-Version"] = headers["Build-Version"] || discovered.buildVersion; + } + + if (discovered.appChannel) { + headers["App-Channel"] = headers["App-Channel"] || discovered.appChannel; + } + + if (discovered.reqImei) { + headers["Req-Imei"] = headers["Req-Imei"] || discovered.reqImei; + } + + if (discovered.sysOrigin) { + headers["Sys-Origin"] = headers["Sys-Origin"] || discovered.sysOrigin; + } + + if (discovered.sysOriginChild) { + headers["Sys-Origin-Child"] = + headers["Sys-Origin-Child"] || discovered.sysOriginChild; + } + + return headers; + } + + function discoverAppHeaders() { + const query = new URLSearchParams(window.location.search); + const collected = []; + + collectCandidate(collected, window.__APP_HEADERS__); + collectCandidate(collected, window.__HEADER_INFO__); + collectCandidate(collected, window.headerInfo); + collectCandidate(collected, parseHeaderObjectFromQuery(query)); + + collectFromStorage(collected, window.localStorage); + collectFromStorage(collected, window.sessionStorage); + + return collected.reduce(function (result, candidate) { + return Object.assign(result, candidate); + }, { + reqLang: query.get("lang") || document.documentElement.lang || "en", + }); + } + + function parseHeaderObjectFromQuery(query) { + const candidate = {}; + mapHeaderValue(candidate, "authorization", query.get("authorization")); + mapHeaderValue(candidate, "authorization", query.get("token")); + mapHeaderValue(candidate, "reqLang", query.get("lang")); + mapHeaderValue(candidate, "appVersion", query.get("appVersion")); + mapHeaderValue(candidate, "buildVersion", query.get("buildVersion")); + mapHeaderValue(candidate, "appChannel", query.get("appChannel")); + mapHeaderValue(candidate, "reqImei", query.get("reqImei")); + mapHeaderValue(candidate, "sysOrigin", query.get("sysOrigin")); + mapHeaderValue(candidate, "sysOriginChild", query.get("sysOriginChild")); + return candidate; + } + + function collectFromStorage(collected, storage) { + if (!storage) { + return; + } + + const preferredKeys = [ + "authorization", + "Authorization", + "token", + "accessToken", + "headerInfo", + "headersFromApp", + "appHeaders", + "persist:root", + "user", + "userInfo", + ]; + + preferredKeys.forEach(function (key) { + try { + collectCandidate(collected, storage.getItem(key)); + } catch (error) { + return; + } + }); + + for (let index = 0; index < storage.length; index += 1) { + try { + const key = storage.key(index); + collectCandidate(collected, storage.getItem(key)); + } catch (error) { + return; + } + } + } + + function collectCandidate(collected, value) { + const normalized = normalizeHeaderCandidate(value, 0); + if (normalized && Object.keys(normalized).length > 0) { + collected.push(normalized); + } + } + + function normalizeHeaderCandidate(value, depth) { + if (depth > 4 || value == null) { + return null; + } + + if (typeof value === "string") { + const trimmed = value.trim(); + + if (!trimmed) { + return null; + } + + if (trimmed[0] === "{" || trimmed[0] === "[") { + try { + return normalizeHeaderCandidate(JSON.parse(trimmed), depth + 1); + } catch (error) { + return null; + } + } + + if (/bearer\\s+/i.test(trimmed)) { + return { + authorization: trimmed, + }; + } + + return null; + } + + if (Array.isArray(value)) { + return value.reduce(function (result, entry) { + const normalized = normalizeHeaderCandidate(entry, depth + 1); + return normalized ? Object.assign(result, normalized) : result; + }, {}); + } + + if (typeof value !== "object") { + return null; + } + + const normalized = {}; + const aliasMap = { + authorization: ["authorization", "Authorization", "token", "accessToken"], + reqLang: ["reqLang", "Req-Lang", "lang", "language"], + appVersion: ["appVersion", "App-Version"], + buildVersion: ["buildVersion", "Build-Version"], + appChannel: ["appChannel", "App-Channel"], + reqImei: ["reqImei", "Req-Imei"], + sysOrigin: ["sysOrigin", "Sys-Origin"], + sysOriginChild: ["sysOriginChild", "Sys-Origin-Child"], + }; + + Object.keys(aliasMap).forEach(function (targetKey) { + aliasMap[targetKey].forEach(function (sourceKey) { + mapHeaderValue(normalized, targetKey, value[sourceKey]); + }); + }); + + ["headerInfo", "headers", "auth", "data", "state"].forEach(function (nestedKey) { + const nestedValue = normalizeHeaderCandidate(value[nestedKey], depth + 1); + if (nestedValue) { + Object.assign(normalized, nestedValue); + } + }); + + Object.keys(value).forEach(function (key) { + if ( + key === "headerInfo" || + key === "headers" || + key === "auth" || + key === "data" || + key === "state" + ) { + return; + } + + if (typeof value[key] === "object") { + const nestedValue = normalizeHeaderCandidate(value[key], depth + 1); + if (nestedValue) { + Object.assign(normalized, nestedValue); + } + } + }); + + return normalized; + } + + function mapHeaderValue(target, key, value) { + if (value == null || value === "" || target[key]) { + return; + } + + target[key] = String(value); + } +})();