在前面的課程中,我們學習了Vue工程化的基礎內容、TS、ElementPlus,那接下來呢,我們要通過一個案例,加強大家對于Vue項目的理解,并掌握Vue項目的開發。 這個案例呢,就是我們之前所做的Tlias智能學習輔助系統。
在這個案例中,我們主要完成 部門管理 和 員工管理 的功能開發。 而今天呢,我們先來完成部門管理的功能開發,而在完成部門管理的功能開發之前,先需要完成基礎的準備工作。 所以今天的課程安排如下:
- 前后端分離開發
- 準備工作
- 頁面布局
- Vue-Router
- 部門管理
1. 前后端分離開發
在之前的課程中,我們介紹過,現在的企業項目開發有2種開發模式:前后臺混合開發 和 前后臺分離開發。
前后臺混合開發,顧名思義就是前臺后臺代碼混在一起開發。這種開發模式有如下缺點:
- 溝通成本高:后臺人員發現前端有問題,需要找前端人員修改,前端修改成功,再交給后臺人員使用
- 分工不明確:后臺開發人員需要開發后臺代碼,也需要開發部分前端代碼。很難培養專業人才
- 不便管理:所有的代碼都在一個工程中
- 難以維護:前端代碼更新,和后臺無關,但是需要整個工程包括后臺一起重新打包部署。
所以我們目前基本都是采用的前后臺分離開發方式,如下圖所示:
我們將原先的工程分為前端工程和后端工程這2個工程,然后前端工程交給專業的前端人員開發,后端工程交給專業的后端人員開發。
前端頁面需要數據,可以通過發送異步請求,從后臺工程獲取。但是,我們前后臺是分開來開發的,那么前端人員怎么知道后臺返回數據的格式呢?后端人員開發,怎么知道前端人員需要的數據格式呢?
所以針對這個問題,我們前后臺統一制定一套規范!我們前后臺開發人員都需要遵循這套規范開發,這就是我們的 接口文檔。接口文檔有離線版和在線版本,接口文檔示可以查詢今天提供 資料/接口文檔 里面的資料。
那么接口文檔的內容怎么來的呢?是我們后臺開發者根據產品經理提供的產品原型和需求文檔所撰寫出來的,產品原型示例可以參考今天提供資料/頁面原型 里面的資料。
那么基于前后臺分離開發的模式下,我們后臺開發者開發一個功能的具體流程如何呢?如下圖所示:
- 需求分析:首先我們需要閱讀需求文檔,分析需求,理解需求。
- 接口定義:查詢接口文檔中關于需求的接口的定義,包括地址,參數,響應數據類型等等
- 前后臺并行開發:各自按照接口文檔進行開發,實現需求
- 測試:前后臺開發完了,各自按照接口文檔進行測試
- 前后段聯調測試:前段工程請求后端工程,測試功能
2. 頁面布局
2.1 準備工作
- 導入資料中準備的基礎工程到VsCode。 【復制出來放在自己的工作目錄下】
- 啟動前端項目,訪問該項目
打開瀏覽器,訪問 http://localhost:5173
在提供的基礎工程中,最基本的頁面布局,已經準備好了。 我們通過頁面原型可以看到頁面的布局如下:
其實要實現這個頁面布局,我們可以借助于 ElementPlus
中提供的 container 容器布局。
Container布局容器,用于布局的容器組件,方便快速搭建頁面的基本結構:
<el-container>
:外層容器。 當子元素中包含 <el-header>
或 <el-footer>
時,全部子元素會垂直上下排列, 否則會水平左右排列。
<el-header>
:頂欄容器。
<el-aside>
:側邊欄容器。
<el-main>
:主要區域容器。
<el-footer>
:底欄容器。
提示:當 <el-container>
子元素中包含 <el-header>
或 <el-footer>
時,全部子元素會垂直上下排列, 否則會水平左右排列。
2.2 左側菜單布局
接下來,我們再來完成左側菜單欄的制作。
左側菜單欄的制作,也不需要我們自己實現,其實在 ElementPlus
中已經提供了對應的菜單組件,我們可以直接參考【PS: 其實就是復制過來,參考頁面原型和需求,將其改造成我們需要的樣子就可以了】。
參考代碼的出處:
然后就可以參考其提供的源碼,復制到我們的側邊欄部分 <el-aside> ... </el-aside>
,然后根據我們案例的需要進行改造,改造成我們需要的樣子即可。
最終左側菜單欄的代碼如下 ,大家可以直接導入到 views/layout/index.vue
的側邊欄區域 <el-aside> ... </el-aside>
:
<el-menu><!-- 首頁菜單 --><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>
最終,瀏覽器打開的效果如下:
到目前為止,views/layout/index.vue
中的整體內容如下:
<script setup></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-menu><!-- 首頁菜單 --><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-aside><el-main>右側核心展示區域</el-main></el-container></el-container></div>
</template><style scoped>
.header {background-image: linear-gradient(to right, #00547d, #007fa4, #00aaa0, #00d072, #a8eb12);
}.title {color: white;font-size: 40px;font-family: 楷體;line-height: 60px;font-weight: bolder;
}.right_tool{float: right;line-height: 60px;
}a {color: white;text-decoration: none;
}.aside {width: 220px;border-right: 1px solid #ccc;height: 730px;
}
</style>
目前,我們點擊左側的菜單,右側主區域展示的內容,還不能做到動態變化。 那應該如何做到動態變化呢 ?
要解決這個問題,就需要通過VueRouter來解決。
3. VueRouter
3.1 介紹
- Vue Router:Vue的官方路由。 為Vue提供富有表現力、可配置的、方便的路由。
- Vue中的路由,主要定義的是路徑與組件之間的對應關系。
比如,我們打開一個網站,點擊左側菜單,地址欄的地址發生變化。 地址欄地址一旦發生變化,在主區域顯示對應的頁面組件。
VueRouter主要由以下三個部分組成,如下所示:
- VueRouter:路由器類,根據路由請求在路由視圖中動態渲染選中的組件
- <router-link>:請求鏈接組件,瀏覽器會解析成<a>
- <router-view>:動態視圖組件,用來渲染展示與路由路徑對應的組件
3.2 基礎路由配置
1). 在 views/layout/index.vue
中,調整代碼,具體調整位置如下:
- 在左側菜單欄的
<el-menu>
標簽上添加router
屬性,這會讓 Element Plus 的<el-menu>
組件自動根據路由來激活對應的菜單項。 - 使用
<router-view>
組件來渲染根據路由動態變化的內容。 - 確保每個
<el-menu-item>
的index
屬性值與你想要導航到的路徑相匹配。
<script setup>
// 無需額外導入,因為我們只是使用了 Element Plus 和 Vue Router 的基本功能
</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-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="/report/emp"><el-icon><InfoFilled /></el-icon>員工信息統計</el-menu-item><el-menu-item index="/report/stu"><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-aside><!-- 主展示區域 --><el-main><router-view></router-view></el-main></el-container></el-container></div>
</template><style scoped>
.header {background-image: linear-gradient(to right, #00547d, #007fa4, #00aaa0, #00d072, #a8eb12);
}.title {color: white;font-size: 40px;font-family: 楷體;line-height: 60px;font-weight: bolder;
}.right_tool{float: right;line-height: 60px;
}a {color: white;text-decoration: none;
}.aside {width: 220px;border-right: 1px solid #ccc;height: 730px;
}
</style>
2). 在 router/index.js 中配置請求路徑與組件之間的關系。
import { createRouter, createWebHistory} from 'vue-router';import IndexView from '@/views/index/index.vue';
import ClazzView from '@/views/clazz/index.vue';
import StuView from '@/views/stu/index.vue';
import DeptView from '@/views/dept/index.vue';
import EmpView from '@/views/emp/index.vue';
import EmpReportView from '@/views/report/emp/index.vue';
import StuReportView from '@/views/report/stu/index.vue';
import LogView from '@/views/log/index.vue';
import LoginView from '@/views/login/index.vue';const routes = [{ path: '/index', component: IndexView },{ path: '/clazz', component: ClazzView },{ path: '/stu', component: StuView },{ path: '/dept', component: DeptView },{ path: '/emp', component: EmpView },{ path: '/report/emp', component: EmpReportView },{ path: '/report/stu', component: StuReportView },{ path: '/log', component: LogView },{ path: '/login', component: LoginView },
];const router = createRouter({history: createWebHistory(),routes,
});export default router;
經過這么兩步操作之后,我們就可以看到,在頁面上,點擊左側菜單,右側主展示區域,就會顯示出對應的頁面了。
那要完成這個功能效果,我們就需要用到Vue生態中的路由 Vue-Router
。
3.3 完善路由配置
上述我們只是完成了最基本的路由配置。 并經過測試我們發現,如果我們訪問 /login 路徑,會發現,登錄頁面是在layout頁面中嵌套展示的,這肯定是不合適的。
那接下來,我們就來優化一下路由的配置。最終配置形式如下,在 router/index.js
中做如下配置:
import { createRouter, createWebHistory } from 'vue-router'import IndexView from '@/views/index/index.vue'
import ClazzView from '@/views/clazz/index.vue'
import DeptView from '@/views/dept/index.vue'
import EmpView from '@/views/emp/index.vue'
import LogView from '@/views/log/index.vue'
import StuView from '@/views/stu/index.vue'
import EmpReportView from '@/views/report/emp/index.vue'
import StuReportView from '@/views/report/stu/index.vue'
import LayoutView from '@/views/layout/index.vue'
import LoginView from '@/views/login/index.vue'const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes: [{path: '/', name: '',component: LayoutView,redirect: '/index', //重定向children: [{path: 'index', name: 'index', component: IndexView},{path: 'clazz', name: 'clazz', component: ClazzView},{path: 'stu', name: 'stu', component: StuView},{path: 'dept', name: 'dept', component: DeptView},{path: 'emp', name: 'emp', component: EmpView},{path: 'log', name: 'log', component: LogView},{path: 'empReport', name: 'empReport', component: EmpReportView},{path: 'stuReport', name: 'stuReport', component: StuReportView},]},{path: '/login', name: 'login', component: LoginView}]
})export default router
具體的執行訪問流程如下:
- 當點擊左側菜單欄的員工管理菜單時,最終地址欄會訪問路徑
/emp
。 - 此時VueRouter,會自動的到所配置的路由表(
router/index.js
)中,查找與該路徑對應的組件,并展示在路由展示組件<router-view>
對應的位置中。
4. 部門管理
部門管理的頁面內容,寫在 src/views/dept/index.vue
中。
4.1 部門列表
4.1.1 基本布局
首先,根據頁面原型、需求說明、接口文檔,先完成頁面的基本布局 。 可以參考 ElementPlus
中的組件,拷貝過來適當做一個改造。
我們先來完成頁面的基本布局。
部門管理組件 src/views/dept/index.vue
具體的頁面布局代碼如下:
<script setup>
import { ref } from 'vue';// 示例數據
const deptList= ref([{ id: 1, name: '學工部', createTime: '2024-09-01T23:06:29', updateTime: '2024-09-01T23:06:29' },{ id: 2, name: '教研部', createTime: '2024-09-01T23:06:29', updateTime: '2024-09-01T23:06:29' }
]);// 編輯部門 - 根據ID查詢回顯數據
const handleEdit = (id) => {console.log(`Edit item with ID ${id}`);// 在這里實現編輯功能
};// 刪除部門 - 根據ID刪除部門
const handleDelete = (id) => {console.log(`Delete item with ID ${id}`);// 在這里實現刪除功能
};
</script><template><h1>部門管理</h1><!-- 按鈕靠頁面右側顯示 --><el-button type="primary" @click="handleEdit" style="float: right;"> + 新增部門</el-button> <br><br><el-table :data="deptList" border style="width: 100%;"><el-table-column type="index" label="序號" width="100" align="center"/><el-table-column prop="name" label="部門名稱" width="300" align="center"/><el-table-column prop="updateTime" label="最后修改時間" width="300" align="center"/><el-table-column fixed="right" label="操作" align="center"><template #default="scope"><el-button size="small" @click="handleEdit(scope.row.id)">修改</el-button><el-button size="small" type="danger" @click="handleDelete(scope.row.id)">刪除</el-button></template></el-table-column></el-table></template><style scoped></style>
表格中每一列展示的屬性 prop
都是根據接口文檔來的,接口文檔返回什么樣的數據,我們就安裝對應的數據格式進行解析。
最終頁面效果,展示如下:
4.1.2 加載數據
根據需求,需要在新增、修改、刪除部門之后,加載最新的部門數據。 在打開頁面之后,也需要自動加載部門數據。 那接下來,我們就需要基于axios發送異步請求,動態獲取數據。
需要在 src/views/dept/index.vue
中增加如下代碼,在頁面加載完成發送異步請求(https://apifoxmock.com/m1/3128855-1224313-default/depts
),動態加載的Axios。
<script setup>
import {ref, onMounted} from 'vue'
import axios from 'axios'//聲明列表展示數據
let deptList= ref([])//動態加載數據-查詢部門
const queryAll = async () => {const result = await axios.get('https://apifoxmock.com/m1/3128855-1224313-default/depts')deptList.value = result.data.data
}//鉤子函數
onMounted(() => {queryAll()
})// 編輯部門 - 根據ID查詢回顯數據
const handleEdit = (id) => {console.log(`Edit item with ID ${id}`);// 在這里實現編輯功能
};// 刪除部門 - 根據ID刪除部門
const handleDelete = (id) => {console.log(`Delete item with ID ${id}`);// 在這里實現刪除功能
};
</script>
添加代碼后,最終 src/views/dept/index.vue
完整代碼如下:
<script setup>
import {ref, onMounted} from 'vue'
import axios from 'axios'//聲明列表展示數據
let deptList= ref([])//動態加載數據-查詢部門
const queryAll = async () => {const result = await axios.get('https://apifoxmock.com/m1/3161925-0-default/depts')deptList.value = result.data.data
}//鉤子函數
onMounted(() => {queryAll()
})// 編輯部門 - 根據ID查詢回顯數據
const handleEdit = (id) => {console.log(`Edit item with ID ${id}`);// 在這里實現編輯功能
};// 刪除部門 - 根據ID刪除部門
const handleDelete = (id) => {console.log(`Delete item with ID ${id}`);// 在這里實現刪除功能
};
</script><template><h1>部門管理</h1><!-- 按鈕靠頁面右側顯示 --><el-button type="primary" @click="handleEdit" style="float: right;"> + 新增部門</el-button> <br><br><el-table :data="deptList" border style="width: 100%;"><el-table-column type="index" label="序號" width="100" align="center"/><el-table-column prop="name" label="部門名稱" width="300" align="center"/><el-table-column prop="updateTime" label="最后修改時間" width="300" align="center"/><el-table-column fixed="right" label="操作" align="center"><template #default="scope"><el-button size="small" @click="handleEdit(scope.row.id)">修改</el-button><el-button size="small" type="danger" @click="handleDelete(scope.row.id)">刪除</el-button></template></el-table-column></el-table>
</template><style scoped></style>
頁面效果如下,頁面一加載,馬上就會查詢出所有的部門數據:
我們可以看到數據可以正常的查詢出來,并展示在頁面中。
思考:直接在Vue組件中,基于axios發送異步請求,存在什么問題?
我們剛才在完成部門列表查詢時,是直接基于axios發送異步請求,直接將接口的請求地址放在組件文件 .vue
中。 而如果開發一個大型的項目,組件文件可能會很多很多很多,如果前端開發完畢,進行前后端聯調測試了,需要修改請求地址,那么此時,就需要找到每一個 .vue
文件,然后挨個修改。 所以上述的代碼,雖然實現了動態加載數據的功能。 但是存在以下問題:
- 請求路徑難以維護
- 數據解析繁瑣
4.1.3 程序優化
1). 為了解決上述問題,我們在前端項目開發時,通常會定義一個請求處理的工具類 - src/utils/request.js
。 在這個工具類中,對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.js
import request from "@/utils/request"//列表查詢
export const queryAllApi = () => request.get('/depts')
3). 修改 src/views/dept/index.vue
中的代碼
現在就不需要每次直接調用axios發送異步請求了,只需要將我們定義的對應模塊的API導入進來,就可以直接使用了。
<script setup>
import {ref, onMounted} from 'vue'
import {queryAllApi} from '@/api/dept'//聲明列表展示數據
let deptList= ref([])//動態加載數據-查詢部門
const queryAll = async () => {const result = await queryAllApi()deptList.value = result.data
}//鉤子函數
onMounted(() => {queryAll()
})// 編輯部門 - 根據ID查詢回顯數據
const handleEdit = (id) => {console.log(`Edit item with ID ${id}`);// 在這里實現編輯功能
};// 刪除部門 - 根據ID刪除部門
const handleDelete = (id) => {console.log(`Delete item with ID ${id}`);// 在這里實現刪除功能
};
</script>
做完上面這三步之后,我們打開瀏覽器發現,并不能訪問到接口數據。原因是因為,目前請求路徑不對。
4). 在 vite.config.js
中配置前端請求服務器的信息
在服務器中配置代理proxy的信息,并在配置代理時,執行目標服務器。 以及url路徑重寫的規則。
import { fileURLToPath, URL } from 'node:url'import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'// https://vitejs.dev/config/
export default defineConfig({plugins: [vue(),vueJsx(),],resolve: {alias: {'@': fileURLToPath(new URL('./src', import.meta.url))}},server: {proxy: {'/api': {target: 'http://localhost:8080',secure: false,changeOrigin: true,rewrite: (path) => path.replace(/^\/api/, ''),}}}
})
添加內容為,綠色陰影部分的代碼。
然后,我們就可以啟動服務器端的程序(將之前開發的服務端程序啟動起來測試一下 ),進行測試了(【注意:測試時, 記得將令牌校驗的過濾器及攔截器, 以及記錄日志的AOP程序 全部注釋】)。
4.2 新增部門
4.2.1 功能實現
接下來,我們再來完成新增部門的功能實現。
新增部門和編輯部門,可以同用一個Dialog對話框。而這個對話框中,主要包括兩個ElementPlus中的組件,分別為:Dialog對話框組件,Form表單組件。
1). 在 src/views/dept/index.vue
中完成頁面布局,并編寫交互邏輯,完成數據綁定。
完整代碼如下:
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { queryAllApi, addDeptApi } from '@/api/dept'// 聲明列表展示數據
let deptList= ref([])// 動態加載數據 - 查詢部門
const queryAll = async () => {const result = await queryAllApi()deptList.value = result.data
}// 鉤子函數
onMounted(() => {queryAll()
})// 編輯部門 - 根據ID查詢回顯數據
const handleEdit = (id) => {console.log(`Edit item with ID ${id}`);// 在這里實現編輯功能
};// 刪除部門 - 根據ID刪除部門
const handleDelete = (id) => {console.log(`Delete item with ID ${id}`);// 在這里實現刪除功能
};const formTitle = ref('')
//新增部門
const add = () => {formTitle.value = '新增部門'showDialog.value = truedeptForm.value = {name: ''}
}// 新增部門對話框的狀態
const showDialog = ref(false)
// 表單數據
const deptForm = ref({name: ''})
// 表單驗證規則
const formRules = ref({name: [{ required: true, message: '請輸入部門名稱', trigger: 'blur' },{ min: 2, max: 10, message: '長度在 2 到 10 個字符', trigger: 'blur' }]
})// 表單標簽寬度
const formLabelWidth = ref('120px')
// 表單引用
const deptFormRef = ref(null)// 重置表單
const resetForm = () => {deptFormRef.value.resetFields()
}// 提交表單
const save = async () => {await deptFormRef.value.validate(async valid => {if (!valid) return// 提交表單const result = await addDeptApi(deptForm.value)if(result.code){ElMessage.success('操作成功')// 關閉對話框showDialog.value = false// 重置表單resetForm()// 重新加載數據queryAll()}else {ElMessage.error(result.msg)}})
}
</script><template><h1>部門管理</h1><!-- 按鈕靠頁面右側顯示 --><el-button type="primary" @click="add()" style="float: right;"> + 新增部門</el-button> <br><br><!-- 數據展示表格 --><el-table :data="deptList" border style="width: 100%;"><el-table-column type="index" label="序號" width="100" align="center"/><el-table-column prop="name" label="部門名稱" width="300" align="center"/><el-table-column prop="updateTime" label="最后修改時間" width="300" align="center"/><el-table-column fixed="right" label="操作" align="center"><template #default="scope"><el-button size="small" @click="handleEdit(scope.row.id)">修改</el-button><el-button size="small" type="danger" @click="handleDelete(scope.row.id)">刪除</el-button></template></el-table-column></el-table><!-- 新增部門的對話框 --><el-dialog v-model="showDialog" :title="formTitle" width="30%" @close="resetForm"><el-form :model="deptForm" :rules="formRules" ref="deptFormRef"><el-form-item label="部門名稱" prop="name" label-width="80px"><el-input v-model="deptForm.name" autocomplete="off"></el-input></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button @click="showDialog = false">取消</el-button><el-button type="primary" @click="save">確定</el-button></span></template></el-dialog></template><style scoped></style>
2). 在 src/api/dept.js
中增加如下代碼
//添加部門
export const addDeptApi = (data) => request.post('/depts', data)
目前 src/api/dept.js
文件中完整代碼如下:
import request from '@/utils/request'//查詢全部部門
export const queryAllApi = () => request.get('/depts')//添加部門
export const addDeptApi = (data) => request.post('/depts', data)
打開瀏覽器進行測試,效果如下:
點擊保存之后,就會將數據保存到數據庫,并重新查詢出最新的數據,展示在頁面中。
4.2.2 表單校驗
Form 組件允許你驗證用戶的輸入是否符合規范,來幫助你找到和糾正錯誤。Form
組件提供了表單驗證的功能,只需為 rules
屬性傳入約定的驗證規則,并將 form-Item
的 prop
屬性設置為需要驗證的特殊鍵值即可。
操作步驟:
- 通過ref屬性注冊元素的引用。
- 定義表單校驗規則,通過rules屬性與表單綁定,并通過prop屬性綁定對應的表單項。
- 表單提交時,校驗表單,校驗通過,則允許提交表單。
4.3 修改部門
對于修改操作,通常會分為兩步進行:
- 查詢回顯
- 保存修改
交互邏輯:
- 點擊 編輯 按鈕,根據ID進行查詢,彈出對話框,完成頁面回顯展示。(查詢回顯)
- 點擊 確定 按鈕,保存修改后的數據,完成數據更新操作。(保存修改)
4.3.1 查詢回顯
1). 在 src/api/dept.js
中定義根據id查詢的請求
//根據ID查詢
export const queryInfoApi = (id) => request.get(`/depts/${id}`)
2). 在 src/views/dept/index.vue
中添加根據ID查詢回顯的邏輯
為修改按鈕綁定事件 <template></template>
:
<el-button size="small" @click="handleEdit(scope.row.id)">修改</el-button>
在 <script> </script>
添加JS邏輯:
// 編輯部門 - 根據ID查詢回顯數據
const handleEdit = async (id) => {console.log(`Edit item with ID ${id}`);formTitle.value = '修改部門'showDialog.value = truedeptForm.value = {name: ''}const result = await queryInfoApi(id)if(result.code){deptForm.value = result.data}
};
到目前為止,完整的 src/views/dept/index.vue
代碼如下:
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { queryAllApi, addDeptApi, queryInfoApi } from '@/api/dept'// 聲明列表展示數據
let deptList= ref([])// 動態加載數據 - 查詢部門
const queryAll = async () => {const result = await queryAllApi()deptList.value = result.data
}// 鉤子函數
onMounted(() => {queryAll()
})// 編輯部門 - 根據ID查詢回顯數據
const handleEdit = async (id) => {console.log(`Edit item with ID ${id}`);formTitle.value = '修改部門'showDialog.value = truedeptForm.value = {name: ''}const result = await queryInfoApi(id)if(result.code){deptForm.value = result.data}
};const formTitle = ref('')
//新增部門
const add = () => {formTitle.value = '新增部門'showDialog.value = truedeptForm.value = {name: ''}
}// 刪除部門 - 根據ID刪除部門
const handleDelete = (id) => {console.log(`Delete item with ID ${id}`);// 在這里實現刪除功能
};// 新增部門對話框的狀態
const showDialog = ref(false)
// 表單數據
const deptForm = ref({name: ''})
// 表單驗證規則
const formRules = ref({name: [{ required: true, message: '請輸入部門名稱', trigger: 'blur' },{ min: 2, max: 10, message: '長度在 2 到 10 個字符', trigger: 'blur' }]
})// 表單引用
const deptFormRef = ref(null)// 重置表單
const resetForm = () => {deptFormRef.value.resetFields()
}// 提交表單
const save = async () => {await deptFormRef.value.validate(async valid => {if (!valid) return// 提交表單const result = await addDeptApi(deptForm.value)if(result.code){ElMessage.success('操作成功')// 關閉對話框showDialog.value = false// 重置表單resetForm()// 重新加載數據queryAll()}else {ElMessage.error(result.msg)}})
}
</script><template><h1>部門管理</h1><!-- 按鈕靠頁面右側顯示 --><el-button type="primary" @click="add()" style="float: right;"> + 新增部門</el-button> <br><br><!-- 數據展示表格 --><el-table :data="deptList" border style="width: 100%;"><el-table-column type="index" label="序號" width="100" align="center"/><el-table-column prop="name" label="部門名稱" width="300" align="center"/><el-table-column prop="updateTime" label="最后修改時間" width="300" align="center"/><el-table-column fixed="right" label="操作" align="center"><template #default="scope"><el-button size="small" @click="handleEdit(scope.row.id)">修改</el-button><el-button size="small" type="danger" @click="handleDelete(scope.row.id)">刪除</el-button></template></el-table-column></el-table><!-- 新增部門的對話框 --><el-dialog v-model="showDialog" :title="formTitle" width="30%" @close="resetForm"><el-form :model="deptForm" :rules="formRules" ref="deptFormRef"><el-form-item label="部門名稱" prop="name" label-width="80px"><el-input v-model="deptForm.name" autocomplete="off"></el-input></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button @click="showDialog = false">取消</el-button><el-button type="primary" @click="save">確定</el-button></span></template></el-dialog></template><style scoped></style>
打開瀏覽器測試一下,根據ID查詢部門頁面回顯:
4.3.2 保存修改
由于 新增部門 和 修改部門使用的是同一個Dialog對話框,當前點擊 “確定” 按鈕的時候,有可能執行的是新增操作,也有可能是修改操作。
那應該如何辨別到底是新增,還是修改操作呢 ?
其實,我們只需要根據 deptForm
對象的id屬性值,來判斷即可。 如果沒有id,則是新增操作 ;如果有id,則是修改操作。
所以,保存修改功能實現如下:
1). 在 src/api/dept.js
中增加如下修改部門的請求
//修改部門
export const updateDeptApi = (data) => request.put('/depts', data)
2). 在 src/views/dept/index.vue
中引入上述函數,并完善(修改) save 函數的邏輯
import { queryAllApi, addDeptApi, queryInfoApi, updateDeptApi } from '@/api/dept'
// 提交表單
const save = async () => {await deptFormRef.value.validate(async valid => {if (!valid) return// 提交表單let result = null;if(deptForm.value.id){result = await updateDeptApi(deptForm.value) // 修改}else {result = await addDeptApi(deptForm.value) // 新增} if(result.code){ElMessage.success('操作成功')// 關閉對話框showDialog.value = false// 重置表單resetForm()// 重新加載數據queryAll()}else {ElMessage.error(result.msg)}})
}
最終完整代碼如下:
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { queryAllApi, addDeptApi, queryInfoApi, updateDeptApi } from '@/api/dept'// 聲明列表展示數據
let deptList = ref([])// 動態加載數據 - 查詢部門
const queryAll = async () => {const result = await queryAllApi()deptList .value = result.data
}// 鉤子函數
onMounted(() => {queryAll()
})const formTitle = ref('')
//新增部門
const add = () => {formTitle.value = '新增部門'showDialog.value = truedeptForm.value = {name: ''}
}// 編輯部門 - 根據ID查詢回顯數據
const handleEdit = async (id) => {console.log(`Edit item with ID ${id}`);formTitle.value = '修改部門'showDialog.value = truedeptForm.value = {name: ''}const result = await queryInfoApi(id)if(result.code){deptForm.value = result.data}
};// 刪除部門 - 根據ID刪除部門
const handleDelete = (id) => {console.log(`Delete item with ID ${id}`);// 在這里實現刪除功能
};// 新增部門對話框的狀態
const showDialog = ref(false)
// 表單數據
const deptForm = ref({name: ''})
// 表單驗證規則
const formRules = ref({name: [{ required: true, message: '請輸入部門名稱', trigger: 'blur' },{ min: 2, max: 10, message: '長度在 2 到 10 個字符', trigger: 'blur' }]
})// 表單引用
const deptFormRef = ref(null)// 重置表單
const resetForm = () => {deptFormRef.value.resetFields()
}// 提交表單
const save = async () => {await deptFormRef.value.validate(async valid => {if (!valid) return// 提交表單let result = null;if(deptForm.value.id){result = await updateDeptApi(deptForm.value) // 修改}else {result = await addDeptApi(deptForm.value) // 新增} if(result.code){ElMessage.success('操作成功')// 關閉對話框showDialog.value = false// 重置表單resetForm()// 重新加載數據queryAll()}else {ElMessage.error(result.msg)}})
}
</script><template><h1>部門管理</h1><!-- 按鈕靠頁面右側顯示 --><el-button type="primary" @click="add()" style="float: right;"> + 新增部門</el-button> <br><br><!-- 數據展示表格 --><el-table :data="deptList" border style="width: 100%;"><el-table-column type="index" label="序號" width="100" align="center"/><el-table-column prop="name" label="部門名稱" width="300" align="center"/><el-table-column prop="updateTime" label="最后修改時間" width="300" align="center"/><el-table-column fixed="right" label="操作" align="center"><template #default="scope"><el-button size="small" @click="handleEdit(scope.row.id)">修改</el-button><el-button size="small" type="danger" @click="handleDelete(scope.row.id)">刪除</el-button></template></el-table-column></el-table><!-- 新增部門的對話框 --><el-dialog v-model="showDialog" :title="formTitle" width="30%" @close="resetForm"><el-form :model="deptForm" :rules="formRules" ref="deptFormRef"><el-form-item label="部門名稱" prop="name" label-width="80px"><el-input v-model="deptForm.name" autocomplete="off"></el-input></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button @click="showDialog = false">取消</el-button><el-button type="primary" @click="save">確定</el-button></span></template></el-dialog></template><style scoped></style>
打開瀏覽器,測試修改員工功能:
4.4 刪除部門
- 點擊刪除按鈕,需要刪除當前這條數據,刪除完成之后,刷新頁面,展示出最新的數據。
- 由于刪除是一個比較危險的操作,為避免誤操作,通常會在點擊刪除之后,彈出確認框進行確認。
- Element 組件:ElMessageBox消息彈出框組件
具體操作步驟:
1). 在 src/api/dept.js
中增加如下刪除部門的請求
//刪除部門
export const deleteDeptApi = (id) => request.delete(`/depts?id=${id}`)
2). 在 src/views/dept/index.vue
中為 刪除 按鈕綁定事件
<el-button size="small" type="danger" @click="handleDelete(scope.row.id)">刪除</el-button>
3). 在 src/views/dept/index.vue
編寫根據ID刪除數據的函數 。
// 刪除部門 - 根據ID刪除部門
const handleDelete = (id) => {console.log(`Delete item with ID ${id}`);//刪除部門時, 需要彈出一個確認框, 如果是確認, 則刪除部門ElMessageBox.confirm('此操作將永久刪除該部門, 是否繼續?', '提示', {confirmButtonText: '確定',cancelButtonText: '取消',type: 'warning'}).then(async () => {// 刪除部門const result = await deleteDeptApi(id)if(result.code){ElMessage.success('刪除成功')queryAll()}})
};
注意上述,用到了ElementPlus中的 ElMessageBox組件,需要 import 導入進來。代碼如下:
import { ElMessage, ElMessageBox } from 'element-plus'
到目前為止,完整的 src/views/dept/index.vue
代碼如下:
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { queryAllApi, addDeptApi, queryInfoApi, updateDeptApi, deleteDeptApi } from '@/api/dept'// 聲明列表展示數據
let deptList= ref([])// 動態加載數據 - 查詢部門
const queryAll = async () => {const result = await queryAllApi()deptList.value = result.data
}// 鉤子函數
onMounted(() => {queryAll()
})const formTitle = ref('')
//新增部門
const add = () => {formTitle.value = '新增部門'showDialog.value = truedeptForm.value = {name: ''}
}// 編輯部門 - 根據ID查詢回顯數據
const handleEdit = async (id) => {console.log(`Edit item with ID ${id}`);formTitle.value = '修改部門'showDialog.value = truedeptForm.value = {name: ''}const result = await queryInfoApi(id)if(result.code){deptForm.value = result.data}
};// 刪除部門 - 根據ID刪除部門
const handleDelete = (id) => {console.log(`Delete item with ID ${id}`);//刪除部門時, 需要彈出一個確認框, 如果是確認, 則刪除部門ElMessageBox.confirm('此操作將永久刪除該部門, 是否繼續?', '提示', {confirmButtonText: '確定',cancelButtonText: '取消',type: 'warning'}).then(async () => {// 刪除部門const result = await deleteDeptApi(id)if(result.code){ElMessage.success('刪除成功')queryAll()}})
};// 新增部門對話框的狀態
const showDialog = ref(false)
// 表單數據
const deptForm = ref({name: ''})
// 表單驗證規則
const formRules = ref({name: [{ required: true, message: '請輸入部門名稱', trigger: 'blur' },{ min: 2, max: 10, message: '長度在 2 到 10 個字符', trigger: 'blur' }]
})// 表單引用
const deptFormRef = ref(null)// 重置表單
const resetForm = () => {deptFormRef.value.resetFields()
}// 提交表單
const save = async () => {await deptFormRef.value.validate(async valid => {if (!valid) return// 提交表單let result = null;if(deptForm.value.id){result = await updateDeptApi(deptForm.value) // 修改}else {result = await addDeptApi(deptForm.value) // 新增} if(result.code){ElMessage.success('操作成功')// 關閉對話框showDialog.value = false// 重置表單resetForm()// 重新加載數據queryAll()}else {ElMessage.error(result.msg)}})
}
</script><template><h1>部門管理</h1><!-- 按鈕靠頁面右側顯示 --><el-button type="primary" @click="add()" style="float: right;"> + 新增部門</el-button> <br><br><!-- 數據展示表格 --><el-table :data="deptList" border style="width: 100%;"><el-table-column type="index" label="序號" width="100" align="center"/><el-table-column prop="name" label="部門名稱" width="300" align="center"/><el-table-column prop="updateTime" label="最后修改時間" width="300" align="center"/><el-table-column fixed="right" label="操作" align="center"><template #default="scope"><el-button size="small" @click="handleEdit(scope.row.id)">修改</el-button><el-button size="small" type="danger" @click="handleDelete(scope.row.id)">刪除</el-button></template></el-table-column></el-table><!-- 新增部門的對話框 --><el-dialog v-model="showDialog" :title="formTitle" width="30%" @close="resetForm"><el-form :model="deptForm" :rules="formRules" ref="deptFormRef"><el-form-item label="部門名稱" prop="name" label-width="80px"><el-input v-model="deptForm.name" autocomplete="off"></el-input></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button @click="showDialog = false">取消</el-button><el-button type="primary" @click="save">確定</el-button></span></template></el-dialog></template><style scoped></style>
打開瀏覽器,測試刪除員工功能: