feat: add manager gift duration options

This commit is contained in:
zhx 2026-07-16 19:58:45 +08:00
parent 09df809a5d
commit 22c188b84f
3 changed files with 101 additions and 8 deletions

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<title>Manager Center</title> <title>Manager Center</title>
<link rel="stylesheet" href="./style.css?v=20260626-0100" /> <link rel="stylesheet" href="./style.css?v=20260716-gift-days" />
</head> </head>
<body> <body>
<div class="manager-center" aria-label="Manager Center" data-i18n-aria="page_label" data-loading="true"> <div class="manager-center" aria-label="Manager Center" data-i18n-aria="page_label" data-loading="true">
@ -224,6 +224,27 @@
<button type="button" role="option" data-value="DATA_CARD" data-i18n="prop_type_data_card">Profile Card</button> <button type="button" role="option" data-value="DATA_CARD" data-i18n="prop_type_data_card">Profile Card</button>
</div> </div>
</div> </div>
<label class="field-label gift-days-label" id="giftDaysLabelText" data-i18n="gift_duration">Gift Duration</label>
<div class="custom-select" id="giftDaysSelect" data-value="30">
<button
class="custom-select-button"
id="giftDaysButton"
type="button"
aria-expanded="false"
aria-haspopup="listbox"
aria-labelledby="giftDaysLabelText giftDaysSelectedLabel"
>
<span id="giftDaysSelectedLabel">30 days</span>
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="m7 9 5 5 5-5" />
</svg>
</button>
<div class="custom-select-menu" id="giftDaysMenu" role="listbox" aria-labelledby="giftDaysLabelText" hidden>
<button type="button" role="option" data-value="30">30 days</button>
<button type="button" role="option" data-value="60">60 days</button>
<button type="button" role="option" data-value="90">90 days</button>
</div>
</div>
<div class="props-status" id="propsStatus" role="status" hidden></div> <div class="props-status" id="propsStatus" role="status" hidden></div>
<div class="props-list" id="propsList"></div> <div class="props-list" id="propsList"></div>
<button class="primary-action" id="sendPropsButton" type="button" disabled data-i18n="send">Send</button> <button class="primary-action" id="sendPropsButton" type="button" disabled data-i18n="send">Send</button>
@ -264,6 +285,6 @@
</div> </div>
<div class="toast" id="toast" role="status" hidden></div> <div class="toast" id="toast" role="status" hidden></div>
</div> </div>
<script src="./script.js?v=20260626-0100" defer></script> <script src="./script.js?v=20260716-gift-days" defer></script>
</body> </body>
</html> </html>

View File

@ -12,6 +12,8 @@
const defaultPropsType = "RIDE"; const defaultPropsType = "RIDE";
// 后端经理中心 VIP 赠送默认 30 天;接口缺 days 时前端兜底展示同一口径。 // 后端经理中心 VIP 赠送默认 30 天;接口缺 days 时前端兜底展示同一口径。
const defaultNobleVIPGiftDays = 30; const defaultNobleVIPGiftDays = 30;
// 经理赠送只开放运营确认的三个有效期,展示与提交共用该列表。
const giftDayOptions = [30, 60, 90];
const fallbackMessages = { const fallbackMessages = {
en: { en: {
@ -46,6 +48,7 @@
gift_props: "Gift Props", gift_props: "Gift Props",
gift_props_disabled: "Gift Props disabled", gift_props_disabled: "Gift Props disabled",
gift_type: "Gift Type", gift_type: "Gift Type",
gift_duration: "Gift Duration",
prop_type_ride: "Ride", prop_type_ride: "Ride",
prop_type_noble_vip: "Noble VIP", prop_type_noble_vip: "Noble VIP",
prop_type_avatar_frame: "Avatar Frame", prop_type_avatar_frame: "Avatar Frame",
@ -93,6 +96,7 @@
country_required: "Select country", country_required: "Select country",
prop_not_giftable: "This prop is not available for manager gifting", prop_not_giftable: "This prop is not available for manager gifting",
vip_not_giftable: "This noble prop cannot be gifted", vip_not_giftable: "This noble prop cannot be gifted",
invalid_gift_days: "Gift duration must be 30, 60, or 90 days",
manager_feature_disabled: "This feature is disabled by admin", manager_feature_disabled: "This feature is disabled by admin",
request_failed: "Request failed", request_failed: "Request failed",
enter_user: "Enter user ID or account" enter_user: "Enter user ID or account"
@ -129,6 +133,7 @@
gift_props: "إرسال العناصر", gift_props: "إرسال العناصر",
gift_props_disabled: "إرسال العناصر معطل", gift_props_disabled: "إرسال العناصر معطل",
gift_type: "نوع الهدية", gift_type: "نوع الهدية",
gift_duration: "مدة الهدية",
prop_type_ride: "مركبة", prop_type_ride: "مركبة",
prop_type_noble_vip: "VIP نبيل", prop_type_noble_vip: "VIP نبيل",
prop_type_avatar_frame: "إطار الصورة", prop_type_avatar_frame: "إطار الصورة",
@ -176,6 +181,7 @@
country_required: "Select country", country_required: "Select country",
prop_not_giftable: "هذا العنصر غير متاح كهديّة من المدير", prop_not_giftable: "هذا العنصر غير متاح كهديّة من المدير",
vip_not_giftable: "لا يمكن إهداء عنصر VIP هذا", vip_not_giftable: "لا يمكن إهداء عنصر VIP هذا",
invalid_gift_days: "يجب أن تكون مدة الهدية 30 أو 60 أو 90 يومًا",
manager_feature_disabled: "تم تعطيل هذه الميزة من الإدارة", manager_feature_disabled: "تم تعطيل هذه الميزة من الإدارة",
request_failed: "فشل الطلب", request_failed: "فشل الطلب",
enter_user: "أدخل معرّف المستخدم أو الحساب" enter_user: "أدخل معرّف المستخدم أو الحساب"
@ -212,6 +218,7 @@
gift_props: "Eşya Gönder", gift_props: "Eşya Gönder",
gift_props_disabled: "Eşya gönderme kapalı", gift_props_disabled: "Eşya gönderme kapalı",
gift_type: "Hediye Türü", gift_type: "Hediye Türü",
gift_duration: "Hediye Süresi",
prop_type_ride: "Araç", prop_type_ride: "Araç",
prop_type_noble_vip: "Noble VIP", prop_type_noble_vip: "Noble VIP",
prop_type_avatar_frame: "Avatar Çerçevesi", prop_type_avatar_frame: "Avatar Çerçevesi",
@ -259,6 +266,7 @@
country_required: "Select country", country_required: "Select country",
prop_not_giftable: "Bu eşya yönetici hediyesi için açık değil", prop_not_giftable: "Bu eşya yönetici hediyesi için açık değil",
vip_not_giftable: "Bu noble eşyası gönderilemez", vip_not_giftable: "Bu noble eşyası gönderilemez",
invalid_gift_days: "Hediye süresi 30, 60 veya 90 gün olmalıdır",
manager_feature_disabled: "Bu özellik admin tarafından kapatıldı", manager_feature_disabled: "Bu özellik admin tarafından kapatıldı",
request_failed: "İstek başarısız", request_failed: "İstek başarısız",
enter_user: "Kullanıcı ID veya hesap gir" enter_user: "Kullanıcı ID veya hesap gir"
@ -295,6 +303,7 @@
gift_props: "Kirim Properti", gift_props: "Kirim Properti",
gift_props_disabled: "Kirim properti dinonaktifkan", gift_props_disabled: "Kirim properti dinonaktifkan",
gift_type: "Jenis Hadiah", gift_type: "Jenis Hadiah",
gift_duration: "Durasi Hadiah",
prop_type_ride: "Kendaraan", prop_type_ride: "Kendaraan",
prop_type_noble_vip: "Noble VIP", prop_type_noble_vip: "Noble VIP",
prop_type_avatar_frame: "Bingkai Avatar", prop_type_avatar_frame: "Bingkai Avatar",
@ -342,6 +351,7 @@
country_required: "Select country", country_required: "Select country",
prop_not_giftable: "Properti ini tidak tersedia untuk hadiah manajer", prop_not_giftable: "Properti ini tidak tersedia untuk hadiah manajer",
vip_not_giftable: "Properti noble ini tidak dapat dikirim", vip_not_giftable: "Properti noble ini tidak dapat dikirim",
invalid_gift_days: "Durasi hadiah harus 30, 60, atau 90 hari",
manager_feature_disabled: "Fitur ini dinonaktifkan oleh admin", manager_feature_disabled: "Fitur ini dinonaktifkan oleh admin",
request_failed: "Permintaan gagal", request_failed: "Permintaan gagal",
enter_user: "Masukkan ID pengguna atau akun" enter_user: "Masukkan ID pengguna atau akun"
@ -356,6 +366,7 @@
propsType: defaultPropsType, propsType: defaultPropsType,
props: [], props: [],
selectedPropsId: "", selectedPropsId: "",
selectedGiftDays: defaultNobleVIPGiftDays,
countries: [], countries: [],
selectedCountryId: "", selectedCountryId: "",
countriesLoading: false, countriesLoading: false,
@ -533,6 +544,17 @@
button.setAttribute("aria-expanded", String(open)); button.setAttribute("aria-expanded", String(open));
} }
function setGiftDaysMenuOpen(open) {
const root = $("#giftDaysSelect");
const button = $("#giftDaysButton");
const menu = $("#giftDaysMenu");
if (!root || !button || !menu) return;
root.classList.toggle("is-open", open);
menu.hidden = !open;
button.setAttribute("aria-expanded", String(open));
}
function setCountryMenuOpen(open) { function setCountryMenuOpen(open) {
const root = $("#countrySelect"); const root = $("#countrySelect");
const button = $("#countryButton"); const button = $("#countryButton");
@ -557,6 +579,30 @@
}); });
} }
function renderGiftDaysSelect() {
const root = $("#giftDaysSelect");
const label = $("#giftDaysSelectedLabel");
if (root) root.dataset.value = String(state.selectedGiftDays);
if (label) label.textContent = formatDays(state.selectedGiftDays);
document.querySelectorAll("#giftDaysMenu [data-value]").forEach((button) => {
const days = Number(button.dataset.value);
const isSelected = days === state.selectedGiftDays;
button.textContent = formatDays(days);
button.classList.toggle("is-selected", isSelected);
button.setAttribute("aria-selected", String(isSelected));
});
}
function chooseGiftDays(value) {
const days = Number(value);
if (!giftDayOptions.includes(days)) return;
state.selectedGiftDays = days;
setGiftDaysMenuOpen(false);
renderGiftDaysSelect();
renderProps();
}
function renderCountrySelect() { function renderCountrySelect() {
const root = $("#countrySelect"); const root = $("#countrySelect");
const label = $("#countrySelectedLabel"); const label = $("#countrySelectedLabel");
@ -593,6 +639,7 @@
async function choosePropsType(value) { async function choosePropsType(value) {
const nextValue = String(value || defaultPropsType); const nextValue = String(value || defaultPropsType);
setPropsTypeMenuOpen(false); setPropsTypeMenuOpen(false);
setGiftDaysMenuOpen(false);
setCountryMenuOpen(false); setCountryMenuOpen(false);
if (nextValue === state.propsType) { if (nextValue === state.propsType) {
renderPropsTypeSelect(); renderPropsTypeSelect();
@ -618,6 +665,7 @@
renderMessages(messages); renderMessages(messages);
renderLanguageControls(messages); renderLanguageControls(messages);
renderPropsTypeSelect(); renderPropsTypeSelect();
renderGiftDaysSelect();
renderCountrySelect(); renderCountrySelect();
renderManager(state.manager); renderManager(state.manager);
renderTarget(); renderTarget();
@ -1038,10 +1086,8 @@
} }
} }
function giftDays(item) { function giftDays() {
const days = Number(item?.days); return giftDayOptions.includes(state.selectedGiftDays) ? state.selectedGiftDays : defaultNobleVIPGiftDays;
if (Number.isFinite(days) && days > 0) return days;
return String(item?.type || state.propsType).toUpperCase() === "NOBLE_VIP" ? defaultNobleVIPGiftDays : 7;
} }
function renderManager(manager) { function renderManager(manager) {
@ -1148,7 +1194,7 @@
const days = document.createElement("span"); const days = document.createElement("span");
days.className = "prop-days"; days.className = "prop-days";
days.textContent = formatDays(giftDays(item)); days.textContent = formatDays(giftDays());
button.append(cover, copy, days); button.append(cover, copy, days);
button.addEventListener("click", () => { button.addEventListener("click", () => {
@ -1438,7 +1484,8 @@
method: "POST", method: "POST",
body: { body: {
acceptUserId: displayUserID(state.target), acceptUserId: displayUserID(state.target),
propsId: state.selectedPropsId propsId: state.selectedPropsId,
days: state.selectedGiftDays
} }
}); });
closeModals(); closeModals();
@ -1574,6 +1621,7 @@
if (code === "country_required") return message("country_required"); if (code === "country_required") return message("country_required");
if (code === "prop_not_giftable") return message("prop_not_giftable"); if (code === "prop_not_giftable") return message("prop_not_giftable");
if (code === "vip_not_giftable") return message("vip_not_giftable"); if (code === "vip_not_giftable") return message("vip_not_giftable");
if (code === "invalid_gift_days") return message("invalid_gift_days");
if (code === "manager_feature_disabled") return message("manager_feature_disabled"); if (code === "manager_feature_disabled") return message("manager_feature_disabled");
return error?.message || message("request_failed"); return error?.message || message("request_failed");
} }
@ -1582,6 +1630,8 @@
$(".back-button")?.addEventListener("click", closePage); $(".back-button")?.addEventListener("click", closePage);
$(".language-button")?.addEventListener("click", (event) => { $(".language-button")?.addEventListener("click", (event) => {
event.stopPropagation(); event.stopPropagation();
setPropsTypeMenuOpen(false);
setGiftDaysMenuOpen(false);
setCountryMenuOpen(false); setCountryMenuOpen(false);
setLanguageMenuOpen(Boolean($(".language-menu")?.hidden)); setLanguageMenuOpen(Boolean($(".language-menu")?.hidden));
}); });
@ -1595,6 +1645,7 @@
document.addEventListener("click", () => { document.addEventListener("click", () => {
setLanguageMenuOpen(false); setLanguageMenuOpen(false);
setPropsTypeMenuOpen(false); setPropsTypeMenuOpen(false);
setGiftDaysMenuOpen(false);
setCountryMenuOpen(false); setCountryMenuOpen(false);
}); });
$("#addBDLeaderButton")?.addEventListener("click", () => openActionModal("addBDLeader")); $("#addBDLeaderButton")?.addEventListener("click", () => openActionModal("addBDLeader"));
@ -1620,6 +1671,7 @@
event.stopPropagation(); event.stopPropagation();
setLanguageMenuOpen(false); setLanguageMenuOpen(false);
setCountryMenuOpen(false); setCountryMenuOpen(false);
setGiftDaysMenuOpen(false);
setPropsTypeMenuOpen(Boolean($("#propsTypeMenu")?.hidden)); setPropsTypeMenuOpen(Boolean($("#propsTypeMenu")?.hidden));
}); });
document.querySelectorAll("#propsTypeMenu [data-value]").forEach((button) => { document.querySelectorAll("#propsTypeMenu [data-value]").forEach((button) => {
@ -1628,16 +1680,31 @@
await choosePropsType(button.dataset.value); await choosePropsType(button.dataset.value);
}); });
}); });
$("#giftDaysButton")?.addEventListener("click", (event) => {
event.stopPropagation();
setLanguageMenuOpen(false);
setPropsTypeMenuOpen(false);
setCountryMenuOpen(false);
setGiftDaysMenuOpen(Boolean($("#giftDaysMenu")?.hidden));
});
document.querySelectorAll("#giftDaysMenu [data-value]").forEach((button) => {
button.addEventListener("click", (event) => {
event.stopPropagation();
chooseGiftDays(button.dataset.value);
});
});
$("#countryButton")?.addEventListener("click", (event) => { $("#countryButton")?.addEventListener("click", (event) => {
event.stopPropagation(); event.stopPropagation();
setLanguageMenuOpen(false); setLanguageMenuOpen(false);
setPropsTypeMenuOpen(false); setPropsTypeMenuOpen(false);
setGiftDaysMenuOpen(false);
setCountryMenuOpen(Boolean($("#countryMenu")?.hidden)); setCountryMenuOpen(Boolean($("#countryMenu")?.hidden));
}); });
document.addEventListener("keydown", (event) => { document.addEventListener("keydown", (event) => {
if (event.key !== "Escape") return; if (event.key !== "Escape") return;
setLanguageMenuOpen(false); setLanguageMenuOpen(false);
setPropsTypeMenuOpen(false); setPropsTypeMenuOpen(false);
setGiftDaysMenuOpen(false);
setCountryMenuOpen(false); setCountryMenuOpen(false);
closeModals(); closeModals();
}); });
@ -1652,6 +1719,7 @@
async function init() { async function init() {
bindEvents(); bindEvents();
renderPropsTypeSelect(); renderPropsTypeSelect();
renderGiftDaysSelect();
await applyLanguage(currentLang); await applyLanguage(currentLang);
if (!readRawParam("token")) { if (!readRawParam("token")) {
setLoading(false); setLoading(false);

View File

@ -634,6 +634,10 @@ textarea:focus {
max-height: min(24vh, 144px); max-height: min(24vh, 144px);
} }
.gift-days-label {
margin-top: 12px;
}
.custom-select-menu button { .custom-select-menu button {
min-height: 36px; min-height: 36px;
border-radius: 6px; border-radius: 6px;