diff --git a/apps/admin/src/i18n/admin-messages.ts b/apps/admin/src/i18n/admin-messages.ts index d9d3515..c3b34a5 100644 --- a/apps/admin/src/i18n/admin-messages.ts +++ b/apps/admin/src/i18n/admin-messages.ts @@ -87,8 +87,21 @@ const zh: Record = { 'deposit.confirm_reopen': '订单将恢复为待审核,可再次批准或拒绝。确定继续?', 'deposit.delete': '删除', 'deposit.confirm_delete': '【仅删除记录】将永久删除本订单记录及截图文件。确定继续?', + 'deposit.delete_failed': '删除失败', 'deposit.prev': '上一页', 'deposit.next': '下一页', + 'deposit.audit_history': '审核记录', + 'deposit.audit_action': '操作', + 'deposit.audit_actor': '操作人', + 'deposit.audit_time': '时间', + 'deposit.audit_remark': '备注', + 'deposit.audit_submitted': '提交申请', + 'deposit.audit_approved': '审核通过', + 'deposit.audit_rejected': '审核拒绝', + 'deposit.audit_revoked': '撤销审核', + 'deposit.audit_reopened': '重新审核', + 'deposit.audit_deleted': '已删除', + 'deposit.audit_empty': '暂无审核记录', 'deposit.add_method': '+ 添加', 'deposit.display_name': '展示名称', 'deposit.details': '详情', @@ -357,8 +370,21 @@ const en: Record = { 'deposit.confirm_reopen': 'The order will return to pending for approve or reject. Continue?', 'deposit.delete': 'Delete', 'deposit.confirm_delete': '[Records only] Permanently delete this order record and screenshot. Continue?', + 'deposit.delete_failed': 'Delete failed', 'deposit.prev': 'Prev', 'deposit.next': 'Next', + 'deposit.audit_history': 'Review history', + 'deposit.audit_action': 'Action', + 'deposit.audit_actor': 'Actor', + 'deposit.audit_time': 'Time', + 'deposit.audit_remark': 'Remark', + 'deposit.audit_submitted': 'Submitted', + 'deposit.audit_approved': 'Approved', + 'deposit.audit_rejected': 'Rejected', + 'deposit.audit_revoked': 'Approval revoked', + 'deposit.audit_reopened': 'Reopened for review', + 'deposit.audit_deleted': 'Deleted', + 'deposit.audit_empty': 'No review history yet', 'deposit.add_method': '+ Add', 'deposit.display_name': 'Display Name', 'deposit.details': 'Details', @@ -625,8 +651,21 @@ const ms: Record = { 'deposit.confirm_reopen': 'Pesanan kembali menunggu untuk lulus atau tolak. Teruskan?', 'deposit.delete': 'Padam', 'deposit.confirm_delete': '[Rekod sahaja] Padam rekod pesanan dan tangkapan skrin. Teruskan?', + 'deposit.delete_failed': 'Gagal memadam', 'deposit.prev': 'Sebelum', 'deposit.next': 'Seterus', + 'deposit.audit_history': 'Sejarah semakan', + 'deposit.audit_action': 'Tindakan', + 'deposit.audit_actor': 'Pengendali', + 'deposit.audit_time': 'Masa', + 'deposit.audit_remark': 'Catatan', + 'deposit.audit_submitted': 'Dihantar', + 'deposit.audit_approved': 'Diluluskan', + 'deposit.audit_rejected': 'Ditolak', + 'deposit.audit_revoked': 'Kelulusan dibatalkan', + 'deposit.audit_reopened': 'Dibuka semula', + 'deposit.audit_deleted': 'Dipadam', + 'deposit.audit_empty': 'Tiada sejarah semakan', 'deposit.add_method': '+ Tambah', 'deposit.display_name': 'Nama Paparan', 'deposit.details': 'Butiran', diff --git a/apps/admin/src/main.ts b/apps/admin/src/main.ts index 5e86e18..71473ec 100644 --- a/apps/admin/src/main.ts +++ b/apps/admin/src/main.ts @@ -4,6 +4,9 @@ import 'element-plus/dist/index.css'; import App from './App.vue'; import router from './router'; import { createAdminI18n } from './i18n'; +import { applyElementPlusDialogDefaults } from './plugins/element-plus-defaults'; + +applyElementPlusDialogDefaults(); async function bootstrap() { const i18n = await createAdminI18n(); diff --git a/apps/admin/src/plugins/element-plus-defaults.ts b/apps/admin/src/plugins/element-plus-defaults.ts new file mode 100644 index 0000000..eb691fe --- /dev/null +++ b/apps/admin/src/plugins/element-plus-defaults.ts @@ -0,0 +1,6 @@ +import { ElDialog } from 'element-plus'; + +/** Prevent accidental form data loss from mask clicks across admin dialogs. */ +export function applyElementPlusDialogDefaults() { + ElDialog.props.closeOnClickModal.default = false; +} diff --git a/apps/admin/src/views/DepositOrders.vue b/apps/admin/src/views/DepositOrders.vue index 86e8b93..b15f078 100644 --- a/apps/admin/src/views/DepositOrders.vue +++ b/apps/admin/src/views/DepositOrders.vue @@ -2,6 +2,7 @@ import { ref, onMounted } from 'vue'; import { ElMessage, ElMessageBox } from 'element-plus'; import { useAdminLocale } from '../composables/useAdminLocale'; +import { resolveApiError } from '../i18n/form-validation'; import api from '../api'; import AdminTableEmpty from '../components/AdminTableEmpty.vue'; import { formatAmount } from '../utils/format-amount'; @@ -27,6 +28,20 @@ interface DepositOrderRow { paymentMethodName: string | null; } +interface DepositAuditLogRow { + id: string; + action: string; + actorId: string | null; + actorType: string; + actorUsername: string | null; + statusBefore: string | null; + statusAfter: string; + amount: string | null; + approvedAmount: string | null; + remark: string | null; + createdAt: string; +} + const items = ref([]); const total = ref(0); const page = ref(1); @@ -53,6 +68,12 @@ const rejectReason = ref(''); const previewUrl = ref(''); const previewVisible = ref(false); +// Audit history dialog +const auditDialogVisible = ref(false); +const auditTarget = ref(null); +const auditLogs = ref([]); +const auditLoading = ref(false); + async function fetchList() { loading.value = true; try { @@ -87,6 +108,32 @@ function openScreenshot(url: string) { previewVisible.value = true; } +async function openAuditHistory(row: DepositOrderRow) { + auditTarget.value = row; + auditDialogVisible.value = true; + auditLoading.value = true; + auditLogs.value = []; + try { + const { data } = await api.get(`/admin/deposit-orders/${row.id}/audit-logs`); + auditLogs.value = data.data?.items ?? []; + } catch { + auditLogs.value = []; + } finally { + auditLoading.value = false; + } +} + +function auditActionLabel(action: string) { + const key = `deposit.audit_${action.toLowerCase()}`; + const translated = t(key); + return translated !== key ? translated : action; +} + +function auditActorLabel(row: DepositAuditLogRow) { + if (row.actorUsername) return row.actorUsername; + return row.actorType === 'PLAYER' ? t('deposit.player') : '-'; +} + async function confirmDepositAction(message: string, title: string): Promise { try { await ElMessageBox.confirm(message, title, { @@ -101,8 +148,7 @@ async function confirmDepositAction(message: string, title: string): Promise{{ row.remark || row.rejectReason || '-' }} {{ new Date(row.createdAt).toLocaleString() }} +