liu
10 天以前 763058b5b25f23a45ad046e97599df3d710378a5
feat(login): 重做康意智能登录页视觉

- src/layouts/auth.vue:改为左右分栏布局
- 左侧品牌区:深色工业风背景(网格 + 光晕)、康意标识、
「让每一道工序 都看得见、算得清」主标语、三个能力点
- 右侧表单区:白色圆角卡片,柔和阴影
- 移动端自动隐藏左侧面板,改为顶部小 Logo + 单卡片
- 兼容横版组合 Logo(宽高比 > 1.8 时整张展示,不重复品牌名)
- 后端 loginDescription 仅写公司名变体时回退默认介绍文案

- src/views/_core/authentication/login.vue
- 标题改为「欢迎登录」,副标题「请输入账号和密码以继续」
- 登录按钮与输入框等高,圆角统一

- company.config.ts:KYZN 的 logo/favicon 改为康意自有标识
- public/logos/KYZN.svg:新增康意智能品牌标识

Co-Authored-By: Claude <noreply@anthropic.com>
已添加1个文件
已修改3个文件
261 ■■■■■ 文件已修改
company.config.ts 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/logos/KYZN.svg 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/auth.vue 228 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/_core/authentication/login.vue 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
company.config.ts
@@ -61,8 +61,8 @@
  },
  KYZN: {
    name: '康意智能',
    logo: '/logos/YZFXLogo.png',
    favicon: '/logos/YZFXLogo.ico',
    logo: '/logos/KYZN.svg',
    favicon: '/logos/KYZN.svg',
    apiUrl: 'http://101.245.92.159:9008/admin-api',
  },
  CHDL: {
public/logos/KYZN.svg
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="none" role="img" aria-label="康意智能">
  <defs>
    <linearGradient id="kyzn-g" x1="4" y1="2" x2="60" y2="62" gradientUnits="userSpaceOnUse">
      <stop stop-color="#38BDF8"/>
      <stop offset="0.55" stop-color="#0EA5E9"/>
      <stop offset="1" stop-color="#4338CA"/>
    </linearGradient>
  </defs>
  <rect width="64" height="64" rx="16" fill="url(#kyzn-g)"/>
  <path d="M21 17v30" stroke="#FFFFFF" stroke-width="5" stroke-linecap="round"/>
  <path d="M21 33.5 36.5 17" stroke="#FFFFFF" stroke-width="5" stroke-linecap="round"/>
  <path d="M21 33.5 38 47" stroke="#FFFFFF" stroke-width="5" stroke-linecap="round"/>
  <circle cx="46" cy="15.5" r="3" fill="#A5F3FC"/>
  <circle cx="47.5" cy="48" r="3" fill="#A5F3FC"/>
  <path d="M40 32h7" stroke="#A5F3FC" stroke-width="2.5" stroke-linecap="round" opacity="0.85"/>
</svg>
src/layouts/auth.vue
@@ -1,13 +1,12 @@
<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();
@@ -16,32 +15,219 @@
  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>
src/views/_core/authentication/login.vue
@@ -131,6 +131,8 @@
      ref="loginRef"
      :form-schema="formSchema"
      :loading="authStore.loginLoading"
      title="欢迎登录"
      sub-title="请输入账号和密码以继续"
      :show-code-login="false"
      :show-qrcode-login="false"
      :show-third-party-login="false"
@@ -151,3 +153,14 @@
    />
  </div>
</template>
<style scoped>
/* ä¸Žè¾“入框保持同一高度与圆角,视觉上更整齐 */
:deep(button[aria-label='login']) {
  height: 2.5rem;
  border-radius: 0.5rem;
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: 0.04em;
}
</style>