??前言
書接上回,上一篇文章介紹了一個(gè)基于 Vue3
和 ElementPlus
的聯(lián)系人列表管理后臺(tái)小 demo (Vue3 + ElementPlus實(shí)戰(zhàn)學(xué)習(xí)——模擬簡(jiǎn)單的聯(lián)系人列表管理后臺(tái)),在有了上一篇文章的基礎(chǔ)上,我們?cè)囍?axios
來(lái)獲取數(shù)據(jù),而不是用寫死的數(shù)據(jù),然后用 Node.js + Vue3 + ElementPlus
來(lái)實(shí)現(xiàn)聯(lián)系人列表管理后臺(tái)的 demo 。功能包括功能包括了數(shù)據(jù)的展示、添加功能、編輯功能、刪除功能以及列表分頁(yè)功能。
??demo 介紹
通過(guò)上一篇文章的鋪墊,我們可以繼續(xù)使用那套布局,因此使用到的 ElementPlus
組件也是基本上一樣的。功能包括了數(shù)據(jù)的展示、編輯功能、刪除功能以及列表分頁(yè)功能,在這基礎(chǔ)上,新添加了一個(gè)添加聯(lián)系人的功能。
關(guān)于后臺(tái)數(shù)據(jù)方面,不同于上一篇文章,這里使用到 Node.js
模擬后端接口環(huán)境。因此需要用到 axios
來(lái)獲取數(shù)據(jù),然后展示出來(lái)。
接下來(lái)我們來(lái)分析每個(gè)功能具體如何實(shí)現(xiàn),以及核心代碼,首先我們可以看到一些效果圖如下。
添加聯(lián)系人的模塊。
編輯聯(lián)系人的彈窗。
刪除聯(lián)系人的彈窗。
??后端與接口的調(diào)試
??關(guān)于運(yùn)行后端項(xiàng)目
在搭建項(xiàng)目之前,我們要先把 Node 后端的數(shù)據(jù)和接口處理好,首先把后端接口跑起來(lái),然后再進(jìn)行接口的測(cè)試。
這里有兩種方法運(yùn)行后端接口,第一種是通過(guò) cmd
來(lái)運(yùn)行,第二種是直接在 vscode
上面跑。這里我認(rèn)為在 vscode
上面跑比較方便,有功能的上的需求可以直接修改,同時(shí)也可以手動(dòng)修改 JSON
數(shù)據(jù)。接下來(lái)我們來(lái)用這兩種方法來(lái)運(yùn)行一下。
首先看一下 Node 后端項(xiàng)目的目錄結(jié)構(gòu),我們可以看到這個(gè)后端的 api
,包含非常經(jīng)典的增刪查改功能,同時(shí)也是本次項(xiàng)目使用到的接口名稱。然后 index.js
是這個(gè)后端項(xiàng)目的入口文件,運(yùn)行只需要把 index.js
跑起來(lái)即可,通過(guò) node index
來(lái)運(yùn)行后端接口程序。
我們先用 cmd
來(lái)跑一次,在該項(xiàng)目文件的目錄欄輸入 cmd
,打開(kāi) cmd
窗口,然后輸入 node index
,回車運(yùn)行。如果出現(xiàn) http://127.0.0.1:9999/api/select 地址,就代表后端接口運(yùn)行起來(lái)了。
然后我們?cè)僭囋嚳从?vscode
來(lái)跑,打開(kāi)終端(快捷鍵:Ctrl + ~ ),輸入 node index
,回車運(yùn)行。如果出現(xiàn) http://127.0.0.1:9999/api/select 地址,就代表后端接口運(yùn)行起來(lái)了。
??關(guān)于接口的調(diào)試
后端接口運(yùn)行成功后,出現(xiàn)的 http://127.0.0.1:9999/api/select 這個(gè)地址,我們可以通過(guò)瀏覽器或相關(guān)軟件(這里用到的是 postman)來(lái)測(cè)試一下這個(gè)接口。(圖一為瀏覽器測(cè)試、圖二為 postman 測(cè)試)
我們可以看到返回的是一個(gè) JSON 格式數(shù)據(jù),每請(qǐng)求一次這個(gè)地址,我們都可以看到終端出現(xiàn)查詢結(jié)果的提示,這里我們請(qǐng)求了兩次。
最后要注意,在開(kāi)始寫前端后臺(tái)的代碼時(shí),要保持后端接口的運(yùn)行,如果關(guān)閉了這個(gè)終端或者 Ctrl + c
了,這個(gè)后端接口服務(wù)會(huì)終止,導(dǎo)致數(shù)據(jù)獲取不到。然后接下來(lái)我們來(lái)具體看看如何實(shí)現(xiàn)這個(gè)后臺(tái)項(xiàng)目。
??功能分析
在分析具體功能之前,我們先看看這個(gè)后臺(tái)的布局和設(shè)計(jì)。首先數(shù)據(jù)展示跟上一篇文章的基本上一模一樣,都是實(shí)現(xiàn)分頁(yè)功能,每頁(yè)展示五條數(shù)據(jù),操作包括了編輯和刪除功能,不同的是多了個(gè)添加聯(lián)系人的按鈕功能,因此還新增一個(gè)添加聯(lián)系人的窗口。
然后我們分析一下這個(gè)后臺(tái)要用到的 Element 組件,比如有 el-row
、el-button
、el-form
、el-card
、el-dialog
、el-table
、el-pagination
等等。
這個(gè)后臺(tái)的 template 部分結(jié)構(gòu)很簡(jiǎn)單,只包括了數(shù)據(jù)列表和彈窗,通過(guò) el-card
、el-table
、el-button
實(shí)現(xiàn)數(shù)據(jù)列表,el-dialog
、el-form
、el-button
實(shí)現(xiàn)彈窗的部分。
介紹完基本的布局和設(shè)計(jì)思路,我們來(lái)看看具體的功能有哪些,又是怎么樣實(shí)現(xiàn)的。
??數(shù)據(jù)的展示與分頁(yè)功能
上面我們也把后端接口服務(wù)跑起來(lái)了,同時(shí)也測(cè)試了接口的數(shù)據(jù)。接下來(lái)我們先通過(guò) axios 來(lái)異步獲取數(shù)據(jù),我們可以通過(guò)下圖的代碼來(lái)獲取數(shù)據(jù)。
通過(guò) console.log(contactList.list);
我們可以在控制臺(tái)看到成功獲取到了數(shù)據(jù)。
接下來(lái)我們用相關(guān)的組件布局好初始的頁(yè)面,如下圖(無(wú)數(shù)據(jù)時(shí))。
從上到下首先是標(biāo)題,然后是添加聯(lián)系人的按鈕,這里用到了兩個(gè) el-row
來(lái)區(qū)分這兩個(gè)模塊。然后是數(shù)據(jù)列表的部分,列表包括了 id 、姓名、電話以及相關(guān)操作,操作包括了編輯功能和刪除功能的按鈕。
如上圖代碼截圖所示,這里使用到了 el-card
、el-table
等相關(guān)組件來(lái)完成布局。然后是數(shù)據(jù)列表的分頁(yè),這里用到的是 el-pagination
實(shí)現(xiàn)分頁(yè)的功能,五條數(shù)據(jù)為一頁(yè)。
其中 table 中的 data 是 pageData
,我們對(duì)其的 id 進(jìn)行了處理,為了保證其 id 是按照遞增的順序展示出來(lái),而不是每一頁(yè)都是 1-5 的排序。其中還有一個(gè)特點(diǎn)就是,通過(guò)后端接口插入的數(shù)據(jù)顯示的 id 是時(shí)間戳,因?yàn)槲覀儾灰欢ㄖ雷詈蟛迦氲哪菞l數(shù)據(jù)的 id 是多少,也沒(méi)必要去特地看一眼,然后才接著插入數(shù)據(jù)。因此我們默認(rèn)插入的 id 是時(shí)間戳,然后再對(duì) id 進(jìn)行處理。
??添加功能
相比上一篇文章的 demo 功能,這個(gè)項(xiàng)目多了一個(gè)添加聯(lián)系人的功能,我們通過(guò)后端寫好的 insert
接口來(lái)實(shí)現(xiàn)添加,我們只需要在使用 axios
獲取數(shù)據(jù)的時(shí)候,使用到這個(gè)接口,以及傳遞對(duì)應(yīng)的數(shù)據(jù)給后端即可實(shí)現(xiàn)添加功能,接下來(lái)我們看看 Node 代碼。
其中紅框的部分是一些對(duì)添加的數(shù)據(jù)的判斷邏輯,然后就是把新傳遞的參數(shù)寫入 JSON
格式數(shù)據(jù),最后返回結(jié)果信息。接下來(lái)我們通過(guò) post
請(qǐng)求以及寫好的添加聯(lián)系人模塊來(lái)添加一條數(shù)據(jù)。
在添加聯(lián)系人窗口輸入數(shù)據(jù),然后點(diǎn)擊確認(rèn)添加。
然后我們可以在終端看到添加成功的反饋,以及 JSON
數(shù)據(jù)中出現(xiàn)了剛剛新添加的數(shù)據(jù)。
然后在后臺(tái)的聯(lián)系人列表也可以看到。
通過(guò)上面的一系列操作,我們實(shí)現(xiàn)了添加聯(lián)系人的功能,其中添加聯(lián)系人的窗口這里沒(méi)用使用彈窗的形式,而是使用卡片嵌套表單的形式。為了使添加聯(lián)系人成功后會(huì)自動(dòng)刷新列表,方便數(shù)據(jù)的顯示,這里我們要把通過(guò) axios
異步獲取數(shù)據(jù)的方法封裝到一個(gè)函數(shù),然后掛載到 onMounted
函數(shù),這樣就方便在任意地方調(diào)用了。
??編輯功能
這下來(lái)我們來(lái)看看編輯功能,通過(guò)點(diǎn)擊編輯按鈕,然后出現(xiàn)彈窗,對(duì)數(shù)據(jù)進(jìn)行修改和保持,這里使用到了 el-dialog
和 ElMessage
實(shí)現(xiàn)窗口的出現(xiàn)、隱藏以及一些交互效果(消息框)。
關(guān)于編輯功能,我們通過(guò)后端寫好的 update
接口來(lái)實(shí)現(xiàn)編輯功能,我們只需要在使用 axios
獲取數(shù)據(jù)的時(shí)候,使用到這個(gè)接口,以及傳遞對(duì)應(yīng)的數(shù)據(jù)給后端即可實(shí)現(xiàn)編輯功能,接下來(lái)我們看看 Node 代碼。
其中紅框的部分是一些對(duì)編輯的數(shù)據(jù)的判斷邏輯,然后就是把新傳遞的參數(shù)重新修改代替原本的數(shù)據(jù),然后寫入 JSON
格式數(shù)據(jù),最后返回結(jié)果信息。接下來(lái)我們通過(guò) post
請(qǐng)求以及寫好的編輯模塊來(lái)編輯剛剛新添加的那一條數(shù)據(jù)。
找到剛剛新添加的那條數(shù)據(jù),然后點(diǎn)擊編輯按鈕,把姓名測(cè)試添加改成測(cè)試編輯。
然后我們可以在終端看到 id:xxx 編輯成功的反饋,以及 JSON
數(shù)據(jù)中更新了剛剛編輯的數(shù)據(jù)。
通過(guò)上面的一系列操作,我們實(shí)現(xiàn)了編輯功能,這里的編輯功能模塊就是用了彈窗的形式,不同于添加聯(lián)系人功能模塊。
??刪除功能
最后我們來(lái)看看刪除功能,通過(guò)點(diǎn)擊刪除按鈕,然后出現(xiàn)是否確認(rèn)刪除的彈窗,這里使用到了 ElMessageBox
實(shí)現(xiàn)彈窗的出現(xiàn)以及確認(rèn)、取消的交互效果。
關(guān)于刪除功能,我們通過(guò)后端寫好的 delete
接口來(lái)實(shí)現(xiàn)刪除功能,我們只需要在使用 axios
獲取數(shù)據(jù)的時(shí)候,使用到這個(gè)接口,以及傳遞聯(lián)系人的 id 給后端即可實(shí)現(xiàn)刪除功能,接下來(lái)我們看看 Node 代碼和 js 代碼。
然后找到剛剛編輯的那條數(shù)據(jù),然后點(diǎn)擊刪除按鈕,刪除這條數(shù)據(jù)。
然后我們可以在終端看到 id:xxx 刪除功的反饋,以及 JSON
數(shù)據(jù)中刪除這條數(shù)據(jù)。
通過(guò)上面的一系列操作,我們實(shí)現(xiàn)了刪除功能。至此這個(gè)后臺(tái)的功能已經(jīng)基本全部實(shí)現(xiàn)了,上述的功能分析以及代碼分析的完整內(nèi)容還需要到具體的源碼中去編寫、瀏覽了才能體驗(yàn)的到了,最后附上完整代碼,供大家參考和學(xué)習(xí)。
??完整代碼
<template>
<div class="contact-list">
<!-- 標(biāo)題 -->
<el-row justify="center">
<h1 style="text-align: center">Node.js 聯(lián)系人列表管理后臺(tái)</h1>
</el-row>
<!-- 添加聯(lián)系人按鈕 -->
<el-row justify="center">
<el-button type="primary" style="text-align: center" @click="addShowForm">添加聯(lián)系人</el-button>
</el-row>
<br />
<!-- 添加聯(lián)系人表單窗口 -->
<el-card class="add-card" v-if="addFormVisible">
<el-row justify="center">
<h1 style="text-align: center">添加</h1>
</el-row>
<el-form :model="formData" label-width="60px" style="text-align: center">
<el-col :sm="{ span: 12, offset: 5 }" :xs="{ span: 24 }">
<el-form-item label="姓名" prop="name">
<el-input v-model="formData.name" placeholder="請(qǐng)輸入聯(lián)系人姓名"></el-input>
</el-form-item>
</el-col>
<el-col :sm="{ span: 12, offset: 5 }">
<el-form-item label="電話" prop="tel">
<el-input v-model="formData.tel" placeholder="請(qǐng)輸入聯(lián)系人電話"></el-input>
</el-form-item>
</el-col>
<el-col>
<el-button type="primary" @click="addContact">確認(rèn)添加</el-button>
<el-button @click="addFormVisible = false">取 消</el-button>
<el-button @click="refreshFormData()">清空</el-button>
</el-col>
</el-form>
</el-card>
<!-- 原編輯聯(lián)系人表單窗口 -->
<!-- <el-card class="edit-card" v-if="editFormVisible">
<el-row justify="center">
<h1 style="text-align: center">編輯</h1>
</el-row>
<el-form :model="row" label-width="60px" style="text-align: center">
<el-col :sm="{ span: 12, offset: 5 }" :xs="{ span: 24 }">
<el-form-item label="姓名" prop="name">
<el-input v-model="row"></el-input>
</el-form-item>
</el-col>
<el-col :sm="{ span: 12, offset: 5 }">
<el-form-item label="電話" prop="tel">
<el-input v-model="row"></el-input>
</el-form-item>
</el-col>
<el-col>
<el-button type="primary" @click="editContact">確認(rèn)修改</el-button>
<el-button @click="editFormVisible = false">取 消</el-button>
</el-col>
</el-form>
</el-card> -->
<!-- 編輯聯(lián)系人dialog窗口 -->
<el-dialog title="編輯" v-model="editFormVisible" width="30%">
<el-form :model="formData">
<el-form-item label="姓名">
<el-input v-model="formData.name"></el-input>
</el-form-item>
<el-form-item label="電話">
<el-input v-model="formData.tel"></el-input>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="editFormVisible = false">取消</el-button>
<el-button type="primary" @click="editContact()">保存</el-button>
</template>
</el-dialog>
<!-- 聯(lián)系人數(shù)據(jù)表格 -->
<el-card class="list-card">
<el-table :data="pagedData" empty-text="暫無(wú)聯(lián)系人" stripe>
<el-table-column prop="virtualId" label="id" width="60" align="center"></el-table-column>
<el-table-column prop="name" label="姓名" align="center"></el-table-column>
<el-table-column prop="tel" label="電話" align="center"></el-table-column>
<el-table-column label="操作" width="150" align="center">
<template #default="{ row }">
<el-button size="small" @click="editShowForm(row)">編輯</el-button>
<el-button type="primary" size="small" @click="delContact(row)">刪除</el-button>
</template>
</el-table-column>
</el-table>
<el-row style="margin-top: 20px">
<el-col :span="24">
<el-pagination v-model:current-page="currentPage" :page-size="5" layout="prev, pager, next"
:total="contactList.list.length" @current-change="handleCurrentChange"></el-pagination>
</el-col>
</el-row>
</el-card>
</div>
</template>
<script setup>
// import { ElMessage, ElMessageBox } from "element-plus";
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import axios from "axios";
// 給json數(shù)據(jù)定義一個(gè)list
const contactList = reactive({
list: [],
});
// 當(dāng)前頁(yè)數(shù)
const currentPage = ref(1);
// 當(dāng)前頁(yè)數(shù)變化時(shí)的回調(diào)函數(shù)
const handleCurrentChange = (val) => {
currentPage.value = val;
};
// 分頁(yè)后的數(shù)據(jù)
// const pagedData = computed(() => {
// const start = (currentPage.value - 1) * 5;
// const end = start + 5;
// return contactList.list.slice(start, end);
// });
const pagedData = computed(() => {
let start = (currentPage.value - 1) * 5;
const end = start + 5;
const res = contactList.list.slice(start, end);
for (let data of res) {
data.virtualId = ++start;
}
return res;
});
// axios默認(rèn)數(shù)據(jù)
const instance = axios.create({
baseURL: "http://127.0.0.1:9999/api/",
timeout: 10000,
});
// 在組件掛載完畢后調(diào)用 refreshList 函數(shù)
onMounted(() => {
// instance.get("select").then((res) => {
// contactList.list = res.data.data.data;
// console.log(contactList.list);
// });
refreshList();
});
// 封裝這個(gè)方法,方便在任意地方調(diào)用
const refreshList = async () => {
await instance
.get("select")
.then((res) => {
if (res.data.code === 200) {
contactList.list = res.data.data.data;
console.log(contactList.list);
} else {
ElMessage({
message: res.data.message,
grouping: true,
type: "error",
});
}
})
.catch((err) => {
ElMessage({
message: err,
grouping: true,
type: "error",
});
});
};
// 表單的數(shù)據(jù)
const formData = reactive({
id: "",
name: "",
tel: "",
});
// 清空f(shuō)ormData數(shù)據(jù)
const refreshFormData = () => {
formData.id = "";
formData.name = "";
formData.tel = "";
};
// 添加聯(lián)系人模塊
const addFormVisible = ref(false);
const addShowForm = () => {
addFormVisible.value = true;
};
const addContact = () => {
// console.log(formData);
const params = {
name: formData.name,
tel: formData.tel,
};
instance
.post("insert", params)
.then((res) => {
if (res.data.code === 200) {
ElMessage({
message: "添加成功!",
grouping: true,
type: "success",
});
refreshList();
refreshFormData();
} else {
ElMessage({
message: res.data.message,
grouping: true,
type: "error",
});
}
})
.catch((err) => {
ElMessage({
message: err,
grouping: true,
type: "error",
});
});
};
// 編輯聯(lián)系人模塊
const editFormVisible = ref(false);
const editShowForm = (row) => {
formData.id = row.id;
formData.name = row.name;
formData.tel = row.tel;
editFormVisible.value = true;
};
const editContact = () => {
const params = {
id: formData.id,
name: formData.name,
tel: formData.tel,
};
console.log(params);
// .then(() => {
instance
.post("update", params)
.then((res) => {
if (res.data.code === 200) {
ElMessage({
message: "編輯成功!",
grouping: true,
type: "success",
});
editFormVisible.value = false;
refreshList();
} else {
ElMessage({
message: res.data.message,
grouping: true,
type: "error",
});
}
})
.catch((err) => {
ElMessage({
message: err,
grouping: true,
type: "error",
});
});
// })
// .catch(() => {
// ElMessage({
// type: "info",
// message: "已取消編輯!",
// });
// });
};
// 刪除聯(lián)系人模塊
const delContact = (row) => {
console.log(row);
ElMessageBox.confirm(`確定要?jiǎng)h除聯(lián)系人${row.name}嗎`, "Warning", {
confirmButtonText: "確認(rèn)",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
instance
.delete("delete?id=" + row.id)
.then((res) => {
if (res.data.code === 200) {
ElMessage({
type: "success",
message: "刪除成功!",
});
refreshList();
} else {
ElMessage({
message: res.data.message,
grouping: true,
type: "error",
});
}
})
.catch((err) => {
ElMessage({
message: err,
grouping: true,
type: "error",
});
});
})
.catch(() => {
ElMessage({
type: "info",
message: "已取消刪除!",
});
});
};
</script>
<style scoped>
.contact-list {
max-width: 800px;
margin: auto;
padding: 20px;
}
.add-card {
margin-bottom: 20px;
}
.list-card {
overflow-x: auto;
}
el-form {
margin: 0 auto;
}
/* el-dialog 遮罩突然變黑問(wèn)題解決 */
.v-modal {
opacity: 0.5 !important;
background: rgba(0, 0, 0, 0.5) !important;
}
@media (max-width: 768px) {
.add-card {
width: 100%;
box-shadow: none;
border-radius: 0;
}
.list-card {
width: 100%;
box-shadow: none;
border-radius: 0;
}
el-dialog {
--el-dialog-width: 80%;
}
}
@media only screen and (min-width: 768px) {
.el-col-sm-offset-1 {
margin-left: 0;
}
}
</style>
如果需要 Node 后端代碼,或者完整項(xiàng)目的,可以私信或者在評(píng)論區(qū)留言,我會(huì)第一時(shí)間回復(fù)。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-492390.html
??最后
通過(guò)這篇文章的實(shí)戰(zhàn)進(jìn)階學(xué)習(xí),我們可以學(xué)會(huì)一個(gè)基于 Vue3 + Node.js + ElementPlus
實(shí)現(xiàn)的聯(lián)系人列表管理后臺(tái)的 demo,同時(shí)對(duì) axios
的使用更上一層樓,文章中的 Node 不是本次項(xiàng)目的重點(diǎn),這篇文章重點(diǎn)是練習(xí) axios 多方面的用法,因此后續(xù)會(huì)講講、記錄一下 axios 進(jìn)一步封裝的操作。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-492390.html
到了這里,關(guān)于axios實(shí)戰(zhàn)進(jìn)階練習(xí)——基于 Vue3 + Node.js + ElementPlus 實(shí)現(xiàn)的聯(lián)系人列表管理后臺(tái)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!