简介
在上一教程中,您构建了发票应用程序的后台服务器。在本教程中,您将构建用户将与之交互的应用程序部分,即用户界面。
<$>[注] 注: 这是三部曲系列的第二部分。第一篇教程是 如何使用 Node 构建轻量级发票应用程序:数据库和 API。第三篇教程是【如何使用 Vue 和 Node 构建轻量级发票应用程序:JWT 身份验证和发送发票](https://andsky.com/tech/tutorials/how-to-build-a-lightweight-invoicing-app-with-vue-and-node-jwt-authentication-and-sending-invoices)。 <$>
本教程中的用户界面将使用 Vue构建,允许用户登录查看和创建发票。
先决条件
要完成本教程,您需要
- 本地安装 Node.js,您可以按照 如何安装 Node.js 并创建本地开发环境 进行安装。
本教程使用 Node v16.1.0、npm
v7.12.1、Vue v2.6.11、Vue Router v3.2.0、axios
v0.21.1 和 Bootstrap v5.0.1 进行了验证。
步骤 1 - 设置项目
您可以使用 @vue/cli
创建一个新的 Vue.js 项目。
<$>[注]
注意: 您应该可以将这个新项目目录放在上一个教程中创建的 invoicing-app
目录旁边。这引入了将 server
和 client
分开的常见做法。
<$>
在终端窗口中,使用以下命令:
1npx @vue/cli create --inlinePreset='{ "useConfigFiles": false, "plugins": { "@vue/cli-plugin-babel": {}, "@vue/cli-plugin-eslint": { "config": "base", "lintOn": ["save"] } }, "router": true, "routerHistoryMode": true }' invoicing-app-frontend
这将使用内联预设配置,通过 Vue Router 创建 Vue.js 项目。
导航至新建的项目目录:
1cd invoicing-app-frontend
启动项目,验证是否存在错误。
1npm run serve
如果您在网络浏览器中访问本地应用程序(通常位于 localhost:8080
),您将看到一条"欢迎访问您的 Vue.js 应用程序"
的消息。
这将创建一个示例 "Vue "项目,我们将在本文中以此为基础进行构建。
对于该发票应用程序的前端,将向后端服务器发出大量请求。
为此,我们将使用 axios。要安装 axios
,请在项目目录下运行该命令:
1npm install [email protected]
为了允许在应用程序中使用一些默认样式,您将使用 Bootstrap
。
首先,在代码编辑器中打开 "public/index.html "文件。
将 CDN 托管的 Bootstrap CSS 文件添加到文档的 "head "部分:
1[label public/index.html]
2<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x" crossorigin="anonymous">
将 CDN 托管的 Popper 和 Bootstrap JavaScript 文件添加到文档的 "head "部分:
1[label public/index.html]
2<script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js" integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p" crossorigin="anonymous"></script>
3<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-Atwg2Pkwv9vp0ygtn1JAojH0nYbwNJLPhwyoVbhoPwBhjQPR5VtM2+xf0Uwh9KtT" crossorigin="anonymous"></script>
您可以用以下代码行替换 App.vue
中的内容:
1[label src/App.vue]
2<template>
3 <div id="app">
4 <router-view/>
5 </div>
6</template>
你可以忽略或删除自动生成的 src/views/Home.vue
、src/views/About.vue
和 src/components/HelloWorld.vue
文件。
至此,您就有了一个包含 Axios 和 Bootstrap 的新 Vue 项目。
第 2 步 - 配置 Vue 路由器
在此应用中,您将有两条主要路线:
/
呈现登录页面/dashboard
用于显示用户仪表板
要配置这些路由,请打开 src/router/index.js
,并用以下代码行更新:
1[label src/router/index.js]
2import Vue from 'vue'
3import VueRouter from 'vue-router'
4
5import SignUp from '@/components/SignUp'
6import Dashboard from '@/components/Dashboard'
7
8Vue.use(VueRouter)
9
10const routes = [
11 {
12 path: '/',
13 name: 'SignUp',
14 component: SignUp
15 },
16 {
17 path: '/dashboard',
18 name: 'Dashboard',
19 component: Dashboard
20 }
21]
22
23const router = new VueRouter({
24 mode: 'history',
25 base: process.env.BASE_URL,
26 routes
27})
28
29export default router
这指定了用户访问应用程序时应向其显示的组件。
第 3 步 - 创建组件
组件使应用程序的前端更加模块化和可重用。本应用程序将包含以下组件:
- 标题
- 导航
- 注册(和登录)
- 仪表板
- 创建发票
- 查看发票
创建页眉组件
如果用户已登录,"页眉 "组件会显示应用程序名称和 "导航"。
在 src/components
目录中创建一个 Header.vue
文件。该组件文件包含以下代码行:
1[label src/components/Header.vue]
2<template>
3 <nav class="navbar navbar-light bg-light">
4 <div class="navbar-brand m-0 p-3 h1 align-self-start">{{title}}</div>
5 <template v-if="user != null">
6 <Navigation v-bind:name="user.name" v-bind:company="user.company_name"/>
7 </template>
8 </nav>
9</template>
10
11<script>
12import Navigation from './Navigation'
13
14export default {
15 name: "Header",
16 props : ["user"],
17 components: {
18 Navigation
19 },
20 data() {
21 return {
22 title: "Invoicing App",
23 };
24 }
25};
26</script>
页眉组件有一个名为 "用户 "的 "属性"。任何将使用页眉组件的组件都将传递该prop
。在页眉的模板中,会导入导航
组件,并使用条件渲染来确定是否显示导航
。
创建导航组件
导航 "组件是侧边栏,用于放置不同操作的链接。
在 /src/components
目录中创建一个新的 Navigation.vue
组件。该组件的模板如下:
1[label src/components/Navigation.vue]
2<template>
3 <div class="flex-grow-1">
4 <div class="navbar navbar-expand-lg">
5 <ul class="navbar-nav flex-grow-1 flex-row">
6 <li class="nav-item">
7 <a class="nav-link" v-on:click="setActive('create')">Create Invoice</a>
8 </li>
9 <li class="nav-item">
10 <a class="nav-link" v-on:click="setActive('view')">View Invoices</a>
11 </li>
12 </ul>
13 </div>
14 <div class="navbar-text"><em>Company: {{ company }}</em></div>
15 <div class="navbar-text h3">Welcome, {{ name }}</div>
16 </div>
17</template>
18
19...
接下来,在代码编辑器中打开 Navigation.vue
文件,添加以下代码行:
1[label src/components/Navigation.vue]
2...
3
4<script>
5export default {
6 name: "Navigation",
7 props: ["name", "company"],
8 methods: {
9 setActive(option) {
10 this.$parent.$parent.isactive = option;
11 },
12 }
13};
14</script>
创建该组件时包含两个 "道具":用户名和公司名。当用户点击导航链接时,"setActive "方法将更新调用 "导航 "组件父组件(在本例中为 "仪表盘")的组件。
创建注册组件
SignUp "组件包含注册和登录表单。在 /src/components
目录中创建一个新文件。
首先,创建组件:
1[label src/components/SignUp.vue]
2<template>
3 <div class="container">
4 <Header/>
5
6 <ul class="nav nav-tabs" role="tablist">
7 <li class="nav-item" role="presentation">
8 <button class="nav-link active" id="login-tab" data-bs-toggle="tab" data-bs-target="#login" type="button" role="tab" aria-controls="login" aria-selected="true">Login</button>
9 </li>
10 <li class="nav-item" role="presentation">
11 <button class="nav-link" id="register-tab" data-bs-toggle="tab" data-bs-target="#register" type="button" role="tab" aria-controls="register" aria-selected="false">Register</button>
12 </li>
13 </ul>
14
15 <div class="tab-content p-3">
16 ...
17 </div>
18 </div>
19</template>
20
21<script>
22import axios from "axios"
23
24import Header from "./Header"
25
26export default {
27 name: "SignUp",
28 components: {
29 Header
30 },
31 data() {
32 return {
33 model: {
34 name: "",
35 email: "",
36 password: "",
37 c_password: "",
38 company_name: ""
39 },
40 loading: "",
41 status: ""
42 };
43 },
44 methods: {
45 ...
46 }
47}
48</script>
导入 "标题 "组件,并指定组件的数据属性。
接下来,创建方法来处理数据提交时发生的情况:
1[label src/components/SignUp.vue]
2...
3
4 methods: {
5 validate() {
6 // checks to ensure passwords match
7 if (this.model.password != this.model.c_password) {
8 return false;
9 }
10 return true;
11 },
12
13 ...
14 }
15
16...
验证() "方法执行检查,以确保用户发送的数据符合我们的要求。
1[label src/components/SignUp.vue]
2 ...
3
4 methods: {
5 ...
6
7 register() {
8 const formData = new FormData();
9 let valid = this.validate();
10
11 if (valid) {
12 formData.append("name", this.model.name);
13 formData.append("email", this.model.email);
14 formData.append("company_name", this.model.company_name);
15 formData.append("password", this.model.password);
16
17 this.loading = "Registering you, please wait";
18
19 // Post to server
20 axios.post("http://localhost:3128/register", formData).then(res => {
21 // Post a status message
22 this.loading = "";
23
24 if (res.data.status == true) {
25 // now send the user to the next route
26 this.$router.push({
27 name: "Dashboard",
28 params: { user: res.data.user }
29 });
30 } else {
31 this.status = res.data.message;
32 }
33 });
34 } else {
35 alert("Passwords do not match");
36 }
37 },
38
39 ...
40 }
41
42...
当用户尝试注册新账户时,组件的 register
方法会处理相关操作。首先,使用 validate
方法验证数据。然后,如果符合所有条件,就使用 formData
方法准备提交数据。
我们还定义了组件的 loading
属性,以便让用户知道他们的表单何时正在处理。最后,我们使用 axios
向后台服务器发送一个 POST
请求。当服务器收到状态为 "true "的响应时,用户将被引导至仪表板。否则,将向用户显示错误信息。
1[label src/components/SignUp.vue]
2 ...
3
4 methods: {
5 ...
6
7 login() {
8 const formData = new FormData();
9
10 formData.append("email", this.model.email);
11 formData.append("password", this.model.password);
12 this.loading = "Logging In";
13
14 // Post to server
15 axios.post("http://localhost:3128/login", formData).then(res => {
16 // Post a status message
17 this.loading = "";
18
19 if (res.data.status == true) {
20 // now send the user to the next route
21 this.$router.push({
22 name: "Dashboard",
23 params: { user: res.data.user }
24 });
25 } else {
26 this.status = res.data.message;
27 }
28 });
29 }
30 }
31
32...
登录 "方法与 "注册 "方法类似。数据会被准备好并发送到后端服务器以验证用户身份。如果用户存在且详细信息匹配,用户将被引导至其仪表板。
现在来看看注册模板:
1[label src/components/SignUp.vue]
2<template>
3 <div class="container">
4 ...
5
6 <div class="tab-content p-3">
7 <div id="login" class="tab-pane fade show active" role="tabpanel" aria-labelledby="login-tab">
8 <div class="row">
9 <div class="col-md-12">
10 <form @submit.prevent="login">
11 <div class="form-group mb-3">
12 <label for="login-email" class="label-form">Email:</label>
13 <input id="login-email" type="email" required class="form-control" placeholder="[email protected]" v-model="model.email">
14 </div>
15
16 <div class="form-group mb-3">
17 <label for="login-password" class="label-form">Password:</label>
18 <input id="login-password" type="password" required class="form-control" placeholder="Password" v-model="model.password">
19 </div>
20
21 <div class="form-group">
22 <button class="btn btn-primary">Log In</button>
23 {{ loading }}
24 {{ status }}
25 </div>
26 </form>
27 </div>
28 </div>
29 </div>
30
31 ...
32 </div>
33 </div>
34</template>
登录表单如上图所示,输入字段与创建组件时指定的相应数据属性相关联。点击表单的提交按钮后,组件的 "登录 "方法将被调用。
通常,当点击表单的提交按钮时,表单会通过 GET
或 POST
请求提交。我们在创建表格时添加了 <form @submit.prevent="login">
来覆盖默认行为,并指定应调用登录函数,而不是使用这种方式。
注册表也是这样的:
1[label src/components/SignUp.vue]
2<template>
3 <div class="container">
4 ...
5
6 <div class="tab-content p-3">
7 ...
8
9 <div id="register" class="tab-pane fade" role="tabpanel" aria-labelledby="register-tab">
10 <div class="row">
11 <div class="col-md-12">
12 <form @submit.prevent="register">
13 <div class="form-group mb-3">
14 <label for="register-name" class="label-form">Name:</label>
15 <input id="register-name" type="text" required class="form-control" placeholder="Full Name" v-model="model.name">
16 </div>
17
18 <div class="form-group mb-3">
19 <label for="register-email" class="label-form">Email:</label>
20 <input id="register-email" type="email" required class="form-control" placeholder="[email protected]" v-model="model.email">
21 </div>
22
23 <div class="form-group mb-3">
24 <label for="register-company" class="label-form">Company Name:</label>
25 <input id="register-company" type="text" required class="form-control" placeholder="Company Name" v-model="model.company_name">
26 </div>
27
28 <div class="form-group mb-3">
29 <label for="register-password" class="label-form">Password:</label>
30 <input id="register-password" type="password" required class="form-control" placeholder="Password" v-model="model.password">
31 </div>
32
33 <div class="form-group mb-3">
34 <label for="register-confirm" class="label-form">Confirm Password:</label>
35 <input id="register-confirm" type="password" required class="form-control" placeholder="Confirm Password" v-model="model.c_password">
36 </div>
37
38 <div class="form-group mb-3">
39 <button class="btn btn-primary">Register</button>
40 {{ loading }}
41 {{ status }}
42 </div>
43 </form>
44 </div>
45 </div>
46 </div>
47 </div>
48 </div>
49</template>
这里还使用了 @submit.prevent
在点击提交按钮时调用 register
方法。
现在,使用此命令运行开发服务器:
1npm run serve
访问浏览器中的 localhost:8080
查看新创建的登录和注册页面。
<$>[注]
注意: 在尝试使用用户界面时,您需要运行 invoicing-app
服务器。此外,您可能会遇到 CORS(跨源资源共享)错误,可能需要通过设置 Access-Control-Allow-Origin
标头来解决。
<$>
尝试登录和注册新用户。
创建仪表板组件
当用户转到 /dashboard
路由时,将显示仪表盘组件。默认情况下,它会显示 "标题 "和 "创建发票 "组件。
在 src/components
目录中创建 Dashboard.vue
文件。该组件包含以下代码行:
1[label src/component/Dashboard.vue]
2<template>
3 <div class="container">
4 <Header v-bind:user="user"/>
5 <template v-if="this.isactive == 'create'">
6 <CreateInvoice />
7 </template>
8 <template v-else>
9 <ViewInvoices />
10 </template>
11 </div>
12</template>
13
14...
在模板下方添加以下代码行:
1[label src/component/Dashboard.vue]
2...
3
4<script>
5import Header from "./Header";
6import CreateInvoice from "./CreateInvoice";
7import ViewInvoices from "./ViewInvoices";
8
9export default {
10 name: "Dashboard",
11 components: {
12 Header,
13 CreateInvoice,
14 ViewInvoices,
15 },
16 data() {
17 return {
18 isactive: 'create',
19 title: "Invoicing App",
20 user : (this.$route.params.user) ? this.$route.params.user : null
21 };
22 }
23};
24</script>
创建创建发票组件
创建发票 "组件包含创建新发票所需的表单。在 src/components
目录中创建一个新文件:
编辑 CreateInvoice
组件,使其看起来像这样:
1[label src/components/CreateInvoice.vue]
2<template>
3 <div class="container">
4 <div class="tab-pane p-3 fade show active">
5 <div class="row">
6 <div class="col-md-12">
7 <h3>Enter details below to create invoice</h3>
8 <form @submit.prevent="onSubmit">
9 <div class="form-group mb-3">
10 <label for="create-invoice-name" class="form-label">Invoice Name:</label>
11 <input id="create-invoice-name" type="text" required class="form-control" placeholder="Invoice Name" v-model="invoice.name">
12 </div>
13
14 <div class="form-group mb-3">
15 Invoice Price: <span>${{ invoice.total_price }}</span>
16 </div>
17
18 ...
19 </form>
20 </div>
21 </div>
22 </div>
23 </div>
24</template>
这将创建一个接受发票名称并显示发票总价的表单。总价是将该发票各个交易的价格相加得出的。
让我们看看交易是如何添加到发票中的:
1[label src/components/CreateInvoice.vue]
2 ...
3
4 <form @submit.prevent="onSubmit">
5 ...
6
7 <hr />
8 <h3>Transactions </h3>
9 <div class="form-group">
10 <button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#transactionModal">Add Transaction</button>
11
12 <!-- Modal -->
13 <div class="modal fade" id="transactionModal" tabindex="-1" aria-labelledby="transactionModalLabel" aria-hidden="true">
14 <div class="modal-dialog" role="document">
15 <div class="modal-content">
16 <div class="modal-header">
17 <h5 class="modal-title" id="exampleModalLabel">Add Transaction</h5>
18 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
19 </div>
20 <div class="modal-body">
21 <div class="form-group mb-3">
22 <label for="txn_name_modal" class="form-label">Transaction name:</label>
23 <input id="txn_name_modal" type="text" class="form-control">
24 </div>
25 <div class="form-group mb-3">
26 <label for="txn_price_modal" class="form-label">Price ($):</label>
27 <input id="txn_price_modal" type="numeric" class="form-control">
28 </div>
29 </div>
30 <div class="modal-footer">
31 <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Discard Transaction</button>
32 <button type="button" class="btn btn-primary" data-bs-dismiss="modal" v-on:click="saveTransaction()">Save Transaction</button>
33 </div>
34 </div>
35 </div>
36 </div>
37
38 </div>
39
40 ...
41 </form>
42
43 ...
系统会显示一个按钮,供用户添加新交易。点击 "添加交易 "按钮后,系统会显示一个模态窗口,让用户输入交易的详细信息。单击 "保存交易 "按钮后,一个方法会将该交易添加到现有交易中。
1[label src/components/CreateInvoice.vue]
2 ...
3
4 <form @submit.prevent="onSubmit">
5 ...
6
7 <div class="col-md-12">
8 <table class="table">
9 <thead>
10 <tr>
11 <th scope="col">#</th>
12 <th scope="col">Transaction Name</th>
13 <th scope="col">Price ($)</th>
14 <th scope="col"></th>
15 </tr>
16 </thead>
17 <tbody>
18 <template v-for="txn in transactions">
19 <tr :key="txn.id">
20 <th>{{ txn.id }}</th>
21 <td>{{ txn.name }}</td>
22 <td>{{ txn.price }} </td>
23 <td><button type="button" class="btn btn-danger" v-on:click="deleteTransaction(txn.id)">Delete</button></td>
24 </tr>
25 </template>
26 </tbody>
27 </table>
28 </div>
29
30 <div class="form-group">
31 <button class="btn btn-primary">Create Invoice</button>
32 {{ loading }}
33 {{ status }}
34 </div>
35 </form>
36
37 ...
现有交易以表格形式显示。点击删除 按钮后,有关交易将从交易列表中删除,并重新计算 "发票价格"。最后,"创建发票 "按钮会触发一个函数,然后准备数据并将其发送到后台服务器,以便创建发票。
我们也来看看 "创建发票 "组件的组件结构:
1[label src/components/CreateInvoice.vue]
2...
3
4<script>
5import axios from "axios";
6
7export default {
8 name: "CreateInvoice",
9 data() {
10 return {
11 invoice: {
12 name: "",
13 total_price: 0
14 },
15 transactions: [],
16 nextTxnId: 1,
17 loading: "",
18 status: ""
19 };
20 },
21 methods: {
22 ...
23 }
24};
25</script>
首先,您定义了组件的数据属性。该组件将有一个发票对象,其中包含发票的 "名称 "和 "总价"。它还将拥有一个包含 nextTxnId
索引的 transactions
数组。这将跟踪交易和变量,以便向用户发送状态更新。
1[label src/components/CreateInvoice.vue]
2 ...
3
4 methods: {
5 saveTransaction() {
6 // append data to the arrays
7 let name = document.getElementById("txn_name_modal").value;
8 let price = document.getElementById("txn_price_modal").value;
9
10 if (name.length != 0 && price > 0) {
11 this.transactions.push({
12 id: this.nextTxnId,
13 name: name,
14 price: price
15 });
16
17 this.nextTxnId++;
18 this.calcTotal();
19
20 // clear their values
21 document.getElementById("txn_name_modal").value = "";
22 document.getElementById("txn_price_modal").value = "";
23 }
24 },
25
26 ...
27 }
28
29...
这里还定义了 CreateInvoice
组件的方法。saveTransaction() "方法获取事务表单模态中的值,然后将它们添加到事务列表中。deleteTransaction() "方法从事务列表中删除现有事务对象,而 "calcTotal() "方法则在添加或删除新事务时重新计算发票总价。
1[label src/components/CreateInvoice.vue]
2 ...
3
4 methods: {
5 ...
6
7 deleteTransaction(id) {
8 let newList = this.transactions.filter(function(el) {
9 return el.id !== id;
10 });
11
12 this.nextTxnId--;
13 this.transactions = newList;
14 this.calcTotal();
15 },
16
17 calcTotal() {
18 let total = 0;
19
20 this.transactions.forEach(element => {
21 total += parseInt(element.price, 10);
22 });
23 this.invoice.total_price = total;
24 },
25
26 ...
27 }
28
29...
最后,onSubmit()
方法会将表单提交到后台服务器。在该方法中,formData
和 axios
用于发送请求。包含事务对象的事务数组被分成两个不同的数组。一个数组包含交易名称,另一个包含交易价格。然后,服务器会尝试处理请求并向用户发回响应。
1[label src/components/CreateInvoice.vue]
2 ...
3
4 methods: {
5 ...
6
7 onSubmit() {
8 const formData = new FormData();
9
10 this.transactions.forEach(element => {
11 formData.append("txn_names[]", element.name);
12 formData.append("txn_prices[]", element.price)
13 });
14
15 formData.append("name", this.invoice.name);
16 formData.append("user_id", this.$route.params.user.id);
17 this.loading = "Creating Invoice, please wait ...";
18
19 // Post to server
20 axios.post("http://localhost:3128/invoice", formData).then(res => {
21 // Post a status message
22 this.loading = "";
23
24 if (res.data.status == true) {
25 this.status = res.data.message;
26 } else {
27 this.status = res.data.message;
28 }
29 });
30 }
31 }
32
33...
当您回到 localhost:8080
上的应用程序并登录时,您将被重定向到一个仪表板。
创建 ViewInvoice 组件
现在您可以创建发票了,下一步就是创建发票及其状态的可视化图片。为此,请在应用程序的 src/components
目录中创建一个 ViewInvoices.vue
文件。
编辑文件,使其看起来像这样:
1[label src/components/ViewInvoices.vue]
2<template>
3 <div>
4 <div class="tab-pane p-3 fade show active">
5 <div class="row">
6 <div class="col-md-12">
7 <h3>Here is a list of your invoices</h3>
8 <table class="table">
9 <thead>
10 <tr>
11 <th scope="col">Invoice #</th>
12 <th scope="col">Invoice Name</th>
13 <th scope="col">Status</th>
14 <th scope="col"></th>
15 </tr>
16 </thead>
17 <tbody>
18 <template v-for="invoice in invoices">
19 <tr :key="invoice.id">
20 <th scope="row">{{ invoice.id }}</th>
21 <td>{{ invoice.name }}</td>
22 <td v-if="invoice.paid == 0">Unpaid</td>
23 <td v-else>Paid</td>
24 <td><a href="#" class="btn btn-success">To Invoice</a></td>
25 </tr>
26 </template>
27 </tbody>
28 </table>
29 </div>
30 </div>
31 </div>
32 </div>
33</template>
34
35...
上面的模板包含一个表格,显示用户创建的发票。它还包含一个按钮,当用户点击发票时,该按钮会将用户带入单张发票页面。
1[label src/components/ViewInvoice.vue]
2...
3
4<script>
5import axios from "axios";
6
7export default {
8 name: "ViewInvoices",
9 data() {
10 return {
11 invoices: [],
12 user: this.$route.params.user
13 };
14 },
15 mounted() {
16 axios
17 .get(`http://localhost:3128/invoice/user/${this.user.id}`)
18 .then(res => {
19 if (res.data.status == true) {
20 this.invoices = res.data.invoices;
21 }
22 });
23 }
24};
25</script>
查看发票 "组件的数据属性是发票数组和用户详细信息。用户详细信息是从路由参数中获取的。当该组件被 "挂载 "时,会向后台服务器发出 "GET "请求,以获取用户创建的发票列表,然后使用前面显示的模板显示出来。
进入"/仪表板 "后,单击 "导航 "中的 "查看发票 "**选项,即可看到发票和付款状态列表。
结论
在本系列的这一部分中,您使用 Vue 的概念配置了发票应用程序的用户界面。