@go-wind/admin-react 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +74 -2
- package/dist/access/access-control.d.ts +39 -0
- package/dist/access/field-permission.d.ts +11 -0
- package/dist/access/index.d.ts +4 -0
- package/dist/access/use-access.d.ts +50 -0
- package/dist/admin-react.css +2 -0
- package/dist/admin-react.js +5432 -0
- package/dist/admin-react.js.map +1 -0
- package/dist/auth-gateway.d.ts +54 -0
- package/dist/components/Loading/index.d.ts +22 -0
- package/dist/env.d.ts +5 -0
- package/dist/hooks/useProTableScrollY.d.ts +22 -0
- package/dist/hooks/useTime.d.ts +6 -0
- package/dist/hooks/useTitle.d.ts +1 -0
- package/dist/hooks/useTokenRefresh.d.ts +26 -0
- package/dist/i18n/components/LocaleSwitcher/index.d.ts +1 -0
- package/dist/i18n/components/index.d.ts +1 -0
- package/dist/i18n/config/i18n.d.ts +18 -0
- package/dist/i18n/hooks/index.d.ts +2 -0
- package/dist/i18n/hooks/useI18n.d.ts +96 -0
- package/dist/i18n/hooks/useLocaleSync.d.ts +4 -0
- package/dist/i18n/index.d.ts +3 -0
- package/dist/i18n/utils/detector.d.ts +26 -0
- package/dist/i18n/utils/formatter.d.ts +38 -0
- package/dist/i18n/utils/index.d.ts +3 -0
- package/dist/i18n/utils/sync.d.ts +27 -0
- package/dist/index.d.ts +34 -0
- package/dist/locales/en-US/index.d.ts +2 -0
- package/dist/locales/index.d.ts +10 -0
- package/dist/locales/zh-CN/index.d.ts +2 -0
- package/dist/nprogress-CwfUlis6.js +159 -0
- package/dist/nprogress-CwfUlis6.js.map +1 -0
- package/dist/preferences/components/PreferencesPanel/AppearancePanel.d.ts +3 -0
- package/dist/preferences/components/PreferencesPanel/GeneralPanel.d.ts +3 -0
- package/dist/preferences/components/PreferencesPanel/LayoutPanel.d.ts +3 -0
- package/dist/preferences/components/PreferencesPanel/ShortcutKeyPanel.d.ts +3 -0
- package/dist/preferences/components/PreferencesPanel/index.d.ts +8 -0
- package/dist/preferences/components/ThemeProvider/index.d.ts +9 -0
- package/dist/preferences/components/index.d.ts +2 -0
- package/dist/preferences/config/constants.d.ts +24 -0
- package/dist/preferences/config/darkTheme.d.ts +32 -0
- package/dist/preferences/config/default.d.ts +3 -0
- package/dist/preferences/config/index.d.ts +4 -0
- package/dist/preferences/config/lightTheme.d.ts +33 -0
- package/dist/preferences/hooks/index.d.ts +3 -0
- package/dist/preferences/hooks/useLocale.d.ts +23 -0
- package/dist/preferences/hooks/usePreferences.d.ts +31 -0
- package/dist/preferences/hooks/useThemeConfig.d.ts +2 -0
- package/dist/preferences/index.d.ts +5 -0
- package/dist/preferences/store/index.d.ts +24 -0
- package/dist/preferences/theme-transition.d.ts +17 -0
- package/dist/preferences/types/app.d.ts +238 -0
- package/dist/preferences/types/index.d.ts +4 -0
- package/dist/preferences/types/layout.d.ts +78 -0
- package/dist/preferences/types/preferences-root.d.ts +48 -0
- package/dist/preferences/types/theme.d.ts +29 -0
- package/dist/preferences/utils/diff.d.ts +7 -0
- package/dist/preferences/utils/merge.d.ts +9 -0
- package/dist/query-client.d.ts +9 -0
- package/dist/rolldown-runtime-BbCBk-LD.js +19 -0
- package/dist/router/factory.d.ts +7 -0
- package/dist/router/generators/generate-menus.d.ts +13 -0
- package/dist/router/generators/generate-routes-backend.d.ts +12 -0
- package/dist/router/generators/generate-routes-frontend.d.ts +17 -0
- package/dist/router/generators/index.d.ts +3 -0
- package/dist/router/index.d.ts +5 -0
- package/dist/router/routes-context.d.ts +8 -0
- package/dist/router/types/index.d.ts +207 -0
- package/dist/router/utils/index.d.ts +8 -0
- package/dist/router/utils/inject-redirect.d.ts +6 -0
- package/dist/router/utils/lazy.d.ts +11 -0
- package/dist/router/utils/loader.d.ts +14 -0
- package/dist/router/utils/menu.d.ts +5 -0
- package/dist/router/utils/merge-route-modules.d.ts +23 -0
- package/dist/router/utils/reset-routes.d.ts +18 -0
- package/dist/router/utils/sort-routes.d.ts +5 -0
- package/dist/router/utils/transform-meta-to-handle.d.ts +11 -0
- package/dist/storage/index.d.ts +2 -0
- package/dist/storage/storage.class.d.ts +125 -0
- package/dist/storage/storage.types.d.ts +199 -0
- package/dist/stores/auth.d.ts +56 -0
- package/dist/stores/index.d.ts +2 -0
- package/dist/stores/user.d.ts +32 -0
- package/dist/transport/rest/captcha-headers.d.ts +2 -0
- package/dist/transport/rest/index.d.ts +8 -0
- package/dist/transport/rest/modules/downloader.d.ts +9 -0
- package/dist/transport/rest/modules/interceptor.d.ts +9 -0
- package/dist/transport/rest/modules/uploader.d.ts +10 -0
- package/dist/transport/rest/pagination.d.ts +73 -0
- package/dist/transport/rest/preset-interceptors.d.ts +25 -0
- package/dist/transport/rest/request-api.d.ts +6 -0
- package/dist/transport/rest/request-client.d.ts +87 -0
- package/dist/transport/rest/types.d.ts +38 -0
- package/dist/transport/rest/utils.d.ts +16 -0
- package/dist/transport/sse/event.d.ts +44 -0
- package/dist/transport/sse/index.d.ts +5 -0
- package/dist/transport/sse/sse_client.d.ts +76 -0
- package/dist/transport/sse/types.d.ts +27 -0
- package/dist/types.d.ts +44 -0
- package/dist/utils/color.d.ts +27 -0
- package/dist/utils/date.d.ts +5 -0
- package/dist/utils/diff.d.ts +6 -0
- package/dist/utils/index.d.ts +9 -0
- package/dist/utils/inference.d.ts +111 -0
- package/dist/utils/merge.d.ts +17 -0
- package/dist/utils/nprogress.d.ts +11 -0
- package/dist/utils/tree.d.ts +27 -0
- package/dist/utils/url.d.ts +11 -0
- package/dist/utils/utils.d.ts +37 -0
- package/package.json +70 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-react.js","names":["welcomeBack","loginDescription","tenantCodeLabel","tenantCodePlaceholder","usernameLabel","usernamePlaceholder","usernameRequired","passwordLabel","passwordPlaceholder","passwordRequired","rememberAccount","loginButton","loggingIn","switchLanguage","switchToDarkMode","switchToLightMode","systemTitle","systemDescription","copyright","hasAccount","backToLogin","confirmPasswordPlaceholder","confirmPasswordRequired","passwordMismatch","passwordMinLength","loginSuccess","loginFailed","sessionExpired","captchaLabel","captchaPlaceholder","captchaRequired","captchaRefresh","langZhCN","langEnUS","mfaChallengeTitle","mfaChallengeDesc","mfaCodeLabel","mfaCodePlaceholder","mfaCodeRequired","mfaCodeFormatError","mfaVerify","mfaCancel","mfaVerifyFailed","mfaNotRequired","forgotPassword","noAccount","ssoLoginFailed","ssoButton","ssoCallbackFailed","button","header","lockScreen","refresh","home","iframe","fallback","sidebar","skeleton","loading","table","unknownPage","pageContainer","tabs","app","clipboard","placeholder","requestError","request","dateRange","modal","drawer","bold","italic","strike","underline","code","subscript","superscript","textColor","highlightColor","bulletList","orderedList","taskList","blockquote","insertCodeBlock","insertTable","deleteTable","insertHorizontalRule","left","center","right","justify","undo","redo","clearFormatting","clearContent","clear_content_confirm","removeUrl","insertUrl","uploadImage","insertVideo","insertIframe","importMarkdown","words","chars","insert_url","input_url","codeLanguage","selectLanguage","codeContent","codeContentPlaceholder","videoUrl","videoUrlPlaceholder","videoWidth","iframeUrl","iframeUrlPlaceholder","iframeWidth","iframeHeight","iframeTitle","iframeTitlePlaceholder","allowFullscreen","allowFullscreenEnabled","allowFullscreenDisabled","insertRowBefore","insertRowAfter","deleteRow","insertColBefore","insertColAfter","deleteCol","mergeCells","splitCell","toggleHeaderRow","toggleHeaderColumn","toggleHeaderCell","jsonParseError","unknownError","jsonSerializeError","enterFullscreen","exitFullscreen","fontSize","indent","outdent","title","subtitle","tabs","actions","appearance","layout","shortcut","general","modules","__import_glob__0_0_","__import_glob__0_1_","__import_glob__0_2_","__import_glob__0_3_","enUS","tabs","__import_glob__0_0_","__import_glob__0_1_","__import_glob__0_2_","__import_glob__0_3_","zhCN","zhCN","enUS","isPlainObject","theme","antdTheme","i18next","antdTheme","_Fragment","Loading","__toESM","__exportAll","axios","i18n","zhCN","enUS","dayjs","devMode","configureAdminRuntime","options","isDevMode","gateway","setAuthGateway","g","getAuthGateway","auth_exports$1","backToLogin$1","captchaLabel$1","captchaPlaceholder$1","captchaRefresh$1","captchaRequired$1","confirmPasswordPlaceholder$1","confirmPasswordRequired$1","copyright$1","auth_default$1","forgotPassword$1","hasAccount$1","langEnUS$1","langZhCN$1","loggingIn$1","loginButton$1","loginDescription$1","loginFailed$1","loginSuccess$1","mfaCancel$1","mfaChallengeDesc$1","mfaChallengeTitle$1","mfaCodeFormatError$1","mfaCodeLabel$1","mfaCodePlaceholder$1","mfaCodeRequired$1","mfaNotRequired$1","mfaVerify$1","mfaVerifyFailed$1","noAccount$1","passwordLabel$1","passwordMinLength$1","passwordMismatch$1","passwordPlaceholder$1","passwordRequired$1","rememberAccount$1","sessionExpired$1","ssoButton$1","ssoCallbackFailed$1","ssoLoginFailed$1","switchLanguage$1","switchToDarkMode$1","switchToLightMode$1","systemDescription$1","systemTitle$1","tenantCodeLabel$1","tenantCodePlaceholder$1","usernameLabel$1","usernamePlaceholder$1","usernameRequired$1","welcomeBack$1","common_exports$1","app$1","button$1","clipboard$1","dateRange$1","common_default$1","drawer$1","fallback$1","header$1","home$1","iframe$1","loading$1","lockScreen$1","modal$1","pageContainer$1","placeholder$1","refresh$1","request$1","requestError$1","sidebar$1","skeleton$1","table$1","tabs$3","unknownPage$1","\"add\"","\"search\"","\"back\"","\"ok\"","\"confirm\"","\"cancel\"","\"edit\"","\"delete\"","\"submit\"","\"reset\"","\"retry\"","\"more\"","\"copy\"","\"save\"","\"close\"","\"profile\"","\"settings\"","\"inbox\"","\"lockScreen\"","\"logout\"","\"logoutConfirmTitle\"","\"logoutConfirmMessage\"","\"fullscreen\"","\"exitFullscreen\"","\"helpDoc\"","\"notification\"","\"guest\"","\"switchToLight\"","\"switchToDark\"","\"expandSidebar\"","\"collapseSidebar\"","\"refresh\"","\"switchLanguage\"","\"searchPlaceholder\"","\"searchShortcut\"","\"searching\"","\"noResults\"","\"noHistory\"","\"searchRecent\"","\"semanticTitle\"","\"searchSelect\"","\"searchSwitch\"","\"searchClose\"","\"title\"","\"placeholder\"","\"lock\"","\"unlock\"","\"entry\"","\"backToLogin\"","\"errorTip\"","\"noUrlTitle\"","\"noUrlDesc\"","\"loading\"","\"loadErrorTitle\"","\"loadErrorDesc\"","\"embeddedPage\"","\"unauthorized\"","\"unauthorizedDesc\"","\"forbidden\"","\"forbiddenDesc\"","\"pageNotFound\"","\"pageNotFoundDesc\"","\"internalError\"","\"internalErrorDesc\"","\"comingSoon\"","\"offlineError\"","\"offlineErrorDesc\"","\"errorFallback\"","\"subTitle\"","\"backToHome\"","\"refreshPage\"","\"errorDetails\"","\"copied\"","\"copySuccess\"","\"copyFailed\"","\"errorMessage\"","\"callStack\"","\"componentStack\"","\"expandMenu\"","\"collapseMenu\"","\"switchToManual\"","\"switchToAuto\"","\"text\"","\"success\"","\"failed\"","\"initializing\"","\"loadingRouter\"","\"columnResizeHint\"","\"defaultTitle\"","\"forbiddenTitle\"","\"unpin\"","\"pin\"","\"maximize\"","\"exitMaximize\"","\"reload\"","\"openInNewWindow\"","\"closeLeft\"","\"closeRight\"","\"closeOthers\"","\"closeAll\"","\"defaultName\"","\"copyDenied\"","\"underDevelopment\"","\"input\"","\"select\"","\"networkError\"","\"timeout\"","\"badRequest\"","\"notFound\"","\"requestTimeout\"","\"internalServerError\"","\"error\"","\"unknownError\"","\"reason\"","\"BAD_REQUEST\"","\"INVALID_GRANT_TYPE\"","\"INVALID_USERID\"","\"INVALID_TOKEN\"","\"INVALID_PASSWORD\"","\"UNAUTHORIZED\"","\"USER_FREEZE\"","\"INCORRECT_APP_SECRET\"","\"INCORRECT_ACCESS_TOKEN\"","\"INCORRECT_REFRESH_TOKEN\"","\"TOKEN_EXPIRED\"","\"TOKEN_NOT_EXIST\"","\"PAYMENT_REQUIRED\"","\"FORBIDDEN\"","\"NOT_FOUND\"","\"USER_NOT_FOUND\"","\"METHOD_NOT_ALLOWED\"","\"NOT_ACCEPTABLE\"","\"PROXY_AUTHENTICATION_REQUIRED\"","\"REQUEST_TIMEOUT\"","\"CONFLICT\"","\"GONE\"","\"LENGTH_REQUIRED\"","\"PRECONDITION_FAILED\"","\"PAYLOAD_TOO_LARGE\"","\"URI_TOO_LONG\"","\"UNSUPPORTED_MEDIA_TYPE\"","\"RANGE_NOT_SATISFIABLE\"","\"EXPECTATION_FAILED\"","\"IM_A_TEAPOT\"","\"MISDIRECTED_REQUEST\"","\"UNPROCESSABLE_ENTITY\"","\"LOCKED\"","\"FAILED_DEPENDENCY\"","\"TOO_EARLY\"","\"UPGRADE_REQUIRED\"","\"PRECONDITION_REQUIRED\"","\"TOO_MANY_REQUESTS\"","\"REQUEST_HEADER_FIELDS_TOO_LARGE\"","\"UNAVAILABLE_FOR_LEGAL_REASONS\"","\"INTERNAL_SERVER_ERROR\"","\"NOT_IMPLEMENTED\"","\"BAD_GATEWAY\"","\"SERVICE_UNAVAILABLE\"","\"GATEWAY_TIMEOUT\"","\"HTTP_VERSION_NOT_SUPPORTED\"","\"VARIANT_ALSO_NEGOTIATES\"","\"INSUFFICIENT_STORAGE\"","\"LOOP_DETECTED\"","\"NOT_EXTENDED\"","\"NETWORK_AUTHENTICATION_REQUIRED\"","\"NETWORK_READ_TIMEOUT_ERROR\"","\"NETWORK_CONNECT_TIMEOUT_ERROR\"","\"USER_DISABLED\"","\"USER_LOCKED\"","\"DUPLICATE_ENTRY\"","\"VALIDATION_FAILED\"","\"INTERNAL_ERROR\"","\"status\"","\"today\"","\"yesterday\"","\"thisWeek\"","\"lastWeek\"","\"thisMonth\"","\"lastMonth\"","\"create\"","\"update\"","\"view\"","\"unsavedMessage\"","\"discardButton\"","\"import\"","\"uploadText\"","\"uploadHint\"","\"downloadTemplate\"","\"importSuccess\"","\"importFailed\"","\"noDataParsed\"","\"readFileFailed\"","\"export\"","\"csv\"","\"xlsx\"","\"serverXlsx\"","\"serverSuccess\"","\"serverFailed\"","editor_exports$1","allowFullscreen$1","allowFullscreenDisabled$1","allowFullscreenEnabled$1","blockquote$1","bold$1","bulletList$1","center$1","chars$1","clearContent$1","clearFormatting$1","clear_content_confirm$1","code$1","codeContent$1","codeContentPlaceholder$1","codeLanguage$1","editor_default$1","deleteCol$1","deleteRow$1","deleteTable$1","enterFullscreen$1","exitFullscreen$1","fontSize$1","highlightColor$1","iframeHeight$1","iframeTitle$1","iframeTitlePlaceholder$1","iframeUrl$1","iframeUrlPlaceholder$1","iframeWidth$1","importMarkdown$1","indent$1","input_url$1","insertCodeBlock$1","insertColAfter$1","insertColBefore$1","insertHorizontalRule$1","insertIframe$1","insertRowAfter$1","insertRowBefore$1","insertTable$1","insertUrl$1","insertVideo$1","insert_url$1","italic$1","jsonParseError$1","jsonSerializeError$1","justify$1","left$1","mergeCells$1","orderedList$1","outdent$1","redo$1","removeUrl$1","right$1","selectLanguage$1","splitCell$1","strike$1","subscript$1","superscript$1","taskList$1","textColor$1","toggleHeaderCell$1","toggleHeaderColumn$1","toggleHeaderRow$1","underline$1","undo$1","unknownError$1","uploadImage$1","videoUrl$1","videoUrlPlaceholder$1","videoWidth$1","words$1","preferences_exports$1","actions$1","appearance$1","preferences_default$1","general$1","layout$1","shortcut$1","subtitle$1","tabs$2","title$1","\"appearance\"","\"layout\"","\"shortcut\"","\"general\"","\"copySettings\"","\"copyPreferencesSuccess\"","\"copyPreferencesFailed\"","\"clearCacheAndLogout\"","\"theme\"","\"lightMode\"","\"darkMode\"","\"followSystem\"","\"darkSidebar\"","\"darkHeader\"","\"builtinThemes\"","\"themes\"","\"default\"","\"violet\"","\"pink\"","\"yellow\"","\"skyBlue\"","\"green\"","\"zinc\"","\"deepGreen\"","\"deepBlue\"","\"orange\"","\"rose\"","\"neutral\"","\"slate\"","\"gray\"","\"custom\"","\"radius\"","\"other\"","\"colorWeakMode\"","\"colorGrayMode\"","\"content\"","\"modes\"","\"sidebarNav\"","\"sidebarMixedNav\"","\"headerNav\"","\"mixedNav\"","\"fullContent\"","\"contentModes\"","\"wide\"","\"compact\"","\"sidebar\"","\"showSidebar\"","\"collapseShowTitle\"","\"width\"","\"header\"","\"showHeader\"","\"mode\"","\"headerModes\"","\"fixed\"","\"auto\"","\"static\"","\"navigationMenu\"","\"menuStyle\"","\"menuStyles\"","\"rounded\"","\"plain\"","\"menuSplit\"","\"accordionMode\"","\"breadcrumb\"","\"enableBreadcrumb\"","\"hideWhenOnlyOne\"","\"showIcon\"","\"showHomeButton\"","\"breadcrumbStyle\"","\"breadcrumbStyles\"","\"normal\"","\"background\"","\"tabbar\"","\"enableTabbar\"","\"persistTabs\"","\"draggableSort\"","\"showTabbarIcon\"","\"showMoreButton\"","\"showMaximizeButton\"","\"tabbarStyle\"","\"tabbarStyles\"","\"chrome\"","\"card\"","\"brisk\"","\"widgets\"","\"globalSearch\"","\"themeToggle\"","\"languageToggle\"","\"sidebarToggle\"","\"preferencesPosition\"","\"positions\"","\"hidden\"","\"footer\"","\"showFooter\"","\"fixedAtBottom\"","\"copyright\"","\"enableCopyright\"","\"companyName\"","\"companySite\"","\"date\"","\"icpNumber\"","\"icpLink\"","\"global\"","\"shortcutKeys\"","\"language\"","\"languages\"","\"zhCN\"","\"enUS\"","\"dynamicTitle\"","\"watermark\"","\"checkUpdates\"","\"animation\"","\"progressBar\"","\"pageTransition\"","\"transitions\"","\"fade\"","\"fadeDown\"","\"fadeSlide\"","\"fadeUp\"","modules$1","enUS$1","path","mod","ns","auth_exports","backToLogin","captchaLabel","captchaPlaceholder","captchaRefresh","captchaRequired","confirmPasswordPlaceholder","confirmPasswordRequired","copyright","auth_default","forgotPassword","hasAccount","langEnUS","langZhCN","loggingIn","loginButton","loginDescription","loginFailed","loginSuccess","mfaCancel","mfaChallengeDesc","mfaChallengeTitle","mfaCodeFormatError","mfaCodeLabel","mfaCodePlaceholder","mfaCodeRequired","mfaNotRequired","mfaVerify","mfaVerifyFailed","noAccount","passwordLabel","passwordMinLength","passwordMismatch","passwordPlaceholder","passwordRequired","rememberAccount","sessionExpired","ssoButton","ssoCallbackFailed","ssoLoginFailed","switchLanguage","switchToDarkMode","switchToLightMode","systemDescription","systemTitle","tenantCodeLabel","tenantCodePlaceholder","usernameLabel","usernamePlaceholder","usernameRequired","welcomeBack","common_exports","app","button","clipboard","dateRange","common_default","drawer","fallback","header","home","iframe","loading","lockScreen","modal","pageContainer","placeholder","refresh","request","requestError","sidebar","skeleton","table","tabs$1","unknownPage","editor_exports","allowFullscreen","allowFullscreenDisabled","allowFullscreenEnabled","blockquote","bold","bulletList","center","chars","clearContent","clearFormatting","clear_content_confirm","code","codeContent","codeContentPlaceholder","codeLanguage","editor_default","deleteCol","deleteRow","deleteTable","enterFullscreen","exitFullscreen","fontSize","highlightColor","iframeHeight","iframeTitle","iframeTitlePlaceholder","iframeUrl","iframeUrlPlaceholder","iframeWidth","importMarkdown","indent","input_url","insertCodeBlock","insertColAfter","insertColBefore","insertHorizontalRule","insertIframe","insertRowAfter","insertRowBefore","insertTable","insertUrl","insertVideo","insert_url","italic","jsonParseError","jsonSerializeError","justify","left","mergeCells","orderedList","outdent","redo","removeUrl","right","selectLanguage","splitCell","strike","subscript","superscript","taskList","textColor","toggleHeaderCell","toggleHeaderColumn","toggleHeaderRow","underline","undo","unknownError","uploadImage","videoUrl","videoUrlPlaceholder","videoWidth","preferences_exports","actions","appearance","preferences_default","general","layout","shortcut","subtitle","tabs","title","modules","zhCN$1","supportedLocales","FALLBACK_LOCALE","coreResources","CORE_NAMESPACES","defaultRetry","failureCount","error","queryClient","QueryClient","attemptIndex","defaultShouldDehydrateQuery","query","configureQueryClient","queries","StorageManager","prefix","onMetrics","onSync","storageType","defaultTTL","enableSync","key","raw","meta","size","serialized","ttl","map","i","k","shortKey","parsed","value","defaultValue","fullKey","retryError","keysToRemove","rawKey","index","count","result","keys","onProgress","skipExpired","includeRaw","resolve","items","entries","item","err","removed","failed","batchItem","maxItems","maxUsageMB","allMeta","currentItems","currentUsage","sum","m","sortedKeys","evicted","keysToEvict","now","metas","ma","a","mb","b","strategy","scoreA","scoreB","freqWeight","timeDecay","timeDecayB","msg","callback","total","est","pendingCaptchaHeaders","setCaptchaHeaders","id","consumeCaptchaHeaders","headers","makeUpdateMask","defaultIdGenerator","rnd","getDefaultErrorMsg","resData","reason","REFRESH_QUEUE_TIMEOUT_MS","authenticateResponseInterceptor","response","config","enableRefreshToken","doReAuthenticate","client","timer","newToken","formatToken","reject","cb","doRefreshToken","handledError","refreshError","errorMessageResponseInterceptor","getErrorMsg","makeErrorMessage","merge","target","source","sourceValue","targetValue","isPlainObject$1","createMerge","customizer","mergeWithArrayOverride","obj","isFunction","isObject","isString","isUndefined","isBoolean","isEmpty","isHttpUrl","url","isWindow","isMacOs","isWindowsOs","isNumber","getFirstNonNullOrUndefined","values","addLight","cc","color","amount","c","lighten","bindMethods","prototype","instance","descriptor","propertyName","propertyValue","scrollToTop","scrollToBottom","scrollTo","amountFormatter","nProgressInstance","loadNprogress","startProgress","stopProgress","formatDate","dateString","formatDateTime","timestamp","date","DATE_FORMAT","TIME_FORMAT","TIME_PICKER_FORMAT","getRandomColor","hash","str","getCharColor","char","hexToRgb","bigint","hex","rgbToHex","r","parseColor","s","full","x","hsl","parts","h","sl","l","n","f","v","mixColor","base","o","other","p","pct","getUrlAllParam","search","arr","getUrlParam","arraysEqual","counter","diff","findDifferences","o1","o2","diffResult","valueDiff","obj1","obj2","traverseTreeValues","dfs","getValue","treeNode","children","childProps","child","tree","filterTree","_filterTree","nodes","filter","node","mapTree","mapperNode","mapper","FileDownloader","finalConfig","defaultRequestInterceptorConfig","defaultResponseInterceptorConfig","InterceptorManager","fulfilled","rejected","FileUploader","formData","data","RequestClient","callbacks","defaultConfig","axiosConfig","requestConfig","interceptorManager","fileUploader","fileDownloader","token","requestId","responseData","status","errAny","thrown","requestApi","body","PaginationQuery","_","idx","formValues","cleaned","fuzzy","needCleanTenant","rest","orderBy","SSEClient","rawData","handlers","eventName","event","fetchEventSource","targetUrl","handler","SSE_EVENT","globalSSEClient","detector_exports","detectBrowserLocale","getFinalLocale","getStoredLocale","isSupportedLocale","setStoredLocale","STORAGE_KEY","DEFAULT_LOCALE","SUPPORTED_LOCALES","browserLanguages","lang","exactMatch","supported","prefixMatch","stored","locale","formatLocaleDate","targetDate","formatNumber","num","formatCurrency","defaultOptions","formatPercent","formatRelativeTime","diffMs","SECOND","MINUTE","HOUR","DAY","unit","absDiff","syncLocaleToBrowser","rtlLocales","setupLocaleSync","handleLanguageChange","newLocale","currentLocale","syncPageTitle","t","titleKey","params","initLocaleSync","finalLocale","useI18n","useTranslation","BUILT_IN_THEME_PRESETS","COLOR_PRESETS","SECONDARY_TEXT_LIGHT","SECONDARY_TEXT_DARK","DARK_PALETTE","darkThemeTokens","inputLike","darkThemeComponents","defaultPreferences","LIGHT_PALETTE","lightThemeTokens","lightThemeComponents","createJSONStorage","storage","getStorage","parse","str2","name","isPromiseLike","newValue","toSyncThenable","onFulfilled","toThenable","fn","input","onRejected","persist","state","currentState","persistedState","baseOptions","hasHydrated","hydrationVersion","hydrationListeners","finishHydrationListeners","args","set","get","api","setItem","savedSetState","configResult","stateFromStorage","hydrate","currentVersion","postRehydrationCallback","deserializedStorageValue","migration","migrated","migratedState","migrationResult","e","newOptions","mergeDeep","isValidElement","isPlainObject","usePreferencesStore","create","overrides","version","themePref","usePreferences","useMemo","preferences","theme","shortcutKeys","transition","navigation","tabbar","breadcrumb","footer","logo","widget","isDark","isMobile","updatePreferences","updateTheme","themeOverrides","updateApp","appOverrides","newMode","antdLocales","useLocale","isZhCN","isEnUS","antdLocale","setLocale","useCallback","setZhCN","setEnUS","toggleLocale","getLocaleName","useThemeConfig","theme$1","systemIsDark","setSystemIsDark","useState","mediaQuery","effectiveIsDark","algorithms","tokens","diffDeep","current","currentValue","initialValue","initial","nested","isEqual","ACCESS_TOKEN_REFRESH_INTERVAL","SAFETY_BUFFER_MS","MIN_INTERVAL_MS","refreshTimer","activeSseBaseUrl","getRefreshExpireAt","match","row","val","computeNextInterval","useAuthStore","refreshExpireAt","refreshRemaining","accessRemaining","accessTokenExpireAt","scheduleRefresh","tick","profile","startRefreshTimer","stopRefreshTimer","reconnectSSEServer","accessToken","userInfo","sseUrl","connectSSEServer","sseBaseUrl","disconnectSSEServer","DEFAULT_ACCESS_EXPIRES_IN","applySuccessfulLogin","accessTokenPayload","onSuccess","rawHomePath","captcha","opId","info","useUserStore","roles","codes","fields","tenantId","startThemeViewTransition","origin","y","endRadius","clipPath","update","BUILTIN_THEMES","RADIUS_OPTIONS","AppearancePanel","handleThemeModeChange","mode","setPreferences","handleBuiltinThemeChange","type","handleRadiusChange","BulbOutlined","MoonOutlined","LaptopOutlined","Switch","checked","BgColorsOutlined","Segmented","LAYOUT_OPTIONS","CONTENT_COMPACT_OPTIONS","LayoutPanel","handleLayoutChange","handleContentCompactChange","compact","handleSidebarWidthChange","width","option","QuestionCircleOutlined","Space","Button","MinusOutlined","InputNumber","PlusOutlined","Select","Input","ShortcutKeyPanel","TRANSITION_OPTIONS","LANGUAGE_OPTIONS","GeneralPanel","opt","TAB_OPTIONS","TAB_COMPONENTS","PreferencesPanel","activeTab","setActiveTab","ActiveComponent","Drawer","ReloadOutlined","CloseOutlined","onClose","CopyOutlined","message","LogoutOutlined","localeMap","ThemeProvider","themePrefs","appPrefs","handleChange","effectiveMode","themeConfig","components","useLayoutEffect","root","bgLayout","colorText","useEffect","proIntl","enUSIntl","zhCNIntl","defaultMessage","watermarkText","watermarkColor","ConfigProvider","ProConfigProvider","Watermark","useLocaleSync","initialized","useRef","detected","initI18n","initReactI18next","initialLang","lngs","localeLabels","LocaleSwitcher","lng","changeLocale","checkAccessByRoles","requiredRoles","userRoleSet","userRoles","checkAccessByCodes","requiredCodes","userCodesSet","accessCodes","checkAccessByAuthority","authority","allSet","useAccess","getAccessStatic","hasAccessByRoles","hasAccessByCodes","hasAccessByAuthority","getAllPermissions","AccessControl","hasAccess","Fragment","parseResourceHiddenFields","entry","dotIndex","resource","field","isFieldHidden","injectRedirects","routes","route","firstChild","sortRoutes","transformMetaToHandle","transformRoutesWithHandle","transformed","ThemeLoading","prefTheme","defaultTheme","themeOverride","autoHide","progress","statusConfig","text","LoadingOutlined","content","Spin","subText","Progress","baseClasses","fullScreen","className","style","generateRoutesByBackend","menuRoutes","fetchMenuListAsync","normalizedPageMap","pageMap","normalizedKey","normalizeViewPath","rebaseBackendTree","convertRoutes","layoutMap","componentPath","Component","normalizedPath","candidates","candidate","createElement","Suspense","Navigate","fullPath","resolveRoutePath","parentPath","childPath","normalized","generateRoutesByFrontend","walk","kept","pass","hasPermission","permissions","mvwf","menuVisibleWithForbidden","next","forbiddenElement","generateMenus","routePathMap","buildRoutePathMap","getAllRoutes","menus","convertRouteToMenu","sortMenus","menu","traverse","resolvedPath","hideChildrenInMenu","parents","currentParents","menuPath","link","redirect","hideInMenu","menuList","shouldShowMenu","separateRoutes","layoutRoutes","otherRoutes","pickFirstMenuPath","createAccessibleRouter","isBackendMode","homeTarget","createBrowserRouter","AccessibleRoutesContext","createContext","isAppRoute","createLazyRoute","LazyComponent","lazy","loader","extractRoutes","module","namedExport","filterValidRoutes","filterRoutesByPermission","acc","filtered","childFiltered","sortMenuTree","transformRoutesToMenu","menuItem","mergeRouteModules","mergedRoutes","modulePath","routeModule","routeModules","moduleRoutes","filterEmpty","sortByPath","mergeRouteModulesAsync","resetAccessibleRoutes","filterRoutesByWhitelist","allRoutes","staticRouteNames","whitelist","extractRouteNames","useTitle","prev","timeFormat","language","useTimes","time","setTime","useProTableScrollY","enabled","rafId","observedContainer","resize","container","containerRef","requireFlexParent","parentStyle","resizeObserver","mutationObserver","tableBody","containerHeight","containerRect","aboveSpace","pagination","belowSpace","pagStyle","availableHeight","bodyTable","timers"],"sources":["../src/env.ts","../src/auth-gateway.ts","../src/locales/en-US/auth.json","../src/locales/en-US/common.json","../src/locales/en-US/editor.json","../src/locales/en-US/preferences.json","../src/locales/en-US/index.ts","../src/locales/zh-CN/auth.json","../src/locales/zh-CN/common.json","../src/locales/zh-CN/editor.json","../src/locales/zh-CN/preferences.json","../src/locales/zh-CN/index.ts","../src/locales/index.ts","../src/query-client.ts","../src/storage/storage.class.ts","../src/transport/rest/captcha-headers.ts","../src/transport/rest/utils.ts","../src/transport/rest/preset-interceptors.ts","../src/utils/merge.ts","../src/utils/inference.ts","../src/utils/utils.ts","../src/utils/nprogress.ts","../src/utils/date.ts","../src/utils/color.ts","../src/utils/url.ts","../src/utils/diff.ts","../src/utils/tree.ts","../src/transport/rest/modules/downloader.ts","../src/transport/rest/modules/interceptor.ts","../src/transport/rest/modules/uploader.ts","../src/transport/rest/request-client.ts","../src/transport/rest/request-api.ts","../src/transport/rest/pagination.ts","../src/transport/sse/sse_client.ts","../src/transport/sse/event.ts","../src/transport/sse/index.ts","../src/i18n/utils/detector.ts","../src/i18n/utils/formatter.ts","../src/i18n/utils/sync.ts","../src/i18n/hooks/useI18n.ts","../src/preferences/config/constants.ts","../src/preferences/config/darkTheme.ts","../src/preferences/config/default.ts","../src/preferences/config/lightTheme.ts","../../../node_modules/.pnpm/zustand@5.1.0_@types+react@19.3.0_react@19.3.1_use-sync-external-store@1.7.0_react@19.3.1_/node_modules/zustand/esm/middleware.mjs","../src/preferences/utils/merge.ts","../src/preferences/store/index.ts","../src/preferences/hooks/usePreferences.ts","../src/preferences/hooks/useLocale.ts","../src/preferences/hooks/useThemeConfig.ts","../src/preferences/utils/diff.ts","../src/hooks/useTokenRefresh.ts","../src/stores/auth.ts","../src/stores/user.ts","../src/preferences/theme-transition.ts","../src/preferences/components/PreferencesPanel/AppearancePanel.tsx","../src/preferences/components/PreferencesPanel/LayoutPanel.tsx","../src/preferences/components/PreferencesPanel/ShortcutKeyPanel.tsx","../src/preferences/components/PreferencesPanel/GeneralPanel.tsx","../src/preferences/components/PreferencesPanel/index.tsx","../src/preferences/components/ThemeProvider/index.tsx","../src/i18n/hooks/useLocaleSync.ts","../src/i18n/config/i18n.ts","../src/i18n/components/LocaleSwitcher/index.tsx","../src/access/use-access.ts","../src/access/access-control.tsx","../src/access/field-permission.ts","../src/router/utils/inject-redirect.ts","../src/router/utils/sort-routes.ts","../src/router/utils/transform-meta-to-handle.ts","../src/components/Loading/index.tsx","../src/router/generators/generate-routes-backend.ts","../src/router/generators/generate-routes-frontend.ts","../src/router/generators/generate-menus.ts","../src/router/factory.ts","../src/router/routes-context.ts","../src/router/types/index.ts","../src/router/utils/lazy.tsx","../src/router/utils/loader.ts","../src/router/utils/menu.ts","../src/router/utils/merge-route-modules.ts","../src/router/utils/reset-routes.ts","../src/hooks/useTitle.ts","../src/hooks/useTime.ts","../src/hooks/useProTableScrollY.ts"],"sourcesContent":["/**\n * 包内不读 import.meta.env——lib 构建会把它静态替换成\"包构建时\"的值,\n * 宿主开发环境的真实 DEV 态传不进来。因此包统一走运行时注入的 dev 标志,\n * 由宿主在 bootstrap 阶段调用 configureAdminRuntime({ isDev: import.meta.env.DEV })。\n */\nlet devMode = false;\n\nexport function configureAdminRuntime(options: { isDev?: boolean } = {}): void {\n devMode = Boolean(options.isDev);\n}\n\n/** 宿主是否处于开发环境(用于 dev-only 诊断告警,生产恒为 false) */\nexport function isDevMode(): boolean {\n return devMode;\n}\n","import type { AdminUserInfo } from './types';\n\n/**\n * AuthGateway:认证数据面与机制面的唯一边界。\n *\n * 本包只提供认证\"机制\"(token 内存态、刷新调度、MFA 挑战态、SSE 重连、登出清理),\n * 所有实际的后端调用(登录/登出/刷新/MFA 验证/SSO/拉取用户信息)与口令加密都由宿主\n * 应用启动时通过 setAuthGateway 注入。这样宿主可以自由再生成自己的 API 客户端\n * (如 go-wind-admin 的 `make ts` 产物)而不与包的发布版本漂移。\n */\n\n/** 登录请求参数(最小结构;宿主可经 [key: string]: unknown 附加协议字段) */\nexport interface AdminLoginParams {\n username?: string;\n password?: string;\n [key: string]: unknown;\n}\n\n/** 登录/刷新/MFA 验证的统一结果(最小结构,字段名与后端 proto3 JSON 对齐) */\nexport interface AdminTokenResult {\n /** 访问令牌;为空且 mfa_operation_id 非空表示进入 MFA 挑战 */\n access_token?: string;\n /** 有效期(秒);proto3 JSON 下 int64 可能是字符串,消费方需 Number() 转换 */\n expires_in?: number | string;\n /** MFA 挑战 operation_id;非空表示密码校验通过、需二次验证 */\n mfa_operation_id?: string;\n [key: string]: unknown;\n}\n\nexport interface AuthGateway {\n /** 登录。password 必须已经 encryptPassword 加密为信封密文。 */\n login(params: AdminLoginParams): Promise<AdminTokenResult>;\n /** 完成 MFA 挑战(TOTP)。成功返回含真 token 的结果。 */\n verifyMfa(params: { operationId: string; totpCode: string }): Promise<AdminTokenResult>;\n /** 完成 OIDC SSO 登录:code+state 换令牌。 */\n ssoLogin(params: { code: string; state: string }): Promise<AdminTokenResult>;\n /** 服务端登出。失败由实现方决定是否吞错——机制层继续本地清理。 */\n logout(): Promise<unknown>;\n /** 刷新 access token(refresh token 走 HttpOnly Cookie,由实现方保证浏览器携带)。 */\n refresh(): Promise<AdminTokenResult>;\n /** 拉取当前登录用户信息(登录成功与 token 刷新后惰性补拉共用)。 */\n fetchProfile(): Promise<AdminUserInfo>;\n /**\n * 口令加密(AES 信封/混合信封,与后端 NeedDecrypt 同规)。\n * 密钥属宿主环境配置(如 VITE_AES_KEY / VITE_PASSWORD_RSA_PUB),由实现方持有。\n */\n encryptPassword(plain: string): Promise<string>;\n}\n\nlet gateway: AuthGateway | null = null;\n\n/** 宿主启动时(bootstrap)注入认证数据面实现。重复注入覆盖旧的。 */\nexport function setAuthGateway(g: AuthGateway): void {\n gateway = g;\n}\n\n/** 获取已注入的 gateway;未注入即抛错(fail-fast,不吞错)。 */\nexport function getAuthGateway(): AuthGateway {\n if (!gateway) {\n throw new Error(\n '[admin-react] AuthGateway 尚未注入:请在应用 bootstrap 阶段调用 setAuthGateway({...})',\n );\n }\n return gateway;\n}\n","{\n \"welcomeBack\": \"Welcome Back\",\n \"loginDescription\": \"Please enter your account information to login\",\n \"tenantCodeLabel\": \"Tenant code\",\n \"tenantCodePlaceholder\": \"Enter tenant code (leave empty for platform)\",\n \"usernameLabel\": \"Username\",\n \"usernamePlaceholder\": \"Username, email or mobile number\",\n \"usernameRequired\": \"Please enter username\",\n \"passwordLabel\": \"Password\",\n \"passwordPlaceholder\": \"Please enter password\",\n \"passwordRequired\": \"Please enter password\",\n \"rememberAccount\": \"Remember Account\",\n \"loginButton\": \"Login\",\n \"loggingIn\": \"Logging in...\",\n \"switchLanguage\": \"Switch Language\",\n \"switchToDarkMode\": \"Switch to Dark Mode\",\n \"switchToLightMode\": \"Switch to Light Mode\",\n \"systemTitle\": \"GoWind Admin Management System\",\n \"systemDescription\": \"Ready-to-use enterprise-level admin management system\",\n \"copyright\": \"Copyright © {{year}} GoWind\",\n \"hasAccount\": \"Already have an account?\",\n \"backToLogin\": \"Login Now\",\n \"confirmPasswordPlaceholder\": \"Please enter password again\",\n \"confirmPasswordRequired\": \"Please confirm password\",\n \"passwordMismatch\": \"Passwords do not match\",\n \"passwordMinLength\": \"Password must be at least 6 characters\",\n \"loginSuccess\": \"Login successful\",\n \"loginFailed\": \"Login failed\",\n \"sessionExpired\": \"Session expired, please log in again\",\n \"captchaLabel\": \"Captcha\",\n \"captchaPlaceholder\": \"Please enter captcha\",\n \"captchaRequired\": \"Please enter captcha\",\n \"captchaRefresh\": \"Click to refresh captcha\",\n \"langZhCN\": \"中文\",\n \"langEnUS\": \"English\",\n \"mfaChallengeTitle\": \"Multi-Factor Authentication\",\n \"mfaChallengeDesc\": \"Enter the 6-digit verification code from your authenticator app to continue.\",\n \"mfaCodeLabel\": \"Verification code\",\n \"mfaCodePlaceholder\": \"Enter 6-digit code\",\n \"mfaCodeRequired\": \"Please enter the verification code\",\n \"mfaCodeFormatError\": \"Verification code must be 6 digits\",\n \"mfaVerify\": \"Verify\",\n \"mfaCancel\": \"Cancel\",\n \"mfaVerifyFailed\": \"MFA verification failed\",\n \"mfaNotRequired\": \"No MFA challenge in progress\",\n \"forgotPassword\": \"Forgot Password\",\n \"noAccount\": \"Don't have an account?\",\n \"ssoLoginFailed\": \"SSO login failed\",\n \"ssoButton\": \"Sign in with SSO\",\n \"ssoCallbackFailed\": \"SSO login failed\"\n}\n","{\n \"button\": {\n \"add\": \"Add\",\n \"search\": \"Search\",\n \"back\": \"Back\",\n \"ok\": \"OK\",\n \"confirm\": \"Confirm\",\n \"cancel\": \"Cancel\",\n \"edit\": \"Edit\",\n \"delete\": \"Delete\",\n \"submit\": \"Submit\",\n \"reset\": \"Reset\",\n \"retry\": \"Retry\",\n \"more\": \"More\",\n \"copy\": \"Copy\",\n \"save\": \"Save\",\n \"close\": \"Close\"\n },\n \"header\": {\n \"profile\": \"Profile\",\n \"settings\": \"Preferences\",\n \"inbox\": \"Inbox\",\n \"lockScreen\": \"Lock Screen\",\n \"logout\": \"Logout\",\n \"logoutConfirmTitle\": \"Notice\",\n \"logoutConfirmMessage\": \"Do you want to logout?\",\n \"fullscreen\": \"Fullscreen\",\n \"exitFullscreen\": \"Exit Fullscreen\",\n \"helpDoc\": \"Help Docs\",\n \"notification\": \"Notifications\",\n \"guest\": \"Guest\",\n \"switchToLight\": \"Switch to Light Mode\",\n \"switchToDark\": \"Switch to Dark Mode\",\n \"expandSidebar\": \"Expand Sidebar\",\n \"collapseSidebar\": \"Collapse Sidebar\",\n \"refresh\": \"Refresh Page\",\n \"switchLanguage\": \"Switch Language\",\n \"search\": \"Search\",\n \"searchPlaceholder\": \"Search menu or features...\",\n \"searchShortcut\": \"Ctrl K\",\n \"searching\": \"Searching…\",\n \"noResults\": \"No matching pages found\",\n \"noHistory\": \"No recent searches\",\n \"searchRecent\": \"Recent\",\n \"semanticTitle\": \"Semantic Search\",\n \"searchSelect\": \"Select\",\n \"searchSwitch\": \"Switch\",\n \"searchClose\": \"Close\"\n },\n \"lockScreen\": {\n \"title\": \"Lock Screen\",\n \"placeholder\": \"Please enter password\",\n \"lock\": \"Locking\",\n \"unlock\": \"Click to unlock\",\n \"entry\": \"Enter the system\",\n \"backToLogin\": \"Back to login\",\n \"errorTip\": \"Password error, please re-enter\"\n },\n \"refresh\": \"Refresh\",\n \"home\": \"Home\",\n \"iframe\": {\n \"noUrlTitle\": \"No Embedded Page URL Configured\",\n \"noUrlDesc\": \"Please provide an iframe URL in the route config (via state.url or query parameter url)\",\n \"loading\": \"Loading embedded page...\",\n \"loadErrorTitle\": \"Load Failed\",\n \"loadErrorDesc\": \"Failed to load iframe content, please check if the link is valid\",\n \"embeddedPage\": \"Embedded Page\"\n },\n \"fallback\": {\n \"unauthorized\": \"Unauthorized\",\n \"unauthorizedDesc\": \"You don't have permission to access this page. Please log in and try again.\",\n \"forbidden\": \"Forbidden\",\n \"forbiddenDesc\": \"You don't have permission to access this resource.\",\n \"pageNotFound\": \"Page Not Found\",\n \"pageNotFoundDesc\": \"Sorry, the page you visited does not exist.\",\n \"internalError\": \"Internal Server Error\",\n \"internalErrorDesc\": \"An internal server error occurred. Please try again later.\",\n \"comingSoon\": \"Coming Soon\",\n \"offlineError\": \"Offline\",\n \"offlineErrorDesc\": \"Network connection has been lost. Please check your network settings.\",\n \"errorFallback\": {\n \"title\": \"Page Rendering Error\",\n \"subTitle\": \"An unexpected error occurred during component loading. It has been intercepted to prevent application crash. You can try to retry or go back to home page.\",\n \"retry\": \"Retry\",\n \"backToHome\": \"Back to Home\",\n \"refreshPage\": \"Refresh Page\",\n \"errorDetails\": \"Error Details (Development Environment Only)\",\n \"copy\": \"Copy\",\n \"copied\": \"Copied\",\n \"copySuccess\": \"Error info copied to clipboard\",\n \"copyFailed\": \"Copy failed, please select and copy manually\",\n \"errorMessage\": \"Error Message:\",\n \"callStack\": \"Call Stack:\",\n \"componentStack\": \"Component Stack:\"\n }\n },\n \"sidebar\": {\n \"expandMenu\": \"Expand Menu\",\n \"collapseMenu\": \"Collapse Menu\",\n \"switchToManual\": \"Switch to Manual Mode (Fixed Sidebar)\",\n \"switchToAuto\": \"Switch to Auto Mode (Expand on Hover)\"\n },\n \"skeleton\": {\n \"loading\": \"Loading\"\n },\n \"loading\": {\n \"text\": \"Loading\",\n \"success\": \"Loaded successfully\",\n \"failed\": \"Failed to load\",\n \"initializing\": \"Initializing\",\n \"loadingRouter\": \"Loading route configuration...\"\n },\n \"table\": {\n \"columnResizeHint\": \"Drag to resize column, double-click to auto-fit\"\n },\n \"unknownPage\": \"Unknown Page\",\n \"pageContainer\": {\n \"defaultTitle\": \"Page\",\n \"forbiddenTitle\": \"Access Denied\",\n \"forbiddenDesc\": \"You do not have permission to view this page. Please contact your administrator to request access.\"\n },\n \"tabs\": {\n \"close\": \"Close\",\n \"unpin\": \"Unpin\",\n \"pin\": \"Pin\",\n \"maximize\": \"Maximize\",\n \"exitMaximize\": \"Exit Maximize\",\n \"reload\": \"Reload\",\n \"openInNewWindow\": \"Open in new window\",\n \"closeLeft\": \"Close tabs to the left\",\n \"closeRight\": \"Close tabs to the right\",\n \"closeOthers\": \"Close other tabs\",\n \"closeAll\": \"Close all tabs\"\n },\n \"app\": {\n \"defaultName\": \"Admin\"\n },\n \"clipboard\": {\n \"copyFailed\": \"Copy failed, please copy manually\",\n \"copyDenied\": \"Copy operation was denied\"\n },\n \"placeholder\": {\n \"underDevelopment\": \"{{pageName}} - Under Development\",\n \"input\": \"Please enter\",\n \"select\": \"Please select\"\n },\n \"requestError\": {\n \"networkError\": \"Network connection error, please check your network settings and try again\",\n \"timeout\": \"Request timeout, please check your network connection and try again\",\n \"badRequest\": \"Bad request\",\n \"unauthorized\": \"Unauthorized, please login\",\n \"forbidden\": \"Insufficient permissions\",\n \"notFound\": \"The requested resource does not exist\",\n \"requestTimeout\": \"Request timeout\",\n \"internalServerError\": \"Internal server error, please try again later\"\n },\n \"request\": {\n \"error\": {\n \"networkError\": \"Network connection error, please check your network settings and try again\",\n \"timeout\": \"Request timeout, please check your network connection and try again\",\n \"unknownError\": \"Request error occurred, please try again later\"\n },\n \"reason\": {\n \"BAD_REQUEST\": \"Bad request\",\n \"INVALID_GRANT_TYPE\": \"Invalid grant type\",\n \"INVALID_USERID\": \"Invalid user ID\",\n \"INVALID_TOKEN\": \"Invalid token\",\n \"INVALID_PASSWORD\": \"Invalid password, please re-enter\",\n \"UNAUTHORIZED\": \"Unauthorized, please login\",\n \"USER_FREEZE\": \"User is frozen, please contact administrator\",\n \"INCORRECT_APP_SECRET\": \"Incorrect app secret\",\n \"INCORRECT_ACCESS_TOKEN\": \"Incorrect access token\",\n \"INCORRECT_REFRESH_TOKEN\": \"Incorrect refresh token\",\n \"TOKEN_EXPIRED\": \"Token has expired\",\n \"TOKEN_NOT_EXIST\": \"Token does not exist\",\n \"PAYMENT_REQUIRED\": \"Payment required\",\n \"FORBIDDEN\": \"Insufficient permissions\",\n \"NOT_FOUND\": \"The requested resource does not exist\",\n \"USER_NOT_FOUND\": \"User not found\",\n \"METHOD_NOT_ALLOWED\": \"Method not allowed\",\n \"NOT_ACCEPTABLE\": \"Not acceptable\",\n \"PROXY_AUTHENTICATION_REQUIRED\": \"Proxy authentication required\",\n \"REQUEST_TIMEOUT\": \"Request timeout\",\n \"CONFLICT\": \"Request conflict\",\n \"GONE\": \"Resource has been deleted\",\n \"LENGTH_REQUIRED\": \"Content-Length required\",\n \"PRECONDITION_FAILED\": \"Precondition failed\",\n \"PAYLOAD_TOO_LARGE\": \"Payload too large\",\n \"URI_TOO_LONG\": \"Request URI too long\",\n \"UNSUPPORTED_MEDIA_TYPE\": \"Unsupported media type\",\n \"RANGE_NOT_SATISFIABLE\": \"Range not satisfiable\",\n \"EXPECTATION_FAILED\": \"Expectation failed\",\n \"IM_A_TEAPOT\": \"I'm a teapot\",\n \"MISDIRECTED_REQUEST\": \"Misdirected request\",\n \"UNPROCESSABLE_ENTITY\": \"Unprocessable entity\",\n \"LOCKED\": \"Resource locked\",\n \"FAILED_DEPENDENCY\": \"Dependency failed\",\n \"TOO_EARLY\": \"Too early\",\n \"UPGRADE_REQUIRED\": \"Upgrade required\",\n \"PRECONDITION_REQUIRED\": \"Precondition required\",\n \"TOO_MANY_REQUESTS\": \"Too many requests\",\n \"REQUEST_HEADER_FIELDS_TOO_LARGE\": \"Request header fields too large\",\n \"UNAVAILABLE_FOR_LEGAL_REASONS\": \"Unavailable for legal reasons\",\n \"INTERNAL_SERVER_ERROR\": \"Internal server error, please try again later\",\n \"NOT_IMPLEMENTED\": \"Not implemented\",\n \"BAD_GATEWAY\": \"Bad gateway\",\n \"SERVICE_UNAVAILABLE\": \"Service unavailable\",\n \"GATEWAY_TIMEOUT\": \"Gateway timeout\",\n \"HTTP_VERSION_NOT_SUPPORTED\": \"HTTP version not supported\",\n \"VARIANT_ALSO_NEGOTIATES\": \"Variant also negotiates\",\n \"INSUFFICIENT_STORAGE\": \"Insufficient storage\",\n \"LOOP_DETECTED\": \"Loop detected\",\n \"NOT_EXTENDED\": \"Not extended\",\n \"NETWORK_AUTHENTICATION_REQUIRED\": \"Network authentication required\",\n \"NETWORK_READ_TIMEOUT_ERROR\": \"Network read timeout\",\n \"NETWORK_CONNECT_TIMEOUT_ERROR\": \"Network connect timeout\",\n \"USER_DISABLED\": \"User has been disabled\",\n \"USER_LOCKED\": \"User has been locked\",\n \"DUPLICATE_ENTRY\": \"Data already exists\",\n \"VALIDATION_FAILED\": \"Validation failed\",\n \"INTERNAL_ERROR\": \"Internal server error\"\n },\n \"status\": {\n \"400\": \"Bad request\",\n \"401\": \"Unauthorized, please login\",\n \"402\": \"Payment required\",\n \"403\": \"Insufficient permissions\",\n \"404\": \"The requested resource does not exist\",\n \"405\": \"Method not allowed\",\n \"406\": \"Not acceptable\",\n \"407\": \"Proxy authentication required\",\n \"408\": \"Request timeout\",\n \"409\": \"Request conflict\",\n \"410\": \"Resource has been deleted\",\n \"411\": \"Content-Length required\",\n \"412\": \"Precondition failed\",\n \"413\": \"Payload too large\",\n \"414\": \"Request URI too long\",\n \"415\": \"Unsupported media type\",\n \"416\": \"Range not satisfiable\",\n \"417\": \"Expectation failed\",\n \"418\": \"I'm a teapot\",\n \"421\": \"Misdirected request\",\n \"422\": \"Unprocessable entity\",\n \"423\": \"Resource locked\",\n \"424\": \"Dependency failed\",\n \"425\": \"Too early\",\n \"426\": \"Upgrade required\",\n \"428\": \"Precondition required\",\n \"429\": \"Too many requests\",\n \"431\": \"Request header fields too large\",\n \"451\": \"Unavailable for legal reasons\",\n \"500\": \"Internal server error, please try again later\",\n \"501\": \"Not implemented\",\n \"502\": \"Bad gateway\",\n \"503\": \"Service unavailable\",\n \"504\": \"Gateway timeout\",\n \"505\": \"HTTP version not supported\",\n \"506\": \"Variant also negotiates\",\n \"507\": \"Insufficient storage\",\n \"508\": \"Loop detected\",\n \"510\": \"Not extended\",\n \"511\": \"Network authentication required\",\n \"598\": \"Network read timeout\",\n \"599\": \"Network connect timeout\"\n }\n },\n \"import\": {\n \"title\": \"Import Data\",\n \"uploadText\": \"Drag file here, or click to upload\",\n \"uploadHint\": \"Only .xlsx files are supported\",\n \"downloadTemplate\": \"Download Template\",\n \"importSuccess\": \"Imported successfully ({{count}} rows)\",\n \"importFailed\": \"Import failed\",\n \"noDataParsed\": \"No data parsed\",\n \"readFileFailed\": \"Failed to read file\"\n },\n \"export\": {\n \"title\": \"Export All\",\n \"csv\": \"CSV (Comma Separated)\",\n \"xlsx\": \"Excel Workbook\",\n \"success\": \"Exported {{count}} rows\",\n \"failed\": \"Export failed\",\n \"serverXlsx\": \"Server-side full (XLSX)\",\n \"serverSuccess\": \"Export file generated\",\n \"serverFailed\": \"Server-side export failed\"\n },\n \"dateRange\": {\n \"today\": \"Today\",\n \"yesterday\": \"Yesterday\",\n \"thisWeek\": \"This Week\",\n \"lastWeek\": \"Last Week\",\n \"thisMonth\": \"This Month\",\n \"lastMonth\": \"Last Month\"\n },\n \"modal\": {\n \"create\": \"Create {{moduleName}}\",\n \"update\": \"Update {{moduleName}}\",\n \"view\": \"View {{moduleName}}\"\n },\n \"drawer\": {\n \"unsavedMessage\": \"The form has been modified. Do you want to discard this edit?\",\n \"discardButton\": \"Discard\"\n }\n}\n","{\n \"bold\": \"Bold\",\n \"italic\": \"Italic\",\n \"strike\": \"Strikethrough\",\n \"underline\": \"Underline\",\n \"code\": \"Code\",\n \"subscript\": \"Subscript\",\n \"superscript\": \"Superscript\",\n \"textColor\": \"Text Color\",\n \"highlightColor\": \"Highlight Color\",\n \"bulletList\": \"Bullet List\",\n \"orderedList\": \"Ordered List\",\n \"taskList\": \"Task List\",\n \"blockquote\": \"Blockquote\",\n \"insertCodeBlock\": \"Code Block\",\n \"insertTable\": \"Insert Table\",\n \"deleteTable\": \"Delete Table\",\n \"insertHorizontalRule\": \"Horizontal Rule\",\n \"left\": \"Align Left\",\n \"center\": \"Align Center\",\n \"right\": \"Align Right\",\n \"justify\": \"Justify\",\n \"undo\": \"Undo\",\n \"redo\": \"Redo\",\n \"clearFormatting\": \"Clear Formatting\",\n \"clearContent\": \"Clear Content\",\n \"clear_content_confirm\": \"Are you sure you want to clear all content?\",\n \"removeUrl\": \"Remove Link\",\n \"insertUrl\": \"Insert Link\",\n \"uploadImage\": \"Upload Image\",\n \"insertVideo\": \"Insert Video\",\n \"insertIframe\": \"Insert Iframe\",\n \"importMarkdown\": \"Import Markdown\",\n \"words\": \"Words\",\n \"chars\": \"Chars\",\n \"insert_url\": \"Insert Link\",\n \"input_url\": \"Please enter URL\",\n \"codeLanguage\": \"Language\",\n \"selectLanguage\": \"Select language\",\n \"codeContent\": \"Code\",\n \"codeContentPlaceholder\": \"Enter code here\",\n \"videoUrl\": \"Video URL\",\n \"videoUrlPlaceholder\": \"Please enter video URL\",\n \"videoWidth\": \"Width\",\n \"iframeUrl\": \"Iframe URL\",\n \"iframeUrlPlaceholder\": \"Please enter URL\",\n \"iframeWidth\": \"Width\",\n \"iframeHeight\": \"Height\",\n \"iframeTitle\": \"Title\",\n \"iframeTitlePlaceholder\": \"Please enter title\",\n \"allowFullscreen\": \"Allow Fullscreen\",\n \"allowFullscreenEnabled\": \"Enabled\",\n \"allowFullscreenDisabled\": \"Disabled\",\n \"insertRowBefore\": \"Insert Row Before\",\n \"insertRowAfter\": \"Insert Row After\",\n \"deleteRow\": \"Delete Row\",\n \"insertColBefore\": \"Insert Column Before\",\n \"insertColAfter\": \"Insert Column After\",\n \"deleteCol\": \"Delete Column\",\n \"mergeCells\": \"Merge Cells\",\n \"splitCell\": \"Split Cell\",\n \"toggleHeaderRow\": \"Toggle Header Row\",\n \"toggleHeaderColumn\": \"Toggle Header Column\",\n \"toggleHeaderCell\": \"Toggle Header Cell\",\n \"jsonParseError\": \"JSON parse error: {{error}}\",\n \"unknownError\": \"Unknown error\",\n \"jsonSerializeError\": \"JSON serialization error\",\n \"enterFullscreen\": \"Fullscreen\",\n \"exitFullscreen\": \"Exit Fullscreen\",\n \"fontSize\": \"Font Size\",\n \"indent\": \"Increase Indent\",\n \"outdent\": \"Decrease Indent\"\n}\n","{\n \"title\": \"Preferences\",\n \"subtitle\": \"Customize Preferences & Live Preview\",\n \"tabs\": {\n \"appearance\": \"Appearance\",\n \"layout\": \"Layout\",\n \"shortcut\": \"Shortcuts\",\n \"general\": \"General\"\n },\n \"actions\": {\n \"reset\": \"Reset\",\n \"close\": \"Close\",\n \"copySettings\": \"Copy Settings\",\n \"copyPreferencesSuccess\": \"Copied: preference diff is now on your clipboard\",\n \"copyPreferencesFailed\": \"Copy failed: clipboard unavailable\",\n \"clearCacheAndLogout\": \"Clear Cache & Logout\"\n },\n \"appearance\": {\n \"theme\": \"Theme\",\n \"lightMode\": \"Light\",\n \"darkMode\": \"Dark\",\n \"followSystem\": \"Auto\",\n \"darkSidebar\": \"Dark Sidebar\",\n \"darkHeader\": \"Dark Header\",\n \"builtinThemes\": \"Built-in Themes\",\n \"themes\": {\n \"default\": \"Default\",\n \"violet\": \"Violet\",\n \"pink\": \"Pink\",\n \"yellow\": \"Yellow\",\n \"skyBlue\": \"Sky Blue\",\n \"green\": \"Green\",\n \"zinc\": \"Zinc\",\n \"deepGreen\": \"Deep Green\",\n \"deepBlue\": \"Deep Blue\",\n \"orange\": \"Orange\",\n \"rose\": \"Rose\",\n \"neutral\": \"Neutral\",\n \"slate\": \"Slate\",\n \"gray\": \"Gray\",\n \"custom\": \"Custom\"\n },\n \"radius\": \"Radius\",\n \"other\": \"Other\",\n \"colorWeakMode\": \"Color Weak Mode\",\n \"colorGrayMode\": \"Gray Mode\"\n },\n \"layout\": {\n \"layout\": \"Layout\",\n \"content\": \"Content\",\n \"modes\": {\n \"sidebarNav\": \"Vertical\",\n \"sidebarMixedNav\": \"Dual Column\",\n \"headerNav\": \"Horizontal\",\n \"mixedNav\": \"Mixed Menu\",\n \"fullContent\": \"Full Content\"\n },\n \"content\": \"Content\",\n \"contentModes\": {\n \"wide\": \"Wide\",\n \"compact\": \"Compact\"\n },\n \"sidebar\": \"Sidebar\",\n \"showSidebar\": \"Show Sidebar\",\n \"collapseMenu\": \"Collapse Menu\",\n \"collapseShowTitle\": \"Show Title When Collapsed\",\n \"width\": \"Width\",\n \"header\": \"Header\",\n \"showHeader\": \"Show Header\",\n \"mode\": \"Mode\",\n \"headerModes\": {\n \"fixed\": \"Fixed\",\n \"auto\": \"Auto\",\n \"static\": \"Static\"\n },\n \"navigationMenu\": \"Navigation Menu\",\n \"menuStyle\": \"Menu Style\",\n \"menuStyles\": {\n \"rounded\": \"Rounded\",\n \"plain\": \"Plain\"\n },\n \"menuSplit\": \"Menu Split\",\n \"accordionMode\": \"Accordion Mode\",\n \"breadcrumb\": \"Breadcrumb\",\n \"enableBreadcrumb\": \"Enable Breadcrumb\",\n \"hideWhenOnlyOne\": \"Hide When Only One\",\n \"showIcon\": \"Show Icon\",\n \"showHomeButton\": \"Show Home Button\",\n \"breadcrumbStyle\": \"Breadcrumb Style\",\n \"breadcrumbStyles\": {\n \"normal\": \"Normal\",\n \"background\": \"Background\"\n },\n \"tabbar\": \"Tab Bar\",\n \"enableTabbar\": \"Enable Tab Bar\",\n \"persistTabs\": \"Persist Tabs\",\n \"draggableSort\": \"Draggable Sort\",\n \"showTabbarIcon\": \"Show Tab Icon\",\n \"showMoreButton\": \"Show More Button\",\n \"showMaximizeButton\": \"Show Maximize Button\",\n \"tabbarStyle\": \"Tab Style\",\n \"tabbarStyles\": {\n \"chrome\": \"Chrome\",\n \"card\": \"Card\",\n \"brisk\": \"Brisk\",\n \"plain\": \"Plain\"\n },\n \"widgets\": \"Widgets\",\n \"globalSearch\": \"Global Search\",\n \"themeToggle\": \"Theme Toggle\",\n \"languageToggle\": \"Language Toggle\",\n \"fullscreen\": \"Fullscreen\",\n \"notification\": \"Notification\",\n \"lockScreen\": \"Lock Screen\",\n \"sidebarToggle\": \"Sidebar Toggle\",\n \"refresh\": \"Refresh\",\n \"preferencesPosition\": \"Preferences Position\",\n \"positions\": {\n \"auto\": \"Auto\",\n \"fixed\": \"Fixed\",\n \"hidden\": \"Hidden\"\n },\n \"footer\": \"Footer\",\n \"showFooter\": \"Show Footer\",\n \"fixedAtBottom\": \"Fixed at Bottom\",\n \"copyright\": \"Copyright\",\n \"enableCopyright\": \"Enable Copyright\",\n \"companyName\": \"Company Name\",\n \"companySite\": \"Company Website\",\n \"date\": \"Date\",\n \"icpNumber\": \"ICP Number\",\n \"icpLink\": \"ICP Link\"\n },\n \"shortcut\": {\n \"global\": \"Global\",\n \"shortcutKeys\": \"Shortcut Keys\",\n \"globalSearch\": \"Global Search\",\n \"logout\": \"Logout\",\n \"lockScreen\": \"Lock Screen\"\n },\n \"general\": {\n \"general\": \"General\",\n \"language\": \"Language\",\n \"languages\": {\n \"zhCN\": \"简体中文\",\n \"enUS\": \"English\"\n },\n \"dynamicTitle\": \"Dynamic Title\",\n \"watermark\": \"Watermark\",\n \"checkUpdates\": \"Check Updates\",\n \"animation\": \"Animation\",\n \"progressBar\": \"Page Transition Progress\",\n \"loading\": \"Page Transition Loading\",\n \"pageTransition\": \"Page Transition Animation\",\n \"transitions\": {\n \"fade\": \"Fade\",\n \"fadeDown\": \"Fade Down\",\n \"fadeSlide\": \"Fade Slide\",\n \"fadeUp\": \"Fade Up\"\n }\n }\n}\n","const modules = import.meta.glob<{ default: Record<string, unknown> }>('./*.json', {\n eager: true,\n});\n\nconst enUS: Record<string, Record<string, unknown>> = {};\nfor (const [path, mod] of Object.entries(modules)) {\n const ns = path.replace('./', '').replace(/\\.json$/, '');\n enUS[ns] = mod.default;\n}\n\nexport default enUS;\n","{\n \"welcomeBack\": \"欢迎回来\",\n \"loginDescription\": \"请输入您的账户信息以登录系统\",\n \"tenantCodeLabel\": \"租户编号\",\n \"tenantCodePlaceholder\": \"请输入租户编号(留空为平台登录)\",\n \"usernameLabel\": \"用户名\",\n \"usernamePlaceholder\": \"用户名 / 邮箱 / 手机号\",\n \"usernameRequired\": \"请输入用户名\",\n \"passwordLabel\": \"密码\",\n \"passwordPlaceholder\": \"请输入密码\",\n \"passwordRequired\": \"请输入密码\",\n \"rememberAccount\": \"记住账号\",\n \"loginButton\": \"登录\",\n \"loggingIn\": \"登录中...\",\n \"switchLanguage\": \"切换语言\",\n \"switchToDarkMode\": \"切换暗黑模式\",\n \"switchToLightMode\": \"切换亮色模式\",\n \"systemTitle\": \"风行中后台管理系统\",\n \"systemDescription\": \"开箱即用的企业级中后台管理系统\",\n \"copyright\": \"Copyright © {{year}} GoWind\",\n \"hasAccount\": \"已有账号?\",\n \"backToLogin\": \"立即登录\",\n \"confirmPasswordPlaceholder\": \"请再次输入密码\",\n \"confirmPasswordRequired\": \"请确认密码\",\n \"passwordMismatch\": \"两次输入的密码不一致\",\n \"passwordMinLength\": \"密码长度不能少于6位\",\n \"loginSuccess\": \"登录成功\",\n \"loginFailed\": \"登录失败\",\n \"sessionExpired\": \"认证已过期,请重新登录\",\n \"captchaLabel\": \"验证码\",\n \"captchaPlaceholder\": \"请输入验证码\",\n \"captchaRequired\": \"请输入验证码\",\n \"captchaRefresh\": \"点击刷新验证码\",\n \"langZhCN\": \"中文\",\n \"langEnUS\": \"English\",\n \"mfaChallengeTitle\": \"多因素认证\",\n \"mfaChallengeDesc\": \"请输入认证器应用中显示的 6 位验证码以继续。\",\n \"mfaCodeLabel\": \"动态验证码\",\n \"mfaCodePlaceholder\": \"请输入 6 位验证码\",\n \"mfaCodeRequired\": \"请输入验证码\",\n \"mfaCodeFormatError\": \"验证码必须为 6 位数字\",\n \"mfaVerify\": \"验证\",\n \"mfaCancel\": \"取消\",\n \"mfaVerifyFailed\": \"MFA 验证失败\",\n \"mfaNotRequired\": \"当前无 MFA 验证进行中\",\n \"forgotPassword\": \"忘记密码\",\n \"noAccount\": \"您没有账号?\",\n \"ssoLoginFailed\": \"SSO 登录失败\",\n \"ssoButton\": \"使用企业账号登录\",\n \"ssoCallbackFailed\": \"SSO 登录失败\"\n}\n","{\n \"button\": {\n \"add\": \"新增\",\n \"search\": \"搜索\",\n \"back\": \"返回\",\n \"ok\": \"确定\",\n \"confirm\": \"确定\",\n \"cancel\": \"取消\",\n \"edit\": \"编辑\",\n \"delete\": \"删除\",\n \"submit\": \"确定\",\n \"reset\": \"取消\",\n \"retry\": \"重试\",\n \"more\": \"更多\",\n \"copy\": \"复制\",\n \"save\": \"保存\",\n \"close\": \"关闭\"\n },\n \"home\": \"首页\",\n \"header\": {\n \"profile\": \"个人中心\",\n \"settings\": \"偏好设置\",\n \"inbox\": \"收件箱\",\n \"lockScreen\": \"锁定屏幕\",\n \"logout\": \"退出登录\",\n \"logoutConfirmTitle\": \"提示\",\n \"logoutConfirmMessage\": \"确定要退出登录吗?\",\n \"fullscreen\": \"全屏\",\n \"exitFullscreen\": \"退出全屏\",\n \"helpDoc\": \"帮助文档\",\n \"notification\": \"通知\",\n \"guest\": \"访客\",\n \"switchToLight\": \"切换到亮色模式\",\n \"switchToDark\": \"切换到暗色模式\",\n \"expandSidebar\": \"展开侧边栏\",\n \"collapseSidebar\": \"收起侧边栏\",\n \"refresh\": \"刷新页面\",\n \"switchLanguage\": \"切换语言\",\n \"search\": \"搜索\",\n \"searchPlaceholder\": \"搜索菜单或功能...\",\n \"searchShortcut\": \"Ctrl K\",\n \"searching\": \"搜索中…\",\n \"noResults\": \"未找到相关页面\",\n \"noHistory\": \"暂无最近搜索\",\n \"searchRecent\": \"最近\",\n \"semanticTitle\": \"语义搜索\",\n \"searchSelect\": \"选择\",\n \"searchSwitch\": \"切换\",\n \"searchClose\": \"关闭\"\n },\n \"lockScreen\": {\n \"title\": \"锁定屏幕\",\n \"placeholder\": \"请输入锁屏密码\",\n \"lock\": \"锁定\",\n \"unlock\": \"点击解锁\",\n \"entry\": \"进入系统\",\n \"backToLogin\": \"返回登录\",\n \"errorTip\": \"密码错误,请重新输入\"\n },\n \"refresh\": \"刷新\",\n \"iframe\": {\n \"noUrlTitle\": \"未配置嵌入页面地址\",\n \"noUrlDesc\": \"请在路由配置中提供 iframe URL(通过 state.url 或 query 参数 url)\",\n \"loading\": \"正在加载嵌入页面...\",\n \"loadErrorTitle\": \"加载失败\",\n \"loadErrorDesc\": \"iframe 内容加载失败,请检查链接是否有效\",\n \"embeddedPage\": \"嵌入页面\"\n },\n \"fallback\": {\n \"unauthorized\": \"未授权\",\n \"unauthorizedDesc\": \"您没有权限访问此页面,请登录后再试。\",\n \"forbidden\": \"禁止访问\",\n \"forbiddenDesc\": \"您没有权限访问此资源。\",\n \"pageNotFound\": \"页面未找到\",\n \"pageNotFoundDesc\": \"抱歉,您访问的页面不存在。\",\n \"internalError\": \"服务器错误\",\n \"internalErrorDesc\": \"服务器发生内部错误,请稍后重试。\",\n \"comingSoon\": \"即将推出\",\n \"offlineError\": \"网络离线\",\n \"offlineErrorDesc\": \"网络连接已断开,请检查您的网络设置。\",\n \"errorFallback\": {\n \"title\": \"页面渲染异常\",\n \"subTitle\": \"组件加载过程中遇到意外错误,已自动拦截以防止应用崩溃。您可以尝试重试或返回首页。\",\n \"retry\": \"重试加载\",\n \"backToHome\": \"返回首页\",\n \"refreshPage\": \"刷新页面\",\n \"errorDetails\": \"错误详情(仅开发环境可见)\",\n \"copy\": \"复制\",\n \"copied\": \"已复制\",\n \"copySuccess\": \"错误信息已复制到剪贴板\",\n \"copyFailed\": \"复制失败,请手动选择文本复制\",\n \"errorMessage\": \"错误信息:\",\n \"callStack\": \"调用堆栈:\",\n \"componentStack\": \"组件堆栈:\"\n }\n },\n \"sidebar\": {\n \"expandMenu\": \"展开菜单\",\n \"collapseMenu\": \"折叠菜单\",\n \"switchToManual\": \"切换为手动模式(固定菜单)\",\n \"switchToAuto\": \"切换为自动模式(鼠标悬停展开)\"\n },\n \"skeleton\": {\n \"loading\": \"加载中\"\n },\n \"loading\": {\n \"text\": \"加载中\",\n \"success\": \"加载成功\",\n \"failed\": \"加载失败\",\n \"initializing\": \"初始化中\",\n \"loadingRouter\": \"正在加载路由配置...\"\n },\n \"table\": {\n \"columnResizeHint\": \"拖动调整列宽,双击恢复自适应\"\n },\n \"unknownPage\": \"未知页面\",\n \"pageContainer\": {\n \"defaultTitle\": \"页面\",\n \"forbiddenTitle\": \"无权限访问\",\n \"forbiddenDesc\": \"您当前没有权限查看此页面,请联系管理员申请权限。\"\n },\n \"tabs\": {\n \"close\": \"关闭\",\n \"unpin\": \"取消固定\",\n \"pin\": \"固定\",\n \"maximize\": \"最大化\",\n \"exitMaximize\": \"退出最大化\",\n \"reload\": \"重新加载\",\n \"openInNewWindow\": \"在新窗口打开\",\n \"closeLeft\": \"关闭左侧标签页\",\n \"closeRight\": \"关闭右侧标签页\",\n \"closeOthers\": \"关闭其它标签页\",\n \"closeAll\": \"关闭全部标签页\"\n },\n \"app\": {\n \"defaultName\": \"管理系统\"\n },\n \"clipboard\": {\n \"copyFailed\": \"复制失败,请手动复制\",\n \"copyDenied\": \"复制操作被拒绝\"\n },\n \"placeholder\": {\n \"underDevelopment\": \"{{pageName}} - 开发中\",\n \"input\": \"请输入\",\n \"select\": \"请选择\"\n },\n \"requestError\": {\n \"networkError\": \"网络连接错误,请检查网络设置后重试\",\n \"timeout\": \"请求超时,请检查网络连接后重试\",\n \"badRequest\": \"请求参数错误\",\n \"unauthorized\": \"未授权,请登录\",\n \"forbidden\": \"权限不足\",\n \"notFound\": \"请求的资源不存在\",\n \"requestTimeout\": \"请求超时\",\n \"internalServerError\": \"服务器内部错误,请稍后重试\"\n },\n \"request\": {\n \"error\": {\n \"networkError\": \"网络连接错误,请检查网络设置后重试\",\n \"timeout\": \"请求超时,请检查网络连接后重试\",\n \"unknownError\": \"请求发生错误,请稍后重试\"\n },\n \"reason\": {\n \"BAD_REQUEST\": \"请求参数错误\",\n \"INVALID_GRANT_TYPE\": \"无效的授权类型\",\n \"INVALID_USERID\": \"无效的用户ID\",\n \"INVALID_TOKEN\": \"无效的令牌\",\n \"INVALID_PASSWORD\": \"密码错误,请重新输入\",\n \"UNAUTHORIZED\": \"未授权,请登录\",\n \"USER_FREEZE\": \"用户被冻结,请联系管理员\",\n \"INCORRECT_APP_SECRET\": \"应用密钥错误\",\n \"INCORRECT_ACCESS_TOKEN\": \"访问令牌错误\",\n \"INCORRECT_REFRESH_TOKEN\": \"刷新令牌错误\",\n \"TOKEN_EXPIRED\": \"令牌已过期\",\n \"TOKEN_NOT_EXIST\": \"令牌不存在\",\n \"PAYMENT_REQUIRED\": \"需要付费\",\n \"FORBIDDEN\": \"权限不足\",\n \"NOT_FOUND\": \"请求的资源不存在\",\n \"USER_NOT_FOUND\": \"用户不存在\",\n \"METHOD_NOT_ALLOWED\": \"请求方法不允许\",\n \"NOT_ACCEPTABLE\": \"不可接受的请求\",\n \"PROXY_AUTHENTICATION_REQUIRED\": \"需要代理认证\",\n \"REQUEST_TIMEOUT\": \"请求超时\",\n \"CONFLICT\": \"请求冲突\",\n \"GONE\": \"资源已删除\",\n \"LENGTH_REQUIRED\": \"需要Content-Length\",\n \"PRECONDITION_FAILED\": \" precondition失败\",\n \"PAYLOAD_TOO_LARGE\": \"请求体过大\",\n \"URI_TOO_LONG\": \"请求URI过长\",\n \"UNSUPPORTED_MEDIA_TYPE\": \"不支持的媒体类型\",\n \"RANGE_NOT_SATISFIABLE\": \"范围不可满足\",\n \"EXPECTATION_FAILED\": \"期望失败\",\n \"IM_A_TEAPOT\": \"我是茶壶\",\n \"MISDIRECTED_REQUEST\": \"请求被错误路由\",\n \"UNPROCESSABLE_ENTITY\": \"无法处理的请求\",\n \"LOCKED\": \"资源被锁定\",\n \"FAILED_DEPENDENCY\": \"依赖失败\",\n \"TOO_EARLY\": \"请求太早\",\n \"UPGRADE_REQUIRED\": \"需要升级\",\n \"PRECONDITION_REQUIRED\": \"需要前提条件\",\n \"TOO_MANY_REQUESTS\": \"请求过于频繁\",\n \"REQUEST_HEADER_FIELDS_TOO_LARGE\": \"请求头过大\",\n \"UNAVAILABLE_FOR_LEGAL_REASONS\": \"因法律原因不可用\",\n \"INTERNAL_SERVER_ERROR\": \"服务器内部错误,请稍后重试\",\n \"NOT_IMPLEMENTED\": \"功能未实现\",\n \"BAD_GATEWAY\": \"网关错误\",\n \"SERVICE_UNAVAILABLE\": \"服务不可用\",\n \"GATEWAY_TIMEOUT\": \"网关超时\",\n \"HTTP_VERSION_NOT_SUPPORTED\": \"不支持的HTTP版本\",\n \"VARIANT_ALSO_NEGOTIATES\": \"协商失败\",\n \"INSUFFICIENT_STORAGE\": \"存储空间不足\",\n \"LOOP_DETECTED\": \"检测到循环\",\n \"NOT_EXTENDED\": \"未扩展\",\n \"NETWORK_AUTHENTICATION_REQUIRED\": \"需要网络认证\",\n \"NETWORK_READ_TIMEOUT_ERROR\": \"网络读取超时\",\n \"NETWORK_CONNECT_TIMEOUT_ERROR\": \"网络连接超时\",\n \"USER_DISABLED\": \"用户已被禁用\",\n \"USER_LOCKED\": \"用户已被锁定\",\n \"DUPLICATE_ENTRY\": \"数据已存在\",\n \"VALIDATION_FAILED\": \"数据校验失败\",\n \"INTERNAL_ERROR\": \"服务器内部错误\"\n },\n \"status\": {\n \"400\": \"请求参数错误\",\n \"401\": \"未授权,请登录\",\n \"402\": \"需要付费\",\n \"403\": \"权限不足\",\n \"404\": \"请求的资源不存在\",\n \"405\": \"请求方法不允许\",\n \"406\": \"不可接受的请求\",\n \"407\": \"需要代理认证\",\n \"408\": \"请求超时\",\n \"409\": \"请求冲突\",\n \"410\": \"资源已删除\",\n \"411\": \"需要Content-Length\",\n \"412\": \"前提条件失败\",\n \"413\": \"请求体过大\",\n \"414\": \"请求URI过长\",\n \"415\": \"不支持的媒体类型\",\n \"416\": \"范围不可满足\",\n \"417\": \"期望失败\",\n \"418\": \"我是茶壶\",\n \"421\": \"请求被错误路由\",\n \"422\": \"无法处理的请求\",\n \"423\": \"资源被锁定\",\n \"424\": \"依赖失败\",\n \"425\": \"请求太早\",\n \"426\": \"需要升级\",\n \"428\": \"需要前提条件\",\n \"429\": \"请求过于频繁\",\n \"431\": \"请求头过大\",\n \"451\": \"因法律原因不可用\",\n \"500\": \"服务器内部错误,请稍后重试\",\n \"501\": \"功能未实现\",\n \"502\": \"网关错误\",\n \"503\": \"服务不可用\",\n \"504\": \"网关超时\",\n \"505\": \"不支持的HTTP版本\",\n \"506\": \"协商失败\",\n \"507\": \"存储空间不足\",\n \"508\": \"检测到循环\",\n \"510\": \"未扩展\",\n \"511\": \"需要网络认证\",\n \"598\": \"网络读取超时\",\n \"599\": \"网络连接超时\"\n }\n },\n \"import\": {\n \"title\": \"导入数据\",\n \"uploadText\": \"将文件拖到此处,或点击上传\",\n \"uploadHint\": \"仅支持 .xlsx 格式文件\",\n \"downloadTemplate\": \"下载导入模板\",\n \"importSuccess\": \"导入成功({{count}} 行)\",\n \"importFailed\": \"导入失败\",\n \"noDataParsed\": \"未解析到数据\",\n \"readFileFailed\": \"读取文件失败\"\n },\n \"export\": {\n \"title\": \"导出全部\",\n \"csv\": \"CSV (逗号分隔)\",\n \"xlsx\": \"Excel 工作簿\",\n \"success\": \"已导出 {{count}} 行\",\n \"failed\": \"导出失败\",\n \"serverXlsx\": \"服务端全量 (XLSX)\",\n \"serverSuccess\": \"导出文件已生成\",\n \"serverFailed\": \"服务端导出失败\"\n },\n \"dateRange\": {\n \"today\": \"今天\",\n \"yesterday\": \"昨天\",\n \"thisWeek\": \"本周\",\n \"lastWeek\": \"上周\",\n \"thisMonth\": \"本月\",\n \"lastMonth\": \"上月\"\n },\n \"modal\": {\n \"create\": \"创建{{moduleName}}\",\n \"update\": \"更新{{moduleName}}\",\n \"view\": \"查看{{moduleName}}\"\n },\n \"drawer\": {\n \"unsavedMessage\": \"表单内容已修改,是否放弃本次编辑?\",\n \"discardButton\": \"确定放弃\"\n }\n}\n","{\n \"bold\": \"粗体\",\n \"italic\": \"斜体\",\n \"strike\": \"删除线\",\n \"underline\": \"下划线\",\n \"code\": \"代码\",\n \"subscript\": \"下标\",\n \"superscript\": \"上标\",\n \"textColor\": \"文字颜色\",\n \"highlightColor\": \"高亮颜色\",\n \"bulletList\": \"无序列表\",\n \"orderedList\": \"有序列表\",\n \"taskList\": \"任务列表\",\n \"blockquote\": \"引用\",\n \"insertCodeBlock\": \"代码块\",\n \"insertTable\": \"插入表格\",\n \"deleteTable\": \"删除表格\",\n \"insertHorizontalRule\": \"水平线\",\n \"left\": \"左对齐\",\n \"center\": \"居中\",\n \"right\": \"右对齐\",\n \"justify\": \"两端对齐\",\n \"undo\": \"撤销\",\n \"redo\": \"重做\",\n \"clearFormatting\": \"清除格式\",\n \"clearContent\": \"清空内容\",\n \"clear_content_confirm\": \"确定要清空所有内容吗?\",\n \"removeUrl\": \"移除链接\",\n \"insertUrl\": \"插入链接\",\n \"uploadImage\": \"上传图片\",\n \"insertVideo\": \"插入视频\",\n \"insertIframe\": \"插入iframe\",\n \"importMarkdown\": \"导入Markdown\",\n \"words\": \"词\",\n \"chars\": \"字符\",\n \"insert_url\": \"插入链接\",\n \"input_url\": \"请输入URL\",\n \"codeLanguage\": \"编程语言\",\n \"selectLanguage\": \"选择语言\",\n \"codeContent\": \"代码内容\",\n \"codeContentPlaceholder\": \"请输入代码\",\n \"videoUrl\": \"视频URL\",\n \"videoUrlPlaceholder\": \"请输入视频URL\",\n \"videoWidth\": \"宽度\",\n \"iframeUrl\": \"Iframe URL\",\n \"iframeUrlPlaceholder\": \"请输入URL\",\n \"iframeWidth\": \"宽度\",\n \"iframeHeight\": \"高度\",\n \"iframeTitle\": \"标题\",\n \"iframeTitlePlaceholder\": \"请输入标题\",\n \"allowFullscreen\": \"允许全屏\",\n \"allowFullscreenEnabled\": \"已启用\",\n \"allowFullscreenDisabled\": \"已禁用\",\n \"insertRowBefore\": \"上方插入行\",\n \"insertRowAfter\": \"下方插入行\",\n \"deleteRow\": \"删除行\",\n \"insertColBefore\": \"左侧插入列\",\n \"insertColAfter\": \"右侧插入列\",\n \"deleteCol\": \"删除列\",\n \"mergeCells\": \"合并单元格\",\n \"splitCell\": \"拆分单元格\",\n \"toggleHeaderRow\": \"切换表头行\",\n \"toggleHeaderColumn\": \"切换表头列\",\n \"toggleHeaderCell\": \"切换表头单元格\",\n \"jsonParseError\": \"JSON解析错误: {{error}}\",\n \"unknownError\": \"未知错误\",\n \"jsonSerializeError\": \"JSON序列化错误\",\n \"enterFullscreen\": \"全屏\",\n \"exitFullscreen\": \"退出全屏\",\n \"fontSize\": \"字体大小\",\n \"indent\": \"增加缩进\",\n \"outdent\": \"减少缩进\"\n}\n","{\n \"title\": \"偏好设置\",\n \"subtitle\": \"自定义偏好设置 & 实时预览\",\n \"tabs\": {\n \"appearance\": \"外观\",\n \"layout\": \"布局\",\n \"shortcut\": \"快捷键\",\n \"general\": \"通用\"\n },\n \"actions\": {\n \"reset\": \"重置\",\n \"close\": \"关闭\",\n \"copySettings\": \"复制偏好设置\",\n \"copyPreferencesSuccess\": \"复制成功,偏好增量已写入剪贴板\",\n \"copyPreferencesFailed\": \"复制失败,剪贴板不可用\",\n \"clearCacheAndLogout\": \"清空缓存 & 退出登录\"\n },\n \"appearance\": {\n \"theme\": \"主题\",\n \"lightMode\": \"浅色\",\n \"darkMode\": \"深色\",\n \"followSystem\": \"跟随系统\",\n \"darkSidebar\": \"深色侧边栏\",\n \"darkHeader\": \"深色顶栏\",\n \"builtinThemes\": \"内置主题\",\n \"themes\": {\n \"default\": \"默认\",\n \"violet\": \"紫罗兰\",\n \"pink\": \"樱花粉\",\n \"yellow\": \"柠檬黄\",\n \"skyBlue\": \"天蓝色\",\n \"green\": \"浅绿色\",\n \"zinc\": \"锌色灰\",\n \"deepGreen\": \"深绿色\",\n \"deepBlue\": \"深蓝色\",\n \"orange\": \"橙黄色\",\n \"rose\": \"玫瑰红\",\n \"neutral\": \"中性色\",\n \"slate\": \"石板灰\",\n \"gray\": \"中灰色\",\n \"custom\": \"自定义\"\n },\n \"radius\": \"圆角\",\n \"other\": \"其它\",\n \"colorWeakMode\": \"色弱模式\",\n \"colorGrayMode\": \"灰色模式\"\n },\n \"layout\": {\n \"layout\": \"布局\",\n \"content\": \"内容\",\n \"modes\": {\n \"sidebarNav\": \"垂直\",\n \"sidebarMixedNav\": \"双列菜单\",\n \"headerNav\": \"水平\",\n \"mixedNav\": \"混合菜单\",\n \"fullContent\": \"内容全屏\"\n },\n \"content\": \"内容\",\n \"contentModes\": {\n \"wide\": \"流式\",\n \"compact\": \"定宽\"\n },\n \"sidebar\": \"侧边栏\",\n \"showSidebar\": \"显示侧边栏\",\n \"collapseMenu\": \"折叠菜单\",\n \"collapseShowTitle\": \"折叠显示菜单名\",\n \"width\": \"宽度\",\n \"header\": \"顶栏\",\n \"showHeader\": \"显示顶栏\",\n \"mode\": \"模式\",\n \"headerModes\": {\n \"fixed\": \"固定\",\n \"auto\": \"自动\",\n \"static\": \"静态\"\n },\n \"navigationMenu\": \"导航菜单\",\n \"menuStyle\": \"导航菜单风格\",\n \"menuStyles\": {\n \"rounded\": \"圆润\",\n \"plain\": \"朴素\"\n },\n \"menuSplit\": \"导航菜单分离\",\n \"accordionMode\": \"侧边导航菜单手风琴模式\",\n \"breadcrumb\": \"面包屑导航\",\n \"enableBreadcrumb\": \"开启面包屑导航\",\n \"hideWhenOnlyOne\": \"仅有一个时隐藏\",\n \"showIcon\": \"显示面包屑图标\",\n \"showHomeButton\": \"显示首页按钮\",\n \"breadcrumbStyle\": \"面包屑风格\",\n \"breadcrumbStyles\": {\n \"normal\": \"常规\",\n \"background\": \"背景\"\n },\n \"tabbar\": \"标签栏\",\n \"enableTabbar\": \"启用标签栏\",\n \"persistTabs\": \"持久化标签页\",\n \"draggableSort\": \"启动拖拽排序\",\n \"showTabbarIcon\": \"显示标签栏图标\",\n \"showMoreButton\": \"显示更多按钮\",\n \"showMaximizeButton\": \"显示最大化按钮\",\n \"tabbarStyle\": \"标签页风格\",\n \"tabbarStyles\": {\n \"chrome\": \"Chrome\",\n \"card\": \"卡片\",\n \"brisk\": \"轻快\",\n \"plain\": \"简洁\"\n },\n \"widgets\": \"小部件\",\n \"globalSearch\": \"启用全局搜索\",\n \"themeToggle\": \"启用主题切换\",\n \"languageToggle\": \"启用语言切换\",\n \"fullscreen\": \"启用全屏\",\n \"notification\": \"启用通知\",\n \"lockScreen\": \"启用锁屏\",\n \"sidebarToggle\": \"启用侧边栏切换\",\n \"refresh\": \"启用刷新\",\n \"preferencesPosition\": \"偏好设置位置\",\n \"positions\": {\n \"auto\": \"自动\",\n \"fixed\": \"固定\",\n \"hidden\": \"隐藏\"\n },\n \"footer\": \"底栏\",\n \"showFooter\": \"显示底栏\",\n \"fixedAtBottom\": \"固定在底部\",\n \"copyright\": \"版权\",\n \"enableCopyright\": \"启用版权\",\n \"companyName\": \"公司名\",\n \"companySite\": \"公司主页\",\n \"date\": \"日期\",\n \"icpNumber\": \"ICP备案号\",\n \"icpLink\": \"ICP网站链接\"\n },\n \"shortcut\": {\n \"global\": \"全局\",\n \"shortcutKeys\": \"快捷键\",\n \"globalSearch\": \"全局搜索\",\n \"logout\": \"退出登录\",\n \"lockScreen\": \"锁定屏幕\"\n },\n \"general\": {\n \"general\": \"通用\",\n \"language\": \"语言\",\n \"languages\": {\n \"zhCN\": \"简体中文\",\n \"enUS\": \"English\"\n },\n \"dynamicTitle\": \"动态标题\",\n \"watermark\": \"水印\",\n \"checkUpdates\": \"定时检查更新\",\n \"animation\": \"动画\",\n \"progressBar\": \"页面切换进度条\",\n \"loading\": \"页面切换 Loading\",\n \"pageTransition\": \"页面切换动画\",\n \"transitions\": {\n \"fade\": \"淡入\",\n \"fadeDown\": \"淡入下滑\",\n \"fadeSlide\": \"淡入滑动\",\n \"fadeUp\": \"淡入上滑\"\n }\n }\n}\n","const modules = import.meta.glob<{ default: Record<string, unknown> }>('./*.json', {\n eager: true,\n});\n\nconst zhCN: Record<string, Record<string, unknown>> = {};\nfor (const [path, mod] of Object.entries(modules)) {\n const ns = path.replace('./', '').replace(/\\.json$/, '');\n zhCN[ns] = mod.default;\n}\n\nexport default zhCN;\n","import type { ResourceLanguage } from 'i18next';\nimport enUS from './en-US';\nimport zhCN from './zh-CN';\n\nexport type SupportedLocale = 'zh-CN' | 'en-US';\n\nexport const supportedLocales: SupportedLocale[] = ['zh-CN', 'en-US'];\n\nexport const FALLBACK_LOCALE: SupportedLocale = 'zh-CN';\n\n/**\n * 包内置的框架命名空间文案(common/auth/editor/preferences)。\n * 宿主把业务命名空间与之合并后传给 initI18n——同名命名空间宿主可整体覆盖。\n */\nexport const coreResources: Record<SupportedLocale, ResourceLanguage> = {\n 'zh-CN': zhCN,\n 'en-US': enUS,\n};\n\nexport const CORE_NAMESPACES = Object.keys(zhCN);\n","import {QueryClient, defaultShouldDehydrateQuery} from '@tanstack/react-query'\n\n/**\n * 默认重试策略:生产姿态——仅网络错误重试 1 次。\n * 宿主开发环境可用 configureQueryClient({ isDev: true }) 切换为\"任何错误重试 3 次\"。\n * (包内不读 import.meta.env——lib 构建会把它静态替换成包构建时的值。)\n */\nfunction defaultRetry(failureCount: number, error: Error) {\n return failureCount < 1 && Boolean(error.message?.includes('Network'));\n}\n\n// 单独导出,方便测试 & 维护\nexport const queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n // 管理后台不缓存查询响应:每次组件挂载都向后端重新请求\n staleTime: 0,\n\n // 查询在无观察者(组件卸载)后立即回收,跨挂载不残留缓存\n gcTime: 0,\n\n // 窗口聚焦时自动重新验证\n refetchOnWindowFocus: false,\n\n // 重试策略:见 defaultRetry / configureQueryClient\n retry: defaultRetry,\n\n // 重试延迟:指数退避\n retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),\n },\n mutations: {\n // 突变失败后自动失效相关查询,触发重新获取\n onError: (error) => {\n console.error('Mutation error:', error);\n },\n },\n dehydrate: {\n // SSR 时只反序列化成功的数据\n shouldDehydrateQuery: (query) =>\n defaultShouldDehydrateQuery(query) ||\n query.state.status === 'pending',\n },\n },\n})\n\n/**\n * 宿主在 bootstrap 阶段调整 query 行为。\n * @param options.isDev 开发环境姿态:任何查询错误重试 3 次(默认仅网络错误重试 1 次)\n */\nexport function configureQueryClient(options: { isDev?: boolean } = {}): void {\n const queries = queryClient.getDefaultOptions().queries;\n if (!queries) return;\n queries.retry = options.isDev\n ? (failureCount: number) => failureCount < 3\n : defaultRetry;\n}\n","import type {\n BatchGetOptions,\n BatchProgressCallback,\n BatchResult,\n BatchSetItem,\n IStorageCache,\n StorageManagerOptions,\n StorageMeta,\n StorageMetrics,\n StorageValue,\n SyncMessage,\n} from \"./storage.types\";\n\nclass StorageManager implements IStorageCache {\n private readonly prefix: string | undefined;\n private readonly storage: Storage;\n private readonly options: Required<Omit<StorageManagerOptions, \"onMetrics\" | \"onSync\">>;\n private readonly defaultTTL?: number | null;\n private readonly tabId: string;\n private channel: BroadcastChannel | null = null;\n private metrics: StorageMetrics = {\n hits: 0,\n misses: 0,\n expirations: 0,\n sets: 0,\n errors: 0,\n quotaUsageBytes: 0,\n hitRate: 0,\n };\n private metricsDebounceTimer: number | null = null;\n private readonly onMetricsCallback?: (metrics: StorageMetrics) => void;\n private onSyncCallback?: (event: SyncMessage) => void;\n\n constructor(options: StorageManagerOptions = {}) {\n const {\n prefix = \"\",\n storageType = \"localStorage\",\n compressionThreshold = 1024,\n evictionStrategy = \"hybrid\",\n maxItems = 0,\n maxUsageMB = 10,\n defaultTTL = null,\n enableSync = true,\n onMetrics,\n onSync,\n } = options;\n\n this.prefix = prefix;\n this.options = {\n prefix,\n storageType,\n compressionThreshold,\n evictionStrategy,\n maxItems,\n maxUsageMB,\n defaultTTL,\n enableSync,\n };\n this.onMetricsCallback = onMetrics;\n this.onSyncCallback = onSync;\n this.tabId =\n typeof window !== \"undefined\"\n ? `${prefix}_${Math.random().toString(36).substring(2, 10)}`\n : \"ssr\";\n\n // SSR 兼容\n if (typeof window === \"undefined\") {\n this.storage = {} as Storage;\n return;\n }\n\n this.storage = storageType === \"localStorage\" ? window.localStorage : window.sessionStorage;\n this.defaultTTL = defaultTTL;\n\n if (enableSync) this.initSync();\n this.updateQuotaUsage().catch(() => {});\n }\n\n /**\n * 获取完整存储键(安全添加前缀)\n */\n private getFullKey(key: string): string {\n return this.prefix ? `${this.prefix}:${key}` : key;\n }\n\n private getMetaKey(key: string): string {\n return this.prefix ? `${this.prefix}:__meta:${key}` : `__meta:${key}`;\n }\n\n private readMeta(key: string): StorageMeta | null {\n const raw = this.storage.getItem(this.getMetaKey(key));\n if (!raw) return null;\n try {\n return JSON.parse(raw);\n } catch {\n return null;\n }\n }\n\n private writeMeta(key: string, meta: StorageMeta): void {\n try {\n this.storage.setItem(this.getMetaKey(key), JSON.stringify(meta));\n } catch (error) {\n console.warn(\"[StorageManager] Failed to write meta:\", error);\n }\n }\n\n /**\n * 访问时更新元数据(访问次数和最后访问时间)\n */\n private updateMetaOnAccess(key: string): void {\n const meta = this.readMeta(key);\n if (meta) {\n meta.a += 1; // 增加访问次数\n meta.t = Date.now(); // 更新最后访问时间\n this.writeMeta(key, meta);\n } else {\n // 如果没有元数据,创建新的\n this.writeMeta(key, {\n e: null,\n s: 0,\n a: 1,\n t: Date.now(),\n c: false,\n });\n }\n }\n\n /**\n * 设置时更新元数据\n */\n private updateMetaOnSet(key: string, serialized: string, ttl?: number | null): void {\n const size = new Blob([serialized]).size;\n const expiry =\n ttl !== undefined && ttl !== null\n ? Date.now() + ttl\n : this.defaultTTL\n ? Date.now() + this.defaultTTL\n : null;\n\n const existingMeta = this.readMeta(key);\n const meta: StorageMeta = {\n e: expiry,\n s: size,\n a: existingMeta?.a || 0, // 保留访问次数\n t: Date.now(), // 更新最后修改时间\n c: false,\n };\n this.writeMeta(key, meta);\n }\n\n /**\n * 移除元数据\n */\n private removeMeta(key: string): void {\n try {\n this.storage.removeItem(this.getMetaKey(key));\n } catch (error) {\n console.warn(\"[StorageManager] Failed to remove meta:\", error);\n }\n }\n\n private getAllMeta(): Map<string, StorageMeta> {\n const map = new Map<string, StorageMeta>();\n const prefix = this.prefix ? `${this.prefix}:__meta:` : \"__meta:\";\n\n for (let i = 0; i < this.storage.length; i++) {\n const k = this.storage.key(i);\n if (k?.startsWith(prefix)) {\n const shortKey = k.slice(prefix.length);\n const meta = this.readMeta(shortKey);\n if (meta) map.set(shortKey, meta);\n }\n }\n return map;\n }\n\n /**\n * 安全解析存储值\n */\n private parseValue<T>(raw: string | null): T | null {\n if (!raw) return null;\n try {\n const parsed: StorageValue<T> = JSON.parse(raw);\n // 检查过期\n if (parsed.expiry !== null && Date.now() > parsed.expiry) {\n this.trackMetric(\"expirations\");\n return null;\n }\n return parsed.data;\n } catch (error) {\n console.error(`[StorageManager] Parse error:`, error);\n this.trackMetric(\"errors\");\n return null;\n }\n }\n\n /**\n * 序列化存储值\n */\n private serializeValue<T>(data: T, ttl?: number | null): string {\n const expiry =\n ttl !== undefined && ttl !== null\n ? Date.now() + ttl\n : this.defaultTTL\n ? Date.now() + this.defaultTTL\n : null;\n\n const value: StorageValue<T> = { data, expiry };\n return JSON.stringify(value);\n }\n\n /**\n * 获取存储项\n */\n getItem<T>(key: string, defaultValue: T | null = null): T | null {\n if (!this.storage) return defaultValue;\n\n const fullKey = this.getFullKey(key);\n const raw = this.storage.getItem(fullKey);\n\n // 如果键不存在,记录未命中\n if (raw === null) {\n this.trackMetric(\"misses\");\n return defaultValue;\n }\n\n const parsed = this.parseValue<T>(raw);\n\n // 如果解析失败或过期,清理脏数据\n if (parsed === null) {\n this.storage.removeItem(fullKey);\n this.removeMeta(key);\n // parseValue 已经追踪了 expirations 或 errors\n return defaultValue;\n }\n\n // 记录命中并更新元数据\n this.trackMetric(\"hits\");\n this.updateMetaOnAccess(key);\n\n return parsed;\n }\n\n /**\n * 设置存储项\n */\n setItem<T>(key: string, value: T, ttl?: number | null): void {\n if (!this.storage) return;\n\n const fullKey = this.getFullKey(key);\n try {\n const serialized = this.serializeValue(value, ttl);\n this.storage.setItem(fullKey, serialized);\n\n // 更新元数据\n this.updateMetaOnSet(key, serialized, ttl);\n\n // 记录设置操作\n this.trackMetric(\"sets\");\n\n // 检查是否需要驱逐\n this.checkAndEvict();\n\n // 广播同步消息\n this.broadcast({ type: \"set\", key, ts: Date.now() });\n } catch (error) {\n this.trackMetric(\"errors\");\n // 处理 QuotaExceededError\n if (error instanceof DOMException && error.name === \"QuotaExceededError\") {\n console.warn(\"[StorageManager] Storage quota exceeded, clearing expired items...\");\n this.clearExpiredItems();\n // 重试一次\n try {\n const serialized = this.serializeValue(value, ttl);\n this.storage.setItem(fullKey, serialized);\n this.updateMetaOnSet(key, serialized, ttl);\n this.trackMetric(\"sets\");\n this.broadcast({ type: \"set\", key, ts: Date.now() });\n } catch (retryError) {\n console.error(\"[StorageManager] Failed to set item after cleanup:\", retryError);\n this.trackMetric(\"errors\");\n }\n } else {\n console.error(\"[StorageManager] Set item error:\", error);\n }\n }\n }\n\n /**\n * 移除存储项\n */\n removeItem(key: string): void {\n if (!this.storage) return;\n const fullKey = this.getFullKey(key);\n this.storage.removeItem(fullKey);\n\n // 清理元数据\n this.removeMeta(key);\n\n // 广播同步消息\n this.broadcast({ type: \"remove\", key, ts: Date.now() });\n }\n\n /**\n * 检查键是否存在且未过期\n */\n hasItem(key: string): boolean {\n return this.getItem(key) !== null;\n }\n\n /**\n * 清除所有带前缀的项(使用倒序遍历避免索引错位)\n */\n clear(): void {\n if (!this.storage) return;\n\n const keysToRemove: string[] = [];\n\n // 倒序遍历,收集要删除的 key\n for (let i = this.storage.length - 1; i >= 0; i--) {\n const key = this.storage.key(i);\n if (key && (this.prefix === \"\" || key.startsWith(`${this.prefix}:`))) {\n // 排除元数据键,单独处理\n if (!key.includes(\":__meta:\")) {\n keysToRemove.push(key);\n }\n this.storage.removeItem(key);\n }\n }\n\n // 清理元数据\n keysToRemove.forEach((key) => {\n const shortKey =\n this.prefix && key.startsWith(`${this.prefix}:`) ? key.slice(this.prefix.length + 1) : key;\n this.removeMeta(shortKey);\n });\n\n // 广播同步消息\n this.broadcast({ type: \"clear\", key: \"*\", ts: Date.now() });\n }\n\n /**\n * 清除所有过期项\n */\n clearExpiredItems(): void {\n if (!this.storage) return;\n\n const keysToRemove: string[] = [];\n\n // 先收集要删除的 key,避免遍历中修改集合\n for (let i = 0; i < this.storage.length; i++) {\n const key = this.storage.key(i);\n if (!key) continue;\n\n // 跳过元数据键\n if (key.includes(\":__meta:\")) {\n continue;\n }\n\n // 只检查带前缀的项\n if (this.prefix && !key.startsWith(`${this.prefix}:`)) {\n continue;\n }\n\n const raw = this.storage.getItem(key);\n if (raw) {\n try {\n const parsed: StorageValue<unknown> = JSON.parse(raw);\n if (parsed.expiry !== null && Date.now() > parsed.expiry) {\n keysToRemove.push(key);\n }\n } catch {\n // 解析失败的脏数据也清理\n keysToRemove.push(key);\n }\n }\n }\n\n // 统一删除\n keysToRemove.forEach((key) => {\n this.storage?.removeItem(key);\n // 清理对应的元数据\n const shortKey =\n this.prefix && key.startsWith(`${this.prefix}:`) ? key.slice(this.prefix.length + 1) : key;\n this.removeMeta(shortKey);\n this.trackMetric(\"expirations\");\n });\n }\n\n /**\n * 获取存储键(带前缀过滤)\n */\n key(index: number): string | null {\n if (!this.storage) return null;\n\n const rawKey = this.storage.key(index);\n if (!rawKey) return null;\n\n // 返回不带前缀的 key\n if (this.prefix && rawKey.startsWith(`${this.prefix}:`)) {\n return rawKey.slice(this.prefix.length + 1);\n }\n return this.prefix ? null : rawKey;\n }\n\n /**\n * 获取有效存储项数量(带前缀过滤)\n */\n length(): number {\n if (!this.storage) return 0;\n\n if (!this.prefix) {\n return this.storage.length;\n }\n\n let count = 0;\n const prefix = `${this.prefix}:`;\n for (let i = 0; i < this.storage.length; i++) {\n if (this.storage.key(i)?.startsWith(prefix)) {\n count++;\n }\n }\n return count;\n }\n\n /**\n * 获取底层 Storage 实例(用于高级操作)\n */\n getRawStorage(): Storage | null {\n return this.storage;\n }\n\n /**\n * 批量获取存储项\n * @param keys 要获取的键数组\n * @param options 获取选项\n * @param onProgress 进度回调(每处理完一个项触发)\n * @returns Promise<Record<string, T | null>> 键值映射\n */\n async getItems<T>(\n keys: string[],\n options: BatchGetOptions = {},\n onProgress?: BatchProgressCallback\n ): Promise<Record<string, T | null>> {\n const { skipExpired = true, includeRaw = false } = options;\n const result: Record<string, T | null> = {};\n\n if (!this.storage || keys.length === 0) {\n return result;\n }\n\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n const fullKey = this.getFullKey(key);\n\n try {\n const raw = this.storage.getItem(fullKey);\n\n if (!raw) {\n result[key] = null;\n this.trackMetric(\"misses\");\n onProgress?.({ current: i + 1, total: keys.length, key, status: \"failed\" });\n continue;\n }\n\n const parsed: StorageValue<T> = JSON.parse(raw);\n\n // 检查过期\n if (parsed.expiry !== null && Date.now() > parsed.expiry) {\n this.trackMetric(\"expirations\");\n if (skipExpired) {\n this.storage.removeItem(fullKey);\n this.removeMeta(key);\n result[key] = null;\n onProgress?.({ current: i + 1, total: keys.length, key, status: \"expired\" });\n } else {\n // 不过滤过期项,返回原始数据(调用方自行处理)\n result[key] = includeRaw ? (parsed as unknown as T) : null;\n }\n continue;\n }\n\n result[key] = parsed.data;\n this.trackMetric(\"hits\");\n this.updateMetaOnAccess(key);\n onProgress?.({ current: i + 1, total: keys.length, key, status: \"success\" });\n } catch (error) {\n console.error(`[StorageManager] getItems error for key \"${key}\":`, error);\n this.trackMetric(\"errors\");\n // 解析失败则清理脏数据\n this.storage.removeItem(fullKey);\n this.removeMeta(key);\n result[key] = null;\n onProgress?.({ current: i + 1, total: keys.length, key, status: \"failed\" });\n }\n\n // 微任务让出主线程,避免阻塞 UI\n if (i % 50 === 0) {\n await new Promise((resolve) => setTimeout(resolve, 0));\n }\n }\n\n return result;\n }\n\n /**\n * 批量设置存储项\n * @param items 键值对映射,值可以是普通值或 BatchSetItem 配置\n * @param onProgress 进度回调\n * @returns Promise<BatchResult<T>> 操作结果统计\n */\n async setItems<T>(\n items: Record<string, T | BatchSetItem<T>>,\n onProgress?: BatchProgressCallback\n ): Promise<BatchResult<T>> {\n const result: BatchResult<T> = {\n success: {},\n failed: {},\n expired: [],\n };\n\n if (!this.storage) {\n // 将所有项标记为失败\n for (const key of Object.keys(items)) {\n result.failed[key] = new Error(\"Storage not available\");\n }\n return result;\n }\n\n const entries = Object.entries(items);\n\n for (let i = 0; i < entries.length; i++) {\n const [key, item] = entries[i];\n const fullKey = this.getFullKey(key);\n\n try {\n // 解析值配置\n const { value, ttl } = this._normalizeBatchValue(item);\n const serialized = this.serializeValue(value, ttl);\n\n this.storage.setItem(fullKey, serialized);\n this.updateMetaOnSet(key, serialized, ttl);\n result.success[key] = value;\n this.trackMetric(\"sets\");\n onProgress?.({ current: i + 1, total: entries.length, key, status: \"success\" });\n } catch (error) {\n this.trackMetric(\"errors\");\n const err = error instanceof Error ? error : new Error(String(error));\n\n // 处理配额超限:自动清理过期项后重试\n if (err.name === \"QuotaExceededError\") {\n console.warn(\n \"[StorageManager] Quota exceeded during batch set, cleaning expired items...\"\n );\n this.clearExpiredItems();\n\n try {\n const { value, ttl } = this._normalizeBatchValue(item);\n const serialized = this.serializeValue(value, ttl);\n this.storage.setItem(fullKey, serialized);\n this.updateMetaOnSet(key, serialized, ttl);\n result.success[key] = value;\n this.trackMetric(\"sets\");\n onProgress?.({ current: i + 1, total: entries.length, key, status: \"success\" });\n continue;\n } catch (retryError) {\n result.failed[key] =\n retryError instanceof Error ? retryError : new Error(\"Retry failed\");\n this.trackMetric(\"errors\");\n onProgress?.({ current: i + 1, total: entries.length, key, status: \"failed\" });\n continue;\n }\n }\n\n result.failed[key] = err;\n onProgress?.({ current: i + 1, total: entries.length, key, status: \"failed\" });\n }\n\n // 每 50 项让出主线程\n if (i % 50 === 0) {\n await new Promise((resolve) => setTimeout(resolve, 0));\n }\n }\n\n // 检查是否需要驱逐\n this.checkAndEvict();\n\n // 广播同步消息\n this.broadcast({ type: \"set\", key: \"*\", ts: Date.now() });\n\n return result;\n }\n\n /**\n * 批量移除存储项\n * @param keys 要移除的键数组\n * @returns Promise<{ removed: string[]; failed: Record<string, Error> }>\n */\n async removeItems(keys: string[]): Promise<{ removed: string[]; failed: Record<string, Error> }> {\n const removed: string[] = [];\n const failed: Record<string, Error> = {};\n\n if (!this.storage) {\n for (const key of keys) {\n failed[key] = new Error(\"Storage not available\");\n }\n return { removed, failed };\n }\n\n for (const key of keys) {\n try {\n const fullKey = this.getFullKey(key);\n this.storage.removeItem(fullKey);\n this.removeMeta(key);\n removed.push(key);\n } catch (error) {\n failed[key] = error instanceof Error ? error : new Error(String(error));\n }\n }\n\n // 广播同步消息\n if (removed.length > 0) {\n this.broadcast({ type: \"remove\", key: \"*\", ts: Date.now() });\n }\n\n return { removed, failed };\n }\n\n /**\n * 批量检查键是否存在(且未过期)\n * @param keys 要检查的键数组\n * @returns Promise<Record<string, boolean>>\n */\n async hasItems(keys: string[]): Promise<Record<string, boolean>> {\n const result: Record<string, boolean> = {};\n\n if (!this.storage) {\n for (const key of keys) result[key] = false;\n return result;\n }\n\n for (const key of keys) {\n result[key] = this.getItem(key) !== null;\n }\n\n return result;\n }\n\n /**\n * 内部方法:标准化批量设置的值配置\n */\n private _normalizeBatchValue<T>(item: T | BatchSetItem<T>): { value: T; ttl?: number | null } {\n if (item && typeof item === \"object\" && \"value\" in item) {\n const batchItem = item as BatchSetItem<T>;\n return { value: batchItem.value, ttl: batchItem.ttl };\n }\n return { value: item as T };\n }\n\n private checkAndEvict(): void {\n const { maxItems, maxUsageMB } = this.options;\n if (maxItems === 0 && maxUsageMB === 0) return;\n\n const allMeta = this.getAllMeta();\n let currentItems = allMeta.size;\n let currentUsage = Array.from(allMeta.values()).reduce((sum, m) => sum + m.s, 0);\n\n if (currentItems <= maxItems && currentUsage <= maxUsageMB * 1024 * 1024) return;\n\n // 排序策略\n const sortedKeys = this.sortByEvictionPriority(allMeta);\n let evicted = 0;\n const keysToEvict: string[] = [];\n\n for (const key of sortedKeys) {\n if (currentItems <= maxItems && currentUsage <= maxUsageMB * 1024 * 1024) break;\n keysToEvict.push(key);\n currentItems--;\n currentUsage -= allMeta.get(key)!.s;\n evicted++;\n }\n\n // 异步执行删除,避免阻塞主线程\n if (keysToEvict.length > 0) {\n setTimeout(() => {\n keysToEvict.forEach((k) => this.removeItem(k));\n console.log(\n `[StorageManager] Evicted ${evicted} items using ${this.options.evictionStrategy} strategy`\n );\n }, 0);\n }\n }\n\n private sortByEvictionPriority(metas: Map<string, StorageMeta>): string[] {\n const now = Date.now();\n const keys = Array.from(metas.keys());\n\n return keys.sort((a, b) => {\n const ma = metas.get(a)!;\n const mb = metas.get(b)!;\n\n // 1. 已过期的优先淘汰\n if (ma.e !== null && ma.e < now) return -1;\n if (mb.e !== null && mb.e < now) return 1;\n\n // 2. 按策略计算权重\n const strategy = this.options.evictionStrategy;\n let scoreA: number, scoreB: number;\n\n if (strategy === \"lru\") {\n scoreA = ma.t;\n scoreB = mb.t;\n } else if (strategy === \"lfu\") {\n scoreA = ma.a;\n scoreB = mb.a;\n } else {\n // hybrid: 频率权重 60%,新鲜度 40%\n const freqWeight = 0.6;\n const timeDecay = Math.log(now - ma.t + 1);\n scoreA = ma.a * freqWeight - timeDecay * (1 - freqWeight);\n const timeDecayB = Math.log(now - mb.t + 1);\n scoreB = mb.a * freqWeight - timeDecayB * (1 - freqWeight);\n }\n\n return scoreA - scoreB; // 分数低的先淘汰\n });\n }\n\n private initSync(): void {\n if (typeof window === \"undefined\" || !window.BroadcastChannel) return;\n\n this.channel = new BroadcastChannel(`__sm_sync__${this.prefix || \"default\"}`);\n this.channel.onmessage = (e: MessageEvent<SyncMessage>) => {\n const msg = e.data;\n if (msg.tabId === this.tabId) return; // 忽略自身发出的消息\n\n this.handleSyncMessage(msg);\n this.onSyncCallback?.(msg);\n };\n }\n\n private handleSyncMessage(msg: SyncMessage): void {\n // 同步当前实例状态,避免脏读\n if (msg.type === \"remove\" || msg.type === \"clear\") {\n // 元数据已随 removeItem 清理,无需额外操作\n }\n // set 操作已在 getItem 中通过元数据保证一致性\n }\n\n private broadcast(msg: Omit<SyncMessage, \"tabId\">): void {\n if (!this.channel || !this.options.enableSync) return;\n this.channel.postMessage({ ...msg, tabId: this.tabId });\n }\n\n onSync(callback: (event: SyncMessage) => void): () => void {\n this.onSyncCallback = callback;\n return () => {\n this.onSyncCallback = undefined;\n };\n }\n\n // ================= 监控埋点 =================\n\n private trackMetric(key: keyof StorageMetrics): void {\n (this.metrics as any)[key]++;\n this.updateHitRate();\n this.debounceReportMetrics();\n }\n\n private updateHitRate(): void {\n const total = this.metrics.hits + this.metrics.misses;\n this.metrics.hitRate = total === 0 ? 0 : this.metrics.hits / total;\n }\n\n private debounceReportMetrics(): void {\n if (typeof window === \"undefined\") return;\n if (this.metricsDebounceTimer) clearTimeout(this.metricsDebounceTimer);\n this.metricsDebounceTimer = window.setTimeout(() => {\n this.onMetricsCallback?.({ ...this.metrics });\n }, 500);\n }\n\n private async updateQuotaUsage(): Promise<void> {\n if (typeof window === \"undefined\" || !navigator.storage?.estimate) return;\n try {\n const est = await navigator.storage.estimate();\n this.metrics.quotaUsageBytes = est.usage || 0;\n } catch {\n // Fallback 估算\n let size = 0;\n for (let i = 0; i < this.storage.length; i++) {\n const k = this.storage.key(i);\n if (k) size += (k.length + (this.storage.getItem(k)?.length || 0)) * 2;\n }\n this.metrics.quotaUsageBytes = size;\n }\n }\n\n getMetrics(): StorageMetrics {\n return { ...this.metrics };\n }\n\n resetMetrics(): void {\n this.metrics = {\n hits: 0,\n misses: 0,\n expirations: 0,\n sets: 0,\n errors: 0,\n quotaUsageBytes: 0,\n hitRate: 0,\n };\n }\n}\n\nexport { StorageManager };\n","/**\n * 临时存放登录请求需要携带的验证码 Header。\n *\n * 后端登录通过 HTTP Header(X-Captcha-Id / X-Captcha-Value)校验验证码,\n * 而生成的 ApiClient 无法携带 per-request header。此处用一个模块级持有器\n * 作为旁路:由 auth store 在调用 loginMutation.execute 前设置,\n * transport.unary 消费后即清除(一次性)。\n */\nlet pendingCaptchaHeaders: Record<string, string> | null = null;\n\nexport function setCaptchaHeaders(id: string, value: string) {\n pendingCaptchaHeaders = {\n 'X-Captcha-Id': id,\n 'X-Captcha-Value': value,\n };\n}\n\nexport function consumeCaptchaHeaders(): Record<string, string> | null {\n const headers = pendingCaptchaHeaders;\n pendingCaptchaHeaders = null;\n return headers;\n}\n","import type { HttpResponse } from './types';\n\n/**\n * 创建更新掩码字符串\n * @param keys - 字段键名数组\n * @returns 逗号分隔的字符串\n */\nexport function makeUpdateMask(keys: string[]): string {\n return [...keys, 'id'].join(',');\n}\n\n/**\n * 默认的请求 ID 生成器\n */\nexport function defaultIdGenerator(): string {\n try {\n // 优先使用标准 API\n const rnd = (\n globalThis as unknown as { crypto?: { randomUUID?: () => string } }\n )?.crypto?.randomUUID?.();\n if (typeof rnd === 'string' && rnd.length > 0) return rnd;\n } catch {\n // ignore\n }\n // 降级方案\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n}\n\n/**\n * 默认的错误消息提取(不依赖 i18n,纯逻辑 fallback)\n * 仅基于 reason;不再回退到后端 message 字段(该字段后续将被移除)。\n * 如需 i18n 翻译,通过 RequestClientCallbacks.getErrorMsg 注入。\n */\nexport function getDefaultErrorMsg(error: unknown): string {\n // 网络错误\n const errStr = String(error ?? '');\n if (errStr.includes('Network Error')) {\n return 'Network Error';\n }\n\n // 超时\n if (\n error &&\n typeof error === 'object' &&\n 'message' in error &&\n String(error.message).includes('timeout')\n ) {\n return 'Request Timeout';\n }\n\n // 获取后端返回数据\n const resData =\n error &&\n typeof error === 'object' &&\n 'response' in error &&\n error.response &&\n typeof error.response === 'object' &&\n 'data' in error.response\n ? (error.response.data as HttpResponse)\n : undefined;\n\n if (!resData) {\n return 'Unknown Error';\n }\n\n const { reason } = resData;\n\n // 1. 优先使用 reason(原始值,i18n 翻译由注入的 getErrorMsg 负责)\n if (reason) {\n return reason;\n }\n\n // 2. 兜底:不再回退到后端 message 字段\n return 'Unknown Error';\n}\n","import axios from 'axios';\n\nimport type { RequestClient } from './request-client';\nimport type { MakeErrorMessageFn, ResponseInterceptorConfig } from './types';\nimport { getDefaultErrorMsg } from './utils';\n\n/** 排队等待刷新的超时兜底:刷新请求自身有 10s 客户端超时,此值仅防异常挂起 */\nconst REFRESH_QUEUE_TIMEOUT_MS = 30_000;\n\n/**\n * 认证响应拦截器:处理 401 错误,支持自动刷新 token 和重新认证\n * @param client 请求客户端实例\n * @param doReAuthenticate 重新认证函数,返回 Promise<void>\n * @param doRefreshToken 刷新 token 函数,返回 Promise<string>,成功时返回新的 token\n * @param enableRefreshToken 是否启用刷新 token 功能\n * @param formatToken 格式化 token 的函数,接受原始 token 字符串,返回格式化后的 token 字符串(如添加 \"Bearer \" 前缀),如果返回 null 则不设置 Authorization 头\n * @returns 响应拦截器配置对象\n */\nexport const authenticateResponseInterceptor = ({\n client,\n doReAuthenticate,\n doRefreshToken,\n enableRefreshToken,\n formatToken,\n}: {\n client: RequestClient;\n doReAuthenticate: () => Promise<void>;\n doRefreshToken: () => Promise<string>;\n enableRefreshToken: boolean;\n formatToken: (token: string) => null | string;\n}): ResponseInterceptorConfig => {\n return {\n rejected: async (error) => {\n const { config, response } = error;\n\n /// 不是 401 → 直接抛错,交给错误拦截器处理\n if (response?.status !== 401) {\n throw error;\n }\n\n // refresh-token 请求自身返回 401,说明 refresh token 已失效\n // 必须直接走重新认证,否则会死锁(刷新请求进入队列等待自己完成)\n if (config.url?.includes('/refresh-token')) {\n await doReAuthenticate();\n throw Object.assign(error, {\n __handledByAuthInterceptor: true,\n });\n }\n\n // 判断是否启用了 refreshToken 功能\n // 如果没有启用或者已经是重试请求了,直接跳转到重新登录\n if (!enableRefreshToken || config.__isRetryRequest) {\n await doReAuthenticate();\n // 标记错误已由认证拦截器处理\n\n throw Object.assign(error, {\n __handledByAuthInterceptor: true,\n });\n }\n\n // 如果正在刷新 token,则将请求加入队列,等待刷新完成。\n // 两道防挂死兜底(历史故障:队列无超时无 reject,刷新链路一旦挂起,\n // 全部数据页渐进进入永久 loading,只能重登恢复):\n // 1. 排队重试带 __isRetryRequest——重试再 401 不再入刷,防循环;\n // 2. 排队条目 30s 超时摘除并拒绝——刷新链路异常挂起时请求快速失败。\n if (client.isRefreshing) {\n config.__isRetryRequest = true;\n return new Promise((resolve, reject) => {\n const callback = (newToken: string) => {\n clearTimeout(timer);\n // 刷新失败时队列会以空串唤醒:重发注定再吃 401,直接以已处理的\n // 认证错误拒绝,交由调用方与 doReAuthenticate 收尾\n if (!newToken) {\n reject(\n Object.assign(new Error('Authentication required'), {\n __handledByAuthInterceptor: true,\n }),\n );\n return;\n }\n config.headers.Authorization = formatToken(newToken);\n resolve(client.request(config.url, { ...config }));\n };\n const timer = setTimeout(() => {\n client.refreshTokenQueue = client.refreshTokenQueue.filter((cb) => cb !== callback);\n console.warn('[Auth] 刷新等待超时,排队请求快速失败:', config.url);\n reject(\n Object.assign(new Error('Authentication refresh wait timeout'), {\n __handledByAuthInterceptor: true,\n }),\n );\n }, REFRESH_QUEUE_TIMEOUT_MS);\n client.refreshTokenQueue.push(callback);\n });\n }\n\n // 标记开始刷新 token\n client.isRefreshing = true;\n // 标记当前请求为重试请求,避免无限循环\n config.__isRetryRequest = true;\n\n try {\n const newToken = await doRefreshToken();\n\n // refreshToken 返回空字符串表示无法刷新,直接走重新认证\n if (!newToken) {\n client.refreshTokenQueue.forEach((callback) => callback(''));\n client.refreshTokenQueue = [];\n await doReAuthenticate();\n const handledError = Object.assign(new Error('Authentication required'), {\n __handledByAuthInterceptor: true,\n });\n return Promise.reject(handledError);\n }\n\n // 处理队列中的请求\n client.refreshTokenQueue.forEach((callback) => callback(newToken));\n // 清空队列\n client.refreshTokenQueue = [];\n\n return client.request(error.config.url, { ...error.config });\n } catch (refreshError) {\n // 如果刷新 token 失败,处理错误(如强制登出或跳转登录页面)\n client.refreshTokenQueue.forEach((callback) => callback(''));\n client.refreshTokenQueue = [];\n\n console.error('Refresh token failed:', refreshError);\n\n await doReAuthenticate();\n\n // 标记错误已由认证拦截器处理,不继续抛出错误,避免触发错误消息拦截器\n const handledError = Object.assign(new Error('Authentication required'), {\n __handledByAuthInterceptor: true,\n });\n return Promise.reject(handledError);\n } finally {\n client.isRefreshing = false;\n }\n },\n };\n};\n\n/**\n * 错误消息拦截器:提取错误文本并回调\n * @param makeErrorMessage 错误消息回调函数\n * @param getErrorMsg 获取错误消息函数,默认为 getDefaultErrorMsg\n * @returns 响应拦截器配置对象\n */\nexport const errorMessageResponseInterceptor = (\n makeErrorMessage?: MakeErrorMessageFn,\n getErrorMsg: (error: unknown) => string = getDefaultErrorMsg,\n): ResponseInterceptorConfig => {\n return {\n rejected: (error: unknown) => {\n // 取消请求不处理\n if (axios.isCancel(error)) {\n return Promise.reject(error);\n }\n\n // 已由认证拦截器处理的错误,不弹窗\n if (error && typeof error === 'object' && '__handledByAuthInterceptor' in error) {\n return Promise.reject(error);\n }\n\n // 统一获取错误信息并弹窗\n const msg = getErrorMsg(error);\n makeErrorMessage?.(msg, error);\n\n return Promise.reject(error);\n },\n };\n};\n","/**\n * 深度合并对象\n * @param target - 目标对象\n * @param source - 源对象\n * @returns 合并后的新对象\n */\nexport function merge<T extends Record<string, any>, U extends Record<string, any>>(\n target: T,\n source: U\n): T & U {\n const result: any = {...target};\n\n for (const key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n const sourceValue = source[key];\n const targetValue = target[key as keyof T];\n\n // 如果两个值都是普通对象,则递归合并\n if (\n isPlainObject(targetValue) &&\n isPlainObject(sourceValue)\n ) {\n result[key] = merge(\n targetValue as Record<string, any>,\n sourceValue as Record<string, any>\n );\n }\n // 否则直接使用源值覆盖\n else {\n result[key] = sourceValue;\n }\n }\n }\n\n return result;\n}\n\n/**\n * 创建自定义合并函数\n * @param customizer - 自定义处理函数\n * @returns 合并函数\n */\nexport function createMerge(\n customizer?: (\n obj: Record<string, any>,\n key: string,\n value: any\n ) => boolean | void\n) {\n return function <T extends Record<string, any>, U extends Record<string, any>>(\n target: T,\n source: U\n ): T & U {\n const result: any = {...target};\n\n for (const key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n const sourceValue = source[key];\n const targetValue = target[key as keyof T];\n\n // 如果有自定义处理函数,先尝试调用\n if (customizer) {\n const handled = customizer(result, key, sourceValue);\n if (handled === true) {\n continue; // 如果自定义函数返回true,跳过默认处理\n }\n }\n\n // 如果两个值都是普通对象,则递归合并\n if (\n isPlainObject(targetValue) &&\n isPlainObject(sourceValue)\n ) {\n result[key] = createMerge(customizer)(\n targetValue as Record<string, any>,\n sourceValue as Record<string, any>\n );\n }\n // 否则直接使用源值覆盖\n else {\n result[key] = sourceValue;\n }\n }\n }\n\n return result;\n };\n}\n\n/**\n * 带数组覆盖功能的合并函数\n */\nexport const mergeWithArrayOverride = createMerge((obj, key, value) => {\n if (Array.isArray(obj[key]) && Array.isArray(value)) {\n obj[key] = value;\n return true; // 表示已处理,跳过默认逻辑\n }\n return false; // 表示未处理,继续默认逻辑\n});\n\n/**\n * 判断是否为普通对象\n * @param value - 要判断的值\n * @returns 是否为普通对象\n */\nfunction isPlainObject(value: any): value is Record<string, any> {\n return (\n value !== null &&\n typeof value === 'object' &&\n Object.prototype.toString.call(value) === '[object Object]' &&\n !Array.isArray(value)\n );\n}\n","/**\n * 检查传入的值是否为函数。\n *\n * @param {unknown} value 要检查的值。\n * @returns {boolean} 如果值是函数,返回 true,否则返回 false。\n */\nfunction isFunction(value: unknown): value is (...args: unknown[]) => unknown {\n return typeof value === 'function';\n}\n\n/**\n * 检查传入的值是否为对象(不包括 null 和数组)。\n *\n * @param {unknown} value 要检查的值。\n * @returns {boolean} 如果值是对象,返回 true,否则返回 false。\n */\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * 检查传入的值是否为字符串。\n *\n * @param {unknown} value 要检查的值。\n * @returns {boolean} 如果值是字符串,返回 true,否则返回 false。\n */\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\n/**\n * 检查传入的值是否为undefined。\n *\n * @param {unknown} value 要检查的值。\n * @returns {boolean} 如果值是undefined,返回true,否则返回false。\n */\nfunction isUndefined(value?: unknown): value is undefined {\n return value === undefined;\n}\n\n/**\n * 检查传入的值是否为boolean\n * @param value\n * @returns 如果值是布尔值,返回true,否则返回false。\n */\nfunction isBoolean(value: unknown): value is boolean {\n return typeof value === 'boolean';\n}\n\n/**\n * 检查传入的值是否为空。\n *\n * 以下情况将被认为是空:\n * - 值为null。\n * - 值为undefined。\n * - 值为一个空字符串。\n * - 值为一个长度为0的数组。\n * - 值为一个没有元素的Map或Set。\n * - 值为一个没有属性的对象。\n *\n * @param {T} value 要检查的值。\n * @returns {boolean} 如果值为空,返回true,否则返回false。\n */\nfunction isEmpty<T = unknown>(value?: T): value is T {\n if (value === null || value === undefined) {\n return true;\n }\n\n if (Array.isArray(value) || isString(value)) {\n return value.length === 0;\n }\n\n if (value instanceof Map || value instanceof Set) {\n return value.size === 0;\n }\n\n if (isObject(value)) {\n return Object.keys(value).length === 0;\n }\n\n return false;\n}\n\n/**\n * 检查传入的字符串是否为有效的HTTP或HTTPS URL。\n *\n * @param {string} url 要检查的字符串。\n * @return {boolean} 如果字符串是有效的HTTP或HTTPS URL,返回true,否则返回false。\n */\nfunction isHttpUrl(url?: string): boolean {\n if (!url) {\n return false;\n }\n // 使用正则表达式测试URL是否以http:// 或 https:// 开头\n const httpRegex = /^https?:\\/\\/.*$/;\n return httpRegex.test(url);\n}\n\n/**\n * 检查传入的值是否为window对象。\n *\n * @param {unknown} value 要检查的值。\n * @returns {boolean} 如果值是window对象,返回true,否则返回false。\n */\nfunction isWindow(value: unknown): value is Window {\n if (typeof window === 'undefined') {\n return false;\n }\n\n return (\n value !== null &&\n typeof value === 'object' &&\n 'constructor' in value &&\n value.constructor.name === 'Window' &&\n 'self' in value &&\n value === window\n );\n}\n\n/**\n * 检查当前运行环境是否为Mac OS。\n *\n * 这个函数通过检查navigator.userAgent字符串来判断当前运行环境。\n * 如果userAgent字符串中包含\"macintosh\"或\"mac os x\"(不区分大小写),则认为当前环境是Mac OS。\n *\n * @returns {boolean} 如果当前环境是Mac OS,返回true,否则返回false。\n */\nfunction isMacOs(): boolean {\n const macRegex = /macintosh|mac os x/i;\n return macRegex.test(navigator.userAgent);\n}\n\n/**\n * 检查当前运行环境是否为Windows OS。\n *\n * 这个函数通过检查navigator.userAgent字符串来判断当前运行环境。\n * 如果userAgent字符串中包含\"windows\"或\"win32\"(不区分大小写),则认为当前环境是Windows OS。\n *\n * @returns {boolean} 如果当前环境是Windows OS,返回true,否则返回false。\n */\nfunction isWindowsOs(): boolean {\n const windowsRegex = /windows|win32/i;\n return windowsRegex.test(navigator.userAgent);\n}\n\n/**\n * 检查传入的值是否为数字\n * @param value\n */\nfunction isNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value);\n}\n\n/**\n * Returns the first value in the provided list that is neither `null` nor `undefined`.\n *\n * This function iterates over the input values and returns the first one that is\n * not strictly equal to `null` or `undefined`. If all values are either `null` or\n * `undefined`, it returns `undefined`.\n *\n * @template T - The type of the input values.\n * @param {...(T | null | undefined)[]} values - A list of values to evaluate.\n * @returns {T | undefined} - The first value that is not `null` or `undefined`, or `undefined` if none are found.\n *\n * @example\n * // Returns 42 because it is the first non-null, non-undefined value.\n * getFirstNonNullOrUndefined(undefined, null, 42, 'hello'); // 42\n *\n * @example\n * // Returns 'hello' because it is the first non-null, non-undefined value.\n * getFirstNonNullOrUndefined(null, undefined, 'hello', 123); // 'hello'\n *\n * @example\n * // Returns undefined because all values are either null or undefined.\n * getFirstNonNullOrUndefined(undefined, null); // undefined\n */\nfunction getFirstNonNullOrUndefined<T>(\n ...values: (null | T | undefined)[]\n): T | undefined {\n for (const value of values) {\n if (value !== undefined && value !== null) {\n return value;\n }\n }\n return undefined;\n}\n\nexport {\n getFirstNonNullOrUndefined,\n isBoolean,\n isEmpty,\n isFunction,\n isHttpUrl,\n isMacOs,\n isNumber,\n isObject,\n isString,\n isUndefined,\n isWindow,\n isWindowsOs,\n};\n","/**\n * Sums the passed percentage to the R, G or B of a HEX color\n * @param {string} color The color to change\n * @param {number} amount The amount to change the color by\n * @returns {string} The processed part of the color\n */\nexport function addLight(color: string, amount: number): string {\n const cc = parseInt(color, 16) + amount\n const c = cc > 255 ? 255 : cc\n return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`\n}\n\n/**\n * Lightens a 6 char HEX color according to the passed percentage\n * @param {string} color The color to change\n * @param {number} amount The amount to change the color by\n * @returns {string} The processed color represented as HEX\n */\nexport function lighten(color: string, amount: number): string {\n color = color.indexOf('#') >= 0 ? color.substring(1, color.length) : color\n amount = Math.trunc((255 * amount) / 100)\n return `#${addLight(color.substring(0, 2), amount)}${addLight(\n color.substring(2, 4),\n amount\n )}${addLight(color.substring(4, 6), amount)}`\n}\n\n/**\n * 绑定实例方法\n * @param instance 实例\n */\nexport function bindMethods<T extends object>(instance: T): void {\n const prototype = Object.getPrototypeOf(instance);\n const propertyNames = Object.getOwnPropertyNames(prototype);\n\n propertyNames.forEach((propertyName) => {\n const descriptor = Object.getOwnPropertyDescriptor(prototype, propertyName);\n const propertyValue = instance[propertyName as keyof T];\n\n if (\n typeof propertyValue === 'function' &&\n propertyName !== 'constructor' &&\n descriptor &&\n !descriptor.get &&\n !descriptor.set\n ) {\n instance[propertyName as keyof T] = propertyValue.bind(instance);\n }\n });\n}\n\n/**\n * 滚动到顶部\n */\nexport function scrollToTop() {\n window.scrollTo({top: 0, behavior: 'smooth'})\n}\n\n/**\n * 滚动到底部\n */\nexport function scrollToBottom() {\n window.scrollTo({top: document.body.scrollHeight, behavior: 'smooth'})\n}\n\n/**\n * 滚动到指定元素\n * @param element 目标元素\n */\nexport function scrollTo(element: HTMLElement) {\n element.scrollIntoView({behavior: 'smooth'})\n}\n\n/**\n * 金额格式化3000->3,000\n * @param amount - 金额\n */\nexport function amountFormatter(amount: number) {\n return `${amount}`.replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',');\n}\n\n","import type NProgress from \"nprogress\";\n\n// 创建一个NProgress实例的变量,初始值为null\nlet nProgressInstance: null | typeof NProgress = null;\n\n/**\n * 动态加载NProgress库,并进行配置。\n * 此函数首先检查是否已经加载过NProgress库,如果已经加载过,则直接返回NProgress实例。\n * 否则,动态导入NProgress库,进行配置,然后返回NProgress实例。\n *\n * @returns NProgress实例的Promise对象。\n */\nasync function loadNprogress() {\n if (nProgressInstance) {\n return nProgressInstance;\n }\n nProgressInstance = await import(\"nprogress\");\n nProgressInstance.configure({\n showSpinner: true,\n speed: 300,\n });\n return nProgressInstance;\n}\n\n/**\n * 开始显示进度条。\n * 此函数首先加载NProgress库,然后调用NProgress的start方法开始显示进度条。\n */\nasync function startProgress() {\n const nprogress = await loadNprogress();\n nprogress?.start();\n}\n\n/**\n * 停止显示进度条,并隐藏进度条。\n * 此函数首先加载NProgress库,然后调用NProgress的done方法停止并隐藏进度条。\n */\nasync function stopProgress() {\n const nprogress = await loadNprogress();\n nprogress?.done();\n}\n\nexport { startProgress, stopProgress };\n","export function formatDate(dateString: string | undefined) {\n if (!dateString) return ''\n return new Date(dateString).toLocaleString()\n}\n\n// 格式化日期时间\nexport const formatDateTime = (timestamp?: any) => {\n if (!timestamp) return '-';\n try {\n let date: Date;\n if (timestamp.seconds) {\n date = new Date(timestamp.seconds * 1000);\n } else if (typeof timestamp === 'string') {\n date = new Date(timestamp);\n } else {\n return '-';\n }\n return date.toLocaleString('zh-CN', {\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n });\n } catch (error) {\n return '-';\n }\n};\n\n// 日期格式化\nexport const DATE_FORMAT = 'YYYY-MM-DD';\nexport const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';\nexport const TIME_PICKER_FORMAT = 'HH:mm:ss';\n","/**\n * 生成基于字符串的固定随机色(HSL模式,保证饱和度和明度适中)\n *\n * 浅底色(l=85):只能配深色墨(最差色相对深色墨仍 ≥11:1),配白字不可读。\n * @param str\n */\nexport const getRandomColor = (str: string) => {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n hash = str.charCodeAt(i) + ((hash << 5) - hash);\n }\n const hue = Math.abs(hash % 360);\n return `hsl(${hue}, 50%, 85%)`;\n};\n\n/**\n * 根据首字母生成固定随机色(头像实底色,前景固定近白)\n *\n * 明度取 28% 是量出来的:本函数的产物一律配 antd/EP 头像的白字,而 hsl 同饱和度下\n * 45% 明度是\"两种墨都不合格\"的死区(360 个色相里最差 white 2.03 / dark 2.01);\n * 28% + s60 保证任意色相下白字 ≥4.86:1(判据与换算见 docs/design-language.md §2.1)。\n * @param char\n */\nexport const getCharColor = (char: string) => {\n let hash = 0;\n for (let i = 0; i < char.length; i++) {\n hash = char.charCodeAt(i) + ((hash << 5) - hash);\n }\n const hue = Math.abs(hash % 360);\n const saturation = 60;\n const lightness = 28;\n return `hsl(${hue}, ${saturation}%, ${lightness}%)`;\n};\n\n// 辅助函数:将十六进制颜色转换为 RGB\nexport function hexToRgb(hex: string): [number, number, number] {\n const bigint = parseInt(hex.slice(1), 16);\n return [(bigint >> 16) & 255, (bigint >> 8) & 255, bigint & 255];\n}\n\n// 辅助函数:将 RGB 转换为十六进制颜色\nexport function rgbToHex(r: number, g: number, b: number): string {\n return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;\n}\n\n/** 解析 `#rgb`/`#rrggbb`/`hsl(h s% l%)`/`hsl(h, s%, l%)` 为 [r,g,b](0-255);无法识别返回 null */\nexport function parseColor(c: string): [number, number, number] | null {\n const s = c.trim();\n if (s.startsWith('#')) {\n const hex = s.slice(1);\n const full = hex.length === 3 ? hex.split('').map((x) => x + x).join('') : hex.slice(0, 6);\n if (!/^[0-9a-fA-F]{6}$/.test(full)) return null;\n return hexToRgb(`#${full}`);\n }\n const hsl = s.match(/^hsla?\\(([^)]+)\\)$/i);\n if (hsl) {\n const parts = hsl[1].split(/[\\s,/]+/).filter(Boolean);\n if (parts.length < 3) return null;\n const h = parseFloat(parts[0]) / 360;\n const sl = parseFloat(parts[1]) / 100;\n const l = parseFloat(parts[2]) / 100;\n if ([h, sl, l].some((n) => Number.isNaN(n))) return null;\n const f = (n: number) => {\n const k = (n + h * 12) % 12;\n return l - sl * Math.min(l, 1 - l) * Math.max(-1, Math.min(k - 3, 9 - k, 1));\n };\n return [f(0) * 255, f(8) * 255, f(4) * 255].map((v) => Math.round(v)) as [number, number, number];\n }\n return null;\n}\n\n/**\n * 与 CSS `color-mix(in srgb, base pct%, other)` 同口径的 JS 版本。\n * 存在的理由:antd 的 token 值(colorLink、Menu.itemSelectedColor 等)会被 antd 再做\n * 一次色彩运算,不能填 `var(--gowind-*)`,所以「语义色作文字」的降档/升档要在 TS 里算一遍;\n * 比例与 vue-element 端 `styles/vendors/_element-plus.scss` §3 完全一致,改动须同步两处。\n */\nexport function mixColor(base: string, other: string, pct: number): string {\n const b = parseColor(base);\n const o = parseColor(other);\n if (!b || !o) return base;\n const p = Math.max(0, Math.min(1, pct));\n return rgbToHex(...(b.map((v, i) => Math.round(p * v + (1 - p) * o[i])) as [number, number, number]));\n}\n","/**\n * 获取url中的全部参数\n * @param search - url参数\n */\n\nexport function getUrlAllParam(search: string): Record<string, string> {\n if (!search) return {};\n // 去除首个字符串问号\n if (search?.[0] === '?') search = search.substring(1, search.length);\n\n const arr = search.split('&'); // 分割数组\n const result: Record<string, string> = {};\n\n for (let i = 0; i < arr.length; i++) {\n const value = arr[i]?.split('=');\n if (value?.length === 2) {\n result[value[0]] = value[1];\n }\n }\n\n return result;\n}\n\n/**\n * 获取url中参数某个值\n * @param search - url参数\n * @param key - 搜索值\n */\nexport function getUrlParam(search: string, key: string) {\n if (!search || !key) return '';\n // 去除首个字符串问号\n if (search?.[0] === '?') search = search.substring(1, search.length);\n const urlParams = getUrlAllParam(search);\n return urlParams?.[key] || '';\n}\n","// type Diff<T = any> = T;\n\n// 比较两个数组是否相等\n\nfunction arraysEqual<T>(a: T[], b: T[]): boolean {\n if (a.length !== b.length) return false;\n const counter = new Map<T, number>();\n for (const value of a) {\n counter.set(value, (counter.get(value) || 0) + 1);\n }\n for (const value of b) {\n const count = counter.get(value);\n if (count === undefined || count === 0) {\n return false;\n }\n counter.set(value, count - 1);\n }\n return true;\n}\n\n// 深度对比两个值\n// function deepEqual<T>(oldVal: T, newVal: T): boolean {\n// if (\n// typeof oldVal === 'object' &&\n// oldVal !== null &&\n// typeof newVal === 'object' &&\n// newVal !== null\n// ) {\n// return Array.isArray(oldVal) && Array.isArray(newVal)\n// ? arraysEqual(oldVal, newVal)\n// : diff(oldVal as any, newVal as any) === null;\n// } else {\n// return oldVal === newVal;\n// }\n// }\n\n// // diff 函数\n// function diff<T extends object>(\n// oldObj: T,\n// newObj: T,\n// ignoreFields: (keyof T)[] = [],\n// ): { [K in keyof T]?: Diff<T[K]> } | null {\n// const difference: { [K in keyof T]?: Diff<T[K]> } = {};\n\n// for (const key in oldObj) {\n// if (ignoreFields.includes(key)) continue;\n// const oldValue = oldObj[key];\n// const newValue = newObj[key];\n\n// if (!deepEqual(oldValue, newValue)) {\n// difference[key] = newValue;\n// }\n// }\n\n// return Object.keys(difference).length === 0 ? null : difference;\n// }\n\ntype DiffResult<T> = Partial<{\n [K in keyof T]: T[K] extends object ? DiffResult<T[K]> : T[K];\n}>;\n\nfunction diff<T extends Record<string, any>>(obj1: T, obj2: T): DiffResult<T> {\n function findDifferences(o1: any, o2: any): any {\n if (Array.isArray(o1) && Array.isArray(o2)) {\n if (!arraysEqual(o1, o2)) {\n return o2;\n }\n return undefined;\n }\n\n if (typeof o1 === \"object\" && typeof o2 === \"object\" && o1 !== null && o2 !== null) {\n const diffResult: any = {};\n\n const keys = new Set([...Object.keys(o1), ...Object.keys(o2)]);\n keys.forEach((key) => {\n const valueDiff = findDifferences(o1[key], o2[key]);\n if (valueDiff !== undefined) {\n diffResult[key] = valueDiff;\n }\n });\n\n return Object.keys(diffResult).length > 0 ? diffResult : undefined;\n }\n\n return o1 === o2 ? undefined : o2;\n }\n\n return findDifferences(obj1, obj2);\n}\n\nexport { arraysEqual, diff };\n","interface TreeConfigOptions {\n // 子属性的名称,默认为'children'\n childProps: string;\n}\n\n/**\n * @zh_CN 遍历树形结构,并返回所有节点中指定的值。\n * @param tree 树形结构数组\n * @param getValue 获取节点值的函数\n * @param options 作为子节点数组的可选属性名称。\n * @returns 所有节点中指定的值的数组\n */\nfunction traverseTreeValues<T, V>(\n tree: T[],\n getValue: (node: T) => V,\n options?: TreeConfigOptions\n): V[] {\n const result: V[] = [];\n const {childProps} = options || {\n childProps: \"children\",\n };\n\n const dfs = (treeNode: T) => {\n const value = getValue(treeNode);\n result.push(value);\n const children = (treeNode as Record<string, any>)?.[childProps];\n if (!children) {\n return;\n }\n if (children.length > 0) {\n for (const child of children) {\n dfs(child);\n }\n }\n };\n\n for (const treeNode of tree) {\n dfs(treeNode);\n }\n return result.filter(Boolean);\n}\n\n/**\n * 根据条件过滤给定树结构的节点,并以原有顺序返回所有匹配节点的数组。\n * @param tree 要过滤的树结构的根节点数组。\n * @param filter 用于匹配每个节点的条件。\n * @param options 作为子节点数组的可选属性名称。\n * @returns 包含所有匹配节点的数组。\n */\nfunction filterTree<T extends Record<string, any>>(\n tree: T[],\n filter: (node: T) => boolean,\n options?: TreeConfigOptions\n): T[] {\n const {childProps} = options || {\n childProps: \"children\",\n };\n\n const _filterTree = (nodes: T[]): T[] => {\n return nodes.filter((node: Record<string, any>) => {\n if (filter(node as T)) {\n if (node[childProps]) {\n node[childProps] = _filterTree(node[childProps]);\n }\n return true;\n }\n return false;\n });\n };\n\n return _filterTree(tree);\n}\n\n/**\n * 根据条件重新映射给定树结构的节\n * @param tree 要过滤的树结构的根节点数组。\n * @param mapper 用于map每个节点的条件。\n * @param options 作为子节点数组的可选属性名称。\n */\nfunction mapTree<T, V extends Record<string, any>>(\n tree: T[],\n mapper: (node: T) => V,\n options?: TreeConfigOptions\n): V[] {\n const {childProps} = options || {\n childProps: \"children\",\n };\n return tree.map((node) => {\n const mapperNode: Record<string, any> = mapper(node);\n if (mapperNode[childProps]) {\n mapperNode[childProps] = mapTree(mapperNode[childProps], mapper, options);\n }\n return mapperNode as V;\n });\n}\n\nexport {filterTree, mapTree, traverseTreeValues};\n","import type { AxiosRequestConfig } from 'axios';\n\nimport type { RequestClient } from '../request-client';\nimport type { RequestResponse } from '../types';\n\nclass FileDownloader {\n private client: RequestClient;\n\n constructor(client: RequestClient) {\n this.client = client;\n }\n\n public async download(url: string, config?: AxiosRequestConfig): Promise<RequestResponse<Blob>> {\n const finalConfig: AxiosRequestConfig = {\n ...config,\n responseType: 'blob',\n };\n\n return await this.client.get<RequestResponse<Blob>>(url, finalConfig);\n }\n}\n\nexport { FileDownloader };\n","import type { AxiosInstance, AxiosResponse } from 'axios';\n\nimport type { RequestInterceptorConfig, ResponseInterceptorConfig } from '../types';\n\nconst defaultRequestInterceptorConfig: RequestInterceptorConfig = {\n fulfilled: (response) => response,\n rejected: (error) => Promise.reject(error),\n};\n\nconst defaultResponseInterceptorConfig: ResponseInterceptorConfig = {\n fulfilled: (response: AxiosResponse) => response,\n rejected: (error) => Promise.reject(error),\n};\n\nclass InterceptorManager {\n private axiosInstance: AxiosInstance;\n\n constructor(instance: AxiosInstance) {\n this.axiosInstance = instance;\n }\n\n addRequestInterceptor({\n fulfilled,\n rejected,\n }: RequestInterceptorConfig = defaultRequestInterceptorConfig) {\n this.axiosInstance.interceptors.request.use(fulfilled, rejected);\n }\n\n addResponseInterceptor<T = any>({\n fulfilled,\n rejected,\n }: ResponseInterceptorConfig<T> = defaultResponseInterceptorConfig) {\n this.axiosInstance.interceptors.response.use(fulfilled, rejected);\n }\n}\n\nexport { InterceptorManager };\n","import type { AxiosRequestConfig, AxiosResponse } from 'axios';\n\nimport type { RequestClient } from '../request-client';\n\nclass FileUploader {\n private client: RequestClient;\n\n constructor(client: RequestClient) {\n this.client = client;\n }\n\n public async upload(\n url: string,\n data: { file: Blob | File } & Record<string, any>,\n config?: AxiosRequestConfig,\n ): Promise<AxiosResponse> {\n const formData = new FormData();\n\n Object.entries(data).forEach(([key, value]) => {\n formData.append(key, value);\n });\n\n // 注意:不要手动设置 Content-Type 为 'multipart/form-data'。\n // 浏览器在发送 FormData 时会自动生成带 boundary 的\n // 'multipart/form-data; boundary=----WebKitFormBoundaryXXX',\n // 手动写死会导致 boundary 丢失,后端无法解析请求体。\n // 这里显式删除调用方可能误传的 Content-Type,确保由浏览器自动设置。\n const headers: Record<string, any> = { ...config?.headers };\n delete headers['Content-Type'];\n delete headers['content-type'];\n const finalConfig: AxiosRequestConfig = { ...config, headers };\n\n // 使用 request 方法代替 post 方法,避免类型推断问题\n return this.client.request(url, {\n method: 'POST',\n data: formData,\n ...finalConfig,\n });\n }\n}\n\nexport { FileUploader };\n","import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, CreateAxiosDefaults } from 'axios';\n\nimport axios from 'axios';\n\nimport { merge, bindMethods } from '../../utils';\n\nimport { FileDownloader } from './modules/downloader';\nimport { InterceptorManager } from './modules/interceptor';\nimport { FileUploader } from './modules/uploader';\nimport {\n authenticateResponseInterceptor,\n errorMessageResponseInterceptor,\n} from './preset-interceptors';\nimport { defaultIdGenerator, getDefaultErrorMsg } from './utils';\nimport type { RequestClientCallbacks, RequestClientOptions, RequestContentType } from './types';\n\nclass RequestClient {\n private readonly instance: AxiosInstance;\n\n /**\n * 错误消息提取函数(由应用层注入,接入 i18n;未注入时回退到 getDefaultErrorMsg)。\n * 仅基于 reason 翻译,不返回后端 message。\n */\n private readonly getErrorMsg: (error: unknown) => string;\n\n public addRequestInterceptor: InterceptorManager['addRequestInterceptor'];\n public addResponseInterceptor: InterceptorManager['addResponseInterceptor'];\n\n public download: FileDownloader['download'];\n // 是否正在刷新token\n public isRefreshing = false;\n // 刷新token队列\n public refreshTokenQueue: ((token: string) => void)[] = [];\n public upload: FileUploader['upload'];\n\n // ==========================\n // 静态单例管理\n // ==========================\n private static _instance: RequestClient | null = null;\n\n /**\n * 初始化全局单例(在 bootstrap 时调用一次)\n */\n static init(baseURL: string, callbacks: RequestClientCallbacks) {\n RequestClient._instance = new RequestClient({ baseURL }, callbacks);\n }\n\n /**\n * 获取全局单例\n */\n static getInstance(): RequestClient {\n if (!RequestClient._instance) {\n throw new Error('RequestClient not initialized. Call RequestClient.init() during bootstrap.');\n }\n return RequestClient._instance;\n }\n\n /**\n * 构造函数,创建 Axios 实例并注册拦截器\n * @param options - Axios 请求配置\n * @param callbacks - 业务回调接口(token、认证、错误处理),由应用层注入\n */\n constructor(options: RequestClientOptions = {}, callbacks?: RequestClientCallbacks) {\n // 错误消息提取函数:优先使用应用层注入的 i18n 实现,否则回退到默认实现。\n this.getErrorMsg = callbacks?.getErrorMsg ?? getDefaultErrorMsg;\n\n // 合并默认配置和传入的配置\n const defaultConfig: CreateAxiosDefaults<RequestContentType> = {\n headers: {\n 'Content-Type': 'application/json;charset=utf-8' as RequestContentType,\n },\n timeout: 10_000,\n withCredentials: true,\n };\n const { ...axiosConfig } = options;\n const requestConfig = merge(axiosConfig, defaultConfig);\n this.instance = axios.create(requestConfig);\n\n bindMethods(this);\n\n // 实例化拦截器管理器\n const interceptorManager = new InterceptorManager(this.instance);\n this.addRequestInterceptor = interceptorManager.addRequestInterceptor.bind(interceptorManager);\n this.addResponseInterceptor =\n interceptorManager.addResponseInterceptor.bind(interceptorManager);\n\n // 实例化文件上传器\n const fileUploader = new FileUploader(this);\n this.upload = fileUploader.upload.bind(fileUploader);\n // 实例化文件下载器\n const fileDownloader = new FileDownloader(this);\n this.download = fileDownloader.download.bind(fileDownloader);\n\n // ==========================\n // 注册内置拦截器\n // ==========================\n if (callbacks) {\n this.setupInterceptors(callbacks);\n }\n }\n\n /**\n * 格式化令牌\n */\n private formatToken(token: null | string) {\n return token ? `Bearer ${token}` : null;\n }\n\n /**\n * 注册所有内置拦截器\n */\n private setupInterceptors(callbacks: RequestClientCallbacks) {\n this.useTokenInterceptor(callbacks);\n this.useRequestIdInterceptor();\n this.useLocaleInterceptor(callbacks);\n // responseData 必须在 auth 之前(vben/ele 同序)。401 走 rejected 链,\n // auth 拿到的仍是完整 AxiosError;反过来 auth 在前时,刷新成功后的重试\n // 结果(已解包的响应体)会再流经 responseData.fulfilled 被二次解包:\n // status 为 undefined 落入 throw 分支,最终 `errAny.response.data` 把\n // 字面量 undefined 当错误抛给页面,UI 在恢复成功时必现失败态。\n this.useResponseDataInterceptor();\n this.useAuthInterceptor(callbacks);\n this.useErrorMessageInterceptor(callbacks);\n }\n\n /**\n * 请求拦截器:注入 Authorization Token\n */\n private useTokenInterceptor(callbacks: RequestClientCallbacks) {\n this.addRequestInterceptor({\n fulfilled: (config) => {\n if (callbacks.getToken) {\n const token = callbacks.getToken();\n config.headers.Authorization = this.formatToken(token);\n }\n return config as never;\n },\n });\n }\n\n /**\n * 请求拦截器:注入 X-Request-ID 和 XMLHttpRequest 标识\n */\n private useRequestIdInterceptor() {\n this.addRequestInterceptor({\n fulfilled: (config) => {\n const requestId = config.headers['X-Request-ID'] || defaultIdGenerator();\n (config as any)._requestId = requestId;\n config.headers['X-Request-ID'] = requestId;\n config.headers['X-Requested-With'] = 'XMLHttpRequest';\n return config as never;\n },\n });\n }\n\n /**\n * 请求拦截器:注入 Accept-Language\n */\n private useLocaleInterceptor(callbacks: RequestClientCallbacks) {\n this.addRequestInterceptor({\n fulfilled: (config) => {\n if (callbacks.getLocale) {\n config.headers['Accept-Language'] = callbacks.getLocale();\n }\n return config as never;\n },\n });\n }\n\n /**\n * 响应拦截器:解构响应数据(只返回 data 部分)\n */\n private useResponseDataInterceptor() {\n this.addResponseInterceptor({\n fulfilled: (response) => {\n const { data: responseData, status } = response;\n\n if (status >= 200 && status < 400) {\n return responseData;\n }\n\n throw Object.assign({}, responseData, { response });\n },\n });\n }\n\n /**\n * 401 认证拦截器:自动刷新 Token 或跳转登录页\n */\n private useAuthInterceptor(callbacks: RequestClientCallbacks) {\n this.addResponseInterceptor(\n authenticateResponseInterceptor({\n client: this,\n doReAuthenticate: async () => {\n console.warn('Token expired, redirecting to login...');\n if (callbacks.onReAuthenticate) {\n await callbacks.onReAuthenticate(true);\n } else {\n console.error(\n 'onReAuthenticate callback not set. Call RequestClient.init() during bootstrap.',\n );\n }\n },\n doRefreshToken: async () => {\n if (callbacks.refreshToken) {\n return await callbacks.refreshToken();\n }\n return '';\n },\n enableRefreshToken: true,\n formatToken: this.formatToken.bind(this),\n }),\n );\n }\n\n /**\n * 统一错误消息拦截器:提取错误文本并回调\n */\n private useErrorMessageInterceptor(callbacks: RequestClientCallbacks) {\n this.addResponseInterceptor(\n errorMessageResponseInterceptor((msg: string) => {\n callbacks.onError?.(msg);\n }, this.getErrorMsg),\n );\n }\n\n /**\n * DELETE请求方法\n */\n public delete<T = never>(url: string, config?: AxiosRequestConfig): Promise<T> {\n return this.request<T>(url, { ...config, method: 'DELETE' });\n }\n\n /**\n * GET请求方法\n */\n public get<T = never>(url: string, config?: AxiosRequestConfig): Promise<T> {\n return this.request<T>(url, { ...config, method: 'GET' });\n }\n\n /**\n * POST请求方法\n */\n public post<T = never>(url: string, data?: never, config?: AxiosRequestConfig): Promise<T> {\n return this.request<T>(url, { ...config, data, method: 'POST' });\n }\n\n /**\n * PUT请求方法\n */\n public put<T = never>(url: string, data?: never, config?: AxiosRequestConfig): Promise<T> {\n return this.request<T>(url, { ...config, data, method: 'PUT' });\n }\n\n /**\n * 通用的请求方法\n */\n public async request<T>(url: string, config: AxiosRequestConfig): Promise<T> {\n try {\n const response: AxiosResponse<T> = await this.instance({\n url,\n ...config,\n });\n return response as T;\n } catch (error: unknown) {\n // 已由认证拦截器处理的错误(如 token 过期跳转登录页)必须原样抛出,\n // 保留 __handledByAuthInterceptor 标记,避免被下方逻辑误判。\n if (\n error &&\n typeof error === 'object' &&\n '__handledByAuthInterceptor' in error\n ) {\n throw error;\n }\n\n // 错误提示统一基于 reason 经 i18n 翻译,不再依赖后端 message 字段。\n // 用翻译后的文案覆盖抛出对象的 message,使所有读取 .message 的调用方\n // 都得到本地化文本,同时保留其余字段(如 code/reason)供业务逻辑判断。\n const msg = this.getErrorMsg(error);\n // error 为 unknown;用 any 访问 response.data 以兼容拦截器解包后的结构\n const errAny = error as any;\n const thrown: unknown =\n errAny && typeof errAny === 'object' && errAny.response\n ? (errAny.response.data as unknown)\n : error;\n try {\n if (thrown && typeof thrown === 'object') {\n (thrown as { message?: string }).message = msg;\n }\n } catch {\n // 忽略只读属性等赋值异常\n }\n throw thrown;\n }\n }\n}\n\nexport { RequestClient };\n","import { RequestClient } from './request-client';\n\nexport function requestApi({\n path,\n method,\n body,\n headers,\n}: {\n path: string;\n method: string;\n body: null | string;\n headers?: Record<string, string>;\n}) {\n return RequestClient.getInstance().request(path, {\n method,\n data: body,\n headers,\n } as never);\n}\n","// 全局通用分页查询类\nexport class PaginationQuery {\n paging?: { page?: number; pageSize?: number };\n formValues?: Record<string, unknown> | null;\n fieldMask?: string | string[] | null;\n orderBy?: string[] | null;\n\n isTenantUser: boolean = false;\n\n constructor(data?: Partial<PaginationQuery>) {\n if (data) {\n this.paging = data.paging;\n this.formValues = data.formValues;\n this.fieldMask = data.fieldMask;\n this.orderBy = data.orderBy;\n this.isTenantUser = data?.isTenantUser ?? false;\n }\n }\n\n /**\n * 移除对象中的 null 和 undefined 值\n * @param obj - 需要清理的对象\n * @returns 清理后的对象\n */\n private static removeNullUndefined<T extends Record<string, unknown>>(obj: T): Partial<T> {\n return Object.fromEntries(\n Object.entries(obj).filter(([_, v]) => v !== null && v !== undefined && v !== ''),\n ) as Partial<T>;\n }\n\n /**\n * key 末段是否已是 go-crud 支持的查询操作符(created_at__gte、type__not 等)。\n * 这类 key 不能再叠加 __contains:go-crud 会把 `a__gte__contains`\n * 解析成 `a CONTAINS value`,对时间/布尔列直接 SQL 报错(500),\n * \"排除\"等语义也可能被静默反转。\n *\n * 下表与 go-crud `filter/operator_converter.go` 的 operatorMap 别名集\n * 逐字对齐(含 i_* 前缀变体、数值比较长拼写/连字符拼写、is_not_null\n * 拼写族、json_contains/array_contains/exists/search/exact/iexact)。\n * 后端映射表变更时须同步此表;匹配前做小写归一(后端同)。此表缺项\n * 会让对应后缀键被追加 __contains 而语义反转或 500(2026-09-13 修复的缺口)。\n */\n private static hasOperatorSuffix(key: string): boolean {\n const idx = key.lastIndexOf('__');\n if (idx === -1) return false;\n return [\n 'eq', 'equal', 'equals',\n 'ne', 'neq', 'not', 'not_equal', 'not_equals', 'not-equal',\n 'gt', 'greater_than', 'greater-than',\n 'gte', 'greater_than_or_equal', 'greater_equals', 'greater_or_equal', 'greater-or-equal',\n 'lt', 'less_than', 'less-than',\n 'lte', 'less_than_or_equal', 'less_equals', 'less_or_equal', 'less-or-equal',\n 'like', 'ilike', 'i_like', 'not_like', 'notlike',\n 'in', 'nin', 'not_in', 'notin',\n 'is_null', 'isnull', 'is_not_null', 'isnot_null', 'isnotnull', 'not_isnull',\n 'between', 'range',\n 'regexp', 'regex', 'iregexp', 'i_regexp', 'iregex',\n 'contains', 'icontains', 'i_contains',\n 'starts_with', 'startswith', 'istarts_with', 'i_starts_with', 'istartswith',\n 'ends_with', 'endswith', 'iends_with', 'i_ends_with', 'iendswith',\n 'json_contains', 'array_contains', 'exists', 'search', 'exact', 'iexact', 'i_exact',\n ].includes(key.slice(idx + 2).toLowerCase());\n }\n\n /**\n * 创建列表查询 JSON 过滤字符串\n *\n * 后端 go-crud 的裸 `{\"field\": value}` 走 EQ 精确匹配;搜索框输入部分关键词\n * 必须使用 `field__contains` 才是模糊匹配。这里约定:字符串值统一转\n * `__contains`(contains 是完整值精确匹配的超集,对下拉枚举等完整值查询\n * 结果一致),非字符串(数字/布尔)保持 EQ 精确语义。\n * @param formValues - 查询表单值\n * @param needCleanTenant - 是否需要清理租户字段\n * @returns JSON 字符串或 undefined\n */\n private static makeQueryString(\n formValues?: null | Record<string, unknown>,\n needCleanTenant: boolean = false,\n ): string | undefined {\n if (formValues === null || formValues === undefined) {\n return undefined;\n }\n\n // 去除掉空值\n const cleaned = this.removeNullUndefined(formValues);\n\n if (cleaned === undefined) return undefined;\n\n // 若是数组,直接按数组处理\n if (Array.isArray(cleaned)) {\n return cleaned.length === 0 ? undefined : JSON.stringify(cleaned);\n }\n\n // 过滤掉空对象\n if (Object.keys(cleaned).length === 0) {\n return undefined;\n }\n\n // 字符串值转模糊匹配。ID 类字段(*_id/idXxx)即使值是字符串也保持 EQ:\n // 它们指向数字列且多为页面隐式固定参数,contains 会导致 SQL 报错或误匹配。\n // 已带操作符后缀的 key(field__gte / field__not 等)保持原样。\n const fuzzy = Object.fromEntries(\n Object.entries(cleaned).map(([key, value]) => [\n typeof value === 'string' &&\n !/(_id$|Id$|ID$|^id$)/.test(key) &&\n !this.hasOperatorSuffix(key)\n ? `${key}__contains`\n : key,\n value,\n ]),\n );\n\n if (needCleanTenant) {\n // 删除租户相关字段 tenant_id 和 tenantId\n const { tenant_id, tenantId, ...rest } = fuzzy as Record<string, unknown>;\n\n // 过滤掉空对象\n if (Object.keys(rest).length === 0) {\n return undefined;\n }\n\n return JSON.stringify(rest);\n }\n\n // 默认返回整个 fuzzy 对象的 JSON 字符串\n return JSON.stringify(fuzzy);\n }\n\n /**\n * 创建排序字符串\n * @param orderBy - 排序字段数组\n * @returns JSON 字符串或 undefined\n */\n private static makeOrderBy(orderBy?: null | string[]): string | undefined {\n if (orderBy === undefined) {\n orderBy = ['-created_at'];\n }\n if (orderBy === null) {\n orderBy = ['-created_at'];\n }\n return JSON.stringify(orderBy) ?? undefined;\n }\n\n // 是否不分页\n get noPaging(): boolean {\n return !this.paging?.page && !this.paging?.pageSize;\n }\n\n // 生成 orderBy 字符串\n get orderByString(): string | undefined {\n return PaginationQuery.makeOrderBy(this.orderBy);\n }\n\n // 生成 query 字符串\n get queryString(): string | undefined {\n return PaginationQuery.makeQueryString(this.formValues, this.isTenantUser);\n }\n\n // 自动格式化 fieldMask\n private get formattedFieldMask(): string | undefined {\n if (!this.fieldMask) return undefined;\n\n // 数组 → 逗号分隔\n if (Array.isArray(this.fieldMask)) {\n return this.fieldMask.filter(Boolean).join(',');\n }\n\n // 字符串直接返回\n return this.fieldMask.trim() || undefined;\n }\n\n // 直接生成后端需要的 pagination_PagingRequest\n toRawParams() {\n return {\n page: this.paging?.page,\n pageSize: this.paging?.pageSize,\n noPaging: this.noPaging,\n fieldMask: this.formattedFieldMask,\n orderBy: this.orderByString,\n query: this.queryString,\n sorting: undefined,\n offset: undefined,\n limit: undefined,\n token: undefined,\n filter: undefined,\n filterExpr: undefined,\n };\n }\n}\n\n/**\n * 分页查询结果类型\n */\nexport type PaginationResult<T> = {\n items: T[]; // 数据项\n total: number; // 总数\n};\n","import type { EventSourceMessage } from '@microsoft/fetch-event-source/lib/cjs/parse';\n\nimport type {\n SSEClientConfig,\n SSEConnectionStatus,\n SSEEventHandler,\n SSEEventName,\n SSETransport,\n} from './types';\n\nimport { fetchEventSource } from '@microsoft/fetch-event-source';\n\n/**\n * SSE客户端\n */\nexport class SSEClient {\n private abortController: AbortController | null = null;\n private config: Required<SSEClientConfig>;\n private eventSource: EventSource | null = null;\n // 存储事件监听器(键:事件名,值:回调数组)\n private handlers = new Map<SSEEventName, SSEEventHandler[]>();\n private status: SSEConnectionStatus = 'disconnected';\n private readonly transport: SSETransport;\n\n constructor(config: SSEClientConfig) {\n // 合并默认配置\n this.config = {\n withCredentials: false,\n reconnectDelay: 3000,\n autoParseJson: true,\n headers: {},\n transport: 'fetch-event-source',\n ...config,\n };\n this.transport = this.config.transport;\n }\n\n /**\n * 解析数据(自动处理 JSON 格式)\n * @param rawData\n * @private\n */\n private parseData(rawData: string): unknown {\n if (!this.config.autoParseJson) {\n return rawData;\n }\n try {\n return JSON.parse(rawData);\n } catch {\n // 非 JSON 格式数据直接返回原始字符串\n return rawData;\n }\n }\n\n /**\n * 触发事件监听器\n * @param eventName\n * @param data\n * @param event\n * @private\n */\n private triggerHandler<T = unknown>(\n eventName: SSEEventName,\n data: T,\n event: Event | EventSourceMessage,\n ): void {\n const handlers = this.handlers.get(eventName);\n if (handlers) {\n handlers.forEach((handler) => {\n try {\n handler(data, event as MessageEvent);\n } catch (error) {\n console.error(`SSE 事件 ${eventName} 处理失败:`, error);\n }\n });\n }\n }\n\n /**\n * 通过 EventSource 建立 SSE 连接\n * @param url 连接 URL\n * @private\n */\n _connectByEventSource(url: string): void {\n this.eventSource = new EventSource(url, {\n withCredentials: this.config.withCredentials,\n });\n\n // 监听连接成功事件\n this.eventSource.addEventListener('open', (event) => {\n this.status = 'connected';\n this.triggerHandler('open', undefined, event);\n });\n\n // 监听默认消息事件(服务器未指定 event 字段时触发)\n this.eventSource.addEventListener('message', (event: MessageEvent) => {\n const data = this.parseData(event.data);\n this.triggerHandler('message', data, event);\n });\n\n // 监听错误事件(连接断开、网络异常等)\n this.eventSource.addEventListener('error', (event: Event) => {\n this.status = 'error';\n this.triggerHandler('error', undefined, event as MessageEvent);\n\n // 连接关闭时尝试重连(排除手动关闭的情况)\n if (this.eventSource?.readyState === EventSource.CLOSED) {\n this.status = 'disconnected';\n setTimeout(() => this.connect(), this.config.reconnectDelay);\n }\n });\n }\n\n /**\n * 通过 fetchEventSource 建立 SSE 连接(支持更灵活的配置和控制)\n * @param url 连接 URL\n * @private\n */\n async _connectByFetchEventSource(url: string): Promise<void> {\n // 用于手动关闭\n this.abortController = new AbortController();\n\n await fetchEventSource(url, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n ...this.config.headers,\n },\n credentials: this.config.withCredentials ? 'include' : 'same-origin',\n signal: this.abortController.signal,\n openWhenHidden: true, // 后台页保持连接(可选)\n\n // 连接成功\n onopen: async (response) => {\n if (response.ok) {\n this.status = 'connected';\n this.triggerHandler('open', undefined, new Event('open'));\n } else {\n throw new Error(`SSE 连接失败 ${response.status}`);\n }\n },\n\n // 接收消息\n onmessage: (event: EventSourceMessage) => {\n const data = this.parseData(event.data);\n\n // 自定义事件名(event: xxx)\n if (event.event && event.event !== 'message') {\n this.triggerHandler(event.event as SSEEventName, data, event);\n }\n\n // 默认 message 事件\n this.triggerHandler('message', data, event);\n },\n\n // 错误 & 重连\n onerror: (err) => {\n this.status = 'error';\n this.triggerHandler('error', err, new Event('error'));\n\n // 手动关闭不重连:抛出异常会被 fetchEventSource 当作致命错误,\n // 触发 reject 终止重试(区别于返回数值表示「稍后重试」)。\n if (this.abortController?.signal.aborted) {\n throw err;\n }\n\n // fetchEventSource 的运行时契约:onerror 返回数值时,\n // 该数值会被用作下一次重试的 setTimeout 间隔(ms)。\n // 默认不返回时库使用 DefaultRetryInterval=1000ms,这里返回配置的\n // reconnectDelay,避免服务端持续断开时形成紧密重连风暴。\n console.log(`[SSE] ${this.config.reconnectDelay}ms 后重试...`);\n return this.config.reconnectDelay;\n },\n\n // 关闭\n onclose: () => {\n // 服务端主动关闭连接时 fetchEventSource 默认会 resolve 终止整个流程、不再重连。\n // 这里抛出异常使其落入 onerror 的处理路径(库会把 onclose 抛出的异常\n // 交给 catch 分支,进而调用 onerror),由 onerror 返回 reconnectDelay\n // 实现带退避的重连。\n this.status = 'disconnected';\n throw new Error('SSE 连接关闭,准备重连');\n },\n });\n }\n\n /**\n * 关闭 SSE 连接\n */\n close(): void {\n if (this.eventSource) {\n this.eventSource.close();\n this.eventSource = null;\n }\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n }\n this.status = 'disconnected';\n }\n\n /**\n * 建立 SSE 连接\n * @param url 可选连接 URL,默认为配置中的 URL\n */\n connect(url?: string): Error | null {\n if (this.status === 'connected' || this.status === 'connecting') {\n console.warn('SSE 连接已存在或正在建立中');\n return new Error('SSE 连接已存在或正在建立中');\n }\n\n const targetUrl = url === undefined || url === '' ? this.config.url : url;\n\n this.status = 'connecting';\n if (this.transport === 'event-source') {\n this._connectByEventSource(targetUrl);\n } else {\n // _connectByFetchEventSource 是 async,其内部的 throw(如 onopen 失败)\n // 会让 Promise reject。这里必须捕获,否则会变成 Unhandled Promise Rejection,\n // 且 this.status 会永久停在 'connecting',导致后续 connect() 被状态守卫挡住、\n // 再也连不上。失败时重置为 disconnected 以允许后续重连。\n this._connectByFetchEventSource(targetUrl).catch((err) => {\n this.status = 'disconnected';\n console.error('[SSE] connect failed:', err);\n });\n }\n\n return null;\n }\n\n /**\n * 获取当前连接状态\n */\n getStatus(): SSEConnectionStatus {\n return this.status;\n }\n\n /**\n * 移除事件监听器\n * @param eventName\n * @param handler\n */\n off<T = unknown>(eventName: SSEEventName, handler?: SSEEventHandler<T>): void {\n const handlers = this.handlers.get(eventName);\n if (!handlers) return;\n\n if (handler) {\n // 移除指定回调\n this.handlers.set(\n eventName,\n handlers.filter((h) => h !== handler),\n );\n } else {\n // 移除所有回调\n this.handlers.delete(eventName);\n }\n }\n\n /**\n * 注册事件监听器(支持泛型指定数据类型)\n * @param eventName\n * @param handler\n */\n on<T = unknown>(eventName: SSEEventName, handler: SSEEventHandler<T>): void {\n if (!this.handlers.has(eventName)) {\n this.handlers.set(eventName, []);\n }\n // 存储回调(类型断言确保类型安全)\n this.handlers.get(eventName)?.push(handler as SSEEventHandler);\n\n // 对自定义事件(非 open/error/message),需要额外注册到 EventSource\n if (!['error', 'message', 'open'].includes(eventName) && this.eventSource) {\n this.eventSource.addEventListener(eventName, (event) => {\n const data = this.parseData((event as MessageEvent).data);\n this.triggerHandler(eventName, data, event as MessageEvent);\n });\n }\n }\n\n /**\n * 关闭当前连接并使用新的 URL / headers 重新连接\n * 适用于 token 刷新后需要携带新凭证的场景\n * @param url 可选的新连接 URL,默认沿用配置中的 URL\n */\n reconnect(url?: string): void {\n this.close();\n this.connect(url);\n }\n\n /**\n * 动态设置/更新请求头(不会影响正在进行中的连接,下次 connect 时生效)\n * 注意:EventSource 模式不支持自定义请求头,此方法仅对 fetch-event-source 模式生效\n * @param headers 键值对,会浅合并到当前 headers 配置中\n */\n setHeaders(headers: Record<string, string>): void {\n this.config.headers = { ...this.config.headers, ...headers };\n }\n}\n","/**\n * SSE 事件名注册表(react 端副本)。\n *\n * 真相源是后端 `backend/pkg/sseevent/sseevent.go`:它把常量写进帧的 `event:` 行,\n * 这里按同名注册回调,中间没有编译期检查(`SSEEventName` 带 `| string`,等于不约束)。\n * 新增事件要两边同时改;已发布事件的取值不可改——改名等于对线上前端静默断流。\n */\nexport const SSE_EVENT = {\n /**\n * 站内信收件行推送。\n *\n * data 为 `InternalMessageRecipient` 的 protojson:camelCase 键(`messageId` /\n * `recipientUserId` / `createdAt`),`status` 是枚举名字符串(`RECEIVED` / `READ`),\n * 与 REST 收件箱接口返回的形状一致。\n */\n Notification: 'notification',\n\n /**\n * AI 对话流式片段推送。\n *\n * data 为 `ChatChunkEvent` 的 protojson:camelCase 键(`conversationId` /\n * `seq` / `delta`)。一次对话产生多个 chunk 帧,按 conversationId 归组、\n * seq 顺序累积渲染;POST /admin/v1/ai/chat/completions 的同步响应携带\n * 完整回复,以响应为准校正累积文本。\n */\n AIChatChunk: 'ai_chat_chunk',\n\n /**\n * AI 对话的工具调用可见化推送。\n *\n * data 为 `ChatToolEvent` 的 protojson:camelCase 键(`conversationId` /\n * `name` / `arguments` / `result`)。模型每发起一次本地工具调用并执行完成\n * 后推一帧;工具轮不进消息落库(落库的 assistant 消息只有最终答案),\n * 本事件是前端展示\"模型正在调工具\"的唯一信息源(尽力而为,丢帧可容忍)。\n */\n AIChatTool: 'ai_chat_tool',\n\n /**\n * AI Agent 试运行实时推送。\n *\n * data 为 `AgentRunEvent` 的 protojson:camelCase 键(`runId` / `appId` /\n * `seq` / `phase`(node_start|node_end|error|done) / `nodeId` / `summary` /\n * `status`)。一次试运行产生多帧;Run 同步响应携带完整 trace 与终态,\n * 以响应为准校正(尽力而为,丢帧可容忍)。streamId 与对话同为发起用户流。\n */\n AgentRun: 'ai_agent_run',\n} as const;\n","import { SSEClient } from './sse_client';\n\nexport * from './sse_client';\nexport * from './types';\nexport * from './event';\n\nexport const globalSSEClient = new SSEClient({\n url: '/api/sse',\n withCredentials: false,\n reconnectDelay: 5000,\n autoParseJson: true,\n});\n","import type { SupportedLocale } from '../../locales';\n\n// 配置常量(和你的 i18n 配置保持一致)\nconst STORAGE_KEY = 'app_locale';\nconst DEFAULT_LOCALE: SupportedLocale = 'zh-CN';\nconst SUPPORTED_LOCALES: readonly SupportedLocale[] = ['zh-CN', 'en-US'] as const;\n\n/**\n * 从浏览器环境自动检测首选语言\n * @returns 检测到的语言代码\n */\nexport const detectBrowserLocale = (): SupportedLocale => {\n // 1. 优先读取浏览器首选语言列表\n const browserLanguages = navigator.languages ?? [navigator.language];\n\n for (const lang of browserLanguages) {\n // 完全匹配(zh-CN → zh-CN)\n const exactMatch = SUPPORTED_LOCALES.find((supported) => supported === lang);\n if (exactMatch) return exactMatch;\n\n // 前缀匹配(zh → zh-CN,en → en-US)\n const prefixMatch = SUPPORTED_LOCALES.find((supported) =>\n supported.startsWith(lang.split('-')[0]),\n );\n if (prefixMatch) return prefixMatch;\n }\n\n // 2. 无匹配时返回默认语言\n return DEFAULT_LOCALE;\n};\n\n/**\n * 从本地存储读取用户上次选择的语言\n * @returns 存储的语言代码 | null\n */\nexport const getStoredLocale = (): SupportedLocale | null => {\n try {\n const stored = localStorage.getItem(STORAGE_KEY);\n if (stored && SUPPORTED_LOCALES.includes(stored as SupportedLocale)) {\n return stored as SupportedLocale;\n }\n return null;\n } catch (e) {\n // 隐私模式/存储禁用时静默失败\n console.warn('[i18n detector] 读取本地存储失败', e);\n return null;\n }\n};\n\n/**\n * 存储用户选择的语言到本地存储\n * @param locale 要存储的语言代码\n */\nexport const setStoredLocale = (locale: SupportedLocale): void => {\n try {\n localStorage.setItem(STORAGE_KEY, locale);\n } catch (e) {\n console.warn('[i18n detector] 写入本地存储失败', e);\n }\n};\n\n/**\n * 综合检测:优先级 存储语言 > 浏览器语言 > 默认语言\n * @returns 最终生效的语言代码\n */\nexport const getFinalLocale = (): SupportedLocale => {\n return getStoredLocale() ?? detectBrowserLocale();\n};\n\n/**\n * 校验语言是否为项目支持的语言\n * @param locale 待校验的语言代码\n */\nexport const isSupportedLocale = (locale: string): locale is SupportedLocale => {\n return SUPPORTED_LOCALES.includes(locale as SupportedLocale);\n};\n","import type { SupportedLocale } from '../../locales';\n\n/**\n * 日期格式化工具(Intl 本地化格式;与 utils/date 的 dayjs 版 formatDate 同名冲突,\n * 故此处命名为 formatLocaleDate)\n * @param date 日期对象/时间戳\n * @param locale 目标语言\n * @param options Intl.DateTimeFormat 配置\n */\nexport const formatLocaleDate = (\n date: Date | number | string,\n locale: SupportedLocale,\n options?: Intl.DateTimeFormatOptions,\n): string => {\n const targetDate = typeof date === 'string' || typeof date === 'number' ? new Date(date) : date;\n\n // 默认格式化配置\n const defaultOptions: Intl.DateTimeFormatOptions = {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n };\n\n return new Intl.DateTimeFormat(locale, { ...defaultOptions, ...options }).format(targetDate);\n};\n\n/**\n * 数字格式化工具(支持千分位、小数位)\n * @param num 数字\n * @param locale 目标语言\n * @param options Intl.NumberFormat 配置\n */\nexport const formatNumber = (\n num: number,\n locale: SupportedLocale,\n options?: Intl.NumberFormatOptions,\n): string => {\n const defaultOptions: Intl.NumberFormatOptions = {\n maximumFractionDigits: 2,\n };\n\n return new Intl.NumberFormat(locale, { ...defaultOptions, ...options }).format(num);\n};\n\n/**\n * 货币格式化工具\n * @param amount 金额\n * @param locale 目标语言\n * @param currency 货币代码(CNY/USD等)\n * @param options Intl.NumberFormat 配置\n */\nexport const formatCurrency = (\n amount: number,\n locale: SupportedLocale,\n currency: string = 'CNY',\n options?: Intl.NumberFormatOptions,\n): string => {\n const defaultOptions: Intl.NumberFormatOptions = {\n style: 'currency',\n currency,\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n };\n\n return new Intl.NumberFormat(locale, { ...defaultOptions, ...options }).format(amount);\n};\n\n/**\n * 百分比格式化工具\n * @param value 小数(0.85 → 85%)\n * @param locale 目标语言\n * @param options Intl.NumberFormat 配置\n */\nexport const formatPercent = (\n value: number,\n locale: SupportedLocale,\n options?: Intl.NumberFormatOptions,\n): string => {\n const defaultOptions: Intl.NumberFormatOptions = {\n style: 'percent',\n maximumFractionDigits: 2,\n };\n\n return new Intl.NumberFormat(locale, { ...defaultOptions, ...options }).format(value);\n};\n\n/**\n * 相对时间格式化(如“3分钟前”“昨天”)\n * @param date 目标日期\n * @param locale 目标语言\n * @param options Intl.RelativeTimeFormat 配置\n */\nexport const formatRelativeTime = (\n date: Date | number,\n locale: SupportedLocale,\n options?: Intl.RelativeTimeFormatOptions,\n): string => {\n const targetDate = typeof date === 'number' ? new Date(date) : date;\n const now = Date.now();\n const diffMs = targetDate.getTime() - now;\n\n // 时间单位换算\n const SECOND = 1000;\n const MINUTE = 60 * SECOND;\n const HOUR = 60 * MINUTE;\n const DAY = 24 * HOUR;\n\n let value: number;\n let unit: Intl.RelativeTimeFormatUnit;\n\n const absDiff = Math.abs(diffMs);\n if (absDiff < MINUTE) {\n value = Math.round(diffMs / SECOND);\n unit = 'second';\n } else if (absDiff < HOUR) {\n value = Math.round(diffMs / MINUTE);\n unit = 'minute';\n } else if (absDiff < DAY) {\n value = Math.round(diffMs / HOUR);\n unit = 'hour';\n } else {\n value = Math.round(diffMs / DAY);\n unit = 'day';\n }\n\n return new Intl.RelativeTimeFormat(locale, { numeric: 'auto', ...options }).format(value, unit);\n};\n","import i18n from 'i18next';\nimport type { SupportedLocale } from '../../locales';\nimport { setStoredLocale, isSupportedLocale } from './detector';\n\n/**\n * 同步语言到浏览器环境\n * 1. 更新 HTML lang 属性\n * 2. 更新 HTML dir 属性(RTL 语言支持)\n * 3. 持久化到本地存储\n * @param locale 目标语言\n */\nexport const syncLocaleToBrowser = (locale: SupportedLocale): void => {\n // 1. 更新 HTML lang 属性\n document.documentElement.lang = locale;\n\n // 2. 支持 RTL 语言(如阿拉伯语、希伯来语)\n const rtlLocales = ['ar-SA', 'he-IL'] as const;\n document.documentElement.dir = (rtlLocales as readonly string[]).includes(locale) ? 'rtl' : 'ltr';\n\n // 3. 持久化存储\n setStoredLocale(locale);\n};\n\n/**\n * 监听 i18n 语言变化,自动触发同步\n * @param callback 语言变化时的额外回调\n * @returns 取消监听的函数\n */\nexport const setupLocaleSync = (callback?: (locale: SupportedLocale) => void): (() => void) => {\n const handleLanguageChange = (newLocale: string) => {\n if (isSupportedLocale(newLocale)) {\n // 执行核心同步\n syncLocaleToBrowser(newLocale);\n // 执行自定义回调\n callback?.(newLocale);\n }\n };\n\n // 监听 i18n 语言变化事件\n i18n.on('languageChanged', handleLanguageChange);\n\n // 立即同步一次当前语言\n const currentLocale = i18n.language;\n if (isSupportedLocale(currentLocale)) {\n syncLocaleToBrowser(currentLocale);\n }\n\n // 返回取消监听的函数\n return () => {\n i18n.off('languageChanged', handleLanguageChange);\n };\n};\n\n/**\n * 同步页面标题(配合 i18n 实现标题多语言)\n * @param t 翻译函数\n * @param titleKey 翻译 key\n * @param params 翻译参数\n */\nexport const syncPageTitle = (\n t: (key: string, params?: Record<string, any>) => string,\n titleKey: string,\n params?: Record<string, any>,\n): void => {\n document.title = t(titleKey, params);\n};\n\n/**\n * 初始化语言同步(项目启动时调用一次)\n * 自动读取存储/浏览器语言,同步到 i18n 和浏览器环境\n */\nexport const initLocaleSync = async (): Promise<void> => {\n // 避免循环依赖,这里动态导入 detector 的 getFinalLocale\n const { getFinalLocale } = await import('./detector');\n const finalLocale = getFinalLocale();\n\n // 同步到 i18n\n if (i18n.language !== finalLocale) {\n await i18n.changeLanguage(finalLocale);\n } else {\n // 语言一致时,直接同步到浏览器\n syncLocaleToBrowser(finalLocale);\n }\n};\n","import { useTranslation, type UseTranslationOptions } from 'react-i18next';\nimport { type SupportedLocale } from '../../locales';\n\n// 类型安全 t 函数\nexport type TFunction = (key: string, options?: { [key: string]: any }) => string;\n\n// 封装 useTranslation,自动推导命名空间\n// 支持核心命名空间和扩展命名空间\nexport const useI18n = <N extends string = 'common'>(\n ns?: N | readonly N[],\n options?: UseTranslationOptions<any>,\n) => {\n // 统一转成数组,默认使用 'common' 命名空间\n const namespace = ns ? (Array.isArray(ns) ? ns : [ns]) : ['common'];\n\n const { t, i18n, ...rest } = useTranslation(namespace as any, options);\n\n // 类型安全的 t 函数\n const typedT = ((key: string, options?: any) => {\n return t(key, options);\n }) as TFunction;\n\n // 切换语言\n const changeLocale = async (locale: SupportedLocale) => {\n await i18n.changeLanguage(locale);\n };\n\n return {\n t: typedT,\n i18n,\n changeLocale,\n ...rest,\n };\n};\n","import type { BuiltinThemeType } from '../types';\n\ninterface BuiltinThemePreset {\n color: string;\n darkPrimaryColor?: string;\n primaryColor?: string;\n type: BuiltinThemeType;\n}\n\nconst BUILT_IN_THEME_PRESETS: BuiltinThemePreset[] = [\n {\n color: '#006BE6',\n type: 'default',\n },\n {\n color: 'hsl(245 82% 67%)',\n type: 'violet',\n },\n {\n color: 'hsl(347 77% 60%)',\n type: 'pink',\n },\n {\n color: 'hsl(42 84% 61%)',\n type: 'yellow',\n },\n {\n color: 'hsl(231 98% 65%)',\n type: 'sky-blue',\n },\n {\n color: 'hsl(161 90% 43%)',\n type: 'green',\n },\n {\n color: 'hsl(240 5% 26%)',\n darkPrimaryColor: 'hsl(0 0% 98%)',\n primaryColor: 'hsl(240 5.9% 10%)',\n type: 'zinc',\n },\n\n {\n color: 'hsl(181 84% 32%)',\n type: 'deep-green',\n },\n\n {\n color: 'hsl(211 91% 39%)',\n type: 'deep-blue',\n },\n {\n color: 'hsl(18 89% 40%)',\n type: 'orange',\n },\n {\n color: 'hsl(0 75% 42%)',\n type: 'rose',\n },\n\n {\n color: 'hsl(0 0% 25%)',\n darkPrimaryColor: 'hsl(0 0% 98%)',\n primaryColor: 'hsl(240 5.9% 10%)',\n type: 'neutral',\n },\n {\n color: 'hsl(215 25% 27%)',\n darkPrimaryColor: 'hsl(0 0% 98%)',\n primaryColor: 'hsl(240 5.9% 10%)',\n type: 'slate',\n },\n {\n color: 'hsl(217 19% 27%)',\n darkPrimaryColor: 'hsl(0 0% 98%)',\n primaryColor: 'hsl(240 5.9% 10%)',\n type: 'gray',\n },\n {\n color: '',\n type: 'custom',\n },\n];\n\nexport const COLOR_PRESETS = [...BUILT_IN_THEME_PRESETS].slice(0, 7);\n\n/**\n * 「次要文字」档(docs/design-language.md §2.2)。\n *\n * 浅色侧不直接用 gray-500 #6B7280:它是按白底量的,实测离开白底就掉下 4.5——\n * 白 4.83 / 页面大底 #F5F5F5 4.43 / 卡片灰 #F0F2F5 4.31 / 标签栏 #F0F0F0 4.24。\n * 沿同一色相压暗到 #676E7C 后四处全部达标:5.12 / 4.70 / 4.57 / 4.50(数值由\n * `node .zcode/tmp/hue-calc.mjs` 同口径的 sRGB 相对亮度算出)。\n * 三端共用这两个值(vue-element 的 --el-text-color-secondary、vue-vben 的 --gray-*),\n * 改动须同步 docs/design-language.md §2.2 与三端。\n */\nexport const SECONDARY_TEXT_LIGHT = '#676E7C';\n\n/** 暗色侧的次要文字 = §2.3 的 #8B949E(L0 6.23 / L1 5.77 / L2 5.34 / L3 4.77 全达标) */\nexport const SECONDARY_TEXT_DARK = '#8B949E';\n\nexport { BUILT_IN_THEME_PRESETS };\n\nexport type { BuiltinThemePreset };\n","import type { ThemeConfig } from 'antd';\n\n/**\n * 暗黑模式扩展色板 —— 暗夜蓝黑大底 + gray 系一楼(2026-08-24 定稿)\n *\n * L0 #0B0F19 地面:全局大背景(暗夜蓝黑,最暗)\n * L1 #111827 一楼:搜索卡片 + 抽屉 + 表格容器(gray-900,比大底微亮)\n * L2 #161F33 二楼:输入框/下拉框(比卡片微亮,\"浮起\"式,避免黑洞感)\n * L3 #1F2937 表头独立次级深色;#1C2128 下拉/弹窗浮层\n *\n * 主色 hsl(212 100% 45%)(#006BE6,见 docs/design-language.md);文字基色 #F8FAFC;placeholder #6E7681\n */\nexport const DARK_PALETTE = {\n bgLayout: '#0B0F19',\n bgNav: '#111827',\n bgSurface: '#111827',\n bgElevated: '#1C2128',\n bgInset: '#111827',\n borderInput: 'rgba(148, 163, 184, 0.22)',\n borderInputHover: 'rgba(148, 163, 184, 0.4)',\n // 卡片描边 = 分隔线 = §2.3 的 8% 白一档(下面 colorBorderSecondary 直接引用,不留第二份真相)。\n // 此前这里是 5% 白,正是 §2.3 点名\"卡片与大底糊成一片\"的档,且与 token 实际生效值不一致。\n borderCard: 'rgba(255, 255, 255, 0.08)',\n textPrimary: '#F8FAFC',\n textSecondary: '#8B949E',\n textTertiary: '#6E7681',\n // placeholder 取三级灰(2026-10-16 由 #9CA3AF 下调):placeholder 的\"显眼度\"要按\n // 它与自己控件里已输入值的**分离度**判,不是按它对底色的对比度判。浅侧\n // #1F2937/#9CA3AF = 5.78 倍,暗侧同值只有 2.43 倍(16.96 vs 6.99),于是暗色下\n // 提示语和真内容一样跳。降到本档后分离度 4.39 倍,且复用已有文字档、不新增 hex。\n placeholder: '#6E7681',\n accentBlue: '#006BE6',\n} as const;\n\n/**\n * 暗黑模式扩展 token —— 叠加在 theme.darkAlgorithm 之上的精调\n * (用户 2026-08-24 定稿:暗夜蓝黑大底 + gray 一楼;主色 2026-09-08 起对齐设计语言规范)\n */\nexport const darkThemeTokens: ThemeConfig['token'] = {\n // 地面:最暗的暗夜蓝黑大底\n colorBgBase: '#0B0F19',\n colorBgLayout: '#0B0F19',\n // 一楼:卡片、抽屉、表格容器(比大底微亮)\n colorBgContainer: '#111827',\n // 浮层:下拉、弹窗\n colorBgElevated: '#1C2128',\n // 文字:明亮高级白为基,三阶灰拉开对比\n colorTextBase: '#F8FAFC',\n colorText: '#F8FAFC',\n colorTextSecondary: '#8B949E',\n colorTextTertiary: '#6E7681',\n colorTextPlaceholder: DARK_PALETTE.placeholder,\n // 边框:拒绝隐形。输入类控件边框用 slate 蓝灰(带蓝相,和整页蓝黑调一致,\n // 纯白 α 边框在蓝黑底上会显\"脏灰\");分隔线 8% 白保证卡片/表格\n // 与大底之间有可感知的层次(5% 时几乎隐形,整页糊成一片)\n colorBorder: 'rgba(148, 163, 184, 0.28)',\n colorBorderSecondary: DARK_PALETTE.borderCard,\n colorFill: 'rgba(255, 255, 255, 0.14)',\n colorFillSecondary: 'rgba(255, 255, 255, 0.09)',\n colorFillTertiary: 'rgba(255, 255, 255, 0.05)',\n colorFillQuaternary: 'rgba(255, 255, 255, 0.03)',\n // 浮层列表项 hover:默认派生自 colorFillTertiary(5% 白),在 #1C2128\n // 浮层上几乎不可察;提到 8% 让 Select/Dropdown/Menu 等悬停可感知,\n // 仍保持\"暗底 + 细亮显形\"的克制风格(不加发光)\n controlItemBgHover: 'rgba(255, 255, 255, 0.08)',\n // 浮层投影:暗色下必须用黑投影才有\"浮起\"感(antd v6 darkAlgorithm 派生的\n // 白色投影在暗底上不可见,弹层会像贴在页面上)\n boxShadowSecondary: '0 6px 16px rgba(0, 0, 0, 0.45), 0 3px 6px rgba(0, 0, 0, 0.3)',\n // 表单标签降一档亮度:让\"值\"成为视觉主角,标签退为辅助信息\n colorTextHeading: '#DCE3ED',\n // 卡片/弹窗/抽屉大圆角(对应规范\"卡片 rounded-xl\")\n borderRadiusLG: 12,\n};\n\n/**\n * 暗黑模式组件级 token:\n * 输入类控件用更暗的内嵌底(#0A0F1C 蓝黑,与卡片对立)靠亮边框显形;\n * hover 边框提亮同色系 slate、focus 用主色边框 + 3px 柔光,状态反馈清晰;\n * Drawer 加深遮罩突出悬浮;Table 表头独立次级深色 + 主色行悬停/选中。\n * 注意:antd v6 已移除 .ant-select-selector/.ant-drawer-content,\n * 边框等需 CSS 打根/新层(见 pro-components-dark.css)。\n */\nconst inputLike = {\n // 输入类控件与表面同层(2026-09-08 对齐 vben 观感修订:vben 的输入框\n // 与卡片同底色、仅靠边框区分,实测观感更通透;此前的\"浮起 lighter\"\n // 与\"内嵌更暗\"两个方案先后被否)\n colorBgContainer: '#111827',\n colorBorder: 'rgba(255, 255, 255, 0.1)',\n hoverBorderColor: 'rgba(255, 255, 255, 0.2)',\n activeBorderColor: '#006BE6',\n activeShadow: '0 0 0 3px rgba(0, 107, 230, 0.12)',\n};\n\nexport const darkThemeComponents: ThemeConfig['components'] = {\n Input: { ...inputLike },\n Select: { ...inputLike },\n InputNumber: { ...inputLike },\n DatePicker: { ...inputLike },\n TreeSelect: { ...inputLike },\n Drawer: {\n colorBgMask: 'rgba(0, 0, 0, 0.6)',\n paddingLG: 24,\n },\n Modal: {\n // 遮罩与 Drawer 同档(0.6):弹窗是模态重交互面,背后的页面要压得更暗\n // 才衬得出悬浮层;antd 默认 0.45 在 L0 深色大底上几乎不可感\n colorBgMask: 'rgba(0, 0, 0, 0.6)',\n },\n Table: {\n // colorHeaderBg 非合法 antd Token(Table 表头色由 colorBgContainer/colorFillAlter 派生),原值 '#1F2937' 移除\n // 行悬停/选中用主色微底,行状态一眼可辨(默认派生的 5% 白在暗色下不可察)\n rowHoverBg: 'rgba(0, 107, 230, 0.08)',\n rowSelectedBg: 'rgba(0, 107, 230, 0.15)',\n rowSelectedHoverBg: 'rgba(0, 107, 230, 0.2)',\n headerSplitColor: 'transparent',\n },\n Menu: {\n // 展开的子菜单面板不铺底色(design-language.md §4「侧边栏菜单交互态」:\n // 「父级展开链路只做文字/图标提亮,不铺底色」)。\n // 必须在 token 层关掉、不能只删 CSS:出厂值 `darkSubMenuItemBg: '#000c17'`\n // (es/menu/style/index.js:449)比原先那条 rgba(0,0,0,.22) 的凹陷更黑,规则一撤就露出来。\n // 两个键都要写,因为它们喂的是**不同的 CSS 变量**(实测 cssVar 模式):\n // .ant-menu-dark(侧栏在暗色下走这套,SiderMenu/index.tsx 的 theme=isDark?'dark':'light')\n // → --ant-menu-dark-sub-menu-item-bg ← darkSubMenuItemBg\n // .ant-menu-light(暗色应用里未显式指定 theme 的 Menu)\n // → --ant-menu-sub-menu-item-bg ← subMenuItemBg\n // 只改后者时暗色侧栏纹丝不动(#000c17 依旧),这就是本条注释存在的理由。\n darkSubMenuItemBg: 'transparent',\n subMenuItemBg: 'transparent',\n // 折叠后弹出的子菜单浮层是「浮层」,取 §2.3 的 #1C2128,不是 antd 出厂藏青 #001529。\n // 根 UL 有 SiderMenu/index.tsx:229 的 inline background:'transparent' 兜着,portal 里这一层没有。\n // 只补这一个键:实测弹层容器底读 --ant-menu-dark-popup-bg ← darkPopupBg;\n // darkItemBg → itemBg 只喂根 UL(已被 inline 盖住),动它属无据扩面。\n darkPopupBg: '#1C2128',\n },\n};\n","import type {Preferences} from \"../types\";\n\nconst defaultPreferences: Preferences = {\n app: {\n accessMode: \"frontend\",\n authPageLayout: \"panel-right\",\n checkUpdatesInterval: 1,\n colorGrayMode: false,\n colorWeakMode: false,\n compact: false,\n contentCompact: \"wide\",\n defaultAvatar: \"/default-avatar.png\",\n dynamicTitle: true,\n enableCheckUpdates: false,\n enablePreferences: true,\n enableRefreshToken: false,\n enableTenant: false,\n isMobile: false,\n layout: \"sidebar-nav\",\n locale: \"zh-CN\",\n loginExpiredMode: \"page\",\n name: \"GoWind Admin\",\n version: \"0.0.0\",\n preferencesButtonPosition: \"auto\",\n watermark: false,\n },\n breadcrumb: {\n enable: true,\n hideOnlyOne: false,\n showHome: true,\n showIcon: true,\n styleType: \"normal\",\n },\n copyright: {\n companyName: \"GoWind\",\n companySiteLink: \"https://www.gowind.cloud\",\n date: \"2026\",\n enable: true,\n icp: \"\",\n icpLink: \"\",\n settingShow: true,\n },\n footer: {\n enable: false,\n fixed: false,\n },\n header: {\n enable: true,\n hidden: false,\n mode: \"fixed\",\n },\n logo: {\n enable: true,\n source: \"/logo.png\",\n },\n navigation: {\n accordion: true,\n split: true,\n styleType: \"rounded\",\n },\n shortcutKeys: {\n enable: true,\n globalLockScreen: true,\n globalLogout: true,\n globalPreferences: true,\n globalSearch: true,\n },\n sidebar: {\n collapsed: false,\n collapsedShowTitle: false,\n enable: true,\n expandOnHover: false,\n extraCollapse: true,\n hidden: false,\n width: 224,\n },\n tabbar: {\n draggable: true,\n enable: true,\n height: 38,\n keepAlive: true,\n persist: true,\n showIcon: true,\n showMaximize: true,\n showMore: true,\n styleType: \"chrome\",\n },\n theme: {\n builtinType: \"default\",\n colorDestructive: \"hsl(348 100% 61%)\",\n colorPrimary: \"hsl(212 100% 45%)\",\n colorSuccess: \"hsl(144 57% 58%)\",\n colorWarning: \"hsl(42 84% 61%)\",\n mode: \"dark\",\n radius: \"8\",\n semiDarkHeader: false,\n semiDarkSidebar: false,\n },\n transition: {\n enable: true,\n loading: true,\n name: \"fade-slide\",\n progress: true,\n },\n widget: {\n fullscreen: true,\n globalSearch: true,\n languageToggle: true,\n lockScreen: true,\n notification: true,\n refresh: true,\n sidebarToggle: true,\n themeToggle: true,\n },\n};\n\nexport {defaultPreferences};\n","import type { ThemeConfig } from 'antd';\n\n/**\n * 浅色模式扩展色板 —— docs/design-language.md §2.2 的落地值\n *\n * canvas #F1F3F6 页面画布(卡片在其上\"浮起\")\n * surface #FFFFFF 卡片 / 表格容器 / 顶栏 / 侧栏\n * border #E4E4E7 通用边框(≈ zinc-200)\n *\n * 沿革:本文件 2026-10-16 之前不存在——react 端此前只有 darkTheme.ts 一套覆盖,浅色全部\n * 走 antd defaultAlgorithm 派生值(实测画布 #F5F5F5、卡片边框 #F0F0F0、卡片投影 none),\n * 白卡压在 #F5F5F5 上分离度只有 1.049、边框对白底 1.045,等于\"没有层级系统\"。\n * §2.2 开头那条\"react 半 ✓ … 要严格对齐需新增一套 light tokens,未做,属新工作\"记录的就是这里。\n */\nexport const LIGHT_PALETTE = {\n bgLayout: '#F1F3F6',\n bgSurface: '#FFFFFF',\n borderCard: '#E4E4E7',\n} as const;\n\n/**\n * 浅色模式扩展 token —— 叠加在 theme.defaultAlgorithm 之上\n *\n * 只补\"层级\"这一条线(画布/容器/边框/圆角),文字档不动:\n * 主/常规/次要三档已由 src/styles/semantic-text.css 与 colorTextDescription\n * (constants.ts 的 SECONDARY_TEXT_LIGHT)接上,再在 token 层覆盖一遍会派生出\n * 第二套真相。\n *\n * colorTextPlaceholder 同样**故意不动**:§2.2 记的浅色档是 #9CA3AF,但 antd\n * 默认的 rgba(0,0,0,.25)(合成 #BFBFBF)与已输入值 #1F2937 的分离度 6.06 倍,\n * 比 #9CA3AF 的 5.78 倍更\"退后\"。本轮的诉求是去掉生硬、不是加重提示语,\n * 钉成 #9CA3AF 会把暗色那轮刚压下去的问题在浅色侧重犯一遍。\n */\nexport const lightThemeTokens: ThemeConfig['token'] = {\n // 画布:§2.2 的 #F1F3F6(冷调微蓝),替代 antd 派生的中性 #F5F5F5\n colorBgLayout: LIGHT_PALETTE.bgLayout,\n colorBgContainer: LIGHT_PALETTE.bgSurface,\n // 边框:antd 默认 colorBorderSecondary 是 #F0F0F0,在白底上 1.045 倍、几乎隐形,\n // 卡片只剩一条看不见的线 ⇒ \"平\"。提到 §2.2 档后 1.135 倍,卡片边界可辨。\n // 注意连带效应(antd/es/theme/util/alias.js:51):\n // colorSplit = getAlphaColor(colorBorderSecondary, colorBgContainer),\n // 所以这一条同时把浅色分隔线从 rgba(5,5,5,.06) 抬到等效 #E4E4E7 ——\n // 正好补齐 §2.2 的边框档(此前浅色抽屉/表头分隔线实测只有 1.140,低于规范 1.269)。\n colorBorderSecondary: LIGHT_PALETTE.borderCard,\n // 大圆角:§2.4 卡片/抽屉/弹窗 = 12px。不设时浅色拿的是 borderRadius 派生值\n // (偏好 radius=8 ⇒ 实测 10px),而暗色在 darkThemeTokens 里显式钉了 12,两态形状对不上。\n borderRadiusLG: 12,\n};\n\n// 浅色投影两档(静止 0 1px 2px / 悬浮 0 4px 12px)不在这里:antd 没有可覆写的\n// \"带边框卡片静止投影\"token——boxShadowCard 是内部派生值,写进 ThemeConfig.token\n// 会被 TS 挡掉(AliasToken 上不存在),而它只服务 hoverable 卡片的 hover。\n// 因此两档统一落在 styles/global.css 的 [data-theme='light'] .ant-card 上,\n// 那里静止/悬浮都能覆盖,不留第二处真相。\n\n// 浅色组件级 token 只有一条:展开的子菜单面板不铺底色。\n// antd 出厂在这里给的是 `subMenuItemBg = colorFillAlter`(白底上 = rgba(0,0,0,.02),实测合成 #FAFAFA,\n// 分离度 1.02 肉眼不可察),但 §4「侧边栏菜单交互态」说的是\"不铺底色\"而不是\"铺一层看不见的底色\"——\n// 留着它,暗色收平了、亮色仍有一档,两态结构对不上;且这正是本轮判定 react 违规的那条规则的同族。\nexport const lightThemeComponents: ThemeConfig['components'] = {\n Menu: {\n subMenuItemBg: 'transparent',\n },\n};\n\n// 其余浅色没有组件级 token:Card 描边已由全局 colorBorderSecondary 覆盖(组件级默认取它),\n// 输入类边框刻意留在 antd 的 colorBorder #D9D9D9 —— §2.2 的 #E4E4E7 比 #D9D9D9 更浅,\n// 套到输入框上会让控件描边倒退一档。\n","const reduxImpl = (reducer, initial) => (set, _get, api) => {\n api.dispatch = (action) => {\n set((state) => reducer(state, action), false, action);\n return action;\n };\n api.dispatchFromDevtools = true;\n return { dispatch: (...args) => api.dispatch(...args), ...initial };\n};\nconst redux = reduxImpl;\n\nconst shouldDispatchFromDevtools = (api) => !!api.dispatchFromDevtools && typeof api.dispatch === \"function\";\nconst trackedConnections = /* @__PURE__ */ new Map();\nconst getTrackedConnectionState = (name) => {\n const api = trackedConnections.get(name);\n if (!api) return {};\n return Object.fromEntries(\n Object.entries(api.stores).map(([key, api2]) => [key, api2.getState()])\n );\n};\nconst extractConnectionInformation = (store, extensionConnector, options) => {\n if (store === void 0) {\n return {\n type: \"untracked\",\n connection: extensionConnector.connect(options)\n };\n }\n const existingConnection = trackedConnections.get(options.name);\n if (existingConnection) {\n return { type: \"tracked\", store, ...existingConnection };\n }\n const newConnection = {\n connection: extensionConnector.connect(options),\n stores: {}\n };\n trackedConnections.set(options.name, newConnection);\n return { type: \"tracked\", store, ...newConnection };\n};\nconst removeStoreFromTrackedConnections = (name, store, api) => {\n if (store === void 0) return;\n const connectionInfo = trackedConnections.get(name);\n if ((connectionInfo == null ? void 0 : connectionInfo.stores[store]) !== api) return;\n delete connectionInfo.stores[store];\n if (Object.keys(connectionInfo.stores).length === 0) {\n trackedConnections.delete(name);\n }\n};\nconst v8StackLineRe = /^at (?:new |async )?(.+?) \\(/;\nconst geckoStackLineRe = /^([^@]+)@/;\nfunction findCallerName(stack) {\n var _a, _b, _c;\n if (!stack) return void 0;\n const traceLines = stack.split(\"\\n\");\n const apiSetStateLineIndex = traceLines.findIndex(\n (traceLine) => traceLine.includes(\"api.setState\")\n );\n if (apiSetStateLineIndex < 0) return void 0;\n const callerLine = ((_a = traceLines[apiSetStateLineIndex + 1]) == null ? void 0 : _a.trim()) || \"\";\n return ((_b = v8StackLineRe.exec(callerLine)) == null ? void 0 : _b[1]) || ((_c = geckoStackLineRe.exec(callerLine)) == null ? void 0 : _c[1]);\n}\nconst devtoolsImpl = (fn, devtoolsOptions = {}) => (set, get, api) => {\n const { enabled, anonymousActionType, store, actionCreators, ...options } = devtoolsOptions;\n let extensionConnector;\n try {\n extensionConnector = (enabled != null ? enabled : (import.meta.env ? import.meta.env.MODE : void 0) !== \"production\") && window.__REDUX_DEVTOOLS_EXTENSION__;\n } catch (e) {\n }\n if (!extensionConnector) {\n return fn(set, get, api);\n }\n let connection = void 0;\n let isRecording = true;\n api.setState = ((state, replace, nameOrAction) => {\n const r = set(state, replace);\n if (!isRecording) return r;\n const action = nameOrAction === void 0 ? {\n type: anonymousActionType || findCallerName(new Error().stack) || \"anonymous\"\n } : typeof nameOrAction === \"string\" ? { type: nameOrAction } : nameOrAction;\n if (store === void 0) {\n connection == null ? void 0 : connection.send(action, get());\n return r;\n }\n connection == null ? void 0 : connection.send(\n {\n ...action,\n type: `${store}/${action.type}`\n },\n {\n ...getTrackedConnectionState(options.name),\n [store]: api.getState()\n }\n );\n return r;\n });\n let unsubscribeFromStore;\n api.devtools = {\n cleanup: () => {\n unsubscribeFromStore == null ? void 0 : unsubscribeFromStore();\n unsubscribeFromStore = void 0;\n removeStoreFromTrackedConnections(options.name, store, api);\n if ((store === void 0 || !trackedConnections.has(options.name)) && connection && typeof connection.unsubscribe === \"function\") {\n connection.unsubscribe();\n }\n }\n };\n const setStateFromDevtools = (...a) => {\n const originalIsRecording = isRecording;\n isRecording = false;\n set(...a);\n isRecording = originalIsRecording;\n };\n const initialState = fn(api.setState, get, api);\n const actionCreatorObjects = actionCreators && getActionCreatorObjects(initialState, actionCreators);\n const connectionInformation = extractConnectionInformation(\n store,\n extensionConnector,\n { ...options, actionCreators: actionCreatorObjects }\n );\n connection = connectionInformation.connection;\n const findActionCreator = (name) => {\n const actionCreator = actionCreatorObjects == null ? void 0 : actionCreatorObjects.find(\n (candidate) => candidate.name === name\n );\n if (!actionCreator && actionCreators && store === void 0) {\n console.error(\n `[zustand devtools middleware] Action type \"${name}\" not found in actionCreators`\n );\n }\n return actionCreator;\n };\n if (connectionInformation.type === \"untracked\") {\n connection == null ? void 0 : connection.init(initialState);\n } else {\n connectionInformation.stores[connectionInformation.store] = api;\n connection == null ? void 0 : connection.init(\n Object.fromEntries(\n Object.entries(connectionInformation.stores).map(([key, store2]) => [\n key,\n key === connectionInformation.store ? initialState : store2.getState()\n ])\n )\n );\n }\n if (shouldDispatchFromDevtools(api)) {\n let didWarnAboutReservedActionType = false;\n const originalDispatch = api.dispatch;\n api.dispatch = (...args) => {\n if ((import.meta.env ? import.meta.env.MODE : void 0) !== \"production\" && args[0].type === \"__setState\" && !didWarnAboutReservedActionType) {\n console.warn(\n '[zustand devtools middleware] \"__setState\" action type is reserved to set state from the devtools. Avoid using it.'\n );\n didWarnAboutReservedActionType = true;\n }\n originalDispatch(...args);\n };\n }\n unsubscribeFromStore = connection.subscribe((message) => {\n var _a;\n const reviver = typeof options.serialize === \"object\" ? options.serialize.reviver : void 0;\n switch (message.type) {\n case \"ACTION\": {\n if (typeof message.payload !== \"string\") {\n if (shouldDispatchFromDevtools(api)) {\n console.error(\n '[zustand devtools middleware] The action dropdown is not supported with the redux middleware. Dispatch a custom action in JSON format instead, e.g. { \"type\": \"increment\" }'\n );\n return;\n }\n if (!actionCreators) {\n console.error(\n \"[zustand devtools middleware] Unsupported action format\"\n );\n return;\n }\n const actionCreator2 = findActionCreator(message.payload.name);\n if (!actionCreator2) return;\n const args2 = parseDropdownArgs(message.payload, reviver);\n if (!args2) return;\n return actionCreator2.func(...args2);\n }\n let action;\n try {\n action = JSON.parse(message.payload, reviver);\n } catch (e) {\n let errorMessage = `[zustand devtools middleware] Malformed JSON. When dispatching custom actions, please format the payload as a JSON string.\n Examples:`;\n if (shouldDispatchFromDevtools(api)) {\n errorMessage += `\n - { \"type\": \"increment\", \"amount\": 1 }\n - { \"type\": \"reset\" }\n `;\n } else {\n errorMessage += `\n - { \"type\": \"increment\" }\n - { \"type\": \"increment\", \"args\": [1] }\n If you have args with default values or want to be \\`undefined\\`, prefer using the object syntax so you can omit them.\n - { \"type\": \"increment\", \"args\": { \"amount\": 1 } }\n `;\n }\n console.error(errorMessage.replace(/\\n\\s+/g, \"\\n\"));\n return;\n }\n if (action.type === \"__setState\") {\n if (store === void 0) {\n if (!(\"state\" in action)) {\n console.warn(\n `\n [zustand devtools middleware] Calling __setState without a state property.\n This may not be what you intended, you should explicitly set the state you want to set.\n Example: { \"type\": \"__setState\", \"state\": { \"foo\": \"bar\" } }\n `.replace(/\\n\\s+/g, \"\\n\")\n );\n return;\n }\n setStateFromDevtools(action.state);\n return;\n }\n if (Object.keys(action.state).length !== 1) {\n console.error(\n `\n [zustand devtools middleware] Unsupported __setState action format.\n When using 'store' option in devtools(), the 'state' should have only one key, which is a value of 'store' that was passed in devtools(),\n and value of this only key should be a state object. Example: { \"type\": \"__setState\", \"state\": { \"abc123Store\": { \"foo\": \"bar\" } } }\n `.replace(/\\n\\s+/g, \"\\n\")\n );\n }\n const stateFromDevtools = action.state[store];\n if (stateFromDevtools === void 0 || stateFromDevtools === null) {\n return;\n }\n if (JSON.stringify(api.getState()) !== JSON.stringify(stateFromDevtools)) {\n setStateFromDevtools(stateFromDevtools);\n }\n return;\n }\n if (shouldDispatchFromDevtools(api)) {\n api.dispatch(action);\n return;\n }\n const actionCreator = findActionCreator(action.type);\n if (!actionCreator) return;\n const args = action.args;\n return actionCreator.func(\n ...Array.isArray(args) ? args : actionCreator.args.map((name) => args == null ? void 0 : args[name])\n );\n }\n case \"DISPATCH\":\n switch (message.payload.type) {\n case \"RESET\":\n setStateFromDevtools(initialState);\n if (store === void 0) {\n return connection == null ? void 0 : connection.init(api.getState());\n }\n return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name));\n case \"COMMIT\":\n if (store === void 0) {\n connection == null ? void 0 : connection.init(api.getState());\n return;\n }\n return connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name));\n case \"ROLLBACK\":\n return parseJsonThen(message.state, reviver, (state) => {\n if (store === void 0) {\n setStateFromDevtools(state);\n connection == null ? void 0 : connection.init(api.getState());\n return;\n }\n setStateFromDevtools(state[store]);\n connection == null ? void 0 : connection.init(getTrackedConnectionState(options.name));\n });\n case \"JUMP_TO_STATE\":\n case \"JUMP_TO_ACTION\":\n return parseJsonThen(message.state, reviver, (state) => {\n if (store === void 0) {\n setStateFromDevtools(state);\n return;\n }\n if (JSON.stringify(api.getState()) !== JSON.stringify(state[store])) {\n setStateFromDevtools(state[store]);\n }\n });\n case \"IMPORT_STATE\": {\n const { nextLiftedState } = message.payload;\n const lastComputedState = (_a = nextLiftedState.computedStates.slice(-1)[0]) == null ? void 0 : _a.state;\n if (!lastComputedState) return;\n if (store === void 0) {\n setStateFromDevtools(lastComputedState);\n } else {\n setStateFromDevtools(lastComputedState[store]);\n }\n connection == null ? void 0 : connection.send(\n null,\n // FIXME no-any\n nextLiftedState\n );\n return;\n }\n case \"PAUSE_RECORDING\":\n return isRecording = !isRecording;\n }\n return;\n }\n });\n return initialState;\n};\nconst devtools = devtoolsImpl;\nconst parseJsonThen = (stringified, reviver, fn) => {\n let parsed;\n try {\n parsed = JSON.parse(stringified, reviver);\n } catch (e) {\n console.error(\n \"[zustand devtools middleware] Could not parse the received json\",\n e\n );\n }\n if (parsed !== void 0) fn(parsed);\n};\nfunction maskToRegex(mask) {\n const escaped = mask.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\").replace(/\\*\\*|\\*/g, (wildcard) => wildcard === \"**\" ? \".*\" : \"[^.]*\");\n return new RegExp(`^${escaped}$`);\n}\nconst isPlainObject = (value) => typeof value === \"object\" && value !== null && Object.getPrototypeOf(value) === Object.prototype;\nconst getStateFunctions = (state, walked = /* @__PURE__ */ new Map()) => {\n var _a;\n if (!isPlainObject(state)) return [];\n const known = walked.get(state);\n if (known) return known;\n walked.set(state, []);\n const functions = [];\n for (const key of Object.keys(state)) {\n const value = (_a = Object.getOwnPropertyDescriptor(state, key)) == null ? void 0 : _a.value;\n if (typeof value === \"function\") {\n functions.push([key, value]);\n } else {\n for (const [path, func] of getStateFunctions(value, walked)) {\n functions.push([`${key}.${path}`, func]);\n }\n }\n }\n walked.set(state, functions);\n return functions;\n};\nconst sourceTokenRe = /\"(?:\\\\.|[^\"\\\\])*\"|'(?:\\\\.|[^'\\\\])*'|`(?:\\\\.|[^`\\\\])*`|\\/\\*[\\s\\S]*?\\*\\/|\\/\\/.*|[\\s\\S]/g;\nconst getParamNames = (func) => {\n var _a, _b;\n const tokens = ((_a = func.toString().match(sourceTokenRe)) != null ? _a : []).filter(\n (token) => !/^\\/[/*]/.test(token)\n );\n const arrowParam = (_b = /^(?:async\\s+)?([^\\s(=]+)\\s*=>/.exec(tokens.join(\"\"))) == null ? void 0 : _b[1];\n if (arrowParam) return [arrowParam];\n const params = [\"\"];\n let depth = 0;\n let inDefaultValue = false;\n for (const token of tokens.slice(tokens.indexOf(\"(\") + 1)) {\n if (depth === 0) {\n if (token === \")\") break;\n if (token === \",\") {\n params.push(\"\");\n inDefaultValue = false;\n continue;\n }\n if (token === \"=\") inDefaultValue = true;\n }\n if (\"([{\".includes(token)) depth++;\n if (\")]}\".includes(token)) depth--;\n if (!inDefaultValue) params[params.length - 1] += token;\n }\n return params.map((param) => param.trim()).filter((param) => param && !param.startsWith(\"...\"));\n};\nconst createActionCreatorObject = (name, func) => ({ name, func, args: getParamNames(func) });\nfunction getActionCreatorObjects(state, actionCreators) {\n const actions = /* @__PURE__ */ new Map();\n const matchers = Object.entries(actionCreators).map(([key, picked]) => ({\n matcher: maskToRegex(key),\n picked,\n key\n })).sort((a, b) => {\n const aIsDoubleWildcard = a.key.includes(\"**\");\n const bIsDoubleWildcard = b.key.includes(\"**\");\n if (aIsDoubleWildcard !== bIsDoubleWildcard) {\n return aIsDoubleWildcard ? -1 : 1;\n }\n const aIsSingleWildcard = a.key.includes(\"*\") && !aIsDoubleWildcard;\n const bIsSingleWildcard = b.key.includes(\"*\") && !bIsDoubleWildcard;\n if (aIsSingleWildcard !== bIsSingleWildcard) {\n return aIsSingleWildcard ? -1 : 1;\n }\n return a.key.length - b.key.length;\n });\n for (const [name, func] of getStateFunctions(state)) {\n for (const { matcher, picked } of matchers) {\n if (matcher.test(name)) {\n if (picked === false) {\n actions.delete(name);\n continue;\n }\n actions.set(name, createActionCreatorObject(name, func));\n }\n }\n }\n for (const [name, picked] of Object.entries(actionCreators)) {\n if (typeof picked === \"function\") {\n actions.set(name, createActionCreatorObject(name, picked));\n }\n }\n return Array.from(actions.values());\n}\nconst parseDropdownArgs = ({ args, rest }, reviver) => {\n var _a;\n const parse = (arg) => arg ? JSON.parse(arg, reviver) : void 0;\n try {\n const restArgs = (_a = parse(rest)) != null ? _a : [];\n if (!Array.isArray(restArgs)) throw new Error(\"...rest is not an array\");\n return [...args.map(parse), ...restArgs];\n } catch (e) {\n console.error(\n '[zustand devtools middleware] Could not parse the arguments from the action dropdown. Each one must be JSON, e.g. 5, \"foo\" or { \"by\": 5 }, and ...rest a JSON array.',\n e\n );\n return void 0;\n }\n};\n\nconst subscribeWithSelectorImpl = (fn) => (set, get, api) => {\n const origSubscribe = api.subscribe;\n api.subscribe = ((selector, optListener, options) => {\n let listener = selector;\n if (optListener) {\n const equalityFn = (options == null ? void 0 : options.equalityFn) || Object.is;\n let currentSlice = selector(api.getState());\n listener = (state) => {\n const nextSlice = selector(state);\n if (!equalityFn(currentSlice, nextSlice)) {\n const previousSlice = currentSlice;\n optListener(currentSlice = nextSlice, previousSlice);\n }\n };\n if (options == null ? void 0 : options.fireImmediately) {\n optListener(currentSlice, currentSlice);\n }\n }\n return origSubscribe(listener);\n });\n const initialState = fn(set, get, api);\n return initialState;\n};\nconst subscribeWithSelector = subscribeWithSelectorImpl;\n\nfunction combine(initialState, create) {\n return (...args) => Object.assign({}, initialState, create(...args));\n}\n\nfunction createJSONStorage(getStorage, options) {\n let storage;\n try {\n storage = getStorage();\n } catch (e) {\n return;\n }\n const persistStorage = {\n getItem: (name) => {\n var _a;\n const parse = (str2) => {\n if (str2 === null) {\n return null;\n }\n return JSON.parse(str2, options == null ? void 0 : options.reviver);\n };\n const str = (_a = storage.getItem(name)) != null ? _a : null;\n if (isPromiseLike(str)) {\n return str.then(parse);\n }\n return parse(str);\n },\n setItem: (name, newValue) => storage.setItem(name, JSON.stringify(newValue, options == null ? void 0 : options.replacer)),\n removeItem: (name) => storage.removeItem(name)\n };\n return persistStorage;\n}\nconst isPromiseLike = (value) => typeof (value == null ? void 0 : value.then) === \"function\";\nconst toSyncThenable = (value) => ({\n then(onFulfilled) {\n return onFulfilled ? toThenable(onFulfilled)(value) : this;\n }\n});\nconst toThenable = (fn) => (input) => {\n try {\n const result = fn(input);\n return isPromiseLike(result) ? result : toSyncThenable(result);\n } catch (error) {\n return {\n then(_onFulfilled, onRejected) {\n return onRejected ? toThenable(onRejected)(error) : this;\n }\n };\n }\n};\nconst persistImpl = (config, baseOptions) => (set, get, api) => {\n let options = {\n storage: createJSONStorage(() => window.localStorage),\n partialize: (state) => state,\n version: 0,\n merge: (persistedState, currentState) => ({\n ...currentState,\n ...persistedState\n }),\n ...baseOptions\n };\n let hasHydrated = false;\n let hydrationVersion = 0;\n const hydrationListeners = /* @__PURE__ */ new Set();\n const finishHydrationListeners = /* @__PURE__ */ new Set();\n let storage = options.storage;\n if (!storage) {\n return config(\n (...args) => {\n console.warn(\n `[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.`\n );\n set(...args);\n },\n get,\n api\n );\n }\n const setItem = () => {\n const state = options.partialize({ ...get() });\n return storage.setItem(options.name, {\n state,\n version: options.version\n });\n };\n const savedSetState = api.setState;\n api.setState = (...args) => {\n savedSetState(...args);\n return setItem();\n };\n const configResult = config(\n (...args) => {\n set(...args);\n return setItem();\n },\n get,\n api\n );\n api.getInitialState = () => configResult;\n let stateFromStorage;\n const hydrate = () => {\n var _a, _b;\n if (!storage) return;\n const currentVersion = ++hydrationVersion;\n hasHydrated = false;\n hydrationListeners.forEach((cb) => {\n var _a2;\n return cb((_a2 = get()) != null ? _a2 : configResult);\n });\n const postRehydrationCallback = ((_b = options.onRehydrateStorage) == null ? void 0 : _b.call(options, (_a = get()) != null ? _a : configResult)) || void 0;\n return toThenable(storage.getItem.bind(storage))(options.name).then((deserializedStorageValue) => {\n if (deserializedStorageValue) {\n if (typeof deserializedStorageValue.version === \"number\" && deserializedStorageValue.version !== options.version) {\n if (options.migrate) {\n const migration = options.migrate(\n deserializedStorageValue.state,\n deserializedStorageValue.version\n );\n if (isPromiseLike(migration)) {\n return migration.then((result) => [true, result]);\n }\n return [true, migration];\n }\n console.error(\n `State loaded from storage couldn't be migrated since no migrate function was provided`\n );\n } else {\n return [false, deserializedStorageValue.state];\n }\n }\n return [false, void 0];\n }).then((migrationResult) => {\n var _a2;\n if (currentVersion !== hydrationVersion) {\n return;\n }\n const [migrated, migratedState] = migrationResult;\n stateFromStorage = options.merge(\n migratedState,\n (_a2 = get()) != null ? _a2 : configResult\n );\n set(stateFromStorage, true);\n if (migrated) {\n return setItem();\n }\n }).then(() => {\n if (currentVersion !== hydrationVersion) {\n return;\n }\n postRehydrationCallback == null ? void 0 : postRehydrationCallback(get(), void 0);\n stateFromStorage = get();\n hasHydrated = true;\n finishHydrationListeners.forEach((cb) => cb(stateFromStorage));\n }).then(\n () => void 0,\n (e) => {\n if (currentVersion !== hydrationVersion) {\n return;\n }\n postRehydrationCallback == null ? void 0 : postRehydrationCallback(void 0, e);\n }\n );\n };\n api.persist = {\n setOptions: (newOptions) => {\n options = {\n ...options,\n ...newOptions\n };\n if (newOptions.storage) {\n storage = newOptions.storage;\n }\n },\n clearStorage: () => {\n ++hydrationVersion;\n storage == null ? void 0 : storage.removeItem(options.name);\n },\n getOptions: () => options,\n rehydrate: () => {\n try {\n return Promise.resolve(hydrate());\n } catch (error) {\n return Promise.reject(error);\n }\n },\n hasHydrated: () => hasHydrated,\n onHydrate: (cb) => {\n hydrationListeners.add(cb);\n return () => {\n hydrationListeners.delete(cb);\n };\n },\n onFinishHydration: (cb) => {\n finishHydrationListeners.add(cb);\n return () => {\n finishHydrationListeners.delete(cb);\n };\n }\n };\n if (!options.skipHydration) {\n hydrate();\n }\n return stateFromStorage || configResult;\n};\nconst persist = persistImpl;\n\nfunction ssrSafe(config, isSSR = typeof window === \"undefined\") {\n return (set, get, api) => {\n if (!isSSR) {\n return config(set, get, api);\n }\n const ssrSet = () => {\n throw new Error(\"Cannot set state of Zustand store in SSR\");\n };\n api.setState = ssrSet;\n return config(ssrSet, get, api);\n };\n}\n\nexport { combine, createJSONStorage, devtools, persist, redux, subscribeWithSelector, ssrSafe as unstable_ssrSafe };\n","import type {DeepPartial} from \"../types\";\nimport {isValidElement} from \"react\";\n\n/**\n * 深度合并对象\n */\nexport function mergeDeep<T extends object>(target: T, source: DeepPartial<T>): T {\n const result: any = {...target};\n\n for (const key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n const sourceValue = source[key as keyof DeepPartial<T>];\n const targetValue = target[key as keyof T];\n\n // 跳过 React 元素、DOM 节点、函数等非纯对象\n if (\n isValidElement(sourceValue) || \n sourceValue instanceof Element || \n typeof sourceValue === 'function' ||\n sourceValue === null\n ) {\n console.warn(`[mergeDeep] 跳过非纯对象字段: ${String(key)}, type:`, typeof sourceValue);\n continue;\n }\n\n // 如果两个值都是普通对象,则递归合并\n if (\n isPlainObject(targetValue) &&\n isPlainObject(sourceValue)\n ) {\n result[key] = mergeDeep(\n targetValue as Record<string, any>,\n sourceValue as DeepPartial<any>\n );\n }\n // 否则直接使用源值覆盖(包括 undefined 的情况)\n else if (sourceValue !== undefined) {\n result[key] = sourceValue;\n }\n }\n }\n\n return result;\n}\n\n/**\n * 判断是否为普通对象\n */\nexport function isPlainObject(value: any): value is Record<string, any> {\n return (\n value !== null &&\n typeof value === 'object' &&\n Object.prototype.toString.call(value) === '[object Object]' &&\n !Array.isArray(value)\n );\n}\n","import {create} from 'zustand';\nimport {persist} from 'zustand/middleware';\n\nimport {defaultPreferences} from '../config/default';\nimport type {DeepPartial, Preferences} from '../types';\nimport {mergeDeep} from \"../utils/merge\";\n\n\nexport interface PreferencesState {\n preferences: Preferences;\n setPreferences: (overrides: DeepPartial<Preferences>) => void;\n resetPreferences: () => void;\n getPreference: <K extends keyof Preferences>(key: K) => Preferences[K];\n}\n\nexport const usePreferencesStore = create<PreferencesState>()(\n persist(\n (set, get) => ({\n preferences: defaultPreferences,\n\n setPreferences: (overrides) => {\n set((state) => ({\n preferences: mergeDeep(state.preferences, overrides),\n }));\n },\n\n resetPreferences: () => {\n set({preferences: defaultPreferences});\n },\n\n getPreference: (key) => {\n return get().preferences[key];\n },\n }),\n {\n name: 'app-preferences',\n // v1 → v2:设计语言规范(docs/design-language.md)回归 vben 深蓝\n // hsl(212 100% 45%) 并统一默认圆角 8。历史上 v0→v1 曾把默认主色迁到\n // #3B82F6;本版把默认主题的两种旧蓝一并收敛到规范值。仅当主题仍为\n // 内置 default 时迁移,用户自选的主题色/圆角不受影响。\n version: 2,\n migrate: (persistedState, version) => {\n const state = (persistedState ?? {}) as { preferences?: Preferences };\n if (version < 2) {\n const themePref = state.preferences?.theme;\n if (themePref && themePref.builtinType === 'default') {\n if (\n themePref.colorPrimary === 'hsl(212 100% 45%)' ||\n themePref.colorPrimary === '#3B82F6'\n ) {\n themePref.colorPrimary = 'hsl(212 100% 45%)';\n }\n if (!themePref.radius || themePref.radius === '6') {\n themePref.radius = '8';\n }\n console.info(\n '[Preferences] 迁移 v<2:默认主题对齐设计语言规范(hsl(212 100% 45%) + radius 8)'\n );\n }\n }\n return state as { preferences: Preferences };\n },\n partialize: (state) => ({preferences: state.preferences}),\n }\n )\n);\n","import {useMemo} from 'react';\nimport {usePreferencesStore} from '../store';\nimport type {DeepPartial, Preferences} from '../types';\n\n/**\n * 偏好设置 Hook\n * 提供便捷的偏好设置访问和更新方法\n */\nexport function usePreferences() {\n const {\n preferences,\n setPreferences,\n resetPreferences,\n getPreference,\n } = usePreferencesStore();\n\n // 便捷访问器\n const app = useMemo(() => preferences.app, [preferences.app]);\n const theme = useMemo(() => preferences.theme, [preferences.theme]);\n const shortcutKeys = useMemo(() => preferences.shortcutKeys, [preferences.shortcutKeys]);\n const transition = useMemo(() => preferences.transition, [preferences.transition]);\n const copyright = useMemo(() => preferences.copyright, [preferences.copyright]);\n const navigation = useMemo(() => preferences.navigation, [preferences.navigation]);\n const tabbar = useMemo(() => preferences.tabbar, [preferences.tabbar]);\n const breadcrumb = useMemo(() => preferences.breadcrumb, [preferences.breadcrumb]);\n const sidebar = useMemo(() => preferences.sidebar, [preferences.sidebar]);\n const header = useMemo(() => preferences.header, [preferences.header]);\n const footer = useMemo(() => preferences.footer, [preferences.footer]);\n const logo = useMemo(() => preferences.logo, [preferences.logo]);\n const widget = useMemo(() => preferences.widget, [preferences.widget]);\n\n // 计算属性\n const isDark = useMemo(() => {\n return theme.mode === 'dark' ||\n (theme.mode === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches);\n }, [theme.mode]);\n\n const isMobile = useMemo(() => {\n return app.isMobile;\n }, [app.isMobile]);\n\n // 更新方法\n const updatePreferences = (overrides: DeepPartial<Preferences>) => {\n setPreferences(overrides);\n };\n\n const updateTheme = (themeOverrides: DeepPartial<Preferences['theme']>) => {\n setPreferences({theme: themeOverrides});\n };\n\n const updateApp = (appOverrides: DeepPartial<Preferences['app']>) => {\n setPreferences({app: appOverrides});\n };\n\n // 主题切换\n const toggleTheme = () => {\n const newMode = theme.mode === 'dark' ? 'light' : 'dark';\n updateTheme({mode: newMode});\n };\n\n const setThemeMode = (mode: Preferences['theme']['mode']) => {\n updateTheme({mode});\n };\n\n // 语言切换\n const setLanguage = (locale: Preferences['app']['locale']) => {\n updateApp({locale});\n };\n\n return {\n // 完整偏好设置\n preferences,\n\n // 分组访问\n app,\n theme,\n shortcutKeys,\n transition,\n copyright,\n navigation,\n tabbar,\n breadcrumb,\n sidebar,\n header,\n footer,\n logo,\n widget,\n\n // 计算属性\n isDark,\n isMobile,\n\n // 通用更新方法\n updatePreferences,\n resetPreferences,\n getPreference,\n\n // 便捷更新方法\n updateTheme,\n updateApp,\n toggleTheme,\n setThemeMode,\n setLanguage,\n };\n}\n","import {useCallback, useMemo} from 'react';\n\nimport zhCN from 'antd/locale/zh_CN';\nimport enUS from 'antd/locale/en_US';\nimport type {Locale} from 'antd/es/locale';\nimport {useTranslation} from 'react-i18next';\n\nimport {usePreferences} from './usePreferences';\nimport type {SupportedLanguagesType} from '../types/layout';\n\n\n/**\n * Ant Design 语言包映射\n */\nconst antdLocales: Record<SupportedLanguagesType, Locale> = {\n 'zh-CN': zhCN,\n 'en-US': enUS,\n};\n\n/**\n * 语言切换 Hook\n * 提供便捷的语言切换和管理功能,同时支持 Ant Design 国际化\n */\nexport function useLocale() {\n const {app, updateApp, preferences} = usePreferences();\n\n // 当前语言\n const locale = app.locale;\n\n // 是否为中文\n const isZhCN = locale === 'zh-CN';\n\n // 是否为英文\n const isEnUS = locale === 'en-US';\n\n // Ant Design 语言包\n const antdLocale = useMemo(() => {\n return antdLocales[locale];\n }, [locale]);\n\n // 切换语言\n const setLocale = useCallback((newLocale: SupportedLanguagesType) => {\n updateApp({locale: newLocale});\n\n // 可选:刷新页面以应用语言变更\n // window.location.reload();\n }, [updateApp]);\n\n // 切换到中文\n const setZhCN = useCallback(() => {\n setLocale('zh-CN');\n }, [setLocale]);\n\n // 切换到英文\n const setEnUS = useCallback(() => {\n setLocale('en-US');\n }, [setLocale]);\n\n // 切换语言(在中英文之间切换)\n const toggleLocale = useCallback(() => {\n const newLocale = locale === 'zh-CN' ? 'en-US' : 'zh-CN';\n setLocale(newLocale);\n }, [locale, setLocale]);\n\n const {t} = useTranslation('auth');\n\n // 获取语言显示名称\n const getLocaleName = useCallback((lang: SupportedLanguagesType): string => {\n const names: Record<SupportedLanguagesType, string> = {\n 'zh-CN': t('langZhCN'),\n 'en-US': t('langEnUS'),\n };\n return names[lang];\n }, [t]);\n\n // 当前语言的显示名称\n const localeName = getLocaleName(locale);\n\n // 获取所有支持的语言列表\n const supportedLocales: Array<{\n value: SupportedLanguagesType;\n label: string;\n }> = [\n {value: 'zh-CN', label: t('langZhCN')},\n {value: 'en-US', label: t('langEnUS')},\n ];\n\n return {\n // 当前语言\n locale,\n localeName,\n\n // Ant Design 语言包\n antdLocale,\n\n // 语言判断\n isZhCN,\n isEnUS,\n\n // 切换方法\n setLocale,\n setZhCN,\n setEnUS,\n toggleLocale,\n\n // 辅助方法\n getLocaleName,\n supportedLocales,\n\n // 完整偏好设置(供高级使用)\n preferences,\n };\n}\n","import {useMemo, useState, useEffect} from 'react';\nimport {theme as antdTheme, type ThemeConfig, type MappingAlgorithm} from 'antd';\n\nimport {darkThemeComponents, darkThemeTokens, lightThemeComponents} from '../config';\nimport {SECONDARY_TEXT_DARK, SECONDARY_TEXT_LIGHT} from '../config/constants';\nimport {mixColor} from '../../utils/color';\nimport {usePreferencesStore} from '../store';\n\nexport const useThemeConfig = (): ThemeConfig => {\n const {theme, app} = usePreferencesStore((state) => state.preferences);\n\n // 响应式跟踪系统暗色模式(仅 auto 模式需要)\n const [systemIsDark, setSystemIsDark] = useState(\n () => window.matchMedia('(prefers-color-scheme: dark)').matches,\n );\n\n useEffect(() => {\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n const handler = (e: MediaQueryListEvent) => setSystemIsDark(e.matches);\n mediaQuery.addEventListener('change', handler);\n return () => mediaQuery.removeEventListener('change', handler);\n }, []);\n\n // 计算有效主题:auto 模式下跟随系统偏好\n const effectiveIsDark = useMemo(() => {\n if (theme.mode === 'dark') return true;\n if (theme.mode === 'light') return false;\n if (theme.mode === 'auto') return systemIsDark;\n return false;\n }, [theme.mode, systemIsDark]);\n\n return useMemo((): ThemeConfig => {\n const algorithms: MappingAlgorithm[] = [];\n if (effectiveIsDark) algorithms.push(antdTheme.darkAlgorithm);\n if (app.compact) algorithms.push(antdTheme.compactAlgorithm);\n\n const tokens: ThemeConfig['token'] = {\n colorPrimary: theme.colorPrimary,\n colorSuccess: theme.colorSuccess,\n colorWarning: theme.colorWarning,\n colorError: theme.colorDestructive,\n // 与全局 ThemeProvider 同步:内层 ConfigProvider 会重算 token,\n // 不写这两行则该子树的链接/info 回到 antd 默认 #1677FF(另一支蓝)。\n colorInfo: theme.colorPrimary,\n // 与全局 ThemeProvider 同步(同一套推导,否则内层子树又回到 2.90:1 的暗色主色文字)\n colorLink: effectiveIsDark ? mixColor(theme.colorPrimary, '#ffffff', 0.7) : theme.colorPrimary,\n colorTextDescription: effectiveIsDark ? SECONDARY_TEXT_DARK : SECONDARY_TEXT_LIGHT,\n borderRadius: Number.parseInt(theme.radius) || 6,\n };\n // 与全局 ThemeProvider 保持同一份暗色色阶,避免内层 ConfigProvider 覆盖外层\n if (effectiveIsDark) Object.assign(tokens, darkThemeTokens);\n\n return {\n algorithm: algorithms.length > 0 ? algorithms : antdTheme.defaultAlgorithm,\n token: tokens,\n components: effectiveIsDark ? darkThemeComponents : lightThemeComponents,\n };\n }, [effectiveIsDark, theme, app]);\n};\n","import type {DeepPartial} from \"../types\";\nimport {isPlainObject} from \"./merge\";\n\n/**\n * 深度比较 initial 与 current,产出「与出厂默认的增量」对象(只含被改过的叶子值)。\n * 与 vben 的 diffPreference 同语义:复制偏好设置时导出的是增量 overrides,\n * 不是全量快照(对齐 framework/effects/layouts/widgets/preferences/preferences-drawer.vue 的 handleCopy)。\n */\nexport function diffDeep<T extends object>(initial: T, current: DeepPartial<T>): DeepPartial<T> {\n const result: Record<string, any> = {};\n\n for (const key in current) {\n if (!Object.prototype.hasOwnProperty.call(current, key)) {\n continue;\n }\n const currentValue = (current as Record<string, any>)[key];\n const initialValue = (initial as Record<string, any>)[key];\n\n if (isPlainObject(currentValue) && isPlainObject(initialValue)) {\n const nested = diffDeep(initialValue, currentValue);\n if (Object.keys(nested).length > 0) {\n result[key] = nested;\n }\n } else if (!isEqual(currentValue, initialValue)) {\n result[key] = currentValue;\n }\n }\n\n return result as DeepPartial<T>;\n}\n\n/**\n * 标量与数组按值比较:数组不等整体替换(与 mergeDeep 的覆盖语义对齐,不做逐项合并)。\n */\nfunction isEqual(a: any, b: any): boolean {\n if (a === b) {\n return true;\n }\n if (Array.isArray(a) && Array.isArray(b)) {\n return a.length === b.length && a.every((item, i) => isEqual(item, b[i]));\n }\n // NaN 与 NaN 视为相等,避免误报差异\n return Number.isNaN(a) && Number.isNaN(b);\n}\n","/**\n * Token 定时刷新管理\n * 主动定时刷新 access_token,避免请求时才被动刷新导致延迟\n * 对齐 Vue 版 use-token-refresh.ts\n */\nimport { getAuthGateway } from '../auth-gateway';\nimport { globalSSEClient } from '../transport/sse';\nimport { useAuthStore } from '../stores/auth';\n\n// ==============================\n// 常量\n// ==============================\n\n/** Access Token 刷新间隔(1.5 小时) */\nconst ACCESS_TOKEN_REFRESH_INTERVAL = 90 * 60 * 1000;\n\n/** 在 access token 到期前多久开始刷新 */\nconst SAFETY_BUFFER_MS = 5 * 60 * 1000;\n/** 最小刷新间隔(避免立即重试风暴) */\nconst MIN_INTERVAL_MS = 3 * 1000;\n\n// ==============================\n// 模块级状态(单例)\n// ==============================\n\nlet refreshTimer: ReturnType<typeof setTimeout> | null = null;\n\n/**\n * 当前 SSE 基础地址(由宿主经 connectSSEServer(url) 注入,不含 stream 参数)。\n * 包内不读 import.meta.env——lib 构建会把它静态替换成包构建时的值。\n */\nlet activeSseBaseUrl = '/api/sse';\n\n// ==============================\n// Cookie 读取辅助\n// ==============================\n\n/**\n * 从 refresh_exp cookie 读取 refresh token 的过期时间戳(Unix 秒)。\n * refresh_exp 为非 HttpOnly cookie,仅含过期时间戳,无敏感信息。\n * 由后端 setRefreshCookies 在登录/刷新时写入。\n * 返回毫秒级时间戳或 null(cookie 不存在/已过期)。\n */\nfunction getRefreshExpireAt(): number | null {\n const match = document.cookie\n .split('; ')\n .find((row) => row.startsWith('refresh_exp='));\n if (!match) return null;\n const parts = match.split('=');\n if (parts.length < 2) return null;\n const val = parseInt(parts[1], 10);\n if (!Number.isFinite(val) || val <= 0) return null;\n return val * 1000;\n}\n\n// ==============================\n// 核心:计算下次刷新间隔\n// ==============================\n\nfunction computeNextInterval(): number {\n const { accessTokenExpireAt } = useAuthStore.getState();\n const now = Date.now();\n\n // refresh token 快过期 → 快速重试(使其在过期前尽快刷新)\n const refreshExpireAt = getRefreshExpireAt();\n const refreshRemaining = (refreshExpireAt ?? 0) - now;\n if (refreshExpireAt && refreshRemaining <= SAFETY_BUFFER_MS) {\n return MIN_INTERVAL_MS;\n }\n\n // access token 已过期或没有 → 快速重试\n const accessRemaining = (accessTokenExpireAt ?? 0) - now;\n if (!accessTokenExpireAt || accessRemaining <= 0) {\n return MIN_INTERVAL_MS;\n }\n\n // access token 快过期 → 快速重试\n if (accessRemaining <= SAFETY_BUFFER_MS) {\n return MIN_INTERVAL_MS;\n }\n\n // 正常情况:在过期前提前刷新(80% 剩余时间,不超过默认间隔)\n return Math.floor(\n Math.max(\n MIN_INTERVAL_MS,\n Math.min(ACCESS_TOKEN_REFRESH_INTERVAL, (accessRemaining - SAFETY_BUFFER_MS) * 0.8),\n ),\n );\n}\n\n// ==============================\n// 核心:定时刷新调度\n// ==============================\n\nfunction scheduleRefresh(): void {\n stopRefreshTimer();\n\n const tick = async () => {\n try {\n const now = Date.now();\n\n // refresh token 不存在或已过期 → 强制登出\n // refresh token 过期时间从 refresh_exp cookie 读取\n const refreshExpireAt = getRefreshExpireAt();\n if (!refreshExpireAt) {\n useAuthStore.getState().forceLogout();\n return;\n }\n\n // refresh token 快过期 → 强制登出\n if (refreshExpireAt - now <= SAFETY_BUFFER_MS) {\n useAuthStore.getState().forceLogout();\n return;\n }\n\n // 执行刷新\n const newToken = await useAuthStore.getState().refreshToken();\n\n // token 刷新成功后,使用新 token 重连 SSE\n if (newToken) {\n // react 无刷新后 userInfo 拉取路径(AuthGuard 只查 token),刷新页面后 userInfo 为 null。\n // SSE streamID 已改为 userId,无 userInfo 则无法重连,故在首次 tick 时惰性补拉一次。\n // 运行于此处可保证 queryClient 已就绪(mount 后),与 login 内 fetchUserProfile 同源。\n if (useAuthStore.getState().userInfo === null) {\n try {\n // 经 AuthGateway 惰性补拉用户信息(SSE streamID 为 userId,无 userInfo 无法重连)\n const profile = await getAuthGateway().fetchProfile();\n if (profile) {\n useAuthStore.getState().setUserInfo(profile);\n }\n } catch (e) {\n console.error('[TokenRefresh] lazy fetch user profile failed:', e);\n }\n }\n reconnectSSEServer();\n }\n } catch (error) {\n console.error('[TokenRefresh] 定时刷新失败:', error);\n } finally {\n // 无论成功失败,只要定时器还在,就安排下一次\n if (refreshTimer !== null) {\n refreshTimer = globalThis.setTimeout(tick, computeNextInterval());\n }\n }\n };\n\n refreshTimer = globalThis.setTimeout(tick, computeNextInterval());\n}\n\n// ==============================\n// 公开 API\n// ==============================\n\n/**\n * 启动定时刷新\n * 在登录成功后调用\n */\nexport function startRefreshTimer(): void {\n scheduleRefresh();\n}\n\n/**\n * 停止定时刷新\n * 在登出、token 失效时调用\n */\nexport function stopRefreshTimer(): void {\n if (refreshTimer !== null) {\n globalThis.clearTimeout(refreshTimer);\n refreshTimer = null;\n }\n}\n\n/**\n * 重连 SSE 服务器(使用最新的 access token)\n * 在 token 刷新成功后调用,确保 SSE 连接携带新凭证\n */\nexport function reconnectSSEServer(): void {\n const { accessToken, userInfo } = useAuthStore.getState();\n\n // streamID 已改为 userId,鉴权仍走 Authorization 头。两者缺一不可。\n if (!accessToken || userInfo?.id == null) {\n console.warn('[TokenRefresh] No access token or userId, skip SSE reconnect');\n return;\n }\n\n // 仅在当前已有 SSE 连接时才重连,避免无连接时意外建立连接\n const currentStatus = globalSSEClient.getStatus();\n if (currentStatus === 'disconnected') {\n console.log('[TokenRefresh] SSE is disconnected, skip reconnect');\n return;\n }\n\n const sseUrl = `${activeSseBaseUrl}?stream=${userInfo.id}`;\n globalSSEClient.setHeaders({ Authorization: `Bearer ${accessToken}` });\n globalSSEClient.reconnect(sseUrl);\n console.log('[TokenRefresh] SSE reconnected with new token');\n}\n\n/**\n * 连接 SSE 服务器\n * 使用当前 access token 建立 SSE 连接。\n * @param sseBaseUrl SSE 基础地址(宿主从自身 env 解析后传入,如 `${VITE_SSE_URL ?? '/api/sse'}`),\n * 后续定时刷新后的重连会复用该地址。\n */\nexport function connectSSEServer(sseBaseUrl: string): void {\n const { accessToken, userInfo } = useAuthStore.getState();\n\n activeSseBaseUrl = sseBaseUrl;\n\n // streamID 已改为 userId,鉴权仍走 Authorization 头。两者缺一不可。\n if (!accessToken || userInfo?.id == null) {\n console.warn('[TokenRefresh] No access token or userId, skip SSE connection');\n return;\n }\n\n const sseUrl = `${activeSseBaseUrl}?stream=${userInfo.id}`;\n globalSSEClient.setHeaders({ Authorization: `Bearer ${accessToken}` });\n globalSSEClient.connect(sseUrl);\n}\n\n/**\n * 关闭 SSE 连接\n */\nexport function disconnectSSEServer(): void {\n globalSSEClient.close();\n}\n","import { create, type StoreApi } from 'zustand';\nimport { persist } from 'zustand/middleware';\nimport i18next from 'i18next';\n\nimport { getAuthGateway, type AdminLoginParams, type AdminTokenResult } from '../auth-gateway';\nimport type { AdminUserInfo } from '../types';\nimport { startRefreshTimer, stopRefreshTimer, disconnectSSEServer } from '../hooks/useTokenRefresh';\nimport { queryClient } from '../query-client';\nimport { setCaptchaHeaders } from '../transport/rest';\n\n/**\n * 令牌载荷\n */\nexport interface TokenPayload {\n /**\n * 令牌值\n */\n value: string;\n /**\n * 令牌过期时间\n */\n expiresAt?: number;\n}\n\nexport interface AuthState {\n // Token 状态(仅存内存,不落 localStorage)\n accessToken: string | null;\n accessTokenExpireAt: number | null;\n\n // 用户状态(不持久化,避免脏数据)\n userInfo: AdminUserInfo | null;\n\n // MFA 挑战状态(不持久化):登录密码校验通过但需二次验证时,后端返回 operation_id。\n // 非空表示当前处于 MFA 挑战待验证阶段,前端据此跳挑战页。\n mfaOperationId: string | null;\n\n // UI 状态\n loginLoading: boolean;\n error: string | null;\n\n // 动作\n login: (\n params: AdminLoginParams,\n onSuccess?: () => void,\n captcha?: { id: string; value: string },\n ) => Promise<void>;\n /**\n * 完成 MFA 挑战:用 operation_id + TOTP 码调后端验证,通过则后端返回真 token,\n * 复用登录成功流程(存 token / 拉用户信息 / 跳首页)。失败抛错。\n */\n completeMfaChallenge: (\n totpCode: string,\n onSuccess?: () => void,\n ) => Promise<void>;\n /** 完成 OIDC SSO 登录:code+state 换令牌,复用登录成功流程;失败抛错 */\n completeSsoLogin: (\n code: string,\n state: string,\n onSuccess?: () => void,\n ) => Promise<void>;\n logout: (redirect?: boolean) => Promise<void>;\n refreshToken: () => Promise<string>;\n reauthenticate: () => void;\n /** 强制登出:纯前端清除认证状态 + 跳转登录页,不调后端接口(用于 token 已失效场景) */\n forceLogout: () => void;\n setUserInfo: (info: AdminUserInfo) => void;\n clearError: () => void;\n $reset: () => void;\n}\n\n// ========== 常量 ==========\nconst DEFAULT_ACCESS_EXPIRES_IN = 7200; // 2 小时\n\n// applySuccessfulLogin 处理\"已拿到含真 token 的登录结果\"后的统一流程:\n// 存 token → 拉用户信息 → 启动刷新定时器 → 跳转。\n// 登录成功与 MFA 验证成功都复用此函数。\nasync function applySuccessfulLogin(\n set: StoreApi<AuthState>['setState'],\n response: AdminTokenResult,\n now: number,\n onSuccess?: () => void,\n): Promise<void> {\n // 保存 Token\n const accessTokenPayload: TokenPayload = {\n value: response.access_token || '',\n // expires_in 为 int64(proto3 JSON 下是字符串),参与运算前转回 number\n expiresAt: now + Number(response.expires_in || DEFAULT_ACCESS_EXPIRES_IN) * 1000,\n };\n\n set({\n accessToken: accessTokenPayload.value,\n accessTokenExpireAt: accessTokenPayload.expiresAt,\n });\n\n // refresh token 通过 HttpOnly Cookie 传输,前端不可读也不存内存。\n // 页面刷新后由 bootstrap 静默恢复(凭 cookie 调 /refresh-token 换新 access token)。\n\n // 获取用户信息(交给 React Query 处理缓存,这里只更新 Zustand)\n const userInfo = await getAuthGateway().fetchProfile();\n set({ userInfo });\n\n // 启动定时刷新 token\n startRefreshTimer();\n\n // 执行成功回调或跳转\n if (onSuccess) {\n onSuccess();\n } else if (userInfo?.homePath) {\n // 校验 homePath 必须为同源相对路径,防止服务端返回值导致开放重定向\n const rawHomePath = userInfo.homePath;\n if (\n typeof rawHomePath === 'string' &&\n rawHomePath.startsWith('/') &&\n !rawHomePath.startsWith('//')\n ) {\n window.location.href = rawHomePath;\n }\n }\n}\n\n// ========== Store 实现 ==========\nexport const useAuthStore = create<AuthState>()(\n persist(\n (set, get) => ({\n // 初始状态\n accessToken: null,\n accessTokenExpireAt: null,\n userInfo: null,\n mfaOperationId: null,\n loginLoading: false,\n error: null,\n\n // 登录\n login: async (params, onSuccess, captcha) => {\n set({ loginLoading: true, error: null });\n\n try {\n // 1. 调用登录接口(若有验证码,先设置一次性 Header,由 transport 消费)\n if (captcha) {\n setCaptchaHeaders(captcha.id, captcha.value);\n }\n const gateway = getAuthGateway();\n const response = await gateway.login({\n ...params,\n password: params.password ? await gateway.encryptPassword(params.password) : undefined,\n });\n\n // ===== MFA 闸门:后端在密码校验通过、需二次验证时返回 mfa_operation_id,=====\n // ===== access_token 为空。此时不写任何 token,跳转 MFA 挑战页。\n if (response.mfa_operation_id) {\n set({ mfaOperationId: response.mfa_operation_id });\n // 跳 MFA 挑战页(路由守卫亦会据此强制跳转)\n if (onSuccess) {\n onSuccess();\n }\n return;\n }\n\n const now = Date.now();\n await applySuccessfulLogin(set, response, now, onSuccess);\n } catch (err: any) {\n const errorMsg = err?.message || i18next.t('auth:loginFailed');\n // 登录失败时清除任何可能被部分写入的认证状态(含空 token / 未来 expiry / MFA 状态),\n // 避免脏数据残留导致后续鉴权误判。\n set({\n error: errorMsg,\n accessToken: null,\n accessTokenExpireAt: null,\n mfaOperationId: null,\n });\n throw err;\n } finally {\n set({ loginLoading: false });\n }\n },\n\n // 完成 MFA 挑战\n completeMfaChallenge: async (totpCode, onSuccess) => {\n set({ loginLoading: true, error: null });\n try {\n const opId = get().mfaOperationId;\n if (!opId) {\n throw new Error(i18next.t('auth:mfaNotRequired'));\n }\n // 调 MFA 验证接口;后端校验通过后返回含真 token 的登录结果。\n const response = await getAuthGateway().verifyMfa({\n operationId: opId,\n totpCode: totpCode,\n });\n\n // MFA 验证成功后清掉挑战态,复用登录成功流程存 token / 拉用户信息 / 跳转。\n set({ mfaOperationId: null });\n await applySuccessfulLogin(set, response, Date.now(), onSuccess);\n } catch (err: any) {\n const errorMsg = err?.message || i18next.t('auth:mfaVerifyFailed');\n set({ error: errorMsg, mfaOperationId: null });\n throw err;\n } finally {\n set({ loginLoading: false });\n }\n },\n\n // 完成 OIDC SSO 登录:后端已用 code+state 验证并返回含真 token 的登录结果,\n // 复用登录成功流程(存 token / 拉用户信息 / 跳转)。\n completeSsoLogin: async (code, state, onSuccess) => {\n set({ loginLoading: true, error: null });\n try {\n const response = await getAuthGateway().ssoLogin({ code, state });\n await applySuccessfulLogin(set, response, Date.now(), onSuccess);\n } catch (err: any) {\n set({ error: err?.message || i18next.t('auth:ssoLoginFailed'), loginLoading: false });\n throw err;\n }\n },\n\n\n // 登出(主动,调后端接口)\n // 清除状态后由 React 组件响应状态变化自然重定向\n logout: async (_redirect = true) => {\n stopRefreshTimer();\n disconnectSSEServer();\n try {\n // 服务端登出失败不影响本地清理,但必须留痕(token 失效/网络/权限问题的线索)\n await getAuthGateway().logout().catch((error) => {\n console.error('服务端登出请求失败(已继续本地清理):', error);\n });\n } finally {\n // 清除 queryClient 缓存,防止登出期间被缓存污染的查询结果\n // (如 getMe 因 401 返回 null 被 fetchQuery 缓存)导致重新登录时命中脏数据\n queryClient.clear();\n\n // 清除 localStorage 中的持久化数据\n localStorage.removeItem('auth-storage');\n localStorage.removeItem('user-storage');\n\n // 清除内存中的状态\n set({\n accessToken: null,\n accessTokenExpireAt: null,\n userInfo: null,\n error: null,\n loginLoading: false,\n });\n }\n },\n\n // 刷新 Token\n // refresh token 以 HttpOnly Cookie 传输,刷新请求由浏览器自动携带 cookie,\n // 前端无需(也无法)读取 refresh token 值。\n refreshToken: async () => {\n try {\n const response = await getAuthGateway().refresh();\n\n const now = Date.now();\n set({\n accessToken: response.access_token,\n accessTokenExpireAt: now + Number(response.expires_in || DEFAULT_ACCESS_EXPIRES_IN) * 1000,\n });\n\n return response.access_token || '';\n } catch (err) {\n console.error('Refresh token failed:', err);\n get().forceLogout();\n return '';\n }\n },\n\n // 重认证(兜底)\n reauthenticate: () => {\n console.warn('Token invalid, please re-login');\n set({ error: i18next.t('auth:sessionExpired') });\n },\n\n // 强制登出:纯前端操作,不调后端接口\n // 用于 token 已失效(401)场景,避免调 logout API 又触发 401 死循环\n // 只清除状态,不做页面跳转(让 React 组件响应状态变化自然重定向)\n forceLogout: () => {\n stopRefreshTimer();\n disconnectSSEServer();\n console.warn('Force logout: clearing auth state');\n // 清除 queryClient 缓存,防止缓存污染导致重新登录失败\n queryClient.clear();\n localStorage.removeItem('auth-storage');\n localStorage.removeItem('user-storage');\n set({\n accessToken: null,\n accessTokenExpireAt: null,\n userInfo: null,\n mfaOperationId: null,\n error: null,\n loginLoading: false,\n });\n },\n\n // 设置用户信息\n setUserInfo: (info) => set({ userInfo: info }),\n\n // 清除错误\n clearError: () => set({ error: null }),\n\n // 重置(用于测试/登出)\n $reset: () =>\n set({\n accessToken: null,\n accessTokenExpireAt: null,\n userInfo: null,\n mfaOperationId: null,\n loginLoading: false,\n error: null,\n }),\n }),\n {\n name: 'auth-storage', // localStorage key\n // Token(access/refresh 及其过期时间)全部仅存内存,不落 localStorage。\n // 刷新页面后 token 丢失 → AuthGuard 看到 accessToken 为 null → 跳登录页。\n // 登出时仍 localStorage.removeItem('auth-storage') 清理历史残留数据。\n partialize: () => ({}),\n },\n ),\n);\n","import {create} from 'zustand';\nimport {persist} from 'zustand/middleware';\n\nimport type {AdminUserBasic} from '../types';\n\n// ========== 状态定义 ==========\nexport interface UserState {\n // 核心状态\n userInfo: AdminUserBasic | null;\n userRoles: string[]; // 角色码(来自 userInfo.roles)\n accessCodes: string[]; // 权限码(来自 GetMyPermissionCode)\n hiddenFields: string[]; // 字段权限隐藏字段集(\"资源.字段\" 串,来自 GetMyPermissionCode)\n\n // 计算属性(函数形式,React 中不需要 computed)\n tenantId: string | null; // 租户id(uint64,proto3 JSON 下为字符串)\n isLoggedIn: boolean;\n\n // 动作\n setUserInfo: (info: AdminUserBasic | null) => void;\n setUserRoles: (roles: string[]) => void;\n setAccessCodes: (codes: string[]) => void;\n setHiddenFields: (fields: string[]) => void;\n isTenantUser: () => boolean;\n $reset: () => void;\n}\n\n// ========== Store 实现 ==========\nexport const useUserStore = create<UserState>()(\n persist(\n (set, get) => ({\n // 初始状态\n userInfo: null,\n userRoles: [],\n accessCodes: [],\n hiddenFields: [],\n\n // 计算属性(函数形式,每次调用时计算)\n get tenantId() {\n return get().userInfo?.tenantId ?? null;\n },\n\n get isLoggedIn() {\n return get().userInfo !== null;\n },\n\n // 设置用户信息(同时更新角色)\n setUserInfo: (info) => {\n set({\n userInfo: info,\n userRoles: info?.roles ?? [],\n });\n },\n\n // 单独设置角色\n setUserRoles: (roles) => {\n set({userRoles: roles});\n },\n\n // 单独设置权限码\n setAccessCodes: (codes) => {\n set({accessCodes: codes});\n },\n\n // 单独设置字段权限隐藏字段集\n setHiddenFields: (fields) => {\n set({hiddenFields: fields});\n },\n\n // 判断是否为租户用户(tenantId 为字符串 id,非 '0' 且非空即视为租户用户)\n isTenantUser: () => {\n const {tenantId} = get();\n return tenantId !== null && tenantId !== undefined && tenantId !== '' && tenantId !== '0';\n },\n\n // 重置状态\n $reset: () => {\n set({\n userInfo: null,\n userRoles: [],\n accessCodes: [],\n hiddenFields: [],\n });\n },\n }),\n {\n name: 'user-storage', // localStorage key\n // ✅ 可选:只持久化用户信息(角色可从用户信息派生)\n partialize: (state) => ({\n userInfo: state.userInfo,\n // userRoles 可从 userInfo?.roles 派生,不持久化避免不一致\n }),\n }\n )\n);\n","import { flushSync } from 'react-dom';\n\nexport interface ThemeTransitionOrigin {\n x: number;\n y: number;\n}\n\nexport interface ThemeTransitionOptions {\n /** 执行状态更新的函数,内部会以 flushSync 包裹以保证过渡快照时机 */\n update: () => void;\n /** 动效圆心(通常是点击点);缺省时取视口中心 */\n origin?: ThemeTransitionOrigin;\n}\n\n/**\n * 主题切换的 View Transitions 圆形扩散动效(ele/ThemeSwitch 风格):\n * 始终让新画面自圆心扩张覆盖旧画面。切暗时暗色从点击点立即绽开,\n * 不存在\"旧画面收缩\"方案里点击点附近久久残留亮色的问题。\n * 浏览器不支持或用户偏好减少动效(prefers-reduced-motion)时退化为直接切换。\n */\nexport function startThemeViewTransition({\n update,\n origin,\n}: ThemeTransitionOptions): void {\n if (!document.startViewTransition || window.matchMedia('(prefers-reduced-motion: reduce)').matches) {\n update();\n return;\n }\n\n const x = origin?.x ?? window.innerWidth / 2;\n const y = origin?.y ?? window.innerHeight / 2;\n const endRadius = Math.hypot(\n Math.max(x, window.innerWidth - x),\n Math.max(y, window.innerHeight - y),\n );\n const clipPath = [\n `circle(0px at ${x}px ${y}px)`,\n `circle(${endRadius}px at ${x}px ${y}px)`,\n ];\n\n const transition = document.startViewTransition(() => {\n // React 状态更新走异步批处理,flushSync 保证回调返回前 DOM 与\n // useLayoutEffect 副作用(data-theme/根背景色)都已落地,否则过渡\n // 新快照会拍到旧画面。update 阶段渲染循环暂停(rAF 不触发),\n // 回调必须同步完成,不能等帧。\n flushSync(update);\n });\n\n transition.ready\n .then(() => {\n document.documentElement.animate(\n {\n clipPath,\n },\n {\n duration: 450,\n easing: 'ease-out',\n // 必须 forwards:不填充的话动画一结束 clip 立即回退为无裁剪,\n // 而过渡伪元素树要到 finished 后才拆除,中间多出的帧会裸绘\n // 底层旧快照造成闪屏。新画面本就在顶层,此处兜底语义不变。\n fill: 'forwards',\n pseudoElement: '::view-transition-new(root)',\n },\n );\n })\n .catch(() => {\n // best-effort:ready 失败(如过渡被浏览器跳过)只影响动效,主题切换本身已生效\n });\n transition.finished.catch(() => {\n // best-effort:finished 的拒绝仅代表过渡异常中止,无可恢复动作\n });\n}\n","import React from 'react';\nimport { Segmented, Switch } from 'antd';\nimport { BulbOutlined, LaptopOutlined, MoonOutlined, BgColorsOutlined } from '@ant-design/icons';\nimport { useTranslation } from 'react-i18next';\nimport { usePreferencesStore } from '../../store';\nimport { startThemeViewTransition } from '../../theme-transition';\nimport type { BuiltinThemeType, ThemeModeType } from '../../types';\nimport './AppearancePanel.style.less';\n\n/** 内置主题色配置 */\nconst BUILTIN_THEMES: { nameKey: string; color: string; type: BuiltinThemeType }[] = [\n { nameKey: 'appearance.themes.default', color: '#006BE6', type: 'default' },\n { nameKey: 'appearance.themes.violet', color: '#722ed1', type: 'violet' },\n { nameKey: 'appearance.themes.pink', color: '#eb2f96', type: 'pink' },\n { nameKey: 'appearance.themes.yellow', color: '#fadb14', type: 'yellow' },\n { nameKey: 'appearance.themes.skyBlue', color: '#52c41a', type: 'sky-blue' },\n { nameKey: 'appearance.themes.green', color: '#13c2c2', type: 'green' },\n { nameKey: 'appearance.themes.zinc', color: '#595959', type: 'zinc' },\n { nameKey: 'appearance.themes.deepGreen', color: '#13a8a8', type: 'deep-green' },\n { nameKey: 'appearance.themes.deepBlue', color: '#3B82F6', type: 'deep-blue' },\n { nameKey: 'appearance.themes.orange', color: '#fa8c16', type: 'orange' },\n { nameKey: 'appearance.themes.rose', color: '#f5222d', type: 'rose' },\n { nameKey: 'appearance.themes.neutral', color: '#595959', type: 'neutral' },\n { nameKey: 'appearance.themes.slate', color: '#54687a', type: 'slate' },\n { nameKey: 'appearance.themes.gray', color: '#595959', type: 'gray' },\n { nameKey: 'appearance.themes.custom', color: 'custom', type: 'custom' },\n];\n\n/** 圆角选项 */\nconst RADIUS_OPTIONS = [\n { label: '0', value: '0' },\n { label: '0.25', value: '0.25' },\n { label: '0.5', value: '0.5' },\n { label: '0.75', value: '0.75' },\n { label: '1', value: '1' },\n];\n\nexport const AppearancePanel: React.FC = () => {\n const { preferences, setPreferences } = usePreferencesStore();\n const { t } = useTranslation('preferences');\n\n // 主题模式切换(light/dark 带圆形扩散动效;auto 跟随系统无确定方向,直接切换)\n const handleThemeModeChange = (mode: ThemeModeType, event?: React.MouseEvent<HTMLElement>) => {\n if (mode === 'auto') {\n setPreferences({ theme: { mode } });\n return;\n }\n startThemeViewTransition({\n origin: event ? { x: event.clientX, y: event.clientY } : undefined,\n update: () => setPreferences({ theme: { mode } }),\n });\n };\n\n // 内置主题切换\n const handleBuiltinThemeChange = (type: BuiltinThemeType) => {\n const theme = BUILTIN_THEMES.find((t) => t.type === type);\n if (theme) {\n setPreferences({ theme: { builtinType: type, colorPrimary: theme.color } });\n }\n };\n\n // 圆角切换\n const handleRadiusChange = (radius: string) => {\n setPreferences({ theme: { radius } });\n };\n\n return (\n <div className=\"appearance-panel\">\n {/* 主题模式 */}\n <section className=\"appearance-section\">\n <h3 className=\"section-title\">{t('appearance.theme')}</h3>\n <div className=\"theme-mode-options\">\n <div className=\"theme-mode-wrapper\" onClick={(e) => handleThemeModeChange('light', e)}>\n <div\n className={`theme-mode-item ${preferences.theme.mode === 'light' ? 'active' : ''}`}\n >\n <BulbOutlined className=\"theme-icon\" />\n </div>\n <span className=\"theme-mode-label\">{t('appearance.lightMode')}</span>\n </div>\n <div className=\"theme-mode-wrapper\" onClick={(e) => handleThemeModeChange('dark', e)}>\n <div className={`theme-mode-item ${preferences.theme.mode === 'dark' ? 'active' : ''}`}>\n <MoonOutlined className=\"theme-icon\" />\n </div>\n <span className=\"theme-mode-label\">{t('appearance.darkMode')}</span>\n </div>\n <div className=\"theme-mode-wrapper\" onClick={() => handleThemeModeChange('auto')}>\n <div className={`theme-mode-item ${preferences.theme.mode === 'auto' ? 'active' : ''}`}>\n <LaptopOutlined className=\"theme-icon\" />\n </div>\n <span className=\"theme-mode-label\">{t('appearance.followSystem')}</span>\n </div>\n </div>\n </section>\n\n {/* 深色侧边栏和顶栏 */}\n <section className=\"appearance-section\">\n <div className=\"preference-item\">\n <span>{t('appearance.darkSidebar')}</span>\n <Switch\n checked={preferences.theme.semiDarkSidebar}\n onChange={(checked) => setPreferences({ theme: { semiDarkSidebar: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('appearance.darkHeader')}</span>\n <Switch\n checked={preferences.theme.semiDarkHeader}\n onChange={(checked) => setPreferences({ theme: { semiDarkHeader: checked } })}\n />\n </div>\n </section>\n\n {/* 内置主题 */}\n <section className=\"appearance-section\">\n <h3 className=\"section-title\">{t('appearance.builtinThemes')}</h3>\n <div className=\"builtin-themes-grid\">\n {BUILTIN_THEMES.map((theme) => (\n <div\n key={theme.type}\n className=\"builtin-theme-wrapper\"\n onClick={() => handleBuiltinThemeChange(theme.type)}\n >\n <div\n className={`builtin-theme-item ${preferences.theme.builtinType === theme.type ? 'active' : ''}`}\n >\n <div\n className=\"theme-color-preview\"\n style={{\n backgroundColor: theme.type === 'custom' ? 'transparent' : theme.color,\n border: theme.type === 'custom' ? '1px dashed #666' : 'none',\n }}\n >\n {theme.type === 'custom' && <BgColorsOutlined className=\"custom-icon\" />}\n </div>\n </div>\n <span className=\"theme-name\">{t(theme.nameKey)}</span>\n </div>\n ))}\n </div>\n </section>\n\n {/* 圆角 */}\n <section className=\"appearance-section\">\n <h3 className=\"section-title\">{t('appearance.radius')}</h3>\n <Segmented\n options={RADIUS_OPTIONS}\n value={preferences.theme.radius}\n onChange={(value) => handleRadiusChange(value as string)}\n className=\"radius-segmented\"\n block\n />\n </section>\n\n {/* 其它 */}\n <section className=\"appearance-section\">\n <h3 className=\"section-title\">{t('appearance.other')}</h3>\n <div className=\"preference-item\">\n <span>{t('appearance.colorWeakMode')}</span>\n <Switch\n checked={preferences.app.colorWeakMode}\n onChange={(checked) => setPreferences({ app: { colorWeakMode: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('appearance.colorGrayMode')}</span>\n <Switch\n checked={preferences.app.colorGrayMode}\n onChange={(checked) => setPreferences({ app: { colorGrayMode: checked } })}\n />\n </div>\n </section>\n </div>\n );\n};\n","import React from 'react';\nimport { Button, Input, InputNumber, Segmented, Select, Space, Switch } from 'antd';\nimport { MinusOutlined, PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons';\nimport { useTranslation } from 'react-i18next';\nimport { usePreferencesStore } from '../../store';\nimport type { ContentCompactType, LayoutType } from '../../types';\nimport './LayoutPanel.style.less';\n\n/** 布局选项 */\nconst LAYOUT_OPTIONS = [\n {\n label: 'layout.modes.sidebarNav',\n value: 'sidebar-nav',\n icon: '📋',\n },\n {\n label: 'layout.modes.sidebarMixedNav',\n value: 'sidebar-mixed-nav',\n icon: '📑',\n },\n {\n label: 'layout.modes.headerNav',\n value: 'header-nav',\n icon: '',\n },\n {\n label: 'layout.modes.mixedNav',\n value: 'mixed-nav',\n icon: '📊',\n },\n {\n label: 'layout.modes.fullContent',\n value: 'full-content',\n icon: '📱',\n },\n];\n\n/** 内容宽度选项 */\nconst CONTENT_COMPACT_OPTIONS = [\n { label: 'layout.contentModes.wide', value: 'wide' },\n { label: 'layout.contentModes.compact', value: 'compact' },\n];\n\nexport const LayoutPanel: React.FC = () => {\n const { preferences, setPreferences } = usePreferencesStore();\n const { t } = useTranslation('preferences');\n\n const handleLayoutChange = (layout: LayoutType) => {\n setPreferences({ app: { layout } });\n };\n\n const handleContentCompactChange = (compact: ContentCompactType) => {\n setPreferences({ app: { contentCompact: compact } });\n };\n\n const handleSidebarWidthChange = (width: number | null) => {\n if (width && width >= 180 && width <= 320) {\n setPreferences({ sidebar: { width } });\n }\n };\n\n return (\n <div className=\"layout-panel\">\n {/* 布局选择 */}\n <section className=\"layout-section\">\n <h3 className=\"section-title\">{t('layout.layout')}</h3>\n <div className=\"layout-grid\">\n {LAYOUT_OPTIONS.map((option) => (\n <div\n key={option.value}\n className=\"layout-wrapper\"\n onClick={() => handleLayoutChange(option.value as LayoutType)}\n >\n <div\n className={`layout-item ${preferences.app.layout === option.value ? 'active' : ''}`}\n >\n <div className=\"layout-preview\">\n {/* 渲染真实的布局示意图 */}\n {option.value === 'sidebar-nav' && (\n <div className=\"layout-visual sidebar-nav\">\n <div className=\"sidebar\" />\n <div className=\"main-content\">\n <div className=\"header-bar gray\" />\n <div className=\"content-area\">\n <div className=\"block-row\">\n <div className=\"block\" />\n <div className=\"block\" />\n </div>\n <div className=\"block\" />\n </div>\n </div>\n </div>\n )}\n {option.value === 'sidebar-mixed-nav' && (\n <div className=\"layout-visual sidebar-mixed-nav\">\n <div className=\"sidebar thin primary\" />\n <div className=\"sidebar thin gray\" />\n <div className=\"main-content\">\n <div className=\"header-bar gray\" />\n <div className=\"content-area\">\n <div className=\"block-row\">\n <div className=\"block\" />\n <div className=\"block\" />\n </div>\n <div className=\"block\" />\n </div>\n </div>\n </div>\n )}\n {option.value === 'header-nav' && (\n <div className=\"layout-visual header-nav\">\n <div className=\"content-wrapper\">\n <div className=\"header-bar full-width\">\n <div className=\"menu-item\" />\n <div className=\"menu-item\" />\n <div className=\"menu-item\" />\n <div className=\"menu-item\" />\n <div className=\"menu-item\" />\n </div>\n <div className=\"content-area\">\n <div className=\"block-row\">\n <div className=\"block\" />\n <div className=\"block\" />\n </div>\n <div className=\"block\" />\n </div>\n </div>\n </div>\n )}\n {option.value === 'mixed-nav' && (\n <div className=\"layout-visual mixed-nav\">\n <div className=\"content-wrapper\">\n <div className=\"header-bar full-width\">\n <div className=\"menu-item\" />\n <div className=\"menu-item\" />\n <div className=\"menu-item\" />\n </div>\n <div className=\"main-content\">\n <div className=\"sidebar thin gray\" />\n <div className=\"content-area\">\n <div className=\"block-row\">\n <div className=\"block\" />\n <div className=\"block\" />\n </div>\n <div className=\"block\" />\n </div>\n </div>\n </div>\n </div>\n )}\n {option.value === 'full-content' && (\n <div className=\"layout-visual full-content\">\n <div className=\"content-area full-width\">\n <div className=\"block-row\">\n <div className=\"block\" />\n <div className=\"block\" />\n </div>\n <div className=\"block\" />\n </div>\n </div>\n )}\n </div>\n </div>\n <div className=\"layout-label\">\n <span>{t(option.label)}</span>\n <QuestionCircleOutlined className=\"help-icon\" />\n </div>\n </div>\n ))}\n </div>\n </section>\n\n {/* 内容宽度 */}\n <section className=\"layout-section\">\n <h3 className=\"section-title\">{t('layout.content')}</h3>\n <div className=\"content-compact-grid\">\n {CONTENT_COMPACT_OPTIONS.map((option) => (\n <div\n key={option.value}\n className=\"content-compact-wrapper\"\n onClick={() => handleContentCompactChange(option.value as ContentCompactType)}\n >\n <div\n className={`content-compact-item ${preferences.app.contentCompact === option.value ? 'active' : ''}`}\n >\n <div className=\"content-preview\">\n <div className={`preview-bar ${option.value === 'compact' ? 'narrow' : 'wide'}`} />\n </div>\n </div>\n <span>{t(option.label)}</span>\n </div>\n ))}\n </div>\n </section>\n\n {/* 侧边栏设置 */}\n <section className=\"layout-section\">\n <h3 className=\"section-title\">{t('layout.sidebar')}</h3>\n <div className=\"preference-item\">\n <span>{t('layout.showSidebar')}</span>\n <Switch\n checked={preferences.sidebar.enable}\n onChange={(checked) => setPreferences({ sidebar: { enable: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.collapseMenu')}</span>\n <Switch\n checked={preferences.sidebar.collapsed}\n onChange={(checked) => setPreferences({ sidebar: { collapsed: checked } })}\n />\n </div>\n <div className={`preference-item ${!preferences.sidebar.collapsed ? 'disabled' : ''}`}>\n <span>{t('layout.collapseShowTitle')}</span>\n <Switch\n disabled={!preferences.sidebar.collapsed}\n checked={preferences.sidebar.collapsedShowTitle}\n onChange={(checked) => setPreferences({ sidebar: { collapsedShowTitle: checked } })}\n />\n </div>\n <div className=\"preference-item width-control\">\n <span>{t('layout.width')}</span>\n <Space size={4}>\n <Button\n size=\"small\"\n icon={<MinusOutlined />}\n onClick={() => handleSidebarWidthChange(preferences.sidebar.width - 8)}\n disabled={preferences.sidebar.width <= 180}\n />\n <InputNumber\n min={180}\n max={320}\n value={preferences.sidebar.width}\n onChange={handleSidebarWidthChange}\n style={{ width: 70 }} // 从80压缩到70\n />\n <Button\n size=\"small\"\n icon={<PlusOutlined />}\n onClick={() => handleSidebarWidthChange(preferences.sidebar.width + 8)}\n disabled={preferences.sidebar.width >= 320}\n />\n </Space>\n </div>\n </section>\n\n {/* 顶栏设置 */}\n <section className=\"layout-section\">\n <h3 className=\"section-title\">{t('layout.header')}</h3>\n <div className=\"preference-item\">\n <span>{t('layout.showHeader')}</span>\n <Switch\n checked={preferences.header.enable}\n onChange={(checked) => setPreferences({ header: { enable: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.mode')}</span>\n <Segmented\n options={[\n { label: t('layout.headerModes.fixed'), value: 'fixed' },\n { label: t('layout.headerModes.auto'), value: 'auto' },\n { label: t('layout.headerModes.static'), value: 'static' },\n ]}\n value={preferences.header.mode}\n onChange={(value) => setPreferences({ header: { mode: value as any } })}\n />\n </div>\n </section>\n\n {/* 导航菜单 */}\n <section className=\"layout-section\">\n <h3 className=\"section-title\">{t('layout.navigationMenu')}</h3>\n <div className=\"preference-item\">\n <span>{t('layout.menuStyle')}</span>\n <Segmented\n options={[\n { label: t('layout.menuStyles.rounded'), value: 'rounded' },\n { label: t('layout.menuStyles.plain'), value: 'plain' },\n ]}\n value={preferences.navigation.styleType}\n onChange={(value) => setPreferences({ navigation: { styleType: value as any } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.menuSplit')}</span>\n <Switch\n checked={preferences.navigation.split}\n onChange={(checked) => setPreferences({ navigation: { split: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.accordionMode')}</span>\n <Switch\n checked={preferences.navigation.accordion}\n onChange={(checked) => setPreferences({ navigation: { accordion: checked } })}\n />\n </div>\n </section>\n\n {/* 面包屑导航 */}\n <section className=\"layout-section\">\n <h3 className=\"section-title\">{t('layout.breadcrumb')}</h3>\n <div className=\"preference-item\">\n <span>{t('layout.enableBreadcrumb')}</span>\n <Switch\n checked={preferences.breadcrumb.enable}\n onChange={(checked) => setPreferences({ breadcrumb: { enable: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.hideWhenOnlyOne')}</span>\n <Switch\n checked={preferences.breadcrumb.hideOnlyOne}\n onChange={(checked) => setPreferences({ breadcrumb: { hideOnlyOne: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.showIcon')}</span>\n <Switch\n checked={preferences.breadcrumb.showIcon}\n onChange={(checked) => setPreferences({ breadcrumb: { showIcon: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.showHomeButton')}</span>\n <Switch\n checked={preferences.breadcrumb.showHome}\n onChange={(checked) => setPreferences({ breadcrumb: { showHome: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.breadcrumbStyle')}</span>\n <Segmented\n options={[\n { label: t('layout.breadcrumbStyles.normal'), value: 'normal' },\n { label: t('layout.breadcrumbStyles.background'), value: 'background' },\n ]}\n value={preferences.breadcrumb.styleType}\n onChange={(value) => setPreferences({ breadcrumb: { styleType: value as any } })}\n />\n </div>\n </section>\n\n {/* 标签栏 */}\n <section className=\"layout-section\">\n <h3 className=\"section-title\">{t('layout.tabbar')}</h3>\n <div className=\"preference-item\">\n <span>{t('layout.enableTabbar')}</span>\n <Switch\n checked={preferences.tabbar.enable}\n onChange={(checked) => setPreferences({ tabbar: { enable: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.persistTabs')}</span>\n <Switch\n checked={preferences.tabbar.persist}\n onChange={(checked) => setPreferences({ tabbar: { persist: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.draggableSort')}</span>\n <Switch\n checked={preferences.tabbar.draggable}\n onChange={(checked) => setPreferences({ tabbar: { draggable: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.showTabbarIcon')}</span>\n <Switch\n checked={preferences.tabbar.showIcon}\n onChange={(checked) => setPreferences({ tabbar: { showIcon: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.showMoreButton')}</span>\n <Switch\n checked={preferences.tabbar.showMore}\n onChange={(checked) => setPreferences({ tabbar: { showMore: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.showMaximizeButton')}</span>\n <Switch\n checked={preferences.tabbar.showMaximize}\n onChange={(checked) => setPreferences({ tabbar: { showMaximize: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.tabbarStyle')}</span>\n <Select\n style={{ width: 120 }}\n options={[\n { label: t('layout.tabbarStyles.chrome'), value: 'chrome' },\n { label: t('layout.tabbarStyles.card'), value: 'card' },\n { label: t('layout.tabbarStyles.brisk'), value: 'brisk' },\n { label: t('layout.tabbarStyles.plain'), value: 'plain' },\n ]}\n value={preferences.tabbar.styleType}\n onChange={(value) => setPreferences({ tabbar: { styleType: value as any } })}\n />\n </div>\n </section>\n\n {/* 小部件 */}\n <section className=\"layout-section\">\n <h3 className=\"section-title\">{t('layout.widgets')}</h3>\n <div className=\"preference-item\">\n <span>{t('layout.globalSearch')}</span>\n <Switch\n checked={preferences.widget.globalSearch}\n onChange={(checked) => setPreferences({ widget: { globalSearch: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.themeToggle')}</span>\n <Switch\n checked={preferences.widget.themeToggle}\n onChange={(checked) => setPreferences({ widget: { themeToggle: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.languageToggle')}</span>\n <Switch\n checked={preferences.widget.languageToggle}\n onChange={(checked) => setPreferences({ widget: { languageToggle: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.fullscreen')}</span>\n <Switch\n checked={preferences.widget.fullscreen}\n onChange={(checked) => setPreferences({ widget: { fullscreen: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.notification')}</span>\n <Switch\n checked={preferences.widget.notification}\n onChange={(checked) => setPreferences({ widget: { notification: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.lockScreen')}</span>\n <Switch\n checked={preferences.widget.lockScreen}\n onChange={(checked) => setPreferences({ widget: { lockScreen: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.sidebarToggle')}</span>\n <Switch\n checked={preferences.widget.sidebarToggle}\n onChange={(checked) => setPreferences({ widget: { sidebarToggle: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.refresh')}</span>\n <Switch\n checked={preferences.widget.refresh}\n onChange={(checked) => setPreferences({ widget: { refresh: checked } })}\n />\n </div>\n <div className=\"preference-item\">\n <span>{t('layout.preferencesPosition')}</span>\n <Select\n style={{ width: 120 }}\n options={[\n { label: t('layout.positions.auto'), value: 'auto' },\n { label: t('layout.positions.fixed'), value: 'fixed' },\n { label: t('layout.positions.hidden'), value: 'hidden' },\n ]}\n value={preferences.app.preferencesButtonPosition}\n onChange={(value) =>\n setPreferences({ app: { preferencesButtonPosition: value as any } })\n }\n />\n </div>\n </section>\n\n {/* 底栏 */}\n <section className=\"layout-section\">\n <h3 className=\"section-title\">{t('layout.footer')}</h3>\n <div className=\"preference-item\">\n <span>{t('layout.showFooter')}</span>\n <Switch\n checked={preferences.footer.enable}\n onChange={(checked) => setPreferences({ footer: { enable: checked } })}\n />\n </div>\n <div className={`preference-item ${!preferences.footer.enable ? 'disabled' : ''}`}>\n <span>{t('layout.fixedAtBottom')}</span>\n <Switch\n disabled={!preferences.footer.enable}\n checked={preferences.footer.fixed}\n onChange={(checked) => setPreferences({ footer: { fixed: checked } })}\n />\n </div>\n </section>\n\n {/* 版权 */}\n <section className=\"layout-section\">\n <h3 className=\"section-title\">{t('layout.copyright')}</h3>\n <div className=\"preference-item\">\n <span>{t('layout.enableCopyright')}</span>\n <Switch\n checked={preferences.copyright.enable}\n onChange={(checked) => setPreferences({ copyright: { enable: checked } })}\n />\n </div>\n <div className={`preference-item ${!preferences.copyright.enable ? 'disabled' : ''}`}>\n <span>{t('layout.companyName')}</span>\n <Input\n style={{ width: 200 }}\n value={preferences.copyright.companyName}\n onChange={(e) => setPreferences({ copyright: { companyName: e.target.value } })}\n disabled={!preferences.copyright.enable}\n />\n </div>\n <div className={`preference-item ${!preferences.copyright.enable ? 'disabled' : ''}`}>\n <span>{t('layout.companySite')}</span>\n <Input\n style={{ width: 200 }}\n value={preferences.copyright.companySiteLink}\n onChange={(e) => setPreferences({ copyright: { companySiteLink: e.target.value } })}\n disabled={!preferences.copyright.enable}\n />\n </div>\n <div className={`preference-item ${!preferences.copyright.enable ? 'disabled' : ''}`}>\n <span>{t('layout.date')}</span>\n <Input\n style={{ width: 200 }}\n value={preferences.copyright.date}\n onChange={(e) => setPreferences({ copyright: { date: e.target.value } })}\n disabled={!preferences.copyright.enable}\n />\n </div>\n <div className={`preference-item ${!preferences.copyright.enable ? 'disabled' : ''}`}>\n <span>{t('layout.icpNumber')}</span>\n <Input\n style={{ width: 200 }}\n value={preferences.copyright.icp}\n onChange={(e) => setPreferences({ copyright: { icp: e.target.value } })}\n disabled={!preferences.copyright.enable}\n />\n </div>\n <div className={`preference-item ${!preferences.copyright.enable ? 'disabled' : ''}`}>\n <span>{t('layout.icpLink')}</span>\n <Input\n style={{ width: 200 }}\n value={preferences.copyright.icpLink}\n onChange={(e) => setPreferences({ copyright: { icpLink: e.target.value } })}\n disabled={!preferences.copyright.enable}\n />\n </div>\n </section>\n </div>\n );\n};\n","import React from 'react';\nimport { Switch } from 'antd';\nimport { useTranslation } from 'react-i18next';\nimport { usePreferencesStore } from '../../store';\nimport './ShortcutKeyPanel.style.less';\n\nexport const ShortcutKeyPanel: React.FC = () => {\n const { preferences, setPreferences } = usePreferencesStore();\n const { t } = useTranslation('preferences');\n\n return (\n <div className=\"shortcut-panel\">\n <section className=\"shortcut-section\">\n <h3 className=\"section-title\">{t('shortcut.global')}</h3>\n\n <div className=\"preference-item\">\n <span>{t('shortcut.shortcutKeys')}</span>\n <Switch\n checked={preferences.shortcutKeys.enable}\n onChange={(checked) => setPreferences({ shortcutKeys: { enable: checked } })}\n />\n </div>\n\n <div className={`preference-item ${!preferences.shortcutKeys.enable ? 'disabled' : ''}`}>\n <span>{t('shortcut.globalSearch')}</span>\n <div className=\"shortcut-key-row\">\n <kbd className=\"kbd-badge\">Ctrl</kbd>\n <kbd className=\"kbd-badge\">K</kbd>\n <Switch\n disabled={!preferences.shortcutKeys.enable}\n checked={preferences.shortcutKeys.globalSearch}\n onChange={(checked) => setPreferences({ shortcutKeys: { globalSearch: checked } })}\n />\n </div>\n </div>\n\n <div className={`preference-item ${!preferences.shortcutKeys.enable ? 'disabled' : ''}`}>\n <span>{t('shortcut.logout')}</span>\n <div className=\"shortcut-key-row\">\n <kbd className=\"kbd-badge\">Alt</kbd>\n <kbd className=\"kbd-badge\">Q</kbd>\n <Switch\n disabled={!preferences.shortcutKeys.enable}\n checked={preferences.shortcutKeys.globalLogout}\n onChange={(checked) => setPreferences({ shortcutKeys: { globalLogout: checked } })}\n />\n </div>\n </div>\n\n <div className={`preference-item ${!preferences.shortcutKeys.enable ? 'disabled' : ''}`}>\n <span>{t('shortcut.lockScreen')}</span>\n <div className=\"shortcut-key-row\">\n <kbd className=\"kbd-badge\">Alt</kbd>\n <kbd className=\"kbd-badge\">L</kbd>\n <Switch\n disabled={!preferences.shortcutKeys.enable}\n checked={preferences.shortcutKeys.globalLockScreen}\n onChange={(checked) =>\n setPreferences({ shortcutKeys: { globalLockScreen: checked } })\n }\n />\n </div>\n </div>\n </section>\n </div>\n );\n};\n","import React from 'react';\nimport { Select, Switch } from 'antd';\nimport { useTranslation } from 'react-i18next';\nimport { usePreferencesStore } from '../../store';\nimport type { SupportedLanguagesType } from '../../types';\nimport './GeneralPanel.style.less';\n\n/** 页面切换动画选项 */\nconst TRANSITION_OPTIONS = [\n { label: 'general.transitions.fade', value: 'fade', icon: '✨' },\n { label: 'general.transitions.fadeDown', value: 'fade-down', icon: '️' },\n { label: 'general.transitions.fadeSlide', value: 'fade-slide', icon: '↔️' },\n { label: 'general.transitions.fadeUp', value: 'fade-up', icon: '⬆️' },\n];\n\n/** 语言选项 */\nconst LANGUAGE_OPTIONS = [\n { label: 'general.languages.zhCN', value: 'zh-CN' },\n { label: 'general.languages.enUS', value: 'en-US' },\n];\n\nexport const GeneralPanel: React.FC = () => {\n const { preferences, setPreferences } = usePreferencesStore();\n const { t } = useTranslation('preferences');\n\n const handleLanguageChange = (locale: SupportedLanguagesType) => {\n setPreferences({ app: { locale } });\n };\n\n return (\n <div className=\"general-panel\">\n <section className=\"general-section\">\n <h3 className=\"section-title\">{t('general.general')}</h3>\n\n <div className=\"preference-item\">\n <span>{t('general.language')}</span>\n <Select\n value={preferences.app.locale}\n options={LANGUAGE_OPTIONS.map((opt) => ({ ...opt, label: t(opt.label) }))}\n onChange={handleLanguageChange}\n style={{ width: 200 }}\n />\n </div>\n\n <div className=\"preference-item\">\n <span>{t('general.dynamicTitle')}</span>\n <Switch\n checked={preferences.app.dynamicTitle}\n onChange={(checked) => setPreferences({ app: { dynamicTitle: checked } })}\n />\n </div>\n\n <div className=\"preference-item\">\n <span>{t('general.watermark')}</span>\n <Switch\n checked={preferences.app.watermark}\n onChange={(checked) => setPreferences({ app: { watermark: checked } })}\n />\n </div>\n\n <div className=\"preference-item\">\n <span>{t('general.checkUpdates')}</span>\n <Switch\n checked={preferences.app.enableCheckUpdates}\n onChange={(checked) => setPreferences({ app: { enableCheckUpdates: checked } })}\n />\n </div>\n </section>\n\n <section className=\"general-section\">\n <h3 className=\"section-title\">{t('general.animation')}</h3>\n\n <div className=\"preference-item\">\n <span>{t('general.progressBar')}</span>\n <Switch\n checked={preferences.transition.progress}\n onChange={(checked) => setPreferences({ transition: { progress: checked } })}\n />\n </div>\n\n <div className=\"preference-item\">\n <span>{t('general.loading')}</span>\n <Switch\n checked={preferences.transition.loading}\n onChange={(checked) => setPreferences({ transition: { loading: checked } })}\n />\n </div>\n\n <div className=\"preference-item\">\n <span>{t('general.pageTransition')}</span>\n <Switch\n checked={preferences.transition.enable}\n onChange={(checked) => setPreferences({ transition: { enable: checked } })}\n />\n </div>\n\n <div\n className={`transition-animations ${!preferences.transition.enable ? 'disabled' : ''}`}\n >\n <div className=\"transition-grid\">\n {TRANSITION_OPTIONS.map((option) => (\n <div\n key={option.value}\n className=\"transition-wrapper\"\n onClick={() => {\n if (preferences.transition.enable) {\n setPreferences({ transition: { name: option.value } });\n }\n }}\n >\n <div\n className={`transition-item ${preferences.transition.name === option.value ? 'active' : ''}`}\n >\n <div className=\"transition-preview\">\n <div className={`animation-box ${option.value}`} />\n </div>\n </div>\n <span className=\"transition-label\">{t(option.label)}</span>\n </div>\n ))}\n </div>\n </div>\n </section>\n </div>\n );\n};\n","import React, { useState } from 'react';\nimport { Button, Drawer, Segmented, message } from 'antd';\nimport { ReloadOutlined, CloseOutlined, CopyOutlined, LogoutOutlined } from '@ant-design/icons';\nimport { useTranslation } from 'react-i18next';\n\nimport { usePreferencesStore } from '../../store';\nimport { defaultPreferences } from '../../config/default';\nimport { diffDeep } from '../../utils/diff';\nimport { useAuthStore } from '../../../stores';\nimport { AppearancePanel } from './AppearancePanel';\nimport { LayoutPanel } from './LayoutPanel';\nimport { ShortcutKeyPanel } from './ShortcutKeyPanel';\nimport { GeneralPanel } from './GeneralPanel';\nimport './PreferencesPanel.style.less';\n\ninterface PreferencesPanelProps {\n open: boolean;\n onClose: () => void;\n}\n\ntype TabType = 'appearance' | 'layout' | 'shortcut' | 'general';\n\nconst TAB_OPTIONS = [\n { label: 'appearance', value: 'appearance' },\n { label: 'layout', value: 'layout' },\n { label: 'shortcut', value: 'shortcut' },\n { label: 'general', value: 'general' },\n];\n\nconst TAB_COMPONENTS = {\n appearance: AppearancePanel,\n layout: LayoutPanel,\n shortcut: ShortcutKeyPanel,\n general: GeneralPanel,\n};\n\nexport const PreferencesPanel: React.FC<PreferencesPanelProps> = ({ open, onClose }) => {\n const { resetPreferences } = usePreferencesStore();\n const { t } = useTranslation('preferences');\n const [activeTab, setActiveTab] = useState<TabType>('appearance');\n\n const ActiveComponent = TAB_COMPONENTS[activeTab];\n\n const handleReset = () => {\n resetPreferences();\n };\n\n // 对齐 vben preferences-drawer 的 handleCopy:复制「与出厂默认的增量」而非全量快照,\n // 产物是可回贴的 overrides 对象(diffDeep 语义见 core/preferences/utils/diff.ts)。\n const handleCopy = async () => {\n const { preferences } = usePreferencesStore.getState();\n const overrides = diffDeep(defaultPreferences, preferences);\n try {\n await navigator.clipboard.writeText(JSON.stringify(overrides, null, 2));\n message.success(t('actions.copyPreferencesSuccess'));\n } catch (error) {\n console.error('[PreferencesPanel] 复制偏好设置失败:', error);\n message.error(t('actions.copyPreferencesFailed'));\n }\n };\n\n const handleLogout = async () => {\n try {\n await useAuthStore.getState().logout(true);\n } catch (error) {\n console.error('Logout failed:', error);\n }\n };\n\n return (\n <Drawer\n title={\n <div className=\"drawer-header\">\n <div>\n <h2 className=\"drawer-title\">{t('title')}</h2>\n <p className=\"drawer-subtitle\">{t('subtitle')}</p>\n </div>\n <div className=\"drawer-actions\">\n <Button type=\"text\" icon={<ReloadOutlined />} onClick={handleReset} title={t('actions.reset')} />\n <Button type=\"text\" icon={<CloseOutlined />} onClick={onClose} title={t('actions.close')} />\n </div>\n </div>\n }\n placement=\"right\"\n size={360}\n open={open}\n onClose={onClose}\n closable={false}\n className=\"preferences-drawer\"\n footer={\n <div className=\"drawer-footer\">\n <Button type=\"primary\" icon={<CopyOutlined />} onClick={handleCopy}>\n {t('actions.copySettings')}\n </Button>\n <Button type=\"link\" danger icon={<LogoutOutlined />} onClick={handleLogout}>\n {t('actions.clearCacheAndLogout')}\n </Button>\n </div>\n }\n >\n {/* Tab 切换 */}\n <div className=\"drawer-tabs\">\n <Segmented\n options={TAB_OPTIONS.map(opt => ({ ...opt, label: t(`tabs.${opt.label}`) }))}\n value={activeTab}\n onChange={(value) => setActiveTab(value as TabType)}\n block\n />\n </div>\n\n {/* 内容区域 */}\n <div className=\"drawer-content\">\n <ActiveComponent />\n </div>\n </Drawer>\n );\n};\n","import React, { useEffect, useLayoutEffect, useMemo, useState } from 'react';\n\nimport { ConfigProvider, Watermark, theme as antdTheme, type ThemeConfig } from 'antd';\nimport zhCN from 'antd/locale/zh_CN';\nimport enUS from 'antd/locale/en_US';\n// import jaJP from 'antd/locale/ja_JP';\nimport { ProConfigProvider, enUSIntl, zhCNIntl } from '@ant-design/pro-components';\nimport i18n from 'i18next';\n\nimport { usePreferencesStore } from '../../store';\nimport {\n DARK_PALETTE,\n LIGHT_PALETTE,\n darkThemeComponents,\n darkThemeTokens,\n lightThemeComponents,\n lightThemeTokens,\n} from '../../config';\nimport { SECONDARY_TEXT_DARK, SECONDARY_TEXT_LIGHT } from '../../config/constants';\nimport { mixColor } from '../../../utils/color';\nimport type { SupportedLanguagesType } from '../../types';\n\nexport interface ThemeProviderProps {\n children: React.ReactNode;\n}\n\nconst localeMap: Record<SupportedLanguagesType, any> = {\n 'zh-CN': zhCN,\n 'en-US': enUS,\n};\n\n/**\n * 全局主题提供者\n * 负责:AntD 主题动态切换、系统深色模式同步、CSS 滤镜、水印、国际化\n */\nexport const ThemeProvider = ({ children }: ThemeProviderProps) => {\n // 1. 精确订阅偏好设置(分别订阅 theme 和 app 模块)\n const themePrefs = usePreferencesStore((state) => state.preferences.theme);\n const appPrefs = usePreferencesStore((state) => state.preferences.app);\n\n // 2. 系统主题监听(仅当 mode === 'auto' 时生效)\n const [systemIsDark, setSystemIsDark] = useState(false);\n useEffect(() => {\n if (themePrefs.mode !== 'auto') return;\n\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n setSystemIsDark(mediaQuery.matches);\n\n const handleChange = (e: MediaQueryListEvent) => setSystemIsDark(e.matches);\n mediaQuery.addEventListener('change', handleChange);\n return () => mediaQuery.removeEventListener('change', handleChange);\n }, [themePrefs.mode]);\n\n // 计算最终生效的主题模式\n const effectiveMode = useMemo(() => {\n return themePrefs.mode === 'auto' ? (systemIsDark ? 'dark' : 'light') : themePrefs.mode;\n }, [themePrefs.mode, systemIsDark]);\n\n // 3. 生成 Ant Design 主题配置\n const themeConfig = useMemo((): ThemeConfig => {\n const algorithms = [];\n if (effectiveMode === 'dark') algorithms.push(antdTheme.darkAlgorithm);\n if (appPrefs.compact) algorithms.push(antdTheme.compactAlgorithm);\n\n const tokens: ThemeConfig['token'] = {\n colorPrimary: themePrefs.colorPrimary,\n colorSuccess: themePrefs.colorSuccess,\n colorWarning: themePrefs.colorWarning,\n colorError: themePrefs.colorDestructive,\n // §2.1 声明\"react 端 colorInfo 同值\",但代码里从未设过 colorInfo/colorLink,\n // 于是链接与 info 态走 antd 默认 #1677FF(浅色实测:链接文字 #1677FF on 白 4.10:1,\n // 且与主色 #006BE6 不同蓝)。显式钉住主色后链接 = #006BE6 on 白 4.76:1。\n colorInfo: themePrefs.colorPrimary,\n // 暗色下不能直接用主色当链接色:darkAlgorithm 把 #006BE6 再派生成 #035EC7,\n // 实测\"收起/文档/公告\"等链接文字 2.90:1(暗色大底 #111827 上);按 §2.1 文字档\n // 反解(主色 70% + 白 30%)= #4D97EE,同底 5.89:1。\n colorLink:\n effectiveMode === 'dark'\n ? mixColor(themePrefs.colorPrimary, '#ffffff', 0.7)\n : themePrefs.colorPrimary,\n borderRadius: Number.parseInt(themePrefs.radius) || 6,\n // colorTextDescription 驱动 .ant-typography-secondary / 面包屑分隔符 /\n // Descriptions 标签 / Empty 描述——antd 默认 rgba(0,0,0,.45) 实测只有 3.35:1(浅色)\n // 且暗色派生到 #6E7681 只有 3.86:1;两侧都按 §2.2 钉成\"次要文字\"档。\n colorTextDescription: effectiveMode === 'dark' ? SECONDARY_TEXT_DARK : SECONDARY_TEXT_LIGHT,\n };\n\n // 两态各自叠加一套精调 token:暗色是蓝黑分层色阶,浅色是 §2.2 的画布/边框/大圆角档\n // (浅色的卡片投影不在 token 层,antd 没有对应可覆写 token,落 global.css)。\n // 浅色这套 2026-10-16 才补上——此前浅色完全走 defaultAlgorithm 派生值,卡片边框 #F0F0F0\n // 对白底只有 1.045 倍且无任何投影,是\"整体生硬/发平\"的主因(见 lightTheme.ts 沿革)。\n if (effectiveMode === 'dark') Object.assign(tokens, darkThemeTokens);\n else Object.assign(tokens, lightThemeTokens);\n\n // 输入类控件的内嵌底色、抽屉遮罩、表格行悬停等组件级精调只在暗色需要:\n // 浅色的对应档由 defaultAlgorithm 派生即可,额外覆写会形成第二套真相。\n // 浅色那套只有一条 Menu.subMenuItemBg(见 lightTheme.ts),不是\"没有组件级 token\"了。\n const components: ThemeConfig['components'] =\n effectiveMode === 'dark' ? darkThemeComponents : lightThemeComponents;\n\n return {\n algorithm: algorithms.length > 0 ? algorithms : antdTheme.defaultAlgorithm,\n token: tokens,\n components,\n };\n }, [effectiveMode, appPrefs.compact, themePrefs]);\n\n // 4. 同步根背景色和文本颜色(解决暗黑模式白色闪烁)\n // 用 useLayoutEffect:主题切换走 startThemeViewTransition 时,flushSync 提交\n // 阶段就会同步执行 layout effect,保证 data-theme/根背景色在过渡新快照\n // 截取前落地(View Transition 的 update 阶段不跑 rAF,不能靠等帧)。\n useLayoutEffect(() => {\n const root = document.documentElement;\n const body = document.body;\n\n // 根背景写各自动态画布档:暗色 L0,浅色 §2.2 canvas(原先硬编码 antd 的 #f5f5f5,\n // 与 lightThemeTokens.colorBgLayout 不一致会让首帧/回弹处露出旧画布色)\n const bgLayout =\n effectiveMode === 'dark' ? DARK_PALETTE.bgLayout : LIGHT_PALETTE.bgLayout;\n root.style.backgroundColor = bgLayout;\n body.style.backgroundColor = bgLayout;\n\n const colorText = effectiveMode === 'dark'\n ? 'rgba(230, 237, 243, 0.92)'\n : 'rgba(0, 0, 0, 0.85)';\n root.style.color = colorText;\n\n // 标记主题模式,供 CSS 选择器使用\n root.setAttribute('data-theme', effectiveMode);\n // 同时维护标准的 html.dark class:本应用自己的样式一律读 data-theme,\n // 但第三方组件(@go-wind/editor-react、@go-wind/bi-vue 等)按 html.dark 判暗色,\n // 只给 data-theme 它们会退回去跟随系统偏好,出现\"应用亮色、组件暗色\"。\n root.classList.toggle('dark', effectiveMode === 'dark');\n\n return () => {\n root.style.backgroundColor = '';\n body.style.backgroundColor = '';\n root.style.color = '';\n root.removeAttribute('data-theme');\n root.classList.remove('dark');\n };\n }, [effectiveMode]);\n\n // 4.1 主色/语义色锚点变量:错误页插画等自定义 CSS 的取色源,同时是\n // styles/semantic-text.css 里「语义色作文字」降档的输入(换主色后文字档跟着联动)。\n // 写在 <html> 上——antd v6 的 --ant-* cssVar 只注入在包裹层内,<html> 作用域引用会落空。\n useEffect(() => {\n const root = document.documentElement;\n root.style.setProperty('--app-color-primary', themePrefs.colorPrimary);\n root.style.setProperty('--app-color-success', themePrefs.colorSuccess);\n root.style.setProperty('--app-color-warning', themePrefs.colorWarning);\n root.style.setProperty('--app-color-destructive', themePrefs.colorDestructive);\n return () => {\n root.style.removeProperty('--app-color-primary');\n root.style.removeProperty('--app-color-success');\n root.style.removeProperty('--app-color-warning');\n root.style.removeProperty('--app-color-destructive');\n };\n }, [themePrefs.colorPrimary, themePrefs.colorSuccess, themePrefs.colorWarning, themePrefs.colorDestructive]);\n\n // 5. 应用 CSS 滤镜(色弱 / 灰色模式)\n useEffect(() => {\n const root = document.documentElement;\n if (appPrefs.colorWeakMode) {\n root.style.filter = 'invert(80%)';\n } else if (appPrefs.colorGrayMode) {\n root.style.filter = 'grayscale(100%)';\n } else {\n root.style.filter = '';\n }\n }, [appPrefs.colorWeakMode, appPrefs.colorGrayMode]);\n\n // 6. 国际化配置\n const locale = localeMap[appPrefs.locale] ?? zhCN;\n\n // 6.1 ProComponents 搜索表单按钮文案统一为「搜索」(内置 zh_CN 是「查询」),\n // 其余文案透传对应内置语言包\n const proIntl = useMemo(() => {\n const base = appPrefs.locale === 'en-US' ? enUSIntl : zhCNIntl;\n return {\n locale: base.locale,\n getMessage: (id: string, defaultMessage: string) =>\n id === 'tableForm.search'\n ? i18n.t('button.search', { ns: 'common' })\n : base.getMessage(id, defaultMessage),\n };\n }, [appPrefs.locale]);\n\n // 7. 水印内容\n const watermarkText = appPrefs.watermark ? `${appPrefs.name} ${appPrefs.version}` : '';\n\n // 8. 水印颜色(跟随主题)\n const watermarkColor = useMemo(() => {\n return effectiveMode === 'dark'\n ? 'rgba(255, 255, 255, 0.1)'\n : 'rgba(0, 0, 0, 0.15)';\n }, [effectiveMode]);\n\n return (\n <ConfigProvider theme={themeConfig} locale={locale}>\n <ProConfigProvider intl={proIntl}>\n {watermarkText ? (\n <Watermark\n content={watermarkText}\n font={{ fontSize: 14, color: watermarkColor }}\n gap={[120, 120]}\n offset={[60, 60]}\n rotate={-22}\n zIndex={1}\n >\n {children}\n </Watermark>\n ) : (\n children\n )}\n </ProConfigProvider>\n </ConfigProvider>\n );\n};\n","import { useCallback, useEffect, useRef } from 'react';\n\nimport { useI18n } from './useI18n';\nimport { usePreferencesStore } from '../../preferences';\nimport type { SupportedLocale } from '../../locales';\n\nexport const useLocaleSync = () => {\n const { i18n } = useI18n();\n // 使用稳定的选择器,避免每次渲染创建新对象\n const locale = usePreferencesStore((state) => state.preferences.app.locale);\n const setPreferences = usePreferencesStore((state) => state.setPreferences);\n\n // 标记是否已初始化\n const initialized = useRef(false);\n\n // preferences 变更 → 切换 i18n 语言\n useEffect(() => {\n if (locale && i18n.language !== locale) {\n i18n.changeLanguage(locale).catch((error) => {\n console.error('Failed to switch language:', error);\n });\n }\n // 同步 <html lang/dir>:切换语言后必须更新,否则屏幕阅读器/浏览器翻译/a11y\n // 仍按旧语言工作。此前 react 端缺失这一步(vue-element 在 setI18nLanguage 有做)。\n // locale 取值仅 'zh-CN'/'en-US',均为 LTR,dir 始终为 'ltr'。\n if (locale) {\n document.documentElement.lang = locale;\n document.documentElement.dir = 'ltr';\n }\n }, [locale]);\n\n // i18n 初始化后,只在第一次反向同步到 store\n useEffect(() => {\n if (i18n.isInitialized && !locale && !initialized.current) {\n initialized.current = true;\n const detected = i18n.language as SupportedLocale;\n setPreferences({ app: { locale: detected } });\n }\n }, [i18n.isInitialized, locale, setPreferences]);\n\n // 便捷切换方法\n const changeLocale = useCallback(\n (newLocale: SupportedLocale) => {\n setPreferences({ app: { locale: newLocale } });\n },\n [setPreferences],\n );\n\n return { changeLocale };\n};\n","import i18n from 'i18next';\nimport { initReactI18next } from 'react-i18next';\nimport { FALLBACK_LOCALE, type SupportedLocale } from '../../locales';\n\n/**\n * initI18n 的资源配置由宿主注入:包只带机制(引擎/回退/命名空间约定),\n * 文案 = 包内置框架命名空间(coreResources)+ 宿主业务命名空间合并后传入。\n */\nexport interface InitI18nOptions {\n /** 合并后的完整语言资源(语言 -> 命名空间 -> 文案) */\n resources: Record<string, Record<string, Record<string, unknown>>>;\n /** 全部命名空间列表(一般取 merged['zh-CN'] 的键) */\n namespaces: string[];\n /** 支持的语言列表(默认 zh-CN/en-US) */\n supportedLngs?: readonly string[];\n /** 回退语言(默认 zh-CN) */\n fallbackLng?: string;\n /** 开发环境缺失 key 告警(宿主传 import.meta.env.DEV,包内不读 env) */\n missingKeyWarn?: boolean;\n}\n\nexport const initI18n = async (initialLang: SupportedLocale, options: InitI18nOptions) => {\n await i18n.use(initReactI18next).init({\n lng: initialLang,\n resources: options.resources as unknown as import('i18next').Resource, // 包框架命名空间 + 宿主扩展模块(宿主合并后传入)\n fallbackLng: options.fallbackLng ?? FALLBACK_LOCALE,\n supportedLngs: options.supportedLngs ?? ['zh-CN', 'en-US'],\n\n defaultNS: 'common',\n ns: options.namespaces, // 声明所有命名空间\n\n // 缺失 key 处理(开发环境)\n missingKeyHandler: options.missingKeyWarn\n ? (lngs: readonly string[], ns: string, key: string) => {\n console.warn(`[i18n] Missing: \"${key}\" in \"${ns}\" for \"${lngs[0]}\"`);\n }\n : undefined,\n });\n\n return i18n;\n};\n","import { Select } from 'antd';\nimport { useLocaleSync } from '../../../i18n';\nimport { supportedLocales } from '../../../locales';\nimport { usePreferencesStore } from '../../../preferences';\n\nconst localeLabels: Record<string, string> = {\n 'zh-CN': '🇨🇳 简体中文',\n 'en-US': '🇺🇸 English',\n};\n\nexport const LocaleSwitcher = () => {\n const { changeLocale } = useLocaleSync();\n\n return (\n <Select\n size=\"small\"\n value={usePreferencesStore((s) => s.preferences.app.locale)}\n options={supportedLocales.map((lng) => ({\n label: localeLabels[lng],\n value: lng,\n }))}\n onChange={(value) => changeLocale(value)}\n />\n );\n};\n","/**\n * 权限鉴权 Hook\n * 对齐 Vue-Vben 版 use-access.ts,提供角色码/权限码独立鉴权\n *\n * 鉴权码来源:\n * - 角色码:userInfo.roles → useUserStore.userRoles\n * - 权限码:GetMyPermissionCode → useUserStore.accessCodes\n * - UI 绑定:meta.authority(角色码和权限码的混合数组)\n */\nimport { useCallback } from 'react';\nimport { useUserStore } from '../stores/user';\n\n/**\n * 核心权限判断逻辑(纯函数,可复用)\n */\nfunction checkAccessByRoles(userRoles: string[], requiredRoles: string[]): boolean {\n if (!requiredRoles.length) return true;\n const userRoleSet = new Set(userRoles);\n return requiredRoles.some((item) => userRoleSet.has(item));\n}\n\nfunction checkAccessByCodes(accessCodes: string[], requiredCodes: string[]): boolean {\n if (!requiredCodes.length) return true;\n const userCodesSet = new Set(accessCodes);\n return requiredCodes.some((item) => userCodesSet.has(item));\n}\n\nfunction checkAccessByAuthority(\n userRoles: string[],\n accessCodes: string[],\n authority: string[] | undefined,\n): boolean {\n if (!authority?.length) return true;\n const allSet = new Set([...userRoles, ...accessCodes]);\n return authority.some((item) => allSet.has(item));\n}\n\n/**\n * React Hook:权限鉴权(组件内使用)\n *\n * @example\n * ```tsx\n * const { hasAccessByCodes, hasAccessByRoles, hasAccessByAuthority } = useAccess();\n *\n * // 按钮级权限\n * {hasAccessByCodes(['sys:user:create']) && <Button>新建</Button>}\n * {hasAccessByRoles(['admin']) && <Button>系统设置</Button>}\n * ```\n */\nfunction useAccess() {\n const userRoles = useUserStore((s) => s.userRoles);\n const accessCodes = useUserStore((s) => s.accessCodes);\n\n /**\n * 基于角色码判断是否有权限(useCallback 稳定化引用)\n */\n const hasAccessByRoles = useCallback(\n (roles: string[]): boolean => checkAccessByRoles(userRoles, roles),\n [userRoles],\n );\n\n /**\n * 基于权限码判断是否有权限(useCallback 稳定化引用)\n */\n const hasAccessByCodes = useCallback(\n (codes: string[]): boolean => checkAccessByCodes(accessCodes, codes),\n [accessCodes],\n );\n\n /**\n * 基于 meta.authority 判断是否有权限(useCallback 稳定化引用)\n */\n const hasAccessByAuthority = useCallback(\n (authority: string[] | undefined): boolean =>\n checkAccessByAuthority(userRoles, accessCodes, authority),\n [userRoles, accessCodes],\n );\n\n /**\n * 获取合并后的完整权限列表(useCallback 稳定化引用)\n */\n const getAllPermissions = useCallback(\n (): string[] => [...userRoles, ...accessCodes],\n [userRoles, accessCodes],\n );\n\n return {\n /** 用户的角色码列表 */\n userRoles,\n /** 用户的权限码列表 */\n accessCodes,\n /** 基于角色码鉴权 */\n hasAccessByRoles,\n /** 基于权限码鉴权 */\n hasAccessByCodes,\n /** 基于 meta.authority 鉴权(角色码+权限码混合匹配) */\n hasAccessByAuthority,\n /** 获取合并权限列表 */\n getAllPermissions,\n };\n}\n\n/**\n * 非组件内的静态访问方法(用于路由生成、工具函数等非 React 场景)\n *\n * @example\n * ```ts\n * // 在路由生成中使用\n * const freshPermissions = getAccessStatic().getAllPermissions();\n * ```\n */\nexport function getAccessStatic() {\n const { userRoles, accessCodes } = useUserStore.getState();\n\n function hasAccessByRoles(roles: string[]): boolean {\n return checkAccessByRoles(userRoles, roles);\n }\n\n function hasAccessByCodes(codes: string[]): boolean {\n return checkAccessByCodes(accessCodes, codes);\n }\n\n function hasAccessByAuthority(authority: string[] | undefined): boolean {\n return checkAccessByAuthority(userRoles, accessCodes, authority);\n }\n\n /**\n * 获取合并后的完整权限列表(角色码 + 权限码)\n */\n function getAllPermissions(): string[] {\n return [...userRoles, ...accessCodes];\n }\n\n return {\n userRoles,\n accessCodes,\n hasAccessByRoles,\n hasAccessByCodes,\n hasAccessByAuthority,\n getAllPermissions,\n };\n}\n\nexport { useAccess, checkAccessByRoles, checkAccessByCodes, checkAccessByAuthority };\n","/**\n * AccessControl 权限控制组件\n * 对齐 Vue 版 <AccessControl>,无权限时不渲染子元素\n *\n * @example\n * ```tsx\n * // 基于权限码\n * <AccessControl codes={['sys:user:create']} type=\"code\">\n * <Button>新建用户</Button>\n * </AccessControl>\n *\n * // 基于角色码\n * <AccessControl codes={['ADMIN']} type=\"role\">\n * <Button>管理员操作</Button>\n * </AccessControl>\n *\n * // 混合匹配(meta.authority 模式)\n * <AccessControl codes={['admin', 'sys:user:export']} type=\"authority\">\n * <Button>导出</Button>\n * </AccessControl>\n *\n * // 自定义 fallback\n * <AccessControl codes={['sys:user:delete']} type=\"code\" fallback={<span>无权限</span>}>\n * <Button danger>删除</Button>\n * </AccessControl>\n * ```\n */\nimport type { ReactNode } from 'react';\nimport { useAccess } from './use-access';\n\nexport interface AccessControlProps {\n /** 子元素 */\n children: ReactNode;\n /** 所需权限码/角色码列表 */\n codes: string[];\n /** 权限类型 */\n type?: 'code' | 'role' | 'authority';\n /** 无权限时的替代内容(默认不渲染) */\n fallback?: ReactNode;\n}\n\nexport const AccessControl = ({\n children,\n codes,\n type = 'code',\n fallback = null,\n}: AccessControlProps) => {\n const { hasAccessByCodes, hasAccessByRoles, hasAccessByAuthority } = useAccess();\n\n let hasAccess: boolean;\n\n switch (type) {\n case 'role':\n hasAccess = hasAccessByRoles(codes);\n break;\n case 'authority':\n hasAccess = hasAccessByAuthority(codes);\n break;\n case 'code':\n default:\n hasAccess = hasAccessByCodes(codes);\n break;\n }\n\n return hasAccess ? <>{children}</> : <>{fallback}</>;\n};\n","/**\n * 字段级权限工具\n *\n * 隐藏字段集来自 GetMyPermissionCode 响应的 hiddenFields(\"资源.字段\" 串数组,\n * 如 \"User.email\"),由后端按角色配置在登录期聚合。前端用它驱动列/字段显隐;\n * 真正的数据裁剪在后端响应侧完成,这里只管 UI 呈现。\n */\n\n/** 从 \"资源.字段\" 串数组解析出指定资源的隐藏字段名集合 */\nexport function parseResourceHiddenFields(\n entries: string[] | undefined | null,\n resource: string,\n): Set<string> {\n const result = new Set<string>();\n for (const entry of entries ?? []) {\n const dotIndex = entry.indexOf('.');\n if (dotIndex <= 0) continue;\n if (entry.slice(0, dotIndex) !== resource) continue;\n const field = entry.slice(dotIndex + 1);\n if (field) result.add(field);\n }\n return result;\n}\n\n/** 判断指定资源的某字段是否被隐藏 */\nexport function isFieldHidden(\n entries: string[] | undefined | null,\n resource: string,\n field: string,\n): boolean {\n return parseResourceHiddenFields(entries, resource).has(field);\n}\n","import {mapTree} from \"../../utils\";\nimport type {AppRoute} from \"../../router/types\";\n\n/**\n * 自动注入重定向\n * 规则:父路由有子路由且未配置 redirect 时,指向第一个子路由\n */\nexport const injectRedirects = (routes: AppRoute[]): AppRoute[] => {\n return mapTree(routes, (route) => {\n // 已有 redirect 或无子路由,直接返回\n if (route.redirect || !route.children?.length) {\n return route;\n }\n\n const firstChild = route.children[0];\n\n // 子路由路径无效(相对路径/含参数),跳过\n if (!firstChild?.path || !firstChild.path.startsWith('/')) {\n return route;\n }\n\n // 注入重定向(不可变更新)\n return {\n ...route,\n redirect: firstChild.path,\n };\n });\n};\n","import type {AppRoute} from \"../../router/types\";\n\n/**\n * 递归排序路由树(按 meta.order 升序)\n */\nexport const sortRoutes = (routes: AppRoute[]): AppRoute[] => {\n return routes\n .map((route) => ({\n ...route,\n children: route.children?.length ? sortRoutes(route.children) : undefined,\n }))\n .sort((a, b) => {\n const orderA = a.meta?.order ?? Number.MAX_SAFE_INTEGER;\n const orderB = b.meta?.order ?? Number.MAX_SAFE_INTEGER;\n return orderA - orderB;\n });\n};\n","import type { AppRouteObject, RouteMeta } from '../types';\nimport type { RouteHandle } from '../../types';\n\n/**\n * 将 meta 转换为 React Router 的 handle\n * 这样 useMatches() 就能获取到路由元数据\n */\nexport function transformMetaToHandle(meta?: RouteMeta): RouteHandle | undefined {\n if (!meta) return undefined;\n\n return {\n title: meta.title,\n icon: typeof meta.icon === 'string' ? meta.icon : undefined,\n authority: meta.authority,\n // 展开其他自定义字段(排除已明确指定的字段)\n ...((({ title, icon, authority, ...rest }) => rest)(meta)),\n };\n}\n\n/**\n * 递归转换路由配置,将 meta 映射到 handle\n */\nexport function transformRoutesWithHandle(routes: AppRouteObject[]): AppRouteObject[] {\n return routes.map((route) => {\n const transformed: AppRouteObject = {\n ...route,\n // 将 meta 同时设置为 handle(React Router v6 需要)\n handle: transformMetaToHandle(route.meta),\n };\n\n // 递归处理子路由\n if (route.children && route.children.length > 0) {\n transformed.children = transformRoutesWithHandle(route.children);\n }\n\n return transformed;\n });\n}\n","import {useMemo} from 'react';\nimport {ConfigProvider, Spin, Progress, type ThemeConfig} from 'antd';\nimport {LoadingOutlined} from '@ant-design/icons';\nimport {usePreferencesStore, useThemeConfig} from '../../preferences';\nimport {useI18n} from '../../i18n';\n\nexport interface ThemeLoadingProps {\n text?: string;\n subText?: string;\n fullScreen?: boolean;\n className?: string;\n style?: React.CSSProperties;\n\n /** 进度值 0-100 */\n progress?: number;\n\n /** 加载状态:'loading' | 'success' | 'error' */\n status?: 'loading' | 'success' | 'error';\n\n /** 自动隐藏(当 progress=100 时) */\n autoHide?: boolean;\n\n /** 自定义主题覆盖 */\n themeOverride?: Partial<ReturnType<typeof useThemeConfig>>;\n}\n\n/**\n * 主题化 Loading 组件\n * 自动同步应用主题配置(颜色、圆角、暗黑模式等)\n */\nexport const ThemeLoading = ({\n text,\n subText,\n fullScreen = false,\n className = '',\n style,\n progress,\n status = 'loading',\n autoHide = true,\n themeOverride,\n }: ThemeLoadingProps) => {\n const {theme: prefTheme} = usePreferencesStore(state => state.preferences);\n const defaultTheme = useThemeConfig();\n const {t} = useI18n('common');\n\n // 合并主题配置\n const themeConfig = useMemo((): ThemeConfig => {\n return {\n ...defaultTheme,\n token: {\n ...defaultTheme.token,\n ...themeOverride?.token,\n },\n };\n }, [defaultTheme, themeOverride]);\n\n // 自动隐藏逻辑\n if (autoHide && progress === 100 && status === 'loading') {\n return null;\n }\n\n // 状态对应的图标/颜色\n const statusConfig = useMemo(() => {\n switch (status) {\n case 'success':\n return {\n icon: <span className=\"text-[color:var(--gowind-success-text)] text-4xl\">✓</span>,\n color: themeConfig.token?.colorSuccess,\n textColor: 'var(--gowind-success-text)',\n text: text || t('loading.success'),\n };\n case 'error':\n return {\n icon: <span className=\"text-[color:var(--gowind-danger-text)] text-4xl\">✕</span>,\n color: themeConfig.token?.colorError,\n textColor: 'var(--gowind-danger-text)',\n text: text || t('loading.failed'),\n };\n default:\n return {\n icon: (\n <LoadingOutlined\n style={{fontSize: 40, color: themeConfig.token?.colorPrimary}}\n spin\n />\n ),\n color: themeConfig.token?.colorPrimary,\n textColor: 'var(--gowind-primary-text)',\n text: text || t('loading.text'),\n };\n }\n }, [status, themeConfig.token, text, t]);\n\n const content = (\n <ConfigProvider theme={themeConfig}>\n <div className=\"flex flex-col items-center justify-center gap-4 animate-fade-in\">\n {/* 状态图标 */}\n <div className=\"transition-transform duration-300 hover:scale-105\">\n {status === 'loading' ? (\n <Spin indicator={statusConfig.icon} size=\"large\"/>\n ) : (\n <div className=\"w-10 h-10 flex items-center justify-center\">\n {statusConfig.icon}\n </div>\n )}\n </div>\n\n {/* 文字区域 */}\n <div className=\"text-center space-y-1\">\n <div\n className=\"font-medium\"\n style={{color: statusConfig.textColor}}\n >\n {statusConfig.text}\n </div>\n {subText && (\n <div className=\"text-[color:var(--gowind-secondary-text)] text-xs\">\n {subText}\n </div>\n )}\n </div>\n\n {/* 进度环(仅 loading 状态) */}\n {status === 'loading' && typeof progress === 'number' && progress < 100 && (\n <Progress\n type=\"circle\"\n percent={progress}\n size={72}\n strokeColor={statusConfig.color}\n trailColor=\"var(--ant-color-fill-tertiary)\"\n format={() => `${Math.round(progress)}%`}\n className=\"mt-2\"\n />\n )}\n </div>\n </ConfigProvider>\n );\n\n const baseClasses = `\n flex items-center justify-center\n ${fullScreen\n ? 'fixed inset-0 z-[1000] bg-[var(--ant-color-bg-container)]/95 backdrop-blur-md'\n : 'py-6'\n }\n ${className}\n `.trim();\n\n return (\n <div\n className={baseClasses}\n style={{\n ...style,\n // 暗黑模式适配\n backgroundColor: prefTheme.mode === 'dark'\n ? 'rgba(0, 0, 0, 0.85)'\n : undefined,\n }}\n role=\"status\"\n aria-live=\"polite\"\n aria-label={statusConfig.text}\n >\n {content}\n </div>\n );\n};\n\nexport default ThemeLoading;\n","import {type ComponentType, createElement, Suspense} from 'react';\nimport {Navigate} from 'react-router-dom';\n\nimport {mapTree} from '../../utils';\nimport {isDevMode} from '../../env';\nimport Loading from '../../components/Loading';\nimport type {AppRouteObject, BackendRoute, ComponentRecord, GenerateRoutesOptions} from \"../../router/types\"; // 全局加载组件\n\n/**\n * 动态生成路由 - 后端方式(React 版本)\n * @param options - 配置选项\n * @returns 转换后的路由树(AppRouteObject[]),可直接传入 createBrowserRouter\n */\nexport async function generateRoutesByBackend(\n options: GenerateRoutesOptions\n): Promise<AppRouteObject[]> {\n const {fetchMenuListAsync, layoutMap = {}, pageMap = {}} = options;\n\n try {\n // 1. 从后端获取菜单路由(组件路径为字符串)\n const menuRoutes = await fetchMenuListAsync?.();\n if (!menuRoutes?.length) {\n return [];\n }\n\n // 2. 预处理 pageMap:标准化路径键名(支持多种写法)\n const normalizedPageMap: ComponentRecord = {};\n for (const [key, value] of Object.entries(pageMap)) {\n const normalizedKey = normalizeViewPath(key);\n normalizedPageMap[normalizedKey] = value;\n // 兼容:如果传入的是带后缀的,也注册不带后缀的映射\n if (normalizedKey.endsWith('.tsx')) {\n normalizedPageMap[normalizedKey.replace(/\\.tsx$/, '')] = value;\n }\n }\n\n // 3. 归位嵌套绝对子路径 + 补目录组缺省重定向,再递归转换路由树\n rebaseBackendTree(menuRoutes);\n return convertRoutes(menuRoutes, layoutMap, normalizedPageMap);\n } catch (error) {\n console.error('Failed to generate routes from backend:', error);\n return [];\n }\n}\n\n/**\n * 递归转换后端路由为 React 路由\n */\nfunction convertRoutes(\n routes: BackendRoute[],\n layoutMap: ComponentRecord,\n pageMap: ComponentRecord\n): AppRouteObject[] {\n // 注意:不手动递归 children,由 mapTree 自动处理递归\n // children 必须保留在返回对象中,mapTree 才能自动递归\n return mapTree(routes, (node) => {\n const {component: componentPath, name, ...rest} = node;\n\n if (!name) {\n console.error('Route name is required', node);\n }\n\n const route: AppRouteObject = {\n ...rest,\n name,\n };\n\n if (componentPath) {\n let Component: ComponentType<any> | undefined;\n\n // 1. 优先匹配布局组件\n if (layoutMap[componentPath]) {\n Component = layoutMap[componentPath];\n }\n // 2. 匹配页面组件(标准化路径后查找)\n else {\n const normalizedPath = normalizeViewPath(componentPath);\n // 尝试多种后缀组合\n const candidates = [\n normalizedPath,\n normalizedPath.endsWith('.tsx') ? normalizedPath : `${normalizedPath}.tsx`,\n normalizedPath.endsWith('/index') ? normalizedPath : `${normalizedPath}/index`,\n `${normalizedPath}/index.tsx`,\n ];\n\n for (const candidate of candidates) {\n if (pageMap[candidate]) {\n Component = pageMap[candidate];\n break;\n }\n }\n }\n\n // 3. 渲染元素:必须用 Suspense 包裹 lazy 组件\n if (Component) {\n route.element = createElement(\n Suspense,\n {fallback: createElement(Loading)},\n createElement(Component)\n );\n } else {\n // 开发环境警告,生产环境降级处理(dev 标志由宿主运行时注入,见 src/env.ts)\n if (isDevMode()) {\n console.warn(`Component not found for path: ${componentPath}`);\n }\n route.element = createElement('div', null, `404 - Component \"${componentPath}\" not found`);\n }\n }\n\n // 处理 redirect:将后端返回的 redirect 转换为 React Router 可识别的导航\n // 1. 有 redirect + 有 children → 注入 index 子路由进行重定向\n // 2. 有 redirect + 无 component → 纯重定向路由,element 设为 <Navigate>\n if (rest.redirect) {\n if (rest.children?.length) {\n // 在 children 最前面注入 index 路由(匹配父路径本身)\n const indexRoute: AppRouteObject = {\n index: true,\n element: createElement(Navigate, {to: rest.redirect, replace: true}),\n };\n route.children = [indexRoute, ...(rest.children as any[])];\n } else if (!componentPath) {\n // 纯重定向路由(无组件、无子路由)\n route.element = createElement(Navigate, {to: rest.redirect, replace: true});\n }\n }\n\n return route;\n });\n}\n\n/**\n * 归位后端菜单树中的嵌套绝对子路径,并为缺省目录组注入重定向。\n *\n * 后端菜单树是 vue-router 风格:目录组内子项常带绝对路径(/dashboard →\n * /analytics、/profile → /profile)。vue-router 允许嵌套绝对子路径,而\n * react-router 禁止(除非以父路径全路径为前缀),createBrowserRouter 会直接\n * 抛错卡死路由创建。这里把绝对子路径归位为父目录组内的相对路径:\n * - 前缀与父路径匹配者剥掉父前缀(/profile → 空路径 index);\n * - 其余剥掉开头斜杠(/analytics → analytics),URL 随父路径延伸为\n * /dashboard/analytics,与前端模式静态路由的 URL 空间保持一致。\n */\nfunction rebaseBackendTree(nodes: BackendRoute[], parentPath = ''): void {\n for (const node of nodes) {\n const fullPath = node.path ? resolveRoutePath(parentPath, node.path) : parentPath;\n\n if (parentPath && node.path?.startsWith('/')) {\n node.path = node.path === parentPath || node.path.startsWith(`${parentPath}/`)\n ? node.path.slice(parentPath.length).replace(/^\\/+/, '')\n : node.path.replace(/^\\/+/, '');\n }\n\n // 目录组缺省重定向:convertRoutes 会将其转成 index <Navigate>,否则\n // 直访目录组路径(/dashboard)只渲染出空布局。首个子路径为空(归位成\n // index 的 /profile 组)或重定向目标等于自身(自指死循环)时不注入。\n const firstChild = node.children?.[0];\n if (parentPath && node.children?.length && !node.redirect && firstChild?.path) {\n const target = resolveRoutePath(fullPath, firstChild.path);\n if (target !== fullPath) {\n node.redirect = target;\n }\n }\n\n if (node.children?.length) {\n rebaseBackendTree(node.children, fullPath);\n }\n }\n}\n\nfunction resolveRoutePath(parentPath: string, childPath: string): string {\n if (!childPath) return parentPath;\n if (childPath.startsWith('/')) return childPath;\n return `${parentPath.replace(/\\/+$/, '')}/${childPath}`;\n}\n\n/**\n * 标准化视图路径(适配 React 目录结构)\n * @param path - 后端返回的组件路径,如 \"./views/system/user\" 或 \"/views/dashboard\"\n */\nexport function normalizeViewPath(path: string): string {\n // 1. 去除相对路径前缀\n const normalized = path.replace(/^(\\.\\/|\\.\\.\\/)+/, '');\n\n // 2. 确保以 / 开头\n const withSlash = normalized.startsWith('/') ? normalized : `/${normalized}`;\n\n // 3. 去除目录前缀(适配 React 项目结构)\n // 支持多种常见目录:/views, /pages, /src/pages 等;后端组件路径带 app/ 段\n // (如 app/opm/user/list/index.vue),而 React pageMap 键不含 app/,一并剥掉;\n // 后端种子用下划线目录(vue-element 端约定,如 internal_message),React\n // 页面目录统一横线,归一化时转换\n return withSlash\n .replace(/^\\/(src\\/)?(views|pages)\\//i, '/') // 去除 /views/ 或 /pages/\n .replace(/^\\/app\\//, '/') // 去除 /app/ 段\n .replace(/\\.(vue|tsx|jsx|ts)$/i, '') // 去除组件文件扩展名(后端下发的是 vue 端路径 xxx/index.vue)\n .replace(/_/g, '-') // 下划线 → 横线(React 页面目录约定)\n .replace(/\\/+$/, ''); // 去除末尾斜杠\n}\n","import type {ReactNode} from \"react\";\n\nimport type {AppRouteObject, RouteMeta} from \"../../router/types\";\n\n/**\n * 动态生成路由 - 前端权限过滤方式\n * @param routes - 静态路由表(AppRouteObject[],含 meta)\n * @param permissions - 用户权限码列表\n * @param forbiddenElement - 无权限时渲染的元素(<Forbidden />)\n */\nexport async function generateRoutesByFrontend(\n routes: AppRouteObject[],\n permissions: string[],\n forbiddenElement?: ReactNode\n): Promise<AppRouteObject[]> {\n // 纯变换:逐层构造新节点(element 等属性按引用共享),绝不回写传入的树。\n // 该树是模块级单例(router/modules/* 经 business-routes 装配),本函数在\n // 未登录时就会以空权限被调用——就地过滤会把带 authority 的节点从单例里\n // 永久剔除,登录后重建也找不回。vue 端以 accessible 层 cloneDeep 防线解决同类\n // 问题(实测 lodash 对嵌套函数按引用保留,cloneDeep 亦可行);此处取纯过滤:\n // 未触碰子树零拷贝,且避免整树复制含冻结的 React 元素。\n const walk = (nodes: AppRouteObject[]): AppRouteObject[] => {\n const kept: AppRouteObject[] = [];\n for (const node of nodes) {\n const pass = hasPermission(node, permissions);\n const mvwf = menuVisibleWithForbidden(node);\n if (!pass && !mvwf) {\n continue;\n }\n const children = Array.isArray(node.children)\n ? walk(node.children)\n : undefined;\n const next: AppRouteObject =\n children === undefined ? node : {...node, children};\n if (mvwf && forbiddenElement) {\n // 对齐 vue-vben / vue-element:声明了 menuVisibleWithForbidden\n // 的路由保留在菜单中,但无论是否持码,元素一律换成 403\n kept.push({...next, element: forbiddenElement});\n } else {\n kept.push(next);\n }\n }\n return kept;\n };\n return walk(routes);\n}\n\n/**\n * 判断路由是否有权限访问\n * @param route - 路由对象\n * @param permissions - 用户权限码列表\n * @returns 是否允许访问\n */\nfunction hasPermission(route: AppRouteObject, permissions: string[]): boolean {\n const meta = route.meta as RouteMeta | undefined;\n\n // 情况 1: 无权限要求 = 公开路由(登录页、404 等)\n if (!meta?.authority?.length) {\n return true;\n }\n\n // 情况 2: 配置了 ignoreAccess = 忽略权限检查\n if (meta?.ignoreAccess) {\n return true;\n }\n\n // 情况 3: 检查权限码数组(authority 中任一元素匹配用户权限即可)\n if (meta.authority.some((code) => permissions.includes(code))) {\n return true;\n }\n\n // 情况 4: 无权限,但可能是\"菜单可见但禁止访问\",交给上层处理\n return false;\n}\n\n/**\n * 判断路由是否声明了\"菜单可见,但访问返回 403\"\n * @param route - 路由对象\n * @returns 是否为 menuVisibleWithForbidden 节点\n */\nfunction menuVisibleWithForbidden(route: AppRouteObject): boolean {\n const meta = route.meta as RouteMeta | undefined;\n\n // 有权限要求且显式声明了 menuVisibleWithForbidden\n return (\n !!meta?.authority?.length &&\n meta?.menuVisibleWithForbidden === true\n );\n}\n\nexport {hasPermission};\n","import {filterTree, mapTree} from '../../utils';\nimport type {AppMenu, AppRouteObject, RouteMeta} from \"../../router/types\";\n\n/**\n * 根据路由配置生成菜单列表\n * @param routes - 路由配置数组(AppRouteObject[],含 meta)\n * @param getAllRoutes - 获取完整路由树的函数(用于解析动态路径)\n * @returns 菜单树数组(AppMenu[]),可直接传给 ProLayout\n */\nexport async function generateMenus(\n routes: AppRouteObject[],\n getAllRoutes: () => AppRouteObject[]\n): Promise<AppMenu[]> {\n // 1. 构建路由映射:name → 最终 path(用于处理动态路由/重定向)\n const allRoutes = getAllRoutes();\n const routePathMap = buildRoutePathMap(allRoutes);\n\n // 2. 递归转换路由 → 菜单\n let menus = mapTree<AppRouteObject, AppMenu>(routes, (route): AppMenu => {\n return convertRouteToMenu(route, routePathMap);\n });\n\n // 3. 递归排序(按 meta.order)\n menus = sortMenus(menus);\n\n // 4. 过滤隐藏菜单(meta.hideInMenu)\n menus = filterTree<AppMenu>(menus, (menu) => menu.show !== false);\n\n return menus;\n}\n\n/**\n * 构建路由映射:name → 最终 path\n * 用于处理动态路由、重定向等场景\n */\nfunction buildRoutePathMap(routes: AppRouteObject[]): Record<string, string> {\n const map: Record<string, string> = {};\n\n const traverse = (routeList: AppRouteObject[], parentPath = '') => {\n routeList.forEach((route) => {\n // 计算完整路径(处理相对路径)\n const fullPath = route.path?.startsWith('/')\n ? route.path\n : `${parentPath}/${route.path}`.replace(/\\/+/g, '/');\n\n // 优先使用 name 作为 key,其次使用 path\n const key = route.name || route.path || '';\n if (key) {\n map[key] = fullPath;\n }\n\n // 递归处理子路由\n if (route.children?.length) {\n traverse(route.children, fullPath);\n }\n });\n };\n\n traverse(routes);\n return map;\n}\n\n/**\n * 单个路由 → 菜单项转换\n */\nfunction convertRouteToMenu(\n route: AppRouteObject,\n routePathMap: Record<string, string>\n): AppMenu {\n const meta = (route.meta || {}) as RouteMeta;\n\n // 解析路径:优先使用路由映射中的最终 path\n const resolvedPath = route.name && routePathMap[route.name]\n ? routePathMap[route.name]\n : route.path || '';\n\n // 提取菜单相关元数据(带默认值)\n const {\n title = '',\n icon,\n activeIcon,\n order,\n hideInMenu = false,\n hideChildrenInMenu = false,\n badge,\n badgeType,\n badgeVariants,\n link,\n redirect,\n menuVisibleWithForbidden,\n } = meta;\n\n // 菜单名称:title > name > path\n const name = title || route.name || resolvedPath || 'Unnamed';\n\n // 处理子菜单\n let children: AppMenu[] = [];\n if (route.children?.length && !hideChildrenInMenu) {\n children = route.children as unknown as AppMenu[]; // 递归时由 mapTree 处理\n }\n\n // 注入父子关系(用于面包屑/高亮)\n const parents = route.parents || [];\n const currentParents = [...parents, resolvedPath].filter(Boolean);\n\n // 决定菜单点击跳转路径\n // 优先级:link(外链) > redirect(重定向) > resolvedPath\n const menuPath = link || redirect || resolvedPath;\n\n return {\n // 必填字段\n name: route.name || resolvedPath, // 唯一标识\n path: menuPath, // 跳转路径\n label: name, // 显示名称\n\n // 可选字段\n icon,\n activeIcon,\n order,\n badge,\n badgeType,\n badgeVariants,\n disabled: meta.disabled,\n show: !hideInMenu,\n\n // 父子关系\n parent: route.parent || parents[parents.length - 1],\n parents: currentParents,\n\n // 子菜单(递归)\n children,\n\n // 权限/行为\n authority: meta.authority,\n menuVisibleWithForbidden,\n\n // 扩展字段\n meta, // 保留完整元数据供后续使用\n };\n}\n\n/**\n * 递归排序菜单树(按 order 字段)\n */\nfunction sortMenus(menuList: AppMenu[]): AppMenu[] {\n return menuList\n .map((menu) => ({\n ...menu,\n // 递归排序子菜单\n children: menu.children?.length ? sortMenus(menu.children) : [],\n }))\n .sort((a, b) => {\n // order 越小越靠前,undefined 排最后\n const orderA = a.order ?? Number.MAX_SAFE_INTEGER;\n const orderB = b.order ?? Number.MAX_SAFE_INTEGER;\n return orderA - orderB;\n });\n}\n\n/**\n * 工具:判断菜单是否应该显示(权限 + 配置)\n * 可用于二次过滤\n */\nexport const shouldShowMenu = (menu: AppMenu, permissions: string[]): boolean => {\n // 1. 配置隐藏\n if (menu.show === false) return false;\n\n // 2. 无权限要求 = 显示\n if (!menu.authority?.length) return true;\n\n // 3. 检查权限\n if (menu.authority.some(code => permissions.includes(code))) return true;\n\n // 4. 无权限但配置了可见 = 显示但禁用(由前端控制)\n return menu.menuVisibleWithForbidden === true;\n};\n","import { createElement } from 'react';\nimport { createBrowserRouter, Navigate, type RouteObject } from 'react-router-dom';\n\nimport { injectRedirects } from './utils/inject-redirect';\nimport { sortRoutes } from './utils/sort-routes';\nimport { transformRoutesWithHandle } from './utils/transform-meta-to-handle';\nimport type { GenerateMenuAndRoutesOptions, AppRoute, AppRouteObject } from './types';\nimport { generateRoutesByBackend, generateRoutesByFrontend } from '../router/generators';\nimport type { AccessModeType } from '../preferences';\n\n/**\n * 从路由列表中分离出:\n * - layoutRoutes: 包含 MainLayout/AuthGuard 的根路由(path='/')\n * - staticRoutes: 不受 AuthGuard 保护的静态路由(auth/login/error 等)\n */\nfunction separateRoutes(routes: AppRouteObject[]) {\n const layoutRoutes: AppRouteObject[] = []; // path='/' 的布局路由\n const otherRoutes: AppRouteObject[] = []; // 其他静态路由(auth/error等)\n\n for (const route of routes) {\n if (route.path === '/' && route.children) {\n layoutRoutes.push(route);\n } else {\n otherRoutes.push(route);\n }\n }\n\n return { layoutRoutes, otherRoutes };\n}\n\n/**\n * 取排序后首个可见菜单的落地路径:优先目录组 redirect(后端生成器已为缺省\n * 目录注入首个子路由全路径),无 redirect 则取首个子路由全路径;目录组子级\n * 全隐藏时递归下钻。全部落空返回 null(调用方回退 DEFAULT_HOME)。\n */\nfunction pickFirstMenuPath(routes: AppRouteObject[]): string | null {\n for (const route of routes) {\n if (route.meta?.hideInMenu) continue;\n if (route.redirect) return route.redirect;\n const firstChild =\n route.children?.find((c) => c.path && !c.meta?.hideInMenu) ??\n route.children?.find((c) => c.path);\n if (firstChild?.path) {\n return firstChild.path.startsWith('/')\n ? firstChild.path\n : `${route.path}/${firstChild.path}`;\n }\n if (route.children?.length) {\n const nested = pickFirstMenuPath(route.children);\n if (nested) return nested;\n }\n }\n return null;\n}\n\nexport const createAccessibleRouter = async (\n mode: AccessModeType,\n options: GenerateMenuAndRoutesOptions,\n): Promise<{ router: ReturnType<typeof createBrowserRouter>; routes: AppRouteObject[] }> => {\n let routes: AppRouteObject[] = [...options.routes];\n let isBackendMode = false;\n\n // 根据模式生成路由\n switch (mode) {\n case 'backend': {\n isBackendMode = true;\n // 后端模式:从 API 获取路由树,动态转换组件\n if (!options.fetchMenuListAsync) {\n console.warn('[Router] Backend mode requires fetchMenuListAsync, falling back to frontend mode');\n routes = await generateRoutesByFrontend(\n routes,\n options.permissions ?? [],\n options.forbiddenElement,\n );\n } else {\n // 分离布局路由与静态路由(auth/error 等不受 AuthGuard 保护)\n const { otherRoutes } = separateRoutes(routes);\n\n // 后端返回的路由树(根节点 component=\"BasicLayout\",已包含 Layout)。\n // 注意:后端生成器只消费 fetchMenuListAsync/layoutMap/pageMap\n const backendRoutes = await generateRoutesByBackend({\n fetchMenuListAsync: options.fetchMenuListAsync,\n layoutMap: options.layoutMap,\n pageMap: options.pageMap,\n });\n\n // 合并:后端路由 + 静态路由(auth/error)\n routes = [...backendRoutes, ...otherRoutes];\n }\n break;\n }\n case 'frontend':\n default: {\n // 前端模式:基于静态路由 + 权限过滤\n routes = await generateRoutesByFrontend(\n routes,\n options.permissions ?? [],\n options.forbiddenElement,\n );\n break;\n }\n }\n\n if (options.autoInjectRedirect !== false)\n routes = injectRedirects(routes as unknown as AppRoute[]) as unknown as AppRouteObject[];\n if (options.autoSort !== false)\n routes = sortRoutes(routes as unknown as AppRoute[]) as unknown as AppRouteObject[];\n\n // 后端模式没有 '/' 主布局容器(目录组各自挂实路径),补根落地路由重定向到\n // 首个可用菜单,对齐前端模式 '/' index Navigate(DEFAULT_HOME)的登录落地语义。\n // 兜底路径由宿主经 defaultHomePath 注入(原为包内硬编码 ROUTES.DEFAULT_HOME)。\n if (isBackendMode) {\n const homeTarget = pickFirstMenuPath(routes) ?? options.defaultHomePath;\n if (homeTarget) {\n routes = [\n {\n path: '/',\n element: createElement(Navigate, { to: homeTarget, replace: true }),\n meta: { title: 'routes:home', hideInMenu: true, hideInTab: true },\n },\n ...routes,\n ];\n } else {\n console.warn('[Router] Backend mode: no visible menu and no defaultHomePath provided, skip root landing route');\n }\n }\n\n // 将 meta 转换为 handle,使 useMatches() 能获取路由元数据\n routes = transformRoutesWithHandle(routes);\n\n const router = createBrowserRouter(routes as RouteObject[], {\n future: {\n v7_relativeSplatPath: true,\n },\n });\n // 路由树随 router 一并返回:侧栏(MainLayout→useMenuData)需镜像实际挂载的路由\n return { router, routes };\n};\n","import { createContext } from \"react\";\n\nimport type { AppRouteObject } from \"./types\";\n\n/**\n * 当前实际挂载的路由树(createAccessibleRouter 的产物):\n * 后端模式=后端下发路由;前端模式=权限过滤后的静态路由。\n * 侧栏等消费方必须镜像此树保证\"菜单即路由\"——后端模式下若回退静态全量表,\n * 会出现未授权菜单可见、点击 404 的越权观感。\n */\nexport const AccessibleRoutesContext = createContext<AppRouteObject[] | null>(null);\n","import type {RouteObject} from \"react-router-dom\";\nimport React, {type ComponentType} from \"react\";\nimport type {ReactNode} from \"react\";\n\nexport type IconName = string; // 图标名,如 \"UserOutlined\"\nexport type IconType = IconName | ComponentType<{ className?: string; style?: React.CSSProperties }>;\n\nexport type BadgeVariant = 'default' | 'primary' | 'success' | 'warning' | 'destructive' | string;\nexport type BadgeType = 'dot' | 'normal';\n\n// AccessModeType 唯一定义在 preferences/types/layout(偏好设置的消费方),\n// 此处 re-export 保持 router API 兼容,避免双定义在包 barrel 上产生导出冲突\nexport type { AccessModeType } from '../../preferences/types/layout';\n\n/**\n * 后端返回的路由对象(组件路径为字符串)\n */\nexport interface BackendRoute extends Omit<AppRouteObject, 'element' | 'children'> {\n component?: string;\n children?: BackendRoute[];\n}\n\n/**\n * generateRoutesByBackend 的选项。只声明后端生成器实际消费的字段——\n * 此前这里的 staticRoutes/mode 等字段从未被消费,形成\"传了也没用\"的误导性签名。\n */\nexport interface GenerateRoutesOptions {\n /** 从后端拉取菜单路由树(component 为字符串路径) */\n fetchMenuListAsync?: () => Promise<BackendRoute[]>;\n /** 布局组件映射(component 字符串 → React 组件) */\n layoutMap?: ComponentRecord;\n /** 页面组件映射(view 路径 → React 组件) */\n pageMap?: ComponentRecord;\n}\n\nexport interface GenerateRoutesResult {\n routes: AppRouteObject[];\n menus: AppMenu[];\n}\n\nexport interface RouteMeta {\n // 基础信息\n title?: string; // 必填:菜单/标签页标题\n icon?: IconType; // 菜单/标签页图标\n activeIcon?: IconType; // 激活时图标\n\n // 权限控制\n authority?: string[]; // 权限码数组(包含权限码和角色码,前端/后端路由统一使用)\n ignoreAccess?: boolean; // 忽略权限,公开访问\n\n // 菜单/标签页显示控制\n hideInMenu?: boolean; // 菜单中隐藏\n hideInTab?: boolean; // 标签页中隐藏\n hideInBreadcrumb?: boolean; // 面包屑中隐藏\n hideChildrenInMenu?: boolean; // 子路由不在菜单显示\n\n // 特殊行为\n menuVisibleWithForbidden?: boolean; // 菜单可见,但访问返回 403\n activePath?: string; // 手动指定激活的菜单路径\n affixTab?: boolean; // 固定标签页\n affixTabOrder?: number; // 固定标签页排序\n keepAlive?: boolean; // 页面缓存\n loaded?: boolean; // 路由是否已加载(内部使用)\n\n // 外部链接/iframe\n link?: string; // 外链跳转 URL\n iframeSrc?: string; // iframe 嵌入地址\n openInNewWindow?: boolean; // 新窗口打开\n\n // 其他\n order?: number; // 菜单排序\n query?: Record<string, any>; // 路由携带参数\n maxNumOfOpenTab?: number; // 标签页最大打开数(-1 无限制)\n\n // 菜单徽标\n badge?: string; // 徽标内容\n badgeType?: 'dot' | 'normal'; // 徽标类型\n badgeVariants?: BadgeVariant; // 徽标颜色\n\n // 其他扩展(保留灵活性)\n [key: string]: any;\n}\n\nexport interface AppRoute extends Omit<RouteObject, 'meta' | 'children' | 'element'> {\n // 必填字段\n name: string; // 路由唯一标识(权限匹配关键)\n path: string; // 路由路径(修正 any → string)\n\n // 可选字段\n redirect?: string;\n index?: boolean;\n\n // 元数据\n meta?: RouteMeta;\n\n // 子路由(递归)\n children?: AppRoute[];\n\n // React Router v6+ 核心:用 element 替代 component\n element?: ReactNode;\n\n // 扩展:后端返回的组件路径字符串(用于动态加载)\n componentPath?: string;\n\n // 面包屑/菜单辅助字段\n parent?: string;\n parents?: string[];\n}\n\n\n/**\n * AppRouteObject = RouteObject + meta + 业务字段\n * 用于替代原生的 RouteObject,携带业务元数据\n */\nexport interface AppRouteObject extends Omit<RouteObject, 'children'> {\n // 路由唯一标识(权限匹配关键)\n // 注:index 路由(index: true)不需要 name 和 path,所以改为可选\n name?: string;\n\n path?: string;\n\n // 元数据(核心扩展)\n meta?: RouteMeta;\n\n redirect?: string;\n\n icon?: IconType;\n activeIcon?: IconType;\n\n disabled?: boolean;\n show?: boolean;\n\n order?: number;\n\n // 子路由(递归使用 AppRouteObject)\n children?: AppRouteObject[];\n\n // 业务辅助字段\n parent?: string; // 父级路径\n parents?: string[]; // 所有父级路径(面包屑用)\n\n componentPath?: string; // 后端返回的组件路径字符串(动态加载用)\n\n element?: ReactNode;\n}\n\nexport interface AppMenuBadge {\n badge?: string;\n badgeType?: BadgeType;\n badgeVariants?: BadgeVariant;\n}\n\nexport interface AppMenu extends AppMenuBadge {\n // ========== 必填 ==========\n name: string; // 唯一标识(与路由 name 对应)\n path: string; // 点击跳转路径\n label: string; // 菜单显示名称\n\n // ========== 可选 ==========\n icon?: IconType;\n activeIcon?: IconType;\n\n order?: number; // 排序值(越小越前)\n\n badge?: string;\n badgeType?: 'dot' | 'normal';\n badgeVariants?: BadgeVariant;\n\n disabled?: boolean;\n show?: boolean; // 控制菜单渲染(默认 true)\n\n // 父子关系(用于面包屑/高亮)\n parent?: string;\n parents?: string[];\n\n // 子菜单\n children?: AppMenu[];\n\n // 权限控制\n authority?: string[];\n menuVisibleWithForbidden?: boolean;\n\n // 扩展:保留原始元数据\n meta?: RouteMeta;\n\n // 其他自定义字段\n [key: string]: any;\n}\n\n// React 组件映射类型\nexport type ComponentRecordType = Record<string, ComponentType<any>>;\nexport type ComponentRecord = Record<string, ComponentType<any>>;\n\nexport interface GenerateMenuAndRoutesOptions {\n fetchMenuListAsync?: () => Promise<BackendRoute[]>;\n forbiddenElement?: React.ReactNode;\n layoutMap?: ComponentRecordType;\n pageMap?: ComponentRecordType;\n permissions?: string[];\n roles?: string[];\n routes: AppRouteObject[];\n autoInjectRedirect?: boolean;\n autoSort?: boolean;\n /** 登录落地兜底路径(宿主注入,如 ROUTES.DEFAULT_HOME);后端模式下无可见菜单时使用 */\n defaultHomePath?: string;\n}\n\n\n/**\n * 路由模块的默认导出类型\n * 每个动态导入的路由文件应默认导出 AppRouteObject[]\n */\nexport interface RouteModuleType {\n default: AppRouteObject[];\n}\n\n/**\n * Vite glob 导入的动态模块类型\n * 兼容 .tsx/.ts 文件 + 默认导出\n */\nexport type RouteModuleRecord = Record<string, RouteModuleType | AppRouteObject[] | (() => Promise<RouteModuleType>)>;\n\nexport const isAppRoute = (route: any): route is AppRouteObject => {\n return route && typeof route.name === 'string' && typeof route.path === 'string';\n};\n\n\nexport interface MenuRecordBadgeRaw {\n /**\n * 徽标\n */\n badge?: string;\n /**\n * 徽标类型\n */\n badgeType?: \"dot\" | \"normal\";\n /**\n * 徽标颜色\n */\n badgeVariants?: \"destructive\" | \"primary\" | string;\n}\n\n/**\n * 菜单原始对象\n */\nexport interface MenuRecordRaw extends MenuRecordBadgeRaw {\n /**\n * 激活时的图标名\n */\n activeIcon?: string;\n /**\n * 子菜单\n */\n children?: MenuRecordRaw[];\n /**\n * 是否禁用菜单\n * @default false\n */\n disabled?: boolean;\n /**\n * 图标名\n */\n icon?: IconType;\n /**\n * 菜单名\n */\n name: string;\n /**\n * 排序号\n */\n order?: number;\n /**\n * 父级路径\n */\n parent?: string;\n /**\n * 所有父级路径\n */\n parents?: string[];\n /**\n * 菜单路径,唯一,可当作key\n */\n path: string;\n /**\n * 是否显示菜单\n * @default true\n */\n show?: boolean;\n}\n","import { Suspense, lazy, type ComponentType } from 'react';\nimport { Spin } from 'antd';\n\n/**\n * 创建懒加载路由元素\n * 自动包裹 Suspense + Spin fallback\n *\n * @param loader Vite 动态导入函数,如 () => import('@/pages/dashboard')\n * @returns 可直接赋值给 route.element 的 ReactNode\n */\nexport const createLazyRoute = (loader: () => Promise<{ default: ComponentType<any> }>) => {\n const LazyComponent = lazy(loader);\n\n return (\n <Suspense fallback={<Spin size=\"large\" style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100%' }} />}>\n <LazyComponent />\n </Suspense>\n );\n};\n","import type { AppRouteObject } from '../types';\nimport { isDevMode } from '../../env';\n\n/**\n * 展平模块导出(兼容 default / 具名导出)\n * 纯函数,无副作用,可单元测试\n */\nexport const extractRoutes = (module: any): AppRouteObject[] => {\n if (Array.isArray(module?.default)) return module.default;\n\n const namedExport = Object.values(module).find(\n (val: any) => Array.isArray(val) && val.length > 0,\n );\n\n return Array.isArray(namedExport) ? namedExport : [];\n};\n\n/**\n * 过滤无效路由(开发环境调试用)\n */\nexport const filterValidRoutes = (routes: AppRouteObject[]): AppRouteObject[] => {\n if (!isDevMode()) return routes;\n\n return routes.filter((route) => {\n if (!route.path) {\n console.warn(`[router] Route missing \"path\":`, route);\n return false;\n }\n if (!route.name && route.path !== '*') {\n console.warn(`[router] Route missing \"name\":`, route);\n }\n return true;\n });\n};\n\n/**\n * 根据权限过滤路由树\n */\nexport const filterRoutesByPermission = (\n routes: AppRouteObject[],\n permissions: string[],\n): AppRouteObject[] => {\n return routes.reduce<AppRouteObject[]>((acc, route) => {\n // 检查当前路由权限\n const hasPerm = !route.meta?.authority?.length || route.meta.authority.some((code) => permissions.includes(code));\n if (!hasPerm && !route.meta?.menuVisibleWithForbidden) {\n return acc; // 无权限且不可见 = 过滤\n }\n\n // 递归处理子路由\n const filtered = { ...route };\n if (route.children?.length) {\n const childFiltered = filterRoutesByPermission(route.children, permissions);\n if (childFiltered.length > 0 || !route.meta?.hideChildrenInMenu) {\n filtered.children = childFiltered;\n }\n }\n\n acc.push(filtered);\n return acc;\n }, []);\n};\n","import type { ProLayoutProps } from '@ant-design/pro-components';\nimport type { AppRouteObject } from '../../router/types';\n\ntype MenuRoute = AppRouteObject;\n\n/**\n * 递归排序菜单树(按 order 字段)\n */\nconst sortMenuTree = (menuList: any[]): any[] => {\n return menuList\n .map((menu) => ({\n ...menu,\n // 递归排序子菜单\n children: menu.children?.length ? sortMenuTree(menu.children) : undefined,\n }))\n .sort((a, b) => {\n // order 越小越靠前,undefined 排最后\n const orderA = a.order ?? Number.MAX_SAFE_INTEGER;\n const orderB = b.order ?? Number.MAX_SAFE_INTEGER;\n return orderA - orderB;\n });\n};\n\nexport const transformRoutesToMenu = (\n routes: MenuRoute[],\n permissions: string[],\n parentPath: string = '',\n): NonNullable<ProLayoutProps['route']>['routes'] => {\n const menus = routes\n .filter((route) => {\n // 过滤隐藏菜单\n if (route.meta?.hideInMenu) return false;\n\n const meta = route.meta;\n // 权限过滤与路由闸(generate-routes-frontend.hasPermission)同语义:\n // 无权限要求=保留;持有任一权限码=保留;两者皆否但声明了\n // menuVisibleWithForbidden 的路由保留进菜单(点击后由路由侧渲染 403),\n // 对齐 vue-vben / vue-element 的\"菜单可见但禁止访问\"\n if (!meta?.authority?.length) return true;\n if (meta.authority.some((code: string) => permissions.includes(code))) return true;\n return meta?.menuVisibleWithForbidden === true;\n })\n .map((route) => {\n // 处理路径:将相对路径转换为绝对路径\n const fullPath = route.path?.startsWith('/')\n ? route.path\n : `${parentPath}/${route.path}`.replace(/\\/+/g, '/');\n\n const menuItem: any = {\n path: fullPath, // 使用完整路径作为 key\n // label 来自后端模式的原始路由字段(AppRouteObject 无此声明),缺省回退 meta.title\n name: (route as MenuRoute & { label?: string }).label || route.meta?.title,\n icon: route.meta?.icon,\n order: route.meta?.order, // 提取排序字段\n };\n\n if (route.children) {\n menuItem.children = transformRoutesToMenu(route.children, permissions, fullPath);\n }\n\n return menuItem;\n })\n .filter(Boolean);\n\n // 排序菜单树\n return sortMenuTree(menus);\n};\n","import type {AppRouteObject, RouteMeta, RouteModuleRecord, RouteModuleType} from \"../../router\";\nimport {isDevMode} from \"../../env\";\n\n\n/**\n * 合并动态路由模块的默认导出\n *\n * @param routeModules - Vite import.meta.glob 返回的模块对象\n * @param options - 可选配置\n * @returns 合并后的路由配置数组(AppRouteObject[])\n *\n * @example\n *\n */\nexport function mergeRouteModules(\n routeModules: RouteModuleRecord,\n options?: {\n /** 是否过滤空模块 */\n filterEmpty?: boolean;\n /** 是否按路径排序 */\n sortByPath?: boolean;\n }\n): AppRouteObject[] {\n const {filterEmpty = true, sortByPath = false} = options || {};\n\n const mergedRoutes: AppRouteObject[] = [];\n\n for (const [modulePath, routeModule] of Object.entries(routeModules)) {\n try {\n // 兼容多种导出形式\n let moduleRoutes: AppRouteObject[] = [];\n\n if (typeof routeModule === 'function') {\n // 异步模块:跳过(需用 mergeRouteModulesAsync)\n continue;\n } else if (Array.isArray(routeModule)) {\n // 直接导出数组\n moduleRoutes = routeModule;\n } else if (routeModule && typeof routeModule === 'object') {\n // 默认导出对象\n moduleRoutes = (routeModule as RouteModuleType).default || [];\n }\n\n // 过滤空模块\n if (filterEmpty && moduleRoutes.length === 0) {\n continue;\n }\n\n // 可选:注入模块路径信息(便于调试,dev 标志由宿主运行时注入)\n if (isDevMode()) {\n moduleRoutes.forEach((route) => {\n if (!route.meta) {\n route.meta = {} as RouteMeta;\n }\n (route.meta as any).__modulePath = modulePath;\n });\n }\n\n mergedRoutes.push(...moduleRoutes);\n } catch (error) {\n console.warn(`Failed to load route module: ${modulePath}`, error);\n }\n }\n\n // 可选:按路径字典序排序\n if (sortByPath) {\n mergedRoutes.sort((a, b) => (a.path || '').localeCompare(b.path || ''));\n }\n\n return mergedRoutes;\n}\n\n/**\n * 异步合并动态路由模块(用于 lazy 加载)\n * @param routeModules - Vite import.meta.glob 返回的异步模块对象\n * @returns Promise<AppRouteObject[]>\n */\nexport async function mergeRouteModulesAsync(\n routeModules: Record<string, () => Promise<RouteModuleType | AppRouteObject[]>>\n): Promise<AppRouteObject[]> {\n const results = await Promise.all(\n Object.values(routeModules).map(async (loader) => {\n try {\n const module = await loader();\n if (Array.isArray(module)) {\n return module;\n }\n return (module as RouteModuleType).default || [];\n } catch (error) {\n console.warn('Failed to async load route module:', error);\n return [];\n }\n })\n );\n\n return results.flat();\n}\n","import {traverseTreeValues} from '../../utils';\nimport type {AppRouteObject} from \"../../router/types\";\n\n/**\n * 重置可访问路由(保留白名单)\n *\n * ⚠️ React Router v6+ 不支持运行时修改路由,\n * 此函数返回过滤后的路由树,需配合 createBrowserRouter 重新创建 router\n *\n * @param allRoutes - 当前完整路由树(含动态路由)\n * @param staticRouteNames - 静态路由白名单(保留这些路由)\n * @returns 过滤后的路由树(仅含白名单路由)\n */\nexport function resetAccessibleRoutes(\n allRoutes: AppRouteObject[],\n staticRouteNames: string[]\n): AppRouteObject[] {\n // 1. 构建白名单 Set(O(1) 查找)\n const whitelist = new Set(staticRouteNames.filter(Boolean));\n\n // 2. 递归过滤路由树\n return filterRoutesByWhitelist(allRoutes, whitelist);\n}\n\n/**\n * 递归过滤路由树,仅保留白名单中的路由\n */\nfunction filterRoutesByWhitelist(\n routes: AppRouteObject[],\n whitelist: Set<string>\n): AppRouteObject[] {\n return routes.reduce<AppRouteObject[]>((acc, route) => {\n // 路由必须有 name 才能被过滤\n if (!route.name) {\n console.warn(`Route with path \"${route.path}\" is missing a \"name\" field and will be skipped.`);\n return acc;\n }\n\n // 如果当前路由在白名单中,保留它并递归处理子路由\n if (whitelist.has(route.name)) {\n const filtered = {...route};\n\n if (route.children?.length) {\n const childFiltered = filterRoutesByWhitelist(route.children, whitelist);\n if (childFiltered.length > 0) {\n filtered.children = childFiltered;\n } else {\n // 子路由全被过滤,但父路由在白名单中:保留父路由但清空 children\n filtered.children = undefined;\n }\n }\n\n acc.push(filtered);\n }\n // 如果当前路由不在白名单,直接跳过(及其所有子路由)\n\n return acc;\n }, []);\n}\n\n/**\n * 提取路由树中所有有效的 name(用于生成白名单)\n * @param routes - 路由树\n * @returns 有效的路由 name 数组\n */\nexport function extractRouteNames(routes: AppRouteObject[]): string[] {\n return traverseTreeValues<AppRouteObject, string | undefined>(\n routes,\n (route) => {\n if (!route.name) {\n console.warn(`Route with path \"${route.path}\" is missing a \"name\" field.`);\n return undefined;\n }\n return route.name;\n }\n ).filter((name): name is string => Boolean(name));\n}\n","import { useEffect } from 'react';\n\nexport function useTitle(title: string) {\n useEffect(() => {\n const prev = document.title;\n document.title = title;\n return () => {\n document.title = prev; // 离开时恢复\n };\n }, [title]);\n}\n","import {useEffect, useState, useRef} from 'react';\nimport dayjs from 'dayjs';\nimport {useTranslation} from 'react-i18next';\n\n// 时间格式随界面语言切换:中文“年月日”,其他语言用 ISO 风格\nconst timeFormat = (language: string | undefined) =>\n (language ?? '').startsWith('zh') ? 'YYYY年MM月DD日 HH:mm:ss' : 'YYYY-MM-DD HH:mm:ss';\n\n/**\n * @description 获取本地时间\n */\nexport const useTimes = () => {\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const {i18n} = useTranslation();\n const [time, setTime] = useState(() => dayjs().format(timeFormat(i18n.language)));\n\n useEffect(() => {\n setTime(dayjs().format(timeFormat(i18n.language)));\n\n timer.current = setInterval(() => {\n setTime(dayjs().format(timeFormat(i18n.language)));\n }, 1000);\n\n return () => {\n if (timer.current) {\n clearInterval(timer.current);\n timer.current = null;\n }\n };\n }, [i18n.language]);\n\n return {\n time,\n };\n};\n","import { useEffect, type RefObject } from 'react';\n\n/**\n * 为 ProTable 的 .ant-table-body 动态设置高度,使分页器固定在容器底部。\n *\n * 核心思路(参考 CSDN 验证可行的方案):\n * 1. scroll.y 给一个初始像素值,让 antd 创建 .ant-table-body 滚动结构\n * 2. JS 测量容器高度、分页器位置,直接操作 .ant-table-body.style.maxHeight\n * 3. 不依赖 CSS flex 穿透 antd 内部 DOM(因为 antd 内部层级不可控)\n *\n * 注意:列表页通常不需要直接使用本 hook——ListTable 已内置同样的撑满逻辑\n * (页面不传 scroll.y 时自动接管)。手写样板会被 eslint 警告。\n *\n * @param containerRef - .page-container-content 容器 div 的 ref\n * @param options.enabled - 是否启用(默认 true)。ListTable 内部用它做按需接管。\n * @param options.requireFlexParent - 要求容器父级必须是 flex column(默认 false,\n * 即维持历史行为)。ListTable 的自动撑满开它:外层不是 ContentContainer 这类\n * flex 列容器(如 Drawer 内嵌表)时不接管,避免把自然高度的表压瘪。\n */\nexport function useProTableScrollY(\n containerRef: RefObject<HTMLElement | null>,\n options?: { enabled?: boolean; requireFlexParent?: boolean },\n): string {\n const { enabled = true, requireFlexParent = false } = options ?? {};\n useEffect(() => {\n if (!enabled) return;\n let rafId = 0;\n // 容器可能晚于本 hook 挂载(如 Drawer 首帧不渲染 children):\n // 先观察 body 等容器出现,resize() 内再把观察迁移到真实容器。\n let observedContainer: HTMLElement | null = null;\n\n const resize = () => {\n const container = containerRef.current;\n if (!container) return;\n\n if (requireFlexParent) {\n // 外层不是可撑高的 flex 列容器(无确定高度的普通块级父级)时,\n // 按 containerHeight-natural 的差值改写 body 会把表越压越瘪,直接不接管。\n const parentStyle = container.parentElement ? getComputedStyle(container.parentElement) : null;\n if (!parentStyle || !parentStyle.display.includes('flex') || parentStyle.flexDirection !== 'column') {\n return;\n }\n }\n\n if (observedContainer !== container) {\n observedContainer = container;\n resizeObserver.disconnect();\n resizeObserver.observe(container);\n mutationObserver.disconnect();\n mutationObserver.observe(container, { childList: true, subtree: true });\n }\n\n cancelAnimationFrame(rafId);\n rafId = requestAnimationFrame(() => {\n const tableBody = container.querySelector('.ant-table-body') as HTMLElement | null;\n if (!tableBody) return;\n\n const containerHeight = container.clientHeight;\n if (containerHeight <= 0) return;\n\n const containerRect = container.getBoundingClientRect();\n const bodyRect = tableBody.getBoundingClientRect();\n\n // 表格体上方的空间(搜索栏 + 工具栏 + 表头 + 所有间距)\n const aboveSpace = bodyRect.top - containerRect.top;\n\n // 分页器占用的空间(高度 + margin)\n const pagination = container.querySelector('.ant-pagination') as HTMLElement | null;\n let belowSpace = 0;\n if (pagination) {\n const pagStyle = getComputedStyle(pagination);\n belowSpace =\n pagination.offsetHeight +\n (parseFloat(pagStyle.marginTop) || 0) +\n (parseFloat(pagStyle.marginBottom) || 0);\n }\n\n // 安全边距(给分页器留出视觉呼吸空间,确保表格底线完整可见)\n const buffer = 24;\n\n // 计算表格体可用高度\n const availableHeight = Math.max(containerHeight - aboveSpace - belowSpace - buffer, 100);\n tableBody.style.maxHeight = `${availableHeight}px`;\n tableBody.style.minHeight = `${availableHeight}px`;\n\n // 空列表时占位行只占自然高度,被 minHeight 撑高的 body 会露出大段白底。\n // body 高度是 min/max 撑出来的(height 仍为 auto),内层表格写 height:100% 解析不到,\n // 必须给显式像素值;有数据时还原,行高由内容决定。\n const bodyTable = tableBody.querySelector(':scope > table') as HTMLElement | null;\n if (bodyTable) {\n const isEmpty = !!tableBody.querySelector('.ant-table-placeholder');\n bodyTable.style.height = isEmpty ? `${availableHeight}px` : '';\n }\n\n // 根据内容是否溢出动态切换 overflow-y\n // antd 默认设置 overflow-y: scroll,在 Windows 上会始终显示滚动条轨道\n // 内容不超出时用 hidden 隐藏滚动条,超出时用 auto 按需显示\n if (tableBody.scrollHeight <= tableBody.clientHeight) {\n tableBody.style.overflowY = 'hidden';\n } else {\n tableBody.style.overflowY = 'auto';\n }\n });\n };\n\n // 多次延迟重试,确保 ProTable 异步渲染完成\n const timers = [\n setTimeout(resize, 50),\n setTimeout(resize, 200),\n setTimeout(resize, 500),\n setTimeout(resize, 1500),\n ];\n\n // 监听容器尺寸变化\n const resizeObserver = new ResizeObserver(resize);\n // 监听 DOM 变化(antd 重新渲染)\n const mutationObserver = new MutationObserver(resize);\n if (containerRef.current) {\n observedContainer = containerRef.current;\n resizeObserver.observe(containerRef.current);\n mutationObserver.observe(containerRef.current, { childList: true, subtree: true });\n } else {\n // 容器尚未挂载(Drawer 首帧不渲染 children):观察 body,等 resize() 迁移\n mutationObserver.observe(document.body, { childList: true, subtree: true });\n }\n\n return () => {\n cancelAnimationFrame(rafId);\n timers.forEach(clearTimeout);\n resizeObserver.disconnect();\n mutationObserver.disconnect();\n };\n }, [containerRef, enabled, requireFlexParent]);\n\n // 返回初始像素值,仅用于触发 antd 创建 .ant-table-body\n return '100px';\n}\n"],"ignoreList":[44],"mappings":"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"}
|