65.9K
CodeProject 正在变化。 阅读更多。
Home

[TinyERP: 企业应用程序的 SPA] 管理员工

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (2投票s)

2018年10月31日

Apache

6分钟阅读

viewsIcon

9871

添加新“管理员工”功能的步骤

概述

在本文中,我们将通过步骤来添加新的“管理员工”功能。

你们中的一些人可能会对项目结构有疑问,请写下来。我们稍后会修改。

正如上一部分所述,Web 应用程序使用客户端和服务器模块。所以

  • 客户端:将负责与最终用户交互。因此,它将在 UI 上显示系统信息(报表、数据等)或接收用户操作(例如:输入值、单击按钮执行操作等)
  • 服务器端:处理系统的业务逻辑(应用程序)

例如,使用“发送邮件”功能

  • 客户端允许最终用户输入收件人地址、主题、邮件内容、附件(如果有)等,并通过 UI 上的“发送”按钮监听来自最终用户的“发送请求”。
  • 服务器端:将处理“发送邮件”逻辑,例如:接收以上信息、验证、发送邮件、存储在必要的存储库中等。

“员工管理”通常是 HRM 模块中的一项功能。在这种情况下,我们将做同样的事情。

对于客户端

modules 文件夹下添加“hrm”文件夹,结构如下图所示

模块的起始点位于“hrmModule.ts

import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
import { CommonModule } from "@angular/common";
import { FormsModule } from "@angular/forms";
import { AppCommon, BaseModule, ModuleConfig, ModuleNames } from "@app/common";
import { HrmRoute } from "./hrmRoute";
import ioc from "./_share/config/ioc";
import routes from "./_share/config/routes";
import mainMenus from "./_share/config/mainMenus";

@NgModule({
    imports: [CommonModule, FormsModule, AppCommon, HrmRoute],
    declarations: [],
    schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class HrmModule extends BaseModule {
    constructor() {
        super(new ModuleConfig(ModuleNames.HRM, ioc, routes));
        this.mainMenus = mainMenus;
    }
}

在此文件中,我们注册 HRM 模块所需的信息,例如:名称、IOC 注册、路由、菜单。

模块的路由定义在“<moduleName>Route.ts”文件中,在此情况下为 hrmModule.ts

import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
import { RouterModule } from "@angular/router";
import { CommonModule } from "@angular/common";
import { FormsModule } from "@angular/forms";
import helperFacade, { AppCommon } from "@app/common";
import routes from "./_share/config/routes";
import { Staffs } from "./pages/staffs";
let routeConfigs = [
    { path: "", redirectTo: routes.staffs.path, pathMatch: "full" },
    { path: routes.staffs.path, component: Staffs}
];
@NgModule({
    imports: [CommonModule, FormsModule, RouterModule.forChild(routeConfigs), AppCommon],
    exports: [RouterModule, Staffs],
    declarations: [Staffs],
    schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class HrmRoute { }

在这里,我们将每个相对 URI 与指定的页面进行映射。在此文件中,我们将“<...>/staffs”映射到“Staffs”页面。因此,当用户浏览到“<uri>/staffs”时,Staffs 页面将在浏览器中渲染。

对于“./_share/config/routes.ts”,定义对象比在我们的应用程序中使用“魔术字符串”更简单。默认情况下,我们通常像这样定义路由

let routeConfigs = [
    { path: "", redirectTo: "staffs", pathMatch: "full" },
    { path: "staffs", component: Staffs}
];

使用此代码,我们将“staffs”用作 string。如“编码技巧”中所述,这可能会导致潜在的错误。如果您的成员在“staff”而不是“staffs”时输入错误,系统将崩溃或抛出异常。

因此,我更喜欢我们使用这种格式

let routeConfigs = [
    { path: "", redirectTo: routes.staffs.path, pathMatch: "full" },
    { path: routes.staffs.path, component: Staffs}
];

这并不能保证 100% 成功,但可以降低系统中潜在错误的百分比。

对于“./_share/config/route.ts”,没有什么特别的

let routes: any = {
    staffs: { name: "hrm.staffs", path: "staffs" }
};
export default routes;

对于 ioc.tsmainMenus.ts。它们目前是空的。

继续处理“pages”文件夹中的 Staffs 页面

pages/staffs.html”包含“Staffs”页面的 HTML,我们目前使用硬编码文本

<page>
    <page-header>Manage Staffs</page-header>
    <page-content>Content of "manage staffs" page</page-content>
</page>

我们可以看到,“page”组件已定义。这将处理系统中所有页面的通用行为/UI。因此,在每个页面(在此情况下为 HRM 模块中的Staffs页面)中。我们需要为“Staffs”页面提供必要的功能,而“page/staffs.ts”包含“Staffs”页面的逻辑。

import {Component} from "@angular/core";
import {BasePage} from "@app/common";

@Component({
    templateUrl:"src/modules/hrm/pages/staffs.html"
})
export class Staffs extends BasePage<any>{

}

这是一个纯粹的 Angular 组件(请参阅“Angular 组件”了解更多信息。系统中所有页面都需要继承自 BasePage<Model> 类。

好的,到目前为止,我们已经定义了新的 HRM 模块并将“staffs”映射到了 Staffs 页面。

最后一步是将 HRM 注册到当前应用程序。在 TinyERP 中,我们可以拥有多个应用程序,每个应用程序可以有不同数量的模块。“dashboard”目前是默认应用程序。

打开“<root>/src/apps/dashboard/modules.ts”并注册 HRM 模块

import { ModuleNames, IModuleConfigItem } from "@app/common";
let modules: Array<IModuleConfigItem> = [
    { name: ModuleNames.Support, urlPrefix: ModuleNames.Support, path: ModuleNames.Support},
    { name: ModuleNames.HRM, urlPrefix: ModuleNames.HRM, path: ModuleNames.HRM}
];
export default modules;

然后,在“<root>/apps/dashboard/config/themes.ts”中声明哪个主题可以使用 HRM 模块(请参阅 modules 属性)

import { ITheme, AppThemeType, ModuleNames } from "@app/common";
let themes: Array<ITheme> = [
    { 
        name: AppThemeType.Default, 
        isDefault:true,
        urlPrefix: AppThemeType.Default, 
        modules: [
            {name: ModuleNames.Support, isDefault:true},
            {name:ModuleNames.HRM}
        ]
    }
];
export default themes;

好的,如果您有疑问,请推迟到下一部分“修改管理员工”。

现在,您可以编译并运行。浏览器中的结果如下所示

好的,以上结果只是表明新的 HRM 模块已集成到当前系统中。

继续在 Staffs 页面添加员工列表,我们有一个可用的网格控件(具有基本功能),这是 Jquery Table 控件的包装器。有关“在 Angular 中包装当前 js/jquery 控件”的更多信息,请访问我的博客,一篇关于此主题的新文章将在 http://tranthanhtu.vn 上发布:

<page>
    <page-header>Manage Staffs</page-header>
    <page-content>
        <grid 
            [options]="model.options" 
            [fetch]="fetch">
        </grid>
    </page-content>
</page>

如果将此页面与上一页进行比较,有一个非常简单的更改,即网格控件及其选项和 fetch 属性。

对于 [options]:这指定了如何初始化网格控件,例如列、标题等。

对于 [fetch]:这将用于从远程源获取数据并将其绑定到网格控件。

如果您对网格控件有更多功能需求,可以采用相同的方式,将所需的当前网格控件包装到 Angular 中(如果不可用)。

让我们稍微了解一下“model”,每个页面都应该有自己的模型,其中包含必要的数据并降低“staffs.ts”的复杂性。这很简单

export class StaffsModel{
    public options: any = {};
    constructor(){
        this.options = {
            columns: [
                { field: "firstName", title: "First Name"},
                { field: "lastName", title: "Last Name"},
                { field: "department", title: "Department"}
            ]
        };
    }
}

在此模型中,我们为网格声明了 3 列:firstNamelastNamedepartment

每列都有字段和标题(显示在网格标题上的文本),并且在 staffs.ts 中我们也做了一些小改动

export class Staffs extends BasePage<StaffsModel>{
    public model: StaffsModel;
    constructor() {
        super();
        let self = this;
        self.model = new StaffsModel();
    }
    public fetch(): Promise {
        let def: Promise = PromiseFactory.create();
        let service: IStaffService = window.ioc.resolve(LocalIoCNames.IStaffService);
        service.getStaffs().then(function (searchResult: any) {
            def.resolve(searchResult.items || []);
        });
        return def;
    }
}

有一个新的模型,类型为 StaffsModelfetch 方法。

在 fetch 中,只需调用相应的服务即可从远程源获取数据(在 IStaffService 接口和 StaffService 类中定义)。目前,网格仅提供示例的基本功能,如果您需要更复杂的功能,请随时在“<root>/src/modules/common/components/grid/”中进行更改,例如:传递分页参数等。

对于 LocalIoCNames.IStaffService,这只是一个映射到 string 的常量,因为我不喜欢使用魔术代码。有关更多信息,请参阅编码技巧模式。

export const LocalIoCNames = {
    IStaffService: "IStaffService"
};

以及 IStaffService

export interface IStaffService{
    getStaffs():Promise;
}

这只是一个接口,我们可以在应用程序的任何地方使用这个接口。

ServiceStaffIServiceStaff 的实现

import {BaseService, Promise, IConnector, IoCNames} from "@app/common";
import {IStaffService} from "./istaffService";
export class StaffService extends BaseService implements IStaffService{
    public getStaffs():Promise{
        let uri="/api/hrm/staffs.json";
        let iconnector: IConnector = window.ioc.resolve(IoCNames.IConnector);
        return iconnector.get(uri);
    }
}

我们可以看到 StaffService 继承自 BaseService,它定义在“app/common”中。

在此类中,我们使用 IConnector 并从“staffs.json”文件获取数据,因为此时 API 尚不可用。我们将用 API URI 替换它。

以及 json 文件的内容

{
    "errors":[],
    "status":"200",
    "data":{
        "totalItems":"2",
        "items":[
            {"firstName":"Tu", "lastName":"Tran", "department":"Department 1"},
            {"firstName":"Tu1", "lastName":"Tran", "department":"Department 1"}
        ]
    }
}

这只是为了模拟来自 RESTful Web 服务的响应。

好的,让我们回顾一下

  • 定义具有网格的staffs页面:完成
  • 从远程源加载数据并绑定到网格:完成
  • 实现 IStaffServiceStaffService:完成
  • 调用 json 文件中的硬编码值:完成

好的,让我们编译并运行看看效果

哦,抛出了一个异常。此错误与 IOC 注册有关,我们有 IStaffServiceStaffService,但没有将它们映射在一起。让我们将其添加到“<module>/_share/config/ioc.ts”中

import {LocalIoCNames} from "../enum";
import {IoCLifeCycle} from "@app/common";
import {StaffService} from "../services/staffService";
let ioc: Array<IIoCConfigItem> = [
    { name: LocalIoCNames.IStaffService, instance: StaffService, lifeCycle: IoCLifeCycle.Transient }
];
export default ioc;

再次编译并刷新,我们将看到如下结果

好的,很棒。它完美地工作了。

我希望在左侧面板上看到“管理员工”,这样我就可以通过单击此菜单来访问此功能。将以下行添加到“<module folder>/_share/config/mainMenus.ts

import { IResourceManager, IoCNames } from "@app/common";

let mainMenus: Array<any> = [
    { text: "Manage Staffs", url: "default/hrm/staffs", cls: "" },    
];
export default mainMenus;

让我们再次编译并刷新

左侧面板上有“管理员工”。

客户端的内容现在有点长了,所以让我们继续我下一篇文章中的 API 部分,它也可能会很长。

有关此部分中参考源代码,请查看 https://github.com/tranthanhtu0vn/TinyERP (分支:feature/manage_staff)。

系列中的其他文章

感谢您的阅读。

注意:请点赞并与您的朋友分享,我将非常感激。

© . All rights reserved.