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