[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-public-YKEHfsPd":3,"public-project-articles-YKEHfsPd":17},{"id":4,"uuid":5,"project_id":6,"title":7,"content":8,"type":9,"status":10,"public_enabled":10,"views":11,"sort":12,"created_at":13,"updated_at":14,"project_title":15,"project_slug":16},774,"YKEHfsPd",49,"06. Springboot3+vue3实现增删改查、分页查询、批量删除（下）","## 路由警告\n\n![](https:\u002F\u002Fcdn.nlark.com\u002Fyuque\u002F0\u002F2025\u002Fpng\u002F751015\u002F1740227421111-c696e2e0-60df-43cf-9938-dd49066e78cb.png)\n\nroter\u002Findex.js\n\n![](https:\u002F\u002Fcdn.nlark.com\u002Fyuque\u002F0\u002F2025\u002Fpng\u002F751015\u002F1740227464194-6ab46aed-bd6d-49ae-bcc0-3165af6fcee5.png)\n\n## 新增数据\n\n### 流程\n\n1. 在新增的按钮上面加上点击事件\n2. 定义弹窗和表单的页面代码\n3. 点击触发弹窗打开（清空form 对象）\n4. 表单做一下数据绑定以及表单验证\n5. 后台要有对应的新增的接口来接受数据\n6. 新增的接口负责把数据插入到数据库里面\n7. 表单输入数据后点击确认按钮，把表单的数据传给后台接口\n8. 在新增成功之后再次加载表格的数据，关闭弹窗\n\n### Dialog 弹窗\n\n```vue\n\u003Cel-dialog title=\"管理员信息\" v-model=\"data.formVisible\" width=\"500\">\n     \n  \u003Ctemplate #footer>\n    \u003Cdiv class=\"dialog-footer\">\n      \u003Cel-button @click=\"data.formVisible = false\">取 消\u003C\u002Fel-button>\n      \u003Cel-button type=\"primary\" @click=\"add\">保 存\u003C\u002Fel-button>\n    \u003C\u002Fdiv>\n  \u003C\u002Ftemplate>\n\u003C\u002Fel-dialog>\n```\n\n### Form 表单\n\n```vue\n\u003Cel-form :model=\"data.form\" label-width=\"80px\" style=\"padding: 20px 30px 10px 0\">\n  \u003Cel-form-item label=\"账号\">\n    \u003Cel-input v-model=\"data.form.username\" autocomplete=\"off\" \u002F>\n  \u003C\u002Fel-form-item>\n  \u003Cel-form-item label=\"名称\">\n    \u003Cel-input v-model=\"data.form.name\" autocomplete=\"off\" \u002F>\n  \u003C\u002Fel-form-item>\n  \u003Cel-form-item label=\"电话\">\n    \u003Cel-input v-model=\"data.form.phone\" autocomplete=\"off\" \u002F>\n  \u003C\u002Fel-form-item>\n  \u003Cel-form-item label=\"邮箱\">\n    \u003Cel-input v-model=\"data.form.email\" autocomplete=\"off\" \u002F>\n  \u003C\u002Fel-form-item>\n\u003C\u002Fel-form>\n```\n\n### 出现“系统异常”怎么办？\n\n![](https:\u002F\u002Fcdn.nlark.com\u002Fyuque\u002F0\u002F2025\u002Fpng\u002F751015\u002F1740228659770-ed58c26f-ddb2-4a88-b897-8a85f3998ad2.png)\n\n看控制台的错误\n\n![](https:\u002F\u002Fcdn.nlark.com\u002Fyuque\u002F0\u002F2025\u002Fpng\u002F751015\u002F1740228772400-b1020ca3-ff3a-460b-ac21-51a044e0c997.png)\n\n针对这个错误解决问题\n\n### 表单验证\n\n1. 在表单 el-form 上有三个必须的属性\n\n\u003Cfont style=\"color:#bcbec4;background-color:#1e1f22;\">ref=\"formRef\" :model=\"data.form\" :rules=\"data.rules\"\u003C\u002Ffont>\n\n2. 在 el-form-item 上 写上表单项的 prop\n\n\u003Cfont style=\"color:#bcbec4;background-color:#1e1f22;\">el-form-item prop=\"username\"\u003C\u002Ffont>\n\n3. 在 rules 里面定义验证的规则\n4. 定义 formRef 对象，作为表单的引用\n5. 通过 formRef 对象进行表单验证\n\n\n\n```vue\nformRef.value.validate((valid) => {\n    if (valid) {  \n     \u002F\u002F 验证通过的情况下\n    }\n})\n```\n\n\n\n**destroy-on-close 属性可以重置 dialog**\n\n****\n\n### 引入 Java 工具栏  Hutool\n\n```xml\n\u003C!-- Java的工具类 -->\n\u003Cdependency>\n    \u003CgroupId>cn.hutool\u003C\u002FgroupId>\n    \u003CartifactId>hutool-all\u003C\u002FartifactId>\n    \u003Cversion>5.8.25\u003C\u002Fversion>\n\u003C\u002Fdependency>\n```\n\n### admin 的新增流程\n\n```java\npublic void add(Admin admin) {\n    \u002F\u002F 根据新的账号查询数据库  是否存在同样账号的数据\n    Admin dbAdmin = adminMapper.selectByUsername(admin.getUsername());\n    if (dbAdmin != null) {\n        throw new CustomerException(\"账号重复\");\n    }\n    \u002F\u002F 默认密码\n    if (StrUtil.isBlank(admin.getPassword())) {\n        admin.setPassword(\"admin\");\n    }\n    adminMapper.insert(admin);\n}\n```\n\n## 编辑数据\n\n### 流程\n\n1. 在编辑的按钮上面加上点击事件\n2. 定义弹窗和表单的页面代码\n3. 点击触发弹窗打开（将行对象的数据 row 深度拷贝给 form 对象）\n4. 表单做一下数据绑定以及表单验证\n5. 后台要有对应的修改的接口来接受数据\n6. 修改的接口负责把数据更新到数据库里面\n7. 表单输入数据后点击确认按钮，把表单的数据传给后台接口\n8. 在修改成功之后再次加载表格的数据，关闭弹窗\n\n### 报了 405 错误\n\n405 错误表示你请求的方法类型跟后端定义的方法类型不一致，比如前端是 put 请求，后台是 post 接口\n\n![](https:\u002F\u002Fcdn.nlark.com\u002Fyuque\u002F0\u002F2025\u002Fpng\u002F751015\u002F1740230770414-8753a877-2997-45a5-9304-eb18ad776f7f.png)\n\n![](https:\u002F\u002Fcdn.nlark.com\u002Fyuque\u002F0\u002F2025\u002Fpng\u002F751015\u002F1740230779665-d97242f3-c67d-487c-9c5c-73ee1746e3f1.png)\n\n\n\n兼容 新增和修改\n\n```vue\nconst save = () => {\n  data.form.id ? update() : add()\n}\n```\n\n## 删除数据\n\n### 单个删除\n\n```vue\nconst del = (id) => {\n  ElMessageBox.confirm('删除后无法恢复，您确认删除吗？', '删除确认', { type: 'warning' }).then(res => {\n    request.delete('\u002Fadmin\u002Fdelete\u002F' + id).then(res => {\n      if (res.code === '200') {\n        ElMessage.success('删除成功')\n        load()\n      } else {\n        ElMessage.error(res.msg)\n      }\n    })\n  }).catch(err => {})\n}\n```\n\n### 批量删除\n\ntable 的多选事件 \u003Cfont style=\"background-color:rgb(245, 247, 250);\">@selection-change=\"handleSelectionChange\"\u003C\u002Ffont>\n\n\u003Cfont style=\"background-color:rgb(245, 247, 250);\">\u003C\u002Ffont>\n\n```vue\nconst handleSelectionChange = (rows) => {  \u002F\u002F rows 就是实际选择的数组\n  data.rows = rows\n}\n\nconst deleteBatch = () => {\n  if (data.rows.length === 0) {\n    ElMessage.warning('请选择数据')\n    return\n  }\n  ElMessageBox.confirm('删除后无法恢复，您确认删除吗？', '删除确认', { type: 'warning' }).then(res => {\n    request.delete('\u002Fadmin\u002FdeleteBatch', { data: data.rows }).then(res => {\n      if (res.code === '200') {\n        ElMessage.success('批量删除成功')\n        load()\n      } else {\n        ElMessage.error(res.msg)\n      }\n    })\n  }).catch(err => {})\n}\n```\n\n## 本节课的代码案例\n\nAdmin.vue\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cdiv class=\"card\" style=\"margin-bottom: 5px\">\n      \u003Cel-input clearable @clear=\"load\" style=\"width: 260px; margin-right: 5px\" v-model=\"data.username\" placeholder=\"请输入账号查询\" :prefix-icon=\"Search\">\u003C\u002Fel-input>\n      \u003Cel-input clearable @clear=\"load\" style=\"width: 260px; margin-right: 5px\" v-model=\"data.name\" placeholder=\"请输入名称查询\" :prefix-icon=\"Search\">\u003C\u002Fel-input>\n      \u003Cel-button type=\"primary\" @click=\"load\">查 询\u003C\u002Fel-button>\n      \u003Cel-button @click=\"reset\">重 置\u003C\u002Fel-button>\n    \u003C\u002Fdiv>\n    \u003Cdiv class=\"card\" style=\"margin-bottom: 5px\">\n      \u003Cel-button type=\"primary\" @click=\"handleAdd\">新 增\u003C\u002Fel-button>\n      \u003Cel-button type=\"danger\" @click=\"deleteBatch\">批量删除\u003C\u002Fel-button>\n      \u003Cel-button type=\"success\">批量导入\u003C\u002Fel-button>\n      \u003Cel-button type=\"info\">批量导出\u003C\u002Fel-button>\n    \u003C\u002Fdiv>\n\n    \u003Cdiv class=\"card\" style=\"margin-bottom: 5px\">\n      \u003Cel-table :data=\"data.tableData\" style=\"width: 100%\" @selection-change=\"handleSelectionChange\"\n                :header-cell-style=\"{ color: '#333', backgroundColor: '#eaf4ff' }\">\n        \u003Cel-table-column type=\"selection\" width=\"55\" \u002F>\n        \u003Cel-table-column prop=\"username\" label=\"账号\" \u002F>\n        \u003Cel-table-column prop=\"name\" label=\"名称\" \u002F>\n        \u003Cel-table-column prop=\"phone\" label=\"电话\" \u002F>\n        \u003Cel-table-column prop=\"email\" label=\"邮箱\" \u002F>\n        \u003Cel-table-column label=\"操作\" width=\"100\">\n          \u003Ctemplate #default=\"scope\">\n            \u003Cel-button type=\"primary\" icon=\"Edit\" circle @click=\"handleEdit(scope.row)\">\u003C\u002Fel-button>\n            \u003Cel-button type=\"danger\" icon=\"Delete\" circle @click=\"del(scope.row.id)\">\u003C\u002Fel-button>\n          \u003C\u002Ftemplate>\n        \u003C\u002Fel-table-column>\n      \u003C\u002Fel-table>\n    \u003C\u002Fdiv>\n    \u003Cdiv class=\"card\">\n      \u003Cel-pagination\n          v-model:current-page=\"data.pageNum\"\n          v-model:page-size=\"data.pageSize\"\n          layout=\"total, sizes, prev, pager, next, jumper\"\n          :page-sizes=\"[5, 10, 20]\"\n          :total=\"data.total\"\n          @current-change=\"load\"\n          @size-change=\"load\"\n      \u002F>\n    \u003C\u002Fdiv>\n\n    \u003Cel-dialog title=\"管理员信息\" v-model=\"data.formVisible\" width=\"30%\" destroy-on-close>\n      \u003Cel-form ref=\"formRef\" :model=\"data.form\" :rules=\"data.rules\" label-width=\"80px\" style=\"padding: 20px 30px 10px 0\">\n        \u003Cel-form-item prop=\"username\" label=\"账号\">\n          \u003Cel-input v-model=\"data.form.username\" autocomplete=\"off\" \u002F>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item prop=\"name\" label=\"名称\">\n          \u003Cel-input v-model=\"data.form.name\" autocomplete=\"off\" \u002F>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item prop=\"phone\" label=\"电话\">\n          \u003Cel-input v-model=\"data.form.phone\" autocomplete=\"off\" \u002F>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item prop=\"email\" label=\"邮箱\">\n          \u003Cel-input v-model=\"data.form.email\" autocomplete=\"off\" \u002F>\n        \u003C\u002Fel-form-item>\n      \u003C\u002Fel-form>\n      \u003Ctemplate #footer>\n        \u003Cdiv class=\"dialog-footer\">\n          \u003Cel-button @click=\"data.formVisible = false\">取 消\u003C\u002Fel-button>\n          \u003Cel-button type=\"primary\" @click=\"save\">保 存\u003C\u002Fel-button>\n        \u003C\u002Fdiv>\n      \u003C\u002Ftemplate>\n    \u003C\u002Fel-dialog>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { reactive, ref } from \"vue\";\nimport {Search} from \"@element-plus\u002Ficons-vue\";\nimport request from \"@\u002Futils\u002Frequest.js\";\nimport {ElMessage, ElMessageBox} from \"element-plus\";\n\nconst data = reactive({\n  username: null,\n  name: null,\n  pageNum: 1,\n  pageSize: 5,\n  total: 0,\n  tableData: [],\n  formVisible: false,\n  form: {},\n  rules: {\n    username: [\n      { required: true, message: '请填写账号', trigger: 'blur' }\n    ],\n    name: [\n      { required: true, message: '请填写名称', trigger: 'blur' }\n    ],\n    phone: [\n      { required: true, message: '请填写手机', trigger: 'blur' }\n    ],\n    email: [\n      { required: true, message: '请填写邮箱', trigger: 'blur' }\n    ]\n  },\n  rows: []\n})\n\nconst formRef = ref()\n\nconst load = () => {\n  request.get('\u002Fadmin\u002FselectPage', {\n    params: {\n      pageNum: data.pageNum,\n      pageSize: data.pageSize,\n      username: data.username,\n      name: data.name\n    }\n  }).then(res => {\n    if (res.code === '200') {\n      data.tableData = res.data.list\n      data.total = res.data.total\n    } else {\n      ElMessage.error(res.msg)\n    }\n  })\n}\nload()\n\nconst reset = () => {\n  data.username = null\n  data.name = null\n  load()\n}\n\nconst handleAdd = () => {\n  data.formVisible = true\n  data.form = {}\n}\n\nconst add = () => {\n  \u002F\u002F formRef 是表单的引用\n  formRef.value.validate((valid) => {\n    if (valid) {   \u002F\u002F 验证通过的情况下\n      request.post('\u002Fadmin\u002Fadd', data.form).then(res => {\n        if (res.code === '200') {\n          data.formVisible = false\n          ElMessage.success('新增成功')\n          load()\n        } else {\n          ElMessage.error(res.msg)\n        }\n      })\n    }\n  })\n}\n\nconst handleEdit = (row) => {\n  data.form = JSON.parse(JSON.stringify(row))  \u002F\u002F 深度拷贝数据\n  data.formVisible = true\n}\n\nconst update = () => {\n  \u002F\u002F formRef 是表单的引用\n  formRef.value.validate((valid) => {\n    if (valid) {   \u002F\u002F 验证通过的情况下\n      request.put('\u002Fadmin\u002Fupdate', data.form).then(res => {\n        if (res.code === '200') {\n          data.formVisible = false\n          ElMessage.success('修改成功')\n          load()\n        } else {\n          ElMessage.error(res.msg)\n        }\n      })\n    }\n  })\n}\n\nconst save = () => {\n  data.form.id ? update() : add()\n}\n\nconst del = (id) => {\n  ElMessageBox.confirm('删除后无法恢复，您确认删除吗？', '删除确认', { type: 'warning' }).then(res => {\n    request.delete('\u002Fadmin\u002Fdelete\u002F' + id).then(res => {\n      if (res.code === '200') {\n        ElMessage.success('删除成功')\n        load()\n      } else {\n        ElMessage.error(res.msg)\n      }\n    })\n  }).catch(err => {})\n}\n\nconst handleSelectionChange = (rows) => {  \u002F\u002F rows 就是实际选择的数组\n  data.rows = rows\n}\n\nconst deleteBatch = () => {\n  if (data.rows.length === 0) {\n    ElMessage.warning('请选择数据')\n    return\n  }\n  ElMessageBox.confirm('删除后无法恢复，您确认删除吗？', '删除确认', { type: 'warning' }).then(res => {\n    request.delete('\u002Fadmin\u002FdeleteBatch', { data: data.rows }).then(res => {\n      if (res.code === '200') {\n        ElMessage.success('批量删除成功')\n        load()\n      } else {\n        ElMessage.error(res.msg)\n      }\n    })\n  }).catch(err => {})\n}\n\u003C\u002Fscript>\n\n\n```\n\n \n\nAdminController\n\n```java\npackage com.example.controller;\n\nimport com.example.common.Result;\nimport com.example.entity.Admin;\nimport com.example.service.AdminService;\nimport com.github.pagehelper.PageInfo;\nimport jakarta.annotation.Resource;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\n\n@RestController\n@RequestMapping(\"\u002Fadmin\")\npublic class AdminController {\n\n    @Resource\n    AdminService adminService;\n\n    @PostMapping(\"\u002Fadd\")\n    public Result add(@RequestBody Admin admin) {  \u002F\u002F @RequestBody 接收前端传来的 json参数\n        adminService.add(admin);\n        return Result.success();\n    }\n\n    @PutMapping(\"\u002Fupdate\")\n    public Result update(@RequestBody Admin admin) {  \u002F\u002F @RequestBody 接收前端传来的 json参数\n        adminService.update(admin);\n        return Result.success();\n    }\n\n    @DeleteMapping(\"\u002Fdelete\u002F{id}\")\n    public Result delete(@PathVariable Integer id) {  \u002F\u002F @PathVariable 接收前端传来的路径参数\n        adminService.deleteById(id);\n        return Result.success();\n    }\n\n    @DeleteMapping(\"\u002FdeleteBatch\")\n    public Result deleteBatch(@RequestBody List\u003CAdmin> list) {  \u002F\u002F  @RequestBody 接收前端传来的 json数组\n        adminService.deleteBatch(list);\n        return Result.success();\n    }\n\n    @GetMapping(\"\u002FselectAll\")  \u002F\u002F   完整的请求路径：http:\u002F\u002Fip:port\u002Fadmin\u002FselectAll\n    public Result selectAll() {\n        List\u003CAdmin> adminList = adminService.selectAll();\n        return Result.success(adminList);\n    }\n\n    \u002F**\n     * 分页查询\n     * pageNum: 当前的页码\n     * pageSize：每页的个数\n     *\u002F\n    @GetMapping(\"\u002FselectPage\")\n    public Result selectPage(@RequestParam(defaultValue = \"1\") Integer pageNum,\n                             @RequestParam(defaultValue = \"10\") Integer pageSize,\n                             Admin admin) {\n        PageInfo\u003CAdmin> pageInfo = adminService.selectPage(pageNum, pageSize, admin);\n        return Result.success(pageInfo);  \u002F\u002F 返回的是分页的对象\n    }\n\n}\n\n```\n\nAdminService\n\n```java\npackage com.example.service;\n\nimport cn.hutool.core.util.StrUtil;\nimport com.example.entity.Admin;\nimport com.example.exception.CustomerException;\nimport com.example.mapper.AdminMapper;\nimport com.github.pagehelper.PageHelper;\nimport com.github.pagehelper.PageInfo;\nimport jakarta.annotation.Resource;\nimport org.springframework.stereotype.Service;\n\nimport java.util.List;\n\n@Service\npublic class AdminService {\n\n    @Resource\n    AdminMapper adminMapper;\n\n    public void add(Admin admin) {\n        \u002F\u002F 根据新的账号查询数据库  是否存在同样账号的数据\n        Admin dbAdmin = adminMapper.selectByUsername(admin.getUsername());\n        if (dbAdmin != null) {\n            throw new CustomerException(\"账号重复\");\n        }\n        \u002F\u002F 默认密码\n        if (StrUtil.isBlank(admin.getPassword())) {\n            admin.setPassword(\"admin\");\n        }\n        adminMapper.insert(admin);\n    }\n\n    public void update(Admin admin) {\n        adminMapper.updateById(admin);\n    }\n\n    public void deleteById(Integer id) {\n        adminMapper.deleteById(id);\n    }\n\n    public void deleteBatch(List\u003CAdmin> list) {\n        for (Admin admin : list) {\n            this.deleteById(admin.getId());\n        }\n    }\n\n    public List\u003CAdmin> selectAll() {\n        return adminMapper.selectAll(null);\n    }\n\n    public PageInfo\u003CAdmin> selectPage(Integer pageNum, Integer pageSize, Admin admin) {\n        \u002F\u002F 开启分页查询\n        PageHelper.startPage(pageNum, pageSize);\n        List\u003CAdmin> list = adminMapper.selectAll(admin);\n        return PageInfo.of(list);\n    }\n\n}\n\n```\n\nAdminMapper\n\n```java\npackage com.example.mapper;\n\nimport com.example.entity.Admin;\nimport org.apache.ibatis.annotations.Delete;\nimport org.apache.ibatis.annotations.Select;\n\nimport java.util.List;\n\npublic interface AdminMapper {\n\n    List\u003CAdmin> selectAll(Admin admin);\n\n    void insert(Admin admin);\n\n    @Select(\"select * from `admin` where username = #{username}\")\n    Admin selectByUsername(String username);\n\n    void updateById(Admin admin);\n\n    @Delete(\"delete from `admin` where id = #{id}\")\n    void deleteById(Integer id);\n\n}\n\n```\n\nAdminMapper.xml\n\n```xml\n\u003C?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n\u003C!DOCTYPE mapper\n        PUBLIC \"-\u002F\u002Fmybatis.org\u002F\u002FDTD Mapper 3.0\u002F\u002FEN\"\n        \"https:\u002F\u002Fmybatis.org\u002Fdtd\u002Fmybatis-3-mapper.dtd\">\n\u003Cmapper namespace=\"com.example.mapper.AdminMapper\">\n\n    \u003Cselect id=\"selectAll\" resultType=\"com.example.entity.Admin\">\n        select * from `admin`\n        \u003Cwhere>\n            \u003Cif test=\"username != null\">username like concat('%', #{username}, '%')\u003C\u002Fif>\n            \u003Cif test=\"name != null\">and name like concat('%', #{name}, '%')\u003C\u002Fif>  \u003C!-- 相当于 name like '%1%' -->\n        \u003C\u002Fwhere>\n        order by id desc\n    \u003C\u002Fselect>\n\n    \u003Cinsert id=\"insert\">\n        insert into `admin` (username, password, name, phone, email)\n        values(#{username}, #{password}, #{name}, #{phone}, #{email})\n    \u003C\u002Finsert>\n\n    \u003Cupdate id=\"updateById\">\n        update `admin` set username = #{username}, password = #{password}, name = #{name}, phone = #{phone}, email = #{email}\n        where id = #{id}\n    \u003C\u002Fupdate>\n\n\u003C\u002Fmapper>\n```\n\n","coding",1,7760,1520,"2025-02-22 22:00:02","2026-05-03 22:49:02","带小白做毕设2025系列课程","graduation-project-2025",{"project":18,"items":19},{"id":6,"title":15,"slug":16},[20,28,35,42,49,56,63,64,71,78,85,92,99,106,113,120,127,134,141,148,155],{"id":21,"uuid":22,"project_id":6,"title":23,"type":9,"status":10,"public_enabled":10,"views":24,"sort":25,"created_at":26,"updated_at":27,"project_title":15,"project_slug":16},766,"XmlcAcY0","00. 带小白做毕设2025课程介绍",19012,1512,"2025-02-22 15:29:01","2026-05-07 15:33:28.189425+00",{"id":29,"uuid":30,"project_id":6,"title":31,"type":9,"status":10,"public_enabled":10,"views":32,"sort":33,"created_at":34,"updated_at":14,"project_title":15,"project_slug":16},767,"nmjXCdVH","01. 前端Vue3 框架的快速搭建以及项目工程的讲解",15797,1513,"2025-02-13 17:13:40",{"id":36,"uuid":37,"project_id":6,"title":38,"type":9,"status":10,"public_enabled":10,"views":39,"sort":40,"created_at":41,"updated_at":14,"project_title":15,"project_slug":16},768,"pMdPrVeH","02. 使用Vue3集成Element-Plus快速搭建一个管理系统的页面框架",15959,1514,"2025-02-14 11:25:07",{"id":43,"uuid":44,"project_id":6,"title":45,"type":9,"status":10,"public_enabled":10,"views":46,"sort":47,"created_at":48,"updated_at":14,"project_title":15,"project_slug":16},771,"8PikYMQU","03. Springboot3框架的快速搭建以及项目工程的讲解",12768,1517,"2025-02-21 17:21:51",{"id":50,"uuid":51,"project_id":6,"title":52,"type":9,"status":10,"public_enabled":10,"views":53,"sort":54,"created_at":55,"updated_at":14,"project_title":15,"project_slug":16},772,"Q1TCG9Jj","04. Springboot3整合MyBatis实现数据库操作",11144,1518,"2025-03-07 15:50:30",{"id":57,"uuid":58,"project_id":6,"title":59,"type":9,"status":10,"public_enabled":10,"views":60,"sort":61,"created_at":62,"updated_at":14,"project_title":15,"project_slug":16},773,"De7YPnEc","05. Springboot3+vue3实现增删改查、分页查询、批量删除（上）",10827,1519,"2025-02-22 15:09:19",{"id":4,"uuid":5,"project_id":6,"title":7,"type":9,"status":10,"public_enabled":10,"views":11,"sort":12,"created_at":13,"updated_at":14,"project_title":15,"project_slug":16},{"id":65,"uuid":66,"project_id":6,"title":67,"type":9,"status":10,"public_enabled":10,"views":68,"sort":69,"created_at":70,"updated_at":14,"project_title":15,"project_slug":16},775,"sNDKpWVJ","07. Springboot3+Vue3实现excel批量导入导出",6552,1521,"2025-02-23 10:49:24",{"id":72,"uuid":73,"project_id":6,"title":74,"type":9,"status":10,"public_enabled":10,"views":75,"sort":76,"created_at":77,"updated_at":14,"project_title":15,"project_slug":16},776,"1uMP9O6C","08. Springboot3+vue3实现登录注册功能",7964,1522,"2025-02-23 18:14:13",{"id":79,"uuid":80,"project_id":6,"title":81,"type":9,"status":10,"public_enabled":10,"views":82,"sort":83,"created_at":84,"updated_at":14,"project_title":15,"project_slug":16},777,"WahvQp1v","09. Springboot3+vue3实现JWT登录鉴权",7151,1523,"2025-02-23 21:58:00",{"id":86,"uuid":87,"project_id":6,"title":88,"type":9,"status":10,"public_enabled":10,"views":89,"sort":90,"created_at":91,"updated_at":14,"project_title":15,"project_slug":16},778,"QFFAqZh1","10. Springboot3+vue3实现文件上传和下载",6171,1524,"2025-02-24 14:16:27",{"id":93,"uuid":94,"project_id":6,"title":95,"type":9,"status":10,"public_enabled":10,"views":96,"sort":97,"created_at":98,"updated_at":14,"project_title":15,"project_slug":16},1278,"S2eL2g5L","11. Springboot3+vue3实现个人中心、修改密码",5945,1525,"2025-02-24 18:10:59",{"id":100,"uuid":101,"project_id":6,"title":102,"type":9,"status":10,"public_enabled":10,"views":103,"sort":104,"created_at":105,"updated_at":14,"project_title":15,"project_slug":16},1279,"LkN8Mmsn","12. Springboot3+Vue3实现系统公告功能",4967,1526,"2025-02-25 11:50:13",{"id":107,"uuid":108,"project_id":6,"title":109,"type":9,"status":10,"public_enabled":10,"views":110,"sort":111,"created_at":112,"updated_at":14,"project_title":15,"project_slug":16},1280,"i7wziuEN","13. Springboot3+Vue3实现角色权限控制",4446,1527,"2025-02-25 11:51:38",{"id":114,"uuid":115,"project_id":6,"title":116,"type":9,"status":10,"public_enabled":10,"views":117,"sort":118,"created_at":119,"updated_at":14,"project_title":15,"project_slug":16},1281,"pGwiTCRn","14. Springboot3+Vue3实现富文本编辑器功能",4578,1528,"2025-02-26 16:04:58",{"id":121,"uuid":122,"project_id":6,"title":123,"type":9,"status":10,"public_enabled":10,"views":124,"sort":125,"created_at":126,"updated_at":14,"project_title":15,"project_slug":16},1282,"tZ8iDql5","15. Springboot3+Vue3实现模块之间的关联",4454,1529,"2025-02-26 18:28:55",{"id":128,"uuid":129,"project_id":6,"title":130,"type":9,"status":10,"public_enabled":10,"views":131,"sort":132,"created_at":133,"updated_at":14,"project_title":15,"project_slug":16},1283,"gb01JPC2","16. Springboot3+Vue3实现echarts数据统计",4307,1530,"2025-03-03 16:58:21",{"id":135,"uuid":136,"project_id":6,"title":137,"type":9,"status":10,"public_enabled":10,"views":138,"sort":139,"created_at":140,"updated_at":14,"project_title":15,"project_slug":16},1284,"59bDkSFf","17. Springboot3+Vue3实现提交审核业务功能",3793,1531,"2025-03-04 11:58:16",{"id":142,"uuid":143,"project_id":6,"title":144,"type":9,"status":10,"public_enabled":10,"views":145,"sort":146,"created_at":147,"updated_at":14,"project_title":15,"project_slug":16},1285,"gApyb58X","18. Springboot3+Vue3实现预约审核业务功能",3332,1532,"2025-03-05 20:07:24",{"id":149,"uuid":150,"project_id":6,"title":151,"type":9,"status":10,"public_enabled":10,"views":152,"sort":153,"created_at":154,"updated_at":14,"project_title":15,"project_slug":16},1286,"XfpY5re0","19. Springboot3+Vue3实现前台首页的设计",3508,1533,"2025-03-05 20:08:12",{"id":156,"uuid":157,"project_id":6,"title":158,"type":9,"status":10,"public_enabled":10,"views":159,"sort":160,"created_at":161,"updated_at":14,"project_title":15,"project_slug":16},1287,"BnSPRBOc","20. Springboot3+Vue3实现前台轮播图和详情页的设计",4062,1534,"2025-03-17 17:13:36"]