diff --git a/apps/admin/components.d.ts b/apps/admin/components.d.ts index 13b5d35..b2383bf 100644 --- a/apps/admin/components.d.ts +++ b/apps/admin/components.d.ts @@ -69,6 +69,7 @@ declare module 'vue' { InviteHistoryPanel: typeof import('./src/components/InviteHistoryPanel.vue')['default'] InviteManageDialog: typeof import('./src/components/InviteManageDialog.vue')['default'] LeagueArchiveDialog: typeof import('./src/components/LeagueArchiveDialog.vue')['default'] + LeagueRowActions: typeof import('./src/components/LeagueRowActions.vue')['default'] LogoUrlField: typeof import('./src/components/LogoUrlField.vue')['default'] MatchArchiveDialog: typeof import('./src/components/MatchArchiveDialog.vue')['default'] MatchesSubNav: typeof import('./src/components/MatchesSubNav.vue')['default'] diff --git a/apps/admin/src/App.vue b/apps/admin/src/App.vue index 4d8bc84..6dc29bc 100644 --- a/apps/admin/src/App.vue +++ b/apps/admin/src/App.vue @@ -65,6 +65,7 @@ body::-webkit-scrollbar { height: 100%; min-height: 0; overflow: hidden; + gap: 14px; } .admin-list-page > .page-toolbar, .admin-list-page > .filter-card, @@ -78,16 +79,16 @@ body::-webkit-scrollbar { justify-content: flex-end; align-items: center; gap: 8px; - margin: 0 0 8px; + margin: 0; } .admin-list-page > .tool-card { - margin-bottom: 8px; + margin-bottom: 0; } .admin-list-page > .filter-card { - margin-bottom: 8px; + margin-bottom: 0; } .admin-list-page > .list-chrome { - margin-bottom: 8px; + margin-bottom: 0; display: flex; flex-direction: column; gap: 6px; @@ -204,7 +205,7 @@ body::-webkit-scrollbar { margin-right: 0; } .admin-list-page > .list-settings { - margin-bottom: 8px; + margin-bottom: 0; } .list-settings :deep(.el-collapse-item__header) { height: 36px; @@ -250,7 +251,16 @@ body::-webkit-scrollbar { min-height: 0; display: flex; flex-direction: column; - padding: 0 12px 12px; + padding: 12px; +} + +/* data-card 内不再嵌套第二层卡片边框,避免双层顶边重合 */ +.admin-list-page > .data-card .table-wrap, +.admin-list-page > .data-card .admin-table-wrap { + border: none; + border-radius: 0; + box-shadow: none; + background: transparent; } .admin-list-page .table-wrap { flex: 1; diff --git a/apps/admin/src/components/ContentImageField.vue b/apps/admin/src/components/ContentImageField.vue index 1459588..69c9edb 100644 --- a/apps/admin/src/components/ContentImageField.vue +++ b/apps/admin/src/components/ContentImageField.vue @@ -3,6 +3,8 @@ import { ref } from 'vue'; import { ElMessage } from 'element-plus'; import { useAdminLocale } from '../composables/useAdminLocale'; import api from '../api'; +import { fetchMediaLibraryImages } from '../utils/media-library'; +import { resolveApiError } from '../i18n/form-validation'; const props = withDefaults( defineProps<{ @@ -23,7 +25,7 @@ const MAX_UPLOAD_SIZE = 5 * 1024 * 1024; const uploading = ref(false); const mediaPickerVisible = ref(false); -const mediaFiles = ref>([]); +const mediaFiles = ref>([]); const mediaLoading = ref(false); async function uploadImage(file: File) { @@ -67,12 +69,10 @@ async function openMediaPicker() { mediaPickerVisible.value = true; mediaLoading.value = true; try { - const res = await api.get('/admin/files', { - params: { category: props.category, pageSize: 200 }, - }); - mediaFiles.value = res.data.data.items ?? []; - } catch { + mediaFiles.value = await fetchMediaLibraryImages({ pageSize: 200 }); + } catch (err: unknown) { mediaFiles.value = []; + ElMessage.error(resolveApiError(err, t, 'content.upload.load_media_failed')); } finally { mediaLoading.value = false; } @@ -133,6 +133,7 @@ function pickMediaFile(url: string) { width="680px" destroy-on-close append-to-body + :z-index="4000" >
{{ t('common.loading') }}
{{ t('content.upload.no_media') }}
@@ -148,6 +149,7 @@ function pickMediaFile(url: string) {
SVG
{{ file.filename }}
+
{{ file.category }}
@@ -306,11 +308,18 @@ function pickMediaFile(url: string) { } .media-name { - padding: 6px 8px; + padding: 6px 8px 2px; font-size: 11px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + +.media-category { + padding: 0 8px 6px; + font-size: 10px; + color: var(--text-muted); + opacity: 0.85; +} diff --git a/apps/admin/src/components/ContentRichEditor.vue b/apps/admin/src/components/ContentRichEditor.vue index 4590f00..85d014b 100644 --- a/apps/admin/src/components/ContentRichEditor.vue +++ b/apps/admin/src/components/ContentRichEditor.vue @@ -3,6 +3,8 @@ import { ref, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'; import { ElMessage } from 'element-plus'; import { useAdminLocale } from '../composables/useAdminLocale'; import api from '../api'; +import { fetchMediaLibraryImages } from '../utils/media-library'; +import { resolveApiError } from '../i18n/form-validation'; const props = withDefaults( defineProps<{ @@ -27,7 +29,7 @@ const editorRef = ref(null); const mediaPickerVisible = ref(false); /** blob/object URL -> File,保存时由父组件调用 uploadPendingImages 上传 */ const pendingFiles = new Map(); -const mediaFiles = ref>([]); +const mediaFiles = ref>([]); const mediaLoading = ref(false); const syncing = ref(false); let savedRange: Range | null = null; @@ -241,12 +243,10 @@ async function openMediaPicker() { mediaPickerVisible.value = true; mediaLoading.value = true; try { - const res = await api.get('/admin/files', { - params: { category: props.uploadCategory, pageSize: 200 }, - }); - mediaFiles.value = res.data.data.items ?? []; - } catch { + mediaFiles.value = await fetchMediaLibraryImages({ pageSize: 200 }); + } catch (err: unknown) { mediaFiles.value = []; + ElMessage.error(resolveApiError(err, t, 'content.upload.load_media_failed')); } finally { mediaLoading.value = false; } @@ -352,6 +352,7 @@ onBeforeUnmount(() => { width="680px" destroy-on-close append-to-body + :z-index="4000" >
{{ t('common.loading') }}
{{ t('content.upload.no_media') }}
@@ -367,6 +368,7 @@ onBeforeUnmount(() => {
SVG
{{ file.filename }}
+
{{ file.category }}
@@ -521,11 +523,18 @@ onBeforeUnmount(() => { } .media-name { - padding: 6px 8px; + padding: 6px 8px 2px; font-size: 11px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + +.media-category { + padding: 0 8px 6px; + font-size: 10px; + color: var(--text-muted); + opacity: 0.85; +} diff --git a/apps/admin/src/components/LeagueRowActions.vue b/apps/admin/src/components/LeagueRowActions.vue new file mode 100644 index 0000000..40f87f1 --- /dev/null +++ b/apps/admin/src/components/LeagueRowActions.vue @@ -0,0 +1,98 @@ + + + + + diff --git a/apps/admin/src/components/PlayerWalletLedgerDialog.vue b/apps/admin/src/components/PlayerWalletLedgerDialog.vue index 55574ca..6cedd7f 100644 --- a/apps/admin/src/components/PlayerWalletLedgerDialog.vue +++ b/apps/admin/src/components/PlayerWalletLedgerDialog.vue @@ -139,7 +139,7 @@ watch( - + @@ -181,7 +181,7 @@ watch( - + @@ -193,35 +193,35 @@ watch( - + - + - + - + - + - + @@ -298,6 +298,10 @@ watch( diff --git a/apps/admin/src/views/DepositOrders.vue b/apps/admin/src/views/DepositOrders.vue index 2d41b28..e39bd5c 100644 --- a/apps/admin/src/views/DepositOrders.vue +++ b/apps/admin/src/views/DepositOrders.vue @@ -311,8 +311,11 @@ onActivated(() => { {{ row.methodType }} {{ formatAmount(row.amount) }} + + {{ t('media.cleanup_expired_tag') || '已清理' }} + {
{{ t('deposit.screenshot') }}: - +
+ {{ t('media.cleanup_expired_tag') || '已清理' }} +
+
@@ -763,6 +769,37 @@ onActivated(() => { color: #ffffff; } +.expired-screenshot-tag { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 4px 10px; + font-size: 11px; + color: var(--text-muted); + background: var(--accent-hover); + border: 1px dashed var(--border); + border-radius: 6px; + user-select: none; + font-weight: 700; +} + +.expired-screenshot-box { + width: 120px; + height: 120px; + display: inline-flex; + align-items: center; + justify-content: center; + background: var(--accent-hover); + border: 1px dashed var(--border); + border-radius: 6px; + margin-top: 4px; +} +.expired-screenshot-box .expired-text { + font-size: 12px; + color: var(--text-muted); + font-weight: 700; +} + @media (max-width: 700px) { .toolbar, .filters { diff --git a/apps/admin/src/views/Matches.vue b/apps/admin/src/views/Matches.vue index 33fb7d4..7cd1cbd 100644 --- a/apps/admin/src/views/Matches.vue +++ b/apps/admin/src/views/Matches.vue @@ -1,15 +1,15 @@ diff --git a/apps/admin/src/views/MatchesOutrights.vue b/apps/admin/src/views/MatchesOutrights.vue index 243c086..fe5011b 100644 --- a/apps/admin/src/views/MatchesOutrights.vue +++ b/apps/admin/src/views/MatchesOutrights.vue @@ -1,13 +1,12 @@