| | |
| | | <script lang="ts" setup> |
| | | import { computed, onMounted } from 'vue'; |
| | | import { computed, onMounted, ref } from 'vue'; |
| | | |
| | | import { AuthPageLayout } from '@vben/layouts'; |
| | | import { preferences } from '@vben/preferences'; |
| | | |
| | | import { $t } from '#/locales'; |
| | | import { useCompanyConfigStore } from '#/store/company-config'; |
| | | |
| | | declare const __APP_LOGO__: string; |
| | | declare const __APP_NAME__: string; |
| | | |
| | | const companyConfigStore = useCompanyConfigStore(); |
| | | |
| | |
| | | companyConfigStore.loadFromServer(); |
| | | }); |
| | | |
| | | const appName = computed(() => preferences.copyright.companyName); |
| | | const appName = computed( |
| | | () => preferences.copyright.companyName || __APP_NAME__ || 'åº·ææºè½', |
| | | ); |
| | | |
| | | // ç»å½é¡µ logo ä¼å
åæ°æ®åºé
ç½®ï¼preferences.logo.sourceï¼ï¼æªé
ç½®æ¶åéç¼è¯æé»è®¤ __APP_LOGO__ |
| | | const logo = computed(() => { |
| | | const source = preferences.logo.source || __APP_LOGO__; |
| | | // æªé
ç½®å
¬å¸æ å¿æ¶ä¸å±ç¤ºæ¡æ¶æ¼ç¤º Logoã |
| | | return source?.includes('@vbenjs/static-source') ? '' : source; |
| | | }); |
| | | const logoDark = computed(() => preferences.logo.sourceDark); |
| | | // æ é¢/æè¿°ä¼å
åå
¬å¸ä¿¡æ¯é
ç½®ï¼æªé
ç½®æ¶åéæ åææ¡ |
| | | const pageTitle = computed( |
| | | () => companyConfigStore.loginTitle || $t('authentication.pageTitle'), |
| | | ); |
| | | const pageDescription = computed( |
| | | () => companyConfigStore.loginDescription || $t('authentication.pageDesc'), |
| | | ); |
| | | |
| | | const DEFAULT_DESCRIPTION = |
| | | 'è¦ç计åãç产ãè´¨éãä»å¨ä¸è®¾å¤æ°éçä¸ä½åå·¥ä¸ AI æºé å¹³å°'; |
| | | |
| | | /** å»æç©ºç½ä¸æ ç¹ï¼ç¨äºå¤æé
ç½®ææ¡æ¯å¦åªæ¯æåçåååäºä¸é */ |
| | | function normalizeText(text: string) { |
| | | return text.replace(/[\s·ã,ï¼.ãâ\-_/]/g, ''); |
| | | } |
| | | |
| | | /** ä¸¤æ®µææ¡çå
Œ
±åç¼é¿åº¦ */ |
| | | function commonPrefixLength(a: string, b: string) { |
| | | const limit = Math.min(a.length, b.length); |
| | | let index = 0; |
| | | while (index < limit && a[index] === b[index]) { |
| | | index += 1; |
| | | } |
| | | return index; |
| | | } |
| | | |
| | | const pageDescription = computed(() => { |
| | | const custom = companyConfigStore.loginDescription?.trim(); |
| | | if (!custom) { |
| | | return DEFAULT_DESCRIPTION; |
| | | } |
| | | // å端æ loginDescription å¡«æäºãåº·ææºé å¹³å°ãè¿ç±»å
¬å¸åå使¶ï¼ |
| | | // 主æ é¢ä¸ä¼ç¬¬äºæ¬¡åºç°åçåï¼è¿éæ¹ç¨é»è®¤ä»ç» |
| | | const text = normalizeText(custom); |
| | | const brand = normalizeText(appName.value); |
| | | const looksLikeBrand = |
| | | text.length <= 12 && commonPrefixLength(text, brand) >= 3; |
| | | return looksLikeBrand ? DEFAULT_DESCRIPTION : custom; |
| | | }); |
| | | |
| | | // å端å¯è½ä¸ä¼ ã徿 +æåãçæ¨ªçç»å logoï¼å¡è¿æ¹å½¢æ¡ä¼ç¼©æä¸å¢ï¼æ¤æ¶æ¹ä¸ºæ´å¼ å±ç¤ºå¹¶ä¸åéå¤åçå |
| | | const logoIsLockup = ref(false); |
| | | |
| | | function handleLogoLoad(event: Event) { |
| | | const img = event.target as HTMLImageElement; |
| | | logoIsLockup.value = |
| | | img.naturalWidth > 0 && img.naturalWidth / img.naturalHeight > 1.8; |
| | | } |
| | | |
| | | const year = new Date().getFullYear(); |
| | | |
| | | const highlights = [ |
| | | { title: 'ç产éæ', desc: '订åãå·¥åä¸è´¨æ£è¿åº¦å®æ¶å¯è§' }, |
| | | { title: 'æ°æ®é©±å¨', desc: 'è®¾å¤æ°éä¸ç»è¥ææ ä¸ä½ååæ' }, |
| | | { title: 'æºè½åå', desc: 'æé ERP / MES / WMS ä¸å¡é¾è·¯' }, |
| | | ]; |
| | | </script> |
| | | |
| | | <template> |
| | | <AuthPageLayout |
| | | :app-name="appName" |
| | | :logo="logo" |
| | | :logo-dark="logoDark" |
| | | :page-description="pageDescription" |
| | | :page-title="pageTitle" |
| | | <div |
| | | class="relative flex min-h-screen w-full overflow-hidden bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-slate-100" |
| | | > |
| | | <!-- èªå®ä¹å·¥å
·æ --> |
| | | <!-- <template #toolbar></template> --> |
| | | </AuthPageLayout> |
| | | <!-- 左侧åçåº --> |
| | | <section |
| | | class="relative hidden w-[54%] flex-col justify-between overflow-hidden bg-slate-950 px-14 py-12 lg:flex xl:px-20" |
| | | > |
| | | <!-- èæ¯è£
饰 --> |
| | | <div class="pointer-events-none absolute inset-0"> |
| | | <div |
| | | class="absolute -left-24 top-1/4 h-96 w-96 rounded-full bg-sky-500/25 blur-3xl" |
| | | ></div> |
| | | <div |
| | | class="absolute -bottom-24 right-0 h-[30rem] w-[30rem] rounded-full bg-indigo-500/25 blur-3xl" |
| | | ></div> |
| | | <div class="brand-grid absolute inset-0"></div> |
| | | <div |
| | | class="absolute inset-x-0 bottom-0 h-64 bg-gradient-to-t from-slate-950 to-transparent" |
| | | ></div> |
| | | </div> |
| | | |
| | | <!-- åç --> |
| | | <div class="relative z-10 flex items-center gap-3"> |
| | | <img |
| | | v-if="logo" |
| | | :src="logo" |
| | | :alt="appName" |
| | | :class=" |
| | | logoIsLockup |
| | | ? 'h-12 w-auto max-w-[15rem] object-contain' |
| | | : 'h-12 w-12 rounded-2xl object-contain' |
| | | " |
| | | @load="handleLogoLoad" |
| | | /> |
| | | <div |
| | | v-else |
| | | class="flex h-12 w-12 items-center justify-center rounded-2xl bg-gradient-to-br from-sky-400 to-indigo-600 shadow-lg shadow-sky-500/30" |
| | | > |
| | | <span class="text-xl font-bold text-white">康</span> |
| | | </div> |
| | | <div v-if="!logoIsLockup" class="leading-tight"> |
| | | <p class="text-lg font-semibold tracking-wide text-white"> |
| | | {{ appName }} |
| | | </p> |
| | | <p class="text-[11px] tracking-[0.2em] text-slate-400"> |
| | | KANGYI INTELLIGENT |
| | | </p> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- ä¸»ææ¡ --> |
| | | <div class="relative z-10 max-w-xl"> |
| | | <p |
| | | class="mb-5 inline-flex items-center gap-2 rounded-full border border-sky-400/30 bg-sky-400/10 px-3 py-1 text-xs text-sky-300" |
| | | > |
| | | <span class="h-1.5 w-1.5 rounded-full bg-sky-400"></span> |
| | | å·¥ä¸ AI æºé å¹³å° |
| | | </p> |
| | | <h1 |
| | | class="text-4xl font-bold leading-[1.2] tracking-tight text-white xl:text-5xl" |
| | | > |
| | | 让æ¯ä¸éå·¥åº<br /> |
| | | <span |
| | | class="bg-gradient-to-r from-sky-400 via-cyan-300 to-indigo-300 bg-clip-text text-transparent" |
| | | > |
| | | é½çå¾è§ãç®å¾æ¸
|
| | | </span> |
| | | </h1> |
| | | <p class="mt-5 max-w-lg text-sm leading-relaxed text-slate-300"> |
| | | {{ pageDescription }} |
| | | </p> |
| | | |
| | | <ul class="mt-10 grid gap-3 sm:grid-cols-3"> |
| | | <li |
| | | v-for="item in highlights" |
| | | :key="item.title" |
| | | class="relative min-h-[6.25rem] overflow-hidden rounded-xl border border-white/10 bg-white/[0.04] p-4 backdrop-blur-sm transition-all hover:-translate-y-0.5 hover:border-sky-400/30 hover:bg-white/[0.07]" |
| | | > |
| | | <span |
| | | class="absolute inset-x-4 top-0 h-px bg-gradient-to-r from-transparent via-sky-400/60 to-transparent" |
| | | ></span> |
| | | <p class="text-sm font-medium text-white">{{ item.title }}</p> |
| | | <p class="mt-1.5 text-xs leading-relaxed text-slate-400"> |
| | | {{ item.desc }} |
| | | </p> |
| | | </li> |
| | | </ul> |
| | | </div> |
| | | |
| | | <div class="relative z-10 text-xs text-slate-500"> |
| | | © {{ year }} {{ appName }} · ä¿çæææå© |
| | | </div> |
| | | </section> |
| | | |
| | | <!-- å³ä¾§è¡¨ååº --> |
| | | <section |
| | | class="relative flex flex-1 flex-col items-center justify-center px-4 py-12 sm:px-8" |
| | | > |
| | | <!-- ç§»å¨ç«¯åç --> |
| | | <div class="mb-8 flex items-center gap-2.5 lg:hidden"> |
| | | <img |
| | | v-if="logo" |
| | | :src="logo" |
| | | :alt="appName" |
| | | :class=" |
| | | logoIsLockup |
| | | ? 'h-10 w-auto max-w-[12rem] object-contain' |
| | | : 'h-10 w-10 rounded-xl object-contain' |
| | | " |
| | | @load="handleLogoLoad" |
| | | /> |
| | | <div |
| | | v-else |
| | | class="flex h-10 w-10 items-center justify-center rounded-xl bg-gradient-to-br from-sky-500 to-indigo-600" |
| | | > |
| | | <span class="text-base font-bold text-white">康</span> |
| | | </div> |
| | | <div v-if="!logoIsLockup" class="leading-tight"> |
| | | <p class="text-base font-semibold">{{ appName }}</p> |
| | | <p class="text-[10px] tracking-[0.2em] text-slate-400"> |
| | | KANGYI INTELLIGENT |
| | | </p> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="w-full max-w-[26rem]"> |
| | | <div |
| | | class="rounded-3xl border border-slate-200/80 bg-white p-7 shadow-[0_24px_70px_-28px_rgba(15,23,42,0.35)] sm:p-9 dark:border-slate-800 dark:bg-slate-900" |
| | | > |
| | | <RouterView /> |
| | | </div> |
| | | |
| | | <p class="mt-6 text-center text-xs text-slate-400"> |
| | | © {{ year }} {{ appName }} |
| | | </p> |
| | | </div> |
| | | </section> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | .brand-grid { |
| | | background-image: |
| | | linear-gradient(to right, rgb(255 255 255 / 6%) 1px, transparent 1px), |
| | | linear-gradient(to bottom, rgb(255 255 255 / 6%) 1px, transparent 1px); |
| | | background-size: 48px 48px; |
| | | mask-image: radial-gradient(ellipse at 40% 45%, #000 20%, transparent 78%); |
| | | -webkit-mask-image: radial-gradient( |
| | | ellipse at 40% 45%, |
| | | #000 20%, |
| | | transparent 78% |
| | | ); |
| | | } |
| | | </style> |