From dc7f2d04834d2adadf601a5b68b4841a889b923f Mon Sep 17 00:00:00 2001 From: Zyy Date: Tue, 16 Jun 2026 17:36:42 +0800 Subject: [PATCH] feat: restore frontend login page Co-authored-by: Codex --- web/env.d.ts | 3 + web/index.html | 8 +- web/package.json | 3 + web/pnpm-lock.yaml | 44 ++++ web/src/components/base/BaseBadge.vue | 14 +- web/src/components/base/BaseButton.vue | 18 +- web/src/components/base/BaseCard.vue | 12 +- web/src/components/base/BaseEmpty.vue | 8 +- web/src/components/base/BaseIconButton.vue | 4 +- web/src/components/base/BaseInput.vue | 12 +- web/src/components/base/BaseModal.vue | 8 +- web/src/components/base/BaseScrollbar.vue | 239 ++++++++++++++++++ web/src/components/base/BaseSegmented.vue | 6 +- web/src/components/base/BaseSelect.vue | 6 +- web/src/components/base/BaseSpinner.vue | 4 +- web/src/components/base/BaseTable.vue | 12 +- web/src/components/base/BaseToast.vue | 18 +- web/src/layout/components/AppSidebar.vue | 97 +++++-- web/src/layout/components/AppTopbar.vue | 74 +++--- web/src/layout/index.vue | 19 +- web/src/main.ts | 2 + web/src/router/index.ts | 2 +- web/src/router/routes.ts | 6 + web/src/stores/index.ts | 3 + web/src/stores/ui.ts | 24 ++ web/src/styles/base.css | 2 +- web/src/styles/index.css | 4 +- web/src/views/dashboard/DashboardView.vue | 10 +- .../components/AccountOverviewCard.vue | 128 +++++----- .../dashboard/components/EventLogCard.vue | 42 +-- .../views/dashboard/components/MetricCard.vue | 34 +-- .../dashboard/components/RequestTrendCard.vue | 41 ++- .../components/ServiceStatusCard.vue | 34 +-- web/src/views/login/LoginView.vue | 160 ++++++++++++ web/tests/root-routing.test.mjs | 20 ++ web/vite.config.ts | 2 +- 36 files changed, 847 insertions(+), 276 deletions(-) create mode 100644 web/src/components/base/BaseScrollbar.vue create mode 100644 web/src/stores/ui.ts create mode 100644 web/src/views/login/LoginView.vue create mode 100644 web/tests/root-routing.test.mjs diff --git a/web/env.d.ts b/web/env.d.ts index 11f02fe2a..d7b433c2d 100644 --- a/web/env.d.ts +++ b/web/env.d.ts @@ -1 +1,4 @@ /// + +declare module '@fontsource-variable/inter' +declare module '@fontsource-variable/jetbrains-mono' diff --git a/web/index.html b/web/index.html index 4bef0543b..4b99e07db 100644 --- a/web/index.html +++ b/web/index.html @@ -3,13 +3,7 @@ - - - - + Codex Proxy RS diff --git a/web/package.json b/web/package.json index 4fa50fc80..086fd8803 100644 --- a/web/package.json +++ b/web/package.json @@ -10,11 +10,14 @@ "preview": "vite preview" }, "dependencies": { + "@fontsource-variable/inter": "^5.2.8", + "@fontsource-variable/jetbrains-mono": "^5.2.8", "@lucide/vue": "^1.18.0", "axios": "^1.18.0", "echarts": "^6.1.0", "gsap": "^3.15.0", "pinia": "^3.0.4", + "pinia-plugin-persistedstate": "^4.7.1", "vue": "^3.5.38", "vue-router": "^5.1.0" }, diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index d9aa1bc6e..853aef20e 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -8,6 +8,12 @@ importers: .: dependencies: + '@fontsource-variable/inter': + specifier: ^5.2.8 + version: 5.2.8 + '@fontsource-variable/jetbrains-mono': + specifier: ^5.2.8 + version: 5.2.8 '@lucide/vue': specifier: ^1.18.0 version: 1.18.0(vue@3.5.38(typescript@6.0.3)) @@ -23,6 +29,9 @@ importers: pinia: specifier: ^3.0.4 version: 3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)) + pinia-plugin-persistedstate: + specifier: ^4.7.1 + version: 4.7.1(pinia@3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3))) vue: specifier: ^3.5.38 version: 3.5.38(typescript@6.0.3) @@ -104,6 +113,12 @@ packages: '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + '@fontsource-variable/inter@5.2.8': + resolution: {integrity: sha512-kOfP2D+ykbcX/P3IFnokOhVRNoTozo5/JxhAIVYLpea/UBmCQ/YWPBfWIDuBImXX/15KH+eKh4xpEUyS2sQQGQ==} + + '@fontsource-variable/jetbrains-mono@5.2.8': + resolution: {integrity: sha512-WBA9elru6Jdp5df2mES55wuOO0WIrn3kpXnI4+W2ek5u3ZgLS9XS4gmIlcQhiZOWEKl95meYdvK7xI+ETLCq/Q==} + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -484,6 +499,9 @@ packages: supports-color: optional: true + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -747,6 +765,20 @@ packages: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} + pinia-plugin-persistedstate@4.7.1: + resolution: {integrity: sha512-WHOqh2esDlR3eAaknPbqXrkkj0D24h8shrDPqysgCFR6ghqP/fpFfJmMPJp0gETHsvrh9YNNg6dQfo2OEtDnIQ==} + peerDependencies: + '@nuxt/kit': '>=3.0.0' + '@pinia/nuxt': '>=0.10.0' + pinia: '>=3.0.0' + peerDependenciesMeta: + '@nuxt/kit': + optional: true + '@pinia/nuxt': + optional: true + pinia: + optional: true + pinia@3.0.4: resolution: {integrity: sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==} peerDependencies: @@ -978,6 +1010,10 @@ snapshots: tslib: 2.8.1 optional: true + '@fontsource-variable/inter@5.2.8': {} + + '@fontsource-variable/jetbrains-mono@5.2.8': {} + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -1330,6 +1366,8 @@ snapshots: dependencies: ms: 2.1.3 + defu@6.1.7: {} + delayed-stream@1.0.0: {} detect-libc@2.1.2: {} @@ -1539,6 +1577,12 @@ snapshots: picomatch@4.0.4: {} + pinia-plugin-persistedstate@4.7.1(pinia@3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3))): + dependencies: + defu: 6.1.7 + optionalDependencies: + pinia: 3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)) + pinia@3.0.4(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3)): dependencies: '@vue/devtools-api': 7.7.9 diff --git a/web/src/components/base/BaseBadge.vue b/web/src/components/base/BaseBadge.vue index 11d2fe8da..149959561 100644 --- a/web/src/components/base/BaseBadge.vue +++ b/web/src/components/base/BaseBadge.vue @@ -12,16 +12,16 @@ const props = withDefaults(defineProps<{ }) const variantClasses: Record = { - default: 'bg-[var(--cp-default-bg)] text-[var(--cp-text-secondary)]', - normal: 'bg-[var(--cp-normal-bg)] text-[var(--cp-normal-text)]', - success: 'bg-[var(--cp-success-bg)] text-[var(--cp-success-text)]', - warning: 'bg-[var(--cp-warning-bg)] text-[var(--cp-warning-text)]', - danger: 'bg-[var(--cp-danger-bg)] text-[var(--cp-danger-text)]', - info: 'bg-[var(--cp-info-bg)] text-[var(--cp-info-text)]', + default: 'bg-(--cp-default-bg) text-(--cp-text-secondary)', + normal: 'bg-(--cp-normal-bg) text-(--cp-normal-text)', + success: 'bg-(--cp-success-bg) text-(--cp-success-text)', + warning: 'bg-(--cp-warning-bg) text-(--cp-warning-text)', + danger: 'bg-(--cp-danger-bg) text-(--cp-danger-text)', + info: 'bg-(--cp-info-bg) text-(--cp-info-text)', } const badgeClasses = computed(() => [ - 'inline-flex min-h-[26px] items-center justify-center gap-[7px] whitespace-nowrap rounded-[var(--cp-tag-radius)] px-2.5 text-xs font-semibold leading-none', + 'inline-flex min-h-[26px] items-center justify-center gap-[7px] whitespace-nowrap rounded-(--cp-tag-radius) px-2.5 text-xs font-semibold leading-none', variantClasses[props.variant], ]) diff --git a/web/src/components/base/BaseButton.vue b/web/src/components/base/BaseButton.vue index 0519553c5..1a7f5a8b0 100644 --- a/web/src/components/base/BaseButton.vue +++ b/web/src/components/base/BaseButton.vue @@ -22,21 +22,21 @@ const props = withDefaults(defineProps<{ const sizeClasses: Record = { large: 'h-10 px-4 text-[13px]', default: 'h-8 px-3.5 text-[13px]', - small: 'h-6 rounded-[var(--cp-button-radius-small)] px-2.5 text-xs', + small: 'h-6 rounded-(--cp-button-radius-small) px-2.5 text-xs', } const variantClasses: Record = { - default: 'bg-[var(--cp-default-bg)] text-[var(--cp-default-text)] hover:bg-[var(--cp-default-bg-hover)]', - primary: 'bg-[var(--cp-info)] text-[var(--cp-info-on)] hover:bg-[var(--cp-info-hover)]', - success: 'bg-[var(--cp-success-bg)] text-[var(--cp-success-text)] hover:bg-[var(--cp-success-bg-hover)]', - warning: 'bg-[var(--cp-warning-bg)] text-[var(--cp-warning-text)] hover:bg-[var(--cp-warning-bg-hover)]', - danger: 'bg-[var(--cp-danger-bg)] text-[var(--cp-danger-text)] hover:bg-[var(--cp-danger-bg-hover)]', - plain: 'bg-[var(--cp-info-bg)] text-[var(--cp-info-text)] hover:bg-[var(--cp-info-bg-hover)]', - text: 'h-auto bg-transparent p-0 text-[var(--cp-info-text)] hover:text-[var(--cp-info-hover)]', + default: 'bg-(--cp-default-bg) text-(--cp-default-text) hover:bg-(--cp-default-bg-hover)', + primary: 'bg-(--cp-info) text-(--cp-info-on) hover:bg-(--cp-info-hover)', + success: 'bg-(--cp-success-bg) text-(--cp-success-text) hover:bg-(--cp-success-bg-hover)', + warning: 'bg-(--cp-warning-bg) text-(--cp-warning-text) hover:bg-(--cp-warning-bg-hover)', + danger: 'bg-(--cp-danger-bg) text-(--cp-danger-text) hover:bg-(--cp-danger-bg-hover)', + plain: 'bg-(--cp-info-bg) text-(--cp-info-text) hover:bg-(--cp-info-bg-hover)', + text: 'h-auto bg-transparent p-0 text-(--cp-info-text) hover:text-(--cp-info-hover)', } const buttonClasses = computed(() => [ - 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-[var(--cp-button-radius-base)] border-0 font-bold leading-none transition-colors disabled:cursor-not-allowed disabled:bg-[var(--cp-disabled-bg)] disabled:text-[var(--cp-disabled-text)]', + 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-(--cp-button-radius-base) border-0 font-bold leading-none transition-colors disabled:cursor-not-allowed disabled:bg-(--cp-disabled-bg) disabled:text-(--cp-disabled-text)', sizeClasses[props.size], variantClasses[props.variant], ]) diff --git a/web/src/components/base/BaseCard.vue b/web/src/components/base/BaseCard.vue index a23e4d1b0..464cb94ce 100644 --- a/web/src/components/base/BaseCard.vue +++ b/web/src/components/base/BaseCard.vue @@ -5,27 +5,29 @@ const props = withDefaults(defineProps<{ description?: string padded?: boolean radiusClass?: string + shadowClass?: string }>(), { as: 'section', title: undefined, description: undefined, padded: true, - radiusClass: 'rounded-[var(--cp-card-radius)]', + radiusClass: 'rounded-(--cp-card-radius)', + shadowClass: 'shadow-(--cp-shadow-card)', })