基础crud项目(前端部分+总结)

发布于:2025-09-05 ⋅ 阅读:(22) ⋅ 点赞:(0)

本人根据自己对前端微不足道的理解和 AI 老师的指导下,艰难地完成了基础crud代码的全栈开发,算是自己的第一个 Java 项目,对此做个简单总结。

后端部分


在前后端分离开发中,前端负责页面交互与数据展示,后端提供接口支持。本文基于 Vue3+Element Plus 构建用户管理前端页面,实现新增、编辑、删除、搜索核心功能,配合后端 RESTful 接口完成联调,并解决开发中常见的报错问题,适合新手快速上手。

技术栈

JavaSE,MySQL,Spring,SpringMVC,MyBatis,SpringBoot,Vue3,Element Plus。

原理

后端原理

依赖注入:通过 @Autowired 实现 Service 与 Dao、Controller 与 Service 层的解耦;
MyBatis 数据映射:开启 map-underscore-to-camel-case 自动转换数据库下划线字段(如 user_id)与 Java 驼峰属性(如 userId),通过注解式 SQL 实现 CRUD;
RESTful 接口规范:用 @GetMapping/{id}/@PostMapping/@PutMapping/{userId}/@DeleteMapping/{id} 对应查、增、改、删操作,路径传主键 + 请求体传数据,确保语义清晰。
整合 Lombok 简化 Pojo 类代码。

前端原理
Vue3 响应式:通过ref/reactive定义响应式数据(如userList/formData),数据变化时自动更新页面(如表格、表单)。
Element Plus 组件化:复用表格(el-table)、表单(el-form)、对话框(el-dialog)等组件,快速搭建交互界面,减少原生 DOM 操作。
Fetch 异步请求:通过原生Fetch API调用后端 RESTful 接口,实现 “数据请求 - 响应 - 页面更新” 闭环,配合async/await简化异步代码。

功能

后端功能

提供用户数据的完整 CRUD 接口:
查:按 ID 查单个用户(/users/{id})、查所有用户(/users);
增:新增用户(/users,请求体传待增字段);
改:按 ID 更新用户信息(/users,请求体传待更字段);
删:按 ID 删除用户(/users/{id})。

前端功能(交互 + 数据展示)
数据展示:用el-table展示用户列表,加载时显示动画,搜索空结果时提示 “未找到用户”。
新增用户:点击 “新增” 打开对话框,表单验证(ID 必为数字、用户名 / 密码必填),提交后刷新列表。
编辑用户:点击 “编辑” 填充当前用户数据,ID 禁用不可改,提交后更新列表。
搜索用户:输入 ID 回车 / 点击搜索,查询单个用户;清空输入框显示所有用户。
删除用户:点击 “删除” 弹出确认框,确认后删除并刷新列表。

前端代码

<template>
    <div class="user-page">
        <h1>用户管理</h1>

        <!-- 操作区 -->
        <div class="operation-bar">
            <el-input v-model="searchId"
                      placeholder="输入ID搜索"
                      style="width: 200px"
                      clearable
                      @keyup.enter="handleSearch" />
            <el-button type="primary" icon="Search" @click="handleSearch">搜索</el-button>
            <el-button type="success" icon="Plus" @click="openAddDialog">新增用户</el-button>
        </div>

        <!-- 用户表格 -->
        <el-table :data="userList"
                  border
                  style="width: 100%"
                  :loading="loading">
            <el-table-column prop="userId" label="ID" width="80" align="center" />
            <el-table-column prop="userName" label="用户名" width="150" align="center" />
            <el-table-column prop="password" label="密码" align="center" />
            <el-table-column label="操作" width="240" align="center">
                <template #default="scope">
                    <el-button type="primary"
                               size="small"
                               icon="Edit"
                               @click="openEditDialog(scope.row)"
                               style="margin-right: 5px">
                        编辑
                    </el-button>
                    <el-button type="danger"
                               size="small"
                               icon="Delete"
                               @click="handleDelete(scope.row.userId)">
                        删除
                    </el-button>
                </template>
            </el-table-column>
        </el-table>

        <!-- 新增用户对话框 -->
        <el-dialog title="新增用户"
                   v-model="addDialogVisible"
                   width="300px">
            <el-form :model="addForm" :rules="addRules" ref="addFormRef" label-width="80px">
                <el-form-item label="用户ID" prop="userId">
                    <el-input v-model.number="addForm.userId" placeholder="请输入ID" />
                </el-form-item>
                <el-form-item label="用户名" prop="userName">
                    <el-input v-model="addForm.userName" />
                </el-form-item>
                <el-form-item label="密码" prop="password">
                    <el-input v-model="addForm.password" type="password" />
                </el-form-item>
            </el-form>
            <template #footer>
                <el-button @click="addDialogVisible = false">取消</el-button>
                <el-button type="primary" @click="handleAddSubmit">确认新增</el-button>
            </template>
        </el-dialog>

        <!-- 编辑用户对话框 -->
        <el-dialog title="编辑用户"
                   v-model="editDialogVisible"
                   width="300px">
            <el-form :model="editForm" :rules="editRules" ref="editFormRef" label-width="80px">
                <!-- 编辑时ID不可修改,仅展示 -->
                <el-form-item label="用户ID">
                    <el-input v-model="editForm.userId" disabled />
                </el-form-item>
                <el-form-item label="用户名" prop="userName">
                    <el-input v-model="editForm.userName" />
                </el-form-item>
                <el-form-item label="密码" prop="password">
                    <el-input v-model="editForm.password" type="password" />
                </el-form-item>
            </el-form>
            <template #footer>
                <el-button @click="editDialogVisible = false">取消</el-button>
                <el-button type="primary" @click="handleEditSubmit">确认编辑</el-button>
            </template>
        </el-dialog>

        <!-- 删除确认对话框 -->
        <el-dialog title="确认删除"
                   v-model="deleteDialogVisible"
                   width="300px">
            <p>确定要删除ID为 {{ deleteId }} 的用户吗?</p>
            <template #footer>
                <el-button @click="deleteDialogVisible = false">取消</el-button>
                <el-button type="danger" @click="confirmDelete">确认删除</el-button>
            </template>
        </el-dialog>
    </div>
</template>

<script setup>
    import { ref, reactive, onMounted } from 'vue'
    import { ElMessage } from 'element-plus'
    import { Search, Plus, Edit, Delete } from '@element-plus/icons-vue'

    // 基础数据
    const userList = ref([])
    const loading = ref(false)
    const searchId = ref('')

    // 新增相关
    const addDialogVisible = ref(false)          // 新增对话框显示状态
    const addFormRef = ref(null)                 // 新增表单引用
    const addForm = reactive({                   // 新增表单数据
        userId: 0,
        userName: '',
        password: ''
    })
    const addRules = reactive({                  // 新增表单验证规则
        userId: [
            { required: true, message: '请输入用户ID', trigger: 'blur' },
            { type: 'number', message: 'ID必须是数字', trigger: 'blur' }
        ],
        userName: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
        password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
    })

    // 编辑相关
    const editDialogVisible = ref(false)         // 编辑对话框显示状态
    const editFormRef = ref(null)                // 编辑表单引用
    const editForm = reactive({                  // 编辑表单数据
        userId: '',
        userName: '',
        password: ''
    })
    const editRules = reactive({                 // 编辑表单验证规则(无需验证ID)
        userName: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
        password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
    })

    // 删除相关
    const deleteDialogVisible = ref(false)
    const deleteId = ref(0)

    // 后端接口地址
    const API_URL = 'http://localhost:8081/users'

    // 1. 获取所有用户
    const getAllUsers = async () => {
        loading.value = true
        try {
            const res = await fetch(API_URL)
            if (!res.ok) throw new Error('获取失败')
            userList.value = await res.json()
        } catch (err) {
            console.error('获取用户失败:', err)
            ElMessage.error('获取用户失败')
        } finally {
            loading.value = false
        }
    }

    // 2. 搜索用户
    const handleSearch = async () => {
        if (!searchId.value) {
            getAllUsers()
            return
        }

        loading.value = true
        try {
            const res = await fetch(`${API_URL}/${searchId.value}`)
            if (res.status === 404) {
                userList.value = []
                ElMessage.warning('用户不存在')
                return
            }
            if (!res.ok) throw new Error('搜索失败')
            userList.value = [await res.json()]
        } catch (err) {
            console.error('搜索失败:', err)
            ElMessage.error('搜索失败')
        } finally {
            loading.value = false
        }
    }

    // 3. 打开新增对话框
    const openAddDialog = () => {
        // 重置新增表单
        addForm.userId = ''
        addForm.userName = ''
        addForm.password = ''
        addDialogVisible.value = true
    }

    // 4. 提交新增表单
    const handleAddSubmit = async () => {
        if (!addFormRef.value) return
        try {
            // 验证新增表单
            await addFormRef.value.validate()

            // 发送新增请求
            const res = await fetch(API_URL, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(addForm)  // 提交新增表单数据
            })

            if (!res.ok) throw new Error('新增失败')
            ElMessage.success('新增成功')
            addDialogVisible.value = false  // 关闭新增对话框
            getAllUsers()                   // 刷新列表
        } catch (err) {
            console.error('新增失败:', err)
            ElMessage.error(err.message || '新增失败')
        }
    }

    // 5. 打开编辑对话框
    const openEditDialog = (user) => {
        // 填充编辑表单
        editForm.userId = user.userId
        editForm.userName = user.userName
        editForm.password = user.password
        editDialogVisible.value = true
    }

    // 6. 提交编辑表单
    const handleEditSubmit = async () => {
        if (!editFormRef.value) return
        try {
            // 验证编辑表单
            await editFormRef.value.validate()

            // 发送编辑请求(URL包含原ID)
            const res = await fetch(API_URL, {
                method: 'PUT',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(editForm)  // 提交编辑表单数据
            })

            if (!res.ok) throw new Error('编辑失败')
            ElMessage.success('编辑成功')
            editDialogVisible.value = false  // 关闭编辑对话框
            getAllUsers()                    // 刷新列表
        } catch (err) {
            console.error('编辑失败:', err)
            ElMessage.error(err.message || '编辑失败')
        }
    }

    // 7. 删除用户
    const handleDelete = (id) => {
        deleteId.value = id
        deleteDialogVisible.value = true
    }

    // 确认删除
    const confirmDelete = async () => {
        try {
            const res = await fetch(`${API_URL}/${deleteId.value}`, {
                method: 'DELETE'
            })
            if (!res.ok) throw new Error('删除失败')

            ElMessage.success('删除成功')
            deleteDialogVisible.value = false
            getAllUsers()
        } catch (err) {
            console.error('删除失败:', err)
            ElMessage.error('删除失败')
        }
    }

    // 初始化
    onMounted(() => {
        getAllUsers()
    })
</script>

<style scoped>
    .user-page {
        padding: 20px;
        max-width: 1000px;
        margin: 0 auto;
    }

    .operation-bar {
        margin-bottom: 15px;
        display: flex;
        gap: 10px;
        align-items: center;
    }

    h1 {
        color: #333;
        font-size: 20px;
        margin-bottom: 20px;
    }
</style>

后端代码已展示在文前博客链接中。

问题和挑战

1.输入数字 ID 仍提示 “ID 必须是数字”
普通 v-model 会将输入值转为字符串(如输入 101,实际是 "101"),而验证规则 type: 'number' 校验的是数据类型,不是格式。
解决方案:用 v-model.number 自动将输入字符串转为数字,如果是字符串则转为NaN,符合要求。

2.编辑功能报 “Failed to fetch”
编辑请求 URL 错误(如 http://localhost:8081/users/undefined),可能是 editForm.userId 未正确赋值;也可能是后端 PUT 接口路径与前端不一致。
解决方案:
打印 URL 确认正确性:在 handleEditSubmit 中添加 console.log;修改前端接口路径。

3.前端页面中文乱码
这个确实花了很长时间,反复部署各种配置,跟着 AI 和各路大佬的博客调了好久,最终发现还是经典的 encoding 问题。
附上博客

4.后端 mybatis-plus 与 springboot 版本冲突
试了好多版本都不能正常运行,最后选择放弃阿里的 mybatis-plus,使用 mybatis。毕竟差距确实不大,进行复杂 Dao 层开发时肯定还是要自己手搓,就当练习基础语法了。

5.Linux相关
因为 MySQL 老师讲企业开发中都是在 Linux 环境下进行部署等操作的,于是跟着 AI 一步一步在 Linux 虚拟机中安装 MySQL 和 Redis(本项目没用到Redis),期间有不少问题是自己根本不熟悉 Linux 造成的,后来特地学习了 Linux 相关知识,计划日后继续学习 JVM。

6.“白学”问题
这一点见仁见智,但是部分课程确实白学。我最开始学2021年黑马的 Javaweb,确实好多东西已经用不上了,连老师也是提前准备或对着 ppt 敲的。不过很多人认为学习了 springboot 就没必要学习 SSM 全家桶,这一点我持否认态度。毕竟我一开始也是这样想的时候,直接去看 springboot 根本看不懂,连URL都不知道是什么。学习确实需要脚踏实地,不能妄想一步登天。

总结

整个暑假都在 Java 课程中度过,期间学习了不少前所未闻的知识。虽然第一次做的 Java 项目还是只有基础的crud,但是“守得云开见月明”,相信在自己的努力下,一切终将美好。


网站公告

今日签到

点亮在社区的每一天
去签到