[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-public-2l7FVzgx":3,"public-project-articles-2l7FVzgx":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},1090,"2l7FVzgx",54,"04. 开发普通用户登录、注册、个人信息、修改密码功能","## WebController\n\n```java\npackage com.example.controller;\n\nimport com.example.common.Result;\nimport com.example.entity.Account;\nimport com.example.entity.User;\nimport com.example.service.AdminService;\nimport com.example.service.UserService;\nimport jakarta.annotation.Resource;\nimport org.springframework.web.bind.annotation.*;\n\n\n@RestController\npublic class WebController {\n\n    @Resource\n    private AdminService adminService;\n    @Resource\n    private UserService userService;\n\n\n    \u002F**\n     * 默认请求接口\n     *\u002F\n    @GetMapping(\"\u002F\")\n    public Result hello() {\n        return Result.success();\n    }\n\n    \u002F**\n     * 登录\n     *\u002F\n    @PostMapping(\"\u002Flogin\")\n    public 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        return Result.success(ac);\n    }\n\n    \u002F**\n     * 注册\n     *\u002F\n    @PostMapping(\"\u002Fregister\")\n    public Result register(@RequestBody User user) {\n        userService.add(user);\n        return Result.success();\n    }\n\n    \u002F**\n     * 修改密码\n     *\u002F\n    @PutMapping(\"\u002FupdatePassword\")\n    public Result updatePassword(@RequestBody Account account) {\n        if (\"管理员\".equals(account.getRole())) {\n            adminService.updatePassword(account);\n        }\n        if (\"普通用户\".equals(account.getRole())) {\n            userService.updatePassword(account);\n        }\n        return Result.success();\n    }\n\n}\n\n```\n\n\n\n## 前端 Login.vue\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: #1967e3\">欢 迎 登 录\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=\"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        \u003Cel-form-item>\n          \u003Cel-button size=\"large\" type=\"primary\" style=\"width: 100%\" @click=\"login\">登 录\u003C\u002Fel-button>\n        \u003C\u002Fel-form-item>\n      \u003C\u002Fel-form>\n      \u003Cdiv style=\"text-align: right;\">\n        还没有账号？请 \u003Ca href=\"\u002Fregister\">注册\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    }\n  })\n\n  const formRef = ref()\n\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            router.push('\u002Fmanager\u002Fhome')\n            localStorage.setItem('system-user', JSON.stringify(res.data))\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: #2e3143;\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## 前端注册  Register.vue\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: #1967e3\">欢 迎 注 册\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=\"confirmPassword\">\n          \u003Cel-input :prefix-icon=\"Lock\" size=\"large\" v-model=\"data.form.confirmPassword\" placeholder=\"请确认密码\" show-password \u002F>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item>\n          \u003Cel-button size=\"large\" type=\"primary\" style=\"width: 100%\" @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 validatePass = (rule, value, callback) => {\n    if (!value) {\n      callback(new Error('请确认密码'))\n    } else if (value !== data.form.password) {\n      callback(new Error('两次输入密码不一致'))\n    } else {\n      callback()\n    }\n  }\n\n  const data = reactive({\n    form: { role: 'USER' },\n    rules: {\n      username: [\n        { required: true, message: '请输入账号', trigger: 'blur' },\n      ],\n      password: [\n        { required: true, message: '请输入密码', trigger: 'blur' },\n      ],\n      confirmPassword: [\n        { validator: validatePass, trigger: 'blur' },\n      ],\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: #2e3143;\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## 前端 Person.vue\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv style=\"width: 40%\">\n    \u003Cdiv class=\"card\" style=\"padding: 30px\">\n      \u003Cel-form ref=\"formRef\" :model=\"data.user\" :rules=\"data.rules\" label-width=\"100px\" style=\"padding-right: 50px\">\n        \u003Cdiv style=\"margin: 20px 0; text-align: center\">\n          \u003Cel-upload :show-file-list=\"false\" class=\"avatar-uploader\" :action=\"uploadUrl\" :on-success=\"handleFileUpload\">\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\u002Fdiv>\n        \u003Cel-form-item label=\"账号\" prop=\"username\">\n          \u003Cel-input disabled v-model=\"data.user.username\" autocomplete=\"off\" \u002F>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item label=\"名称\" prop=\"name\">\n          \u003Cel-input v-model=\"data.user.name\" autocomplete=\"off\" \u002F>\n        \u003C\u002Fel-form-item>\n        \u003Cdiv v-if=\"data.user.role === '普通用户'\">\n          \u003Cel-form-item label=\"手机号\" prop=\"phone\">\n            \u003Cel-input placeholder=\"请输入手机号\" v-model=\"data.user.phone\" autocomplete=\"off\" \u002F>\n          \u003C\u002Fel-form-item>\n          \u003Cel-form-item label=\"邮箱\" prop=\"email\">\n            \u003Cel-input placeholder=\"请输入邮箱\" v-model=\"data.user.email\" autocomplete=\"off\" \u002F>\n          \u003C\u002Fel-form-item>\n        \u003C\u002Fdiv>\n        \u003Cdiv style=\"text-align: center\">\n          \u003Cel-button type=\"primary\" @click=\"save\">保存\u003C\u002Fel-button>\n        \u003C\u002Fdiv>\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\n\u002F\u002F 文件上传的接口地址\nconst uploadUrl = import.meta.env.VITE_BASE_URL + '\u002Ffiles\u002Fupload'\n\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    name: [\n      {required: true, message: '请输入名称', trigger: 'blur'}\n    ],\n  }\n})\n\nconst handleFileUpload = (file) => {\n  data.user.avatar = file.data\n}\n\nconst emit = defineEmits([\"updateUser\"])\n\u002F\u002F 把当前修改的用户信息存储到后台数据库\nconst save = () => {\n  formRef.value.validate(valid => {\n    if (valid) {\n      if (data.user.role === '管理员') {\n        request.put('\u002Fadmin\u002Fupdate', data.user).then(res => {\n          if (res.code === '200') {\n            ElMessage.success('更新成功')\n            \u002F\u002F把更新后的用户信息存储到缓存\n            localStorage.setItem('system-user', JSON.stringify(data.user))\n            emit('updateUser')\n          } else {\n            ElMessage.error(res.msg)\n          }\n        })\n      }\n      if (data.user.role === '普通用户') {\n        request.put('\u002Fuser\u002Fupdate', data.user).then(res => {\n          if (res.code === '200') {\n            ElMessage.success('更新成功')\n            \u002F\u002F把更新后的用户信息存储到缓存\n            localStorage.setItem('system-user', JSON.stringify(data.user))\n            emit('updateUser')\n          } else {\n            ElMessage.error(res.msg)\n          }\n        })\n      }\n    }\n  })\n}\n\u003C\u002Fscript>\n\n\u003Cstyle scoped>\n.avatar-uploader .avatar {\n  width: 120px;\n  height: 120px;\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: 120px;\n  height: 120px;\n  text-align: center;\n}\n\u003C\u002Fstyle>\n```\n\n## 前端 Password.vue\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv style=\"width: 40%\">\n    \u003Cdiv class=\"card\" style=\"padding: 30px\">\n      \u003Cel-form ref=\"formRef\" :rules=\"data.rules\" :model=\"data.user\" label-width=\"100px\" style=\"padding-right: 50px\">\n        \u003Cel-form-item label=\"原密码\" prop=\"password\">\n          \u003Cel-input v-model=\"data.user.password\" show-password \u002F>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item label=\"新密码\" prop=\"newPassword\">\n          \u003Cel-input v-model=\"data.user.newPassword\" show-password \u002F>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item label=\"确认新密码\" prop=\"confirmPassword\">\n          \u003Cel-input v-model=\"data.user.confirmPassword\" show-password \u002F>\n        \u003C\u002Fel-form-item>\n        \u003Cdiv style=\"text-align: center\">\n          \u003Cel-button type=\"primary\" @click=\"save\">保存\u003C\u002Fel-button>\n        \u003C\u002Fdiv>\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\";\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\n\u002F\u002F 把当前修改的用户信息存储到后台数据库\nconst save = () => {\n  formRef.value.validate(valid => {\n    if (valid) {\n      if (data.user.password === data.user.newPassword) {\n        ElMessage.error('新密码不能和原密码一致')\n        return\n      }\n      if (data.user.newPassword !== data.user.confirmPassword) {\n        ElMessage.error('确认新密码错误')\n        return\n      }\n      request.put('\u002FupdatePassword', data.user).then(res => {\n        if (res.code === '200') {\n          ElMessage.success('修改密码成功')\n          \u002F\u002F把更新后的用户信息存储到缓存\n          localStorage.setItem('system-user', JSON.stringify(data.user))\n          router.push('\u002Flogin')\n        } else {\n          ElMessage.error(res.msg)\n        }\n      })\n    }\n  })\n}\n\u003C\u002Fscript>\n```\n\n","coding",1,365,2131,"2025-12-10 16:11:48","2026-05-03 22:49:02","基于SpringBoot3+Vue3的校园物品分享系统","campus-item-sharing",{"project":18,"items":19},{"id":6,"title":15,"slug":16},[20,27,34,41,42,50,57,64,71,78,85,92,99],{"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},1085,"bKvz3GMB","01. 校园物品分享系统介绍",1584,2118,"2026-04-01 22:12:21",{"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},1086,"G7CRiFiL","02. 导入并运行项目脚手架",670,2119,"2025-12-09 16:59:49",{"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},1087,"pgDLuOpz","03. 开发普通用户信息管理功能",544,2121,"2025-12-09 16:59:38",{"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":43,"uuid":44,"project_id":6,"title":45,"type":9,"status":10,"public_enabled":10,"views":46,"sort":47,"created_at":48,"updated_at":49,"project_title":15,"project_slug":16},1091,"l5SB4JYK","05. 开发系统公告管理功能",417,2132,"2025-12-10 16:12:08","2026-05-07 15:36:12.649662+00",{"id":51,"uuid":52,"project_id":6,"title":53,"type":9,"status":10,"public_enabled":10,"views":54,"sort":55,"created_at":56,"updated_at":14,"project_title":15,"project_slug":16},1098,"mXLOXdBC","06. 开发物品分类信息管理功能",297,2143,"2025-12-11 16:37:57",{"id":58,"uuid":59,"project_id":6,"title":60,"type":9,"status":10,"public_enabled":10,"views":61,"sort":62,"created_at":63,"updated_at":14,"project_title":15,"project_slug":16},1099,"nj87VT4L","07. 开发物品信息管理功能",406,2144,"2025-12-11 16:38: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},1107,"nGHKfpf4","08. 开发用户端物品展示功能",329,2162,"2025-12-15 17:27:07",{"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},1123,"A6caJxFd","09. 开发物品申请交换功能",322,2196,"2025-12-19 16:16:03",{"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},1135,"s5I1gomY","10. 开发物品收藏功能",285,2223,"2025-12-23 17:38:15",{"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},1159,"qadunvVA","11. 开发论坛帖子管理功能",263,2266,"2025-12-30 16:08:47",{"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},1202,"CpPqHdiT","12. 开发论坛帖子展示功能",220,2339,"2026-01-19 17:04:21",{"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},1203,"7YABYvbd","13. 开发论坛帖子点赞和评论功能",239,2340,"2026-01-19 17:04:37"]