父子节点 构建_如何使用节点构建轻量级发票应用程序:用户界面

news/2024/8/26 18:18:46

父子节点 构建

介绍 (Introduction)

In the first part of this series, you set up the backend server for the invoicing application. In this tutorial you will build the part of the application that users will interact with, known as the user interface.

在本系列的第一部分中,您将为发票应用程序设置后端服务器。 在本教程中,您将构建用户将与之交互的应用程序部分,即用户界面。

先决条件 (Prerequisites)

To follow through this article, you’ll need the following:

要继续阅读本文,您需要满足以下条件:

  • Node installed on your machine

    安装在计算机上的节点
  • NPM installed on your machine

    NPM安装在您的计算机上
  • Have read through the first part of this series.

    阅读了本系列的第一部分 。

第1步-安装Vue (Step 1 — Installing Vue)

To build the frontend of this application, you’ll be using Vue. Vue is a progressive JavaScript framework used for building useful and interactive user interfaces. To install Vue, run the following command:

要构建此应用程序的前端,您将使用Vue 。 Vue是一个渐进式JavaScript框架,用于构建有用的交互式用户界面。 要安装Vue,请运行以下命令:

  • npm install -g vue-cli

    npm install -g vue-cli

To confirm your Vue installation, run this command:

要确认您的Vue安装,请运行以下命令:

vue --version

The version number will be returned if Vue is installed.

如果安装了Vue,将返回版本号。

第2步-创建项目 (Step 2 — Creating the Project)

To create a new project with Vue, run the following command and then follow the prompt:

要使用Vue创建新项目,请运行以下命令,然后按照提示进行操作:

  • vue init webpack invoicing-app-frontend

    Vue初始化Webpack发票应用前端

This creates a sample Vue project that we’ll build upon in this article.

这将创建一个示例Vue项目,在本文中将以此为基础。

For the frontend of this invoicing application, a lot of requests are going to be made to the backend server. To do this, we’ll make use of axios. To install axios, run the command in your project directory:

对于此开票应用程序的前端,将对后端服务器提出许多请求。 为此,我们将使用axios 。 要安装axios ,请在您的项目目录中运行命令:

  • npm install axios --save

    npm install axios-保存

To allow some default styling in the application, you will make use of Bootstrap. To add it to your application, add the following to the index.html file in the project:

要在应用程序中允许某些默认样式,您将使用Bootstrap 。 要将其添加到您的应用程序中,请将以下内容添加到项目中的index.html文件中:

index.html
index.html
...
      <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">
    ...
        <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js" integrity="sha384-cs/chFZiN24E4KMATLdqdvsezGxaGsi4hLGOzlXwp5UZB1LY//20VyM2taTB4QvJ" crossorigin="anonymous"></script>
        <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js" integrity="sha384-uefMccjFJAIv6A+rW+L4AHf99KvxDjWSu1z9VI8SKNVmz4sk7buKt/6v9KI65qnm"crossorigin="anonymous"></script>
    ...

步骤3 —配置Vue路由器 (Step 3 — Configuring Vue Router)

For this application, you are going to have two major routes:

对于此应用程序,您将有两条主要路线:

  • / to render the login page

    /呈现登录页面

  • /dashboard to render the user dashboard

    /dashboard呈现用户仪表板

To configure these routes, open the src/router/index.js and update it to look like this:

要配置这些路由,请打开src/router/index.js并将其更新为如下所示:

src/router/index.js
src /路由器/index.js
import Vue from 'vue'
import Router from 'vue-router'
import SignUp from '@/components/SignUp'
import Dashboard from '@/components/Dashboard'
Vue.use(Router)
export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'SignUp',
      component: SignUp
    },
    {
      path: '/dashboard',
      name: 'Dashboard',
      component: Dashboard
    },
  ]
})

This specifies the components that should be displayed to the user when they visit your application.

这指定了当用户访问您的应用程序时应向用户显示的组件。

第4步-创建组件 (Step 4 — Creating Components)

One of the major selling points of Vue is the component structure. Components allow the frontend of your application to be more modular and reusable. This application will have the following components:

Vue的主要卖点之一是组件结构。 组件使您的应用程序的前端更加模块化和可重用。 该应用程序将具有以下组件:

  • Sign Up/Sign In

    注册登录
  • Header

    标头
  • Navigation

    导航
  • Dashboard

    仪表板
  • View Invoices

    查看发票
  • Create Invoice

    创建发票
  • Single Invoice

    单张发票

The Navigation component is the sidebar that will house the links of different actions. Create a new component in the /src/components directory:

导航组件是侧栏,将包含不同操作的链接。 在/src/components目录中创建一个新组件:

  • touch SideNav.vue

    触摸SideNav.vue

Now, edit the SideNav.vue like this:

现在,像这样编辑SideNav.vue

src/components/SideNav.vue
src / components / SideNav.vue
<script>
export default {
  name: "SideNav",
  props: ["name", "company"],
  methods: {
   setActive(option) {
      this.$parent.$parent.isactive = option;
    },
    openNav() {
      document.getElementById("leftsidenav").style.width = "20%";
    },
    closeNav() {
      document.getElementById("leftsidenav").style.width = "0%";
    }
  }
};
</script>

...

The component is created with two props: the name of the user and the name of the company. The two methods add the collapsible functionality to the sidebar. The setActive method will update the component calling the parent of the SideNav component, in this case Dashboard, when a user clicks on a navigation link.

该组件使用两个props创建:用户名和公司名。 这两种方法将可折叠功能添加到侧栏。 当用户单击导航链接时, setActive方法将更新调用SideNav组件父级的组件,在本例中为Dashboard

The component has the following template:

该组件具有以下模板:

src/components/SideNav.vue
src / components / SideNav.vue
...
  <template>
    <div>
        <span style="font-size:30px;cursor:pointer" v-on:click="openNav">&#9776;</span>
        <div id="leftsidenav" class="sidenav">
            <p style="font-size:12px;cursor:pointer" v-on:click="closeNav"><em>Close Nav</em></p>
            <p><em>Company: {{ company }} </em></p>
            <h3>Welcome, {{ name }}</h3>
            <p class="clickable" v-on:click="setActive('create')">Create Invoice</p>
            <p class="clickable" v-on:click="setActive('view')">View Invoices</p>
        </div>
    </div>
</template>
...

The Header component displays the name of the application and side bar if a user is signed in. Create a Header.vue file in the src/components directory:

如果用户登录,则Header组件将显示应用程序的名称和侧栏。在src/components目录中创建Header.vue文件:

  • touch Header.vue

    触摸Header.vue

The component file will look like this:

组件文件将如下所示:

src/components/Header.vue
src / components / Header.vue
<template>
    <nav class="navbar navbar-light bg-light">
        <template v-if="user != null">
            <SideNav v-bind:name="user.name" v-bind:company="user.company_name"/>
        </template>
        <span class="navbar-brand mb-0 h1">{{title}}</span>
    </nav>
</template>
<script>
import SideNav from './SideNav'
export default {
  name: "Header",
  props : ["user"],
  components: {
    SideNav
  },
  data() {
    return {
      title: "Invoicing App",
    };
  }
};
</script>

The header component has a single prop called user. This prop will be passed by any component that will use the header component. In template for the header, the SideNav component created earlier is imported and conditional rendering is used to determine if the SideNav should be displayed or not.

标头组件具有一个称为user prop 。 该prop将由将使用标头组件的任何组件传递。 在标题模板中,将导入先前创建的SideNav组件,并使用条件渲染来确定是否应显示SideNav

The SignIn component houses the sign up and sign in form. Create a new file in /src/components directory:

SignIn组件包含注册和登录表单。 在/src/components目录中创建一个新文件:

  • touch SignIn.vue

    触摸SignIn.vue

The component to register and login a user is a little more complex than the two earlier components. The app needs to have the login and register forms on the same page.

注册和登录用户的组件比前两个组件要复杂一些。 该应用程序需要在同一页面上具有登录和注册表单。

First, create the component:

首先,创建组件:

src/components/SignIn.vue
src / components / SignIn.vue
<script>
import Header from "./Header";
import axios from "axios";
export default {
  name: "SignUp",
  components: {
    Header
  },
  data() {
    return {
      model: {
        name: "",
        email: "",
        password: "",
        c_password: "",
        company_name: ""
      },
      loading: "",
      status: ""
    };
  },
  ...

The Header component is imported and the data properties of the components are also specified. Next, create the methods to handle what happens when data is submitted:

导入了Header组件,并且还指定了组件的数据属性。 接下来,创建用于处理提交数据时发生的情况的方法:

src/components/SignIn.vue
src / components / SignIn.vue
...
  methods: {
    validate() {
      // checks to ensure passwords match
      if( this.model.password != this.model.c_password){
        return false;
      }
      return true;
    },
    ...

The validate() method performs checks to make sure the data sent by the user meets our requirements.

validate()方法执行检查以确保用户发送的数据符合我们的要求。

src/components/SignIn.vue
src / components / SignIn.vue
...
register() {
const formData = new FormData();
let valid = this.validate();
if(valid){
formData.append("name", this.model.name);
formData.append("email", this.model.email);
formData.append("company_name", this.model.company_name);
formData.append("password", this.model.password);

this.loading = "Registering you, please wait";
// Post to server
axios.post("http://localhost:3128/register", formData).then(res => {
  // Post a status message
  this.loading = "";
  if (res.data.status == true) {
    // now send the user to the next route
    this.$router.push({
      name: "Dashboard",
      params: { user: res.data.user }
    });
  } else {
    this.status = res.data.message;
  }
});
  }else{
alert("Passwords do not match");
  }
},
...

The register method of the component handles the action when a user tries to register a new account. First, the data is validated using the validate method. Then if all criteria are met, the data is prepared for submission using the formData.

当用户尝试注册新帐户时,组件的register方法将处理该操作。 首先,使用validate方法验证数据。 然后,如果满足所有条件,则准备使用formData提交数据。

We’ve also defined the loading property of the component to let the user know when their form is being processed. Finally, a POST request is sent to the backend server using axios. When a response is received from the server with a status of true, the user is directed to the dashboard. Otherwise, an error message is displayed to the user.

我们还定义了组件的loading属性,以使用户知道何时处理其表单。 最后,使用axios将POST请求发送到后端服务器。 当从服务器收到状态为true的响应时, true用户定向到仪表板。 否则,将向用户显示错误消息。

src/components/SignIn.vue
src / components / SignIn.vue
...
  login() {
  const formData = new FormData();
  formData.append("email", this.model.email);
  formData.append("password", this.model.password);
  this.loading = "Signing in";
  // Post to server
  axios.post("http://localhost:3128/login", formData).then(res => {
    // Post a status message
    this.loading = "";
    if (res.data.status == true) {
      // now send the user to the next route
      this.$router.push({
        name: "Dashboard",
        params: { user: res.data.user }
      });
    } else {
      this.status = res.data.message;
    }
  });
}
  }
};
</script>

Just like the register method, the data is prepared and sent over to the backend server to authenticate the user. If the user exists and the details match, the user is directed to their dashboard.

就像register方法一样,准备数据并将其发送到后端服务器以对用户进行身份验证。 如果用户存在并且详细信息匹配,则将用户定向到其仪表板。

Now, take a look at the template for the SignUp component:

现在,看一下SignUp组件的模板:

src/components/SignUp.vue
src / components / SignUp.vue
<template>
   ...
   <div class="tab-pane fade show active" id="pills-login" role="tabpanel" aria-labelledby="pills-login-tab">
  <div class="row">
  <div class="col-md-12">
      <form @submit.prevent="login">
          <div class="form-group">
              <label for="">Email:</label>
              <input type="email" required class="form-control" placeholder="eg chris@invoiceapp.com" v-model="model.email">
          </div>

          <div class="form-group">
              <label for="">Password:</label>
              <input type="password" required class="form-control" placeholder="Enter Password" v-model="model.password">
          </div>

          <div class="form-group">
              <button class="btn btn-primary" >Login</button>
              {{ loading }}
              {{ status }}
          </div>
      </form> 
  </div>
  </div>
  </div>
  ...

The login in form is shown above and the input fields are linked to the respective data properties specified when the components were created. When the submit button of the form is clicked, the login method of the component is called.

上面以登录形式显示,输入字段链接到创建组件时指定的相应数据属性。 单击表单的提交按钮时,将调用组件的login方法。

Usually, when the submit button of a form is clicked, the form is submitted via a GET or POST request. Instead of using that, we added <form @submit.prevent="login"> when creating the form to override the default behavior and specify that the login function should be called.

通常,当单击表单的提交按钮时, 通过GETPOST请求提交表单。 代替使用它,我们在创建表单时添加了<form @submit.prevent="login">以覆盖默认行为并指定应调用登录功能。

The register form also looks like this:

注册表格也如下所示:

src/components/SignIn.vue
src / components / SignIn.vue
...
<div class="tab-pane fade" id="pills-register" role="tabpanel" aria-labelledby="pills-register-tab">
  <div class="row">
  <div class="col-md-12">
      <form @submit.prevent="register">
          <div class="form-group">
              <label for="">Name:</label>
              <input type="text" required class="form-control" placeholder="eg Chris" v-model="model.name">
          </div>
          <div class="form-group">
              <label for="">Email:</label>
              <input type="email" required class="form-control" placeholder="eg chris@invoiceapp.com" v-model="model.email">
          </div>

          <div class="form-group">
              <label for="">Company Name:</label>
              <input type="text" required class="form-control" placeholder="eg Chris Tech" v-model="model.company_name">
          </div>
          <div class="form-group">
              <label for="">Password:</label>
              <input type="password" required class="form-control" placeholder="Enter Password" v-model="model.password">
          </div>
          <div class="form-group">
              <label for="">Confirm Password:</label>
              <input type="password" required class="form-control" placeholder="Confirm Passowrd" v-model="model.confirm_password">
          </div>
          <div class="form-group">
              <button class="btn btn-primary" >Register</button>
              {{ loading }}
              {{ status }}
          </div>
      </form>
  </div>
  </div>
  ...
</template>

The @submit.prevent is also used here to call the register method when the submit button is clicked.

单击提交按钮时,这里还使用@submit.prevent来调用register方法。

Now, run your development server using this command:

现在,使用以下命令运行开发服务器:

  • npm run dev

    npm run dev

Visit localhost:8080/ on your browser to see the newly created login and sign up page.

在浏览器上访问localhost:8080/以查看新创建的登录和注册页面。

The Dashboard component will be displayed when the user gets routed to the /dashboard route. It displays the Header and the CreateInvoice component by default. Create the Dashboard.vue file in the src/components directory

当用户被路由到/dashboard路由时,将显示Dashboard组件。 默认情况下,它显示HeaderCreateInvoice组件。 在src/components目录中创建Dashboard.vue文件

  • touch Dashboard.vue

    触摸Dashboard.vue

Edit the file to look like this:

编辑文件,如下所示:

src/component/Dashboard.vue
src / component / Dashboard.vue
<script>
import Header from "./Header";
import CreateInvoice from "./CreateInvoice";
import ViewInvoices from "./ViewInvoices";
export default {
  name: "Dashboard",
  components: {
    Header,
    CreateInvoice,
    ViewInvoices,
  },
  data() {
    return {
      isactive: 'create',
      title: "Invoicing App",
      user : (this.$route.params.user) ? this.$route.params.user : null
    };
  }
};
</script>
...

Above, the necessary components are imported and rendered based on the template below:

上面,根据以下模板导入和渲染了必要的组件:

src/component/Dashboard.vue
src / component / Dashboard.vue
...
<template>
  <div class="container-fluid" style="padding: 0px;">
    <Header v-bind:user="user"/>
    <template v-if="this.isactive == 'create'">
      <CreateInvoice />
    </template>
    <template v-else>
      <ViewInvoices />
    </template>
  </div>
</template>

The CreateInvoice component contains the form needed to create a new invoice. Create a new file in the src/components directory:

CreateInvoice组件包含创建新发票所需的表单。 在src/components目录中创建一个新文件:

  • touch CreateInvoice.vue

    触摸CreateInvoice.vue

Edit the CreateInvoice component to look like this:

编辑CreateInvoice组件,如下所示:

src/components/CreateInvoice.vue
src / components / CreateInvoice.vue
<template>
  <div>
    <div class="container">
      <div class="tab-pane fade show active">
        <div class="row">
          <div class="col-md-12">
            <h3>Enter Details below to Create Invoice</h3>
            <form @submit.prevent="onSubmit">
              <div class="form-group">
                <label for="">Invoice Name:</label>
                  <input type="text" required class="form-control" placeholder="eg Seller's Invoice" v-model="invoice.name">
              </div>
              <div class="form-group">
                <label for="">Invoice Price:</label><span> $ {{ invoice.total_price }}</span>
              </div>
              ...

This creates a form that accepts the name of the invoice and displays the total price of the invoice. The total price is obtained by summing up the prices of individual transactions for the invoice.

这将创建一个接受发票名称并显示发票总价的表格。 总价是通过将发票的各个交易的价格相加得出的。

Let’s take a look at how transactions are added to the invoice:

让我们看一下如何将交易添加到发票中:

src/components/CreateInvoice.vue
src / components / CreateInvoice.vue
...
<hr />
<h3> Transactions </h3>
<div class="form-group">
  <label for="">Add Transaction:</label>
  <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#transactionModal">
    +
  </button>
  <!-- Modal -->
  <div class="modal fade" id="transactionModal" tabindex="-1" role="dialog" aria-labelledby="transactionModalLabel" aria-hidden="true">
    <div class="modal-dialog" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="modal-title" id="exampleModalLabel">Add Transaction</h5>
          <button type="button" class="close" data-dismiss="modal" aria-label="Close">
            <span aria-hidden="true">&times;</span>
          </button>
        </div>
        <div class="modal-body">
          <div class="form-group">
            <label for="">Transaction name</label>
            <input type="text" id="txn_name_modal" class="form-control">
          </div>
          <div class="form-group">
            <label for="">Price ($)</label>
            <input type="numeric" id="txn_price_modal" class="form-control">
          </div>
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-secondary" data-dismiss="modal">Discard Transaction</button>
          <button type="button" class="btn btn-primary" data-dismiss="modal" v-on:click="saveTransaction()">Save transaction</button>
        </div>
      </div>
    </div>
  </div>
</div>
...

A button is displayed for the user to add a new transaction. When the button is clicked, a modal is shown to the user to enter the details of the transaction. When the Save Transaction button is clicked, a method adds it to the existing transactions.

显示一个按钮,供用户添加新交易。 单击该按钮时,将向用户显示一个模式,以输入交易的详细信息。 单击“ Save Transaction按钮时,一种方法会将其添加到现有事务中。

src/components/CreateInvoice.vue
src / components / CreateInvoice.vue
...
<div class="col-md-12">
  <table class="table">
    <thead>
      <tr>
        <th scope="col">#</th>
        <th scope="col">Transaction Name</th>
        <th scope="col">Price ($)</th>
        <th scope="col"></th>
      </tr>
    </thead>
    <tbody>
      <template v-for="txn in transactions">
        <tr :key="txn.id">
          <th>{{ txn.id }}</th>
          <td>{{ txn.name }}</td>
          <td>{{ txn.price }} </td>
          <td><button type="button" class="btn btn-danger" v-on:click="deleteTransaction(txn.id)">X</button></td>
        </tr>
      </template>
    </tbody>
  </table>
</div>

  <div class="form-group">
    <button class="btn btn-primary" >Create Invoice</button>
    {{ loading }}
    {{ status }}
  </div>
</form> 
  </div>
</div>
  </div>
</div>
  </div>
</template>
...

The existing transactions are displayed in a tabular format. When the X button is clicked, the transaction in question is deleted from the transaction list and the Invoice Price is recalculated. Finally, the Create Invoice button triggers a function that then prepares the data and sends it to the backend server for creation of the invoice.

现有交易以表格格式显示。 单击X按钮后,相关交易将从交易列表中删除,并重新计算nvoice Price 。 最后,“ Create Invoice按钮将触发一个函数,该函数随后准备数据并将其发送到后端服务器以创建发票。

Let’s also take a look at the component structure of the Create Invoice component:

我们还看一下“ Create Invoice组件的组件结构:

src/components/CreateInvoice.vue
src / components / CreateInvoice.vue
...
<script>
import axios from "axios";
export default {
  name: "CreateInvoice",
  data() {
    return {
      invoice: {
        name: "",
        total_price: 0
      },
      transactions: [],
      nextTxnId: 1,
      loading: "",
      status: ""
    };
  },
  ...

First, you defined the data properties for the component. The component will have an invoice object containing the invoice name and total_price. It’ll also have an array of transactions with the nextTxnId index. This will keep track of the transactions and variables to send status updates to the user.

首先,您定义了组件的数据属性。 该组件将具有包含发票nametotal_price的发票对象。 它还将具有一个带有nextTxnId索引的transactions数组。 这将跟踪事务和变量,以将状态更新发送给用户。

src/components/CreateInvoice.vue
src / components / CreateInvoice.vue
...
  methods: {
    saveTransaction() {
      // append data to the arrays
      let name = document.getElementById("txn_name_modal").value;
      let price = document.getElementById("txn_price_modal").value;

      if( name.length != 0 && price > 0){
        this.transactions.push({
          id: this.nextTxnId,
          name: name,
          price: price
        });
        this.nextTxnId++;
        this.calcTotal();
        // clear their values
        document.getElementById("txn_name_modal").value = "";
        document.getElementById("txn_price_modal").value = "";
      }
    },
    ...

The methods for the CreateInvoice component are also defined here. The saveTransaction() method takes the values in the transaction form modal and then adds it to the transaction list. The deleteTransaction() method deletes an existing transaction object from the list of transactions while the calcTotal() method recalculates the total invoice price when a new transaction is added or deleted.

CreateInvoice组件的方法也在此处定义。 saveTransaction()方法采用交易形式模式中的值,然后将其添加到交易列表中。 deleteTransaction()方法从事务列表中删除现有事务对象,而calcTotal()方法在添加或删除新事务时重新计算总发票价格。

src/components/CreateInvoice.vue
src / components / CreateInvoice.vue
...
deleteTransaction(id) {
  let newList = this.transactions.filter(function(el) {
    return el.id !== id;
  });
  this.nextTxnId--;
  this.transactions = newList;
  this.calcTotal();
},
calcTotal(){
  let total = 0;
  this.transactions.forEach(element => {
    total += parseInt(element.price);
  });
  this.invoice.total_price = total;
},
...

Finally, the onSubmit() method will submit the form to the backend server. In the method, formData and axios are used to send the requests. The transaction array containing the transaction objects is split into two different arrays. One array holds the transaction names and the other holds the transaction prices. The server then attempts to process the request and send back a response to the user.

最后, onSubmit()方法会将表单提交到后端服务器。 在该方法中, formDataaxios用于发送请求。 包含事务对象的事务数组分为两个不同的数组。 一个数组保存交易名称,另一个保存交易价格。 然后,服务器尝试处理该请求,并将响应发送回用户。

src/components/CreateInvoice.vue
src / components / CreateInvoice.vue
onSubmit() {
  const formData = new FormData();
  // format for request
  let txn_names = [];
  let txn_prices = [];
  this.transactions.forEach(element => {
    txn_names.push(element.name);
    txn_prices.push(element.price);
  });
  formData.append("name", this.invoice.name);
  formData.append("txn_names", txn_names);
  formData.append("txn_prices", txn_prices);
  formData.append("user_id", this.$route.params.user.id);
  this.loading = "Creating Invoice, please wait ...";
  // Post to server
  axios.post("http://localhost:3128/invoice", formData).then(res => {
    // Post a status message
    this.loading = "";
    if (res.data.status == true) {
      this.status = res.data.message;
    } else {
      this.status = res.data.message;
    }
  });
}
  }
};
</script>

When you go back to the application on localhost:8080 and sign in, you will get redirected to a dashboard.

当您回到localhost:8080上的应用程序并登录时,您将被重定向到仪表板。

Now that you can create invoices, the next step is to create a visual picture of invoices and their statuses. To do this, create a ViewInvoices.vue file in the src/components directory of the application.

现在您可以创建发票,下一步是创建发票及其状态的可视化图片。 为此,请在应用程序的src/components目录中创建一个ViewInvoices.vue文件。

  • touch ViewInvoices.vue

    触摸ViewInvoices.vue

Edit the file to look like this:

编辑文件,如下所示:

src/components/ViewInvoices.vue
src / components / ViewInvoices.vue
<template>
  <div>
    <div class="container">
      <div class="tab-pane fade show active">
        <div class="row">
          <div class="col-md-12">
            <h3>Here are a list of your Invoices</h3>
            <table class="table">
              <thead>
                <tr>
                  <th scope="col">Invoice #</th>
                  <th scope="col">Invoice Name</th>
                  <th scope="col">Status</th>
                  <th scope="col"></th>
                </tr>
              </thead>
              <tbody>
                <template v-for="invoice in invoices">
                  <tr>
                    <th scope="row">{{ invoice.id }}</th>
                    <td>{{ invoice.name }}</td>
                    <td v-if="invoice.paid == 0 "> Unpaid </td>
                    <td v-else> Paid </td>
                    <td ><a href="#" class="btn btn-success">TO INVOICE</a></td>                         </tr>
                </template>
              </tbody>
            </table>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
    ...

The template above contains a table displaying the invoices a user has created. It also has a button that takes the user to a single invoice page when an invoice is clicked.

上面的模板包含一个显示用户创建的发票的表。 它还具有一个按钮,单击发票时,该按钮会将用户带到单个发票页面。

src/components/ViewInvoice.vue
src / components / ViewInvoice.vue
...
<script>
import axios from "axios";
export default {
  name: "ViewInvoices",
  data() {
    return {
      invoices: [],
      user: this.$route.params.user
    };
  },
  mounted() {
    axios
      .get(`http://localhost:3128/invoice/user/${this.user.id}`)
      .then(res => {
        if (res.data.status == true) {
          this.invoices = res.data.invoices;
        }
      });
  }
};
</script>

The ViewInvoices component has its data properties as an array of invoices and the user details. The user details are obtained from the route parameters. When the component is mounted, a GET request is made to the backend server to fetch the list of invoices created by the user which are then displayed using the template that was shown earlier.

ViewInvoices组件的数据属性为发票和用户详细信息的数组。 用户详细信息从路由参数获取。 mounted组件后,将向后端服务器发出GET请求,以获取用户创建的发票清单,然后使用之前显示的模板显示这些清单。

When you go to the Dashboard, click the View Invoices option on the SideNav to see a listing of invoices with the payment status.

当您转到仪表板时,单击SideNav上的“ 查看发票”选项,以查看具有付款状态的发票清单。

结论 (Conclusion)

In this part of the series, you configured the user interface of the invoicing application using concepts from Vue. In the next part of the series, you will take a look at how to add JWT authentication to maintain user sessions, view single invoices, and send invoices via email to recipients.

在本系列的这一部分中,您使用Vue中的概念配置了发票应用程序的用户界面。 在本系列的下一部分中,您将了解如何添加JWT身份验证以维护用户会话,查看单个发票以及通过电子邮件将发票发送给收件人。

翻译自: https://www.digitalocean.com/community/tutorials/how-to-build-a-lightweight-invoicing-app-with-node-user-interface

父子节点 构建


http://www.niftyadmin.cn/n/3649057.html

相关文章

Android零碎小知识

获取当前的版本号 public double getVersionCode() {try {int versionCode getPackageManager().getPackageInfo(getPackageName(), 0).versionCode;return versionCode;} catch (PackageManager.NameNotFoundException e) {e.printStackTrace();}return 0; }将版本号设置成1.…

TabActivity实现多页显示效果

由于手机屏幕有限&#xff0c;所以我们要尽量充分利用屏幕资源。在我们的应用程序中通常有多个Activity&#xff0c;而且会经常切换显示&#xff0c;这样我们就可以用TabActivity来显示。其效果如图1所示。 图1 tabActivity显示效果 本文就来研究TabActivity。根据帮助文档的解…

网页开端第五次培训笔记

JavaScript简介 JavaScript是一种具有面向对象能力的、解释型的程序设计语言。更具体点&#xff0c;它是基于对象和时间驱动并具有相对安全性的客户端脚本语言。它的主要目的是&#xff0c;验证发往服务器端的数据、增加Web互动、加强用户体验度等。 JavaScript的组成 ECMASc…

react中使用构建缓存_如何使用React,GraphQL和Okta构建健康跟踪应用

react中使用构建缓存介绍 (Introduction) In this tutorial, you will build a health tracking app using GraphQL API with Vesper framework, TypeORM, and MySQL as a database. These are Node frameworks, and you’ll use TypeScript for the language. For the client,…

动态添加流式布局

自定义流式布局&#xff1a;之前的一篇文章写过&#xff0c;这里就不阐述了&#xff1a;http://blog.csdn.net/qq_24675479/article/details/78921070 随后封装一个方法工具类&#xff1a;GradientDrawable代替shape,StateListDrawable替换selector设置 public class DrawUtil…

[收藏]一个广为流传的关于项目管理的通俗讲解

这是一个广为流传的关于项目管理的通俗讲解 转载出处&#xff1a;http://www.mypm.net/bbs/article.asp?titleid19753&ntypeid24想首先问大家一个问题&#xff1a;你觉得中国人聪明还是美国人聪明&#xff1f; 我见过最好的回答是美籍华人。 我们说美国人很愚蠢&#xff0…

android设置Activity背景色为透明的3种方法

方法一&#xff1a;这种方法比较简单&#xff0c;只有一个步骤&#xff0c;只需要在配置文件中把需要设置为透明的activity的样式设置为 android:theme"android:style/Theme.Translucent" 即可&#xff0c;这种方式只改变背景的颜色&#xff0c;对其他控件没有影响。…

网页开端第六次培训笔记

运算符 算数运算符赋值和扩展运算符比较运算符&#xff08;""比较值与类型&#xff09;逻辑运算符三目运算符 控制语句 单选择双选择多选择switch结构循环while循环do...whilefor循环死循环break与continue 数组 数组定义&#xff08;隐式定义、直接实例化、定义数…