feat(admin,api,player): 代理层级管理、额度上下分与玩家钱包详情

新增代理管理器与二级代理体系,完善信用额度/上下分上下文与冻结策略;代理端玩家与子代理管理增强;玩家端新增钱包详情页与交易筛选优化。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-08 15:34:12 +08:00
parent b2216abd0c
commit 414998ce36
54 changed files with 6641 additions and 481 deletions

View File

@@ -5,6 +5,7 @@ import { ElMessage, ElMessageBox } from 'element-plus';
import { useAdminLocale } from '../composables/useAdminLocale';
import { formatAmount, formatAmountFull } from '../utils/format-amount';
import AdminTableEmpty from '../components/AdminTableEmpty.vue';
import { resolveApiError } from '../i18n/form-validation';
import api from '../api';
interface CashbackBatch {
@@ -104,13 +105,6 @@ function statusTagType(status: string) {
return 'warning';
}
function apiErrorMessage(err: unknown, fallback: string) {
const msg = (err as { response?: { data?: { message?: string | string[] } } })?.response?.data?.message;
if (Array.isArray(msg)) return msg.join('');
if (typeof msg === 'string' && msg.trim()) return msg;
return fallback;
}
function tableSummary(param: {
columns: TableColumnCtx<CashbackPreviewItem>[];
data: CashbackPreviewItem[];
@@ -141,6 +135,8 @@ async function loadHistory() {
});
history.value = (data.data.items ?? []) as CashbackBatch[];
historyTotal.value = data.data.total ?? 0;
} catch (err) {
ElMessage.error(resolveApiError(err, t, 'msg.load_failed'));
} finally {
historyLoading.value = false;
}
@@ -162,7 +158,7 @@ async function generatePreview() {
}
await loadHistory();
} catch (err) {
ElMessage.error(apiErrorMessage(err, t('msg.error')));
ElMessage.error(resolveApiError(err, t, 'msg.save_failed'));
} finally {
loading.value = false;
}
@@ -186,7 +182,7 @@ async function confirmBatchId(batchId: string) {
if (detail.value?.batch.id === batchId) detailVisible.value = false;
await loadHistory();
} catch (err) {
ElMessage.error(apiErrorMessage(err, t('msg.error')));
ElMessage.error(resolveApiError(err, t, 'msg.save_failed'));
}
}
@@ -208,7 +204,7 @@ async function cancelBatchId(batchId: string) {
if (detail.value?.batch.id === batchId) detailVisible.value = false;
await loadHistory();
} catch (err) {
ElMessage.error(apiErrorMessage(err, t('msg.error')));
ElMessage.error(resolveApiError(err, t, 'msg.save_failed'));
}
}
@@ -224,6 +220,9 @@ async function openDetail(batchId: string) {
try {
const { data } = await api.get(`/admin/cashbacks/${batchId}`);
detail.value = data.data as CashbackPreview;
} catch (err) {
detailVisible.value = false;
ElMessage.error(resolveApiError(err, t, 'msg.load_failed'));
} finally {
detailLoading.value = false;
}