07_前端开发基础_Vue3实战
[TOC]
前言
本章节我们将切换到 前端开发 的视角。
我们将使用 frontend-student 项目作为练习环境,该项目已经搭建好了基础框架(Vue 3 + Vite + Element Plus),但 登录 和 商品管理 模块的代码已被清空。
你的任务是:手动实现这两个模块的前端逻辑,并与后端接口进行对接!
后端接口文档地址:http://localhost:8080/doc.html (请确保后端项目已启动)
一、前端环境准备
1.1 安装 Node.js
前端开发离不开 Node.js 环境。请确保你已安装 Node.js (建议 v16+)。
验证命令:
node -v
npm -v
1.2 启动项目
进入 frontend-student 目录,安装依赖并启动:
# 进入目录
cd frontend-student
# 安装依赖
npm install
# 启动项目
npm run dev
启动成功后,浏览器打开 http://localhost:5173。你会看到一个空白的登录页。
二、实战:登录模块 (Login.vue)
目标:实现用户登录界面,调用后端 /api/login 接口,登录成功后跳转到首页。
文件位置:src/views/Login.vue
2.1 编写 HTML 结构 (Template)
使用 Element Plus 的表单组件。
<template>
<div class="login-container">
<el-card class="login-card">
<template #header>
<h2 style="text-align: center">进销存系统登录</h2>
</template>
<el-form :model="form" ref="formRef" :rules="rules">
<el-form-item prop="username">
<el-input v-model="form.username" placeholder="用户名" prefix-icon="User" />
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="form.password"
placeholder="密码"
prefix-icon="Lock"
type="password"
show-password
@keyup.enter="handleLogin"
/>
</el-form-item>
<el-button type="primary" style="width: 100%" @click="handleLogin" :loading="loading">
立即登录
</el-button>
</el-form>
</el-card>
</div>
</template>
2.2 编写 JS 逻辑 (Script)
使用 Vue 3 的 setup 语法糖和 axios 请求。
<script setup>
import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router'
import request from '@/utils/request' // 已封装好的 axios 实例
import { ElMessage } from 'element-plus'
const router = useRouter()
const loading = ref(false)
const formRef = ref(null)
// 响应式表单数据
const form = reactive({
username: '',
password: ''
})
// 表单校验规则
const rules = {
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
}
const handleLogin = () => {
// 1. 校验表单
formRef.value.validate(async (valid) => {
if (!valid) return
loading.value = true
try {
// 2. 调用后端接口
// 后端接口: POST /api/login, 参数: { username, password }
const res = await request.post('/login', form)
// 3. 处理响应
ElMessage.success('登录成功')
// 登录成功后跳转到首页
router.push('/')
} catch (error) {
// 错误处理已在 request.js 拦截器中统一处理,这里可省略或做额外操作
console.error(error)
} finally {
loading.value = false
}
})
}
</script>
2.3 添加样式 (Style)
<style scoped>
.login-container {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-color: #f0f2f5;
}
.login-card {
width: 400px;
}
</style>
三、实战:商品管理 (Product.vue)
目标:实现商品的增删改查 (CRUD)。为了让大家更好地理解,我们将这个大功能拆解为 5 个小步骤,逐一实现。
文件位置:src/views/Product.vue
3.1 第一步:基础列表展示 (Read)
先实现最简单的功能:从后端获取数据,展示在表格中。不考虑搜索和分页。
3.1.1 编写 HTML (Template)
只保留表格部分,使用 el-table 组件。
<template>
<div class="page-container">
<!-- 表格 -->
<el-table :data="tableData" border stripe v-loading="loading">
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="name" label="商品名称" />
<el-table-column prop="categoryName" label="分类" />
<el-table-column prop="price" label="价格">
<template #default="{ row }">¥{{ row.price }}</template>
</el-table-column>
<el-table-column prop="stock" label="库存" />
</el-table>
</div>
</template>
3.1.2 编写 JS (Script)
定义数据并调用 /api/products 接口。
<script setup>
import { ref, onMounted } from 'vue'
import request from '@/utils/request'
// 响应式数据
const tableData = ref([])
const loading = ref(false)
// 获取商品列表的方法
const fetchData = async () => {
loading.value = true
try {
// 暂时不传分页参数,后端默认会返回第一页
const res = await request.get('/products')
// 注意:后端返回结构是 res.data.data.records (MyBatis-Plus 分页结构)
tableData.value = res.data.data.records
} finally {
loading.value = false
}
}
// 页面加载完成后自动调用
onMounted(() => {
fetchData()
})
</script>
<style scoped>
.page-container {
padding: 20px;
}
</style>
3.2 第二步:添加搜索和分页
数据量大了需要分页,且需要根据名称搜索。
3.2.1 修改 HTML
在表格上方添加 搜索框,在表格下方添加 分页组件。
<template>
<div class="page-container">
<!-- 1. 工具栏 (新增) -->
<div class="toolbar">
<el-input
v-model="searchName"
placeholder="搜索商品名称"
style="width: 200px"
clearable
@clear="fetchData"
@keyup.enter="fetchData"
>
<template #append>
<el-button icon="Search" @click="fetchData" />
</template>
</el-input>
</div>
<!-- 2. 表格 (保持不变) -->
<el-table :data="tableData" border stripe v-loading="loading">
<!-- ...列定义... -->
</el-table>
<!-- 3. 分页 (新增) -->
<div class="pagination">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next"
:total="total"
@size-change="fetchData"
@current-change="fetchData"
/>
</div>
</div>
</template>
3.2.2 修改 JS
添加分页参数和搜索参数,并更新 fetchData 方法。
// ... 之前的 import ...
// 新增响应式数据
const searchName = ref('')
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(0) // 总条数
const fetchData = async () => {
loading.value = true
try {
// 传入参数
const res = await request.get('/products', {
params: {
page: currentPage.value,
size: pageSize.value,
name: searchName.value
}
})
tableData.value = res.data.data.records
total.value = res.data.data.total // 更新总条数
} finally {
loading.value = false
}
}
// ... onMounted ...
记得添加样式:
.toolbar {
margin-bottom: 20px;
display: flex;
}
.pagination {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
3.3 第三步:添加“新建商品”功能 (Create)
通过弹窗 (el-dialog) 来实现商品的添加。
3.3.1 修改 HTML
- 在工具栏添加“新建”按钮。
- 添加弹窗组件。
<!-- 工具栏 -->
<div class="toolbar">
<!-- 新增按钮 -->
<el-button type="primary" icon="Plus" @click="openCreateDialog">新建商品</el-button>
<!-- ...搜索框... -->
</div>
<!-- ...表格... -->
<!-- 弹窗 (新增) -->
<el-dialog v-model="dialogVisible" title="新建商品" width="500px">
<el-form :model="form" label-width="80px">
<el-form-item label="名称">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="分类">
<!-- 下拉框数据来源于 categories -->
<el-select v-model="form.categoryId" placeholder="请选择分类" style="width: 100%">
<el-option v-for="c in categories" :key="c.id" :label="c.name" :value="c.id" />
</el-select>
</el-form-item>
<el-form-item label="价格">
<el-input-number v-model="form.price" :min="0" :precision="2" />
</el-form-item>
<el-form-item label="库存">
<el-input-number v-model="form.stock" :min="0" :precision="0" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitCreateForm">确定</el-button>
</template>
</el-dialog>
3.3.2 修改 JS
添加弹窗控制、表单数据、分类列表获取、提交逻辑。
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
// ... 之前的代码 ...
const dialogVisible = ref(false)
const categories = ref([]) // 分类下拉数据
const form = reactive({
name: '',
categoryId: null,
price: 0,
stock: 0
})
// 获取分类列表 (用于下拉框)
const fetchCategories = async () => {
const res = await request.get('/categories')
categories.value = res.data.data
}
// 打开新建弹窗
const openCreateDialog = () => {
// 重置表单
form.name = ''
form.categoryId = null
form.price = 0
form.stock = 0
dialogVisible.value = true
// 如果分类数据为空,则请求一次
if (categories.value.length === 0) {
fetchCategories()
}
}
// 提交新建表单
const submitCreateForm = async () => {
try {
// 调用后端 POST 接口
await request.post('/products', form)
ElMessage.success('添加成功')
dialogVisible.value = false
fetchData() // 刷新列表
} catch (error) {
console.error(error)
}
}
3.4 第四步:添加“编辑商品”功能 (Update)
复用同一个弹窗,但需要区分是“新建”还是“编辑”。
3.4.1 修改 HTML
- 在表格中添加“操作”列(包含编辑按钮)。
- 修改弹窗标题,动态显示“新建”或“编辑”。
- 注意:我们将之前的“新建商品”按钮的点击事件也修改为
openDialog。
<!-- 工具栏 (修改按钮点击事件) -->
<div class="toolbar">
<el-button type="primary" icon="Plus" @click="openDialog()">新建商品</el-button>
<!-- ...搜索框... -->
</div>
<!-- 表格 (添加操作列) -->
<el-table ...>
<!-- ...其他列... -->
<el-table-column label="操作" width="180">
<template #default="{ row }">
<el-button type="primary" link @click="openDialog(row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<!-- 弹窗 (标题动态化, 按钮事件修改) -->
<el-dialog v-model="dialogVisible" :title="form.id ? '编辑商品' : '新建商品'" width="500px">
<!-- ...表单内容不变... -->
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<!-- 按钮点击事件改为统一的 submitForm -->
<el-button type="primary" @click="submitForm">确定</el-button>
</template>
</el-dialog>
3.4.2 修改 JS
增加 id 字段,并合并新建和编辑的逻辑。我们将之前的 openCreateDialog 和 submitCreateForm 改造为通用的 openDialog 和 submitForm。
// 1. 修改 form 定义,增加 id 字段
const form = reactive({
id: null, // 新增
name: '',
categoryId: null,
price: 0,
stock: 0
})
// 2. 定义通用的打开弹窗方法 (替换原有的 openCreateDialog)
const openDialog = (row = null) => {
if (row) {
// 编辑模式:回显数据
Object.assign(form, row)
} else {
// 新建模式:重置表单
form.id = null
form.name = ''
form.categoryId = null
form.price = 0
form.stock = 0
}
dialogVisible.value = true
if (categories.value.length === 0) {
fetchCategories()
}
}
// 3. 定义通用的提交方法 (替换原有的 submitCreateForm)
const submitForm = async () => {
try {
if (form.id) {
// 有ID,说明是编辑 -> PUT
await request.put('/products', form)
} else {
// 无ID,说明是新建 -> POST
await request.post('/products', form)
}
ElMessage.success('操作成功')
dialogVisible.value = false
fetchData() // 刷新列表
} catch (error) {
console.error(error)
}
}
3.5 第五步:添加“删除”功能 (Delete)
最后完善删除逻辑。
3.5.1 修改 HTML
在操作列添加“删除”按钮,使用 el-popconfirm 二次确认。
<el-table-column label="操作" width="180">
<template #default="{ row }">
<!-- 编辑按钮 -->
<el-button type="primary" link @click="openDialog(row)">编辑</el-button>
<!-- 删除按钮 (新增) -->
<el-popconfirm title="确定删除吗?" @confirm="handleDelete(row.id)">
<template #reference>
<el-button type="danger" link>删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
3.5.2 修改 JS
添加删除方法。
const handleDelete = async (id) => {
try {
await request.delete(`/products/${id}`)
ElMessage.success('删除成功')
fetchData() // 刷新列表
} catch (error) {
console.error(error)
}
}
四、总结
通过本章节的练习,你应该掌握了:
- Vue 3 组合式 API (
ref,reactive,onMounted) 的基本使用。 - Element Plus 常用组件 (
el-table,el-form,el-dialog) 的使用。 - Axios 前后端数据交互(GET, POST, PUT, DELETE)。
前端开发的本质就是:获取数据 -> 渲染页面 -> 响应交互 -> 提交数据。希望你能通过这个案例举一反三!
评论