From 91bfcf61f44f353dfebc54a57b5b774721de3e9a Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期一, 19 十月 2026 09:39:22 +0800
Subject: [PATCH] feat(auth): 更新门户认证页面布局和样式

---
 src/views/dashboard/analytics/modules/quick-actions.vue |  160 ++++++++++++-----------------------------------------
 1 files changed, 37 insertions(+), 123 deletions(-)

diff --git a/src/views/dashboard/analytics/modules/quick-actions.vue b/src/views/dashboard/analytics/modules/quick-actions.vue
index ea58f12..eec4c36 100644
--- a/src/views/dashboard/analytics/modules/quick-actions.vue
+++ b/src/views/dashboard/analytics/modules/quick-actions.vue
@@ -1,135 +1,49 @@
 <script lang="ts" setup>
+import type { MenuRecordRaw } from '@vben/types';
+import { computed } from 'vue';
 import { IconifyIcon } from '@vben/icons';
+import { usePortalMenus } from '#/components/portal/use-portal-menus';
 
-defineOptions({ name: 'DashboardQuickActions' });
-
-const emit = defineEmits<{
-  navigate: [name: string];
-}>();
-
-interface QuickAction {
-  title: string;
-  icon: string;
-  route: string;
+const { features, leaves, goMenu } = usePortalMenus();
+const actions = computed(() => {
+  const f = features.value;
+  const preferred = [f.todo, f.create, f.application, f.guideline, f.reimbursement, f.notice].filter((item): item is MenuRecordRaw => Boolean(item));
+  return [...new Map([...preferred, ...leaves.value].map((menu) => [menu.path, menu])).values()].slice(0, 6);
+});
+function caption(menu: MenuRecordRaw) {
+  if (/todo|寰呭姙/.test(menu.path + menu.name)) return '浠诲姟澶勭悊 路 杩涘害璺熻繘';
+  if (/application|鐢虫姤/.test(menu.path + menu.name)) return '鍦ㄧ嚎鐢虫姤 路 杩涘害鏌ヨ';
+  if (/guideline|鎸囧崡/.test(menu.path + menu.name)) return '鎸囧崡鏌ヨ 路 鐢虫姤鍙傝��';
+  if (/notice|鍏憡/.test(menu.path + menu.name)) return '閫氱煡鍙戝竷 路 淇℃伅鏌ラ槄';
+  if (/reimbursement|鎶ラ攢/.test(menu.path + menu.name)) return '璐圭敤鎶ラ攢 路 鍗曟嵁鏌ヨ';
+  return '涓氬姟鍔炵悊 路 淇℃伅鏌ヨ';
 }
-
-/** 蹇嵎鍏ュ彛锛氬搴旂郴缁熷疄闄呰彍鍗曪紙瀹℃壒涓績 / 璇鹃绠$悊 / 璐㈠姟鎶ラ攢 / 閫氱煡鍏憡锛� */
-const actions: QuickAction[] = [
-  { title: '鍙戣捣娴佺▼', icon: 'lucide:circle-plus', route: 'BpmProcessInstanceCreate' },
-  { title: '寰呭姙浠诲姟', icon: 'lucide:list-todo', route: 'BpmTaskTodo' },
-  { title: '宸插姙浠诲姟', icon: 'lucide:circle-check-big', route: 'BpmTaskDone' },
-  { title: '鎴戠殑娴佺▼', icon: 'lucide:git-branch', route: 'BpmProcessInstance' },
-  { title: '璇鹃鐢虫姤', icon: 'lucide:file-text', route: 'ResearchApplication' },
-  { title: '璇鹃鎸囧崡', icon: 'lucide:book-open', route: 'ResearchGuideline' },
-  { title: '璐㈠姟鎶ラ攢', icon: 'lucide:receipt', route: 'FinanceReimbursement' },
-  { title: '閫氱煡鍏憡', icon: 'lucide:bell', route: 'BpmOaNotice' },
-];
 </script>
 
 <template>
-  <div class="elite-card p-5">
-    <div class="elite-head">
-      <h3 class="elite-title">
-        <span class="elite-bar"></span>
-        蹇嵎鍏ュ彛
-      </h3>
+  <section v-if="actions.length" class="portal-quick-strip" aria-labelledby="quick-title">
+    <div class="portal-quick-label"><h2 id="quick-title">蹇嵎鍏ュ彛</h2><p>涓�绔欏紡鏈嶅姟<br />楂樻晥渚挎嵎</p><IconifyIcon icon="lucide:circle-arrow-right" /></div>
+    <div class="portal-quick-actions" :style="{ '--columns': actions.length }">
+      <button v-for="menu in actions" :key="menu.path" type="button" @click="goMenu(menu)"><span class="portal-quick-icon"><IconifyIcon :icon="typeof menu.icon === 'string' ? menu.icon : 'lucide:arrow-up-right'" /></span><strong>{{ menu.name }}</strong><span class="portal-quick-caption">{{ caption(menu) }}</span></button>
     </div>
-    <div class="grid grid-cols-4 gap-3">
-      <div
-        v-for="action in actions"
-        :key="action.route"
-        class="elite-action group flex cursor-pointer flex-col items-center gap-2 rounded-xl p-3 transition-colors duration-300"
-        @click="emit('navigate', action.route)"
-      >
-        <div
-          class="elite-icon flex size-10 items-center justify-center rounded-xl transition-shadow duration-300"
-        >
-          <IconifyIcon :icon="action.icon" class="size-5 text-[#e3c567]" />
-        </div>
-        <span class="elite-action-label">{{ action.title }}</span>
-      </div>
-    </div>
-  </div>
+  </section>
 </template>
 
 <style scoped>
-.elite-card {
-  background: #fdfcfa;
-  border: 1px solid rgba(212, 175, 55, 0.22);
-  border-radius: 14px;
-  box-shadow: 0 2px 14px rgba(15, 36, 64, 0.05);
-  transition:
-    border-color 0.3s ease,
-    box-shadow 0.3s ease;
-}
-
-.elite-card:hover {
-  border-color: rgba(212, 175, 55, 0.5);
-  box-shadow: 0 6px 20px rgba(15, 36, 64, 0.09);
-}
-
-.dark .elite-card {
-  background: #101a2b;
-  border-color: rgba(212, 175, 55, 0.2);
-  box-shadow: 0 2px 14px rgba(0, 0, 0, 0.3);
-}
-
-.elite-head {
-  display: flex;
-  align-items: center;
-  margin-bottom: 16px;
-}
-
-.elite-title {
-  display: flex;
-  align-items: center;
-  gap: 10px;
-  margin: 0;
-  font-size: 15px;
-  font-weight: 600;
-  letter-spacing: 1px;
-  color: #0f2440;
-}
-
-.dark .elite-title {
-  color: #f0ead8;
-}
-
-.elite-bar {
-  display: inline-block;
-  width: 3px;
-  height: 15px;
-  border-radius: 2px;
-  background: linear-gradient(180deg, #e3c567, #b8901f);
-}
-
-/* 鍥炬爣搴曪細钘忛潚娓愬彉 + 閲戣壊鍥炬爣 */
-.elite-icon {
-  color: #e3c567;
-  background: linear-gradient(135deg, #1e4571, #0f2440);
-  border: 1px solid rgba(212, 175, 55, 0.3);
-  box-shadow: 0 4px 12px rgba(15, 36, 64, 0.18);
-}
-
-/* 鎮仠鍙彁浜噾鑹叉弿杈癸紝涓嶅仛浣嶇Щ */
-.elite-action:hover .elite-icon {
-  box-shadow:
-    0 0 0 1px rgba(212, 175, 55, 0.7),
-    0 4px 14px rgba(15, 36, 64, 0.22);
-}
-
-.elite-action-label {
-  font-size: 12px;
-  font-weight: 500;
-  letter-spacing: 0.5px;
-  color: #8a94a6;
-}
-
-.elite-action:hover .elite-action-label {
-  color: #b8901f;
-}
-
-.dark .elite-action:hover .elite-action-label {
-  color: #e3c567;
-}
+.portal-quick-strip { display: flex; overflow: hidden; border-radius: 8px; background: #fff; box-shadow: 0 5px 24px rgb(25 70 110 / 6%); }
+.portal-quick-label { width: 170px; flex: none; padding: 24px 30px; color: #fff; background: #b8222c; clip-path: polygon(0 0, 88% 0, 100% 50%, 88% 100%, 0 100%); }
+.portal-quick-label h2 { margin: 0 0 9px; font-size: 23px; font-weight: 650; white-space: nowrap; }
+.portal-quick-label p { margin: 0 0 10px; color: #fff4f4; font-size: 13px; line-height: 1.6; letter-spacing: .07em; }
+.portal-quick-label :deep(svg) { width: 18px; height: 18px; }
+.portal-quick-actions { display: grid; min-width: 0; flex: 1; grid-template-columns: repeat(var(--columns), minmax(0, 1fr)); align-items: center; padding: 18px 12px; }
+.portal-quick-actions button { display: flex; min-width: 0; min-height: 116px; flex-direction: column; align-items: center; justify-content: center; gap: 8px; padding: 4px 6px; border-right: 1px solid #e4edf7; text-align: center; cursor: pointer; }
+.portal-quick-actions button:last-child { border-right: 0; }
+.portal-quick-icon { display: grid; width: 55px; height: 55px; place-items: center; border-radius: 50%; color: #2877d1; background: #eaf3ff; font-size: 29px; }
+.portal-quick-actions button:nth-child(4) .portal-quick-icon { color: #139d8a; background: #e8f7f4; }
+.portal-quick-actions strong { max-width: 100%; overflow: hidden; color: #173958; font-size: 14px; font-weight: 700; text-overflow: ellipsis; white-space: nowrap; }
+.portal-quick-caption { color: #5c7187; font-size: 10px; white-space: nowrap; }
+.portal-quick-actions button:hover { background: #f4f8fd; }
+button:focus-visible { outline: 2px solid #2c70b2; outline-offset: -2px; }
+@media (max-width: 1050px) { .portal-quick-label { width: 145px; padding: 24px 22px; } .portal-quick-caption { font-size: 9px; } }
+@media (max-width: 767px) { .portal-quick-strip { flex-direction: column; } .portal-quick-label { display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 15px; padding: 14px 18px; clip-path: none; } .portal-quick-label h2 { margin: 0; font-size: 20px; } .portal-quick-label p { margin: 0; font-size: 11px; } .portal-quick-label p br { display: none; } .portal-quick-actions { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px 0; padding: 16px 6px; } .portal-quick-actions button:nth-child(3n) { border-right: 0; } .portal-quick-caption { font-size: 9px; } .portal-quick-icon { width: 46px; height: 46px; font-size: 24px; } .portal-quick-actions strong { font-size: 13px; } }
 </style>

--
Gitblit v1.9.3