【SpringBoot3+Vue3】五【完】【實戰篇】-前端(配合后端)

目錄

一、環境準備

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 vue@latest# 項目名稱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.scss'import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'import App from './App.vue'const 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></script><template>
春天的菠菜</template><style scoped></style>

4、啟動項目驗證

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)
</script><template><el-row class="login-page"><el-col :span="12" class="bg"></el-col><el-col :span="6" :offset="3" class="form"><!-- 注冊表單 --><el-form ref="form" size="large" autocomplete="off" v-if="isRegister"><el-form-item><h1>注冊</h1></el-form-item><el-form-item><el-input :prefix-icon="User" placeholder="請輸入用戶名"></el-input></el-form-item><el-form-item><el-input :prefix-icon="Lock" type="password" placeholder="請輸入密碼"></el-input></el-form-item><el-form-item><el-input :prefix-icon="Lock" type="password" placeholder="請輸入再次密碼"></el-input></el-form-item><!-- 注冊按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space>注冊</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = false">← 返回</el-link></el-form-item></el-form><!-- 登錄表單 --><el-form ref="form" size="large" autocomplete="off" v-else><el-form-item><h1>登錄</h1></el-form-item><el-form-item><el-input :prefix-icon="User" placeholder="請輸入用戶名"></el-input></el-form-item><el-form-item><el-input name="password" :prefix-icon="Lock" type="password" placeholder="請輸入密碼"></el-input></el-form-item><el-form-item class="flex"><div class="flex"><el-checkbox>記住我</el-checkbox><el-link type="primary" :underline="false">忘記密碼?</el-link></div></el-form-item><!-- 登錄按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space>登錄</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = true">注冊 →</el-link></el-form-item></el-form></el-col></el-row>
</template><style lang="scss" 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'</script><template>
<LoginVue/>
</template><style scoped></style>

1.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:''
})</script><template><el-row class="login-page"><el-col :span="12" class="bg"></el-col><el-col :span="6" :offset="3" class="form"><!-- 注冊表單 --><el-form ref="form" size="large" autocomplete="off" v-if="isRegister" :model="registerData"><el-form-item><h1>注冊</h1></el-form-item><el-form-item><el-input :prefix-icon="User" placeholder="請輸入用戶名" v-model="registerData.username"></el-input></el-form-item><el-form-item><el-input :prefix-icon="Lock" type="password" placeholder="請輸入密碼" v-model="registerData.password"></el-input></el-form-item><el-form-item><el-input :prefix-icon="Lock" type="password" placeholder="請輸入再次密碼" v-model="registerData.rePassword"></el-input></el-form-item><!-- 注冊按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space>注冊</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = false">← 返回</el-link></el-form-item></el-form><!-- 登錄表單 --><el-form ref="form" size="large" autocomplete="off" v-else><el-form-item><h1>登錄</h1></el-form-item><el-form-item><el-input :prefix-icon="User" placeholder="請輸入用戶名"></el-input></el-form-item><el-form-item><el-input name="password" :prefix-icon="Lock" type="password" placeholder="請輸入密碼"></el-input></el-form-item><el-form-item class="flex"><div class="flex"><el-checkbox>記住我</el-checkbox><el-link type="primary" :underline="false">忘記密碼?</el-link></div></el-form-item><!-- 登錄按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space>登錄</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = true">注冊 →</el-link></el-form-item></el-form></el-col></el-row>
</template><style lang="scss" 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'}]
}</script><template><el-row class="login-page"><el-col :span="12" class="bg"></el-col><el-col :span="6" :offset="3" class="form"><!-- 注冊表單 --><el-form ref="form" size="large" autocomplete="off" v-if="isRegister" :model="registerData" :rules="rules">  <!-- 2.1  :model="registerData"表單聲明屬性 --> <!-- 2.2  :rules="rules"綁定校驗 --><el-form-item><h1>注冊</h1></el-form-item><el-form-item prop="username">  <!-- 2.2 prop="username"綁定校驗 --><el-input :prefix-icon="User" placeholder="請輸入用戶名" v-model="registerData.username"></el-input>  <!-- 2.1 v-model="registerData.username"綁定屬性 --></el-form-item><el-form-item prop="password"> <!-- 2.2 prop="password"綁定校驗 --><el-input :prefix-icon="Lock" type="password" placeholder="請輸入密碼" v-model="registerData.password"></el-input>  <!-- 2.1 v-model="registerData.password"綁定屬性 --></el-form-item><el-form-item prop="rePassword">  <!-- 2.2 prop="rePassword"綁定校驗 --><el-input :prefix-icon="Lock" type="password" placeholder="請輸入再次密碼" v-model="registerData.rePassword"></el-input>  <!-- 2.1 v-model="registerData.rePassword"綁定屬性 --></el-form-item><!-- 注冊按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space>注冊</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = false">← 返回</el-link></el-form-item></el-form><!-- 登錄表單 --><el-form ref="form" size="large" autocomplete="off" v-else><el-form-item><h1>登錄</h1></el-form-item><el-form-item><el-input :prefix-icon="User" placeholder="請輸入用戶名"></el-input></el-form-item><el-form-item><el-input name="password" :prefix-icon="Lock" type="password" placeholder="請輸入密碼"></el-input></el-form-item><el-form-item class="flex"><div class="flex"><el-checkbox>記住我</el-checkbox><el-link type="primary" :underline="false">忘記密碼?</el-link></div></el-form-item><!-- 登錄按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space>登錄</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = true">注冊 →</el-link></el-form-item></el-form></el-col></el-row>
</template><style lang="scss" 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('注冊失敗')}
}</script><template><el-row class="login-page"><el-col :span="12" class="bg"></el-col><el-col :span="6" :offset="3" class="form"><!-- 注冊表單 --><el-form ref="form" size="large" autocomplete="off" v-if="isRegister" :model="registerData" :rules="rules">  <!-- 2.1  :model="registerData"表單聲明屬性 --> <!-- 2.2  :rules="rules"綁定校驗 --><el-form-item><h1>注冊</h1></el-form-item><el-form-item prop="username">  <!-- 2.2 prop="username"綁定校驗 --><el-input :prefix-icon="User" placeholder="請輸入用戶名" v-model="registerData.username"></el-input>  <!-- 2.1 v-model="registerData.username"綁定屬性 --></el-form-item><el-form-item prop="password"> <!-- 2.2 prop="password"綁定校驗 --><el-input :prefix-icon="Lock" type="password" placeholder="請輸入密碼" v-model="registerData.password"></el-input>  <!-- 2.1 v-model="registerData.password"綁定屬性 --></el-form-item><el-form-item prop="rePassword">  <!-- 2.2 prop="rePassword"綁定校驗 --><el-input :prefix-icon="Lock" type="password" placeholder="請輸入再次密碼" v-model="registerData.rePassword"></el-input>  <!-- 2.1 v-model="registerData.rePassword"綁定屬性 --></el-form-item><!-- 注冊按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space @click="register"> <!-- 3.3  @click="register"  綁定注冊單擊事件 -->注冊</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = false">← 返回</el-link></el-form-item></el-form><!-- 登錄表單 --><el-form ref="form" size="large" autocomplete="off" v-else><el-form-item><h1>登錄</h1></el-form-item><el-form-item><el-input :prefix-icon="User" placeholder="請輸入用戶名"></el-input></el-form-item><el-form-item><el-input name="password" :prefix-icon="Lock" type="password" placeholder="請輸入密碼"></el-input></el-form-item><el-form-item class="flex"><div class="flex"><el-checkbox>記住我</el-checkbox><el-link type="primary" :underline="false">忘記密碼?</el-link></div></el-form-item><!-- 登錄按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space>登錄</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = true">注冊 →</el-link></el-form-item></el-form></el-col></el-row>
</template><style lang="scss" 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:url'import { 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 綁定數據 復用注冊表單的數據模型</script><template><el-row class="login-page"><el-col :span="12" class="bg"></el-col><el-col :span="6" :offset="3" class="form"><!-- 注冊表單 --><el-form ref="form" size="large" autocomplete="off" v-if="isRegister" :model="registerData" :rules="rules">  <!-- 二 注冊2.1  :model="registerData"表單聲明屬性 --> <!-- 二 注冊2.2  :rules="rules"綁定校驗 --><el-form-item><h1>注冊</h1></el-form-item><el-form-item prop="username">  <!--二 注冊 2.2 prop="username"綁定校驗 --><el-input :prefix-icon="User" placeholder="請輸入用戶名" v-model="registerData.username"></el-input>  <!--二 注冊 2.1 v-model="registerData.username"綁定屬性 --></el-form-item><el-form-item prop="password"> <!-- 二 注冊2.2 prop="password"綁定校驗 --><el-input :prefix-icon="Lock" type="password" placeholder="請輸入密碼" v-model="registerData.password"></el-input>  <!-- 二 注冊2.1 v-model="registerData.password"綁定屬性 --></el-form-item><el-form-item prop="rePassword">  <!-- 2.2 prop="rePassword"綁定校驗 --><el-input :prefix-icon="Lock" type="password" placeholder="請輸入再次密碼" v-model="registerData.rePassword"></el-input>  <!-- 二 注冊2.1 v-model="registerData.rePassword"綁定屬性 --></el-form-item><!-- 注冊按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space @click="register"> <!-- 二 注冊3.3  @click="register"  綁定注冊單擊事件 -->注冊</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = false">← 返回</el-link></el-form-item></el-form><!-- 登錄表單 --><el-form ref="form" size="large" autocomplete="off" v-else model="registerDate">  <!-- 三 登錄 3.1  :model="registerData"表單聲明屬性,復用注冊 --><el-form-item><h1>登錄</h1></el-form-item><el-form-item><el-input :prefix-icon="User" placeholder="請輸入用戶名" v-model="registerData.username"></el-input> <!--三 登錄 3.1 v-model="registerData.username"綁定屬性 --></el-form-item><el-form-item><el-input name="password" :prefix-icon="Lock" type="password" placeholder="請輸入密碼" v-model="registerData.password"></el-input> <!--三 登錄 3.1 v-model="registerData.password"綁定屬性 --></el-form-item>                <el-form-item class="flex"><div class="flex"><el-checkbox>記住我</el-checkbox><el-link type="primary" :underline="false">忘記密碼?</el-link></div></el-form-item><!-- 登錄按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space>登錄</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = true">注冊 →</el-link></el-form-item></el-form></el-col></el-row>
</template><style lang="scss" 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  數據校驗 復用注冊表單的數據校驗</script><template><el-row class="login-page"><el-col :span="12" class="bg"></el-col><el-col :span="6" :offset="3" class="form"><!-- 注冊表單 --><el-form ref="form" size="large" autocomplete="off" v-if="isRegister" :model="registerData" :rules="rules">  <!-- 二 注冊2.1  :model="registerData"表單聲明屬性 --> <!-- 二 注冊2.2  :rules="rules"綁定校驗 --><el-form-item><h1>注冊</h1></el-form-item><el-form-item prop="username">  <!--二 注冊 2.2 prop="username"綁定校驗 --><el-input :prefix-icon="User" placeholder="請輸入用戶名" v-model="registerData.username"></el-input>  <!--二 注冊 2.1 v-model="registerData.username"綁定屬性 --></el-form-item><el-form-item prop="password"> <!-- 二 注冊2.2 prop="password"綁定校驗 --><el-input :prefix-icon="Lock" type="password" placeholder="請輸入密碼" v-model="registerData.password"></el-input>  <!-- 二 注冊2.1 v-model="registerData.password"綁定屬性 --></el-form-item><el-form-item prop="rePassword">  <!-- 2.2 prop="rePassword"綁定校驗 --><el-input :prefix-icon="Lock" type="password" placeholder="請輸入再次密碼" v-model="registerData.rePassword"></el-input>  <!-- 二 注冊2.1 v-model="registerData.rePassword"綁定屬性 --></el-form-item><!-- 注冊按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space @click="register"> <!-- 二 注冊3.3  @click="register"  綁定注冊單擊事件 -->注冊</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = false">← 返回</el-link></el-form-item></el-form><!-- 登錄表單 --><el-form ref="form" size="large" autocomplete="off" v-else model="registerDate" rules="rules">  <!-- 三 登錄 3.1  :model="registerData"表單聲明屬性,復用注冊 --> <!-- 三 登錄 3.2  :rules="rules"綁定校驗 --><el-form-item><h1>登錄</h1></el-form-item><el-form-item prop="username"> <!--三 登錄 3.2  prop="username"綁定校驗 --><el-input :prefix-icon="User" placeholder="請輸入用戶名" v-model="registerData.username"></el-input> <!--三 登錄 3.1 v-model="registerData.username"綁定屬性 --></el-form-item><el-form-item prop="username"> <!--三 登錄 3.2  prop="username"綁定校驗 --><el-input name="password" :prefix-icon="Lock" type="password" placeholder="請輸入密碼" v-model="registerData.password"></el-input> <!--三 登錄 3.1 v-model="registerData.password"綁定屬性 --></el-form-item>               <el-form-item class="flex"><div class="flex"><el-checkbox>記住我</el-checkbox><el-link type="primary" :underline="false">忘記密碼?</el-link></div></el-form-item><!-- 登錄按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space>登錄</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = true">注冊 →</el-link></el-form-item></el-form></el-col></el-row>
</template><style lang="scss" 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('登錄失敗')}}</script><template><el-row class="login-page"><el-col :span="12" class="bg"></el-col><el-col :span="6" :offset="3" class="form"><!-- 注冊表單 --><el-form ref="form" size="large" autocomplete="off" v-if="isRegister" :model="registerData" :rules="rules">  <!-- 二 注冊2.1  :model="registerData"表單聲明屬性 --> <!-- 二 注冊2.2  :rules="rules"綁定校驗 --><el-form-item><h1>注冊</h1></el-form-item><el-form-item prop="username">  <!--二 注冊 2.2 prop="username"綁定校驗 --><el-input :prefix-icon="User" placeholder="請輸入用戶名" v-model="registerData.username"></el-input>  <!--二 注冊 2.1 v-model="registerData.username"綁定屬性 --></el-form-item><el-form-item prop="password"> <!-- 二 注冊2.2 prop="password"綁定校驗 --><el-input :prefix-icon="Lock" type="password" placeholder="請輸入密碼" v-model="registerData.password"></el-input>  <!-- 二 注冊2.1 v-model="registerData.password"綁定屬性 --></el-form-item><el-form-item prop="rePassword">  <!-- 2.2 prop="rePassword"綁定校驗 --><el-input :prefix-icon="Lock" type="password" placeholder="請輸入再次密碼" v-model="registerData.rePassword"></el-input>  <!-- 二 注冊2.1 v-model="registerData.rePassword"綁定屬性 --></el-form-item><!-- 注冊按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space @click="register"> <!-- 二 注冊3.3  @click="register"  綁定注冊單擊事件 -->注冊</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = false">← 返回</el-link></el-form-item></el-form><!-- 登錄表單 --><el-form ref="form" size="large" autocomplete="off" v-else model="registerDate" rules="rules">  <!-- 三 登錄 3.1  :model="registerData"表單聲明屬性,復用注冊 --> <!-- 三 登錄 3.2  :rules="rules"綁定校驗 --><el-form-item><h1>登錄</h1></el-form-item><el-form-item prop="username"> <!--三 登錄 3.2  prop="username"綁定校驗 --><el-input :prefix-icon="User" placeholder="請輸入用戶名" v-model="registerData.username"></el-input> <!--三 登錄 3.1 v-model="registerData.username"綁定屬性 --></el-form-item><el-form-item prop="username"> <!--三 登錄 3.2  prop="username"綁定校驗 --><el-input name="password" :prefix-icon="Lock" type="password" placeholder="請輸入密碼" v-model="registerData.password"></el-input> <!--三 登錄 3.1 v-model="registerData.password"綁定屬性 --></el-form-item>                <el-form-item class="flex"><div class="flex"><el-checkbox>記住我</el-checkbox><el-link type="primary" :underline="false">忘記密碼?</el-link></div></el-form-item><!-- 登錄按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space @click="login">登錄</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = true">注冊 →</el-link></el-form-item></el-form></el-col></el-row>
</template><style lang="scss" 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:''}}</script><template><el-row class="login-page"><el-col :span="12" class="bg"></el-col><el-col :span="6" :offset="3" class="form"><!-- 注冊表單 --><el-form ref="form" size="large" autocomplete="off" v-if="isRegister" :model="registerData" :rules="rules">  <!-- 二 注冊2.1  :model="registerData"表單聲明屬性 --> <!-- 二 注冊2.2  :rules="rules"綁定校驗 --><el-form-item><h1>注冊</h1></el-form-item><el-form-item prop="username">  <!--二 注冊 2.2 prop="username"綁定校驗 --><el-input :prefix-icon="User" placeholder="請輸入用戶名" v-model="registerData.username"></el-input>  <!--二 注冊 2.1 v-model="registerData.username"綁定屬性 --></el-form-item><el-form-item prop="password"> <!-- 二 注冊2.2 prop="password"綁定校驗 --><el-input :prefix-icon="Lock" type="password" placeholder="請輸入密碼" v-model="registerData.password"></el-input>  <!-- 二 注冊2.1 v-model="registerData.password"綁定屬性 --></el-form-item><el-form-item prop="rePassword">  <!-- 2.2 prop="rePassword"綁定校驗 --><el-input :prefix-icon="Lock" type="password" placeholder="請輸入再次密碼" v-model="registerData.rePassword"></el-input>  <!-- 二 注冊2.1 v-model="registerData.rePassword"綁定屬性 --></el-form-item><!-- 注冊按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space @click="register"> <!-- 二 注冊3.3  @click="register"  綁定注冊單擊事件 -->注冊</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = false;clearRegisterDate()"> <!--三 登錄 4  調用函數clearRegisterDate() 清空數據 -->← 返回</el-link></el-form-item></el-form><!-- 登錄表單 --><el-form ref="form" size="large" autocomplete="off" v-else model="registerDate" rules="rules">  <!-- 三 登錄 3.1  :model="registerData"表單聲明屬性,復用注冊 --> <!-- 三 登錄 3.2  :rules="rules"綁定校驗 --><el-form-item><h1>登錄</h1></el-form-item><el-form-item prop="username"> <!--三 登錄 3.2  prop="username"綁定校驗 --><el-input :prefix-icon="User" placeholder="請輸入用戶名" v-model="registerData.username"></el-input> <!--三 登錄 3.1 v-model="registerData.username"綁定屬性 --></el-form-item><el-form-item prop="username"> <!--三 登錄 3.2  prop="username"綁定校驗 --><el-input name="password" :prefix-icon="Lock" type="password" placeholder="請輸入密碼" v-model="registerData.password"></el-input> <!--三 登錄 3.1 v-model="registerData.password"綁定屬性 --></el-form-item>                <el-form-item class="flex"><div class="flex"><el-checkbox>記住我</el-checkbox><el-link type="primary" :underline="false">忘記密碼?</el-link></div></el-form-item><!-- 登錄按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space @click="login">登錄</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = true;clearRegisterDate()">  <!--三 登錄 4  調用函數clearRegisterDate() 清空數據 -->注冊 →</el-link></el-form-item></el-form></el-col></el-row>
</template><style lang="scss" 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:''}}</script><template><el-row class="login-page"><el-col :span="12" class="bg"></el-col><el-col :span="6" :offset="3" class="form"><!-- 注冊表單 --><el-form ref="form" size="large" autocomplete="off" v-if="isRegister" :model="registerData" :rules="rules">  <!-- 二 注冊2.1  :model="registerData"表單聲明屬性 --> <!-- 二 注冊2.2  :rules="rules"綁定校驗 --><el-form-item><h1>注冊</h1></el-form-item><el-form-item prop="username">  <!--二 注冊 2.2 prop="username"綁定校驗 --><el-input :prefix-icon="User" placeholder="請輸入用戶名" v-model="registerData.username"></el-input>  <!--二 注冊 2.1 v-model="registerData.username"綁定屬性 --></el-form-item><el-form-item prop="password"> <!-- 二 注冊2.2 prop="password"綁定校驗 --><el-input :prefix-icon="Lock" type="password" placeholder="請輸入密碼" v-model="registerData.password"></el-input>  <!-- 二 注冊2.1 v-model="registerData.password"綁定屬性 --></el-form-item><el-form-item prop="rePassword">  <!-- 2.2 prop="rePassword"綁定校驗 --><el-input :prefix-icon="Lock" type="password" placeholder="請輸入再次密碼" v-model="registerData.rePassword"></el-input>  <!-- 二 注冊2.1 v-model="registerData.rePassword"綁定屬性 --></el-form-item><!-- 注冊按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space @click="register"> <!-- 二 注冊3.3  @click="register"  綁定注冊單擊事件 -->注冊</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = false;clearRegisterDate()"> <!--三 登錄 4  調用函數clearRegisterDate() 清空數據 -->← 返回</el-link></el-form-item></el-form><!-- 登錄表單 --><el-form ref="form" size="large" autocomplete="off" v-else model="registerDate" rules="rules">  <!-- 三 登錄 3.1  :model="registerData"表單聲明屬性,復用注冊 --> <!-- 三 登錄 3.2  :rules="rules"綁定校驗 --><el-form-item><h1>登錄</h1></el-form-item><el-form-item prop="username"> <!--三 登錄 3.2  prop="username"綁定校驗 --><el-input :prefix-icon="User" placeholder="請輸入用戶名" v-model="registerData.username"></el-input> <!--三 登錄 3.1 v-model="registerData.username"綁定屬性 --></el-form-item><el-form-item prop="username"> <!--三 登錄 3.2  prop="username"綁定校驗 --><el-input name="password" :prefix-icon="Lock" type="password" placeholder="請輸入密碼" v-model="registerData.password"></el-input> <!--三 登錄 3.1 v-model="registerData.password"綁定屬性 --></el-form-item>                <el-form-item class="flex"><div class="flex"><el-checkbox>記住我</el-checkbox><el-link type="primary" :underline="false">忘記密碼?</el-link></div></el-form-item><!-- 登錄按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space @click="login">登錄</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = true;clearRegisterDate()">  <!--三 登錄 4  調用函數clearRegisterDate() 清空數據 -->注冊 →</el-link></el-form-item></el-form></el-col></el-row>
</template><style lang="scss" 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:''}}</script><template><el-row class="login-page"><el-col :span="12" class="bg"></el-col><el-col :span="6" :offset="3" class="form"><!-- 注冊表單 --><el-form ref="form" size="large" autocomplete="off" v-if="isRegister" :model="registerData" :rules="rules">  <!-- 二 注冊2.1  :model="registerData"表單聲明屬性 --> <!-- 二 注冊2.2  :rules="rules"綁定校驗 --><el-form-item><h1>注冊</h1></el-form-item><el-form-item prop="username">  <!--二 注冊 2.2 prop="username"綁定校驗 --><el-input :prefix-icon="User" placeholder="請輸入用戶名" v-model="registerData.username"></el-input>  <!--二 注冊 2.1 v-model="registerData.username"綁定屬性 --></el-form-item><el-form-item prop="password"> <!-- 二 注冊2.2 prop="password"綁定校驗 --><el-input :prefix-icon="Lock" type="password" placeholder="請輸入密碼" v-model="registerData.password"></el-input>  <!-- 二 注冊2.1 v-model="registerData.password"綁定屬性 --></el-form-item><el-form-item prop="rePassword">  <!-- 2.2 prop="rePassword"綁定校驗 --><el-input :prefix-icon="Lock" type="password" placeholder="請輸入再次密碼" v-model="registerData.rePassword"></el-input>  <!-- 二 注冊2.1 v-model="registerData.rePassword"綁定屬性 --></el-form-item><!-- 注冊按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space @click="register"> <!-- 二 注冊3.3  @click="register"  綁定注冊單擊事件 -->注冊</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = false;clearRegisterDate()"> <!--三 登錄 4  調用函數clearRegisterDate() 清空數據 -->← 返回</el-link></el-form-item></el-form><!-- 登錄表單 --><el-form ref="form" size="large" autocomplete="off" v-else model="registerDate" rules="rules">  <!-- 三 登錄 3.1  :model="registerData"表單聲明屬性,復用注冊 --> <!-- 三 登錄 3.2  :rules="rules"綁定校驗 --><el-form-item><h1>登錄</h1></el-form-item><el-form-item prop="username"> <!--三 登錄 3.2  prop="username"綁定校驗 --><el-input :prefix-icon="User" placeholder="請輸入用戶名" v-model="registerData.username"></el-input> <!--三 登錄 3.1 v-model="registerData.username"綁定屬性 --></el-form-item><el-form-item prop="username"> <!--三 登錄 3.2  prop="username"綁定校驗 --><el-input name="password" :prefix-icon="Lock" type="password" placeholder="請輸入密碼" v-model="registerData.password"></el-input> <!--三 登錄 3.1 v-model="registerData.password"綁定屬性 --></el-form-item>                <el-form-item class="flex"><div class="flex"><el-checkbox>記住我</el-checkbox><el-link type="primary" :underline="false">忘記密碼?</el-link></div></el-form-item><!-- 登錄按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space @click="login">登錄</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = true;clearRegisterDate()">  <!--三 登錄 4  調用函數clearRegisterDate() 清空數據 -->注冊 →</el-link></el-form-item></el-form></el-col></el-row>
</template><style lang="scss" 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'
</script><template><!-- element-plus 中的容器 --><el-container class="layout-container"><!-- 左側菜單 --><el-aside width="200px"><div class="el-aside__logo"></div><!-- element-plus 菜單標簽 --><el-menu active-text-color="#ffd04b" background-color="#232323"  text-color="#fff"router><el-menu-item ><el-icon><Management /></el-icon><span>文章分類</span></el-menu-item><el-menu-item ><el-icon><Promotion /></el-icon><span>文章管理</span></el-menu-item><el-sub-menu ><template #title><el-icon><UserFilled /></el-icon><span>個人中心</span></template><el-menu-item ><el-icon><User /></el-icon><span>基本資料</span></el-menu-item><el-menu-item ><el-icon><Crop /></el-icon><span>更換頭像</span></el-menu-item><el-menu-item ><el-icon><EditPen /></el-icon><span>重置密碼</span></el-menu-item></el-sub-menu></el-menu></el-aside><!-- 右側主區域 --><el-container><!-- 頭部區域 --><el-header><div>編碼集中營:<strong>春天的菠菜</strong></div><el-dropdown placement="bottom-end"><span class="el-dropdown__box"><el-avatar :src="avatar" /><el-icon><CaretBottom /></el-icon></span><template #dropdown><el-dropdown-menu><el-dropdown-item command="profile" :icon="User">基本資料</el-dropdown-item><el-dropdown-item command="avatar" :icon="Crop">更換頭像</el-dropdown-item><el-dropdown-item command="password" :icon="EditPen">重置密碼</el-dropdown-item><el-dropdown-item command="logout" :icon="SwitchButton">退出登錄</el-dropdown-item></el-dropdown-menu></template></el-dropdown></el-header><!-- 中間區域 --><el-main><div style="width: 1290px; height: 570px;border: 1px solid red;">內容展示區</div></el-main><!-- 底部區域 --><el-footer>國際大事件 ?2023 Created by 春天的菠菜</el-footer></el-container></el-container>
</template><style lang="scss" 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';</script><template>
<LoginVue/>
<!-- 五 LayoutVue-->
<LayoutVue/>
</template><style scoped></style>

2、路由

?

?2.1 安裝vue-router

npm install vue-router@4

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 router

2.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 '@/router'import App from './App.vue'const 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';</script><template><!-- 五  2.4 --><router-view></router-view><!-- 五  2.4 -->
<!-- <LoginVue/> -->
<!-- 五 1.1 LayoutVue-->
<LayoutVue/>
</template><style scoped></style>

2.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:''}}</script><template><el-row class="login-page"><el-col :span="12" class="bg"></el-col><el-col :span="6" :offset="3" class="form"><!-- 注冊表單 --><el-form ref="form" size="large" autocomplete="off" v-if="isRegister" :model="registerData" :rules="rules">  <!-- 二 注冊2.1  :model="registerData"表單聲明屬性 --> <!-- 二 注冊2.2  :rules="rules"綁定校驗 --><el-form-item><h1>注冊</h1></el-form-item><el-form-item prop="username">  <!--二 注冊 2.2 prop="username"綁定校驗 --><el-input :prefix-icon="User" placeholder="請輸入用戶名" v-model="registerData.username"></el-input>  <!--二 注冊 2.1 v-model="registerData.username"綁定屬性 --></el-form-item><el-form-item prop="password"> <!-- 二 注冊2.2 prop="password"綁定校驗 --><el-input :prefix-icon="Lock" type="password" placeholder="請輸入密碼" v-model="registerData.password"></el-input>  <!-- 二 注冊2.1 v-model="registerData.password"綁定屬性 --></el-form-item><el-form-item prop="rePassword">  <!-- 2.2 prop="rePassword"綁定校驗 --><el-input :prefix-icon="Lock" type="password" placeholder="請輸入再次密碼" v-model="registerData.rePassword"></el-input>  <!-- 二 注冊2.1 v-model="registerData.rePassword"綁定屬性 --></el-form-item><!-- 注冊按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space @click="register"> <!-- 二 注冊3.3  @click="register"  綁定注冊單擊事件 -->注冊</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = false;clearRegisterDate()"> <!--三 登錄 4  調用函數clearRegisterDate() 清空數據 -->← 返回</el-link></el-form-item></el-form><!-- 登錄表單 --><el-form ref="form" size="large" autocomplete="off" v-else model="registerDate" rules="rules">  <!-- 三 登錄 3.1  :model="registerData"表單聲明屬性,復用注冊 --> <!-- 三 登錄 3.2  :rules="rules"綁定校驗 --><el-form-item><h1>登錄</h1></el-form-item><el-form-item prop="username"> <!--三 登錄 3.2  prop="username"綁定校驗 --><el-input :prefix-icon="User" placeholder="請輸入用戶名" v-model="registerData.username"></el-input> <!--三 登錄 3.1 v-model="registerData.username"綁定屬性 --></el-form-item><el-form-item prop="username"> <!--三 登錄 3.2  prop="username"綁定校驗 --><el-input name="password" :prefix-icon="Lock" type="password" placeholder="請輸入密碼" v-model="registerData.password"></el-input> <!--三 登錄 3.1 v-model="registerData.password"綁定屬性 --></el-form-item>                <el-form-item class="flex"><div class="flex"><el-checkbox>記住我</el-checkbox><el-link type="primary" :underline="false">忘記密碼?</el-link></div></el-form-item><!-- 登錄按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space @click="login">登錄</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = true;clearRegisterDate()">  <!--三 登錄 4  調用函數clearRegisterDate() 清空數據 -->注冊 →</el-link></el-form-item></el-form></el-col></el-row>
</template><style lang="scss" 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.vue'import 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 router

3.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'
</script><template><!-- element-plus 中的容器 --><el-container class="layout-container"><!-- 左側菜單 --><el-aside width="200px"><div class="el-aside__logo"></div><!-- element-plus 菜單標簽 --><el-menu active-text-color="#ffd04b" background-color="#232323"  text-color="#fff"router><el-menu-item index="/article/category">  <!-- 五 3.3  --><el-icon><Management /></el-icon><span>文章分類</span></el-menu-item><el-menu-item index="/article/manage">  <!-- 五 3.3  --><el-icon><Promotion /></el-icon><span>文章管理</span></el-menu-item><el-sub-menu ><template #title><el-icon><UserFilled /></el-icon><span>個人中心</span></template><el-menu-item index="/user/info">  <!-- 五 3.3  --><el-icon><User /></el-icon><span>基本資料</span></el-menu-item><el-menu-item index="/user/avatar">  <!-- 五 3.3  --><el-icon><Crop /></el-icon><span>更換頭像</span></el-menu-item><el-menu-item index="/user/resetPassword">  <!-- 五 3.3  --><el-icon><EditPen /></el-icon><span>重置密碼</span></el-menu-item></el-sub-menu></el-menu></el-aside><!-- 右側主區域 --><el-container><!-- 頭部區域 --><el-header><div>編碼集中營:<strong>春天的菠菜</strong></div><el-dropdown placement="bottom-end"><span class="el-dropdown__box"><el-avatar :src="avatar" /><el-icon><CaretBottom /></el-icon></span><template #dropdown><el-dropdown-menu><el-dropdown-item command="profile" :icon="User">基本資料</el-dropdown-item><el-dropdown-item command="avatar" :icon="Crop">更換頭像</el-dropdown-item><el-dropdown-item command="password" :icon="EditPen">重置密碼</el-dropdown-item><el-dropdown-item command="logout" :icon="SwitchButton">退出登錄</el-dropdown-item></el-dropdown-menu></template></el-dropdown></el-header><!-- 中間區域 --><el-main><!-- 五 3.3  --><!-- <div style="width: 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>
</template><style lang="scss" 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.js'import {Edit,Delete
} from '@element-plus/icons-vue'
import { ref } from 'vue'
const categorys = ref([{"id": 3,"categoryName": "美食","categoryAlias": "my","createTime": "2023-09-02 12:06:59","updateTime": "2023-09-02 12:06:59"},{"id": 4,"categoryName": "娛樂","categoryAlias": "yl","createTime": "2023-09-02 12:08:16","updateTime": "2023-09-02 12:08:16"},{"id": 5,"categoryName": "軍事","categoryAlias": "js","createTime": "2023-09-02 12:08:33","updateTime": "2023-09-02 12:08:33"}
])// 六 1.2 聲明一個異步函數
const articleCategoryList = async()=>{let result = await articleCategoryListService()categorys.value = result.data;}
articleCategoryList();</script>
<template><el-card class="page-container"><template #header><div class="header"><span>文章分類</span><div class="extra"><el-button type="primary">添加分類</el-button></div></div></template><el-table :data="categorys" style="width: 100%"><el-table-column label="序號" width="100" type="index"> </el-table-column><el-table-column label="分類名稱" prop="categoryName"></el-table-column><el-table-column label="分類別名" prop="categoryAlias"></el-table-column><el-table-column label="操作" width="100"><template #default="{ row }"><el-button :icon="Edit" circle plain type="primary" ></el-button><el-button :icon="Delete" circle plain type="danger"></el-button></template></el-table-column><template #empty><el-empty description="沒有數據" /></template></el-table></el-card>
</template><style lang="scss" 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 '@/router'import App from './App.vue'
//六 2.2
import {createPinia} from 'pinia'const 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:''}}</script><template><el-row class="login-page"><el-col :span="12" class="bg"></el-col><el-col :span="6" :offset="3" class="form"><!-- 注冊表單 --><el-form ref="form" size="large" autocomplete="off" v-if="isRegister" :model="registerData" :rules="rules">  <!-- 二 注冊2.1  :model="registerData"表單聲明屬性 --> <!-- 二 注冊2.2  :rules="rules"綁定校驗 --><el-form-item><h1>注冊</h1></el-form-item><el-form-item prop="username">  <!--二 注冊 2.2 prop="username"綁定校驗 --><el-input :prefix-icon="User" placeholder="請輸入用戶名" v-model="registerData.username"></el-input>  <!--二 注冊 2.1 v-model="registerData.username"綁定屬性 --></el-form-item><el-form-item prop="password"> <!-- 二 注冊2.2 prop="password"綁定校驗 --><el-input :prefix-icon="Lock" type="password" placeholder="請輸入密碼" v-model="registerData.password"></el-input>  <!-- 二 注冊2.1 v-model="registerData.password"綁定屬性 --></el-form-item><el-form-item prop="rePassword">  <!-- 2.2 prop="rePassword"綁定校驗 --><el-input :prefix-icon="Lock" type="password" placeholder="請輸入再次密碼" v-model="registerData.rePassword"></el-input>  <!-- 二 注冊2.1 v-model="registerData.rePassword"綁定屬性 --></el-form-item><!-- 注冊按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space @click="register"> <!-- 二 注冊3.3  @click="register"  綁定注冊單擊事件 -->注冊</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = false;clearRegisterDate()"> <!--三 登錄 4  調用函數clearRegisterDate() 清空數據 -->← 返回</el-link></el-form-item></el-form><!-- 登錄表單 --><el-form ref="form" size="large" autocomplete="off" v-else model="registerDate" rules="rules">  <!-- 三 登錄 3.1  :model="registerData"表單聲明屬性,復用注冊 --> <!-- 三 登錄 3.2  :rules="rules"綁定校驗 --><el-form-item><h1>登錄</h1></el-form-item><el-form-item prop="username"> <!--三 登錄 3.2  prop="username"綁定校驗 --><el-input :prefix-icon="User" placeholder="請輸入用戶名" v-model="registerData.username"></el-input> <!--三 登錄 3.1 v-model="registerData.username"綁定屬性 --></el-form-item><el-form-item prop="username"> <!--三 登錄 3.2  prop="username"綁定校驗 --><el-input name="password" :prefix-icon="Lock" type="password" placeholder="請輸入密碼" v-model="registerData.password"></el-input> <!--三 登錄 3.1 v-model="registerData.password"綁定屬性 --></el-form-item>                <el-form-item class="flex"><div class="flex"><el-checkbox>記住我</el-checkbox><el-link type="primary" :underline="false">忘記密碼?</el-link></div></el-form-item><!-- 登錄按鈕 --><el-form-item><el-button class="button" type="primary" auto-insert-space @click="login">登錄</el-button></el-form-item><el-form-item class="flex"><el-link type="info" :underline="false" @click="isRegister = true;clearRegisterDate()">  <!--三 登錄 4  調用函數clearRegisterDate() 清空數據 -->注冊 →</el-link></el-form-item></el-form></el-col></el-row>
</template><style lang="scss" 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.Authorization=tokenStore.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 '@/router'import App from './App.vue'
//六 2.2
import {createPinia} from 'pinia'// 六  4.2 導入持久化插件
import {createPersistedState} from'pinia-persistedstate-plugin'const 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.Authorization=tokenStore.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.status===401){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.js'import {Edit,Delete
} from '@element-plus/icons-vue'
import { ref } from 'vue'
const categorys = ref([{"id": 3,"categoryName": "美食","categoryAlias": "my","createTime": "2023-09-02 12:06:59","updateTime": "2023-09-02 12:06:59"},{"id": 4,"categoryName": "娛樂","categoryAlias": "yl","createTime": "2023-09-02 12:08:16","updateTime": "2023-09-02 12:08:16"},{"id": 5,"categoryName": "軍事","categoryAlias": "js","createTime": "2023-09-02 12:08:33","updateTime": "2023-09-02 12:08:33"}
])// 六 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>
<template><el-card class="page-container"><template #header><div class="header"><span>文章分類</span><div class="extra"><el-button type="primary" @click="dialogVisible = true">添加分類</el-button>  <!--  六 6.1  @click="dialogVisible = true"綁定點擊事件--></div></div></template><el-table :data="categorys" style="width: 100%"><el-table-column label="序號" width="100" type="index"> </el-table-column><el-table-column label="分類名稱" prop="categoryName"></el-table-column><el-table-column label="分類別名" prop="categoryAlias"></el-table-column><el-table-column label="創建時間" prop="createTime"></el-table-column><el-table-column label="更新時間" prop="updateTime"></el-table-column><el-table-column label="操作" width="100"><template #default="{ row }"><el-button :icon="Edit" circle plain type="primary" ></el-button><el-button :icon="Delete" circle plain type="danger"></el-button></template></el-table-column><template #empty><el-empty description="沒有數據" /></template></el-table><!--  六  6.1 添加分類彈窗 --><el-dialog v-model="dialogVisible" title="添加彈層" width="30%"><el-form :model="categoryModel" :rules="rules" label-width="100px" style="padding-right: 30px"><el-form-item label="分類名稱" prop="categoryName"><el-input v-model="categoryModel.categoryName" minlength="1" maxlength="10"></el-input></el-form-item><el-form-item label="分類別名" prop="categoryAlias"><el-input v-model="categoryModel.categoryAlias" minlength="1" maxlength="15"></el-input></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="addCategory"> 確認 </el-button> <!--  六 6.1 @click="addCategory"綁定點擊事件--></span></template></el-dialog></el-card>
</template><style lang="scss" 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 title=ref('')
7.1.2 在彈窗上綁定標題?ArticleCategory.vue
 <el-dialog v-model="dialogVisible" :title="title" width="30%">
7.1.3 為添加分類按鈕綁定事件?ArticleCategory.vue
<el-button type="primary" @click="title='添加分類';dialogVisible = true">添加分類</el-button>
7.1.4 為修改分類按鈕綁定事件?ArticleCategory.vue
<el-button :icon="Edit" circle plain type="primary" @click="title='修改分類';dialogVisible=true"></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.js'import {Edit,Delete
} from '@element-plus/icons-vue'
import { ref } from 'vue'
const categorys = ref([{"id": 3,"categoryName": "美食","categoryAlias": "my","createTime": "2023-09-02 12:06:59","updateTime": "2023-09-02 12:06:59"},{"id": 4,"categoryName": "娛樂","categoryAlias": "yl","createTime": "2023-09-02 12:08:16","updateTime": "2023-09-02 12:08:16"},{"id": 5,"categoryName": "軍事","categoryAlias": "js","createTime": "2023-09-02 12:08:33","updateTime": "2023-09-02 12:08:33"}
])// 六 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 title=ref('')// 六 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>
<template><el-card class="page-container"><template #header><div class="header"><span>文章分類</span><div class="extra"><el-button type="primary" @click="title='添加分類';dialogVisible = true">添加分類</el-button>  <!--  六 6.1  @click="dialogVisible = true"綁定點擊事件   六7.1.3 title='添加分類';--> </div></div></template><el-table :data="categorys" style="width: 100%"><el-table-column label="序號" width="100" type="index"> </el-table-column><el-table-column label="分類名稱" prop="categoryName"></el-table-column><el-table-column label="分類別名" prop="categoryAlias"></el-table-column><el-table-column label="創建時間" prop="createTime"></el-table-column><el-table-column label="更新時間" prop="updateTime"></el-table-column><el-table-column label="操作" width="100"><template #default="{ row }"><el-button :icon="Edit" circle plain type="primary" @click="showDialog(row)"></el-button>  <!--  六 7.1.4 @click="title='修改分類';dialogVisible=true"綁定事件   六 7.2 showDialog(row)--><el-button :icon="Delete" circle plain type="danger"></el-button></template></el-table-column><template #empty><el-empty description="沒有數據" /></template></el-table><!--  六  6.1 添加分類彈窗 --><el-dialog v-model="dialogVisible" :title="title" width="30%">   <!--  六 7.1.2 :title="title"綁定標題--><el-form :model="categoryModel" :rules="rules" label-width="100px" style="padding-right: 30px"><el-form-item label="分類名稱" prop="categoryName"><el-input v-model="categoryModel.categoryName" minlength="1" maxlength="10"></el-input></el-form-item><el-form-item label="分類別名" prop="categoryAlias"><el-input v-model="categoryModel.categoryAlias" minlength="1" maxlength="15"></el-input></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="addCategory"> 確認 </el-button> <!--  六 6.1 @click="addCategory"綁定點擊事件--></span></template></el-dialog></el-card>
</template><style lang="scss" 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 class="dialog-footer"><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="title==='添加分類'? 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.js'import {Edit,Delete
} from '@element-plus/icons-vue'
import { ref } from 'vue'
const categorys = ref([{"id": 3,"categoryName": "美食","categoryAlias": "my","createTime": "2023-09-02 12:06:59","updateTime": "2023-09-02 12:06:59"},{"id": 4,"categoryName": "娛樂","categoryAlias": "yl","createTime": "2023-09-02 12:08:16","updateTime": "2023-09-02 12:08:16"},{"id": 5,"categoryName": "軍事","categoryAlias": "js","createTime": "2023-09-02 12:08:33","updateTime": "2023-09-02 12:08:33"}
])// 六 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 title=ref('')// 六 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 updateCategory=async ()=>{let result = await articleCategoryUpdateService(categoryModel.value)ElMessage.success(result.message? result.message:'修改成功')//隱藏彈窗dialogVisible.value=false//再次訪問后臺接口,查詢所有分類articleCategoryList()
}</script>
<template><el-card class="page-container"><template #header><div class="header"><span>文章分類</span><div class="extra"><el-button type="primary" @click="title='添加分類';dialogVisible = true">添加分類</el-button>  <!--  六 6.1  @click="dialogVisible = true"綁定點擊事件   六7.1.3 title='添加分類';--> </div></div></template><el-table :data="categorys" style="width: 100%"><el-table-column label="序號" width="100" type="index"> </el-table-column><el-table-column label="分類名稱" prop="categoryName"></el-table-column><el-table-column label="分類別名" prop="categoryAlias"></el-table-column><el-table-column label="創建時間" prop="createTime"></el-table-column><el-table-column label="更新時間" prop="updateTime"></el-table-column><el-table-column label="操作" width="100"><template #default="{ row }"><el-button :icon="Edit" circle plain type="primary" @click="showDialog(row)"></el-button>  <!--  六 7.1.4 @click="title='修改分類';dialogVisible=true"綁定事件   六 7.2 showDialog(row)--><el-button :icon="Delete" circle plain type="danger"></el-button></template></el-table-column><template #empty><el-empty description="沒有數據" /></template></el-table><!--  六  6.1 添加分類彈窗 --><el-dialog v-model="dialogVisible" :title="title" width="30%">   <!--  六 7.1.2 :title="title"綁定標題--><el-form :model="categoryModel" :rules="rules" label-width="100px" style="padding-right: 30px"><el-form-item label="分類名稱" prop="categoryName"><el-input v-model="categoryModel.categoryName" minlength="1" maxlength="10"></el-input></el-form-item><el-form-item label="分類別名" prop="categoryAlias"><el-input v-model="categoryModel.categoryAlias" minlength="1" maxlength="15"></el-input></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="title==='添加分類'? addCategory():updateCategory()"> 確認 </el-button> <!--  六 6.1 @click="addCategory"綁定點擊事件  六 7.3.1 @click="title==='添加分類'? addCategory():updateCategory()"--></span></template></el-dialog></el-card>
</template><style lang="scss" 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.js'import {Edit,Delete
} from '@element-plus/icons-vue'
import { ref } from 'vue'
const categorys = ref([{"id": 3,"categoryName": "美食","categoryAlias": "my","createTime": "2023-09-02 12:06:59","updateTime": "2023-09-02 12:06:59"},{"id": 4,"categoryName": "娛樂","categoryAlias": "yl","createTime": "2023-09-02 12:08:16","updateTime": "2023-09-02 12:08:16"},{"id": 5,"categoryName": "軍事","categoryAlias": "js","createTime": "2023-09-02 12:08:33","updateTime": "2023-09-02 12:08:33"}
])// 六 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 title=ref('')// 六 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 updateCategory=async ()=>{let result = await articleCategoryUpdateService(categoryModel.value)ElMessage.success(result.message? result.message:'修改成功')//隱藏彈窗dialogVisible.value=false//再次訪問后臺接口,查詢所有分類articleCategoryList()
}//六 7.3.4 清空模型數據
const clearCategoryModel = ()=>{categoryModel.value.categoryName='',categoryModel.value.categoryAlias=''
}</script>
<template><el-card class="page-container"><template #header><div class="header"><span>文章分類</span><div class="extra"><el-button type="primary" @click="title='添加分類';dialogVisible = true;clearCategoryModel()">添加分類</el-button>  <!--  六 6.1  @click="dialogVisible = true"綁定點擊事件   六7.1.3 title='添加分類'; 六 7.3.4 清空模型數據 clearCategoryModel()--> </div></div></template><el-table :data="categorys" style="width: 100%"><el-table-column label="序號" width="100" type="index"> </el-table-column><el-table-column label="分類名稱" prop="categoryName"></el-table-column><el-table-column label="分類別名" prop="categoryAlias"></el-table-column><el-table-column label="創建時間" prop="createTime"></el-table-column><el-table-column label="更新時間" prop="updateTime"></el-table-column><el-table-column label="操作" width="100"><template #default="{ row }"><el-button :icon="Edit" circle plain type="primary" @click="showDialog(row)"></el-button>  <!--  六 7.1.4 @click="title='修改分類';dialogVisible=true"綁定事件   六 7.2 showDialog(row)--><el-button :icon="Delete" circle plain type="danger"></el-button></template></el-table-column><template #empty><el-empty description="沒有數據" /></template></el-table><!--  六  6.1 添加分類彈窗 --><el-dialog v-model="dialogVisible" :title="title" width="30%">   <!--  六 7.1.2 :title="title"綁定標題--><el-form :model="categoryModel" :rules="rules" label-width="100px" style="padding-right: 30px"><el-form-item label="分類名稱" prop="categoryName"><el-input v-model="categoryModel.categoryName" minlength="1" maxlength="10"></el-input></el-form-item><el-form-item label="分類別名" prop="categoryAlias"><el-input v-model="categoryModel.categoryAlias" minlength="1" maxlength="15"></el-input></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="title==='添加分類'? addCategory():updateCategory()"> 確認 </el-button> <!--  六 6.1 @click="addCategory"綁定點擊事件  六 7.3.1 @click="title==='添加分類'? addCategory():updateCategory()"--></span></template></el-dialog></el-card>
</template><style lang="scss" 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?id='+id)
}

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 :icon="Edit" circle plain type="primary" @click="showDialog(row)"></el-button>  <!--  六 7.1.4 @click="title='修改分類';dialogVisible=true"綁定事件   六 7.2 showDialog(row)--><el-button :icon="Delete" circle plain type="danger" @click="deleteCategory(row)"></el-button> <!--  六 8.3 @click="deleteCategory(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.js'import {Edit,Delete
} from '@element-plus/icons-vue'
import { ref } from 'vue'
const categorys = ref([{"id": 3,"categoryName": "美食","categoryAlias": "my","createTime": "2023-09-02 12:06:59","updateTime": "2023-09-02 12:06:59"},{"id": 4,"categoryName": "娛樂","categoryAlias": "yl","createTime": "2023-09-02 12:08:16","updateTime": "2023-09-02 12:08:16"},{"id": 5,"categoryName": "軍事","categoryAlias": "js","createTime": "2023-09-02 12:08:33","updateTime": "2023-09-02 12:08:33"}
])// 六 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 title=ref('')// 六 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 updateCategory=async ()=>{let result = await articleCategoryUpdateService(categoryModel.value)ElMessage.success(result.message? result.message:'修改成功')//隱藏彈窗dialogVisible.value=false//再次訪問后臺接口,查詢所有分類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>
<template><el-card class="page-container"><template #header><div class="header"><span>文章分類</span><div class="extra"><el-button type="primary" @click="title='添加分類';dialogVisible = true;clearCategoryModel()">添加分類</el-button>  <!--  六 6.1  @click="dialogVisible = true"綁定點擊事件   六7.1.3 title='添加分類'; 六 7.3.4 清空模型數據 clearCategoryModel()--> </div></div></template><el-table :data="categorys" style="width: 100%"><el-table-column label="序號" width="100" type="index"> </el-table-column><el-table-column label="分類名稱" prop="categoryName"></el-table-column><el-table-column label="分類別名" prop="categoryAlias"></el-table-column><el-table-column label="創建時間" prop="createTime"></el-table-column><el-table-column label="更新時間" prop="updateTime"></el-table-column><el-table-column label="操作" width="100"><template #default="{ row }"><el-button :icon="Edit" circle plain type="primary" @click="showDialog(row)"></el-button>  <!--  六 7.1.4 @click="title='修改分類';dialogVisible=true"綁定事件   六 7.2 showDialog(row)--><el-button :icon="Delete" circle plain type="danger" @click="deleteCategory(row)"></el-button> <!--  六 8.3 @click="deleteCategory(row)" 綁定刪除事件--></template></el-table-column><template #empty><el-empty description="沒有數據" /></template></el-table><!--  六  6.1 添加分類彈窗 --><el-dialog v-model="dialogVisible" :title="title" width="30%">   <!--  六 7.1.2 :title="title"綁定標題--><el-form :model="categoryModel" :rules="rules" label-width="100px" style="padding-right: 30px"><el-form-item label="分類名稱" prop="categoryName"><el-input v-model="categoryModel.categoryName" minlength="1" maxlength="10"></el-input></el-form-item><el-form-item label="分類別名" prop="categoryAlias"><el-input v-model="categoryModel.categoryAlias" minlength="1" maxlength="15"></el-input></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="title==='添加分類'? addCategory():updateCategory()"> 確認 </el-button> <!--  六 6.1 @click="addCategory"綁定點擊事件  六 7.3.1 @click="title==='添加分類'? addCategory():updateCategory()"--></span></template></el-dialog></el-card>
</template><style lang="scss" 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-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"}
])//用戶搜索時選中的分類id
const categoryId=ref('')//用戶搜索時選中的發布狀態
const state=ref('')//文章列表數據模型
const articles = ref([{"id": 5,"title": "陜西旅游攻略","content": "兵馬俑,華清池,法門寺,華山...愛去哪去哪...","coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png","state": "草稿","categoryId": 2,"createTime": "2023-09-03 11:55:30","updateTime": "2023-09-03 11:55:30"},{"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>
<template><el-card class="page-container"><template #header><div class="header"><span>文章管理</span><div class="extra"><el-button type="primary">添加文章</el-button></div></div></template><!-- 搜索表單 --><el-form inline><el-form-item label="文章分類:"><el-select placeholder="請選擇" v-model="categoryId"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName":value="c.id"></el-option></el-select></el-form-item><el-form-item label="發布狀態:"><el-select placeholder="請選擇" v-model="state"><el-option label="已發布" value="已發布"></el-option><el-option label="草稿" value="草稿"></el-option></el-select></el-form-item><el-form-item><el-button type="primary">搜索</el-button><el-button>重置</el-button></el-form-item></el-form><!-- 文章列表 --><el-table :data="articles" style="width: 100%"><el-table-column label="文章標題" width="400" prop="title"></el-table-column><el-table-column label="分類" prop="categoryId"></el-table-column><el-table-column label="發表時間" prop="createTime"> </el-table-column><el-table-column label="狀態" prop="state"></el-table-column><el-table-column label="操作" width="100"><template #default="{ row }"><el-button :icon="Edit" circle plain type="primary"></el-button><el-button :icon="Delete" circle plain type="danger"></el-button></template></el-table-column><template #empty><el-empty description="沒有數據" /></template></el-table><!-- 分頁條 --><el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[3, 5 ,10, 15]"layout="jumper, total, sizes, prev, pager, next" background :total="total" @size-change="onSizeChange"@current-change="onCurrentChange" style="margin-top: 20px; justify-content: flex-end" /></el-card>
</template>
<style lang="scss" 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 '@/router'import App from './App.vue'
//六 2.2
import {createPinia} from 'pinia'// 六  4.2 導入持久化插件
import {createPersistedState} from 'pinia-persistedstate-plugin'const 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?id='+id)
}// 八 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-vue'import { 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 categoryId=ref('')//用戶搜索時選中的發布狀態
const state=ref('')//文章列表數據模型
const articles = ref([{"id": 5,"title": "陜西旅游攻略","content": "兵馬俑,華清池,法門寺,華山...愛去哪去哪...","coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png","state": "草稿","categoryId": 2,"createTime": "2023-09-03 11:55:30","updateTime": "2023-09-03 11:55:30"},{"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>
<template><el-card class="page-container"><!-- 1 標題 --><template #header><div class="header"><span>文章管理</span><div class="extra"><el-button type="primary">添加文章</el-button></div></div></template><!-- 2 搜索表單 --><el-form inline><el-form-item label="文章分類:"><el-select placeholder="請選擇" v-model="categoryId"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName":value="c.id"></el-option></el-select></el-form-item><el-form-item label="發布狀態:"><el-select placeholder="請選擇" v-model="state"><el-option label="已發布" value="已發布"></el-option><el-option label="草稿" value="草稿"></el-option></el-select></el-form-item><el-form-item><el-button type="primary">搜索</el-button><el-button>重置</el-button></el-form-item></el-form><!-- 3 文章列表 --><el-table :data="articles" style="width: 100%"><el-table-column label="文章標題" width="400" prop="title"></el-table-column><el-table-column label="分類" prop="categoryName"></el-table-column><el-table-column label="發表時間" prop="createTime"> </el-table-column><el-table-column label="更新時間" prop="updateTime"> </el-table-column><el-table-column label="狀態" prop="state"></el-table-column><el-table-column label="操作" width="100"><template #default="{ row }"><el-button :icon="Edit" circle plain type="primary"></el-button><el-button :icon="Delete" circle plain type="danger"></el-button></template></el-table-column><template #empty><el-empty description="沒有數據" /></template></el-table><!-- 4 分頁條 --><el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[3, 5 ,10, 15]"layout="jumper, total, sizes, prev, pager, next" background :total="total" @size-change="onSizeChange"@current-change="onCurrentChange" style="margin-top: 20px; justify-content: flex-end" /></el-card>
</template>
<style lang="scss" 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-vue'import { 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 categoryId=ref('')//用戶搜索時選中的發布狀態
const state=ref('')//文章列表數據模型
const articles = ref([{"id": 5,"title": "陜西旅游攻略","content": "兵馬俑,華清池,法門寺,華山...愛去哪去哪...","coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png","state": "草稿","categoryId": 2,"createTime": "2023-09-03 11:55:30","updateTime": "2023-09-03 11:55:30"},{"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>
<template><el-card class="page-container"><!-- 1 標題 --><template #header><div class="header"><span>文章管理</span><div class="extra"><el-button type="primary">添加文章</el-button></div></div></template><!-- 2 搜索表單 --><el-form inline><el-form-item label="文章分類:"><el-select placeholder="請選擇" v-model="categoryId"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName":value="c.id"></el-option></el-select></el-form-item><el-form-item label="發布狀態:"><el-select placeholder="請選擇" v-model="state"><el-option label="已發布" value="已發布"></el-option><el-option label="草稿" value="草稿"></el-option></el-select></el-form-item><el-form-item><el-button type="primary" @click="articleList">搜索</el-button>  <!-- 七 2  綁定查詢事件--><el-button @click="categoryId='';state='';articleList()">重置</el-button>  <!-- 七 2  綁定重置事件--></el-form-item></el-form><!-- 3 文章列表 --><el-table :data="articles" style="width: 100%"><el-table-column label="文章標題" width="400" prop="title"></el-table-column><el-table-column label="分類" prop="categoryName"></el-table-column><el-table-column label="發表時間" prop="createTime"> </el-table-column><el-table-column label="更新時間" prop="updateTime"> </el-table-column><el-table-column label="狀態" prop="state"></el-table-column><el-table-column label="操作" width="100"><template #default="{ row }"><el-button :icon="Edit" circle plain type="primary"></el-button><el-button :icon="Delete" circle plain type="danger"></el-button></template></el-table-column><template #empty><el-empty description="沒有數據" /></template></el-table><!-- 4 分頁條 --><el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[3, 5 ,10, 15]"layout="jumper, total, sizes, prev, pager, next" background :total="total" @size-change="onSizeChange"@current-change="onCurrentChange" style="margin-top: 20px; justify-content: flex-end" /></el-card>
</template>
<style lang="scss" 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-model="visibleDrawer" title="添加文章" direction="rtl" size="50%"><!-- 添加文章表單 --><el-form :model="articleModel" label-width="100px" ><el-form-item label="文章標題" ><el-input v-model="articleModel.title" placeholder="請輸入標題"></el-input></el-form-item><el-form-item label="文章分類"><el-select placeholder="請選擇" v-model="articleModel.categoryId"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName" :value="c.id"></el-option></el-select></el-form-item><el-form-item label="文章封面"><el-upload class="avatar-uploader" :auto-upload="false" :show-file-list="false"><img v-if="articleModel.coverImg" :src="articleModel.coverImg" class="avatar" /><el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon></el-upload></el-form-item><el-form-item label="文章內容"><div class="editor">富文本編輯器</div></el-form-item><el-form-item><el-button type="primary">發布</el-button><el-button type="info">草稿</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-vue'import { 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 categoryId=ref('')//用戶搜索時選中的發布狀態
const state=ref('')//文章列表數據模型
const articles = ref([{"id": 5,"title": "陜西旅游攻略","content": "兵馬俑,華清池,法門寺,華山...愛去哪去哪...","coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png","state": "草稿","categoryId": 2,"createTime": "2023-09-03 11:55:30","updateTime": "2023-09-03 11:55:30"},{"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>
<template><el-card class="page-container"><!-- 1 標題 --><template #header><div class="header"><span>文章管理</span><div class="extra"><el-button type="primary" @click="visibleDrawer = true">添加文章</el-button>  <!-- 七 3.2 @click="visibleDrawer = true" 綁定查詢事件--></div></div></template><!-- 2 搜索表單 --><el-form inline><el-form-item label="文章分類:"><el-select placeholder="請選擇" v-model="categoryId"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName":value="c.id"></el-option></el-select></el-form-item><el-form-item label="發布狀態:"><el-select placeholder="請選擇" v-model="state"><el-option label="已發布" value="已發布"></el-option><el-option label="草稿" value="草稿"></el-option></el-select></el-form-item><el-form-item><el-button type="primary" @click="articleList">搜索</el-button>  <!-- 七 2  綁定查詢事件--><el-button @click="categoryId='';state='';articleList()">重置</el-button>  <!-- 七 2  綁定重置事件--></el-form-item></el-form><!-- 3 文章列表 --><el-table :data="articles" style="width: 100%"><el-table-column label="文章標題" width="400" prop="title"></el-table-column><el-table-column label="分類" prop="categoryName"></el-table-column><el-table-column label="發表時間" prop="createTime"> </el-table-column><el-table-column label="更新時間" prop="updateTime"> </el-table-column><el-table-column label="狀態" prop="state"></el-table-column><el-table-column label="操作" width="100"><template #default="{ row }"><el-button :icon="Edit" circle plain type="primary"></el-button><el-button :icon="Delete" circle plain type="danger"></el-button></template></el-table-column><template #empty><el-empty description="沒有數據" /></template></el-table><!-- 4 分頁條 --><el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[3, 5 ,10, 15]"layout="jumper, total, sizes, prev, pager, next" background :total="total" @size-change="onSizeChange"@current-change="onCurrentChange" style="margin-top: 20px; justify-content: flex-end" /><!-- 七 3.1 抽屜 --><el-drawer v-model="visibleDrawer" title="添加文章" direction="rtl" size="50%"><!-- 添加文章表單 --><el-form :model="articleModel" label-width="100px" ><el-form-item label="文章標題" ><el-input v-model="articleModel.title" placeholder="請輸入標題"></el-input></el-form-item><el-form-item label="文章分類"><el-select placeholder="請選擇" v-model="articleModel.categoryId"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName" :value="c.id"></el-option></el-select></el-form-item><el-form-item label="文章封面"><el-upload class="avatar-uploader" :auto-upload="false" :show-file-list="false"><img v-if="articleModel.coverImg" :src="articleModel.coverImg" class="avatar" /><el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon></el-upload></el-form-item><el-form-item label="文章內容"><div class="editor">富文本編輯器</div></el-form-item><el-form-item><el-button type="primary">發布</el-button><el-button type="info">草稿</el-button></el-form-item></el-form></el-drawer></el-card>
</template>
<style lang="scss" 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-quill@latest --save

3.3.2 導入組件和樣式:
import { QuillEditor } from '@vueup/vue-quill'
import '@vueup/vue-quill/dist/vue-quill.snow.css'
3.3.3 頁面使用quill組件:?
<quill-editortheme="snow"v-model:content="articleModel.content"contentType="html">
</quill-editor>
?
3.3.4 樣式美化:
.editor {width: 100%;:deep(.ql-editor) {min-height: 200px;}
}
<script setup>
import {Edit,Delete
} from '@element-plus/icons-vue'import { 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 categoryId=ref('')//用戶搜索時選中的發布狀態
const state=ref('')//文章列表數據模型
const articles = ref([{"id": 5,"title": "陜西旅游攻略","content": "兵馬俑,華清池,法門寺,華山...愛去哪去哪...","coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png","state": "草稿","categoryId": 2,"createTime": "2023-09-03 11:55:30","updateTime": "2023-09-03 11:55:30"},{"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>
<template><el-card class="page-container"><!-- 1 標題 --><template #header><div class="header"><span>文章管理</span><div class="extra"><el-button type="primary" @click="visibleDrawer = true">添加文章</el-button>  <!-- 七 3.2 @click="visibleDrawer = true" 綁定查詢事件--></div></div></template><!-- 2 搜索表單 --><el-form inline><el-form-item label="文章分類:"><el-select placeholder="請選擇" v-model="categoryId"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName":value="c.id"></el-option></el-select></el-form-item><el-form-item label="發布狀態:"><el-select placeholder="請選擇" v-model="state"><el-option label="已發布" value="已發布"></el-option><el-option label="草稿" value="草稿"></el-option></el-select></el-form-item><el-form-item><el-button type="primary" @click="articleList">搜索</el-button>  <!-- 七 2  綁定查詢事件--><el-button @click="categoryId='';state='';articleList()">重置</el-button>  <!-- 七 2  綁定重置事件--></el-form-item></el-form><!-- 3 文章列表 --><el-table :data="articles" style="width: 100%"><el-table-column label="文章標題" width="400" prop="title"></el-table-column><el-table-column label="分類" prop="categoryName"></el-table-column><el-table-column label="發表時間" prop="createTime"> </el-table-column><el-table-column label="更新時間" prop="updateTime"> </el-table-column><el-table-column label="狀態" prop="state"></el-table-column><el-table-column label="操作" width="100"><template #default="{ row }"><el-button :icon="Edit" circle plain type="primary"></el-button><el-button :icon="Delete" circle plain type="danger"></el-button></template></el-table-column><template #empty><el-empty description="沒有數據" /></template></el-table><!-- 4 分頁條 --><el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[3, 5 ,10, 15]"layout="jumper, total, sizes, prev, pager, next" background :total="total" @size-change="onSizeChange"@current-change="onCurrentChange" style="margin-top: 20px; justify-content: flex-end" /><!-- 七 3.1 抽屜 --><el-drawer v-model="visibleDrawer" title="添加文章" direction="rtl" size="50%"><!-- 添加文章表單 --><el-form :model="articleModel" label-width="100px" ><el-form-item label="文章標題" ><el-input v-model="articleModel.title" placeholder="請輸入標題"></el-input></el-form-item><el-form-item label="文章分類"><el-select placeholder="請選擇" v-model="articleModel.categoryId"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName" :value="c.id"></el-option></el-select></el-form-item><el-form-item label="文章封面"><el-upload class="avatar-uploader" :auto-upload="false" :show-file-list="false"><img v-if="articleModel.coverImg" :src="articleModel.coverImg" class="avatar" /><el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon></el-upload></el-form-item><el-form-item label="文章內容"><div class="editor"><quill-editor theme="snow" v-model:content="articleModel.content" contentType="html"> <!-- 七 3.3.3頁面使用quill組件 --></quill-editor></div></el-form-item><el-form-item><el-button type="primary">發布</el-button><el-button type="info">草稿</el-button></el-form-item></el-form></el-drawer></el-card>
</template>
<style lang="scss" 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-item><el-form-item label="文章封面"><!-- auto-upload:是否自動上傳action: 服務器接口路徑name: 上傳的文件字段名headers: 設置上傳的請求頭on-success: 上傳成功的回調函數 --><el-upload class="avatar-uploader" :auto-upload="true" :show-file-list="false"action="/api/upload"name="file":headers="{Authorization:tokenStore.token}":on-success="uploadSuccess"><img v-if="articleModel.coverImg" :src="articleModel.coverImg" class="avatar" /><el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon></el-upload></el-form-item>

注意:

  1. 由于這個請求時el-upload自動發送的異步請求,并沒有使用咱們的request.js請求工具,所以在請求的路ing上,需要加上/api, 這個時候請求代理才能攔截到這個請求,轉發到后臺服務器上

  2. 要攜帶請求頭,還需要導入pinia狀態才可以使用

    import { useTokenStore } from '@/stores/token.js'
    const tokenStore = useTokenStore();
    <script setup>
    import {Edit,Delete
    } from '@element-plus/icons-vue'import { 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.coverImg=img.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 categoryId=ref('')//用戶搜索時選中的發布狀態
    const state=ref('')//文章列表數據模型
    const articles = ref([{"id": 5,"title": "陜西旅游攻略","content": "兵馬俑,華清池,法門寺,華山...愛去哪去哪...","coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png","state": "草稿","categoryId": 2,"createTime": "2023-09-03 11:55:30","updateTime": "2023-09-03 11:55:30"},{"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>
    <template><el-card class="page-container"><!-- 1 標題 --><template #header><div class="header"><span>文章管理</span><div class="extra"><el-button type="primary" @click="visibleDrawer = true">添加文章</el-button>  <!-- 七 3.2 @click="visibleDrawer = true" 綁定查詢事件--></div></div></template><!-- 2 搜索表單 --><el-form inline><el-form-item label="文章分類:"><el-select placeholder="請選擇" v-model="categoryId"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName":value="c.id"></el-option></el-select></el-form-item><el-form-item label="發布狀態:"><el-select placeholder="請選擇" v-model="state"><el-option label="已發布" value="已發布"></el-option><el-option label="草稿" value="草稿"></el-option></el-select></el-form-item><el-form-item><el-button type="primary" @click="articleList">搜索</el-button>  <!-- 七 2  綁定查詢事件--><el-button @click="categoryId='';state='';articleList()">重置</el-button>  <!-- 七 2  綁定重置事件--></el-form-item></el-form><!-- 3 文章列表 --><el-table :data="articles" style="width: 100%"><el-table-column label="文章標題" width="400" prop="title"></el-table-column><el-table-column label="分類" prop="categoryName"></el-table-column><el-table-column label="發表時間" prop="createTime"> </el-table-column><el-table-column label="更新時間" prop="updateTime"> </el-table-column><el-table-column label="狀態" prop="state"></el-table-column><el-table-column label="操作" width="100"><template #default="{ row }"><el-button :icon="Edit" circle plain type="primary"></el-button><el-button :icon="Delete" circle plain type="danger"></el-button></template></el-table-column><template #empty><el-empty description="沒有數據" /></template></el-table><!-- 4 分頁條 --><el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[3, 5 ,10, 15]"layout="jumper, total, sizes, prev, pager, next" background :total="total" @size-change="onSizeChange"@current-change="onCurrentChange" style="margin-top: 20px; justify-content: flex-end" /><!-- 七 3.1 抽屜 --><el-drawer v-model="visibleDrawer" title="添加文章" direction="rtl" size="50%"><!-- 添加文章表單 --><el-form :model="articleModel" label-width="100px" ><el-form-item label="文章標題" ><el-input v-model="articleModel.title" placeholder="請輸入標題"></el-input></el-form-item><el-form-item label="文章分類"><el-select placeholder="請選擇" v-model="articleModel.categoryId"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName" :value="c.id"></el-option></el-select></el-form-item><el-form-item label="文章封面"><!-- auto-upload:是否自動上傳action: 服務器接口路徑name: 上傳的文件字段名headers: 設置上傳的請求頭on-success: 上傳成功的回調函數 --><el-upload class="avatar-uploader" :auto-upload="true" :show-file-list="false"action="/api/upload"name="file":headers="{Authorization:tokenStore.token}":on-success="uploadSuccess"><img v-if="articleModel.coverImg" :src="articleModel.coverImg" class="avatar" /><el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon></el-upload></el-form-item><el-form-item label="文章內容"><div class="editor"><quill-editor theme="snow" v-model:content="articleModel.content" contentType="html"> <!-- 七 3.3.3頁面使用quill組件 --></quill-editor></div></el-form-item><el-form-item><el-button type="primary">發布</el-button><el-button type="info">草稿</el-button></el-form-item></el-form></el-drawer></el-card>
    </template>
    <style lang="scss" 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?id='+id)
}// 七 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-item><el-button type="primary" @click="addArticle('已發布')">發布</el-button><el-button type="info" @click="addArticle('草稿')">草稿</el-button>
</el-form-item>
3.5.3?ArticleManage.vue中提供addArticle函數完成添加文章接口的調用
const addArticle=async (state)=>{articleModel.value.state = statelet result = await articleAddService(articleModel.value);ElMessage.success(result.message? result.message:'添加成功')//再次調用getArticles,獲取文章articleList()//隱藏抽屜visibleDrawer.value=false
}
<script setup>
import {Edit,Delete
} from '@element-plus/icons-vue'import { 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.coverImg=img.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 categoryId=ref('')//用戶搜索時選中的發布狀態
const state=ref('')//文章列表數據模型
const articles = ref([{"id": 5,"title": "陜西旅游攻略","content": "兵馬俑,華清池,法門寺,華山...愛去哪去哪...","coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png","state": "草稿","categoryId": 2,"createTime": "2023-09-03 11:55:30","updateTime": "2023-09-03 11:55:30"},{"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 addArticle=async (state)=>{articleModel.value.state = statelet result = await articleAddService(articleModel.value);ElMessage.success(result.message? result.message:'添加成功')//再次調用getArticles,獲取文章articleList()//隱藏抽屜visibleDrawer.value=false
}</script>
<template><el-card class="page-container"><!-- 1 標題 --><template #header><div class="header"><span>文章管理</span><div class="extra"><el-button type="primary" @click="visibleDrawer = true">添加文章</el-button>  <!-- 七 3.2 @click="visibleDrawer = true" 綁定查詢事件--></div></div></template><!-- 2 搜索表單 --><el-form inline><el-form-item label="文章分類:"><el-select placeholder="請選擇" v-model="categoryId"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName":value="c.id"></el-option></el-select></el-form-item><el-form-item label="發布狀態:"><el-select placeholder="請選擇" v-model="state"><el-option label="已發布" value="已發布"></el-option><el-option label="草稿" value="草稿"></el-option></el-select></el-form-item><el-form-item><el-button type="primary" @click="articleList">搜索</el-button>  <!-- 七 2  綁定查詢事件--><el-button @click="categoryId='';state='';articleList()">重置</el-button>  <!-- 七 2  綁定重置事件--></el-form-item></el-form><!-- 3 文章列表 --><el-table :data="articles" style="width: 100%"><el-table-column label="文章標題" width="400" prop="title"></el-table-column><el-table-column label="分類" prop="categoryName"></el-table-column><el-table-column label="發表時間" prop="createTime"> </el-table-column><el-table-column label="更新時間" prop="updateTime"> </el-table-column><el-table-column label="狀態" prop="state"></el-table-column><el-table-column label="操作" width="100"><template #default="{ row }"><el-button :icon="Edit" circle plain type="primary"></el-button><el-button :icon="Delete" circle plain type="danger"></el-button></template></el-table-column><template #empty><el-empty description="沒有數據" /></template></el-table><!-- 4 分頁條 --><el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[3, 5 ,10, 15]"layout="jumper, total, sizes, prev, pager, next" background :total="total" @size-change="onSizeChange"@current-change="onCurrentChange" style="margin-top: 20px; justify-content: flex-end" /><!-- 七 3.1 抽屜 --><el-drawer v-model="visibleDrawer" title="添加文章" direction="rtl" size="50%"><!-- 添加文章表單 --><el-form :model="articleModel" label-width="100px" ><el-form-item label="文章標題" ><el-input v-model="articleModel.title" placeholder="請輸入標題"></el-input></el-form-item><el-form-item label="文章分類"><el-select placeholder="請選擇" v-model="articleModel.categoryId"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName" :value="c.id"></el-option></el-select></el-form-item><el-form-item label="文章封面"><!-- auto-upload:是否自動上傳action: 服務器接口路徑name: 上傳的文件字段名headers: 設置上傳的請求頭on-success: 上傳成功的回調函數 --><el-upload class="avatar-uploader" :auto-upload="true" :show-file-list="false"action="/api/upload"name="file":headers="{Authorization:tokenStore.token}":on-success="uploadSuccess"><img v-if="articleModel.coverImg" :src="articleModel.coverImg" class="avatar" /><el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon></el-upload></el-form-item><el-form-item label="文章內容"><div class="editor"><quill-editor theme="snow" v-model:content="articleModel.content" contentType="html"> <!-- 七 3.3.3頁面使用quill組件 --></quill-editor></div></el-form-item><el-form-item><el-form-item><el-button type="primary" @click="addArticle('已發布')">發布</el-button> <!-- 七 3.5.2 @click="addArticle('已發布')"--><el-button type="info" @click="addArticle('草稿')">草稿</el-button> <!-- 七 3.5.2@click="addArticle('草稿')"--></el-form-item></el-form-item></el-form></el-drawer></el-card>
</template>
<style lang="scss" 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 title=ref('')
4.1.2 在抽屜上綁定標題
 <el-drawer v-model="visibleDrawer" :title="title" direction="rtl" size="50%">
4.1.3 為添加文章按鈕綁定事件
 <el-button type="primary" @click="title='添加文章';visibleDrawer = true">添加文章</el-button> 
4.1.4 為修改文章按鈕綁定事件
 <el-button :icon="Edit" circle plain type="primary" @click="title='修改文章';visibleDrawer='true'"></el-button> 

4.2 數據回顯

當點擊修改文章按鈕時,需要把當前這一條數據的詳細信息顯示到修改文章的抽屜上,這個叫回顯

4.2.1 通過插槽的方式得到被點擊按鈕所在行的數據
 <template #default="{ row }"><el-button :icon="Edit" circle plain type="primary" @click="showDrawer(row)"></el-button> <!--七4.1.4 @click="title='修改文章';visibleDrawer='true'"  七 4.2 showDrawer(row) --><el-button :icon="Delete" circle plain type="danger"></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-vue'import { 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.coverImg=img.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 categoryId=ref('')//用戶搜索時選中的發布狀態
const state=ref('')//文章列表數據模型
const articles = ref([{"id": 5,"title": "陜西旅游攻略","content": "兵馬俑,華清池,法門寺,華山...愛去哪去哪...","coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png","state": "草稿","categoryId": 2,"createTime": "2023-09-03 11:55:30","updateTime": "2023-09-03 11:55:30"},{"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 addArticle=async (state)=>{articleModel.value.state = statelet result = await articleAddService(articleModel.value);ElMessage.success(result.message? result.message:'添加成功')//再次調用getArticles,獲取文章articleList()//隱藏抽屜visibleDrawer.value=false
}//<!-- 七 4.1 抽屜:title="title" -綁定標題 -->
const title=ref('')// 七 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>
<template><el-card class="page-container"><!-- 1 標題 --><template #header><div class="header"><span>文章管理</span><div class="extra"><el-button type="primary" @click="title='添加文章';visibleDrawer = true">添加文章</el-button>  <!-- 七 3.2 @click="visibleDrawer = true" 綁定查詢事件 七 4.1.3 title='添加文章'; --></div></div></template><!-- 2 搜索表單 --><el-form inline><el-form-item label="文章分類:"><el-select placeholder="請選擇" v-model="categoryId"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName":value="c.id"></el-option></el-select></el-form-item><el-form-item label="發布狀態:"><el-select placeholder="請選擇" v-model="state"><el-option label="已發布" value="已發布"></el-option><el-option label="草稿" value="草稿"></el-option></el-select></el-form-item><el-form-item><el-button type="primary" @click="articleList">搜索</el-button>  <!-- 七 2  綁定查詢事件--><el-button @click="categoryId='';state='';articleList()">重置</el-button>  <!-- 七 2  綁定重置事件--></el-form-item></el-form><!-- 3 文章列表 --><el-table :data="articles" style="width: 100%"><el-table-column label="文章標題" width="400" prop="title"></el-table-column><el-table-column label="分類" prop="categoryName"></el-table-column><el-table-column label="發表時間" prop="createTime"> </el-table-column><el-table-column label="更新時間" prop="updateTime"> </el-table-column><el-table-column label="狀態" prop="state"></el-table-column><el-table-column label="操作" width="100"><template #default="{ row }"><el-button :icon="Edit" circle plain type="primary" @click="showDrawer(row)"></el-button> <!--七4.1.4 @click="title='修改文章';visibleDrawer='true'"  七 4.2.1 showDrawer(row) --><el-button :icon="Delete" circle plain type="danger"></el-button></template></el-table-column><template #empty><el-empty description="沒有數據" /></template></el-table><!-- 4 分頁條 --><el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[3, 5 ,10, 15]"layout="jumper, total, sizes, prev, pager, next" background :total="total" @size-change="onSizeChange"@current-change="onCurrentChange" style="margin-top: 20px; justify-content: flex-end" /><!-- 七 3.1 抽屜 --><el-drawer v-model="visibleDrawer" :title="title" direction="rtl" size="50%"> <!-- 七 4.1 抽屜:title="title" -綁定標題 --><!-- 添加文章表單 --><el-form :model="articleModel" label-width="100px" ><el-form-item label="文章標題" ><el-input v-model="articleModel.title" placeholder="請輸入標題"></el-input></el-form-item><el-form-item label="文章分類"><el-select placeholder="請選擇" v-model="articleModel.categoryId"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName" :value="c.id"></el-option></el-select></el-form-item><el-form-item label="文章封面"><!-- auto-upload:是否自動上傳action: 服務器接口路徑name: 上傳的文件字段名headers: 設置上傳的請求頭on-success: 上傳成功的回調函數 --><el-upload class="avatar-uploader" :auto-upload="true" :show-file-list="false"action="/api/upload"name="file":headers="{Authorization:tokenStore.token}":on-success="uploadSuccess"><img v-if="articleModel.coverImg" :src="articleModel.coverImg" class="avatar" /><el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon></el-upload></el-form-item><el-form-item label="文章內容"><div class="editor"><quill-editor theme="snow" v-model:content="articleModel.content" contentType="html"> <!-- 七 3.3.3頁面使用quill組件 --></quill-editor></div></el-form-item><el-form-item><el-form-item><el-button type="primary" @click="addArticle('已發布')">發布</el-button> <!-- 七 3.5.2 @click="addArticle('已發布')"--><el-button type="info" @click="addArticle('草稿')">草稿</el-button> <!-- 七 3.5.2@click="addArticle('草稿')"--></el-form-item></el-form-item></el-form></el-drawer></el-card>
</template>
<style lang="scss" 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?id='+id)
}// 七 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-item><el-button type="primary" @click="title==='添加文章'? addArticle('已發布'): updateArticle('已發布')">發布</el-button> <!-- 七 3.5.2 @click="addArticle('已發布')  七 4.3.2 ;title==='添加文章'? addArticle('已發布'):updateArticle('已發布')"--><el-button type="info" @click="title==='添加文章'? addArticle('草稿'): updateArticle('草稿')">草稿</el-button> <!-- 七 3.5.2@click="addArticle('草稿')"  七 4.3.2 ;title==='添加文章'? addArticle('草稿'):updateArticle('草稿')--></el-form-item>
4.3.3 調用接口完成修改的函數
//七 4.4.4 修改文章
const updateArticle=async ()=>{let result = await articleUpdateService(articleModel.value)ElMessage.success(result.message? result.message:'修改成功')//隱藏抽屜visibleDrawer.value=false//再次訪問后臺接口,查詢所有文章articleList()
}

由于現在修改和新增共用了一個數據模型,所以在點擊添加文章后,有時候會顯示數據,此時可以將articleModel中的數據清空

//七 4.4.3 清空模型數據
const clearArticleModel = ()=>{articleModel.value.title='',articleModel.value.categoryId='',articleModel.value.coverImg='',articleModel.value.content='<br>',articleModel.value.state=''
}

修改 添加文章按鈕的點擊事件()

<el-button type="primary" @click="title='添加文章';visibleDrawer = true;clearArticleModel()">添加文章</el-button>  <!-- 七 3.2 @click="visibleDrawer = true" 綁定查詢事件 七 4.1.3 title='添加文章';  七 4.3.3 ;clearArticleModel() -->
<script setup>
import {Edit,Delete
} from '@element-plus/icons-vue'import { 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.coverImg=img.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 categoryId=ref('')//用戶搜索時選中的發布狀態
const state=ref('')//文章列表數據模型
const articles = ref([{"id": 5,"title": "陜西旅游攻略","content": "兵馬俑,華清池,法門寺,華山...愛去哪去哪...","coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png","state": "草稿","categoryId": 2,"createTime": "2023-09-03 11:55:30","updateTime": "2023-09-03 11:55:30"},{"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 addArticle=async (state)=>{articleModel.value.state = statelet result = await articleAddService(articleModel.value);ElMessage.success(result.message? result.message:'添加成功')//再次調用getArticles,獲取文章articleList()//隱藏抽屜visibleDrawer.value=false
}//<!-- 七 4.1 抽屜:title="title" -綁定標題 -->
const title=ref('')// 七 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 updateArticle=async ()=>{let result = await articleUpdateService(articleModel.value)ElMessage.success(result.message? result.message:'修改成功')//隱藏抽屜visibleDrawer.value=false//再次訪問后臺接口,查詢所有文章articleList()
}//七 4.4.3 清空模型數據
const clearArticleModel = ()=>{articleModel.value.title='',articleModel.value.categoryId='',articleModel.value.coverImg='',articleModel.value.content=' ', // 這里清空不了富文本,沒有找到解決方案 暫時多了一個空格處理articleModel.value.state=''
}</script>
<template><el-card class="page-container"><!-- 1 標題 --><template #header><div class="header"><span>文章管理</span><div class="extra"><el-button type="primary" @click="title='添加文章';visibleDrawer = true;clearArticleModel()">添加文章</el-button>  <!-- 七 3.2 @click="visibleDrawer = true" 綁定查詢事件 七 4.1.3 title='添加文章';  七 4.3.3 ;clearArticleModel() --></div></div></template><!-- 2 搜索表單 --><el-form inline><el-form-item label="文章分類:"><el-select placeholder="請選擇" v-model="categoryId"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName":value="c.id"></el-option></el-select></el-form-item><el-form-item label="發布狀態:"><el-select placeholder="請選擇" v-model="state"><el-option label="已發布" value="已發布"></el-option><el-option label="草稿" value="草稿"></el-option></el-select></el-form-item><el-form-item><el-button type="primary" @click="articleList">搜索</el-button>  <!-- 七 2  綁定查詢事件--><el-button @click="categoryId='';state='';articleList()">重置</el-button>  <!-- 七 2  綁定重置事件--></el-form-item></el-form><!-- 3 文章列表 --><el-table :data="articles" style="width: 100%"><el-table-column label="文章標題" width="400" prop="title"></el-table-column><el-table-column label="分類" prop="categoryName"></el-table-column><el-table-column label="發表時間" prop="createTime"> </el-table-column><el-table-column label="更新時間" prop="updateTime"> </el-table-column><el-table-column label="狀態" prop="state"></el-table-column><el-table-column label="操作" width="100"><template #default="{ row }"><el-button :icon="Edit" circle plain type="primary" @click="showDrawer(row)"></el-button> <!--七4.1.4 @click="title='修改文章';visibleDrawer='true'"  七 4.2.1 showDrawer(row) --><el-button :icon="Delete" circle plain type="danger"></el-button></template></el-table-column><template #empty><el-empty description="沒有數據" /></template></el-table><!-- 4 分頁條 --><el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[3, 5 ,10, 15]"layout="jumper, total, sizes, prev, pager, next" background :total="total" @size-change="onSizeChange"@current-change="onCurrentChange" style="margin-top: 20px; justify-content: flex-end" /><!-- 七 3.1 抽屜 --><el-drawer v-model="visibleDrawer" :title="title" direction="rtl" size="50%"> <!-- 七 4.1 抽屜:title="title" -綁定標題 --><!-- 添加文章表單 --><el-form :model="articleModel" label-width="100px" ><el-form-item label="文章標題" ><el-input v-model="articleModel.title" placeholder="請輸入標題"></el-input></el-form-item><el-form-item label="文章分類"><el-select placeholder="請選擇" v-model="articleModel.categoryId"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName" :value="c.id"></el-option></el-select></el-form-item><el-form-item label="文章封面"><!-- auto-upload:是否自動上傳action: 服務器接口路徑name: 上傳的文件字段名headers: 設置上傳的請求頭on-success: 上傳成功的回調函數 --><el-upload class="avatar-uploader" :auto-upload="true" :show-file-list="false"action="/api/upload"name="file":headers="{Authorization:tokenStore.token}":on-success="uploadSuccess"><img v-if="articleModel.coverImg" :src="articleModel.coverImg" class="avatar" /><el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon></el-upload></el-form-item><el-form-item label="文章內容"><div class="editor"><quill-editor theme="snow" v-model:content="articleModel.content" contentType="html"> <!-- 七 3.3.3頁面使用quill組件 --></quill-editor></div></el-form-item><el-form-item><el-form-item><el-button type="primary" @click="title==='添加文章'? addArticle('已發布'): updateArticle('已發布')">發布</el-button> <!-- 七 3.5.2 @click="addArticle('已發布')  七 4.3.2 ;title==='添加文章'? addArticle('已發布'):updateArticle('已發布')"--><el-button type="info" @click="title==='添加文章'? addArticle('草稿'): updateArticle('草稿')">草稿</el-button> <!-- 七 3.5.2@click="addArticle('草稿')"  七 4.3.2 ;title==='添加文章'? addArticle('草稿'):updateArticle('草稿')--></el-form-item></el-form-item></el-form></el-drawer></el-card>
</template>
<style lang="scss" 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?id='+id)
}// 七 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?id='+id)}

5.2 為刪除按鈕綁定事件

<template #default="{ row }"><el-button :icon="Edit" circle plain type="primary" @click="showDrawer(row)"></el-button> <!--七4.1.4 @click="title='修改文章';visibleDrawer='true'"  七 4.2.1 showDrawer(row) --><el-button :icon="Delete" circle plain type="danger" @click="deleteArticle(row)"></el-button> <!-- 七 5.2 為刪除按鈕綁定事件--></template>

5.3 添加刪除彈窗,當用戶點擊確認后,調用接口刪除分類

<script setup>
import {Edit,Delete
} from '@element-plus/icons-vue'import { 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.coverImg=img.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 categoryId=ref('')//用戶搜索時選中的發布狀態
const state=ref('')//文章列表數據模型
const articles = ref([{"id": 5,"title": "陜西旅游攻略","content": "兵馬俑,華清池,法門寺,華山...愛去哪去哪...","coverImg": "https://big-event-gwd.oss-cn-beijing.aliyuncs.com/9bf1cf5b-1420-4c1b-91ad-e0f4631cbed4.png","state": "草稿","categoryId": 2,"createTime": "2023-09-03 11:55:30","updateTime": "2023-09-03 11:55:30"},{"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 addArticle=async (state)=>{articleModel.value.state = statelet result = await articleAddService(articleModel.value);ElMessage.success(result.message? result.message:'添加成功')//再次調用getArticles,獲取文章articleList()//隱藏抽屜visibleDrawer.value=false
}//<!-- 七 4.1 抽屜:title="title" -綁定標題 -->
const title=ref('')// 七 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 updateArticle=async (state)=>{articleModel.value.state = statelet result = await articleUpdateService(articleModel.value)ElMessage.success(result.message? result.message:'修改成功')//隱藏抽屜visibleDrawer.value=false//再次訪問后臺接口,查詢所有文章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>
<template><el-card class="page-container"><!-- 1 標題 --><template #header><div class="header"><span>文章管理</span><div class="extra"><el-button type="primary" @click="title='添加文章';visibleDrawer = true;clearArticleModel()">添加文章</el-button>  <!-- 七 3.2 @click="visibleDrawer = true" 綁定查詢事件 七 4.1.3 title='添加文章';  七 4.3.3 ;clearArticleModel() --></div></div></template><!-- 2 搜索表單 --><el-form inline><el-form-item label="文章分類:"><el-select placeholder="請選擇" v-model="categoryId"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName":value="c.id"></el-option></el-select></el-form-item><el-form-item label="發布狀態:"><el-select placeholder="請選擇" v-model="state"><el-option label="已發布" value="已發布"></el-option><el-option label="草稿" value="草稿"></el-option></el-select></el-form-item><el-form-item><el-button type="primary" @click="articleList">搜索</el-button>  <!-- 七 2  綁定查詢事件--><el-button @click="categoryId='';state='';articleList()">重置</el-button>  <!-- 七 2  綁定重置事件--></el-form-item></el-form><!-- 3 文章列表 --><el-table :data="articles" style="width: 100%"><el-table-column label="文章標題" width="400" prop="title"></el-table-column><el-table-column label="分類" prop="categoryName"></el-table-column><el-table-column label="發表時間" prop="createTime"> </el-table-column><el-table-column label="更新時間" prop="updateTime"> </el-table-column><el-table-column label="狀態" prop="state"></el-table-column><el-table-column label="操作" width="100"><template #default="{ row }"><el-button :icon="Edit" circle plain type="primary" @click="showDrawer(row)"></el-button> <!--七4.1.4 @click="title='修改文章';visibleDrawer='true'"  七 4.2.1 showDrawer(row) --><el-button :icon="Delete" circle plain type="danger" @click="deleteArticle(row)"></el-button> <!-- 七 5.2 為刪除按鈕綁定事件--></template></el-table-column><template #empty><el-empty description="沒有數據" /></template></el-table><!-- 4 分頁條 --><el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[3, 5 ,10, 15]"layout="jumper, total, sizes, prev, pager, next" background :total="total" @size-change="onSizeChange"@current-change="onCurrentChange" style="margin-top: 20px; justify-content: flex-end" /><!-- 七 3.1 抽屜 --><el-drawer v-model="visibleDrawer" :title="title" direction="rtl" size="50%"> <!-- 七 4.1 抽屜:title="title" -綁定標題 --><!-- 添加文章表單 --><el-form :model="articleModel" label-width="100px" ><el-form-item label="文章標題" ><el-input v-model="articleModel.title" placeholder="請輸入標題"></el-input></el-form-item><el-form-item label="文章分類"><el-select placeholder="請選擇" v-model="articleModel.categoryId"><el-option v-for="c in categorys" :key="c.id" :label="c.categoryName" :value="c.id"></el-option></el-select></el-form-item><el-form-item label="文章封面"><!-- auto-upload:是否自動上傳action: 服務器接口路徑name: 上傳的文件字段名headers: 設置上傳的請求頭on-success: 上傳成功的回調函數 --><el-upload class="avatar-uploader" :auto-upload="true" :show-file-list="false"action="/api/upload"name="file":headers="{Authorization:tokenStore.token}":on-success="uploadSuccess"><img v-if="articleModel.coverImg" :src="articleModel.coverImg" class="avatar" /><el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon></el-upload></el-form-item><el-form-item label="文章內容"><div class="editor"><quill-editor theme="snow" v-model:content="articleModel.content" contentType="html"> <!-- 七 3.3.3頁面使用quill組件 --></quill-editor></div></el-form-item><el-form-item><el-form-item><el-button type="primary" @click="title==='添加文章'? addArticle('已發布'): updateArticle('已發布')">發布</el-button> <!-- 七 3.5.2 @click="addArticle('已發布')  七 4.3.2 ;title==='添加文章'? addArticle('已發布'):updateArticle('已發布')"--><el-button type="info" @click="title==='添加文章'? addArticle('草稿'): updateArticle('草稿')">草稿</el-button> <!-- 七 3.5.2@click="addArticle('草稿')"  七 4.3.2 ;title==='添加文章'? addArticle('草稿'):updateArticle('草稿')--></el-form-item></el-form-item></el-form></el-drawer></el-card>
</template>
<style lang="scss" 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.js'const 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 :src="userInfoStore.info.userPic ? userInfoStore.info.userPic : avatar" />
?全部Layout.vue
<script 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.js'const userInfoStore = useUserInfoStore();
//獲取個人信息
const getUserInfo = async ()=>{let result = await userInfoGetService();//存儲piniauserInfoStore.info =result.data;
}
getUserInfo()</script><template><!-- element-plus 中的容器 --><el-container class="layout-container"><!-- 左側菜單 --><el-aside width="200px"><div class="el-aside__logo"></div><!-- element-plus 菜單標簽 --><el-menu active-text-color="#ffd04b" background-color="#232323"  text-color="#fff"router><el-menu-item index="/article/category">  <!-- 五 3.3  --><el-icon><Management /></el-icon><span>文章分類</span></el-menu-item><el-menu-item index="/article/manage">  <!-- 五 3.3  --><el-icon><Promotion /></el-icon><span>文章管理</span></el-menu-item><el-sub-menu ><template #title><el-icon><UserFilled /></el-icon><span>個人中心</span></template><el-menu-item index="/user/info">  <!-- 五 3.3  --><el-icon><User /></el-icon><span>基本資料</span></el-menu-item><el-menu-item index="/user/avatar">  <!-- 五 3.3  --><el-icon><Crop /></el-icon><span>更換頭像</span></el-menu-item><el-menu-item index="/user/resetPassword">  <!-- 五 3.3  --><el-icon><EditPen /></el-icon><span>重置密碼</span></el-menu-item></el-sub-menu></el-menu></el-aside><!-- 右側主區域 --><el-container><!-- 頭部區域 --><el-header><div>編碼集中營:<strong>{{ userInfoStore.info.nickname ? userInfoStore.info.nickname : userInfoStore.info.usrename }}</strong></div> <!-- 八 4 {{ userInfoStore.info.nickname ? userInfoStore.info.nickname : userInfoStore.info.usrename }}--><el-dropdown placement="bottom-end"><span class="el-dropdown__box"><el-avatar :src="userInfoStore.info.userPic ? userInfoStore.info.userPic : avatar" /> <!-- 八 4 userInfoStore.info.userPic ? userInfoStore.info.userPic : avatar--><el-icon><CaretBottom /></el-icon></span><template #dropdown><el-dropdown-menu><el-dropdown-item command="profile" :icon="User">基本資料</el-dropdown-item><el-dropdown-item command="avatar" :icon="Crop">更換頭像</el-dropdown-item><el-dropdown-item command="password" :icon="EditPen">重置密碼</el-dropdown-item><el-dropdown-item command="logout" :icon="SwitchButton">退出登錄</el-dropdown-item></el-dropdown-menu></template></el-dropdown></el-header><!-- 中間區域 --><el-main><!-- 五 3.3  --><!-- <div style="width: 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>
</template><style lang="scss" 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 placement="bottom-end" @command="handleCommand"><span class="el-dropdown__box"><el-avatar :src="userInfoStore.info.userPic ? userInfoStore.info.userPic : avatar" /> <!-- 八 4 userInfoStore.info.userPic ? userInfoStore.info.userPic : avatar--><el-icon><CaretBottom /></el-icon></span><template #dropdown><el-dropdown-menu><el-dropdown-item command="info" :icon="User">基本資料</el-dropdown-item><el-dropdown-item command="avatar" :icon="Crop">更換頭像</el-dropdown-item><el-dropdown-item command="resetPassword" :icon="EditPen">重置密碼</el-dropdown-item><el-dropdown-item command="logout" :icon="SwitchButton">退出登錄</el-dropdown-item></el-dropdown-menu></template></el-dropdown>

在el-dropdown標簽上綁定command事件,當有條目被點擊后,會觸發這個事件

<el-dropdown placement="bottom-end" @command="handleCommand">

提供handleCommand函數,參數為點擊條目的command屬性值

//dropDown條目被點擊后,回調的函數
import {useRouter} from 'vue-router'
const router = useRouter()
const handleCommand = (command)=>{if(command==='logout'){//退出登錄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.js'const 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)}
}</script><template><!-- element-plus 中的容器 --><el-container class="layout-container"><!-- 左側菜單 --><el-aside width="200px"><div class="el-aside__logo"></div><!-- element-plus 菜單標簽 --><el-menu active-text-color="#ffd04b" background-color="#232323"  text-color="#fff"router><el-menu-item index="/article/category">  <!-- 五 3.3  --><el-icon><Management /></el-icon><span>文章分類</span></el-menu-item><el-menu-item index="/article/manage">  <!-- 五 3.3  --><el-icon><Promotion /></el-icon><span>文章管理</span></el-menu-item><el-sub-menu ><template #title><el-icon><UserFilled /></el-icon><span>個人中心</span></template><el-menu-item index="/user/info">  <!-- 五 3.3  --><el-icon><User /></el-icon><span>基本資料</span></el-menu-item><el-menu-item index="/user/avatar">  <!-- 五 3.3  --><el-icon><Crop /></el-icon><span>更換頭像</span></el-menu-item><el-menu-item index="/user/resetPassword">  <!-- 五 3.3  --><el-icon><EditPen /></el-icon><span>重置密碼</span></el-menu-item></el-sub-menu></el-menu></el-aside><!-- 右側主區域 --><el-container><!-- 頭部區域 --><el-header><div>編碼集中營:<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 placement="bottom-end" @command="handleCommand"><span class="el-dropdown__box"><el-avatar :src="userInfoStore.info.userPic ? userInfoStore.info.userPic : avatar" /> <!-- 八 4 userInfoStore.info.userPic ? userInfoStore.info.userPic : avatar--><el-icon><CaretBottom /></el-icon></span><template #dropdown><el-dropdown-menu><el-dropdown-item command="info" :icon="User">基本資料</el-dropdown-item><el-dropdown-item command="avatar" :icon="Crop">更換頭像</el-dropdown-item><el-dropdown-item command="resetPassword" :icon="EditPen">重置密碼</el-dropdown-item><el-dropdown-item command="logout" :icon="SwitchButton">退出登錄</el-dropdown-item></el-dropdown-menu></template></el-dropdown></el-header><!-- 中間區域 --><el-main><!-- 五 3.3  --><!-- <div style="width: 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>
</template><style lang="scss" 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: 'zs@163.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>
<template><el-card class="page-container"><template #header><div class="header"><span>基本資料</span></div></template><el-row><el-col :span="12"><el-form :model="userInfo" :rules="rules" label-width="100px" size="large"><el-form-item label="登錄名稱"><el-input v-model="userInfo.username" disabled></el-input></el-form-item><el-form-item label="用戶昵稱" prop="nickname"><el-input v-model="userInfo.nickname"></el-input></el-form-item><el-form-item label="用戶郵箱" prop="email"><el-input v-model="userInfo.email"></el-input></el-form-item><el-form-item><el-button type="primary">提交修改</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 type="primary" @click="updateUserInfo">提交修改</el-button> <!-- 十  3  @click="updateUserInfo"-->

提供updateUserInfo函數

const updateUserInfo = async ()=>{let result = await userInfoUpdateService(userInfo.value)ElMessage.success(result.message? result.message:'修改成功')//更新pinia中的數據userInfoStore.info.nickname=userInfo.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.nickname=userInfo.value.nicknameuserInfoStore.info.email = userInfo.value.email
}
</script>
<template><el-card class="page-container"><template #header><div class="header"><span>基本資料</span></div></template><el-row><el-col :span="12"><el-form :model="userInfo" :rules="rules" label-width="100px" size="large"><el-form-item label="登錄名稱"><el-input v-model="userInfo.username" disabled></el-input></el-form-item><el-form-item label="用戶昵稱" prop="nickname"><el-input v-model="userInfo.nickname"></el-input></el-form-item><el-form-item label="用戶郵箱" prop="email"><el-input v-model="userInfo.email"></el-input></el-form-item><el-form-item><el-button type="primary" @click="updateUserInfo">提交修改</el-button> <!-- 十  3  @click="updateUserInfo"--></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</script><template><el-card class="page-container"><template #header><div class="header"><span>更換頭像</span></div></template><el-row><el-col :span="12"><el-upload ref="uploadRef"class="avatar-uploader" :show-file-list="false"><img v-if="imgUrl" :src="imgUrl" class="avatar" /><img v-else src="avatar" width="278" /></el-upload><br /><el-button type="primary" :icon="Plus" size="large"  @click="uploadRef.$el.querySelector('input').click()">選擇圖片</el-button><el-button type="success" :icon="Upload" size="large">上傳頭像</el-button></el-col></el-row></el-card>
</template><style lang="scss" 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.js'const userInfoStore = useUserInfoStore()
//用戶頭像地址
const imgUrl=ref(userInfoStore.info.userPic)

img標簽上綁定圖片地址

<img v-if="imgUrl" :src="imgUrl" class="avatar" />
<img v-else :src="avatar" width="278" />

3、 頭像上傳

為el-upload指定屬性值,分別有:

  • action: 服務器接口路徑

  • headers: 設置請求頭,需要攜帶token

  • on-success: 上傳成功的回調函數

  • name: 上傳圖片的字段名稱

<el-upload ref="uploadRef"class="avatar-uploader" :show-file-list="false":auto-upload="true"action="/api/upload"name="file":headers="{Authorization:tokenStore.token}":on-success="uploadSuccess"><img v-if="imgUrl" :src="imgUrl" class="avatar" /><img v-else src="avatar" width="278" /></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 type="primary" :icon="Plus" size="large" ?@click="uploadRef.$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 type="success" :icon="Upload" size="large" @click="updateAvatar"> <!-- 十  4  @click="updateAvatar"-->上傳頭像</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.userPic=imgUrl.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.js'const userInfoStore = useUserInfoStore()
//用戶頭像地址
const imgUrl=ref(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.userPic=imgUrl.value
}
</script><template><el-card class="page-container"><template #header><div class="header"><span>更換頭像</span></div></template><el-row><el-col :span="12"><!-- auto-upload:是否自動上傳action: 服務器接口路徑name: 上傳的文件字段名headers: 設置上傳的請求頭on-success: 上傳成功的回調函數                     --><el-upload ref="uploadRef"class="avatar-uploader" :show-file-list="false":auto-upload="true"action="/api/upload"name="file":headers="{Authorization:tokenStore.token}":on-success="uploadSuccess"><img v-if="imgUrl" :src="imgUrl" class="avatar" /><img v-else :src="avatar" width="278" /></el-upload><br /><el-button type="primary" :icon="Plus" size="large"  @click="uploadRef.$el.querySelector('input').click()">選擇圖片</el-button><el-button type="success" :icon="Upload" size="large" @click="updateAvatar"> <!-- 十  4  @click="updateAvatar"-->上傳頭像</el-button></el-col></el-row></el-card>
</template><style lang="scss" 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中存儲的token(pinia中個人信息沒有存儲密碼信息,所以不處理)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>
<template><el-card class="page-container"><template #header><div class="header"><span>重置密碼</span></div></template><el-row><el-col :span="12">                <el-form :model="userPwd" :rules="rules" label-width="100px" size="large"><el-form-item label="原密碼" prop="old_pwd"><el-input type="password" v-model="userPwd.old_pwd"></el-input></el-form-item><el-form-item label="新密碼" prop="new_pwd"><el-input type="password" v-model="userPwd.new_pwd"></el-input></el-form-item><el-form-item label="確認密碼" prop="re_pwd"><el-input type="password" v-model="userPwd.re_pwd"></el-input></el-form-item><el-form-item><el-button type="primary" @click="updateUserPwd">提交修改</el-button></el-form-item></el-form></el-col></el-row></el-card>
</template>

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/166822.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/166822.shtml
英文地址,請注明出處:http://en.pswp.cn/news/166822.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

JeecgBoot3.5漏洞升級—快速文檔

近幾年來&#xff0c;黑客攻擊行為呈現出日益復雜和隱蔽的趨勢&#xff0c;對個人和組織的安全造成了嚴重威脅。黑客們不斷尋找新的漏洞和安全漏洞&#xff0c;利用各種手段進行網絡攻擊&#xff0c;包括惡意軟件、網絡釣魚、勒索軟件等。因此&#xff0c;我們每個人都需要關注…

Windows系統下載并安裝 Windows PowerShell

PowerShell下載地址 一 windows7 版本 **若要確定您正在使用的 Microsoft PowerShelll 版本,請在 Windows 7 或 Windows Server 2008R2 或 Windows Server 2012 計算機上執行以下操作: 單擊“開始”,單擊“所有程序”,單擊“附件”,單擊“Windows PowerShell”,然后單擊…

渲染農場渲染一分鐘動畫需要多少錢?需要渲染多少時間?

現在很公司都開始使用渲染農場渲染動畫&#xff0c;但是還是有很多人不知道渲染農場渲染動畫需要多少錢&#xff0c;需要渲染多少時間。在這篇文章中我們將為你一一解答&#xff0c;為你提供一個清晰的參考。 渲染農場的收費通常是按照渲染的使用時間收費&#xff0c;渲染十分…

華大基因助力烏茲別克斯坦精準醫學發展,共筑健康絲綢之路

今年上半年&#xff0c;中國與中亞五國元首齊聚陜西西安&#xff0c;舉辦中國&#xff0d;中亞峰會。過去的20年里&#xff0c;中國已經成為中亞國家的主要投資來源國&#xff0c;總投資額接近400億美元。烏茲別克斯坦是中國&#xff0d;中亞合作機制的重要參與者&#xff0c;烏…

elasticsearch命令大全

1、查詢全部索引 GET /_cat/indices?v2、要按文檔數對所有索引進行降序排序 GET /_cat/indices?v&hindex,docs.count&sdocs.count:descv&#xff1a;參數用于顯示列標題 h&#xff1a;參數用于指定要顯示的列 s&#xff1a;參數用于指定按哪一列進行排序 desc&…

CAD隨機多面體_圓柱試件3D插件

插件介紹 CAD隨機多面體_圓柱試件3D插件可用于在AutoCAD軟件內生成隨機三維多面體及外側圓柱體試件。插件可確保多面體之間不發生干涉&#xff0c;且多面體與外側圓柱體試件之間保持適配關系&#xff0c;確保生成的模型導入有限元軟件后幾何合理有效。本插件主要可應用于三維混…

講概念談愿景AI Agent名不副實?看實在智能RPA Agent智能體如何落地!

OpenAI在首屆開發者大會上推出了GPTs和Assitant API&#xff0c;不僅改寫了AI Agent的構建范式&#xff0c;也把AI智能體的應用推向一個新高潮。GPTs和GPT商店&#xff0c;使得用戶無需編碼通過自然語言就能創建并擁有多個專屬私人助理&#xff0c;且可以如在蘋果應用商店一樣在…

開發教育APP的意義

開發教育APP的意義有以下幾點&#xff1a; 促進教育資源發揮效果&#xff1a;通過教育APP軟件開發&#xff0c;可以讓現代教育資源有效合理的進行利用&#xff0c;并且能夠更進一步的提升發揮功能&#xff0c;促進教育資源全面發揮其具體作用和效果&#xff0c;從而提升教育行…

3.3.1詳解linux內核鏈表list_head及其接口應用

文章目錄 1 list定義2 list接口2.1 list初始化方法1:定義并初始化鏈表方法2:先定義再初始化鏈表2.2 list_add2.3 list_del2.4 list_replace2.5 list_move2.6 list_splice3 list遍歷3.1 list_entry3.2 list_first_entry3.3 list_last_entry3.4 list_first_entry_or_null3.5 li…

Y-MODEM協議定制上位機

最近在使用N32G031和STM32F10X系列單片機進行IAP&#xff0c;使用的是Ymodem協議。單片機上的軟件已經完成了&#xff0c;一般是使用secureCRT這樣的工具作為上位機來進行測試&#xff0c;后來想做一個定制化的簡單的上位機。在網上找了下資料&#xff0c;以下這篇文章寫的使用…

淺談安科瑞無線測溫設備在挪威某項目的應用

摘要&#xff1a;安科瑞無線溫度設備裝置通過無線溫度收發器和各無線溫度傳感器直接進行溫度值的傳輸&#xff0c;并采用液晶顯示各無線溫度傳感器所測溫度。 Absrtact:Acre wireless temperature device directly transmits the temperature value through the wireless temp…

基于51單片機傾角MPU6050老人跌倒遠程GSM短信報警器+源程序

一、系統方案 1、本設計采用這51單片機作為主控器。 2、MPU6050角度值送到液晶1602顯示。 3、紅外傳感器檢測心率。 4、跌倒遠程GSM報警。 二、硬件設計 原理圖如下&#xff1a; 三、單片機軟件設計 1、首先是系統初始化 void LCD_Init() //初始化液晶時間顯示 { write_com…

yarn:無法加載文件 C:\Users\***\AppData\Roaming\npm\yarn.ps1,因為在此系統上禁止運行腳本

原因&#xff1a;PowerShell 腳本的執行有著嚴格的安全策略限制&#xff01; 解決方案&#xff1a;管理員身份啟動Windows PowerShell 在命令行中輸入set-ExecutionPolicy RemoteSigned 再使用yarn就可以了

SQL常見函數整理 _ LAG() 向上偏移

1. 用法 窗口函數&#xff0c;用于訪問窗口中當前行之前的行的數據。該函數可以根據需要計算當前行之前的值&#xff0c;使我們能夠輕松地比較不同行之間的差異和變化。 2. 基本語法 LAG(column, offset, default_value) OVER (ORDER BY column)column&#xff1a;代表在返回…

【UE5】資源(Asset)

了解UE游戲的基本構成 資源&#xff08;Asset&#xff09;: 在UE中&#xff0c;資源&#xff08;Asset&#xff09;是指游戲中使用到的各種素材&#xff0c;例如模型、紋理、材質、聲音、動畫、藍圖、數據表格、關卡等&#xff08;通常以uasset結尾&#xff09;&#xff0c;他…

Java shiro框架,切換身份后刷新subject的用戶屬性

/*** title 切換身份&#xff0c;登錄后&#xff0c;動態更改subject的用戶屬性* param principal* desc principal為用戶的認證信息*/ public static void reloadAuthorizing(User principal) throws Exception{Subject subject SecurityUtils.getSubject();String realmNam…

如何在springboot項目中使用minio上傳下載刪除文件

引入maven依賴 <!-- minio --> <dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.2.2</version> </dependency>申請 bucket | access_key | secret_key 項目中配置相關參數 mini…

ROLLUP 的幾點說明(十七)

ROLLUP 最根本的作用是提高某些查詢的查詢效率&#xff08;無論是通過聚合來減少數據量&#xff0c;還是修改列順序以匹配前綴索引&#xff09;。因此 ROLLUP 的含義已經超出了“上卷”的范圍。這也是為什么在源代碼中&#xff0c;將其命名為 Materialized Index&#xff08;物…

土壤教學經典用圖30張

一、土壤分布 二、土壤形成與氣候 三、土壤形成與地形 四、土壤形成與成土母質 五、成土過程示意圖 六、土壤剖面實景圖 七、土壤剖面示意圖 八、土壤質地 以上圖片多來源于 人教、湘教、魯教、中圖、滬教 五套新教材及地圖冊

忘記7-zip密碼,如何解壓文件?

7z壓縮包設置了密碼&#xff0c;解壓的時候就需要輸入正確對密碼才能順利解壓出文件&#xff0c;正常當我們解壓文件或者刪除密碼的時候&#xff0c;雖然方法多&#xff0c;但是都需要輸入正確的密碼才能完成。忘記密碼就無法進行操作。 那么&#xff0c;忘記了7z壓縮包的密碼…