2026-05-11 18:14:15 +08:00

472 lines
15 KiB
JavaScript

(function () {
const API_BASE_URL = "https://jvapi.haiyihy.com";
const GO_API_PATH_PREFIX = "/go";
const LOCAL_GO_API_BASE_URL = "http://127.0.0.1:1100";
const defaultPropsType = "RIDE";
const state = {
manager: null,
target: null,
propsType: defaultPropsType,
props: [],
selectedPropsId: ""
};
const params = readURLParams();
function readURLParams() {
const result = new URLSearchParams(window.location.search);
const hashQuery = window.location.hash.includes("?") ? window.location.hash.split("?")[1] : "";
const hashParams = new URLSearchParams(hashQuery);
hashParams.forEach((value, key) => {
if (!result.has(key)) result.set(key, value);
});
return result;
}
function readRawParam(name) {
const queries = [window.location.search.replace(/^\?/, "")];
if (window.location.hash.includes("?")) queries.push(window.location.hash.split("?")[1]);
for (const query of queries) {
for (const part of query.split("&")) {
if (!part) continue;
const equalIndex = part.indexOf("=");
const rawKey = equalIndex >= 0 ? part.slice(0, equalIndex) : part;
const rawValue = equalIndex >= 0 ? part.slice(equalIndex + 1) : "";
let decodedKey = "";
try {
decodedKey = decodeURIComponent(rawKey);
} catch (error) {
continue;
}
if (decodedKey !== name) continue;
try {
return decodeURIComponent(rawValue.replace(/\+/g, "%2B"));
} catch (error) {
return rawValue;
}
}
}
return "";
}
function $(selector) {
return document.querySelector(selector);
}
function apiBaseURL() {
const override = window.HYAPP_API_BASE_URL || params.get("apiBase");
if (override) return String(override).replace(/\/+$/, "");
return API_BASE_URL.replace(/\/+$/, "");
}
function isLocalDocumentsFile() {
if (window.location.protocol !== "file:") return false;
let pathname = window.location.pathname || "";
try {
pathname = decodeURIComponent(pathname);
} catch (error) {
return false;
}
return pathname.startsWith("/Users/hy/Documents/");
}
function goApiBaseURL() {
const override = window.HYAPP_GO_API_BASE_URL || params.get("goApiBase") || params.get("goBase");
if (override) return String(override).replace(/\/+$/, "");
if (isLocalDocumentsFile()) return `${LOCAL_GO_API_BASE_URL}${GO_API_PATH_PREFIX}`;
return `${apiBaseURL()}${GO_API_PATH_PREFIX}`;
}
function buildRequestURL(baseURL, path) {
const base = `${String(baseURL || API_BASE_URL).replace(/\/+$/, "")}/`;
const target = String(path || "").replace(/^\/+/, "");
return new URL(target, base).toString();
}
function normalizeAuthorization(token) {
const value = String(token || "").trim();
if (!value) return "";
return /^bearer\s+/i.test(value) ? value : `Bearer ${value}`;
}
function authHeadersFromToken(token) {
const sysOrigin = params.get("sysOrigin") || params.get("origin") || "ATYOU";
return {
Authorization: normalizeAuthorization(token),
"req-lang": params.get("lang") || "zh-CN",
"req-client": "H5",
"req-version": "V2",
"req-zone": Intl.DateTimeFormat().resolvedOptions().timeZone || "Asia/Shanghai",
"req-app-intel": "version=1.0.0;build=1;model=H5;sysVersion=Web;channel=Web",
"req-sys-origin": `origin=${sysOrigin};originChild=${sysOrigin}`
};
}
async function requestGoJSON(path, options = {}) {
const token = readRawParam("token");
if (!token) {
const error = new Error("缺少登录信息");
error.code = "missing_token";
throw error;
}
const headers = authHeadersFromToken(token);
const request = {
method: options.method || "GET",
cache: "no-store",
headers
};
if (options.body !== undefined) {
headers["Content-Type"] = "application/json";
request.body = JSON.stringify(options.body);
}
const response = await fetch(buildRequestURL(goApiBaseURL(), path), request);
const data = await response.json().catch(() => ({}));
if (!response.ok || data.status === false) {
const error = new Error(data.errorMsg || data.message || `Request failed: ${response.status}`);
error.code = data.code || data.errorCodeName || "";
error.response = data;
throw error;
}
return data.body ?? data.data ?? null;
}
function endpointProfile() {
return "/app/h5/manager-center/profile";
}
function endpointSearchUser(account) {
return `/app/h5/manager-center/users/search?account=${encodeURIComponent(account)}`;
}
function endpointProps(type) {
return `/app/h5/manager-center/props?type=${encodeURIComponent(type || "")}`;
}
function endpointSendProps() {
return "/app/h5/manager-center/props/send";
}
function endpointBanUser() {
return "/app/h5/manager-center/users/ban";
}
function setLoading(isLoading) {
const root = $(".manager-center");
const mask = $("#loadingMask");
if (root) root.dataset.loading = isLoading ? "true" : "false";
if (mask) mask.hidden = !isLoading;
}
function setStatus(node, text, type = "") {
if (!node) return;
node.textContent = text || "";
node.hidden = !text;
node.classList.toggle("is-error", type === "error");
}
function showToast(text) {
const toast = $("#toast");
if (!toast) return;
toast.textContent = text || "";
toast.hidden = !text;
window.clearTimeout(showToast.timer);
showToast.timer = window.setTimeout(() => {
toast.hidden = true;
}, 2300);
}
function closePage() {
try {
if (window.app && typeof window.app.closePage === "function") {
window.app.closePage();
return;
}
if (window.FlutterPageControl && typeof window.FlutterPageControl.postMessage === "function") {
window.FlutterPageControl.postMessage("close_page");
return;
}
if (window.history.length > 1) window.history.back();
} catch (error) {
console.error("close manager center failed:", error);
if (window.history.length > 1) window.history.back();
}
}
function firstLetter(name, fallback) {
const value = String(name || fallback || "U").trim();
return value ? value.slice(0, 1).toUpperCase() : "U";
}
function renderAvatar(imgSelector, fallbackSelector, url, name, fallback) {
const img = $(imgSelector);
const fallbackNode = $(fallbackSelector);
if (!img || !fallbackNode) return;
const src = String(url || "").trim();
fallbackNode.textContent = firstLetter(name, fallback);
if (src) {
img.src = src;
img.hidden = false;
fallbackNode.hidden = true;
} else {
img.removeAttribute("src");
img.hidden = true;
fallbackNode.hidden = false;
}
}
function displayName(user) {
return user?.userNickname || user?.nickname || user?.name || "-";
}
function displayUserID(user) {
return String(user?.userId || user?.id || "-");
}
function displayAccount(user) {
return user?.account ? ` (${user.account})` : "";
}
function canBanTarget(target) {
return Boolean(target) && target.canBan !== false && !target.hasRecharge;
}
function renderManager(manager) {
$("#managerName").textContent = displayName(manager);
$("#managerUid").textContent = `UID: ${displayUserID(manager)}${displayAccount(manager)}`;
renderAvatar("#managerAvatar", "#managerAvatarFallback", manager?.userAvatar, displayName(manager), "M");
}
function renderTarget() {
const card = $("#targetCard");
const banCard = $("#banCard");
if (!state.target) {
if (card) card.hidden = true;
if (banCard) banCard.hidden = true;
updateActionState();
return;
}
const target = state.target;
card.hidden = false;
banCard.hidden = false;
$("#targetName").textContent = displayName(target);
$("#targetUid").textContent = `UID: ${displayUserID(target)}${displayAccount(target)}`;
$("#targetAccountStatus").textContent = target.accountStatus || "-";
const rechargeStatus = $("#targetRechargeStatus");
const canBan = canBanTarget(target);
rechargeStatus.textContent = target.hasRecharge ? "已充值" : (canBan ? "未充值" : "充值状态未知");
rechargeStatus.classList.toggle("is-danger", Boolean(target.hasRecharge) || !canBan);
const banEligibility = $("#banEligibility");
banEligibility.textContent = canBan ? "可封禁" : "不可封禁";
banEligibility.classList.toggle("is-danger", !canBan);
renderAvatar("#targetAvatar", "#targetAvatarFallback", target.userAvatar, displayName(target), "U");
updateActionState();
}
function renderProps() {
const list = $("#propsList");
if (!list) return;
list.replaceChildren();
if (!state.props.length) {
setStatus($("#propsStatus"), "没有可赠送道具");
updateActionState();
return;
}
setStatus($("#propsStatus"), "");
state.props.forEach((item) => {
const button = document.createElement("button");
button.type = "button";
button.className = "prop-option";
button.dataset.propsId = String(item.id);
if (String(item.id) === String(state.selectedPropsId)) {
button.classList.add("is-selected");
}
const cover = document.createElement("span");
cover.className = "prop-cover";
if (item.cover || item.sourceUrl) {
const img = document.createElement("img");
img.alt = "";
img.src = item.cover || item.sourceUrl;
cover.appendChild(img);
} else {
const fallback = document.createElement("span");
fallback.textContent = firstLetter(item.name, "G");
cover.appendChild(fallback);
}
const copy = document.createElement("span");
copy.className = "prop-copy";
const name = document.createElement("span");
name.className = "prop-name";
name.textContent = item.name || `ID ${item.id}`;
const meta = document.createElement("span");
meta.className = "prop-meta";
meta.textContent = item.vipLevel ? `${item.type} · VIP ${item.vipLevel}` : item.type;
copy.append(name, meta);
const days = document.createElement("span");
days.className = "prop-days";
days.textContent = `${item.days || 0}`;
button.append(cover, copy, days);
button.addEventListener("click", () => {
state.selectedPropsId = String(item.id);
renderProps();
updateActionState();
});
list.appendChild(button);
});
updateActionState();
}
function updateActionState() {
const sendButton = $("#sendPropsButton");
const banButton = $("#banButton");
if (sendButton) sendButton.disabled = !state.target || !state.selectedPropsId;
if (banButton) banButton.disabled = !canBanTarget(state.target);
}
async function loadProfile() {
const body = await requestGoJSON(endpointProfile());
state.manager = body?.manager || body || {};
renderManager(state.manager);
}
async function loadProps() {
setStatus($("#propsStatus"), "加载中...");
state.props = [];
state.selectedPropsId = "";
renderProps();
const body = await requestGoJSON(endpointProps(state.propsType));
state.props = Array.isArray(body?.records) ? body.records : [];
state.selectedPropsId = state.props.length ? String(state.props[0].id) : "";
renderProps();
}
async function searchUser(event) {
event.preventDefault();
const input = $("#accountInput");
const account = String(input?.value || "").trim();
if (!account) {
setStatus($("#searchStatus"), "请输入用户 ID 或账号", "error");
return;
}
$("#searchButton").disabled = true;
setStatus($("#searchStatus"), "搜索中...");
try {
const body = await requestGoJSON(endpointSearchUser(account));
state.target = body?.user || body || null;
renderTarget();
setStatus($("#searchStatus"), "");
} catch (error) {
state.target = null;
renderTarget();
setStatus($("#searchStatus"), friendlyError(error), "error");
} finally {
$("#searchButton").disabled = false;
}
}
async function sendProps() {
if (!state.target || !state.selectedPropsId) return;
if (!window.confirm("确认赠送该道具?")) return;
const button = $("#sendPropsButton");
button.disabled = true;
try {
await requestGoJSON(endpointSendProps(), {
method: "POST",
body: {
acceptUserId: displayUserID(state.target),
propsId: state.selectedPropsId
}
});
showToast("赠送成功");
} catch (error) {
showToast(friendlyError(error));
} finally {
updateActionState();
}
}
async function banUser() {
if (!canBanTarget(state.target)) return;
if (!window.confirm("确认封禁该用户?")) return;
const button = $("#banButton");
button.disabled = true;
setStatus($("#banStatus"), "提交中...");
try {
await requestGoJSON(endpointBanUser(), {
method: "POST",
body: {
userId: displayUserID(state.target),
reason: $("#banReason").value || ""
}
});
state.target.accountStatus = "ARCHIVE";
renderTarget();
setStatus($("#banStatus"), "");
showToast("封禁成功");
} catch (error) {
setStatus($("#banStatus"), friendlyError(error), "error");
} finally {
updateActionState();
}
}
function friendlyError(error) {
const code = error?.code || error?.response?.code || error?.response?.errorCodeName || "";
if (code === "missing_token") return "缺少登录信息";
if (code === "not_manager") return "当前账号不是经理";
if (code === "user_not_found") return "用户不存在";
if (code === "user_recharged") return "该用户已有充值记录,不能封禁";
if (code === "prop_not_giftable") return "该道具未开放经理赠送";
if (code === "vip_not_giftable") return "该贵族道具不可赠送";
return error?.message || "请求失败";
}
function bindEvents() {
$(".back-button")?.addEventListener("click", closePage);
$("#searchForm")?.addEventListener("submit", searchUser);
$("#propsTypeSelect")?.addEventListener("change", async (event) => {
state.propsType = event.target.value || defaultPropsType;
try {
await loadProps();
} catch (error) {
setStatus($("#propsStatus"), friendlyError(error), "error");
}
});
$("#sendPropsButton")?.addEventListener("click", sendProps);
$("#banButton")?.addEventListener("click", banUser);
}
async function init() {
bindEvents();
$("#propsTypeSelect").value = state.propsType;
if (!readRawParam("token")) {
setLoading(false);
setStatus($("#searchStatus"), "缺少登录信息", "error");
return;
}
try {
await Promise.all([loadProfile(), loadProps()]);
} catch (error) {
showToast(friendlyError(error));
} finally {
setLoading(false);
}
}
init();
})();