[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-public-3XNG04wA":3,"public-project-articles-3XNG04wA":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},1011,"3XNG04wA",52,"04. 带你开发用户登录、注册、个人信息、修改密码功能","## 用户登录后端接口实现\n\n```java\n\u002F**\n * 登录\n *\u002F\n@PostMapping(\"\u002Flogin\")\npublic Result login(@RequestBody Account account) {\n    Account ac = null;\n    if (\"管理员\".equals(account.getRole())) {\n        ac = adminService.login(account);\n    }\n    if (\"普通用户\".equals(account.getRole())) {\n        ac = userService.login(account);\n    }\n    if (ac == null) {\n        return Result.error(\"登录失败，用户不存在\");\n    }\n    return Result.success(ac);\n}\n```\n\n在 service 里面做一些查询处理\n\n```java\npublic Account login(Account account) {\n    User dbUser = userMapper.selectByUsername(account.getUsername());\n    if (ObjectUtil.isNull(dbUser)) {\n        throw new CustomException(\"用户不存在\");\n    }\n    if (!account.getPassword().equals(dbUser.getPassword())) {\n        throw new CustomException(\"账号或密码错误\");\n    }\n    return dbUser;\n}\n```\n\n## 登录前台实现\n\n```vue\n\u003Cel-form-item prop=\"role\">\n  \u003Cel-select size=\"large\" style=\"width: 100%\" v-model=\"data.form.role\">\n    \u003Cel-option value=\"普通用户\" label=\"普通用户\">\u003C\u002Fel-option>\n    \u003Cel-option value=\"管理员\" label=\"管理员\">\u003C\u002Fel-option>\n  \u003C\u002Fel-select>\n\u003C\u002Fel-form-item>\n```\n\n```vue\n\u002F\u002F 点击登录按钮的时候会触发这个方法\n  const login = () => {\n    formRef.value.validate((valid => {\n      if (valid) {\n        \u002F\u002F 调用后台的接口\n        request.post('\u002Flogin', data.form).then(res => {\n          if (res.code === '200') {\n            ElMessage.success(\"登录成功\")\n            localStorage.setItem('system-user', JSON.stringify(res.data))\n            if (res.data.role === '管理员') {\n              router.push('\u002Fmanager\u002Fhome')\n            } else {\n              router.push('\u002Ffront\u002Fhome')\n            }\n          } else {\n            ElMessage.error(res.msg)\n          }\n        })\n      }\n    })).catch(error => {\n      console.error(error)\n    })\n  }\n```\n\n前端路由\n\n```vue\n{\n  path: '\u002Ffront',\n  component: () => import('@\u002Fviews\u002FFront.vue'),\n  redirect: '\u002Ffront\u002Fhome',\n  children: [\n    { path: 'home', component: () => import('@\u002Fviews\u002Ffront\u002FHome.vue')},\n  ]\n},\n```\n\n## 注册后端接口\n\n```java\n\u002F**\n * 注册\n *\u002F\n@PostMapping(\"\u002Fregister\")\npublic Result register(@RequestBody User user) {\n    if (!user.getPassword().equals(user.getNewPassword())) {\n        return Result.error(\"两次输入密码不一致\");\n    }\n    userService.add(user);\n    return Result.success();\n}\n```\n\n## 注册页面\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv class=\"login-container\">\n    \u003Cdiv class=\"login-box\">\n      \u003Cdiv style=\"font-weight: bold; font-size: 30px; text-align: center; margin-bottom: 30px; color: #0c9c7a\">欢 迎 注 册\u003C\u002Fdiv>\n      \u003Cel-form :model=\"data.form\"  ref=\"formRef\" :rules=\"data.rules\">\n        \u003Cel-form-item prop=\"username\">\n          \u003Cel-input :prefix-icon=\"User\" size=\"large\" v-model=\"data.form.username\" placeholder=\"请输入账号\" \u002F>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item prop=\"password\">\n          \u003Cel-input :prefix-icon=\"Lock\" size=\"large\" v-model=\"data.form.password\" placeholder=\"请输入密码\" show-password \u002F>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item prop=\"newPassword\">\n          \u003Cel-input :prefix-icon=\"Lock\" size=\"large\" v-model=\"data.form.newPassword\" placeholder=\"请确认密码\" show-password \u002F>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item>\n          \u003Cel-button size=\"large\" style=\"width: 100%; background-color: #0c9c7a; border-color: #0c9c7a; color: white\" @click=\"register\">注 册\u003C\u002Fel-button>\n        \u003C\u002Fel-form-item>\n      \u003C\u002Fel-form>\n      \u003Cdiv style=\"text-align: right;\">\n        已有账号？请 \u003Ca href=\"\u002Flogin\">登录\u003C\u002Fa>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\n  import { reactive, ref } from \"vue\";\n  import { User, Lock } from \"@element-plus\u002Ficons-vue\";\n  import request from \"@\u002Futils\u002Frequest\";\n  import {ElMessage} from \"element-plus\";\n  import router from \"@\u002Frouter\";\n\n  const data = reactive({\n    form: { role: '普通用户' },\n    rules: {\n      username: [\n        { required: true, message: '请输入账号', trigger: 'blur' },\n      ],\n      password: [\n        { required: true, message: '请输入密码', trigger: 'blur' },\n      ],\n      newPassword: [\n        { required: true, message: '请确认密码', trigger: 'blur' },\n      ],\n    }\n  })\n\n  const formRef = ref()\n\n  \u002F\u002F 点击登录按钮的时候会触发这个方法\n  const register = () => {\n    formRef.value.validate((valid => {\n      if (valid) {\n        \u002F\u002F 调用后台的接口\n        request.post('\u002Fregister', data.form).then(res => {\n          if (res.code === '200') {\n            ElMessage.success(\"恭喜您！注册成功\")\n            router.push('\u002Flogin')\n          } else {\n            ElMessage.error(res.msg)\n          }\n        })\n      }\n    })).catch(error => {\n      console.error(error)\n    })\n  }\n\n\u003C\u002Fscript>\n\n\u003Cstyle scoped>\n.login-container {\n  height: 100vh;\n  overflow:hidden;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  background: #0c9c7a;\n  background-size: cover;\n}\n.login-box {\n  width: 350px;\n  padding: 50px 30px;\n  border-radius: 5px;\n  box-shadow: 0 0 10px rgba(255, 255, 255, 0.3);\n  background-color: #fff;\n}\n\u003C\u002Fstyle>\n```\n\n## 用户前台框架 Front.vue\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cdiv style=\"height: 60px; background-color: #2e3143; display: flex; align-items: center; border-bottom: 1px solid #ddd\">\n      \u003Cdiv style=\"flex: 1\">\n        \u003Cdiv style=\"padding-left: 20px; display: flex; align-items: center\">\n          \u003Cimg src=\"@\u002Fassets\u002Fimgs\u002Flogo.png\" alt=\"\" style=\"width: 40px\">\n          \u003Cdiv style=\"font-weight: bold; font-size: 24px; margin-left: 5px; color: #fff\">校园小卖部\u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n      \u003Cdiv style=\"width: fit-content; padding-right: 10px;\">\n        \u003Cel-dropdown>\n          \u003Cdiv style=\"display: flex; align-items: center;\">\n            \u003Cimg style=\"width: 40px; height: 40px; border-radius: 50%\" :src=\"data.user.avatar || 'https:\u002F\u002Fcube.elemecdn.com\u002F3\u002F7c\u002F3ea6beec64369c2642b92c6726f1epng.png'\" alt=\"\">\n            \u003Cspan style=\"color: #fff; margin-left: 5px\">{{ data.user.name || '代码小白' }}\u003C\u002Fspan>\n          \u003C\u002Fdiv>\n          \u003Ctemplate #dropdown>\n            \u003Cel-dropdown-menu>\n              \u003Cel-dropdown-item @click.native=\"logout\">退出登录\u003C\u002Fel-dropdown-item>\n            \u003C\u002Fel-dropdown-menu>\n          \u003C\u002Ftemplate>\n        \u003C\u002Fel-dropdown>\n\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n\n    \u003Cdiv style=\"background-color: #f0f2ff\">\n      \u003Crouter-view \u002F>\n    \u003C\u002Fdiv>\n\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { reactive } from \"vue\";\nimport router from \"@\u002Frouter\";\nimport {ElMessage} from \"element-plus\";\n\nconst data = reactive({\n  user: JSON.parse(localStorage.getItem('system-user') || '{}')\n})\n\nconst logout = () => {\n  localStorage.removeItem('system-user')\n  router.push('\u002Flogin')\n  ElMessage.success('退出成功')\n}\n\u003C\u002Fscript>\n\n\u003Cstyle>\n.el-tooltip__trigger {\n  cursor: pointer;\n  outline: none !important;\n}\n\u003C\u002Fstyle>\n```\n\n## 个人信息页面 front\u002FPerson.vue\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv class=\"front-container\" style=\"width: 40%\">\n    \u003Cdiv class=\"card\" style=\"padding: 20px\">\n      \u003Cdiv style=\"font-size: 20px; margin-bottom: 40px; text-align: center\">个人信息页\u003C\u002Fdiv>\n\n      \u003Cel-form ref=\"formRef\" :model=\"data.user\" :rules=\"data.rules\" label-width=\"80px\" style=\"padding-right: 30px\">\n        \u003Cel-form-item prop=\"avatar\" label=\"头像\">\n          \u003Cel-upload\n              class=\"avatar-uploader\"\n              :action=\"baseUrl + '\u002Ffiles\u002Fupload'\"\n              :show-file-list=\"false\"\n              :on-success=\"handleFileUpload\"\n          >\n            \u003Cimg v-if=\"data.user.avatar\" :src=\"data.user.avatar\" class=\"avatar\" \u002F>\n            \u003Cel-icon v-else class=\"avatar-uploader-icon\">\u003CPlus \u002F>\u003C\u002Fel-icon>\n          \u003C\u002Fel-upload>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item prop=\"username\" label=\"账号\">\n          \u003Cel-input :disabled=\"data.user.id !== undefined\" v-model=\"data.user.username\" placeholder=\"请输入账号\" autocomplete=\"off\">\u003C\u002Fel-input>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item prop=\"name\" label=\"姓名\">\n          \u003Cel-input v-model=\"data.user.name\" placeholder=\"请输入姓名\" autocomplete=\"off\">\u003C\u002Fel-input>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item prop=\"account\" label=\"账户余额\">\n          \u003Cdiv style=\"color: red; font-weight: bold\">￥{{ data.user.account }}\u003C\u002Fdiv>\n        \u003C\u002Fel-form-item>\n        \u003Cdiv style=\"text-align: center\">\n          \u003Cel-button type=\"primary\" size=\"large\" @click=\"update\">保 存\u003C\u002Fel-button>\n        \u003C\u002Fdiv>\n\n      \u003C\u002Fel-form>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { reactive,ref } from \"vue\";\nimport request from \"@\u002Futils\u002Frequest\";\nimport {ElMessage} from \"element-plus\";\n\nconst emit = defineEmits(['updateUser'])\n\nconst baseUrl = import.meta.env.VITE_BASE_URL\nconst formRef = ref()\nconst data = reactive({\n  user: JSON.parse(localStorage.getItem('system-user') || '{}'),\n  rules: {\n    username: [\n      { required: true, message: '请输入账号', trigger: 'blur' },\n    ]\n  }\n})\n\nconst loadUser = () => {\n  request.get('\u002Fuser\u002FselectById\u002F' + data.user.id).then(res => {\n    data.user = res.data\n    \u002F\u002F 存储最新的用户信息\n    localStorage.setItem('system-user', JSON.stringify(res.data))\n    emit('updateUser')\n  })\n}\nloadUser()\n\nconst handleFileUpload = (res) => {\n  data.user.avatar = res.data\n}\n\nconst update = () => {\n  request.put('\u002Fuser\u002Fupdate', data.user).then(res => {\n    if (res.code === '200') {\n      ElMessage.success('更新成功')\n      loadUser()\n    } else {\n      ElMessage.error(res.msg)\n    }\n  })\n}\n\n\u003C\u002Fscript>\n\n\u003Cstyle scoped>\n.avatar-uploader .avatar {\n  width: 130px;\n  height: 130px;\n  display: block;\n}\n\u003C\u002Fstyle>\n\n\u003Cstyle>\n.avatar-uploader .el-upload {\n  border: 1px dashed var(--el-border-color);\n  border-radius: 6px;\n  cursor: pointer;\n  position: relative;\n  overflow: hidden;\n  transition: var(--el-transition-duration-fast);\n}\n\n.avatar-uploader .el-upload:hover {\n  border-color: var(--el-color-primary);\n}\n\n.el-icon.avatar-uploader-icon {\n  font-size: 28px;\n  color: #8c939d;\n  width: 130px;\n  height: 130px;\n  text-align: center;\n}\n\u003C\u002Fstyle>\n\n```\n\n```vue\n\u003Cdiv style=\"background-color: #f0f2ff\">\n  \u003Crouter-view @updateUser=\"updateUser\" \u002F>\n\u003C\u002Fdiv>\n\n```\n\n```vue\n\u002F\u002F 更新Front里面的user对象为最新值\nconst updateUser = () => {\n  data.user = JSON.parse(localStorage.getItem('system-user') || '{}')\n}\n\n```\n\n## 修改密码 front\u002FPassword.vue\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv class=\"front-container\" style=\"width: 40%\">\n    \u003Cdiv class=\"card\" style=\"padding: 20px\">\n      \u003Cdiv style=\"font-size: 20px; margin-bottom: 40px; text-align: center\">修改密码\u003C\u002Fdiv>\n      \u003Cel-form ref=\"formRef\" :model=\"data.user\" :rules=\"data.rules\" label-width=\"100px\" style=\"padding-right: 30px\">\n        \u003Cel-form-item prop=\"password\" label=\"原密码\">\n          \u003Cel-input v-model=\"data.user.password\" autocomplete=\"off\" show-password>\u003C\u002Fel-input>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item prop=\"newPassword\" label=\"新密码\">\n          \u003Cel-input v-model=\"data.user.newPassword\" autocomplete=\"off\" show-password>\u003C\u002Fel-input>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item prop=\"confirmPassword\" label=\"确认新密码\">\n          \u003Cel-input v-model=\"data.user.confirmPassword\" autocomplete=\"off\" show-password>\u003C\u002Fel-input>\n        \u003C\u002Fel-form-item>\n        \u003Cdiv style=\"text-align: center\">\n          \u003Cel-button type=\"primary\" size=\"large\" @click=\"updatePassword\">保 存\u003C\u002Fel-button>\n        \u003C\u002Fdiv>\n      \u003C\u002Fel-form>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { reactive,ref } from \"vue\";\nimport request from \"@\u002Futils\u002Frequest\";\nimport {ElMessage} from \"element-plus\";\nimport router from \"@\u002Frouter\";\n\nconst formRef = ref()\nconst data = reactive({\n  user: JSON.parse(localStorage.getItem('system-user') || '{}'),\n  rules: {\n    password: [\n      { required: true, message: '请输入原密码', trigger: 'blur' },\n    ],\n    newPassword: [\n      { required: true, message: '请输入新密码', trigger: 'blur' },\n    ],\n    confirmPassword: [\n      { required: true, message: '请确认新密码', trigger: 'blur' },\n    ]\n  }\n})\n\nconst updatePassword = () => {\n  if (data.user.newPassword !== data.user.confirmPassword) {\n    ElMessage.warning('两次输入的新密码不同，请确认！')\n    return\n  }\n  request.put('\u002FupdatePassword', data.user).then(res => {\n    if (res.code === '200') {\n      ElMessage.success('更新成功')\n      logout()\n    } else {\n      ElMessage.error(res.msg)\n    }\n  })\n}\n\nconst logout = () => {\n  router.push('\u002Flogin')\n  localStorage.removeItem('system-user')\n}\n\u003C\u002Fscript>\n\n```\n\n","coding",1,6474,2003,"2025-11-17 17:03:58","2026-05-03 22:49:02","青哥带小白做毕设2026所有资料汇总","qingge-code-2026",{"project":18,"items":19},{"id":6,"title":15,"slug":16},[20,27,34,41,48,49,56,63,70,77,84,91,98,105,112,119,127,134],{"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":14,"project_title":15,"project_slug":16},993,"mgoFKmGT","00. 带小白做毕设课程介绍以及脚手架获取",32063,1966,"2026-03-29 19:37:39",{"id":28,"uuid":29,"project_id":6,"title":30,"type":9,"status":10,"public_enabled":10,"views":31,"sort":32,"created_at":33,"updated_at":14,"project_title":15,"project_slug":16},998,"1kCk1d2E","01. 导入并运行项目脚手架",14398,1974,"2025-11-11 16:24:50",{"id":35,"uuid":36,"project_id":6,"title":37,"type":9,"status":10,"public_enabled":10,"views":38,"sort":39,"created_at":40,"updated_at":14,"project_title":15,"project_slug":16},1003,"8ZlQ12IX","02. 带你开发一个基础的用户管理模块（上）",12150,1987,"2025-11-12 16:53:01",{"id":42,"uuid":43,"project_id":6,"title":44,"type":9,"status":10,"public_enabled":10,"views":45,"sort":46,"created_at":47,"updated_at":14,"project_title":15,"project_slug":16},1006,"6mmUNf9i","03. 带你开发一个基础的用户管理模块（下）",7782,1994,"2025-11-13 16:48:27",{"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":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},1017,"LnAAYydZ","05. 带你开发商品分类管理功能",5467,2012,"2025-11-24 17:30:23",{"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},1018,"584qohhy","06. 开发商品信息管理功能",4836,2013,"2025-11-24 17:30:12",{"id":64,"uuid":65,"project_id":6,"title":66,"type":9,"status":10,"public_enabled":10,"views":67,"sort":68,"created_at":69,"updated_at":14,"project_title":15,"project_slug":16},1024,"scxsifCZ","07. 开发轮播图信息管理功能",3313,2021,"2025-11-24 17:29:58",{"id":71,"uuid":72,"project_id":6,"title":73,"type":9,"status":10,"public_enabled":10,"views":74,"sort":75,"created_at":76,"updated_at":14,"project_title":15,"project_slug":16},1041,"AjujV8Gq","08. 带你实现前台首页功能",4058,2039,"2025-11-24 17:31:31",{"id":78,"uuid":79,"project_id":6,"title":80,"type":9,"status":10,"public_enabled":10,"views":81,"sort":82,"created_at":83,"updated_at":14,"project_title":15,"project_slug":16},1045,"p6yD8ZxS","09. 带你实现商品搜索、商品分类检索功能",3005,2048,"2025-11-25 16:35:45",{"id":85,"uuid":86,"project_id":6,"title":87,"type":9,"status":10,"public_enabled":10,"views":88,"sort":89,"created_at":90,"updated_at":14,"project_title":15,"project_slug":16},1050,"7gHUek7z","10. 带你实现商品详情页功能",2622,2053,"2025-11-26 17:22:20",{"id":92,"uuid":93,"project_id":6,"title":94,"type":9,"status":10,"public_enabled":10,"views":95,"sort":96,"created_at":97,"updated_at":14,"project_title":15,"project_slug":16},1059,"aX7rviTw","11. 带你实现商品收藏功能",2667,2067,"2025-11-28 17:00:21",{"id":99,"uuid":100,"project_id":6,"title":101,"type":9,"status":10,"public_enabled":10,"views":102,"sort":103,"created_at":104,"updated_at":14,"project_title":15,"project_slug":16},1060,"xcmGXr9E","12. 带你开发用户模拟充值功能",2443,2068,"2026-02-02 14:24:37",{"id":106,"uuid":107,"project_id":6,"title":108,"type":9,"status":10,"public_enabled":10,"views":109,"sort":110,"created_at":111,"updated_at":14,"project_title":15,"project_slug":16},1065,"z8IN0tX8","13. 带你实现购物车功能",2461,2078,"2025-12-01 17:16:05",{"id":113,"uuid":114,"project_id":6,"title":115,"type":9,"status":10,"public_enabled":10,"views":116,"sort":117,"created_at":118,"updated_at":14,"project_title":15,"project_slug":16},1069,"NfBIfhVx","14. 带你实现商品下单功能",3131,2086,"2025-12-02 16:57:21",{"id":120,"uuid":121,"project_id":6,"title":122,"type":9,"status":10,"public_enabled":10,"views":123,"sort":124,"created_at":125,"updated_at":126,"project_title":15,"project_slug":16},1072,"PDQdczrn","14.1 带你实现订单配送功能",2772,2097,"2025-12-03 16:10:40","2026-05-07 15:36:12.649662+00",{"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},1075,"RYH15Bfu","15. 带你实现订单评价功能",2507,2103,"2025-12-04 17:10:47",{"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},1079,"iUdzrNPu","16. 带你实现后台数据统计功能",2991,2107,"2025-12-05 17:37:57"]