云
7 天以前 21599300cc73ff7384b74cc5e4ac2cbd82bdc293
src/packages/effects/layouts/src/authentication/authentication.vue
@@ -7,7 +7,6 @@
import { Copyright } from '../basic/copyright';
import AuthenticationFormView from './form.vue';
import SloganIcon from './icons/slogan.vue';
import Toolbar from './toolbar.vue';
interface Props {
@@ -43,19 +42,18 @@
 * @zh_CN 根据主题选择合适的 logo 图标
 */
const logoSrc = computed(() => {
  // 如果是暗色主题且提供了 logoDark,则使用暗色主题的 logo
  if (isDark.value && props.logoDark) {
    return props.logoDark;
  }
  // 否则使用默认的 logo
  return props.logo;
});
</script>
<template>
  <div
    :class="[isDark ? 'dark' : '']"
    class="flex min-h-full flex-1 overflow-x-hidden select-none"
    class="agri-auth flex min-h-full flex-1 overflow-x-hidden select-none"
  >
    <template v-if="toolbar">
      <slot name="toolbar">
@@ -65,7 +63,7 @@
    <!-- 左侧认证面板 -->
    <AuthenticationFormView
      v-if="authPanelLeft"
      class="min-h-full w-2/5 flex-1"
      class="agri-left-panel min-h-full w-2/5 flex-1"
      data-side="left"
    >
      <template v-if="copyright" #copyright>
@@ -79,10 +77,10 @@
    </AuthenticationFormView>
    <slot name="logo">
      <!-- 头部 Logo 和应用名称 -->
      <!-- 头部 Logo 和应用名称(小屏展示,大屏由品牌区代替) -->
      <div
        v-if="logoSrc || appName"
        class="absolute top-0 left-0 z-10 flex flex-1"
        class="agri-mini-logo absolute top-0 left-0 z-10 flex flex-1"
        @click="clickLogo"
      >
        <div
@@ -104,34 +102,38 @@
    </slot>
    <!-- 系统介绍 -->
    <div v-if="!authPanelCenter" class="relative hidden w-0 flex-1 lg:block">
      <div
        class="absolute inset-0 size-full bg-background-deep dark:bg-[#070709]"
      >
        <div class="login-background absolute top-0 left-0 size-full"></div>
    <div v-if="!authPanelCenter" class="relative hidden w-0 flex-[1.18] lg:block">
      <div class="agri-promo-layer absolute inset-0 size-full overflow-hidden">
        <div
          :key="authPanelLeft ? 'left' : authPanelRight ? 'right' : 'center'"
          class="mr-20 flex-col-center h-full px-8 text-center"
          :class="{
            'enter-x': authPanelLeft,
            '-enter-x': authPanelRight,
          }"
          class="agri-promo relative flex h-full flex-col items-end justify-start px-10 pt-16 text-left"
        >
          <template v-if="sloganImage">
            <img
              :alt="appName"
              :src="sloganImage"
              class="h-64 w-2/5 animate-float"
            />
          </template>
          <SloganIcon v-else :alt="appName" class="h-64 w-2/5 animate-float" />
          <div class="text-1xl mt-6 font-sans text-foreground lg:text-2xl">
            {{ pageTitle }}
          <!-- 品牌区 -->
          <div class="agri-brand">
            <div class="flex items-center gap-4">
              <img
                src="/images/tahe-logo.png"
                alt="塔河种业"
                class="h-16 w-auto shrink-0 rounded-xl bg-white/95 p-1.5 shadow-lg"
              />
              <div>
                <div class="agri-brand-title text-3xl font-semibold tracking-[0.14em] xl:text-4xl">
                  {{ pageTitle }}
                </div>
                <div class="agri-brand-sub mt-2 text-xs tracking-[0.3em] xl:text-sm">
                  AGRICULTURE DIGITAL PLATFORM
                </div>
              </div>
            </div>
            <div class="agri-brand-tag mt-6 flex items-center justify-end gap-4">
              <span class="agri-brand-tag-line h-px w-12"></span>
              <span class="text-base tracking-[0.22em]">{{ pageDescription }}</span>
              <span class="agri-brand-tag-line h-px w-12"></span>
            </div>
          </div>
          <div class="mt-2 dark:text-muted-foreground">
            {{ pageDescription }}
          </div>
        </div>
      </div>
    </div>
@@ -154,37 +156,302 @@
    </div>
    <!-- 右侧认证面板 -->
    <AuthenticationFormView
      v-if="authPanelRight"
      class="min-h-full w-2/5 flex-1"
      data-side="right"
    >
      <template v-if="copyright" #copyright>
        <slot name="copyright">
          <Copyright
            v-if="preferences.copyright.enable"
            v-bind="preferences.copyright"
          />
        </slot>
      </template>
    </AuthenticationFormView>
    <div v-if="authPanelRight" class="agri-form-wrap relative">
      <AuthenticationFormView class="agri-form-panel min-h-full w-full" data-side="right" />
      <!-- 卡片右上树叶装饰 -->
      <svg
        class="pointer-events-none absolute top-0 right-0 h-36 w-40"
        viewBox="0 0 180 150"
        aria-hidden="true"
      >
        <path d="M180 4c-26 22-38 52-36 92" fill="none" stroke="#4ade80" stroke-width="2" stroke-linecap="round" />
        <g fill="#4ade80" opacity="0.85">
          <path d="M160 22q-16-10-30-2 12 12 30 2Z" />
          <path d="M150 44q-18-6-30 4 14 10 30-4Z" />
          <path d="M146 70q-18-2-28 10 16 7 28-10Z" />
          <path d="M146 96q-16 2-24 14 16 4 24-14Z" />
        </g>
        <g fill="#86efac" opacity="0.7">
          <path d="M164 34q4-16 18-22 2 16-18 22Z" />
          <path d="M156 60q6-14 20-18 0 16-20 18Z" />
          <path d="M152 86q8-12 22-14-2 16-22 14Z" />
        </g>
        <g fill="#bbf7d0" opacity="0.45">
          <path d="M96 118q-14-8-26-1 10 10 26 1Z" />
          <path d="M70 136q-12-6-22 0 9 8 22 0Z" />
        </g>
      </svg>
      <!-- 卡片底部农场插画 -->
      <svg
        class="agri-farm pointer-events-none absolute inset-x-0 bottom-0 h-24 w-full"
        viewBox="0 0 560 130"
        preserveAspectRatio="xMidYMax slice"
        aria-hidden="true"
      >
        <path d="M0 92 Q120 62 260 88 T560 80 V130 H0 Z" fill="#dcfce7" />
        <path d="M0 112 Q160 88 320 110 T560 104 V130 H0 Z" fill="#bbf7d0" />
        <g fill="#a7f3d0">
          <path d="M36 76 L58 58 L80 76 Z" />
          <rect x="42" y="76" width="32" height="22" fill="#ecfdf5" />
          <rect x="54" y="84" width="9" height="14" fill="#86efac" />
        </g>
        <g>
          <rect x="116" y="78" width="4" height="16" fill="#86efac" />
          <circle cx="118" cy="70" r="12" fill="#86efac" />
          <rect x="146" y="84" width="3.5" height="12" fill="#6ee7b7" />
          <circle cx="148" cy="78" r="8" fill="#6ee7b7" />
        </g>
        <g stroke="#6ee7b7" stroke-width="3" stroke-linecap="round">
          <path d="M470 100V64" />
          <path d="M470 64l-14-10M470 64l16-6M470 64l-2 18" />
        </g>
        <g fill="#a7f3d0">
          <path d="M498 82 L514 68 L530 82 Z" />
          <rect x="503" y="82" width="22" height="16" fill="#ecfdf5" />
        </g>
      </svg>
    </div>
  </div>
</template>
<style scoped>
.login-background {
  background:
    radial-gradient(circle at 72% 38%, hsl(var(--primary) / 24%), transparent 42%),
    linear-gradient(145deg, hsl(var(--background-deep)) 0%, hsl(var(--background)) 100%);
  filter: blur(70px);
.agri-auth {
  position: relative;
  min-height: 100dvh;
  overflow-y: auto;
  background-color: #124b39;
  background-image: url('/agriculture-login-bg.png');
  background-position: center;
  background-size: cover;
  isolation: isolate;
}
.dark {
  .login-background {
    background:
      radial-gradient(circle at 72% 38%, hsl(var(--primary) / 18%), transparent 42%),
      linear-gradient(145deg, hsl(var(--background-deep)) 0%, hsl(var(--background)) 100%);
    filter: blur(70px);
.login-background {
  background-image: none;
  filter: saturate(1.06) contrast(1.01);
}
.agri-left-panel,
.agri-left-panel :deep(*) {
  background-color: transparent !important;
}
/* 品牌文字:白色 + 文字阴影保证在背景图上的可读性 */
.agri-brand-title {
  color: #fff;
  text-shadow: 0 2px 10px rgba(2, 44, 34, 0.6);
}
.agri-brand-sub {
  color: rgba(240, 253, 250, 0.92);
  text-shadow: 0 1px 6px rgba(2, 44, 34, 0.55);
}
.agri-brand-tag {
  color: rgba(240, 253, 250, 0.92);
  text-shadow: 0 1px 6px rgba(2, 44, 34, 0.55);
}
.agri-brand-tag-line { background: rgba(167, 243, 208, 0.8); }
/* 大屏隐藏左上角小 logo,由品牌区展示 */
@media (min-width: 1024px) {
  .agri-mini-logo {
    display: none;
  }
}
/* ---------- 品牌与内容 ---------- */
.agri-promo {
  max-width: 780px;
  margin-left: clamp(2rem, 6vw, 7rem);
  padding-top: 3rem;
  padding-bottom: 4.5rem;
}
/* ---------- 右侧登录卡片 ---------- */
.agri-form-wrap {
  align-self: center;
  max-height: calc(100dvh - 5rem);
  margin: 2.5rem clamp(1rem, 4vw, 4.5rem) 2.5rem 1.5rem;
  width: 40%;
  flex: 0.82 1 0%;
  /* 外层统一圆角裁剪,避免底部插画等兄弟装饰把卡片直角化 */
  overflow: hidden;
  border-radius: 1.75rem;
  box-shadow: 0 24px 70px rgba(2, 44, 34, 0.24);
}
.agri-form-panel {
  overflow: auto;
  border: 1px solid rgba(255, 255, 255, 0.9);
  border-radius: 1.75rem;
  background: rgba(250, 253, 251, 0.96) !important;
  backdrop-filter: blur(18px);
}
/* 表单内容上移,为底部插画留出空间 */
.agri-form-wrap :deep(.side-content) {
  margin-bottom: 4.5rem;
}
/* 标题下的绿色装饰条 */
.agri-form-wrap :deep(h2 + p)::after {
  content: '';
  display: block;
  width: 2.75rem;
  height: 0.28rem;
  margin-top: 0.8rem;
  border-radius: 9999px;
  background: #16a34a;
}
/* 输入框前缀图标 */
.agri-form-wrap :deep(input[autocomplete='username']),
.agri-form-wrap :deep(input[autocomplete='current-password']) {
  padding-left: 2.6rem;
  background-repeat: no-repeat;
  background-position: 0.85rem center;
  background-size: 1.05rem;
}
.agri-form-wrap :deep(input[autocomplete='username']) {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2'/%3E%3Ccircle cx='12' cy='7' r='4'/%3E%3C/svg%3E");
}
.agri-form-wrap :deep(input[autocomplete='current-password']) {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='11' width='18' height='11' rx='2'/%3E%3Cpath d='M7 11V7a5 5 0 0 1 10 0v4'/%3E%3C/svg%3E");
}
/* 输入框整体质感:加高、圆角、柔和边框、聚焦绿色光圈 */
.agri-form-wrap :deep(input) {
  height: 3rem;
  border-radius: 0.75rem;
  border-color: rgba(15, 118, 110, 0.16);
  background-color: #fff;
  font-size: 0.95rem;
  transition: border-color 200ms ease, box-shadow 200ms ease;
}
.agri-form-wrap :deep(input:hover) {
  border-color: rgba(16, 185, 129, 0.45);
}
.agri-form-wrap :deep(input:focus),
.agri-form-wrap :deep(input:focus-visible) {
  border-color: #10b981;
  box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.16);
  outline: none;
}
.dark .agri-form-wrap :deep(input) {
  border-color: rgba(255, 255, 255, 0.16);
  background-color: rgba(255, 255, 255, 0.06);
}
.dark .agri-form-wrap :deep(input:focus),
.dark .agri-form-wrap :deep(input:focus-visible) {
  border-color: #34d399;
  box-shadow: 0 0 0 3px rgba(52, 211, 153, 0.2);
}
@media (max-width: 1023px) {
  .agri-farm {
    display: none;
  }
}
/* ---------- 暗色模式 ---------- */
.dark .login-background {
  background-image:
    linear-gradient(90deg, rgba(2, 26, 20, 0.9), rgba(2, 26, 20, 0.62)),
    url('/agriculture-login-bg.png');
}
.dark .agri-form-panel {
  background: rgba(13, 31, 25, 0.96) !important;
}
.dark .agri-form-wrap > svg {
  opacity: 0.35;
}
/* ---------- 响应式 ---------- */
@media (max-width: 1023px) {
  .agri-promo { margin-left: 2rem; }
  .agri-form-wrap { margin-right: 1.25rem; }
}
@media (max-height: 720px) and (min-width: 1024px) {
  .agri-form-wrap {
    max-height: calc(100dvh - 2rem);
    margin-top: 1rem;
    margin-bottom: 1rem;
  }
  .agri-promo {
    padding-top: 1.5rem;
    padding-bottom: 3rem;
  }
  .agri-promo .mt-12 {
    margin-top: 2rem;
  }
}
@media (max-width: 767px) {
  .agri-form-wrap {
    align-self: stretch;
    margin: 1rem;
    width: auto;
    flex: none;
  }
  .agri-form-panel {
    border-radius: 1.5rem;
  }
}
/* ---------- 入场动画(纯 CSS,动画结束必然停留于可见态) ---------- */
@media (prefers-reduced-motion: no-preference) {
  .agri-promo {
    animation: agri-in-left 0.75s cubic-bezier(0.22, 0.61, 0.36, 1) both;
  }
  .agri-form-wrap {
    animation: agri-in-right 0.75s cubic-bezier(0.22, 0.61, 0.36, 1) both;
  }
}
@keyframes agri-in-left {
  from {
    opacity: 0;
    transform: translateX(-28px);
  }
}
@keyframes agri-in-right {
  from {
    opacity: 0;
    transform: translateX(28px);
  }
}
@keyframes agri-in-up {
  from {
    opacity: 0;
    transform: translateY(18px);
  }
}
@media (prefers-reduced-motion: reduce) {
  .agri-auth :deep(*) {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
</style>