2025-12-18 13:50:39 +08:00
|
|
|
# Layout Component Examples
|
|
|
|
|
|
|
|
|
|
## Example 1: Dashboard Page
|
|
|
|
|
|
|
|
|
|
```vue
|
|
|
|
|
<template>
|
|
|
|
|
<AppLayout>
|
|
|
|
|
<div class="space-y-6">
|
|
|
|
|
<h1 class="text-3xl font-bold text-gray-900">Dashboard</h1>
|
|
|
|
|
|
2025-12-25 08:40:12 -08:00
|
|
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
|
2025-12-18 13:50:39 +08:00
|
|
|
<!-- Stats Cards -->
|
2025-12-25 08:40:12 -08:00
|
|
|
<div class="rounded-lg bg-white p-6 shadow">
|
2025-12-18 13:50:39 +08:00
|
|
|
<div class="text-sm text-gray-600">API Keys</div>
|
|
|
|
|
<div class="text-2xl font-bold text-gray-900">5</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-12-25 08:40:12 -08:00
|
|
|
<div class="rounded-lg bg-white p-6 shadow">
|
2025-12-18 13:50:39 +08:00
|
|
|
<div class="text-sm text-gray-600">Total Usage</div>
|
|
|
|
|
<div class="text-2xl font-bold text-gray-900">1,234</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-12-25 08:40:12 -08:00
|
|
|
<div class="rounded-lg bg-white p-6 shadow">
|
2025-12-18 13:50:39 +08:00
|
|
|
<div class="text-sm text-gray-600">Balance</div>
|
|
|
|
|
<div class="text-2xl font-bold text-indigo-600">${{ balance }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-12-25 08:40:12 -08:00
|
|
|
<div class="rounded-lg bg-white p-6 shadow">
|
2025-12-18 13:50:39 +08:00
|
|
|
<div class="text-sm text-gray-600">Status</div>
|
|
|
|
|
<div class="text-2xl font-bold text-green-600">Active</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-12-25 08:40:12 -08:00
|
|
|
<div class="rounded-lg bg-white p-6 shadow">
|
|
|
|
|
<h2 class="mb-4 text-xl font-semibold">Recent Activity</h2>
|
2025-12-18 13:50:39 +08:00
|
|
|
<p class="text-gray-600">No recent activity</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</AppLayout>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2025-12-25 08:40:12 -08:00
|
|
|
import { computed } from 'vue'
|
|
|
|
|
import { AppLayout } from '@/components/layout'
|
|
|
|
|
import { useAuthStore } from '@/stores'
|
2025-12-18 13:50:39 +08:00
|
|
|
|
2025-12-25 08:40:12 -08:00
|
|
|
const authStore = useAuthStore()
|
|
|
|
|
const balance = computed(() => authStore.user?.balance.toFixed(2) || '0.00')
|
2025-12-18 13:50:39 +08:00
|
|
|
</script>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Example 2: Login Page
|
|
|
|
|
|
|
|
|
|
```vue
|
|
|
|
|
<template>
|
|
|
|
|
<AuthLayout>
|
2025-12-25 08:40:12 -08:00
|
|
|
<h2 class="mb-6 text-2xl font-bold text-gray-900">Welcome Back</h2>
|
2025-12-18 13:50:39 +08:00
|
|
|
|
|
|
|
|
<form @submit.prevent="handleSubmit" class="space-y-4">
|
|
|
|
|
<div>
|
2025-12-25 08:40:12 -08:00
|
|
|
<label for="username" class="mb-1 block text-sm font-medium text-gray-700">
|
2025-12-18 13:50:39 +08:00
|
|
|
Username
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
id="username"
|
|
|
|
|
v-model="form.username"
|
|
|
|
|
type="text"
|
|
|
|
|
required
|
2025-12-25 08:40:12 -08:00
|
|
|
class="w-full rounded-lg border border-gray-300 px-3 py-2 focus:border-transparent focus:ring-2 focus:ring-indigo-500"
|
2025-12-18 13:50:39 +08:00
|
|
|
placeholder="Enter your username"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
2025-12-25 08:40:12 -08:00
|
|
|
<label for="password" class="mb-1 block text-sm font-medium text-gray-700">
|
2025-12-18 13:50:39 +08:00
|
|
|
Password
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
id="password"
|
|
|
|
|
v-model="form.password"
|
|
|
|
|
type="password"
|
|
|
|
|
required
|
2025-12-25 08:40:12 -08:00
|
|
|
class="w-full rounded-lg border border-gray-300 px-3 py-2 focus:border-transparent focus:ring-2 focus:ring-indigo-500"
|
2025-12-18 13:50:39 +08:00
|
|
|
placeholder="Enter your password"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
type="submit"
|
|
|
|
|
:disabled="loading"
|
2025-12-25 08:40:12 -08:00
|
|
|
class="w-full rounded-lg bg-indigo-600 px-4 py-2 text-white transition-colors hover:bg-indigo-700 disabled:cursor-not-allowed disabled:opacity-50"
|
2025-12-18 13:50:39 +08:00
|
|
|
>
|
|
|
|
|
{{ loading ? 'Logging in...' : 'Login' }}
|
|
|
|
|
</button>
|
|
|
|
|
</form>
|
|
|
|
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
<p class="text-gray-600">
|
|
|
|
|
Don't have an account?
|
2025-12-25 08:40:12 -08:00
|
|
|
<router-link to="/register" class="font-medium text-indigo-600 hover:underline">
|
2025-12-18 13:50:39 +08:00
|
|
|
Sign up
|
|
|
|
|
</router-link>
|
|
|
|
|
</p>
|
|
|
|
|
</template>
|
|
|
|
|
</AuthLayout>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2025-12-25 08:40:12 -08:00
|
|
|
import { ref } from 'vue'
|
|
|
|
|
import { useRouter } from 'vue-router'
|
|
|
|
|
import { AuthLayout } from '@/components/layout'
|
|
|
|
|
import { useAuthStore, useAppStore } from '@/stores'
|
2025-12-18 13:50:39 +08:00
|
|
|
|
2025-12-25 08:40:12 -08:00
|
|
|
const router = useRouter()
|
|
|
|
|
const authStore = useAuthStore()
|
|
|
|
|
const appStore = useAppStore()
|
2025-12-18 13:50:39 +08:00
|
|
|
|
|
|
|
|
const form = ref({
|
|
|
|
|
username: '',
|
2025-12-25 08:40:12 -08:00
|
|
|
password: ''
|
|
|
|
|
})
|
2025-12-18 13:50:39 +08:00
|
|
|
|
2025-12-25 08:40:12 -08:00
|
|
|
const loading = ref(false)
|
2025-12-18 13:50:39 +08:00
|
|
|
|
|
|
|
|
async function handleSubmit() {
|
2025-12-25 08:40:12 -08:00
|
|
|
loading.value = true
|
2025-12-18 13:50:39 +08:00
|
|
|
try {
|
2025-12-25 08:40:12 -08:00
|
|
|
await authStore.login(form.value)
|
|
|
|
|
appStore.showSuccess('Login successful!')
|
|
|
|
|
await router.push('/dashboard')
|
2025-12-18 13:50:39 +08:00
|
|
|
} catch (error) {
|
2025-12-25 08:40:12 -08:00
|
|
|
appStore.showError('Invalid username or password')
|
2025-12-18 13:50:39 +08:00
|
|
|
} finally {
|
2025-12-25 08:40:12 -08:00
|
|
|
loading.value = false
|
2025-12-18 13:50:39 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Example 3: API Keys Page with Custom Header Title
|
|
|
|
|
|
|
|
|
|
```vue
|
|
|
|
|
<template>
|
|
|
|
|
<AppLayout>
|
|
|
|
|
<div class="space-y-6">
|
|
|
|
|
<!-- Custom page header -->
|
|
|
|
|
<div class="flex items-center justify-between">
|
|
|
|
|
<h1 class="text-3xl font-bold text-gray-900">API Keys</h1>
|
|
|
|
|
<button
|
|
|
|
|
@click="showCreateModal = true"
|
2025-12-25 08:40:12 -08:00
|
|
|
class="rounded-lg bg-indigo-600 px-4 py-2 text-white transition-colors hover:bg-indigo-700"
|
2025-12-18 13:50:39 +08:00
|
|
|
>
|
|
|
|
|
Create New Key
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- API Keys List -->
|
2025-12-25 08:40:12 -08:00
|
|
|
<div class="overflow-hidden rounded-lg bg-white shadow">
|
2025-12-18 13:50:39 +08:00
|
|
|
<table class="min-w-full divide-y divide-gray-200">
|
|
|
|
|
<thead class="bg-gray-50">
|
|
|
|
|
<tr>
|
2025-12-25 08:40:12 -08:00
|
|
|
<th class="px-6 py-3 text-left text-xs font-medium uppercase text-gray-500">Name</th>
|
|
|
|
|
<th class="px-6 py-3 text-left text-xs font-medium uppercase text-gray-500">Key</th>
|
|
|
|
|
<th class="px-6 py-3 text-left text-xs font-medium uppercase text-gray-500">
|
2025-12-18 13:50:39 +08:00
|
|
|
Status
|
|
|
|
|
</th>
|
2025-12-25 08:40:12 -08:00
|
|
|
<th class="px-6 py-3 text-left text-xs font-medium uppercase text-gray-500">
|
2025-12-18 13:50:39 +08:00
|
|
|
Created
|
|
|
|
|
</th>
|
2025-12-25 08:40:12 -08:00
|
|
|
<th class="px-6 py-3 text-right text-xs font-medium uppercase text-gray-500">
|
2025-12-18 13:50:39 +08:00
|
|
|
Actions
|
|
|
|
|
</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
2025-12-25 08:40:12 -08:00
|
|
|
<tbody class="divide-y divide-gray-200 bg-white">
|
2025-12-18 13:50:39 +08:00
|
|
|
<tr v-for="key in apiKeys" :key="key.id">
|
2025-12-25 08:40:12 -08:00
|
|
|
<td class="whitespace-nowrap px-6 py-4">{{ key.name }}</td>
|
2025-12-18 13:50:39 +08:00
|
|
|
<td class="px-6 py-4 font-mono text-sm">{{ key.key }}</td>
|
|
|
|
|
<td class="px-6 py-4">
|
|
|
|
|
<span
|
2025-12-25 08:40:12 -08:00
|
|
|
class="rounded-full px-2 py-1 text-xs"
|
|
|
|
|
:class="
|
|
|
|
|
key.status === 'active'
|
|
|
|
|
? 'bg-green-100 text-green-800'
|
|
|
|
|
: 'bg-red-100 text-red-800'
|
|
|
|
|
"
|
2025-12-18 13:50:39 +08:00
|
|
|
>
|
|
|
|
|
{{ key.status }}
|
|
|
|
|
</span>
|
|
|
|
|
</td>
|
|
|
|
|
<td class="px-6 py-4 text-sm text-gray-500">
|
|
|
|
|
{{ new Date(key.created_at).toLocaleDateString() }}
|
|
|
|
|
</td>
|
|
|
|
|
<td class="px-6 py-4 text-right">
|
2025-12-25 08:40:12 -08:00
|
|
|
<button class="text-sm text-red-600 hover:text-red-800">Delete</button>
|
2025-12-18 13:50:39 +08:00
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</AppLayout>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2025-12-25 08:40:12 -08:00
|
|
|
import { ref } from 'vue'
|
|
|
|
|
import { AppLayout } from '@/components/layout'
|
|
|
|
|
import type { ApiKey } from '@/types'
|
2025-12-18 13:50:39 +08:00
|
|
|
|
2025-12-25 08:40:12 -08:00
|
|
|
const showCreateModal = ref(false)
|
|
|
|
|
const apiKeys = ref<ApiKey[]>([])
|
2025-12-18 13:50:39 +08:00
|
|
|
|
|
|
|
|
// Fetch API keys on mount
|
|
|
|
|
// fetchApiKeys();
|
|
|
|
|
</script>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Example 4: Admin Users Page
|
|
|
|
|
|
|
|
|
|
```vue
|
|
|
|
|
<template>
|
|
|
|
|
<AppLayout>
|
|
|
|
|
<div class="space-y-6">
|
|
|
|
|
<div class="flex items-center justify-between">
|
|
|
|
|
<h1 class="text-3xl font-bold text-gray-900">User Management</h1>
|
|
|
|
|
<button
|
|
|
|
|
@click="showCreateUser = true"
|
2025-12-25 08:40:12 -08:00
|
|
|
class="rounded-lg bg-indigo-600 px-4 py-2 text-white transition-colors hover:bg-indigo-700"
|
2025-12-18 13:50:39 +08:00
|
|
|
>
|
|
|
|
|
Create User
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Users Table -->
|
2025-12-25 08:40:12 -08:00
|
|
|
<div class="rounded-lg bg-white shadow">
|
2025-12-18 13:50:39 +08:00
|
|
|
<div class="p-6">
|
|
|
|
|
<div class="space-y-4">
|
2025-12-25 08:40:12 -08:00
|
|
|
<div
|
|
|
|
|
v-for="user in users"
|
|
|
|
|
:key="user.id"
|
|
|
|
|
class="flex items-center justify-between border-b pb-4"
|
|
|
|
|
>
|
2025-12-18 13:50:39 +08:00
|
|
|
<div>
|
|
|
|
|
<div class="font-medium text-gray-900">{{ user.username }}</div>
|
|
|
|
|
<div class="text-sm text-gray-500">{{ user.email }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="flex items-center space-x-4">
|
|
|
|
|
<span
|
2025-12-25 08:40:12 -08:00
|
|
|
class="rounded-full px-2 py-1 text-xs"
|
|
|
|
|
:class="
|
|
|
|
|
user.role === 'admin'
|
|
|
|
|
? 'bg-purple-100 text-purple-800'
|
|
|
|
|
: 'bg-blue-100 text-blue-800'
|
|
|
|
|
"
|
2025-12-18 13:50:39 +08:00
|
|
|
>
|
|
|
|
|
{{ user.role }}
|
|
|
|
|
</span>
|
|
|
|
|
<span class="text-sm font-medium text-gray-700">
|
|
|
|
|
${{ user.balance.toFixed(2) }}
|
|
|
|
|
</span>
|
2025-12-25 08:40:12 -08:00
|
|
|
<button class="text-sm text-indigo-600 hover:text-indigo-800">Edit</button>
|
2025-12-18 13:50:39 +08:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</AppLayout>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2025-12-25 08:40:12 -08:00
|
|
|
import { ref } from 'vue'
|
|
|
|
|
import { AppLayout } from '@/components/layout'
|
|
|
|
|
import type { User } from '@/types'
|
2025-12-18 13:50:39 +08:00
|
|
|
|
2025-12-25 08:40:12 -08:00
|
|
|
const showCreateUser = ref(false)
|
|
|
|
|
const users = ref<User[]>([])
|
2025-12-18 13:50:39 +08:00
|
|
|
|
|
|
|
|
// Fetch users on mount
|
|
|
|
|
// fetchUsers();
|
|
|
|
|
</script>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Example 5: Profile Page
|
|
|
|
|
|
|
|
|
|
```vue
|
|
|
|
|
<template>
|
|
|
|
|
<AppLayout>
|
|
|
|
|
<div class="max-w-2xl space-y-6">
|
|
|
|
|
<h1 class="text-3xl font-bold text-gray-900">Profile Settings</h1>
|
|
|
|
|
|
|
|
|
|
<!-- User Info Card -->
|
2025-12-25 08:40:12 -08:00
|
|
|
<div class="space-y-4 rounded-lg bg-white p-6 shadow">
|
2025-12-18 13:50:39 +08:00
|
|
|
<h2 class="text-xl font-semibold text-gray-900">Account Information</h2>
|
|
|
|
|
|
2025-12-25 08:40:12 -08:00
|
|
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
2025-12-18 13:50:39 +08:00
|
|
|
<div>
|
2025-12-25 08:40:12 -08:00
|
|
|
<label class="mb-1 block text-sm font-medium text-gray-700"> Username </label>
|
|
|
|
|
<div class="rounded-lg bg-gray-50 px-3 py-2 text-gray-900">
|
2025-12-18 13:50:39 +08:00
|
|
|
{{ user?.username }}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
2025-12-25 08:40:12 -08:00
|
|
|
<label class="mb-1 block text-sm font-medium text-gray-700"> Email </label>
|
|
|
|
|
<div class="rounded-lg bg-gray-50 px-3 py-2 text-gray-900">
|
2025-12-18 13:50:39 +08:00
|
|
|
{{ user?.email }}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
2025-12-25 08:40:12 -08:00
|
|
|
<label class="mb-1 block text-sm font-medium text-gray-700"> Role </label>
|
|
|
|
|
<div class="rounded-lg bg-gray-50 px-3 py-2">
|
2025-12-18 13:50:39 +08:00
|
|
|
<span
|
2025-12-25 08:40:12 -08:00
|
|
|
class="rounded-full px-2 py-1 text-xs"
|
|
|
|
|
:class="
|
|
|
|
|
user?.role === 'admin'
|
|
|
|
|
? 'bg-purple-100 text-purple-800'
|
|
|
|
|
: 'bg-blue-100 text-blue-800'
|
|
|
|
|
"
|
2025-12-18 13:50:39 +08:00
|
|
|
>
|
|
|
|
|
{{ user?.role }}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
2025-12-25 08:40:12 -08:00
|
|
|
<label class="mb-1 block text-sm font-medium text-gray-700"> Balance </label>
|
|
|
|
|
<div class="rounded-lg bg-gray-50 px-3 py-2 font-semibold text-indigo-600">
|
2025-12-18 13:50:39 +08:00
|
|
|
${{ user?.balance.toFixed(2) }}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Change Password Card -->
|
2025-12-25 08:40:12 -08:00
|
|
|
<div class="space-y-4 rounded-lg bg-white p-6 shadow">
|
2025-12-18 13:50:39 +08:00
|
|
|
<h2 class="text-xl font-semibold text-gray-900">Change Password</h2>
|
|
|
|
|
|
|
|
|
|
<form @submit.prevent="handleChangePassword" class="space-y-4">
|
|
|
|
|
<div>
|
2025-12-25 08:40:12 -08:00
|
|
|
<label for="old-password" class="mb-1 block text-sm font-medium text-gray-700">
|
2025-12-18 13:50:39 +08:00
|
|
|
Current Password
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
id="old-password"
|
|
|
|
|
v-model="passwordForm.old_password"
|
|
|
|
|
type="password"
|
|
|
|
|
required
|
2025-12-25 08:40:12 -08:00
|
|
|
class="w-full rounded-lg border border-gray-300 px-3 py-2 focus:ring-2 focus:ring-indigo-500"
|
2025-12-18 13:50:39 +08:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div>
|
2025-12-25 08:40:12 -08:00
|
|
|
<label for="new-password" class="mb-1 block text-sm font-medium text-gray-700">
|
2025-12-18 13:50:39 +08:00
|
|
|
New Password
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
id="new-password"
|
|
|
|
|
v-model="passwordForm.new_password"
|
|
|
|
|
type="password"
|
|
|
|
|
required
|
2025-12-25 08:40:12 -08:00
|
|
|
class="w-full rounded-lg border border-gray-300 px-3 py-2 focus:ring-2 focus:ring-indigo-500"
|
2025-12-18 13:50:39 +08:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
type="submit"
|
2025-12-25 08:40:12 -08:00
|
|
|
class="rounded-lg bg-indigo-600 px-4 py-2 text-white transition-colors hover:bg-indigo-700"
|
2025-12-18 13:50:39 +08:00
|
|
|
>
|
|
|
|
|
Update Password
|
|
|
|
|
</button>
|
|
|
|
|
</form>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</AppLayout>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2025-12-25 08:40:12 -08:00
|
|
|
import { ref, computed } from 'vue'
|
|
|
|
|
import { AppLayout } from '@/components/layout'
|
|
|
|
|
import { useAuthStore, useAppStore } from '@/stores'
|
2025-12-18 13:50:39 +08:00
|
|
|
|
2025-12-25 08:40:12 -08:00
|
|
|
const authStore = useAuthStore()
|
|
|
|
|
const appStore = useAppStore()
|
2025-12-18 13:50:39 +08:00
|
|
|
|
2025-12-25 08:40:12 -08:00
|
|
|
const user = computed(() => authStore.user)
|
2025-12-18 13:50:39 +08:00
|
|
|
|
|
|
|
|
const passwordForm = ref({
|
|
|
|
|
old_password: '',
|
2025-12-25 08:40:12 -08:00
|
|
|
new_password: ''
|
|
|
|
|
})
|
2025-12-18 13:50:39 +08:00
|
|
|
|
|
|
|
|
async function handleChangePassword() {
|
|
|
|
|
try {
|
|
|
|
|
// await changePasswordAPI(passwordForm.value);
|
2025-12-25 08:40:12 -08:00
|
|
|
appStore.showSuccess('Password updated successfully!')
|
|
|
|
|
passwordForm.value = { old_password: '', new_password: '' }
|
2025-12-18 13:50:39 +08:00
|
|
|
} catch (error) {
|
2025-12-25 08:40:12 -08:00
|
|
|
appStore.showError('Failed to update password')
|
2025-12-18 13:50:39 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Tips for Using Layouts
|
|
|
|
|
|
|
|
|
|
1. **Page Titles**: Set route meta to automatically display page titles in the header
|
|
|
|
|
2. **Loading States**: Use `appStore.setLoading(true/false)` for global loading indicators
|
|
|
|
|
3. **Toast Notifications**: Use `appStore.showSuccess()`, `appStore.showError()`, etc.
|
|
|
|
|
4. **Authentication**: All authenticated pages should use `AppLayout`
|
|
|
|
|
5. **Auth Pages**: Login and Register pages should use `AuthLayout`
|
|
|
|
|
6. **Sidebar State**: The sidebar state persists across navigation
|
|
|
|
|
7. **Mobile First**: All examples are responsive by default using Tailwind's mobile-first approach
|