| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- <template>
- <aside class="sidebar">
- <div class="sidebar-header">
- <div class="sidebar-logo">订</div>
- <div class="sidebar-brand-wrap">
- <div class="sidebar-brand">发货管理</div>
- <div class="sidebar-brand-sub">Order Shipping v2.0</div>
- </div>
- </div>
- <nav class="sidebar-nav">
- <!-- 金山烟草 -->
- <div class="nav-section-label">金山烟草</div>
- <div
- class="nav-item"
- :class="{ active: currentPage === 'js-orders' }"
- @click="$emit('navigate', 'js-orders')"
- >
- <svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
- <path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/>
- <line x1="3" y1="6" x2="21" y2="6"/>
- <path d="M16 10a4 4 0 0 1-8 0"/>
- </svg>
- <span>订单管理</span>
- <span v-if="pendingCount.js > 0" class="nav-badge">{{ pendingCount.js }}</span>
- </div>
- <div
- class="nav-item"
- :class="{ active: currentPage === 'js-import' }"
- @click="$emit('navigate', 'js-import')"
- >
- <svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
- <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
- <polyline points="17 8 12 3 7 8"/>
- <line x1="12" y1="3" x2="12" y2="15"/>
- </svg>
- <span>物流导入</span>
- </div>
- <div
- v-if="role === 'admin'"
- class="nav-item"
- :class="{ active: currentPage === 'js-logs' }"
- @click="$emit('navigate', 'js-logs')"
- >
- <svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
- <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
- <polyline points="14 2 14 8 20 8"/>
- <line x1="16" y1="13" x2="8" y2="13"/>
- <line x1="16" y1="17" x2="8" y2="17"/>
- </svg>
- <span>操作日志</span>
- </div>
- <!-- 嘉定烟草 -->
- <div class="nav-section-label" style="margin-top: var(--space-3);">嘉定烟草</div>
- <div
- class="nav-item"
- :class="{ active: currentPage === 'jd-orders' }"
- @click="$emit('navigate', 'jd-orders')"
- >
- <svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
- <path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"/>
- <line x1="3" y1="6" x2="21" y2="6"/>
- <path d="M16 10a4 4 0 0 1-8 0"/>
- </svg>
- <span>订单管理</span>
- <span v-if="pendingCount.jd > 0" class="nav-badge">{{ pendingCount.jd }}</span>
- </div>
- <div
- class="nav-item"
- :class="{ active: currentPage === 'jd-import' }"
- @click="$emit('navigate', 'jd-import')"
- >
- <svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
- <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
- <polyline points="17 8 12 3 7 8"/>
- <line x1="12" y1="3" x2="12" y2="15"/>
- </svg>
- <span>物流导入</span>
- </div>
- <div
- v-if="role === 'admin'"
- class="nav-item"
- :class="{ active: currentPage === 'jd-logs' }"
- @click="$emit('navigate', 'jd-logs')"
- >
- <svg class="nav-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
- <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
- <polyline points="14 2 14 8 20 8"/>
- <line x1="16" y1="13" x2="8" y2="13"/>
- <line x1="16" y1="17" x2="8" y2="17"/>
- </svg>
- <span>操作日志</span>
- </div>
- </nav>
- <div class="sidebar-footer">
- <div class="sidebar-user" @click="$emit('logout')">
- <div class="avatar">{{ username?.charAt(0)?.toUpperCase() || 'U' }}</div>
- <div class="sidebar-user-info">
- <div class="sidebar-user-name">{{ username }}</div>
- <div class="sidebar-user-role">{{ role === 'admin' ? '管理员' : '普通用户' }} · 点击退出</div>
- </div>
- <svg class="nav-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color: var(--text-tertiary);">
- <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/>
- <polyline points="16 17 21 12 16 7"/>
- <line x1="21" y1="12" x2="9" y2="12"/>
- </svg>
- </div>
- </div>
- </aside>
- </template>
- <script setup>
- defineProps({
- currentPage: String,
- username: String,
- role: String,
- pendingCount: { type: Object, default: () => ({ js: 0, jd: 0 }) }
- })
- defineEmits(['navigate', 'logout'])
- </script>
- <style scoped>
- .sidebar {
- width: var(--sidebar-width);
- flex-shrink: 0;
- background: var(--bg-surface);
- border-right: 1px solid var(--border-default);
- display: flex;
- flex-direction: column;
- position: sticky;
- top: 0;
- height: 100vh;
- transition: width var(--dur-normal) var(--ease-out);
- z-index: 50;
- }
- .sidebar-header {
- height: var(--header-height);
- padding: 0 var(--space-5);
- display: flex;
- align-items: center;
- gap: var(--space-3);
- border-bottom: 1px solid var(--border-subtle);
- }
- .sidebar-logo {
- width: 32px; height: 32px;
- border-radius: var(--radius-sm);
- background: var(--primary-600);
- color: white;
- display: flex;
- align-items: center;
- justify-content: center;
- font-weight: 800;
- font-size: var(--text-md);
- flex-shrink: 0;
- }
- .sidebar-brand { font-weight: 700; font-size: var(--text-md); color: var(--text-primary); }
- .sidebar-brand-sub { font-size: var(--text-xs); color: var(--text-tertiary); font-weight: 500; }
- .sidebar-nav { flex: 1; padding: var(--space-4); overflow-y: auto; }
- .nav-section-label {
- font-size: var(--text-xs);
- font-weight: 700;
- text-transform: uppercase;
- letter-spacing: 0.06em;
- color: var(--text-tertiary);
- padding: var(--space-3) var(--space-3) var(--space-2);
- }
- .nav-item {
- display: flex;
- align-items: center;
- gap: var(--space-3);
- padding: 9px var(--space-3);
- border-radius: var(--radius-sm);
- color: var(--text-secondary);
- font-size: var(--text-base);
- font-weight: 500;
- cursor: pointer;
- margin-bottom: 2px;
- position: relative;
- transition: all var(--dur-fast) var(--ease-out);
- }
- .nav-item:hover { background: var(--bg-hover); color: var(--text-primary); }
- .nav-item.active {
- background: var(--primary-50);
- color: var(--primary-700);
- font-weight: 600;
- }
- .nav-item.active::before {
- content: '';
- position: absolute;
- left: -16px;
- top: 50%;
- transform: translateY(-50%);
- width: 3px;
- height: 20px;
- background: var(--primary-600);
- border-radius: var(--radius-full);
- }
- .nav-icon { width: 18px; height: 18px; flex-shrink: 0; }
- .nav-icon-sm { width: 15px; height: 15px; flex-shrink: 0; }
- .nav-badge {
- margin-left: auto;
- font-size: var(--text-xs);
- font-weight: 700;
- padding: 1px 7px;
- border-radius: var(--radius-full);
- background: var(--danger-500);
- color: white;
- }
- .sidebar-footer {
- padding: var(--space-4);
- border-top: 1px solid var(--border-subtle);
- }
- .sidebar-user {
- display: flex;
- align-items: center;
- gap: var(--space-3);
- padding: var(--space-2);
- border-radius: var(--radius-md);
- cursor: pointer;
- transition: background var(--dur-fast) var(--ease-out);
- }
- .sidebar-user:hover { background: var(--bg-hover); }
- .sidebar-user-info { flex: 1; min-width: 0; }
- .sidebar-user-name { font-size: var(--text-sm); font-weight: 600; color: var(--text-primary); }
- .sidebar-user-role { font-size: var(--text-xs); color: var(--text-tertiary); }
- .avatar {
- width: 36px; height: 36px;
- border-radius: var(--radius-full);
- display: inline-flex;
- align-items: center;
- justify-content: center;
- font-size: var(--text-sm);
- font-weight: 700;
- background: var(--primary-600);
- color: white;
- flex-shrink: 0;
- }
- @media (max-width: 768px) {
- .sidebar {
- position: fixed;
- transform: translateX(-100%);
- }
- }
- </style>
|