通过构建一个简单的 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
我不会保存这个设置,但如果你想在以后的项目中使用它,你可以保存。
然后我们进入该目录并运行命令,npm run serve这将启动我们的开发服务器。这样我们就可以在继续项目设置之前,检查一切是否按预期运行。
现在我们有了这些,就可以着手为我们的项目设置 Bootstrap 了。
我们首先安装依赖项:
npm install bootstrap bootstrap-vue
安装完这些组件后,我们需要在 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");
您可能已经注意到,我们还添加了一个图标插件。这使我们能够在整个项目中使用 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"
}
]
}
现在我们可以通过运行以下命令来提供这些记录:
json-server --watch db.json
这将使我们能够通过访问“ 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>
如您所见,我们还有一个尚未创建的 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>
这样一来,我们的应用程序顶部就会出现一个漂亮的标题栏,上面包含徽标和一些文字:
你已经创建了你的第一个 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>
现在我们可以使用 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>
我们的 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>
我们还有三个模态框,分别用于向列表中添加新客户、更新现有客户信息和删除现有记录:
<!-- 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>
如您所见,我们正在导入表单本身,这意味着我们需要创建一个 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>
以及 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>
我们的删除弹窗会显示一条确认信息,要求用户确认是否需要删除该记录:
<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>
结果将是:
点击“活跃客户”,我们可以筛选表格,只显示当前处于活跃状态的客户:
此函数用于将 items 数组重置为包含所有客户的原始形式:
setFilterTotalIsActive() {
this.tableHeader = "Total Customers";
this.getCustomerData();
}
此功能负责过滤掉所有当前不活跃的客户:
setFilterActiveIsActive() {
this.tableHeader = "Active Customers";
this.items = this.activeCustomersData;
}
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);
});
},
我们还会根据客户状态有条件地渲染绿色标签,在客户不活跃时渲染红色标签:
这是通过以下方式实现的:
<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>
如您所见,如果 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);
});
},
现在您可能已经注意到这些 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>
如您所见,子组件的名称是我们放入 $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!";
},
更新功能
既然我们可以创建新客户,我们就可以专注于编辑现有记录了。
这通常包含两个部分:
- 识别并加载记录的特定数据
- 将更新后的数据发送到后端进行存储
所以,我们首先需要识别记录。为此,我们有 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>
这是我们数据表的一部分,我们再次使用<template></template>它来自定义渲染的内容。
这里我们调用一个函数,该函数会将 id 赋值给 data 方法返回的属性:
getRowData(id) {
this.$refs["edit-customer-modal"].show();
this.customerId = id;
},
我们使用 `$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>
我们将 customerId 和存储的 ID 一起传递给一个同名属性。您可以随意命名,但我们这里恰好使用了相同的名称。
语法是:<prop name>="whatever you want to pass"
现在我们需要在模态框组件中注册这个属性名称,才能使用它:
name: "CreateCustomerModal",
props: {
customerId: Number,
},
在导出默认值中,我们可以添加此 props 属性,从而允许我们在子组件中访问它。
完成这些设置后,我们现在可以向我们的 API 发出请求,并通过提供我们的 ID 来获取特定的记录:
getCusomterByID() {
axios
.get(`http://localhost:3000/customers/${this.customerId}`)
.then((response) => {
this.customer = response.data;
})
.catch((error) => {
console.log(error);
});
},
这将返回我们需要的数据,以便使用当前存储的数据预填充输入字段:
现在让我们更改正在使用的电子邮件地址,然后单击更新按钮保存更改:
而这将在我们的表格中有所体现:
这要归功于我们的 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);
});
},
这里我们使用 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>
就像我们在编辑模态框中所做的那样,我们也传入了项目的 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>
这里基本上还是重复了我们在前面例子中所做的操作。
当用户执行操作时,我们会发出请求,然后向父数据表组件发送一些事件,以关闭模态框,重新加载数据以反映我们的更改并显示成功提示。
这里我们还有一个关闭模态框的选项,因此还有一个额外的方法,当用户决定采取这种方式时,该方法会发出一个事件。
我们还使用了红色文字和红色背景作为按钮,以向用户表明这是一个非常重要的操作,可能会产生负面后果。
确认删除后,我们可以再次看到成功提示,并可以验证该记录已从表中删除:
结论
希望您喜欢这篇 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












