sync(theme-4): 同步 main 最新 API/Admin 与玩家端逻辑

从 main 同步站内信手动发送、媒体库选择、列表子路由重构等 API/Admin 改动;玩家端仅合并 ADMIN_CUSTOM 富文本、消息预览与充值截图压缩逻辑,保留 theme-4 样式。
This commit is contained in:
2026-06-22 14:12:40 +08:00
parent 5fbb1fd1f6
commit cffad00652
50 changed files with 4654 additions and 1186 deletions

View File

@@ -5,10 +5,12 @@ import { useI18n } from 'vue-i18n';
import imageCompression from 'browser-image-compression';
import api from '../api';
import GoldSpinner from '../components/GoldSpinner.vue';
import { useDepositNotifications } from '../composables/useDepositNotifications';
const router = useRouter();
const route = useRoute();
const { t } = useI18n();
const { trackPendingOrder } = useDepositNotifications();
const reapplyOrderId = computed(() => {
const id = route.query.orderId;
@@ -93,30 +95,43 @@ function selectMethod(m: PaymentMethod) {
}
const MAX_ORIGINAL_BYTES = 10 * 1024 * 1024;
const MAX_SCREENSHOT_BYTES = 1024 * 1024;
const MAX_SCREENSHOT_BYTES = 300 * 1024;
async function compressScreenshot(file: File): Promise<File> {
const baseOptions = {
maxSizeMB: 1,
maxWidthOrHeight: 1920,
maxSizeMB: 0.3,
maxWidthOrHeight: 1200,
useWebWorker: true,
maxIteration: 15,
maxIteration: 10,
fileType: 'image/webp',
} as const;
const attempts = [
{ ...baseOptions, initialQuality: 0.85 },
{ ...baseOptions, initialQuality: 0.65, maxWidthOrHeight: 1600 },
{ ...baseOptions, initialQuality: 0.5, maxWidthOrHeight: 1280 },
{ ...baseOptions, initialQuality: 0.8 },
{ ...baseOptions, initialQuality: 0.6, maxWidthOrHeight: 1000 },
];
let compressed: File | null = null;
for (const options of attempts) {
const compressed = (await imageCompression(file, options)) as File;
if (compressed.size <= MAX_SCREENSHOT_BYTES) {
return compressed;
try {
compressed = (await imageCompression(file, options)) as File;
if (compressed.size <= MAX_SCREENSHOT_BYTES) {
break;
}
} catch (err) {
console.error('Compression attempt failed:', err);
}
}
throw new Error('COMPRESS_TOO_LARGE');
if (!compressed) {
throw new Error('COMPRESS_FAILED');
}
const name = file.name.replace(/\.[^/.]+$/, '') + '.webp';
return new File([compressed], name, {
type: 'image/webp',
lastModified: Date.now(),
});
}
async function handleFileChange(event: Event) {
@@ -186,6 +201,7 @@ async function handleSubmit() {
const { data } = await api.post(`/player/deposit-orders/${reapplyOrderId.value}/reapply`, fd);
const result = data.data;
orderNo.value = result?.orderNo ?? '';
if (result?.id) trackPendingOrder(String(result.id));
success.value = true;
return;
}
@@ -194,6 +210,7 @@ async function handleSubmit() {
const { data } = await api.post('/player/deposit-orders', fd);
const result = data.data;
orderNo.value = result?.orderNo ?? '';
if (result?.id) trackPendingOrder(String(result.id));
success.value = true;
} catch (e: any) {
alert(e.response?.data?.message || t('recharge.submit_failed'));