第 29 章:综合实战
学习目标
- 用本教程所有技术搭一个后台
- 整合路由、Pinia、组件库、请求
- 实现权限与表单
- 跑通打包部署
一、需求
一个简单的用户管理后台:
- 登录 / 退出
- 用户列表(分页 + 搜索)
- 新增 / 编辑 / 删除 user
- 权限控制
二、目录结构
src/
├─ api/ # axios 封装 + 各模块
├─ assets/
├─ components/ # 通用组件
├─ composables/ # hooks
├─ directives/ # 自定义指令
├─ layouts/ # Layout
├─ router/ # 路由
├─ stores/ # Pinia
├─ styles/ # 全局样式
├─ utils/ # 工具
├─ views/ # 页面
├─ App.vue
└─ main.ts三、main.ts
ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPersist from 'pinia-plugin-persistedstate'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import { router } from './router'
import './styles/index.scss'
import { permission } from './directives/permission'
const app = createApp(App)
const pinia = createPinia()
pinia.use(piniaPersist)
app.use(pinia).use(router).use(ElementPlus)
app.directive('permission', permission)
app.mount('#app')四、Layout
vue
<!-- src/layouts/AdminLayout.vue -->
<script setup lang="ts">
import { useUserStore } from '@/stores/user'
import { useRouter } from 'vue-router'
const user = useUserStore()
const router = useRouter()
const logout = () => {
user.logout()
router.push('/login')
}
</script>
<template>
<el-container>
<el-aside width="220px">
<Sidebar />
</el-aside>
<el-container>
<el-header>
<span>{{ user.name }}</span>
<el-button @click="logout">退出</el-button>
</el-header>
<el-main>
<router-view v-slot="{ Component, route }">
<transition name="fade" mode="out-in">
<component :is="Component" :key="route.fullPath" />
</transition>
</router-view>
</el-main>
</el-container>
</el-container>
</template>五、路由 + 守卫
ts
router.beforeEach((to) => {
const user = useUserStore()
if (to.meta.requiresAuth && !user.token) return '/login'
})六、Login 页
vue
<script setup lang="ts">
import { useUserStore } from '@/stores/user'
import { useRouter } from 'vue-router'
const form = reactive({ username: '', password: '' })
const user = useUserStore()
const router = useRouter()
const submit = async () => {
await user.login(form.username, form.password)
router.push('/users')
}
</script>
<template>
<el-form @submit.prevent="submit">
<el-input v-model="form.username" />
<el-input v-model="form.password" type="password" />
<el-button native-type="submit">登录</el-button>
</el-form>
</template>七、用户列表
vue
<script setup lang="ts">
import { useUserStore } from '@/stores/user'
import { userApi } from '@/api/user'
const store = useUserStore()
const list = ref<User[]>([])
const total = ref(0)
const page = ref(1)
const loading = ref(false)
const load = async () => {
loading.value = true
const res = await userApi.list({ page: page.value })
list.value = res.items
total.value = res.total
loading.value = false
}
onMounted(load)
</script>
<template>
<el-table :data="list" v-loading="loading">
<el-table-column prop="id" label="ID" />
<el-table-column prop="name" label="姓名" />
<el-table-column label="操作">
<el-button v-permission="'user:delete'" type="danger">删除</el-button>
</el-table-column>
</el-table>
<el-pagination v-model:current-page="page" :total="total" @current-change="load" />
</template>八、关键点回顾
| 技术 | 落地 |
|---|---|
<script setup> | 所有组件 |
defineProps <{}> | TS 类型 |
v-model | 表单 |
v-permission | 按钮权限 |
| Pinia + persist | 状态持久化 |
| 路由守卫 | 登录拦截 |
| Element Plus | UI 框架 |
| axios 封装 | 请求统一 |
九、本章小结
| 步骤 | 工具 |
|---|---|
| 初始化 | Vite + Vue 3 |
| 状态 | Pinia |
| 路由 | vue-router |
| UI | Element Plus |
| 权限 | v-permission |
| 部署 | nginx / Vercel |
动手练习
- 跑通登录:admin / 123 登录成功跳用户列表
- 新增用户:dialog + form,提交刷新列表
- 删除:带
v-permission控制,删完刷新
下一章:第 30 章:Vue 生态资源 →