axios 实战进阶练习——基于 Vue3 + Node.js + ElementPlus 实现的联系人列表管理后台

32 篇文章 12 订阅
4 篇文章 1 订阅


在这里插入图片描述

📋前言

书接上回,上一篇文章介绍了一个基于 Vue3ElementPlus 的联系人列表管理后台小 demo (Vue3 + ElementPlus实战学习——模拟简单的联系人列表管理后台),在有了上一篇文章的基础上,我们试着用 axios 来获取数据,而不是用写死的数据,然后用 Node.js + Vue3 + ElementPlus 来实现联系人列表管理后台的 demo 。功能包括功能包括了数据的展示、添加功能、编辑功能、删除功能以及列表分页功能。


🎯demo 介绍

通过上一篇文章的铺垫,我们可以继续使用那套布局,因此使用到的 ElementPlus 组件也是基本上一样的。功能包括了数据的展示、编辑功能、删除功能以及列表分页功能,在这基础上,新添加了一个添加联系人的功能。

关于后台数据方面,不同于上一篇文章,这里使用到 Node.js 模拟后端接口环境。因此需要用到 axios 来获取数据,然后展示出来。

接下来我们来分析每个功能具体如何实现,以及核心代码,首先我们可以看到一些效果图如下。
在这里插入图片描述
添加联系人的模块。
在这里插入图片描述
编辑联系人的弹窗。
在这里插入图片描述
删除联系人的弹窗。
在这里插入图片描述


🎯后端与接口的调试

🧩关于运行后端项目

在搭建项目之前,我们要先把 Node 后端的数据和接口处理好,首先把后端接口跑起来,然后再进行接口的测试。

这里有两种方法运行后端接口,第一种是通过 cmd 来运行,第二种是直接在 vscode 上面跑。这里我认为在 vscode 上面跑比较方便,有功能的上的需求可以直接修改,同时也可以手动修改 JSON 数据。接下来我们来用这两种方法来运行一下。

首先看一下 Node 后端项目的目录结构,我们可以看到这个后端的 api ,包含非常经典的增删查改功能,同时也是本次项目使用到的接口名称。然后 index.js 是这个后端项目的入口文件,运行只需要把 index.js 跑起来即可,通过 node index 来运行后端接口程序。
在这里插入图片描述
我们先用 cmd 来跑一次,在该项目文件的目录栏输入 cmd ,打开 cmd 窗口,然后输入 node index ,回车运行。如果出现 http://127.0.0.1:9999/api/select 地址,就代表后端接口运行起来了。
在这里插入图片描述
在这里插入图片描述
然后我们再试试看用 vscode 来跑,打开终端(快捷键:Ctrl + ~ ),输入 node index ,回车运行。如果出现 http://127.0.0.1:9999/api/select 地址,就代表后端接口运行起来了。
在这里插入图片描述

🧩关于接口的调试

后端接口运行成功后,出现的 http://127.0.0.1:9999/api/select 这个地址,我们可以通过浏览器或相关软件(这里用到的是 postman)来测试一下这个接口。(图一为浏览器测试、图二为 postman 测试)
在这里插入图片描述
在这里插入图片描述
我们可以看到返回的是一个 JSON 格式数据,每请求一次这个地址,我们都可以看到终端出现查询结果的提示,这里我们请求了两次。
在这里插入图片描述
最后要注意,在开始写前端后台的代码时,要保持后端接口的运行,如果关闭了这个终端或者 Ctrl + c 了,这个后端接口服务会终止,导致数据获取不到。然后接下来我们来具体看看如何实现这个后台项目。


🎯功能分析

在分析具体功能之前,我们先看看这个后台的布局和设计。首先数据展示跟上一篇文章的基本上一模一样,都是实现分页功能,每页展示五条数据,操作包括了编辑和删除功能,不同的是多了个添加联系人的按钮功能,因此还新增一个添加联系人的窗口。

然后我们分析一下这个后台要用到的 Element 组件,比如有 el-rowel-buttonel-formel-cardel-dialogel-tableel-pagination 等等。

这个后台的 template 部分结构很简单,只包括了数据列表和弹窗,通过 el-cardel-tableel-button 实现数据列表,el-dialogel-formel-button 实现弹窗的部分。
在这里插入图片描述
介绍完基本的布局和设计思路,我们来看看具体的功能有哪些,又是怎么样实现的。

🧩数据的展示与分页功能

上面我们也把后端接口服务跑起来了,同时也测试了接口的数据。接下来我们先通过 axios 来异步获取数据,我们可以通过下图的代码来获取数据。
在这里插入图片描述
通过 console.log(contactList.list); 我们可以在控制台看到成功获取到了数据。
在这里插入图片描述
接下来我们用相关的组件布局好初始的页面,如下图(无数据时)。
在这里插入图片描述
从上到下首先是标题,然后是添加联系人的按钮,这里用到了两个 el-row 来区分这两个模块。
在这里插入图片描述然后是数据列表的部分,列表包括了 id 、姓名、电话以及相关操作,操作包括了编辑功能和删除功能的按钮。
在这里插入图片描述
在这里插入图片描述
如上图代码截图所示,这里使用到了 el-cardel-table 等相关组件来完成布局。然后是数据列表的分页,这里用到的是 el-pagination 实现分页的功能,五条数据为一页。
在这里插入图片描述
其中 table 中的 data 是 pageData ,我们对其的 id 进行了处理,为了保证其 id 是按照递增的顺序展示出来,而不是每一页都是 1-5 的排序。其中还有一个特点就是,通过后端接口插入的数据显示的 id 是时间戳,因为我们不一定知道最后插入的那条数据的 id 是多少,也没必要去特地看一眼,然后才接着插入数据。因此我们默认插入的 id 是时间戳,然后再对 id 进行处理。

🧩添加功能

相比上一篇文章的 demo 功能,这个项目多了一个添加联系人的功能,我们通过后端写好的 insert 接口来实现添加,我们只需要在使用 axios 获取数据的时候,使用到这个接口,以及传递对应的数据给后端即可实现添加功能,接下来我们看看 Node 代码。
在这里插入图片描述
其中红框的部分是一些对添加的数据的判断逻辑,然后就是把新传递的参数写入 JSON 格式数据,最后返回结果信息。接下来我们通过 post 请求以及写好的添加联系人模块来添加一条数据。
在这里插入图片描述
在添加联系人窗口输入数据,然后点击确认添加。
在这里插入图片描述
然后我们可以在终端看到添加成功的反馈,以及 JSON 数据中出现了刚刚新添加的数据。
在这里插入图片描述
然后在后台的联系人列表也可以看到。
在这里插入图片描述
通过上面的一系列操作,我们实现了添加联系人的功能,其中添加联系人的窗口这里没用使用弹窗的形式,而是使用卡片嵌套表单的形式。为了使添加联系人成功后会自动刷新列表,方便数据的显示,这里我们要把通过 axios 异步获取数据的方法封装到一个函数,然后挂载到 onMounted 函数,这样就方便在任意地方调用了。

🧩编辑功能

这下来我们来看看编辑功能,通过点击编辑按钮,然后出现弹窗,对数据进行修改和保持,这里使用到了 el-dialogElMessage 实现窗口的出现、隐藏以及一些交互效果(消息框)。
在这里插入图片描述
关于编辑功能,我们通过后端写好的 update 接口来实现编辑功能,我们只需要在使用 axios 获取数据的时候,使用到这个接口,以及传递对应的数据给后端即可实现编辑功能,接下来我们看看 Node 代码。
在这里插入图片描述
其中红框的部分是一些对编辑的数据的判断逻辑,然后就是把新传递的参数重新修改代替原本的数据,然后写入 JSON 格式数据,最后返回结果信息。接下来我们通过 post 请求以及写好的编辑模块来编辑刚刚新添加的那一条数据。
在这里插入图片描述
找到刚刚新添加的那条数据,然后点击编辑按钮,把姓名测试添加改成测试编辑
在这里插入图片描述
在这里插入图片描述
然后我们可以在终端看到 id:xxx 编辑成功的反馈,以及 JSON 数据中更新了刚刚编辑的数据。
在这里插入图片描述
通过上面的一系列操作,我们实现了编辑功能,这里的编辑功能模块就是用了弹窗的形式,不同于添加联系人功能模块。

🧩删除功能

最后我们来看看删除功能,通过点击删除按钮,然后出现是否确认删除的弹窗,这里使用到了 ElMessageBox 实现弹窗的出现以及确认、取消的交互效果。

关于删除功能,我们通过后端写好的 delete 接口来实现删除功能,我们只需要在使用 axios 获取数据的时候,使用到这个接口,以及传递联系人的 id 给后端即可实现删除功能,接下来我们看看 Node 代码和 js 代码。
在这里插入图片描述
在这里插入图片描述

然后找到刚刚编辑的那条数据,然后点击删除按钮,删除这条数据。
在这里插入图片描述
然后我们可以在终端看到 id:xxx 删除功的反馈,以及 JSON 数据中删除这条数据。
在这里插入图片描述
通过上面的一系列操作,我们实现了删除功能。至此这个后台的功能已经基本全部实现了,上述的功能分析以及代码分析的完整内容还需要到具体的源码中去编写、浏览了才能体验的到了,最后附上完整代码,供大家参考和学习


🎯完整代码

<template>
  <div class="contact-list">
    <!-- 标题 -->
    <el-row justify="center">
      <h1 style="text-align: center">Node.js 联系人列表管理后台</h1>
    </el-row>
    <!-- 添加联系人按钮 -->
    <el-row justify="center">
      <el-button type="primary" style="text-align: center" @click="addShowForm">添加联系人</el-button>
    </el-row>
    <br />
    <!-- 添加联系人表单窗口 -->
    <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="请输入联系人姓名"></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="请输入联系人电话"></el-input>
          </el-form-item>
        </el-col>
        <el-col>
          <el-button type="primary" @click="addContact">确认添加</el-button>
          <el-button @click="addFormVisible = false">取 消</el-button>
          <el-button @click="refreshFormData()">清空</el-button>
        </el-col>
      </el-form>
    </el-card>

    <!-- 原编辑联系人表单窗口 -->
    <!-- <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">确认修改</el-button>
          <el-button @click="editFormVisible = false">取 消</el-button>
        </el-col>
      </el-form>
    </el-card> -->

    <!-- 编辑联系人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>

    <!-- 联系人数据表格 -->
    <el-card class="list-card">
      <el-table :data="pagedData" empty-text="暂无联系人" 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数据定义一个list
const contactList = reactive({
  list: [],
});

// 当前页数
const currentPage = ref(1);

// 当前页数变化时的回调函数
const handleCurrentChange = (val) => {
  currentPage.value = val;
};

// 分页后的数据
// 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默认数据
const instance = axios.create({
  baseURL: "http://127.0.0.1:9999/api/",
  timeout: 10000,
});

// 在组件挂载完毕后调用 refreshList 函数
onMounted(() => {
  // instance.get("select").then((res) => {
  //   contactList.list = res.data.data.data;
  //   console.log(contactList.list);
  // });
  refreshList();
});
// 封装这个方法,方便在任意地方调用
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",
      });
    });
};

// 表单的数据
const formData = reactive({
  id: "",
  name: "",
  tel: "",
});

// 清空formData数据
const refreshFormData = () => {
  formData.id = "";
  formData.name = "";
  formData.tel = "";
};

// 添加联系人模块
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",
      });
    });
};

// 编辑联系人模块
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: "已取消编辑!",
  //   });
  // });
};

// 删除联系人模块
const delContact = (row) => {
  console.log(row);
  ElMessageBox.confirm(`确定要删除联系人${row.name}`, "Warning", {
    confirmButtonText: "确认",
    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 遮罩突然变黑问题解决 */
.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 后端代码,或者完整项目的,可以私信或者在评论区留言,我会第一时间回复。


📝最后

通过这篇文章的实战进阶学习,我们可以学会一个基于 Vue3 + Node.js + ElementPlus 实现的联系人列表管理后台的 demo,同时对 axios 的使用更上一层楼,文章中的 Node 不是本次项目的重点,这篇文章重点是练习 axios 多方面的用法,因此后续会讲讲、记录一下 axios 进一步封装的操作。
在这里插入图片描述

好的,我可以为您提供一些基本的思路。首先,我们需要建立一个基于Vue3的前端页面,然后使用Node.js实现后端接口,以便将数据保存到数据库中。以下是详细步骤: 1. 创建Vue3项目 我们可以使用Vue CLI来创建一个新的Vue3项目。在终端中输入以下命令: ``` vue create volunteer-activity ``` 2. 安装所需的依赖包 我们需要安装一些必要的依赖包,包括axios(与后端进行通信),element-plus(UI框架)等。在终端中输入以下命令: ``` cd volunteer-activity npm install axios element-plus --save ``` 3. 创建前端页面 我们可以使用Vue3的组件来构建前端页面。可以创建一个发布活动的表单,包括活动名称、活动时间、活动地点、活动描述等信息。可以使用Element Plus的表单组件,如el-form、el-form-item等。 4. 创建Node.js后端 我们需要使用Node.js实现后端接口,以便将数据保存到数据库中。可以使用Express框架来实现。在终端中输入以下命令: ``` npm install express body-parser mongoose --save ``` 5. 定义数据模型 我们需要定义一个数据模型,以便在数据库中保存活动数据。可以使用Mongoose库来定义数据模型。在后端代码中,可以创建一个活动模型,在其中定义活动名称、活动时间、活动地点、活动描述等属性。 6. 定义路由 我们需要定义一个路由,以便在前端页面中通过axios与后端进行通信。可以使用Express框架来定义路由。在后端代码中,可以创建一个活动路由,其中包括创建活动、获取所有活动、获取单个活动、更新活动、删除活动等功能。 7. 实现前后端数据交互 我们需要在前端页面中使用axios来与后端进行通信。可以创建一个提交表单的方法,在其中使用axios向后端发送数据。在后端代码中,可以使用Mongoose库来连接数据库,并通过调用活动模型来保存、更新、获取、删除活动数据。 8. 测试并部署 我们可以在本地测试前后端数据交互是否正常。如果一切正常,可以将应用程序部署到服务器上。 以上是基于Vue3和Node.js实现志愿者活动发布页面的大致流程。需要注意的是,这只是一个基本的示例,具体的实现可能会有所不同,具体实现还需要根据实际情况进行调整。
评论 48
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黛琳ghz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值