vite.config.ts
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
//配置Vite開發環境
export default defineConfig({
//使用vue外掛
plugins: [vue()],
//伺服器配置
server: {
//指定伺服器埠
port: 9090,
//配置代理,用於將本地伺服器的請求轉發到其他伺服器
proxy: {
//觸發代理的路徑字首
'/api': {
//指定請求轉發的目標伺服器地址
target: 'http://localhost:8080',
//改變請求的源,允許跨域請求
changeOrigin: true
}
}
}
})
Model8080.ts
//學生的基本資訊資料結構
export interface Student {
id: number,
name: string,
sex: string,
birthday: string
}
//新增和修改學生時需要傳入的資料結構
export interface StudentSave {
name: string,
sex: string,
birthday: string
}
//查詢學生時需要傳入的查詢條件
export interface StudentQuery {
name?: string,
sex?: string | null,
pageNum: number,
pageSize: number
}
//Spring框架返回的分頁結果資料結構
export interface SpringPage<T> {
code: number,
data: { records: T[], total: number },
message?: string
}
//Spring框架返回的字串結果資料結構
export interface SpringString {
data: string,
message?: string,
code: number
}
//匯入Axios庫中的AxiosResponse型別
import {AxiosResponse} from 'axios'
//Axios庫返回的分頁結果資料結構,繼承AxiosResponse,將SpringPage作為其響應體型別
export interface AxiosRespPage<T> extends AxiosResponse<SpringPage<T>> {
}
//Axios庫返回的字串結果資料結構,繼承AxiosResponse,將SpringString作為其響應體型別
export interface AxiosRespString extends AxiosResponse<SpringString> {
}
Student.vue
<template>
<a-row class="button-container">
<!-- 新增按鈕:點選觸發onAdd()函式,開啟新增學生資訊的模態框 -->
<a-button type="primary" @click="onAdd">新增</a-button>
<!-- 刪除選中按鈕:點選觸發onBatchRemove()函式,批次刪除選中的學生資訊 -->
<a-popconfirm title="確認要刪除選中學生嗎?" ok-text="確定" cancel-text="取消"
@confirm="onBatchRemove" @visibleChange="onVisibleChange" :visible="visible">
<a-button type="primary">刪除選中</a-button>
</a-popconfirm>
<!-- 姓名搜尋框 -->
<div class="input-container">
<a-input v-model:value="student.name" placeholder="姓名" :allowClear="true"></a-input>
</div>
<!-- 性別選擇框 -->
<div class="select-container">
<a-select v-model:value="student.sex" placeholder="性別" :allowClear="true">
<a-select-option value="男">男</a-select-option>
<a-select-option value="女">女</a-select-option>
</a-select>
</div>
<!-- 搜尋按鈕:點選觸發query()函式,在分頁、排序、篩選等操作後重新搜尋學生資訊 -->
<a-button class="search-button" @click="query" type="primary">搜尋</a-button>
</a-row>
<hr>
<!-- 學生資訊表格:columns屬性設定列配置,data-source屬性設定資料來源,row-key屬性設定行唯一標識,
pagination屬性設定分頁配置,@change事件繫結查詢函式query,:row-selection屬性設定行選擇配置 -->
<a-table :columns="columns" :data-source="students" row-key="id" :pagination="pagination"
@change="query" :row-selection="{selectedRowKeys:ids,onChange:onSelectChange}">
<!-- 在bodyCell列模板中,根據列的dataIndex判斷是否為"operation"列,如果是,則渲染修改和刪除按鈕 -->
<template #bodyCell="{column, record}">
<template v-if="column.dataIndex==='operation'">
<!-- 修改按鈕:點選觸發onEdit()函式,開啟修改學生資訊的模態框,並將選中的學生資訊傳遞給模態框 -->
<a @click="onEdit(record)">修改</a>
<a-divider type="vertical"></a-divider>
<!-- 刪除按鈕:點選觸發onRemove()函式,並傳遞要刪除的學生ID -->
<a-popconfirm title="確認要刪除該學生嗎?" ok-text="確定" cancel-text="取消" @confirm="onRemove(record.id)">
<a>刪除</a>
</a-popconfirm>
</template>
</template>
</a-table>
<!-- Save元件,用於顯示和編輯學生資訊的模態框,透過屬性和資料模型與父元件進行互動,並透過@saved事件觸發onSaved()方法 -->
<Save :id="id" :save="save" v-model:visible="saveVisible" @saved="onSaved"></Save>
</template>
<script setup lang="ts">
import axios from "axios";
import {ref, computed, reactive} from "vue";
import {usePagination, useRequest} from "vue-request";
import {AxiosRespPage, AxiosRespString, Student, StudentQuery} from "./model/Model8080";
import {PaginationProps} from "ant-design-vue";
import Save from "./Save.vue";
//新增、修改學生資訊的相關變數和函式
const saveVisible = ref(false) //定義saveVisible物件,用於儲存是否顯示儲存學生資訊的對話方塊的可見性狀態,初始值為false
const id = ref(0) //定義id物件,用於儲存要進行新增或修改的學生的ID,初始值為0
const save = reactive({name: '', sex: '', birthday: ''}) //定義save物件,用於儲存要進行新增或修改的學生資訊
//開啟新增學生資訊的模態框
function onAdd() {
saveVisible.value = true //將saveVisible的值設為true,以顯示模態框
id.value = 0 //將id的值設為0,用於標識新增的學生資訊
Object.assign(save, {name: '', sex: '', birthday: ''}) //使用Object.assign方法初始化save物件
}
//開啟修改學生資訊的模態框,並將選中的學生資訊傳遞給模態框
function onEdit(record: Student) {
saveVisible.value = true //將saveVisible的值設為true,以顯示模態框
id.value = record.id //將id的值賦給record.id,用於標識修改的學生資訊
Object.assign(save, record) //使用Object.assign方法將record物件的屬性和值複製給save物件,實現對save物件的更新
}
//在儲存學生資訊後呼叫,用於重新搜尋學生資訊
function onSaved() {
search(student.value)
}
//分頁、搜尋學生資訊的相關變數和函式
const student = ref({pageNum: 1, pageSize: 5, name: '', sex: null, birthday: null})
//呼叫usePagination函式實現分頁
const {data, total, run: search} = usePagination<AxiosRespPage<Student>, StudentQuery[]>(
(records) => axios.get('/api/student/findByPage', {params: records}),
{
defaultParams: [student.value], //指定請求分頁資料時需要傳遞的預設引數
pagination: {
currentKey: "pageNum", //當前頁碼
pageSizeKey: 'pageSize', //每頁顯示條數
totalKey: 'data.data.totalRow' //總記錄數
}
}
)
//在分頁、排序、篩選等操作後重新搜尋學生資訊
function query(pagination: PaginationProps) {
student.value.pageNum = pagination.current ?? 1 //當前頁碼
student.value.pageSize = pagination.pageSize ?? 5 //每頁顯示條數
search(student.value)
}
//計算分頁控制元件的配置
const pagination = computed<PaginationProps>(() => {
return {
current: student.value.pageNum, //當前頁碼
pageSize: student.value.pageSize, //每頁顯示條數
total: total.value, //總記錄數
showSizeChanger: true, //每頁顯示條數的下拉選單
pageSizeOptions: ["5", "10", "15", "20"] //自定義下拉選單內容
}
})
//計算學生列表
const students = computed(() => {
return data.value?.data.data.records || [] //獲取資料,如果該值存在則返回,否則返回一個空陣列
})
//刪除學生資訊的相關函式
async function onRemove(id: number) {
await remove(id) //呼叫remove函式,刪除指定ID的學生資訊,等待其返回結果
search(student.value)
}
//刪除學生資訊非同步請求
const {runAsync: remove} = useRequest<AxiosRespString, number[]>(
(id) => axios.delete(`/api/student/remove/${id}`),
{
manual: true //請求需要手動觸發
}
)
//批次刪除選中的學生資訊
const ids = ref<number[]>([]) //定義ids物件,用於儲存選中的學生ID列表
//當選擇的學生髮生變化時,更新ids的值為新的學生ID列表
function onSelectChange(keys: number[]) {
ids.value = keys
}
//呼叫batchRemove介面刪除選中的學生,然後清空ids的值,並重新搜尋學生
async function onBatchRemove() {
await batchRemove(ids.value)
ids.value = []
search(student.value)
}
const {runAsync: batchRemove} = useRequest<AxiosRespString, number[][]>(
(ids) => axios.delete(`/api/student/batchRemove/${ids.join(',')}`),
{
manual: true //請求需要手動觸發
}
)
const visible = ref(false) //關閉批次刪除學生資訊的模態框
//控制刪除選中學生的模態框的顯示與隱藏
function onVisibleChange(v: boolean) {
if (!v) { //隱藏
visible.value = false
} else { //顯示
visible.value = ids.value.length > 0
}
}
//表格列的定義
const columns = ref([
{
title: "編號",
dataIndex: "id",
},
{
title: "姓名",
dataIndex: "name",
},
{
title: "性別",
dataIndex: "sex",
},
{
title: "生日",
dataIndex: "birthday",
},
{
title: '操作',
dataIndex: 'operation'
}
]);
</script>
<style scoped> /* 樣式僅應用於當前元件的元素 */
.button-container {
display: flex;
align-items: center;
}
.input-container {
display: flex;
align-items: center;
margin-left: 15px; /* 調整與按鈕的間距 */
}
.select-container {
display: flex;
align-items: center;
margin-left: 15px; /* 適當調整與輸入框的間距 */
}
.search-button {
margin-left: 15px; /* 調整與選擇框的間距 */
}
button {
margin: 5px; /* 調整按鈕的間距 */
}
</style>
Save.vue
<template>
<!-- 模態框元件,用於展示和編輯使用者資訊 -->
<a-modal :visible="visible" :title="title" @ok="onOk" @cancel="onCancel">
<a-form>
<!-- 如果存在使用者ID,則顯示使用者ID輸入框,此處僅用於展示,不可編輯 -->
<a-form-item label="編號" v-if="id">
<a-input v-bind:value="id" readonly></a-input>
</a-form-item>
<!-- 姓名輸入框,帶有表單驗證資訊 -->
<a-form-item label="姓名" v-bind="validateInfos.name">
<a-input v-model:value="save.name"></a-input>
</a-form-item>
<!-- 性別選擇器,支援選擇男性或女性 -->
<a-form-item label="性別" v-bind="validateInfos.sex">
<a-radio-group v-model:value="save.sex">
<a-radio-button value="男">男</a-radio-button>
<a-radio-button value="女">女</a-radio-button>
</a-radio-group>
</a-form-item>
<!-- 生日選擇器,支援選擇日期,繫結表單驗證資訊 -->
<a-form-item label="生日" v-bind="validateInfos.birthday">
<a-date-picker v-model:value="save.birthday" value-format="YYYY-MM-DD"></a-date-picker>
</a-form-item>
</a-form>
</a-modal>
</template>
<script setup lang="ts">
import axios from "axios";
import {ref, computed} from "vue";
import {useRequest} from "vue-request";
import {AxiosRespString, StudentSave} from "./model/Model8080";
import {Form} from 'ant-design-vue'
//定義元件接收的props
const props = defineProps<{ id: number, save: StudentSave, visible: boolean }>()
//透過props中的id值決定顯示的標題
const title = computed(() => props.id === 0 ? '新增學生' : '修改學生')
//定義元件向父元件傳送的事件
const emit = defineEmits(['update:visible', 'saved'])
//點選確認按鈕時的處理邏輯
async function onOk() {
try {
//提交前校驗
await validate()
//判斷是新增還是編輯
if (props.id === 0) {
await add(props.save)
} else {
await edit(props.save)
}
//通知父元件儲存成功,並關閉當前元件
emit('saved')
emit('update:visible', false)
} catch (e) {
console.error(e)
}
}
//點選取消按鈕時的邏輯:關閉當前元件
function onCancel() {
emit('update:visible', false)
}
//新增學生資訊非同步請求
const {runAsync: add} = useRequest<AxiosRespString, StudentSave[]>(
(student) => axios.post('/api/student/add', student),
{
manual: true //手動觸發請求
}
)
//編輯學生資訊非同步請求
const {runAsync: edit} = useRequest<AxiosRespString, StudentSave[]>(
(student) => axios.put('/api/student/edit', student),
{
manual: true //手動觸發請求
}
)
//定義表單校驗規則
const rules = ref({
name: [
{required: true, message: '請輸入姓名'},
{min: 2, message: '字元數至少為2'}
],
sex: [
{required: true, message: '請選擇性別'}
],
birthday: [
{required: true, message: '請選擇生日'}
]
})
//使用Ant Design Vue的Form.useForm方法將表單和校驗規則進行繫結,獲取校驗資訊和校驗函式
const {validateInfos, validate} = Form.useForm(props.save, rules)
</script>