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>