使用 Angular 2、ASP.NET Core 1.1 和 Entity Framework Core 构建 SPA(第一部分)






4.72/5 (13投票s)
如何设置带 Angular 2 的 ASP.NET Core 项目
引言
这是本系列的第一部分。在本系列中,我们将使用 Angular 2、ASP.NET Core 1.1 和 Entity Framework Core 创建一个 SPA 应用程序。在这里,我们使用 Angular 2 作为应用程序的用户界面,使用 ASP.NET Core MVC 执行服务器端任务,并使用 Entity Framework Core 执行所有数据库级别的操作。在本系列中,我们创建一个员工管理系统项目。在此管理系统中,我们提供查看所有员工列表、编辑现有员工、删除特定员工以及录入新员工等功能。让我们来看看本系列的议程。
议程
- 设置 Angular 2 的开发环境
- 了解应用程序的结构
- 将 Entity Framework 添加到项目中
- 创建新的页面以查看
Employee
列表以及进行Insert
、Update
和Delete
操作 - 执行 Angular 2 路由
- 添加服务
先决条件
在开始本系列工作之前,您的系统必须完成一些最低限度的配置。
- Visual Studio 2015 Update 3 或 Visual Studio 2017
- .NET Core 1.0
- Type Script 2.0
- Node.js 版本 6 或更高版本
满足以上所有要求后,我们现在可以通过几个简单的步骤设置 SPA 应用程序。
入门
开始的最简单方法是使用提供的项目模板之一。这些模板集成到标准的 dotnet new 命令中,可在 Windows、Mac 和 Linux 上使用。要安装(单页应用程序)SPA 模板,请打开命令提示符并运行以下命令。
dotnet new --install Microsoft.AspNetCore.SpaTemplates::*
上述命令需要一些时间,并安装 SPA 的多个模板,如 Angular、Knockout.js、React 等。所有这些模板的好处是,我们不必担心整个设置过程。
要生成一个新项目,首先,创建一个空文件夹,并将此项目命名为 EMS
。这里,EMS 是“Employee Management System”(员工管理系统)的缩写。创建空文件夹后,更改目录并进入此项目。然后运行命令 dotnet new angular
。
此命令创建一个 Angular 2 应用程序的模板项目。然后运行 dotnet restore
命令,此命令会生成 MSBuild 文件并恢复所有 .NET 依赖项。
在安装所有 .NET 依赖项后,我们现在安装 Node.js 依赖项。运行 npm install
命令,此命令需要几分钟时间来安装所需的 Node.js 依赖项。
在创建模板项目并安装所有必需的依赖项后,现在运行 start EMS.csproj
命令。此命令将在 Visual Studio 中打开您的项目。我这里使用的是 Visual Studio 2017。
现在按 Ctrl + F5 运行项目。
运行项目后,如果看到上面的屏幕,那么恭喜您!您已成功创建了 Angular 2 应用程序。如果您对 .ts 文件或 .html 文件进行了任何更改,只要保存更改,您将立即获得实时更新,而无需刷新浏览器。
应用程序结构
成功创建并运行应用程序后,让我们了解项目结构。现在打开 **Solution Explorer**(解决方案资源管理器)窗口,您会发现以下项目结构。
以下是我们应用程序的主要部分。
依赖项
此部分包含三种类型的依赖项:“npm”依赖项与我们的客户端应用程序相关,“NuGet”包含 .NET Core 级别的依赖项。“SDK”部分包含系统级别的依赖项。
launchSettings.json
此 json 文件包含与每个调试配置文件相关的项目特定设置,Visual Studio 配置用于启动应用程序,包括任何应使用的环境变量。此文件定义了 applicationURl、SSL 端口号和身份验证模式。
wwwroot
wwwroot 部分包含 dist 文件夹,这里的 dist 文件夹包含我们 ClientApp
的编译代码。我们在模板(.ts)或 .html 页面中编写的所有代码都会转换为编译代码并保存在一个 main-client.js 文件中。此文件包含我们在 ClientApp 文件夹中创建的所有组件、服务和其他资源的编译形式。
如果您查看 Home
控制器的 Index.cshtml 视图,您会发现我们在应用程序启动过程中引用了 main-client.js 文件。
@{
ViewData[Title] = "Home Page";
}
<app asp-prerender-module="ClientApp/dist/main-server">Loading...</app>
<script src="~/dist/vendor.js" asp-append-version="true"></script>
@section scripts {
<script src="~/dist/main-client.js"
asp-append-version="true"></script>
}
ClientApp
这是我们项目的客户端部分。在这里,我们编写所有与 Angular2 相关的代码,并创建组件和服务。App 文件夹包含组件和 app.module.ts 文件。boot-client.ts 文件定义了根组件选择器。
控制器
在此部分,我们创建 MVC 控制器。在此项目中,我们将使用控制器从数据库获取数据。
视图
它定义了 MVC 项目的 V 部分,我认为我们已经了解了 Views。
Appsettings.json
取代 web.config,现在所有设置都位于 appsettings.json 中,它包含以键值对形式的所有配置信息。在此文件中,我们定义了连接字符串和其他应用程序级别的设置。
Packages.json
此文件包含所有 Angular2 相关的依赖项。
Program.cs
这是应用程序的入口点,用于托管应用程序。在此文件中,我们仅通过 WebHostBuilder
完成配置和启动主机的任务。
Startup.cs
Startup.cs 文件充当中介件,并提供系统运行所需的所有服务。
tsconfig.cs
此文件包含 Type Script 配置设置。
Webpack.config.js
Webpack 充当包管理器和模块打包器。它获取所有项目模块及其依赖项,并生成代表这些模块的静态资产。Webpack 构建和打包 CSS、JavaScript 文件、图像以及所有其他静态内容。
配置 Entity Framework
现在我们使用 Code First 方法创建数据库和表。在我们的应用程序中,我们将使用 SQL Server 作为数据库,因此我们需要安装用于 SQL Server 的 Entity Framework。要为 SQL Server 配置 Entity Framework,我们需要添加 SQL Server 数据库提供程序。
打开 **Package Manager Console**(程序包管理器控制台)并运行以下命令
Install-Package Microsoft.EntityFrameworkCore.SqlServer
我们将使用一些 Entity Framework 工具来维护数据库。因此,我们也需要安装工具包。所以运行下面的命令来添加所有必需的工具。
Install-Package Microsoft.EntityFrameworkCore.Tools
创建实体类
添加 Entity Framework 的依赖项后,让我们创建 Employee
和 Project
实体类。Employee
和 Project
实体之间将存在多对一关系,这意味着一个员工一次只能有一个项目,但一个项目可以有多个员工。首先,创建一个 Model 文件夹,并将 Employee
类添加到此文件夹中。
Employee 实体
在 Models 文件夹中添加一个 Employee
类,并将 Employee
类的代码替换为以下代码
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EMS.Models
{
public class Employee
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int EmployeeId { get; set; }
public string EmployeeName { get; set; }
public string Designation { get; set; }
public string Skills { get; set; }
public int ProjectId { get; set; }
public Project Project { get; set; }
}
}
在上面的代码中,EmployeeId
属性是 Employee
实体的**主键**,并且它也是标识类型。ProjectId
是**外键**,对应的导航属性是 Project
。
Project 实体
现在,在 Model 文件夹中添加另一个类,并将此类命名为 Project
,将此类的代码替换为以下代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace EMS.Model
{
public class Project
{
public int ProjectId { get; set; }
public string ProjectName { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
}
ProjectId
属性是此实体的**主键**,其他属性定义了项目的信息。
添加 DbContext 类
任何 Entity Framework 结构的主要类是 DbContext
类。在此类中,我们定义用于在数据库中创建表的所有实体。现在,在 Model 文件夹中添加另一个类,并将其命名为 EmployeeContext
。此类将从 System.Data.Entity.DbContext
类派生。将此类的代码替换为以下代码。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using EMS.Models;
namespace EMS.Model
{
public class EmployeeContext:DbContext
{
public EmployeeContext(DbContextOptions<EmployeeContext> options):base(options)
{
}
public DbSet<Employee> Employee { get; set; }
public DbSet<Project> Project { get; set; }
}
}
在上面的代码中,我们为 Employee
和 Project
类定义了两个 Dbset
属性。此属性将在数据库中创建两个表,名称将与 Dbset
属性中定义的名称相同。
使用依赖注入注册 Context
创建所有实体和 dbContext
类后,我们现在添加一个 Entity Framework 服务,以便需要此服务的任何组件都可以通过构造函数提供。在我们的控制器构造函数中,我们使用此服务获取 context 实例。现在打开 startup.cs 并将以下代码添加到 ConfigureServices
方法中。
services.AddDbContext<EmployeeContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
在上面的代码中,我们注册了一个 DbContext
服务,这里的 DefaultConnection
提供了在我们 appsettings.json 中定义的连接字符串。现在打开您的 appsettings.json 文件并添加以下代码。将服务器名称替换为您的 SQL Server 名称。
{
"ConnectionStrings": {
"DefaultConnection": "Server=*******;Database=Employee;
Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
添加迁移并创建数据库
配置 dbContext
和实体类后,现在我们添加迁移,并使用 Update-Database
命令,我们将创建我们的项目数据库。所以,首先,我们添加迁移,为此,打开您的 **Package Manager Console**(程序包管理器控制台),运行 Add-Migration firstMigration
命令。此命令将在您的项目中添加 Migrations 文件夹,Migrations 文件夹中的第一个文件包含有关如何创建数据库的所有信息。
现在我们将运行 Update-Database
命令,此命令会获取迁移类中可用的代码并更新您的数据库。在我们的例子中,此命令会在 SQL Server 中创建一个 Employee
数据库,该数据库包含我们在模型类中之前定义的所有表和属性。
现在打开您的 SQL Server,您会发现 Employee
数据库已被添加。
添加初始数据
现在我们的表和数据库已准备就绪,让我们向表中添加一些初始数据。
向 Project 表添加数据
INSERT INTO dbo.Project
(
ProjectName,
StartDate,
EndDate
)
VALUES( N'Lions',CAST('02/01/2017' as datetime),_
CAST('04/05/2017' AS datetime) ),( N'OUP',CAST('08/09/2016' AS datetime),_
CAST('12/03/2017' AS datetime) ),
( N'VMesh',CAST('12/04/2016' as date),CAST('04/01/2017' as date) )
向 Employee 表添加数据
insert into Employee
(Designation,EmployeeName,ProjectId,Skills)
values('Developer','Raj Kumar',2,'C#,Asp.Net,MVC'),
('Mobile Developer','Ankur Verma',3,'Java, Android Studio, Xamarin'),
('Developer','Dheeraj Sharma',1,'C#,Asp.Net,MVC,AngularJS'),
('Developer','Dhramveer',2,'C#,Asp.Net,MVC,AngularJS,Node.js'),
('Mobile Developer','Shivam Kumar',1,'Java, Android Studio, Xamarin')
现在我们的数据库和应用程序设置已准备就绪,让我们开始处理应用程序的**视图**部分。
显示员工列表
现在转到 App 文件夹中的 app.modules.ts 文件。在此文件中,您会发现定义了一些默认路由,如 "counter
"、“home
”和 "fetch-data
"。我们不需要所有这些路由,所以从路由表中删除 "counter
" 和 "fetch-data
" 路由,并从组件部分删除这些组件。在完成所有这些更改后,现在转到 "navmenu.component.html" 文件,并从 navmenu.component.html 中删除 "Counter
" 和 "Fetch Data
" 项目。
完成以上所有更改后,我们的应用程序现在看起来如下
现在我们将添加项目中所需的一些组件。
Employee Detail Component(员工详情组件)
现在我们将添加一个详情组件,并使用此组件显示员工的详细信息。
右键单击 components 文件夹并添加一个新文件夹,将其命名为 details。此文件夹包含员工详情组件的内容。现在在此文件夹中添加一个 typescript 文件,并将其命名为 details.component.ts,然后粘贴以下代码
import { Component } from '@angular/core';
@Component({
selector: 'employee-detail',
templateUrl: './details.component.html'
})
export class DetailsComponent {
}
现在我们需要一个模板文件来此组件,所以右键单击 details 文件夹并添加一个 html 文件,将其命名为 details.component.html,然后粘贴以下代码
<h2>This is Detail Component</h2>
Edit Employee Component(编辑员工组件)
在此项目中,我们将提供编辑任何现有员工详细信息的功能,为此,我们需要添加一个 editEmployee
组件。所以,右键单击 components 文件夹并添加一个新文件夹,将其命名为 editEmployee。现在添加一个 typescript 文件,将其命名为 editEmployee.component.ts,然后粘贴以下代码
import { Component } from '@angular/core';
@Component({
selector: 'edit-employee',
templateUrl: './editEmployee.component.html'
})
export class editEmployeeComponent {
}
现在,添加 html 模板文件,将其命名为 editEmployee.component.html,然后粘贴以下代码
<h1>Edit Employee</h1>
New Employee Component(新员工组件)
在此组件中,我们编写代码来录入新员工条目。右键单击 components 文件夹并添加 newEmployee 文件夹。添加一个 typescript 文件,并将其命名为 newEmployee.component.ts,然后粘贴以下代码
import { Component } from '@angular/core';
@Component({
selector: 'new-employee',
templateUrl: './newEmployee.component.html'
})
export class newEmployeeComponent {
}
现在添加一个 html 模板文件,并将其命名为 newEmployee.component.html,然后粘贴以下代码
<h1>This is New Employee component</h1>
添加完项目所有必需的组件后,现在 ClientApp
部分的结构如下
为组件添加路由
添加所有必需的组件后,现在我们执行这些组件的路由。打开 app.modules.ts 文件并将以下代码粘贴到该文件中
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { UniversalModule } from 'angular2-universal';
import { AppComponent } from './components/app/app.component'
import { NavMenuComponent } from './components/navmenu/navmenu.component';
import { HomeComponent } from './components/home/home.component';
import { DetailsComponent } from './components/details/details.component';
import { newEmployeeComponent } from './components/newEmployee/newEmployee.component';
import { editEmployeeComponent }
from './components/editEmployee/editEmployee.component';
@NgModule({
bootstrap: [ AppComponent ],
declarations: [
AppComponent,
NavMenuComponent,
HomeComponent,
DetailsComponent,
newEmployeeComponent,
editEmployeeComponent
],
imports: [
UniversalModule, // Must be first import. This automatically
// imports BrowserModule, HttpModule, and JsonpModule too.
RouterModule.forRoot([
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: 'details/:id', component: DetailsComponent },
{ path: 'new', component: newEmployeeComponent },
{ path: 'edit/:id', component: editEmployeeComponent },
{ path: '**', redirectTo: 'home' }
])
]
})
export class AppModule {
}
在上面的代码中,我们在 declarations
部分注册了所有组件,并为所有这些组件执行了路由。
在 navmenu 中添加新项目
打开 navmenu.component.html 文件并将以下代码粘贴到该文件中
<div class='main-nav'>
<div class='navbar navbar-inverse'>
<div class='navbar-header'>
<button type='button' class='navbar-toggle'
data-toggle='collapse' data-target='.navbar-collapse'>
<span class='sr-only'>Toggle navigation</span>
<span class='icon-bar'></span>
<span class='icon-bar'></span>
<span class='icon-bar'></span>
</button>
<a class='navbar-brand' [routerLink]="['/home']">EMS</a>
</div>
<div class='clearfix'></div>
<div class='navbar-collapse collapse'>
<ul class='nav navbar-nav'>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/home']">
<span class='glyphicon glyphicon-home'></span> Home
</a>
</li>
<li [routerLinkActive]="['link-active']">
<a [routerLink]="['/new']">
<span class='glyphicon glyphicon-user'></span> New Employee
</a>
</li>
</ul>
</div>
</div>
</div>
在上面的代码中,我们向 nav 菜单添加了 New Employee
(新员工)项目。点击此菜单项,我们可以打开 **New Employee**(新员工)页面并录入新员工条目。
到目前为止,我们已经创建了所有组件,执行了路由,并在 nav 菜单中添加了一个新项目。让我们检查所有这些更改是否正常工作。当您运行此应用程序时,将打开以下屏幕
当您单击 **New Employee**(新员工)菜单项时,将显示以下屏幕
显示员工列表
在我们的项目的首页,我们将显示所有员工的列表。我们将显示员工的 EmployeeName
(员工姓名)、Designation
(职位)、Project
(项目)信息。首先,我们需要创建一个 API,使用它,我们可以获取所有员工的列表。现在转到 Controllers 文件夹并添加一个新的 "API Controller"(API 控制器),将其命名为 EmployeeController
,然后粘贴以下代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using EMS.Model;
using EMS.ViewModel;
using Microsoft.EntityFrameworkCore;
namespace EMS.Controllers
{
[Produces("application/json")]
[Route("api/Employee")]
public class EmployeeController : Controller
{
private readonly EmployeeContext _context;
public EmployeeController(EmployeeContext context)
{
_context = context;
}
[HttpGet]
public async Task<IActionResult> EmployeeList()
{
List<Employee_Project> ilIst = new List<Employee_Project>();
var listData = await (from emp in _context.Employee
join pro in _context.Project
on emp.ProjectId equals pro.ProjectId
select new
{
emp.EmployeeId,
emp.EmployeeName,
emp.Designation,
pro.ProjectName
}
).ToListAsync();
listData.ForEach(x =>
{
Employee_Project Obj = new Employee_Project();
Obj.EmployeeId = x.EmployeeId;
Obj.Designation = x.Designation;
Obj.EmployeeName = x.EmployeeName;
Obj.Project = x.ProjectName;
ilIst.Add(Obj);
});
return Json(ilIst);
}
}
}
在上面的代码行中,我们创建了一个异步方法。异步编程是 ASP.NET Core 的默认模式。异步编程提供了一种以非阻塞方式编写代码的机制。我们创建了一个异步 EmployeeList
方法。在第一行代码 List<Employee_Project> ilIst
中,我们创建了一个 Employee_Project
ViewModel
类型的列表。这里,Employee_Project
是一个 ViewModel
。
ViewModel
在 ASP.NET MVC 中用于仅显示所需信息,ViewModel
也用于显示来自两个或多个模型的数据。现在我们在项目中添加一个 ViewModel 文件夹。在项目中添加一个新文件夹,并将其命名为 ViewModels。创建 viewModels 文件夹后,在 ViewModels 文件夹中添加一个类。右键单击 ViewModels 文件夹并添加一个新类,将其命名为 Employee_Project
。创建类后,将以下代码粘贴到该类中
namespace EMS.ViewModel
{
public class Employee_Project
{
public int EmployeeId { get; set; }
public string EmployeeName { get; set; }
public string Designation { get; set; }
public string Project { get; set; }
}
}
使用 Linq 查询,我们获取所有员工的 EmployeeId
(员工 ID)、EmployeeName
(员工姓名)、Designation
(职位)、ProjectName
(项目名称)信息,并使用 ForEach
方法将这些值添加到 iList
对象中。在最后一行代码中,我们将 ilist
数据转换为 JSON 格式,并将此 JSON 结果返回给所需资源。
现在我们的 API 已准备好返回员工列表,让我们检查一下它是否正常工作。打开您的浏览器并粘贴 https://:54273/api/employee URL,然后按 Enter。如果您收到以下 JSON 结果,则表示我们的 API 工作正常。
现在我们的 API 已准备好返回员工列表,让我们使用此 API 并显示员工信息。我们需要一个服务来使用此 API 并获取结果。在 app 部分添加一个 Service 文件夹。创建文件夹后,添加一个 typescript 文件,将其命名为 services.ts,然后将以下代码粘贴到该文件中
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
@Injectable()
export class EmployeeServcies {
constructor(private http: Http) {
}
getEmployeeList() {
return this.http.get('https://:54273/api/employee');
}
}
在上面的代码中,我们创建了一个 EmployeeServcies
类,并使用 @Injectable
元数据对其进行装饰。@Injectable
元数据用于将类定义为服务。在此服务类中,我们添加了 getEmployeeList
方法,在此方法中,我们使用 HTTP
类的 get
方法来执行到服务器的 HTTP get
请求。
创建服务后,现在我们需要在 "app.modules.ts" 文件中注册此服务。打开您的 app.modules.ts 文件,导入此服务,并在 providers 中注册此服务。我们将此服务注册在 app.modules.ts 文件中,这意味着现在此服务是全局服务,我们可以在任何组件中使用此服务,而无需在每个组件中注册此服务。
创建并注册服务后,现在我们在 home 组件中使用此服务,所以打开 home.component.ts 文件并粘贴以下代码
import { Component } from '@angular/core';
import { EmployeeServcies } from '../../Services/services';
import { Response } from '@angular/http';
@Component({
selector: 'home',
templateUrl: './home.component.html'
})
export class HomeComponent {
public EmployeeList = [];
public constructor(private empService: EmployeeServcies) {
this.empService.getEmployeeList()
.subscribe(
(data: Response) => (this.EmployeeList= data.json())
);
}
}
在上面的代码中,我们创建了 EmployeeServices
服务的**实例 (empService)**,并使用该服务的 getEmployeeList
方法来获取员工列表。您可以看到我们正在使用 subscribe
。实际上,Angular 2 提供了一种用于运行异步请求的新模式,称为 Observables
,http 是 Angular 1 的 $http
的后继者。而不是返回一个 Promise,它的 http.get()
方法返回一个 Observable
对象。使用 subscribe
方法,我们可以使用 observable
,subscribe
方法告诉 observable
:“请执行您的任务,这里有人在收听并关注您,当您返回结果时,请执行该回调函数”。在 subscribe
方法中,我们获取数据并将数据分配给 EmployeeList
,现在我们使用此列表来显示员工列表。
打开 home.component.html 文件并粘贴以下代码
<div class="row">
<div class="col-md-12">
<h3>Employee List</h3>
<br />
<br />
<br />
</div>
</div>
<div class="row">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>
S.No.
</th>
<th>
EmployeeName
</th>
<th>
Designation
</th>
<th>
Project
</th>
<th>
Action
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let empData of EmployeeList; let i = index;
trackBy: employeeId">
<td>
{{i+1}}
</td>
<td>
{{empData.employeeName}}
</td>
<td>
{{empData.designation}}
</td>
<td>
{{empData.project}}
</td>
<td>
<a [routerLink]="['/details/',empData.employeeId]"
class="btn btn-primary">
Detail
</a>
<a [routerLink]="['/edit/',empData.employeeId]"
class="btn btn-success">
Edit
</a>
<a
class="btn btn-danger">
Delete
</a>
</td>
</tr>
</table>
</div>
</div>
在上面的代码中,我们对 EmployeeList
执行了 *ngFor
,并创建了一个员工列表。我们还为每个员工条目添加了三个锚标签(Edit(编辑)、Delete(删除)和 Detail(详情))。我们使用 routerLink
将锚标签链接到我们应用程序的特定部分。
进行所有更改后,现在保存项目并刷新浏览器,您将获得以下结果
摘要
这是 "使用 Angular 2、ASP.NET Core 1.1 和 Entity Framework Core 构建 SPA" 系列的第一部分。今天,我们学习了如何设置带 Angular2 的 .NET Core 项目,并了解了项目的结构。我们还在该项目中进行了 EF Core(Entity Framework)的设置。我们创建了一些组件并为这些组件执行了路由。我们创建了服务,通过内置的 "http" 服务从 Web API 获取数据,并将这些数据以表格格式显示出来。在下一篇文章中,我们将创建一些屏幕来添加、删除或编辑员工信息。我们还将提供搜索和排序员工信息的功能。
历史
- 2017 年 4 月 4 日:初始版本