diff --git a/docs/cli.md b/docs/cli.md index 2e9139fefd..8c6c4cb773 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -151,3 +151,34 @@ npm run el:bundle.windows 可将 Angular 打包成一个 Exe 应用程序。 > 脚手架对 Electron 的实现来自 [angular-electron-seed](https://github.com/sean-perkins/angular-electron-seed);有关更多细节可参考。 + +## [page] 命令 + +@delon/cli 扩展了 `ng generate page` (简化:`ng g page`) 命令用于生成业务组件页。 + +### 参数 + +`page` 命令是由 `ng g component` 基础向下构建。 + +**命令格式** + +```bash +ng g page [page name] -t=view --modal +``` + +| Alias | Arguments | Default | Summary | +| --------- | --------- | ------- | ------- | +| `-t` | `--type` | `list` | 指定页面类型,值包括:`list`、`view`、`edit` | +| - | `--modal` | `true` | 是否模态框,限:`view`、`edit` 时有效 | + +例如: + +```bash +# 生成日志列表页 +ng g page log + +cd log + +# 生成日志详情页 +ng g page view -t=view +``` diff --git a/src/core/cli/collection.json b/src/core/cli/collection.json index 188a1f08ef..bbfe92a77a 100644 --- a/src/core/cli/collection.json +++ b/src/core/cli/collection.json @@ -70,6 +70,11 @@ "factory": "./app-shell", "description": "Create an app shell.", "schema": "./app-shell/schema.json" + }, + "page": { + "factory": "./page", + "description": "Create a page component.", + "schema": "./page/schema.json" } } } diff --git a/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-edit__.component.html b/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-edit__.component.html new file mode 100644 index 0000000000..d775f47ca3 --- /dev/null +++ b/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-edit__.component.html @@ -0,0 +1,4 @@ +
+

Page Name

+
+ diff --git a/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-edit__.component.spec.ts b/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-edit__.component.spec.ts new file mode 100644 index 0000000000..1bef5549eb --- /dev/null +++ b/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-edit__.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { <%= classify(name) %>Component } from './<%= dasherize(name) %>.component'; + +describe('<%= classify(name) %>Component', () => { + let component: <%= classify(name) %>Component; + let fixture: ComponentFixture<<%= classify(name) %>Component>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ <%= classify(name) %>Component ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(<%= classify(name) %>Component); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-edit__.component.ts b/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-edit__.component.ts new file mode 100644 index 0000000000..f685263951 --- /dev/null +++ b/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-edit__.component.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@angular/core'; + +@Injectable() +export class <%= classify(name) %>Service { + + constructor() { } + +} diff --git a/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-list__.component.html b/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-list__.component.html new file mode 100644 index 0000000000..105ae5adef --- /dev/null +++ b/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-list__.component.html @@ -0,0 +1,20 @@ + + +
+
+
+
+
+
+ +
+
+
+
+ + +
+
+
+ +
diff --git a/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-list__.component.spec.ts b/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-list__.component.spec.ts new file mode 100644 index 0000000000..1bef5549eb --- /dev/null +++ b/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-list__.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { <%= classify(name) %>Component } from './<%= dasherize(name) %>.component'; + +describe('<%= classify(name) %>Component', () => { + let component: <%= classify(name) %>Component; + let fixture: ComponentFixture<<%= classify(name) %>Component>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ <%= classify(name) %>Component ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(<%= classify(name) %>Component); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-list__.component.ts b/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-list__.component.ts new file mode 100644 index 0000000000..497987c4c2 --- /dev/null +++ b/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-list__.component.ts @@ -0,0 +1,30 @@ +import { Component, OnInit<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%> } from '@angular/core'; +import { _HttpClient } from '@delon/theme'; +import { SimpleTableColumn, SimpleTableComponent } from '@delon/abc'; + +@Component({ + selector: '<%= selector %>',<% if(inlineTemplate) { %> + template: ` + + + + + `,<% } else { %> + templateUrl: './<%= dasherize(name) %>.component.html',<% } if(inlineStyle) { %><% } else { %> + styleUrls: ['./<%= dasherize(name) %>.component.<%= styleext %>']<% } %><% if(!!viewEncapsulation) { %>, + encapsulation: ViewEncapsulation.<%= viewEncapsulation %><% } if (changeDetection !== 'Default') { %>, + changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %> +}) +export class <%= classify(name) %>Component implements OnInit { + + params: any = {}; + url = '/'; + columns: SimpleTableColumn[] = [ + { title: '编号', index: 'id' } + ]; + + constructor(private http: _HttpClient) { } + + ngOnInit() { } + +} diff --git a/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-view__.component.html b/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-view__.component.html new file mode 100644 index 0000000000..9d3394703b --- /dev/null +++ b/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-view__.component.html @@ -0,0 +1,19 @@ +<% if(modal) { %> + + 10000 + +<% } else { %> + + + + 10000 + + + + 10000 + +<% } %> diff --git a/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-view__.component.spec.ts b/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-view__.component.spec.ts new file mode 100644 index 0000000000..1bef5549eb --- /dev/null +++ b/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-view__.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { <%= classify(name) %>Component } from './<%= dasherize(name) %>.component'; + +describe('<%= classify(name) %>Component', () => { + let component: <%= classify(name) %>Component; + let fixture: ComponentFixture<<%= classify(name) %>Component>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ <%= classify(name) %>Component ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(<%= classify(name) %>Component); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-view__.component.ts b/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-view__.component.ts new file mode 100644 index 0000000000..63b705e551 --- /dev/null +++ b/src/core/cli/page/files/__path__/__name@dasherize@if-flat__/__name@dasherize@if-view__.component.ts @@ -0,0 +1,46 @@ +import { Component, OnInit<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }%><% if(changeDetection !== 'Default') { %>, ChangeDetectionStrategy<% }%> } from '@angular/core'; +import { NzModalSubject } from 'ng-zorro-antd'; +import { _HttpClient } from '@delon/theme'; + +@Component({ + selector: '<%= selector %>',<% if(inlineTemplate) { %> + template: `<% if(modal) { %> + + + 10000 + + <% } else { %> + + + + 10000 + + + + 10000 + + <% } %> + `,<% } else { %> + templateUrl: './<%= dasherize(name) %>.component.html',<% } if(inlineStyle) { %><% } else { %> + styleUrls: ['./<%= dasherize(name) %>.component.<%= styleext %>']<% } %><% if(!!viewEncapsulation) { %>, + encapsulation: ViewEncapsulation.<%= viewEncapsulation %><% } if (changeDetection !== 'Default') { %>, + changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %> +}) +export class <%= classify(name) %>Component implements OnInit { + private readonly URI = '/'; + i: any; + + constructor(public http: _HttpClient, private subject: NzModalSubject) { } + + ngOnInit() { + this.http.get(this.URI + this.i.id).subscribe((res: any) => this.i = res.d); + } + + close() { + this.subject.destroy(); + } +} diff --git a/src/core/cli/page/index.d.ts b/src/core/cli/page/index.d.ts new file mode 100644 index 0000000000..51cb2e9bac --- /dev/null +++ b/src/core/cli/page/index.d.ts @@ -0,0 +1,3 @@ +import { Rule } from '@angular-devkit/schematics'; +import { Schema as ServiceOptions } from './schema'; +export default function (options: ServiceOptions): Rule; diff --git a/src/core/cli/page/index.js b/src/core/cli/page/index.js new file mode 100644 index 0000000000..5978620719 --- /dev/null +++ b/src/core/cli/page/index.js @@ -0,0 +1,101 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +const core_1 = require("@angular-devkit/core"); +const schematics_1 = require("@angular-devkit/schematics"); +require("rxjs/add/operator/merge"); +const ts = require("typescript"); +const stringUtils = require("../strings"); +const ast_utils_1 = require("../utility/ast-utils"); +const change_1 = require("../utility/change"); +const find_module_1 = require("../utility/find-module"); +function addDeclarationToNgModule(options) { + return (host) => { + if (options.skipImport || !options.module) { + return host; + } + const modulePath = options.module; + const text = host.read(modulePath); + if (text === null) { + throw new schematics_1.SchematicsException(`File ${modulePath} does not exist.`); + } + const sourceText = text.toString('utf-8'); + const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true); + const componentPath = `/${options.sourceDir}/${options.path}/` + + (options.flat ? '' : stringUtils.dasherize(options.name) + '/') + + stringUtils.dasherize(options.name) + + '.component'; + const relativePath = find_module_1.buildRelativePath(modulePath, componentPath); + const classifiedName = stringUtils.classify(`${options.name}Component`); + const declarationChanges = ast_utils_1.addDeclarationToModule(source, modulePath, classifiedName, relativePath); + const declarationRecorder = host.beginUpdate(modulePath); + for (const change of declarationChanges) { + if (change instanceof change_1.InsertChange) { + declarationRecorder.insertLeft(change.pos, change.toAdd); + } + } + host.commitUpdate(declarationRecorder); + if (options.export) { + // Need to refresh the AST because we overwrote the file in the host. + const text = host.read(modulePath); + if (text === null) { + throw new schematics_1.SchematicsException(`File ${modulePath} does not exist.`); + } + const sourceText = text.toString('utf-8'); + const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true); + const exportRecorder = host.beginUpdate(modulePath); + const exportChanges = ast_utils_1.addExportToModule(source, modulePath, stringUtils.classify(`${options.name}Component`), relativePath); + for (const change of exportChanges) { + if (change instanceof change_1.InsertChange) { + exportRecorder.insertLeft(change.pos, change.toAdd); + } + } + host.commitUpdate(exportRecorder); + } + return host; + }; +} +function buildSelector(options) { + let selector = stringUtils.dasherize(options.name); + if (options.prefix) { + selector = `${options.prefix}-${selector}`; + } + return selector; +} +function default_1(options) { + const sourceDir = options.sourceDir; + if (!sourceDir) { + throw new schematics_1.SchematicsException(`sourceDir option is required.`); + } + return (host, context) => { + options.selector = options.selector || buildSelector(options); + options.path = options.path ? core_1.normalize(options.path) : options.path; + options.module = find_module_1.findModuleFromOptions(host, options); + const templateSource = schematics_1.apply(schematics_1.url('./files'), [ + options.spec ? schematics_1.noop() : schematics_1.filter(path => !path.endsWith('.spec.ts')), + options.inlineStyle ? schematics_1.filter(path => !path.endsWith('.__styleext__')) : schematics_1.noop(), + options.inlineTemplate ? schematics_1.filter(path => !path.endsWith('.html')) : schematics_1.noop(), + schematics_1.filter(path => path.includes(`if-${options.type}`)), + schematics_1.template(Object.assign({}, stringUtils, { + 'if-flat': (s) => options.flat ? '' : s, + 'if-list': (s) => s, + 'if-edit': (s) => s, + 'if-view': (s) => s + }, options)), + schematics_1.move(sourceDir), + ]); + return schematics_1.chain([ + schematics_1.branchAndMerge(schematics_1.chain([ + addDeclarationToNgModule(options), + schematics_1.mergeWith(templateSource), + ])), + ])(host, context); + }; +} +exports.default = default_1; diff --git a/src/core/cli/page/schema.d.ts b/src/core/cli/page/schema.d.ts new file mode 100644 index 0000000000..3a2bfa4593 --- /dev/null +++ b/src/core/cli/page/schema.d.ts @@ -0,0 +1,82 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +export interface Schema { + /** + * The path to create the component. + */ + path?: string; + /** + * The path of the source directory. + */ + sourceDir?: string; + /** + * The root of the application. + */ + appRoot?: string; + /** + * The name of the component. + */ + name: string; + /** + * Specifies if the style will be in the ts file. + */ + inlineStyle?: boolean; + /** + * Specifies if the template will be in the ts file. + */ + inlineTemplate?: boolean; + /** + * Specifies the view encapsulation strategy. + */ + viewEncapsulation?: ('Emulated' | 'Native' | 'None'); + /** + * Specifies the change detection strategy. + */ + changeDetection?: ('Default' | 'OnPush'); + /** + * The prefix to apply to generated selectors. + */ + prefix?: string; + /** + * The file extension to be used for style files. + */ + styleext?: string; + /** + * Specifies if a spec file is generated. + */ + spec?: boolean; + /** + * Flag to indicate if a dir is created. + */ + flat?: boolean; + /** + * Flag to skip the module import. + */ + skipImport?: boolean; + /** + * The selector to use for the component. + */ + selector?: string; + /** + * Allows specification of the declaring module. + */ + module?: string; + /** + * Specifies if declaring module exports the component. + */ + export?: boolean; + /** + * The page type + */ + type?: ('list' | 'edit' | 'view'); + /** + * Specifies if edit or view page using modal mode. + */ + modal?: boolean; +} diff --git a/src/core/cli/page/schema.json b/src/core/cli/page/schema.json new file mode 100644 index 0000000000..fcf5f62279 --- /dev/null +++ b/src/core/cli/page/schema.json @@ -0,0 +1,108 @@ +{ + "$schema": "http://json-schema.org/schema", + "id": "SchematicsNgAlainPageComponent", + "title": "ng-alain page Options Schema", + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "The path to create the component.", + "default": "app", + "visible": false + }, + "sourceDir": { + "type": "string", + "description": "The path of the source directory.", + "default": "src", + "alias": "sd", + "visible": false + }, + "appRoot": { + "type": "string", + "description": "The root of the application.", + "visible": false + }, + "name": { + "type": "string", + "description": "The name of the component." + }, + "inlineStyle": { + "description": "Specifies if the style will be in the ts file.", + "type": "boolean", + "default": true, + "alias": "is" + }, + "inlineTemplate": { + "description": "Specifies if the template will be in the ts file.", + "type": "boolean", + "default": false, + "alias": "it" + }, + "viewEncapsulation": { + "description": "Specifies the view encapsulation strategy.", + "enum": ["Emulated", "Native", "None"], + "type": "string", + "alias": "ve" + }, + "changeDetection": { + "description": "Specifies the change detection strategy.", + "enum": ["Default", "OnPush"], + "type": "string", + "default": "Default", + "alias": "cd" + }, + "prefix": { + "type": "string", + "description": "The prefix to apply to generated selectors.", + "default": "app", + "alias": "p" + }, + "styleext": { + "description": "The file extension to be used for style files.", + "type": "string", + "default": "less" + }, + "spec": { + "type": "boolean", + "description": "Specifies if a spec file is generated.", + "default": false + }, + "flat": { + "type": "boolean", + "description": "Flag to indicate if a dir is created.", + "default": false + }, + "skipImport": { + "type": "boolean", + "description": "Flag to skip the module import.", + "default": false + }, + "selector": { + "type": "string", + "description": "The selector to use for the component." + }, + "module": { + "type": "string", + "description": "Allows specification of the declaring module.", + "alias": "m" + }, + "export": { + "type": "boolean", + "default": false, + "description": "Specifies if declaring module exports the component." + }, + "type": { + "description": "The page type, includes: list, edit, view", + "enum": ["list", "edit", "view"], + "type": "string", + "default": "list", + "alias": "t" + }, + "modal": { + "type": "boolean", + "default": true, + "description": "Specifies if edit or view page using modal mode." + } + }, + "required": ["name"] +} diff --git a/src/core/theme/styles/app/antd/_preserve-white-spaces.less b/src/core/theme/styles/app/antd/_preserve-white-spaces.less index aa64a8bdf8..588c2a1230 100644 --- a/src/core/theme/styles/app/antd/_preserve-white-spaces.less +++ b/src/core/theme/styles/app/antd/_preserve-white-spaces.less @@ -4,7 +4,8 @@ // 表单 [nz-form] { // 按钮 - .ant-btn + .ant-btn { + .ant-btn + .ant-btn, + .ant-btn + nz-popconfirm { margin-left: @white-spacing; } }