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:
56
src/components/layout/TopBar.vue
Normal file
56
src/components/layout/TopBar.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<header class="topbar">
|
||||
<div>
|
||||
<div class="eyebrow">Global finance operations</div>
|
||||
<h1>{{ currentView.title }}</h1>
|
||||
<p>{{ currentView.desc }}</p>
|
||||
</div>
|
||||
<div class="top-actions">
|
||||
<label class="search">
|
||||
<span v-html="searchIcon"></span>
|
||||
<input :value="search" type="search" placeholder="搜索申请人、单号、费用类型" @input="emit('update:search', $event.target.value)" />
|
||||
</label>
|
||||
<button class="btn" type="button" @click="emit('batchApprove')">
|
||||
<span v-html="checkIcon"></span>
|
||||
批量通过
|
||||
</button>
|
||||
<button class="btn primary" type="button" @click="emit('openChat')">
|
||||
<span v-html="messageIcon"></span>
|
||||
合规对话
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { icons } from '../../data/icons.js'
|
||||
|
||||
defineProps({
|
||||
currentView: { type: Object, required: true },
|
||||
search: { type: String, default: '' }
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:search', 'batchApprove', 'openChat'])
|
||||
|
||||
const searchIcon = icons.search
|
||||
const checkIcon = icons.check
|
||||
const messageIcon = icons.message
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.topbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
padding: 22px 28px;
|
||||
border-bottom: 1px solid var(--line);
|
||||
background: rgba(255,255,255,.9);
|
||||
backdrop-filter: blur(14px);
|
||||
}
|
||||
.topbar p { margin-top: 6px; color: var(--muted); font-size: 13px; }
|
||||
.top-actions { display: flex; align-items: center; justify-content: flex-end; gap: 10px; flex-wrap: wrap; }
|
||||
.search { position: relative; min-width: 300px; }
|
||||
.search span { position: absolute; left: 13px; top: 50%; width: 18px; height: 18px; transform: translateY(-50%); color: var(--muted); }
|
||||
.search span svg { width: 18px; height: 18px; stroke: currentColor; stroke-width: 2; fill: none; stroke-linecap: round; stroke-linejoin: round; }
|
||||
.search input { width: 100%; height: 42px; padding: 0 14px 0 40px; border: 1px solid var(--line); border-radius: var(--radius); }
|
||||
</style>
|
||||
Reference in New Issue
Block a user