买花网站代码,买的虚拟主机怎么做网站,石家庄做网站最好的公司有哪些,互联网加项目策划书目录
一、环境准备
1、创建Vue工程
2、安装依赖
2.1 安装项目所需要的vue依赖
2.2 安装element-plus依赖
2.2.1 安装
2.2.2 项目导入element-plus
2.3 安装axios依赖
2.4 安装sass依赖
3、目录调整
3.1 删除部分默认目录下文件
3.1.1 src/components下自动生成的…目录
一、环境准备
1、创建Vue工程
2、安装依赖
2.1 安装项目所需要的vue依赖
2.2 安装element-plus依赖
2.2.1 安装
2.2.2 项目导入element-plus
2.3 安装axios依赖
2.4 安装sass依赖
3、目录调整
3.1 删除部分默认目录下文件
3.1.1 src/components下自动生成的内容(删除文件夹下全部内容)
3.1.2 src/assets 下自动生成的内容(删除文件夹下全部内容)
3.2 api、utils、views目录处理
3.2.1 src下新建目录api、utils、views 3.2.2 utils下新增request.js文件
3.3 将资料中的静态资源拷贝到assets目录下
3.4 修改app.vue生成的内容
4、启动项目验证
二、注册功能
1、页面搭建
1.1 views下新建Login.vue
1.2 App.vue导入Login.vue
1.3 查看结果
2、为注册页面绑定数据与事件
2.1 定义数据模型Login.vue 2.2 注册页面Login.vue表单校验
2.3 验证
3、注册页面后台接口调用
3.1 启动后端服务
3.2 api目录下新建user.js文件
3.3 Login.vue页面完成调用
4、跨域问题解决
4.1 request.js
4.2 vite.config.js
5、测试验证
三、登录功能
1、登录Login.vue绑定数据
2、 登录Login.vue数据校验
3、登录页面后端接口调用
3.1 user.js提供登录调用接口
3.2 Login.vue调用后端接口
4、测试验证
5、优化登录表单与注册表单数据显示问题Login.vue
四、优化axios响应拦截器与alert
1、axios响应拦截器
2.1 request.js
2.2 优化Login.vue
2、使用element-plus优化alert
2.1 request.js引用ElMessage 组件处理
2.2 Login.vue引用ElMessage 组件处理
五、主页面布局
1、Layout页面
1.1 views下新增Layout.vue 1.2 App.vue导入Layout.vue
2、路由 2.1 安装vue-router
2.2 创建路由器index.js
2.3 在main.js使用vue-router
2.4 App.vue中声明router-view标签
2.5 Login.vue优化登录成功跳转
2.6 测试验证
3、子路由
3.1 views下创建vue文件
3.1.1 ArticleCategory.vue
3.1.2 ArticleManage.vue
3.1.3 UserAvatar.vue
3.1.4 UserInfo.vue
3.1.5 UserResetPassword.vue
3.2 index.js
3.3 Layout.vue声明router-view标签
3.4 测试验证
六、文章分类
1、列表查询
1.1 article.js
1.2 ArticleCategory.vue
2、Pinia状态管理库
2.1 安装pinia
2.2 main.js导入pinia
2.3 定义store
2.4 使用Store
2.5 测试验证 3、 使用axios请求拦截器解决token繁琐问题
编辑 3.1 request.js添加请求拦截器
3.2 article.js移除之前添加的请求头
4、pinia-persistedstate-plugin持久化插件
4.1 安装
4.2 pinia中使用persist插件
4.3 在创建定义状态是配置持久化
5、未登录统一处理
6、添加文章分类
6.1 ArticleCategory.vue
6.2 在article.js中提供添加分类的函数
6.3 测试验证
7、修改文章分类
7.1 修改分类弹窗页面
7.1.1 弹窗标题显示 ArticleCategory.vue
7.1.2 在弹窗上绑定标题 ArticleCategory.vue
7.1.3 为添加分类按钮绑定事件 ArticleCategory.vue
7.1.4 为修改分类按钮绑定事件 ArticleCategory.vue
7.2 修改数据回显 ArticleCategory.vue
7.3 修改文章分类接口调用
7.3.1 article.js中提供修改分类的函数
7.3.2 修改确定按钮的绑定事件 ArticleCategory.vue
7.3.3 调用接口完成修改的函数 ArticleCategory.vue
7.3.4 优化添加时数据回显
8、删除文章分类 8.1 删除接口
8.2 删除确认框 8.3 为删除按钮绑定事件
8.4 调用删除接口
8.5 测试验证
七、文章列表
1、 文章列表查询 1.1 文章列表页面组件ArticleManage.vue
1.2 解决分页控件英文显示问题main.js
1.3 文章分类数据回显ArticleMange.vue
1.4 文章列表接口调用
1.4.1 article.js中提供获取文章列表数据的函数
1.4.2 ArticleManage.vue中调用接口获取数据
1.4.3 当分页条的当前页和每页条数发生变化重新发送请求获取数据
2 、搜索和重置
3、添加文章
3.1 添加文章抽屉组件
3.2 为添加文章按钮添加单击事件展示抽屉
3.3 富文本编辑器
3.3.1 安装
3.3.2 导入组件和样式
3.3.3 页面使用quill组件
3.3.4 样式美化
3.4 文章封面图片上传
3.5 添加文章接口调用
3.5.1 article.js中提供添加文章函数
3.5.2 为已发布和草稿按钮绑定事件
3.5.3 ArticleManage.vue中提供addArticle函数完成添加文章接口的调用
4、编辑文章
4.1 修改文章抽屉页面
4.1.1 抽屉标题显示 定义标题
4.1.2 在抽屉上绑定标题
4.1.3 为添加文章按钮绑定事件
4.1.4 为修改文章按钮绑定事件
4.2 数据回显
4.2.1 通过插槽的方式得到被点击按钮所在行的数据
4.2.2 回显函数
4.3 修改文章接口调用
4.3.1 article.js中提供修改文章的函数
4.3.2 修改发布与草稿按钮的绑定事件
4.3.3 调用接口完成修改的函数
5、删除文章
5.1 接口调用
5.2 为删除按钮绑定事件
5.3 添加删除弹窗当用户点击确认后调用接口删除分类
八、顶部导航栏信息展示
1、 user.js中提供获取个人信息的函数
2、 src/stores/userInfo.js中定义个人中心状态
3、 Layout.vue中获取个人信息并存储到pinia中
4、Layout.vue的顶部导航栏中展示昵称和头像
九 、页面右上角下拉菜单el-dropdown中功能实现
1、路由实现
2、 退出登录实现
十、基本资料修改
1、 基本资料页面组件UserInfo.vue
2、 表单数据回显
3、 接口调用
十一、修改头像
1、 修改头像页面组件UserAvatar.vue
2、 头像回显
3、 头像上传
4、 上传头像接口调用
十二、 重置密码
1、user.js 重置密码 2、UserResetPassword.vue 前言本项目的终结篇是一名vue3完成本次课程的前端页面。 一、环境准备
1、创建Vue工程
npm init vuelatest# 项目名称spring3vue3project 2、安装依赖
进入到创建项目目录 2.1 安装项目所需要的vue依赖
# 项目所需要的vue依赖
npm install 2.2 安装element-plus依赖
2.2.1 安装
# 安装element-plus依赖
npm install element-plus --save 2.2.2 项目导入element-plus
打开项目
code .
修改src/main.js
import ./assets/main.scssimport { createApp } from vue
import ElementPlus from element-plus
import element-plus/dist/index.cssimport App from ./App.vueconst app createApp(App)
app.use(ElementPlus)app.mount(#app)2.3 安装axios依赖 npm install axios 2.4 安装sass依赖
# 安装sass依赖 好像是关于css的
npm install sass -D 3、目录调整
直接进到项目文件夹下操作
3.1 删除部分默认目录下文件
3.1.1 src/components下自动生成的内容(删除文件夹下全部内容) 3.1.2 src/assets 下自动生成的内容(删除文件夹下全部内容) 3.2 api、utils、views目录处理
3.2.1 src下新建目录api、utils、views 3.2.2 utils下新增request.js文件
// 定制请求实例//导入axios
import axios from axios; //定义一个变量记录公共的前缀baseURL
const baseURL http://localhost:8080
const instance axios.create({baseURL})// 添加响应拦截器
instance.interceptors.response.use(result{return result.data;},err{alert(服务异常)return Promise.reject(err);//异步的状态转化成失败的状态}
)export default instance; 3.3 将资料中的静态资源拷贝到assets目录下
如需关注发私信 3.4 修改app.vue生成的内容
删除app.vue中自动生成的内容
script setup/scripttemplate
春天的菠菜/templatestyle scoped/style4、启动项目验证
npm run dev
二、注册功能 1、页面搭建
1.1 views下新建Login.vue
script setup
import { User, Lock } from element-plus/icons-vue
import { ref } from vue
//控制注册与登录表单的显示 默认显示注册
const isRegister ref(false)
/scripttemplateel-row classlogin-pageel-col :span12 classbg/el-colel-col :span6 :offset3 classform!-- 注册表单 --el-form refform sizelarge autocompleteoff v-ifisRegisterel-form-itemh1注册/h1/el-form-itemel-form-itemel-input :prefix-iconUser placeholder请输入用户名/el-input/el-form-itemel-form-itemel-input :prefix-iconLock typepassword placeholder请输入密码/el-input/el-form-itemel-form-itemel-input :prefix-iconLock typepassword placeholder请输入再次密码/el-input/el-form-item!-- 注册按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space注册/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister false← 返回/el-link/el-form-item/el-form!-- 登录表单 --el-form refform sizelarge autocompleteoff v-elseel-form-itemh1登录/h1/el-form-itemel-form-itemel-input :prefix-iconUser placeholder请输入用户名/el-input/el-form-itemel-form-itemel-input namepassword :prefix-iconLock typepassword placeholder请输入密码/el-input/el-form-itemel-form-item classflexdiv classflexel-checkbox记住我/el-checkboxel-link typeprimary :underlinefalse忘记密码/el-link/div/el-form-item!-- 登录按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space登录/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister true注册 →/el-link/el-form-item/el-form/el-col/el-row
/templatestyle langscss scoped
/* 样式 */
.login-page {height: 100vh;background-color: #fff;.bg {background: url(/assets/logo2.png) no-repeat 60% center / 240px auto,url(/assets/login_bg.jpg) no-repeat center / cover;border-radius: 0 20px 20px 0;}.form {display: flex;flex-direction: column;justify-content: center;user-select: none;.title {margin: 0 auto;}.button {width: 100%;}.flex {width: 100%;display: flex;justify-content: space-between;}}
}
/style
1.2 App.vue导入Login.vue
script setup
import LoginVue from /views/Login.vue/scripttemplate
LoginVue/
/templatestyle scoped/style1.3 查看结果 2、为注册页面绑定数据与事件
2.1 定义数据模型Login.vue
script setup
import { User, Lock } from element-plus/icons-vue
import { ref } from vue
//控制注册与登录表单的显示 默认显示注册
const isRegister ref(false)//定义数据模型const registerData ref({username:,password:,rePassword:
})/scripttemplateel-row classlogin-pageel-col :span12 classbg/el-colel-col :span6 :offset3 classform!-- 注册表单 --el-form refform sizelarge autocompleteoff v-ifisRegister :modelregisterDatael-form-itemh1注册/h1/el-form-itemel-form-itemel-input :prefix-iconUser placeholder请输入用户名 v-modelregisterData.username/el-input/el-form-itemel-form-itemel-input :prefix-iconLock typepassword placeholder请输入密码 v-modelregisterData.password/el-input/el-form-itemel-form-itemel-input :prefix-iconLock typepassword placeholder请输入再次密码 v-modelregisterData.rePassword/el-input/el-form-item!-- 注册按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space注册/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister false← 返回/el-link/el-form-item/el-form!-- 登录表单 --el-form refform sizelarge autocompleteoff v-elseel-form-itemh1登录/h1/el-form-itemel-form-itemel-input :prefix-iconUser placeholder请输入用户名/el-input/el-form-itemel-form-itemel-input namepassword :prefix-iconLock typepassword placeholder请输入密码/el-input/el-form-itemel-form-item classflexdiv classflexel-checkbox记住我/el-checkboxel-link typeprimary :underlinefalse忘记密码/el-link/div/el-form-item!-- 登录按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space登录/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister true注册 →/el-link/el-form-item/el-form/el-col/el-row
/templatestyle langscss scoped
/* 样式 */
.login-page {height: 100vh;background-color: #fff;.bg {background: url(/assets/logo2.png) no-repeat 60% center / 240px auto,url(/assets/login_bg.jpg) no-repeat center / cover;border-radius: 0 20px 20px 0;}.form {display: flex;flex-direction: column;justify-content: center;user-select: none;.title {margin: 0 auto;}.button {width: 100%;}.flex {width: 100%;display: flex;justify-content: space-between;}}
}
/style 2.2 注册页面Login.vue表单校验 表单校验可看官网
script setup
import { User, Lock } from element-plus/icons-vue
import { ref } from vue
//控制注册与登录表单的显示 默认显示注册
const isRegister ref(false)//2.1定义数据模型const registerData ref({username:,password:,rePassword:
})//2.2自定义rePassword需要自定义校验规则校验规则的函数,三个参rule 规则,value 值,callback 回调函数
const checkRePassword (rule,value,callback){if(value ){callback(new Error(请再次确认密码))}else if(value ! registerData.value.password){callback(new Error(请确保两次密码输入一致性))}else{callback() //校验通过}
}//2.2 定义表单校验规则,rePassword需要自定义校验规则
const rules {username:[{required:true,message:请输入用户名,trigger:blur},{min:5,max:16,message:长度为5-16位非空字符,trigger:blur}],password:[{required:true,message:请输入密码,trigger:blur},{min:5,max:16,message:长度为5-16位非空字符,trigger:blur}],rePassword:[{validator:checkRePassword,trigger:blur}]
}/scripttemplateel-row classlogin-pageel-col :span12 classbg/el-colel-col :span6 :offset3 classform!-- 注册表单 --el-form refform sizelarge autocompleteoff v-ifisRegister :modelregisterData :rulesrules !-- 2.1 :modelregisterData表单声明属性 -- !-- 2.2 :rulesrules绑定校验 --el-form-itemh1注册/h1/el-form-itemel-form-item propusername !-- 2.2 propusername绑定校验 --el-input :prefix-iconUser placeholder请输入用户名 v-modelregisterData.username/el-input !-- 2.1 v-modelregisterData.username绑定属性 --/el-form-itemel-form-item proppassword !-- 2.2 proppassword绑定校验 --el-input :prefix-iconLock typepassword placeholder请输入密码 v-modelregisterData.password/el-input !-- 2.1 v-modelregisterData.password绑定属性 --/el-form-itemel-form-item proprePassword !-- 2.2 proprePassword绑定校验 --el-input :prefix-iconLock typepassword placeholder请输入再次密码 v-modelregisterData.rePassword/el-input !-- 2.1 v-modelregisterData.rePassword绑定属性 --/el-form-item!-- 注册按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space注册/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister false← 返回/el-link/el-form-item/el-form!-- 登录表单 --el-form refform sizelarge autocompleteoff v-elseel-form-itemh1登录/h1/el-form-itemel-form-itemel-input :prefix-iconUser placeholder请输入用户名/el-input/el-form-itemel-form-itemel-input namepassword :prefix-iconLock typepassword placeholder请输入密码/el-input/el-form-itemel-form-item classflexdiv classflexel-checkbox记住我/el-checkboxel-link typeprimary :underlinefalse忘记密码/el-link/div/el-form-item!-- 登录按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space登录/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister true注册 →/el-link/el-form-item/el-form/el-col/el-row
/templatestyle langscss scoped
/* 样式 */
.login-page {height: 100vh;background-color: #fff;.bg {background: url(/assets/logo2.png) no-repeat 60% center / 240px auto,url(/assets/login_bg.jpg) no-repeat center / cover;border-radius: 0 20px 20px 0;}.form {display: flex;flex-direction: column;justify-content: center;user-select: none;.title {margin: 0 auto;}.button {width: 100%;}.flex {width: 100%;display: flex;justify-content: space-between;}}
}
/style
2.3 验证 3、注册页面后台接口调用 3.1 启动后端服务
启动前面章节后端服务以及redis
3.2 api目录下新建user.js文件
//导入request.js请求工具
import request from /utils/request.js// 提供调用注册接口的函数
export const userRegisterService (registerData) {//借助于URLSearchParam完成参数传递const params new URLSearchParams();for(let key in registerData){params.append(key,registerData[key]);}return request.post(/user/register,params);}
3.3 Login.vue页面完成调用
script setup
import { User, Lock } from element-plus/icons-vue
import { ref } from vue
// 3.3 导入注册的接口
import { userRegisterService} from /api/user.js//控制注册与登录表单的显示 默认显示注册
const isRegister ref(false)//2.1定义数据模型const registerData ref({username:,password:,rePassword:
})//2.2自定义rePassword需要自定义校验规则校验规则的函数,三个参rule 规则,value 值,callback 回调函数
const checkRePassword (rule,value,callback){if(value ){callback(new Error(请再次确认密码))}else if(value ! registerData.value.password){callback(new Error(请确保两次密码输入一致性))}else{callback() //校验通过}
}//2.2 定义表单校验规则,rePassword需要自定义校验规则
const rules {username:[{required:true,message:请输入用户名,trigger:blur},{min:5,max:16,message:长度为5-16位非空字符,trigger:blur}],password:[{required:true,message:请输入密码,trigger:blur},{min:5,max:16,message:长度为5-16位非空字符,trigger:blur}],rePassword:[{validator:checkRePassword,trigger:blur}]
}//3.3 调用后台接口,完成注册
const register async(){// registerData 是一个响应式对象如果要获取值需要.valuelet result await userRegisterService(registerData.value);if(result.code 1){//注册成功alert(result.msg?result.msg : 注册成功)}else{// 注册失败alert(注册失败)}
}/scripttemplateel-row classlogin-pageel-col :span12 classbg/el-colel-col :span6 :offset3 classform!-- 注册表单 --el-form refform sizelarge autocompleteoff v-ifisRegister :modelregisterData :rulesrules !-- 2.1 :modelregisterData表单声明属性 -- !-- 2.2 :rulesrules绑定校验 --el-form-itemh1注册/h1/el-form-itemel-form-item propusername !-- 2.2 propusername绑定校验 --el-input :prefix-iconUser placeholder请输入用户名 v-modelregisterData.username/el-input !-- 2.1 v-modelregisterData.username绑定属性 --/el-form-itemel-form-item proppassword !-- 2.2 proppassword绑定校验 --el-input :prefix-iconLock typepassword placeholder请输入密码 v-modelregisterData.password/el-input !-- 2.1 v-modelregisterData.password绑定属性 --/el-form-itemel-form-item proprePassword !-- 2.2 proprePassword绑定校验 --el-input :prefix-iconLock typepassword placeholder请输入再次密码 v-modelregisterData.rePassword/el-input !-- 2.1 v-modelregisterData.rePassword绑定属性 --/el-form-item!-- 注册按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space clickregister !-- 3.3 clickregister 绑定注册单击事件 --注册/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister false← 返回/el-link/el-form-item/el-form!-- 登录表单 --el-form refform sizelarge autocompleteoff v-elseel-form-itemh1登录/h1/el-form-itemel-form-itemel-input :prefix-iconUser placeholder请输入用户名/el-input/el-form-itemel-form-itemel-input namepassword :prefix-iconLock typepassword placeholder请输入密码/el-input/el-form-itemel-form-item classflexdiv classflexel-checkbox记住我/el-checkboxel-link typeprimary :underlinefalse忘记密码/el-link/div/el-form-item!-- 登录按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space登录/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister true注册 →/el-link/el-form-item/el-form/el-col/el-row
/templatestyle langscss scoped
/* 样式 */
.login-page {height: 100vh;background-color: #fff;.bg {background: url(/assets/logo2.png) no-repeat 60% center / 240px auto,url(/assets/login_bg.jpg) no-repeat center / cover;border-radius: 0 20px 20px 0;}.form {display: flex;flex-direction: column;justify-content: center;user-select: none;.title {margin: 0 auto;}.button {width: 100%;}.flex {width: 100%;display: flex;justify-content: space-between;}}
}
/style
验证存在跨域问题 4、跨域问题解决 4.1 request.js
// 定制请求实例//导入axios npm install axios
import axios from axios;//定义一个变量记录公共的前缀baseURL
// const baseURL http://localhost:8080
const baseURL /api // 注释上面代码解决跨域问题
const instance axios.create({baseURL})// 添加响应拦截器
instance.interceptors.response.use(result{return result.data;},err{alert(服务异常)return Promise.reject(err);//异步的状态转化成失败的状态}
)export default instance;
4.2 vite.config.js
import { fileURLToPath, URL } from node:urlimport { defineConfig } from vite
import vue from vitejs/plugin-vue// https://vitejs.dev/config/
export default defineConfig({plugins: [vue(),],resolve: {alias: {: fileURLToPath(new URL(./src, import.meta.url))}},// server 部分都是为了解决跨域问题server:{proxy:{/api:{//获取路径中包含了/api的请求target:http://localhost:8080,//后台服务所在的源changeOrigin:true,//修改源rewrite:(path)path.replace(/^\/api/,)///api替换为}}}
}) 5、测试验证 三、登录功能 1、登录Login.vue绑定数据
script setup
import { User, Lock } from element-plus/icons-vue
import { ref } from vue
//二 注册 3.3 导入注册的接口
import { userRegisterService} from /api/user.js//控制注册与登录表单的显示 默认显示注册
const isRegister ref(false)//二 注册 2.1定义数据模型const registerData ref({username:,password:,rePassword:
})//二 注册 2.2自定义rePassword需要自定义校验规则校验规则的函数,三个参rule 规则,value 值,callback 回调函数
const checkRePassword (rule,value,callback){if(value ){callback(new Error(请再次确认密码))}else if(value ! registerData.value.password){callback(new Error(请确保两次密码输入一致性))}else{callback() //校验通过}
}//二 注册 2.2 定义表单校验规则,rePassword需要自定义校验规则
const rules {username:[{required:true,message:请输入用户名,trigger:blur},{min:5,max:16,message:长度为5-16位非空字符,trigger:blur}],password:[{required:true,message:请输入密码,trigger:blur},{min:5,max:16,message:长度为5-16位非空字符,trigger:blur}],rePassword:[{validator:checkRePassword,trigger:blur}]
}// 二 注册 3.3 调用后台接口,完成注册
const register async(){// registerData 是一个响应式对象如果要获取值需要.valuelet result await userRegisterService(registerData.value);if(result.code 1){//注册成功alert(result.msg?result.msg : 注册成功)}else{// 注册失败alert(注册失败)}
}// 三 登录 1 绑定数据 复用注册表单的数据模型/scripttemplateel-row classlogin-pageel-col :span12 classbg/el-colel-col :span6 :offset3 classform!-- 注册表单 --el-form refform sizelarge autocompleteoff v-ifisRegister :modelregisterData :rulesrules !-- 二 注册2.1 :modelregisterData表单声明属性 -- !-- 二 注册2.2 :rulesrules绑定校验 --el-form-itemh1注册/h1/el-form-itemel-form-item propusername !--二 注册 2.2 propusername绑定校验 --el-input :prefix-iconUser placeholder请输入用户名 v-modelregisterData.username/el-input !--二 注册 2.1 v-modelregisterData.username绑定属性 --/el-form-itemel-form-item proppassword !-- 二 注册2.2 proppassword绑定校验 --el-input :prefix-iconLock typepassword placeholder请输入密码 v-modelregisterData.password/el-input !-- 二 注册2.1 v-modelregisterData.password绑定属性 --/el-form-itemel-form-item proprePassword !-- 2.2 proprePassword绑定校验 --el-input :prefix-iconLock typepassword placeholder请输入再次密码 v-modelregisterData.rePassword/el-input !-- 二 注册2.1 v-modelregisterData.rePassword绑定属性 --/el-form-item!-- 注册按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space clickregister !-- 二 注册3.3 clickregister 绑定注册单击事件 --注册/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister false← 返回/el-link/el-form-item/el-form!-- 登录表单 --el-form refform sizelarge autocompleteoff v-else modelregisterDate !-- 三 登录 3.1 :modelregisterData表单声明属性复用注册 --el-form-itemh1登录/h1/el-form-itemel-form-itemel-input :prefix-iconUser placeholder请输入用户名 v-modelregisterData.username/el-input !--三 登录 3.1 v-modelregisterData.username绑定属性 --/el-form-itemel-form-itemel-input namepassword :prefix-iconLock typepassword placeholder请输入密码 v-modelregisterData.password/el-input !--三 登录 3.1 v-modelregisterData.password绑定属性 --/el-form-item el-form-item classflexdiv classflexel-checkbox记住我/el-checkboxel-link typeprimary :underlinefalse忘记密码/el-link/div/el-form-item!-- 登录按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space登录/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister true注册 →/el-link/el-form-item/el-form/el-col/el-row
/templatestyle langscss scoped
/* 样式 */
.login-page {height: 100vh;background-color: #fff;.bg {background: url(/assets/logo2.png) no-repeat 60% center / 240px auto,url(/assets/login_bg.jpg) no-repeat center / cover;border-radius: 0 20px 20px 0;}.form {display: flex;flex-direction: column;justify-content: center;user-select: none;.title {margin: 0 auto;}.button {width: 100%;}.flex {width: 100%;display: flex;justify-content: space-between;}}
}
/style
2、 登录Login.vue数据校验
script setup
import { User, Lock } from element-plus/icons-vue
import { ref } from vue
//二 注册 3.3 导入注册的接口
import { userRegisterService} from /api/user.js//控制注册与登录表单的显示 默认显示注册
const isRegister ref(false)//二 注册 2.1定义数据模型const registerData ref({username:,password:,rePassword:
})//二 注册 2.2自定义rePassword需要自定义校验规则校验规则的函数,三个参rule 规则,value 值,callback 回调函数
const checkRePassword (rule,value,callback){if(value ){callback(new Error(请再次确认密码))}else if(value ! registerData.value.password){callback(new Error(请确保两次密码输入一致性))}else{callback() //校验通过}
}//二 注册 2.2 定义表单校验规则,rePassword需要自定义校验规则
const rules {username:[{required:true,message:请输入用户名,trigger:blur},{min:5,max:16,message:长度为5-16位非空字符,trigger:blur}],password:[{required:true,message:请输入密码,trigger:blur},{min:5,max:16,message:长度为5-16位非空字符,trigger:blur}],rePassword:[{validator:checkRePassword,trigger:blur}]
}// 二 注册 3.3 调用后台接口,完成注册
const register async(){// registerData 是一个响应式对象如果要获取值需要.valuelet result await userRegisterService(registerData.value);if(result.code 1){//注册成功alert(result.msg?result.msg : 注册成功)}else{// 注册失败alert(注册失败)}
}// 三 登录 1 绑定数据 复用注册表单的数据模型
// 三 登录 2 数据校验 复用注册表单的数据校验/scripttemplateel-row classlogin-pageel-col :span12 classbg/el-colel-col :span6 :offset3 classform!-- 注册表单 --el-form refform sizelarge autocompleteoff v-ifisRegister :modelregisterData :rulesrules !-- 二 注册2.1 :modelregisterData表单声明属性 -- !-- 二 注册2.2 :rulesrules绑定校验 --el-form-itemh1注册/h1/el-form-itemel-form-item propusername !--二 注册 2.2 propusername绑定校验 --el-input :prefix-iconUser placeholder请输入用户名 v-modelregisterData.username/el-input !--二 注册 2.1 v-modelregisterData.username绑定属性 --/el-form-itemel-form-item proppassword !-- 二 注册2.2 proppassword绑定校验 --el-input :prefix-iconLock typepassword placeholder请输入密码 v-modelregisterData.password/el-input !-- 二 注册2.1 v-modelregisterData.password绑定属性 --/el-form-itemel-form-item proprePassword !-- 2.2 proprePassword绑定校验 --el-input :prefix-iconLock typepassword placeholder请输入再次密码 v-modelregisterData.rePassword/el-input !-- 二 注册2.1 v-modelregisterData.rePassword绑定属性 --/el-form-item!-- 注册按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space clickregister !-- 二 注册3.3 clickregister 绑定注册单击事件 --注册/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister false← 返回/el-link/el-form-item/el-form!-- 登录表单 --el-form refform sizelarge autocompleteoff v-else modelregisterDate rulesrules !-- 三 登录 3.1 :modelregisterData表单声明属性复用注册 -- !-- 三 登录 3.2 :rulesrules绑定校验 --el-form-itemh1登录/h1/el-form-itemel-form-item propusername !--三 登录 3.2 propusername绑定校验 --el-input :prefix-iconUser placeholder请输入用户名 v-modelregisterData.username/el-input !--三 登录 3.1 v-modelregisterData.username绑定属性 --/el-form-itemel-form-item propusername !--三 登录 3.2 propusername绑定校验 --el-input namepassword :prefix-iconLock typepassword placeholder请输入密码 v-modelregisterData.password/el-input !--三 登录 3.1 v-modelregisterData.password绑定属性 --/el-form-item el-form-item classflexdiv classflexel-checkbox记住我/el-checkboxel-link typeprimary :underlinefalse忘记密码/el-link/div/el-form-item!-- 登录按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space登录/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister true注册 →/el-link/el-form-item/el-form/el-col/el-row
/templatestyle langscss scoped
/* 样式 */
.login-page {height: 100vh;background-color: #fff;.bg {background: url(/assets/logo2.png) no-repeat 60% center / 240px auto,url(/assets/login_bg.jpg) no-repeat center / cover;border-radius: 0 20px 20px 0;}.form {display: flex;flex-direction: column;justify-content: center;user-select: none;.title {margin: 0 auto;}.button {width: 100%;}.flex {width: 100%;display: flex;justify-content: space-between;}}
}
/style
3、登录页面后端接口调用 3.1 user.js提供登录调用接口
//导入request.js请求工具
import request from /utils/request.js// 二 注册函数 提供调用注册接口的函数
export const userRegisterService (registerData) {//借助于URLSearchParam完成参数传递const params new URLSearchParams();for(let key in registerData){params.append(key,registerData[key]);}return request.post(/user/register,params);}// 三 登录函数 提供调用登录接口的函数
export const userLoginService (loginData){//借助于URLSearchParam完成参数传递const params new URLSearchParams();for(let key in loginData){params.append(key,loginData[key]);}return request.post(/user/login,params);}3.2 Login.vue调用后端接口 script setup
import { User, Lock } from element-plus/icons-vue
import { ref } from vue
//二 注册 3.3 导入注册的接口 //三 登录 3.3 导入登录的接口
import { userRegisterService,userLoginService} from /api/user.js//控制注册与登录表单的显示 默认显示注册
const isRegister ref(false)//二 注册 2.1定义数据模型const registerData ref({username:,password:,rePassword:
})//二 注册 2.2自定义rePassword需要自定义校验规则校验规则的函数,三个参rule 规则,value 值,callback 回调函数
const checkRePassword (rule,value,callback){if(value ){callback(new Error(请再次确认密码))}else if(value ! registerData.value.password){callback(new Error(请确保两次密码输入一致性))}else{callback() //校验通过}
}//二 注册 2.2 定义表单校验规则,rePassword需要自定义校验规则
const rules {username:[{required:true,message:请输入用户名,trigger:blur},{min:5,max:16,message:长度为5-16位非空字符,trigger:blur}],password:[{required:true,message:请输入密码,trigger:blur},{min:5,max:16,message:长度为5-16位非空字符,trigger:blur}],rePassword:[{validator:checkRePassword,trigger:blur}]
}// 二 注册 3.3 调用后台接口,完成注册
const register async(){// registerData 是一个响应式对象如果要获取值需要.valuelet result await userRegisterService(registerData.value);if(result.code 1){//注册成功alert(result.msg?result.msg : 注册成功)}else{// 注册失败alert(注册失败)}
}// 三 登录 1 绑定数据 复用注册表单的数据模型
// 三 登录 2 数据校验 复用注册表单的数据校验
// 三 登录 3 登录函数
const login async(){ //调用接口完成登录let result await userLoginService(registerData.value);if(result.code 1){//登录成功alert(result.msg?result.msg : 登录成功)}else{// 登录失败alert(登录失败)}}/scripttemplateel-row classlogin-pageel-col :span12 classbg/el-colel-col :span6 :offset3 classform!-- 注册表单 --el-form refform sizelarge autocompleteoff v-ifisRegister :modelregisterData :rulesrules !-- 二 注册2.1 :modelregisterData表单声明属性 -- !-- 二 注册2.2 :rulesrules绑定校验 --el-form-itemh1注册/h1/el-form-itemel-form-item propusername !--二 注册 2.2 propusername绑定校验 --el-input :prefix-iconUser placeholder请输入用户名 v-modelregisterData.username/el-input !--二 注册 2.1 v-modelregisterData.username绑定属性 --/el-form-itemel-form-item proppassword !-- 二 注册2.2 proppassword绑定校验 --el-input :prefix-iconLock typepassword placeholder请输入密码 v-modelregisterData.password/el-input !-- 二 注册2.1 v-modelregisterData.password绑定属性 --/el-form-itemel-form-item proprePassword !-- 2.2 proprePassword绑定校验 --el-input :prefix-iconLock typepassword placeholder请输入再次密码 v-modelregisterData.rePassword/el-input !-- 二 注册2.1 v-modelregisterData.rePassword绑定属性 --/el-form-item!-- 注册按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space clickregister !-- 二 注册3.3 clickregister 绑定注册单击事件 --注册/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister false← 返回/el-link/el-form-item/el-form!-- 登录表单 --el-form refform sizelarge autocompleteoff v-else modelregisterDate rulesrules !-- 三 登录 3.1 :modelregisterData表单声明属性复用注册 -- !-- 三 登录 3.2 :rulesrules绑定校验 --el-form-itemh1登录/h1/el-form-itemel-form-item propusername !--三 登录 3.2 propusername绑定校验 --el-input :prefix-iconUser placeholder请输入用户名 v-modelregisterData.username/el-input !--三 登录 3.1 v-modelregisterData.username绑定属性 --/el-form-itemel-form-item propusername !--三 登录 3.2 propusername绑定校验 --el-input namepassword :prefix-iconLock typepassword placeholder请输入密码 v-modelregisterData.password/el-input !--三 登录 3.1 v-modelregisterData.password绑定属性 --/el-form-item el-form-item classflexdiv classflexel-checkbox记住我/el-checkboxel-link typeprimary :underlinefalse忘记密码/el-link/div/el-form-item!-- 登录按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space clicklogin登录/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister true注册 →/el-link/el-form-item/el-form/el-col/el-row
/templatestyle langscss scoped
/* 样式 */
.login-page {height: 100vh;background-color: #fff;.bg {background: url(/assets/logo2.png) no-repeat 60% center / 240px auto,url(/assets/login_bg.jpg) no-repeat center / cover;border-radius: 0 20px 20px 0;}.form {display: flex;flex-direction: column;justify-content: center;user-select: none;.title {margin: 0 auto;}.button {width: 100%;}.flex {width: 100%;display: flex;justify-content: space-between;}}
}
/style
4、测试验证 5、优化登录表单与注册表单数据显示问题Login.vue
script setup
import { User, Lock } from element-plus/icons-vue
import { ref } from vue
//二 注册 3.3 导入注册的接口 //三 登录 3.3 导入登录的接口
import { userRegisterService,userLoginService} from /api/user.js//控制注册与登录表单的显示 默认显示注册
const isRegister ref(false)//二 注册 2.1定义数据模型const registerData ref({username:,password:,rePassword:
})//二 注册 2.2自定义rePassword需要自定义校验规则校验规则的函数,三个参rule 规则,value 值,callback 回调函数
const checkRePassword (rule,value,callback){if(value ){callback(new Error(请再次确认密码))}else if(value ! registerData.value.password){callback(new Error(请确保两次密码输入一致性))}else{callback() //校验通过}
}//二 注册 2.2 定义表单校验规则,rePassword需要自定义校验规则
const rules {username:[{required:true,message:请输入用户名,trigger:blur},{min:5,max:16,message:长度为5-16位非空字符,trigger:blur}],password:[{required:true,message:请输入密码,trigger:blur},{min:5,max:16,message:长度为5-16位非空字符,trigger:blur}],rePassword:[{validator:checkRePassword,trigger:blur}]
}// 二 注册 3.3 调用后台接口,完成注册
const register async(){// registerData 是一个响应式对象如果要获取值需要.valuelet result await userRegisterService(registerData.value);if(result.code 1){//注册成功alert(result.msg?result.msg : 注册成功)}else{// 注册失败alert(注册失败)}
}// 三 登录 1 绑定数据 复用注册表单的数据模型
// 三 登录 2 数据校验 复用注册表单的数据校验
// 三 登录 3 登录函数
const login async(){ //调用接口完成登录let result await userLoginService(registerData.value);if(result.code 1){//登录成功alert(result.msg?result.msg : 登录成功)}else{// 登录失败alert(登录失败)}}
// 三 登录 4 定义函数清空数据模型数据(将数据清空)
const clearRegisterDate (){registerData.value{username:,password:,rePassword:}}/scripttemplateel-row classlogin-pageel-col :span12 classbg/el-colel-col :span6 :offset3 classform!-- 注册表单 --el-form refform sizelarge autocompleteoff v-ifisRegister :modelregisterData :rulesrules !-- 二 注册2.1 :modelregisterData表单声明属性 -- !-- 二 注册2.2 :rulesrules绑定校验 --el-form-itemh1注册/h1/el-form-itemel-form-item propusername !--二 注册 2.2 propusername绑定校验 --el-input :prefix-iconUser placeholder请输入用户名 v-modelregisterData.username/el-input !--二 注册 2.1 v-modelregisterData.username绑定属性 --/el-form-itemel-form-item proppassword !-- 二 注册2.2 proppassword绑定校验 --el-input :prefix-iconLock typepassword placeholder请输入密码 v-modelregisterData.password/el-input !-- 二 注册2.1 v-modelregisterData.password绑定属性 --/el-form-itemel-form-item proprePassword !-- 2.2 proprePassword绑定校验 --el-input :prefix-iconLock typepassword placeholder请输入再次密码 v-modelregisterData.rePassword/el-input !-- 二 注册2.1 v-modelregisterData.rePassword绑定属性 --/el-form-item!-- 注册按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space clickregister !-- 二 注册3.3 clickregister 绑定注册单击事件 --注册/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister false;clearRegisterDate() !--三 登录 4 调用函数clearRegisterDate() 清空数据 --← 返回/el-link/el-form-item/el-form!-- 登录表单 --el-form refform sizelarge autocompleteoff v-else modelregisterDate rulesrules !-- 三 登录 3.1 :modelregisterData表单声明属性复用注册 -- !-- 三 登录 3.2 :rulesrules绑定校验 --el-form-itemh1登录/h1/el-form-itemel-form-item propusername !--三 登录 3.2 propusername绑定校验 --el-input :prefix-iconUser placeholder请输入用户名 v-modelregisterData.username/el-input !--三 登录 3.1 v-modelregisterData.username绑定属性 --/el-form-itemel-form-item propusername !--三 登录 3.2 propusername绑定校验 --el-input namepassword :prefix-iconLock typepassword placeholder请输入密码 v-modelregisterData.password/el-input !--三 登录 3.1 v-modelregisterData.password绑定属性 --/el-form-item el-form-item classflexdiv classflexel-checkbox记住我/el-checkboxel-link typeprimary :underlinefalse忘记密码/el-link/div/el-form-item!-- 登录按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space clicklogin登录/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister true;clearRegisterDate() !--三 登录 4 调用函数clearRegisterDate() 清空数据 --注册 →/el-link/el-form-item/el-form/el-col/el-row
/templatestyle langscss scoped
/* 样式 */
.login-page {height: 100vh;background-color: #fff;.bg {background: url(/assets/logo2.png) no-repeat 60% center / 240px auto,url(/assets/login_bg.jpg) no-repeat center / cover;border-radius: 0 20px 20px 0;}.form {display: flex;flex-direction: column;justify-content: center;user-select: none;.title {margin: 0 auto;}.button {width: 100%;}.flex {width: 100%;display: flex;justify-content: space-between;}}
}
/style
四、优化axios响应拦截器与alert
1、axios响应拦截器 2.1 request.js
上图右侧的文件名错误
// 定制请求实例//导入axios npm install axios
import axios from axios;//定义一个变量记录公共的前缀baseURL
// const baseURL http://localhost:8080
const baseURL /api // 注释上面代码解决跨域问题
const instance axios.create({baseURL})// 添加响应拦截器
instance.interceptors.response.use(result{// 判断业务状态码if(result.data.code 1){//成功正常返回数据return result.data;}// 操作失败alert(result.data.msg?result.data.msg : 服务异常)// 异步操作的状态转换为失败return Promise.reject(result.data)},err{alert(服务异常)return Promise.reject(err);//异步的状态转化成失败的状态}
)export default instance;
2.2 优化Login.vue
script setup
import { User, Lock } from element-plus/icons-vue
import { ref } from vue
//二 注册 3.3 导入注册的接口 //三 登录 3.3 导入登录的接口
import { userRegisterService,userLoginService} from /api/user.js//控制注册与登录表单的显示 默认显示注册
const isRegister ref(false)//二 注册 2.1定义数据模型const registerData ref({username:,password:,rePassword:
})//二 注册 2.2自定义rePassword需要自定义校验规则校验规则的函数,三个参rule 规则,value 值,callback 回调函数
const checkRePassword (rule,value,callback){if(value ){callback(new Error(请再次确认密码))}else if(value ! registerData.value.password){callback(new Error(请确保两次密码输入一致性))}else{callback() //校验通过}
}//二 注册 2.2 定义表单校验规则,rePassword需要自定义校验规则
const rules {username:[{required:true,message:请输入用户名,trigger:blur},{min:5,max:16,message:长度为5-16位非空字符,trigger:blur}],password:[{required:true,message:请输入密码,trigger:blur},{min:5,max:16,message:长度为5-16位非空字符,trigger:blur}],rePassword:[{validator:checkRePassword,trigger:blur}]
}// 二 注册 3.3 调用后台接口,完成注册
const register async(){// registerData 是一个响应式对象如果要获取值需要.valuelet result await userRegisterService(registerData.value);/* 使用axios的 request.js 统一处理了优化这里代码if(result.code 1){//注册成功alert(result.msg?result.msg : 注册成功)}else{// 注册失败alert(注册失败)} */alert(result.msg?result.msg : 注册成功)
}// 三 登录 1 绑定数据 复用注册表单的数据模型
// 三 登录 2 数据校验 复用注册表单的数据校验
// 三 登录 3 登录函数
const login async(){ //调用接口完成登录let result await userLoginService(registerData.value);/* 使用axios的 request.js 统一处理了优化这里代码if(result.code 1){//登录成功alert(result.msg?result.msg : 登录成功)}else{// 登录失败alert(登录失败)}*/alert(result.msg?result.msg : 登录成功)}
// 三 登录 4 定义函数清空数据模型数据(将数据清空)
const clearRegisterDate (){registerData.value{username:,password:,rePassword:}}/scripttemplateel-row classlogin-pageel-col :span12 classbg/el-colel-col :span6 :offset3 classform!-- 注册表单 --el-form refform sizelarge autocompleteoff v-ifisRegister :modelregisterData :rulesrules !-- 二 注册2.1 :modelregisterData表单声明属性 -- !-- 二 注册2.2 :rulesrules绑定校验 --el-form-itemh1注册/h1/el-form-itemel-form-item propusername !--二 注册 2.2 propusername绑定校验 --el-input :prefix-iconUser placeholder请输入用户名 v-modelregisterData.username/el-input !--二 注册 2.1 v-modelregisterData.username绑定属性 --/el-form-itemel-form-item proppassword !-- 二 注册2.2 proppassword绑定校验 --el-input :prefix-iconLock typepassword placeholder请输入密码 v-modelregisterData.password/el-input !-- 二 注册2.1 v-modelregisterData.password绑定属性 --/el-form-itemel-form-item proprePassword !-- 2.2 proprePassword绑定校验 --el-input :prefix-iconLock typepassword placeholder请输入再次密码 v-modelregisterData.rePassword/el-input !-- 二 注册2.1 v-modelregisterData.rePassword绑定属性 --/el-form-item!-- 注册按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space clickregister !-- 二 注册3.3 clickregister 绑定注册单击事件 --注册/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister false;clearRegisterDate() !--三 登录 4 调用函数clearRegisterDate() 清空数据 --← 返回/el-link/el-form-item/el-form!-- 登录表单 --el-form refform sizelarge autocompleteoff v-else modelregisterDate rulesrules !-- 三 登录 3.1 :modelregisterData表单声明属性复用注册 -- !-- 三 登录 3.2 :rulesrules绑定校验 --el-form-itemh1登录/h1/el-form-itemel-form-item propusername !--三 登录 3.2 propusername绑定校验 --el-input :prefix-iconUser placeholder请输入用户名 v-modelregisterData.username/el-input !--三 登录 3.1 v-modelregisterData.username绑定属性 --/el-form-itemel-form-item propusername !--三 登录 3.2 propusername绑定校验 --el-input namepassword :prefix-iconLock typepassword placeholder请输入密码 v-modelregisterData.password/el-input !--三 登录 3.1 v-modelregisterData.password绑定属性 --/el-form-item el-form-item classflexdiv classflexel-checkbox记住我/el-checkboxel-link typeprimary :underlinefalse忘记密码/el-link/div/el-form-item!-- 登录按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space clicklogin登录/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister true;clearRegisterDate() !--三 登录 4 调用函数clearRegisterDate() 清空数据 --注册 →/el-link/el-form-item/el-form/el-col/el-row
/templatestyle langscss scoped
/* 样式 */
.login-page {height: 100vh;background-color: #fff;.bg {background: url(/assets/logo2.png) no-repeat 60% center / 240px auto,url(/assets/login_bg.jpg) no-repeat center / cover;border-radius: 0 20px 20px 0;}.form {display: flex;flex-direction: column;justify-content: center;user-select: none;.title {margin: 0 auto;}.button {width: 100%;}.flex {width: 100%;display: flex;justify-content: space-between;}}
}
/style
2、使用element-plus优化alert
2.1 request.js引用ElMessage 组件处理
// 定制请求实例//导入axios npm install axios
import axios from axios;
// 四 4.2 element-plus封装的组件
import { ElMessage } from element-plus;//定义一个变量记录公共的前缀baseURL
// const baseURL http://localhost:8080
const baseURL /api // 注释上面代码解决跨域问题
const instance axios.create({baseURL})// 添加响应拦截器
instance.interceptors.response.use(result{// 四 4.1 判断业务状态码if(result.data.code 1){//成功正常返回数据return result.data;}// 操作失败// alert(result.data.msg?result.data.msg : 服务异常) // 四 4.2 优化alertElMessage.error(result.data.msg?result.data.msg : 服务异常)// 异步操作的状态转换为失败return Promise.reject(result.data)},err{alert(服务异常)return Promise.reject(err);//异步的状态转化成失败的状态}
)export default instance;
2.2 Login.vue引用ElMessage 组件处理
script setup
import { User, Lock } from element-plus/icons-vue
import { ref } from vue
//二 注册 3.3 导入注册的接口 //三 登录 3.3 导入登录的接口
import { userRegisterService,userLoginService} from /api/user.js// 四 4.2 element-plus封装的组件
import { ElMessage } from element-plus;//控制注册与登录表单的显示 默认显示注册
const isRegister ref(false)//二 注册 2.1定义数据模型const registerData ref({username:,password:,rePassword:
})//二 注册 2.2自定义rePassword需要自定义校验规则校验规则的函数,三个参rule 规则,value 值,callback 回调函数
const checkRePassword (rule,value,callback){if(value ){callback(new Error(请再次确认密码))}else if(value ! registerData.value.password){callback(new Error(请确保两次密码输入一致性))}else{callback() //校验通过}
}//二 注册 2.2 定义表单校验规则,rePassword需要自定义校验规则
const rules {username:[{required:true,message:请输入用户名,trigger:blur},{min:5,max:16,message:长度为5-16位非空字符,trigger:blur}],password:[{required:true,message:请输入密码,trigger:blur},{min:5,max:16,message:长度为5-16位非空字符,trigger:blur}],rePassword:[{validator:checkRePassword,trigger:blur}]
}// 二 注册 3.3 调用后台接口,完成注册
const register async(){// registerData 是一个响应式对象如果要获取值需要.valuelet result await userRegisterService(registerData.value);/* 使用axios的 request.js 统一处理了优化这里代码if(result.code 1){//注册成功alert(result.msg?result.msg : 注册成功)}else{// 注册失败alert(注册失败)} */// 四 4.2 优化alert// alert(result.msg?result.msg : 注册成功)ElMessage.success(result.msg?result.msg : 注册成功)
}// 三 登录 1 绑定数据 复用注册表单的数据模型
// 三 登录 2 数据校验 复用注册表单的数据校验
// 三 登录 3 登录函数
const login async(){ //调用接口完成登录let result await userLoginService(registerData.value);/* 使用axios的 request.js 统一处理了优化这里代码if(result.code 1){//登录成功alert(result.msg?result.msg : 登录成功)}else{// 登录失败alert(登录失败)}*/// 四 4.2 优化alert// alert(result.msg?result.msg : 登录成功)ElMessage.success(result.msg?result.msg : 登录成功)}
// 三 登录 4 定义函数清空数据模型数据(将数据清空)
const clearRegisterDate (){registerData.value{username:,password:,rePassword:}}/scripttemplateel-row classlogin-pageel-col :span12 classbg/el-colel-col :span6 :offset3 classform!-- 注册表单 --el-form refform sizelarge autocompleteoff v-ifisRegister :modelregisterData :rulesrules !-- 二 注册2.1 :modelregisterData表单声明属性 -- !-- 二 注册2.2 :rulesrules绑定校验 --el-form-itemh1注册/h1/el-form-itemel-form-item propusername !--二 注册 2.2 propusername绑定校验 --el-input :prefix-iconUser placeholder请输入用户名 v-modelregisterData.username/el-input !--二 注册 2.1 v-modelregisterData.username绑定属性 --/el-form-itemel-form-item proppassword !-- 二 注册2.2 proppassword绑定校验 --el-input :prefix-iconLock typepassword placeholder请输入密码 v-modelregisterData.password/el-input !-- 二 注册2.1 v-modelregisterData.password绑定属性 --/el-form-itemel-form-item proprePassword !-- 2.2 proprePassword绑定校验 --el-input :prefix-iconLock typepassword placeholder请输入再次密码 v-modelregisterData.rePassword/el-input !-- 二 注册2.1 v-modelregisterData.rePassword绑定属性 --/el-form-item!-- 注册按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space clickregister !-- 二 注册3.3 clickregister 绑定注册单击事件 --注册/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister false;clearRegisterDate() !--三 登录 4 调用函数clearRegisterDate() 清空数据 --← 返回/el-link/el-form-item/el-form!-- 登录表单 --el-form refform sizelarge autocompleteoff v-else modelregisterDate rulesrules !-- 三 登录 3.1 :modelregisterData表单声明属性复用注册 -- !-- 三 登录 3.2 :rulesrules绑定校验 --el-form-itemh1登录/h1/el-form-itemel-form-item propusername !--三 登录 3.2 propusername绑定校验 --el-input :prefix-iconUser placeholder请输入用户名 v-modelregisterData.username/el-input !--三 登录 3.1 v-modelregisterData.username绑定属性 --/el-form-itemel-form-item propusername !--三 登录 3.2 propusername绑定校验 --el-input namepassword :prefix-iconLock typepassword placeholder请输入密码 v-modelregisterData.password/el-input !--三 登录 3.1 v-modelregisterData.password绑定属性 --/el-form-item el-form-item classflexdiv classflexel-checkbox记住我/el-checkboxel-link typeprimary :underlinefalse忘记密码/el-link/div/el-form-item!-- 登录按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space clicklogin登录/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister true;clearRegisterDate() !--三 登录 4 调用函数clearRegisterDate() 清空数据 --注册 →/el-link/el-form-item/el-form/el-col/el-row
/templatestyle langscss scoped
/* 样式 */
.login-page {height: 100vh;background-color: #fff;.bg {background: url(/assets/logo2.png) no-repeat 60% center / 240px auto,url(/assets/login_bg.jpg) no-repeat center / cover;border-radius: 0 20px 20px 0;}.form {display: flex;flex-direction: column;justify-content: center;user-select: none;.title {margin: 0 auto;}.button {width: 100%;}.flex {width: 100%;display: flex;justify-content: space-between;}}
}
/style 五、主页面布局
1、Layout页面 1.1 views下新增Layout.vue
script setup
import {Management,Promotion,UserFilled,User,Crop,EditPen,SwitchButton,CaretBottom
} from element-plus/icons-vue
import avatar from /assets/default.png
/scripttemplate!-- element-plus 中的容器 --el-container classlayout-container!-- 左侧菜单 --el-aside width200pxdiv classel-aside__logo/div!-- element-plus 菜单标签 --el-menu active-text-color#ffd04b background-color#232323 text-color#fffrouterel-menu-item el-iconManagement //el-iconspan文章分类/span/el-menu-itemel-menu-item el-iconPromotion //el-iconspan文章管理/span/el-menu-itemel-sub-menu template #titleel-iconUserFilled //el-iconspan个人中心/span/templateel-menu-item el-iconUser //el-iconspan基本资料/span/el-menu-itemel-menu-item el-iconCrop //el-iconspan更换头像/span/el-menu-itemel-menu-item el-iconEditPen //el-iconspan重置密码/span/el-menu-item/el-sub-menu/el-menu/el-aside!-- 右侧主区域 --el-container!-- 头部区域 --el-headerdiv编码集中营strong春天的菠菜/strong/divel-dropdown placementbottom-endspan classel-dropdown__boxel-avatar :srcavatar /el-iconCaretBottom //el-icon/spantemplate #dropdownel-dropdown-menuel-dropdown-item commandprofile :iconUser基本资料/el-dropdown-itemel-dropdown-item commandavatar :iconCrop更换头像/el-dropdown-itemel-dropdown-item commandpassword :iconEditPen重置密码/el-dropdown-itemel-dropdown-item commandlogout :iconSwitchButton退出登录/el-dropdown-item/el-dropdown-menu/template/el-dropdown/el-header!-- 中间区域 --el-maindiv stylewidth: 1290px; height: 570px;border: 1px solid red;内容展示区/div/el-main!-- 底部区域 --el-footer国际大事件 ©2023 Created by 春天的菠菜/el-footer/el-container/el-container
/templatestyle langscss scoped
.layout-container {height: 100vh;.el-aside {background-color: #232323;__logo {height: 120px;background: url(/assets/logo.png) no-repeat center / 120px auto;}.el-menu {border-right: none;}}.el-header {background-color: #fff;display: flex;align-items: center;justify-content: space-between;.el-dropdown__box {display: flex;align-items: center;.el-icon {color: #999;margin-left: 10px;}:active,:focus {outline: none;}}}.el-footer {display: flex;align-items: center;justify-content: center;font-size: 14px;color: #666;}
}
/style 1.2 App.vue导入Layout.vue
script setup
import LoginVue from /views/Login.vue
import LayoutVue from /views/Layout.vue;/scripttemplate
LoginVue/
!-- 五 LayoutVue--
LayoutVue/
/templatestyle scoped/style2、路由 2.1 安装vue-router
npm install vue-router4 2.2 创建路由器index.js
src下新增router文件夹在router下新增index.js import { createRouter, createWebHistory } from vue-router//导入组件
import LoginVue from /views/Login.vue
import LayoutVue from /views/Layout.vue//定义路由关系
const routes [{ path: /login, component: LoginVue },{ path: /, component: LayoutVue }
]//创建路由器
const router createRouter({history: createWebHistory(),routes: routes
})//导出路由
export default router2.3 在main.js使用vue-router
import ./assets/main.scss // 本项目使用sassimport { createApp } from vue
import ElementPlus from element-plus
import element-plus/dist/index.css
//五 2.3
import router from /routerimport App from ./App.vueconst app createApp(App)
//五 2.3
app.use(router)app.use(ElementPlus)
app.mount(#app)2.4 App.vue中声明router-view标签
script setup
// 五 2.4
// import LoginVue from /views/Login.vue
// import LayoutVue from /views/Layout.vue;/scripttemplate!-- 五 2.4 --router-view/router-view!-- 五 2.4 --
!-- LoginVue/ --
!-- 五 1.1 LayoutVue--
LayoutVue/
/templatestyle scoped/style2.5 Login.vue优化登录成功跳转
script setup
import { User, Lock } from element-plus/icons-vue
import { ref } from vue
//二 注册 3.3 导入注册的接口 //三 登录 3.3 导入登录的接口
import { userRegisterService,userLoginService} from /api/user.js// 四 4.2 element-plus封装的组件
import { ElMessage } from element-plus;// 五 2.5 导入路由
import {useRouter} from vue-router// 五 2.5 调用路由
const router useRouter()//控制注册与登录表单的显示 默认显示注册
const isRegister ref(false)//二 注册 2.1定义数据模型const registerData ref({username:,password:,rePassword:
})//二 注册 2.2自定义rePassword需要自定义校验规则校验规则的函数,三个参rule 规则,value 值,callback 回调函数
const checkRePassword (rule,value,callback){if(value ){callback(new Error(请再次确认密码))}else if(value ! registerData.value.password){callback(new Error(请确保两次密码输入一致性))}else{callback() //校验通过}
}//二 注册 2.2 定义表单校验规则,rePassword需要自定义校验规则
const rules {username:[{required:true,message:请输入用户名,trigger:blur},{min:5,max:16,message:长度为5-16位非空字符,trigger:blur}],password:[{required:true,message:请输入密码,trigger:blur},{min:5,max:16,message:长度为5-16位非空字符,trigger:blur}],rePassword:[{validator:checkRePassword,trigger:blur}]
}// 二 注册 3.3 调用后台接口,完成注册
const register async(){// registerData 是一个响应式对象如果要获取值需要.valuelet result await userRegisterService(registerData.value);/* 使用axios的 request.js 统一处理了优化这里代码if(result.code 1){//注册成功alert(result.msg?result.msg : 注册成功)}else{// 注册失败alert(注册失败)} */// 四 4.2 优化alert// alert(result.msg?result.msg : 注册成功)ElMessage.success(result.msg?result.msg : 注册成功)
}// 三 登录 1 绑定数据 复用注册表单的数据模型
// 三 登录 2 数据校验 复用注册表单的数据校验
// 三 登录 3 登录函数
const login async(){ //调用接口完成登录let result await userLoginService(registerData.value);/* 使用axios的 request.js 统一处理了优化这里代码if(result.code 1){//登录成功alert(result.msg?result.msg : 登录成功)}else{// 登录失败alert(登录失败)}*/// 四 4.2 优化alert// alert(result.msg?result.msg : 登录成功)ElMessage.success(result.msg?result.msg : 登录成功)// 五 2.5 跳转首页 借助于路由router.push(/)}
// 三 登录 4 定义函数清空数据模型数据(将数据清空)
const clearRegisterDate (){registerData.value{username:,password:,rePassword:}}/scripttemplateel-row classlogin-pageel-col :span12 classbg/el-colel-col :span6 :offset3 classform!-- 注册表单 --el-form refform sizelarge autocompleteoff v-ifisRegister :modelregisterData :rulesrules !-- 二 注册2.1 :modelregisterData表单声明属性 -- !-- 二 注册2.2 :rulesrules绑定校验 --el-form-itemh1注册/h1/el-form-itemel-form-item propusername !--二 注册 2.2 propusername绑定校验 --el-input :prefix-iconUser placeholder请输入用户名 v-modelregisterData.username/el-input !--二 注册 2.1 v-modelregisterData.username绑定属性 --/el-form-itemel-form-item proppassword !-- 二 注册2.2 proppassword绑定校验 --el-input :prefix-iconLock typepassword placeholder请输入密码 v-modelregisterData.password/el-input !-- 二 注册2.1 v-modelregisterData.password绑定属性 --/el-form-itemel-form-item proprePassword !-- 2.2 proprePassword绑定校验 --el-input :prefix-iconLock typepassword placeholder请输入再次密码 v-modelregisterData.rePassword/el-input !-- 二 注册2.1 v-modelregisterData.rePassword绑定属性 --/el-form-item!-- 注册按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space clickregister !-- 二 注册3.3 clickregister 绑定注册单击事件 --注册/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister false;clearRegisterDate() !--三 登录 4 调用函数clearRegisterDate() 清空数据 --← 返回/el-link/el-form-item/el-form!-- 登录表单 --el-form refform sizelarge autocompleteoff v-else modelregisterDate rulesrules !-- 三 登录 3.1 :modelregisterData表单声明属性复用注册 -- !-- 三 登录 3.2 :rulesrules绑定校验 --el-form-itemh1登录/h1/el-form-itemel-form-item propusername !--三 登录 3.2 propusername绑定校验 --el-input :prefix-iconUser placeholder请输入用户名 v-modelregisterData.username/el-input !--三 登录 3.1 v-modelregisterData.username绑定属性 --/el-form-itemel-form-item propusername !--三 登录 3.2 propusername绑定校验 --el-input namepassword :prefix-iconLock typepassword placeholder请输入密码 v-modelregisterData.password/el-input !--三 登录 3.1 v-modelregisterData.password绑定属性 --/el-form-item el-form-item classflexdiv classflexel-checkbox记住我/el-checkboxel-link typeprimary :underlinefalse忘记密码/el-link/div/el-form-item!-- 登录按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space clicklogin登录/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister true;clearRegisterDate() !--三 登录 4 调用函数clearRegisterDate() 清空数据 --注册 →/el-link/el-form-item/el-form/el-col/el-row
/templatestyle langscss scoped
/* 样式 */
.login-page {height: 100vh;background-color: #fff;.bg {background: url(/assets/logo2.png) no-repeat 60% center / 240px auto,url(/assets/login_bg.jpg) no-repeat center / cover;border-radius: 0 20px 20px 0;}.form {display: flex;flex-direction: column;justify-content: center;user-select: none;.title {margin: 0 auto;}.button {width: 100%;}.flex {width: 100%;display: flex;justify-content: space-between;}}
}
/style
2.6 测试验证
登录页面 http://127.0.0.1:5173/login 3、子路由 3.1 views下创建vue文件 3.1.1 ArticleCategory.vue
template文章分类
/template
3.1.2 ArticleManage.vue
template文章管理
/template
3.1.3 UserAvatar.vue
template更换头像
/template
3.1.4 UserInfo.vue
template基本资料
/template
3.1.5 UserResetPassword.vue
template重置密码
/template
3.2 index.js
import { createRouter, createWebHistory } from vue-router//导入组件
import LoginVue from /views/Login.vue
import LayoutVue from /views/Layout.vueimport ArticleCategoryVue from /views/article/ArticleCategory.vue
import ArticleManageVue from /views/article/ArticleManage.vue
import UserAvatarVue from /views/user/UserAvatar.vue
import UserInfoVue from /views/user/UserInfo.vue
import UserResetPasswordVue from /views/user/UserResetPassword.vue//定义路由关系
const routes [{ path: /login, component: LoginVue },{path: /, component: LayoutVue,redirect:/article/manage, children: [{ path: /article/category, component: ArticleCategoryVue },{ path: /article/manage, component: ArticleManageVue },{ path: /user/info, component: UserInfoVue },{ path: /user/avatar, component: UserAvatarVue },{ path: /user/resetPassword, component: UserResetPasswordVue }]}
]//创建路由器
const router createRouter({history: createWebHistory(),routes: routes
})//导出路由
export default router3.3 Layout.vue声明router-view标签
script setup
import {Management,Promotion,UserFilled,User,Crop,EditPen,SwitchButton,CaretBottom
} from element-plus/icons-vue
import avatar from /assets/default.png
/scripttemplate!-- element-plus 中的容器 --el-container classlayout-container!-- 左侧菜单 --el-aside width200pxdiv classel-aside__logo/div!-- element-plus 菜单标签 --el-menu active-text-color#ffd04b background-color#232323 text-color#fffrouterel-menu-item index/article/category !-- 五 3.3 --el-iconManagement //el-iconspan文章分类/span/el-menu-itemel-menu-item index/article/manage !-- 五 3.3 --el-iconPromotion //el-iconspan文章管理/span/el-menu-itemel-sub-menu template #titleel-iconUserFilled //el-iconspan个人中心/span/templateel-menu-item index/user/info !-- 五 3.3 --el-iconUser //el-iconspan基本资料/span/el-menu-itemel-menu-item index/user/avatar !-- 五 3.3 --el-iconCrop //el-iconspan更换头像/span/el-menu-itemel-menu-item index/user/resetPassword !-- 五 3.3 --el-iconEditPen //el-iconspan重置密码/span/el-menu-item/el-sub-menu/el-menu/el-aside!-- 右侧主区域 --el-container!-- 头部区域 --el-headerdiv编码集中营strong春天的菠菜/strong/divel-dropdown placementbottom-endspan classel-dropdown__boxel-avatar :srcavatar /el-iconCaretBottom //el-icon/spantemplate #dropdownel-dropdown-menuel-dropdown-item commandprofile :iconUser基本资料/el-dropdown-itemel-dropdown-item commandavatar :iconCrop更换头像/el-dropdown-itemel-dropdown-item commandpassword :iconEditPen重置密码/el-dropdown-itemel-dropdown-item commandlogout :iconSwitchButton退出登录/el-dropdown-item/el-dropdown-menu/template/el-dropdown/el-header!-- 中间区域 --el-main!-- 五 3.3 --!-- div stylewidth: 1290px; height: 570px;border: 1px solid red;内容展示区/div --router-view/router-view/el-main!-- 底部区域 --el-footer国际大事件 ©2023 Created by 春天的菠菜/el-footer/el-container/el-container
/templatestyle langscss scoped
.layout-container {height: 100vh;.el-aside {background-color: #232323;__logo {height: 120px;background: url(/assets/logo.png) no-repeat center / 120px auto;}.el-menu {border-right: none;}}.el-header {background-color: #fff;display: flex;align-items: center;justify-content: space-between;.el-dropdown__box {display: flex;align-items: center;.el-icon {color: #999;margin-left: 10px;}:active,:focus {outline: none;}}}.el-footer {display: flex;align-items: center;justify-content: center;font-size: 14px;color: #666;}
}
/style
3.4 测试验证 六、文章分类
1、列表查询 1.1 article.js
api下新建article.js
//导入request.js请求工具
import request from /utils/request.js// 六 文章分类列表查询函数
export const articleCategoryListService (registerData) { return request.get(/category);}
1.2 ArticleCategory.vue
script setup
// 六 1.2 声明一个异步函数
import {articleCategoryListService} from /api/article.jsimport {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}
])// 六 1.2 声明一个异步函数
const articleCategoryList async(){let result await articleCategoryListService()categorys.value result.data;}
articleCategoryList();/script
templateel-card classpage-containertemplate #headerdiv classheaderspan文章分类/spandiv classextrael-button typeprimary添加分类/el-button/div/div/templateel-table :datacategorys stylewidth: 100%el-table-column label序号 width100 typeindex /el-table-columnel-table-column label分类名称 propcategoryName/el-table-columnel-table-column label分类别名 propcategoryAlias/el-table-columnel-table-column label操作 width100template #default{ row }el-button :iconEdit circle plain typeprimary /el-buttonel-button :iconDelete circle plain typedanger/el-button/template/el-table-columntemplate #emptyel-empty description没有数据 //template/el-table/el-card
/templatestyle langscss scoped
.page-container {min-height: 100%;box-sizing: border-box;.header {display: flex;align-items: center;justify-content: space-between;}
}
/style
此时页面访问存在token问题
2、Pinia状态管理库 2.1 安装pinia
npm install pinia 2.2 main.js导入pinia
在main.js中引入pinia创建pinia实例并调用vue应用实例的use方法使用pinia
import ./assets/main.scss // 本项目使用sassimport { createApp } from vue
import ElementPlus from element-plus
import element-plus/dist/index.css
//五 2.3
import router from /routerimport App from ./App.vue
//六 2.2
import {createPinia} from piniaconst app createApp(App)
//六 2.2
const pinia createPinia();
app.use(pinia)
//五 2.3
app.use(router)app.use(ElementPlus)
app.mount(#app)2.3 定义store
在src/stores目录下定义token.js
//定义store
import {defineStore} from pinia
import {ref} from vue
/* 第一个参数:名字,唯一性第二个参数:函数,函数的内部可以定义状态的所有内容返回值: 函数
*/
export const useTokenStore defineStore(token,(){//定义状态的内容//1.响应式变量const token ref()//2.定义一个函数,修改token的值const setToken (newToken){token.value newToken}//3.函数,移除token的值const removeToken (){token.value}return {token,setToken,removeToken}
});
2.4 使用Store
在需要使用状态的地方导入/stores/*.js , 使用即可
在Login.vue中导入/stores/token.js, 并且当用户登录成功后将token保存pinia中
script setup
import { User, Lock } from element-plus/icons-vue
import { ref } from vue
//二 注册 3.3 导入注册的接口 //三 登录 3.3 导入登录的接口
import { userRegisterService,userLoginService} from /api/user.js// 四 4.2 element-plus封装的组件
import { ElMessage } from element-plus;// 五 2.5 导入路由
import {useRouter} from vue-router// 六 2.4 导入token状态
import { useTokenStore } from /stores/token.js// 五 2.5 调用路由
const router useRouter()// 六 2.4 调用useTokenStore得到状态
const tokenStore useTokenStore();//控制注册与登录表单的显示 默认显示注册
const isRegister ref(false)//二 注册 2.1定义数据模型const registerData ref({username:,password:,rePassword:
})//二 注册 2.2自定义rePassword需要自定义校验规则校验规则的函数,三个参rule 规则,value 值,callback 回调函数
const checkRePassword (rule,value,callback){if(value ){callback(new Error(请再次确认密码))}else if(value ! registerData.value.password){callback(new Error(请确保两次密码输入一致性))}else{callback() //校验通过}
}//二 注册 2.2 定义表单校验规则,rePassword需要自定义校验规则
const rules {username:[{required:true,message:请输入用户名,trigger:blur},{min:5,max:16,message:长度为5-16位非空字符,trigger:blur}],password:[{required:true,message:请输入密码,trigger:blur},{min:5,max:16,message:长度为5-16位非空字符,trigger:blur}],rePassword:[{validator:checkRePassword,trigger:blur}]
}// 二 注册 3.3 调用后台接口,完成注册
const register async(){// registerData 是一个响应式对象如果要获取值需要.valuelet result await userRegisterService(registerData.value);/* 使用axios的 request.js 统一处理了优化这里代码if(result.code 1){//注册成功alert(result.msg?result.msg : 注册成功)}else{// 注册失败alert(注册失败)} */// 四 4.2 优化alert// alert(result.msg?result.msg : 注册成功)ElMessage.success(result.msg?result.msg : 注册成功)
}// 三 登录 1 绑定数据 复用注册表单的数据模型
// 三 登录 2 数据校验 复用注册表单的数据校验
// 三 登录 3 登录函数
const login async(){ //调用接口完成登录let result await userLoginService(registerData.value);/* 使用axios的 request.js 统一处理了优化这里代码if(result.code 1){//登录成功alert(result.msg?result.msg : 登录成功)}else{// 登录失败alert(登录失败)}*/// 四 4.2 优化alert// alert(result.msg?result.msg : 登录成功)ElMessage.success(result.msg?result.msg : 登录成功)// 六 2.4 保存token存储到piniatokenStore.setToken(result.data)// 五 2.5 跳转首页 借助于路由router.push(/)}
// 三 登录 4 定义函数清空数据模型数据(将数据清空)
const clearRegisterDate (){registerData.value{username:,password:,rePassword:}}/scripttemplateel-row classlogin-pageel-col :span12 classbg/el-colel-col :span6 :offset3 classform!-- 注册表单 --el-form refform sizelarge autocompleteoff v-ifisRegister :modelregisterData :rulesrules !-- 二 注册2.1 :modelregisterData表单声明属性 -- !-- 二 注册2.2 :rulesrules绑定校验 --el-form-itemh1注册/h1/el-form-itemel-form-item propusername !--二 注册 2.2 propusername绑定校验 --el-input :prefix-iconUser placeholder请输入用户名 v-modelregisterData.username/el-input !--二 注册 2.1 v-modelregisterData.username绑定属性 --/el-form-itemel-form-item proppassword !-- 二 注册2.2 proppassword绑定校验 --el-input :prefix-iconLock typepassword placeholder请输入密码 v-modelregisterData.password/el-input !-- 二 注册2.1 v-modelregisterData.password绑定属性 --/el-form-itemel-form-item proprePassword !-- 2.2 proprePassword绑定校验 --el-input :prefix-iconLock typepassword placeholder请输入再次密码 v-modelregisterData.rePassword/el-input !-- 二 注册2.1 v-modelregisterData.rePassword绑定属性 --/el-form-item!-- 注册按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space clickregister !-- 二 注册3.3 clickregister 绑定注册单击事件 --注册/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister false;clearRegisterDate() !--三 登录 4 调用函数clearRegisterDate() 清空数据 --← 返回/el-link/el-form-item/el-form!-- 登录表单 --el-form refform sizelarge autocompleteoff v-else modelregisterDate rulesrules !-- 三 登录 3.1 :modelregisterData表单声明属性复用注册 -- !-- 三 登录 3.2 :rulesrules绑定校验 --el-form-itemh1登录/h1/el-form-itemel-form-item propusername !--三 登录 3.2 propusername绑定校验 --el-input :prefix-iconUser placeholder请输入用户名 v-modelregisterData.username/el-input !--三 登录 3.1 v-modelregisterData.username绑定属性 --/el-form-itemel-form-item propusername !--三 登录 3.2 propusername绑定校验 --el-input namepassword :prefix-iconLock typepassword placeholder请输入密码 v-modelregisterData.password/el-input !--三 登录 3.1 v-modelregisterData.password绑定属性 --/el-form-item el-form-item classflexdiv classflexel-checkbox记住我/el-checkboxel-link typeprimary :underlinefalse忘记密码/el-link/div/el-form-item!-- 登录按钮 --el-form-itemel-button classbutton typeprimary auto-insert-space clicklogin登录/el-button/el-form-itemel-form-item classflexel-link typeinfo :underlinefalse clickisRegister true;clearRegisterDate() !--三 登录 4 调用函数clearRegisterDate() 清空数据 --注册 →/el-link/el-form-item/el-form/el-col/el-row
/templatestyle langscss scoped
/* 样式 */
.login-page {height: 100vh;background-color: #fff;.bg {background: url(/assets/logo2.png) no-repeat 60% center / 240px auto,url(/assets/login_bg.jpg) no-repeat center / cover;border-radius: 0 20px 20px 0;}.form {display: flex;flex-direction: column;justify-content: center;user-select: none;.title {margin: 0 auto;}.button {width: 100%;}.flex {width: 100%;display: flex;justify-content: space-between;}}
}
/style
在article.js中导入/stores/token.js, 从pinia中获取到存储的token在发起查询文章分类列表的时候把token通过请求头的形式携带给服务器
//导入request.js请求工具
import request from /utils/request.js
// 六 2.4 导入/stores/token.js
import { useTokenStore } from /stores/token.js// 六 文章分类列表查询函数
export const articleCategoryListService (registerData) { // 六 2.4获取token状态const tokenStore useTokenStore()//通过请求头Authorization携带tokenreturn request.get(/category, { headers: { Authorization: tokenStore.token } });}
2.5 测试验证 3、 使用axios请求拦截器解决token繁琐问题
当进入主页后将来要与后台交互都需要携带token如果每次请求都写这样的代码将会比较繁琐此时可以将携带token的代码通过请求拦截器统一处理 3.1 request.js添加请求拦截器
在 src/util/request.js中
// 定制请求实例//导入axios npm install axios
import axios from axios;
// 四 4.2 element-plus封装的组件
import { ElMessage } from element-plus;// 六 3 导入token状态
import { useTokenStore } from /stores/token.js;//定义一个变量记录公共的前缀baseURL
// const baseURL http://localhost:8080
const baseURL /api // 注释上面代码解决跨域问题
const instance axios.create({baseURL})
//六 3 添加请求拦截器
instance.interceptors.request.use((config){//在发送请求之前做什么let tokenStore useTokenStore()//如果token中有值在携带if(tokenStore.token){config.headers.AuthorizationtokenStore.token}return config},(err){//如果请求错误做什么Promise.reject(err)}
)// 添加响应拦截器
instance.interceptors.response.use(result{// 四 4.1 判断业务状态码if(result.data.code 1){//成功正常返回数据return result.data;}// 操作失败// alert(result.data.msg?result.data.msg : 服务异常) // 四 4.2 优化alertElMessage.error(result.data.msg?result.data.msg : 服务异常)// 异步操作的状态转换为失败return Promise.reject(result.data)},err{alert(服务异常)return Promise.reject(err);//异步的状态转化成失败的状态}
)export default instance;
3.2 article.js移除之前添加的请求头
//导入request.js请求工具
import request from /utils/request.js
// 六 2.4 导入/stores/token.js
import { useTokenStore } from /stores/token.js// 六 文章分类列表查询函数
export const articleCategoryListService (registerData) { // 六 2.4获取token状态// 六 3.2// const tokenStore useTokenStore()//通过请求头Authorization携带token// return request.get(/category, { headers: { Authorization: tokenStore.token } });return request.get(/category);} 4、pinia-persistedstate-plugin持久化插件 4.1 安装
npm install pinia-persistedstate-plugin 4.2 pinia中使用persist插件
在main.js中
import ./assets/main.scss // 本项目使用sassimport { createApp } from vue
import ElementPlus from element-plus
import element-plus/dist/index.css
//五 2.3
import router from /routerimport App from ./App.vue
//六 2.2
import {createPinia} from pinia// 六 4.2 导入持久化插件
import {createPersistedState} frompinia-persistedstate-pluginconst app createApp(App)
//六 2.2
const pinia createPinia();
// 六 4.2
const persist createPersistedState()
//六 4.2 pinia使用持久化插件
pinia.use(persist)
app.use(pinia)
//五 2.3
app.use(router)app.use(ElementPlus)
app.mount(#app)4.3 在创建定义状态是配置持久化
在src/stores/token.js中
//定义store
import {defineStore} from pinia
import {ref} from vue
/* 第一个参数:名字,唯一性第二个参数:函数,函数的内部可以定义状态的所有内容返回值: 函数
*/
export const useTokenStore defineStore(token,(){//定义状态的内容//1.响应式变量const token ref()//2.定义一个函数,修改token的值const setToken (newToken){token.value newToken}//3.函数,移除token的值const removeToken (){token.value}return {token,setToken,removeToken}
},{persist:true// 六 4.3持久化存储
});
5、未登录统一处理
在后续访问接口时如果没有登录则前端不携带token后台服务器会返回响应状态码401代表未登录此时可以在axios的响应拦截器中统一对未登录的情况做处理request.js
// 定制请求实例//导入axios npm install axios
import axios from axios;
// 四 4.2 element-plus封装的组件
import { ElMessage } from element-plus;// 六 3.1 导入token状态
import { useTokenStore } from /stores/token.js;// 六 5 导入路由跳转到首页
import router from /router//定义一个变量记录公共的前缀baseURL
// const baseURL http://localhost:8080
const baseURL /api // 注释上面代码解决跨域问题
const instance axios.create({baseURL})
//六 3 添加请求拦截器
instance.interceptors.request.use((config){//在发送请求之前做什么let tokenStore useTokenStore()//如果token中有值在携带if(tokenStore.token){config.headers.AuthorizationtokenStore.token}return config},(err){//如果请求错误做什么Promise.reject(err)}
)// 添加响应拦截器
instance.interceptors.response.use(result{// 四 4.1 判断业务状态码if(result.data.code 1){//成功正常返回数据return result.data;}// 操作失败// alert(result.data.msg?result.data.msg : 服务异常) // 四 4.2 优化alert// ElMessage.error(result.data.msg?result.data.msg : 服务异常)ElMessage.error(result.data.msg || 服务异常)// 异步操作的状态转换为失败return Promise.reject(result.data)},err{// 六 5 如果响应状态码时401代表未登录给出对应的提示并跳转到登录页if(err.response.status401){ElMessage.error(请先登录)router.push(/login)}else{ElMessage.error(服务异常);}return Promise.reject(err);//异步的状态转化成失败的状态
}
)export default instance;
6、添加文章分类 6.1 ArticleCategory.vue
添加弹窗分类、数据模型和校验规则、添加分类按钮单击事件确认按钮单击事件、在页面中调用接口
script setup// 六 6.2 element-plus封装的组件
import { ElMessage } from element-plus;
// 六 1.2 articleCategoryListService声明一个异步函数 6.2articleCategoryAddService
import {articleCategoryListService,articleCategoryAddService} from /api/article.jsimport {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}
])// 六 1.2 声明一个异步函数
const articleCategoryList async(){let result await articleCategoryListService()categorys.value result.data;}
articleCategoryList();// 六 6.1 控制添加分类弹窗
const dialogVisible ref(false)//六 6.1 添加分类数据模型
const categoryModel ref({categoryName: ,categoryAlias:
})
//六 6.1 添加分类表单校验
const rules {categoryName: [{ required: true, message: 请输入分类名称, trigger: blur },],categoryAlias: [{ required: true, message: 请输入分类别名, trigger: blur },]
}
// 六 6.1 调用接口添加表单
const addCategory async (){let result await articleCategoryAddService(categoryModel.value);ElMessage.success(result.message? result.message:添加成功)//隐藏弹窗dialogVisible.value false//再次访问后台接口查询所有分类articleCategoryList()
}/script
templateel-card classpage-containertemplate #headerdiv classheaderspan文章分类/spandiv classextrael-button typeprimary clickdialogVisible true添加分类/el-button !-- 六 6.1 clickdialogVisible true绑定点击事件--/div/div/templateel-table :datacategorys stylewidth: 100%el-table-column label序号 width100 typeindex /el-table-columnel-table-column label分类名称 propcategoryName/el-table-columnel-table-column label分类别名 propcategoryAlias/el-table-columnel-table-column label创建时间 propcreateTime/el-table-columnel-table-column label更新时间 propupdateTime/el-table-columnel-table-column label操作 width100template #default{ row }el-button :iconEdit circle plain typeprimary /el-buttonel-button :iconDelete circle plain typedanger/el-button/template/el-table-columntemplate #emptyel-empty description没有数据 //template/el-table!-- 六 6.1 添加分类弹窗 --el-dialog v-modeldialogVisible title添加弹层 width30%el-form :modelcategoryModel :rulesrules label-width100px stylepadding-right: 30pxel-form-item label分类名称 propcategoryNameel-input v-modelcategoryModel.categoryName minlength1 maxlength10/el-input/el-form-itemel-form-item label分类别名 propcategoryAliasel-input v-modelcategoryModel.categoryAlias minlength1 maxlength15/el-input/el-form-item/el-formtemplate #footerspan classdialog-footerel-button clickdialogVisible false取消/el-buttonel-button typeprimary clickaddCategory 确认 /el-button !-- 六 6.1 clickaddCategory绑定点击事件--/span/template/el-dialog/el-card
/templatestyle langscss scoped
.page-container {min-height: 100%;box-sizing: border-box;.header {display: flex;align-items: center;justify-content: space-between;}
}
/style
6.2 在article.js中提供添加分类的函数 //导入request.js请求工具
import request from /utils/request.js
// 六 2.4 导入/stores/token.js
import { useTokenStore } from /stores/token.js// 六 文章分类列表查询函数
export const articleCategoryListService (registerData) { // 六 2.4获取token状态// 六 3.2// const tokenStore useTokenStore()//通过请求头Authorization携带token// return request.get(/category, { headers: { Authorization: tokenStore.token } });return request.get(/category);}// 六 6.2 文章分类 添加文章分类 函数 json 参数
export const articleCategoryAddService (categoryModel) {return request.post(/category, categoryModel)
}
6.3 测试验证 7、修改文章分类
分析编辑与新增的弹窗页面可以复用优化实施
7.1 修改分类弹窗页面
修改分类弹窗和新增文章分类弹窗长的一样所以可以复用添加分类的弹窗
7.1.1 弹窗标题显示 ArticleCategory.vue
定义标题
//弹窗标题
const titleref()
7.1.2 在弹窗上绑定标题 ArticleCategory.vue el-dialog v-modeldialogVisible :titletitle width30%
7.1.3 为添加分类按钮绑定事件 ArticleCategory.vue
el-button typeprimary clicktitle添加分类;dialogVisible true添加分类/el-button
7.1.4 为修改分类按钮绑定事件 ArticleCategory.vue
el-button :iconEdit circle plain typeprimary clicktitle修改分类;dialogVisibletrue/el-button
7.2 修改数据回显 ArticleCategory.vue
当点击修改分类按钮时需要把当前这一条数据的详细信息显示到修改分类的弹窗上这个叫回显
通过插槽的方式得到被点击按钮所在行的数据
script setup// 六 6.2 element-plus封装的组件
import { ElMessage } from element-plus;
// 六 1.2 articleCategoryListService声明一个异步函数 6.2articleCategoryAddService
import {articleCategoryListService,articleCategoryAddService} from /api/article.jsimport {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}
])// 六 1.2 声明一个异步函数
const articleCategoryList async(){let result await articleCategoryListService()categorys.value result.data;}
articleCategoryList();// 六 6.1 控制添加分类弹窗
const dialogVisible ref(false)//六 6.1 添加分类数据模型
const categoryModel ref({categoryName: ,categoryAlias:
})
//六 6.1 添加分类表单校验
const rules {categoryName: [{ required: true, message: 请输入分类名称, trigger: blur },],categoryAlias: [{ required: true, message: 请输入分类别名, trigger: blur },]
}
// 六 6.1 调用接口添加表单
const addCategory async (){let result await articleCategoryAddService(categoryModel.value);ElMessage.success(result.message? result.message:添加成功)//隐藏弹窗dialogVisible.value false//再次访问后台接口查询所有分类articleCategoryList()
}// 六 7.1.1 弹窗标题
const titleref()// 六 7.2展示编辑弹窗
const showDialog (row){title.value 修改分类;dialogVisible.value true//数据拷贝categoryModel.value.categoryName row.categoryName;categoryModel.value.categoryAlias row.categoryAlias;//扩展id属性将来需要传参给后台完成分类的修改categoryModel.value.id row.id
}/script
templateel-card classpage-containertemplate #headerdiv classheaderspan文章分类/spandiv classextrael-button typeprimary clicktitle添加分类;dialogVisible true添加分类/el-button !-- 六 6.1 clickdialogVisible true绑定点击事件 六7.1.3 title添加分类;-- /div/div/templateel-table :datacategorys stylewidth: 100%el-table-column label序号 width100 typeindex /el-table-columnel-table-column label分类名称 propcategoryName/el-table-columnel-table-column label分类别名 propcategoryAlias/el-table-columnel-table-column label创建时间 propcreateTime/el-table-columnel-table-column label更新时间 propupdateTime/el-table-columnel-table-column label操作 width100template #default{ row }el-button :iconEdit circle plain typeprimary clickshowDialog(row)/el-button !-- 六 7.1.4 clicktitle修改分类;dialogVisibletrue绑定事件 六 7.2 showDialog(row)--el-button :iconDelete circle plain typedanger/el-button/template/el-table-columntemplate #emptyel-empty description没有数据 //template/el-table!-- 六 6.1 添加分类弹窗 --el-dialog v-modeldialogVisible :titletitle width30% !-- 六 7.1.2 :titletitle绑定标题--el-form :modelcategoryModel :rulesrules label-width100px stylepadding-right: 30pxel-form-item label分类名称 propcategoryNameel-input v-modelcategoryModel.categoryName minlength1 maxlength10/el-input/el-form-itemel-form-item label分类别名 propcategoryAliasel-input v-modelcategoryModel.categoryAlias minlength1 maxlength15/el-input/el-form-item/el-formtemplate #footerspan classdialog-footerel-button clickdialogVisible false取消/el-buttonel-button typeprimary clickaddCategory 确认 /el-button !-- 六 6.1 clickaddCategory绑定点击事件--/span/template/el-dialog/el-card
/templatestyle langscss scoped
.page-container {min-height: 100%;box-sizing: border-box;.header {display: flex;align-items: center;justify-content: space-between;}
}
/style
7.3 修改文章分类接口调用 7.3.1 article.js中提供修改分类的函数
//导入request.js请求工具
import request from /utils/request.js
// 六 2.4 导入/stores/token.js
import { useTokenStore } from /stores/token.js// 六 文章分类列表查询函数
export const articleCategoryListService (registerData) { // 六 2.4获取token状态// 六 3.2// const tokenStore useTokenStore()//通过请求头Authorization携带token// return request.get(/category, { headers: { Authorization: tokenStore.token } });return request.get(/category);}// 六 6.2 文章分类 添加文章分类 函数 json 参数
export const articleCategoryAddService (categoryModel) {return request.post(/category, categoryModel)
}//六 7.3.1 修改分类
export const articleCategoryUpdateService (categoryModel){return request.put(/category,categoryModel)}
7.3.2 修改确定按钮的绑定事件 ArticleCategory.vue span classdialog-footerel-button clickdialogVisible false取消/el-buttonel-button typeprimary clicktitle添加分类? addCategory():updateCategory() 确认 /el-button/span
7.3.3 调用接口完成修改的函数 ArticleCategory.vue
script setup// 六 6.2 element-plus封装的组件
import { ElMessage } from element-plus;
// 六 1.2 articleCategoryListService声明一个异步函数 6.2articleCategoryAddService 7.3.3 articleCategoryUpdateService
import {articleCategoryListService,articleCategoryAddService,articleCategoryUpdateService} from /api/article.jsimport {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}
])// 六 1.2 声明一个异步函数
const articleCategoryList async(){let result await articleCategoryListService()categorys.value result.data;}
articleCategoryList();// 六 6.1 控制添加分类弹窗
const dialogVisible ref(false)//六 6.1 添加分类数据模型
const categoryModel ref({categoryName: ,categoryAlias:
})
//六 6.1 添加分类表单校验
const rules {categoryName: [{ required: true, message: 请输入分类名称, trigger: blur },],categoryAlias: [{ required: true, message: 请输入分类别名, trigger: blur },]
}
// 六 6.1 调用接口添加表单
const addCategory async (){let result await articleCategoryAddService(categoryModel.value);ElMessage.success(result.message? result.message:添加成功)//隐藏弹窗dialogVisible.value false//再次访问后台接口查询所有分类articleCategoryList()
}// 六 7.1.1 弹窗标题
const titleref()// 六 7.2展示编辑弹窗
const showDialog (row){title.value 修改分类;dialogVisible.value true//数据拷贝categoryModel.value.categoryName row.categoryName;categoryModel.value.categoryAlias row.categoryAlias;//扩展id属性将来需要传参给后台完成分类的修改categoryModel.value.id row.id
}// 六 7.3.3修改分类
const updateCategoryasync (){let result await articleCategoryUpdateService(categoryModel.value)ElMessage.success(result.message? result.message:修改成功)//隐藏弹窗dialogVisible.valuefalse//再次访问后台接口查询所有分类articleCategoryList()
}/script
templateel-card classpage-containertemplate #headerdiv classheaderspan文章分类/spandiv classextrael-button typeprimary clicktitle添加分类;dialogVisible true添加分类/el-button !-- 六 6.1 clickdialogVisible true绑定点击事件 六7.1.3 title添加分类;-- /div/div/templateel-table :datacategorys stylewidth: 100%el-table-column label序号 width100 typeindex /el-table-columnel-table-column label分类名称 propcategoryName/el-table-columnel-table-column label分类别名 propcategoryAlias/el-table-columnel-table-column label创建时间 propcreateTime/el-table-columnel-table-column label更新时间 propupdateTime/el-table-columnel-table-column label操作 width100template #default{ row }el-button :iconEdit circle plain typeprimary clickshowDialog(row)/el-button !-- 六 7.1.4 clicktitle修改分类;dialogVisibletrue绑定事件 六 7.2 showDialog(row)--el-button :iconDelete circle plain typedanger/el-button/template/el-table-columntemplate #emptyel-empty description没有数据 //template/el-table!-- 六 6.1 添加分类弹窗 --el-dialog v-modeldialogVisible :titletitle width30% !-- 六 7.1.2 :titletitle绑定标题--el-form :modelcategoryModel :rulesrules label-width100px stylepadding-right: 30pxel-form-item label分类名称 propcategoryNameel-input v-modelcategoryModel.categoryName minlength1 maxlength10/el-input/el-form-itemel-form-item label分类别名 propcategoryAliasel-input v-modelcategoryModel.categoryAlias minlength1 maxlength15/el-input/el-form-item/el-formtemplate #footerspan classdialog-footerel-button clickdialogVisible false取消/el-buttonel-button typeprimary clicktitle添加分类? addCategory():updateCategory() 确认 /el-button !-- 六 6.1 clickaddCategory绑定点击事件 六 7.3.1 clicktitle添加分类? addCategory():updateCategory()--/span/template/el-dialog/el-card
/templatestyle langscss scoped
.page-container {min-height: 100%;box-sizing: border-box;.header {display: flex;align-items: center;justify-content: space-between;}
}
/style
7.3.4 优化添加时数据回显
由于现在修改和新增共用了一个数据模型所以在点击添加分类后有时候会显示数据此时可以将categoryModel中的数据清空
//清空模型数据
const clearCategoryModel (){categoryModel.value.categoryName,categoryModel.value.categoryAlias
}
script setup// 六 6.2 element-plus封装的组件
import { ElMessage } from element-plus;
// 六 1.2 articleCategoryListService声明一个异步函数 6.2articleCategoryAddService 7.3.3 articleCategoryUpdateService
import {articleCategoryListService,articleCategoryAddService,articleCategoryUpdateService} from /api/article.jsimport {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}
])// 六 1.2 声明一个异步函数
const articleCategoryList async(){let result await articleCategoryListService()categorys.value result.data;}
articleCategoryList();// 六 6.1 控制添加分类弹窗
const dialogVisible ref(false)//六 6.1 添加分类数据模型
const categoryModel ref({categoryName: ,categoryAlias:
})
//六 6.1 添加分类表单校验
const rules {categoryName: [{ required: true, message: 请输入分类名称, trigger: blur },],categoryAlias: [{ required: true, message: 请输入分类别名, trigger: blur },]
}
// 六 6.1 调用接口添加表单
const addCategory async (){let result await articleCategoryAddService(categoryModel.value);ElMessage.success(result.message? result.message:添加成功)//隐藏弹窗dialogVisible.value false//再次访问后台接口查询所有分类articleCategoryList()
}// 六 7.1.1 弹窗标题
const titleref()// 六 7.2展示编辑弹窗
const showDialog (row){title.value 修改分类;dialogVisible.value true//数据拷贝categoryModel.value.categoryName row.categoryName;categoryModel.value.categoryAlias row.categoryAlias;//扩展id属性将来需要传参给后台完成分类的修改categoryModel.value.id row.id
}// 六 7.3.3修改分类
const updateCategoryasync (){let result await articleCategoryUpdateService(categoryModel.value)ElMessage.success(result.message? result.message:修改成功)//隐藏弹窗dialogVisible.valuefalse//再次访问后台接口查询所有分类articleCategoryList()
}//六 7.3.4 清空模型数据
const clearCategoryModel (){categoryModel.value.categoryName,categoryModel.value.categoryAlias
}/script
templateel-card classpage-containertemplate #headerdiv classheaderspan文章分类/spandiv classextrael-button typeprimary clicktitle添加分类;dialogVisible true;clearCategoryModel()添加分类/el-button !-- 六 6.1 clickdialogVisible true绑定点击事件 六7.1.3 title添加分类; 六 7.3.4 清空模型数据 clearCategoryModel()-- /div/div/templateel-table :datacategorys stylewidth: 100%el-table-column label序号 width100 typeindex /el-table-columnel-table-column label分类名称 propcategoryName/el-table-columnel-table-column label分类别名 propcategoryAlias/el-table-columnel-table-column label创建时间 propcreateTime/el-table-columnel-table-column label更新时间 propupdateTime/el-table-columnel-table-column label操作 width100template #default{ row }el-button :iconEdit circle plain typeprimary clickshowDialog(row)/el-button !-- 六 7.1.4 clicktitle修改分类;dialogVisibletrue绑定事件 六 7.2 showDialog(row)--el-button :iconDelete circle plain typedanger/el-button/template/el-table-columntemplate #emptyel-empty description没有数据 //template/el-table!-- 六 6.1 添加分类弹窗 --el-dialog v-modeldialogVisible :titletitle width30% !-- 六 7.1.2 :titletitle绑定标题--el-form :modelcategoryModel :rulesrules label-width100px stylepadding-right: 30pxel-form-item label分类名称 propcategoryNameel-input v-modelcategoryModel.categoryName minlength1 maxlength10/el-input/el-form-itemel-form-item label分类别名 propcategoryAliasel-input v-modelcategoryModel.categoryAlias minlength1 maxlength15/el-input/el-form-item/el-formtemplate #footerspan classdialog-footerel-button clickdialogVisible false取消/el-buttonel-button typeprimary clicktitle添加分类? addCategory():updateCategory() 确认 /el-button !-- 六 6.1 clickaddCategory绑定点击事件 六 7.3.1 clicktitle添加分类? addCategory():updateCategory()--/span/template/el-dialog/el-card
/templatestyle langscss scoped
.page-container {min-height: 100%;box-sizing: border-box;.header {display: flex;align-items: center;justify-content: space-between;}
}
/style
8、删除文章分类 8.1 删除接口 //导入request.js请求工具
import request from /utils/request.js
// 六 2.4 导入/stores/token.js
import { useTokenStore } from /stores/token.js// 六 文章分类列表查询函数
export const articleCategoryListService (registerData) { // 六 2.4获取token状态// 六 3.2// const tokenStore useTokenStore()//通过请求头Authorization携带token// return request.get(/category, { headers: { Authorization: tokenStore.token } });return request.get(/category);}// 六 6.2 文章分类 添加文章分类 函数 json 参数
export const articleCategoryAddService (categoryModel) {return request.post(/category, categoryModel)
}//六 7.3.1 修改分类
export const articleCategoryUpdateService (categoryModel){return request.put(/category,categoryModel)}//六 8.1 删除分类
export const articleCategoryDeleteService (id) {return request.delete(/category?idid)
} 8.2 删除确认框
//删除分类 给删除按钮绑定事件
const deleteCategory (row) {ElMessageBox.confirm(你确认删除该分类信息吗,温馨提示,{confirmButtonText: 确认,cancelButtonText: 取消,type: warning,}).then(() {//用户点击了确认ElMessage({type: success,message: 删除成功,})}).catch(() {//用户点击了取消ElMessage({type: info,message: 取消删除,})})
} 8.3 为删除按钮绑定事件
template #default{ row }el-button :iconEdit circle plain typeprimary clickshowDialog(row)/el-button !-- 六 7.1.4 clicktitle修改分类;dialogVisibletrue绑定事件 六 7.2 showDialog(row)--el-button :iconDelete circle plain typedanger clickdeleteCategory(row)/el-button !-- 六 8.3 clickdeleteCategory(row) 绑定删除事件--/template
8.4 调用删除接口
script setup// 六 6.2 ElMessage element-plus封装的组件 六 8.2 ElMessageBox
import { ElMessage,ElMessageBox } from element-plus;
// 六 1.2 articleCategoryListService声明一个异步函数 6.2articleCategoryAddService 7.3.3 articleCategoryUpdateService 8.2 articleCategoryDeleteService
import {articleCategoryListService,articleCategoryAddService,articleCategoryUpdateService,articleCategoryDeleteService} from /api/article.jsimport {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}
])// 六 1.2 声明一个异步函数
const articleCategoryList async(){let result await articleCategoryListService()categorys.value result.data;}
articleCategoryList();// 六 6.1 控制添加分类弹窗
const dialogVisible ref(false)//六 6.1 添加分类数据模型
const categoryModel ref({categoryName: ,categoryAlias:
})
//六 6.1 添加分类表单校验
const rules {categoryName: [{ required: true, message: 请输入分类名称, trigger: blur },],categoryAlias: [{ required: true, message: 请输入分类别名, trigger: blur },]
}
// 六 6.1 调用接口添加表单
const addCategory async (){let result await articleCategoryAddService(categoryModel.value);ElMessage.success(result.message? result.message:添加成功)//隐藏弹窗dialogVisible.value false//再次访问后台接口查询所有分类articleCategoryList()
}// 六 7.1.1 弹窗标题
const titleref()// 六 7.2展示编辑弹窗
const showDialog (row){title.value 修改分类;dialogVisible.value true//数据拷贝categoryModel.value.categoryName row.categoryName;categoryModel.value.categoryAlias row.categoryAlias;//扩展id属性将来需要传参给后台完成分类的修改categoryModel.value.id row.id
}// 六 7.3.3修改分类
const updateCategoryasync (){let result await articleCategoryUpdateService(categoryModel.value)ElMessage.success(result.message? result.message:修改成功)//隐藏弹窗dialogVisible.valuefalse//再次访问后台接口查询所有分类articleCategoryList()
}//六 7.3.4 清空模型数据
const clearCategoryModel (){categoryModel.value.categoryName,categoryModel.value.categoryAlias
}// 六 8.2 删除分类 给删除按钮绑定事件
const deleteCategory (row) {// 提示用户 确认框ElMessageBox.confirm(你确认删除该分类信息吗,温馨提示,{confirmButtonText: 确认,cancelButtonText: 取消,type: warning,}).then(async() {//用户点击了确认let result await articleCategoryDeleteService(row.id)// 给出提示ElMessage({type: success,message: 删除成功,})//刷新列表//再次访问后台接口查询所有分类articleCategoryList()}).catch(() {//用户点击了取消ElMessage({type: info,message: 取消删除,})})
}/script
templateel-card classpage-containertemplate #headerdiv classheaderspan文章分类/spandiv classextrael-button typeprimary clicktitle添加分类;dialogVisible true;clearCategoryModel()添加分类/el-button !-- 六 6.1 clickdialogVisible true绑定点击事件 六7.1.3 title添加分类; 六 7.3.4 清空模型数据 clearCategoryModel()-- /div/div/templateel-table :datacategorys stylewidth: 100%el-table-column label序号 width100 typeindex /el-table-columnel-table-column label分类名称 propcategoryName/el-table-columnel-table-column label分类别名 propcategoryAlias/el-table-columnel-table-column label创建时间 propcreateTime/el-table-columnel-table-column label更新时间 propupdateTime/el-table-columnel-table-column label操作 width100template #default{ row }el-button :iconEdit circle plain typeprimary clickshowDialog(row)/el-button !-- 六 7.1.4 clicktitle修改分类;dialogVisibletrue绑定事件 六 7.2 showDialog(row)--el-button :iconDelete circle plain typedanger clickdeleteCategory(row)/el-button !-- 六 8.3 clickdeleteCategory(row) 绑定删除事件--/template/el-table-columntemplate #emptyel-empty description没有数据 //template/el-table!-- 六 6.1 添加分类弹窗 --el-dialog v-modeldialogVisible :titletitle width30% !-- 六 7.1.2 :titletitle绑定标题--el-form :modelcategoryModel :rulesrules label-width100px stylepadding-right: 30pxel-form-item label分类名称 propcategoryNameel-input v-modelcategoryModel.categoryName minlength1 maxlength10/el-input/el-form-itemel-form-item label分类别名 propcategoryAliasel-input v-modelcategoryModel.categoryAlias minlength1 maxlength15/el-input/el-form-item/el-formtemplate #footerspan classdialog-footerel-button clickdialogVisible false取消/el-buttonel-button typeprimary clicktitle添加分类? addCategory():updateCategory() 确认 /el-button !-- 六 6.1 clickaddCategory绑定点击事件 六 7.3.1 clicktitle添加分类? addCategory():updateCategory()--/span/template/el-dialog/el-card
/templatestyle langscss scoped
.page-container {min-height: 100%;box-sizing: border-box;.header {display: flex;align-items: center;justify-content: space-between;}
}
/style
8.5 测试验证 七、文章列表
1、 文章列表查询 1.1 文章列表页面组件ArticleManage.vue
script setup
import {Edit,Delete
} from element-plus/icons-vueimport { 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}
])//用户搜索时选中的分类id
const categoryIdref()//用户搜索时选中的发布状态
const stateref()//文章列表数据模型
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},{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},{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(20)//总条数
const pageSize ref(3)//每页条数//当每页条数发生了变化调用此函数
const onSizeChange (size) {pageSize.value size
}
//当前页码发生变化调用此函数
const onCurrentChange (num) {pageNum.value num
}
/script
templateel-card classpage-containertemplate #headerdiv classheaderspan文章管理/spandiv classextrael-button typeprimary添加文章/el-button/div/div/template!-- 搜索表单 --el-form inlineel-form-item label文章分类el-select placeholder请选择 v-modelcategoryIdel-option v-forc in categorys :keyc.id :labelc.categoryName:valuec.id/el-option/el-select/el-form-itemel-form-item label发布状态el-select placeholder请选择 v-modelstateel-option label已发布 value已发布/el-optionel-option label草稿 value草稿/el-option/el-select/el-form-itemel-form-itemel-button typeprimary搜索/el-buttonel-button重置/el-button/el-form-item/el-form!-- 文章列表 --el-table :dataarticles stylewidth: 100%el-table-column label文章标题 width400 proptitle/el-table-columnel-table-column label分类 propcategoryId/el-table-columnel-table-column label发表时间 propcreateTime /el-table-columnel-table-column label状态 propstate/el-table-columnel-table-column label操作 width100template #default{ row }el-button :iconEdit circle plain typeprimary/el-buttonel-button :iconDelete circle plain typedanger/el-button/template/el-table-columntemplate #emptyel-empty description没有数据 //template/el-table!-- 分页条 --el-pagination v-model:current-pagepageNum v-model:page-sizepageSize :page-sizes[3, 5 ,10, 15]layoutjumper, total, sizes, prev, pager, next background :totaltotal size-changeonSizeChangecurrent-changeonCurrentChange stylemargin-top: 20px; justify-content: flex-end //el-card
/template
style langscss scoped
.page-container {min-height: 100%;box-sizing: border-box;.header {display: flex;align-items: center;justify-content: space-between;}
}
/style
1.2 解决分页控件英文显示问题main.js
import ./assets/main.scss // 本项目使用sassimport { createApp } from vue
import ElementPlus from element-plus
import element-plus/dist/index.css
// 七 1.2 解决分页控件显示英文问题
import locale from element-plus/dist/locale/zh-cn.js
//五 2.3
import router from /routerimport App from ./App.vue
//六 2.2
import {createPinia} from pinia// 六 4.2 导入持久化插件
import {createPersistedState} from pinia-persistedstate-pluginconst app createApp(App)
//六 2.2
const pinia createPinia();
// 六 4.2
const persist createPersistedState()
//六 4.2 pinia使用持久化插件
pinia.use(persist)
app.use(pinia)
//五 2.3
app.use(router)
// 七 1.2 解决分页控件显示英文问题
app.use(ElementPlus,{locale})
app.mount(#app)1.3 文章分类数据回显ArticleMange.vue
//文章列表查询
import { articleCategoryListService } from /api/article.js
const getArticleCategoryList async () {//获取所有分类let resultC await articleCategoryListService();categorys.value resultC.data
}
getArticleCategoryList(); 1.4 文章列表接口调用 1.4.1 article.js中提供获取文章列表数据的函数
//导入request.js请求工具
import request from /utils/request.js
// 六 2.4 导入/stores/token.js
import { useTokenStore } from /stores/token.js// 六 文章分类列表查询函数
export const articleCategoryListService (registerData) { // 六 2.4获取token状态// 六 3.2// const tokenStore useTokenStore()//通过请求头Authorization携带token// return request.get(/category, { headers: { Authorization: tokenStore.token } });return request.get(/category);}// 六 6.2 文章分类 添加文章分类 函数 json 参数
export const articleCategoryAddService (categoryModel) {return request.post(/category, categoryModel)
}//六 7.3.1 修改分类
export const articleCategoryUpdateService (categoryModel){return request.put(/category,categoryModel)}//六 8.1 删除分类
export const articleCategoryDeleteService (id) {return request.delete(/category?idid)
}// 八 1.4.1 文章列表查询
export const articleListService (params) {return request.get(/article, { params: params })}
1.4.2 ArticleManage.vue中调用接口获取数据
script setup
import {Edit,Delete
} from element-plus/icons-vueimport { ref } from vue// 八 1.3 articleCategoryListService搜索查询条件 文章列表查询 导入 八 1.4.2 articleListService
import { articleCategoryListService ,articleListService} from /api/article.js//文章分类数据模型
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}
])//用户搜索时选中的分类id
const categoryIdref()//用户搜索时选中的发布状态
const stateref()//文章列表数据模型
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},{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},{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(20)//总条数
const pageSize ref(3)//每页条数//当每页条数发生了变化调用此函数
const onSizeChange (size) {pageSize.value size
}
//当前页码发生变化调用此函数
const onCurrentChange (num) {pageNum.value num
}// 八 1.3 搜索查询条件 文章列表查询
const getArticleCategoryList async () {//获取所有分类let resultC await articleCategoryListService();categorys.value resultC.data
}// 八 1.4.2 获取文章列表数据
const articleList async () {let params {pageNum: pageNum.value,pageSize: pageSize.value,categoryId: categoryId.value ? categoryId.value : null,state: state.value ? state.value : null}let result await articleListService(params);//渲染视图total.value result.data.total;articles.value result.data.items;//处理数据,给数据模型扩展一个属性categoryName,分类名称for (let i 0; i articles.value.length; i) {let article articles.value[i];for (let j 0; j categorys.value.length; j) {if (article.categoryId categorys.value[j].id) {article.categoryName categorys.value[j].categoryName;}}}
}// 八 1.3 搜索查询条件 文章列表查询 调用函数getArticleCategoryList
getArticleCategoryList();
// 八 1.4.2 获取文章列表数据
articleList();/script
templateel-card classpage-container!-- 1 标题 --template #headerdiv classheaderspan文章管理/spandiv classextrael-button typeprimary添加文章/el-button/div/div/template!-- 2 搜索表单 --el-form inlineel-form-item label文章分类el-select placeholder请选择 v-modelcategoryIdel-option v-forc in categorys :keyc.id :labelc.categoryName:valuec.id/el-option/el-select/el-form-itemel-form-item label发布状态el-select placeholder请选择 v-modelstateel-option label已发布 value已发布/el-optionel-option label草稿 value草稿/el-option/el-select/el-form-itemel-form-itemel-button typeprimary搜索/el-buttonel-button重置/el-button/el-form-item/el-form!-- 3 文章列表 --el-table :dataarticles stylewidth: 100%el-table-column label文章标题 width400 proptitle/el-table-columnel-table-column label分类 propcategoryName/el-table-columnel-table-column label发表时间 propcreateTime /el-table-columnel-table-column label更新时间 propupdateTime /el-table-columnel-table-column label状态 propstate/el-table-columnel-table-column label操作 width100template #default{ row }el-button :iconEdit circle plain typeprimary/el-buttonel-button :iconDelete circle plain typedanger/el-button/template/el-table-columntemplate #emptyel-empty description没有数据 //template/el-table!-- 4 分页条 --el-pagination v-model:current-pagepageNum v-model:page-sizepageSize :page-sizes[3, 5 ,10, 15]layoutjumper, total, sizes, prev, pager, next background :totaltotal size-changeonSizeChangecurrent-changeonCurrentChange stylemargin-top: 20px; justify-content: flex-end //el-card
/template
style langscss scoped
.page-container {min-height: 100%;box-sizing: border-box;.header {display: flex;align-items: center;justify-content: space-between;}
}
/style 1.4.3 当分页条的当前页和每页条数发生变化重新发送请求获取数据
//当每页条数发生了变化调用此函数
const onSizeChange (size) {pageSize.value sizearticleList() //七 1.4.3 **当分页条的当前页和每页条数发生变化重新发送请求获取数据**
}
//当前页码发生变化调用此函数
const onCurrentChange (num) {pageNum.value numarticleList() //七 1.4.3 **当分页条的当前页和每页条数发生变化重新发送请求获取数据**
}
2 、搜索和重置
为搜索按钮绑定单击事件调用getArticles函数即可
为重置按钮绑定单击事件清除categoryId和state的之即可
script setup
import {Edit,Delete
} from element-plus/icons-vueimport { ref } from vue// 七 1.3 articleCategoryListService搜索查询条件 文章列表查询 导入 八 1.4.2 articleListService
import { articleCategoryListService ,articleListService} from /api/article.js//文章分类数据模型
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}
])//用户搜索时选中的分类id
const categoryIdref()//用户搜索时选中的发布状态
const stateref()//文章列表数据模型
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},{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},{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(20)//总条数
const pageSize ref(3)//每页条数//当每页条数发生了变化调用此函数
const onSizeChange (size) {pageSize.value sizearticleList() //七 1.4.3 **当分页条的当前页和每页条数发生变化重新发送请求获取数据**
}
//当前页码发生变化调用此函数
const onCurrentChange (num) {pageNum.value numarticleList() //七 1.4.3 **当分页条的当前页和每页条数发生变化重新发送请求获取数据**
}// 七 1.3 搜索查询条件 文章列表查询
const getArticleCategoryList async () {//获取所有分类let resultC await articleCategoryListService();categorys.value resultC.data
}// 七 1.4.2 获取文章列表数据
const articleList async () {let params {pageNum: pageNum.value,pageSize: pageSize.value,categoryId: categoryId.value ? categoryId.value : null,state: state.value ? state.value : null}let result await articleListService(params);//渲染视图total.value result.data.total;articles.value result.data.items;//处理数据,给数据模型扩展一个属性categoryName,分类名称for (let i 0; i articles.value.length; i) {let article articles.value[i];for (let j 0; j categorys.value.length; j) {if (article.categoryId categorys.value[j].id) {article.categoryName categorys.value[j].categoryName;}}}
}//七 1.3 搜索查询条件 文章列表查询 调用函数getArticleCategoryList
getArticleCategoryList();
// 七 1.4.2 获取文章列表数据
articleList();/script
templateel-card classpage-container!-- 1 标题 --template #headerdiv classheaderspan文章管理/spandiv classextrael-button typeprimary添加文章/el-button/div/div/template!-- 2 搜索表单 --el-form inlineel-form-item label文章分类el-select placeholder请选择 v-modelcategoryIdel-option v-forc in categorys :keyc.id :labelc.categoryName:valuec.id/el-option/el-select/el-form-itemel-form-item label发布状态el-select placeholder请选择 v-modelstateel-option label已发布 value已发布/el-optionel-option label草稿 value草稿/el-option/el-select/el-form-itemel-form-itemel-button typeprimary clickarticleList搜索/el-button !-- 七 2 绑定查询事件--el-button clickcategoryId;state;articleList()重置/el-button !-- 七 2 绑定重置事件--/el-form-item/el-form!-- 3 文章列表 --el-table :dataarticles stylewidth: 100%el-table-column label文章标题 width400 proptitle/el-table-columnel-table-column label分类 propcategoryName/el-table-columnel-table-column label发表时间 propcreateTime /el-table-columnel-table-column label更新时间 propupdateTime /el-table-columnel-table-column label状态 propstate/el-table-columnel-table-column label操作 width100template #default{ row }el-button :iconEdit circle plain typeprimary/el-buttonel-button :iconDelete circle plain typedanger/el-button/template/el-table-columntemplate #emptyel-empty description没有数据 //template/el-table!-- 4 分页条 --el-pagination v-model:current-pagepageNum v-model:page-sizepageSize :page-sizes[3, 5 ,10, 15]layoutjumper, total, sizes, prev, pager, next background :totaltotal size-changeonSizeChangecurrent-changeonCurrentChange stylemargin-top: 20px; justify-content: flex-end //el-card
/template
style langscss scoped
.page-container {min-height: 100%;box-sizing: border-box;.header {display: flex;align-items: center;justify-content: space-between;}
}
/style
3、添加文章 3.1 添加文章抽屉组件 import {Plus} from element-plus/icons-vue
//控制抽屉是否显示
const visibleDrawer ref(false)
//添加表单数据模型
const articleModel ref({title: ,categoryId: ,coverImg: ,content:,sta
!-- 抽屉 --el-drawer v-modelvisibleDrawer title添加文章 directionrtl size50%!-- 添加文章表单 --el-form :modelarticleModel label-width100px el-form-item label文章标题 el-input v-modelarticleModel.title placeholder请输入标题/el-input/el-form-itemel-form-item label文章分类el-select placeholder请选择 v-modelarticleModel.categoryIdel-option v-forc in categorys :keyc.id :labelc.categoryName :valuec.id/el-option/el-select/el-form-itemel-form-item label文章封面el-upload classavatar-uploader :auto-uploadfalse :show-file-listfalseimg v-ifarticleModel.coverImg :srcarticleModel.coverImg classavatar /el-icon v-else classavatar-uploader-iconPlus //el-icon/el-upload/el-form-itemel-form-item label文章内容div classeditor富文本编辑器/div/el-form-itemel-form-itemel-button typeprimary发布/el-buttonel-button typeinfo草稿/el-button/el-form-item/el-form/el-drawer /* 抽屉样式 */
.avatar-uploader {:deep() {.avatar {width: 178px;height: 178px;display: block;}.el-upload {border: 1px dashed var(--el-border-color);border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;transition: var(--el-transition-duration-fast);}.el-upload:hover {border-color: var(--el-color-primary);}.el-icon.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;text-align: center;}}
}
.editor {width: 100%;:deep(.ql-editor) {min-height: 200px;}
}
3.2 为添加文章按钮添加单击事件展示抽屉
script setup
import {Edit,Delete
} from element-plus/icons-vueimport { ref } from vue// 七 1.3 articleCategoryListService搜索查询条件 文章列表查询 导入 八 1.4.2 articleListService
import { articleCategoryListService ,articleListService} from /api/article.js// 七 3.1 导入添加抽屉组件
import {Plus} from element-plus/icons-vue
// 七 3.1 控制抽屉是否显示
const visibleDrawer ref(false)
// 七 3.1 添加表单数据模型
const articleModel ref({title: ,categoryId: ,coverImg: ,content:,state:
})//文章分类数据模型
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}
])//用户搜索时选中的分类id
const categoryIdref()//用户搜索时选中的发布状态
const stateref()//文章列表数据模型
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},{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},{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(20)//总条数
const pageSize ref(3)//每页条数//当每页条数发生了变化调用此函数
const onSizeChange (size) {pageSize.value sizearticleList() //七 1.4.3 **当分页条的当前页和每页条数发生变化重新发送请求获取数据**
}
//当前页码发生变化调用此函数
const onCurrentChange (num) {pageNum.value numarticleList() //七 1.4.3 **当分页条的当前页和每页条数发生变化重新发送请求获取数据**
}// 七 1.3 搜索查询条件 文章列表查询
const getArticleCategoryList async () {//获取所有分类let resultC await articleCategoryListService();categorys.value resultC.data
}// 七 1.4.2 获取文章列表数据
const articleList async () {let params {pageNum: pageNum.value,pageSize: pageSize.value,categoryId: categoryId.value ? categoryId.value : null,state: state.value ? state.value : null}let result await articleListService(params);//渲染视图total.value result.data.total;articles.value result.data.items;//处理数据,给数据模型扩展一个属性categoryName,分类名称for (let i 0; i articles.value.length; i) {let article articles.value[i];for (let j 0; j categorys.value.length; j) {if (article.categoryId categorys.value[j].id) {article.categoryName categorys.value[j].categoryName;}}}
}//七 1.3 搜索查询条件 文章列表查询 调用函数getArticleCategoryList
getArticleCategoryList();
// 七 1.4.2 获取文章列表数据
articleList();/script
templateel-card classpage-container!-- 1 标题 --template #headerdiv classheaderspan文章管理/spandiv classextrael-button typeprimary clickvisibleDrawer true添加文章/el-button !-- 七 3.2 clickvisibleDrawer true 绑定查询事件--/div/div/template!-- 2 搜索表单 --el-form inlineel-form-item label文章分类el-select placeholder请选择 v-modelcategoryIdel-option v-forc in categorys :keyc.id :labelc.categoryName:valuec.id/el-option/el-select/el-form-itemel-form-item label发布状态el-select placeholder请选择 v-modelstateel-option label已发布 value已发布/el-optionel-option label草稿 value草稿/el-option/el-select/el-form-itemel-form-itemel-button typeprimary clickarticleList搜索/el-button !-- 七 2 绑定查询事件--el-button clickcategoryId;state;articleList()重置/el-button !-- 七 2 绑定重置事件--/el-form-item/el-form!-- 3 文章列表 --el-table :dataarticles stylewidth: 100%el-table-column label文章标题 width400 proptitle/el-table-columnel-table-column label分类 propcategoryName/el-table-columnel-table-column label发表时间 propcreateTime /el-table-columnel-table-column label更新时间 propupdateTime /el-table-columnel-table-column label状态 propstate/el-table-columnel-table-column label操作 width100template #default{ row }el-button :iconEdit circle plain typeprimary/el-buttonel-button :iconDelete circle plain typedanger/el-button/template/el-table-columntemplate #emptyel-empty description没有数据 //template/el-table!-- 4 分页条 --el-pagination v-model:current-pagepageNum v-model:page-sizepageSize :page-sizes[3, 5 ,10, 15]layoutjumper, total, sizes, prev, pager, next background :totaltotal size-changeonSizeChangecurrent-changeonCurrentChange stylemargin-top: 20px; justify-content: flex-end /!-- 七 3.1 抽屉 --el-drawer v-modelvisibleDrawer title添加文章 directionrtl size50%!-- 添加文章表单 --el-form :modelarticleModel label-width100px el-form-item label文章标题 el-input v-modelarticleModel.title placeholder请输入标题/el-input/el-form-itemel-form-item label文章分类el-select placeholder请选择 v-modelarticleModel.categoryIdel-option v-forc in categorys :keyc.id :labelc.categoryName :valuec.id/el-option/el-select/el-form-itemel-form-item label文章封面el-upload classavatar-uploader :auto-uploadfalse :show-file-listfalseimg v-ifarticleModel.coverImg :srcarticleModel.coverImg classavatar /el-icon v-else classavatar-uploader-iconPlus //el-icon/el-upload/el-form-itemel-form-item label文章内容div classeditor富文本编辑器/div/el-form-itemel-form-itemel-button typeprimary发布/el-buttonel-button typeinfo草稿/el-button/el-form-item/el-form/el-drawer/el-card
/template
style langscss scoped
.page-container {min-height: 100%;box-sizing: border-box;.header {display: flex;align-items: center;justify-content: space-between;}
}/* 七 3.1 抽屉样式 */
.avatar-uploader {:deep() {.avatar {width: 178px;height: 178px;display: block;}.el-upload {border: 1px dashed var(--el-border-color);border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;transition: var(--el-transition-duration-fast);}.el-upload:hover {border-color: var(--el-color-primary);}.el-icon.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;text-align: center;}}
}
.editor {width: 100%;:deep(.ql-editor) {min-height: 200px;}
}
/style
3.3 富文本编辑器
文章内容需要使用到富文本编辑器这里咱们使用一个开源的富文本编辑器 Quill
官网地址 VueQuill | Rich Text Editor Component for Vue 3
3.3.1 安装
npm install vueup/vue-quilllatest --save 3.3.2 导入组件和样式
import { QuillEditor } from vueup/vue-quill
import vueup/vue-quill/dist/vue-quill.snow.css
3.3.3 页面使用quill组件
quill-editorthemesnowv-model:contentarticleModel.contentcontentTypehtml
/quill-editor
3.3.4 样式美化
.editor {width: 100%;:deep(.ql-editor) {min-height: 200px;}
}
script setup
import {Edit,Delete
} from element-plus/icons-vueimport { ref } from vue// 七 1.3 articleCategoryListService搜索查询条件 文章列表查询 导入 八 1.4.2 articleListService
import { articleCategoryListService ,articleListService} from /api/article.js// 七 3.1 导入添加抽屉组件
import {Plus} from element-plus/icons-vue// 七 3.3.2 导入组件和样式
import { QuillEditor } from vueup/vue-quill
import vueup/vue-quill/dist/vue-quill.snow.css// 七 3.1 控制抽屉是否显示
const visibleDrawer ref(false)
// 七 3.1 添加表单数据模型
const articleModel ref({title: ,categoryId: ,coverImg: ,content:,state:
})//文章分类数据模型
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}
])//用户搜索时选中的分类id
const categoryIdref()//用户搜索时选中的发布状态
const stateref()//文章列表数据模型
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},{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},{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(20)//总条数
const pageSize ref(3)//每页条数//当每页条数发生了变化调用此函数
const onSizeChange (size) {pageSize.value sizearticleList() //七 1.4.3 **当分页条的当前页和每页条数发生变化重新发送请求获取数据**
}
//当前页码发生变化调用此函数
const onCurrentChange (num) {pageNum.value numarticleList() //七 1.4.3 **当分页条的当前页和每页条数发生变化重新发送请求获取数据**
}// 七 1.3 搜索查询条件 文章列表查询
const getArticleCategoryList async () {//获取所有分类let resultC await articleCategoryListService();categorys.value resultC.data
}// 七 1.4.2 获取文章列表数据
const articleList async () {let params {pageNum: pageNum.value,pageSize: pageSize.value,categoryId: categoryId.value ? categoryId.value : null,state: state.value ? state.value : null}let result await articleListService(params);//渲染视图total.value result.data.total;articles.value result.data.items;//处理数据,给数据模型扩展一个属性categoryName,分类名称for (let i 0; i articles.value.length; i) {let article articles.value[i];for (let j 0; j categorys.value.length; j) {if (article.categoryId categorys.value[j].id) {article.categoryName categorys.value[j].categoryName;}}}
}//七 1.3 搜索查询条件 文章列表查询 调用函数getArticleCategoryList
getArticleCategoryList();
// 七 1.4.2 获取文章列表数据
articleList();/script
templateel-card classpage-container!-- 1 标题 --template #headerdiv classheaderspan文章管理/spandiv classextrael-button typeprimary clickvisibleDrawer true添加文章/el-button !-- 七 3.2 clickvisibleDrawer true 绑定查询事件--/div/div/template!-- 2 搜索表单 --el-form inlineel-form-item label文章分类el-select placeholder请选择 v-modelcategoryIdel-option v-forc in categorys :keyc.id :labelc.categoryName:valuec.id/el-option/el-select/el-form-itemel-form-item label发布状态el-select placeholder请选择 v-modelstateel-option label已发布 value已发布/el-optionel-option label草稿 value草稿/el-option/el-select/el-form-itemel-form-itemel-button typeprimary clickarticleList搜索/el-button !-- 七 2 绑定查询事件--el-button clickcategoryId;state;articleList()重置/el-button !-- 七 2 绑定重置事件--/el-form-item/el-form!-- 3 文章列表 --el-table :dataarticles stylewidth: 100%el-table-column label文章标题 width400 proptitle/el-table-columnel-table-column label分类 propcategoryName/el-table-columnel-table-column label发表时间 propcreateTime /el-table-columnel-table-column label更新时间 propupdateTime /el-table-columnel-table-column label状态 propstate/el-table-columnel-table-column label操作 width100template #default{ row }el-button :iconEdit circle plain typeprimary/el-buttonel-button :iconDelete circle plain typedanger/el-button/template/el-table-columntemplate #emptyel-empty description没有数据 //template/el-table!-- 4 分页条 --el-pagination v-model:current-pagepageNum v-model:page-sizepageSize :page-sizes[3, 5 ,10, 15]layoutjumper, total, sizes, prev, pager, next background :totaltotal size-changeonSizeChangecurrent-changeonCurrentChange stylemargin-top: 20px; justify-content: flex-end /!-- 七 3.1 抽屉 --el-drawer v-modelvisibleDrawer title添加文章 directionrtl size50%!-- 添加文章表单 --el-form :modelarticleModel label-width100px el-form-item label文章标题 el-input v-modelarticleModel.title placeholder请输入标题/el-input/el-form-itemel-form-item label文章分类el-select placeholder请选择 v-modelarticleModel.categoryIdel-option v-forc in categorys :keyc.id :labelc.categoryName :valuec.id/el-option/el-select/el-form-itemel-form-item label文章封面el-upload classavatar-uploader :auto-uploadfalse :show-file-listfalseimg v-ifarticleModel.coverImg :srcarticleModel.coverImg classavatar /el-icon v-else classavatar-uploader-iconPlus //el-icon/el-upload/el-form-itemel-form-item label文章内容div classeditorquill-editor themesnow v-model:contentarticleModel.content contentTypehtml !-- 七 3.3.3页面使用quill组件 --/quill-editor/div/el-form-itemel-form-itemel-button typeprimary发布/el-buttonel-button typeinfo草稿/el-button/el-form-item/el-form/el-drawer/el-card
/template
style langscss scoped
.page-container {min-height: 100%;box-sizing: border-box;.header {display: flex;align-items: center;justify-content: space-between;}
}/* 七 3.1 抽屉样式 */
.avatar-uploader {:deep() {.avatar {width: 178px;height: 178px;display: block;}.el-upload {border: 1px dashed var(--el-border-color);border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;transition: var(--el-transition-duration-fast);}.el-upload:hover {border-color: var(--el-color-primary);}.el-icon.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;text-align: center;}}
}
.editor {width: 100%;:deep(.ql-editor) {min-height: 200px;}
}
// 七 3.3.4 样式
.editor {width: 100%; :deep(.ql-editor) {min-height: 200px;}
}
/style 3.4 文章封面图片上传
将来当点击图标选择本地图片后el-upload这个组件会自动发送请求把图片上传到指定的服务器上而不需要我们自己使用axios发送异步请求所以需要给el-upload标签添加一些属性控制请求的发送
auto-upload:是否自动上传
action: 服务器接口路径
name: 上传的文件字段名
headers: 设置上传的请求头
on-success: 上传成功的回调函数
/el-form-itemel-form-item label文章封面!-- auto-upload:是否自动上传action: 服务器接口路径name: 上传的文件字段名headers: 设置上传的请求头on-success: 上传成功的回调函数 --el-upload classavatar-uploader :auto-uploadtrue :show-file-listfalseaction/api/uploadnamefile:headers{Authorization:tokenStore.token}:on-successuploadSuccessimg v-ifarticleModel.coverImg :srcarticleModel.coverImg classavatar /el-icon v-else classavatar-uploader-iconPlus //el-icon/el-upload/el-form-item
注意 由于这个请求时el-upload自动发送的异步请求并没有使用咱们的request.js请求工具所以在请求的路ing上需要加上/api, 这个时候请求代理才能拦截到这个请求转发到后台服务器上 要携带请求头还需要导入pinia状态才可以使用 import { useTokenStore } from /stores/token.js
const tokenStore useTokenStore(); script setup
import {Edit,Delete
} from element-plus/icons-vueimport { ref } from vue// 七 1.3 articleCategoryListService搜索查询条件 文章列表查询 导入 八 1.4.2 articleListService
import { articleCategoryListService ,articleListService} from /api/article.js// 七 3.1 导入添加抽屉组件
import {Plus} from element-plus/icons-vue// 七 3.3.2 导入组件和样式
import { QuillEditor } from vueup/vue-quill
import vueup/vue-quill/dist/vue-quill.snow.css// 七 3.4 文章封面上传 导入token
import { useTokenStore } from /stores/token.js
const tokenStore useTokenStore();
// 七 3.4 上传图片成功回调
const uploadSuccess (img) {//img就是后台响应的数据格式为{code:状态码message提示信息data: 图片的存储地址}articleModel.value.coverImgimg.data
}// 七 3.1 控制抽屉是否显示
const visibleDrawer ref(false)
// 七 3.1 添加表单数据模型
const articleModel ref({title: ,categoryId: ,coverImg: ,content:,state:
})//文章分类数据模型
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}
])//用户搜索时选中的分类id
const categoryIdref()//用户搜索时选中的发布状态
const stateref()//文章列表数据模型
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},{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},{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(20)//总条数
const pageSize ref(3)//每页条数//当每页条数发生了变化调用此函数
const onSizeChange (size) {pageSize.value sizearticleList() //七 1.4.3 **当分页条的当前页和每页条数发生变化重新发送请求获取数据**
}
//当前页码发生变化调用此函数
const onCurrentChange (num) {pageNum.value numarticleList() //七 1.4.3 **当分页条的当前页和每页条数发生变化重新发送请求获取数据**
}// 七 1.3 搜索查询条件 文章列表查询
const getArticleCategoryList async () {//获取所有分类let resultC await articleCategoryListService();categorys.value resultC.data
}// 七 1.4.2 获取文章列表数据
const articleList async () {let params {pageNum: pageNum.value,pageSize: pageSize.value,categoryId: categoryId.value ? categoryId.value : null,state: state.value ? state.value : null}let result await articleListService(params);//渲染视图total.value result.data.total;articles.value result.data.items;//处理数据,给数据模型扩展一个属性categoryName,分类名称for (let i 0; i articles.value.length; i) {let article articles.value[i];for (let j 0; j categorys.value.length; j) {if (article.categoryId categorys.value[j].id) {article.categoryName categorys.value[j].categoryName;}}}
}//七 1.3 搜索查询条件 文章列表查询 调用函数getArticleCategoryList
getArticleCategoryList();
// 七 1.4.2 获取文章列表数据
articleList();/script
templateel-card classpage-container!-- 1 标题 --template #headerdiv classheaderspan文章管理/spandiv classextrael-button typeprimary clickvisibleDrawer true添加文章/el-button !-- 七 3.2 clickvisibleDrawer true 绑定查询事件--/div/div/template!-- 2 搜索表单 --el-form inlineel-form-item label文章分类el-select placeholder请选择 v-modelcategoryIdel-option v-forc in categorys :keyc.id :labelc.categoryName:valuec.id/el-option/el-select/el-form-itemel-form-item label发布状态el-select placeholder请选择 v-modelstateel-option label已发布 value已发布/el-optionel-option label草稿 value草稿/el-option/el-select/el-form-itemel-form-itemel-button typeprimary clickarticleList搜索/el-button !-- 七 2 绑定查询事件--el-button clickcategoryId;state;articleList()重置/el-button !-- 七 2 绑定重置事件--/el-form-item/el-form!-- 3 文章列表 --el-table :dataarticles stylewidth: 100%el-table-column label文章标题 width400 proptitle/el-table-columnel-table-column label分类 propcategoryName/el-table-columnel-table-column label发表时间 propcreateTime /el-table-columnel-table-column label更新时间 propupdateTime /el-table-columnel-table-column label状态 propstate/el-table-columnel-table-column label操作 width100template #default{ row }el-button :iconEdit circle plain typeprimary/el-buttonel-button :iconDelete circle plain typedanger/el-button/template/el-table-columntemplate #emptyel-empty description没有数据 //template/el-table!-- 4 分页条 --el-pagination v-model:current-pagepageNum v-model:page-sizepageSize :page-sizes[3, 5 ,10, 15]layoutjumper, total, sizes, prev, pager, next background :totaltotal size-changeonSizeChangecurrent-changeonCurrentChange stylemargin-top: 20px; justify-content: flex-end /!-- 七 3.1 抽屉 --el-drawer v-modelvisibleDrawer title添加文章 directionrtl size50%!-- 添加文章表单 --el-form :modelarticleModel label-width100px el-form-item label文章标题 el-input v-modelarticleModel.title placeholder请输入标题/el-input/el-form-itemel-form-item label文章分类el-select placeholder请选择 v-modelarticleModel.categoryIdel-option v-forc in categorys :keyc.id :labelc.categoryName :valuec.id/el-option/el-select/el-form-itemel-form-item label文章封面!-- auto-upload:是否自动上传action: 服务器接口路径name: 上传的文件字段名headers: 设置上传的请求头on-success: 上传成功的回调函数 --el-upload classavatar-uploader :auto-uploadtrue :show-file-listfalseaction/api/uploadnamefile:headers{Authorization:tokenStore.token}:on-successuploadSuccessimg v-ifarticleModel.coverImg :srcarticleModel.coverImg classavatar /el-icon v-else classavatar-uploader-iconPlus //el-icon/el-upload/el-form-itemel-form-item label文章内容div classeditorquill-editor themesnow v-model:contentarticleModel.content contentTypehtml !-- 七 3.3.3页面使用quill组件 --/quill-editor/div/el-form-itemel-form-itemel-button typeprimary发布/el-buttonel-button typeinfo草稿/el-button/el-form-item/el-form/el-drawer/el-card
/template
style langscss scoped
.page-container {min-height: 100%;box-sizing: border-box;.header {display: flex;align-items: center;justify-content: space-between;}
}/* 七 3.1 抽屉样式 */
.avatar-uploader {:deep() {.avatar {width: 178px;height: 178px;display: block;}.el-upload {border: 1px dashed var(--el-border-color);border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;transition: var(--el-transition-duration-fast);}.el-upload:hover {border-color: var(--el-color-primary);}.el-icon.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;text-align: center;}}
}
.editor {width: 100%;:deep(.ql-editor) {min-height: 200px;}
}
// 七 3.3.4 样式
.editor {width: 100%; :deep(.ql-editor) {min-height: 200px;}
}
/style 3.5 添加文章接口调用
3.5.1 article.js中提供添加文章函数 //导入request.js请求工具
import request from /utils/request.js
// 六 2.4 导入/stores/token.js
import { useTokenStore } from /stores/token.js// 六 文章分类列表查询函数
export const articleCategoryListService (registerData) { // 六 2.4获取token状态// 六 3.2// const tokenStore useTokenStore()//通过请求头Authorization携带token// return request.get(/category, { headers: { Authorization: tokenStore.token } });return request.get(/category);}// 六 6.2 文章分类 添加文章分类 函数 json 参数
export const articleCategoryAddService (categoryModel) {return request.post(/category, categoryModel)
}//六 7.3.1 修改分类
export const articleCategoryUpdateService (categoryModel){return request.put(/category,categoryModel)}//六 8.1 删除分类
export const articleCategoryDeleteService (id) {return request.delete(/category?idid)
}// 七 1.4.1 文章列表查询
export const articleListService (params) {return request.get(/article, { params: params })}//七 3.5.1添加文章
export const articleAddService (articleModel){return request.post(/article,articleModel)
}
3.5.2 为已发布和草稿按钮绑定事件
el-form-itemel-button typeprimary clickaddArticle(已发布)发布/el-buttonel-button typeinfo clickaddArticle(草稿)草稿/el-button
/el-form-item
3.5.3 ArticleManage.vue中提供addArticle函数完成添加文章接口的调用
const addArticleasync (state){articleModel.value.state statelet result await articleAddService(articleModel.value);ElMessage.success(result.message? result.message:添加成功)//再次调用getArticles,获取文章articleList()//隐藏抽屉visibleDrawer.valuefalse
}
script setup
import {Edit,Delete
} from element-plus/icons-vueimport { ref } from vue// 七 3.5.3
import { ElMessage } from element-plus// 七 1.3 articleCategoryListService搜索查询条件 文章列表查询 导入 七 1.4.2 articleListService 七3.5.1 articleAddService
import { articleCategoryListService ,articleListService,articleAddService} from /api/article.js// 七 3.1 导入添加抽屉组件
import {Plus} from element-plus/icons-vue// 七 3.3.2 导入组件和样式
import { QuillEditor } from vueup/vue-quill
import vueup/vue-quill/dist/vue-quill.snow.css// 七 3.4 文章封面上传 导入token
import { useTokenStore } from /stores/token.js
const tokenStore useTokenStore();
// 七 3.4 上传图片成功回调
const uploadSuccess (img) {//img就是后台响应的数据格式为{code:状态码message提示信息data: 图片的存储地址}articleModel.value.coverImgimg.data
}// 七 3.1 控制抽屉是否显示
const visibleDrawer ref(false)
// 七 3.1 添加表单数据模型
const articleModel ref({title: ,categoryId: ,coverImg: ,content:,state:
})//文章分类数据模型
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}
])//用户搜索时选中的分类id
const categoryIdref()//用户搜索时选中的发布状态
const stateref()//文章列表数据模型
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},{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},{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(20)//总条数
const pageSize ref(3)//每页条数//当每页条数发生了变化调用此函数
const onSizeChange (size) {pageSize.value sizearticleList() //七 1.4.3 **当分页条的当前页和每页条数发生变化重新发送请求获取数据**
}
//当前页码发生变化调用此函数
const onCurrentChange (num) {pageNum.value numarticleList() //七 1.4.3 **当分页条的当前页和每页条数发生变化重新发送请求获取数据**
}// 七 1.3 搜索查询条件 文章列表查询
const getArticleCategoryList async () {//获取所有分类let resultC await articleCategoryListService();categorys.value resultC.data
}// 七 1.4.2 获取文章列表数据
const articleList async () {let params {pageNum: pageNum.value,pageSize: pageSize.value,categoryId: categoryId.value ? categoryId.value : null,state: state.value ? state.value : null}let result await articleListService(params);//渲染视图total.value result.data.total;articles.value result.data.items;//处理数据,给数据模型扩展一个属性categoryName,分类名称for (let i 0; i articles.value.length; i) {let article articles.value[i];for (let j 0; j categorys.value.length; j) {if (article.categoryId categorys.value[j].id) {article.categoryName categorys.value[j].categoryName;}}}
}//七 1.3 搜索查询条件 文章列表查询 调用函数getArticleCategoryList
getArticleCategoryList();
// 七 1.4.2 获取文章列表数据
articleList();// 七 3.5.3 添加文章
const addArticleasync (state){articleModel.value.state statelet result await articleAddService(articleModel.value);ElMessage.success(result.message? result.message:添加成功)//再次调用getArticles,获取文章articleList()//隐藏抽屉visibleDrawer.valuefalse
}/script
templateel-card classpage-container!-- 1 标题 --template #headerdiv classheaderspan文章管理/spandiv classextrael-button typeprimary clickvisibleDrawer true添加文章/el-button !-- 七 3.2 clickvisibleDrawer true 绑定查询事件--/div/div/template!-- 2 搜索表单 --el-form inlineel-form-item label文章分类el-select placeholder请选择 v-modelcategoryIdel-option v-forc in categorys :keyc.id :labelc.categoryName:valuec.id/el-option/el-select/el-form-itemel-form-item label发布状态el-select placeholder请选择 v-modelstateel-option label已发布 value已发布/el-optionel-option label草稿 value草稿/el-option/el-select/el-form-itemel-form-itemel-button typeprimary clickarticleList搜索/el-button !-- 七 2 绑定查询事件--el-button clickcategoryId;state;articleList()重置/el-button !-- 七 2 绑定重置事件--/el-form-item/el-form!-- 3 文章列表 --el-table :dataarticles stylewidth: 100%el-table-column label文章标题 width400 proptitle/el-table-columnel-table-column label分类 propcategoryName/el-table-columnel-table-column label发表时间 propcreateTime /el-table-columnel-table-column label更新时间 propupdateTime /el-table-columnel-table-column label状态 propstate/el-table-columnel-table-column label操作 width100template #default{ row }el-button :iconEdit circle plain typeprimary/el-buttonel-button :iconDelete circle plain typedanger/el-button/template/el-table-columntemplate #emptyel-empty description没有数据 //template/el-table!-- 4 分页条 --el-pagination v-model:current-pagepageNum v-model:page-sizepageSize :page-sizes[3, 5 ,10, 15]layoutjumper, total, sizes, prev, pager, next background :totaltotal size-changeonSizeChangecurrent-changeonCurrentChange stylemargin-top: 20px; justify-content: flex-end /!-- 七 3.1 抽屉 --el-drawer v-modelvisibleDrawer title添加文章 directionrtl size50%!-- 添加文章表单 --el-form :modelarticleModel label-width100px el-form-item label文章标题 el-input v-modelarticleModel.title placeholder请输入标题/el-input/el-form-itemel-form-item label文章分类el-select placeholder请选择 v-modelarticleModel.categoryIdel-option v-forc in categorys :keyc.id :labelc.categoryName :valuec.id/el-option/el-select/el-form-itemel-form-item label文章封面!-- auto-upload:是否自动上传action: 服务器接口路径name: 上传的文件字段名headers: 设置上传的请求头on-success: 上传成功的回调函数 --el-upload classavatar-uploader :auto-uploadtrue :show-file-listfalseaction/api/uploadnamefile:headers{Authorization:tokenStore.token}:on-successuploadSuccessimg v-ifarticleModel.coverImg :srcarticleModel.coverImg classavatar /el-icon v-else classavatar-uploader-iconPlus //el-icon/el-upload/el-form-itemel-form-item label文章内容div classeditorquill-editor themesnow v-model:contentarticleModel.content contentTypehtml !-- 七 3.3.3页面使用quill组件 --/quill-editor/div/el-form-itemel-form-itemel-form-itemel-button typeprimary clickaddArticle(已发布)发布/el-button !-- 七 3.5.2 clickaddArticle(已发布)--el-button typeinfo clickaddArticle(草稿)草稿/el-button !-- 七 3.5.2clickaddArticle(草稿)--/el-form-item/el-form-item/el-form/el-drawer/el-card
/template
style langscss scoped
.page-container {min-height: 100%;box-sizing: border-box;.header {display: flex;align-items: center;justify-content: space-between;}
}/* 七 3.1 抽屉样式 */
.avatar-uploader {:deep() {.avatar {width: 178px;height: 178px;display: block;}.el-upload {border: 1px dashed var(--el-border-color);border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;transition: var(--el-transition-duration-fast);}.el-upload:hover {border-color: var(--el-color-primary);}.el-icon.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;text-align: center;}}
}
.editor {width: 100%;:deep(.ql-editor) {min-height: 200px;}
}
// 七 3.3.4 样式
.editor {width: 100%; :deep(.ql-editor) {min-height: 200px;}
}
/style
4、编辑文章
4.1 修改文章抽屉页面
修改文章抽屉和新增文章抽屉长的一样所以可以复用添加分类的弹窗
4.1.1 抽屉标题显示 定义标题
//抽屉标题
const titleref()
4.1.2 在抽屉上绑定标题 el-drawer v-modelvisibleDrawer :titletitle directionrtl size50%
4.1.3 为添加文章按钮绑定事件 el-button typeprimary clicktitle添加文章;visibleDrawer true添加文章/el-button
4.1.4 为修改文章按钮绑定事件 el-button :iconEdit circle plain typeprimary clicktitle修改文章;visibleDrawertrue/el-button
4.2 数据回显
当点击修改文章按钮时需要把当前这一条数据的详细信息显示到修改文章的抽屉上这个叫回显
4.2.1 通过插槽的方式得到被点击按钮所在行的数据 template #default{ row }el-button :iconEdit circle plain typeprimary clickshowDrawer(row)/el-button !--七4.1.4 clicktitle修改文章;visibleDrawertrue 七 4.2 showDrawer(row) --el-button :iconDelete circle plain typedanger/el-button/template
4.2.2 回显函数
// 七 4.2.2展示抽屉弹窗
const showDrawer (row){title.value 修改文章;visibleDrawer.value true//数据拷贝articleModel.value.title row.title;articleModel.value.categoryId row.categoryId;articleModel.value.coverImg row.coverImg;articleModel.value.content row.content;//扩展id属性将来需要传参给后台完成文章的修改articleModel.value.id row.id
}
script setup
import {Edit,Delete
} from element-plus/icons-vueimport { ref } from vue// 七 3.5.3
import { ElMessage } from element-plus// 七 1.3 articleCategoryListService搜索查询条件 文章列表查询 导入 七 1.4.2 articleListService 七3.5.1 articleAddService
import { articleCategoryListService ,articleListService,articleAddService} from /api/article.js// 七 3.1 导入添加抽屉组件
import {Plus} from element-plus/icons-vue// 七 3.3.2 导入组件和样式
import { QuillEditor } from vueup/vue-quill
import vueup/vue-quill/dist/vue-quill.snow.css// 七 3.4 文章封面上传 导入token
import { useTokenStore } from /stores/token.js
const tokenStore useTokenStore();
// 七 3.4 上传图片成功回调
const uploadSuccess (img) {//img就是后台响应的数据格式为{code:状态码message提示信息data: 图片的存储地址}articleModel.value.coverImgimg.data
}// 七 3.1 控制抽屉是否显示
const visibleDrawer ref(false)
// 七 3.1 添加表单数据模型
const articleModel ref({title: ,categoryId: ,coverImg: ,content:,state:
})//文章分类数据模型
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}
])//用户搜索时选中的分类id
const categoryIdref()//用户搜索时选中的发布状态
const stateref()//文章列表数据模型
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},{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},{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(20)//总条数
const pageSize ref(3)//每页条数//当每页条数发生了变化调用此函数
const onSizeChange (size) {pageSize.value sizearticleList() //七 1.4.3 **当分页条的当前页和每页条数发生变化重新发送请求获取数据**
}
//当前页码发生变化调用此函数
const onCurrentChange (num) {pageNum.value numarticleList() //七 1.4.3 **当分页条的当前页和每页条数发生变化重新发送请求获取数据**
}// 七 1.3 搜索查询条件 文章列表查询
const getArticleCategoryList async () {//获取所有分类let resultC await articleCategoryListService();categorys.value resultC.data
}// 七 1.4.2 获取文章列表数据
const articleList async () {let params {pageNum: pageNum.value,pageSize: pageSize.value,categoryId: categoryId.value ? categoryId.value : null,state: state.value ? state.value : null}let result await articleListService(params);//渲染视图total.value result.data.total;articles.value result.data.items;//处理数据,给数据模型扩展一个属性categoryName,分类名称for (let i 0; i articles.value.length; i) {let article articles.value[i];for (let j 0; j categorys.value.length; j) {if (article.categoryId categorys.value[j].id) {article.categoryName categorys.value[j].categoryName;}}}
}//七 1.3 搜索查询条件 文章列表查询 调用函数getArticleCategoryList
getArticleCategoryList();
// 七 1.4.2 获取文章列表数据
articleList();// 七 3.5.3 添加文章
const addArticleasync (state){articleModel.value.state statelet result await articleAddService(articleModel.value);ElMessage.success(result.message? result.message:添加成功)//再次调用getArticles,获取文章articleList()//隐藏抽屉visibleDrawer.valuefalse
}//!-- 七 4.1 抽屉:titletitle -绑定标题 --
const titleref()// 七 4.2.2展示抽屉弹窗
const showDrawer (row){title.value 修改文章;visibleDrawer.value true//数据拷贝articleModel.value.title row.title;articleModel.value.categoryId row.categoryId;articleModel.value.coverImg row.coverImg;articleModel.value.content row.content;//扩展id属性将来需要传参给后台完成文章的修改articleModel.value.id row.id
}/script
templateel-card classpage-container!-- 1 标题 --template #headerdiv classheaderspan文章管理/spandiv classextrael-button typeprimary clicktitle添加文章;visibleDrawer true添加文章/el-button !-- 七 3.2 clickvisibleDrawer true 绑定查询事件 七 4.1.3 title添加文章; --/div/div/template!-- 2 搜索表单 --el-form inlineel-form-item label文章分类el-select placeholder请选择 v-modelcategoryIdel-option v-forc in categorys :keyc.id :labelc.categoryName:valuec.id/el-option/el-select/el-form-itemel-form-item label发布状态el-select placeholder请选择 v-modelstateel-option label已发布 value已发布/el-optionel-option label草稿 value草稿/el-option/el-select/el-form-itemel-form-itemel-button typeprimary clickarticleList搜索/el-button !-- 七 2 绑定查询事件--el-button clickcategoryId;state;articleList()重置/el-button !-- 七 2 绑定重置事件--/el-form-item/el-form!-- 3 文章列表 --el-table :dataarticles stylewidth: 100%el-table-column label文章标题 width400 proptitle/el-table-columnel-table-column label分类 propcategoryName/el-table-columnel-table-column label发表时间 propcreateTime /el-table-columnel-table-column label更新时间 propupdateTime /el-table-columnel-table-column label状态 propstate/el-table-columnel-table-column label操作 width100template #default{ row }el-button :iconEdit circle plain typeprimary clickshowDrawer(row)/el-button !--七4.1.4 clicktitle修改文章;visibleDrawertrue 七 4.2.1 showDrawer(row) --el-button :iconDelete circle plain typedanger/el-button/template/el-table-columntemplate #emptyel-empty description没有数据 //template/el-table!-- 4 分页条 --el-pagination v-model:current-pagepageNum v-model:page-sizepageSize :page-sizes[3, 5 ,10, 15]layoutjumper, total, sizes, prev, pager, next background :totaltotal size-changeonSizeChangecurrent-changeonCurrentChange stylemargin-top: 20px; justify-content: flex-end /!-- 七 3.1 抽屉 --el-drawer v-modelvisibleDrawer :titletitle directionrtl size50% !-- 七 4.1 抽屉:titletitle -绑定标题 --!-- 添加文章表单 --el-form :modelarticleModel label-width100px el-form-item label文章标题 el-input v-modelarticleModel.title placeholder请输入标题/el-input/el-form-itemel-form-item label文章分类el-select placeholder请选择 v-modelarticleModel.categoryIdel-option v-forc in categorys :keyc.id :labelc.categoryName :valuec.id/el-option/el-select/el-form-itemel-form-item label文章封面!-- auto-upload:是否自动上传action: 服务器接口路径name: 上传的文件字段名headers: 设置上传的请求头on-success: 上传成功的回调函数 --el-upload classavatar-uploader :auto-uploadtrue :show-file-listfalseaction/api/uploadnamefile:headers{Authorization:tokenStore.token}:on-successuploadSuccessimg v-ifarticleModel.coverImg :srcarticleModel.coverImg classavatar /el-icon v-else classavatar-uploader-iconPlus //el-icon/el-upload/el-form-itemel-form-item label文章内容div classeditorquill-editor themesnow v-model:contentarticleModel.content contentTypehtml !-- 七 3.3.3页面使用quill组件 --/quill-editor/div/el-form-itemel-form-itemel-form-itemel-button typeprimary clickaddArticle(已发布)发布/el-button !-- 七 3.5.2 clickaddArticle(已发布)--el-button typeinfo clickaddArticle(草稿)草稿/el-button !-- 七 3.5.2clickaddArticle(草稿)--/el-form-item/el-form-item/el-form/el-drawer/el-card
/template
style langscss scoped
.page-container {min-height: 100%;box-sizing: border-box;.header {display: flex;align-items: center;justify-content: space-between;}
}/* 七 3.1 抽屉样式 */
.avatar-uploader {:deep() {.avatar {width: 178px;height: 178px;display: block;}.el-upload {border: 1px dashed var(--el-border-color);border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;transition: var(--el-transition-duration-fast);}.el-upload:hover {border-color: var(--el-color-primary);}.el-icon.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;text-align: center;}}
}
.editor {width: 100%;:deep(.ql-editor) {min-height: 200px;}
}
// 七 3.3.4 样式
.editor {width: 100%; :deep(.ql-editor) {min-height: 200px;}
}
/style
4.3 修改文章接口调用
4.3.1 article.js中提供修改文章的函数
//导入request.js请求工具
import request from /utils/request.js
// 六 2.4 导入/stores/token.js
import { useTokenStore } from /stores/token.js// 六 文章分类列表查询函数
export const articleCategoryListService (registerData) { // 六 2.4获取token状态// 六 3.2// const tokenStore useTokenStore()//通过请求头Authorization携带token// return request.get(/category, { headers: { Authorization: tokenStore.token } });return request.get(/category);}// 六 6.2 文章分类 添加文章分类 函数 json 参数
export const articleCategoryAddService (categoryModel) {return request.post(/category, categoryModel)
}//六 7.3.1 修改分类
export const articleCategoryUpdateService (categoryModel){return request.put(/category,categoryModel)}//六 8.1 删除分类
export const articleCategoryDeleteService (id) {return request.delete(/category?idid)
}// 七 1.4.1 文章列表查询
export const articleListService (params) {return request.get(/article, { params: params })}//七 3.5.1添加文章
export const articleAddService (articleModel){return request.post(/article,articleModel)
}//七 4.3.1修改文章
export const articleUpdateService (articleModel){return request.put(/article,articleModel)}
4.3.2 修改发布与草稿按钮的绑定事件
el-form-itemel-button typeprimary clicktitle添加文章? addArticle(已发布): updateArticle(已发布)发布/el-button !-- 七 3.5.2 clickaddArticle(已发布) 七 4.3.2 ;title添加文章? addArticle(已发布):updateArticle(已发布)--el-button typeinfo clicktitle添加文章? addArticle(草稿): updateArticle(草稿)草稿/el-button !-- 七 3.5.2clickaddArticle(草稿) 七 4.3.2 ;title添加文章? addArticle(草稿):updateArticle(草稿)--/el-form-item
4.3.3 调用接口完成修改的函数
//七 4.4.4 修改文章
const updateArticleasync (){let result await articleUpdateService(articleModel.value)ElMessage.success(result.message? result.message:修改成功)//隐藏抽屉visibleDrawer.valuefalse//再次访问后台接口查询所有文章articleList()
} 由于现在修改和新增共用了一个数据模型所以在点击添加文章后有时候会显示数据此时可以将articleModel中的数据清空
//七 4.4.3 清空模型数据
const clearArticleModel (){articleModel.value.title,articleModel.value.categoryId,articleModel.value.coverImg,articleModel.value.contentbr,articleModel.value.state
} 修改 添加文章按钮的点击事件
el-button typeprimary clicktitle添加文章;visibleDrawer true;clearArticleModel()添加文章/el-button !-- 七 3.2 clickvisibleDrawer true 绑定查询事件 七 4.1.3 title添加文章; 七 4.3.3 ;clearArticleModel() --script setup
import {Edit,Delete
} from element-plus/icons-vueimport { ref } from vue// 七 3.5.3
import { ElMessage } from element-plus// 七 1.3 articleCategoryListService搜索查询条件 文章列表查询 导入 七 1.4.2 articleListService 七3.5.1 articleAddService 七 4.3.3articleUpdateService
import { articleCategoryListService ,articleListService,articleAddService,articleUpdateService} from /api/article.js// 七 3.1 导入添加抽屉组件
import {Plus} from element-plus/icons-vue// 七 3.3.2 导入组件和样式
import { QuillEditor } from vueup/vue-quill
import vueup/vue-quill/dist/vue-quill.snow.css// 七 3.4 文章封面上传 导入token
import { useTokenStore } from /stores/token.js
const tokenStore useTokenStore();
// 七 3.4 上传图片成功回调
const uploadSuccess (img) {//img就是后台响应的数据格式为{code:状态码message提示信息data: 图片的存储地址}articleModel.value.coverImgimg.data
}// 七 3.1 控制抽屉是否显示
const visibleDrawer ref(false)
// 七 3.1 添加表单数据模型
const articleModel ref({title: ,categoryId: ,coverImg: ,content:,state:
})//文章分类数据模型
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}
])//用户搜索时选中的分类id
const categoryIdref()//用户搜索时选中的发布状态
const stateref()//文章列表数据模型
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},{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},{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(20)//总条数
const pageSize ref(3)//每页条数//当每页条数发生了变化调用此函数
const onSizeChange (size) {pageSize.value sizearticleList() //七 1.4.3 **当分页条的当前页和每页条数发生变化重新发送请求获取数据**
}
//当前页码发生变化调用此函数
const onCurrentChange (num) {pageNum.value numarticleList() //七 1.4.3 **当分页条的当前页和每页条数发生变化重新发送请求获取数据**
}// 七 1.3 搜索查询条件 文章列表查询
const getArticleCategoryList async () {//获取所有分类let resultC await articleCategoryListService();categorys.value resultC.data
}// 七 1.4.2 获取文章列表数据
const articleList async () {let params {pageNum: pageNum.value,pageSize: pageSize.value,categoryId: categoryId.value ? categoryId.value : null,state: state.value ? state.value : null}let result await articleListService(params);//渲染视图total.value result.data.total;articles.value result.data.items;//处理数据,给数据模型扩展一个属性categoryName,分类名称for (let i 0; i articles.value.length; i) {let article articles.value[i];for (let j 0; j categorys.value.length; j) {if (article.categoryId categorys.value[j].id) {article.categoryName categorys.value[j].categoryName;}}}
}//七 1.3 搜索查询条件 文章列表查询 调用函数getArticleCategoryList
getArticleCategoryList();
// 七 1.4.2 获取文章列表数据
articleList();// 七 3.5.3 添加文章
const addArticleasync (state){articleModel.value.state statelet result await articleAddService(articleModel.value);ElMessage.success(result.message? result.message:添加成功)//再次调用getArticles,获取文章articleList()//隐藏抽屉visibleDrawer.valuefalse
}//!-- 七 4.1 抽屉:titletitle -绑定标题 --
const titleref()// 七 4.2.2展示抽屉弹窗
const showDrawer (row){title.value 修改文章;visibleDrawer.value true//数据拷贝articleModel.value.title row.title;articleModel.value.categoryId row.categoryId;articleModel.value.coverImg row.coverImg;articleModel.value.content row.content;//扩展id属性将来需要传参给后台完成文章的修改articleModel.value.id row.id
}//七 4.4.3 修改文章
const updateArticleasync (){let result await articleUpdateService(articleModel.value)ElMessage.success(result.message? result.message:修改成功)//隐藏抽屉visibleDrawer.valuefalse//再次访问后台接口查询所有文章articleList()
}//七 4.4.3 清空模型数据
const clearArticleModel (){articleModel.value.title,articleModel.value.categoryId,articleModel.value.coverImg,articleModel.value.content , // 这里清空不了富文本没有找到解决方案 暂时多了一个空格处理articleModel.value.state
}/script
templateel-card classpage-container!-- 1 标题 --template #headerdiv classheaderspan文章管理/spandiv classextrael-button typeprimary clicktitle添加文章;visibleDrawer true;clearArticleModel()添加文章/el-button !-- 七 3.2 clickvisibleDrawer true 绑定查询事件 七 4.1.3 title添加文章; 七 4.3.3 ;clearArticleModel() --/div/div/template!-- 2 搜索表单 --el-form inlineel-form-item label文章分类el-select placeholder请选择 v-modelcategoryIdel-option v-forc in categorys :keyc.id :labelc.categoryName:valuec.id/el-option/el-select/el-form-itemel-form-item label发布状态el-select placeholder请选择 v-modelstateel-option label已发布 value已发布/el-optionel-option label草稿 value草稿/el-option/el-select/el-form-itemel-form-itemel-button typeprimary clickarticleList搜索/el-button !-- 七 2 绑定查询事件--el-button clickcategoryId;state;articleList()重置/el-button !-- 七 2 绑定重置事件--/el-form-item/el-form!-- 3 文章列表 --el-table :dataarticles stylewidth: 100%el-table-column label文章标题 width400 proptitle/el-table-columnel-table-column label分类 propcategoryName/el-table-columnel-table-column label发表时间 propcreateTime /el-table-columnel-table-column label更新时间 propupdateTime /el-table-columnel-table-column label状态 propstate/el-table-columnel-table-column label操作 width100template #default{ row }el-button :iconEdit circle plain typeprimary clickshowDrawer(row)/el-button !--七4.1.4 clicktitle修改文章;visibleDrawertrue 七 4.2.1 showDrawer(row) --el-button :iconDelete circle plain typedanger/el-button/template/el-table-columntemplate #emptyel-empty description没有数据 //template/el-table!-- 4 分页条 --el-pagination v-model:current-pagepageNum v-model:page-sizepageSize :page-sizes[3, 5 ,10, 15]layoutjumper, total, sizes, prev, pager, next background :totaltotal size-changeonSizeChangecurrent-changeonCurrentChange stylemargin-top: 20px; justify-content: flex-end /!-- 七 3.1 抽屉 --el-drawer v-modelvisibleDrawer :titletitle directionrtl size50% !-- 七 4.1 抽屉:titletitle -绑定标题 --!-- 添加文章表单 --el-form :modelarticleModel label-width100px el-form-item label文章标题 el-input v-modelarticleModel.title placeholder请输入标题/el-input/el-form-itemel-form-item label文章分类el-select placeholder请选择 v-modelarticleModel.categoryIdel-option v-forc in categorys :keyc.id :labelc.categoryName :valuec.id/el-option/el-select/el-form-itemel-form-item label文章封面!-- auto-upload:是否自动上传action: 服务器接口路径name: 上传的文件字段名headers: 设置上传的请求头on-success: 上传成功的回调函数 --el-upload classavatar-uploader :auto-uploadtrue :show-file-listfalseaction/api/uploadnamefile:headers{Authorization:tokenStore.token}:on-successuploadSuccessimg v-ifarticleModel.coverImg :srcarticleModel.coverImg classavatar /el-icon v-else classavatar-uploader-iconPlus //el-icon/el-upload/el-form-itemel-form-item label文章内容div classeditorquill-editor themesnow v-model:contentarticleModel.content contentTypehtml !-- 七 3.3.3页面使用quill组件 --/quill-editor/div/el-form-itemel-form-itemel-form-itemel-button typeprimary clicktitle添加文章? addArticle(已发布): updateArticle(已发布)发布/el-button !-- 七 3.5.2 clickaddArticle(已发布) 七 4.3.2 ;title添加文章? addArticle(已发布):updateArticle(已发布)--el-button typeinfo clicktitle添加文章? addArticle(草稿): updateArticle(草稿)草稿/el-button !-- 七 3.5.2clickaddArticle(草稿) 七 4.3.2 ;title添加文章? addArticle(草稿):updateArticle(草稿)--/el-form-item/el-form-item/el-form/el-drawer/el-card
/template
style langscss scoped
.page-container {min-height: 100%;box-sizing: border-box;.header {display: flex;align-items: center;justify-content: space-between;}
}/* 七 3.1 抽屉样式 */
.avatar-uploader {:deep() {.avatar {width: 178px;height: 178px;display: block;}.el-upload {border: 1px dashed var(--el-border-color);border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;transition: var(--el-transition-duration-fast);}.el-upload:hover {border-color: var(--el-color-primary);}.el-icon.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;text-align: center;}}
}
.editor {width: 100%;:deep(.ql-editor) {min-height: 200px;}
}
// 七 3.3.4 样式
.editor {width: 100%; :deep(.ql-editor) {min-height: 200px;}
}
/style
5、删除文章
5.1 接口调用
article.js中提供删除分类的函数
//导入request.js请求工具
import request from /utils/request.js
// 六 2.4 导入/stores/token.js
import { useTokenStore } from /stores/token.js// 六 文章分类列表查询函数
export const articleCategoryListService (registerData) { // 六 2.4获取token状态// 六 3.2// const tokenStore useTokenStore()//通过请求头Authorization携带token// return request.get(/category, { headers: { Authorization: tokenStore.token } });return request.get(/category);}// 六 6.2 文章分类 添加文章分类 函数 json 参数
export const articleCategoryAddService (categoryModel) {return request.post(/category, categoryModel)
}//六 7.3.1 修改分类
export const articleCategoryUpdateService (categoryModel){return request.put(/category,categoryModel)}//六 8.1 删除分类
export const articleCategoryDeleteService (id) {return request.delete(/category?idid)
}// 七 1.4.1 文章列表查询
export const articleListService (params) {return request.get(/article, { params: params })}//七 3.5.1添加文章
export const articleAddService (articleModel){return request.post(/article,articleModel)
}//七 4.3.1修改文章
export const articleUpdateService (articleModel){return request.put(/article,articleModel)}//七 5.1删除文章
export const articleDeleteService (id) {return request.delete(/article?idid)}
5.2 为删除按钮绑定事件 template #default{ row }el-button :iconEdit circle plain typeprimary clickshowDrawer(row)/el-button !--七4.1.4 clicktitle修改文章;visibleDrawertrue 七 4.2.1 showDrawer(row) --el-button :iconDelete circle plain typedanger clickdeleteArticle(row)/el-button !-- 七 5.2 为删除按钮绑定事件--/template
5.3 添加删除弹窗当用户点击确认后调用接口删除分类
script setup
import {Edit,Delete
} from element-plus/icons-vueimport { ref } from vue// 七 3.5.3
import { ElMessage,ElMessageBox } from element-plus// 七 1.3 articleCategoryListService搜索查询条件 文章列表查询 导入 七 1.4.2 articleListService 七3.5.1 articleAddService 七 4.3.3articleUpdateService 七 5.3 articleDeleteService
import { articleCategoryListService ,articleListService,articleAddService,articleUpdateService,articleDeleteService} from /api/article.js// 七 3.1 导入添加抽屉组件
import {Plus} from element-plus/icons-vue// 七 3.3.2 导入组件和样式
import { QuillEditor } from vueup/vue-quill
import vueup/vue-quill/dist/vue-quill.snow.css// 七 3.4 文章封面上传 导入token
import { useTokenStore } from /stores/token.js
const tokenStore useTokenStore();
// 七 3.4 上传图片成功回调
const uploadSuccess (img) {//img就是后台响应的数据格式为{code:状态码message提示信息data: 图片的存储地址}articleModel.value.coverImgimg.data
}// 七 3.1 控制抽屉是否显示
const visibleDrawer ref(false)
// 七 3.1 添加表单数据模型
const articleModel ref({title: ,categoryId: ,coverImg: ,content:,state:
})//文章分类数据模型
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}
])//用户搜索时选中的分类id
const categoryIdref()//用户搜索时选中的发布状态
const stateref()//文章列表数据模型
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},{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},{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(20)//总条数
const pageSize ref(3)//每页条数//当每页条数发生了变化调用此函数
const onSizeChange (size) {pageSize.value sizearticleList() //七 1.4.3 **当分页条的当前页和每页条数发生变化重新发送请求获取数据**
}
//当前页码发生变化调用此函数
const onCurrentChange (num) {pageNum.value numarticleList() //七 1.4.3 **当分页条的当前页和每页条数发生变化重新发送请求获取数据**
}// 七 1.3 搜索查询条件 文章列表查询
const getArticleCategoryList async () {//获取所有分类let resultC await articleCategoryListService();categorys.value resultC.data
}// 七 1.4.2 获取文章列表数据
const articleList async () {let params {pageNum: pageNum.value,pageSize: pageSize.value,categoryId: categoryId.value ? categoryId.value : null,state: state.value ? state.value : null}let result await articleListService(params);//渲染视图total.value result.data.total;articles.value result.data.items;//处理数据,给数据模型扩展一个属性categoryName,分类名称for (let i 0; i articles.value.length; i) {let article articles.value[i];for (let j 0; j categorys.value.length; j) {if (article.categoryId categorys.value[j].id) {article.categoryName categorys.value[j].categoryName;}}}
}//七 1.3 搜索查询条件 文章列表查询 调用函数getArticleCategoryList
getArticleCategoryList();
// 七 1.4.2 获取文章列表数据
articleList();// 七 3.5.3 添加文章
const addArticleasync (state){articleModel.value.state statelet result await articleAddService(articleModel.value);ElMessage.success(result.message? result.message:添加成功)//再次调用getArticles,获取文章articleList()//隐藏抽屉visibleDrawer.valuefalse
}//!-- 七 4.1 抽屉:titletitle -绑定标题 --
const titleref()// 七 4.2.2展示抽屉弹窗
const showDrawer (row){title.value 修改文章;visibleDrawer.value true//数据拷贝articleModel.value.title row.title;articleModel.value.categoryId row.categoryId;articleModel.value.coverImg row.coverImg;articleModel.value.content row.content;//扩展id属性将来需要传参给后台完成文章的修改articleModel.value.id row.id
}//七 4.4.3 修改文章
const updateArticleasync (state){articleModel.value.state statelet result await articleUpdateService(articleModel.value)ElMessage.success(result.message? result.message:修改成功)//隐藏抽屉visibleDrawer.valuefalse//再次访问后台接口查询所有文章articleList()
}//七 4.4.3 清空模型数据
const clearArticleModel (){articleModel.value.title,articleModel.value.categoryId,articleModel.value.coverImg,articleModel.value.content , // 这里清空不了富文本没有找到解决方案 暂时多了一个空格处理articleModel.value.state
}// 七 5.3 删除文章记得引用ElMessageBox 给删除按钮绑定事件
const deleteArticle (row) {// 提示用户 确认框ElMessageBox.confirm(你确认删除该文章信息吗,温馨提示,{confirmButtonText: 确认,cancelButtonText: 取消,type: warning,}).then(async() {//用户点击了确认let result await articleDeleteService(row.id)// 给出提示ElMessage({type: success,message: 删除成功,})//刷新列表//再次访问后台接口查询所有文章articleList()}).catch(() {//用户点击了取消ElMessage({type: info,message: 取消删除,})})
}/script
templateel-card classpage-container!-- 1 标题 --template #headerdiv classheaderspan文章管理/spandiv classextrael-button typeprimary clicktitle添加文章;visibleDrawer true;clearArticleModel()添加文章/el-button !-- 七 3.2 clickvisibleDrawer true 绑定查询事件 七 4.1.3 title添加文章; 七 4.3.3 ;clearArticleModel() --/div/div/template!-- 2 搜索表单 --el-form inlineel-form-item label文章分类el-select placeholder请选择 v-modelcategoryIdel-option v-forc in categorys :keyc.id :labelc.categoryName:valuec.id/el-option/el-select/el-form-itemel-form-item label发布状态el-select placeholder请选择 v-modelstateel-option label已发布 value已发布/el-optionel-option label草稿 value草稿/el-option/el-select/el-form-itemel-form-itemel-button typeprimary clickarticleList搜索/el-button !-- 七 2 绑定查询事件--el-button clickcategoryId;state;articleList()重置/el-button !-- 七 2 绑定重置事件--/el-form-item/el-form!-- 3 文章列表 --el-table :dataarticles stylewidth: 100%el-table-column label文章标题 width400 proptitle/el-table-columnel-table-column label分类 propcategoryName/el-table-columnel-table-column label发表时间 propcreateTime /el-table-columnel-table-column label更新时间 propupdateTime /el-table-columnel-table-column label状态 propstate/el-table-columnel-table-column label操作 width100template #default{ row }el-button :iconEdit circle plain typeprimary clickshowDrawer(row)/el-button !--七4.1.4 clicktitle修改文章;visibleDrawertrue 七 4.2.1 showDrawer(row) --el-button :iconDelete circle plain typedanger clickdeleteArticle(row)/el-button !-- 七 5.2 为删除按钮绑定事件--/template/el-table-columntemplate #emptyel-empty description没有数据 //template/el-table!-- 4 分页条 --el-pagination v-model:current-pagepageNum v-model:page-sizepageSize :page-sizes[3, 5 ,10, 15]layoutjumper, total, sizes, prev, pager, next background :totaltotal size-changeonSizeChangecurrent-changeonCurrentChange stylemargin-top: 20px; justify-content: flex-end /!-- 七 3.1 抽屉 --el-drawer v-modelvisibleDrawer :titletitle directionrtl size50% !-- 七 4.1 抽屉:titletitle -绑定标题 --!-- 添加文章表单 --el-form :modelarticleModel label-width100px el-form-item label文章标题 el-input v-modelarticleModel.title placeholder请输入标题/el-input/el-form-itemel-form-item label文章分类el-select placeholder请选择 v-modelarticleModel.categoryIdel-option v-forc in categorys :keyc.id :labelc.categoryName :valuec.id/el-option/el-select/el-form-itemel-form-item label文章封面!-- auto-upload:是否自动上传action: 服务器接口路径name: 上传的文件字段名headers: 设置上传的请求头on-success: 上传成功的回调函数 --el-upload classavatar-uploader :auto-uploadtrue :show-file-listfalseaction/api/uploadnamefile:headers{Authorization:tokenStore.token}:on-successuploadSuccessimg v-ifarticleModel.coverImg :srcarticleModel.coverImg classavatar /el-icon v-else classavatar-uploader-iconPlus //el-icon/el-upload/el-form-itemel-form-item label文章内容div classeditorquill-editor themesnow v-model:contentarticleModel.content contentTypehtml !-- 七 3.3.3页面使用quill组件 --/quill-editor/div/el-form-itemel-form-itemel-form-itemel-button typeprimary clicktitle添加文章? addArticle(已发布): updateArticle(已发布)发布/el-button !-- 七 3.5.2 clickaddArticle(已发布) 七 4.3.2 ;title添加文章? addArticle(已发布):updateArticle(已发布)--el-button typeinfo clicktitle添加文章? addArticle(草稿): updateArticle(草稿)草稿/el-button !-- 七 3.5.2clickaddArticle(草稿) 七 4.3.2 ;title添加文章? addArticle(草稿):updateArticle(草稿)--/el-form-item/el-form-item/el-form/el-drawer/el-card
/template
style langscss scoped
.page-container {min-height: 100%;box-sizing: border-box;.header {display: flex;align-items: center;justify-content: space-between;}
}/* 七 3.1 抽屉样式 */
.avatar-uploader {:deep() {.avatar {width: 178px;height: 178px;display: block;}.el-upload {border: 1px dashed var(--el-border-color);border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;transition: var(--el-transition-duration-fast);}.el-upload:hover {border-color: var(--el-color-primary);}.el-icon.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 178px;height: 178px;text-align: center;}}
}
.editor {width: 100%;:deep(.ql-editor) {min-height: 200px;}
}
// 七 3.3.4 样式
.editor {width: 100%; :deep(.ql-editor) {min-height: 200px;}
}
/style
八、顶部导航栏信息展示 在Layout.vue中页面加载完就发送请求获取个人信息展示并存储到pinia中因为将来在个人中心中修改信息的时候还需要使用
1、 user.js中提供获取个人信息的函数
//导入request.js请求工具
import request from /utils/request.js// 二 注册函数 提供调用注册接口的函数
export const userRegisterService (registerData) {//借助于URLSearchParam完成参数传递const params new URLSearchParams();for(let key in registerData){params.append(key,registerData[key]);}return request.post(/user/register,params);}// 三 登录函数 提供调用登录接口的函数
export const userLoginService (loginData){//借助于URLSearchParam完成参数传递const params new URLSearchParams();for(let key in loginData){params.append(key,loginData[key]);}return request.post(/user/login,params);}// 八 1获取个人信息
export const userInfoGetService (){return request.get(/user/userInfo);}
2、 src/stores/userInfo.js中定义个人中心状态
import {defineStore} from pinia
import {ref} from vue
const useUserInfoStore defineStore(userInfo,(){//定义状态相关的内容const info ref({})const setInfo (newInfo){info.value newInfo}const removeInfo (){info.value {}}return {info,setInfo,removeInfo}},{persist:true})export default useUserInfoStore; 3、 Layout.vue中获取个人信息并存储到pinia中
import {ref} from vue
//导入接口函数
import { userInfoGetService } from /api/user.js
//导入pinia
import useUserInfoStore from /stores/userInfo.jsconst userInfoStore useUserInfoStore();
//获取个人信息
const getUserInfo async (){let result await userInfoGetService();//存储piniauserInfoStore.info result.data;
}
getUserInfo() 4、Layout.vue的顶部导航栏中展示昵称和头像 div编码集中营strong{{ userInfoStore.info.nickname ? userInfoStore.info.nickname : userInfoStore.info.usrename }}/strong/div el-avatar :srcuserInfoStore.info.userPic ? userInfoStore.info.userPic : avatar /
全部Layout.vuescript setup
import {Management,Promotion,UserFilled,User,Crop,EditPen,SwitchButton,CaretBottom
} from element-plus/icons-vue
import avatar from /assets/default.png
// 八 3 个人用户信息
import {ref} from vue
//导入接口函数
import { userInfoGetService } from /api/user.js
//导入pinia
import useUserInfoStore from /stores/userInfo.jsconst userInfoStore useUserInfoStore();
//获取个人信息
const getUserInfo async (){let result await userInfoGetService();//存储piniauserInfoStore.info result.data;
}
getUserInfo()/scripttemplate!-- element-plus 中的容器 --el-container classlayout-container!-- 左侧菜单 --el-aside width200pxdiv classel-aside__logo/div!-- element-plus 菜单标签 --el-menu active-text-color#ffd04b background-color#232323 text-color#fffrouterel-menu-item index/article/category !-- 五 3.3 --el-iconManagement //el-iconspan文章分类/span/el-menu-itemel-menu-item index/article/manage !-- 五 3.3 --el-iconPromotion //el-iconspan文章管理/span/el-menu-itemel-sub-menu template #titleel-iconUserFilled //el-iconspan个人中心/span/templateel-menu-item index/user/info !-- 五 3.3 --el-iconUser //el-iconspan基本资料/span/el-menu-itemel-menu-item index/user/avatar !-- 五 3.3 --el-iconCrop //el-iconspan更换头像/span/el-menu-itemel-menu-item index/user/resetPassword !-- 五 3.3 --el-iconEditPen //el-iconspan重置密码/span/el-menu-item/el-sub-menu/el-menu/el-aside!-- 右侧主区域 --el-container!-- 头部区域 --el-headerdiv编码集中营strong{{ userInfoStore.info.nickname ? userInfoStore.info.nickname : userInfoStore.info.usrename }}/strong/div !-- 八 4 {{ userInfoStore.info.nickname ? userInfoStore.info.nickname : userInfoStore.info.usrename }}--el-dropdown placementbottom-endspan classel-dropdown__boxel-avatar :srcuserInfoStore.info.userPic ? userInfoStore.info.userPic : avatar / !-- 八 4 userInfoStore.info.userPic ? userInfoStore.info.userPic : avatar--el-iconCaretBottom //el-icon/spantemplate #dropdownel-dropdown-menuel-dropdown-item commandprofile :iconUser基本资料/el-dropdown-itemel-dropdown-item commandavatar :iconCrop更换头像/el-dropdown-itemel-dropdown-item commandpassword :iconEditPen重置密码/el-dropdown-itemel-dropdown-item commandlogout :iconSwitchButton退出登录/el-dropdown-item/el-dropdown-menu/template/el-dropdown/el-header!-- 中间区域 --el-main!-- 五 3.3 --!-- div stylewidth: 1290px; height: 570px;border: 1px solid red;内容展示区/div --router-view/router-view/el-main!-- 底部区域 --el-footer国际大事件 ©2023 Created by 春天的菠菜/el-footer/el-container/el-container
/templatestyle langscss scoped
.layout-container {height: 100vh;.el-aside {background-color: #232323;__logo {height: 120px;background: url(/assets/logo.png) no-repeat center / 120px auto;}.el-menu {border-right: none;}}.el-header {background-color: #fff;display: flex;align-items: center;justify-content: space-between;.el-dropdown__box {display: flex;align-items: center;.el-icon {color: #999;margin-left: 10px;}:active,:focus {outline: none;}}}.el-footer {display: flex;align-items: center;justify-content: center;font-size: 14px;color: #666;}
}
/style
九 、页面右上角下拉菜单el-dropdown中功能实现
在el-dropdown中有四个子条目分别是 基本资料 更换头像 重置密码 退出登录
其中其三个起到路由功能跟左侧菜单中【个人中心】下面的二级菜单是同样的功能退出登录需要删除本地pinia中存储的token以及userInfo 1、路由实现
在el-dropdown-item标签上添加command属性属性值和路由表中/user/xxx保持一致
!-- 下拉菜单--!-- command 条目被点击后会触发在事件函数上可以声明一个参数接收条目对应的指令 --el-dropdown placementbottom-end commandhandleCommandspan classel-dropdown__boxel-avatar :srcuserInfoStore.info.userPic ? userInfoStore.info.userPic : avatar / !-- 八 4 userInfoStore.info.userPic ? userInfoStore.info.userPic : avatar--el-iconCaretBottom //el-icon/spantemplate #dropdownel-dropdown-menuel-dropdown-item commandinfo :iconUser基本资料/el-dropdown-itemel-dropdown-item commandavatar :iconCrop更换头像/el-dropdown-itemel-dropdown-item commandresetPassword :iconEditPen重置密码/el-dropdown-itemel-dropdown-item commandlogout :iconSwitchButton退出登录/el-dropdown-item/el-dropdown-menu/template/el-dropdown
在el-dropdown标签上绑定command事件,当有条目被点击后会触发这个事件
el-dropdown placementbottom-end commandhandleCommand
提供handleCommand函数参数为点击条目的command属性值
//dropDown条目被点击后回调的函数
import {useRouter} from vue-router
const router useRouter()
const handleCommand (command){if(commandlogout){//退出登录alert(退出登录)}else{//路由router.push(/user/command)}
} 2、 退出登录实现
script setup
import {Management,Promotion,UserFilled,User,Crop,EditPen,SwitchButton,CaretBottom
} from element-plus/icons-vue
import avatar from /assets/default.png
// 九 1
//dropDown条目被点击后回调的函数
import {useRouter} from vue-router//九 2
import {ElMessage,ElMessageBox} from element-plus
import { useTokenStore } from /stores/token.js// 八 3 个人用户信息
import {ref} from vue
//导入接口函数
import { userInfoGetService } from /api/user.js
//导入pinia
import useUserInfoStore from /stores/userInfo.jsconst userInfoStore useUserInfoStore();
//获取个人信息
const getUserInfo async (){let result await userInfoGetService();//存储piniauserInfoStore.info result.data;
}
getUserInfo()
//九 2const tokenStore useTokenStore()
//九 1
const router useRouter()
const handleCommand (command){//判断指令if(command logout){//退出登录ElMessageBox.confirm(您确认要退出吗?,温馨提示,{confirmButtonText: 确认,cancelButtonText: 取消,type: warning,}).then(async () {//退出登录//1.清空pinia中存储的token以及个人信息tokenStore.removeToken()userInfoStore.removeInfo()//2.跳转到登录页面router.push(/login)ElMessage({type: success,message: 退出登录成功,})}).catch(() {ElMessage({type: info,message: 用户取消了退出登录,})})}else{//路由router.push(/user/command)}
}/scripttemplate!-- element-plus 中的容器 --el-container classlayout-container!-- 左侧菜单 --el-aside width200pxdiv classel-aside__logo/div!-- element-plus 菜单标签 --el-menu active-text-color#ffd04b background-color#232323 text-color#fffrouterel-menu-item index/article/category !-- 五 3.3 --el-iconManagement //el-iconspan文章分类/span/el-menu-itemel-menu-item index/article/manage !-- 五 3.3 --el-iconPromotion //el-iconspan文章管理/span/el-menu-itemel-sub-menu template #titleel-iconUserFilled //el-iconspan个人中心/span/templateel-menu-item index/user/info !-- 五 3.3 --el-iconUser //el-iconspan基本资料/span/el-menu-itemel-menu-item index/user/avatar !-- 五 3.3 --el-iconCrop //el-iconspan更换头像/span/el-menu-itemel-menu-item index/user/resetPassword !-- 五 3.3 --el-iconEditPen //el-iconspan重置密码/span/el-menu-item/el-sub-menu/el-menu/el-aside!-- 右侧主区域 --el-container!-- 头部区域 --el-headerdiv编码集中营strong{{ userInfoStore.info.nickname ? userInfoStore.info.nickname : userInfoStore.info.usrename }}/strong/div !-- 八 4 {{ userInfoStore.info.nickname ? userInfoStore.info.nickname : userInfoStore.info.usrename }}--!-- 下拉菜单--!-- command 条目被点击后会触发在事件函数上可以声明一个参数接收条目对应的指令 --el-dropdown placementbottom-end commandhandleCommandspan classel-dropdown__boxel-avatar :srcuserInfoStore.info.userPic ? userInfoStore.info.userPic : avatar / !-- 八 4 userInfoStore.info.userPic ? userInfoStore.info.userPic : avatar--el-iconCaretBottom //el-icon/spantemplate #dropdownel-dropdown-menuel-dropdown-item commandinfo :iconUser基本资料/el-dropdown-itemel-dropdown-item commandavatar :iconCrop更换头像/el-dropdown-itemel-dropdown-item commandresetPassword :iconEditPen重置密码/el-dropdown-itemel-dropdown-item commandlogout :iconSwitchButton退出登录/el-dropdown-item/el-dropdown-menu/template/el-dropdown/el-header!-- 中间区域 --el-main!-- 五 3.3 --!-- div stylewidth: 1290px; height: 570px;border: 1px solid red;内容展示区/div --router-view/router-view/el-main!-- 底部区域 --el-footer国际大事件 ©2023 Created by 春天的菠菜/el-footer/el-container/el-container
/templatestyle langscss scoped
.layout-container {height: 100vh;.el-aside {background-color: #232323;__logo {height: 120px;background: url(/assets/logo.png) no-repeat center / 120px auto;}.el-menu {border-right: none;}}.el-header {background-color: #fff;display: flex;align-items: center;justify-content: space-between;.el-dropdown__box {display: flex;align-items: center;.el-icon {color: #999;margin-left: 10px;}:active,:focus {outline: none;}}}.el-footer {display: flex;align-items: center;justify-content: center;font-size: 14px;color: #666;}
}
/style
十、基本资料修改 1、 基本资料页面组件UserInfo.vue
views\user\UserInfo.vue
script setup
import { ref } from vue
const userInfo ref({id: 0,username: zhangsan,nickname: zs,email: zs163.com,
})
const rules {nickname: [{ required: true, message: 请输入用户昵称, trigger: blur },{pattern: /^\S{2,10}$/,message: 昵称必须是2-10位的非空字符串,trigger: blur}],email: [{ required: true, message: 请输入用户邮箱, trigger: blur },{ type: email, message: 邮箱格式不正确, trigger: blur }]
}
/script
templateel-card classpage-containertemplate #headerdiv classheaderspan基本资料/span/div/templateel-rowel-col :span12el-form :modeluserInfo :rulesrules label-width100px sizelargeel-form-item label登录名称el-input v-modeluserInfo.username disabled/el-input/el-form-itemel-form-item label用户昵称 propnicknameel-input v-modeluserInfo.nickname/el-input/el-form-itemel-form-item label用户邮箱 propemailel-input v-modeluserInfo.email/el-input/el-form-itemel-form-itemel-button typeprimary提交修改/el-button/el-form-item/el-form/el-col/el-row/el-card
/template 2、 表单数据回显
个人信息之前已经存储到了pinia中只需要从pinia中获取个人信息替换模板数据即可
// 十 2
import useUserInfoStore from /stores/userInfo.js;
const userInfoStore useUserInfoStore()
const userInfo ref({...userInfoStore.info}) 3、 接口调用
在src/api/user.js中提供修改基本资料的函数
//导入request.js请求工具
import request from /utils/request.js// 二 注册函数 提供调用注册接口的函数
export const userRegisterService (registerData) {//借助于URLSearchParam完成参数传递const params new URLSearchParams();for(let key in registerData){params.append(key,registerData[key]);}return request.post(/user/register,params);}// 三 登录函数 提供调用登录接口的函数
export const userLoginService (loginData){//借助于URLSearchParam完成参数传递const params new URLSearchParams();for(let key in loginData){params.append(key,loginData[key]);}return request.post(/user/login,params);}// 八 1获取个人信息
export const userInfoGetService (){return request.get(/user/userInfo);}// 十 3 修改个人信息
export const userInfoUpdateService (userInfo){return request.put(/user/update,userInfo)}
为修改按钮绑定单击事件
el-button typeprimary clickupdateUserInfo提交修改/el-button !-- 十 3 clickupdateUserInfo--提供updateUserInfo函数
const updateUserInfo async (){let result await userInfoUpdateService(userInfo.value)ElMessage.success(result.message? result.message:修改成功)//更新pinia中的数据userInfoStore.info.nicknameuserInfo.value.nicknameuserInfoStore.info.email userInfo.value.email
}
script setup
import { ref } from vue
// 十 3
import { ElMessage } from element-plus;
import { userInfoUpdateService } from /api/user.js;// 十 2
import useUserInfoStore from /stores/userInfo.js;
const userInfoStore useUserInfoStore()
const userInfo ref({...userInfoStore.info})const rules {nickname: [{ required: true, message: 请输入用户昵称, trigger: blur },{pattern: /^\S{2,10}$/,message: 昵称必须是2-10位的非空字符串,trigger: blur}],email: [{ required: true, message: 请输入用户邮箱, trigger: blur },{ type: email, message: 邮箱格式不正确, trigger: blur }]
}// 十 3
const updateUserInfo async (){let result await userInfoUpdateService(userInfo.value)ElMessage.success(result.message? result.message:修改成功)//更新pinia中的数据userInfoStore.info.nicknameuserInfo.value.nicknameuserInfoStore.info.email userInfo.value.email
}
/script
templateel-card classpage-containertemplate #headerdiv classheaderspan基本资料/span/div/templateel-rowel-col :span12el-form :modeluserInfo :rulesrules label-width100px sizelargeel-form-item label登录名称el-input v-modeluserInfo.username disabled/el-input/el-form-itemel-form-item label用户昵称 propnicknameel-input v-modeluserInfo.nickname/el-input/el-form-itemel-form-item label用户邮箱 propemailel-input v-modeluserInfo.email/el-input/el-form-itemel-form-itemel-button typeprimary clickupdateUserInfo提交修改/el-button !-- 十 3 clickupdateUserInfo--/el-form-item/el-form/el-col/el-row/el-card
/template
十一、修改头像 1、 修改头像页面组件UserAvatar.vue
script setup
import { Plus, Upload } from element-plus/icons-vue
import {ref} from vue
import avatar from /assets/default.png
const uploadRef ref()//用户头像地址
const imgUrl avatar/scripttemplateel-card classpage-containertemplate #headerdiv classheaderspan更换头像/span/div/templateel-rowel-col :span12el-upload refuploadRefclassavatar-uploader :show-file-listfalseimg v-ifimgUrl :srcimgUrl classavatar /img v-else srcavatar width278 //el-uploadbr /el-button typeprimary :iconPlus sizelarge clickuploadRef.$el.querySelector(input).click()选择图片/el-buttonel-button typesuccess :iconUpload sizelarge上传头像/el-button/el-col/el-row/el-card
/templatestyle langscss scoped
.avatar-uploader {:deep() {.avatar {width: 278px;height: 278px;display: block;}.el-upload {border: 1px dashed var(--el-border-color);border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;transition: var(--el-transition-duration-fast);}.el-upload:hover {border-color: var(--el-color-primary);}.el-icon.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 278px;height: 278px;text-align: center;}}
}
/style 2、 头像回显
从pinia中读取用户的头像数据
//读取用户信息
import {ref} from vue
import useUserInfoStore from /stores/userInfo.jsconst userInfoStore useUserInfoStore()
//用户头像地址
const imgUrlref(userInfoStore.info.userPic)
img标签上绑定图片地址
img v-ifimgUrl :srcimgUrl classavatar /
img v-else :srcavatar width278 / 3、 头像上传
为el-upload指定属性值分别有 action: 服务器接口路径 headers: 设置请求头需要携带token on-success: 上传成功的回调函数 name: 上传图片的字段名称
el-upload refuploadRefclassavatar-uploader :show-file-listfalse:auto-uploadtrueaction/api/uploadnamefile:headers{Authorization:tokenStore.token}:on-successuploadSuccessimg v-ifimgUrl :srcimgUrl classavatar /img v-else srcavatar width278 //el-upload
提供上传成功的回调函数
//读取token信息
import {useTokenStore} from /stores/token.js
const tokenStore useTokenStore()
//图片上传成功的回调
const uploadSuccess (result){//回显图片imgUrl.value result.data
} 外部触发图片选择
需要获取到el-upload组件然后再通过$el.querySelector(input)获取到el-upload对应的元素触发click事件
// 十 、3//获取el-upload元素
const uploadRef ref()
el-button typeprimary :iconPlus sizelarge clickuploadRef.$el.querySelector(input).click()选择图片
/el-button 4、 上传头像接口调用
在user.js中提供修改头像的函数
//导入request.js请求工具
import request from /utils/request.js// 二 注册函数 提供调用注册接口的函数
export const userRegisterService (registerData) {//借助于URLSearchParam完成参数传递const params new URLSearchParams();for(let key in registerData){params.append(key,registerData[key]);}return request.post(/user/register,params);}// 三 登录函数 提供调用登录接口的函数
export const userLoginService (loginData){//借助于URLSearchParam完成参数传递const params new URLSearchParams();for(let key in loginData){params.append(key,loginData[key]);}return request.post(/user/login,params);}// 八 1获取个人信息
export const userInfoGetService (){return request.get(/user/userInfo);}// 十 3 修改个人信息
export const userInfoUpdateService (userInfo){return request.put(/user/update,userInfo)}// 十 4 修改头像
export const userAvatarUpdateService(avatarUrl){let params new URLSearchParams();params.append(avatarUrl,avatarUrl)return request.patch(/user/updateAvatar,params)
}
为【上传头像】按钮绑定单击事件 el-button typesuccess :iconUpload sizelarge clickupdateAvatar !-- 十 4 clickupdateAvatar--上传头像/el-button
提供updateAvatar函数完成头像更新
//调用接口更新头像url
import {userAvatarUpdateService} from /api/user.js
import {ElMessage} from element-plus
const updateAvatar async (){let result await userAvatarUpdateService(imgUrl.value)ElMessage.success(result.message? result.message:修改成功)//更新pinia中的数据userInfoStore.info.userPicimgUrl.value
}
script setup
import { Plus, Upload } from element-plus/icons-vue
import avatar from /assets/default.png
// 十 3
import { useTokenStore } from /stores/token.js
// 十 4调用接口更新头像url
import {userAvatarUpdateService} from /api/user.js
import {ElMessage} from element-plus
// 十 2
//读取用户信息
import {ref} from vue
import useUserInfoStore from /stores/userInfo.jsconst userInfoStore useUserInfoStore()
//用户头像地址
const imgUrlref(userInfoStore.info.userPic)// 十 3
const tokenStore useTokenStore();
//图片上传成功的回调
const uploadSuccess (result){//回显图片imgUrl.value result.data
}// 十 、3//获取el-upload元素
const uploadRef ref()// 十 4
const updateAvatar async (){let result await userAvatarUpdateService(imgUrl.value)ElMessage.success(result.message? result.message:修改成功)//更新pinia中的数据userInfoStore.info.userPicimgUrl.value
}
/scripttemplateel-card classpage-containertemplate #headerdiv classheaderspan更换头像/span/div/templateel-rowel-col :span12!-- auto-upload:是否自动上传action: 服务器接口路径name: 上传的文件字段名headers: 设置上传的请求头on-success: 上传成功的回调函数 --el-upload refuploadRefclassavatar-uploader :show-file-listfalse:auto-uploadtrueaction/api/uploadnamefile:headers{Authorization:tokenStore.token}:on-successuploadSuccessimg v-ifimgUrl :srcimgUrl classavatar /img v-else :srcavatar width278 //el-uploadbr /el-button typeprimary :iconPlus sizelarge clickuploadRef.$el.querySelector(input).click()选择图片/el-buttonel-button typesuccess :iconUpload sizelarge clickupdateAvatar !-- 十 4 clickupdateAvatar--上传头像/el-button/el-col/el-row/el-card
/templatestyle langscss scoped
.avatar-uploader {:deep() {.avatar {width: 278px;height: 278px;display: block;}.el-upload {border: 1px dashed var(--el-border-color);border-radius: 6px;cursor: pointer;position: relative;overflow: hidden;transition: var(--el-transition-duration-fast);}.el-upload:hover {border-color: var(--el-color-primary);}.el-icon.avatar-uploader-icon {font-size: 28px;color: #8c939d;width: 278px;height: 278px;text-align: center;}}
}
/style
十二、 重置密码
1、user.js 重置密码
//导入request.js请求工具
import request from /utils/request.js// 二 注册函数 提供调用注册接口的函数
export const userRegisterService (registerData) {//借助于URLSearchParam完成参数传递const params new URLSearchParams();for(let key in registerData){params.append(key,registerData[key]);}return request.post(/user/register,params);}// 三 登录函数 提供调用登录接口的函数
export const userLoginService (loginData){//借助于URLSearchParam完成参数传递const params new URLSearchParams();for(let key in loginData){params.append(key,loginData[key]);}return request.post(/user/login,params);}// 八 1获取个人信息
export const userInfoGetService (){return request.get(/user/userInfo);}// 十 3 修改个人信息
export const userInfoUpdateService (userInfo){return request.put(/user/update,userInfo)}// 十一 4 修改头像
export const userAvatarUpdateService(avatarUrl){let params new URLSearchParams();params.append(avatarUrl,avatarUrl)return request.patch(/user/updateAvatar,params)
}// 十二 重置密码
export const userUpdatePwdService(userPwd){ return request.patch(/user/updatePwd,userPwd)
} 2、UserResetPassword.vue views/UserResetPassword.vue
script setup
import { ElMessage } from element-plus;
import { useTokenStore } from /stores/token.js
import { ref } from vue
import { userUpdatePwdService } from /api/user.js;
import {useRouter} from vue-router
const router useRouter()
const userPwd ref({ old_pwd: ,new_pwd: ,re_pwd: ,
})const tokenStore useTokenStore()
const updateUserPwd async (){let result await userUpdatePwdService(userPwd.value)// ElMessage.success(result.message? result.message:修改成功)//退出登录//1.清空pinia中存储的tokenpinia中个人信息没有存储密码信息所以不处理tokenStore.removeToken()//2.跳转到登录页面router.push(/login)ElMessage({type: success,message: 修改密码成功请重新成功,})
}const rules {old_pwd: [{ required: true, message: 请输入原始密码, trigger: blur },{pattern: /^\S{5,16}$/,message: 原始密码必须是5-16位的非空字符串,trigger: blur}],new_pwd: [{ required: true, message: 请输入新密码, trigger: blur },{pattern: /^\S{5,16}$/,message: 新密码必须是5-16位的非空字符串,trigger: blur}],re_pwd: [{ required: true, message: 请输入确认密码, trigger: blur },{pattern: /^\S{5,16}$/,message: 确认密码必须是5-16位的非空字符串,trigger: blur}]
}
/script
templateel-card classpage-containertemplate #headerdiv classheaderspan重置密码/span/div/templateel-rowel-col :span12 el-form :modeluserPwd :rulesrules label-width100px sizelargeel-form-item label原密码 propold_pwdel-input typepassword v-modeluserPwd.old_pwd/el-input/el-form-itemel-form-item label新密码 propnew_pwdel-input typepassword v-modeluserPwd.new_pwd/el-input/el-form-itemel-form-item label确认密码 propre_pwdel-input typepassword v-modeluserPwd.re_pwd/el-input/el-form-itemel-form-itemel-button typeprimary clickupdateUserPwd提交修改/el-button/el-form-item/el-form/el-col/el-row/el-card
/template