diff --git a/backend/internal/server/api_contract_test.go b/backend/internal/server/api_contract_test.go index 848cee8e..9b37956a 100644 --- a/backend/internal/server/api_contract_test.go +++ b/backend/internal/server/api_contract_test.go @@ -491,8 +491,10 @@ func TestAPIContracts(t *testing.T) { service.SettingKeyContactInfo: "support", service.SettingKeyDocURL: "https://docs.example.com", - service.SettingKeyDefaultConcurrency: "5", - service.SettingKeyDefaultBalance: "1.25", + service.SettingKeyDefaultConcurrency: "5", + service.SettingKeyDefaultBalance: "1.25", + service.SettingKeyTableDefaultPageSize: "20", + service.SettingKeyTablePageSizeOptions: "[10,20,50,100]", service.SettingKeyOpsMonitoringEnabled: "false", service.SettingKeyOpsRealtimeMonitoringEnabled: "true", @@ -577,7 +579,7 @@ func TestAPIContracts(t *testing.T) { "purchase_subscription_enabled": false, "purchase_subscription_url": "", "table_default_page_size": 20, - "table_page_size_options": [10, 20, 50], + "table_page_size_options": [10, 20, 50, 100], "min_claude_code_version": "", "max_claude_code_version": "", "allow_ungrouped_key_scheduling": false, diff --git a/frontend/src/components/common/README.md b/frontend/src/components/common/README.md index bfb5d5e9..76f48475 100644 --- a/frontend/src/components/common/README.md +++ b/frontend/src/components/common/README.md @@ -52,7 +52,7 @@ Pagination component with page numbers, navigation, and page size selector. - `total: number` - Total number of items - `page: number` - Current page (1-indexed) - `pageSize: number` - Items per page -- `pageSizeOptions?: number[]` - Available page size options (default: [10, 20, 50]) +- `pageSizeOptions?: number[]` - Available page size options (default: [10, 20, 50, 100]) **Events:** diff --git a/frontend/src/components/layout/AppSidebar.vue b/frontend/src/components/layout/AppSidebar.vue index 09b83f6f..b9e2496f 100644 --- a/frontend/src/components/layout/AppSidebar.vue +++ b/frontend/src/components/layout/AppSidebar.vue @@ -669,11 +669,14 @@ onMounted(() => { opacity: 0; } -/* Custom SVG icon in sidebar: inherit color, constrain size */ +/* Custom SVG icon in sidebar: constrain size without overriding uploaded SVG colors */ +.sidebar-svg-icon { + color: currentColor; +} + .sidebar-svg-icon :deep(svg) { + display: block; width: 1.25rem; height: 1.25rem; - stroke: currentColor; - fill: none; } diff --git a/frontend/src/components/layout/__tests__/AppSidebar.spec.ts b/frontend/src/components/layout/__tests__/AppSidebar.spec.ts new file mode 100644 index 00000000..08c3294a --- /dev/null +++ b/frontend/src/components/layout/__tests__/AppSidebar.spec.ts @@ -0,0 +1,18 @@ +import { readFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' + +import { describe, expect, it } from 'vitest' + +const componentPath = resolve(dirname(fileURLToPath(import.meta.url)), '../AppSidebar.vue') +const componentSource = readFileSync(componentPath, 'utf8') + +describe('AppSidebar custom SVG styles', () => { + it('does not override uploaded SVG fill or stroke colors', () => { + expect(componentSource).toContain('.sidebar-svg-icon {') + expect(componentSource).toContain('color: currentColor;') + expect(componentSource).toContain('display: block;') + expect(componentSource).not.toContain('stroke: currentColor;') + expect(componentSource).not.toContain('fill: none;') + }) +}) diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index 466c7794..64581911 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -4359,7 +4359,7 @@ export default { tableDefaultPageSize: 'Default Rows Per Page', tableDefaultPageSizeHint: 'Must be an integer between 5 and 1000', tablePageSizeOptions: 'Rows Per Page Options', - tablePageSizeOptionsPlaceholder: '10, 20, 50', + tablePageSizeOptionsPlaceholder: '10, 20, 50, 100', tablePageSizeOptionsHint: 'Use commas to separate integers between 5 and 1000; values are deduplicated and sorted on save', tableDefaultPageSizeRangeError: 'Default rows per page must be between {min} and {max}', tablePageSizeOptionsFormatError: 'Invalid options format. Enter comma-separated integers between {min} and {max}', diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts index 2aeafb1b..ac7e0b3a 100644 --- a/frontend/src/i18n/locales/zh.ts +++ b/frontend/src/i18n/locales/zh.ts @@ -4520,7 +4520,7 @@ export default { tableDefaultPageSize: '默认每页条数', tableDefaultPageSizeHint: '必须为 5-1000 之间的整数', tablePageSizeOptions: '可选每页条数列表', - tablePageSizeOptionsPlaceholder: '10, 20, 50', + tablePageSizeOptionsPlaceholder: '10, 20, 50, 100', tablePageSizeOptionsHint: '使用英文逗号分隔,取值范围 5-1000,保存时会自动去重并排序', tableDefaultPageSizeRangeError: '默认每页条数必须在 {min}-{max} 之间', tablePageSizeOptionsFormatError: '可选每页条数格式无效,请输入 {min}-{max} 之间的整数并用英文逗号分隔', diff --git a/frontend/src/stores/app.ts b/frontend/src/stores/app.ts index 8a004a4a..f561d4ca 100644 --- a/frontend/src/stores/app.ts +++ b/frontend/src/stores/app.ts @@ -333,7 +333,7 @@ export const useAppStore = defineStore('app', () => { purchase_subscription_enabled: false, purchase_subscription_url: '', table_default_page_size: 20, - table_page_size_options: [10, 20, 50], + table_page_size_options: [10, 20, 50, 100], custom_menu_items: [], custom_endpoints: [], linuxdo_oauth_enabled: false, diff --git a/frontend/src/utils/__tests__/tablePreferences.spec.ts b/frontend/src/utils/__tests__/tablePreferences.spec.ts index f18ec215..6c7336b6 100644 --- a/frontend/src/utils/__tests__/tablePreferences.spec.ts +++ b/frontend/src/utils/__tests__/tablePreferences.spec.ts @@ -64,11 +64,11 @@ describe('tablePreferences', () => { expect(normalizeTablePageSize(undefined)).toBe(20) }) - it('keeps built-in selectable defaults at 10, 20, 50', () => { + it('keeps built-in selectable defaults at 10, 20, 50, 100', () => { window.__APP_CONFIG__ = { table_default_page_size: 1000 } as any - expect(getConfiguredTablePageSizeOptions()).toEqual([10, 20, 50]) + expect(getConfiguredTablePageSizeOptions()).toEqual([10, 20, 50, 100]) }) }) diff --git a/frontend/src/utils/tablePreferences.ts b/frontend/src/utils/tablePreferences.ts index 1b123851..ba65bf28 100644 --- a/frontend/src/utils/tablePreferences.ts +++ b/frontend/src/utils/tablePreferences.ts @@ -2,7 +2,7 @@ const MIN_TABLE_PAGE_SIZE = 5 const MAX_TABLE_PAGE_SIZE = 1000 export const DEFAULT_TABLE_PAGE_SIZE = 20 -export const DEFAULT_TABLE_PAGE_SIZE_OPTIONS = [10, 20, 50] +export const DEFAULT_TABLE_PAGE_SIZE_OPTIONS = [10, 20, 50, 100] const sanitizePageSize = (value: unknown): number | null => { const size = Number(value) diff --git a/frontend/src/views/admin/SettingsView.vue b/frontend/src/views/admin/SettingsView.vue index 4696d336..6a2cbb57 100644 --- a/frontend/src/views/admin/SettingsView.vue +++ b/frontend/src/views/admin/SettingsView.vue @@ -2487,7 +2487,7 @@ const smtpPasswordManuallyEdited = ref(false) const testEmailAddress = ref('') const registrationEmailSuffixWhitelistTags = ref([]) const registrationEmailSuffixWhitelistDraft = ref('') -const tablePageSizeOptionsInput = ref('10, 20, 50') +const tablePageSizeOptionsInput = ref('10, 20, 50, 100') // Admin API Key 状态 const adminApiKeyLoading = ref(true) @@ -2587,7 +2587,7 @@ const form = reactive({ purchase_subscription_enabled: false, purchase_subscription_url: '', table_default_page_size: tablePageSizeDefault, - table_page_size_options: [10, 20, 50], + table_page_size_options: [10, 20, 50, 100], custom_menu_items: [] as Array<{id: string; label: string; icon_svg: string; url: string; visibility: 'user' | 'admin'; sort_order: number}>, custom_endpoints: [] as Array<{name: string; endpoint: string; description: string}>, frontend_url: '', @@ -2859,7 +2859,7 @@ async function loadSettings() { settings.registration_email_suffix_whitelist ) tablePageSizeOptionsInput.value = formatTablePageSizeOptions( - Array.isArray(settings.table_page_size_options) ? settings.table_page_size_options : [10, 20, 50] + Array.isArray(settings.table_page_size_options) ? settings.table_page_size_options : [10, 20, 50, 100] ) registrationEmailSuffixWhitelistDraft.value = '' form.smtp_password = '' @@ -3076,7 +3076,7 @@ async function saveSettings() { updated.registration_email_suffix_whitelist ) tablePageSizeOptionsInput.value = formatTablePageSizeOptions( - Array.isArray(updated.table_page_size_options) ? updated.table_page_size_options : [10, 20, 50] + Array.isArray(updated.table_page_size_options) ? updated.table_page_size_options : [10, 20, 50, 100] ) registrationEmailSuffixWhitelistDraft.value = '' form.smtp_password = ''