🎨 统一后台分页与设置页布局

This commit is contained in:
2026-06-05 15:23:25 +08:00
parent a66a0b6a77
commit f7108f5b44
5 changed files with 58 additions and 58 deletions
+1 -1
View File
@@ -97,7 +97,7 @@ interface Props {
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
pageSizeOptions: () => [10, 20, 50, 100] pageSizeOptions: () => [5, 10, 20, 50, 100]
}) })
defineEmits<{ defineEmits<{
+10 -7
View File
@@ -81,6 +81,8 @@ const legacyForeverExpiresAt = '2099-12-31T23:59'
const detailPolicyDownloadLimit = 5 const detailPolicyDownloadLimit = 5
const hourInMilliseconds = 60 * 60 * 1000 const hourInMilliseconds = 60 * 60 * 1000
const dayInMilliseconds = 24 * 60 * 60 * 1000 const dayInMilliseconds = 24 * 60 * 60 * 1000
const adminPageSizeOptions = [5, 10, 20, 50, 100] as const
const defaultAdminPageSize = 10
type DetailPolicyActionOption = { type DetailPolicyActionOption = {
action: AdminFilePolicyAction action: AdminFilePolicyAction
@@ -167,8 +169,10 @@ const normalizePresetChoice = <T extends string>(
const normalizePresetSize = (value: unknown) => { const normalizePresetSize = (value: unknown) => {
const normalizedSize = Number(value) const normalizedSize = Number(value)
if (!Number.isFinite(normalizedSize)) return 10 const pageSize = Number.isFinite(normalizedSize) ? Math.trunc(normalizedSize) : defaultAdminPageSize
return Math.min(Math.max(Math.trunc(normalizedSize), 1), 100) return adminPageSizeOptions.includes(pageSize as (typeof adminPageSizeOptions)[number])
? pageSize
: defaultAdminPageSize
} }
const buildDefaultViewPresetParams = (): AdminFileViewPresetParams => ({ const buildDefaultViewPresetParams = (): AdminFileViewPresetParams => ({
@@ -178,7 +182,7 @@ const buildDefaultViewPresetParams = (): AdminFileViewPresetParams => ({
health: 'all', health: 'all',
sortBy: 'created_at', sortBy: 'created_at',
sortOrder: 'desc', sortOrder: 'desc',
size: 10 size: defaultAdminPageSize
}) })
const normalizeViewPresetParams = (params: unknown): AdminFileViewPresetParams => { const normalizeViewPresetParams = (params: unknown): AdminFileViewPresetParams => {
@@ -218,7 +222,7 @@ export function useAdminFiles() {
const summary = ref<AdminFileSummary>(emptySummary()) const summary = ref<AdminFileSummary>(emptySummary())
const params = ref<AdminFileListParams & { total: number }>({ const params = ref<AdminFileListParams & { total: number }>({
page: 1, page: 1,
size: 10, size: defaultAdminPageSize,
total: 0, total: 0,
keyword: '', keyword: '',
status: 'all', status: 'all',
@@ -311,7 +315,7 @@ export function useAdminFiles() {
health: params.value.health || 'all', health: params.value.health || 'all',
sortBy: params.value.sortBy || 'created_at', sortBy: params.value.sortBy || 'created_at',
sortOrder: params.value.sortOrder || 'desc', sortOrder: params.value.sortOrder || 'desc',
size: params.value.size || 10 size: params.value.size || defaultAdminPageSize
}) })
const buildBuiltInViewPreset = ( const buildBuiltInViewPreset = (
@@ -1204,8 +1208,7 @@ export function useAdminFiles() {
} }
const handlePageSizeChange = async (size: number) => { const handlePageSizeChange = async (size: number) => {
if (!Number.isFinite(size) || size < 1) return params.value.size = normalizePresetSize(size)
params.value.size = Math.min(Math.trunc(size), 100)
params.value.page = 1 params.value.page = 1
markViewPresetDirty() markViewPresetDirty()
await loadFiles() await loadFiles()
+2 -2
View File
@@ -89,10 +89,10 @@
<div class="flex h-full min-h-0 min-w-0 flex-1 flex-col"> <div class="flex h-full min-h-0 min-w-0 flex-1 flex-col">
<!-- Header --> <!-- Header -->
<header <header
class="shadow-md border-b transition-colors duration-300 h-16" class="h-14 border-b shadow-md transition-colors duration-300 lg:hidden"
:class="[isDarkMode ? 'bg-[#1a2930] border-[#31454d]' : 'bg-[#f2f6f5] border-[#c8d7da]']" :class="[isDarkMode ? 'bg-[#1a2930] border-[#31454d]' : 'bg-[#f2f6f5] border-[#c8d7da]']"
> >
<div class="flex items-center justify-between h-16 px-4"> <div class="flex h-14 items-center justify-between px-4">
<button @click="toggleSidebar" class="lg:hidden"> <button @click="toggleSidebar" class="lg:hidden">
<MenuIcon class="w-6 h-6" :class="[isDarkMode ? 'text-gray-400' : 'text-gray-600']" /> <MenuIcon class="w-6 h-6" :class="[isDarkMode ? 'text-gray-400' : 'text-gray-600']" />
</button> </button>
+1 -1
View File
@@ -707,7 +707,7 @@
:current-page="historyPage" :current-page="historyPage"
:page-size="historyPageSize" :page-size="historyPageSize"
:total="historyRows.length" :total="historyRows.length"
:page-size-options="[5, 10, 20, 50]" :page-size-options="[5, 10, 20, 50, 100]"
@page-change="historyPage = $event" @page-change="historyPage = $event"
@page-size-change="handleHistoryPageSizeChange" @page-size-change="handleHistoryPageSizeChange"
/> />
+44 -47
View File
@@ -31,59 +31,50 @@ onMounted(() => {
<template> <template>
<div class="p-6"> <div class="p-6">
<div <div
class="sticky top-0 z-20 -mx-6 -mt-6 mb-6 border-b px-6 py-4 backdrop-blur" class="mb-6 flex flex-col gap-3 lg:flex-row lg:items-end lg:justify-between"
:class="[isDarkMode ? 'border-gray-700 bg-gray-900/85' : 'border-gray-200 bg-gray-50/90']"
> >
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between"> <div>
<div> <h2 class="text-2xl font-bold" :class="[isDarkMode ? 'text-white' : 'text-[#2d4650]']">
<h2 class="text-2xl font-bold" :class="[isDarkMode ? 'text-white' : 'text-gray-800']"> {{ t('admin.settings.title') }}
{{ t('admin.settings.title') }} </h2>
</h2> <p
<p class="mt-1 text-sm"
class="mt-1 text-sm" :class="[isDirty ? 'text-amber-500' : isDarkMode ? 'text-gray-400' : 'text-[#586f75]']"
:class="[isDirty ? 'text-amber-500' : isDarkMode ? 'text-gray-400' : 'text-gray-500']" >
> {{ isDirty ? t('manage.settings.unsavedChanges') : t('manage.settings.allChangesSaved') }}
{{ </p>
isDirty ? t('manage.settings.unsavedChanges') : t('manage.settings.allChangesSaved') </div>
}}
</p>
</div>
<div class="flex flex-wrap items-center gap-3"> <div class="flex flex-wrap items-center gap-2">
<BaseButton <BaseButton
variant="secondary" variant="secondary"
:loading="isRefreshing" :loading="isRefreshing"
:disabled="isSaving || isDirty" :disabled="isSaving || isDirty"
:title=" :title="isDirty ? t('manage.settings.refreshBlocked') : t('manage.settings.refreshConfig')"
isDirty ? t('manage.settings.refreshBlocked') : t('manage.settings.refreshConfig') @click="refreshConfig"
" >
@click="refreshConfig" <template #icon>
> <RefreshCwIcon class="mr-2 h-4 w-4" />
<template #icon> </template>
<RefreshCwIcon class="mr-2 h-4 w-4" /> {{ isRefreshing ? t('manage.settings.refreshing') : t('manage.settings.refreshConfig') }}
</template> </BaseButton>
{{
isRefreshing ? t('manage.settings.refreshing') : t('manage.settings.refreshConfig')
}}
</BaseButton>
<BaseButton <BaseButton :loading="isSaving" :disabled="!isDirty || isRefreshing" @click="submitConfig">
:loading="isSaving" <template #icon>
:disabled="!isDirty || isRefreshing" <SaveIcon class="mr-2 h-4 w-4" />
@click="submitConfig" </template>
> {{ isSaving ? t('manage.settings.saving') : t('manage.settings.saveChanges') }}
<template #icon> </BaseButton>
<SaveIcon class="mr-2 h-4 w-4" />
</template>
{{ isSaving ? t('manage.settings.saving') : t('manage.settings.saveChanges') }}
</BaseButton>
</div>
</div> </div>
</div> </div>
<div <div
class="space-y-6 rounded-lg shadow-md p-6" class="theme-2026-card theme-2026-card-hover space-y-8 rounded-[30px] border p-6 shadow-md transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
:class="[isDarkMode ? 'bg-gray-800 bg-opacity-70' : 'bg-white']" :class="[
isDarkMode
? 'border-[#40545c] bg-[#202f36]/90 hover:border-[#7199a8] hover:bg-[#263941] hover:shadow-black/25'
: 'border-[#d7e3e5] bg-[#f8fbfb] hover:border-[#8fb2bf] hover:shadow-[#a9bdc2]/70'
]"
> >
<!-- 基本设置 --> <!-- 基本设置 -->
<section class="space-y-4"> <section class="space-y-4">
@@ -714,4 +705,10 @@ onMounted(() => {
</div> </div>
</div> </div>
</template> </template>
<style scoped></style> <style scoped>
:deep(input:not([type='checkbox'])),
:deep(select),
:deep(textarea) {
border-radius: 30px;
}
</style>