feat: refactor monolithic App.vue into modular Vue component architecture
- Extract 711-line App.vue into 15+ focused files across 5 directories - Add data layer (icons, metrics, policies, auditTrail, requests) - Add composables (useNavigation, useRequests, useChat, useToast) - Add layout components (SidebarRail, TopBar, FilterBar) - Add shared components (PanelHead, InfoRow, ToastNotification) - Add business component (RequestTable) and 5 view components - Extract global CSS to assets/styles/global.css - Add start.sh with WSL/Windows cross-platform support - Add .gitignore for node_modules, dist, and IDE dirs
This commit is contained in:
70
src/components/layout/SidebarRail.vue
Normal file
70
src/components/layout/SidebarRail.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<aside class="rail" aria-label="主导航">
|
||||
<div class="mark">RO</div>
|
||||
<nav class="rail-nav">
|
||||
<button
|
||||
v-for="item in navItems"
|
||||
:key="item.id"
|
||||
class="nav-btn"
|
||||
:class="{ active: activeView === item.id }"
|
||||
type="button"
|
||||
:aria-label="item.label"
|
||||
:title="item.label"
|
||||
@click="emit('navigate', item.id)"
|
||||
>
|
||||
<span v-html="item.icon"></span>
|
||||
</button>
|
||||
</nav>
|
||||
<button class="nav-btn muted" type="button" aria-label="打开合规对话" title="打开合规对话" @click="emit('openChat')">
|
||||
<span v-html="messageIcon"></span>
|
||||
</button>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { icons } from '../../data/icons.js'
|
||||
|
||||
defineProps({
|
||||
navItems: { type: Array, required: true },
|
||||
activeView: { type: String, required: true }
|
||||
})
|
||||
|
||||
const emit = defineEmits(['navigate', 'openChat'])
|
||||
|
||||
const messageIcon = icons.message
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.rail {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
height: 100dvh;
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
gap: 18px;
|
||||
padding: 18px 12px;
|
||||
background: var(--nav);
|
||||
color: #fff;
|
||||
z-index: 20;
|
||||
}
|
||||
.mark { width: 48px; height: 48px; display: grid; place-items: center; border-radius: 14px; background: linear-gradient(135deg,#fff,#9db2ff); color: #10215c; font-weight: 850; }
|
||||
.rail-nav { display: grid; gap: 10px; align-content: start; }
|
||||
.nav-btn {
|
||||
width: 48px;
|
||||
min-height: 48px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border: 0;
|
||||
border-radius: 14px;
|
||||
background: transparent;
|
||||
color: var(--nav-muted);
|
||||
transition: background 160ms var(--ease), color 160ms var(--ease), transform 160ms var(--ease);
|
||||
}
|
||||
.nav-btn:hover, .nav-btn.active { background: rgba(255,255,255,.1); color: #fff; transform: translateY(-1px); }
|
||||
.nav-btn svg { width: 18px; height: 18px; stroke: currentColor; stroke-width: 2; fill: none; stroke-linecap: round; stroke-linejoin: round; }
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.rail { position: sticky; height: auto; grid-template-columns: auto 1fr auto; grid-template-rows: none; padding: 10px 12px; }
|
||||
.rail-nav { display: flex; overflow-x: auto; }
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user