黑马网课springboot3+vue3中大事件项目代码优化(前端篇)
大事件前端代码优化和功能添加详细对比分析
前言
本人在进行黑马网课springboot3加vue3的课程时,在后端中优化了一系列代码,和增加了新的功能,于是对前端代码也进行相应调整,使这些原版项目中没有实现的功能,在优化版项目中实现,以下是我我做出的优化和功能添加对比分析,后端篇优化可以去看我写的这个:黑马网课springboot3+vue3中大事件项目代码优化(后端篇)
详细代码差异对比
1. 用户密码修改功能
优化版项目中的UserResetPassword.vue文件实现了完整的密码修改功能:
<script setup>import{ ref }from'vue'import{ userUpdatePasswordService }from'@/api/user.js'import{ ElMessage }from'element-plus'import{ useRouter }from'vue-router'const router =useRouter()// 表单数据const form =ref({old_pwd:'',new_pwd:'',re_pwd:''})// 表单验证规则const rules ={old_pwd:[{required:true,message:'请输入原密码',trigger:'blur'},{min:6,max:16,message:'密码长度应在6-16位之间',trigger:'blur'}],new_pwd:[{required:true,message:'请输入新密码',trigger:'blur'},{min:6,max:16,message:'密码长度应在6-16位之间',trigger:'blur'}],re_pwd:[{required:true,message:'请再次输入新密码',trigger:'blur'},{validator:(rule, value, callback)=>{if(value !== form.value.new_pwd){callback(newError('两次输入的密码不一致'))}else{callback()}},trigger:'blur'}]}constupdatePassword=async(formRef)=>{// 验证表单try{// 检查两次输入的新密码是否一致if(form.value.new_pwd !== form.value.re_pwd){ ElMessage.error('两次输入的新密码不一致')return}// 调用API更新密码const result =awaituserUpdatePasswordService(form.value.old_pwd, form.value.new_pwd, form.value.re_pwd)if(result.code ===0){ ElMessage.success(result.msg ||'密码修改成功')// 返回个人中心 router.push('/user/info')}}catch(error){ console.error('密码修改失败:', error)}}</script><template><el-card class="page-container"><template #header><div class="header"><span>重置密码</span></div></template><el-row><el-col :span="12"><el-form :model="form":rules="rules" ref="formRef" label-width="100px" status-icon ><el-form-item label="原密码" prop="old_pwd"><el-input v-model="form.old_pwd" type="password" show-password /></el-form-item><el-form-item label="新密码" prop="new_pwd"><el-input v-model="form.new_pwd" type="password" show-password /></el-form-item><el-form-item label="确认新密码" prop="re_pwd"><el-input v-model="form.re_pwd" type="password" show-password /></el-form-item><el-form-item><el-button type="primary" @click="updatePassword(formRef)">提交</el-button></el-form-item></el-form></el-col></el-row></el-card></template><style scoped>.page-container { min-height: 800px;}</style>而原版项目中的UserResetPassword.vue文件:
<template> 重置密码 </template>对应的API接口差异:
优化版项目中的api/user.js新增了密码修改功能:
//修改密码exportconstuserUpdatePasswordService=(oldPwd, newPwd, rePwd)=>{const params =newURLSearchParams(); params.append('old_pwd', oldPwd); params.append('new_pwd', newPwd); params.append('re_pwd', rePwd);return request.post('/user/updatePwd', params);}而原版项目中的api/user.js没有此功能:
2. 用户头像上传功能的差异
优化版项目中的UserAvatar.vue使用了正确的文件上传方式:
//图片上传成功的回调函数import{ userInfoService }from'@/api/user.js'constuploadSuccess=(result)=>{// 直接上传并更新头像,无需额外调用API imgUrl.value = result.data; ElMessage.success('头像上传成功')//修改pinia中的数据 userInfoStore.info.userPic = imgUrl.value // 重新获取用户信息以确保其他组件显示最新数据userInfoService().then(res=>{ userInfoStore.setInfo(res.data);});}import{ElMessage}from'element-plus'// 头像修改按钮现在只是触发文件选择constupdateAvatar=async()=>{// 触发文件选择 uploadRef.value.$el.querySelector('input').click()}优化版项目中的api/user.js定义了正确的文件上传方式:
//修改头像exportconstuserAvatarUpdateService=(file)=>{const formData =newFormData(); formData.append('file', file);return request.post('/user/updateAvatar', formData,{headers:{'Content-Type':'multipart/form-data'}})}而原版项目中的UserAvatar.vue使用了不同的实现方式:
//图片上传成功的回调函数constuploadSuccess=(result)=>{ imgUrl.value = result.data;}import{userAvatarUpdateService}from'@/api/user.js'import{ElMessage}from'element-plus'//头像修改constupdateAvatar=async()=>{//调用接口let result =awaituserAvatarUpdateService(imgUrl.value); ElMessage.success(result.msg? result.msg:'修改成功')//修改pinia中的数据 userInfoStore.info.userPic = imgUrl.value }原版项目中的api/user.js使用了URL参数方式:
//修改头像exportconstuserAvatarUpdateService=(avatarUrl)=>{const params =newURLSearchParams(); params.append('avatarUrl',avatarUrl)return request.patch('/user/updateAvatar',params)}3. 文章管理功能的差异
原版项目中的ArticleManage.vue(老代码):
<script setup>import{ Edit, Delete }from'@element-plus/icons-vue'import{ ref }from'vue'//文章分类数据模型const categorys =ref([{"id":3,"categoryName":"美食","categoryAlias":"my","createTime":"2023-09-02 12:06:59","updateTime":"2023-09-02 12:06:59"},{"id":4,"categoryName":"娱乐","categoryAlias":"yl","createTime":"2023-09-02 12:08:16","updateTime":"2023-09-02 12:08:16"},{"id":5,"categoryName":"军事","categoryAlias":"js","createTime":"2023-09-02 12:08:33","updateTime":"2023-09-02 12:08:33"}])//用户搜索时选中的分类idconst categoryId =ref('')//用户搜索时选中的发布状态const state =ref('')//文章列表数据模型const articles =ref([{"id":5,"title":"陕西旅游攻略","content":"兵马俑,华清池,法门寺,华山...爱去哪去哪...","coverImg":"https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png","state":"草稿","categoryId":2,"createTime":"2023-09-03 11:55:30","updateTime":"2023-09-03 11:55:30"},])//分页条数据模型const pageNum =ref(1)//当前页const total =ref(0)//总条数const pageSize =ref(3)//每页条数//当每页条数发生了变化,调用此函数constonSizeChange=(size)=>{ pageSize.value = size // articleList()}//当页码发生变化后,调用此函数constonCurrentChange=(num)=>{ pageNum.value = num // articleList()}//获取文章列表import{ articleCategoryListService, articleListService, articleAddService, articleUpdateService, articleDeleteService }from'@/api/article.js'constarticleList=async()=>{let params ={pageNum: pageNum.value,pageSize: pageSize.value,categoryId: categoryId.value,state: state.value }let result =awaitarticleListService(params); articles.value = result.data.items; total.value = result.data.total;}articleList();//获取分类数据constgetCategoryList=async()=>{let result =awaitarticleCategoryListService(); categorys.value = result.data;}getCategoryList();//控制添加或修改弹窗的显示const dialogVisible =ref(false)//定义变量,控制弹窗标题const title =ref('')//添加或修改的文章数据模型const articleModel =ref({id:'',title:'',content:'',categoryId:'',state:''})//添加或修改文章的表单校验规则const rules ={title:[{required:true,message:'请输入文章标题',trigger:'blur'},{min:2,max:30,message:'长度为2-30个字符',trigger:'blur'}],categoryId:[{required:true,message:'请选择文章分类',trigger:'change'}],state:[{required:true,message:'请选择发布状态',trigger:'change'}]}//添加按钮的回调函数constaddArticle=()=>{ dialogVisible.value =true title.value ='添加文章'//清空数据模型 articleModel.value ={id:'',title:'',content:'',categoryId:'',state:''}}//编辑按钮的回调函数consteditArticle=(row)=>{ dialogVisible.value =true title.value ='编辑文章'//数据回显 articleModel.value.id = row.id articleModel.value.title = row.title articleModel.value.content = row.content articleModel.value.categoryId = row.categoryId articleModel.value.state = row.state }//确认添加或修改import{ ElMessage }from'element-plus'constconfirm=async()=>{//判断是添加还是修改if(articleModel.value.id){//修改let result =awaitarticleUpdateService(articleModel.value); ElMessage.success(result.msg ? result.msg :'修改成功')}else{//添加let result =awaitarticleAddService(articleModel.value); ElMessage.success(result.msg ? result.msg :'添加成功')}//重新获取文章列表articleList();//隐藏弹窗 dialogVisible.value =false;}//删除文章constdeleteArticle=(row)=>{//调用接口articleDeleteService(row.id).then(result=>{ ElMessage.success(result.msg ? result.msg :'删除成功')//重新获取文章列表articleList();})}</script><template><el-card class="page-container"><template #header><div class="header"><span>文章管理</span></div></template><!-- 搜索区域 --><el-row :gutter="20" style="margin-bottom: 20px;"><el-col :span="4"><el-select v-model="categoryId" placeholder="全部分类" clearable><el-option v-for="c in categorys":key="c.id":label="c.categoryName":value="c.id"/></el-select></el-col><el-col :span="4"><el-select v-model="state" placeholder="全部状态" clearable><el-option label="草稿" value="草稿"/><el-option label="已发布" value="已发布"/></el-select></el-col><el-col :span="4"><el-button type="primary" @click="articleList">搜索</el-button></el-col></el-row><!-- 表格 --><el-table :data="articles" stripe style="width: 100%"><el-table-column prop="title" label="文章标题"/><el-table-column prop="categoryId" label="分类"/><el-table-column prop="state" label="发布状态"/><el-table-column prop="createTime" label="创建时间"/><el-table-column label="操作" width="200"><template #default="{ row }"><el-button type="primary":icon="Edit" size="small" @click="editArticle(row)">编辑</el-button><el-button type="danger":icon="Delete" size="small" @click="deleteArticle(row)">删除</el-button></template></el-table-column></el-table><!-- 分页 --><el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize":page-sizes="[3, 5, 10, 15]":small="false":disabled="false" layout="total, sizes, prev, pager, next, jumper":total="total" @size-change="onSizeChange" @current-change="onCurrentChange" style="margin-top: 20px; justify-content: flex-end;"/><!-- 添加或修改文章的弹窗 --><el-dialog v-model="dialogVisible":title="title" width="600" destroy-on-close><el-form :model="articleModel":rules="rules" label-width="100px" style="width: 500px" status-icon><el-form-item label="文章标题" prop="title"><el-input v-model="articleModel.title"/></el-form-item><el-form-item label="文章分类" prop="categoryId"><el-select v-model="articleModel.categoryId" style="width: 100%"><el-option v-for="c in categorys":key="c.id":label="c.categoryName":value="c.id"/></el-select></el-form-item><el-form-item label="发布状态" prop="state"><el-radio-group v-model="articleModel.state"><el-radio label="草稿"/><el-radio label="已发布"/></el-radio-group></el-form-item><el-form-item label="文章内容" prop="content"><el-input v-model="articleModel.content" type="textarea":rows="4" placeholder="请输入文章内容" maxlength="500" show-word-limit /></el-form-item><el-form-item><el-button type="primary" @click="confirm">确认</el-button></el-form-item></el-form></el-dialog></el-card></template>优化版项目中的ArticleManage.vue(新代码):
<script setup>import{ Edit, Delete }from'@element-plus/icons-vue'import{ ref }from'vue'//定义变量存储选中的文章const selectedArticles =ref([])// 当表格选中项发生变化时的回调函数consthandleSelectionChange=(val)=>{ selectedArticles.value = val }// 批量删除文章constbatchDeleteArticles=async()=>{if(selectedArticles.value.length ===0){ ElMessage.warning('请至少选择一篇文章')return}try{// 获取选中文章的IDconst ids = selectedArticles.value.map(article=> article.id)// 调用批量删除APIconst result =await Promise.all(ids.map(id=>articleDeleteService(id)))if(result.every(res=> res.code ===0)){ ElMessage.success('批量删除成功')// 重新获取文章列表articleList()}else{ ElMessage.error('部分文章删除失败')}}catch(error){ console.error('批量删除失败:', error) ElMessage.error('批量删除失败')}}//文章分类数据模型const categorys =ref([{"id":3,"categoryName":"美食","categoryAlias":"my","createTime":"2023-09-02 12:06:59","updateTime":"2023-09-02 12:06:59"},{"id":4,"categoryName":"娱乐","categoryAlias":"yl","createTime":"2023-09-02 12:08:16","updateTime":"2023-09-02 12:08:16"},{"id":5,"categoryName":"军事","categoryAlias":"js","createTime":"2023-09-02 12:08:33","updateTime":"2023-09-02 12:08:33"}])//用户搜索时选中的分类idconst categoryId =ref('')//用户搜索时选中的发布状态const state =ref('')//文章列表数据模型const articles =ref([])//分页条数据模型const pageNum =ref(1)//当前页const total =ref(0)//总条数const pageSize =ref(3)//每页条数//当每页条数发生了变化,调用此函数constonSizeChange=(size)=>{ pageSize.value = size articleList()}//当页码发生变化后,调用此函数constonCurrentChange=(num)=>{ pageNum.value = num articleList()}//获取文章列表import{ articleCategoryListService, articleListService, articleAddService, articleUpdateService, articleDeleteService }from'@/api/article.js'constarticleList=async()=>{let params ={pageNum: pageNum.value,pageSize: pageSize.value,categoryId: categoryId.value,state: state.value }let result =awaitarticleListService(params); articles.value = result.data.items; total.value = result.data.total;}articleList();//获取分类数据constgetCategoryList=async()=>{let result =awaitarticleCategoryListService(); categorys.value = result.data;}getCategoryList();//控制添加或修改弹窗的显示const dialogVisible =ref(false)//定义变量,控制弹窗标题const title =ref('')//添加或修改的文章数据模型const articleModel =ref({id:'',title:'',content:'',categoryId:'',state:''})//添加或修改文章的表单校验规则const rules ={title:[{required:true,message:'请输入文章标题',trigger:'blur'},{min:2,max:30,message:'长度为2-30个字符',trigger:'blur'}],categoryId:[{required:true,message:'请选择文章分类',trigger:'change'}],state:[{required:true,message:'请选择发布状态',trigger:'change'}]}//添加按钮的回调函数constaddArticle=()=>{ dialogVisible.value =true title.value ='添加文章'//清空数据模型 articleModel.value ={id:'',title:'',content:'',categoryId:'',state:''}}//编辑按钮的回调函数consteditArticle=(row)=>{ dialogVisible.value =true title.value ='编辑文章'//数据回显 articleModel.value.id = row.id articleModel.value.title = row.title articleModel.value.content = row.content articleModel.value.categoryId = row.categoryId articleModel.value.state = row.state }//确认添加或修改import{ ElMessage }from'element-plus'constconfirm=async()=>{//判断是添加还是修改if(articleModel.value.id){//修改let result =awaitarticleUpdateService(articleModel.value); ElMessage.success(result.msg ? result.msg :'修改成功')}else{//添加let result =awaitarticleAddService(articleModel.value); ElMessage.success(result.msg ? result.msg :'添加成功')}//重新获取文章列表articleList();//隐藏弹窗 dialogVisible.value =false;}//删除文章import{ElMessageBox}from'element-plus'constdeleteArticle=(row)=>{ ElMessageBox.confirm('您确认要删除此文章吗?','温馨提示',{confirmButtonText:'确认',cancelButtonText:'取消',type:'warning',}).then(async()=>{//调用接口let result =awaitarticleDeleteService(row.id); ElMessage.success(result.msg ? result.msg :'删除成功')//重新获取文章列表articleList();}).catch(()=>{ ElMessage.info('已取消删除')})}</script><template><el-card class="page-container"><template #header><div class="header"><span>文章管理</span></div></template><!-- 搜索区域 --><el-row :gutter="20" style="margin-bottom: 20px;"><el-col :span="4"><el-select v-model="categoryId" placeholder="全部分类" clearable><el-option v-for="c in categorys":key="c.id":label="c.categoryName":value="c.id"/></el-select></el-col><el-col :span="4"><el-select v-model="state" placeholder="全部状态" clearable><el-option label="草稿" value="草稿"/><el-option label="已发布" value="已发布"/></el-select></el-col><el-col :span="4"><el-button type="primary" @click="articleList">搜索</el-button><el-button type="danger" @click="batchDeleteArticles":disabled="selectedArticles.length === 0"> 批量删除 </el-button></el-col></el-row><!-- 表格 --><el-table :data="articles" stripe style="width: 100%" @selection-change="handleSelectionChange"><el-table-column type="selection" width="50"/><el-table-column prop="title" label="文章标题"/><el-table-column prop="categoryId" label="分类"/><el-table-column prop="state" label="发布状态"/><el-table-column prop="createTime" label="创建时间"/><el-table-column label="操作" width="200"><template #default="{ row }"><el-button type="primary":icon="Edit" size="small" @click="editArticle(row)">编辑</el-button><el-button type="danger":icon="Delete" size="small" @click="deleteArticle(row)">删除</el-button></template></el-table-column></el-table><!-- 分页 --><el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize":page-sizes="[3, 5, 10, 15]":small="false":disabled="false" layout="total, sizes, prev, pager, next, jumper":total="total" @size-change="onSizeChange" @current-change="onCurrentChange" style="margin-top: 20px; justify-content: flex-end;"/><!-- 添加或修改文章的弹窗 --><el-dialog v-model="dialogVisible":title="title" width="600" destroy-on-close><el-form :model="articleModel":rules="rules" label-width="100px" style="width: 500px" status-icon><el-form-item label="文章标题" prop="title"><el-input v-model="articleModel.title"/></el-form-item><el-form-item label="文章分类" prop="categoryId"><el-select v-model="articleModel.categoryId" style="width: 100%"><el-option v-for="c in categorys":key="c.id":label="c.categoryName":value="c.id"/></el-select></el-form-item><el-form-item label="发布状态" prop="state"><el-radio-group v-model="articleModel.state"><el-radio label="草稿"/><el-radio label="已发布"/></el-radio-group></el-form-item><el-form-item label="文章内容" prop="content"><el-input v-model="articleModel.content" type="textarea":rows="4" placeholder="请输入文章内容" maxlength="500" show-word-limit /></el-form-item><el-form-item><el-button type="primary" @click="confirm">确认</el-button></el-form-item></el-form></el-dialog></el-card></template>4. 富文本编辑器集成
优化版项目中的ArticleManage.vue新增了富文本编辑器:
<template><el-form-item label="文章内容" prop="content"><div class="editor-container"><VueQuill ref="quillRef" v-model:content="articleModel.content":options="quillOptions" style="height: 300px;"/></div></el-form-item></template><script setup>import{ VueQuill }from'@vueup/vue-quill'import'@vueup/vue-quill/dist/vue-quill.snow.css'// 富文本编辑器配置const quillOptions ={modules:{toolbar:[['bold','italic','underline','strike'],['blockquote','code-block'],[{'header':1},{'header':2}],[{'list':'ordered'},{'list':'bullet'}],[{'script':'sub'},{'script':'super'}],[{'indent':'-1'},{'indent':'+1'}],[{'direction':'rtl'}],[{'size':['small',false,'large','huge']}],[{'header':[1,2,3,4,5,6,false]}],[{'color':[]},{'background':[]}],[{'font':[]}],[{'align':[]}],['clean']]},placeholder:'请输入文章内容...'}</script>而原版项目中的ArticleManage.vue使用的是普通文本框:
<el-form-itemlabel="文章内容"prop="content"><el-inputv-model="articleModel.content"type="textarea":rows="4"placeholder="请输入文章内容"maxlength="500"show-word-limit/></el-form-item>5. 文章分类管理功能的增强
优化版项目中的ArticleCategory.vue实现了删除确认提示:
//删除分类import{ElMessageBox}from'element-plus'constdeleteCategory=(row)=>{ ElMessageBox.confirm('您确认要删除此分类吗?','温馨提示',{confirmButtonText:'确认',cancelButtonText:'取消',type:'warning',}).then(async()=>{//调用接口let result =awaitarticleCategoryDeleteService(row.id); ElMessage.success(result.msg ? result.msg :'删除成功')//调用获取所有分类的函数articleCategoryList();}).catch(()=>{ ElMessage.info('已取消删除')})}而原版项目中的ArticleCategory.vue直接删除:
//删除分类 const deleteCategory = (row) => { //调用接口 let result = await articleCategoryDeleteService(row.id); ElMessage.success(result.msg ? result.msg : '删除成功') //调用获取所有分类的函数 articleCategoryList(); } 新增功能总结
- 密码修改功能:完整实现了用户密码修改功能,包含原密码验证、新密码确认等功能
- 批量删除文章:在文章管理中实现了批量选择和删除功能
- 富文本编辑器:在文章编辑中集成了VueQuill富文本编辑器
- 删除确认提示:在删除操作前添加了确认对话框
- 文件上传优化:使用FormData进行文件上传,符合标准实践
- 更完善的表单验证:增加了更全面的表单验证规则
结论
经过优化以后,优化版项目在功能完整性、用户体验和代码质量方面都有显著提升。特别是新增的批量删除功能、富文本编辑器集成和更完善的表单验证,使系统更加实用和完善。