Vue3 搭配 Tailwind CSS 是構建現代后臺管理系統的絕佳組合。Vue3 提供了高效的響應式框架,而 Tailwind CSS 則讓樣式編寫變得快速且靈活。下面我將分步驟教你如何創建一個功能完整的后臺管理系統。
第 1 步:創建項目
首先,我們需要使用 Vite 創建一個 Vue3 項目,并安裝 Tailwind CSS、路由、?Font Awesome:
npm init vite@latest admin-system -- --template vue
cd admin-system
npm install
npm install -D tailwindcss@3.4.1 postcss autoprefixer
npx tailwindcss init -p
npm install vue-router@4
npm install font-awesome
第 2 步:配置 Tailwind CSS
創建?tailwind.config.js
?文件并配置:
/** @type {import('tailwindcss').Config} */
module.exports = {content: ["./index.html","./src/**/*.{vue,js,ts,jsx,tsx}",],theme: {extend: {colors: {primary: '#165DFF',secondary: '#36CFC9',success: '#52C41A',warning: '#FAAD14',danger: '#F5222D',info: '#86909C',light: '#F2F3F5',dark: '#1D2129',},fontFamily: {inter: ['Inter', 'sans-serif'],},},},plugins: [],
}
在?src/index.css
?中引入 Tailwind CSS:
@tailwind base;
@tailwind components;
@tailwind utilities;@layer utilities {.content-auto {content-visibility: auto;}.sidebar-item-active {@apply bg-primary/10 text-primary border-l-4 border-primary;}
}
第 4 步:創建布局組件
<template><div class="min-h-screen flex flex-col bg-gray-50"><!-- 頂部導航欄 --><header class="bg-white shadow-sm"><div class="flex items-center justify-between px-4 py-3"><div class="flex items-center"><button @click="toggleSidebar" class="md:hidden text-gray-500 focus:outline-none"><i class="fa fa-bars text-xl"></i></button><div class="ml-4 text-xl font-bold text-primary">管理系統</div></div><div class="flex items-center"><div class="relative mr-4"><input type="text" placeholder="搜索..." class="pl-8 pr-4 py-2 rounded-lg border border-gray-200 focus:outline-none focus:ring-2 focus:ring-primary/50"><i class="fa fa-search absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i></div><div class="relative ml-4"><button class="relative text-gray-500 focus:outline-none"><i class="fa fa-bell text-xl"></i><span class="absolute top-0 right-0 h-4 w-4 bg-red-500 rounded-full flex items-center justify-center text-white text-xs">3</span></button></div><div class="relative ml-6"><button @click="toggleDropdown" class="flex items-center focus:outline-none"><img src="https://picsum.photos/id/1005/40/40" alt="用戶頭像" class="w-8 h-8 rounded-full object-cover"><span class="ml-2 text-sm font-medium">管理員</span><i class="fa fa-angle-down ml-1 text-gray-500"></i></button><div v-show="dropdownVisible" class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-10"><a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"><i class="fa fa-user mr-2"></i>個人信息</a><a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"><i class="fa fa-cog mr-2"></i>設置</a><div class="border-t border-gray-100 my-1"></div><a href="#" @click="logout" class="block px-4 py-2 text-sm text-red-600 hover:bg-gray-100"><i class="fa fa-sign-out mr-2"></i>退出登錄</a></div></div></div></div></header><div class="flex flex-1 overflow-hidden"><!-- 側邊欄導航 --><aside :class="sidebarVisible ? 'translate-x-0' : '-translate-x-full'" class="fixed md:relative z-20 w-64 bg-white shadow-lg h-full transition-transform duration-300 ease-in-out"><nav class="py-4"><div class="px-4 mb-6"><div class="flex items-center"><img src="https://picsum.photos/id/1005/40/40" alt="用戶頭像" class="w-10 h-10 rounded-full object-cover"><div class="ml-3"><div class="text-sm font-medium text-gray-900">管理員</div><div class="text-xs text-gray-500">系統管理員</div></div></div></div><div class="px-2 space-y-1"><a href="/dashboard" class="flex items-center px-4 py-3 text-gray-600 rounded-lg sidebar-item-active"><i class="fa fa-tachometer mr-3"></i><span>儀表盤</span></a><a href="/users" class="flex items-center px-4 py-3 text-gray-600 rounded-lg hover:bg-gray-100 transition-colors duration-200"><i class="fa fa-users mr-3"></i><span>用戶管理</span></a><a href="/products" class="flex items-center px-4 py-3 text-gray-600 rounded-lg hover:bg-gray-100 transition-colors duration-200"><i class="fa fa-shopping-bag mr-3"></i><span>產品管理</span></a><a href="#" class="flex items-center px-4 py-3 text-gray-600 rounded-lg hover:bg-gray-100 transition-colors duration-200"><i class="fa fa-bar-chart mr-3"></i><span>數據分析</span></a><a href="#" class="flex items-center px-4 py-3 text-gray-600 rounded-lg hover:bg-gray-100 transition-colors duration-200"><i class="fa fa-cog mr-3"></i><span>系統設置</span></a></div><div class="px-4 py-4 mt-6 border-t border-gray-100"><div class="text-xs font-medium text-gray-500 uppercase tracking-wider">幫助</div><a href="#" class="flex items-center px-4 py-2 mt-2 text-gray-600 rounded-lg hover:bg-gray-100 transition-colors duration-200"><i class="fa fa-question-circle mr-3"></i><span>幫助中心</span></a><a href="#" class="flex items-center px-4 py-2 text-gray-600 rounded-lg hover:bg-gray-100 transition-colors duration-200"><i class="fa fa-life-ring mr-3"></i><span>聯系支持</span></a></div></nav></aside><!-- 主內容區 --><main class="flex-1 overflow-y-auto p-6 bg-gray-50"><div class="mb-6"><h1 class="text-[clamp(1.5rem,3vw,2.5rem)] font-bold text-gray-900">儀表盤</h1><p class="mt-1 text-gray-500">歡迎使用管理系統</p></div><div class="mb-6 flex flex-col md:flex-row md:space-x-4 space-y-4 md:space-y-0"><div class="bg-white rounded-xl shadow-sm p-6 flex-1"><div class="flex items-center justify-between"><div><p class="text-sm font-medium text-gray-500">用戶總數</p><h3 class="text-3xl font-bold text-gray-900 mt-1">1,284</h3><p class="text-xs text-green-500 mt-2"><i class="fa fa-arrow-up"></i> 12% 較上月</p></div><div class="w-12 h-12 rounded-full bg-blue-100 flex items-center justify-center"><i class="fa fa-users text-primary text-xl"></i></div></div></div><div class="bg-white rounded-xl shadow-sm p-6 flex-1"><div class="flex items-center justify-between"><div><p class="text-sm font-medium text-gray-500">產品總數</p><h3 class="text-3xl font-bold text-gray-900 mt-1">528</h3><p class="text-xs text-green-500 mt-2"><i class="fa fa-arrow-up"></i> 8% 較上月</p></div><div class="w-12 h-12 rounded-full bg-green-100 flex items-center justify-center"><i class="fa fa-shopping-bag text-green-600 text-xl"></i></div></div></div><div class="bg-white rounded-xl shadow-sm p-6 flex-1"><div class="flex items-center justify-between"><div><p class="text-sm font-medium text-gray-500">訂單總數</p><h3 class="text-3xl font-bold text-gray-900 mt-1">2,451</h3><p class="text-xs text-green-500 mt-2"><i class="fa fa-arrow-up"></i> 16% 較上月</p></div><div class="w-12 h-12 rounded-full bg-purple-100 flex items-center justify-center"><i class="fa fa-file-text text-purple-600 text-xl"></i></div></div></div><div class="bg-white rounded-xl shadow-sm p-6 flex-1"><div class="flex items-center justify-between"><div><p class="text-sm font-medium text-gray-500">銷售額</p><h3 class="text-3xl font-bold text-gray-900 mt-1">¥156,284</h3><p class="text-xs text-green-500 mt-2"><i class="fa fa-arrow-up"></i> 23% 較上月</p></div><div class="w-12 h-12 rounded-full bg-yellow-100 flex items-center justify-center"><i class="fa fa-line-chart text-yellow-600 text-xl"></i></div></div></div></div><div class="grid grid-cols-1 lg:grid-cols-3 gap-6"><div class="lg:col-span-2 bg-white rounded-xl shadow-sm p-6"><div class="flex items-center justify-between mb-6"><h2 class="text-lg font-semibold text-gray-900">銷售趨勢</h2><div class="flex space-x-2"><button class="px-3 py-1 text-xs rounded-full bg-gray-100 text-gray-600">日</button><button class="px-3 py-1 text-xs rounded-full bg-primary text-white">周</button><button class="px-3 py-1 text-xs rounded-full bg-gray-100 text-gray-600">月</button></div></div><div class="h-80"><!-- 這里可以放置圖表組件 --><div class="w-full h-full flex items-center justify-center"><p class="text-gray-400">銷售趨勢圖表將顯示在這里</p></div></div></div><div class="bg-white rounded-xl shadow-sm p-6"><div class="flex items-center justify-between mb-6"><h2 class="text-lg font-semibold text-gray-900">銷售分布</h2><button class="text-primary text-sm">查看全部</button></div><div class="h-80"><!-- 這里可以放置圖表組件 --><div class="w-full h-full flex items-center justify-center"><p class="text-gray-400">銷售分布圖表將顯示在這里</p></div></div></div></div><div class="mt-6 bg-white rounded-xl shadow-sm p-6"><div class="flex items-center justify-between mb-6"><h2 class="text-lg font-semibold text-gray-900">最近訂單</h2><button class="text-primary text-sm">查看全部</button></div><div class="overflow-x-auto"><table class="min-w-full divide-y divide-gray-200"><thead><tr><th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">訂單ID</th><th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">客戶</th><th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">產品</th><th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">金額</th><th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">狀態</th><th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">操作</th></tr></thead><tbody class="bg-white divide-y divide-gray-200"><tr><td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">#ORD-12345</td><td class="px-6 py-4 whitespace-nowrap"><div class="flex items-center"><div class="flex-shrink-0 h-10 w-10"><img class="h-10 w-10 rounded-full" src="https://picsum.photos/id/1001/40/40" alt="用戶頭像"></div><div class="ml-4"><div class="text-sm font-medium text-gray-900">張三</div><div class="text-sm text-gray-500">zhangsan@example.com</div></div></div></td><td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">電子產品</td><td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">¥1,299.00</td><td class="px-6 py-4 whitespace-nowrap"><span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">已完成</span></td><td class="px-6 py-4 whitespace-nowrap text-sm text-primary"><button>查看詳情</button></td></tr><tr><td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">#ORD-12346</td><td class="px-6 py-4 whitespace-nowrap"><div class="flex items-center"><div class="flex-shrink-0 h-10 w-10"><img class="h-10 w-10 rounded-full" src="https://picsum.photos/id/1002/40/40" alt="用戶頭像"></div><div class="ml-4"><div class="text-sm font-medium text-gray-900">李四</div><div class="text-sm text-gray-500">lisi@example.com</div></div></div></td><td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">家居用品</td><td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">¥499.00</td><td class="px-6 py-4 whitespace-nowrap"><span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800">處理中</span></td><td class="px-6 py-4 whitespace-nowrap text-sm text-primary"><button>查看詳情</button></td></tr><tr><td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">#ORD-12347</td><td class="px-6 py-4 whitespace-nowrap"><div class="flex items-center"><div class="flex-shrink-0 h-10 w-10"><img class="h-10 w-10 rounded-full" src="https://picsum.photos/id/1003/40/40" alt="用戶頭像"></div><div class="ml-4"><div class="text-sm font-medium text-gray-900">王五</div><div class="text-sm text-gray-500">wangwu@example.com</div></div></div></td><td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">圖書音像</td><td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">¥299.00</td><td class="px-6 py-4 whitespace-nowrap"><span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-100 text-blue-800">已發貨</span></td><td class="px-6 py-4 whitespace-nowrap text-sm text-primary"><button>查看詳情</button></td></tr></tbody></table></div></div></main></div></div>
</template><script setup>
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'const router = useRouter()const sidebarVisible = ref(true)
const dropdownVisible = ref(false)const toggleSidebar = () => {sidebarVisible.value = !sidebarVisible.value
}const toggleDropdown = () => {dropdownVisible.value = !dropdownVisible.value
}const logout = () => {localStorage.removeItem('token')router.push('/login')
}onMounted(() => {// 檢查登錄狀態if (!localStorage.getItem('token')) {router.push('/login')}
})
</script><style scoped>
/* 移動端適配 */
@media (max-width: 768px) {.sidebar {position: fixed;z-index: 100;transform: translateX(-100%);transition: transform 0.3s ease-in-out;}.sidebar-visible {transform: translateX(0);}.overlay {position: fixed;top: 0;left: 0;right: 0;bottom: 0;background-color: rgba(0, 0, 0, 0.5);z-index: 99;display: none;}.sidebar-visible + .overlay {display: block;}
}
</style>
第 5 步:創建登錄頁面
創建登錄頁面?src/views/Login.vue
:
<template><div class="min-h-screen bg-gray-50 flex items-center justify-center p-4"><div class="max-w-md w-full bg-white rounded-xl shadow-lg overflow-hidden"><div class="p-6 sm:p-8"><div class="text-center mb-8"><h1 class="text-2xl font-bold text-gray-900">管理系統登錄</h1><p class="mt-1 text-sm text-gray-500">請輸入賬號密碼登錄</p></div><form @submit.prevent="handleLogin"><div class="space-y-4"><div><label for="email" class="block text-sm font-medium text-gray-700">郵箱</label><div class="mt-1 relative"><div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"><i class="fa fa-envelope text-gray-400"></i></div><input type="email" id="email" v-model="form.email" class="pl-10 block w-full rounded-md border-gray-300 shadow-sm focus:ring-primary focus:border-primary" placeholder="your@email.com"></div></div><div><label for="password" class="block text-sm font-medium text-gray-700">密碼</label><div class="mt-1 relative"><div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"><i class="fa fa-lock text-gray-400"></i></div><input type="password" id="password" v-model="form.password" class="pl-10 block w-full rounded-md border-gray-300 shadow-sm focus:ring-primary focus:border-primary" placeholder="????????"></div></div><div class="flex items-center justify-between"><div class="flex items-center"><input id="remember-me" name="remember-me" type="checkbox" class="h-4 w-4 text-primary focus:ring-primary border-gray-300 rounded"><label for="remember-me" class="ml-2 block text-sm text-gray-900">記住我</label></div><div class="text-sm"><a href="#" class="font-medium text-primary hover:text-primary/80">忘記密碼?</a></div></div><div><button type="submit" class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-primary hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary">登錄</button></div></div></form><div class="mt-6 relative"><div class="absolute inset-0 flex items-center"><div class="w-full border-t border-gray-300"></div></div><div class="relative flex justify-center text-sm"><span class="px-2 bg-white text-gray-500">其他登錄方式</span></div></div><div class="mt-6 grid grid-cols-3 gap-3"><button type="button" class="inline-flex justify-center py-2 px-4 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"><i class="fa fa-weixin text-green-600"></i></button><button type="button" class="inline-flex justify-center py-2 px-4 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"><i class="fa fa-qq text-blue-500"></i></button><button type="button" class="inline-flex justify-center py-2 px-4 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"><i class="fa fa-github text-gray-800"></i></button></div></div><div class="bg-gray-50 px-6 py-4 text-center"><p class="text-sm text-gray-600">還沒有賬號? <a href="#" class="font-medium text-primary hover:text-primary/80">注冊</a></p></div></div></div>
</template><script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'const router = useRouter()const form = ref({email: '',password: ''
})const handleLogin = () => {// 模擬登錄驗證if (form.value.email && form.value.password) {localStorage.setItem('token', 'fake_token')router.push('/dashboard')} else {alert('請輸入郵箱和密碼')}
}
</script>
第 6 步:創建用戶管理頁面
創建用戶管理頁面?src/views/Users.vue
:
<template><div class="bg-white rounded-xl shadow-sm p-6"><div class="flex flex-col md:flex-row md:items-center md:justify-between mb-6"><div><h2 class="text-xl font-bold text-gray-900">用戶管理</h2><p class="mt-1 text-sm text-gray-500">管理系統用戶信息</p></div><div class="mt-4 md:mt-0 flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-3"><div class="relative"><input type="text" v-model="searchQuery" placeholder="搜索用戶..." class="pl-10 pr-4 py-2 rounded-lg border border-gray-200 focus:outline-none focus:ring-2 focus:ring-primary/50 w-full sm:w-64"><i class="fa fa-search absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i></div><button @click="handleCreateUser" class="inline-flex items-center px-4 py-2 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white bg-primary hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary"><i class="fa fa-plus mr-2"></i>創建用戶</button></div></div><div class="overflow-x-auto"><table class="min-w-full divide-y divide-gray-200"><thead class="bg-gray-50"><tr><th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th><th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">用戶信息</th><th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">角色</th><th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">狀態</th><th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">創建時間</th><th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">操作</th></tr></thead><tbody class="bg-white divide-y divide-gray-200"><tr v-for="user in users" :key="user.id"><td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{{ user.id }}</td><td class="px-6 py-4 whitespace-nowrap"><div class="flex items-center"><div class="flex-shrink-0 h-10 w-10"><img class="h-10 w-10 rounded-full" :src="user.avatar" alt="用戶頭像"></div><div class="ml-4"><div class="text-sm font-medium text-gray-900">{{ user.name }}</div><div class="text-sm text-gray-500">{{ user.email }}</div></div></div></td><td class="px-6 py-4 whitespace-nowrap"><span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-100 text-blue-800">{{ user.role }}</span></td><td class="px-6 py-4 whitespace-nowrap"><span :class="user.status === 'active' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'" class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full">{{ user.status === 'active' ? '活躍' : '禁用' }}</span></td><td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ user.createdAt }}</td><td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"><button class="text-indigo-600 hover:text-indigo-900 mr-3" @click="handleEditUser(user)">編輯</button><button class="text-red-600 hover:text-red-900" @click="handleDeleteUser(user)">刪除</button></td></tr></tbody></table></div><div class="mt-6 flex items-center justify-between"><div class="flex-1 flex justify-between sm:hidden"><button class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">上一頁</button><button class="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">下一頁</button></div><div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between"><div><p class="text-sm text-gray-700">顯示第 <span class="font-medium">{{ (currentPage - 1) * pageSize + 1 }}</span> 至 <span class="font-medium">{{ Math.min(currentPage * pageSize, totalUsers) }}</span> 條,共 <span class="font-medium">{{ totalUsers }}</span> 條記錄</p></div><div><nav class="relative z-0 inline-flex rounded-md shadow-sm -space-x-px" aria-label="Pagination"><button :disabled="currentPage === 1" @click="currentPage--" class="relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"><span class="sr-only">上一頁</span><i class="fa fa-chevron-left h-5 w-5"></i></button><button v-for="page in totalPages" :key="page" :class="page === currentPage ? 'z-10 bg-primary text-white' : 'bg-white text-gray-700'" :aria-current="page === currentPage ? 'page' : undefined" @click="currentPage = page" class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium">{{ page }}</button><button :disabled="currentPage === totalPages" @click="currentPage++" class="relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"><span class="sr-only">下一頁</span><i class="fa fa-chevron-right h-5 w-5"></i></button></nav></div></div></div></div>
</template><script setup>
import { ref, computed, onMounted } from 'vue'const searchQuery = ref('')
const currentPage = ref(1)
const pageSize = ref(10)
const totalUsers = ref(124)const totalPages = computed(() => {return Math.ceil(totalUsers.value / pageSize.value)
})const users = ref([{id: 1,name: '張三',email: 'zhangsan@example.com',role: '管理員',status: 'active',avatar: 'https://picsum.photos/id/1001/40/40',createdAt: '2023-01-15'},{id: 2,name: '李四',email: 'lisi@example.com',role: '編輯',status: 'active',avatar: 'https://picsum.photos/id/1002/40/40',createdAt: '2023-02-20'},{id: 3,name: '王五',email: 'wangwu@example.com',role: '普通用戶',status: 'disabled',avatar: 'https://picsum.photos/id/1003/40/40',createdAt: '2023-03-10'},{id: 4,name: '趙六',email: 'zhaoliu@example.com',role: '普通用戶',status: 'active',avatar: 'https://picsum.photos/id/1004/40/40',createdAt: '2023-04-05'},{id: 5,name: '錢七',email: 'qianqi@example.com',role: '編輯',status: 'active',avatar: 'https://picsum.photos/id/1005/40/40',createdAt: '2023-05-12'}
])const handleCreateUser = () => {console.log('創建用戶')// 打開創建用戶模態框
}const handleEditUser = (user) => {console.log('編輯用戶', user)// 打開編輯用戶模態框
}const handleDeleteUser = (user) => {if (confirm(`確定要刪除用戶 ${user.name} 嗎?`)) {console.log('刪除用戶', user)// 調用API刪除用戶}
}onMounted(() => {// 加載用戶數據
})
</script>
第 7 步:創建產品管理頁面
創建產品管理頁面?src/views/Products.vue
:
<template><div class="bg-white rounded-xl shadow-sm p-6"><div class="flex flex-col md:flex-row md:items-center md:justify-between mb-6"><div><h2 class="text-xl font-bold text-gray-900">產品管理</h2><p class="mt-1 text-sm text-gray-500">管理系統產品信息</p></div><div class="mt-4 md:mt-0 flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-3"><div class="relative"><input type="text" v-model="searchQuery" placeholder="搜索產品..." class="pl-10 pr-4 py-2 rounded-lg border border-gray-200 focus:outline-none focus:ring-2 focus:ring-primary/50 w-full sm:w-64"><i class="fa fa-search absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i></div><div class="relative"><select v-model="categoryFilter" class="pl-4 pr-10 py-2 rounded-lg border border-gray-200 focus:outline-none focus:ring-2 focus:ring-primary/50 appearance-none bg-white"><option value="">全部分類</option><option value="electronics">電子產品</option><option value="clothing">服裝</option><option value="home">家居用品</option><option value="books">圖書音像</option></select><i class="fa fa-chevron-down absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 pointer-events-none"></i></div><button @click="handleCreateProduct" class="inline-flex items-center px-4 py-2 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white bg-primary hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary"><i class="fa fa-plus mr-2"></i>創建產品</button></div></div><div class="overflow-x-auto"><table class="min-w-full divide-y divide-gray-200"><thead class="bg-gray-50"><tr><th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th><th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">產品信息</th><th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">分類</th><th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">價格</th><th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">庫存</th><th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">狀態</th><th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">操作</th></tr></thead><tbody class="bg-white divide-y divide-gray-200"><tr v-for="product in products" :key="product.id"><td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{{ product.id }}</td><td class="px-6 py-4 whitespace-nowrap"><div class="flex items-center"><div class="flex-shrink-0 h-12 w-12"><img class="h-12 w-12 rounded-md object-cover" :src="product.image" alt="產品圖片"></div><div class="ml-4"><div class="text-sm font-medium text-gray-900">{{ product.name }}</div><div class="text-sm text-gray-500">{{ product.description }}</div></div></div></td><td class="px-6 py-4 whitespace-nowrap"><span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-purple-100 text-purple-800">{{ product.category }}</span></td><td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">¥{{ product.price.toFixed(2) }}</td><td class="px-6 py-4 whitespace-nowrap"><span :class="product.stock < 10 ? 'text-red-600' : 'text-gray-900'" class="text-sm font-medium">{{ product.stock }}</span></td><td class="px-6 py-4 whitespace-nowrap"><span :class="product.status === 'active' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'" class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full">{{ product.status === 'active' ? '上架' : '下架' }}</span></td><td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"><button class="text-indigo-600 hover:text-indigo-900 mr-3" @click="handleEditProduct(product)">編輯</button><button class="text-red-600 hover:text-red-900" @click="handleDeleteProduct(product)">刪除</button></td></tr></tbody></table></div><div class="mt-6 flex items-center justify-between"><div class="flex-1 flex justify-between sm:hidden"><button class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">上一頁</button><button class="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">下一頁</button></div><div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between"><div><p class="text-sm text-gray-700">顯示第 <span class="font-medium">{{ (currentPage - 1) * pageSize + 1 }}</span> 至 <span class="font-medium">{{ Math.min(currentPage * pageSize, totalProducts) }}</span> 條,共 <span class="font-medium">{{ totalProducts }}</span> 條記錄</p></div><div><nav class="relative z-0 inline-flex rounded-md shadow-sm -space-x-px" aria-label="Pagination"><button :disabled="currentPage === 1" @click="currentPage--" class="relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"><span class="sr-only">上一頁</span><i class="fa fa-chevron-left h-5 w-5"></i></button><button v-for="page in totalPages" :key="page" :class="page === currentPage ? 'z-10 bg-primary text-white' : 'bg-white text-gray-700'" :aria-current="page === currentPage ? 'page' : undefined" @click="currentPage = page" class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium">{{ page }}</button><button :disabled="currentPage === totalPages" @click="currentPage++" class="relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50"><span class="sr-only">下一頁</span><i class="fa fa-chevron-right h-5 w-5"></i></button></nav></div></div></div></div>
</template><script setup>
import { ref, computed, onMounted } from 'vue'const searchQuery = ref('')
const categoryFilter = ref('')
const currentPage = ref(1)
const pageSize = ref(10)
const totalProducts = ref(86)const totalPages = computed(() => {return Math.ceil(totalProducts.value / pageSize.value)
})const products = ref([{id: 1,name: '智能手表',description: '多功能智能手表,支持心率監測、睡眠分析等功能',category: '電子產品',price: 1299.99,stock: 56,status: 'active',image: 'https://picsum.photos/id/1/80/80'},{id: 2,name: '無線耳機',description: '主動降噪無線耳機,提供沉浸式音樂體驗',category: '電子產品',price: 899.99,stock: 32,status: 'active',image: 'https://picsum.photos/id/2/80/80'},{id: 3,name: '純棉T恤',description: '舒適純棉T恤,多種顏色可選',category: '服裝',price: 99.99,stock: 87,status: 'active',image: 'https://picsum.photos/id/3/80/80'},{id: 4,name: '家用咖啡機',description: '全自動家用咖啡機,一鍵制作美味咖啡',category: '家居用品',price: 1999.99,stock: 12,status: 'active',image: 'https://picsum.photos/id/4/80/80'},{id: 5,name: '前端開發實戰',description: '全面講解前端開發技術,從入門到精通',category: '圖書音像',price: 89.99,stock: 5,status: 'active',image: 'https://picsum.photos/id/5/80/80'}
])const handleCreateProduct = () => {console.log('創建產品')// 打開創建產品模態框
}const handleEditProduct = (product) => {console.log('編輯產品', product)// 打開編輯產品模態框
}const handleDeleteProduct = (product) => {if (confirm(`確定要刪除產品 ${product.name} 嗎?`)) {console.log('刪除產品', product)// 調用API刪除產品}
}onMounted(() => {// 加載產品數據
})
</script>
第 8 步:配置主應用
修改?src/main.js
?來配置主應用:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import './index.css'
import 'font-awesome/css/font-awesome.min.css'const app = createApp(App)app.use(router)app.mount('#app')
第 9 步:創建主應用組件
修改?src/App.vue
:
<template><router-view />
</template><script setup>
// 主應用邏輯
</script><style>
/* 全局樣式 */
body {margin: 0;padding: 0;font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
</style>