云
9 小时以前 91bfcf61f44f353dfebc54a57b5b774721de3e9a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<script lang="ts" setup>
import { computed, onMounted } from 'vue';
 
import { IconifyIcon } from '@vben/icons';
import { preferences } from '@vben/preferences';
 
import { useCompanyConfigStore } from '#/store/company-config';
import PortalBrandText from '#/components/portal/portal-brand-text.vue';
 
declare const __APP_LOGO__: string;
 
const companyConfigStore = useCompanyConfigStore();
 
onMounted(() => {
  // 登录页展示 loginTitle:启动加载走的是纯函数(不填 store),进入登录页时拉取一次
  companyConfigStore.loadFromServer();
});
 
const appName = computed(() => preferences.copyright.companyName);
// 登录页 logo 优先取数据库配置(preferences.logo.source),未配置时回退编译期默认 __APP_LOGO__
const logo = computed(() => preferences.logo.source || __APP_LOGO__);
const pageTitle = computed(() => companyConfigStore.loginTitle || '服务供销合作,助力乡村振兴');
const pageDescription = computed(() => companyConfigStore.loginDescription || '连接课题研究、审批协作与日常业务,让每一项工作有序推进。');
</script>
 
<template>
  <div class="portal-auth">
    <header class="auth-masthead">
      <div class="auth-brand">
        <img :src="logo" :alt="appName" />
        <PortalBrandText />
      </div>
      <span class="auth-motto">开放协作 · 便捷服务 · 高效办理</span>
    </header>
    <main class="auth-main">
      <div class="auth-landscape" aria-hidden="true"></div>
      <div class="auth-content">
        <section class="auth-intro" aria-labelledby="auth-heading">
          <h1 id="auth-heading">{{ pageTitle }}</h1>
          <p>{{ pageDescription }}</p>
          <div class="auth-services">
            <div><IconifyIcon icon="lucide:workflow" /><strong>审批协同</strong><span>流程办理 · 待办跟进</span></div>
            <div><IconifyIcon icon="lucide:book-open-text" /><strong>课题管理</strong><span>指南查询 · 课题申报</span></div>
            <div><IconifyIcon icon="lucide:megaphone" /><strong>信息互通</strong><span>通知公告 · 消息提醒</span></div>
          </div>
        </section>
        <section class="auth-form-panel" aria-label="身份认证">
          <RouterView v-slot="{ Component, route }">
            <KeepAlive :include="['Login']">
              <component :is="Component" :key="route.path" />
            </KeepAlive>
          </RouterView>
        </section>
      </div>
    </main>
    <footer class="auth-footer"><span>{{ appName }}</span><span>建议使用最新版 Chrome 或 Edge 浏览器</span></footer>
  </div>
</template>
 
<style scoped>
.portal-auth { --background: 0 0% 100%; --foreground: 213 51% 23%; --muted-foreground: 213 19% 43%; --input-background: 210 75% 99%; --input: 211 29% 81%; --border: 211 37% 89%; display: flex; min-height: 100dvh; flex-direction: column; color-scheme: light; color: #18365a; background: #f5f9fd; }
.auth-masthead { display: flex; width: 100%; min-height: 120px; align-items: center; justify-content: space-between; gap: 24px; padding: 20px max(4vw, calc((100vw - 1280px) / 2)); border-top: 20px solid #d9ecfb; background: #fff; }
.auth-brand { display: flex; min-width: 0; align-items: center; gap: 16px; }
.auth-brand img { width: 56px; height: 56px; object-fit: contain; }
.auth-motto { color: #426689; font-size: 14px; letter-spacing: .12em; white-space: nowrap; }
.auth-main { position: relative; display: flex; min-height: 650px; flex: 1; align-items: center; overflow: hidden; background: #e3f0f9; }
.auth-landscape { position: absolute; inset: 0; background: linear-gradient(90deg, rgb(247 251 255 / 90%), rgb(247 251 255 / 55%) 46%, rgb(247 251 255 / 8%) 75%), url('/static/imgs/portal-hero-v2.png') center 58% / cover; }
.auth-content { position: relative; display: grid; width: min(1320px, 90%); grid-template-columns: minmax(0, 1fr) 420px; align-items: center; gap: 8%; margin: 52px auto 68px; }
.auth-intro { align-self: start; max-width: 680px; padding-top: 56px; }
.auth-intro h1 { max-width: 13em; margin: 0 0 22px; color: #a91e28; font-family: 'KaiTi', 'STKaiti', serif; font-size: clamp(34px, 3.8vw, 54px); font-weight: 800; letter-spacing: .045em; line-height: 1.4; text-wrap: balance; }
.auth-intro > p { max-width: 32em; margin: 0; color: #294e73; font-size: 17px; line-height: 1.9; }
.auth-services { display: flex; gap: 32px; margin-top: 42px; }
.auth-services > div { display: grid; gap: 7px; }
.auth-services :deep(svg) { width: 25px; height: 25px; margin-bottom: 3px; color: #28689b; }
.auth-services strong { font-size: 15px; }
.auth-services span { color: #4c6c88; font-size: 12px; white-space: nowrap; }
.auth-form-panel { position: relative; padding: 38px 38px 30px; border-radius: 12px; background: #fff; box-shadow: 0 18px 56px rgb(24 62 98 / 15%); }
.auth-form-panel::before { position: absolute; top: 0; right: 38px; left: 38px; height: 3px; background: #b7242d; content: ''; }
.auth-footer { display: flex; min-height: 64px; align-items: center; justify-content: center; gap: 28px; padding: 16px 24px; color: #5c7187; background: #fff; font-size: 12px; }
@media (max-width: 1100px) { .auth-content { grid-template-columns: minmax(0, 1fr) 390px; gap: 5%; } .auth-services { gap: 20px; } .auth-services span { white-space: normal; } .auth-form-panel { padding: 32px; } }
@media (max-width: 850px) { .auth-masthead { min-height: 90px; padding: 18px 6%; } .auth-motto { display: none; } .auth-content { width: min(460px, 90%); grid-template-columns: 1fr; gap: 25px; margin: 30px auto 42px; } .auth-intro { padding-top: 0; } .auth-intro h1 { margin-bottom: 10px; font-size: 32px; } .auth-intro > p { font-size: 14px; } .auth-services { display: none; } .auth-main { min-height: 0; } .auth-footer { flex-direction: column; gap: 5px; } .auth-landscape { background-position: 35% center; } }
@media (max-width: 420px) { .auth-form-panel { padding: 28px 24px; } .auth-brand strong { font-size: 22px; } .auth-brand img { width: 46px; height: 46px; } }
@media (max-width: 850px) { .auth-brand :deep(.shell-brand-main) { gap: 10px; }.auth-brand :deep(.shell-brand strong) { max-width: calc(100vw - 120px); font-size: 18px; white-space: normal; }.auth-brand :deep(.shell-brand em) { display: none; }.auth-brand :deep(.shell-brand small) { font-size: 8px; letter-spacing: 0; } }
</style>