style(frontend): 优化 Components 代码风格和结构

- 统一移除语句末尾分号,规范代码格式
- 优化组件类型定义和 props 声明
- 改进组件文档和示例代码
- 提升代码可读性和一致性
This commit is contained in:
ianshaw
2025-12-25 08:40:12 -08:00
parent 1ac8b1f03e
commit 5deef27e1d
38 changed files with 2582 additions and 1485 deletions

View File

@@ -6,20 +6,20 @@
```typescript
// In your view files
import { AppLayout, AuthLayout } from '@/components/layout';
import { AppLayout, AuthLayout } from '@/components/layout'
```
### 2. Use in Routes
```typescript
// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router';
import type { RouteRecordRaw } from 'vue-router';
import { createRouter, createWebHistory } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router'
// Views
import DashboardView from '@/views/DashboardView.vue';
import LoginView from '@/views/auth/LoginView.vue';
import RegisterView from '@/views/auth/RegisterView.vue';
import DashboardView from '@/views/DashboardView.vue'
import LoginView from '@/views/auth/LoginView.vue'
import RegisterView from '@/views/auth/RegisterView.vue'
const routes: RouteRecordRaw[] = [
// Auth routes (no layout needed - views use AuthLayout internally)
@@ -27,13 +27,13 @@ const routes: RouteRecordRaw[] = [
path: '/login',
name: 'Login',
component: LoginView,
meta: { requiresAuth: false },
meta: { requiresAuth: false }
},
{
path: '/register',
name: 'Register',
component: RegisterView,
meta: { requiresAuth: false },
meta: { requiresAuth: false }
},
// User routes (use AppLayout)
@@ -41,31 +41,31 @@ const routes: RouteRecordRaw[] = [
path: '/dashboard',
name: 'Dashboard',
component: DashboardView,
meta: { requiresAuth: true, title: 'Dashboard' },
meta: { requiresAuth: true, title: 'Dashboard' }
},
{
path: '/api-keys',
name: 'ApiKeys',
component: () => import('@/views/ApiKeysView.vue'),
meta: { requiresAuth: true, title: 'API Keys' },
meta: { requiresAuth: true, title: 'API Keys' }
},
{
path: '/usage',
name: 'Usage',
component: () => import('@/views/UsageView.vue'),
meta: { requiresAuth: true, title: 'Usage Statistics' },
meta: { requiresAuth: true, title: 'Usage Statistics' }
},
{
path: '/redeem',
name: 'Redeem',
component: () => import('@/views/RedeemView.vue'),
meta: { requiresAuth: true, title: 'Redeem Code' },
meta: { requiresAuth: true, title: 'Redeem Code' }
},
{
path: '/profile',
name: 'Profile',
component: () => import('@/views/ProfileView.vue'),
meta: { requiresAuth: true, title: 'Profile Settings' },
meta: { requiresAuth: true, title: 'Profile Settings' }
},
// Admin routes (use AppLayout, admin only)
@@ -73,91 +73,91 @@ const routes: RouteRecordRaw[] = [
path: '/admin/dashboard',
name: 'AdminDashboard',
component: () => import('@/views/admin/DashboardView.vue'),
meta: { requiresAuth: true, requiresAdmin: true, title: 'Admin Dashboard' },
meta: { requiresAuth: true, requiresAdmin: true, title: 'Admin Dashboard' }
},
{
path: '/admin/users',
name: 'AdminUsers',
component: () => import('@/views/admin/UsersView.vue'),
meta: { requiresAuth: true, requiresAdmin: true, title: 'User Management' },
meta: { requiresAuth: true, requiresAdmin: true, title: 'User Management' }
},
{
path: '/admin/groups',
name: 'AdminGroups',
component: () => import('@/views/admin/GroupsView.vue'),
meta: { requiresAuth: true, requiresAdmin: true, title: 'Groups' },
meta: { requiresAuth: true, requiresAdmin: true, title: 'Groups' }
},
{
path: '/admin/accounts',
name: 'AdminAccounts',
component: () => import('@/views/admin/AccountsView.vue'),
meta: { requiresAuth: true, requiresAdmin: true, title: 'Accounts' },
meta: { requiresAuth: true, requiresAdmin: true, title: 'Accounts' }
},
{
path: '/admin/proxies',
name: 'AdminProxies',
component: () => import('@/views/admin/ProxiesView.vue'),
meta: { requiresAuth: true, requiresAdmin: true, title: 'Proxies' },
meta: { requiresAuth: true, requiresAdmin: true, title: 'Proxies' }
},
{
path: '/admin/redeem-codes',
name: 'AdminRedeemCodes',
component: () => import('@/views/admin/RedeemCodesView.vue'),
meta: { requiresAuth: true, requiresAdmin: true, title: 'Redeem Codes' },
meta: { requiresAuth: true, requiresAdmin: true, title: 'Redeem Codes' }
},
// Default redirect
{
path: '/',
redirect: '/dashboard',
},
];
redirect: '/dashboard'
}
]
const router = createRouter({
history: createWebHistory(),
routes,
});
routes
})
// Navigation guards
router.beforeEach((to, from, next) => {
const authStore = useAuthStore();
const authStore = useAuthStore()
if (to.meta.requiresAuth && !authStore.isAuthenticated) {
// Redirect to login if not authenticated
next('/login');
next('/login')
} else if (to.meta.requiresAdmin && !authStore.isAdmin) {
// Redirect to dashboard if not admin
next('/dashboard');
next('/dashboard')
} else {
next();
next()
}
});
})
export default router;
export default router
```
### 3. Initialize Stores in main.ts
```typescript
// src/main.ts
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
import router from './router';
import './style.css';
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
import './style.css'
const app = createApp(App);
const pinia = createPinia();
const app = createApp(App)
const pinia = createPinia()
app.use(pinia);
app.use(router);
app.use(pinia)
app.use(router)
// Initialize auth state on app startup
import { useAuthStore } from '@/stores';
const authStore = useAuthStore();
authStore.checkAuth();
import { useAuthStore } from '@/stores'
const authStore = useAuthStore()
authStore.checkAuth()
app.mount('#app');
app.mount('#app')
```
### 4. Update App.vue
@@ -193,7 +193,7 @@ app.mount('#app');
</template>
<script setup lang="ts">
import { AppLayout } from '@/components/layout';
import { AppLayout } from '@/components/layout'
// Your component logic here
</script>
@@ -205,23 +205,21 @@ import { AppLayout } from '@/components/layout';
<!-- src/views/auth/LoginView.vue -->
<template>
<AuthLayout>
<h2 class="text-2xl font-bold text-gray-900 mb-6">Login</h2>
<h2 class="mb-6 text-2xl font-bold text-gray-900">Login</h2>
<!-- Your login form here -->
<template #footer>
<p class="text-gray-600">
Don't have an account?
<router-link to="/register" class="text-indigo-600 hover:underline">
Sign up
</router-link>
<router-link to="/register" class="text-indigo-600 hover:underline"> Sign up </router-link>
</p>
</template>
</AuthLayout>
</template>
<script setup lang="ts">
import { AuthLayout } from '@/components/layout';
import { AuthLayout } from '@/components/layout'
// Your login logic here
</script>
@@ -250,7 +248,7 @@ Replace HTML entity icons with your preferred icon library:
<span class="text-lg">&#128200;</span>
<!-- After (Heroicons example) -->
<ChartBarIcon class="w-5 h-5" />
<ChartBarIcon class="h-5 w-5" />
```
### Sidebar Customization
@@ -261,9 +259,9 @@ Modify navigation items in `AppSidebar.vue`:
// Add/remove/modify navigation items
const userNavItems = [
{ path: '/dashboard', label: 'Dashboard', icon: '&#128200;' },
{ path: '/new-page', label: 'New Page', icon: '&#128196;' }, // Add new item
{ path: '/new-page', label: 'New Page', icon: '&#128196;' } // Add new item
// ...
];
]
```
### Header Customization
@@ -287,10 +285,12 @@ Modify user dropdown in `AppHeader.vue`:
## Mobile Responsive Behavior
### Sidebar
- **Desktop (md+)**: Always visible, can be collapsed to icon-only view
- **Mobile**: Hidden by default, shown via menu toggle in header
### Header
- **Desktop**: Shows full user info and balance
- **Mobile**: Shows compact view with hamburger menu
@@ -299,7 +299,7 @@ To improve mobile experience, you can add overlay and transitions:
```vue
<!-- AppSidebar.vue enhancement for mobile -->
<aside
class="fixed left-0 top-0 h-screen transition-transform duration-300 z-40"
class="fixed left-0 top-0 z-40 h-screen transition-transform duration-300"
:class="[
sidebarCollapsed ? 'w-16' : 'w-64',
// Hide on mobile when collapsed
@@ -314,7 +314,7 @@ To improve mobile experience, you can add overlay and transitions:
<div
v-if="!sidebarCollapsed"
@click="toggleSidebar"
class="fixed inset-0 bg-black bg-opacity-50 z-30 md:hidden"
class="fixed inset-0 z-30 bg-black bg-opacity-50 md:hidden"
></div>
```
@@ -325,9 +325,9 @@ To improve mobile experience, you can add overlay and transitions:
### Auth Store Usage
```typescript
import { useAuthStore } from '@/stores';
import { useAuthStore } from '@/stores'
const authStore = useAuthStore();
const authStore = useAuthStore()
// Check if user is authenticated
if (authStore.isAuthenticated) {
@@ -340,34 +340,34 @@ if (authStore.isAdmin) {
}
// Get current user
const user = authStore.user;
const user = authStore.user
```
### App Store Usage
```typescript
import { useAppStore } from '@/stores';
import { useAppStore } from '@/stores'
const appStore = useAppStore();
const appStore = useAppStore()
// Toggle sidebar
appStore.toggleSidebar();
appStore.toggleSidebar()
// Show notifications
appStore.showSuccess('Operation completed!');
appStore.showError('Something went wrong');
appStore.showInfo('Did you know...');
appStore.showWarning('Be careful!');
appStore.showSuccess('Operation completed!')
appStore.showError('Something went wrong')
appStore.showInfo('Did you know...')
appStore.showWarning('Be careful!')
// Loading state
appStore.setLoading(true);
appStore.setLoading(true)
// ... perform operation
appStore.setLoading(false);
appStore.setLoading(false)
// Or use helper
await appStore.withLoading(async () => {
// Your async operation
});
})
```
---
@@ -388,7 +388,7 @@ To enhance further:
<!-- Add skip to main content link -->
<a
href="#main-content"
class="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 bg-white px-4 py-2 rounded"
class="sr-only rounded bg-white px-4 py-2 focus:not-sr-only focus:absolute focus:left-4 focus:top-4"
>
Skip to main content
</a>
@@ -406,27 +406,27 @@ To enhance further:
```typescript
// AppHeader.test.ts
import { describe, it, expect, beforeEach } from 'vitest';
import { mount } from '@vue/test-utils';
import { createPinia, setActivePinia } from 'pinia';
import AppHeader from '@/components/layout/AppHeader.vue';
import { describe, it, expect, beforeEach } from 'vitest'
import { mount } from '@vue/test-utils'
import { createPinia, setActivePinia } from 'pinia'
import AppHeader from '@/components/layout/AppHeader.vue'
describe('AppHeader', () => {
beforeEach(() => {
setActivePinia(createPinia());
});
setActivePinia(createPinia())
})
it('renders user info when authenticated', () => {
const wrapper = mount(AppHeader);
const wrapper = mount(AppHeader)
// Add assertions
});
})
it('shows dropdown when clicked', async () => {
const wrapper = mount(AppHeader);
await wrapper.find('button').trigger('click');
expect(wrapper.find('.dropdown').exists()).toBe(true);
});
});
const wrapper = mount(AppHeader)
await wrapper.find('button').trigger('click')
expect(wrapper.find('.dropdown').exists()).toBe(true)
})
})
```
---
@@ -443,7 +443,7 @@ Layout components are automatically code-split when imported:
```typescript
// This creates a separate chunk for layout components
import { AppLayout } from '@/components/layout';
import { AppLayout } from '@/components/layout'
```
### Reducing Re-renders
@@ -451,7 +451,7 @@ import { AppLayout } from '@/components/layout';
Layout components use `computed` refs to prevent unnecessary re-renders:
```typescript
const sidebarCollapsed = computed(() => appStore.sidebarCollapsed);
const sidebarCollapsed = computed(() => appStore.sidebarCollapsed)
// This only re-renders when sidebarCollapsed changes
```
@@ -460,21 +460,25 @@ const sidebarCollapsed = computed(() => appStore.sidebarCollapsed);
## Troubleshooting
### Sidebar not showing
- Check if `useAppStore` is properly initialized
- Verify Tailwind classes are being processed
- Check z-index conflicts with other components
### Routes not highlighting in sidebar
- Ensure route paths match exactly
- Check `isActive()` function logic
- Verify `useRoute()` is working correctly
### User info not displaying
- Ensure auth store is initialized with `checkAuth()`
- Verify user is logged in
- Check localStorage for auth data
### Mobile menu not working
- Verify `toggleSidebar()` is called correctly
- Check responsive breakpoints (md:)
- Test on actual mobile device or browser dev tools