feat: 开户备注、账单展示优化与后台代理管理增强

- 新增初始上分备注(日常上分/开户赠金/自定义)及前后台校验与展示

- 优化钱包流水类型与备注显示,区分管理员/代理/玩家上下分

- 修复登录后语言被后端覆盖的问题,登录时同步当前语言到服务端

- 后台代理/玩家表格操作栏重构,充值订单增加备注列

- 前台个人中心、充值、账单与验证码组件体验优化

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-11 17:23:58 +08:00
parent 10485ecfaf
commit 03e72ca9b2
46 changed files with 3721 additions and 1059 deletions

View File

@@ -44,11 +44,25 @@ export function useAppLocale() {
applyLocale(code);
}
/** 将当前语言同步到后端,仅在有 token 时执行,不修改本地 locale */
async function syncLocaleToBackend() {
if (!auth.token) return;
try {
await api.post('/player/language', { locale: locale.value });
if (auth.user) {
auth.user = { ...auth.user, locale: locale.value };
localStorage.setItem('user', JSON.stringify(auth.user));
}
} catch {
/* ignore */
}
}
function initFromUser(userLocale?: string | null) {
if (userLocale && (SUPPORTED_LOCALES as readonly string[]).includes(userLocale)) {
applyLocale(userLocale);
}
}
return { locales: APP_LOCALES, setLocale, applyLocale, initFromUser };
return { locales: APP_LOCALES, setLocale, applyLocale, initFromUser, syncLocaleToBackend };
}

View File

@@ -16,8 +16,10 @@ export function usePullToRefresh(options: PullToRefreshOptions) {
const progress = computed(() => Math.min(pullDistance.value / maxPull, 1));
let scrollEl: HTMLElement | null = null;
let startX = 0;
let startY = 0;
let pulling = false;
let locked = false;
function findScrollEl(): HTMLElement | null {
return document.querySelector('.layout > .main') as HTMLElement | null;
@@ -41,19 +43,38 @@ export function usePullToRefresh(options: PullToRefreshOptions) {
if (!scrollEl || refreshing.value) return;
if (scrollEl.scrollTop > 4) return;
if (isInsideScrollableChild(e.target)) return;
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
pulling = true;
locked = false;
}
function handleTouchMove(e: TouchEvent) {
if (!pulling || refreshing.value) return;
const delta = e.touches[0].clientY - startY;
if (delta <= 0) {
const dx = e.touches[0].clientX - startX;
const dy = e.touches[0].clientY - startY;
if (!locked) {
if (Math.abs(dx) > Math.abs(dy) * 1.5) {
pulling = false;
pullDistance.value = 0;
spinning.value = false;
return;
}
if (dy > 0) {
locked = true;
}
}
if (!locked) return;
if (dy <= 0) {
pullDistance.value = 0;
spinning.value = false;
return;
}
const damped = Math.min(delta * 0.7, maxPull);
e.preventDefault();
const damped = Math.min(dy * 0.7, maxPull);
pullDistance.value = damped;
spinning.value = damped >= threshold * 0.5;
}
@@ -61,6 +82,7 @@ export function usePullToRefresh(options: PullToRefreshOptions) {
function handleTouchEnd() {
if (!pulling) return;
pulling = false;
locked = false;
if (pullDistance.value >= threshold && !refreshing.value) {
refreshing.value = true;
spinning.value = true;