From e9283ffe6822b12ec5dd2ccf4dc13a369b227a61 Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期一, 30 三月 2026 08:32:06 +0800
Subject: [PATCH] chore: sync rsf-design from isolated worktree

---
 rsf-design/src/views/dashboard/console/index.vue |  271 +++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 238 insertions(+), 33 deletions(-)

diff --git a/rsf-design/src/views/dashboard/console/index.vue b/rsf-design/src/views/dashboard/console/index.vue
index 39fa3be..90691e1 100644
--- a/rsf-design/src/views/dashboard/console/index.vue
+++ b/rsf-design/src/views/dashboard/console/index.vue
@@ -1,44 +1,249 @@
-<!-- 宸ヤ綔鍙伴〉闈� -->
 <template>
-  <div>
-    <CardList></CardList>
+  <div class="art-full-height flex flex-col gap-5">
+    <section
+      class="overflow-hidden rounded-3xl border border-white/10 bg-[linear-gradient(135deg,var(--art-main-bg-color),var(--art-card-bg-color))] p-6 shadow-[0_24px_80px_rgba(15,23,42,0.08)]"
+    >
+      <div class="flex flex-col gap-6 lg:flex-row lg:items-end lg:justify-between">
+        <div class="max-w-3xl">
+          <div
+            class="mb-3 inline-flex items-center gap-2 rounded-full border border-emerald-500/20 bg-emerald-500/10 px-3 py-1 text-xs font-medium text-emerald-600"
+          >
+            <span class="size-2 rounded-full bg-emerald-500"></span>
+            RSF Phase 1 Landing
+          </div>
+          <h1
+            class="m-0 text-3xl font-semibold tracking-tight text-[var(--art-gray-900)] md:text-4xl"
+          >
+            杩愯楠ㄦ灦宸茬粡鍒囧埌 `rsf-design`
+          </h1>
+          <p class="mt-4 max-w-2xl text-sm leading-7 text-[var(--art-gray-600)] md:text-base">
+            褰撳墠鍏ュ彛宸茬粡鎺ュ叆鐪熷疄鍚庣鐧诲綍銆佸姩鎬佽彍鍗曞拰鏉冮檺閾捐矾銆傝繖涓椤靛彧灞曠ず宸茬粡鍙敤鐨� phase-1
+            鑳藉姏锛屼笉鍐嶄繚鐣欐ā鏉块噷鐨勭ず渚嬪浘琛ㄥ拰婕旂ず鏁版嵁銆�
+          </p>
+        </div>
 
-    <ElRow :gutter="20">
-      <ElCol :sm="24" :md="12" :lg="10">
-        <ActiveUser />
-      </ElCol>
-      <ElCol :sm="24" :md="12" :lg="14">
-        <SalesOverview />
-      </ElCol>
-    </ElRow>
+        <div class="rounded-2xl border border-white/10 bg-white/70 px-4 py-3 backdrop-blur-sm">
+          <p class="text-xs uppercase tracking-[0.24em] text-[var(--art-gray-500)]"
+            >Current Entry</p
+          >
+          <p class="mt-2 text-lg font-semibold text-[var(--art-gray-900)]">
+            {{ currentUserName }}
+          </p>
+          <p class="mt-1 text-sm text-[var(--art-gray-600)]">
+            {{ currentUserRoleText }} 路 {{ currentMenuLabel }}
+          </p>
+        </div>
+      </div>
+    </section>
 
-    <ElRow :gutter="20">
-      <ElCol :sm="24" :md="24" :lg="12">
-        <NewUser />
-      </ElCol>
-      <ElCol :sm="24" :md="12" :lg="6">
-        <Dynamic />
-      </ElCol>
-      <ElCol :sm="24" :md="12" :lg="6">
-        <TodoList />
-      </ElCol>
-    </ElRow>
+    <section class="grid gap-4 md:grid-cols-3">
+      <ArtStatsCard
+        title="宸叉帴鍏ュ悗绔�"
+        :count="backendSwitchCount"
+        description="鐧诲綍銆佺敤鎴蜂俊鎭�佽彍鍗曞叏閮ㄦ潵鑷� rsf-server"
+        icon="ri:server-line"
+      />
+      <ArtStatsCard
+        title="鍔ㄦ�佽彍鍗�"
+        :count="visibleMenuCount"
+        description="浠呭彂甯� phase-1 鍏佽杩涘叆鐨勬柊鍏ュ彛"
+        icon="ri:route-line"
+      />
+      <ArtStatsCard
+        title="鏉冮檺閾捐矾"
+        :count="permissionSignalCount"
+        description="瑙掕壊涓庢潈闄愯妭鐐瑰凡浠庣湡瀹炵敤鎴锋暟鎹仮澶�"
+        icon="ri:shield-check-line"
+      />
+    </section>
 
-    <AboutProject />
+    <section class="grid gap-4 xl:grid-cols-[1.15fr_0.85fr]">
+      <ElCard
+        class="rounded-3xl border border-white/10 shadow-[0_18px_50px_rgba(15,23,42,0.06)]"
+        shadow="never"
+      >
+        <template #header>
+          <div class="flex items-center justify-between">
+            <div>
+              <h2 class="m-0 text-base font-semibold text-[var(--art-gray-900)]">鐪熷疄杩愯鐘舵��</h2>
+              <p class="mt-1 text-xs text-[var(--art-gray-500)]">
+                杩欎簺淇℃伅鏉ヨ嚜褰撳墠鐧诲綍鐢ㄦ埛鍜岃彍鍗� store
+              </p>
+            </div>
+            <ElTag type="success" effect="light">Backend mode</ElTag>
+          </div>
+        </template>
+
+        <div class="grid gap-4 md:grid-cols-2">
+          <div class="rounded-2xl bg-[var(--art-gray-50)] p-4">
+            <p class="text-xs uppercase tracking-[0.2em] text-[var(--art-gray-500)]">User</p>
+            <p class="mt-3 text-xl font-semibold text-[var(--art-gray-900)]">
+              {{ currentUserName }}
+            </p>
+            <p class="mt-2 text-sm text-[var(--art-gray-600)]">
+              {{ currentUserRoleText }}
+            </p>
+            <div class="mt-4 flex flex-wrap gap-2">
+              <ElTag v-for="role in currentRoles" :key="role" type="info" effect="plain">
+                {{ role }}
+              </ElTag>
+              <ElTag v-if="!currentRoles.length" type="info" effect="plain">No roles</ElTag>
+            </div>
+          </div>
+
+          <div class="rounded-2xl bg-[var(--art-gray-50)] p-4">
+            <p class="text-xs uppercase tracking-[0.2em] text-[var(--art-gray-500)]">Permissions</p>
+            <p class="mt-3 text-xl font-semibold text-[var(--art-gray-900)]">
+              {{ currentAuthorities.length }} auth nodes
+            </p>
+            <p class="mt-2 text-sm text-[var(--art-gray-600)]">
+              鏉冮檺鑺傜偣鐩存帴鏉ヨ嚜褰撳墠鐢ㄦ埛鐨勭湡瀹� `authorities` 杞借嵎锛屼笉鍐嶄緷璧栨ā鏉挎紨绀烘�併��
+            </p>
+            <div class="mt-4 flex flex-wrap gap-2">
+              <ElTag v-for="item in previewAuthorities" :key="item" type="warning" effect="plain">
+                {{ item }}
+              </ElTag>
+              <ElTag v-if="!previewAuthorities.length" type="warning" effect="plain">
+                No authorities
+              </ElTag>
+            </div>
+          </div>
+        </div>
+      </ElCard>
+
+      <ElCard
+        class="rounded-3xl border border-white/10 shadow-[0_18px_50px_rgba(15,23,42,0.06)]"
+        shadow="never"
+      >
+        <template #header>
+          <div>
+            <h2 class="m-0 text-base font-semibold text-[var(--art-gray-900)]">鑿滃崟鎺ュ叆娓呭崟</h2>
+            <p class="mt-1 text-xs text-[var(--art-gray-500)]">
+              褰撳墠鑿滃崟鏍戠敤浜庨獙璇� `rsf-design` 鏄惁鐪熸鎺ヤ綇鍚庣鍙戝竷
+            </p>
+          </div>
+        </template>
+
+        <div class="space-y-3">
+          <div
+            v-for="item in menuPreview"
+            :key="item.key"
+            class="flex items-center justify-between rounded-2xl border border-[var(--art-gray-200)] px-4 py-3"
+          >
+            <div>
+              <p class="text-sm font-medium text-[var(--art-gray-900)]">{{ item.title }}</p>
+              <p class="mt-1 text-xs text-[var(--art-gray-500)]">{{ item.description }}</p>
+            </div>
+            <ElTag :type="item.type" effect="light">
+              {{ item.value }}
+            </ElTag>
+          </div>
+        </div>
+      </ElCard>
+    </section>
   </div>
 </template>
 
 <script setup>
-  import CardList from './modules/card-list.vue'
-  import ActiveUser from './modules/active-user.vue'
-
-  import SalesOverview from './modules/sales-overview.vue'
-  import NewUser from './modules/new-user.vue'
-
-  import Dynamic from './modules/dynamic-stats.vue'
-  import TodoList from './modules/todo-list.vue'
-
-  import AboutProject from './modules/about-project.vue'
+  import { storeToRefs } from 'pinia'
+  import { useUserStore } from '@/store/modules/user'
+  import { useMenuStore } from '@/store/modules/menu'
+  import { formatMenuTitle } from '@/utils/router'
 
   defineOptions({ name: 'Console' })
+
+  const userStore = useUserStore()
+  const menuStore = useMenuStore()
+  const { getUserInfo } = storeToRefs(userStore)
+  const { menuList } = storeToRefs(menuStore)
+
+  const currentUser = computed(() => getUserInfo.value || {})
+  const currentUserName = computed(() => {
+    return (
+      currentUser.value.userName ||
+      currentUser.value.username ||
+      currentUser.value.nickname ||
+      'RSF User'
+    )
+  })
+  const currentRoles = computed(() => {
+    const roles = currentUser.value.roles
+    if (!Array.isArray(roles)) return []
+    return roles
+      .map((role) => {
+        if (typeof role === 'string') {
+          return role
+        }
+        return role?.code || role?.name || role?.title || ''
+      })
+      .filter(Boolean)
+  })
+  const currentAuthorities = computed(() => {
+    const authorities = currentUser.value.authorities
+    if (!Array.isArray(authorities)) return []
+    return authorities.map((item) => item?.authority || '').filter(Boolean)
+  })
+  const currentMenuLabel = computed(() => {
+    const firstMenu = menuList.value?.[0]
+    return formatMenuTitle(firstMenu?.meta?.title || 'menus.dashboard.console')
+  })
+  const currentUserRoleText = computed(() => {
+    if (!currentRoles.value.length) {
+      return 'No role information yet'
+    }
+    return `Roles: ${currentRoles.value.join(' / ')}`
+  })
+  const backendSwitchCount = computed(() => {
+    return currentUserName.value !== 'RSF User' || menuList.value.length > 0 ? 1 : 0
+  })
+  const visibleMenuCount = computed(() => countVisibleMenus(menuList.value))
+  const permissionSignalCount = computed(() => {
+    return currentRoles.value.length + currentAuthorities.value.length
+  })
+  const previewAuthorities = computed(() => currentAuthorities.value.slice(0, 4))
+  const menuPreview = computed(() => {
+    const total = visibleMenuCount.value
+    const rootCount = Array.isArray(menuList.value) ? menuList.value.length : 0
+    const firstMenu = menuList.value?.[0]
+    const firstChildren = Array.isArray(firstMenu?.children) ? firstMenu.children.length : 0
+
+    return [
+      {
+        key: 'entry',
+        title: '鍏ュ彛妯″紡',
+        description: '褰撳墠椤甸潰浠� backend mode 杩愯锛岃彍鍗曠敱鏈嶅姟绔┍鍔�',
+        value: 'backend',
+        type: 'success'
+      },
+      {
+        key: 'menus',
+        title: '鍙鑿滃崟',
+        description: '宸查�氳繃鍔ㄦ�佽矾鐢遍�傞厤鍚庤繘鍏ュ墠绔彍鍗曟爲',
+        value: `${total}`,
+        type: 'primary'
+      },
+      {
+        key: 'root',
+        title: '涓�绾х洰褰�',
+        description: '鏍圭骇鑿滃崟鑺傜偣鏁伴噺',
+        value: `${rootCount}`,
+        type: 'info'
+      },
+      {
+        key: 'children',
+        title: '棣栦釜鐩綍瀛愰」',
+        description: '鐢ㄤ簬蹇�熺‘璁よ彍鍗曟爲宸茶姝g‘灞曞紑',
+        value: `${firstChildren}`,
+        type: 'warning'
+      }
+    ]
+  })
+
+  function countVisibleMenus(items) {
+    if (!Array.isArray(items)) return 0
+    return items.reduce((total, item) => {
+      const current = item?.meta?.isHide ? 0 : 1
+      return total + current + countVisibleMenus(item?.children)
+    }, 0)
+  }
 </script>

--
Gitblit v1.9.1