发布于 2026-01-06 9 阅读
0

通过构建一个简单的 CRUD 应用来学习 Vue.js 的基础知识:教程 我们将学习的内容包括:项目设置、后端模拟、组件和页面创建、功能创建、功能更新、功能删除、总结

通过构建一个简单的 CRUD 应用来学习 Vue.js 的基础知识:教程

我们将要探讨的内容

项目设置

模拟后端

创建组件和页面

创建功能

更新功能

删除功能

结论

在这篇文章中,我们将探讨如何使用 Vue 2.x 作为框架来编写单页应用程序。

该应用程序的目标是管理客户数据,这是一个非常基础的客户关系管理系统。

Vue 近年来广受欢迎,而且上手非常容易。优秀的 Vue 团队已经发布了 Vue 3.x,但是 Vue 2.x 中使用的基本概念可以迁移到 Vue 3 中。

我们将要探讨的内容

  • 使用 Vue CLI 设置项目
  • 创建组件
  • 使用 BootstrapVue 进行样式设计
  • 用于 API 模拟的 JSON 服务器
  • 发出HTTP请求
  • 元素的条件渲染
  • 显示数据
  • 处理用户输入

如果您想跟着教程操作,您的电脑上需要安装以下软件:

  • Node.js
  • Vue CLI
  • 你常用的集成开发环境(我用的是 VSCode,但你可以用任何你喜欢的)。

如果您不确定是否应该从 Vue 或 React 等单页框架入手,那么您可以看看我的另一篇文章,我在文中谈到了帮助我提高 Vue 和 React 开发效率的 JavaScript 特性。

项目设置

使用 Vue CLI,我们只需几个命令即可快速搭建完整的 Vue 项目。我们还可以使用 CLI 管理 Vue 相关的依赖项,例如 Vue-Router 和 Vuex。

首先,运行以下命令:

vue create vue-tutorial
Enter fullscreen mode Exit fullscreen mode

然后让我们手动选择特征:
终端截图

然后,我们再加入 Vue-Router:
终端截图

然后我们可以将其余部分配置为:
终端截图

我不会保存这个设置,但如果你想在以后的项目中使用它,你可以保存。

然后我们进入该目录并运行命令,npm run serve这将启动我们的开发服务器。这样我们就可以在继续项目设置之前,检查一切是否按预期运行。

Vue 默认页面已完成设置

现在我们有了这些,就可以着手为我们的项目设置 Bootstrap 了。

我们首先安装依赖项:

npm install bootstrap bootstrap-vue 
Enter fullscreen mode Exit fullscreen mode

安装完这些组件后,我们需要在 IDE 中打开我们的项目并打开 main.js 文件。

在这里,我们将添加几行代码,告诉 Vue 使用我们新安装的 Bootstrap 和 BootstrapVue 依赖项:

import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import { BootstrapVue, IconsPlugin } from "bootstrap-vue";

// Import Bootstrap an BootstrapVue CSS files (order is important)
import "bootstrap/dist/css/bootstrap.css";
import "bootstrap-vue/dist/bootstrap-vue.css";

// Make BootstrapVue available throughout your project
Vue.use(BootstrapVue);
Vue.use(IconsPlugin);

Vue.config.productionTip = false;

new Vue({
  router,
  render: (h) => h(App),
}).$mount("#app");
Enter fullscreen mode Exit fullscreen mode

您可能已经注意到,我们还添加了一个图标插件。这使我们能够在整个项目中使用 Bootstrap 图标。

这时我通常会重启开发服务器并重新编译所有内容。所以只需按下 Ctrl + C 并npm run serve再次运行,即可让一切恢复正常运行。

太棒了!设置完成,我们现在可以开始创建组件和页面了。

模拟后端

作为前端工程师,您很可能会发现您将要使用的 API 端点尚未完全准备就绪。

在这种情况下,我们可以使用 json-server。

安装完成后,我们无需编写任何后端代码即可创建、读取、更新和删除记录。

安装方法如下:

npm install -g json-server

然后我们在根目录下直接创建一个名为 db.json 的新文件,并用我们想要的数据填充它:

{
  "customers": [
    {
      "id": 1,
      "company_name": "My awesome Company",
      "contact_firstname": "Jane",
      "contact_lastname": "Doe",
      "contact_email": "jdoe@awesome.com",
      "acquired_on": "2021-04-03",
      "customer_status": "active"
    },
    {
      "id": 2,
      "company_name": "Company ABC",
      "contact_firstname": "Sarah",
      "contact_lastname": "Jane",
      "contact_email": "sjane@coabc.com",
      "acquired_on": "2021-06-03",
      "customer_status": "active"
    },
    {
      "id": 3,
      "company_name": "Company xYZ",
      "contact_firstname": "Tim",
      "contact_lastname": "Hane",
      "contact_email": "thane@coxyz.com",
      "acquired_on": "2020-12-03",
      "customer_status": "active"
    },
    {
      "id": 4,
      "company_name": "Video Production Consulting",
      "contact_firstname": "Miriam",
      "contact_lastname": "Doe",
      "contact_email": "mdoe@vidprod.com",
      "acquired_on": "2020-07-05",
      "customer_status": "inactive"
    },
    {
      "id": 5,
      "company_name": "Code Writers",
      "contact_firstname": "Jill",
      "contact_lastname": "Wade",
      "contact_email": "jwade@codewrit.com",
      "acquired_on": "2020-04-03",
      "customer_status": "active"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

现在我们可以通过运行以下命令来提供这些记录:

json-server --watch db.json
Enter fullscreen mode Exit fullscreen mode

这将使我们能够通过访问“ http://localhost:3000/customers/ ”来发送和接收数据。

伟大的!

现在我们可以创建自己的应用程序来与这个模拟 API 进行交互。

创建组件和页面

让我们从最顶层的组件开始:App.vue。

接下来我们将移除末尾的样式标签和导航:

<template>
  <div>
    <header-bar></header-bar>
    <b-container>
      <b-row class="mt-5">
        <router-view />
      </b-row>
    </b-container>
  </div>
</template>

<script>
import HeaderBar from "@/components/HeaderBar.vue";

export default {
  components: {
    HeaderBar,
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

如您所见,我们还有一个尚未创建的 HeaderBar.vue 组件。

前往组件目录,创建一个名为 HeaderBar.vue 的新文件,并添加以下代码:

<template>
  <b-navbar toggleable="lg" type="dark" variant="primary">
    <b-container>
      <b-row class="text-white">
        <b-col>
          <b-icon-people-fill class="h2"></b-icon-people-fill>
        </b-col>
        <b-col>
          <span class="h3">CRM</span>
        </b-col>
      </b-row>
    </b-container>
  </b-navbar>
</template>

<script>
export default {
  name: "HeaderBar",
};
</script>
Enter fullscreen mode Exit fullscreen mode

这样一来,我们的应用程序顶部就会出现一个漂亮的标题栏,上面包含徽标和一些文字:

标题栏示例

你已经创建了你的第一个 Vue 组件!太棒了!

如您所见,我们能够轻松地将图标添加到我们的设计中,而无需花费太多精力进行设置。

现在我们可以着手从 Home.vue 中移除默认代码,并开始构建我们的应用程序。

这将是我们概览页面。我们可以在这里搜索客户,也可以快速浏览他们的数据。

我们首先需要安装 Axis,以便能够与后端进行数据收发:

npm install axios

之后,我们可以随时随地根据需要导入它。

现在我们可以创建一个简单的数据表组件:

<template>
  <div>
    <b-row>
      <data-table></data-table>
    </b-row>
  </div>
</template>

<script>
import DataTable from "@/components/DataTable.vue";

export default {
  name: "Home",
  components: {
    DataTable,
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

现在我们可以使用 CustomerOverview 组件来创建 DataTable.vue 组件:

<template>
  <div>
    <b-row>
      <b-alert v-model="showSuccessAlert" variant="success" dismissible>
        {{ alertMessage }}
      </b-alert>
    </b-row>
    <b-row>
      <customer-overview
        :totalCustomers="numberOfCustomers"
        :activeCustomers="activeCustomers"
        @totalCustomersIsActive="setFilterTotalIsActive"
        @activeCustomerIsActive="setFilterActiveIsActive"
      ></customer-overview>
    </b-row>
    <b-row class="mt-3">
      <b-card>
        <b-row align-h="between">
          <b-col cols="6">
            <h3>{{ tableHeader }}</h3>
          </b-col>
          <b-col cols="2">
            <b-row>
              <b-col>
                <b-button
                  variant="primary"
                  id="show-btn"
                  @click="showCreateModal"
                >
                  <b-icon-plus class="text-white"></b-icon-plus>
                  <span class="h6 text-white">New Customer</span>
                </b-button>
              </b-col>
            </b-row>
          </b-col>
        </b-row>
        <b-row class="mt-3">
          <b-table
            striped
            hover
            :items="items"
            :fields="fields"
            class="text-center"
          >
            <template #cell(contact_name)="data">
              {{
                `${data.item.contact_firstname} ${data.item.contact_lastname}`
              }}
            </template>
            <template #cell(customer_status)="data">
              <b-icon-bookmark-check-fill
                variant="success"
                v-if="data.item.customer_status === 'active'"
              ></b-icon-bookmark-check-fill>
              <b-icon-bookmark-x-fill
                variant="danger"
                v-else
              ></b-icon-bookmark-x-fill>
            </template>
            <template #cell(actions)="data">
              <b-row>
                <b-col cols="7">
                  <b-icon-pencil-square
                    class="action-item"
                    variant="primary"
                    @click="getRowData(data.item.id)"
                  ></b-icon-pencil-square>
                </b-col>
                <b-col cols="1">
                  <b-icon-trash-fill
                    class="action-item"
                    variant="danger"
                    @click="showDeleteModal(data.item.id)"
                  ></b-icon-trash-fill>
                </b-col>
              </b-row>
            </template>
          </b-table>
        </b-row>
      </b-card>
    </b-row>

    <!-- Modal for adding new customers -->
    <b-modal
      ref="create-customer-modal"
      size="xl"
      hide-footer
      title="New Customer"
    >
      <create-customer-form
        @closeCreateModal="closeCreateModal"
        @reloadDataTable="getCustomerData"
        @showSuccessAlert="showAlertCreate"
      ></create-customer-form>
    </b-modal>

    <!-- Modal for updating customers -->
    <b-modal
      ref="edit-customer-modal"
      size="xl"
      hide-footer
      title="Edit Customer"
    >
      <edit-customer-form
        @closeEditModal="closeEditModal"
        @reloadDataTable="getCustomerData"
        @showSuccessAlert="showAlertUpdate"
        :customerId="customerId"
      ></edit-customer-form>
    </b-modal>

    <!-- Delete Customer Modal -->
    <b-modal
      ref="delete-customer-modal"
      size="md"
      hide-footer
      title="Confirm Deletion"
    >
      <delete-customer-modal
        @closeDeleteModal="closeDeleteModal"
        @reloadDataTable="getCustomerData"
        @showDeleteAlert="showDeleteSuccessModal"
        :customerId="customerId"
      ></delete-customer-modal>
    </b-modal>
  </div>
</template>

<script>
import axios from "axios";
import CustomerOverview from "@/components/CustomerOverview.vue";
import CreateCustomerForm from "@/components/CreateCustomerForm.vue";
import EditCustomerForm from "@/components/EditCustomerForm.vue";
import DeleteCustomerModal from "@/components/DeleteCustomerModal.vue";

export default {
  components: {
    CustomerOverview,
    CreateCustomerForm,
    EditCustomerForm,
    DeleteCustomerModal,
  },
  data() {
    return {
      // Note 'isActive' is left out and will not appear in the rendered table

      fields: [
        {
          key: "company_name",
          label: "Company Name",
          sortable: false,
        },
        {
          key: "contact_name",
          label: "Contact Name",
          sortable: false,
        },
        {
          key: "contact_email",
          label: "Contact E-Mail",
          sortable: false,
        },
        {
          key: "customer_status",
          label: "Customer Status",
          sortable: false,
        },
        "actions",
      ],
      items: [],
      numberOfCustomers: 0,
      activeCustomers: 0,
      activeCustomersData: [],
      customerId: 0,
      companySearchTerm: "",
      tableHeader: "",
      showSuccessAlert: false,
      alertMessage: "",
    };
  },
  mounted() {
    this.getCustomerData();
  },
  methods: {
    showCreateModal() {
      this.$refs["create-customer-modal"].show();
    },
    closeCreateModal() {
      this.$refs["create-customer-modal"].hide();
    },
    getCustomerData() {
      axios
        .get("http://localhost:3000/customers/")
        .then((response) => {
          this.tableHeader = "Total Customer";
          this.items = response.data;
          this.numberOfCustomers = response.data.length;
          this.activeCustomersData = response.data.filter(
            (item) => item.customer_status === "active"
          );
          this.activeCustomers = this.activeCustomersData.length;
        })
        .catch((error) => {
          console.log(error);
        });
    },
    getRowData(id) {
      this.$refs["edit-customer-modal"].show();
      this.customerId = id;
    },
    closeEditModal() {
      this.$refs["edit-customer-modal"].hide();
    },
    setFilterTotalIsActive() {
      this.tableHeader = "Total Customers";
      this.getCustomerData();
    },
    setFilterActiveIsActive() {
      this.tableHeader = "Active Customers";
      this.items = this.activeCustomersData;
    },
    showAlertCreate() {
      this.showSuccessAlert = true;
      this.alertMessage = "Customer was created successfully!";
    },
    showAlertUpdate() {
      this.showSuccessAlert = true;
      this.alertMessage = "Customer was updated successfully";
    },
    showDeleteModal(id) {
      this.$refs["delete-customer-modal"].show();
      this.customerId = id;
    },
    closeDeleteModal() {
      this.$refs["delete-customer-modal"].hide();
    },
    showDeleteSuccessModal() {
      this.showSuccessAlert = true;
      this.alertMessage = "Customer was deleted successfully!";
    },
  },
};
</script>

<style>
.action-item:hover {
  cursor: pointer;
}
</style>
Enter fullscreen mode Exit fullscreen mode

我们的 CustomerOverview.vue 组件看起来会是这样:

<template>
  <div>
    <b-row class="text-center">
      <b-col>
        <div
          class="filter-card p-3"
          :class="{
            'bg-active-filter': totalIsActive,
            'bg-light': !totalIsActive,
          }"
          @click="totalCustomersIsActiveFilter"
        >
          <h6>Total Customers</h6>
          <h4>
            <strong>{{ totalCustomers }}</strong>
          </h4>
        </div>
      </b-col>
      <b-col>
        <div
          class="filter-card p-3"
          :class="{
            'bg-active-filter': activeIsActive,
            'bg-light': !activeIsActive,
          }"
          @click="activeCustomersIsActiveFilter"
        >
          <h6 class="text-secondary">Active Customers</h6>
          <h4>
            <strong>{{ activeCustomers }}</strong>
          </h4>
        </div>
      </b-col>
    </b-row>
  </div>
</template>

<script>
export default {
  name: "CustomerOverview",
  props: {
    totalCustomers: Number,
    activeCustomers: Number,
  },
  data() {
    return {
      totalIsActive: true,
      activeIsActive: false,
    };
  },
  methods: {
    totalCustomersIsActiveFilter() {
      this.totalIsActive = true;
      this.activeIsActive = false;
      this.$emit("totalCustomersIsActive");
    },
    activeCustomersIsActiveFilter() {
      this.totalIsActive = false;
      this.activeIsActive = true;
      this.$emit("activeCustomerIsActive");
    },
  },
};
</script>

<style>
.filter-card:hover {
  cursor: pointer;
  text-decoration: underline;
}

.bg-active-filter {
  background-color: #e9f1fe;
  color: #074297;
}
</style>
Enter fullscreen mode Exit fullscreen mode

我们还有三个模态框,分别用于向列表中添加新客户、更新现有客户信息和删除现有记录:

<!-- Modal for adding new customers -->
    <!-- Modal for adding new customers -->
    <b-modal
      ref="create-customer-modal"
      size="xl"
      hide-footer
      title="New Customer"
    >
      <create-customer-form
        @closeCreateModal="closeCreateModal"
        @reloadDataTable="getCustomerData"
        @showSuccessAlert="showAlertCreate"
      ></create-customer-form>
    </b-modal>

    <!-- Modal for updating customers -->
    <b-modal
      ref="edit-customer-modal"
      size="xl"
      hide-footer
      title="Edit Customer"
    >
      <edit-customer-form
        @closeEditModal="closeEditModal"
        @reloadDataTable="getCustomerData"
        @showSuccessAlert="showAlertUpdate"
        :customerId="customerId"
      ></edit-customer-form>
    </b-modal>

<!-- Delete Customer Modal -->
    <b-modal
      ref="delete-customer-modal"
      size="md"
      hide-footer
      title="Confirm Deletion"
    >
      <delete-customer-modal
        @closeDeleteModal="closeDeleteModal"
        @reloadDataTable="getCustomerData"
        @showDeleteAlert="showDeleteSuccessModal"
        :customerId="customerId"
      ></delete-customer-modal>
    </b-modal>
Enter fullscreen mode Exit fullscreen mode

如您所见,我们正在导入表单本身,这意味着我们需要创建一个 CreateCustomerForm.vue 文件:

<template>
  <b-form class="mt-3">
    <b-row>
      <b-row>
        <h4 class="text-secondary">Contact Details</h4>
      </b-row>
      <b-col cols="6">
        <b-form-group id="first-name" label="First Name" label-for="first-name">
          <b-form-input
            id="first-name"
            type="text"
            placeholder="First Name"
            v-model="customer.contact_firstname"
          ></b-form-input>
        </b-form-group>
      </b-col>
      <b-col cols="6">
        <b-form-group id="last-name" label="Last Name" label-for="last-name">
          <b-form-input
            id="last-name"
            type="text"
            placeholder="Last Name"
            v-model="customer.contact_lastname"
          ></b-form-input>
        </b-form-group>
      </b-col>
    </b-row>
    <b-row class="mt-3">
      <b-col cols="6">
        <b-form-group id="email" label="E-Mail" label-for="email">
          <b-form-input
            id="email"
            type="email"
            placeholder="example@crm.com"
            v-model="customer.contact_email"
          ></b-form-input>
        </b-form-group>
      </b-col>
    </b-row>
    <b-row class="mt-5">
      <h4 class="text-secondary">Company Details</h4>
    </b-row>
    <b-row>
      <b-col cols="4">
        <b-form-group
          id="company_name"
          label="Company Name"
          label-for="company_name"
        >
          <b-form-input
            id="company_name"
            type="text"
            placeholder="XYZ Industries"
            v-model="customer.company_name"
          ></b-form-input>
        </b-form-group>
      </b-col>
    </b-row>
    <b-row>
      <b-col cols="4">
        <b-form-group
          id="acquired_on"
          label="Acquired On"
          label-for="acquired_on"
        >
          <b-form-input
            id="acquired_on"
            type="date"
            v-model="customer.acquired_on"
          ></b-form-input>
        </b-form-group>
      </b-col>
    </b-row>
    <b-row class="mt-2">
      <b-form-checkbox
        id="customer_status"
        v-model="customer.customer_status"
        name="customer-status"
        value="active"
        unchecked-value="inactive"
      >
        Customer is active
      </b-form-checkbox>
    </b-row>
    <b-row class="mt-4">
      <b-col cols="3">
        <b-button variant="primary" class="px-5" @click="addNewCustomer"
          >Add Customer</b-button
        >
      </b-col>
      <b-col>
        <b-button variant="warning" @click="triggerClose">Close</b-button>
      </b-col>
    </b-row>
  </b-form>
</template>

<script>
import axios from "axios";

export default {
  name: "CreateCustomerModal",
  data() {
    return {
      customer: {},
    };
  },
  methods: {
    triggerClose() {
      this.$emit("closeCreateModal");
    },
    addNewCustomer() {
      axios
        .post("http://localhost:3000/customers/", this.customer)
        .then((response) => {
          console.log(response.data);
          this.$emit("closeCreateModal");
          this.$emit("reloadDataTable");
          this.$emit("showSuccessAlert");
        })
        .catch((error) => {
          console.log(error);
        });
    },
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

以及 EditCustomerForm.vue 组件:

<template>
  <b-form class="mt-3">
    <b-row>
      <b-row>
        <h4 class="text-secondary">Contact Details</h4>
      </b-row>
      <b-col cols="6">
        <b-form-group id="first-name" label="First Name" label-for="first-name">
          <b-form-input
            id="first-name"
            type="text"
            placeholder="First Name"
            v-model="customer.contact_firstname"
          ></b-form-input>
        </b-form-group>
      </b-col>
      <b-col cols="6">
        <b-form-group id="last-name" label="Last Name" label-for="last-name">
          <b-form-input
            id="last-name"
            type="text"
            placeholder="Last Name"
            v-model="customer.contact_lastname"
          ></b-form-input>
        </b-form-group>
      </b-col>
    </b-row>
    <b-row class="mt-3">
      <b-col cols="6">
        <b-form-group id="email" label="E-Mail" label-for="email">
          <b-form-input
            id="email"
            type="email"
            placeholder="example@crm.com"
            v-model="customer.contact_email"
          ></b-form-input>
        </b-form-group>
      </b-col>
    </b-row>
    <b-row class="mt-5">
      <h4 class="text-secondary">Company Details</h4>
    </b-row>
    <b-row>
      <b-col cols="4">
        <b-form-group
          id="company_name"
          label="Company Name"
          label-for="company_name"
        >
          <b-form-input
            id="company_name"
            type="text"
            placeholder="XYZ Industries"
            v-model="customer.company_name"
          ></b-form-input>
        </b-form-group>
      </b-col>
    </b-row>
    <b-row>
      <b-col cols="4">
        <b-form-group
          id="acquired_on"
          label="Acquired On"
          label-for="acquired_on"
        >
          <b-form-input
            id="acquired_on"
            type="date"
            v-model="customer.acquired_on"
          ></b-form-input>
        </b-form-group>
      </b-col>
    </b-row>
    <b-row class="mt-2">
      <b-form-checkbox
        id="customer_status"
        v-model="customer.customer_status"
        name="customer-status"
        value="active"
        unchecked-value="inactive"
      >
        Customer is active
      </b-form-checkbox>
    </b-row>
    <b-row class="mt-4">
      <b-col cols="3">
        <b-button variant="primary" class="px-5" @click="updateCustomer"
          >Update Customer</b-button
        >
      </b-col>
      <b-col>
        <b-button variant="warning" @click="triggerClose">Close</b-button>
      </b-col>
    </b-row>
  </b-form>
</template>

<script>
import axios from "axios";

export default {
  name: "CreateCustomerModal",
  props: {
    customerId: Number,
  },
  data() {
    return {
      customer: {},
    };
  },
  mounted() {
    this.getCusomterByID();
  },
  methods: {
    triggerClose() {
      this.$emit("closeEditModal");
    },
    getCusomterByID() {
      axios
        .get(`http://localhost:3000/customers/${this.customerId}`)
        .then((response) => {
          this.customer = response.data;
        })
        .catch((error) => {
          console.log(error);
        });
    },
    updateCustomer() {
      axios
        .put(
          `http://localhost:3000/customers/${this.customerId}`,
          this.customer
        )
        .then((response) => {
          console.log(response.data);
          this.$emit("closeEditModal");
          this.$emit("reloadDataTable");
          this.$emit("showSuccessAlert");
        })
        .catch((error) => {
          console.log(error);
        });
    },
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

我们的删除弹窗会显示一条确认信息,要求用户确认是否需要删除该记录:

<template>
  <div>
    <b-row class="mt-2 mb-3">
      <h6 class="text-secondary">
        Are you sure you want to delete this customer from your CRM?
      </h6>
    </b-row>
    <b-row class="mt-2 mb-3">
      <p class="text-danger">
        This action is not reversible and may result in the loss if important
        data.
      </p>
    </b-row>
    <b-row class="mt-4">
      <b-col>
        <b-button variant="danger" @click="removeCustomerFromData"
          >Delete Customer</b-button
        >
      </b-col>
      <b-col>
        <b-button variant="warning" @click="triggerClose">Close</b-button>
      </b-col>
    </b-row>
  </div>
</template>

<script>
import axios from "axios";

export default {
  name: "DeleteCustomerModal",
  props: {
    customerId: Number,
  },
  methods: {
    triggerClose() {
      this.$emit("closeDeleteModal");
    },
    removeCustomerFromData() {
      axios
        .delete(`http://localhost:3000/customers/${this.customerId}`)
        .then(() => {
          this.$emit("reloadDataTable");
          this.$emit("showDeleteAlert");
          this.$emit("closeDeleteModal");
        })
        .catch((error) => {
          console.log(error);
        });
    },
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

结果将是:

屏幕截图显示数据表

点击“活跃客户”,我们可以筛选表格,只显示当前处于活跃状态的客户:

仅包含活跃客户的数据表

此函数用于将 items 数组重置为包含所有客户的原始形式:

setFilterTotalIsActive() {
      this.tableHeader = "Total Customers";
      this.getCustomerData();
}
Enter fullscreen mode Exit fullscreen mode

此功能负责过滤掉所有当前不活跃的客户:

setFilterActiveIsActive() {
      this.tableHeader = "Active Customers";
      this.items = this.activeCustomersData;
}
Enter fullscreen mode Exit fullscreen mode

activeCustomersData 对象由我们的 data() 函数返回。当从后端请求数据时,该数组会被填充:

getCustomerData() {
      axios
        .get("http://localhost:3000/customers/")
        .then((response) => {
          this.tableHeader = "Total Customer";
          this.items = response.data;
          this.numberOfCustomers = response.data.length;
          this.activeCustomersData = response.data.filter(
            (item) => item.customer_status === "active"
          ); //filters out any inactive customers
          this.activeCustomers = this.activeCustomersData.length;
        })
        .catch((error) => {
          console.log(error);
        });
    },
Enter fullscreen mode Exit fullscreen mode

我们还会根据客户状态有条件地渲染绿色标签,在客户不活跃时渲染红色标签:

绿色标签与红色标签相对

这是通过以下方式实现的:

          <b-table
            striped
            hover
            :items="items"
            :fields="fields"
            class="text-center"
          >
            <template #cell(contact_name)="data">
              {{
                `${data.item.contact_firstname} ${data.item.contact_lastname}`
              }}
            </template>
            <template #cell(customer_status)="data">
              <b-icon-bookmark-check-fill
                variant="success"
                v-if="data.item.customer_status === 'active'"
              ></b-icon-bookmark-check-fill>
              <b-icon-bookmark-x-fill
                variant="danger"
                v-else
              ></b-icon-bookmark-x-fill>
            </template>
            <template #cell(actions)="data">
              <b-row>
                <b-col cols="7">
                  <b-icon-pencil-square
                    class="action-item"
                    variant="primary"
                    @click="getRowData(data.item.id)"
                  ></b-icon-pencil-square>
                </b-col>
                <b-col cols="1">
                  <b-icon-trash-fill
                    class="action-item"
                    variant="danger"
                    @click="showDeleteModal(data.item.id)"
                  ></b-icon-trash-fill>
                </b-col>
              </b-row>
            </template>
          </b-table>
Enter fullscreen mode Exit fullscreen mode

如您所见,如果 data.item.customer_status 为 active,则我们<b-icon-bookmark-check-fill></b-icon-bookmark-check-fill>使用 success 变体进行渲染。如果客户处于非活动状态,则我们使用<b-icon-bookmark-x-fill></b-icon-bookmark-x-fill>danger 变体进行渲染。

我们使用数据表中的模板来自定义表格中呈现的内容。

我们还使用此渲染器来渲染我们的<b-icon-pencil-square></b-icon-pencil-square>-icon、我们的<b-icon-trash-fill></b-icon-trash-fill>-icon 和来渲染我们联系人的全名。

创建功能

我们只需点击标有“新客户”的按钮,然后填写表格即可:

添加新的客户模态框

然后我们可以点击“添加客户”将数据存储到我们的模拟 API 数据库中。

我们的表格中现在新增了一项:

新增条目

我们可以看到,我们在顶部添加了成功消息,以告知用户客户已成功添加。

具体来说,我们的操作会向后端发送一个包含我们想要存储的数据的 POST 请求:

addNewCustomer() {
    axios
       .post("http://localhost:3000/customers/",this.customer)
        .then((response) => {
          console.log(response.data);
          this.$emit("closeCreateModal");
          this.$emit("reloadDataTable");
          this.$emit("showSuccessAlert");
        })
        .catch((error) => {
          console.log(error);
        });
    },
Enter fullscreen mode Exit fullscreen mode

现在您可能已经注意到这些 this.$emit 事件。这些事件允许我们将数据从子组件(在本例中为模态窗口)发送回父组件(在本例中为数据表)。

首先,我们通过触发 closeCreateModal() 方法关闭模态窗口,重新加载数据,并告诉父组件渲染警报。

这些事件已在我们的父组件中注册:

<!-- Modal for adding new customers -->
    <b-modal
      ref="create-customer-modal"
      size="xl"
      hide-footer
      title="New Customer"
    >
      <create-customer-form
        @closeCreateModal="closeCreateModal"
        @reloadDataTable="getCustomerData"
        @showSuccessAlert="showAlertCreate"
      ></create-customer-form>
    </b-modal>
Enter fullscreen mode Exit fullscreen mode

如您所见,子组件的名称是我们放入 $emit 中的字符串的名称。

这样,Vue 就知道要关注哪个事件,以及当该事件被注册时,它就知道接下来要调用哪个方法:

closeCreateModal() {
      this.$refs["create-customer-modal"].hide();
    },
    getCustomerData() {
      axios
        .get("http://localhost:3000/customers/")
        .then((response) => {
          this.tableHeader = "Total Customer";
          this.items = response.data;
          this.numberOfCustomers = response.data.length;
          this.activeCustomersData = response.data.filter(
            (item) => item.customer_status === "active"
          );
          this.activeCustomers = this.activeCustomersData.length;
        })
        .catch((error) => {
          console.log(error);
        });
    },
showAlertCreate() {
      this.showSuccessAlert = true;
      this.alertMessage = "Customer was created successfully!";
    },
Enter fullscreen mode Exit fullscreen mode

更新功能

既然我们可以创建新客户,我们就可以专注于编辑现有记录了。

这通常包含两个部分:

  • 识别并加载记录的特定数据
  • 将更新后的数据发送到后端进行存储

所以,我们首先需要识别记录。为此,我们有 ID。每个记录的 ID 都是唯一的,这正是我们需要的。

             <template #cell(actions)="data">
              <b-icon-pencil-square
                class="action-item"
                variant="primary"
                @click="getRowData(data.item.id)"
              ></b-icon-pencil-square>
            </template>
Enter fullscreen mode Exit fullscreen mode

这是我们数据表的一部分,我们再次使用<template></template>它来自定义渲染的内容。

这里我们调用一个函数,该函数会将 id 赋值给 data 方法返回的属性:

getRowData(id) {
      this.$refs["edit-customer-modal"].show();
      this.customerId = id;
    },
Enter fullscreen mode Exit fullscreen mode

我们使用 `$refs` 来标识要打开的模态框。在本例中,我们使用的是 `edit-customer-modal` 模态框。可以将 `$refs` 理解为一种选择器,例如 ID 或类名。

我们还会将元素的 id 传递给 customerId,以便在更新调用中使用。

现在customerId我们可以将它传递给子模型。

但是如何做到呢?

因此道具才存在:

<!-- Modal for updating customers -->
    <b-modal
      ref="edit-customer-modal"
      size="xl"
      hide-footer
      title="Edit Customer"
    >
      <edit-customer-form
        @closeEditModal="closeEditModal"
        @reloadDataTable="getCustomerData"
        @showSuccessAlert="showAlertUpdate"
        :customerId="customerId"
      ></edit-customer-form>
    </b-modal>
Enter fullscreen mode Exit fullscreen mode

我们将 customerId 和存储的 ID 一起传递给一个同名属性。您可以随意命名,但我们这里恰好使用了相同的名称。

语法是:<prop name>="whatever you want to pass"

现在我们需要在模态框组件中注册这个属性名称,才能使用它:

  name: "CreateCustomerModal",
  props: {
    customerId: Number,
  },
Enter fullscreen mode Exit fullscreen mode

在导出默认值中,我们可以添加此 props 属性,从而允许我们在子组件中访问它。

完成这些设置后,我们现在可以向我们的 API 发出请求,并通过提供我们的 ID 来获取特定的记录:

getCusomterByID() {
   axios
    .get(`http://localhost:3000/customers/${this.customerId}`)
    .then((response) => {
       this.customer = response.data;
     })
     .catch((error) => {
       console.log(error);
     });
    },
Enter fullscreen mode Exit fullscreen mode

这将返回我们需要的数据,以便使用当前存储的数据预填充输入字段:

预填表格

现在让我们更改正在使用的电子邮件地址,然后单击更新按钮保存更改:

已更改电子邮件的屏幕截图

而这将在我们的表格中有所体现:

屏幕截图显示了表格中更新后的数据。

这要归功于我们的 axios 请求,它将更新后的数据发送到我们的 API:

updateCustomer() {
      axios
        .put(
          `http://localhost:3000/customers/${this.customerId}`,
          this.customer
        )
        .then((response) => {
          console.log(response.data);
          this.$emit("closeEditModal");
          this.$emit("reloadDataTable");
          this.$emit("showSuccessAlert");
        })
        .catch((error) => {
          console.log(error);
        });
    },
Enter fullscreen mode Exit fullscreen mode

这里我们使用 PUT Http 动词来告诉 axios,我们想要使用作为第二个参数定义的数据来更新特定的记录。

因此,我们还需要包含要更新的记录的 ID。

我们再次利用事件来关闭模态框,重新加载表格以查看更改,并显示成功消息。

删除功能

最后,但同样重要的是,我们还有删除功能,它可以通过红色的垃圾桶图标来识别:

垃圾桶图标

点击后,记录不会立即删除,而是会弹出提示,询问用户是否要永久删除此记录:

删除确认模态框

告知用户其操作可能存在的潜在危险,被认为是一种良好的用户体验实践。

由于我们的图标附加了点击事件,因此模态框会显示出来:

<b-col cols="1">
                  <b-icon-trash-fill
                    class="action-item"
                    variant="danger"
                    @click="showDeleteModal(data.item.id)"
                  ></b-icon-trash-fill>
                </b-col>
Enter fullscreen mode Exit fullscreen mode

就像我们在编辑模态框中所做的那样,我们也传入了项目的 id,我们在发出删除请求时需要用到它:

<script>
import axios from "axios";

export default {
  name: "DeleteCustomerModal",
  props: {
    customerId: Number,
  },
  methods: {
    triggerClose() {
      this.$emit("closeDeleteModal");
    },
    removeCustomerFromData() {axios
 .delete(`http://localhost:3000/customers/${this.customerId}`)
        .then(() => {
          this.$emit("reloadDataTable");
          this.$emit("showDeleteAlert");
          this.$emit("closeDeleteModal");
        })
        .catch((error) => {
          console.log(error);
        });
    },
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

这里基本上还是重复了我们在前面例子中所做的操作。

当用户执行操作时,我们会发出请求,然后向父数据表组件发送一些事件,以关闭模态框,重新加载数据以反映我们的更改并显示成功提示。

这里我们还有一个关闭模态框的选项,因此还有一个额外的方法,当用户决定采取这种方式时,该方法会发出一个事件。

我们还使用了红色文字和红色背景作为按钮,以向用户表明这是一个非常重要的操作,可能会产生负面后果。

确认删除后,我们可以再次看到成功提示,并可以验证该记录已从表中删除:

从表中删除记录

结论

希望您喜欢这篇 Vue 入门介绍。以上要点不仅适用于 Vue(无论是 2.x 还是 3.x 版本),也适用于其他单页框架,例如 React、Angular 或 Svelte。

概念保持不变。

如果你一直跟着教程操作,可以看看能否通过将 DataTable.vue 文件拆分成更多组件来简化它。

如果您想在本页上阅读任何内容,请告诉我。

文章来源:https://dev.to/rjzauner/learn-the-basics-of-vue-js-by-building-a-simple-crud-app-a-tutorial-1ipi