目錄
- 部門管理
- 1. 前后端分離開發
- 2. 準備工作
- 2.1 創建Vue項目
- 2.2 安裝依賴
- 2.3 精簡項目
- 3. 頁面布局
- 3.1 介紹
- 3.2 整體布局
- 3.3 左側菜單
- 4. Vue Router
- 4.1 介紹
- 4.2 入門
- 4.3 案例
- 4.4 首頁制作
- 5. 部門管理
- 5.1部門列表
- 5.1.1. 基本布局
- 5.1.2 加載數據
- 5.1.3 程序優化
- 5.2 新增部門
- 5.3 修改部門
- 5.3.1 查詢回顯
- 5.3.2 保存修改
- 5.4 刪除部門
- 5.5 表單校驗
- 5.5.1 ElementPlus 參考
- 5.5.2 實現
部門管理
在前面的課程中,我們學習了Vue工程化的基礎內容、TS、ElementPlus,那接下來呢,我們要通過一個案例,加強大家對于Vue項目的理解,并掌握Vue項目的開發。 這個案例呢,就是我們之前所做的Tlias智能學習輔助系統。
在這個案例中,我們主要完成 部門管理 和 員工管理 的功能開發。 而今天呢,我們先來完成部門管理的功能開發,而在完成部門管理的功能開發之前,先需要完成基礎的準備工作。 所以今天的課程安排如下:
- 前后端分類開發
- 準備工作
- 頁面布局
- Vue-Router
- 部門管理
1. 前后端分離開發
在之前的課程中,我們介紹過,現在的企業項目開發有2種開發模式:前后臺混合開發和前后臺分離開發。
前后臺混合開發,顧名思義就是前臺后臺代碼混在一起開發。這種開發模式有如下缺點:
- 溝通成本高:后臺人員發現前端有問題,需要找前端人員修改,前端修改成功,再交給后臺人員使用
- 分工不明確:后臺開發人員需要開發后臺代碼,也需要開發部分前端代碼。很難培養專業人才
- 不便管理:所有的代碼都在一個工程中
- 難以維護:前端代碼更新,和后臺無關,但是需要整個工程包括后臺一起重新打包部署。
所以我們目前基本都是采用的前后臺分離開發方式,如下圖所示:
我們將原先的工程分為前端工程和后端工程這2個工程,然后前端工程交給專業的前端人員開發,后端工程交給專業的后端人員開發。
前端頁面需要數據,可以通過發送異步請求,從后臺工程獲取。但是,我們前后臺是分開來開發的,那么前端人員怎么知道后臺返回數據的格式呢?后端人員開發,怎么知道前端人員需要的數據格式呢?
所以針對這個問題,我們前后臺統一制定一套規范!我們前后臺開發人員都需要遵循這套規范開發,這就是我們的接口文檔。
那么接口文檔的內容怎么來的呢?是我們后臺開發者根據產品經理提供的產品原型和需求文檔所撰寫出來的。
那么基于前后臺分離開發的模式下,我們后臺開發者開發一個功能的具體流程如何呢?如下圖所示:
- 需求分析:首先我們需要閱讀需求文檔,分析需求,理解需求。
- 接口定義:查詢接口文檔中關于需求的接口的定義,包括地址,參數,響應數據類型等等
- 前后臺并行開發:各自按照接口文檔進行開發,實現需求
- 測試:前后臺開發完了,各自按照接口文檔進行測試
- 前后段聯調測試:前段工程請求后端工程,測試功能
2. 準備工作
2.1 創建Vue項目
在自己工作目錄下,運行 cmd
打開命令行,運行如下指令,來創建vue項目【
npm init vue@latest
2.2 安裝依賴
1). 在命令行中執行如下命令,為創建好的Vue項目安裝 ElementPlus、Axios
的依賴。
npm install element-plus --save
npm install axios
2). 為創建好的 Vue項目 配置ElementPlus (參照官網),在 main.ts
中引入如下配置信息 【注意:是追加如下內容】:
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import zhCn from 'element-plus/es/locale/lang/zh-cn'import * as ElementPlusIconsVue from '@element-plus/icons-vue'//引入ElementPlus的Icon組件
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {app.component(key, component)
}
app.use(ElementPlus, {locale: zhCn})app.mount('#app')
最終完整的 main.ts
文件內容如下:
import { createApp } from 'vue'
import { createPinia } from 'pinia'import App from './App.vue'
import router from './router'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import './assets/main.css'import zhCn from 'element-plus/es/locale/lang/zh-cn'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'const app = createApp(App)for (const [key, component] of Object.entries(ElementPlusIconsVue)) {app.component(key, component)
}
app.use(createPinia())
app.use(router)
app.use(ElementPlus, {locale: zhCn})app.mount('#app')
3). 在 env.d.ts
中引入ElementPlus的語言包(可不做)
declare module 'element-plus/es/locale/lang/zh-cn'
2.3 精簡項目
由于基于Vue腳手架創建的項目里面攜帶了很多的多余的Vue組件。 并準備對應的組件存放目錄 。
- 刪除
components
目錄中的vue文件 - 刪除
views
目錄中的vue文件 - 清空根組件文件
App.vue
中的內容,只保留基礎的vue組件文件的結構標簽<script>
<template>
<style>
3. 頁面布局
3.1 介紹
我們在制作一個頁面的時候,一定是先關注整體的頁面布局,然后再關注具體的細節處理 。 所以這一小節,我們就先來完成頁面的整體布局。
我們會看到,整個頁面分為這么三個部分:
①. 頁頭部分
②. 側邊欄
③. 主區域
而要完成這樣的頁面布局,我們其實是可以借助于 ElementPlus
中提供的 Container布局容器
來實現:
Container布局容器,用于布局的容器組件,方便快速搭建頁面的基本結構:
<el-container>
:外層容器。 當子元素中包含 <el-header>
或 <el-footer>
時,全部子元素會垂直上下排列, 否則會水平左右排列。
<el-header>
:頂欄容器。
<el-aside>
:側邊欄容器。
<el-main>
:主要區域容器。
<el-footer>
:底欄容器。
而針對于我們當前案例的頁面布局,基本的結構如下:
提示:當
<el-container>
子元素中包含<el-header>
或<el-footer>
時,全部子元素會垂直上下排列, 否則會水平左右排列。
3.2 整體布局
我們可以參照 ElementPlus
的官方網站中的 布局,拷貝其源碼,然后對其做一個改造。 具體參照的源碼如下:
1). 在 src/views
目錄下,再創建一個子目錄 layout
,在其中新建一個頁面,頁面命名為:index.vue
。
2). 在 index.vue
中準備好基礎的組件結構后,就可以將代碼直接復制到 <template> </template>
標簽中。
<script setup lang="ts"></script><template><div class="common-layout"><el-container><!-- 頂欄 - header --><el-header>Header</el-header><!-- 左側菜單 & 主區域 --><el-container><el-aside width="200px">Aside</el-aside><el-main>Main</el-main></el-container></el-container></div>
</template><style scoped></style>
然后,我們先根據頁面原型中的布局顯示進行調整。 先完成頂欄部分的制作,具體的代碼如下:
<script setup lang="ts"></script><template><div class="common-layout"><el-container><!-- 頂欄 - header --><el-header class="header"><span class="title">Tlias智能學習輔助系統</span><span class="right_tool"><a href=""><el-icon><EditPen /></el-icon> 修改密碼 </a><a href=""><el-icon><SwitchButton /></el-icon> 退出登錄 </a></span></el-header><!-- 左側菜單 & 主區域 --><el-container><el-aside width="200px">Aside</el-aside><el-main>Main</el-main></el-container></el-container></div>
</template><style scoped>
.header {background-image: linear-gradient(to right, #e70cc5, #e94dcf, #eb6fd8, #ec8bdf, #eea5e6);line-height: 60px;
}.title {color: white;font-size: 35px;font-family: 楷體;}.right_tool {float: right;
}a {text-decoration: none;color: white;
}
</style>
最終的頂欄布局效果如下所示:
3.3 左側菜單
頂欄布局完畢之后,接下來,我們再來完成左側菜單欄的制作。 左側菜單欄的制作,也不需要我們自己實現,其實在 ElementPlus
中已經提供了對應的菜單組件,我們可以直接參考【PS: 其實就是復制過來,參考頁面原型和需求,將其改造成我們需要的樣子就可以了】。
參考代碼的出處如下:
然后就可以參考其提供的源碼,復制到我們的側邊欄部分 <el-aside> ... </el-aside>
,然后根據我們案例的需要進行改造,改造成我們需要的樣子即可。
最終左側菜單欄的代碼如下:
<!-- 左側菜單 -->
<el-aside width="200px" class="aside"><el-scrollbar><el-menu router><!-- 首頁菜單 --><el-menu-item index="/index"><el-icon><Promotion /></el-icon> 首頁</el-menu-item><!-- 班級管理菜單 --><el-sub-menu index="/manage"><template #title><el-icon><Menu /></el-icon> 班級學員管理</template><el-menu-item index="/clazz"><el-icon><HomeFilled /></el-icon>班級管理</el-menu-item><el-menu-item index="/stu"><el-icon><UserFilled /></el-icon>學員管理</el-menu-item></el-sub-menu><!-- 系統信息管理 --><el-sub-menu index="/system"><template #title><el-icon><Tools /></el-icon>系統信息管理</template><el-menu-item index="/dept"><el-icon><HelpFilled /></el-icon>部門管理</el-menu-item><el-menu-item index="/emp"><el-icon><Avatar /></el-icon>員工管理</el-menu-item></el-sub-menu><!-- 數據統計管理 --><el-sub-menu index="/report"><template #title><el-icon><Histogram /></el-icon>數據統計管理</template><el-menu-item index="/empReport"><el-icon><InfoFilled /></el-icon>員工信息統計</el-menu-item><el-menu-item index="/stuReport"><el-icon><Share /></el-icon>學員信息統計</el-menu-item><el-menu-item index="/log"><el-icon><Document /></el-icon>日志信息統計</el-menu-item></el-sub-menu></el-menu></el-scrollbar>
</el-aside>
并在 <style></style>
中添加如下樣式:
.aside {border: 1px solid #ccc;height: 690px;width: 220px;
}
最終,瀏覽器打開的效果如下:
到目前為止,layout/index.vue
中的內容如下:
<script setup lang="ts"></script><template><div class="common-layout"><el-container><!-- 頂欄 - header --><el-header class="header"><span class="title">Tlias智能學習輔助系統</span><span class="right_tool"><a href=""><el-icon><EditPen /></el-icon> 修改密碼 </a><a href=""><el-icon><SwitchButton /></el-icon> 退出登錄 </a></span></el-header><!-- 左側菜單 & 主區域 --><el-container><!-- 左側菜單 --><el-aside width="200px" class="aside"><el-scrollbar><el-menu router><!-- 首頁菜單 --><el-menu-item index="/index"><el-icon><Promotion /></el-icon> 首頁</el-menu-item><!-- 班級管理菜單 --><el-sub-menu index="/manage"><template #title><el-icon><Menu /></el-icon> 班級學員管理</template><el-menu-item index="/clazz"><el-icon><HomeFilled /></el-icon>班級管理</el-menu-item><el-menu-item index="/stu"><el-icon><UserFilled /></el-icon>學員管理</el-menu-item></el-sub-menu><!-- 系統信息管理 --><el-sub-menu index="/system"><template #title><el-icon><Tools /></el-icon>系統信息管理</template><el-menu-item index="/dept"><el-icon><HelpFilled /></el-icon>部門管理</el-menu-item><el-menu-item index="/emp"><el-icon><Avatar /></el-icon>員工管理</el-menu-item></el-sub-menu><!-- 數據統計管理 --><el-sub-menu index="/report"><template #title><el-icon><Histogram /></el-icon>數據統計管理</template><el-menu-item index="/empReport"><el-icon><InfoFilled /></el-icon>員工信息統計</el-menu-item><el-menu-item index="/stuReport"><el-icon><Share /></el-icon>學員信息統計</el-menu-item><el-menu-item index="/log"><el-icon><Document /></el-icon>日志信息統計</el-menu-item></el-sub-menu></el-menu></el-scrollbar></el-aside><el-main>Main</el-main></el-container></el-container></div>
</template><style scoped>
.header {background-image: linear-gradient(to right, #e70cc5, #e94dcf, #eb6fd8, #ec8bdf, #eea5e6);line-height: 60px;
}.title {color: white;font-size: 35px;font-family: 楷體;}.right_tool {float: right;
}a {text-decoration: none;color: white;
}.aside {border: 1px solid #ccc;height: 690px;width: 220px;
}
</style>
目前,我們點擊左側的菜單,右側主區域展示的內容,還不能做到動態變化。 那應該如何做到動態變化呢 ?
那要完成這個功能效果,我們就需要用到Vue生態中的路由 Vue-Router
。
4. Vue Router
4.1 介紹
- Vue Router:Vue的官方路由。 為Vue提供富有表現力、可配置的、方便的路由。
- Vue中的路由,主要定義的是路徑與組件之間的對應關系。
比如,我們打開一個網站,點擊左側菜單,地址欄的地址發生變化。 地址欄地址一旦發生變化,在主區域顯示對應的頁面組件。
VueRouter主要由以下三個部分組成,如下所示:
- VueRouter:路由器類,根據路由請求在路由視圖中動態渲染選中的組件
- <router-link>:請求鏈接組件,瀏覽器會解析成<a>
- <router-view>:動態視圖組件,用來渲染展示與路由路徑對應的組件
4.2 入門
介紹完了VueRouter之后,接下來,我們就通過一個入門程序,來演示一下VueRouter的使用。
1). 安裝 vue-router
(創建Vue項目時,可以選擇)
npm install vue-router@4
2). 在 main.ts
入口文件中進行配置,加入如下配置
import router from './router'//..... 創建完vue的應用實例后,調用app.use
app.use(router)
3). 在 src/views 目錄下再定義一個文件夾,在文件夾中再創建一個 vue 組件文件
4). 定義路由
在 src/router/index.ts
中定義路由表信息,在其中主要是定義請求路徑與組件之間的對應關系。 完整的文件內容如下:
import { createRouter, createWebHistory } from 'vue-router'const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes: [{path: '/',name: 'home',component: () => import('../views/layout/index.vue')},{path: '/index',name: 'index',component: () => import('../views/index/index.vue')}]
})export default router
5). 在 App.vue
根組件中,定義 <RouterView></RouterView>
標簽
該標簽將用于顯示,訪問的請求路徑對應的組件。
<script setup lang="ts"></script><template><RouterView></RouterView>
</template><style scoped></style>
6). 測試
瀏覽器訪問請求路徑 http://127.0.0.1:5173/index,展示如下頁面內容(該頁面內容,就是我們在 index/index.vue
中定義的頁面內容):
瀏覽器訪問請求路徑 http://127.0.0.1:5173/,展示如下頁面內容 (該頁面內容,就是我們在 layout/index.vue
中定義的頁面內容):
到此,我們發現,我們請求不同的請求路徑,就可以在頁面中顯示不同的組件。具體的訪問流程如下:
4.3 案例
那接下來,我們就要基于 VueRouter
來完成點擊 左側菜單,動態切換主展示區域內容的動態效果。
1). 準備案例的空頁面
<script setup lang="ts"></script><template>班級管理|學生管理|員工管理|部門管理|首頁展示
</template><style scoped></style>
2). 在 src/router/index.ts
中配置路由信息
這里我們用到了Vue中的嵌套路由,具體定義方式,主要是在配置路由信息時,通過children
來描述。如你所見,children
配置只是另一個路由數組,就像 routes
本身一樣。因此,你可以根據自己的需要,不斷地嵌套視圖。
import { createRouter, createWebHistory } from 'vue-router'const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes: [{path: '/',name: 'home',component: () => import('../views/layout/index.vue'),redirect: '/index',children: [{path: 'index',name: 'index',component: () => import('../views/index/index.vue') //首頁},{path: 'emp',name: 'emp',component: () => import('../views/emp/index.vue') //員工管理},{path: 'dept',name: 'dept',component: () => import('../views/dept/index.vue') //部門管理},{path: 'clazz',name: 'clazz',component: () => import('../views/clazz/index.vue') //班級管理},{path: 'stu',name: 'stu',component: () => import('../views/stu/index.vue') //學員管理}]}]
})export default router
3). 完善左側菜單欄 layout/index.vue
,菜單欄關聯路由
菜單關聯了路由之后,我們點擊對應的菜單,就會根據菜單的唯一標識 index
,在地址欄中請求訪問對應的地址。
4). 在Vue組件中,動態展示與路由對應的組件 。
需要在 layout/index.vue
中的 <el-main></el-main>
中添加動態路由視圖組件 <RouterView></RouterView>
。如下:
<!-- 主展示區域 -->
<el-main><RouterView></RouterView>
</el-main>
最終完整的 layout/index.vue
代碼如下:
<script setup lang="ts"></script><template><div class="common-layout"><el-container><!-- 頂欄 - header --><el-header class="header"><span class="title">Tlias智能學習輔助系統</span><span class="right_tool"><a href=""><el-icon><EditPen /></el-icon> 修改密碼 </a><a href=""><el-icon><SwitchButton /></el-icon> 退出登錄 </a></span></el-header><!-- 左側菜單 & 主區域 --><el-container><!-- 左側菜單 --><el-aside width="200px" class="aside"><el-scrollbar><el-menu router><!-- 首頁菜單 --><el-menu-item index="/index"><el-icon><Promotion /></el-icon> 首頁</el-menu-item><!-- 班級管理菜單 --><el-sub-menu index="/manage"><template #title><el-icon><Menu /></el-icon> 班級學員管理</template><el-menu-item index="/clazz"><el-icon><HomeFilled /></el-icon>班級管理</el-menu-item><el-menu-item index="/stu"><el-icon><UserFilled /></el-icon>學員管理</el-menu-item></el-sub-menu><!-- 系統信息管理 --><el-sub-menu index="/system"><template #title><el-icon><Tools /></el-icon>系統信息管理</template><el-menu-item index="/dept"><el-icon><HelpFilled /></el-icon>部門管理</el-menu-item><el-menu-item index="/emp"><el-icon><Avatar /></el-icon>員工管理</el-menu-item></el-sub-menu><!-- 數據統計管理 --><el-sub-menu index="/report"><template #title><el-icon><Histogram /></el-icon>數據統計管理</template><el-menu-item index="/empReport"><el-icon><InfoFilled /></el-icon>員工信息統計</el-menu-item><el-menu-item index="/stuReport"><el-icon><Share /></el-icon>學員信息統計</el-menu-item><el-menu-item index="/log"><el-icon><Document /></el-icon>日志信息統計</el-menu-item></el-sub-menu></el-menu></el-scrollbar></el-aside><!-- 主展示區域 --><el-main><RouterView></RouterView></el-main></el-container></el-container></div>
</template><style scoped>
.header {background-image: linear-gradient(to right, #e70cc5, #e94dcf, #eb6fd8, #ec8bdf, #eea5e6);line-height: 60px;
}.title {color: white;font-size: 35px;font-family: 楷體;}.right_tool {float: right;
}a {text-decoration: none;color: white;
}.aside {border: 1px solid #ccc;height: 690px;width: 220px;
}
</style>
5). 測試
4.4 首頁制作
其實首頁,我們只需要展示一張圖片即可。 直接在 index/index.vue
中引入一張圖片即可,具體代碼如下:
<script setup lang="ts"></script><template><img src="@/assets/index.png">
</template><style scoped></style>
最終效果如下:
5. 部門管理
部門管理的頁面內容,寫在 src/views/dept/index.vue
中。
5.1部門列表
5.1.1. 基本布局
首先,根據頁面原型、需求說明、接口文檔,先完成頁面的基本布局 。 可以參考 ElementPlus
中的組件,拷貝過來適當做一個改造。
部門管理組件 src/views/dept/index.vue
具體的頁面布局代碼如下:
<script setup lang="ts">
import {ref} from 'vue'
import type { DeptModelArray } from '@/api/model/model'//聲明列表展示數據
let tableData = ref<DeptModelArray>([])
</script><template><h1>部門管理</h1><el-button type="primary" style="float: right" @click="">+ 新增</el-button><br><br><!-- 部門數據表格 --><el-table :data="tableData" border style="width: 100%"><el-table-column type="index" label="序號" width="80" align="center"/><el-table-column prop="name" label="部門名稱" width="250" align="center"/><el-table-column prop="updateTime" label="最后操作時間" width="300" align="center"/><el-table-column label="操作" align="center"><template #default="scope"><el-button size="small" type="primary" @click="">修改</el-button><el-button size="small" type="danger" @click="">刪除</el-button></template></el-table-column></el-table>
</template><style scoped></style>
表格中每一列展示的屬性 prop
都是根據接口文檔來的,接口文檔返回什么樣的數據,我們就安裝對應的數據格式進行解析。
5.1.2 加載數據
根據需求,需要在新增、修改、刪除部門之后,加載最新的部門數據。 在打開頁面之后,也需要自動加載部門數據。 那接下來,我們就需要基于axios發送異步請求,動態獲取數據。
需要在 src/views/dept/index.vue
中增加如下代碼,在頁面加載完成發送異步請求(https://mock.apifox.com/m1/3161925-0-default/depts),動態加載的Axios。
<script setup lang="ts">
import {ref, onMounted} from 'vue'
import type { DeptModelArray } from '@/api/model/model'
import axios from 'axios'//聲明列表展示數據
let tableData = ref<DeptModelArray>([])//動態加載數據-查詢部門
const queryAll = async () => {const result = await axios.get('https://mock.apifox.com/m1/3161925-0-default/depts')tableData.value = result.data.data
}//鉤子函數
onMounted(() => {queryAll()
})
</script>
添加代碼后,最終 src/views/dept/index.vue
代碼如下:
<script setup lang="ts">
import {ref, onMounted} from 'vue'
import type { DeptModelArray } from '@/api/model/model'
import axios from 'axios'//聲明列表展示數據
let tableData = ref<DeptModelArray>([])//動態加載數據-查詢部門
const queryAll = async () => {const result = await axios.get('https://mock.apifox.com/m1/3161925-0-default/depts')tableData.value = result.data.data
}//鉤子函數
onMounted(() => {queryAll()
})
</script><template><h1>部門管理</h1><el-button type="primary" style="float: right" @click="">+ 新增</el-button><br><br><!-- 部門數據表格 --><el-table :data="tableData" border style="width: 100%"><el-table-column type="index" label="序號" width="80" align="center"/><el-table-column prop="name" label="部門名稱" width="250" align="center"/><el-table-column prop="updateTime" label="最后操作時間" width="300" align="center"/><el-table-column label="操作" align="center"><template #default="scope"><el-button size="small" type="primary" @click="">修改</el-button><el-button size="small" type="danger" @click="">刪除</el-button></template></el-table-column></el-table>
</template><style scoped></style>
代碼編寫完成之后,打開瀏覽器進行測試 ,我們可以看到數據可以正常的查詢出來,并展示在頁面中。
思考:直接在Vue組件中,基于axios發送異步請求,存在什么問題?
我們剛才在完成部門列表查詢時,是直接基于axios發送異步請求,直接將接口的請求地址放在組件文件 .vue
中。 而如果開發一個大型的項目,組件文件可能會很多很多很多,如果前端開發完畢,進行前后端聯調測試了,需要修改請求地址,那么此時,就需要找到每一個 .vue
文件,然后挨個修改。 所以上述的代碼,雖然實現了動態加載數據的功能。 但是存在以下問題:
- 請求路徑難以維護
- 數據解析繁瑣
5.1.3 程序優化
1). 為了解決上述問題,我們在前端項目開發時,通常會定義一個請求處理的工具類 - src/utils/request.ts
。 在這個工具類中,對axios進行了封裝。 具體代碼如下:
import axios from 'axios'//創建axios實例對象
const request = axios.create({baseURL: '/api',timeout: 600000
})//axios的響應 response 攔截器
request.interceptors.response.use((response) => { //成功回調return response.data},(error) => { //失敗回調return Promise.reject(error)}
)export default request
2). 而與服務端進行異步交互的邏輯,通常會按模塊,封裝在一個單獨的API中,如:src/api/dept.ts
import request from "@/utils/request"
import type { ResultModel } from "./model/model"//列表查詢
export const queryAllApi = () => request.get<any, ResultModel>('/depts')
3). 修改 src/views/dept/index.vue
中的代碼
現在就不需要每次直接調用axios發送異步請求了,只需要將我們定義的對應模塊的API導入進來,就可以直接使用了。
<script setup lang="ts">
import {ref, onMounted} from 'vue'
import type { DeptModelArray } from '@/api/model/model'
import {queryAllApi} from '@/api/dept'//聲明列表展示數據
let tableData = ref<DeptModelArray>([])//動態加載數據-查詢部門
const queryAll = async () => {const result = await queryAllApi()tableData.value = result.data
}//鉤子函數
onMounted(() => {queryAll()
})
</script>
做完上面這三部之后,我們打開瀏覽器發現,并不能訪問到接口數據。原因是因為,目前請求路徑不對。
4). 在 vite.config.ts
中配置前端請求服務器的信息
在服務器中配置代理proxy的信息,并在配置代理時,執行目標服務器。 以及url路徑重寫的規則。
server: {proxy: {'/api': {target: 'http://localhost:8080',secure: false,changeOrigin: true,rewrite: (path) => path.replace(/^\/api/, ''),}}}
添加位置如下所示:
然后,我們就可以啟動服務器端的程序,進行測試了(測試時,記得將之前編寫的登錄校驗的過濾器、攔截器、AOP程序全部注釋掉)。
5.2 新增部門
接下來,我們再來完成新增部門的功能實現。
1). 在 src/views/dept/index.vue
中完成頁面布局,并編寫交互邏輯,完成數據綁定。
完整代碼如下:
<script setup lang="ts">
import {ref, onMounted} from 'vue'
import type { DeptModelArray, DeptModel } from '@/api/model/model'
import {queryAllApi, addApi} from '@/api/dept'
import { ElMessage } from 'element-plus';//聲明列表展示數據
let tableData = ref<DeptModelArray>([])//動態加載數據-查詢部門
const queryAll = async () => {const result = await queryAllApi()tableData.value = result.data
}//鉤子函數
onMounted(() => {queryAll()
})//新增部門
const dialogFormVisible = ref<boolean>(false)
const deptForm = ref<DeptModel>({name: ''})
const formTitle = ref<string>('')//點擊新增按鈕觸發的函數
const add = () => {formTitle.value = '新增部門'dialogFormVisible.value = truedeptForm.value = {name: ''}
}//點擊保存按鈕-發送異步請求
const save = async () => {const result = await addApi(deptForm.value)if(result.code){ElMessage.success('操作成功')}else{ElMessage.error(result.msg)}dialogFormVisible.value = falsequeryAll()
}</script><template><h1>部門管理</h1><el-button type="primary" style="float: right" @click="add">+ 新增</el-button><br><br><!-- 部門數據表格 --><el-table :data="tableData" border style="width: 100%"><el-table-column type="index" label="序號" width="80" align="center"/><el-table-column prop="name" label="部門名稱" width="250" align="center"/><el-table-column prop="updateTime" label="最后操作時間" width="300" align="center"/><el-table-column label="操作" align="center"><template #default="scope"><el-button size="small" type="primary" @click="">修改</el-button><el-button size="small" type="danger" @click="">刪除</el-button></template></el-table-column></el-table><!-- 新增部門 / 修改部門對話框 --><el-dialog v-model="dialogFormVisible" :title="formTitle" width="30%"><el-form :model="deptForm"><el-form-item label="部門名稱" label-width="80px"><el-input v-model="deptForm.name" autocomplete="off" /></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button @click="dialogFormVisible = false">取消</el-button><el-button type="primary" @click="save">確定</el-button></span></template></el-dialog></template><style scoped></style>
2). 在 src/api/dept.ts
中增加如下代碼
//添加部門
export const addApi = (dept:DeptModel) => request.post<any, ResultModel>('/depts', dept)
目前 src/api/dept.ts
文件中完整代碼如下:
import request from "@/utils/request"
import type { DeptModel, ResultModel } from "./model/model"//列表查詢
export const queryAllApi = () => request.get<any, ResultModel>('/depts')//添加部門
export const addApi = (dept:DeptModel) => request.post<any, ResultModel>('/depts', dept)
打開瀏覽器進行測試,效果如下:
5.3 修改部門
對于修改操作,通常會分為兩步進行:
- 查詢回顯
- 保存修改
交互邏輯:
- 點擊 編輯 按鈕,根據ID進行查詢,彈出對話框,完成頁面回顯展示。(查詢回顯)
- 點擊 確定 按鈕,保存修改后的數據,完成數據更新操作。(保存修改)
5.3.1 查詢回顯
1). 在 src/api/dept.ts
中定義根據id查詢的請求
//根據ID查詢
export const queryInfoApi = (id:number) => request.get(`/depts/${id}`)
2). 在 src/views/dept/index.vue
中添加根據ID查詢回顯的邏輯
為修改按鈕綁定事件 <template></template>
:
<el-button size="small" type="primary" @click="update(scope.row.id)">修改</el-button>
在 <script> </script>
添加JS邏輯:
//修改部門-查詢回顯
const update = async (id:number) => {formTitle.value = '修改部門'dialogFormVisible.value = truedeptForm.value = {name: ''}const result = await queryInfoApi(id)deptForm.value = result.data
}
到目前為止,完整的 src/views/dept/index.vue
代碼如下:
<script setup lang="ts">
import {ref, onMounted} from 'vue'
import type { DeptModelArray, DeptModel } from '@/api/model/model'
import {queryAllApi, addApi, queryInfoApi} from '@/api/dept'
import { ElMessage } from 'element-plus';//聲明列表展示數據
let tableData = ref<DeptModelArray>([])//動態加載數據-查詢部門
const queryAll = async () => {const result = await queryAllApi()tableData.value = result.data
}//鉤子函數
onMounted(() => {queryAll()
})//新增部門
const dialogFormVisible = ref<boolean>(false)
const deptForm = ref<DeptModel>({name: ''})
const formTitle = ref<string>('')//點擊新增按鈕觸發的函數
const add = () => {formTitle.value = '新增部門'dialogFormVisible.value = truedeptForm.value = {name: ''}
}//點擊保存按鈕-發送異步請求
const save = async () => {const result = await addApi(deptForm.value)if(result.code){ElMessage.success('操作成功')}else{ElMessage.error(result.msg)}dialogFormVisible.value = falsequeryAll()
}//修改部門-查詢回顯
const update = async (id:number) => {formTitle.value = '修改部門'dialogFormVisible.value = truedeptForm.value = {name: ''}const result = await queryInfoApi(id)deptForm.value = result.data
}</script><template><h1>部門管理</h1><el-button type="primary" style="float: right" @click="add">+ 新增</el-button><br><br><!-- 部門數據表格 --><el-table :data="tableData" border style="width: 100%"><el-table-column type="index" label="序號" width="80" align="center"/><el-table-column prop="name" label="部門名稱" width="250" align="center"/><el-table-column prop="updateTime" label="最后操作時間" width="300" align="center"/><el-table-column label="操作" align="center"><template #default="scope"><el-button size="small" type="primary" @click="update(scope.row.id)">修改</el-button><el-button size="small" type="danger" @click="">刪除</el-button></template></el-table-column></el-table><!-- 新增部門 / 修改部門對話框 --><el-dialog v-model="dialogFormVisible" :title="formTitle" width="30%"><el-form :model="deptForm"><el-form-item label="部門名稱" label-width="80px"><el-input v-model="deptForm.name" autocomplete="off" /></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button @click="dialogFormVisible = false">取消</el-button><el-button type="primary" @click="save">確定</el-button></span></template></el-dialog></template><style scoped></style>
5.3.2 保存修改
由于 新增部門 和 修改部門使用的是同一個Dialog對話框,當前點擊 “確定” 按鈕的時候,有可能執行的是新增操作,也有可能是修改操作。
那應該如何辨別到底是新增,還是修改操作呢 ?
其實,我們只需要根據 deptForm
對象的id屬性值,來判斷即可。 如果沒有id,則是新增操作 ;如果有id,則是修改操作。
所以,保存修改功能實現如下:
1). 在 src/api/dept.ts
中增加如下修改部門的請求
//修改部門
export const updateApi = (dept:DeptModel) => request.put<any, ResultModel>('/depts', dept)
2). 在 src/views/dept/index.vue
中完善(修改) save 函數的邏輯
//點擊保存按鈕-發送異步請求
const save = async () => {let result = null;if(deptForm.value.id){result = await updateApi(deptForm.value) //有id, 執行修改操作}else {result = await addApi(deptForm.value) //沒有id, 執行新增操作}if(result.code){ElMessage.success('操作成功')}else{ElMessage.error(result.msg)}dialogFormVisible.value = falsequeryAll()
}
5.4 刪除部門
1). 在 src/api/dept.ts
中增加如下刪除部門的請求
//刪除部門
export const deleteApi = (id:number) => request.delete<any, ResultModel>(`/depts?id=${id}`)
2). 在 src/views/dept/index.vue
中為什么 刪除 按鈕綁定事件
<el-button size="small" type="danger" @click="deleteById(scope.row.id)">刪除</el-button>
3). 在 src/views/dept/index.vue
編寫根據ID刪除數據的函數
//刪除部門
const deleteById =async (id:number) => {//彈出確認框ElMessageBox.confirm('您確認刪除此部門嗎? ', '確認刪除').then( async () => {let result = await deleteApi(id)if(result.code){ //成功ElMessage.success('刪除成功')queryAll()}else {ElMessage.error(result.msg)}}).catch(() => {ElMessage.info('取消刪除')})
}
打開瀏覽器做一個測試:
5.5 表單校驗
目前,我們已經基本完成了部門管理的增刪改查操作。 接下來,我們對部門管理的功能進行,最后一塊完善工作,增加表單校驗。 從頁面原型中,我們可以看到,新增部門的時候部門名稱,不能為空,而且長度得在2-10之間。
5.5.1 ElementPlus 參考
Form 組件允許你驗證用戶的輸入是否符合規范,來幫助你找到和糾正錯誤。Form
組件提供了表單驗證的功能,只需為 rules
屬性傳入約定的驗證規則,并將 form-Item
的 prop
屬性設置為需要驗證的特殊鍵值即可。
5.5.2 實現
1). 定義表單校驗規則
//定義表單校驗規則
const deptFormRef = ref<FormInstance>()
const rules = ref<FormRules<DeptModel>>({name: [{ required: true, message: '部門名稱不能為空', trigger: 'blur' },{ min: 2, max: 10, message: '部門名稱長度在2-10個字之間', trigger: 'blur' },]
})
2). 將表單校驗規則與表單綁定
為表單 <el-form>
綁定 rules
屬性綁定表單校驗規則 。 為每一個表單項,指定 prop
屬性,設置為需要驗證的屬性名。
3). 表單提交時,校驗表單,校驗通過,則允許提交表單。
修改save方法的邏輯,需要加入表單校驗的邏輯。
//點擊保存按鈕-發送異步請求
const save = async (form:FormInstance | undefined) => {if(!form) return;await form.validate(async (valid) => {if (valid) {let result = null;if(deptForm.value.id){result = await updateApi(deptForm.value)}else {result = await addApi(deptForm.value)}if(result.code){ElMessage.success('操作成功')}else{ElMessage.error(result.msg)}dialogFormVisible.value = falsequeryAll()}})
}
4). 重置表單校驗結果
//重置表單校驗結果
const resetForm = (formEl: FormInstance | undefined) => {if (!formEl) returnformEl.resetFields()
}
然后在點擊 “新增” / “修改” 按鈕的時候,調用 resetForm 函數,重置表單校驗結果。
最終,部門管理的完整代碼如下:
1). src/api/dept.ts
import request from "@/utils/request"
import type { DeptModel, ResultModel } from "./model/model"//列表查詢
export const queryAllApi = () => request.get<any, ResultModel>('/depts')//添加部門
export const addApi = (dept:DeptModel) => request.post<any, ResultModel>('/depts', dept)//根據ID查詢
export const queryInfoApi = (id:number) => request.get(`/depts/${id}`)//修改部門
export const updateApi = (dept:DeptModel) => request.put<any, ResultModel>('/depts', dept)//刪除部門
export const deleteApi = (id:number) => request.delete<any, ResultModel>(`/depts?id=${id}`)
2). src/views/dept/index.vue
<script setup lang="ts">
import {ref, onMounted} from 'vue'
import type { DeptModelArray, DeptModel } from '@/api/model/model'
import {queryAllApi, addApi, queryInfoApi, updateApi, deleteApi} from '@/api/dept'
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus';//聲明列表展示數據
let tableData = ref<DeptModelArray>([])//動態加載數據-查詢部門
const queryAll = async () => {const result = await queryAllApi()tableData.value = result.data
}//鉤子函數
onMounted(() => {queryAll()
})//新增部門
const dialogFormVisible = ref<boolean>(false)
const deptForm = ref<DeptModel>({name: ''})
const formTitle = ref<string>('')//點擊新增按鈕觸發的函數
const add = () => {formTitle.value = '新增部門'dialogFormVisible.value = truedeptForm.value = {name: ''}
}//點擊保存按鈕-發送異步請求
const save = async (form:FormInstance | undefined) => {if(!form) return;await form.validate(async (valid) => {if (valid) {let result = null;if(deptForm.value.id){result = await updateApi(deptForm.value)}else {result = await addApi(deptForm.value)}if(result.code){ElMessage.success('操作成功')}else{ElMessage.error(result.msg)}dialogFormVisible.value = falsequeryAll()}})
}//修改部門-查詢回顯
const update = async (id:number) => {formTitle.value = '修改部門'dialogFormVisible.value = truedeptForm.value = {name: ''}const result = await queryInfoApi(id)deptForm.value = result.data
}//刪除部門
const deleteById =async (id:number) => {//彈出確認框ElMessageBox.confirm('您確認刪除此部門嗎? ', '確認刪除').then( async () => {let result = await deleteApi(id)if(result.code){ //成功ElMessage.success('刪除成功')queryAll()}else {ElMessage.error(result.msg)}}).catch(() => {ElMessage.info('取消刪除')})
}//定義表單校驗規則
const deptFormRef = ref<FormInstance>()
const rules = ref<FormRules<DeptModel>>({name: [{ required: true, message: '部門名稱不能為空', trigger: 'blur' },{ min: 2, max: 10, message: '部門名稱長度在2-10個字之間', trigger: 'blur' },]
})//重置表單校驗結果
const resetForm = (form: FormInstance | undefined) => {if (!form) returnform.resetFields()
}
</script><template><h1>部門管理</h1><el-button type="primary" style="float: right" @click="add(); resetForm(deptFormRef);">+ 新增</el-button><br><br><!-- 部門數據表格 --><el-table :data="tableData" border style="width: 100%"><el-table-column type="index" label="序號" width="80" align="center"/><el-table-column prop="name" label="部門名稱" width="250" align="center"/><el-table-column prop="updateTime" label="最后操作時間" width="300" align="center"/><el-table-column label="操作" align="center"><template #default="scope"><el-button size="small" type="primary" @click="update(scope.row.id); resetForm(deptFormRef);">修改</el-button><el-button size="small" type="danger" @click="deleteById(scope.row.id)">刪除</el-button></template></el-table-column></el-table><!-- 新增部門 / 修改部門對話框 --><el-dialog v-model="dialogFormVisible" :title="formTitle" width="30%"><el-form :model="deptForm" :rules="rules" ref="deptFormRef"><el-form-item label="部門名稱" label-width="80px" prop="name"><el-input v-model="deptForm.name" autocomplete="off" /></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button @click="dialogFormVisible = false; resetForm(deptFormRef);">取消</el-button><el-button type="primary" @click="save(deptFormRef)">確定</el-button></span></template></el-dialog></template><style scoped></style>