# @delon/* Component Documentation (中文) 本文件包含所有组件文档的聚合内容。总计 106 个组件。 --- # Documentation --- order: 2 title: CacheService type: Documents --- ## API ### set() | 参数名 | 类型 | 描述 | | ----- | --- | --- | | `key` | `string` | 缓存唯一标识符 | | `data` | `any | Observable` | 缓存数据源,数据源为 `Observable` 时,依然返回 `Observable`,否则返回 `void` | | `options` | `{ type?: 'm' | 's', expire?: number, emitNotify?: boolean }` | `type` 存储类型,'m' 表示内存,'s' 表示持久
`expire` 过期时间,单位 `秒` | ### get() | 参数名 | 类型 | 描述 | | ----- | --- | --- | | `key` | `string` | 缓存唯一标识符 | | `options` | `{ mode?: 'promise' | 'none', type?: 'm' | 's', expire?: number, emitNotify?: boolean }` | `mode` 指定获取缓存的模式:
1、`promise` 表示若不存 `key` 则把 `key` 当URL发起请求并缓存且返回 Observable
2、`none` 表示直接返回数据若KEY不存在则直接返回 `null`

`type` 存储类型,'m' 表示内存,'s' 表示持久
`expire` 过期时间,单位 `秒` | ### getNone() 获取缓存数据,若 `key` 不存在或已过期则返回 null。 ### tryGet() 获取缓存,若不存在则设置缓存对象,参数等同 `set()`。 ### has() 是否缓存 `key`。 ### remove() 移除缓存 `key`。 ### clear() 清空所有缓存。 ### notify() `key` 监听,当 `key` 变更、过期、移除时通知,注意以下若干细节: - 调用后除再次调用 `cancelNotify` 否则永远不过期 - 监听器每 `freq` (默认:3秒) 执行一次过期检查 ### cancelNotify() 取消 `key` 监听 ### hasNotify() `key` 是否已经监听 ### clearNotify() 清空所有 `key` 的监听 ### freq() 设置监听频率,单位:毫秒且最低 `20ms`,默认:`3000ms`。 ## `get` 和 `tryGet` 的区别 本质都是获取并返回缓存数据,`get` 相比 `tryGet` 更简化,前者按KEY即是URL约定的风格,后者需指定数据源对象。 ## 酷操作 ### async 管道 RxJS 和 `async` 管道二者的配合可以帮助我们非常友好的使用缓存数据,例如: ```ts @Component({ template: ` @for (unit of units | async; track $index) {
  • {{unit}}
  • }` }) export class Component { units: this.srv.get('/data/unit') } ``` ### 缓存与请求 有时需要依赖字典获取远程数据时: ```ts this.srv .get('/data/unit') .pipe( map(units => this.http.get(`/trade?unit=${units}`)) ); ``` --- --- type: Documents order: 20 title: Icon --- 自 ng-zorro-antd 1.7.x 以后图标发生破坏性变更,虽然带了诸多[优势](https://ng.ant.design/components/icon/zh#svg-%E5%9B%BE%E6%A0%87),同时也带来几个劣势: - 若采用动态加载会产生额外的HTTP请求 - 若静态加载需要逐一注册图标 - `st` 组件的 `format` 参数无法直接指定图标 ng-alain 默认使用静态加载的做法,毕竟后端使用图标相对于比较有限,即使将 svg 都打包进脚本相比较之前整个 styles 体积上是所有减少,但比较并不多。 而针对以上问题,ng-alain 提供几种方案。 ## 使用icon插件(推荐) **尽可能**从项目中分析并生成静态 Icon,插件会自动在 `src` 目录下生成两个文件: - `src/style-icons.ts` 自定义部分无法解析(例如:远程菜单图标) - `src/style-icons-auto.ts` 命令自动生成文件 > 自动排除 [ng-zorro-antd](https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/components/icon/nz-icon.service.ts#L6) 和 [@delon](https://github.com/ng-alain/delon/blob/master/packages/theme/src/theme.module.ts#L33) 已经加载的图标。 ```bash ng g ng-alain:plugin icon ``` 同时,需要手动在 `startup.service.ts` 中导入: ```ts import { ICONS_AUTO } from '../../../style-icons-auto'; import { ICONS } from '../../../style-icons'; @Injectable() export class StartupService { constructor(iconSrv: NzIconService) { iconSrv.addIcon(...ICONS_AUTO, ...ICONS); } } ``` **有效语法** ```html ``` ## 动态加载 [动态加载](https://ng.ant.design/components/icon/zh#%E9%9D%99%E6%80%81%E5%8A%A0%E8%BD%BD%E4%B8%8E%E5%8A%A8%E6%80%81%E5%8A%A0%E8%BD%BD),这是为了减少包体积而提供的方式。当 NG-ZORRO 检测用户想要渲染的图标还没有静态引入时,会发起 HTTP 请求动态引入。你只需要配置 `angular.json` 文件: ```json { "assets": [ { "glob": "**/*", "input": "./node_modules/@ant-design/icons-angular/src/inline-svg/", "output": "/assets/" } ] } ``` ## 动态使用 不管是静态或动态加载,依然无法解决原始方法 `class="anticon anticon-"` 的便利性,毕竟字符串就是字符串并非 Angular 模板无法被解析,而针对这个问题,提供两种解决办法。 ### 类样式 事实上所有 Antd 图标都可以在 [iconfont](http://www.iconfont.cn/collections/detail?spm=a313x.7781069.1998910419.d9df05512&cid=9402) 找得到,可以点选自己需要的图标并生成相应的 css 文件或 cdn,最后在项目中可以直接使用 `1.7.0` 之前的写法。 > **注意:** 在项目编辑里加上 `anticon anticon-` 前缀才能同之前的类名保持一致。 ``` // angular.json "styles": [ "src/iconfont.css" ], ``` 如果非cdn还需要将相应的 icon 图标文件复制到 `assets` 目录下,同时修改 `iconfont.css` 中 `@font-face` 对应的 url 路径。 ### @angular/elements 动态加载的另一种方式是使用 @angular/elements,只需要 `nz-icon` 指令重新封装成组件。 ```ts import { Component, Input } from '@angular/core'; @Component({ selector: 'nz-icon', template: ``, }) export class IconComponent { @Input() type: string; } ``` 同时在根模块里注册它。 ```ts import { createCustomElement } from '@angular/elements'; @NgModule({ declarations: [], }) export class AppModule { constructor(injector: Injector) { customElements.define('nz-icon', createCustomElement(IconComponent, { injector })); } } ``` 最后。 ```ts @Component({ selector: 'app-demo', template: `
    `, }) export class DemoComponent { constructor(private san: DomSanitizer) { } value = this.san.bypassSecurityTrustHtml( `icon: `, ); } ``` --- --- order: 3 title: Interceptor type: Documents --- # 写在前面 搭配 `httpCacheInterceptor` Http 拦截器,可以将缓存应用到 Http 请求当中。它只有几个特征: - 支持缓存过期时间 - 支持自定义缓存 KEY - 支持任何 Http 请求、任何数据格式 - 符合 Http 缓存响应标准 `Cache-Control` # 如何使用 在 `withInterceptors` 中引入 `httpCacheInterceptor`: ```ts provideHttpClient(withInterceptors([httpCacheInterceptor])) ``` --- --- order: 1 title: LLMs.txt tag: 新增 group: AI --- 本指南介绍如何让 AI 工具更好地理解 NG-ALAIN 组件库。 ## 什么是 LLMs.txt? 我们支持通过 [LLMs.txt](https://llmstxt.org/) 文件向大语言模型(LLMs)提供 `NG-ALAIN` 文档。此功能可帮助 AI 工具更好地理解我们的组件库、API 及使用模式。 ## 可用资源 ### LLMs.txt 聚合文件 我们提供多个聚合文件来帮助 AI 工具访问文档: | 文件 | 说明 | | ---------------------------------------------------------- | ------------------------------------------ | | [llms.txt](https://ng-alain.com/llms.txt) | 导航文件,包含所有文档和组件的链接 | | [llms-full.txt](https://ng-alain.com/llms-full.txt) | 完整的组件文档(英文),包含实现细节和示例 | | [llms-full-cn.txt](https://ng-alain.com/llms-full-cn.txt) | 完整的组件文档(中文) | ### 单个组件文档 通过 `.md` 后缀直接访问单个组件文档: - `https://ng-alain.com/llms/components/auto-focus.en.md`(英文) - `https://ng-alain.com/llms/components/auto-focus.cn.md`(中文) ## 在 AI 工具中的使用 | 工具 | 说明 | 提示词 | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- | | **Cursor** | 使用 `@Docs` 功能引入 LLMs.txt,或添加提示词到 `.cursor/rules`。[文档](https://docs.cursor.com/zh/context/@-symbols/@-docs) | `阅读 https://ng-alain.com/llms-full-cn.txt 并理解 @delon/* 组件库,在编写 @delon/* 代码时使用这些知识。` | | **Windsurf** | 添加提示词到 `.windsurf/rules` 或使用 cascade memories。[文档](https://docs.windsurf.com/windsurf/cascade/memories) | `阅读 https://ng-alain.com/llms-full-cn.txt 并理解 @delon/* 组件库,在编写 @delon/* 代码时使用这些知识。` | | **Claude Code** | 添加到 CLAUDE.md 或使用 `/memory` 持久化。[文档](https://docs.anthropic.com/en/docs/claude-code) | `阅读 https://ng-alain.com/llms-full-cn.txt 并理解 @delon/* 组件库,在编写 @delon/* 代码时使用这些知识。` | | **GitHub Copilot** | 添加到 `.github/copilot-instructions.md`。[文档](https://docs.github.com/zh/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot) | `阅读 https://ng-alain.com/llms-full-cn.txt 并理解 @delon/* 组件库,在编写 @delon/* 代码时使用这些知识。` | | **Codex** | 添加到 `.codex/settings.json` 或 AGENTS.md。[文档](https://github.com/openai/codex) | `阅读 https://ng-alain.com/llms-full-cn.txt 并理解 @delon/* 组件库,在编写 @delon/* 代码时使用这些知识。` | | **Gemini CLI** | 使用 `--context` 参数或添加到 `.gemini/config.json`。[文档](https://ai.google.dev/gemini-api/docs?hl=zh-cn) | `阅读 https://ng-alain.com/llms-full-cn.txt 并理解 @delon/* 组件库,在编写 @delon/* 代码时使用这些知识。` | | **Trae** | 添加到项目的知识源设置中。[文档](https://trae.ai/docs) | `阅读 https://ng-alain.com/llms-full-cn.txt 并理解 @delon/* 组件库,在编写 @delon/* 代码时使用这些知识。` | | **Qoder** | 添加到 `.qoder/config.yml` 或在对话中使用 `@docs`。[文档](https://docs.qoder.com/) | `阅读 https://ng-alain.com/llms-full-cn.txt 并理解 @delon/* 组件库,在编写 @delon/* 代码时使用这些知识。` | | **Neovate Code** | 运行 `neovate` 并使用提示词描述任务。[文档](https://github.com/neovateai/neovate-code) | `阅读 https://ng-alain.com/llms-full-cn.txt 并理解 @delon/* 组件库,在编写 @delon/* 代码时使用这些知识。` | --- --- order: 10 title: ng add subtitle: 创建脚手架 type: Documents --- ## 命令格式 ```bash ng add ng-alain # 如果你想创建一个英文版本,则: ng add ng-alain --defaultLanguage=en ``` ## 额外参数 | 参数名 | 默认值 | 描述 | | ------------------- | ------- | ----------------------------------------------------- | | `--form` | `true` | 是否需要动态表单 | | `--mock` | `true` | 是否需要 mock 功能 | | `--defaultLanguage` | `zh` | 默认语言,[支持语言列表](/cli/plugin/zh#支持语言列表) | | `--codeStyle` | `false` | 是否需要代码风格 | | `--i18n` | `false` | 是否需要国际化支持 | 例如生成一个带有国际化项目: ```bash ng add ng-alain --i18n --defaultLanguage=en ``` 查看更多[插件](/cli/plugin)。 --- --- order: 20 title: ng g subtitle: 业务页 type: Documents --- ## 写在前面 `ng generate`(简写:`ng g`)用于生成业务组件页,默认 Angular 所提供的模板跟 ng-alain 所需要的会有一些不同,例如我们希望生成一个模块时应该包括导入 `SharedModule`。 ng-alain 在此基础上增加了很多很酷的骚操作。 默认情况下所有的代码统一存放于 `app/routes` 下面,可通过 `ng-alain.json` 指向其他目录,例如: ```json { "$schema": "./node_modules/ng-alain/schema.json", "projects": { // 表示 ng-alain 项目都存放于 `app/pages` 下 "ng-alain": { "routesRoot": "app/pages" } } } ``` ## 命令格式 ```bash ng g ng-alain:[command name] [name] [options] ``` 示例: ```bash # 生成一个 trade 模块 ng g ng-alain:module trade # trade 模块下生成一个 TradeListComponent List组件 ng g ng-alain:list list -m=trade # trade 模块下生成一个 TradeEditComponent List组件 及 TradeService 服务类 ng g ng-alain:list list -m=trade --service=root # trade 模块下生成一个 ListComponent List组件 ng g ng-alain:list list -m=trade --withoutModulePrefixInComponentName=true # trade 模块下生成一个 TradeEditComponent Edit组件 ng g ng-alain:edit edit -m=trade ``` > ng-alain 有自己的一套文件组织结构,当你破坏这些结构时,可能会导致下列指令产生异常。 | 参数 | 描述 | | ---- | --- | | `-m` 或 `--module` | 指定目标模块 | | `-t` 或 `--target` | 指定目标路径,支持 `bus/list` 写法 | | `--modal` | 指定是否使用模态框 | | `--withoutPrefix` | 指定选择器名不加前缀 | | `--withoutModulePrefixInComponentName` | 组件名不加模块名前缀 | | `--service` | 指定如何生成服务类,包含:`ignore`、`root`、`none` | ## Module 模块 生成一个 `trade` 模块: ```bash ng g ng-alain:module trade ``` 会在 `routes/trade` 生成两个文件 `trade.module.ts`、`trade-routing.module.ts`,你无法指定不生成 `*-routing.module.ts` 因为这是 ng-alain 的任性。 模块内容包括了导入 `SharedModule` 以及一些统一性导入与导出的方式,并且你不可能破坏这些变量名(例如:`COMPONENTS`、`routes`)。 ## 业务页 目前包含的业务组件页,包括: - `empty` 空白页 - `list` 列表页 - `edit` 编辑页 - `view` 查看页 - `curd` 列表、编辑、查看 在 `trade` 下生成 `list` 列表页: ```bash ng g ng-alain:list list -m=trade ``` **注意:** `-m` 是必须指定的,因为 ng-alain 认为页面应该在某个具体的模块里,而不是幽灵。 ### 骚操作 一般而言,一个模块可能会包含相同类型的业务页,而我们产生的文件结构可能希望是: ``` sys log view view.component.ts log.component.ts sys.module.ts ``` 因此,当你希望生成的查看应该是在 `log` 组件(支持 `log/list` 多级写法)下面时,你可以这样子: ```bash ng g ng-alain:view view -m=sys -t=log ``` **覆盖默认模板页** 若 `list`、`edit`、`view`、`empty` 四个指令所产生默认页并非所在业务期望,可以覆盖它们。 例如覆盖 `list` 命令默认模板,在根目录 `_cli-tpl` 下创建目录名 `_list`,目录结构务必等同 [原始list目录](https://github.com/ng-alain/delon/tree/master/packages/schematics/list/files) 的结构。 ### edit & view 页 对于 `edit`、`view` 默认是以模态框展示来生成,你可以改用页面展示则: ```bash ng g ng-alain:edit [page name] --modal=false ``` > 如果你不小心将收到【No provider for NzModalRef! 】表明你把一个需要使用 `nzModalService` 打开的模态框组件,用了路由注册的方式。打开模态框组件无须注册路由。 ## 自定义页 除上述默认业务页以外,也可以自定义属于项目级别业务页,例如我们创建一个自己的编辑页模板,则只需要在项目的根目录创建以下目录结构(你可以通过 [Github](https://github.com/ng-alain/ng-alain/tree/master/_cli-tpl) 上来获取): ``` └── _cli-tpl │ └── edit // 模板名称 │ └── __path__ // (名称固定值) │ └── __name@dasherize@if-flat__ // (名称固定值) │ ├── __name@dasherize__.component.ts.template // 组件类文件(名称固定值) │ ├── __name@dasherize__.component.html.template // 组件模板文件(名称固定值) │ └── __name@dasherize__.component.spec.ts.template // 组件测试文件(名称固定值) └── src ``` 之后,只需要运行: ```bash ng g ng-alain:tpl [your template name] [name] -m=trade ``` > 自定义页参数同业务页一致。 ### 如何编写模板文件 在自定义页的目录结构里我们看到文件名以 `__` 前缀开头,事实上他们是一些变量占位符,Cli 默认传递一些参数及方法: | 类型 | 参数名 | 默认 | 描述 | | ---- | --------------- | --------- | ---------------------------------------- | | 变量 | project | - | 项目名 | | 变量 | name | - | 名称,相当于命令行的 `` | | 变量 | path | - | 目标路径 | | 变量 | flat | `false` | 文件是否扁平结构 | | 变量 | inlineTemplate | `false` | 是否内联模板(固定值:`false`) | | 变量 | selector | - | 组件 `selector` | | 变量 | componentName | - | 组件名称 | | 变量 | changeDetection | `Default` | 组件 `changeDetection` 值 | | 变量 | modal | - | 是否使用 Modal 展示 | | 方法 | decamelize | - | 将字母串转换为由下划线分隔的所有小写字母 | | 方法 | dasherize | - | 将空格或下划线用破折号替换 | | 方法 | camelize | - | 返回字符串的小骆驼拼写法形式 | | 方法 | classify | - | 返回字符串的大骆驼拼写法形式 | | 方法 | underscore | - | 将字母串转换为由下划线分隔的所有小写字母 | | 方法 | capitalize | - | 返回字符串首字母大写 | 这些变量或方法可以在模板中使用,例如:`<%=componentName%>` 表示组件名,任何使用 `<% %>` 内可以使用 JavaScript 代码。有关模板编写可以参考: - [delon](https://github.com/ng-alain/delon/blob/master/packages/schematics/edit/files/__path__/__name%40dasherize%40if-flat__/__name%40dasherize__.component.html) - [material2](https://github.com/angular/material2/blob/master/src/lib/schematics/dashboard/files/__path__/__name%40dasherize%40if-flat__/__name%40dasherize__.component.html) ### 自定义数据 `tpl` 命令在生成文件之前允许你进一步处理数据,命令在执行过程中会检查是否存在 `_cli-tpl/_fix.js` 文件,并调用 `fix` 方法,方法必须返回一个 `Promise` 对象,例如: > **注:** CLI 是一个 Node JS 程序,因此语法以 Node JS 为准。 ```js function fix(options) { return new Promise((resolve) => { resolve(); }); } module.exports = { fix }; ``` `fix` 只有一个 `options` 参数,它包含 CLI 产生所有参数数据,哪怕是一些未定义参数,例如: ```bash ng g ng-alain:tpl list -m=setting --import-type=UserDto ``` 其中 `import-type` 并不是命令自身的定义参数,但 `options` 会将这些未定义参数转换成 `extraArgs` 对象,因此你接收到的 `options` 会是: ```json { "tplName": "test", "modal": true, ... "extraArgs": { "import-type": "UserDto" } } ``` `options` 对象会传递给模板引擎,由此你可以附加一些处理后的数据给 `options`,并在模板文件内使用它们,例如: ```json { "tplName": "test", "modal": true, ... "extraArgs": { "import-type": "UserDto", "newData": "asdf" } } ``` 你可以将 `newData` 应用到模板当中,例如 `__name@dasherize__.component.html`: ```html <%= extraArgs.newData %> ``` 结果为: ```html asdf ``` --- --- order: 2 title: Schema type: Documents --- ## 写在前面 [JSON Schema](http://json-schema.org/) 是一种标准的定义 JSON 数据结构的规范,并不包含对这些规范转换成表单具体说明,`@delon/form` 也是根据自己的理解并结合 `ng-zorro-antd` 现有数据录入组件库产生的动态构建表单类库。 JSON Schema **始终**都必须有一个类型为 `type="object"` 作为**根节点**,因此一个最简单的 Schema 结构至少是: ```ts schema = { type: 'object', // 可有可无,默认会强制为 `object` properties: {} } ``` 在描述 Schema 说明之前,有必要对表单元素与 Schema 之前的联系做一个系统性说明。 我们知道,表单是由一组HTML元素组件,每一个元素对应一个 Schema 属性,属性有自己的数据类型、格式信息、视觉信息等,但这些信息不足以表述 `ng-zorro-antd` 所提供的丰富API接口。为了更好利用这些API接口,`@delon/form` 除了实现绝大部分 JSON Schema 标准以外,额外唯一增加了一个 `ui` 属性用于表述属性如何渲染的问题。 ### 无污染 当然若你对标准有非常严格,或者 JSON Schema 数据结构是来自后端的产生时,可以通过 `` 来额外对当前 JSON Schema 添加 UI 渲染。例如: ```ts schema = { properties: { url: { type: 'string', title: 'Web Site' } } } ``` 一个URL属性,若我们不希望用于添加 `https://` 前缀的情况下,就单纯的 JSON Schema 结构是无法表述,而 `nz-input` 又支持非常丰富的前后缀文本,则我们可以为 `ui` 定制并增加 `https://` 的前缀文本: ```ts ui = { $url: { addOnBefore: 'https://' } } ``` ui 本身也是一个 JSON 结构,为了区分 JSON Schema 属性名的对应关系,**必须**统一对属性名加上 `$` 前缀;对于数组的元素对象必须使用 `$items` 替代。当KEY为 `*` 时表示对所有子表单元素都有效。 ### 表单元素与数据结构的对应关系 一个完整的表单元素我们认为应该包含以下若干元素: ![](./assets/img/form-input.png) 从左至向各元素描述: | 结构源 | 参数 | 说明 | 类型 | 默认值 | |-----|----|----|----|-----| | Schema | `[required]` | 是否必填项 | `string[]` | - | | Schema | `[title]` | 属性描述 | `string` | - | | ui | `[optional]` | 标签可选信息 | `string` | - | | ui | `[optionalHelp]` | 标签可选帮助 | `string, SFOptionalHelp` | - | | ui | `[placeholder]` | 文字框中显示提示信息 | `string` | - | | Schema | `[description]` | 属性目的性解释 | `string` | - | | - | `[error]` | 错误信息 | `string` | - | ### 一点规范 - 所有 `key` 按驼峰式命名法 - 若你对 JSON Schema 很熟悉,则忽略 **不建议** 字样 ## JSON Schema(SFSchema) JSON Schema 有完整的对每个属性的规范描述,`@delon/form` 当前是基于 [draft-07](http://json-schema.org/) 规范,下列是规范具体说明: ### 常规类 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[type]` | 数据类型,支持 JavaScript 基础类型 | `number,string,boolean,object,array` | `object` | | `[enum]` | 枚举,静态数据源 | `SFSchemaEnumType[]` | - | ### 数值类型 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[minimum]` | 最小值 | `number` | - | | `[exclusiveMinimum]` | 约束是否包括 `minimum` 值 | `boolean` | - | | `[maximum]` | 最大值 | `number` | - | | `[exclusiveMaximum]` | 约束是否包括 `maximum` 值 | `boolean` | - | | `[multipleOf]` | 倍数 | `number` | - | **关于exclusiveMinimum和exclusiveMaximum** `sf` 的实现机制导致无法很好的处理 `type` 类型的错误捕获,因此默认情况下 `sf` 是忽略了所有 `type` (见 [config.ts](https://github.com/ng-alain/delon/blob/master/packages/form/src/config.ts#L12))类型错误,而这两种都错误都会被认为 `type` 类型错误,从而导致触发无效检查的原因。(更多细节请参考 [#676](https://github.com/ng-alain/ng-alain/issues/676#issuecomment-420208459)) ### 字符串类型 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[maxLength]` | 定义字符串的最大长度 | `number` | - | | `[minLength]` | 定义字符串的最小长度 | `number` | - | | `[pattern]` | 验证输入字段正则表达式字符串 | `string` | - | ### 数组类型 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[items]` | 数组元素类型描述,只支持数组对象,若需要基础类型数组可通过其他部件支持 | `SFSchema` | - | | `[minItems]` | 约束数组最小的元素个数 | `number` | - | | `[maxItems]` | 约束数组最大的元素个数 | `number` | - | | `[uniqueItems]` | 约束数组每个元素都不相同 | `boolean` | - | | `[additionalItems]` | 数组额外元素的校验规则 | `SFSchema` | - | ### 对象类型 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[maxProperties]` | 最大属性个数,必须是非负整数 | `number` | - | | `[minProperties]` | 最小属性个数,必须是非负整数 | `number` | - | | `[required]` | 必需属性 | `string[]` | - | | `[properties]` | 定义属性 | `{ [key: string]: SFSchema }` | - | ### 条件类 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[if]` | 条件验证 | `SFSchema` | - | | `[then]` | 条件验证 | `SFSchema` | - | | `[else]` | 条件验证 | `SFSchema` | - | 条件类的校验非常强大和丰富,但是出于会破坏UI导致整个组件构建更复杂,`@delon/form` 仅实现 `required` 的处理,并且把它当成是否显示校验目标,比如:一个登录页,会根据不同登录方式来显示不同登录模式: ```ts schema: SFSchema = { properties: { type: { type: 'string', enum: [ 'mobile', 'name' ], default: 'mobile' }, name: { type: 'string' }, pwd: { type: 'string' }, mobile: { type: 'string' }, code: { type: 'string' } }, required: [ 'type' ], if: { properties: { type: { enum: [ 'mobile' ] } } }, then: { required: [ 'mobile', 'code' ] }, else: { required: [ 'name', 'pwd' ] } }; ``` 上述的最终行为是当登录方式为 `mobile` 时UI显示 `mobile` 和 `code`,反之UI显示 `name` 和 `pwd`。 其实条件类最终被解析成 `ui.visibleIf`,将其转换成如下: ```ts { properties: { login_type: { type: "string", title: "登录方式", enum: [ { label: "手机", value: "mobile" }, { label: "账密", value: "account" } ], default: "mobile", ui: { widget: "radio", styleType: "button" } }, mobile: { type: "string", ui: { visibleIf: { login_type: val => val === "mobile" } } }, code: { type: "number", ui: { visibleIf: { login_type: val => val === "mobile" } } }, name: { type: "string", ui: { visibleIf: { login_type: val => val === "account" } } }, pwd: { type: "string", ui: { type: "password", visibleIf: { login_type: val => val === "account" } } } }, required: ["login_type"] }; ``` ### 逻辑类 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[allOf]` | **不建议** 使用,可用 `required` 替代 | `SFSchema[]` | - | | `[anyOf]` | **不建议** 使用,可用 `required` 和 `minProperties` 替代 | `SFSchema[]` | - | | `[oneOf]` | **不建议** 使用,值必须是其中之一 | `SFSchema[]` | - | > **不建议** 主要是并没有对逻辑类进行UI相关处理,它同条件类类似,会影响UI渲染。 ### 格式与视觉类 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[title]` | 属性描述 | `string` | - | | `[description]` | 属性目的性解释 | `string` | - | | `[default]` | 默认值 | `any` | - | | `[readOnly]` | 是否只读状态,等同 `nzDisabled` | `boolean` | - | | `[format]` | 数据格式,[文档](http://json-schema.org/latest/json-schema-validation.html#rfc.section.7.3) | `string` | - | ### 其他 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[definitions]` | 内部类型定义体 | `SFSchemaDefinition` | - | | `[$ref]` | 引用定义体 | `string` | - | | `[$comment]` | 针对开发者的注释,无任何意义,也不会被校验 | `string` | - | ### 非标准 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[ui]` | 指定UI配置信息,优先级高于 `sf` 组件 `ui` 属性值 | `SFUISchemaItem` | - | ## UI(SFUISchemaItem) UI Schema 结构由通用性和小部件API两部分组成,以下是通用性部分进行接口说明,小部件部分自行参数小部件API。 > 为了小部件的API完整性,小部件Schema说明可能也会包含下列通用性部分。 ### SFUISchema 等同 `` 一组与 JSON Schema 结构相对应的 UI 结构体,类型为:`[ key: string ]: SFUISchemaItem`。 ### 基础类 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[debug]` | 调试模式 | `boolean` | - | | `[order]` | 属性顺序 | `string[]` | - | | `[asyncData]` | 异步静态数据源 | `(input?: any) => Observable` | - | | `[hidden]` | 是否隐藏渲染 | `boolean` | `false` | | `[visibleIf]` | 指定条件时才显示 | `{ [key: string]: any[] | ((value: any, property: FormProperty) => boolean) }` | - | | `[visibleIfLogical]` | 指定多个 `visibleIf` 时采用的逻辑关系 | `or, and` | `or` | | `[acl]` | ACL权限,等同 `can()` 参数值 | `ACLCanType` | - | **visibleIf** 指定条件时才显示,例如: - `visibleIf: { shown: [ true ] }`:当 `shown: true` 时才显示当前属性 - `visibleIf: { shown: [ '$ANY$' ] }`:当 `shown` 包括任意值时 - `visibleIf: { shown: (value: any, property: FormProperty) => value > 0 }`:复杂表达式 ### 校验类 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[liveValidate]` | 是否实时校验 | `boolean` | `true` | | `[firstVisual]` | 是否立即呈现错误视觉 | `boolean` | `false` | | `[onlyVisual]` | 是否只展示错误视觉不显示错误文本 | `boolean` | `false` | | `[ingoreKeywords]` | 忽略某些数据类型校验 | `string[]` | | | `[errors]` | 自定义错误信息文本 | `{ [ key: string ]: string | ((obj: ErrorData) => string) }` | - | | `[showRequired]` | 是否展示必填项标识 `*` | `boolean` | - | | `[validator]` | 自定义校验,最后结果会与 Ajv 校验结果进行合并显示 | `(value: any, formProperty: FormProperty, form: PropertyGroup) => ErrorData[]` | - | ### 数组类 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[items]` | 指定子元素的UI | `SFUISchema` | - | | `[addTitle]` | 指定添加按钮文本 | `string` | `添加` | | `[addType]` | 指定添加按钮风格,等同按钮 `nzType` | `string` | `dashed` | | `[removable]` | 指定是否显示移除按钮 | `boolean` | - | | `[removeTitle]` | 指定移除按钮文本 | `string` | `移除` | ### 表单元素类 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[type]` | 指定 `input` 的 `type` 值 | `string` | `text` | | `[placeholder]` | 文字框中显示提示信息 | `string` | - | | `[autofocus]` | 加载时是否获得焦点 | `boolean` | - | ### 渲染类 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[widget]` | 指定采用什么小部件渲染 | `string` | - | | `[i18n]` | 指 `schema.title` 的国际化键值 | `string` | - | | `[descriptionI18n]` | 指 `schema.description` 的国际化键值 | `string` | - | | `[class]` | 自定义类,等同 `[ngClass]` 值 | `string,string[]` | - | | `[width]` | 指定宽度,单位:`px` | `number` | - | | `[size]` | 元素组件大小 | `default,large,small` | - | | `[grid]` | 响应式属性 | `SFGridSchema` | - | | `[optional]` | 标签可选信息 | `string` | - | | `[optionalHelp]` | 标签可选帮助 | `string, SFOptionalHelp` | - | | `[collapse]` | 标记该字段在表单折叠状态下隐藏 | `boolean` | - | ### 响应式属性 SFGridSchema `grid` 属性等同完整的 [Grid栅格系统](https://ng.ant.design/components/grid/zh),透过 `grid` 可以决定表单如何渲染。 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[gutter]` | 栅格间隔 | `number` | - | | `[span]` | 每个表单元素栅格占位格数,为 `0` 时相当于 `display: none` | `number` | - | | `[xs]` | `<768px` 响应式栅格,可为栅格数或一个包含其他属性的对象 | `number, SFGridSizeSchema` | - | | `[sm]` | `≥768px` 响应式栅格,可为栅格数或一个包含其他属性的对象 | `number, SFGridSizeSchema` | - | | `[md]` | `≥992px` 响应式栅格,可为栅格数或一个包含其他属性的对象 | `number, SFGridSizeSchema` | - | | `[lg]` | `≥1200px` 响应式栅格,可为栅格数或一个包含其他属性的对象 | `number, SFGridSizeSchema` | - | | `[xl]` | `≥1600px` 响应式栅格,可为栅格数或一个包含其他属性的对象 | `number, SFGridSizeSchema` | - | | `[xxl]` | 保留字段,`0.7.0` 后支持 | `number, SFGridSizeSchema` | - | ### 水平布局类 Schema > **务必**二者总和为 `24` | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[spanLabel]` | `label` 所占栅格数 | `number` | 5 | | `[spanControl]` | 表单控件所占栅格数 | `number` | 19 | | `[offsetControl]` | `control` 栅格左侧的间隔格数,间隔内不可以有栅格 | `number` | - | | `[spanLabelFixed]` | `label` 固定宽度 | `number` | - | --- --- order: 40 title: sta subtitle: Swagger API 生成器 type: Documents --- ## 写在前面 若后端API文档是由 Swagger 来描述,将可以通过以下命令来生成一组完整的 API 代码: ```bash ng g ng-alain:sta --url=https://petstore3.swagger.io/api/v3/openapi.json ``` ## 命令格式 ```bash ng g ng-alain:sta --name= --url=<远程URL地址> --filePath=<本地Swagger.json文件> --output=<输出路径> ``` | 参数名 | 默认 | 描述 | |-----|----|----| | `name` | `sta` | Swagger 项目名称 | | `url` | - | 远程 Swagger.json 文件,`url` 与 `filePath` 必须二选一 | | `filePath` | - | 本地 Swagger.json 文件路径,`url` 与 `filePath` 必须二选一 | | `output` | `src/app/${name}` | 输出目录 | | `responseDataField` | - | Response 的真实数据字段 | | `modelTypePrefix` | - | 数据契约名称前缀 | | `httpClientType` | `delon` | HttpClient 请求方式,1. `delon` 使用 `@delon/theme` 的 `_HttpClient`,2. `angular` 使用 `HttpClient` | | `generateApiOptions` | - | swagger-typescript-api [options](https://github.com/acacode/swagger-typescript-api#-usage) | | `tagsMapping` | - | Swagger标签映射字典,当标签为中文时,可以指定用于转换成更加符合规范 Service 名 | ## 使用配置文件 在项目根目录增加 `sta.json`: ```json { "$schema": "./node_modules/ng-alain/sta/schema.json", "filePath": "swagger.json", "tagsMapping": { "部门": "Dept" } } ``` 执行: ```bash ng g ng-alain:sta ``` ## 常见问题 ### 路径与Service的关联 默认会将每个 `path` 第一个 `tags` 合并为一个 Service,请尽可能使用 `[a-zA-Z][-_a-zA-Z]+` 来描述 `tag`。 ### 不符合预期的名称 默认情况下,会根据 `operationId` 项目来处理,否则会自动根据 `path` 与 `method` 组合。为了保持与后端的统一项,建议开启 `operationId` 支持,以下是几种语言开启方法: **.NET CORE** ```cs // Swashbuckle services.AddSwaggerGen(c => { c.CustomOperationIds(e => { var name = e.ActionDescriptor.RouteValues["action"] ?? ""; return name[0].ToString().ToLower() + name.Substring(1); }); } ``` **JAVA** 参考 [Configuring the output of operationId in a Swagger 2.0 spec](https://springfox.github.io/springfox/docs/snapshot/#configuring-the-output-of-operationid-in-a-swagger-2-0-spec). ### 全局Response 当所有 `path` 有固定输出格式时,比如成功、异常都有统一格式时都返回时: ```json { "status": 200, "error": "Error Message", "result": {} } ``` 若是通过拦截器来处理异常消息时,订阅时只需要始终获取 `result` 字段数据时,可以通过指定 `--responseDataField="result"` 来解决。 --- --- order: 2 title: TokenService type: Documents --- `ITokenService` 接口包含了一些对 Token 操作的服务类,例如获取当前 Token 信息: ```ts constructor(@Inject(DA_SERVICE_TOKEN) private tokenService: ITokenService) { console.log(tokenService.get().token); // 如果是 JWT console.log(tokenService.get(JWTTokenModel).token); } ``` ## 过期刷新 订阅 `refresh` 后 Token 过期时会自动触发,当后端支持 Token 刷新可以,可以在过期前刷新 Token 来延长用户授权时长。 > **建议** `refresh` 在整个应用中只订阅一次。 ## API ### 属性 | 参数名 | 类型 | 描述 | |-----|----|----| | `[login_url]` | `string` | 获取 `DelonAuthConfig` 配置的登录地址 | | `[referrer]` | `AuthReferrer` | 获取授权失败前路由信息 | | `[refresh]` | `Observable` | 订阅刷新,订阅时会自动产生一个定时器,每隔一段时间进行一些校验;**注意** 会多次触发,请务必做好业务判断 | ### 方法 | 方法名 | 返回类型 | 描述 | |-----|------|----| | `change()` | `Observable` | 监听 Token 变化回调 | | `set(data: ITokenModel)` | `boolean` | 设置 Token | | `get(type?: any)` | `ITokenModel` | 获取 Token | | `clear(options?: { onlyToken: boolean })` | `void` | 清空 Token | --- --- order: 10 title: 介绍 type: Basic --- ## 什么是NG-ALAIN NG-ALAIN 是一个企业级中后台前端/设计解决方案脚手架,我们秉承 [Ant Design](https://ant.design/) 的设计价值观,目标也非常简单,希望在Angular上面开发企业后台更简单、更快速。随着『设计者』的不断反馈,将持续迭代,逐步沉淀和总结出更多设计模式和相应的代码实现,阐述中后台产品模板/组件/业务场景的最佳实践,也十分期待你的参与和共建。 ## 环境搭建 Angular 开发环境至少需要安装 [Node.js](https://nodejs.org/en/download/)(Node.js 内置了 [npm](https://www.npmjs.com/get-npm) 无须单独安装)、[VSCode编辑器](https://code.visualstudio.com/),其中 Node.js 建议安装 **LTS** 版本,安装完成后可以通过终端窗口中运行: ```bash node -v # 查看 Node.js 当前版本 npm -v # 查看 Npm 当前版本 ``` Npm 默认从国外源来下载包信息,鉴于国内环境因素,在开始下一步前先安装 [nnrm](https://github.com/YunYouJun/nnrm/blob/main/README.zh-CN.md) 并切换至淘宝镜像: ```bash # 安装 nnrm npm install -g nnrm # 将Npm切换至淘宝源(不同 npm 源管理器命令有点不一样,更多细节请参考 nnrm 文档) nnrm use taobao ``` ## 安装 ### 全局 Angular Cli 安装之前请先确保本地已经安装全局 Angular Cli,只有这样才能随时随地在终端使用 `ng` 命令,可以通过终端窗口中运行: ```bash npm install -g @angular/cli@19 ``` ### 创建NG-ALAIN项目 NG-ALAIN 必须先创建一个全新的 Angular 项目,可以通过终端窗口中运行: ```bash ng new my-project --style less --routing cd my-project # 或多重项目 ng new my-workspace --no-create-application cd my-workspace ng g application mgr --style less --routing ``` > 如果你想了解 `--style`、`--routing` 参数,请参考 [ng new](https://angular.io/cli/new#options) 文档。 接下来只需要将 NG-ALAIN 添加到 `my-project` 项目中即可,在 `my-project` 目录下通过终端窗口中运行: ```bash ng add ng-alain ``` > 若多重项目时,需要提供具体的项目名称。 NG-ALAIN 会询问是否需要一些额外的插件,一开始完全可以一路回车,这些插件都是可插拔,后期可以自行添加与移除。 > 以上只会生成干净的项目,可以直接用于生产环境中。你可能在[预览](https://ng-alain.gitee.io/)上看到许多示例页,它们全都可以在 [Github](https://github.com/ng-alain/ng-alain) 查看到源代码,当然也可以通过 Git 克隆代码的形式获得: > ```bash > git clone --depth=1 https://github.com/ng-alain/ng-alain.git my-project > cd my-project > npm install > ``` ### 运行 ```bash npm start ``` 启动完成后会打开浏览器访问 [http://localhost:4200](http://localhost:4200),若你看到如下页面则代表成功了。 ![](./assets/screenshot/start.png | width=700) 恭喜你,你已经成功部署一个 NG-ALAIN 项目。 ## 支持环境 - **受限于 Angular,不再支持 IE** - 现代浏览器,[浏览器支持](https://angular.io/guide/browser-support) - 支持服务端渲染 - [Electron](https://electron.atom.io/) | [Edge](http://godban.github.io/browsers-support-badges/)
    Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
    Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
    Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
    Safari | [Opera](http://godban.github.io/browsers-support-badges/)
    Opera | [Electron](http://godban.github.io/browsers-support-badges/)
    Electron | | --------- | --------- | --------- | --------- | --------- | --------- | | last 2 versions | last 2 versions | last 2 versions | last 2 versions | last 2 versions | last 2 versions ## 如何贡献 在任何形式的参与前,请先阅读 [贡献者文档](/docs/contributing)。如果你希望参与贡献,欢迎 [Pull Request](https://github.com/ng-alain/ng-alain/pulls),或给我们 [报告 Bug](https://github.com/ng-alain/ng-alain/issues)。 > 强烈推荐阅读 [《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way)(本指南不提供此项目的实际支持服务!)、[《如何向开源社区提问题》](https://github.com/seajs/seajs/issues/545) 和 [《如何有效地报告 Bug》](http://www.chiark.greenend.org.uk/%7Esgtatham/bugs-cn.html)、[《如何向开源项目提交无法解答的问题》](https://zhuanlan.zhihu.com/p/25795393),更好的问题更容易获得帮助。 ## 社区互助 如果您在使用的过程中碰到问题,可以通过下面几个途径寻求帮助,同时我们也鼓励资深用户通过下面的途径给新人提供帮助。 通过 Stack Overflow 或者 Segment Fault 提问时,建议加上 `ng-alain` 标签。 1. QQ 群 - [316911865](//shang.qq.com/wpa/qunwpa?idkey=f5102185e4ecf8b641a176596aca3037a45d3452329f69cf3bc496877cd087ff) - [428749721](//shang.qq.com/wpa/qunwpa?idkey=06823e225199af79b0c5ba3bbc89756ee57c2b0cc2115e3f44cc19230db2b0c3) 2. [![Segment Fault](https://gw.alipayobjects.com/zos/rmsportal/hfYFfCvHTQTUKntlJbMF.svg | width=140)](https://segmentfault.com/t/ng-alain)(中文) 3. 加入 NG-ALAIN 自助服务群(中文) ![](./assets/qq-group.png) ## 捐助 如果你觉得 NG-ALAIN 不错,可以考虑自愿为本站打赏或捐助。 ![](./assets/donate.png) --- --- order: 80 title: en-US: Performance zh-CN: 优化 type: Advance --- ## 包体大小优化 分为JavaScript脚本文件和CSS文件,以下只描述脚本部分,有关CSS文件请参考[优化主题系统](/theme/performance)。 **注意:** 建议始终以**业务优先,优化为后**的准则,且在开始前对 NG-ALAIN 有一定了解后再进行优化。本章节的优化方案会随着版本的更迭有所变动,有关细节请持续关注。 ### 结构说明 一般来说,构建后有两个文件会比较大:`scripts.js` 和 `main.js`,而我们优化也主要针对这两项。 **scripts.js** 它来自是 `angular.json` 的 `scripts` 节点的集合,因此,这个文件的大小取决于 `scripts` 节点所引用的第三方组件的大小。 一般来说,当你在使用非 Angular 第三方组件时都应该放在 `scripts` 下。 **main.js** 默认使用 `ng build` 会将所有 `@angular/*`、`ng-zorro-antd`、`@delon/*` 以及一些第三方 Angular 组件都会被打包进来,除非你使用 `--vendor-chunk` 参数来分离这些类库。 ### 优化对象 我们知道,Angular Cli 打包出来的资源文件会包含该文件 hashing 值,它像是文件的唯一标识码,若未对该文件进行模块修改都不会产生新的 hashing 值,这确保用户首次下载资源后不管我们如何再次构建用户都无须再一次下载。 > 事实上,Angular Cli 默认将 `--vendor-chunk` 主要因素是这些 @angular/* 相对于迭代很快。 根据 NG-ALAIN 的[模块注册指导原则](/docs/module),产生的两个 `shared-delon.module.ts`、`shared-zorro.module.ts` 两个专门针对 @Delon 与 NG-ZORRO 共享次级模块导入的汇总。 `@delon/abc`、`@delon/chart`、`ng-zorro-antd` 三个主要类库都支持次级导入,只选择项目所需要的模块将有效的解决包体大小的问题。 ### 结论 待 NG-ALAIN 提供更好的优化支持后,我们会发布 [#684](https://github.com/ng-alain/ng-alain/pull/684) 希望对包体大小有更好的期望结果。 --- --- order: 20 title: 体系结构 type: Basic i18n: need-update --- NG-ALAIN 目标是提供更多通用性业务模块,让开发者更加专注于业务。所以在你着手开始开发前,有必要了解整个 NG-ALAIN 的体系结构,从整体上了解 NG-ALAIN 包含了哪些东西及其含义,才能更好的利用这些业务组件库快速构建项目。 ## 结构图 ![](./assets/screenshot/architecture.png | width=700) | 类库 | 文档 | 描述 | |----|----|----| | **@delon/theme** | [文档](/theme) | 主题系统除了包含 NG-ALAIN 基础框架及所需样式(包含CSS工具集,一套类似Bootstrap)以外,还包含一些通用的数据渲染(Pipe)、服务工具类(页面标题、滚动条等)的集合,这些是日常必不可少的一些组成 | | **@delon/abc** | [文档](/components) | 脚手架内提供了一套默认业务组件,这些组件抽象了控制台业务中的一些常见区块。我们将持续维护和迭代这些组件,为中后台业务提供比 Ant Design 基础组件更高级别的抽象 | | **@delon/chart** | [文档](/chart) | 基于 G2 的基础上二次封装,提供了业务中常用的图表套件,可以单独使用,也可以组合起来实现复杂的展示效果 | | **@delon/form** | [文档](/form) | 基于 [JSON Schema](http://json-schema.org/) 标准的动态构建表单 | | **@delon/auth** | [文档](/auth) | 用户认证模块,用于解决如何获取、存取、使用这三个步骤的用户认证环节 | | **@delon/acl** | [文档](/acl) | 访问控制列表,是一种非常简单的基于角色权限控制,甚至达到控制某个按钮显隐的粒度 | | **@delon/cache** | [文档](/acl) | 将字典、城市数据等缓存至内存或持久化当中,有效减少 Http 请求 | | **@delon/mock** | [文档](/mock) | Mock 会拦截 Angular Http 请求并返回测试数据,当后端未完成接口时 Mock 技术是一项不会影响前端开发进度的工具 | | **@delon/util** | [文档](/util) | 包含数组、延迟、字符串、日期、校验等常见工具集 | | **@delon/testing** | - | 常用测试套件 | | **CLI Schematics** | [文档](/cli) | 快速生成统一的模板、可插拔的插件等 | ## 目录结构 当使用 `ng add ng-alain` 生成后的 NG-ALAIN 脚手架,它的基本目录结构概略图如下: ``` ├── _mock # Mock 数据规则目录 ├── angular.json # Angular 项目配置文件 ├── src │   ├── app │   │   ├── core # 核心 │   │   │   ├── i18n │   │   │   ├── net │   │   │   │   └── default.interceptor.ts # 默认HTTP拦截器 │   │   │   ├── services │   │   │   │   └── startup.service.ts # 初始化项目配置 │   │   │   └── index.ts # 核心导出 │   │   ├── layout # 通用布局 │   │   ├── routes │   │   │   ├── ** # 业务目录 │   │   │   └── routes.ts # 业务路由 │   │   ├── shared # 共享 │   │   │   ├── shared-imports.ts # 一些高频率共享组件的集合 │   │   │   └── index.ts # 共享导出 │   │   ├── app.ts # 根组件 │   │   └── app.config.ts # 全局配置项 │   ├── assets # 本地静态资源 │   ├── environments # 环境变量配置 │   ├── styles # 样式目录 └── └── style.less # 样式引导入口 ``` 以下是针对各个目录及文件说明及使用目的: | 名称 | 描述 | |----|----| | **angular.json** | Angular 工作区及项目的配置文件,参考[Angular文档](https://angular.cn/guide/workspace-config) | | **_mock** | Mock 数据规则目录,若你通过 [命令行工具](/cli) 创建项目时可以指定 `--mock` 参数决定是否需要 Mock 功能 | | **src/app/core/index.ts** | 一些核心业务服务(例如:消息、数据访问等) | | **src/app/core/i18n** | [国际化](/docs/i18n)数据加载及处理相关类,若你通过 [命令行工具](/cli) 创建项目时可以指定 `-di` 参数决定是否需要国际化支持 | | **src/app/core/net** | 默认拦截器,你可以在这里统一处理请求参数、请求异常、业务异常等动作 | | **src/app/core/startup/startup.service.ts** | 当你需要在 Angular 启动前执行一些远程数据(例如:应用信息、用户信息等)时非常有用 | | **src/app/layout** | 布局目录,包含基础布局、空白布局、用户登录布局 | | **src/app/routes** | 业务模块,你的所有业务代码都将在这里 | | **src/app/shared/index.ts** | 一些高频率共享组件的集合,指当你需要针对整个**业务模块都需要**使用的一些第三方模块、自定义组件、自定义指令,都应该存在这里。除此之外,针对 @delon & NG-ZORRO 分别构建了 `shared-delon.module.ts`、`shared-zorro.module.ts` 两种次级共享模块的导入。 | | **src/app/app.config.ts** | 项目全局配置项 | | **src/environments** | 应用环境变量,包含以下值:`SERVER_URL` 所有HTTP请求的前缀;`production` 是否生产环境;`useHash` 路由是否useHash模式 | --- --- order: 60 title: 使用第三方类库 type: Dev --- 除了 NG-ZORRO 基础组件以及 @delon 业务组件以外,有时我们还需要引用其他外部类库,以下将介绍如何使用富文本组件 [ngx-tinymce](https://github.com/cipchk/ngx-tinymce): ## Angular组件 ### 安装依赖包 ```bash npm i -S ngx-tinymce ``` ### 注册 像富文本框你可能需要在所有子模块中都会可能会用到,因为建议在 `SharedModule` 模块中导入和导出他。 ```ts // #region third libs import { NgxTinymceModule } from 'ngx-tinymce'; const THIRDMODULES = [ NgxTinymceModule ]; // #endregion ``` > `region: third libs` 区域是NG-ALAIN的一个编码约定,将所有第三方组件注册至 `THIRDMODULES` 变量中即可,而无须关心 `@NgModule` 内在的写法,有关更多编码约定可以参考[编码规范建议](/docs/style-guide)。 对于部分第三方组件,可能会需要一些配置项,建议在根模块中注册,例如: ```ts import { NgxTinymceModule } from 'ngx-tinymce'; @NgModule({ imports: [ BrowserModule, NgxTinymceModule.forRoot({ baseURL: '//cdn.bootcss.com/tinymce/4.7.13/' }) ] }) export class AppModule { } ``` 接下来你可以在任何子模块中使用 `ngx-tinymce`: ```html ``` ## 非Angular组件 引用一个非 Angular 组件实际上是一个 JavaScript 类库,例如二维码类库 [qrious](https://github.com/neocotic/qrious/),这是一个纯洁的 JavaScript 类库(建议尽可能使用纯洁类库而非带有依赖其他)。 ### 安装依赖包 ```bash npm i -S qrious ``` ### 导入脚本 在 `angular.json` 找到 `scripts` 节点并增加: ```json "scripts": [ "node_modules/qrious/dist/qrious.min.js" ] ``` 如果第三方类库需要额外的样式,还需要在 `styles` 增加路径。 > 注意:需要重新运行 `ng s` 才会生效。 **延迟加载脚本** 上述导入脚本方式会把代码直接打包进 `scripts.js`,这会导致 `scripts.js` 体积变大,NG-ALAIN 提供另一种延迟加载CDN类库脚本解决方案,适用低使用率的业务(例如:[zip](https://cdn.bootcss.com/jszip/3.1.5/jszip.min.js) 压缩),可以利用 [LazyService](/util/lazy) 延迟加载远程CDN脚本。 ### 使用 Angular 是采用 TypeScript 语言,所有类型都必须明确定义才能使用,细节请参考 [Angular 如何使用第三方库](https://zhuanlan.zhihu.com/p/35796451)。 一个调用的示例代码: ```ts declare var QRious: any; @Component() export class DEMOComponent { constructor() { const qr = new QRious(); } } ``` --- --- type: Theme order: 1 title: 全局参数 --- ## 公共类 | 名称 | 默认值 | 功能 | | --- | --- | --- | | `@layout-gutter` | `8px` | antd布局间距,不可改变 | | `@font-size-base` | `14px` | antd字号 | | `@primary-color` | 蓝色 | antd 主色 | | `@mobile-min` | `768px` | PC端 | | `@mobile-max` | `767px` | 移动端 | | `@text-xs` | `@font-size-base - 2` | xs 文本大小 | | `@text-sm` | `@font-size-base + 0` | sm 文本大小 | | `@text-md` | `@font-size-base + 2` | md 文本大小 | | `@text-lg` | `@font-size-base + 4` | lg 文本大小 | | `@text-xl` | `@font-size-base + 8` | xl 文本大小 | | `@text-xxl` | `@font-size-base + 12` | xxl 文本大小 | | `@icon-sm` | `@font-size-base * 2` | sm 图标 | | `@icon-md` | `@font-size-base * 4` | md 图标 | | `@icon-lg` | `@font-size-base * 6` | lg 图标 | | `@icon-xl` | `@font-size-base * 8` | xl 图标 | | `@icon-xxl` | `@font-size-base * 10` | xxl 图标 | | `@h1-font-size` | `32px` | h1字号 | | `@h2-font-size` | `24px` | h2字号 | | `@h3-font-size` | `20px` | h3字号 | | `@h4-font-size` | `16px` | h4字号 | | `@h5-font-size` | `14px` | h5字号 | | `@h6-font-size` | `12px` | h6字号 | | `@enable-all-colors` | `false` | 开启背景、文本颜色
    例如:`.bg-teal`、`.text-teal`
    有关颜色值见样式规则章节 | | `@modal-sm` | `300px` | 小号对话框 | | `@modal-md` | `500px` | 中号对话框 | | `@modal-lg` | `900px` | 大号对话框 | | `@modal-xl` | `1200px` | 超大号对话框 | | `@drawer-sm` | `300px` | 小号抽屉 | | `@drawer-md` | `500px` | 中号抽屉 | | `@drawer-lg` | `900px` | 大号抽屉 | | `@drawer-xl` | `1200px` | 超大号抽屉 | | `@drawer-sm-height` | `200px` | 小号抽屉 | | `@drawer-md-height` | `400px` | 中号抽屉 | | `@drawer-lg-height` | `600px` | 大号抽屉 | | `@drawer-xl-height` | `800px` | 超大号抽屉 | | `@code-border-color` | `#eee` | `` 边框颜色 | | `@code-bg` | `#f7f7f7` | `` 背景颜色 | | `@widths` | `xs @layout-gutter * 10`
    `sm @layout-gutter * 20`
    `md @layout-gutter * 30`
    `lg @layout-gutter * 40`
    `xl @layout-gutter * 50`
    `xxl @layout-gutter * 50` | 宽度 | | `@border-radius-md` | `4px` | 中号边框圆角 | | `@border-radius-lg` | `6px` | 大号边框圆角 | | `@masonry-column-gap` | `@layout-gutter * 2` | CSS瀑布流列与列的间距 | | `@scrollbar-enabled` | `true` | 启用美化滚动条 | | `@scrollbar-width` | `6px` | 美化滚动条宽度 | | `@scrollbar-height` | `6px` | 美化滚动条高度 | | `@scrollbar-track-color` | `rgba(0, 0, 0, 0.3)` | 美化滚动条的轨道颜色 | | `@scrollbar-thumb-color` | `transparent` | 美化滚动条小方块颜色 | | `@scrollbar-table-enabled` | `false` | 启用美化表格滚动条 | | `@rtl-enabled` | `false` | 是否支持 RTL | | `@enabled-util-align` | `true` | 是否启用工具类 align | | `@enabled-util-border` | `true` | 是否启用工具类 border | | `@enabled-util-code` | `true` | 是否启用工具类 code | | `@enabled-util-color` | `true` | 是否启用工具类 color | | `@enabled-util-display` | `true` | 是否启用工具类 display | | `@enabled-util-float` | `true` | 是否启用工具类 float | | `@enabled-util-icon` | `true` | 是否启用工具类 icon | | `@enabled-util-img` | `true` | 是否启用工具类 img | | `@enabled-util-position` | `true` | 是否启用工具类 position | | `@enabled-util-overflow` | `true` | 是否启用工具类 overflow | | `@enabled-util-responsive` | `true` | 是否启用工具类 responsive | | `@enabled-util-spacing` | `true` | 是否启用工具类 spacing | | `@enabled-util-text` | `true` | 是否启用工具类 text | | `@enabled-util-width` | `true` | 是否启用工具类 width | | `@enabled-util-scrollbar` | `true` | 是否启用工具类 scrollbar | | `@enabled-util-other` | `true` | 是否启用工具类 other | ## Ng补丁 ### 通用 | 名称 | 默认值 | 功能 | | --- | --- | --- | | `@preserve-white-spaces-enabled` | `true` | 解决开启 [preserveWhitespaces](https://angular.io/api/core/Component#preserveWhitespaces) 时按钮间可能会出现无缝 | | `@preserve-sf-and-st-spaces` | `16px` | `sf` 与 `st` 间间距 | | `@preserve-buttons-spaces` | 按钮间间距(包括:button、button-group、popconfirm) | | `@router-animation-enabled` | `false` | 是否启用路由切换动画 | | `@router-animation-duration` | `antFadeIn` | 路由切换动画 | | `@router-animation-duration` | `1s` | 路由切换动画时长 | ## Zorro组件补丁 ### 通用 | 名称 | 默认值 | 功能 | | --- | --- | --- | | `@forced-turn-off-nz-modal-animation-enabled` | `false` | 强制关闭 `nz-modal` 动画效果 | ### 表单 | 名称 | 默认值 | 功能 | | --- | --- | --- | | `@form-state-visual-feedback-enabled` | `false` | 开启表单元素的视觉反馈 | | `@search-form-bg` | `#fbfbfb` | 列表页简易搜索表单背景色 | | `@search-form-radius` | `4px` | 列表页简易搜索表单圆角 | ### 表格 指 `nz-table`。 | 名称 | 默认值 | 功能 | | --- | --- | --- | | `@nz-table-img-radius` | `4px` | 表格中的图片圆角 | | `@nz-table-img-margin-right` | `4px` | 表格中的图片右外边距 | | `@nz-table-img-max-width` | `32px` | 表格中的图片最大宽度 | | `@nz-table-img-max-height` | `32px` | 表格中的图片最大高度 | | `@nz-table-even-background` | `none` | 奇偶背景 | | `@nz-table-rep-max-width` | `@mobile-max` | 当移动端屏幕时触发表格响应式 | | `@nz-table-rep-header-background` | `@border-color-split` | 表格响应式:标题背景色 | | `@nz-table-rep-even-background` | `#f9f9f9` | 表格响应式:偶数行背景色 | | `@nz-table-rep-padding-vertical` | `8px` | 表格响应式:单元格垂直间距 | | `@nz-table-rep-padding-horizontal` | `8px` | 表格响应式:单元格水平间距 | | `@nz-table-rep-column-name-width` | `100px` | 表格响应式:列名最大宽度 | | `@nz-table-rep-column-name-text-align` | `right` | 表格响应式:列名文本对齐方式 | | `@nz-table-rep-column-name-padding-right` | `right` | 表格响应式:列名右间距 | | `@nz-table-rep-column-name-color` | `rgba(0, 0, 0, 0.5)` | 表格响应式:列名颜色 | ## 小部件 | 名称 | 默认值 | 功能 | | --- | --- | --- | | `@hafl-enabled` | `true` | 半图 | | `@abs-enabled` | `true` | 中心元素 | | `@masonry-enabled` | `true` | CSS瀑布流列 | | `@setting-drawer-enabled` | `true` | 主题设置 | | `@search__form-enabled` | `true` | Pro搜索框,[DEMO](https://ng-alain.surge.sh/) | --- --- order: 90 title: en-US: Global Configuration zh-CN: 全局配置项 type: Dev --- 我们给众多组件添加了**全局配置**功能,你可以通过全局配置来定义组件的默认行为,从而减少在模板中需要写的代码(让你的代码更加清爽),还能在运行时修改全局配置项。 ## 如何使用 想要为某些组件提供默认配置项,可以使用 `provideAlain` 函数,转入一个符合 `AlainProvideOptions` 接口的对象,例如: ```typescript // global-config.module.ts import { AlainConfig, AlainProvideLang } from '@delon/util/config'; import { ICONS } from '../style-icons'; import { ICONS_AUTO } from '../style-icons-auto'; const defaultLang: AlainProvideLang = { abbr: 'zh-CN', ng: ngLang, zorro: zorroLang, date: dateLang, delon: delonLang }; const alainConfig: AlainConfig = { st: { ps: 3 }, }; export const appConfig: ApplicationConfig = { providers: [ provideAlain({ config: alainConfig, defaultLang, icons: [...ICONS_AUTO, ...ICONS] }) ] }; ``` 这些全局配置项将会被注入 `AlainConfigService` 当中并保存。 ## 关于 NG-ZORRO 全局配置项 请参考 NG-ZORRO [官网文档](https://ng.ant.design/docs/global-config/zh)。 --- --- type: Documents order: 10 title: 包体优化 --- ng-alain 除了提供主题方案以外,还包含一套类似 Bootstrap 的工具集,并且严格遵守 Antd 的设计价值观,对于熟悉 Bootstrap 的人来说会非常友好,因为所有命名方式都尽可能与 Bootstrap 相同。同时,我们为 VSCode 写的 [ng-alain插件](https://marketplace.visualstudio.com/items?itemName=cipchk.ng-alain-vscode) 会自动为你提供这套CSS工具集的智能提醒。 ## 如何优化 我们包含着上百种 Less 变量(包含ng-zorro-antd、ng-alain),这些变更有部分是包含着`-enabled` 后缀,它表示这些类库是可选的。 你可以在 ng-alain 项目的 [theme.less](https://github.com/ng-alain/ng-alain/blob/master/src/styles/theme.less) 中设置为 `false`,可以有效减少 css 文件大小。 ```less // 不需要 masonry 样式 @masonry-enabled: false ``` ## 参数 | 名称 | 默认值 | 功能 | | --- | --- | --- | | `@scrollbar-enabled` | `true` | 启用美化滚动条 | | `@preserve-white-spaces-enabled` | `true` | 解决开启 [preserveWhitespaces](https://angular.io/api/core/Component#preserveWhitespaces) 时按钮间可能会出现无缝 | | `@form-state-visual-feedback-enabled` | `false` | 开启表单元素的视觉反馈 | | `@hafl-enabled` | `true` | 半图 | | `@abs-enabled` | `true` | 中心元素 | | `@masonry-enabled` | `true` | CSS瀑布流列 | | `@setting-drawer-enabled` | `true` | 主题设置 | | `@search-form-enabled` | `true` | 简化搜索框,[DEMO](https://ng-alain.surge.sh/) | | `@search__form-enabled` | `true` | Pro搜索框,[DEMO](https://ng-alain.surge.sh/) | --- --- order: 1000 type: Basic title: 升级到 21.0 版本 --- > 本指南适用于当前版本 ng-alain >= `20` ; > 如果在升级过程中遇到问题,欢迎提出。提问前请阅读 [如何向开源社区提问题](https://github.com/seajs/seajs/issues/545) > 如果发现本指南存在遗漏/错误,请指出! > 或者你遇到了新的问题并解决了,欢迎补充! ## 开始之前 1. 首先确保你 `Node.js` >= `22.21.1` 2. 创建新的分支,或者使用其他方式备份当前项目 3. 删除项目下 `package-lock.json` 或 `yarn.lock` 文件 ## 升级步骤 ### 升级相关依赖 - 将项目升级到 Angular 21 运行 `ng update @angular/core@21 @angular/cli@21 angular-eslint@21 ng-zorro-antd@21 ng-alain@21`。 - _如果你有单独使用 `@angular/cdk` 请执行 `ng update @angular/cdk@21`_ > > NG-ALAIN脚手架升级全部变更文件,请参考:[#2593](https://github.com/ng-alain/ng-alain/pull/2593/files)。 ### 2. ng-zorro-antd BREAKING CHANGES 需要注意 NG-ZORRO 有存在 BREAKING CHANGES;细节请参考 https://github.com/NG-ZORRO/ng-zorro-antd/releases/tag/21.0.0 ### 3. 参考 - 代码风格配置变更 [#2594](https://github.com/ng-alain/ng-alain/pull/2594/files) --- --- order: 20 title: 发送Token type: Documents --- ## 认证风格 通过HTTP拦截器在每一个请求中加入相应的认证信息,这是再好不过。`@delonn/auth` 根据两种不同认证风格,实现两种各自的HTTP拦截器。 ### authSimpleInterceptor 透过 `DelonAuthConfig` 可以指定参数名以及其发送位置,例如: ```ts token_send_key = 'token'; token_send_template = 'Bearer ${token}'; token_send_place = 'header'; ``` 表示在每一个请求的 `header` 加上 `{ token: 'Bearer token_string' }` 数据。 ### authJWTInterceptor 它是一个标准JWT的发送规则,即在 `header` 自动加上 `{ Authorization: 'Bearer token_string' }`。 ### 如何选择? `authSimpleInterceptor` 是一种自由度非常高的风格,你可以将 `token` 放在请求体、请求头等当中。 `authJWTInterceptor` 是一个 JWT 标准,这需要确保后端也采用这类标准。 ## 如何加载 在 `app.config.ts` 加入: ```ts providers: [ // 表示使用JWT风格并用 `localStorage` 存储 Token provideHttpClient(withInterceptors([...(environment.interceptorFns ?? []), authSimpleInterceptor, defaultInterceptor])), // Or JWT provideHttpClient(withInterceptors([...(environment.interceptorFns ?? []), authJWTInterceptor, defaultInterceptor])), ] ``` ## 示例 [DEMO](//ng-alain.github.io/ng-alain/#/passport/login),账密或其他登录方式登录,并观察浏览器 localStorage 存储数据的变化。 --- --- order: 40 title: 和服务端进行交互 type: Dev --- NG-ALAIN 是一套基于 Angular 技术栈的单页面应用,我们提供的是前端代码和本地模拟数据的开发模式, 通过 Restful API 的形式和任何技术栈的服务端应用一起工作。下面将简单介绍和服务端交互的基本写法。 ## 前端请求流程 在 NG-ALAIN 中,一个完整的前端 UI 交互到服务端处理流程是这样的: 1. 首次启动 Angular 执行 `APP_INITIALIZER`; - 通常会在启动前先加载一些APP通用数据,例如:当前已授权用户数据、菜单数据、字典数据、配置项等 2. UI 组件交互操作; 3. 使用封装的 [_HttpClient](/theme/http) 发送请求; 4. 触发用户认证拦截器 [@delon/auth](/auth/getting-started),统一加入 `token` 参数; - 若未存在 `token` 或已过期中断后续请求,直接跳转至登录页; 5. 触发默认拦截器,统一处理前缀等信息; 6. 获取服务端返回; 7. 触发默认拦截器,统一处理请求异常、业务异常等; 8. 数据更新,并刷新 UI。 ### 拦截器 默认情况下在根模块注册了两个拦截器 [SimpleInterceptor](https://github.com/ng-alain/delon/blob/master/packages/auth/token/simple/simple.interceptor.ts) 和 [DefaultInterceptor](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/net/default.interceptor.ts),且执行顺序按注册顺序执行。 **SimpleInterceptor** [用户认证](/auth)内置用于自动为请求添加 `token` 参数的拦截器。这里还有一个叫 [JWTInterceptor](https://github.com/ng-alain/delon/blob/master/packages/auth/token/jwt/jwt.interceptor.ts) 拦截器,是一个标准 JWT 规范,若后端采用标准JWT可以直接换成JWTInterceptor拦截器。 **DefaultInterceptor** [DefaultInterceptor](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/net/default.interceptor.ts) 拦截器只是提供一个拦截器的写法,默认包含了统一处理服务器请求前缀、处理请求异常及业务异常的示例代码,你可以根据你自己的需求做调整。 **注意点** 我们可以把拦截器 `intercept` 方法内,以 `next.handle(req)` 为分界点,前部分为请求前,`pipe` 部分为接收后。这样会更明确知道哪一部分是请求前要做,哪一部分是请求后会执行的。有关更多拦截器知识请参考官网。 ## 开发环境 正常情况下开发环境和生产环境不是同一个后端请求源,实际可以通过配置 [environment](https://github.com/ng-alain/ng-alain/tree/master/src/environments) 目录下 [environment.ts](https://github.com/ng-alain/ng-alain/blob/master/src/environments/environment.ts) 和 [environment.prod.ts](https://github.com/ng-alain/ng-alain/blob/master/src/environments/environment.prod.ts) 改变不同环境的请求源。 > environment 实际是一个JSON对象,可以组织不同形式来满足多请求源的问题。 ## Mock 有时候希望优先开发前端时,可以利用 [@delon/mock](/mock) 来模拟请求数据,实际原理是利用拦截器,对匹配的URL直接返回数据,而不是发送一个HTTP请求,默认情况下只对测试环境有效。当然通常情况下你需要确保 Mock 接口的数据与后端保持一致,你可以在 `_mock` 目录下创建相应的 Mock 接口: ```ts export const USERS = { 'GET /users': { users: [1, 2], total: 2 } } ``` 因此对于测试环境下当遇到 `/users` 请求直接返回 `{ users: [1, 2], total: 2 }` 数据。有关更多 Mock 语法和使用方式参考[这里](/mock)。 **注:** 当你不需要某个请求的 Mock 接口时,务必要注释掉或移除它。 ## 跨域 大部分应用都会前后端分离进行开发,这导致当对后端发起一个请求时会受跨域(CORS)的因素,例如: ```ts http.get(`http://192.168.1.100/api/app`).subscribe(); ``` > 注:如果非 `http` 开头的请求,会在每个请求都会加上 `environment.SERVER_URL` 作为请求 URL 的前缘。 直接返回以下错误: ``` Access to XMLHttpRequest at 'http://192.168.1.100/api/app' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. ``` 而正常解决跨域问题有两种方法,一是让后端开发环境直接支持跨域请求(不推荐,但最简单),二是利用 Angular Cli 提供[代理支持](https://webpack.js.org/configuration/dev-server/#devserver-proxy),开发代理服务器会将 Angular 发送的请求的域和端口转发给后端服务器,CORS 是浏览器的安全限制,在代理服务器与后端服务器之前并不存在 CORS 的问题,这也就是为什么很多人会尝试明明在 Postman 能请求,而在 Angular 下无法请求的原因所在。 假定所有后端请求都是以 `/api` 为前缀时,就可以在 `proxy.conf.js` 配置所有这个前缀都转向新的后端,例如: ```js module.exports = { '/api': { target: 'http://192.168.1.100/api', secure: false } } ``` - `/api` 代理路径,不支持域 - `target` 代理目标地址 - `secure` 代理目标地址如果是 `https` 应该设置为 `true`,反之为 `false` - `pathRewrite` 重写地址,例如 `pathRewrite: {'^/api': '/'}` 将前缀 `/api` 转为 `/` - `changeOrigin` 将主机标头的 `host` 更改为目标URL,有些后端会根据其值来判断是否有效,可能需要通过设置 `true` - `logLevel` 设置为 `debug` 可以终端显示代理转发的消息 更多使用说明请参考[代理到后端服务器](https://angular.cn/guide/build#proxying-to-a-backend-server),以及配置描述请参考[http-proxy-middleware options](https://github.com/chimurai/http-proxy-middleware#options)。 ## 常见问题 **请求可能被拒绝或直接返回 `401` ?** 脚手架默认情况下使用了 `@delon/auth` 的 `SimpleInterceptor` 拦截器,导致在请求过程中若发现无法获取 Token 时会直接返回错误。 [用户认证](/auth)这个过程是中台必备的。 **关于无法显示请求日志** 从 Angular13 开始远程请求的调试日志将不再终端中显示,若有显示日期需求,可自行参考 [How to fix logging for proxy in angular](https://medium.com/@gagandeep.sidhu88/how-to-fix-logging-for-proxy-in-angular-834cf46d437d) 解决。 --- --- order: 30 title: en-US: I18n zh-CN: 国际化 type: Advance --- Angular 国际化提供一种可被提取语言文件的方案,但对于 NG-ALAIN 而言,这并不是最好的方式;这主要受限于 @Delon/* 组件库需要提供一套带有动态翻译的服务,因此,NG-ALAIN 内置一个简易的国际化服务 `ALAIN_I18N_TOKEN` 接口。 ## 如何配置 脚手架是由 `ng-zorro-antd`、`@delon/*` 类库两个重要部分组件,而这两个类库有自己的国际化配置,当进行国际化时需要对这些类库进行相同语言的配置。 ### Angular Angular 配置主要是针对货币、日期格式等,例如中文版本: ```ts import { registerLocaleData } from '@angular/common'; import zh from '@angular/common/locales/zh'; registerLocaleData(zh); ``` ### ng-zorro-antd NG-ZORRO 国际化默认是中文版,例如默认为英文版本: ```ts import { en_US, provideNzI18n } from 'ng-zorro-antd/i18n'; export const appConfig: ApplicationConfig = { providers: [provideNzI18n(en_US)] }; ``` 当然,也可以使用运行时更改: ```ts import { en_US, NzI18nService } from 'ng-zorro-antd/i18n'; ... constructor(private nzI18nService:NzI18nService) { } switchLanguage() { this.nzI18nService.setLocale(en_US); } ``` ### @delon @delon 国际化默认是中文版,例如默认为英文版本: ```ts import { DELON_LOCALE, en_US } from '@delon/theme'; @NgModule({ ... providers : [ { provide: DELON_LOCALE, useValue: en_US } ] }) export class AppModule { } ``` 当然,也可以使用运行时更改: ```ts import { en_US, DelonLocaleService } from '@delon/theme'; ... private readonly i18n = inject(DelonLocaleService); switchLanguage() { this.delonLocaleService.setLocale(en_US); } ``` ## ALAIN_I18N_TOKEN `@delon/*` 类库有许多带有 _i18n_ 字样的数据接口属性(例如:`page-header`、`st` 列描述、`Menu` 菜单数据等等),当你希望这些组件的数据接口能动态根据 Key 值按当前语言自动切换时,你还需要对 `ALAIN_I18N_TOKEN` 定义一个自实现服务接口(例如:[I18NService](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/i18n/i18n.service.ts)),并在 `app.config.ts` 下注册。 ```ts import { I18NService } from '@core'; export const appConfig: ApplicationConfig = { providers: [ provideAlain({ config: alainConfig, defaultLang, i18nClass: I18NService }), ] }; ``` ### i18n管道 为了不受第三方各自管道的命名方式,脚手架包含一个 `i18n` 的管道,它相当于直接调用 `ALAIN_I18N_TOKEN` 的 `fanyi` 方法。 `| i18n` 不会监听语言变更通知所以会有更好的性能,当你明确在切换语言后会重新渲染 Angular 项目时 `| i18n` 会更适合。 ## 如何添加 创建脚手架[命令行](/cli/add) `ng add ng-alain` 时允许指定 `--i18n` 表示是否包含国际化示例代码。 ## 如何删除 示例代码涉及内容包括: - `src/app/core/i18n` 目录 - 替换掉默认布局可能出现的 I18n 的 Pipe 使用 `| i18n` ## 默认语言 不管是否需要国际化,由于 `Angular`、`ng-zorro-antd`、`@delon/*` 等类库的默认语言都不同,因此还需要确保这些类库的默认语言是**同一类型**。一个简单的示例办法可以了解各类库当前语言情况: ```ts import { Component } from '@angular/core'; @Component({ selector: 'app-i18n-test', template: `

    angular

    Date: {{now | date}}

    ng-zorro-antd

    @delon

    1
    `, }) export class I18nTestComponent { now = new Date(); } ``` ### 示例 为了使语言统一性,NG-ALAIN 提供一个在 `AppModule` 根模块里简单的统一配置方式。 #### 中文版 ```ts import { default as ngLang } from '@angular/common/locales/zh'; import { provideNzI18n, zh_CN as zorroLang } from 'ng-zorro-antd/i18n'; import { DELON_LOCALE, zh_CN as delonLang } from '@delon/theme'; import { zhCN as dateLang } from 'date-fns/locale'; import { I18NService } from '@core'; const defaultLang: AlainProvideLang = { abbr: 'zh', ng: ngLang, zorro: zorroLang, date: dateLang, delon: delonLang, }; export const appConfig: ApplicationConfig = { providers: [ provideAlain({ config: alainConfig, defaultLang, i18nClass: I18NService }), ] }; ``` #### 英文版 ```ts import { default as ngLang } from '@angular/common/locales/en'; import { provideNzI18n, en_US as zorroLang } from 'ng-zorro-antd/i18n'; import { DELON_LOCALE, en_US as delonLang } from '@delon/theme'; import { en_US as dateLang } from 'date-fns/locale'; const LANG = { abbr: 'en', ng: ngLang, zorro: zorroLang, delon: delonLang, }; const defaultLang: AlainProvideLang = { abbr: 'en', ng: ngLang, zorro: zorroLang, date: dateLang, delon: delonLang, }; export const appConfig: ApplicationConfig = { providers: [ provideAlain({ config: alainConfig, defaultLang, i18nClass: I18NService }), ] }; ``` ### 命令行 使用 [defaultLanguage](/cli/plugin/zh#defaultLanguage) 插件可以快速切换默认语言环境。 ## 国际化路由 若想通过路由的URL来切换国际化,例如:通过访问 `/zh` 和 `/en` 来变更语言,则只需要在根路由中使用 `alainI18nCanActivate` 守卫: ```ts const routes: Route[] = [ { path: '', component: LayoutComponent, canActivateChild: [alainI18nCanActivate], children: [ { path: '', redirectTo: 'en', pathMatch: 'full' }, { path: ':i18n', component: HomeComponent } ] } ]; ``` > 其中 `:i18n` 是参数固定值,可以通过[全局配置](/docs/global-config) `paramNameOfUrlGuard` 来调整。 --- --- order: 2 title: 国际化 type: Documents --- 为整个 `@delon/*` 类库内建文案提供统一的国际化支持。 ## 使用 ### 全局配置 只需要在根模块重新对 `DELON_LOCALE` 注入目标语言即可全局生效。 ```ts import { DELON_LOCALE, en_US } from '@delon/theme'; @NgModule({ ... providers : [ { provide: DELON_LOCALE, useValue: en_US } ] }) export class AppModule { } ``` ### 运行时修改 `@delon` 提供了一个服务 `DelonLocaleService` 用于动态修改国际化文案。 ```ts import { en_US, DelonLocaleService } from '@delon/theme'; ... constructor(private delonLocaleService: DelonLocaleService) { } switchLanguage() { this.delonLocaleService.setLocale(en_US); } ``` 注意:`en_US` 是语言包名称,以下表格也遵循同样的规则。 ## 支持语言 | 语言 | 语言包名称 | |----|-------| | 英语(美式) | en_US | | 简体中文 | zh_CN | | 繁体中文 | zh_HK | | 繁体中文 | zh_TW | | 土耳其语 | tr_TR | | 波兰语 | pl_PL | | 希腊语 | el_GR | | 朝鲜语 | ko_KR | | 克罗地亚 | hr_HR | | 日语 | ja_JP | | 斯洛文尼亚文 | sl_SI | | 法文 | fr_FR | | 西班牙语 | es_ES | | 意大利语 | it_IT | | 越南语 | vi_VN | | 阿拉伯语 | ar_SA | | 印度尼西亚语 | id_ID | | 高棉语 | km_KH | | 马来语 | ms_MY | | 泰语 | th_TH | ## 增加语言包 如果你找不到你需要的语言包,欢迎你在 [英文语言包](https://github.com/ng-alain/delon/tree/master/packages/theme/src/locale/languages/en-US.ts)(你也可以参考 [#308](https://github.com/ng-alain/delon/pull/308) 向我们贡献语言包)的基础上创建一个新的语言包,并给我们 Pull Request。 --- --- order: 5 title: 如何布局 type: Documents --- @delon/form 布局是基于 [Grid](https://ng.ant.design/components/grid/zh) 栅格系统,而 SFSchema 的[渲染类](/form/schema/en#%E6%B8%B2%E6%9F%93%E7%B1%BB)来决定布局参数。 表单布局分为行内、垂直、水平(默认)三类,它由 [layout](/form/getting-started/zh#API) 决定。 ## 类型 ### 行内 表单项水平行内排列,一般用于简单搜索框。 表单项的宽度由组件自身来决定,你可以使用 `width` 参数来调整其大小,像小部件 `select` 可能会因为未设置默认值倒置宽度极小。 > 可通过设置 [mode](/form/getting-started/zh#mode%E6%9C%89%E4%BB%80%E4%B9%88%E4%BD%9C%E7%94%A8%EF%BC%9F) 参数来快速设置为搜索模式。 ### 垂直 标签和表单控件上下垂直排列。 ### 水平 标签和表单控件水平排列,一般用于编辑页。 水平类型相对于行内与垂直更复杂一点,因为会涉及响应式,决定每个表单项所站的格数是由 [grid](/form/schema#%E5%93%8D%E5%BA%94%E5%BC%8F%E5%B1%9E%E6%80%A7-SFGridSchema) 属性来决定。 > 可通过设置 [mode](/form/getting-started/zh#mode%E6%9C%89%E4%BB%80%E4%B9%88%E4%BD%9C%E7%94%A8%EF%BC%9F) 参数来快速设置为编辑模式。 **非响应式** 非响应式时只需要维护 `span` 属性即可。 **响应式** 响应式是根据 `xs`、`sm`、`md`、`lg`、`xl`、`xxl` 来决定不同屏幕时决定要占用几格,有几个注意点: - 每一行只能有 `24` 格 - 表示两个表单项在同一行,则设置值为 `12` - 表示屏幕 `≥992px` 时两个表单项在同一行,小于则每个表单项为一行,则:`{ sm: 24, md: 12 }` ## 不规则布局 当然,表单不可能每一行都有固定表单项数量,有可能某个表单项会占用一整行,由于栅格系统的因素,这会产生另一个问题:**标签无法对齐**,sf 提供一种方案,即固定所有标签的宽度 `spanLabelFixed` 属性,例如: ```json { "properties": { "email": { "type": "string", "title": "邮箱", "format": "email" }, "name": { "type": "string", "title": "姓名", "minLength": 5 }, "remark": { "type": "string", "title": "描述", "ui": { "widget": "textarea", "autosize": true, "grid": { "span": 24 } } } }, "ui": { "spanLabelFixed": 100, "grid": { "span": 12 } } } ``` ## 按钮 按钮布局渲染同表单项一样布局、参数,可以通过 [SFButton](/form/getting-started#SFButton) 属性来调整按钮渲染风格。 **注意事项** - 值为 `null` 或 `undefined` 表示手动添加按钮,但保留容器 - 值为 `none` 表示手动添加按钮,且不保留容器 - 使用 `spanLabelFixed` 固定标签宽度时,若无 `render.class` 则默认为居中状态 **自定义** 当你希望自定义按钮时,务必设置 `button` 值为 `null`。 ```html ``` ## 展开与收缩 当表单表单项过多时,可以将不重要的字段标记为可折叠,并配合展开/收缩按钮来切换显隐。 **使用方法** 在需要折叠的字段上设置 `ui.collapse = true`: ```json { "properties": { "name": { "type": "string", "title": "姓名" }, "email": { "type": "string", "title": "邮箱" }, "nickname": { "type": "string", "title": "昵称", "ui": { "collapse": true } }, "bio": { "type": "string", "title": "个人简介", "ui": { "collapse": true } } } } ``` 在 `sf` 组件上启用 `expandable` 即可: ```html ``` 展开/收缩按钮会自动出现在提交、重置按钮旁,只有当 **表单中存在 `collapse` 字段** 时按钮才会显示。 按钮文本支持国际化,默认值为 `展开` / `收起`。同时支持通过 `[(expanded)]` 双向绑定外部控制状态: ```html ``` **注意:** `expandable` 和 `collapse` 属于 UI 层面的控制,与字段的 `required` 逻辑无关。 --- --- order: 0 title: 如何开始 type: Dev i18n: need-update --- ## 前序准备 NG-ALAIN 技术栈基于 Typescript、Angular、图表G2 和 NG-ZORRO,在开始尝试使用 NG-ALAIN 脚手架前,请先提前了解和学习这些知识会非常有帮助。如果你是一名 Java 或 C# 后端开发人员,那么恭喜你,你所见到的不管是结构、代码、开发体验等都是你所想的那样。但不管怎么样,想写好 Angular 代码,以下这些文章及社区是你必须要知道的: - 文档类 - [TypeScript中文文档](https://www.tslang.cn/),虽然 TypeScript 跟 Java、C# 语法很像,这是语法基础需要认真阅读 - [Angular中文文档](https://angular.cn/docs),建议一定要花时间阅读文档部分,透过它基本上就可以学会 Angular;同时,也是 Angular API 接口文档 - [NG-ZORRO中文文档](https://ng.ant.design/docs/introduce/zh),NG-ZORRO 作为 NG-ALAIN 的基础组件库,当你不懂某个组件时,它就是最好的文档,包含组件用法及API说明 - [NG-ALAIN中文文档](https://ng-alain.com/),包含所有 `@delon/*` 类型的用法及API说明 - [G2图表中文文档](https://g2.antv.vision/zh),如果需要图表开发,则这份文档是必备 - 辅助类 - [Ant Design 指引文章](https://ant.design/docs/spec/introduce-cn),学习 Ant Design 的设计理念,非常值得阅读的部分 - [NG-ZORRO 社区推荐](https://ng.ant.design/docs/recommendation/zh),一份非常值得学习的清单 - NG-ALAIN 入门视频([YouTube](https://www.youtube.com/watch?v=lPnNKPuULVw&list=PLhWkvn5F8uyJRimbVZ944unzRrHeujngw)、[腾讯视频](http://v.qq.com/vplus/2c1dd5c6db4feeeea25e9827b38c171e/foldervideos/870001501oy1ijf)、[B站](https://space.bilibili.com/12207877/#/channel/detail?cid=50229)) - [NG-ALAIN 知乎专栏](https://zhuanlan.zhihu.com/ng-alain) ## 写在前面 很多人在学习一项新东西时,无外乎写个 Hello World 或是写一个 Http 请求,然后慢慢开始辐射所需要的技术知识。一个 HTTP 请求对中后台而言便是涵盖了 CURD 主要任务,甚至可以说90%时间及功能都在做这项工作。在[介绍](/docs/getting-started)章节中已经描述创建一个 NG-ALAIN 项目并如何运行它,如果此时你也想要写个 Hello World,那么只需要利用 VSCode 打开这个项目,并在 `dashboard.component.html` 文件内输入文本,500ms 后就会在页面上立即呈现。 ## 流程 回过头来我们试着回想一下,一个中后台项目,从启动再到呈现一份订单列表的功能,对于开发者而言包含了哪些事件。无外乎项目启动时应该加载点什么系统配置项,哪些页面用户无权进入;把粒度再想细一点,同一个页面不同的按钮给不同的人用,HTTP请求若产生错误是不是得每次都写相同的处理代码等等。 ### 初始化项目数据 Angular 提供一个DI(依赖注入)令牌 `APP_INITIALIZER` 让应用启动时可以做一些会影响渲染结果的数据,比如:语言数据、菜单数据、用户信息数据、字典数据等,并且必须返回一个 `Observable` 异步,异步意味者可以做很多有趣的事,比如数据来自远程。`APP_INITIALIZER` 只会执行一次,只需要在 `ApplicationConfig` 模块注册它就行了。 NG-ALAIN 脚手架提供了一个如何在启动 Angular 后先加载基础数据以后才会开始渲染的样板代码 [startup.service.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/startup/startup.service.ts)。 1. 提供统一注册 `provideStartup` 函数,只需要在 `app.config.ts` 注册就能生效 2. 提供 `load()` 函数,并确保**无论请求是否成功**都必须返回一个 `Observable` 以供Angular正常渲染,否则会导致Angular无法启动 > 注:NG-ALAIN 提供授权服务,若在请求数据接口无法授权时,可加 `ALLOW_ANONYMOUS` 来标记 NG-ALAIN 提供的 [startup.service.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/startup/startup.service.ts) 内容更加丰富一点,对于完整的中后台而言,大多数项目中以下这些信息都可以必备的: | 数据类型 | 描述 | |------|----| | 应用信息 | 应用名称、描述、年份,信息可以直接注入 `SettingsService`([API](/theme/settings))后直接在HTML模板中访问。
    例如:`this.settingService.setApp(res.app);` | | 用户信息 | 当前用户的姓名、头像、邮箱地址等,信息可以直接注入 `SettingsService`([API](/theme/settings))后直接在HTML模板中访问。
    例如:`this.settingService.setUser(res.user);` | | 布局信息 | 调整主题配置,例如:固定顶部菜单、折叠菜单等。
    例如:`this.settingService.setLayout("fixed", false);` 或 `this.settingService.setLayout("collapsed", false);` | | 菜单数据 | NG-ALAIN 认为菜单数据也是来自远程,也可以任意位置注入 `MenuService`([API](/theme/menu))来改变菜单数据,当然在 Angular 启动之前执行菜单赋值更为合理。
    菜单数据**务必**确保 [Menu](https://github.com/ng-alain/delon/blob/master/packages/theme/src/services/menu/interface.ts) 格式,菜单数据贯穿整个应用,例如:页头自动导航 [page-header](/components/page-header),页标题文本 [TitleService](/theme/title) 等。
    例如:`this.menuService.add(res.menu);` | | 页面标题 | 若页面标题总希望加上应用名称为后缀时,可以注入 `TitleService`([API](/theme/title))重新调整 `suffix` 属性值。
    例如设置页面标题的后缀:`this.titleService.suffix = res.app.name;` | | ACL | 访问控制列表数据,建议在启动前加载ACL访问控制权限数据,有关更多细节可参考 [访问控制列表](/acl)。
    例如设置全量权限:`this.aclService.setFull(true);` | | 国际化 | 建议在启动前优先加载国际化数据包,这样可确保项目启动后页面渲染为目标语言。更多细节参考[国际化](/docs/i18n)。 | ### 业务路由 当 Angular 项目正式启动后会进入渲染动作,根据当前的路由地址来决定一个页面如何渲染,从最顶层路由 [routes.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/routes/routes.ts) 开始一层层寻找,其结构如下: ```ts const routes: Routes = [ { path: '', component: LayoutBasicComponent, children: [ { path: '', redirectTo: 'dashboard', pathMatch: 'full' }, { path: 'dashboard', loadChildren: () => import('./dashboard/routes').then(m => m.routes) }, // 业务子模块 // { path: 'trade', loadChildren: () => import('./trade/routes').then(m => m.routes) }, ] }, // 空白布局 { path: 'blank', component: LayoutBlankComponent, children: [ ] }, { path: '', loadChildren: () => import('./passport/routes').then(m => m.routes) }, { path: 'exception', loadChildren: () => import('./exception/routes').then(m => m.routes) }, // 未命中路由全部跳转至 `exception/404` 页面上 { path: '**', redirectTo: 'exception/404' }, ]; ``` > 上述在业务模块中使用了 `LayoutBasicComponent` 基础布局、用户授权使用了 `LayoutPassportComponent` 用户授权布局以及 `LayoutBlankComponent` 空白布局,以上三种布局都可以在 [layout](https://github.com/ng-alain/ng-alain/tree/master/src/app/layout) 目录下找得到。 > NG-ALAIN 也提供一些[商用主题](https://e.ng-alain.com/)可供选择。 例如当用户访问 `/dashboard` 路由时,会先经过 `LayoutBasicComponent` -> `DashboardComponent`,最终换形成一个庞大的组件树来表示一个具体的页面。NG-ALAIN 脚手架帮助你完成大多数工作,而一个新入门的人更多只需要关心 `DashboardComponent` 业务组件该如何实现。 ### 用户认证与授权 页面能否访问取决于用户是否登录、已经登录还得判断否有授权,而这两项工作分别交给 `@delon/auth` 与 `@delon/acl` 来完成。 #### 用户认证 首先用户访问页面时是由顶层路由开始寻找命中后进行渲染,要想让所有未登录用户跳转到登录页面,可以配置 `canActivate` 选项,`@delon/auth` 已经提供了具体的实现,例如: ```ts const routes: Routes = [ { path: '', component: LayoutBasicComponent, canActivate: [authSimpleCanActivate], children: [ { path: '', redirectTo: 'dashboard', pathMatch: 'full' }, { path: 'dashboard', component: DashboardComponent, data: { title: '仪表盘' } }, ] }, ]; ``` > 这里的 `authSimpleCanActivate` 是因为采用基于 Simple Web Token 认证风格,其他认证方式请参考[用户认证](/auth)章节。 当用户未登录时会直接跳转至 `/passport/login` 页面,如果采用的是 JWT 认证方式,还会对 Token 是否有效进行检验。 #### 用户授权 接者用户访问的页面还需要取决于授权程度,例如系统配置页普通用户肯定无法进入。在初始化项目数据小节里会根据当前用户的 Token 来获得授权的数据,并将数据交给 `@delon/acl`,同时它也提供一组路由守卫的具体实现 `aclCanActivate` 方法,例如希望整个系统配置模块都必须是 `admin` 角色才能访问,则: ```ts const routes: Routes = [ { path: 'sys', canActivate: [aclCanActivate], data: { guard: 'admin' }, children: [ { path: 'config', component: ConfigComponent }, ] }, ]; ``` 此时,当一个未授权 `admin` 角色的用户尝试访问 `/sys/config` 页面时会被跳转至未授权错误页上。 当然还支持粒度有更细操作,比如某个按钮,请参考[ACL](/acl)章节。 ### 拦截网络请求 网络请求是一项非常频繁的工作,如果想优雅的在业务组件内使用网络请求动作的话,那么将服务端URL前缀、异常处理、Token 刷新等操作集中处理是必不可少的,NG-ALAIN 脚手架提供一个 [net](https://github.com/ng-alain/ng-alain/tree/master/src/app/core/net) 文件。它会利用令牌 `HttpInterceptorFn` 起到一种拦截器的效果。 有关更多细节,请参考 [default.interceptor.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/net/default.interceptor.ts) 文件。 ## IDE “工欲善其事,必先利其器是”,NG-ALAIN 脚手架推荐使用 [Visual Studio Code](https://code.visualstudio.com/) IDE,因为 NG-ALAIN 针对 VSCode 增加一些额外的特性,可以更好的帮助你开发。 > 或者直接使用 [NG-ALAIN Extension Pack](https://marketplace.visualstudio.com/items?itemName=cipchk.ng-alain-extension-pack) 套件。 ### 代码片断 - [NG-ALAIN Snippets](https://marketplace.visualstudio.com/items?itemName=cipchk.ng-alain-vscode) ### Class样式智能提醒 ng-alain 内置了大量的工具集样式([API](/theme/tools)),安装以下插件可以直接在HTML模板里直接访问到它们。 - [NG-ALAIN Snippets](https://marketplace.visualstudio.com/items?itemName=cipchk.ng-alain-vscode) --- --- order: 15 title: 存储Token type: Documents --- ## 如何使用 `ITokenService` 接口(默认实现 `TokenService`),只有四个方法及 `login_url` 属性: - `set(data: ITokenModel): boolean` 设置认证信息,同时触发 `change` - `get(): ITokenModel` 获取认证信息 - `clear()` 清除认证信息,同时触发 `change` 参数为 `null` - `change(): Observable` 订阅认证信息变更回调 - `login_url` 获取登录地址,等同 `forRoot()` 所配置的值 因此,当登录过程中后端返回相应的认证信息时,只要符合 `ITokenModel` 接口对象,都可以调用 `set` 方法将认证存储至 `IStore` (默认实现 `LocalStorageStore`) 当中。 ```ts constructor(@Inject(DA_SERVICE_TOKEN) service: ITokenService) { service.set({ token: `asdf` }); service.get().token; // output: asdf } ``` ## 存储类型 默认是采用 `withLocalStorage` 持久化存储,你可以在 `app.config.ts` 变更其他存储方式。 ```ts providers: [ provideHttpClient(withInterceptors([...(environment.interceptorFns ?? []), authJWTInterceptor, defaultInterceptor])), provideAuth(withLocalStorage()), ] ``` 包含三种存储类型: ### withLocalStorage `localStorage` 存储,关掉浏览器后**不丢失**。 ### withSessionStorage `sessionStorage` 存储,关掉浏览器后**丢失**。 ### withMemoryStorage 内存存储,关掉浏览器标签后**丢失**。 ### withCookie Cookie 存储。 --- --- order: 5 title: 定制主题 type: Documents --- Ant Design 设计规范上支持一定程度的样式定制,以满足业务和品牌上多样化的视觉需求,包括但不限于主色、圆角、边框和部分组件的视觉定制。 ![](https://zos.alipayobjects.com/rmsportal/zTFoszBtDODhXfLAazfSpYbSLSEeytoG.png) ## 定制方式 Ant Design 的样式使用了 [Less](http://lesscss.org/) 作为开发语言,并定义了一系列全局/组件的样式变量,你可以根据需求进行相应调整,默认样式变量:[NG-ZORRO](https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/components/style/themes/default.less)、[NG-ALAIN]() 两部分。 ### 初始化项目时定制主题 在初始化项目时 `ng add ng-alain` 时选择自定义主题即可自动配置好自定义主题的相关文件,修改 `src/styles/theme.less` 文件内容就可以自定义主题。 ## 官方主题 我们提供了一些官方主题,欢迎在项目中试用,并且给我们提供反馈。 - 🌑 暗黑主题(9+ 支持) - 📦 紧凑主题(9+ 支持) ### 方式一 在样式文件 `src/styles/theme.less` 更改 `default` 为 `dark` 或 `compact` 覆盖主题变量。 ```less // - `default` 默认主题 // - `dark` 🌑 暗黑主题(9+ 支持) // - `compact` 📦 紧凑主题(9+ 支持) @import '@delon/theme/theme-default.less'; // ==========The following is the custom theme variable area========== // @primary-color: #f50; ``` ### 方式二 如果项目不使用 Less,可在 CSS 文件或者 `angular.json` 的 `styles` 字段中,全量引入 `dark.css` 或者 `compact.css`。 样式文件中: ```css @import "@delon/theme/dark.css"; ``` `angular.json` 中 ```json { "build": { "options": { "styles": [ "node_modules/@delon/theme/dark.css" ] } } } ``` ## 主题切换 当使用 @angular/cli 的方式配置主题时必须为每个主题单独打包应用,当你想切换主题而不重新加载应用时(就像这个网站),你可以使用下面的方法将主题编译到单独的样式文件,并在运行时切换: 注意:确保与主题变量相关的样式存在全局样式中,而不是组件样式中,因为组件样式优先级更高将会导致样式无法被覆盖。 1. 安装依赖 ```bash npm i --save-dev ng-alain-plugin-theme ``` > [ng-alain-plugin-theme](https://github.com/ng-alain/plugin-theme) 是专门针对 NG-ALAIN 生成 `color.less` 及主题CSS文件。 在 `ng-alain.json` 内新增 `theme` 节点: ```json { "$schema": "./node_modules/ng-alain/schema.json", "theme": { "list": [ { "theme": "dark" }, { "key": "dust", "modifyVars": { "@primary-color": "#F5222D" } } ] } } ``` 最后运行以下命令: ```bash npx ng-alain-plugin-theme -t=themeCss ``` 会在 `src/assets/style.dark.css` 和 `src/assets/style.dust.css` 生成两个样式文件。 2. 运行时切换样式 动态创建 `link` 标签,将样式文件动态加载在应用中,反之移除。 > 也可以直接使用 [theme-btn](https://github.com/ng-alain/delon/tree/master/packages/theme/theme-btn/) 组件。 ```ts private readonly doc = inject(DOCUMENT); changeTheme(theme: 'default' | 'dark'): void { const el = this.doc.querySelector('#dark-theme'); if (theme === 'dark') { if (el) return; const style = this.doc.createElement('link'); style.type = 'text/css'; style.rel = 'stylesheet'; style.id = 'dark-theme'; style.href = 'assets/style.dark.css'; this.doc.head.appendChild(style); } else { el?.remove(); } } ``` > 注意:如果你使用 `@delon/chart` 或第三方组件,可能需要手动修改组件来支持相应的主题。 ## 组件开发问题 以上主题切换方式是在一个将所有 Less 样式内容独立于 `src/styles.less` 下面,当正常情况下,还会在组件内定义,就像: ```ts @Component({ selector: 'app-dashboard-analysis', templateUrl: './analysis.component.html', styleUrls: ['./analysis.component.less'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class DashboardAnalysisComponent {} ``` ```less // analysis.component.less @import '@delon/theme/index'; :host ::ng-deep { color: @text-color; } ``` 由于组件内定义的样式独立运行在 Angular 下面,是无法根据 `@import '@delon/theme/theme-compact.less';` 的引入来整体切换成暗黑系,如果你希望在组件内也同样使用暗黑系,则必须将: ```diff // analysis.component.less - @import '@delon/theme/index'; + @import '@delon/theme/theme-dark'; ``` 或者,重新针对某一个主题重新定义: ```less // analysis.component.less [data-theme='dark'] { :host ::ng-deep { // 针对暗黑系重新定义 } } ``` 或紧凑主题: ```less [data-theme='compact'] { :host ::ng-deep { // 针对紧凑重新定义 } } ``` ## 相关文章 - [组件样式](/theme/component-styles) --- --- order: 20 title: en-US: FAQ zh-CN: 常见问题 type: Other --- 提问之前,请先查阅下面的常见问题。 ## 基础 ### Expression Changed After It Has Been Checked Error 错误 Angular 下常见错误,[这篇文章](https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4) 会帮助你理解原因。 ### Can't bind to 'formGroup' since it isn't a known property of 'form' Angular 下常见错误,使用 Reactive Forms 需要额外引入 `ReactiveFormsModule`,可以参考[官方文档](https://angular.io/guide/reactive-forms)。 ### 数据修改后页面为什么没有更新 NG-ZORRO 及 @delon/* 组件默认在 OnPush 模式下工作,mutate 对象或者数组不会触发 Angular 的变更检测,请使用 immutable 方式。 ### 如何使用@delon每日构建版本 NG-ALAIN 提供一个 [delon-builds](https://github.com/ng-alain/delon-builds.git) 仓储作为每日构建版本,它并不是最终稳定版本,但包含最新已修复BUG、最新功能,要使用可以在根目录创建 `delon.sh`: ```bash #!/usr/bin/env bash set -e echo "Download latest @delon version" rm -rf delon-builds git clone --depth 1 https://github.com/ng-alain/delon-builds.git rm -rf node_modules/@delon rm -rf node_modules/ng-alain rsync -am delon-builds/ node_modules/ NG_ALAIN_VERSION=$(node -p "require('./node_modules/ng-alain/package.json').version") rm -rf delon-builds echo "Using ng-alain version: ${NG_ALAIN_VERSION}" ``` 当需要使用@delon的每日构建版本,只需要在运行: ```bash bash delon.sh ``` > 如果是 Windows 环境,请使用 [WSL](https://docs.microsoft.com/en-us/windows/wsl/install) 来执行 Bash 脚本。 ## 安装 ### 为什么找不到 ng-zorro-antd/src/*.less 样式? 两种情况: - 使用 `cnpm` 安装依赖包,会遇到无法找到样式文件。这是由于 `cnpm` 采用的是软链接路径形式,导致 `ng-zorro-antd` 文件夹名有所变动,因此建议改用 `yarn` 安装依赖包,如果是网络因素,请参考下方的如何正确使用淘宝源。 - `ng-zorro-antd` 版本过旧导致部分组件无法加载到相应样式 ### 如何使用其他镜像源? 安装 [nnrm](https://github.com/YunYouJun/nnrm/blob/main/README.zh-CN.md) 插件。 ```bash # 安装 nnrm npm install -g nnrm # 将Npm切换至淘宝源(不同 npm 源管理器命令有点不一样,更多细节请参考 nnrm 文档) nnrm use taobao ``` ## 配置 ### 如何本地部署 antd 图标? 首先,最新的 iconfont 文件可以到 [此链接](https://ant.design/docs/spec/download-cn)([镜像](http://ant-design.gitee.io/docs/spec/download-cn)) 下载。 最后,在 `src/styles/theme.less` 重新定义新的路径: ```less @icon-url: "/assets/iconfont"; ``` > 使用绝对路径或CDN地址。 ### Missing locale data for the locale "zh-cn" 缺少语言导入,参考[app.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/app.module.ts#L6-L25)。 ### 如何本地部署ng-alain.com文档 线上文档将只保留最近三个主版本号的数据,若是低版本可以通过本地部署来查看: ```bash git clone --depth 1 -b full https://github.com/ng-alain/archive-docs.git ng-alain-doc cd ng-alain-doc npm install npm start ``` ### 修复360浏览器下部分显示异常 部分360浏览器内置的 Chrome 核心过低,导致部分 CSS3 未支持,可以手动增加 `.browserslistrc` 更多细节参考 [#2310](https://github.com/ng-alain/ng-alain/issues/2310#issuecomment-1299460266)。 --- --- order: 10 title: 常见问题 type: Documents --- ## 如何忽略某个请求 在调用请求时,加上 `ALLOW_ANONYMOUS`。 ```ts this.http.post(`login`, { name: 'cipchk', pwd: '123456' }, null, { context: new HttpContext().set(ALLOW_ANONYMOUS, true) }); ``` ## 如何捕获无Token时被拦截信息? ```ts // 利用订阅 Error this.http.get('/user').subscribe( res => console.log('success', res), err => console.error('error', err) ); // 或使用 catchError this.http.get('/user').pipe( catchError(err => { console.error('error', err); return of({}); }) ).subscribe(); ``` --- --- order: 99 title: 常见问题 type: Documents --- ## path 有许多方法都需要传递 `path` 参数,它使用 `/` 分隔符来表示访问表单对象路径,例如一个 JSON Schema 示例: ```ts schema: SFSchema = { properties: { app: { type: 'string', title: 'APP', }, user: { type: 'object', properties: { name: { type: 'string', }, age: { type: 'number', }, } }, list: { type: 'array', items: { type: 'object', properties: { key: { type: 'string', }, } } } } }; ``` 以下 `path` 行为都有效: - `/app` - `/user/name` - `/list/0/key` 0 表示数组索引 使用 `/` 开头时表示从根路径查找,反之从当前路径向下查找。 ## 为什么非实时校验部分自定义校验无法生效? 由于非实时校验(设置 `liveValidate:false`)不会重新对每个元素执行一次校验,虽然能做到,但自定义校验有可能存在异步,无法保证执行的顺序,因此非实时校验实际只对 JSON Schema 本身的校验。 ## 如何动态使用 Schema? 一般分为两种情形: **1、Schema 定义后可能受限于某个数据来自远程** ```ts @ViewChild('sf', { static: false }) sf: SFComponent; schema: SFSchema = { properties: { app: { type: 'string', title: '附属应用', ui: 'select', enum: [] } } }; ngOnInit() { this.http.get('/apps').subscribe(res => { this.schema.properties.app.enum = res; this.sf.refreshSchema(); }); } ``` **2、远程 Schema** ```ts schema: SFSchema = { properties: {} }; ngOnInit() { this.http.get('/schema').subscribe(res => this.sf.refreshSchema(res)); } ``` ## 什么时候使用 `default`? Schema 的 `default` 用于设置初始化,一般情况下当修改表单时是需要提供 `formData` 参数,但对于增加表单来说,应该依靠 `default` 提供一个更友好的表单给用户。 ## 如何刷新特定 Schema? 可以通过 `getProperty` 方法来获取某个 Schema 的属性,其属性包含 Schema 数据以及 Ui 数据,可以修改这些数据,并重新调用小部件的 `reset` 方法重新渲染该 Schema,例如: ```ts const statusProperty = this.sf.getProperty('/status')!; statusProperty.schema.enum = ['1', '2', '3']; statusProperty.widget.reset('2'); // 或手动触发 `detectChanges` // statusProperty.widget.detectChanges(); ``` 如果单纯更新某个元素数据,则: ```ts this.sf.getProperty('/name')?.setValue('new name'); ``` ## 为什么无法校验 `required` 从 Ajv 7.x 新增 [strict](https://ajv.js.org/options.html#strict-mode-options) 模式,并且默认为 `true`,当最基本的 `required` 都无法正确校验时,那大概率就是因为 Schema 包含了不符合 Json Schema 格式的信息,可以通过 `debug` 模式验证这一点: ```ts schema: SFSchema = { properties: { month: { type: 'string', format: 'month' } }, required: ['month'], ui: { debug: true } }; ``` 由于这里的 `format: 'month'` 并不是 Json Schema 标准,因此,你可以在 Console 面板得到错误: ``` Error: unknown format "month" ignored in schema at path "#/properties/month" ``` 解决这一问题,只能通过全局配置设置 `strict` 为 `false`: ```ts // src/app/global-config.module.ts const alainConfig: AlainConfig = { sf: { ajv: { strict: false } } }; ``` ## 如何切换显示或隐藏某元素 ```ts this.sf.getProperty('/mobile')?.setVisible(status).widget.detectChanges(); ``` --- --- order: 100 title: 常见问题 type: Documents --- ## 如何自适应容器宽高? G2 当前版本并不会根据容器宽高自适应,目前只会根据浏览器窗体大小才会重新变更图表尺寸,因此需要手动监听容器大小的变化并调用 `chart.forceFit()` 重新渲染图表尺寸。 [comment]: --- --- order: 1 title: 开始使用 type: Documents --- ## 写在前面 @delon/auth 是对认证过程进一步处理,通常其核心在于 Access token 的获取、使用环节,因此将集中解决以下三个问题: + 如何获取认证信息行为方式,例如:账密、社会化登录Github等 + 如何存取认证信息,监听认证信息变化 + 何时使用认证信息,区分不同的认证方式的使用规则,例如:JWT @delon/auth 并不会关心用户界面是怎么样,只需要当登录成功后将 Token 信息转化为 `ITokenService` 类型,它会存储在 `localStorage` 当中(默认情况下)。当你操作 HTTP 请求时,它会自动在 `header` (或其他地方) 里加入 Token 信息。 因此,@delon/auth 不限于 ng-alain 脚手架,任何 Angular 项目都可以使用它。 > @delon/auth 只是解决认证环节,有关于权限控制可以使用 [@delon/acl](/acl)。 ### 流程 - 获取 Token - 存储 Token - 利用HTTP拦截器,将 Token 发送给后端 ## 名词解释 ### Token @delon/auth 认为请求时需要发送的加密字符串称它为 Token 值,不管是采用 JWT 的 `Authorization` 参数,还是 OAuth2 的 `access_token`,这也是每个 HTTP 请求时所携带的值。 因此,`ITokenModel` 接口用于表述认证信息,且只有一个 `token` 属性。 > 注意:Token 值务必是一个字符串值。 ### 认证风格 目前衍生两种风格:Simple Web Token (使用 `SimpleTokenModel`)、Json Web Token(使用 `JWTTokenModel`)具有解析 `payload` 能力。如果有特殊需求也可以自定义实现 `ITokenModel` 接口。 ## 如何使用 安装 `@delon/auth` 依赖包: ```bash npm i -S @delon/auth ``` 在 `app.config.ts` 中配置 `provideAuth` 环境: ```typescript providers: [ // 表示使用JWT风格并用 `localStorage` 存储 Token provideHttpClient(withInterceptors([...(environment.interceptorFns ?? []), authJWTInterceptor, defaultInterceptor])), provideAuth(withLocalStorage()), ] ``` ## AlainAuthConfig | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[store_key]` | `string` | `_token` | `localStorage` 的存储KEY值 | ✅ | | `[token_invalid_redirect]` | `boolean` | `true` | 无效时跳转至登录页,包括:无效token值、token已过期(限JWT) | ✅ | | `[token_exp_offset]` | `number` | `10` | JWT token过期时间偏移值(单位:秒) | ✅ | | `[token_send_key]` | `string` | Token | 发送token参数名 | ✅ | | `[token_send_template]` | `string` | `${token}` | 发送token模板,以 `${属性名}` 表示占位符,属性名要确保存在否则以空字符代替 | ✅ | | `[token_send_place]` | `header,body,url` | `header` | 发送token参数位置 | ✅ | | `[login_url]` | `string` | `/login` | 登录页路由地址 | ✅ | | `[ignores]` | `RegExp[]` | `[/\/assets\//]` | 忽略 URL 地址清单,除此之外还可以通过 [ALLOW_ANONYMOUS](/auth/qa/zh) 进行控制是否忽略。 | ✅ | | `[refreshTime]` | `number` | `3000` | 刷新时长(单位:ms) | ✅ | | `[refreshOffset]` | `number` | `6000` | 偏移值(单位:ms),建议根据 `refreshTime` 倍数来设置 | ✅ | > 可以通过[全局配置](/docs/global-config)覆盖它们。 ## FAQ ### 解决同域下多个NG-ALAIN项目Token污染问题 可以在每个项目里通过[全局配置](/docs/global-config)修改 `store_key` 用于区分。 --- --- order: 1 title: 开始使用 type: Documents --- ACL 全称叫访问控制列表(Access Control List),是一种非常简单的基于角色权限控制方式。一个完全独立 `@delon/acl` 模块([DEMO](//ng-alain.github.io/ng-alain/#/logics/acl))。 ## 如何运行 内部实际是一个 `ACLService` 它提供一套基于角色权限服务类。为了更好的编码体验 ng-alain 有多处组件或模块也依赖于它,例如:`st`、`MenuService` 等,并且这些会以 `acl` 属性的形式表现。因此,当遇到 `acl` 属性都表示 [can](#ACLCanType) 方法的**参数值**。 ## 如何使用 安装 `@delon/acl` 依赖包: ```bash npm i -S @delon/acl ``` 若使用 Standalone 无需要额外导入 `DelonACLModule` 模块,否则: ```typescript import { DelonACLModule } from '@delon/acl'; @NgModule({ imports: [ DelonACLModule ] }) export class AppModule { } ``` ## API ### 参数 | 参数 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[guard_url]` | `string` | 路由守卫失败后跳转 | `/403` | ✅ | | `[preCan]` | `(roleOrAbility: ACLCanType) => ACLType` | `can` 执行前回调 | - | ✅ | > 可以通过[全局配置](/docs/global-config)覆盖它们。 ### ACLService | 方法 | 说明 | |----|----| | `[change]` | 监听ACL变更通知 | | `[data]` | 获取所有ACL数据 | | `setFull(val: boolean)` | 标识当前用户为全量,即不受限 | | `set(value: ACLType)` | 设置当前用户角色或权限能力(会先清除所有) | | `setRole(roles: string[])` | 设置当前用户角色(会先清除所有) | | `setAbility(abilities: (number | string)[])` | 设置当前用户权限能力(会先清除所有) | | `add(value: ACLType)` | 为当前用户增加角色或权限能力 | | `attachRole(roles: string[])` | 为当前用户附加角色 | | `attachAbility(abilities: (number | string)[])` | 为当前用户附加权限 | | `removeRole(roles: string[])` | 为当前用户移除角色 | | `removeAbility(abilities: (number | string)[])` | 为当前用户移除权限 | | `can(roleOrAbility: ACLCanType)` | 当前用户是否有对应角色 | | `canAbility(ability: ACLCanType)` | 当前用户是否有对应权限点 | ### ACLCanType ```ts type ACLCanType = number | number[] | string | string[] | ACLType ``` ### ACLType | 属性 | 类型 | 说明 | 默认 | |----|----|----|----| | `[role]` | `string[]` | 角色 | - | | `[ability]` | `number[], string[]` | 权限点 | - | | `[mode]` | `allOf, oneOf` | `allOf` 表示必须满足所有角色或权限点数组算有效
    `oneOf` 表示只须满足角色或权限点数组中的一项算有效 | `oneOf` | | `[except]` | `boolean` | 是否取反,即结果为 `true` 时表示未授权 | `false` | --- --- order: 1 title: 开始使用 type: Documents --- ## 写在前面 通常把一些远程数据缓存在内存或 `localStorage` 持久化,目的是为了减少 Http 请求的成本;这样的数据通常是字典、城市数据等。 缓存的获取应该是非常简单的,我们不应该把时间浪费在如何保证加载这件事情上,因此 `@delon/cache` 更多是以**约定**为前提。`key` 作为缓存的唯一键值,它不应该只是单纯的一个标识符,如果遵守某种约定它的存在会更有价值。`@delon/cache` 默认情况下不光把 `key` 当作唯一标识符,同时它还是一个用于获取远程数据的有效HTTP,例如: ```ts cacheService.get('/data/unit'); ``` 在以往我们会认为,在它之前应该需要加一个: ```ts cacheService.set('/data/unit', [ '个', '件' ]); ``` 才能够确保获取到缓存数据。 而对于 `@delon/cache` 而言,你无须 `set` 方法,直接使用 `get` 获取到单位字典,因为我们有一种**约定**,当缓存不存在透过 `key` 作为HTTP请求数据缓存后再返回。 缓存的获取与设置都是通过 [CacheService](/cache/service) 来操作,你只需要将 `CacheService` 导入对应的类当中即可。 ## 如何使用 **安装** ```bash npm i -S @delon/cache ``` ### 参数 | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[mode]` | `promise,none` | `promise` | 缓存模式;`promise` 约定模式,允许 `key` 作为 http 获取数据;`none` 正常模式 | ✅ | | `[reName]` | `string` | - | 重命名返回参数,例如:
    `null` 返回体为内容
    `list` 返回体应 `{ list: [] }`
    `result.list` 返回体应 `{ result: { list: [] } }` | ✅ | | `[expire]` | `number` | - | 设置默认过期时间值(单位:秒) | ✅ | | `[prefix]` | `string` | - | 持久化数据键值前缀 | ✅ | | `[meta_key]` | `string` | `__cache_meta` | 持久化数据元数据存储键名 | ✅ | | `[request]` | `(key: string) => Observable` | - | 自定义请求体 | ✅ | > 可以通过[全局配置](/docs/global-config)覆盖它们。 --- --- order: 1 title: 开始使用 type: Documents --- ## 写在前面 Mock 是指通过生成模拟数据让前端开发人员独立于后端进行开发,有时我们也会运用在测试环境中。 `@delon/mock` 是一个简单 Mock 功能,包括以下几个特征: - 任意 Angular 项目 - 开发无侵入 - 超简单用法 - 支持 [@faker-js/faker](https://github.com/faker-js/faker) ## 如何使用 安装 `@delon/mock` 依赖包: ```bash npm i --save-dev @delon/mock ``` 参考 [global-config.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/global-config.module.ts#L26-L30) 导入[Mock 规则数据](/mock/rule)。 ### MockOptions 配置 > 可以通过[全局配置](/docs/global-config)覆盖它们。 | 成员 | 类型 | 默认值 | 说明 | 全局配置 | |----|----|----|-----|------| | `[data]` | `any` | - | Mock 数据规则 | ✅ | | `[delay]` | `number` | `300` | 请求延迟,单位:毫秒 | ✅ | | `[force]` | `boolean` | `false` | 是否强制所有请求都Mock,`true` 表示当请求的URL不存在时直接返回 404 错误,`false` 表示未命中时发送真实HTTP请求 | ✅ | | `[log]` | `boolean` | `true` | 是否打印 Mock 请求信息,弥补浏览器无Network信息;当请求经过 Mock 会接收【👽Mock】 | ✅ | | `[copy]` | `boolean` | `true` | 是否返回副本数据 | ✅ | ### 为什么只对开发环境有效? Mock 并非是真实数据,大部分场景是针对开发本地或测试环境;所以在生产环境中不应该包括 Mock 模块以及规则数据。 当然,也可以将 `environment.ts` 的 `provideMockConfig` 放到 `environment.prod.ts` 下,使得生产环境也使用这种规则,就像 https://ng-alain.github.io/ng-alain/ 一样,需要一些模拟请求来保证环境的运行。 ```ts import { provideMockConfig } from '@delon/mock'; import * as MOCKDATA from '../../_mock'; export const environment = { providers: [provideMockConfig({ data: MOCKDATA })], } as Environment; ``` --- --- order: 1 title: 开始使用 type: Documents --- @delon/util 是一组日常普通使用的工具函数的集合。 --- --- order: 1 title: 开始使用 type: Documents --- @delon/form 是一个基于 [JSON Schema](http://json-schema.org/) 标准的动态构建表单。 ## 特性 - 符合 JSON Schema 标准 - 基于 ng-zorro-antd 基础组件库 - 秉承 Ant Design 价值观 - 二十几种小部件 - 可自定义小部件满足业务需求 - 无任何第三方依赖,可适用所有 antd 项目 ## 如何阅读 在开始之前需要知道文档的一些简单编写规则: - 代码以 `schema.` 开头的表示 JSON Schema 对象属性 - 代码以 `ui.` 开头的表示 UI 对象属性 - 部分小部件数据源分为 **静态** 和 **实时** 两类 - **静态** 理解为 `schema.enum` 值,是符合 JSON Schema 标准,且限数组格式 `any[]` - **实时** 理解为 `ui.asyncData` 值,非 JSON Schema 标准,格式 `(input?: any) => Observable` ## 如何使用 安装 `@delon/form` 依赖包: ```bash npm i -S @delon/form ``` 导入 `DelonFormModule` 模块: ```typescript import { DelonFormModule } from '@delon/form'; @NgModule({ imports: [ DelonFormModule.forRoot() ] }) export class AppModule { } ``` 虽然默认 `@delon/form` 校验是 [ajv](https://ajv.js.org/),但这并不是唯一的选择,你可以覆盖 `SchemaValidatorFactory` 使用其他校验类库。 **全局配置** 请参考[全局配置](/docs/global-config),成员如下: | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[ajv]` | [ajv](https://github.com/ajv-validator/ajv/blob/master/docs/api.md#options) 参数 | `Ajv.Options` | - | | `[ingoreKeywords]` | 是否忽略某些数据类型校验([所有类型](https://github.com/ng-alain/delon/blob/master/packages/form/src/errors.ts#L4)) | `string[]` | `[ 'type', 'enum' ]` | | `[liveValidate]` | 是否实时校验 | `boolean` | `true` | | `[autocomplete]` | 指定表单 `autocomplete` 值 | `on,off` | `null` | | `[firstVisual]` | 是否立即呈现错误视觉 | `boolean` | `false` | | `[onlyVisual]` | 是否只展示错误视觉不显示错误文本,并取消错误文本间距 | `boolean` | `false` | | `[errors]` | 自定义通用错误信息 | `{ [ key: string ]: string }` | `ERRORSDEFAULT` | | `[ui]` | 默认全局布局 | `SFUISchemaItem` | - | | `[size]` | 元素组件大小,用于 `nzSize` 值 | `default,large,small` | - | | `[button]` | 按钮风格 | `SFButton` | `{submit:'提交',submit_type:'primary',reset:'重置',reset_type:'default'}` | | `[uiDateStringFormat]` | date小部件:`type="string"` 且不指定 `schema.format` 和 `ui.format` 时日期格式 | `string` | `yyyy-MM-dd HH:mm:ss` | | `[uiDateNumberFormat]` | date小部件:`type="number"` 且不指定 `schema.format` 和 `ui.format` 时日期格式,默认:`T` 13位Unix Timestamp | `string` | `T` | | `[uiTimeStringFormat]` | time小部件:`type="string"` 且不指定 `schema.format` 和 `ui.format` 时日期格式 | `string` | `HH:mm:ss` | | `[uiTimeNumberFormat]` | time小部件:`type="number"` 且不指定 `schema.format` 和 `ui.format` 时日期格式,默认:`T` 13位Unix Timestamp,日期统一使用 `1970-01-01` | `string` | `T` | | `[uiEmailSuffixes]` | 指定 `format: 'email'` 的默认Email后缀 | `string[]` | `['qq.com', '163.com', 'gmail.com', '126.com', 'aliyun.com']` | | `[delay]` | 是否延迟渲染,需要手动调用 `refreshSchema()` | `boolean` | `false` | 构建一个邮箱、姓名表单: ```ts @Component({ selector: 'app-home', template: ` ` }) export class HomeComponent { schema: SFSchema = { properties: { email: { type: 'string', title: '邮箱', format: 'email', maxLength: 20 }, name: { type: 'string', title: '姓名', minLength: 3 } } }; submit(value: any) { } } ``` ## API ### sf | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[layout]` | 表单布局,等同 `nzLayout` | `'horizontal','vertical','inline'` | `'horizontal'` | | `[schema]` | **必填项** JSON Schema | `SFSchema` | - | | `[ui]` | UI Schema | `SFUISchema` | - | | `[formData]` | 表单默认值 | `any` | - | | `[mode]` | 表单模式,细节见常见问题 | `'default','search','edit'` | `'default'` | | `[button]` | 按钮 | `SFButton|'none'` | `{}` | | `[firstVisual]` | 是否立即呈现错误视觉 | `boolean` | `true` | | `[liveValidate]` | 是否实时校验,`false` 提交时检验 | `boolean` | `true` | | `[autocomplete]` | 指定表单 `autocomplete` 值 | `'on','off'` | `null` | | `[disabled]` | 是否禁用状态 | `boolean` | `false` | | `[loading]` | 是否加载状态,当 `true` 重置按钮禁止状态,提交按钮加载状态 | `boolean` | `false` | | `[noColon]` | 是否不显示 `label` 后面的冒号 | `boolean` | `false` | | `[compact]` | 是否紧凑 | `boolean` | `false` | | `[expandable]` | 是否启用展开/收缩功能 | `boolean` | `false` | | `[expanded]` | 展开/收缩状态,支持双向绑定 | `boolean` | `false` | | `[cleanValue]` | 是否清理未定义 Schema 的数据 | `boolean` | `false` | | `[delay]` | 是否延迟渲染,需要手动调用 `refreshSchema()` | `boolean` | `false` | | `(formChange)` | 数据变更时回调 | `EventEmitter<{}>` | - | | `(formValueChange)` | 值数据变更时回调 | `EventEmitter` | - | | `(formSubmit)` | 提交表单时回调 | `EventEmitter<{}>` | - | | `(formReset)` | 重置表单时回调 | `EventEmitter<{}>` | - | | `(formError)` | 表单校验结果回调 | `EventEmitter` | - | ### SFButton | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[submit]` | 提交按钮文本 | `string` | `提交` | | `[submit_type]` | 提交按钮类型 | `string` | `primary` | | `[submit_icon]` | 提交按钮图标 | `SFButtonIcon` | - | | `[reset]` | 重置按钮文本 | `string` | `重置` | | `[reset_type]` | 重置按钮类型 | `string` | `default` | | `[reset_icon]` | 重置按钮图标 | `SFButtonIcon` | - | | `[search]` | 搜索按钮文本 | `string` | `搜索` | | `[edit]` | 编辑按钮文本 | `string` | `保存` | | `[render]` | 按钮样式 | `SFRenderButton` | - | ### SFValueChange | 参数 | 说明 | 类型 | 默认值 | |----------|-------------|------|---------| | `[value]` | 总是返回完整的数据 | `SFValue` | - | | `[path]` | 当前触发路径 | `string, null` | `null` | | `[pathValue]` | 当前触发路径对应值 | `SFValue` | - | ### 组件方法 | 参数 | 说明 | 返回值 | |----|----|-----| | `valid` | 表单是否有效 | `boolean` | | `value` | 表单值 | `any` | | `refreshSchema` | 刷新 JSON Schema | `void` | | `reset` | 重置表单 | `void` | | `validator` | 手动校验一次表单 | `void` | | `getProperty` | 根据路径获取表单元素属性 | `FormProperty` | | `getValue` | 根据路径获取表单元素当前值 | `any` | | `setValue` | 根据路径设置某个表单元素属性值,若路径不存在会产生异常 | `this` | | `setDisabled` | 根据路径设置某个表单元素 `disabled` 值,若路径不存在会产生异常 | `this` | | `setRequired` | 根据路径设置某个表单元素 `required` 值,若路径不存在会产生异常 | `this` | | `updateFeedback` | 根据路径设置某个表单元素反馈状态 | `this` | > **注:** 所有 path 采用 `/` 来分隔,例如:`/user/name`、`/arr/0/name`。 ### 按钮说明 **注意事项** - 值为 `null` 或 `undefined` 表示手动添加按钮,但保留容器 - 值为 `none` 表示手动添加按钮,且不保留容器 - 使用 `spanLabelFixed` 固定标签宽度时,若无 `render.class` 则默认为居中状态 **自定义** 当你希望自定义按钮时,务必设置 `button` 值为 `null`。 ```html ``` ## 常见问题 ### mode有什么作用? `mode` 只是快捷作用,**且优先级高于一切**,规则如下: - `default` 默认模式,什么也不做 - `search` 搜索模式,自动设置 `layout: inline`、`firstVisual: false`、`liveValidate: false`、`button.submit: '搜索'` - `edit` 编辑模式,自动设置 `layout: horizontal`、`firstVisual: false`、`liveValidate: true`、`button.submit: '保存'` ### Schema国际化 `sf` 并不支持任何 Schema 国际化动作,这是因为本身 Schema 是一组 JSON 值,国际化的实现只需要提供不同语言版本即可。 --- --- order: 1 title: 开始使用 type: Documents --- 图表是基于 [G2](https://antv.alipay.com/zh-cn/g2/3.x/index.html) (3.0) 的基础上二次封装,提供了业务中常用的图表套件,可以单独使用,也可以组合起来实现复杂的展示效果。 > 图表的作用,是帮助我们更好地看懂数据。选择什么图表,需要回答的首要问题是『我有什么数据,需要用图表做什么』,而不是 『图表长成什么样』 。 | 分类名 | 组件清单 | 描述 | ----- | ------- | --- | 比较类 | 迷你柱状图:`g2-mini-bar`
    单一柱状图:`g2-single-bar`
    柱状图:`g2-bar`
    雷达图:`g2-radar`
    迷你区域图:`g2-mini-area`
    迷你进度条:`g2-mini-progress` | - | 分布类 | 折线图:`g2-timeline` | - | 占比类 | 饼图:`g2-pie`
    水波图:`g2-water-wave`
    迷你进度条:`g2-mini-progress`
    迷你区域图:`g2-mini-area` | - | 区间类 | 仪表盘:`g2-gauge`
    迷你区域图:`g2-mini-area` | - | 趋势类 | 折线图:`g2-timeline`
    迷你区域图:`g2-mini-area` | - | 时间类 | 折线图:`g2-timeline`
    迷你区域图:`g2-mini-area` | - | 其它 | 标签云:`g2-tag-cloud`
    图表卡片:`g2-chart-card`
    自定义图表:`g2-chart` | - ## 如何使用 ### G2类库加载 默认情况下,在[全局配置](/docs/global-config)已经指定类库 CDN 地址: ```ts // global-config.module.ts const alainConfig: AlainConfig = { chart: { // 以下是默认配置,如果项目无法外网访问,可以根据 `angular.json` 配置将依赖包直接使用 `./assets***` 路径 libs: [ 'https://gw.alipayobjects.com/os/lib/antv/g2/4.1.4/dist/g2.min.js', 'https://gw.alipayobjects.com/os/lib/antv/data-set/0.11.7/dist/data-set.js', ], }, }; export class DelonModule { static forRoot(): ModuleWithProviders { return { ngModule: DelonModule, providers: [ provideAlainConfig(alainConfig) ] }; } } ``` 当然也可以在 `index.html` 直接引入 CDN 地址,例如: ```html ``` 也可以在 `angular.json` 配置 `assets` 选项(有关 [assets](https://angular.cn/guide/workspace-config#assets-configuration) 文档),从 `node_modules` 来获得G2类库,例如: ```json "assets": [ { "glob": "**/*", "input": "./node_modules/@antv/g2/dist", "output": "/@antv/g2/" }, { "glob": "**/*", "input": "./node_modules/@antv/data-set/dist", "output": "/@antv/data-set/" } ] ``` 最后修改全局配置的 `libs` 参数为: ```ts // global-config.module.ts const alainConfig: AlainConfig = { chart: { libs: [ './assets/@antv/g2/g2.min.js', './assets/@antv/data-set/data-set.js', ], }, }; ``` ### 导入模块 ```ts // shared.module.ts import { G2BarModule } from '@delon/chart/bar'; @NgModule({ imports: [ G2BarModule ], exports: [ G2BarModule ] }) ``` ## 自定义 G2 组件 使用 [g2-chart](/chart/custom) 组件快速自定义一个图表,可以减少不必要的组件渲染过程中所产生的奇怪问题。 ## 配置图表主题 对所有 G2 进行配置图表主题,但只提供接口,有关配置图表主题参数,请参考[G2官网](https://g2.antv.vision/zh/docs/manual/tutorial/theme)。 ```ts // global-config.module.ts const alainConfig: AlainConfig = { chart: { theme: 'dark' }, }; export class DelonModule { static forRoot(): ModuleWithProviders { return { ngModule: DelonModule, providers: [ provideAlainConfig(alainConfig) ] }; } } ``` --- --- order: 1 title: 开始使用 type: Documents --- ## 写在前面 使用 ng-alain 脚手架应尽可能的使用 Angular CLI 所提供的 `ng` 命令组,来构建、升级等,ng-alain 也实现了一些比较酷的事: - 使用 [ng add](/cli/add) 构建空脚手架 - 使用 [ng g](/cli/generate) 来构建模块、业务页 - 可插拔 [插件](/cli/plugin) ## 如何使用 我们不建议直接克隆 Github 源代码,而应该使用 `ng add` 来构建 ng-alain 项目,而构建一个空 ng-alain 只需要简单几个动作: 1、创建一个空 angular 项目 ```bash # 确保使用的是最新版本 Angular cli ng new demo --style less ``` 2、添加 ng-alain 脚手架 ```bash ng add ng-alain # 如果你想创建一个英文版本,则: ng add ng-alain --defaultLanguage=en ``` > 遇到问题请阅读 [常见问题](/docs/faq) 3、运行项目 ```bash ng serve ``` ## 如何升级 建议 Star 或 Watch [源代码](https://github.com/ng-alain/ng-alain)仓库,有助于你更好的在第一时间了解变更细节。 除DEMO示例页以外,会有一些影响外,对于大版本会提供 `ng update` 来解决破坏性变更,对于其他情况的升级请参考 [升级脚手架](/docs/upgrade)。 --- --- order: 1 title: 开始使用 type: Documents --- `@delon/theme` 是 ng-alain 脚手架唯一必须引入的模块。它包含了非常多[主题样式参数](/theme/global),通过覆盖参数数值进而定制一些特别的需求;以及若干通用性[服务](/theme/menu)、[管道](/theme/date)。 ## 样式 ng-alain 默认使用 less 作为样式语言,建议在使用前或者遇到疑问时学习一下 [less](http://lesscss.org/) 的相关特性,如果想获取基础的 CSS 知识或查阅属性,可以参考 [MDN文档](https://developer.mozilla.org/zh-CN/docs/Web/CSS/Reference)。 ## 布局 脚手架提供两种布局:[默认布局](/theme/layout-default)、[空白布局](/theme/layout-blank),脚手架并不包含两种布局样式文件,它位于 `@delon/theme` 类库当中。 ## 脚手架样式 在开发过程中,绝大部分情况下可以利用 ng-alain 提供的工具集来调整间距、颜色、大小、边框等,它是一个套类似 bootstrap 风格的工具集。 或通过 [theme.less](https://github.com/ng-alain/ng-alain/blob/master/src/styles/theme.less) 下定制你的样式,这些样式将会在全局应用中有效,且有两个问题比较突出: - 全局污染 —— CSS 文件中的选择器是全局生效的,不同文件中的同名选择器,根据 build 后生成文件中的先后顺序,后面的样式会将前面的覆盖; - 选择器复杂 —— 为了避免上面的问题,我们在编写样式的时候不得不小心翼翼,类名里会带上限制范围的标识,变得越来越长,多人开发时还很容易导致命名风格混乱,一个元素上使用的选择器个数也可能越来越多。 因此,除非设计师明确需求以外,我们应该尽可能使用组件 `styles` 属性创建组件样式,有关如何Angular样式请参考《[关于Angular样式封装](https://zhuanlan.zhihu.com/p/31235358)》。 ## 样式文件类别 在一个项目中,样式文件根据功能不同,可以划分为不同的类别。 ### theme.less 全局样式文件,在这里你可以进行一些通用设置。 ### 工具集 请参考 [工具集样式](/theme/tools)。 ### 页面级 具体页面相关的样式,例如 [monitor.component.less](https://github.com/ng-alain/ng-alain/blob/master/src/app/routes/dashboard/monitor/monitor.component.less),里面的内容仅和本页面的内容相关。一般情况下,如果不是页面内容特别复杂,有了前面全局样式、工具集样式的配合,这里要写的应该不多。 ## 如何覆盖参数 ng-alain 的样式使用了 [Less](http://lesscss.org/) 作为开发语言,并定义了一系列全局/组件的样式变量,你可以根据需求进行相应调整。 要改变的参数统一放在 [theme.less](https://github.com/ng-alain/ng-alain/blob/master/src/styles/theme.less) LESS文件中,所有参数包括: - [全局参数](/theme/global) - [默认布局](/theme/layout-default) - [空白布局](/theme/layout-blank) 如果以上变量不能满足你的定制需求,可以给我们提 issue。 --- --- order: 30 title: 插件 type: Documents --- ## 写在前面 插件主要是针对项目一些额外 **可选** 行为的添加或移除,例如当你觉得代码风格对你来说意义不大(虽然我不这么看),只需要: ```bash ng g ng-alain:plugin codeStyle -t=remove ``` 又或者希望项目应该有一个统一风格时: ```bash ng g ng-alain:plugin codeStyle ``` ## 命令格式 ```bash ng g ng-alain:plugin [plugin name] -t=[add | remove] ``` 其中 `[plugin name]` 插件名称,从插件列表中获取,`-t` 支持两种值 `add`(默认) 和 `remove`。 ## 插件列表 ### codeStyle 代码风格,有几个规则: - 使用 [angular-eslint](https://github.com/angular-eslint/angular-eslint) 校验 typescript 部分 - 使用 [stylelint](https://github.com/stylelint/stylelint) 校验 less 部分 - 使用 [prettier](https://github.com/prettier/prettier) 代码格式化 - 使用 [husky](https://github.com/typicode/husky) 在你提交代码时进行代码校验和格式化 ng-alain 默认提供了一部分的代码风格配置方案,你可以在项目的根目录下找到这些配置信息,依照你自己的风格进行修饰。 ```bash # add ng g ng-alain:plugin codeStyle # remove ng g ng-alain:plugin codeStyle -t=remove ``` ### docker 支持 Docker 部署。 ```bash # add ng g ng-alain:plugin docker # remove ng g ng-alain:plugin docker -t=remove ``` ### defaultLanguage 变更当前默认语言,参考:[国际化-默认语言](/docs/i18n#默认语言) ```bash # change to [en] ng g ng-alain:plugin defaultLanguage --defaultLanguage=en # change to [zh] ng g ng-alain:plugin defaultLanguage --defaultLanguage=zh # change to [zh-tw] ng g ng-alain:plugin defaultLanguage --defaultLanguage=zh-tw ``` #### 支持语言列表 | 名称 | 语言包名称 | 对应 [Angular](https://github.com/angular/angular/tree/master/packages/common/locales) 语言包 | 对应 [Zorro](http://ng.ant.design/docs/i18n/zh#%E6%94%AF%E6%8C%81%E8%AF%AD%E8%A8%80) 语言包 | 对应 [Delon](/theme/locale) 语言包 | |----|-------|------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------|------------------------------| | 简体中文 | zh-Hans,zh-cn,zh-Hans-CN,zh | zh-Hans,zh-cn,zh-Hans-CN,zh | zh_CN | zh_CN | | 繁体中文 | zh-Hant,zh-tw,zh-Hant-TW | zh-Hant,zh-tw,zh-Hant-TW | zh_TW | zh_TW | | 英语(美式) | en | en | en_US | en_US | | 土耳其语 | tr | tr | tr_TR | tr_TR | | 波兰语 | pl | pl | pl_PL | pl_PL | | 希腊语 | el | el | el_GR | el_GR | | 朝鲜语 | ko | ko | ko_KR | ko_KR | | 克罗地亚 | hr | hr | hr_HR | hr_HR | | 斯洛文尼亚文 | sl | sl | sl_SI | sl_SI | | 法文 | fr | fr | fr_FR | fr_FR | | 西班牙语 | es | es | es_ES | es_ES | | 意大利语 | it | it | it_IT | it_IT | | 阿拉伯语 | ar | ar | ar_EG | ar_SA | ### sts [ng-alain-sts](https://github.com/ng-alain/sts) 插件,构建 Swagger API 转换为列表、编辑页,更多有趣的玩法请自行想象。 ```bash # add ng g ng-alain:plugin sts # remove ng g ng-alain:plugin sts -t=remove ``` ### icon **尽可能**从项目中分析并生成静态 Icon,插件会自动在 `src` 目录下生成两个文件: - `src/style-icons.ts` 自定义部分无法解析(例如:远程菜单图标) - `src/style-icons-auto.ts` 命令自动生成文件 > 自动排除 [ng-zorro-antd](https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/components/icon/nz-icon.service.ts#L6) 和 [@delon](https://github.com/ng-alain/delon/blob/master/packages/theme/src/theme.module.ts#L33) 已经加载的图标。 ```bash ng g ng-alain:plugin icon ``` 同时,需要手动在 `startup.service.ts` 中导入: ```ts import { ICONS_AUTO } from '../../../style-icons-auto'; import { ICONS } from '../../../style-icons'; @Injectable() export class StartupService { constructor(iconSrv: NzIconService) { iconSrv.addIcon(...ICONS_AUTO, ...ICONS); } } ``` **有效语法** ```html ``` ### rtl 支持 RTL 插件,即文本方向设置为“从右向左”。 ```bash ng g ng-alain:plugin rtl ``` > 该插件不支持移除功能,需要自行手动移除相关代码。 --- --- order: 20 title: en-US: New Component zh-CN: 新增业务组件 type: Dev --- 对于一些可能被多处引用的功能模块,建议提炼成业务组件统一管理。这些组件一般有以下特征: - 只负责一块相对独立,稳定的功能; - 没有单独的路由配置; - 可能是纯静态的,仅受父组件(通常是一个页面)传递的参数控制。 下面以一个简单的静态组件为例进行介绍。假设你的应用中经常需要展现图片,这些图片宽度固定,有一个灰色的背景和一定的内边距,有文字介绍,就像下图这样: ![](https://gw.alipayobjects.com/zos/rmsportal/vcRltFiKfHBHFrUcsTtW.png | width=400) 你可以用一个组件来实现这一功能,它有默认的样式,同时可以接收父组件传递的参数进行展示。 ## 新建文件 在 `src/app/shared/components` 下新建一个以组件名命名的文件夹,命名尽量体现组件的功能,这里就叫 `image-wrapper`。在此文件夹下新增 ts 文件、样式文件(如果需要)及组件API说明,命名为 `index.ts`、`index.less`和`README.md`。 > 在使用组件时,默认会在 `index.ts` 中寻找 export 的对象,如果你的组件比较复杂,可以分为多个文件,最后在 `index.ts` 中统一 export,就像这样: > ```ts > // main.component.ts > export class MainComponent {} > > // sub.component.ts > export class SubComponent {} > > // index.ts > export MainComponent from './main.component'; > export SubComponent from './sub.component'; > ``` 你的代码大概是这个样子: ```ts // index.ts import { Component, Input } from '@angular/core'; @Component({ selector: 'image-wrapper', template: `
    @if (desc) {
    {{ desc }}
    }
    `, styleUrls: [ './index.less' ] }) export class ImageWrapperComponent { @Input() style: { [key: string]: string }; @Input() src: string; @Input() desc: string; } ``` ```less // index.less :host { width: 400px; margin: 0 auto; padding: 0 20px 8px; text-align: center; background: #f2f4f5; ::ng-deep { .img { max-width: calc(100% - 32px); margin: 2.4em 1em; vertical-align: middle; box-shadow: 0 8px 20px rgba(143, 168, 191, 0.35); } } } ``` 到这儿组件就建好了,了解更多关于[组件样式](/theme/component-styles)的开发。 ## 注册 组件创建好后,需要将组件导入 `SharedModule` 中,这样所有子模块都可以使用到该组件。 ```ts // shared.module.ts import { ImageWrapperComponent } from './image-wrapper'; const COMPONENTS = [ ImageWrapperComponent ]; ``` ## 使用 在要使用这个组件的地方,按照组件定义的 API 传入参数,直接使用就好: ```html ``` --- --- order: 10 title: en-US: New Page zh-CN: 新增页面 type: Dev --- Angular 虽然是以组件树来渲染一个页面,然后实际开发是以一种模块树来组织代码,使其能更好地**代码复用**。而对于**模块粒度**取决于需求,ng-alain的定位在于中台前端,因此,比较建议从业务角度出发组织你的代码结构。 NG-ALAIN 提供一套非常丰富的 Schematics 模板,可以快速创建符合 NG-ALAIN 特点的模板和页面;同时包含多种可插拔[插件](/cli/plugin)。 > 另:NG-ALAIN 是一个标准的 Angular CLI 项目,你依然可以使用默认的所有命令行。 ## 一、模块 要创建一个页面需要先创建一个模块开始,假如需要一个系统设置相关的模块,执行命令: ```bash ng g ng-alain:module sys ``` CLI 会自动在 `src/app/routes/sys` 下创建 `sys.module.ts` 和 `sys-routing.module.ts` 文件,前者是系统设置模块组件定义文件;后者是系统设置模块路由配置文件。 ```ts // sys.module.ts import { NgModule, Type } from '@angular/core'; import { SharedModule } from '@shared'; import { SysRoutingModule } from './sys-routing.module'; const COMPONENTS: Type[] = []; @NgModule({ imports: [SharedModule, SysRoutingModule], declarations: COMPONENTS, }) export class SysModule {} ``` 模块的作用是导入我们需要的模块,所有 NG-ZORRO、@delon/abc、@delon/chart 等都是按需求加载模块,当前业务页需要哪里外部组件就导入哪些,为了减少这些导入动作,NG-ALAIN 提炼了两个文件 `shared-delon.module.ts`、`shared-zorro.module.ts` 将一些整个项目经常用到的模块合并成一个叫 `SharedModule` 模块内,这也就是为什么需要在业务模块内第一时间导入它。注意:不建议把所有组件都放进 `SharedModule` 内,尽可能将需要用到的模块以二或三次以上使用才放进这里。 以及路由配置模块: ```ts // sys-routing.module.ts import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; const routes: Routes = []; @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule], }) export class SysRoutingModule {} ``` 至此,你可以放心在 `sys` 目录中开始开发像菜单管理、日志、系统配置等业务页面。 ## 二、页面 利用 `ng generate`(可简写为:`ng g`) 命令在 `sys` 目录下创建一个日志列表页: ```bash ng g ng-alain:list log -m=sys ``` > 了解更多请参考[命令行工具](/cli)。 最终,你可以访问 [日志](//localhost:4200/#/sys/log) 页面。 当然日志可能是一个非常丰富的信息,可以增加一个以模态框打开的查看页来显示更多详情。 ```bash ng g ng-alain:view view -m=sys -t=log ``` `-t=log` 表示希望把创建的文件放进至 `sys/log/view` 下面。 --- --- order: 110 title: 服务端渲染(SSR) type: Dev --- 本文描述的是 NG-ALAIN 如何支持服务端渲染(SSR)。 > NG-ALAIN **不推荐**在中后台使用服务端渲染(SSR),这是因为中后台本身对于SSR所带来的好处远大于开发带来的麻烦,但不管怎么样在许多人的要求下,从 `9.5` 版本开始,已经对所有 `@delon/*` 类库支持服务端渲染。 ## 开始之前 在开始之前请先阅读以下文章,它们能够更加快速让你了解 Angular 服务端渲染是如何工作的: - [Angular Universal:Angular 统一平台简介](https://angular.cn/guide/universal) - [Partial Server Side Rendering with Angular 9 and How to Deploy it](https://www.cnc.io/en/blog/angular-server-side-rendering) ## 教程 ### 添加 `@nguniversal/express-engine` 在一个完整的 NG-ALAIN 项目下,执行以下命令: ```bash ng add @nguniversal/express-engine ``` 最后运行: ```bash npm run dev:ssr ``` 此时会以 SSR 的形式运行 NG-ALAIN。 但如果采用默认 `LocalStorageStore` 来存储 Token 的情况下,会提示找不到 `localStorage` 的错误,这是因为服务端并没有这些,它们可能还包含 `window`、`document`、`sessionStorage` 等。 因此,要想在中后台很好的支持 SSR,需要分析所依赖的第三方类库是否支持 SSR,如果没有必须手动处理在服务端下不渲染这些组件。 ### 丢失Token 服务端是无状态的,因此判断请求是否有效授权,目前通用的做法是将 Token 存储在 Cookie 下,在服务端接收请求时再根据 Cookies 来获取 Token 信息。 虽然 NG-ALAIN 提供 `CookieStorageStore` 但它并不支持服务端 SSR 访问 Cookie,因此需要手动构建针对 SSR 的 Token 持久化存储。 推荐使用 [@ngx-utils/cookies](https://github.com/ngx-utils/cookies) 来处理 Cookies,它同时支持客户端与服务端。 > **注意:** 受限于 [#20](https://github.com/ngx-utils/cookies/issues/20) 的原因,由于一直未处理,有人专门解决了这个问题并发布一个新类库 [ngx-utils-cookies-port](https://www.npmjs.com/package/ngx-utils-cookies-port),暂时只能使用它来代替 `@ngx-utils/cookies`,用法一模一样只是模块名换一下,在修复之后再换回来。 要创建一个符合 `@delon/auth` 接口持久化存储类,只需要继承 `IStore` 即可,例如: ```ts import { Injectable } from '@angular/core'; import { IStore, ITokenModel } from '@delon/auth'; import { CookiesService } from 'ngx-utils-cookies-port'; @Injectable() export class AuthStorageStore implements IStore { constructor(private cookies: CookiesService) {} get(key: string): ITokenModel { return JSON.parse(this.cookies.get(key) || '{}') || {}; } set(key: string, value: ITokenModel | null): boolean { this.cookies.put(key, JSON.stringify(value)); return true; } remove(key: string) { this.cookies.remove(key); } } ``` 最后,在 `global-config.module.ts` 内重新注册它: ```diff const alainProvides = [ provideAlainConfig(alainConfig) + { provide: DA_STORE_TOKEN, useClass: AuthStorageStore }, ]; ``` **注意:这里依然需要注册新增的模块,方法请参考 [@ngx-utils/cookies](https://github.com/ngx-utils/cookies#getting-started) 说明。** `@ngx-utils/cookies` 内部会根据 `REQUEST` 来获取当前的请求头信息,因此,我们还需要修改 `server.ts`: ```diff // All regular routes use the Universal engine server.get('*', (req, res) => { res.render(indexHtml, { req, + res, providers: [ { provide: APP_BASE_HREF, useValue: req.baseUrl }, + { provide: 'REQUEST', useValue: req }, + { provide: 'RESPONSE', useValue: res }, ], }); }); ``` --- --- order: 70 title: en-US: Build & Deploy zh-CN: 构建和发布 type: Dev --- ## 构建 当项目开发完毕,只需要运行一行命令就可以打包你的应用: ```bash npm run build ``` NG-ALAIN 本身是一个 Angular CLI 项目,因此也可以参照 [Build](https://angular.io/cli/build) 完成更复杂的构建需求。构建打包成功之后,会在根目录生成 `dist` 文件夹,里面就是构建打包好的文件,包含若干 `*.js`、`*.css`、`index.html` 等静态文件。 ### JavaScript heap out of memory 避免执行 `ng build` 时抛出 **JavaScript heap out of memory**: ```json { "scripts": { "build": "node --max_old_space_size=5120 ./node_modules/@angular/cli/bin/ng build" } } ``` ### 环境变量 当你需要区别开发和部署以及测试环境的时候,可以通过 `src/environments` 文件夹根据不同环境配置相应的参数,配置项同时也可以在应用当中直接调用它们。同时,还需要配置 `angular.json` 内的配置项,最后你可以透过命令改变环境配置。 ### 分析构建文件体积 如果构建文件很大,可以通过 `analyze` 命令构建并分析依赖模块的体积分布,从而优化你的代码。 ```bash npm run analyze ``` 查看分析页: ```bash npm run analyze:view ``` ![](./assets/screenshot/bundle-size.png) ## 发布 对于发布来讲,只需要将最终生成的静态文件,也就是通常情况下 `dist/browser` 文件夹的静态文件发布到你的 cdn 或者静态服务器即可,需要注意的是其中的 `index.html` 通常会是你后台服务的入口页面,在确定了 js 和 css 的静态之后可能需要改变页面的引入路径。 如果你的静态资源是部署到其他域名(例如独立的 cdn 地址),你可以透过 `--base-href` 参数指定一个 cdn 地址。 ```bash ng build --base-href=https://cdn.ng-alain.com/ ``` ### 路由策略 Angular 前端路由有两种不同策略 `HashLocationStrategy` 和 `PathLocationStrategy`。前者是以 `#` 后面的路径进行路由处理,通过 [HTML5 History](//developer.mozilla.org/en-US/docs/Web/API/History_API) 进行前端路由管理,而后者则是类似页面访问路径并没有 `#`,通过服务端的配置,能够访问指定 URL 都定向到当前页面,从而能够进行前端路由管理。 如果你想采用 `PathLocationStrategy` 策略,则需要将 `./src/app/routes/routes.module.ts` 的 `RouterModule.forRoot(routes, { useHash: true })` 换成 `RouterModule.forRoot(routes)`。同时需要服务端做一个映射,比如: express 的例子: ```js app.use(express.static(path.join(__dirname, 'build'))); app.get('/*', function (req, res) { res.sendFile(path.join(__dirname, 'build', 'index.html')); }); ``` egg 的例子: ```js // controller exports.index = function* () { yield this.render('App.jsx', { context: { user: this.session.user, }, }); }; // router app.get('home', '/*', 'home.index'); ``` 有关更多 Angular 路由相当问题,请阅读[官网](//angular.io/guide/router)。 ### Docker NG-ALAIN 提供了一个基于 `nginx` WEB服务完整的构建Angular项目的镜像文件。其中 `nginx` 是采用 [nginx:1.13.5-alpine](https://github.com/nginxinc/docker-nginx/blob/master/mainline/alpine/Dockerfile) 的镜像,基本上可以满足 NG-ALAIN 项目的良好运行环境,倘若有更多需求,你可以利用 `docker run` 轻易的指定 *nginx.conf*。 #### 1、构建镜像 根据 Dockerfile 构建一个完整的 NG-ALAIN 所需要的运行环境的镜像。 ```bash docker build -t ng-alain . ``` #### 2、运行 **基于compose(推荐)** ```bash docker-compose up -d ``` 其细节可以通过 `docker-compose.yml` 修改。 **基于命令式** ```bash docker run -d -p 80:80 --name alain ng-alain ``` 最后你可以访问:http://localhost/ #### 3、关于SSL NG-ALAIN 提供的 Dockerfile 文件相对于比较简单,而实际项目中最常用的是对SSL的支持。 因此,默认情况下你可以将证书放置 `_nginx/ssl` 目录下,并开启 `_nginx/default.conf` 相关SSL配置项即可。 最后,增加 `docker-compose.yml` 的 `ports` 节点: ``` - 443:443 ``` ### 容器部署 参考 [Angular 容器部署](https://zhuanlan.zhihu.com/p/35688938)。 --- --- order: 3 title: 校验错误 type: Documents --- ## 写在前面 JSON Schema 校验过程中会生产一组错误信息,每一个错误都有一个固定的 `keyword` 来表示,允许通过[全局配置](/docs/global-config)来覆盖 `errors` 默认的错误信息,包括处理错误信息国际化问题。例如当某属性为必填性时产生的错误信息为: ```json [{ "keyword": "required", "dataPath": ".client", "schemaPath": "#/required", "params": {"missingProperty":"client"}, "message":"必填项" }] ``` 其中 `message` 用于页面渲染的错误文本。 > **注:**第一次渲染会触发校验,但不会有任何视觉展示,若需要一开始就体现错误视觉效果可以指定 ``。 ## 自定义错误文本 分别支持[全局配置](/docs/global-config) `errors`(一般用于国际化) 或 `ui.errors`(针对某个属性) 结构来处理错误文本。 **ui.errors** ```ts schema: SFSchema = { properties: { email: { type: 'string', title: '邮箱', format: 'email', maxLength: 20, ui: { errors: { 'required': '必填项' } } } } }; ``` ### keyword 不管采用哪种方式来构建错误文本,都必须通过 `keyword` 来区分错误类型(完整类型见 [ERRORSDEFAULT](https://github.com/ng-alain/delon/blob/master/packages/form/src/errors.ts#L4))。 ## 自定义校验 JSON Schema 校验并不一定能够满足一些业务的需求,例如需要根据其他属性值区分不同校验规则: ### 属性校验 ```ts schema: SFSchema = { properties: { type: { type: 'string', title: 'Type', enum: [ { value: 'mobile', label: 'Mobile' }, { value: 'email', label: 'email' }, ], default: 'mobile', }, mobile: { type: 'string', title: 'Mobile', ui: { visibleIf: { type: ['mobile'] }, showRequired: true, validator: val => (!val ? [{ keyword: 'required', message: 'Required mobile' }] : []), }, }, email: { type: 'string', title: 'Email', ui: { visibleIf: { type: ['email'] }, showRequired: true, validator: val => (!val ? [{ keyword: 'required', message: 'Required email' }] : []), }, }, pwd: { type: 'string', title: 'Password', ui: { type: 'password', }, }, }, required: ['type', 'pwd'], }; ``` ### 异步校验 例如一个异步校验用户名是否存在示例: ```ts schema: SFSchema = { properties: { name: { type: 'string', ui: { showRequired: true, validator: (value: any) => this.http.get(`/user/check/${value}`).pipe( map(res => res ? [ { keyword: 'required', message: '用户名已存在'} ] : []) ) } } } }; ``` **注意:** 由于每一次校验都是重新实例一次,因此无法做一些控制的操作,例如:去抖 `debounceTime`。 ### setErrors 利用 `setErrors` 方法来调整错误信息。 ```ts this.sf.getProperty('/name')?.setErrors({ keyword: 'required' }); this.sf.getProperty('/name')?.setErrors({ message: 'Please input your username!' }); // 清理当前错误消息 this.sf.getProperty('/name')?.setErrors(); ``` ## 视觉 可以通过设置[全局配置](/docs/global-config)或 `ui.onlyVisual` 属性控制只展示错误视觉不显示错误文本。 ## Debug JSON Schema 对格式有严格的要求,例如日期格式必须遵守 [RFC3339](https://tools.ietf.org/html/rfc3339#section-5.6) 时间格式: ```ts { properties: { time: { type: 'string', ui: { widget: 'date', mode: 'range' }, title: 'Date', format: 'yyyy-MM-dd HH:mm:ss' } }, ui: { debug: true } } ``` 其中 `format` 是一个错误时间格式,当指定 `debug: true` 时,会在控制台接收到详细的校验错误描述: ``` Error: unknown format "yyyy-MM-dd HH:mm:ss" is used in schema at path "#/properties/time" ``` --- --- type: Theme order: 2 title: 样式工具类 --- ng-alain 在 Ant Design 的基础上生产一套尺寸、间距、颜色等工具类。 > 在 VSCode 安装 [ng-alain snippets](https://marketplace.visualstudio.com/items?itemName=cipchk.ng-alain-vscode) 插件,可以对所有工具集样式名称的智能提醒。 ## 间距 ng-alain 认为这些工具类只会运用在内容区域,且秉承 Ant Design 的设计价值观,间距宽度是以 `8px` 为一个基准单位,并衍生出三个尺寸,分别为: | 名称 | 公式 | 尺寸 | 说明 | | ---- | --- | --- | --- | | `xs` | $gutter / 2 | `4px` | 超小号 | | `sm` | $gutter | `8px` | 小号 | | `md` | $gutter * 2 | `16px` | 中号 | | `lg` | $gutter * 3 | `24px` | 大号 | | `xl` | $gutter * 4 | `32px` | 特大号 | | `xxl` | $gutter * 6 | `48px` | 超大号 | 依这些规则,衍生出 `margin`、`padding`,其命名规则如下: - 类型:`padding`、`margin` - 方向(可选):`top`、`right`、`bottom`、`left`、`x`(相当于 `left`、`right`)、`y`(相当于 `top`、`bottom`) **消除** ```regex [<类型>p|m][<方向>t|r|b|l|x|y]?0 ``` **命名格式** ```regex [<类型>p|m][<方向>t|r|b|l|x|y]?-[<尺寸>sm|md|lg] ``` 示例: ```css .p-sm { padding: 8px !important; } .pt-sm { padding-top: 8px !important; } .m-sm { margin: 16px !important; } .mt-md { margin-top: 16px !important; } .p0 { padding: 0 !important; } ``` ## 色彩 Ant Design 并没有按钮色这一说,而是以视觉效果为基准。默认是蓝色系(拂晓蓝),例如:`nz-button` 的按钮类型 `primary`。 而 ng-alain 依然不会破坏这种规则,但依 [色彩](//ant.design/docs/spec/colors-cn) 章节,产生了一种运用于文本、背景的色系类。 | 名称 | 基本色 | 说明 | |---|---|---| | `red` |
    #f5222d
    | 薄暮:斗志、奔放 | | `volcano` |
    #fa541c
    | 火山:醒目、澎湃 | | `orange` |
    #fa8c16
    | 日暮:温暖、欢快 | | `gold` |
    #faad14
    | 金盏花:活力、积极 | | `yellow` |
    #fadb14
    | 日出:出生、阳光 | | `lime` |
    #a0d911
    | 青柠:自然、生机 | | `green` |
    #f5222d
    | 极光绿:健康、创新 | | `cyan` |
    #13c2c2
    | 明青:希望、坚强 | | `blue` |
    #1890ff
    | 拂晓蓝:包容、科技、普惠 | | `geekblue` |
    #2f54eb
    | 极客蓝:探索、钻研 | | `purple` |
    #722ed1
    | 酱紫:优雅、浪漫 | | `magenta` |
    #eb2f96
    | 法式洋红:平稳、中性 | Ant Design 的基础色板共计 120 个颜色,包含 12 个主色以及衍生色。这些颜色基本可以满足中后台设计中对于颜色的需求。 **分类** | 名称 | 色号 | | ---- | --- | | `light` | 5号 | | `normal` | 6号 | | `dark` | 7号 | **命名格式** ```regex [<类型>text|bg]-[<色彩名>red|volcano|orange|gold|yellow|lime|green|cyan|blue|geekblue|purple|magenta|grey](-[light|dark])?(-h)? ``` > `normal` 本身即是基本色,所以可以被忽略 > `grey` 可能会更常用,所以额外增加 `grey-lighter`、`grey-darker` 别名表示深度 示例: ```less // Text color .text-red-light { color: #fabeb9 !important; } .text-red { color: #f04134 !important; } .text-red-dark { color: #a31837 !important; } // background-color color .bg-red-light { background-color: #fabeb9 !important; } .bg-red { background-color: #f04134 !important; } .bg-red-dark { background-color: #a31837 !important; } // hover color .bg-red-light-h { &:hover { background-color: #fabeb9 !important; } } .bg-red-h { &:hover { background-color: #f04134 !important; } } .bg-red-dark-h { &:hover { background-color: #a31837 !important; } } ``` ### 别名 | 别名 | 色系 | | ---- | --- | | `primary` | `@blue-6`
    #1890ff
    | | `success` | `@green-6`
    #52c41a
    | | `error` | `@red-5`
    #ff4d4f
    | | `warning` | `@gold-6`
    #faad14
    | | `info` | `@blue-6`
    #1890ff
    | | `processing` | `@blue-6`
    #1890ff
    | | `highlight` | `@red-5`
    #ff4d4f
    | | `normal` | `#d9d9d9`
    #d9d9d9
    | 示例: ```less // Text color .text-error-light { color: #fabeb9 !important; } .text-error { color: #f04134 !important; } .text-error-dark { color: #a31837 !important; } // background-color color .bg-error-light { background-color: #fabeb9 !important; } .bg-error { background-color: #f04134 !important; } .bg-error-dark { background-color: #a31837 !important; } // hover color .bg-error-light-h { &:hover { background-color: #fabeb9 !important; } } .bg-error-h { &:hover { background-color: #f04134 !important; } } .bg-error-dark-h { &:hover { background-color: #a31837 !important; } } ``` ### 全量颜色 你可以使用 `@enable-all-colors: true` 来开启所有 120 个颜色的规则。 ```less .text-red-1 { color: #fff1f0 !important; } .text-red-6 { color: #f04134 !important; } .text-red-10 { color: #5c0011 !important; } .bg-red-6 { color: #f04134 !important; } .bg-red-6-h { color: #f04134 !important; } ``` ## 清除浮动 清除浮动 `.clearfix`。 ## 显示 | 别名 | CSS | | ---- | --- | | `d-none` | `display: none` | | `d-block` | `display: block` | | `d-inline-block` | `display: inline-block` | | `d-flex` | `display: flex` | | `d-inline-flex` | `display: inline-flex` | | `justify-content-start` | `justify-content: flex-start` | | `justify-content-end` | `justify-content: flex-end` | | `justify-content-center` | `justify-content: center` | | `justify-content-between` | `justify-content: space-between` | | `justify-content-around` | `justify-content: space-around` | | `align-items-start` | `align-items: flex-start` | | `align-items-end` | `align-items: flex-end` | | `align-items-center` | `align-items: center` | | `align-items-baseline` | `align-items: baseline` | | `align-items-stretch` | `align-items: stretch` | | `align-content-start` | `align-content: flex-start` | | `align-content-end` | `align-content: flex-end` | | `align-content-center` | `align-content: center` | | `align-content-between` | `align-content: space-between` | | `align-content-around` | `align-content: space-around` | | `align-content-stretch` | `align-content: stretch` | | `align-self-auto` | `align-self: auto` | | `align-self-start` | `align-self: flex-start` | | `align-self-end` | `align-self: flex-end` | | `align-self-center` | `align-self: center` | | `align-self-baseline` | `align-self: baseline` | | `align-self-stretch` | `align-self: stretch` | | `flex-center` | `display: flex; align-items: center;` | | `flex-center-between` | `display: flex; align-items: center; align-content: space-between;` | ## 位置 | 名称 | 说明 | | ---- | --- | | `overflow-auto` | `overflow: auto` | | `overflow-hidden` | `overflow: hidden` | | `fixed-top` | 固定顶部 | | `fixed-bottom` | 固定底部 | ## 文本 ### 大小 Ant Design 是以 `14px` 为基准字号。 | 名称 | 说明 | | ---- | --- | | `text-xs` | `12px` | | `text-sm` | `14px` | | `text-md` | `16px` | | `text-lg` | `18px` | | `text-xl` | `22px` | ### 对齐 | 名称 | 说明 | | ---- | --- | | `text-left` | 文本居左 | | `text-center` | 文本居中 | | `text-right` | 文本居右 | ### 溢出 > 容器必须是 `display: inline-block` 或 `display: block`。 | 名称 | 说明 | | ---- | --- | | `text-nowrap` | 文本超出不换行 | | `text-truncate` | 文本超出截取并加 `...` | ### Transformation | 名称 | 说明 | | ---- | --- | | `text-lowercase` | 小写文本 | | `text-uppercase` | 大写文本 | | `text-capitalize` | 首词大写 | | `text-deleted` | 删除线 | ### Weight and italics | 名称 | 说明 | | ---- | --- | | `font-weight-normal` | `font-weight: normal` | | `font-weight-bold` | `font-weight: 700` | | `font-italic` | `font-style: italic` | ### Other | 名称 | 说明 | | ---- | --- | | `text-hover` | `*:hover { color: @primary-color; }` | | `disabled` | 禁止 | ## Borders ### 边框 | 名称 | 说明 | | ---- | --- | | `border` | `border: 1px solid #f5f5f5 !important;` | | `border-0` | `border: 0 !important;` | | `border-top-0` | `border-top: 0 !important;` | | `border-right-0` | `border-right: 0 !important;` | | `border-bottom-0` | `border-bottom: 0 !important;` | | `border-left-0` | `border-left: 0 !important;` | ### 颜色 支持[色彩章节](/theme/tools#色彩)所有的色系&别名写法,例如:`border-red`、`border-success`。 ### 圆角 | 名称 | 说明 | | ---- | --- | | `rounded-0` | `border-radius: 0;` | | `rounded-circle` | `border-radius: 50%;` | | `rounded-sm` | `border-radius: 2px;` | | `rounded-md` | `border-radius: 4px;` | | `rounded-lg` | `border-radius: 6px;` | ## Width | 名称 | 说明 | | ---- | --- | | `width-sm` | `160px` | | `width-md` | `240px` | | `width-lg` | `320px` | | `width-[0-10]` | `0%-100%` | ## Responsive 类似 Bootstrap 响应式规则,当屏幕小于 `480px` 时会隐藏所有 `hidden-xs` 类。 | 名称 | 屏幕 | | ---- | --- | | `hidden-xs` | <480px | | `hidden-sm` | <768px | | `hidden-md` | <992px | | `hidden-lg` | <1200px | | `hidden-pc` | <768px | | `hidden-mobile` | >768px | ## 旋转 ``` .rotate-[360 / 15] ``` 例如: ```css // 旋转15度 .rotate-15 // 旋转90度 .rotate-90 // 旋转360度 .rotate-360 ``` ## Other | 名称 | 说明 | | ---- | --- | | `block-center` | `margin: 0 auto` | | `point` | `cursor: pointer` | | `no-data` | 空数据行 | | `no-resize` | 设置不允许调整元素 | | `bg-center` | 背景图垂直居中 | | `scrollbar` | 美化 div 滚动条 | | `color-weak` | 色弱模式 | ## 小部件 ### 瀑布流 在线[示例](https://ng-alain.surge.sh/#/style/gridmasonry)。 | 名称 | 说明 | | ---- | --- | | `row-masonry` | 行 | | `row-masonry-{xs|sm|md|lg|xl}-{1-10}` | 列,响应式样式 | | `col-masonry` | 列 | ## ng-zorro ### nz-card | 名称 | 说明 | | ---- | --- | | `ant-card__body-nopadding` | 强制内容无间距 | ### nz-modal | 名称 | 说明 | | ---- | --- | | `modal-{sm|md|lg|xl}` | 设置Modal的大小,`wrapClassName: 'modal-lg'` | | `modal-body-nopadding` | 内容无内边距 | | `modal-header` | 自定义Modal时非常有效,[DEMO](https://ng-alain.surge.sh/#/extras/poi) | | `modal-footer` | 自定义Modal时非常有效,[DEMO](https://ng-alain.surge.sh/#/extras/poi) | ### nz-table | 名称 | 说明 | | ---- | --- | | `ant-table-rep__title` | 标题 | | `ant-table-rep__hide-header-footer` | 小屏幕时才显示标题或底部 | [comment]: ### nz-tag | 名称 | 说明 | | ---- | --- | | `ant-tag__plus` | 增加按钮样式 | --- --- order: 30 title: en-US: Module Guidelines zh-CN: 模块注册指导原则 type: Other --- 一直以来 `AppModule`、`CoreModule`、`SharedModule` 模块使用没有很明确的规范,很容易让产生乱用。Angular模块目标是为了使组件、指令、服务和管道功能块更内聚,并每一个功能区域形成独立的业务领域或实用工具的集合。 ## 1) 分类说明 ------------ ### AppModule 根模块,用于引导 Angular 启动;它非常适合导入一些需要在整个应用到处需要的模块,例如:主题系统、用户主认证模块、权限模块等模块,以及一些全局性HTTP拦截器、国际化服务等。 ### CoreModule 核心模块只会被导入一次,它等同 `AppModule`,但我们更应该把它当成一个**纯服务类模块**,例如:消息、数据访问等。 ### SharedModule 我们叫它共享模块;它不应该出现 `providers`,因为 `ShareModule` 会在所有业务模块中被导入,这会导致服务被覆盖。 NG-ZORRO、@delon/abc、@delon/chart 等从版本11开始由一次性导入改成按需导入,为此 NG-ALAIN 提炼了两个文件 `shared-delon.module.ts`、`shared-zorro.module.ts` 将一些整个项目经常用到的模块合并成一个叫 `SharedModule` 模块内,这也就是为什么需要在业务模块内第一时间导入它。虽然这种方式可以减少不必要的导入代码,但同时也会引起编译速度,因此不建议把所有组件都放进 `SharedModule` 内,尽可能将需要用到的模块三次以上使用才放进这里;否则务必在业务模块内自行导入。 ## 2) 建议 ------------ ### AppModule **应** 导入模块: + Angular 模块:`BrowserModule`、`BrowserAnimationsModule`、`HttpClientModule` + `AlainThemeModule` 主题系统 + `AlainAuthModule` 用户认证模块 + `AlainACLModule` 权限模块 + 国际化模块 **应** 包含服务: + Angular 国际化 + HTTP 拦截器 + Angular 启动服务 + `ng-zorro-antd` 基础组件服务 + `@delon/abc` 业务组件服务 **作用:** 贯穿整个应用的定义。 ------------ ### CoreModule **应** 仅只留 `providers` 属性。 **作用:** 一些通用服务,例如:用户消息、HTTP数据访问。 ------------ ### ShareModule **应** 包含定义: + 应用通用自定义业务组件 **应** 导入模块: + Angular 通用模块:`CommonModule`、`FormsModule`、`RouterModule`、`ReactiveFormsModule` + `ng-zorro-antd` 基础组件模块 + `@delon/abc` 业务组件模块 + 第三方通用依赖组件模块 **应** 导出所有包含的模块。 **不应** 有 `providers` 属性。 **作用:** 一些通用自定义、第三方组件定义,减少业务模块的导入。 ------------ ### 业务模块 业务模块应该包括业务定义模块和路由模块。 **模块** **应** 导入模块: + `SharedModule` + 对应的路由模块 **不应**: + 导出任何组件 + 尽可能不要使用 `providers` 属性 **路由模块** **应** 只包括路由的 `import`、`exports` 模块。 --- --- order: 3 title: 粒度控制 type: Documents --- ## 写在前面 很多时候需要对某个按钮进行权限控制,`@delon/acl` 提供一个 `acl` 指令,可以利用角色或权限点对某个按钮、表格、列表等元素进行权限控制。 ## 原理 `[acl]` 默认会在目标元素上增加一个 `acl__hide` 样式,利用 `display: none` 来隐藏未授权元素,它是一个简单、又高效的方式。 以此相对应的 `*aclIf` 是一个结构型指令,它类似 `ngIf` 在未授权时会不渲染该元素。**注:** 为了保持简洁它并不支持 `acl-ability` 权限点配置。 ## 示例 ### 角色 按钮必须拥有 user 角色显示。 ```html ``` 按钮必须拥有 user 或 manage 角色显示。 ```html ``` 按钮必须拥有 user 和 manage 角色显示。 ```html ``` 当拥有 user 角色显示文本框,未授权显示文本。 ```html {{user}} ``` 使用 `except` 反向控制,当未拥有 user 角色时显示。 ```html ``` ### 权限点 按钮必须拥有 10 权限点显示。 ```html ``` acl 指令为了能所传递的值是角色还是权限点,所以以 `string` 类型表示角色、`number` 类型表示权限点,若权限点为字符串,可使用以下写法。 ```html ``` 使用 `mode: 'allOf'` 表示必须同时拥有。 - `oneOf` 表示只须满足角色或权限点数组中的一项算有效(默认) - `allOf` 表示必须满足所有角色或权限点数组算有效 按钮必须拥有 `10` 和 `USER-EDIT` 权限点时显示。 ```html ``` **字符串型权限点** 检查权限是通过 `can` 方法,通过[全局配置](/docs/global-config) `acl.preCan` 方法,可以利用该方法来实现一个字符串区分角色或权限点。 ```ts // global-config.module.ts const alainConfig: AlainConfig = { acl: { preCan: (roleOrAbility) => { const str = roleOrAbility.toString(); return str.startsWith('ability.') ? { ability: [ str ] } : null; } } }; ``` 因此,当传递一个带有 `ability.` 开头的字符串会被认为这是一个权限点,例如: ```html ``` ## API ### *aclIf 参数 | 说明 | 类型 | 默认值 ----------|----------------|----------|------- `[aclIf]` | `can` 方法参数体 | `ACLCanType` | - `[aclIfThen]` | 已授权时显示模板 | `TemplateRef | null` | - `[aclIfElse]` | 未授权时显示模板 | `TemplateRef | null` | - `[except]` | 未授权时显示 | `boolean` | `false` --- --- type: Documents order: 15 title: 组件样式 --- 当你在使用 `ng g ng-alain:list` 来生成页面时,会发现并不会产生 Less 文件,主要是 NG-ALAIN 有自己的一套主题系统,NG-ALAIN 希望利用这套主题系统来构建组件,让 CSS 变成编程化。然而它并不能满足所有需求,本文会针对这方面做一个全面描述。 ## 如何开始 手动创建组件样式文件,下面以 `img.component.less` 为示例: ```less // 导入 Less 变量参数 @import '@delon/theme/index'; :host { // 组件宿主样式 display: block; font-size: 16px; ::ng-deep { // 组件内其他样式 .title { color: @text-color; } } } ``` 这个 `img.component.less` 样式文件包含了许多信息: **~@delon/theme/index** 它包容了 NG-ZORRO、@delon/theme、@delon/abc、@delon/chart 主题系统所有的 Less 变量名,只有这样导入才能使我们在下面引用 `@text-color` 这类 Less 变量,它表示默认的颜色值。 > 如果你正在使用商业主题,例如 PRO 会使用其他路径:`@import 'src/styles/theme.less';`。 **特殊选择器** `:host`、`::ng-deep` 它们是 Angular 特殊选择器: - `:host` 表示组件宿主,假设我们当前的组件名为 `img`,那最终 `:host` 会被解析成 `img` 的样式 - `::ng-deep` 表示禁止对视图包装,这有助于减少生成一些额外的标识符,它会影响子组件的的使用,例如:`.title` 会在 `` 组件内所有包含 `class="title"` 都将有效 ## 主题 NG-ALAIN 至从 `9.3.x` 开始内置暗黑与紧凑两种主题,对于全局只需要修改 [styles.less](https://github.com/ng-alain/ng-alain/blob/master/src/styles.less) 一个参数,例如切换为暗黑主题: ```diff - // @import '@delon/theme/theme-dark.less'; + @import '@delon/theme/theme-dark.less'; ``` > 若是紧凑,只需要换成 `@import '@delon/theme/theme-compact.less';`。 同时,对于组件样式的引入也全部替换成: ```diff - @import '@delon/theme/index'; + @import '@delon/theme/theme-dark'; ``` ## 动态主题 如果你正在制作就像现在网站一样,动态切换不同的主题,那么就必须针对不同的主题额外覆盖,例如当开启暗黑时,将 `.title` 换成 `#000` 颜色值: ```less // 导入 Less 变量参数 @import '@delon/theme/index'; :host { // 组件宿主样式 display: block; font-size: 16px; ::ng-deep { // 组件内其他样式 .title { color: @text-color; } } } [data-theme='dark'] { :host ::ng-deep { .title { color: #000; } } } ``` 若紧凑主题: ```less [data-theme='compact'] { :host ::ng-deep { // 针对紧凑重新定义 } } ``` ## 相关链接 - [Angualr Component styles](https://angular.io/guide/component-styles) - [关于Angular样式封装](https://zhuanlan.zhihu.com/p/31235358) --- --- order: 90 title: en-US: Style Guide zh-CN: 编码规范建议 type: Advance --- Angular CLI 构建的项目就其目录结构而言已经非常棒了,同时官网也有一份 [Angular Style Guide](https://angular.io/guide/styleguide)([中文版](https://angular.cn/guide/styleguide))风格指南,建议好好阅读几遍。除此之外,NG-ALAIN 也有一部分编码风格,如下说明可能对于你阅读代码时有用。 ## 一致的代码风格 NG-ALAIN 使用 [ESLint](https://eslint.org/) 来**保证代码质量** 与 [Prettier](https://prettier.io/) 来**优化代码风格**。 推荐安装几个插件在 vscode 中更友好的开发: - [ng-zorro snippets](https://marketplace.visualstudio.com/items?itemName=cipchk.ng-zorro-vscode) - [ng-alain snippets](https://marketplace.visualstudio.com/items?itemName=cipchk.ng-alain-vscode) - [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) - [Prettier - Code formatter](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) - [EditorConfig for VS Code](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) 当然,NG-ALAIN 为大家准备了一套完整的扩展包,只需要安装 [NG-ALAIN Extension Pack](https://marketplace.visualstudio.com/items?itemName=cipchk.ng-alain-extension-pack) 即可。 ### Git - pre commit 勾子 Angular 提供的 `ng lint` 命令可以非常有效的帮助我们更早发现bug、更高的可读性;如果我们能够保证团队开发过程中每一次 commit 前都自动做一次 staged 中文件的 lint 的话,那不是非常酷吗? NG-ALAIN 配置了每次对 staged 进行 commit 时会预先做 lint,若发现错误则无法提交。 默认开启了 `*.ts`、`*.less` 的提交过程中强制对代码进行格式化,你可以通过修改 `package.json` 的 `husky` 节点来改变些规则。 > 若在执行 `git commit -m "commit"` 时若产生 **hint: The '.husky/pre-commit' hook was ignored because it's not set as executable.** 时,可能是因为权限问题,可以尝试在项目根目录下执行: ```bash chmod ug+x .husky/* chmod ug+x .git/hooks/* ``` ## 风格指南 ### API文档 应用总是免不了业务组件的开发,我们无法保证若干时间之后你还能记得这些,因此务必在每一个业务组件附带 `README.md` 文档,并描述API、DEMO等信息,例如: ```markdown ## 何时使用? 略 ## DEMO 略 ## API 成员 | 说明 | 类型 | 默认值 ----|------|-----|------ src | 图片地址 | `string` | - ``` ### 模块注册 请参数[模块注册指导原则](/docs/module)。 ## 辅助项 `ng-alain` 配置了一些针对 CLI 选项,以便更好进行编码工作。 ### CLI vscode 是编写 Angular 最佳的选择,你可以在项目的任何目录里输入:`ng g c list` 生成组件的相应的文件。 `ng-alain` 默认配置了不生成样式文件&单元测试,因此,你会看到生成的只有 `.ts`、`.html`。这是因为 `ng-alain` 提供了非常丰富的样式API,在大多数页面中自定义样式并不是刚需的;同时,单元测试也是如此。 当然,你可以很容易在 `angular.json` 中调整默认配置。 ### vscode snippets vscode 是编写 Angular 最佳的选择,自然 NG-ALAIN 也制作了相应 snippets 扩展插件:[ng-zorro-vscode](//marketplace.visualstudio.com/items?itemName=cipchk.ng-zorro-vscode) 和 [ng-alain-vscode](//marketplace.visualstudio.com/items?itemName=cipchk.ng-alain-vscode)。 --- --- order: 4 title: 自定义小部件 type: Documents --- ## 写在前面 `@delon/form` 尽可能满足不同需求,除现有内置的十几种基础小部件(部分需要手动注册)外,可以通过以下两种方式进一步扩展需求: ## 自定义小部件 细节请参考 [自定义小部件](/form/custom)。 ## 制作小部件 制作一套项目需求的小部件,可以更快速的开发工作。 ### 编写小部件 **自己创建小部件** 小部件就是一个组件,你只需要继承 `ControlWidget` 就相当于构建一个小部件,例如: ```ts import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ControlWidget, DelonFormModule, SFWidgetProvideConfig } from '@delon/form'; export function withTestWidget(): SFWidgetProvideConfig { return { KEY: 'test', type: TestWidget }; } @Component({ selector: 'sf-tinymce', template: ` `, changeDetection: ChangeDetectionStrategy.OnPush, imports: [DelonFormModule] }) class TestWidget extends ControlWidget implements OnInit { /* 用于注册小部件 KEY 值 */ static readonly KEY = 'test'; // 组件所需要的参数,建议使用 `ngOnInit` 获取 config: any; loadingTip: string; ngOnInit(): void { this.loadingTip = this.ui.loadingTip ?? '加载中……'; this.config = this.ui.config ?? {}; } // reset 可以更好的解决表单重置过程中所需要的新数据问题 reset(value: string) { } change(value: string) { if (this.ui.change) this.ui.change(value); this.setValue(value); } } ``` **sf-item-wrap** 在模板中唯一是利用 `sf-item-wrap` 包裹自定义内容,它内部封装表单基础元素。 **变更检测** 小部件在渲染过程是手动变更检测,大部分情况下 `ControlWidget` 已经很好的管理什么时机应该执行变更检测,在自定义小部件过程中可能会遇到异步操作导致界面并未渲染,此时可以调用 `detectChanges()` 方法来触发一次小部件节点的变更检测。 ### 注册小部件 建议在小部件内定义一个名为 `withXWidth` 并返回 `SFWidgetProvideConfig`,例如: ```ts export function withTestWidget(): SFWidgetProvideConfig { return { KEY: 'test', type: TestWidget }; } ``` 最后,通过 `provideSFConfig` 来注册: ```ts provideSFConfig({ widgets: [ withTestWidget() ] }) ``` 当然为了更友好的维护,建议在Shared目录下定义项目专属的集合,有兴趣可参考 [ng-alain实现](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/)或参考[@delon/form/widgets/autocomplete](https://github.com/ng-alain/delon/tree/master/packages/form/widgets/autocomplete)。 ### 使用自定义小部件 同其他小部件一样,只需要指定 `widget` 值,例如: ```json "test": { "type": "string", "ui": { "widget": "test", "data": "widget parameters" } } ``` --- --- order: 10 title: en-US: Get Token zh-CN: 获取Token type: Documents --- Token 的获取分为两大类,一是自己的用户认证中心,二是社会化登录(本质上还是需要走自己的用户认证中心)。 ## 用户认证中心 一般来说后端会提供一个URL认证地址,我们可以像平常访问一个普通 HTTP 请求一样,将用户输入的用户名和密码等信息发送给用户认证中心,最后会返回一个用户信息包含 Token。因此,对于只需要将这此[Token信息存储](/auth/set)起来即可。 ## 社会化登录 一个完整的社会化登录大概需要两个步骤: - 打开第三方授权框 - 回调后获取认证信息并对[Token信息存储](/auth/set) ### 打开 `SocialService` 提供了 `open()` 方法,用于打开一个登录框。默认情况下它本身并不在注册到任何模块当中,因为 `@delon/auth` 认为需要这类服务通常只会在登录过程中产生,因此没有必要在全局注入;只需要在使用 `SocialService` 对应组件中注入即可,当然你要愿意也可以在根模块中注入。 ```ts @Component({ providers: [ SocialService ] }) export class ProUserLoginComponent { constructor(private socialService: SocialService) {} } ``` 最后,利用 `type` 属性指定以采用什么形式打开一个授权框: ```ts this.socialService.login(`//github.com/login/oauth/authorize?xxxxxx`, '/', { type: 'href' }); // 或使用 window.open 打开授权框并订阅结果 this.socialService.login(`//github.com/login/oauth/authorize?xxxxxx`, '/', { type: 'window' }).subscribe(res => { }); ``` ### 回调 回调页面是指授权成功后携带的认证信息;以往你可能直接在后端将认证信息写入 Cookie 会自动在请求结束后写入浏览器,而对于 Angular 这类单页而言,特别是前后端分离部署时,这种方式变成无法实现。 因此 `@delon/auth` 是从回调页 URL 地址上携带信息作为获取源,当然它会受 URL 本身受限(例如:长度);但我想对一个 Token 值是足够长的,你可以获取到 Token,再获取用户信息。 需要创建一个用于回调的页面,而页面唯一要做的就是在 `ngOnInit` 时调用 `callback()` 方法(例如:[callback.component.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/routes/callback/callback.component.ts#L24)): ```ts // 1、默认根据当前URL地址 this.socialService.callback(); // 2、非 `{ useHash: true }` 路由 this.socialService.callback(`/callback?token=40SOJV-L8oOwwUIs&name=cipchk&uid=1`); // 3、带有 `{ useHash: true }` 路由 this.socialService.callback(`/?token=40SOJV-L8oOwwUIs&name=cipchk&uid=1#/callback`); // 4、指定 `ITokenModel` 对象 this.socialService.callback({ token: '123456789', name: 'cipchk', id: 10000, time: +new Date }); ``` `callback()` 会把URL自动转换成效的 `ITokenModel`。 > 注:对于 `{ useHash: true }` 的路由在很多第三方授权框是不支持回调,若是自己的账号体系,你依然可以使用第3种示例中的URL形式。 --- --- order: 2 title: 规则数据 type: Documents --- ## 写在前面 Mock 规则数据是一个 `Object` 对象,Key 为请求域声明,Value 为响应内容,例如: ```ts export const USERS = { 'GET /users': { users: [1, 2], total: 2 }, } ``` 表示当通过 `HttpClient.get('/users')` 访问时,会直接返回 `{ users: [1, 2], total: 2 }`,并且不会发送任何 HTTP 请求,你可以通过 `Network` 面板来确认。 ## Key 请求域声明 使用 `' '` 空格来区隔请求方法和URL,请求方法可忽略,默认为 `GET`;URL 支持路由参数和正则表达式。一些有效的 Key: ```ts export const USERS = { 'GET /users': null, // GET 可省略 '/users/1': null, // POST 请求 'POST /users/1': null, // 路由参数 '/users/:id': null, // 正则表达式需要用 `()` 包裹 '/data/(.*)': null }; ``` ## Value 响应内容 响应内容支持三种类型:`Object`、`Array`、`(req: MockRequest) => any | Observable | Promise`。 ```ts import { MockStatusError } from '@delon/mock'; export const USERS = { // Array '/users': [ { uid: 1 }, { uid: 2 } ], // Object '/users': { uid: 1 }, // Function '/qs': (req: MockRequest) => req.queryString.pi, // 支持返回完整的 HttpResponse '/http': (req: MockRequest) => new HttpResponse({ body: 'Body', headers: new HttpHeaders({ 'token': '1' }) }), // 发送 Status 错误 '/404': () => { throw new MockStatusError(404); }, // 支持 Observable '/obs': () => of(1), // 支持 Promise '/promise': async () => { await delay(10); return 1; } }; ``` ### MockRequest 名称 | 类型 | 描述 ------------|--------------------|------------------------------------------------------------------ `[params]` | `any` | 路由参数,`/:id` 则 `params.id` `[queryString]` | `any` | URL参数,`/users?pi=1&ps=10` 则 `queryString.pi`、`queryString.ps` `[headers]` | `any` | Headers 值 `[body]` | `any` | 请求 body `[original]` | `HttpRequest` | 原始 `HttpRequest` ### MockStatusError 当你希望响应一个 `404` 异常时。 ## 一些示例 ```ts import { MockStatusError } from '@delon/mock'; export const USERS = { // 支持值为 Object 和 Array 'GET /users': { users: [1, 2], total: 2 }, // GET 可省略 '/users/1': { users: [1, 2], total: 2 }, // POST 请求 'POST /users/1': { uid: 1 }, // 获取请求参数 queryString、headers、body '/qs': (req: MockRequest) => req.queryString.pi, // 路由参数 '/users/:id': (req: MockRequest) => req.params, // /users/100, output: { id: 100 } // 发送 Status 错误 '/404': () => { throw new MockStatusError(404); }, // 使用 () 表示:正则表达式 '/data/(.*)': (req: MockRequest) => req, // 支持 Observable '/obs': () => of(1), // 支持 Promise '/promise': async () => { await delay(10); return 1; } }; ``` ## 存储规则 一般来说 Mock 都是开发过程中需要,因此建议在项目根目录下创建一个 `_mock` 目录,并创建一个 `index.ts` 文件用于导出所有数据规则,参考 [ng-alain/_mock](https://github.com/ng-alain/ng-alain/tree/master/_mock)。 --- --- order: 40 title: 贡献指南 type: Other --- 这篇指南会指导你如何为 NG-ALAIN 贡献一份自己的力量,请在你要提 issue 或者 pull request 之前花几分钟来阅读一遍这篇指南。 ## 行为准则 我们有一份 [行为准则](https://github.com/ng-alain/delon/blob/master/CODE_OF_CONDUCT.md),希望所有的贡献者都能遵守,请花时间阅读一遍全文以确保你能明白哪些是可以做的,哪些是不可以做的。 ## 透明的开发 我们所有的工作都会放在 [ng-alain](https://github.com/ng-alain/ng-alain)、[delon](https://github.com/ng-alain/delon) 上。不管是核心团队的成员还是外部贡献者的 pull request 都需要经过同样流程的 review。 ## Bugs 我们使用 [GitHub Issues](https://github.com/ng-alain/ng-alain/issues) 来做 bug 追踪。如果你想要你发现的 bug 被快速解决,最好的办法就是按照 issues 呈现的模板认真填写每一项;并且能使用这个 [模板](https://stackblitz.com/edit/ng-alain-setup) 来提供重现。 在你报告一个 bug 之前,请先确保已经搜索过已有的 issue 和阅读了 [文档站](https://ng-alain.com/)。 ## 新增功能 如果你有改进我们的 API 或者新增功能的想法,同样按照 issues 呈现的模板认真填写每一项。 ## 第一次贡献 如果你还不清楚怎么在 GitHub 上提 Pull Request ,可以阅读下面这篇文章来学习: [如何优雅地在 GitHub 上贡献代码](https://segmentfault.com/a/1190000000736629) 为了能帮助你开始你的第一次尝试,我们用 [good first issues](https://github.com/ng-alain/ng-alain/labels/good%20first%20issues) 标记了一些比较比较容易修复的 bug 和小功能。这些 issue 可以很好地做为你的首次尝试。 如果你打算开始处理一个 issue,请先检查一下 issue 下面的留言以确保没有别人正在处理这个 issue。如果当前没有人在处理的话你可以留言告知其他人你将会处理这个 issue,以免别人重复劳动。 如果之前有人留言说会处理这个 issue 但是一两个星期都没有动静,那么你也可以接手处理这个 issue,当然还是需要留言告知其他人。 ## Pull Request NG-ALAIN 会关注所有的 pull request,我们会 review 以及合并你的代码,也有可能要求你做一些修改或者告诉你我们为什么不能接受这样的修改。 **在你发送 Pull Request 之前**,请确认你是按照下面的步骤来做的: 1. 在项目根目录下运行了 `yarn`。 2. 如果你修复了一个 bug 或者新增了一个功能,请确保写了相应的测试,这很重要。 3. 确认所有的测试都是通过的 `npm run test`。 4. 确保你的代码通过了 lint 检查 `npm run lint`。小贴士: Lint 会在你 `git commit` 的时候自动运行。 5. 确保你的代码在提交之前经过了正确的 [Rebase](https://www.digitalocean.com/community/tutorials/how-to-rebase-and-update-a-pull-request) 6. 确保你的提交符合[规范](https://github.com/ng-alain/delon/blob/master/CONTRIBUTING.md#-commit-message-guidelines) ## 开发流程 在你 clone 了 ng-alain 或 delon 的代码并且使用 `yarn` 安装完依赖后,你还可以运行下面几个常用的命令: ### delon 基建类库 1. `npm run site` 在本地运行 ng-alain.com 网站 2. `npm run lint` 检查 packages 代码风格 3. `npm run test` 运行 packages 所有类库测试 4. `npm run release` 构建 packages 发布包 ### ng-alain 脚手架 1. `npm start` 在本地运行[脚手架](https://ng-alain.surge.sh/) 2. `npm run lint` 检查代码风格 3. `npm test` 运行测试 4. `npm run build` 构建生产环境网站 --- --- order: 25 title: 路由守卫 type: Documents --- ## 写在前面 当某个路由未发起请求时,意味着无法在拦截器里面对其进行 Token 有效性的验证,而路由守卫可以解决这一问题,例如在你的根路径里: ```ts [ { path: 'home', component: MockComponent, canActivate: [authJWTCanActivate], }, { path: 'my', canActivateChild: [authJWTCanActivateChild], children: [ { path: 'profile', component: MockComponent } ], }, { path: 'login', component: MockComponent, }, ] ``` ## 如何选择? 同样,针对不同认证风格分别为: - `authSimpleCanActivate`, `authSimpleCanActivateChild`, `authSimpleCanMatch` 基于 Simple Web Token 认证风格 - `authJWTCanActivate`, `authJWTCanActivateChild`, `authJWTCanMatch` 基于 Json Web Token 认证风格 --- --- order: 3 title: 路由守卫 type: Documents --- ## 写在前面 路由守卫可以防止未授权用户访问页面。 路由守卫需要单独对每一个路由进行设置,很多时候这看起来很繁琐,`@delon/acl` 实现了通用守卫函数 `aclCanMatch`, `aclCanActivate`, `aclCanActivateChild`,可以在路由注册时透过简单的配置完成一些复杂的操作,甚至支持 `Observable` 类型。 使用固定属性 `guard` 来指定 `ACLCanType` 参数,例如: ```ts const routes: Routes = [ { path: 'auth', canActivate: [ aclCanActivate ], data: { guard: 'user1' as ACLGuardType } }, { path: 'auth', canActivate: [ aclCanActivate ], data: { guard: { role: [ 'user1' ], ability: [ 10, 'USER-EDIT' ], mode: 'allOf' } as ACLGuardType, guard_url: '/no-permisseion' } }, { path: 'obs', canActivate: [ aclCanActivate ], data: { guard: ((_srv, _injector) => { return of('user'); }) as ACLGuardFunctionType, guard_url: '/no-permisseion' } } ] ``` > `guard` 的值必须符合 [ACLCanType](/acl/getting-started#ACLCanType) 类型值。 ## 示例 ```ts import { of } from 'rxjs'; import { aclCanActivate, aclCanActivateChild, aclCanMatch } from '@delon/acl'; const routes: Routes = [ { path: 'guard', component: GuardComponent, children: [ { path: 'auth', component: GuardAuthComponent, canActivate: [ aclCanActivate ], data: { guard: 'user1' } }, { path: 'admin', component: GuardAdminComponent, canActivate: [ aclCanActivate ], data: { guard: 'admin' } } ], canActivateChild: [ aclCanActivateChild ], data: { guard: { role: [ 'user1' ], ability: [ 10, 'USER-EDIT' ], mode: 'allOf' } } }, { path: 'pro', loadChildren: './pro/pro.module#ProModule', canMatch: [ aclCanMatch ], data: { guard: 1 } }, { path: 'pro', loadChildren: './pro/pro.module#ProModule', canMatch: [ aclCanMatch ], data: { guard: of(false).pipe(map(v => 'admin')) } } ]; ``` --- --- order: 90 title: 默认参数 type: Documents --- ng-alain 提供诸多生成模块、页模板,但实际上继承了原生 Angular CLI 组件页的一些参数,例如:`spec` 表示是否生成测试页、`flat` 表示扁平目录、`inline-template` 表示内联模板内容等。 然而,对于这些参数我们没有必要每一次执行命令时都写一遍,可以通过 `angular.json` 对参数预设。 例如: ```json // angular.json { "schematics": { "ng-alain:module": { "routing": true }, "ng-alain:list": { "skipTests": false }, "ng-alain:edit": { "skipTests": false, "modal": true }, "ng-alain:view": { "skipTests": false, "modal": true }, "ng-alain:curd": { "skipTests": false }, "@schematics/angular:module": { "routing": true }, "@schematics/angular:component": { "skipTests": false, "flat": false, "inlineStyle": true, "inlineTemplate": false }, "@schematics/angular:directive": { "skipTests": false }, "@schematics/angular:service": { "skipTests": false } } } ``` 可以通过执行下列命令来获取所有参数描述: ```bash ng g ng-alain:list --help ``` --- # Components --- order: 1 title: _date subtitle: 日期 type: Pipe --- 基于 date-fns 日期格式化,显示更多细节参考 [date-fns](https://date-fns.org/v1.29.0/docs/format)(国内镜像:[moment format](http://momentjs.cn/docs/#/displaying/format/)) 最大好处是 date-fns 支持不同种类的时间格式,例如: + 2018-08-24 18:08:20 + 2018-08-24 + 20180824 + 1503571962333 等等。 ```html {{data.registered | _date: 'yyyy年MM月dd日'}} ``` 输出: ``` 2017年08月24日 ``` --- --- order: 1 title: _HttpClient type: Service --- `@delon/theme` 包含了一个叫 [\_HttpClient](https://github.com/ng-alain/delon/blob/master/packages/theme/src/services/http/http.client.ts) 类,其本质还是调用 Angular 的 `HttpClient`。 ## 特性 - 更友好的调用方法 - 维护 `loading` 属性 - 处理空值 - 统一时间格式为时间戳 - 支持修饰器 @GET、@POST 等 ## 示例 网络请求一般情况下是同 Object 对象做为参数一起使用,例如一个 `get` 请求,原始写法: ```ts HttpClient.get(url, { params: { pi: 1 } }); ``` 而对于 `_HttpClient` 来讲,将参数进一步优化为: ```ts _HttpClient.get(url, { pi: 1 }); ``` ## AlainThemeConfig 通用配置项,例如统一对 `_HttpClient` 设置空值、时间处理方式。 ```ts import { AlainThemeConfig } from '@delon/theme'; export function fnAlainThemeConfig(): AlainThemeConfig { return Object.assign(new AlainThemeConfig(), { http: { nullValueHandling: 'ignore', }, }); } @NgModule({}) export class DelonModule { static forRoot(): ModuleWithProviders { return { ngModule: DelonModule, providers: [ { provide: AlainThemeConfig, useFactory: fnAlainThemeConfig }, ], }; } } ``` ### API | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `nullValueHandling` | 空值处理 | `include,ignore` | `include` | | `dateValueHandling` | 时间值处理 | `timestamp,ignore` | `timestamp` | ## 使用修饰器 目标类都必须继承 `BaseApi` 基类。 ### 示例 ```ts @BaseUrl('/user') @BaseHeaders({ bh: 'a' }) class RestService extends BaseApi { @GET() query(@Query('pi') pi: number, @Query('ps') ps: number): Observable { return; } @GET(':id') get(@Path('id') id: number): Observable { return; } @GET() get(@Payload data: {}): Observable { return; } // 使用 `::id` 来表示转义,若 `id` 值为 `undefined` 会忽略转换,例如: // 当 `id` 为 `10` 时 => 10:type // 当 `id` 为 `undefined` 时 => :id:type @GET(':id::type') get(@Path('id') id: number): Observable { return; } @POST(':id') save(@Path('id') id: number, @Body data: Object): Observable { return; } @POST() save(@Payload data: {}): Observable { return; } @FORM() save(@Payload data: {}): Observable { return; } // 若请求的URL不符合授权要求,会直接抛出 `401` 错误,且不发送请求 @GET('', { acl: 'admin' }) ACL(): Observable { return; } } ``` ### 类 - `@BaseUrl(url: string)` - `@BaseHeaders(headers: HttpHeaders | { [header: string]: string | string[] })` ### 方法 - `@GET(url: string = '', options?: HttpOptions)` - `@POST(url: string = '', options?: HttpOptions)` - `@DELETE(url: string = '', options?: HttpOptions)` - `@PUT(url: string = '', options?: HttpOptions)` - `@HEAD(url: string = '', options?: HttpOptions)` - `@PATCH(url: string = '', options?: HttpOptions)` - `@JSONP(url: string = '', options?: HttpOptions)` - `@OPTIONS(url: string = '', options?: HttpOptions)` #### HttpOptions | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `acl` | ACL 配置,若导入 `@delon/acl` 时自动有效,等同于 `ACLService.can(roleOrAbility: ACLCanType)` 参数值 | `any` | - | | `observe` | 指定响应内容 | `body,events,response` | - | | `responseType` | 指定内容格式 | `arraybuffer,blob,json,text` | - | | `reportProgress` | 是否监听进度事件 | `boolean` | - | | `withCredentials` | 设置 withCredentials | `boolean` | - | ### 参数 - `@Path(key?: string)` URL 路由参数 - `@Query(key?: string)` URL 参数 QueryString - `@Body` 参数 Body - `@Headers(key?: string)` 参数 Headers - `@Payload` 请求负载 - 当支持 Body 时(例如:`POST`、`PUT`)为内容体等同 `@Body` - 当不支持 Body 时(例如:`GET`、`DELETE` 等)为 `QueryString` ## HttpContext ### CUSTOM_ERROR 是否自定义处理异常消息。 ```ts this.http.post(`login`, { name: 'cipchk', pwd: '123456' }, { context: new HttpContext() .set(ALLOW_ANONYMOUS, true) .set(CUSTOM_ERROR, true) }).subscribe({ next: console.log, error: console.log }); ``` ### IGNORE_BASE_URL 是否忽略API前缀。 ```ts // When environment.api.baseUrl set '/api' this.http.get(`/path`) // Request Url: /api/path this.http.get(`/path`, { context: new HttpContext().set(IGNORE_BASE_URL, true) }) // Request Url: /path ``` ### RAW_BODY 是否原样返回请求Body。 --- --- title: acl subtitle: ACL type: Examples --- 结合 `@delon/acl` 权限可以利用一个 Schema 来构建不同角色或权限点的表单。 --- --- title: array subtitle: 数组,树,扁平,分组,去重 type: Tools --- ## ArrayService `ArrayService` 用于数组与树之间的转化、访问等,一般配合 `nz-tree` 一起使用。 > 可以通过[全局配置](/docs/global-config)覆盖 `ArrayService` 设置映射名称。 ### treeToArr 将树结构转换成数组结构。 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `deepMapName` | 深度项名 | `string` | `deep` | | `parentMapName` | 扁平后数组的父数据项名 | `string` | `parent` | | `childrenMapName` | 源数据子项名 | `string` | `children` | | `clearChildren` | 是否移除 `children` 节点 | `boolean` | `true` | | `cb` | 转换成数组结构时回调 | `(item: any, parent: any, deep: number) => void` | - | ### arrToTree 数组转换成树数据。 > 若 `parent_id` 为字符串,则根值**务必**为空字符串。 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `idMapName` | 编号项名 | `string` | `id` | | `parentIdMapName` | 父编号项名 | `string` | `parent_id` | | `rootParentIdValue` | 根父编号值,默认会自动计算得到最合适的根父编号值 | `any` | - | | `childrenMapName` | 子项名 | `string` | `children` | | `cb` | 转换成树数据时回调 | `(item: any) => void` | - | ### arrToTreeNode 数组转换成 `nz-tree` 数据源,通过 `options` 转化项名,也可以使用 `options.cb` 更高级决定数据项。 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `idMapName` | 编号项名 | `string` | `id` | | `parentIdMapName` | 父编号项名 | `string` | `parent_id` | | `titleMapName` | 标题项名 | `string` | `title` | | `isLeafMapName` | 是否叶节点项名,若数据源不存在时自动根据 `children` 值决定是否为叶子节点 | `string` | `isLeaf` | | `checkedMapname` | 节点 Checkbox 是否选中项名 | `string` | `checked` | | `selectedMapname` | 节点本身是否选中项名 | `string` | `selected` | | `expandedMapname` | 节点是否展开(叶子节点无效)项名 | `string` | `expanded` | | `disabledMapname` | 设置是否禁用节点(不可进行任何操作)项名 | `string` | `disabled` | | `cb` | 转换成数组结构时回调 | `(item: any, parent: any, deep: number) => void` | - | ### visitTree 递归访问整个树。 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `childrenMapName` | 子项名 | `string` | `children` | ### findTree 根据条件返回树的第一个值,否则返回 `undefined`。 | 参数 | 说明 | 类型 | 默认值 | |----------|-------------|------|---------| | `childrenMapName` | 子项名 | `string` | `children` | ### getKeysByTreeNode 获取所有已经选中的 `key` 值。 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `includeHalfChecked` | 是否包含半选状态的值 | `boolean` | `true` | | `keyMapName` | 是否重新指定 `key` 键名,若不指定表示使用 `NzTreeNode.key` 值 | `string` | - | | `cb` | 回调,返回一个值 `key` 值,优先级高于其他 | `(item: NzTreeNode, parent: NzTreeNode, deep: number) => any` | - | ### flat 递归扁平数组。 ```ts srv.flat([1, [2, 3, [4, 5, [6]]]]) => [1,2,3,4,5,6] srv.flat([1, [2, 3, [4, 5, [6]]]], 1) => [1,2,3,[4, 5, [6]]] ``` ### groupBy 对数组进行分组。 ```ts srv.groupBy([6.1, 4.2, 6.3], Math.floor) => {"4":[4.2],"6":[6.1,6.3]} srv.groupBy(['one', 'two', 'three'], v => v.length) => {"3":["one","two"],"5":["three"]} ``` ### uniq 创建去重后的数组。 ```ts uniq([1, 2, 2, 3, 1]) => [1,2,3] uniq([{ a: 1 }, { a: 1 }, { a: 2 }], 'a') => [{"a":1},{"a":2}] uniq([{ a: 1 }, { a: 1 }, { a: 2 }], i => (i.a === 1 ? 'a' : 'b')) => [{"a":1},{"a":2}] ``` --- --- title: array subtitle: 数组 type: Widgets order: 2 --- 创建对象数组,只对 `schema.type="array"` 时有效。 ## 关于布局 数组的布局分为数组本身以及数组元素布局,`arraySpan` 决定每个数组元素占栅格数值。 Schema 内嵌 UI 风格: ```ts const schema: SFSchema = { properties: { list: { type: 'array', items: { type: 'object', properties: { a: { type: 'string' }, b: { type: 'number', ui: { spanLabel: 10 } } } }, ui: { spanLabel: 5, grid: { arraySpan: 12 } } } } }; ``` **注意:** `items` 下所有属性都继承于 `list.ui`,最终 `items.a` 为 `5` 个单位、`items.b` 为 `10` 个单位。 Schema 与 UI 分开风格,假如上述 Schema 转化成 UI 写法: ```ts const ui = { $list: { $items: { $b: { spanLabel: 10 } }, spanLabel: 5, grid: { arraySpan: 12 } } }; ``` ## API ### schema 属性 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[items]` | 数组元素类型描述 | `SFSchema` | - | | `[minItems]` | 约束数组最小的元素个数 | `number` | - | | `[maxItems]` | 约束数组最大的元素个数 | `number` | - | | `[uniqueItems]` | 约束数组每个元素都不相同 | `boolean` | - | ### ui 属性 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[addTitle]` | 添加按钮文本 | `string` | `添加` | | `[addType]` | 添加按钮类型,等同 `nzType` | `string` | `dashed` | | `[removable]` | 是否包含移除按钮 | `boolean` | `true` | | `[removeTitle]` | 移除按钮文本 | `string` | `移除` | | `[required]` | 当前项是否为必填,仅影响样式 | `boolean` | - | | `[$items]` | 数组元素类型UI描述 | `SFUISchema` | - | | `(add)` | 添加回调,`property` 表示添加后的表单属性 | `(property: FormProperty) => void` | - | | `(remove)` | 移除回调 | `(index: number) => void` | - | --- --- type: Other title: auto-focus subtitle: 自动获得焦点 description: 组件渲染后自动获得焦点 cols: 1 module: import { AutoFocusModule } from '@delon/abc/auto-focus'; --- 允许在HTML元素出现后立即对其进行设置焦点,默认情况下会对 `input`、`textarea` 带有 `[autofocus="autofocus"]` 生效。 ## API ### [auto-focus] | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[enabled]` | 是否启用 | `boolean` | `true` | | `[delay]` | 延迟时长(单位:毫秒) | `number` | `25` | | `(focus)` | 获得焦点回调 | `void` | `-` | --- --- title: autocomplete subtitle: 自动完成 type: Non-built-in widgets --- 输入框自动完成功能。 ## 如何使用 非内置模块,需要额外在 [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9) 注册 `withAutoCompleteWidget`。 ## 数据源说明 **静态** 指获取后每一次筛选是通过 `filterOption` 过滤,数据来源于 `asyncData`、`enum`。 若 `schema.format: 'email'` 时自动渲染为自动补全邮箱后缀,默认 `['qq.com', '163.com', 'gmail.com', '126.com', 'aliyun.com']` 可通过 `enum` 来重新调整该值或[全局配置](/docs/global-config) `uiEmailSuffixes`。 **实时** 指获取后每一次筛选是通过 `filterOption` 过滤,数据来源于 `asyncData`。 ## API ### schema 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[enum]` | 静态数据源 | `SFSchemaEnumType[]` | - | | `[readOnly]` | 禁用状态 | `boolean` | - | ### ui 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[asyncData]` | 实时数据源 | `(input: string) => Observable` | - | | `[size]` | 大小,等同 `nzSize` | `string` | - | | `[placeholder]` | 在文字框中显示提示讯息 | `string` | - | | `[filterOption]` | 是否根据输入项进行筛选,默认只对 `label` 属性执行不区分大小定 `indexOf` 过滤。当其为一个函数时,会接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 `true`,反之则返回 `false`。 | `boolean or (inputValue: string, option: SFSchemaEnum) => boolean` | `true` | | `[type]` | 模式,自动完成常见邮箱后缀,可以重新使用 `enum` 来指定新后缀 | `email` | - | | `[debounceTime]` | 去抖时间,当实时数据源时默认最少 `50`,单位:毫秒 | `number` | `0` | | `[defaultActiveFirstOption]` | 是否默认高亮第一个选项 | `boolean` | `true` | | `[backfill]` | 使用键盘选择选项的时候把选中项回填到输入框中 | `boolean` | `false` | | `[nzWidth]` | 自定义宽度单位 px | `number` | 触发元素宽度 | | `[change]` | 变更回调 | `(item: NzAutocompleteOptionComponent, orgData: SFSchemaEnum) => void` | - | | `[overlayClassName]` | 下拉根元素的类名称 | `string` | - | | `[overlayStyle]` | 下拉根元素的样式 | `object` | - | | `[compareWith]` | 与 [SelectControlValueAccessor](https://angular.io/api/forms/SelectControlValueAccessor#caveat-option-selection) 相同 | `(o1: any, o2: any) => boolean` | `(o1: any, o2: any) => o1===o2` | --- --- title: boolean subtitle: 开关 type: Widgets order: 4 --- 开关选择器 ## API ### schema 属性 | 参数 | 说明 | 类型 | 默认值 | | ------------ | -------- | --------- | ------ | | `[readOnly]` | 禁用状态 | `boolean` | - | ### ui 属性 | 参数 | 说明 | 类型 | 默认值 | | --------------------- | ------------------- | --------------- | --------- | | `[size]` | 大小,等同 `nzSize` | `default,small` | `default` | | `[checkedChildren]` | 选中时的内容 | `string` | - | | `[unCheckedChildren]` | 非选中时的内容 | `string` | - | | `[loading]` | 加载中的开关 | `boolean` | `false` | --- --- title: browser subtitle: Cookie、Copy、DOM 等 type: Tools --- ## CookieService 一组简单的 Cookie 操作类。 - `cookie` 原始Cookie值 - `getAll` 获取所有Cookie键值对 - `get` 获取指定 `key` 的值 - `put` 设置指定 Cookie 键的值 [comment]: ## isEmpty 用于校验 `` 是否为空,自定义组件时蛮有用。 ## updateHostClass 更新宿主组件样式 `class`,例如: ```ts updateHostClass( this.el.nativeElement, this.renderer, { [ 'classname' ]: true, [ 'classname' ]: this.type === '1', [ this.cls ]: true, [ `a-${this.cls}` ]: true } ) ``` ## copy 复制字符串文档至剪贴板。 ## ScrollService 滚动条控制,允许滚动至指定元素所处位置。 | 接口名 | 参数 | 描述 | |-----|----|----| | `getScrollPosition` | `element?: Element` | 获取滚动条位置 | | `scrollToPosition` | `element: Element | Window, position: [number, number]` | 设置滚动条位置 | | `scrollToElement` | `element?: Element, topOffset = 0` | 设置滚动条至指定元素 | | `scrollToTop` | `topOffset = 0` | 滚动至顶部 | --- --- title: cascader subtitle: 级联选择 type: Non-built-in widgets --- 一般用于省市区,公司层级,事物分类等。 ## 如何使用 非内置模块,需要额外在 [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9) 注册 `withCascaderWidget`。 ## 注意事项 - `default` 或 `formData` 值始终应该保持一个数组,例如:城市级联可能只存储叶节点 `value`,此时需要手动处理并给出完整数据链 `value` 数组 ## 数据源说明 **静态** 指一次性获取数据,数据来源于 `asyncData`、`enum`。 **实时** 指每一次每一次选择会触发HTTP请求,数据来源于 `asyncData`;包含三个参数 `(node: NzCascaderOption, index: number, me: CascaderWidget) => PromiseLike`,其中 `me` 表示当前小部件实例。 ## API ### schema 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[enum]` | 静态数据源 | `SFSchemaEnumType[]` | - | | `[readOnly]` | 禁用状态 | `boolean` | - | ### ui 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[asyncData]` | 异步静态数据源 | `(node: NzCascaderOption, index: number, me: CascaderWidget) => PromiseLike` | - | | `[size]` | 大小,等同 `nzSize` | `string` | - | | `[placeholder]` | 在文字框中显示提示讯息 | `string` | - | | `[showSearch]` | 是否支持搜索 | `bool` | `false` | | `[allowClear]` | 是否显示清除按钮 | `bool` | `true` | | `[clearValue]` | 清空时默认值 | `any` | `undefined` | | `[clearText]` | 清除按钮的标题 | `string` | `清除` | | `[showArrow]` | 是否显示箭头 | `bool` | `true` | | `[showInput]` | 是否显示输入框 | `bool` | `true` | | `[menuClassName]` | 自定义浮层类名 | `string` | - | | `[menuStyle]` | 自定义浮层样式 | `string` | - | | `[columnClassName]` | 弹出菜单中数据列的自定义样式 | `string` | - | | `[notFoundContent]` | 当下拉列表为空时显示的内容 | `string` | - | | `[data]` | 初始化列数据,用于第一列的数据,子列通过选项的 `children` 加载,或者通过 `load` 事件异步加载。 | `Array` | - | | `[enableCache]` | 是否缓存异步加载的数据,若每次异步加载的数据都是变化的,需将该值设置为 false | `bool` | `true` | | `[expandTrigger]` | 次级菜单的展开方式,可选 'click' 和 'hover' | `string` | `click` | | `[changeOnSelect]` | 当此项为 true 时,点选每级菜单选项值都会发生变化,具体见上面的演示 | `bool` | `false` | | `[changeOn]` | 可通过自定义的函数来判断点击菜单选项是否应该发生变化,当函数返回 true 时,将发生变化 | `(option: NzCascaderOption, level: number) => boolean` | - | | `[triggerAction]` | 触发菜单出现的行为 | `('click', 'hover')[]` | `['click']` | | `[valueProperty]` | 值 `value` 的属性名称 | `string` | `value` | | `[labelProperty]` | 值 `label` 的属性名称 | `string` | `label` | | `[multiple]` | 是否多选 | `boolean` | `false` | | `(visibleChange)` | 异步加载事件 | `(value: boolean) => void` | - | | `(change)` | 选项值变更事件 | `(values: any[]) => void` | - | | `(selectionChange)` | 选项变更事件 | `(values: NzCascaderOption[]) => void` | - | | `(clear)` | 内容被清空事件 | `() => void` | - | --- --- type: CURD title: cell subtitle: 单元格数据 cols: 1 order: 4 module: import { CellModule } from '@delon/abc/cell'; --- 内置支持十几种数据类型的格式化,且支持小部件自定义模式。 ## API ### cell | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[value]` | 值 | `unknown` | - | | `[options]` | 选项 | `CellOptions` | - | | `[loading]` | 是否加载中 | `boolean` | `false` | ### CellOptions | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[type]` | 渲染类型 | - | - | | `[tooltip]` | 文字提示 | `string` | - | | `[renderType]` | 渲染类型 | `primary,success,danger,warning` | - | | `[size]` | 大小 | `large,small` | - | | `[unit]` | 单位,也可通过 `value: {text: 100, unit: '元'}` 来指定 | `string` | `-` | | `[default]` | 默认文本 | `string | CellDefaultText` | - | | `[mask]` | 格式化掩码, 参考[文档](https://ng-alain.com/util/format/zh#formatMask) | `string, FormatMaskOption` | - | | `[widget]` | 小部件配置 | `{key?: string, data?: string}` | - | | `[date]` | 日期配置,支持 `几分钟前` 格式化 | `{format?: string}` | - | | `[mega]` | 大数据格式化配置 | `CurrencyMegaOptions` | - | | `[currency]` | 货币配置 | `CurrencyFormatOptions` | - | | `[boolean]` | 布尔配置 | `YNOptions` | - | | `[img]` | 图像配置,支持大图预览 | `{ size?: number; big?: boolean }` | - | | `[link]` | 链接配置 | `{ url?: string; target?: string }` | - | | `[html]` | HTML 配置 | `{ safe?: string }` | - | | `[badge]` | 徽章配置 | `{ data?: CellBadge }` | - | | `[tag]` | 标签配置 | `{ data?: CellTag }` | - | | `[checkbox]` | 复选框配置 | `{ label?: string }` | - | | `[radio]` | 单选框配置 | `{ label?: string }` | - | **渲染类型** - `string` 字符串 - `number` 数字 - `mega` 大数据格式化 - `currency` 货币 - `cny` 转化成人民币表示法 - `boolean` 布尔 - `date` 日期 - `img` 图像,支持大图预览 - `link` 链接 - `html` HTML - `badge` 徽章 - `tag` 标签 - `checkbox` 复选框(支持 `disabled`) - `radio` 单选框(支持 `disabled`) - `enum` 枚举转换 - `widget` 自定义小部件 ## 自定义小部件 实现 `CellWidgetInstance` 接口即可,例如: ```ts import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import type { CellTextResult, CellWidgetInstance } from '@delon/abc/cell'; import { NzMessageService } from 'ng-zorro-antd/message'; import { NzTooltipModule } from 'ng-zorro-antd/tooltip'; @Component({ selector: 'cell-widget-test', template: ` `, host: { '(click)': 'show()' }, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ NzTooltipModule ] }) export class CellTestWidget implements CellWidgetInstance { private readonly msg = inject(NzMessageService); static readonly KEY = 'test'; readonly data!: CellTextResult; show(): void { this.msg.info(`click`); } } ``` 其中 `data` 为固定参数,包含 `value`、`options` 配置项。 最后在 `app.config.ts` 下通过 `provideCellWidgets` 注册小部件,例如: ```ts export const appConfig: ApplicationConfig = { providers: [ provideCellWidgets( { KEY: CellTestWidget.KEY, type: CellTestWidget } ), ] } ``` --- --- title: chart-echarts subtitle: ECharts cols: 1 type: ECharts module: import { ChartEChartsModule } from '@delon/chart/chart-echarts'; --- [ECharts](https://echarts.apache.org/zh/index.html) 一个基于 JavaScript 的开源可视化图表库。使用懒加载 ECharts 脚本,开箱启用。 ## API ### chart-echarts | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[width]` | 图表宽度 | `number, string` | `100%`| | `[height]` | 图表高度 | `number, string` | `400px`| | `[option]` | [配置项](https://echarts.apache.org/zh/option.html#title) | `ChartEChartsOption` | - | | `[theme]` | [主题](https://echarts.apache.org/zh/theme-builder.html)配置 | `string, object` | - | | `[on]` | 等同于 ECharts [on](https://echarts.apache.org/zh/api.html#echartsInstance.on) | `ChartEChartsOn[]` | - | | `(events)` | 事件回调 | `EventEmitter` | - | --- --- title: checkbox subtitle: 多选框 type: Widgets --- 一组可选项中进行多项选择时 ## API ### schema 属性 成员 | 说明 | 类型 | 默认值 ----|------|-----|------ `[enum]` | 数据源,当数据源存在于表示一组多选框 | `SFSchemaEnumType[]` | - `[readOnly]` | 禁用状态 | `boolean` | - `[title]` | 无 `enum` 时表示多选框文本内容 | `string` | - `[description]` | 无 `enum` 时表示多选框后帮助信息 | `string` | - ### ui 属性 成员 | 说明 | 类型 | 默认值 ----|------|-----|------ `[asyncData]` | 异步数据源 | `() => Observable` | - `[span]` | 指定每个选框单元格数量 | `number` | - `[styleType]` | radio的样式 | `default, button` | `default` `[checkAll]` | 是否需要全选 | `boolean` | - `[checkAllText]` | 全选按钮文本 | `string` | `全选` `[change]` | 值变更事件,参数:单个多选框为 `boolean`,否则为 `SFSchemaEnum[]` | `(res: boolean | SFSchemaEnum[]) => void` | - --- --- title: color subtitle: 颜色 type: Non-built-in widgets --- 当用户需要自定义颜色选择的时候使用。 ## 如何使用 非内置模块,需要额外在 [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9) 注册 `withColorWidget`。 ## API ### ui 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[format]` | 颜色格式 | `rgb`|`hex`|`hsb` | `hex` | | `[defaultValue]` | 颜色默认的值 | `string`|`NzColor` | - | | `[allowClear]` | 允许清除选择的颜色 | `boolean` | `false` | | `[trigger]` | 颜色选择器的触发模式 | `hover`|`click` | `click` | | `[showText]` | 显示颜色文本 | `boolean` | `false` | | `[title]` | 设置颜色选择器的标题 | `TemplateRef`|`string` | - | | `(change)` | 颜色变化的回调 | `EventEmitter<{ color: NzColor; format: string }>` | - | | `(formatChange)` | 颜色格式变化的回调 | `EventEmitter<'rgb'|'hex'|'hsb'>` | - | | `[block]` | 是否颜色块 | `boolean` | `false` | --- --- type: Basic title: count-down subtitle: 倒计时 cols: 3 module: import { CountDownModule } from '@delon/abc/count-down'; --- 倒计时组件,依赖 [ngx-countdown](https://github.com/cipchk/ngx-countdown)。 ## 依赖 ``` npm i -S ngx-countdown ``` ## API ### count-down | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[target]` | 目标时间,`number` 表示秒 | `number | Date` | - | | `[config]` | 完整 [Config](https://github.com/cipchk/ngx-countdown#countdownconfig) 参数 | `CountdownConfig` | - | | `(event)` | 事件通知 | `EventEmitter` | - | --- --- title: currency subtitle: 货币管道 type: Pipes module: import { CurrencyPipeModule } from '@delon/util/pipes/currency'; --- > 可以通过[全局配置](/docs/global-config)覆盖 `startingUnit`、`megaUnit`、`precision`、`ingoreZeroPrecision` 等参数。 ## price 用逗号将数字格式化为千位分隔符。 [comment]: ## mega 大数据格式化。 [comment]: ## cny 转化成人民币表示法。 [comment]: --- --- title: custom subtitle: 自定义 type: Widgets --- 定制当前动态表单小部件。更复杂请参考[定制小部件](/form/customize)。 ## 如何使用 务必指定 **sf-template** 是有效路径值,例如: ```html ``` ## API | 参数 | 说明 | 类型 | | ------------- | --------------- | ---------------- | | `[$implicit]` | 当前上下文 | `ControlWidget` | | `[schema]` | 当前节点 Schema | `SFSchema` | | `[ui]` | 当前节点 UI | `SFUISchemaItem` | 上下文包括 `formProperty` 属性,它是传递数据的唯一中间层,因此维护 `formProperty.value` 是唯一与自定义组件通信的媒介。 上下文还包含了一些快捷属性和方法,有关更多细节请阅读 [Widget](https://github.com/ng-alain/delon/blob/master/packages/form/src/widget.ts) 的定义。 --- --- title: date subtitle: 日期 type: Widgets --- 输入或选择日期的控件。 ## 注意事项 - 格式化分为:**数据格式化**表示表单数据和**显示格式化**显示数据(等同 [nzFormat](https://ng.ant.design/components/date-picker/zh#api) 值) - 所有 **数据格式化** 单位,参考 [date-fns format](https://date-fns.org/v1.29.0/docs/format)(国内镜像:[moment format](http://momentjs.cn/docs/#/displaying/format/)) - 指定 `schema.format` 则必须遵守 [RFC3339](https://tools.ietf.org/html/rfc3339#section-5.6) 时间格式,否则都视为格式错误,默认的数据格式化规则: - `date-time` 默认 `yyyy-MM-DDTHH:mm:ssZ`,注意这里采用的是 `type="datetime-local"` 因此会涉及到**时区问题** - `date`、`full-date` 默认 `yyyy-MM-dd` - `time`、`full-time` 默认 `HH:mm:ss` - 非标准:`week` 默认 `yyyy-ww` - 非标准:`month` 默认 `yyyy-MM` - 不指定 `schema.format` 根据 `schema.type` 值按以下规则处理(允许通过[全局配置](/docs/global-config)替换)数据格式化: - `string` 默认 `yyyy-MM-dd HH:mm:ss` - `number` 默认 `T` 13位Unix Timestamp ## API ### schema 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[readOnly]` | 禁用状态 | `boolean` | - | | `[format]` | 数据格式类型 | `string` | - | ### ui 属性 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[mode]` | 渲染模式 | `date,week,month,year` | `date` | | `[rangeMode]` | 范围选择器的选择模式 | `date,week,month,year` | `date` | | `[size]` | 大小,等同 `nzSize` | `default,large,small` | - | | `[placeholder]` | 在文字框中显示提示讯息 | `string, string[]` | - | | `[format]` | 数据格式化 | `string` | - | | `[displayFormat]` | 显示格式化,(等同 [nzFormat](https://ng.ant.design/components/date-picker/zh#api) 值) | `string` | `yyyy-MM-dd HH:mm:ss` | | `[end]` | 日期范围所对应的结束值 `key` | `string` | - | | `[allowClear]` | 是否显示清除按钮 | `boolean` | `true` | | `[className]` | 选择器 className | `string` | - | | `[locale]` | 国际化配置 | `object` | - | | `[popupStyle]` | 额外的弹出日历样式 | `object` | - | | `[dropdownClassName]` | 额外的弹出日历 className | `string` | - | | `[onOpenChange]` | 弹出日历和关闭日历的回调 | `(status: boolean) => void` | - | | `[disabledDate]` | 不可选择的日期 | `(current: Date) => boolean` | - | | `[disabledTime]` | 不可选择的时间 | `(current: Date) => { nzDisabledHours, nzDisabledMinutes, nzDisabledSeconds }` | - | | `[renderExtraFooter]` | 在面板中添加额外的页脚 | `string` | - | | `[showTime]` | 增加时间选择功能,`object` 类型为 [TimePickerOptions](https://ng.ant.design/components/time-picker/en#api) | `object,boolean` | `true` | | `[showToday]` | 是否展示“今天”按钮 | `boolean` | `true` | | `[inputReadOnly]` | 为 input 标签设置只读属性(避免在移动设备上触发小键盘) | `boolean` | `false` | | `[inline]` | 内联模式 | `boolean` | `false` | | `[separator]` | 分隔符 | `string, TemplateRef` | `'~'` | | `[showWeekNumber]` | 是否在每一行显示周数(仅日期选择器支持。周选择器始终显示周数) | `boolean` | `false` | | `[onOk]` | 点击确定按钮的回调 | `(data: Date | Date[]) => void` | - | | `[change]` | 时间发生变化的回调 | `(data: Date | Date[]) => void` | - | --- --- title: date-time subtitle: 日期时间转换 type: Tools --- ## toDate 转换成 `Date` 格式,支持 `Date, number, string` 类型,如果是 `number` 表示 Unix timestamp。 * `formatString` 如果转换失败尝试根据 `formatString` 格式来转换 * `defaultValue` 无效日期应返回的默认值,默认:`new Date(NaN)` * `timestampSecond` 传入值是否秒级 ## formatDate 格式化日期,支持 `Date, number, string` 类型,如果是 `number` 表示 Unix timestamp)。 * 字符串格式请参考 [date-fnd format](https://date-fns.org/v2.30.0/docs/format) * `dateLocale` 建议通过使用 `inject(NZ_DATE_LOCALE)` 与 NG-ZORRO 保持一致 ## dateTimePickerUtil 一组针对 [DatePicker](https://ng.ant.design/components/date-picker/en) 的工具类。 - `now` 当前本地时间 - `date` 当前本地日期(不包含时间部分) - `removeTime` 移除日期的时间部分 - `format` 格式化日期 - `getDiffDays` 计算两个日期相差天数,`0` 表示同一天 - `disabledBeforeDate` 禁用之前日期(默认:今天),一般服务于 `nzDisabledDate` - `disabledAfterDate` 禁用之后日期(默认:今天),一般服务于 `nzDisabledDate` - `disabledBeforeTime` 禁用之前时间(默认:现在),一般服务于 `nzDisabledTime` - `disabledAfterTime` 禁用之后时间(默认:现在),一般服务于 `nzDisabledTime` ```ts disabledDate = dateTimePickerUtil.disabledBeforeDate(); disabledDateTime = dateTimePickerUtil.disabledBeforeTime({ offsetSeconds: 60 * 5 }); ``` ## getTimeDistance 获取时间范围,返回 `[ Date, Date]` 表示开始与结束日期,例如:获取本周时间: ```ts getTimeDistance('week') ``` **参数** - `type` 快捷类型,带 `-` 表示过去一个时间,若指定 `number` 表示天数 - `today`、`-today`、`yesterday` 今天或昨天 - `week`、`-week` 本周或上周 - `month`、`-month` 本月或上月 - `year`、`-year` 今年或去年 - `time` 指定开始时间,默认为:`now` --- --- title: decorator subtitle: 装饰器 type: Tools --- ## @ZoneOutside 装饰方法运行在 `runOutsideAngular` 内。 ```ts class MockClass { constructor(public ngZone: NgZone) {} @ZoneOutside() run(): void {} } ``` ## @ZoneRun 装饰方法运行在 `run` 内。 ```ts class MockClass { constructor(public ngZone: NgZone) {} @ZoneRun() run(): void {} } ``` --- --- type: Basic title: down-file subtitle: 下载文件 cols: 1 module: import { DownFileModule } from '@delon/abc/down-file'; --- 一个基于 `blob` 的文件下载。 ## API ### [down-file] | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[http-data]` | URL请求参数 | `{}` | - | | `[http-body]` | POST请求响应体 | `{}` | - | | `[http-method]` | 请求类型 | `'POST','GET','HEAD','PUT','PATCH','DELETE'` | `'GET'` | | `[http-url]` | 下载地址 | `string` | - | | `[file-name]` | 指定文件名,若为空从服务端返回的 `header` 中获取 `filename`、`x-filename` | `string, (rep: HttpResponse) => string` | - | | `[pre]` | 下载前回调 | `(ev: MouseEvent) => Promise` | - | | `(success)` | 成功回调 | `EventEmitter>` | - | | `(error)` | 错误回调 | `EventEmitter` | - | ## 常见问题 ### 文件名获取顺序 1. `file-name` 2. `content-disposition` 的 `filename*`、`filename` 3. header 参数 `filename`、`x-filename` ### 兼容性 使用 `new Blob()` 来校验[浏览器兼容](https://github.com/eligrey/FileSaver.js/#supported-browsers),若不兼容会在目标元素上增加 `down-file__not-support` 样式。 > 默认不兼容处理行为为隐藏,可以设置 Less 变量为 `@down-file-not-support-behavior: 'disabled'` 表示禁止点击。 --- --- order: 3 title: DrawerHelper subtitle: 抽屉辅助类 type: Service --- 基于 `NzDrawerService` 封装,它解决一些已知问题: - 更友好的处理回调 - 响应式处理 ## create ```ts this.drawerHelper.create('Edit', FormEditComponent, { i }).subscribe(res => this.load()); // 对于组件的成功&关闭的处理说明 // 成功 this.NzDrawerRef.close(data); this.NzDrawerRef.close(true); // 关闭 this.NzDrawerRef.close(); this.NzDrawerRef.close(false); // 关闭所有已打开的抽屉 this.DrawerHelper.closeAll(); ``` 包括 `create` & `static` 分别用于打开普通或静态抽屉。 **自定义组件HTML模板** ```html 内容 ``` 若无需要底部工具条,需要指定参数 `footer: false`。 ## API | 名称 | 类型 | 默认值 | 功能 | | --- | --- | --- | --- | | `size` | 指定抽屉大小,响应式只支持非数字值,若值为数值类型,则根据 `nzPlacement` 自动转化为 `nzHeight` 或 `nzWidth` | `sm,md,lg,xl,number` | `md` | | `footer` | 是否需要工具条 | `boolean` | `true` | | `footerHeight` | 工具条高度 | `number` | `55` | | `exact` | 是否精准(默认:`true`),若返回值非空值(`null`或`undefined`)视为成功,否则视为错误 | `boolean` | `true` | | `drawerOptions` | 抽屉 [NzDrawerOptions](https://ng.ant.design/components/drawer/zh#nzdraweroptions) 参数 | `NzDrawerOptions` | - | ### Method - `closeAll` 关闭所有已打开的抽屉 --- --- type: Layout title: ellipsis subtitle: 文本自动省略号 cols: 1 module: import { EllipsisModule } from '@delon/abc/ellipsis'; --- 文本过长自动处理省略号,支持按照文本长度和最大行数两种方式截取。 ## API ### ellipsis | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[tooltip]` | 移动到文本展示完整内容的提示 | `boolean` | `false` | | `[length]` | 在按照长度截取下的文本最大字符数,超过则截取省略 | `number` | - | | `[lines]` | 在按照行数截取下最大的行数,超过则截取省略 | `number` | - | | `[fullWidthRecognition]` | 是否将全角字符的长度视为2来计算字符串长度 | `boolean` | `false` | | `[tail]` | 指定溢出尾巴 | `string` | `'...'` | --- --- type: Form title: error-collect subtitle: 表单异常消息采集器 cols: 2 module: import { ErrorCollectModule } from '@delon/abc/error-collect'; --- 一个简单的表单异常消息采集器,点击图标跳转相应的位置;必须是一个标准 `form` 表单。 ## API ### error-collect | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[freq]` | 监听频率,单位:毫秒 | `number` | `250` | ✅ | | `[offsetTop]` | 顶部偏移值,单位:`px` | `number` | `145` | ✅ | --- --- type: Layout title: exception subtitle: 异常 cols: 1 module: import { ExceptionModule } from '@delon/abc/exception'; --- 异常页用于对页面特定的异常状态进行反馈。通常,它包含对错误状态的阐述,并向用户提供建议或操作,避免用户感到迷失和困惑。 ## API ### exception | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[type]` | 页面类型,若配置,则自带对应类型默认的 `title`,`desc`,`img`,此默认设置可以被 `title`,`desc`,`img` 覆盖 | `'403','404','500'` | - | ✅ | | `[title]` | 标题 | `string` | - | - | | `[desc]` | 补充描述 | `string` | - | - | | `[img]` | 背景图片地址 | `string` | - | - | | `[backRouterLink]` | 后退路由链接 | `string, any[]` | `/` | - | | `ng-content` | 建议操作,配置此属性时默认的『返回首页』按钮不生效 | `TemplateRef` | - | - | --- --- title: filter subtitle: 过滤数组 type: Pipes module: import { FilterPipeModule } from '@delon/util/pipes/filter'; --- ## filter 过滤数组。 [comment]: --- --- type: Layout title: footer-toolbar subtitle: 底部工具栏 cols: 1 module: import { FooterToolbarModule } from '@delon/abc/footer-toolbar'; --- 固定在底部的工具栏。 ## 何时使用 固定在内容区域的底部,不随滚动条移动,常用于长页面的数据搜集和提交工作。 ## API ### footer-toolbar | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `ng-content` | 工具栏内容,向右对齐 | `-` | - | | `[errorCollect]` | 是否需要 `error-collect`,务必包裹在 `
    ` 元素内 | `boolean` | `false` | | `[extra]` | 额外信息,向左对齐 | `string,TemplateRef` | - | --- --- title: form subtitle: 响应式表单校验 type: Tools --- ## MatchControl 匹配两个控件值,例如: ```ts this.form = new FormGroup({ pwd: new FormControl(''), repwd: new FormControl(''), }, { validators: MatchControl('pwd', 'repwd'), }); ``` ## _Validators 一套用于响应式表单的验证器,包含: - `_Validators.num` 是否为数字 - `_Validators.int` 是否为整数 - `_Validators.decimal` 是否为小数点数值 - `_Validators.idCard` 是否为中华人民共和国居民身份证 - `_Validators.mobile` 是否为手机号(中国) - `_Validators.url` 是否URL地址 - `_Validators.ip4` 是否IP地址(支持v4、v6) - `_Validators.color` 是否颜色代码值 - `_Validators.chinese` 是否中文 每一个验证型都包括着用于表单验证器: ```ts this.valForm = fb.group({ // 手机号 mobile: [null, Validators.compose([Validators.required, _Validators.mobile])] }); ``` --- --- title: format subtitle: 字符,校验,货币,掩码 type: Tools --- ## CurrencyService > 可以通过[全局配置](/docs/global-config)覆盖 `startingUnit`、`megaUnit`、`precision`、`ingoreZeroPrecision` 等参数。 ### format 格式化货币,用逗号将数字格式化为千位分隔符。 ```ts 10000 => `10,000` 10000.567 => `10,000.57` ``` > 若指定则表示使用 Angular 自带的 `currency` 管道来解析,见[文档](https://angular.cn/api/common/CurrencyPipe)。 ### formatMask 格式化掩码。 | 字符 | 描述 | | --- | --- | | `0` | 任意数字,若该位置字符不符合,则默认为 `0` 填充 | | `9` | 任意数字 | | `#` | 任意字符 | | `U` | 转换大写 | | `L` | 转换小写 | | `*` | 转换为 `*` 字符 | ```ts formatMask('123', '(###)') => (123) formatMask('15900000000', '999****9999') => 159****0000 formatMask('aBc', 'UUU') => ABC formatMask('ABc', 'LLL') => abc ``` 或自定义Token: ```ts const option: FormatMaskOption = { mask: 'CC999', tokens: { C: { pattern: /.*/, transform: char => (char === '你' ? 'N' : 'H') } } } formatMask('你好123', option) => NH123 ``` ### mega 大数据格式化。 ```ts 1000 => { value: '1', unit: 'K', unitI18n: '千' } 12456 => { value: '12.46', unit: 'K', unitI18n: '千' } ``` ### cny 转化成人民币表示法。 ```ts 1 => 壹元整 1.34 => 壹元叁角肆分 ``` ## format 字符串格式化。 ```ts format('this is ${name}', { name: 'asdf' }) // output: this is asdf format('this is ${user.name}', { user: { name: 'asdf' } }, true) // output: this is asdf ``` ## REGEX 一组常见的正则表达式。也可以通过 `REGEX_STR` 来获取它们的正则字符串格式,配合 `new RegExp` 完成一些额外的处理。 ## num 是否为数字。 ## int 是否为整数。 ## decimal 是否为小数点数值。 ## idCard 是否为中华人民共和国居民身份证。 ## isMobile 是否为手机号(中国)。 ## isUrl 是否URL地址。 ## isIp 是否IP地址(支持v4、v6)。 ## isColor 是否颜色代码值。 ## isChinese 是否中文。 --- --- title: format subtitle: 掩码 type: Pipes module: import { FormatPipeModule } from '@delon/util/pipes/format'; --- ## mask 格式化掩码。 [comment]: --- --- type: Layout title: full-content subtitle: 全屏工作区 cols: 1 module: import { FullContentModule } from '@delon/abc/full-content'; --- 全屏工作区,常用于带有滚动条表格,一个在线[示例](https://ng-alain.surge.sh/#/delon/st)。 ## API ### full-content | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[(fullscreen)]` | 是否完整全屏(不包括顶部、侧边栏等) | `boolean` | - | | `[hideTitle]` | 完整全屏时是否隐藏标题 | `boolean` | `true` | | `[padding]` | 工作区内边距 | `number` | `24` | ### [full-toggle] 切换是否全屏。 ## 全屏控制 包含三种方式: - 使用 `fullscreen` 双向绑定 - 使用 `[full-toggle]` 指令 - 使用 `FullContentService.toggle()` 服务 --- --- title: g2-bar subtitle: 柱状图 cols: 1 type: G2 module: import { G2BarModule } from '@delon/chart/bar'; --- 使用垂直的柱子显示类别之间的数值比较。其中一个轴表示需要对比的分类维度,另一个轴代表相应的数值。 ## API ### g2-bar | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[repaint]` | 数据再次变更时是否重绘 | `boolean` | `true` | | `[delay]` | 延迟渲染,单位:毫秒 | `number` | `0` | | `[title]` | 图表标题 | `string,TemplateRef` | - | | `[color]` | 图表颜色 | `string` | `rgba(24, 144, 255, 0.85)` | | `[padding]` | 图表内部间距 | `Array | string` | `[32, 0, 32, 40]` | | `[height]` | 图表高度 | `number` | - | | `[data]` | 数据 | `G2BarData[]` | `[]` | | `[autoLabel]` | 在宽度不足时,自动隐藏 x 轴的 label | `boolean` | `true` | | `[interaction]` | 交互类型,none 无 element-active 图形元素,active-region 图表组件,brush 框选,drag-move 移动 | `InteractionType` | `none` | | `[theme]` | 定制图表主题 | `string | LooseObject` | - | | `(clickItem)` | 点击项回调 | `EventEmitter` | - | | `(ready)` | 当G2完成初始化后调用 | `EventEmitter` | - | ### G2BarData | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[x]` | x轴 | `any` | - | | `[y]` | y轴 | `any` | - | | `[color]` | 轴颜色 | `string` | - | --- --- title: g2-card subtitle: 图表卡片 cols: 2 type: G2 module: import { G2CardModule } from '@delon/chart/card'; --- 图表卡片,用于展示图表的卡片容器,可以方便的配合其它图表套件展示丰富信息。 ## API ### g2-card | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[title]` | 卡片标题 | `string,TemplateRef` | - | | `[avatar]` | 头像 | `string,TemplateRef` | - | | `[action]` | 卡片操作 | `string,TemplateRef` | - | | `[total]` | 数据总量(支持HTML) | `string` | - | | `[footer]` | 卡片底部 | `string,TemplateRef` | - | | `[contentHeight]` | 内容区域高度(单位:`px`) | `string` | - | | `[bordered]` | 是否显示边框 | `boolean` | `false` | --- --- title: g2-chart subtitle: 自定义图表 cols: 1 type: G2 module: import { G2CustomModule } from '@delon/chart/custom'; --- 使用 `g2-custom` 组件可以更快速的封装自己的图表组件。 ## 如何使用 一个简单示例模板: ```ts import { Component, ElementRef } from '@angular/core'; @Component({ selector: 'app-demo', template: ` `, }) export class DemoComponent { render(el: ElementRef) { // 开始编写 G2 代码 } } ``` ### 如何开发 G2 可能你会遇到未找到 `G2`,请参考[常见问题](/chart/faq)。 ## 链接 - [G2 文档](https://www.yuque.com/antv/g2-docs) - [G2 示例](https://antv.alipay.com/zh-cn/g2/3.x/demo/index.html) ## API ### g2-custom | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[delay]` | 延迟渲染,单位:毫秒 | `number` | `0` | | `[height]` | 高 | `number` | - | | `[resizeTime]` | resize 事件去抖时长 | `number` | `200` | | `(render)` | 渲染事件 | `EventEmitter` | - | | `(resize)` | resize 事件 | `EventEmitter` | - | | `(destroy)` | 销毁事件 | `EventEmitter` | - | | `[theme]` | 定制图表主题 | `string | LooseObject` | - | --- --- title: g2-gauge subtitle: 仪表盘 cols: 1 type: G2 module: import { G2GaugeModule } from '@delon/chart/gauge'; --- 一种进度展示方式,可以更直观的展示当前的进展情况,通常也可表示占比。 ## API ### g2-gauge | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[repaint]` | 数据再次变更时是否重绘 | `boolean` | `true` | | `[delay]` | 延迟渲染,单位:毫秒 | `number` | `0` | | `[title]` | 图表标题 | `string` | - | | `[height]` | 图表高度 | `number` | - | | `[color]` | 图表颜色 | `string` | `#2F9CFF` | | `[bgColor]` | 图表背景色 | `string` | `#F0F2F5` | | `[percent]` | 进度比例 | `number` | - | | `[padding]` | 内边距 | `Array` | `[10, 10, 30, 10]` | | `[format]` | 坐标轴格式 | `(text: string, item: {}, index: number) => string` | - | | `[theme]` | 定制图表主题 | `string | LooseObject` | - | | `(ready)` | 当G2完成初始化后调用 | `EventEmitter` | - | --- --- title: g2-mini-area subtitle: 迷你区域图 cols: 2 type: G2 module: import { G2MiniAreaModule } from '@delon/chart/mini-area'; --- 面积图又叫区域图。 它是在折线图的基础之上形成的, 它将折线图中折线与自变量坐标轴之间的区域使用颜色或者纹理填充,这样一个填充区域我们叫做面积,颜色的填充可以更好的突出趋势信息。 ## API ### g2-mini-area | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[repaint]` | 数据再次变更时是否重绘 | `boolean` | `true` | | `[delay]` | 延迟渲染,单位:毫秒 | `number` | `0` | | `[color]` | 图表颜色 | `string` | `rgba(24, 144, 255, 0.2)` | | `[borderColor]` | 图表边颜色 | `string` | `#1890FF` | | `[height]` | 图表高度 | `number` | `56` | | `[line]` | 是否显示描边 | `boolean` | `false` | | `[animate]` | 是否显示动画 | `boolean` | `true` | | `[padding]` | 图表内部间距 | `array` | `[8, 8, 8, 8]` | | `[xAxis]` | [x 轴配置](https://www.yuque.com/antv/g2-docs/api-chart#ef1eaedc) | `object` | - | | `[yAxis]` | [y 轴配置](https://www.yuque.com/antv/g2-docs/api-chart#ef1eaedc) | `object` | - | | `[yTooltipSuffix]` | y 轴Tooltip后缀,一般指定单位 | `string` | - | | `[tooltipType]` | Tooltip显示类型 | `'mini','default'` | `'default'` | | `[data]` | 数据 | `G2MiniAreaData[]` | - | | `[theme]` | 定制图表主题 | `string | LooseObject` | - | | `(clickItem)` | 点击项回调 | `EventEmitter` | - | | `(ready)` | 当G2完成初始化后调用 | `EventEmitter` | - | ### G2MiniAreaData | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[x]` | x轴 | `any` | - | | `[y]` | y轴 | `any` | - | --- --- title: g2-mini-bar subtitle: 迷你柱状图 cols: 2 type: G2 module: import { G2MiniBarModule } from '@delon/chart/mini-bar'; --- 迷你柱状图更适合展示简单的区间数据,简洁的表现方式可以很好的减少大数据量的视觉展现压力。 ## API ### g2-mini-bar | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[repaint]` | 数据再次变更时是否重绘 | `boolean` | `true` | | `[delay]` | 延迟渲染,单位:毫秒 | `number` | `0` | | `[color]` | 图表颜色 | `string` | `#1890FF` | | `[height]` | 图表高度 | `number` | - | | `[yTooltipSuffix]` | y 轴Tooltip后缀,一般指定单位 | `string` | - | | `[tooltipType]` | Tooltip显示类型 | `'mini','default'` | `'default'` | | `[borderWidth]` | 线条粗细 | `number` | `5` | | `[padding]` | 图表内部间距 | `array` | `[8, 8, 8, 8]` | | `[data]` | 数据 | `G2MiniBarData[]` | - | | `[theme]` | 定制图表主题 | `string | LooseObject` | - | | `(clickItem)` | 点击项回调 | `EventEmitter` | - | | `(ready)` | 当G2完成初始化后调用 | `EventEmitter` | - | ### G2MiniBarData | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[x]` | x轴 | `any` | - | | `[y]` | y轴 | `any` | - | | `[color]` | 轴颜色 | `string` | - | --- --- title: g2-mini-progress subtitle: 迷你进度条 cols: 1 type: G2 module: import { G2MiniProgressModule } from '@delon/chart/mini-progress'; --- 用于显示跟速度相关图形再适合不过。 ## API ### g2-mini-progress | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[target]` | 目标比例 | `number` | - | | `[color]` | 进度条颜色 | `string` | - | | `[strokeWidth]` | 进度条高度 | `number` | - | | `[percent]` | 进度比例 | `number` | - | --- --- title: g2-pie subtitle: 饼状图 cols: 1 type: G2 module: import { G2PieModule } from '@delon/chart/pie'; --- 用于显示跟速度相关图形再适合不过。 ## API ### g2-pie | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[repaint]` | 数据再次变更时是否重绘 | `boolean` | `true` | | `[delay]` | 延迟渲染,单位:毫秒 | `number` | `0` | | `[animate]` | 是否显示动画 | `boolean` | `true` | | `[color]` | 图表颜色 | `string` | `rgba(24, 144, 255, 0.85)` | | `[height]` | 图表高度 | `number` | - | | `[hasLegend]` | 是否显示 legend | `boolean` | `false` | | `[padding]` | 图表内部间距 | `number[]` | `[12, 0, 12, 0]` | | `[percent]` | 占比 | `number` | - | | `[lineWidth]` | 边框粗细 | `number` | `0` | | `[inner]` | 内部极坐标系的半径 | `number` | `0.75` | | `[blockMaxWidth]` | 多少宽度为块显示 | `number` | `380` | | `[tooltip]` | 是否显示 tooltip | `boolean` | `true` | | `[subTitle]` | 图表子标题 | `string,TemplateRef` | - | | `[total]` | 总量 | `string,number,TemplateRef` | - | | `[valueFormat]` | y轴格式化 | `(y: number) => string` | - | | `[data]` | 数据 | `G2PieData[]` | - | | `[colors]` | 颜色列表 | `string[]` | - | | `[interaction]` | 交互类型,none 无 element-active 图形元素,active-region 图表组件,brush 框选,drag-move 移动 | `InteractionType` | `none` | | `[ratio]` | 百分比配置项 | `G2PieRatio` | `{ text: '占比', inverse: '反比', color: '', inverseColor: '#F0F2F5' }` | | `[theme]` | 定制图表主题 | `string | LooseObject` | - | | `(clickItem)` | 点击项回调 | `EventEmitter` | - | | `(ready)` | 当G2完成初始化后调用 | `EventEmitter` | - | ### G2PieData | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[x]` | x轴 | `any` | - | | `[y]` | y轴 | `number` | - | --- --- title: g2-radar subtitle: 雷达图 cols: 1 type: G2 module: import { G2RadarModule } from '@delon/chart/radar'; --- 雷达图是以相同点开始的轴上表示的三个或更多个定量变量的二维图形的形式显示多变量数据的图形方法。轴的相对位置和角度通常是不知情的。 ## API ### g2-radar | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[repaint]` | 数据再次变更时是否重绘 | `boolean` | `true` | | `[delay]` | 延迟渲染,单位:毫秒 | `number` | `0` | | `[title]` | 图表标题 | `string,TemplateRef` | - | | `[height]` | 图表高度 | `number` | - | | `[hasLegend]` | 是否显示 legend | `boolean` | `false` | | `[padding]` | 图表内部间距 | `array` | `[24, 30, 16, 30]` | | `[colors]` | 颜色列表 | `string[]` | - | | `[data]` | 数据 | `G2RadarData[]` | - | | `[theme]` | 定制图表主题 | `string | LooseObject` | - | | `(clickItem)` | 点击项回调 | `EventEmitter` | - | | `(ready)` | 当G2完成初始化后调用 | `EventEmitter` | - | ### G2RadarData | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[name]` | 名称 | `string` | - | | `[label]` | 标签 | `string` | - | | `[value]` | 值 | `number` | - | --- --- title: g2-single-bar subtitle: 单一柱状图 cols: 1 type: G2 module: import { G2SingleBarModule } from '@delon/chart/single-bar'; --- 单一柱状图更适合在列表中展示简单的区间数据,简洁的表现方式可以很好的减少大数据量的视觉展现压力。 ## API ### g2-single-bar | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[repaint]` | 数据再次变更时是否重绘 | `boolean` | `true` | | `[delay]` | 延迟渲染,单位:毫秒 | `number` | `0` | | `[plusColor]` | 图表颜色 | `string` | `#40a9ff` | | `[minusColor]` | 负值图表颜色 | `string` | `#ff4d4f` | | `[height]` | 图表高度 | `number` | `60` | | `[barSize]` | 柱状高度 | `number` | `30` | | `[min]` | 最小值 | `number` | `0` | | `[max]` | 最大值,若小于0表示显示负值 | `number` | `100` | | `[padding]` | 图表内部间距 | `any` | `0` | | `[value]` | 值 | `number` | `0` | | `[format]` | 显示值格式 | `(value: number) => string` | - | | `[textStyle]` | 显示值样式 | `any` | `{ fontSize: 12, color: '#595959' }` | | `[theme]` | 定制图表主题 | `string | LooseObject` | - | | `(ready)` | 当G2完成初始化后调用 | `EventEmitter` | - | --- --- title: g2-tag-cloud subtitle: 标签云 cols: 1 type: G2 module: import { G2TagCloudModule } from '@delon/chart/tag-cloud'; --- 标签云是一套相关的标签以及与此相应的权重展示方式,一般典型的标签云有 30 至 150 个标签,而权重影响使用的字体大小或其他视觉效果。 ## API ### g2-tag-cloud | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[repaint]` | 数据再次变更时是否重绘 | `boolean` | `true` | | `[delay]` | 延迟渲染,单位:毫秒 | `number` | `200` | | `[height]` | 高度值 | `number` | `200` | | `[width]` | 宽度值,若不指定自动按宿主元素的宽度 | `number` | `0` | | `[data]` | 数据 | `G2TagCloudData[]` | `[]` | | `[theme]` | 定制图表主题 | `string | LooseObject` | - | | `(clickItem)` | 点击项回调 | `EventEmitter` | - | | `(ready)` | 当G2完成初始化后调用 | `EventEmitter` | - | ### G2TagCloudData | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[name]` | 名称 | `string` | - | | `[value]` | 值 | `number` | - | --- --- title: g2-timeline subtitle: 折线图 cols: 1 type: G2 module: import { G2TimelineModule } from '@delon/chart/timeline'; --- 使用 `timeline` 组件可以实现带有时间轴的柱状图展现,而其中的 `x` 属性,则是时间值的指向,默认最多支持同时展现两个指标,分别是 `y1` 和 `y2`。 ## API ### g2-timeline | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[repaint]` | 数据再次变更时是否重绘 | `boolean` | `true` | | `[delay]` | 延迟渲染,单位:毫秒 | `number` | `0` | | `[title]` | 图表标题 | `string,TemplateRef` | - | | `[maxAxis]` | 最大指标数量 | `number` | `2` | | `[data]` | 数据,注:根据 `maxAxis` 值传递指标数据 | `G2TimelineData[]` | - | | `[titleMap]` | 指标别名 | `G2TimelineMap` | - | | `[colorMap]` | 颜色 | `G2TimelineMap` | `{ y1: '#5B8FF9', y2: '#5AD8A6', y3: '#5D7092', y4: '#F6BD16', y5: '#E86452' }` | | `[height]` | 高度值 | `number` | `400` | | `[padding]` | 图表内部间距 | `number[]` | `[40, 8, 64, 40]` | | `[borderWidth]` | 线条 | `number` | `2` | | `[mask]` | 日期格式,使用 [G2 Mask日期格式](https://g2.antv.vision/zh/docs/manual/tutorial/scale#time) | `string` | `HH:mm` | | `[maskSlider]` | 滑动条日期格式,使用 [date-fns 日期格式](https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table) | `string` | `HH:mm` | | `[position]` | 标题位置 | `'top','right','bottom','left'` | `'top'` | | `[slider]` | 是否需要滑动条 | `boolean` | `true` | | `[theme]` | 定制图表主题 | `string | LooseObject` | - | | `(clickItem)` | 点击项回调 | `EventEmitter` | - | | `(ready)` | 当G2完成初始化后调用 | `EventEmitter` | - | ### G2TimelineData | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[time]` | 日期格式 | `Date | number` | - | | `[y1]` | 指标1数据 | `number` | - | | `[y2]` | 指标2数据 | `number` | - | | `[y3]` | 指标3数据 | `number` | - | | `[y4]` | 指标4数据 | `number` | - | | `[y5]` | 指标5数据 | `number` | - | ### G2TimelineMap | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[y1]` | 指标1 | `string` | - | | `[y2]` | 指标2 | `string` | - | | `[y3]` | 指标3 | `string` | - | | `[y4]` | 指标4 | `string` | - | | `[y5]` | 指标5 | `string` | - | --- --- title: g2-water-wave subtitle: 水波图 cols: 1 type: G2 module: import { G2WaterWaveModule } from '@delon/chart/water-wave'; --- 水波图是一种比例的展示方式,可以更直观的展示关键值的占比。 > 默认不支持父元素自适应,可以监听父元素的变化并使用 `render()` 方法来重置渲染。 ## API ### g2-water-wave | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[animate]` | 是否显示动画 | `boolean` | `true` | | `[delay]` | 延迟渲染,单位:毫秒 | `number` | `0` | | `[title]` | 图表标题 | `string,TemplateRef` | - | | `[height]` | 图表高度 | `number` | `160` | | `[color]` | 图表颜色 | `string` | `#1890FF` | | `[percent]` | 进度比例 | `number` | - | --- --- type: Layout title: global-footer subtitle: 全局页脚 cols: 1 module: import { GlobalFooterModule } from '@delon/abc/global-footer'; --- 页脚属于全局导航的一部分,作为对顶部导航的补充,通过传递数据控制展示内容。 ## API ### global-footer | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[links]` | 链接数据 | `GlobalFooterLink` | - | | `[copyright]` | 版权信息 | `TemplateRef` | - | ### global-footer-item | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `ng-content` | 标题 | `string` | - | | `[href]` | 路由链接 | `string` | - | | `[blankTarget]` | 是否打开新窗口 | `boolean` | `false` | ### GlobalFooterLink | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[title]` | 标题 | `string` | - | | `[href]` | 路由链接 | `string` | - | | `[blankTarget]` | 是否打开新窗口 | `boolean` | `false` | --- --- type: Basic order: 1 title: hotkey subtitle: 热键 cols: 2 module: import { HotkeyModule } from '@delon/abc/hotkey'; --- 基于 [@github/hotke](https://github.com/github/hotkey) 热键库。 > 如果不清楚热键值,可通过[热键代码](https://github.github.com/hotkey/hotkey_mapper.html)来获取。 ## API ### [hotkey] | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `hotkey` | 指定[热键格式](https://github.com/github/hotkey#hotkey-string-format) | `string` | - | --- --- title: i18n subtitle: 国际化 type: Examples --- JSON Schema 本身只是一个 JSON 对象,因此本质上已经是支持国际化。此外,`sf` 还支持一些比较快捷的国际化方式,但它支持的元素比较基础:`title`、`description`、`optionalHelp` 三个元素。 --- --- order: 1 title: keys subtitle: 可迭代对象 type: Pipe --- `keys` 将对象数组化。 例如: ```typescript const data = { name: 'cipchk', address: { city: 'shanghai', district: 'changning' } }; ``` 变成可迭代对象: ```html @for (item of data | keys; track $index) {
    {{item.key}} {{item.value | json}}
    } ``` **字典可迭代** ```typescript const data = { 1: '正常', 2: '删除' }; ``` 若希望保持键名为 `number` 数字型: ``` @for (item of data | keys: true; track $index) {
    {{item.key}} {{item.value | json}}
    } ``` > Angular `6.1.0` 以后原生支持 [KeyValuePipe](https://angular.io/api/common/KeyValuePipe),用法有点类似。 --- --- type: Basic order: 2 title: loading subtitle: 加载指示符 cols: 2 module: import { LoadingModule } from '@delon/abc/loading'; --- 全局加载指示符,一般用于某个操作需要中断用户操作。 ## API ### LoadingService | 名称 | 说明 | |----|----| | `open(options?: LoadingShowOptions)` | 打开一个新加载指示符 | | `close()` | 关闭一个加载指示符 | ### LoadingShowOptions | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `type` | 显示类型 | `LoadingType` | `spin` | ✅ | | `text` | 描述文案 | `string` | `加载中...` | ✅ | | `icon` | 类型为 `icon` 的配置项 | `LoadingIcon` | - | ✅ | | `custom` | 类型为 `custom` 的配置项 | `LoadingCustom` | - | ✅ | | `delay` | 延迟显示加载效果的时间(防止闪烁),单位:毫秒 | `number` | - | ✅ | --- --- type: Basic order: 4 title: lodop subtitle: Lodop打印 cols: 1 module: import { LodopModule } from '@delon/abc/lodop'; --- [Lodop](http://c-lodop.com/) 打印的基础实现,商用需要购买KEY。(同时感谢 lodop 为 ng-alain 免费提供正版KEY) > 运行示例的**前提条件**必须安装[Lodop](http://c-lodop.com/download.html)。 ## API ### LodopService | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `cog` | 获取或设置配置项 | `AlainLodopConfig` | - | | `events` | 打印过程通知 | `Observable` | - | | `lodop` | 获取 Lodop 对象 | `Observable` | - | **Lodop加载成功后辅助API** | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `printer` | 获取打印机列表 | `string[]` | - | | `attachCode()` | 附加代码至 `lodop` 对象上 | `void` | - | | `design()` | 运行打印设计手动关闭后返回代码 | `Promise` | - | | `print()` | 立即打印,一般用于批量套打 | `void` | - | ### LodopPrintResult | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `ok` | 是否打印成功 | `boolean` | - | | `error` | 错误信息 | `string` | - | | `code` | 代码 | `string` | - | | `item` | 动态参数上下文对象 | `any` | - | | `parser` | 代码解析表达式 | `RegExp` | - | ### LodopResult | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `ok` | 表示是否加载成功 | `boolean` | - | | `status` | 状态码 | `string` | - | | `lodop` | Lodop 对象 | `Lodop` | - | | `error` | 错误明细 | `any` | - | ### AlainLodopConfig | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `companyName` | 注册单位名称 | `string` | - | ✅ | | `license` | 主注册号 | `string` | - | ✅ | | `licenseA` | 附加注册号A | `string` | - | ✅ | | `licenseB` | 附加注册号B | `string` | - | ✅ | | `url` | Lodop 远程脚本URL地址,**注意**务必使用 `name` 属性指定变量值 | `string` | `//localhost:8443/Lodopfuncs.js` | ✅ | | `name` | Lodop 变量名 | `string` | `LODOP` | ✅ | | `checkMaxCount` | 检查次数,默认 `100`,当检查超过时视为异常,这是因为 Lodop 需要连接 WebSocket | `number` | `100` | ✅ | --- --- title: math subtitle: 范围、四舍五入 type: Tools --- ## inRange 检查 `value` 是否在 `start` 与 `end` 之间,但不包括 `end`。 如果 `end` 没有指定,那么 `start` 设置为 `0`。 如果 `start` 大于 `end`,那么参数会交换以便支持负范围。 ```ts inRange(3, 2, 4); // true inRange(4, 8); // true inRange(4, 2); // false inRange(2, 2); // false inRange(1.2, 2); // true inRange(-3, -2, -6); // true ``` ## ceil 根据 `precision`(精度) 向上舍入 `number`。 ```ts ceil(4.006); // 5 ceil(6.004, 2); // 6.01 ceil(6040, -2); // 6100 ``` ## floor 根据 `precision`(精度) 向下舍入 `number`。 ```ts floor(4.006); // 4 floor(0.046, 2); // 0.04 floor(4060, -2); // 4000 ``` ## round 根据 `precision`(精度) 四舍五入 `number`。 ```ts round(4.006); // 4 round(4.006, 2); // 4.01 round(4060, -2); // 4100 ``` --- --- type: Basic order: 1 title: media subtitle: HTML5媒体 cols: 2 module: import { MediaModule } from '@delon/abc/media'; --- 基于 [plyr](https://github.com/sampotts/plyr) HTML5播放器。 ## 依赖 由于 plyr 脚本大小以及对视频播放并不是刚需的原因,因此采用一种延迟加载脚本的形式,可以通过[全局配置](/docs/global-config)配置来改变默认 CDN 路径,默认情况下使用 `https://cdn.jsdelivr.net/npm/plyr/dist/plyr.min.js`、`https://cdn.jsdelivr.net/npm/plyr/dist/plyr.css`。 **使用本地路径** ```json // angular.json { "glob": "**/{plyr.min.js,plyr.css,plyr.svg}", "input": "./node_modules/plyr/dist", "output": "assets/plyr/" } ``` ```ts // global-config.module.ts const alainConfig: AlainConfig = { media: { urls: ['assets/plyr/plyr.min.js', 'assets/plyr/plyr.css'], options: { iconUrl: 'assets/plyr/plyr.svg', blankVideo: 'https://cdn.plyr.io/static/blank.mp4' } } }; ``` ## API ### [media] | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[type]` | 媒体类型 | `audio, video` | `video` | - | | `[source]` | 媒体数据源 | `string, PlyrMediaSource` | - | - | | `[options]` | 播放器参数 [plyr options](https://github.com/sampotts/plyr#options) | `any` | - | ✅ | | `[delay]` | 延迟初始化,单位:毫秒 | `number` | - | - | | `(ready)` | 准备就绪回调 | `EventEmitter` | - | - | ## FAQ ### 如何更友好的使用 Plyr 类型 ```ts import type * as Plyr from 'plyr'; ``` --- --- title: mention subtitle: 提及 type: Non-built-in widgets --- 提及组件。 ## 如何使用 非内置模块,需要额外在 [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9) 注册 `withMentionWidget`。 ## 注意事项 - 若数据中不包括 `label` 属性,则**务必**指定 `valueWith` 参数。 ## 数据源说明 **静态** 指一次性获取数据,数据来源于 `asyncData`、`enum`。 **实时** 指每一次选择会触发HTTP请求,数据来源于 `loadData`。 ## API ### schema 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[enum]` | 静态数据源 | `SFSchemaEnumType[]` | - | | `[readOnly]` | 禁用状态 | `boolean` | - | | `[minimum]` | 最少提及次数 | `number` | - | | `[maximum]` | 最多提及次数 | `number` | - | ### ui 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[asyncData]` | 异步静态数据源 | `(input: string) => Observable` | - | | `[size]` | 大小,等同 `nzSize` | `string` | - | | `[placeholder]` | 在文字框中显示提示讯息 | `string` | - | | `[loadData]` | 实时数据 | `(option: MentionOnSearchTypes) => Observable` | - | | `[notFoundContent]` | 未找到时的内容 | `string` | `无匹配结果,轻敲空格完成输入` | | `[placement]` | 建议框位置 | `button,top` | `button` | | `[prefix]` | 触发弹出下拉框的字符 | `'string'` `'string[]'` | `@` | | `[valueWith]` | 建议选项的取值方法 | `(value: any) => string` | - | | `[select]` | 下拉框选择建议时回调 | `(value: any) => void` | - | | `[inputStyle]` | 文本框类型 | `text, textarea` | `text` | | `[autosize]` | 自适应内容高度,可设置对象:`{ minRows: 2, maxRows: 6 }` | `{ minRows?: number; maxRows?: number }` | `{ minRows: 1, maxRows: 0 }` | --- --- order: 2 title: MenuService subtitle: 菜单服务 type: Service --- 菜单服务的数据格式是一个 [Menu](https://github.com/ng-alain/delon/blob/master/packages/theme/src/services/menu/interface.ts) 数组,其中 `text` 属性表示菜单文本为必填项,而且本身并不受外部组件的影响(例如[sidebar-nav](/components/sidebar-nav)组件),这是因为菜单是贯穿整个项目必不可少的组成部分,而将其独立成一个服务可以更有效被使用,例如:动态生成导航、标题等。 **建议:** 在 Angular 启动服务([startup.service.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/startup/startup.service.ts))从远程获取到菜单数据后,调用 `add()` 方法。 ## API ### MenuService | 方法 | 描述 | |----|----| | `add` | 设置菜单数据 | | `clear` | 清空菜单数据 | | `resume` | 重置菜单,可能I18N、用户权限变动时需要调用刷新 | | `find` | 利用 `url` 或 `key` 查找菜单 | | `getItem` | 根据 `key` 获取菜单 | | `getPathByUrl` | 根据url获取菜单列表 | | `setItem` | 设置菜单值 | | `open` | 展开某菜单 | | `toggleOpen` | 切换菜单的展开或关闭 | | `openAll` | 展开或关闭所有菜单 | | `getDefaultRedirect` | 返回一个默认跳转的菜单链接 | **recursive** 表示自动向上递归查找,例如菜单数据源包含 `/ware`,则 `/ware/1` 也视为 `/ware` 项。 ### Menu | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `render_type` | 菜单项的渲染类型 | `item, divider` | - | | `text` | 文本(支持HTML),**必填项** | `string` | - | | `i18n` | i18n主键(支持HTML) | `string` | - | | `group` | 是否显示分组名,指[示例](//ng-alain.github.io/ng-alain/)中的【主导航】字样 | `boolean` | `true` | | `link` | 路由,`link`、`externalLink` 二选其一 | `string` | - | | `externalLink` | 外部链接,`link`、`externalLink` 二选其一 | `string` | - | | `target` | 链接 target | `_blank,_self,_parent,_top` | - | | `icon` | 图标,指[示例](//ng-alain.github.io/ng-alain/)中的【仪表盘】前图标,只对一级菜单有效 | `string | MenuIcon` | - | | `badge` | 徽标数,展示的数字,指[示例](//ng-alain.github.io/ng-alain/)中的【小部件】后的红色块。(注:`group:true` 时无效) | `number` | - | | `badgeDot` | 徽标数,显示小红点 | `boolean` | - | | `badgeStatus` | 徽标 Badge [颜色](https://ng.ant.design/components/badge/en#nz-badge) | `success,processing,default,error,warning` | `error` | | `badgeOverflowCount` | 徽标数值显示上限,超过显示 `${badgeOverflowCount}+` | `number` | - | | `open` | 是否打开菜单 | `boolean` | `false` | | `disabled` | 是否禁用菜单 | `boolean` | `false` | | `hide` | 是否隐藏菜单 | `boolean` | `false` | | `hideInBreadcrumb` | 隐藏面包屑,指 `page-header` 组件的自动生成面包屑时有效 | `boolean` | - | | `acl` | ACL配置,若导入 `@delon/acl` 时自动有效,等同于 `ACLService.can(roleOrAbility: ACLCanType)` 参数值 | `any` | - | | `shortcut` | 是否快捷菜单项 | `boolean` | - | | `shortcutRoot` | 快捷菜单根节点 | `boolean` | - | | `reuse` | 是否允许复用,需配合 `reuse-tab` 组件 | `boolean` | - | | `key` | 菜单项唯一标识符,可用于 `getItem`、`setItem` 来更新某个菜单 | `string` | - | | `children` | 子菜单 | `Menu[]` | - | ### MenuIcon | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[type]` | 类型;`img`, `svg` 类型需要 `14px` 宽与高尺寸 | `class,icon,iconfont,img,svg` | `icon` | | `[value]` | 值,包含:类名、图标 `nzType`、图像 | `string` | - | | `[theme]` | 图标主题风格 | `outline,twotone,fill` | `outline` | | `[spin]` | 是否有旋转动画 | `boolean` | `false` | | `[twoToneColor]` | 仅适用双色图标,设置双色图标的主要颜色,仅对当前 icon 生效 | `string` | - | | `[iconfont]` | 指定来自 IconFont 的图标类型 | `string` | - | | `[rotate]` | 图标旋转角度 | `number` | - | > 使用 `iconfont` 类型必须先加载 `NzIconService.fetchFromIconfont`,建议在根模块中执行。 --- --- title: method subtitle: 内置方法 type: Examples --- `SFComponent` 提供一些快捷方法,例如:`setValue`、`setDisabled`、`setRequired` 等 --- --- title: modal subtitle: 模态框 type: Examples --- 在模态框里使用表单是一种非常常见场景,其实 `ng g ng-alain:edit edit` 的时候会得到一个完整示例;会得到这样的一个HTML模板: ```html ``` `.modal-footer` 已经非常友好的融合了自定义动态框。 --- --- order: 3 title: ModalHelper subtitle: 对话框辅助类 type: Service --- 基于 `NzModalService` 封装,它解决一些已知问题: - 更友好的处理回调 - 按钮默认焦点 - 响应式宽度 ## 用法 ```ts this.modalHelper.create(FormEditComponent, { i }).subscribe(res => this.load()); // 成功范例,其中 `nzModalRef` 指目标组件在构造函数 `NzModalRef` 变量名 // 1. 视为成功 this.nzModalRef.close(true); this.nzModalRef.close({ other: 1 }); // 2. 视为失败 this.nzModalRef.close(); // 关闭: this.nzModalRef.destroy(); ``` 包括两个方法体 `create` & `createStatic` 分别打开普通&静态对话框。在 `NzModalService` 基础上新增若干参数。 **自定义组件HTML模板** ```html Your body content ``` ### API | 名称 | 类型 | 默认值 | 描述 | | --- | --- | --- | --- | | `size` | 指定对话框大小 | `sm,md,lg,xl,number,string` | `lg` | | `exact` | 是否精准(默认:`true`),若返回值非空值(`null`或`undefined`)视为成功,否则视为错误 | `boolean` | `true` | | `includeTabs` | 是否包裹标签页 | `boolean` | `false` | | `drag` | 支持拖动 | `boolean, ModalHelperDragOptions` | - | | `useNzData` | 是否强制使用 `nzData` 传递参数,若为 `false` 表示参数会直接映射到组件实例中,其他值只能通过 `NZ_MODAL_DATA` 的方式来获取参数 | `boolean` | `false` | | `modalOptions` | 对话框 [ModalOptions](https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/components/modal/modal-types.ts) 参数 | `ModalOptions` | - | --- --- title: monaco-editor subtitle: Monaco Editor type: Third Widgets --- Markdown编辑器。 ## 如何使用 **安装依赖** `yarn add @ng-util/monaco-editor` - 1、在 `app.config.ts` 下注册 `provideNuMonacoEditorConfig()` - 2、在 [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9) 注册 `withMonacoEditorWidget`。 > 关于更多 Monaco Editor 配置请参考 [@ng-util/monaco-editor](https://github.com/ng-util/ng-util/blob/master/packages/monaco-editor/README.md#usage)。 ## API ### ui 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[options]` | 配置项说明,[见官网](https://microsoft.github.io/monaco-editor/docs.html) | `monaco.editor.IStandaloneEditorConstructionOptions` | - | | `[delay]` | 延迟加载时间 | `number` | - | | `[change]` | 编辑器内容发生改变时会触发该事件 | `(value: string) => void` | - | | `[height]` | Height of monaco editor | `string` | `200px` | | `[model]` | Model of monaco editor | `NuMonacoEditorModel` | - | | `(event)` | Event callback | `EventEmitter` | - | --- --- type: Business title: notice-icon subtitle: 通知菜单 cols: 1 module: import { NoticeIconModule } from '@delon/abc/notice-icon'; --- 用在导航工具栏上,作为整个产品统一的通知中心。 ## API ### notice-icon | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[data]` | 数据 | `NoticeItem[]` | - | | `[count]` | 图标上的消息总数 | `number` | - | | `[loading]` | 弹出卡片加载状态 | `boolean` | `false` | | `[centered]` | 标签页是否居中 | `boolean` | `false` | | `(select)` | 点击列表项的回调 | `EventEmitter` | - | | `(clear)` | 点击清空按钮的回调 | `EventEmitter` | - | | `[popoverVisible]` | 手动控制Popover显示 | `boolean` | `false` | | `(popoverVisibleChange)` | Popover显隐回调 | `EventEmitter` | - | | `[btnClass]` | 按钮类 | `ngClass` | - | | `[btnIconClass]` | Icon图标类 | `ngClass` | - | ### NoticeItem | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[title]` | 标题 | `string` | - | | `[list]` | 列表数据,格式参照下表 | `NoticeIconList[]` | - | | `[emptyText]` | 针对每个 Tab 定制空数据文案 | `string | TemplateRef` | `无通知` | | `[emptyImage]` | 针对每个 Tab 定制空数据图片 | `string` | - | | `[clearText]` | 针对每个 Tab 清空按钮文本 | `string` | `清空` | ### NoticeIconList | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[avatar]` | 头像图片链接 | `string` | - | | `[title]` | 标题 | `string | TemplateRef<{ $implicit: NoticeIconList }>` | - | | `[description]` | 描述信息 | `string | TemplateRef<{ $implicit: NoticeIconList }>` | - | | `[datetime]` | 时间戳 | `string` | - | | `[extra]` | 额外信息,在列表项右上角 | `string` | - | | `[read]` | 是否已读状态 | `boolean` | - | ### NoticeIconSelect | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[title]` | 标题 | `string` | - | | `[item]` | 数据项 | `NoticeItem` | - | | `[event]` | 点击事件 | `Event` | - | --- --- title: number subtitle: 数字 type: Widgets order: 5 --- 通过鼠标或键盘,输入范围内的数值 ## 注意事项 - 若 `type="integer"` 会**强制**移除 `minimum`、`maximum`、`multipleOf` 参数的小数部分。 ## API ### schema 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[minimum]` | 最小值 | `number` | - | | `[exclusiveMinimum]` | 约束是否包括 `minimum` 值,`true` 表示排除 `minimum` 值 | `boolean` | - | | `[maximum]` | 最大值 | `number` | - | | `[exclusiveMaximum]` | 约束是否包括 `maximum` 值,`true` 表示排除 `maximum` 值 | `boolean` | - | | `[multipleOf]` | 倍数 | `number` | `1` | ### ui 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[prefix]` | 前缀,简化 `nzFormatter`、`nzParser` 的使用 | - | - | | `[unit]` | 单位,简化 `nzFormatter`、`nzParser` 的使用 | - | - | | `[formatter]` | 等同 `nzFormatter` | - | - | | `[parser]` | 等同 `nzParser` | - | - | | `[precision]` | 等同 `nzPrecision` | - | - | | `[widgetWidth]` | 指定 `nz-number` 宽度 | `number, string` | `90` | | `[hideStep]` | 隐藏步数操作区 | `boolean` | `false` | | `[change]` | 变更事件 | `(val?: number) => void` | - | ## QA ### 为什么 `unit` 无法变更 NG-ZORRO 所有组件都是 OnPush 模式,一种特殊情况是当需要动态修改 `unit` 时,由于需要触发一次 `ngModel` 变更时才会生效,因此需要使其值发生变更,例如: ```ts const ageProperty = this.sf.getProperty('/age')!; ageProperty.widget.ui.unit = 'c'; ageProperty.widget.setValue(null); ageProperty.widget.setValue(statusProperty.value); ``` --- --- type: G2 title: number-info subtitle: 数据文本 cols: 2 module: import { NumberInfoModule } from '@delon/chart/number-info'; --- 常用在数据卡片中,用于突出展示某个业务数据。 ## API ### number-info | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[title]` | 标题 | `TemplateRef` | - | | `[subTitle]` | 子标题 | `TemplateRef` | - | | `[total]` | 总量 | `string, number` | - | | `[suffix]` | 总量后缀 | `string` | - | | `[subTotal]` | 子总量 | `string, number` | - | | `[status]` | 增加状态 | `'up','down'` | - | | `[theme]` | 状态样式 | `'light','default'` | `'light'` | | `[gap]` | 设置数字和描述直接的间距(像素) | `number` | 8 | --- --- title: object subtitle: 对象 type: Widgets order: 1 --- 创建对象,只对 `schema.type="object"` 时有效。 ## API ### schema 属性 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[properties]` | 定义对象的属性 | `{ [key: string]: SFSchema }` | - | | `[required]` | 必填项属性 | `string[]` | - | | `[maxProperties]` | 最大属性个数,必须是非负整数 | `number` | - | | `[minProperties]` | 最小属性个数,必须是非负整数 | `number` | - | ### ui 属性 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[showExpand]` | 是否显示扩展,点击隐藏内容,限 `type === 'card'` | `boolean` | `true` | | `[expand]` | 展开状态,限 `type === 'card'` | `boolean` | `true` | | `[showTitle]` | 是否显示标题 | `boolean` | `false` | | `[type]` | 渲染类型 | `card, default` | `default` | | `[cardSize]` | 等同 `nzSize` 属性 | `small, default` | `small` | | `[cardBodyStyle]` | 等同 `nzBodyStyle` 属性 | `{ [key: string]: string }` | - | | `[cardBordered]` | 等同 `nzBordered` 属性 | `boolean` | `false` | | `[cardExtra]` | 等同 `nzExtra` 属性 | `string, TemplateRef` | - | | `[cardActions]` | 等同 `nzActions` 属性 | `Array>` | - | --- --- type: Basic title: Observers subtitle: 观察者 order: 7 module: import { ObserversModule } from '@delon/abc/observers'; --- `ObserversModule` 包提供了基于原生 Web 平台的观察者 API(比如 `MutationObserver`) 的便捷指令。 ## API ### [observeSize] 观察DOM大小变化。 | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `(observeSize)` | 事件 | `MutationRecord[]` | - | | --- --- type: Basic title: onboarding subtitle: 用户引导流程 order: 7 module: import { OnboardingModule } from '@delon/abc/onboarding'; --- 用户引导流程,是帮助用户更好的理解和使用产品。 ## API ### OnboardingService 组件只支持使用 `OnboardingService` 服务来构建。 | 成员 | 说明 | |----|----| | `start(data: OnboardingData)` | 开启新的用户引导流程 | | `next()` | 下一项 | | `prev()` | 上一项 | | `done()` | 完成 | ### OnboardingData | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[key]` | 存储标识Key;默认 `localStorage` 本地存储,允许使用 `ONBOARDING_STORE_TOKEN` 变更存储方式 | `string` | - | | `[keyVersion]` | 当前版本 | `unknown` | - | | `[items]` | 引导项列表 | `OnboardingItem[]` | `[]` | | `[mask]` | 是否展示遮罩 | `boolean` | `true` | | `[maskClosable]` | 点击蒙层是否允许关闭 | `boolean` | `true` | | `[showTotal]` | 是否显示总量 | `boolean` | `false` | ### OnboardingItem | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[selectors]` | CSS选项项,用于查找目标元素 | `string` | - | | `[position]` | 引导面板显示位置 | `top, left, right, bottom, topLeft, topRight, bottomLeft, bottomRight, leftTop, leftBottom, rightTop, rightBottom` | `bottomLeft` | | `[className]` | 引导面板类名 | `string` | - | | `[width]` | 引导面板宽度 | `number` | - | | `[title]` | 引导面板标题 | `string, TemplateRef` | - | | `[content]` | 引导面板内容 | `string, SafeHtml, TemplateRef` | - | | `[skip]` | 跳过按钮文本,`null` 表示强击不显示 | `string, TemplateRef, null` | `跳过` | | `[prev]` | 跳过按钮文本,`null` 表示强击不显示 | `string, TemplateRef, null` | `上一项` | | `[next]` | 跳过按钮文本,`null` 表示强击不显示 | `string, TemplateRef, null` | `下一项` | | `[done]` | 跳过按钮文本,`null` 表示强击不显示 | `string, TemplateRef, null` | `完成` | | `[url]` | 目标路由页 | `string` | - | | `[before]` | 当触发 `next` 时表示进入前回调,`number` 表示延迟 | `Observable, number` | - | | `[after]` | 当触发 `prev` 时表示进入前回调,`number` 表示延迟 | `Observable, number` | - | --- --- title: other subtitle: 深获取、拷贝、合并、延迟、断言 type: Tools --- ## omit 忽略 `obj` 指定属性。 ```ts omit({ a: 1, b: 2, c: 3 }, 'a') // { b: 2, c: 3 } omit({ a: 1, b: 2, c: 3 }, ['a', 'c']) // { b: 2 } omit({ a: 1, b: 2 }, key => key === 'a') // { a: 1 } ``` ## deepGet 类似 `_.get`,根据 `path` 获取安全值。 ```ts const obj = { id: 1, user: { name: 'cipchk', age: 18 } }; deepGet(obj, 'id'); // 1 deepGet(obj, 'user.age'); // 18 ``` ## deepCopy 基于 [extend](https://github.com/justmoon/node-extend) 的深度拷贝。 ```ts const source = { a: 1, user: { name: 'cipchk' } }; const obj = deepCopy(source); ``` ## deepMerge 深度合并。 ```ts let original = { a: 1, b: { c: 'c' } }; deepMerge(original, { b: { d: 'd' }, arr: [ 1 ] }); // output: { a: 1, b: { c: 'c', d: 'd' }, arr: [ 1 ] } ``` ## LazyService `LazyService` 用于延迟加载 JS 或 CSS 文件,对于无须被打包 script.js 且又是第三方类库比较大时,非常有用,一个简单的用法,例如: ```ts import { LazyService } from '@delon/util/other'; export class AppComponent { constructor(private lazy: LazyService) {} ngOnInit() { this.lazy.load([ `https://cdn.jsdelivr.net/npm/xlsx/dist/xlsx.full.min.js` ]).then(() => { // do something }); } } ``` ## assert 断言表达式是否符合预期,并在开发模式下会在控制台抛出一个错误。 - `assert` 断言表达式是否符合预期 - `assertEmpty` 断言是否空值(`null` 或 `undefined`) - `assertNumber` 断言是否 `number` 类型 - `assertString` 断言是否 `string` 类型 - `assertArray` 断言是否 `array` 类型 - `assertObservable` 断言是否 `Observable` 类型 --- --- type: Layout title: page-header subtitle: 页头 cols: 1 module: import { PageHeaderModule } from '@delon/abc/page-header'; --- 页头用来声明页面的主题,包含了用户所关注的最重要的信息,使用户可以快速理解当前页面是什么以及它的功能。 ## API ### page-header | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[title]` | 标题名 | `string,TemplateRef` | - | ✅ | | `[titleSub]` | 子标题 | `string,TemplateRef` | - | ✅ | | `[autoTitle]` | 是否自动生成标题,以当前路由从主菜单中定位 | `boolean` | `true` | ✅ | | `[syncTitle]` | 是否自动将标题同步至 `TitleService`、`ReuseService` 下,仅 `title` 为 `string` 类型时有效 | `boolean` | `true` | ✅ | | `[home]` | 首页文本,若指定空表示不显示 | `string` | `首页` | ✅ | | `[homeLink]` | 首页链接 | `string` | `/` | ✅ | | `[homeI18n]` | 首页链接国际化参数 | `string` | - | ✅ | | `[autoBreadcrumb]` | 是否自动生成导航,以当前路由从主菜单中定位 | `boolean` | `true` | ✅ | | `[recursiveBreadcrumb]` | 是否自动向上递归查找,菜单数据源包含 `/ware`,则 `/ware/1` 也视为 `/ware` 项 | `boolean` | `false` | ✅ | | `[loading]` | 是否加载中 | `boolean` | `false` | - | | `[wide]` | 是否定宽 | `boolean` | `false` | - | | `[fixed]` | 是否固定模式 | `boolean` | `false` | ✅ | | `[fixedOffsetTop]` | 固定偏移值 | `number` | `64` | ✅ | | `[breadcrumb]` | 自定义导航区域 | `TemplateRef` | - | - | | `[logo]` | 自定义LOGO区域 | `TemplateRef` | - | - | | `[action]` | 自定义操作区域 | `TemplateRef` | - | - | | `[content]` | 自定义内容区域 | `TemplateRef` | - | - | | `[extra]` | 自定义额外信息区域 | `TemplateRef` | - | - | | `[tab]` | 自定义标签区域 | `TemplateRef` | - | - | ## 常见问题 ### 自动生成导航 默认情况下会根据菜单数据自动生成导航,有时你可能希望隐藏某个节点菜单数据时,可以指定菜单的 `hideInBreadcrumb: true`。 ### 固定模式 固定模式在滚动过程中会覆盖 `reuse-tab` 组件。 --- --- type: Basic order: 3 title: pdf subtitle: Pdf cols: 1 module: import { PdfModule } from '@delon/abc/pdf'; --- 基于 [pdf.js](https://mozilla.github.io/pdf.js/) 的PDF预览组件。 默认PDF预览并不是刚需的原因,因此采用一种延迟加载脚本的形式,可以通过[全局配置](/docs/global-config)配置来改变默认 pdf.js 类库的根路径。 > 组件也是受 [ng2-pdf-viewer](https://github.com/VadimDez/ng2-pdf-viewer) 启发。 **使用本地路径** ```bash npm i -S pdfjs-dist ``` ```json // angular.json { "glob": "{build,web}/**", "input": "./node_modules/pdfjs-dist/", "ignore": ["*.js.map", "*.d.ts"], "output": "assets/pdfjs/" } ``` ```ts // global-config.module.ts const alainConfig: AlainConfig = { pdf: { lib: '/assets/pdfjs/' } }; ``` ## API ### pdf | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[src]` | 指定文档路径 | `string, object, UInt8Array` | - | - | | `[pi]` | 当前页 | `number` | `1` | - | | `[showAll]` | 是否显示全部页 | `boolean` | `true` | ✅ | | `[renderText]` | 是否启用文字层,开启后允许文字选择 | `boolean` | `true` | ✅ | | `[textLayerMode]` | 文字层渲染模式 | `PdfTextLayerMode` | `ENABLE` | - | | `[showBorders]` | 是否显示页面边框 | `boolean` | `false` | ✅ | | `[stickToPage]` | 是否视野保持在 `pi` 页上 | `boolean` | `false` | - | | `[originalSize]` | 控制文档显示大小,`true` 按原始大小,`false` 按容器大小 | `boolean` | `true` | ✅ | | `[fitToPage]` | 控制原始尺寸不会超出容器大小 | `boolean` | `false` | ✅ | | `[zoom]` | 控制缩放文档 | `number` | `1` | - | | `[zoomScale]` | 缩放文档的计算方式 | `PdfZoomScale` | `page-width` | - | | `[rotation]` | 旋转文档 | `number` | `0` | - | | `[autoReSize]` | 是否自动缩放 | `boolean` | `true` | ✅ | | `[externalLinkTarget]` | 外部链接打开形式 | `PdfExternalLinkTarget` | `BLANK` | - | | `[delay]` | 延迟初始化,单位:毫秒 | `number` | - | - | | `(change)` | 变更时回调 | `EventEmitter` | - | - | ### 组件属性 提供一些常用的API接口。 | 名称 | 说明 | | --- | ---- | | `pdf` | 当前 PDF 实例 | | `eventBus` | PDF 文件的事件总线,例如:查找文档等 | | `findController` | 查找控制器,不够新版本已经被 `eventBus` 替代 | | `pageViewer` | 查看控件 | | `linkService` | 导航服务 | ## 常见问题 ### 为什么有时需要指定高度 当启用显示全部页时,如果要让页码的控制产生有效,需要确保组件的高度是一个有效值。 --- --- order: 1 title: PreloadOptionalModules type: Router --- 可选预加载模块,当需要对某些懒加载在第一次页面加载时也一并加载该资源时。例如 `order` 模块默认情况下必须第一次访问 `/order` 路由时才会真正的开始下载资源文件,当通过使用 `PreloadOptionalModules` 并指定 `preload: true` 时,会在 Angular 项目启动后自动下载资源文件。 ```ts @NgModule({ providers: [PreloadOptionalModules], imports: [ RouterModule.forRoot([ { path: 'order', loadChildren: () => import('./order/order.module').then(m => m.OrderModule), data: { preload: true } }, ], { preloadingStrategy: PreloadOptionalModules })] }) ``` --- --- title: qr-code subtitle: 二维码 type: Non-built-in widgets --- 当需要将链接转换成为二维码时使用。 ## 如何使用 非内置模块,需要额外在 [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9) 注册 `withQrCodeWidget`。 ## API ### ui 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[color]` | 二维码颜色 | `string` | `#000` | | `[bgColor]` | 二维码背景颜色 | `string` | `#FFFFFF` | | `[qrSize]` | 二维码大小 | `number` | `160` | | `[padding]` | 二维码填充 | `number \| number[]` | `0` | | `[icon]` | 二维码中 icon 地址 | `string` | - | | `[iconSize]` | 二维码中 icon 大小 | `number` | `40` | | `[bordered]` | 是否有边框 | `boolean` | `true` | | `[status]` | 二维码状态 | `'active'|'expired' |'loading'` | `active` | | `[level]` | 二维码容错等级 | `'L'|'M'|'Q'|'H'` | `M` | | `(refresh)` | 点击"点击刷新"的回调 | `EventEmitter` | - | --- --- type: Business title: quick-menu subtitle: 快速菜单 cols: 1 module: import { QuickMenuModule } from '@delon/abc/quick-menu'; --- 快速菜单,用于右侧隐式辅助列表。 ## API ### quick-menu | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[icon]` | 图标 | `string,TemplateRef` | `question-circle` | | `[top]` | 距离顶部 | `number` | `120` | | `[width]` | 打开后宽度 | `number` | `200` | | `[bgColor]` | 背景 | `string` | - | | `[borderColor]` | 边框颜色 | `string` | - | | `[expand]` | 当前展开状态,可双向绑定 | `boolean` | - | | `(expandChange)` | 当前展开状态改变回调函数 | `EventEmitter` | - | --- --- title: radio subtitle: 单选框 type: Widgets --- 单选框。 ## API ### schema 属性 | 参数 | 说明 | 类型 | 默认值 | |--------------|--------|----------------------|--------| | `[enum]` | 数据源 | `SFSchemaEnumType[]` | - | | `[readOnly]` | 禁用状态 | `boolean` | - | ### ui 属性 | 参数 | 说明 | 类型 | 默认值 | |---------------|------------------|----------------------------------------|-----------| | `[asyncData]` | 异步数据源 | `() => Observable` | - | | `[size]` | 大小,等同 `nzSize` | `string` | - | | `[styleType]` | radio 的样式 | `default, button` | `default` | | `[change]` | 值变更事件 | `(res: SFValue) => void` | - | | `[buttonStyle]` | RadioButton 的风格样式,目前有描边和填色两种风格 | `'outline'|'solid'` | `'outline'` | --- --- type: Form title: range-picker subtitle: 日期范围 cols: 1 module: import { DatePickerModule } from '@delon/abc/date-picker'; --- 基于 `nz-range-picker` 进一步优化,更好的服务于开始与结束日期。 ## API ### [extend] 需要配合 [nz-range-picker](https://ng.ant.design/components/date-picker/zh#nz-range-picker) 一起使用,例如: ```html ``` | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[(ngModelEnd)]` | 结束日期,开始与结束同时有值才会生效 | `Date` | - | | | `[shortcut]` | 设置快捷键 | `boolean, DateRangePickerShortcut` | `false` | ✅ | #### DateRangePickerShortcut | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[enabled]` | 是否启用 | `boolean` | `false` | | `[closed]` | 是否点击后立即关闭面板 | `boolean` | `true` | | `[list]` | 快捷列表,支持使用 `['today', 'yesterday', '-3', '-7', 'week', 'lastWeek', 'month', 'lastMonth', 'year']` 来表示 | `DateRangePickerShortcutItem[]` | `今天,昨天,近3天,近7天,本周,本月,全年` | --- --- title: rate subtitle: 评分 type: Non-built-in widgets --- 对评价进行展示,对事物进行快速的评级操作。 ## 如何使用 非内置模块,需要额外在 [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9) 注册 `withRateWidget`。 ## API ### schema 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[maximum]` | 总星数 | `number` | `5` | | `[multipleOf]` | `0.5` 表示允许半选,其它值表示不允许 | `number` | `0.5` | ### ui 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[allowClear]` | 是否允许再次点击后清除 | `boolean` | `true` | | `[autoFocus]` | 自动获取焦点 | `boolean` | `false` | | `[text]` | 提醒文本模板,`{{value}}` 表示当前值(注意无任何空格) | `string` | - | | `[tooltips]` | 自定义每项的提示信息 | `string[]` | `[]` | --- --- type: Service order: 5 title: ResponsiveService subtitle: 响应式服务 --- 更友好的使用响应式规则,将原 `xs`、`sm` 等属性简化成 `col` 栏,默认响应式规则: | `col` | `<576px` | `≥576px` | `≥768px` | `≥992px` | `≥1200px` | `≥1600px` | | ----- | -------- | -------- | -------- | -------- | --------- | --------- | | `1` | 1 | 1 | 1 | 1 | 1 | 1 | | `2` | 1 | 2 | 2 | 2 | 2 | 2 | | `3` | 1 | 2 | 3 | 3 | 3 | 3 | | `4` | 1 | 2 | 3 | 4 | 4 | 4 | | `5` | 1 | 2 | 3 | 4 | 6 | 6 | | `6` | 1 | 2 | 3 | 4 | 6 | 12 | 为了更好的开发响应式查看、编辑页,这种规则将默认将运用在 `sg`、`sv`、`se` 等组件中。你也可以利用 `ResponsiveConfig` 来改写默认规则。 ## ResponsiveConfig 通用配置项,例如统一对 `ResponsiveService` 设置响应式规则。 ```ts import { AlainThemeConfig } from '@delon/theme'; export function fnAlainThemeConfig(): AlainThemeConfig { return Object.assign(new AlainThemeConfig(), { responsive: { rules: { 1: { xs: 24 }, 2: { xs: 24, sm: 12 }, 3: { xs: 24, sm: 12, md: 8 }, 4: { xs: 24, sm: 12, md: 8, lg: 6 }, 5: { xs: 24, sm: 12, md: 8, lg: 6, xl: 4 }, 6: { xs: 24, sm: 12, md: 8, lg: 6, xl: 4, xxl: 2 }, } }, }); } @NgModule({}) export class DelonModule { static forRoot(): ModuleWithProviders { return { ngModule: DelonModule, providers: [ { provide: AlainThemeConfig, useFactory: fnAlainThemeConfig }, ], }; } } ``` --- --- type: Basic order: 1 title: reuse-tab subtitle: 路由复用标签 cols: 1 module: import { ReuseTabModule } from '@delon/abc/reuse-tab'; --- 复用标签在中台系统非常常见,本质是解决不同路由页切换时组件数据不丢失问题。 最新打开的永远是当前页,而路由复用是指当我们离开当前页时若符合复用条件(即:匹配模式)则保存当前路由所有组件状态(包括子组件),待下一次进入相同路由(即:匹配模式)时再从中缓存中获取到。 ## 如何使用 1. 在 `app.config.ts` 下提供 `provideReuseTabConfig()` 配置 2. 在需要展示标签的地方调用 ``,一般在 `src/app/layout/basic/basic.ts`,例如: ```html - + + ``` > **注意:若不指定 `(activate)` 事件,无法刷新未缓存过的当前标签页。** ## 匹配模式 在项目的任何位置注入 `ReuseTabService` 类,并设置 `mode` 属性,或通过 `` 重新设置值,包括: **0、Menu(推荐,默认值)** 按菜单 ([Menu](/theme/menu#Menu)) 配置。 可复用: ``` { text:'Dashboard' } { text:'Dashboard', reuse: true } ``` 不可复用: ``` { text:'Dashboard', reuse: false } ``` **1、MenuForce** 按菜单 ([Menu](/theme/menu#Menu)) 强制配置。 可复用: ``` { text:'Dashboard', reuse: true } ``` 不可复用: ``` { text:'Dashboard' } { text:'Dashboard', reuse: false } ``` **2、URL** 对所有路由有效,可以配合 `excludes` 过滤无须复用路由。 ## 标签文本 根据以下顺序获取标签文本: 1. 使用 `ReuseTabService.title = 'new title'` 在组件内覆盖文本 2. 路由配置中 `data` 属性中包含 `reuseTitle` > `title` 3. 菜单数据中 `text` 属性 `ReuseTabService` 代码示例: ```ts export class DemoReuseTabEditComponent implements OnInit { id: any; constructor(private route: ActivatedRoute, private reuseTabService: ReuseTabService) {} ngOnInit(): void { this.route.params.subscribe(params => { this.id = params.id; this.reuseTabService.title = `编辑 ${this.id}`; }); } } ``` ## 生命周期 路由复用不会触发现Angular组件生命周期钩子(例如:`ngOnInit` 等),但是往往需要在复用过程中刷新数据,因此提供了两种新生命周期钩子用于临时解决这类问题。 **OnReuseInit** 接口 - `_onReuseInit(type?: ReuseHookOnReuseInitType): void;` 当目前路由在复用过程中时触发,`type` 值分别为: - `init` 当路由复用时 - `refresh` 当触发刷新动作时 **OnReuseDestroy** 接口 - `_onReuseDestroy(): void;` 当目前路由允许复用且进入新路由时触发。 > 以 `_` 开头希望未来 Angular 会有相应的钩子用于快速替换,一个简单的示例: ```ts import { OnReuseDestroy, OnReuseInit, ReuseHookOnReuseInitType } from '@delon/abc/reuse-tab'; @Component() export class DemoComponent implements OnReuseInit, OnReuseDestroy { _onReuseInit(type: ReuseHookOnReuseInitType) { console.log('_onReuseInit', type); } _onReuseDestroy() { console.log('_onReuseDestroy'); } } ``` ## 滚动条位置 开启 `keepingScroll` 会在复用后恢复之前的滚动条位置,有几项注意细节: > **务必**使用路由选项 [scrollPositionRestoration](https://angular.io/api/router/ExtraOptions#scrollPositionRestoration) 来管理滚动条位置 - `true`:表示保持之前滚动条位置 - `false`:表示不对滚动条任何操作 - 若全站使用路由复用时,则设置 `scrollPositionRestoration: 'disabled'`,避免延迟滚动 - 若部分页面路由复用时,则受限于 `scrollPositionRestoration` **优先值** ,会有 `1ms` 延迟恢复滚动条位置 ## API ### ReuseTabService **属性** | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[max]` | 允许最多复用多少个页面,值发生变更时会强制关闭且忽略可关闭条件 | `number` | `10` | | `[mode]` | 设置匹配模式 | `ReuseTabMatchMode` | `0` | | `[debug]` | 是否Debug模式 | `boolean` | `false` | | `[keepingScroll]` | 保持滚动条位置 | `boolean` | `false` | | `[keepingScrollContainer]` | 保持滚动条容器 | `Element` | `window` | | `[excludes]` | 排除规则,限 `mode=URL` | `RegExp[]` | - | | `[items]` | 获取已缓存的路由 | `ReuseTabCached[]` | - | | `[count]` | 获取当前缓存的路由总数 | `number` | - | | `[change]` | 订阅缓存变更通知 | `Observable` | - | | `[title]` | 自定义当前标题 | `string` | - | | `[closable]` | 自定义当前 `closable` 状态 | `boolean` | - | **方法** | 方法名 | 说明 | 返回类型 | |-----|----|------| | `index(url)` | 获取指定路径缓存所在位置,`-1` 表示无缓存 | `number` | | `exists(url)` | 获取指定路径缓存是否存在 | `boolean` | | `get(url)` | 获取指定路径缓存 | `ReuseTabCached` | | `getTitle(url, route?: ActivatedRouteSnapshot)` | 获取标题 | `string` | | `clearTitleCached()` | 清空自定义标题数据 | `void` | | `getClosable(url, route?: ActivatedRouteSnapshot)` | 获取 `closable` 状态 | `string` | | `clearClosableCached()` | 清空 `closable` 缓存 | `void` | | `remove(url)` | 根据URL移除标签,同时触 `change` remove事件 | `void` | | `move(url, position)` | 移动缓存数据,同时触 `change` move事件 | `void` | | `clear()` | 清除所有缓存,同时触 `change` clear事件 | `void` | | `refresh()` | 无任何动作,但会触 `change` refresh事件 | `void` | | `replace(url)` | 强制关闭当前路由(包含不可关闭状态),并重新导航至 `newUrl` 路由 | `void` | ### reuse-tab | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[i18n]` | 右击菜单国际化,支持HTML | `ReuseContextI18n` | - | | `[mode]` | 设置匹配模式 | `ReuseTabMatchMode` | `0` | | `[debug]` | 是否Debug模式 | `boolean` | `false` | | `[max]` | 允许最多复用多少个页面 | `number` | `10` | | `[keepingScroll]` | 保持滚动条位置 | `boolean` | `false` | | `[keepingScrollContainer]` | 保持滚动条容器 | `string | Element` | `window` | | `[excludes]` | 排除规则,限 `mode=URL` | `RegExp[]` | - | | `[allowClose]` | 允许关闭 | `boolean` | `true` | | `[customContextMenu]` | 自定义右键菜单 | `ReuseCustomContextMenu[]` | - | | `[tabBarExtraContent]` | tab bar 上额外的元素 | `TemplateRef` | - | | `[tabBarStyle]` | tab bar 的样式对象 | `object` | - | | `[tabBarGutter]` | tabs 之间的间隙 | `number` | - | | `[tabType]` | tabs 页签的基本样式 | `line, card` | `line` | | `[tabMaxWidth]` | tabs 页签每一项最大宽度,单位:`px` | `number` | - | | `[routeParamMatchMode]` | 包含路由参数时匹配模式,例如:`/view/:id`
    - `strict` 严格模式 `/view/1`、`/view/2` 不同页
    - `loose` 宽松模式 `/view/1`、`/view/2` 相同页且只呈现一个标签
    - `((future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot) => boolean)` 自定义匹配方法 | `strict,loose` | `strict` | | `[disabled]` | 是否禁用 | `boolean` | `false` | | `[titleRender]` | 自定义标题渲染 | `TemplateRef<{ $implicit: ReuseItem }>` | - | | `[storageState]` | 是否存储状态,保持最后一次浏览器的状态 | `boolean` | `false` | | `[canClose]` | 关闭时二次校验 | `(options: { item: ReuseItem; includeNonCloseable: boolean }) => Observable` | - | | `[trackByFn]` | `nz-tabs` 的 `track` 函数 | `(item: ReuseItem) => item.url` | - | | `(close)` | 关闭回调 | `EventEmitter` | - | | `(change)` | 切换时回调,接收的参数至少包含:`active`、`list` 两个参数 | `EventEmitter` | - | **右击菜单** 当按下键盘 `ctrl` 时会强制移除不可关闭项。 ### ReuseTabCached | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[title]` | 标题 | `string` | - | | `[url]` | URL地址 | `string` | - | | `[closable]` | 是否允许关闭 | `boolean` | - | ### ReuseTabNotify | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[active]` | 事件类型 | `title,close,closeRight,clear,move,closable,refresh,add` | - | ### ReuseContextI18n | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[close]` | 关闭 | `string` | - | | `[closeOther]` | 关闭其它 | `string` | - | | `[closeRight]` | 关闭右边 | `string` | - | | `[clear]` | 清空 | `string` | - | ### ReuseCustomContextMenu | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[id]` | 唯一标识符 | `string` | - | | `[title]` | 标题 | `string` | - | | `[fn]` | 处理方法 | `(item: ReuseItem, menu: ReuseCustomContextMenu) => void` | - | | `[disabled]` | 是否禁用 | `(item: ReuseItem) => boolean` | - | ### 路由data 透过路由 `data` 附加数据,可以对部分页面提供覆盖[全局配置](/docs/global-config),例如: ```ts // 指定不复路由 { path: 'p1', component: DemoComponent, data: { reuse: false } } // 指定标签标题 { path: 'p1', component: DemoComponent, data: { title: 'New Title' } } ``` | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[reuse]` | 是否复用 | `boolean` | - | | `[title]` | 标题 | `string` | - | | `[titleI18n]` | I18n标题Key | `string` | - | | `[reuseClosable]` | 是否允许关闭 | `boolean` | - | | `[keepingScroll]` | 是否保持滚动条 | `boolean` | - | > **注:** 以上数据也可在 [Menu](/theme/menu#Menu) 数据中体现。 ## 常见问题 ### 如何Debug? 路由复用会保留组件状态,这可能会带来另一个弊端;复用过程中不会触发Angular生命周期勾子,大部分情况下都能正常运行,有几个常见问题: - `OnDestroy` 可能会处理一些组件外部(例如:`body`)的样式等,可以参考生命周期解决。 - 开启 `debug` 模式后会在 `console` 很多信息这有助于分析路由复用的过程。 ### Max参数 限定最大复用数据可以减少内存的增长,有几个问题需要注意: - `max` 参数值发生变更时会强制关闭且忽略可关闭条件 - 超出 `max` 值时,会关掉最先打开 **可关闭** 的页面,若所有页面都为 **不可关闭** 则忽略关闭 ### 不支持 QueryString 查询参数 复用采用URL来区分是否同一个页面,而 QueryString 查询参数很容易产生重复性误用,因此不支持查询参数,且在复用过程中会强制忽略掉 QueryString 部分。 ### 多应用缓存处理 使用 `provideReuseTabConfig(storeKey: 'newKey')` 或通过覆盖 `REUSE_TAB_CACHED_MANAGER` 改变缓存存储 ,例如在使用微前端(类似[ngx-planet](https://github.com/worktile/ngx-planet))可以重写缓存数据到 `window` 下来实现数据共享。 --- --- type: Service order: 6 title: RTLService subtitle: RTL服务 --- RTL服务控制。 ## API | 接口名 | 参数 | 描述 | |-----|----|----| | `dir` | `Direction` | 获取或设置当前文字方向 | | `nextDir` | `Direction` | 获取下一次文字方向 | | `change` | `Observable` | 订阅变更通知 | | `toggle()` | - | 切换文字方向 | --- --- order: 1 title: safe subtitle: 安全HTML等 type: Pipe --- ## html 简化 `bypassSecurityTrustHtml` 的使用。 ```html
    ``` ## url 简化 `bypassSecurityTrustUrl` 的使用。 ```html ``` --- --- type: CURD title: se subtitle: 编辑 cols: 1 order: 3 module: import { SEModule } from '@delon/abc/se'; --- 简化表单HTML模板的高阶组件,并进一步优化了一些细节: - 更友好的表单校验状态 - 自动化响应式布局 - 自动维护表单 `id` 它由 `se-container` 容器(指令)和 `se` 组件来表示一个表单,一个简单HTML模板表单是这么写的: ```html ``` 同时,会自动处理所有 Angular 内置校验指令,例如:`required`、`maxlength`、`min`、`pattern` 等,并以红色边框来表示无效值状态。 ## API ### se-container | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[gutter]` | 间距,当 `nzLayout:horizontal` 时有效 | `number` | `32` | ✅ | | `[se-container]` | 指定表单元素最多分几列展示,最终一行几列由 col 配置结合[响应式规则](/theme/responsive)决定 | `'1','2','3','4','5','6'` | - | | | `[col]` | 指定表单元素最多分几列展示,最终一行几列由 col 配置结合[响应式规则](/theme/responsive)决定 | `'1','2','3','4','5','6'` | - | ✅ | | `[labelWidth]` | 表单元素默认标签文本宽度,单位:`px` | `number` | `150` | ✅ | | `[nzLayout]` | 表单布局,当 `inline` 时强制大小为 `compact` | `'horizontal','vertical','inline'` | `'horizontal'` | ✅ | | `[size]` | 大小 `compact` 紧凑型,强制忽略 `error`、`extra` 展示 | `'default','compact'` | `'default'` | ✅ | | `[firstVisual]` | 是否立即呈现错误视觉 | `boolean` | `false` | ✅ | | `[ingoreDirty]` | 是否忽略 `dirty` 校验 | `boolean` | `false` | ✅ | | `[line]` | 分隔线 | `boolean` | `false` | - | | `[title]` | 标题 | `string,TemplateRef` | - | | | `[errors]` | 批量修改 `se` 错误消息描述 | `SEErrorRefresh[]` | - | | | `[noColon]` | 默认是否不显示 label 后面的冒号 | `boolean` | `false` | - | ### se | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[col]` | 指定表单元素最多分几列展示,最终一行几列由 col 配置结合[响应式规则](/theme/responsive)决定(继承于 `se-container`) | `'1','2','3','4','5','6'` | - | | `[label]` | 标签文本 | `string, TemplateRef` | - | | `[labelWidth]` | 标签文本宽度,单位:`px`(继承于 `se-container`) | `number` | - | | `[hideLabel]` | 是否隐藏当前 `label` | `boolean` | `false` | | `[optional]` | 标签可选信息 | `string, TemplateRef` | - | | `[optionalHelp]` | 标签可选帮助 | `string, TemplateRef` | - | | `[optionalHelpColor]` | 标签可选帮助背景颜色 | `string` | - | | `[error]` | 错误描述 | `string, TemplateRef, { [key: string]: string, TemplateRef }` | - | | `[extra]` | 额外提示信息 | `string, TemplateRef` | - | | `[required]` | 是否必填项标识符,若不设置自动根据表单元素是否有 `RequiredValidator` 校验来设置值 | `string` | - | | `[controlClass]` | 控件区域样式名 | `string` | - | | `[id]` | 自定义组件 `id` | `string` | - | | `[line]` | 分隔线(继承于 `se-container`) | `boolean` | - | | `[noColon]` | 是否不显示 label 后面的冒号 | `boolean` | `false` | - | ### se-title 用于展示标题,单独一行。 ## 常见问题 ### 什么时候自定义组件id 点击表单的 Label 会将光标定位至相应组件下,而 `ng-edit` 会自动根据 `ngModel` 状态合理的设定 `id`,绝大多数情况下你无须关心 `id` 的绑定状态,当然若你手动指定 `id` 值则优先级更高但同时你需要自己维护组件对应的 `id` 值。 --- --- title: search subtitle: 搜索 type: Examples --- 搜索条件较多时,使用展开/收缩来节省空间。 通过 `mode="search"` 快速设置搜索模式,会自动将 `layout` 设置为 `inline`、`liveValidate` 设置为 `false`、按钮文本改为 `搜索`。 配合 expandable 将不重要的字段标记为 collapse,默认折叠以保持界面简洁。 --- --- title: segmented subtitle: 分段控制器 type: Non-built-in widgets --- - 用于展示多个选项并允许用户选择其中单个选项; - 当切换选中选项时,关联区域的内容会发生变化。 ## 如何使用 非内置模块,需要额外在 [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9) 注册 `withSegmentedWidget`。 ## API ### ui 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[block]` | 将宽度调整为父元素宽度的选项 | `boolean` | false | | | `[asyncData]` | 异步数据 | `() => Observable` | - | | | `(valueChange)` | 当前选中项目变化时触发回调 | `(data: { index: number; item: SFValue }) => void` | - | | --- --- title: select subtitle: 选择器 type: Widgets order: 6 --- 下拉选择器。 ## API ### schema 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[enum]` | 数据源 | `SFSchemaEnumType[]` | - | | `[readOnly]` | 禁用状态 | `boolean` | - | ### ui 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[asyncData]` | 异步数据源 | `() => Observable` | - | | `[size]` | 大小,等同 `nzSize` | `string` | - | | `[compareWith]` | 与 [SelectControlValueAccessor](https://angular.io/api/forms/SelectControlValueAccessor#caveat-option-selection) 相同 | `(o1: any, o2: any) => boolean` | `(o1: any, o2: any) => o1===o2` | | `[placeholder]` | 在文字框中显示提示讯息 | `string` | - | | `[autoClearSearchValue]` | 是否在选中项后清空搜索框,只在 `mode` 为 `multiple` 或 `tags` 时有效。 | `boolean` | `true` | | `[allowClear]` | 支持清除 | `boolean` | `false` | | `[clearValue]` | 清空时默认值 | `any` | `undefined` | | `[variant]` | 变体 | `outlined,borderless,filled,underlined` | `outlined` | | `[autoFocus]` | 默认获取焦点 | `boolean` | `false` | | `[dropdownClassName]` | 下拉菜单的 className 属性 | `string` | - | | `[dropdownMatchSelectWidth]` | 下拉菜单和选择器同宽 | `boolean` | `true` | | `[dropdownStyle]` | 下拉菜单的 style 属性 | `object` | - | | `[serverSearch]` | 是否使用服务端搜索,当为 true 时,将不再在前端对 nz-option 进行过滤 | `boolean` | `false` | | `[searchDebounceTime]` | 搜索抖动时间 | `number` | `300` | | `[searchLoadingText]` | 搜索加载中文本 | `string` | - | | `[onSearch]` | 搜索内容变化回调函数,参数为搜索内容,必须返回 `Promise` 对象 | `(text: string) => Promise` | - | | `[maxMultipleCount]` | 最多选中多少个标签 | `number` | `Infinity` | | `[mode]` | 设置 nz-select 的模式,`tags` 建议增加 `default: null`,否则可能会遇到初始化有一个空的标签。 | `multiple,tags,default` | `default` | | `[notFoundContent]` | 当下拉列表为空时显示的内容 | `string` | - | | `[showSearch]` | 使单选模式可搜索 | `boolean` | `false` | | `[showArrow]` | 是否显示下拉小箭头 | `boolean` | 单选为 `true`,多选为 `false` | | `[tokenSeparators]` | 在 tags 和 multiple 模式下自动分词的分隔符 | `string[]` | `[]` | | `[maxTagCount]` | 最多显示多少个 tag | `number` | - | | `[change]` | 选中的 nz-option 发生变化时,调用此函数 | `(ngModel:any丨any[], orgData: SFSchemaEnum丨SFSchemaEnum[])=>void` | - | | `[openChange]` | 下拉菜单打开关闭回调函数 | `(status: boolean) => void` | - | | `[scrollToBottom]` | 下拉菜单滚动到底部回调,可用于作为动态加载的触发条件 | `() => void` | - | | `[customTemplate]` | 自定义选择框的Template内容 | `TemplateRef<{ $implicit: NzOptionComponent }>` | - | | `[suffixIcon]` | 自定义的选择框后缀图标 | `TemplateRef, string` | - | | `[removeIcon]` | 自定义的多选框清除图标 | `TemplateRef` | - | | `[clearIcon]` | 自定义的多选框清空图标 | `TemplateRef` | - | | `[menuItemSelectedIcon]` | 自定义当前选中的条目图标 | `TemplateRef` | - | | `[maxTagPlaceholder]` | 隐藏 tag 时显示的内容 | `TemplateRef<{ $implicit: any[] }>` | - | | `[optionHeightPx]` | 下拉菜单中每个 Option 的高度 | `number` | `32` | | `[optionOverflowSize]` | 下拉菜单中最多展示的 Option 个数,超出部分滚动 | `number` | `8` | --- --- order: 1 title: SettingsService subtitle: 项目配置项服务 type: Service --- 项目配置项,包含应用[App](https://github.com/ng-alain/delon/blob/master/packages/theme/src/services/settings/interface.ts#L1)、布局[Layout](https://github.com/ng-alain/delon/blob/master/packages/theme/src/services/settings/interface.ts#L15)、用户信息[User](https://github.com/ng-alain/delon/blob/master/packages/theme/src/services/settings/interface.ts#L8)三种数据类型,并且存储持久化在 `localStorage`(参考[#1737](https://github.com/ng-alain/ng-alain/issues/1737)来切换成 `sessionStorage`)。 **建议:** 在 Angular 启动服务([startup.service.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/startup/startup.service.ts))从远程获取到应用、用户数据后,调用 `setApp()`、`setUser()` 方法。 ## API ### SettingsService | 名称 | 类型 | 返回值 | 描述 | |----|----|-----|----| | `layout` | `property` | `Layout` | 布局信息 | | `app` | `property` | `App` | 项目信息 | | `user` | `property` | `User` | 用户信息 | | `notify` | `property` | `Observable` | 当布局、项目、用户信息变更时通知 | | `setLayout(name: string, value: any)` | `method` | `boolean` | 设置布局属性值 | | `setApp(value: App)` | `method` | `boolean` | 设置项目信息 | | `setUser(value: User)` | `method` | `boolean` | 设置用户信息 | ### App | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[name]` | 应用名称 | `string` | - | | `[description]` | 应用描述 | `string` | - | ### User | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[name]` | 当前用户名称 | `string` | - | | `[avatar]` | 当前用户头像 | `string` | - | | `[email]` | 当前用户邮箱 | `string` | - | ### Layout | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[collapsed]` | 是否折叠菜单 | `boolean` | - | | `[lang]` | 当前语言 | `string` | - | | `[colorWeak]` | 色弱模式 | `boolean` | `false` | ## 常见问题 **如何更改本地存储键名?** 允许通过 `global-config.module.ts` 全局配置文件,增加 `ALAIN_SETTING_KEYS` 的配置,例如: ```diff const alainProvides = [ provideAlainConfig(alainConfig), + { + provide: ALAIN_SETTING_KEYS, + useValue: { + layout: 'new-layout', + user: 'new-user', + app: 'new-app', + }, + }, ]; ``` --- --- type: Layout order: 1 title: sg subtitle: 简易栅格 cols: 1 module: import { SGModule } from '@delon/abc/sg'; --- 简化栅格系统运用的高阶组件,它由 `sg-container` 容器(指令)和 `sg` 组件组合代替一个响应式布局: ```html
    1 2 3 4
    ``` 替代: ```html
    1 2 3 4
    ``` ## API ### sg-container | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[gutter]` | 间距,当 `nzLayout:horizontal` 时有效 | `number` | `32` | ✅ | | `[sg-container]` | 指定表单元素最多分几列展示,最终一行几列由 col 配置结合[响应式规则](/theme/responsive)决定, | `'1','2','3','4','5','6'` | - | - | | `[col]` | 指定表单元素最多分几列展示,最终一行几列由 col 配置结合[响应式规则](/theme/responsive)决定, | `'1','2','3','4','5','6'` | `2` | ✅ | ### sg | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[col]` | 指定表单元素最多分几列展示,最终一行几列由 col 配置结合[响应式规则](/theme/responsive)决定, | `'1','2','3','4','5','6'` | - | --- --- title: slider subtitle: 滑动输入条 type: Non-built-in widgets --- 滑动型输入器,展示当前值和可选范围。 ## 如何使用 非内置模块,需要额外在 [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9) 注册 `withSliderWidget`。 ## 注意事项 - 强制忽略 `exclusiveMinimum`、`exclusiveMaximum` ## API ### schema 属性 成员 | 说明 | 类型 | 默认值 ----|------|-----|------ `[minimum]` | 最小值 | `number` | `0` `[maximum]` | 最大值 | `number` | `100` `[multipleOf]` | 倍数 | `number` | `1` ### ui 属性 成员 | 说明 | 类型 | 默认值 ----|------|-----|------ `[range]` | 当添加该属性时,启动双滑块模式 | `Boolean` | - `[marks]` | 刻度标记 | `NzMarks` | - `[dots]` | 是否只能拖拽到刻度上 | `Boolean` | `false` `[included]` | 是否包含。`marks` 不为空对象时有效,值为 `true` 时表示值为包含关系,`false` 表示并列 | `Boolean` | `true` `[vertical]` | 竖直显示。添加该属性时,Slider 为垂直方向。 | `boolean` | `false` `[afterChange]` | 与 `onmouseup` 触发时机一致,把当前值作为参数传入。 | `(value: NzSliderValue) => void` | - `[formatter]` | Slider 会把当前值传给 `nzTipFormatter`,并在 Tooltip 中显示 `nzTipFormatter` 的返回值,若为 null,则隐藏 Tooltip | `(value: number) => string` | - --- --- type: CURD title: st subtitle: 表格 cols: 1 order: 1 module: import { STModule } from '@delon/abc/st'; --- `st` 并不是在创造另一个表格组件,而是在 `nz-table` 基础上以**可配置**形式渲染表格,在中后台里这种方式可以满足绝大多数场景,但又可以更易地管理表格渲染动作。 ## 关于数据源 `data` 支持三种不同格式数据源,整体分为:URL和静态数据两类;但可以透过参数的配置达到不同的效果,同时有非常多参数可通过 `AlainSTConfig` 重置默认值,使整个 `st` 组件模板达到极简。 ### URL 指的是通过一个 URL 字符串来获取数据。 - 通过 `req.params`、`req.method` 等参数解决请求数据格式问题 - 通过 `res.reName` 重置数据 `key` 而无须担心后端数据格式是否满足 `st` 需求 - 通过 `res.process` 可以对表格渲染前对数据进一步优化 - 通过 `page.zeroIndexed` 可以调整 http 请求时 `pi` 参数是否遵循 0 基索引,默认情况下为 1 基索引 - 若返回体的值是数组类型,则强制不分页 - 使用 `_HttpClient` 发起请求,因此满足 [AlainThemeConfig](/theme/http#AlainThemeConfig) 的配置也适用 ### 静态数据 指的是通过指定值为 `STData[]` 或 `Observable`,二者都遵循以下规则: - `page.front` 前端分页,默认:`true` - `true` 由 `st` 根据 `data` 长度受控分页,包括:排序、过滤等 - `false` 由用户通过 `total` 和 `data` 参数受控分页,并维护 `(change)` 当分页变更时重新加载数据 - `page.show` 是否显示分页器;当未指定时若 `ps>total` 情况下自动不显示 ### 常见问题 **Cannot read property 'text' of undefined** 若组件已经加载完毕,此时如果再次改变 `columns` 时可能会出现该错误,这是因为 `st` 每次只会根据 `columns` 对数据处理,当列定义发生改变后可能会因为列定义与现有数据无法配对,可能需要使用 `this.st.resetColumns({ columns: [], emitReload: true })` 来更新列定义并重新加载数据。 **某列显示INVALID DATA** 当在解析列数据时抛出异常时,会强制显示 *INVALID DATA*,例如当某指定 `type: 'number'` 时,而实际获得值为非有效数字型时就会抛出异常。 ## API ### st | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[columns]` | 列描述 | `STColumn[]` | - | - | | `[data]` | 数据源 | `string, STData[], Observable` | - | - | | `[req]` | 请求体配置 | `STReq` | - | ✅ | | `[res]` | 返回体配置 | `STRes` | - | ✅ | | `[pi]` | 当前页码 | `number` | `1` | ✅ | | `[ps]` | 每页数量,默认:`10` | `number` | `10` | ✅ | | `[total]` | 当前总数据,在服务器渲染时需要传入,默认:`0` | `number` | `0` | - | | `[page]` | 分页器配置 | `STPage` | - | ✅ | | `[noResult]` | 无数据时显示内容 | `string,TemplateRef` | - | ✅ | | `[bordered]` | 是否显示边框 | `boolean` | `false` | ✅ | | `[size]` | table大小 | `'small','middle','default'` | `'default'` | ✅ | | `[widthMode]` | 设置表格宽度模式 | `STWidthMode` | - | ✅ | | `[rowClassName]` | 表格行的类名 | `(record: STData, index: number) => string` | - | ✅ | | `[clickRowClassName]` | 点击行切换类名 | `string, STClickRowClassNameType` | - | ✅ | | `[loading]` | 页面是否加载中,当指定 `null` 由 st 受控 | `boolean | null` | `null` | - | | `[loadingIndicator]` | 加载指示符 | `TemplateRef` | - | ✅ | | `[loadingDelay]` | 延迟显示加载效果的时间(防止闪烁) | `number` | `0` | ✅ | | `[delay]` | 是否延迟渲染表格,需手动调用`refreshColumns()`渲染 | `boolean` | `false` | - | | `[scroll]` | 横向或纵向支持滚动,也可用于指定滚动区域的宽高度:`{ x: "300px", y: "300px" }` | `{ y?: string; x?: string }` | - | - | | `[virtualScroll]` | 是否启用虚拟滚动模式,与 `[nzScroll]` 配合使用 | `boolean` | `false` | ✅ | | `[virtualItemSize]` | 虚拟滚动时每一列的高度,与 [cdk itemSize](https://material.angular.io/cdk/scrolling/api) 相同 | `number` | `54` | ✅ | | `[virtualMaxBufferPx]` | 缓冲区最大像素高度,与 [cdk maxBufferPx](https://material.angular.io/cdk/scrolling/api) 相同 | `number` | `200` | ✅ | | `[virtualMinBufferPx]` | 缓冲区最小像素高度,低于该值时将加载新结构,与 [cdk minBufferPx](https://material.angular.io/cdk/scrolling/api) 相同 | `number` | `100` | ✅ | | `[virtualForTrackBy]` | 虚拟滚动数据 `TrackByFunction` 函数 | `TrackByFunction` | - | ✅ | | `[singleSort]` | 单排序规则
    若不指定,则返回:`columnName=ascend|descend`
    若指定,则返回:`sort=columnName.(ascend|descend)` | `STSingleSort` | `null` | ✅ | | `[multiSort]` | 是否多排序,当 `sort` 多个相同值时自动合并,建议后端支持时使用 | `boolean, STMultiSort` | `false` | ✅ | | `[header]` | 表格标题 | `string,TemplateRef` | - | - | | `[showHeader]` | 是否显示列头行 | `boolean` | `true` | - | | `[footer]` | 表格底部 | `string,TemplateRef` | - | - | | `[bodyHeader]` | 表格顶部额外内容,一般用于添加合计行 | `TemplateRef` | - | - | | `[body]` | 表格额外内容,一般用于添加合计行 | `TemplateRef` | - | - | | `[widthConfig]` | 表头分组时指定每列宽度,与 STColumn 的 width 不可混用 | `string[]` | - | - | | `[expandRowByClick]` | 通过点击行来展开子行 | `boolean` | `false` | ✅ | | `[expandAccordion]` | 手风琴模式 | `boolean` | `false` | ✅ | | `[expand]` | 当前列是否包含展开按钮,当数据源中包括 `expand` 表示展开状态 | `TemplateRef<{ $implicit: STData; index: number }>` | - | - | | `[expandIcon]` | 自定义展开图标 | `TemplateRef<{ $implicit: STData; index: number }>` | - | | `[responsive]` | 是否开启响应式 | `boolean` | `true` | ✅ | | `[responsiveHideHeaderFooter]` | 是否在小屏幕下才显示顶部与底部 | `boolean` | `false` | ✅ | | `[resizable]` | 当前表格所有列的调整表头配置项,**不支持多表头** | `STResizable, boolean` | - | - | | `[trackBy]` | `@for` 列表循环的 `TrackByFunction` 函数 | `TrackByFunction` | - | - | | `[drag]` | 拖拽排序 | `STDragOptions, boolean` | - | - | | `(change)` | 变化时回调,包括:`pi`、`ps`、`checkbox`、`radio`、`sort`、`filter`、`click`、`dblClick`、`expand` 变动 | `EventEmitter` | - | - | | `(error)` | 异常时回调 | `EventEmitter` | - | - | ### 组件属性与方法 | 名称 | 说明 | |----|----| | `[filteredData]` | 获取过滤后所有数据
    - 本地数据:包含排序、过滤后不分页数据
    - 远程数据:不传递 `pi`、`ps` 两个参数 | | `[count]` | 获取当前页的条目数 | | `[list]` | 获取当前页的数据列表 | | `resetColumns(options?: STResetColumnsOption)` | 重置列描述 | | `load(pi = 1, extraParams?: any, options?: STLoadOptions)` | 加载指定页 | | `reload(extraParams?: any, options?: STLoadOptions)` | 刷新当前页 | | `reset(extraParams?: any, options?: STLoadOptions)` | 重置且重新设置 `pi` 为 `1`,包含单多选、排序、过滤状态(同默认状态一并清除) | | `addRow(data: STData | STData[], options?: { index?: number })` | 添加行 | | `removeRow(data: STData | STData[] | number)` | 移除行 | | `setRow(index: number | STData, item: STData, options?: { refreshSchema?: boolean; emitReload?: boolean; arrayProcessMethod?: boolean })` | 修改行数据,支持部分字段更新 | | `pureItem(itemOrIndex: STData | number)` | 返回纯净数据,`st` 内部会维护一组用于缓存的数据,这部分数据可能会影响后端 | | `clear(cleanStatus = true)` | 清空所有数据 | | `clearStatus()` | 清空所有状态(包含单多选、排序、过滤状态) | | `clearCheck()` | 清除所有 `checkbox` | | `clearRadio()` | 清除所有 `radio` | | `export(newData?: STData[] | true, opt?: STExportOptions)` | 导出Excel,确保已经导入 `XlsxModule` | 一些细节: - `extraParams` 若不传递表示保留原始值 - `STLoadOptions.merge` 是否合并模式,即 `extraParams` 跟新值合并而非替代 - `STLoadOptions.toTop` 是否跳转至顶部,若不指定由 `page.toTop` 来决定 **使用方式** ```ts @Component({ template: ` ` }) class TestComponent { @ViewChild('st', { static: false }) comp: STComponent; // this.comp.load(); } ``` ### STReq | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[type]` | 分页类型,`page` 使用 `pi`,`ps` 组合;`skip` 使用 `skip`,`limit` 组合 | `page,skip` | `page` | ✅ | | `[params]` | 额外请求参数,默认自动附加 `pi`、`ps` 至URL | `any` | - | - | | `[ignoreParamNull]` | 是否忽略参数中 `null` 或 `undefind` 值 | `Boolean` |`false` | ✅ | | `[method]` | 请求方法 | `'POST','GET','HEAD','PUT','PATCH','DELETE'` | `'GET'` | ✅ | | `[body]` | 请求体 `body`,当 `method: POST` 时有效 | `any` | - | - | | `[headers]` | 请求体 `headers` | `any` | - | ✅ | | `[reName]` | 重命名请求参数 `pi`、`ps` | `STReqReNameType, ((result: any, options: { pi: number; ps: number; total: number }) => { total: number; list: T[] })` | `{ pi: 'pi', ps: 'ps', skip: 'skip', limit: 'limit' }` | ✅ | | `[allInBody]` | 是否将请求所有参数数据都放入 `body` 当中(`url` 地址本身参数除外),仅当 `method: 'POST'` 时有效 | `boolean` | `false` | ✅ | | `[lazyLoad]` | 是否延迟加载数据,即渲染结束后不会主动发起请求 | `boolean` | `false` | ✅ | | `[process]` | 请求前数据处理 | `(requestOptions: STRequestOptions) => STRequestOptions` | - | ✅ | ### STRes | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[reName]` | 重命名返回参数 `total`、`list`,支持 `a.b.c` 的嵌套写法 | `{total:string;list:string}` | - | ✅ | | `[process]` | 数据预处理 | `(data: STData[], rawData?: any) => STData[]` | - | ✅ | ### STPage | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[front]` | 前端分页,当 `data` 为 `any[]` 或 `Observable` 有效 | `boolean` | `true` | ✅ | | `[zeroIndexed]` | 后端分页是否采用`0`基索引,只在`data`类型为`string`时有效 | `boolean` | `false` | ✅ | | `[position]` | 指定分页显示的位置 | `top,bottom,both` | `bottom` | ✅ | | `[placement]` | 指定分页分页方向 | `left,center,right` | `right` | ✅ | | `[show]` | 是否显示分页器 | `boolean` | `true` | ✅ | | `[showSize]` | 是否显示分页器中改变页数 | `boolean` | `false` | ✅ | | `[pageSizes]` | 分页器中每页显示条目数下拉框值 | `number[]` | `[10, 20, 30, 40, 50]` | ✅ | | `[showQuickJumper]` | 是否显示分页器中快速跳转 | `boolean` | `false` | ✅ | | `[total]` | 是否显示总数据量,字符串表示自定义模板(支持三个变量名:`total` 表示数据总量、`range[0]` 和 `range[1]` 表示当前数据范围;**变量名**统一使用双花括号包裹) | `boolean, string` | `false` | ✅ | | `[toTop]` | 切换分页时返回顶部 | `boolean` | `true` | ✅ | | `[toTopOffset]` | 返回顶部偏移值 | `number` | `100` | ✅ | | `[itemRender]` | 用于自定义页码的结构,用法参照 Pagination 组件 | `TemplateRef<{ $implicit: 'page' \| 'prev' \| 'next', page: number }>` | - | ✅ | | `[simple]` | 当添加该属性时,显示为简单分页 | `boolean` | - | ✅ | | `[checkboxIdMap]` | 缓存 checkbox 列表的标识列 | `string` | - | ✅ | ### STError | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[type]` | 异常类型,`req` 表示HTTP请求 | `req` | - | | `[error]` | 异常内容 | `any` | - | ### STChange | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[type]` | 变更类型,包括:`loaded`、`pi`、`ps`、`checkbox`、`radio`、`sort`、`filter`、`filterChange`、`click`、`dblClick`、`expand` | `STChangeType` | - | | `[pi]` | 当前页码 | `number` | - | | `[ps]` | 每页数量 | `number` | - | | `[total]` | 数据总量 | `number` | - | | `[loaded]` | `loaded` 参数 | `STData[]` | - | | `[checkbox]` | `checkbox` 参数 | `STData[]` | - | | `[radio]` | `radio` 参数 | `STData` | - | | `[sort]` | 排序参数 | `STChangeSort` | - | | `[filter]` | 过滤参数 | `STColumn` | - | | `[click]` | 行点击参数 | `STChangeRowClick` | - | | `[dblClick]` | 行双击参数 | `STChangeRowClick` | - | | `[expand]` | `expand` 参数 | `STData` | - | ### STChangeSort | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[value]` | 当前列排序状态 | `ascend,descend` | - | | `[map]` | 所有列排序状态 | `{ [key: string]: string }` | - | | `[column]` | 行描述 | `STColumn` | - | ### STChangeRowClick | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[e]` | 当前行事件 | `Event` | - | | `[item]` | 当前行数据 | `STData` | - | | `[index]` | 当前行索引 | `number` | - | ### STExportOptions | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[sheetname]` | 工作薄名称 | `string` | `Sheet1` | | `[filename]` | 保存的文件名 | `string` | `export.xslx` | | `[callback]` | 保存前的回调 | `(wb: WorkBook) => void` | - | ### STSingleSort | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[key]` | 请求参数名 | `string` | `sort` | ✅ | | `[nameSeparator]` | 列名与状态间分隔符 | `string` | `.` | ✅ | ### STMultiSort | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[key]` | 请求参数名 | `string` | `sort` | ✅ | | `[separator]` | 不同属性间分隔符 | `string` | `-` | ✅ | | `[nameSeparator]` | 列名与状态间分隔符 | `string` | `.` | ✅ | | `[arrayParam]` | 是否以数组的形式传递参数
    `true` 表示使用 `url?sort=name.asc&sort=age.desc` 形式
    `false` 表示使用 `url?sort=name.asc-age.desc` 形式 | `boolean` | `false` | ✅ | | `[keepEmptyKey]` | 是否保持空值的键名
    `true` 表示不管是否有排序都会发送 `key` 键名
    `false` 表示无排序动作时不会发送 `key` 键名 | `boolean` | `true` | ✅ | | `[global]` | **仅限全局配置项有效**,是否全局多排序模式
    `true` 表示所有 `st` 默认为多排序
    `false` 表示需要为每个 `st` 添加 `multiSort` 才会视为多排序模式 | `boolean` | `true` | ✅ | ### STData | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[checked]` | 选择框或单选框状态值 | `boolean` | - | | `[disabled]` | 选择框或单选框 `disabled` 值 | `boolean` | - | | `[expand]` | 是否展开状态 | `boolean` | - | | `[showExpand]` | 是否显示展开按钮 | `boolean` | - | | `[className]` | 行样式 | `string` | - | ### STColumn | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[title]` | 列名 | `string, STColumnTitle` | - | | `[i18n]` | 列名i18n | `string` | - | | `[type]` | `no` 行号
    `checkbox` 多选
    `radio` 单选
    `link` 链接,可触发 `click`
    `img` 图像且居中
    `number` 数字且居右
    `currency` 货币且居右
    `date` 日期格式且居中
    `badge` [徽标](https://ng.ant.design/components/badge/zh)
    `tag` [标签](https://ng.ant.design/components/tag/zh)
    `yn` 将`boolean`类型徽章化 [document](/theme/yn)
    使用 `cell` 组件渲染,见[cell](/components/cell)
    `widget` 自定义小部件来渲染列 | `string` | - | | `[cell]` | 使用 `cell` 组件渲染,见[cell](/components/cell)。 | `CellOptions | ((record: T, column: STColumn) => CellOptions)` | - | | `[index]` | 列数据在数据项中对应的 key,支持 `a.b.c` 的嵌套写法 | `string, string[]` | - | | `[render]` | 自定义渲染ID | `string, TemplateRef, TemplateRef<{ $implicit: STData; index: number }>` | - | | `[renderTitle]` | 标题自定义渲染ID | `string, TemplateRef, TemplateRef<{ $implicit: STColumn; index: number }>` | - | | `[default]` | 当不存在数据(值类型为 `undefined`)时以默认值替代 | `string` | - | | `[buttons]` | 按钮组 | `STColumnButton[]` | - | | `[maxMultipleButton]` | 配置最多显示多少个按钮,多余部分自动生成至 `更多` 下面 | `STColumnMaxMultipleButton, number` | - | | `[width]` | 列宽(数字型表示 `px` 值,**注意:** 若固定列必须是数字),例如:`100`、`10%`、`100px` | `string,number` | - | | `[fixed]` | 固定前后列,当指定时务必指定 `width` 否则视为无效 | `left,right` | - | | `[format]` | 格式化列值 | `(item: STData, col: STColumn, index: number) => string` | - | | `[className]` | 列 `class` 属性值,例如:`text-center` 居中; `text-right` 居右; `text-error` 异常色,更多参考[样式工具类](/theme/tools) | `string` | - | | `[colSpan]` | 合并列 | `number` | - | | `[onCell]` | 设置单元格属性 | `(item: T, index: number) => STOnCellResult;` | - | | `[sort]` | 排序配置项,远程数据配置**优先**规则:
    `true` 表示允许排序,且若数据源为本地数据时会自动生成 `compare: (a, b) => a[index] - b[index]` 方法
    `ascend` 表示升序
    `descend` 表示降序
    `string` 表示远程数据排序相对应 `key` 值 | `true,string,STColumnSort` | - | | `[filter]` | 过滤配置项 | `STColumnFilter` | - | | `[selections]` | 选择功能配置 | `STColumnSelection[]` | - | | `[numberDigits]` | 数字格式,`type=number` 有效 | `string` | - | | `[dateFormat]` | 日期格式,`type=date` 有效 | `string` | `yyyy-MM-dd HH:mm` | | `[currency]` | 货币格式选项,`type=currency` 有效 | `STColumnCurrency` | - | | `[yn]` | 当 `type=yn` 有效 | `STColumnYn` | - | | `[exported]` | 是否允许导出 | `boolean` | `true` | | `[acl]` | ACL权限,等同 `can()` 参数值 | `ACLCanType` | - | | `[click]` | 链接回调 | `(record: STData, instance?: STComponent) => void` | - | | `[badge]` | 徽标配置项 | `STColumnBadge` | - | | `[tag]` | 徽标配置项 | `STColumnTag` | - | | `[enum]` | 枚举配置项 | `{ [key: string]: string; [key: number]: string }` | - | | `[widget]` | 小部件配置项 | `STWidgetColumn` | - | | `[noIndex]` | 行号索引开始值 | `number,(item: STData, col: STColumn, idx: number) => number` | `1` | | `[iif]` | 条件表达式
    1、仅赋值 `columns` 时执行一次
    2、可调用 `resetColumns()` 再一次触发 | `(item: STColumn) => boolean` | - | | `[statistical]` | 统计信息 | `STStatisticalType,STStatistical` | - | | `[resizable]` | 调整表头配置项,**不支持多表头** | `STResizable, boolean` | - | - | | `[children]` | 多表头 | `STColumn[]` | - | | `[safeType]` | 安全渲染方式,支持[全局配置](https://ng-alain.com/docs/global-config/zh) | `text,html,safeHtml` | `safeHtml` | | `[customRequest]` | 覆盖默认的请求行为,可以自定义自己的请求实现,例如:Graphql,支持[全局配置](https://ng-alain.com/docs/global-config/zh) | `(options: STCustomRequestOptions) => Observable` | - | ### STColumnTitle | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[text]` | 列标题,`text` 与 `i18n` 必选其一 | `string` | - | | `[i18n]` | 列标题i18n主键,`text` 与 `i18n` 必选其一 | `string` | - | | `[optional]` | 标签可选信息 | `string` | - | | `[optionalHelp]` | 标签可选帮助 | `string` | - | ### STColumnSort | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[default]` | 排序的受控属性 | `ascend,descend` | - | | `[compare]` | 本地数据的排序函数,使用一个函数(参考 [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) 的 compareFunction),`null` 忽略本地排序,但保持排序功能 | `(a: any, b: any) => number, null` | - | | `[key]` | 远程数据的排序时后端相对应的KEY,默认使用 `index` 属性
    若 `multiSort: false` 时:`key: 'name' => ?name=1&pi=1`
    若 `multiSort: true` 允许多个排序 key 存在,或使用 `STMultiSort` 指定多列排序key合并规则 | `string` | - | | `[reName]` | 远程数据的排序时后端相对应的VALUE
    `{ ascend: '0', descend: '1' }` 结果 `?name=1&pi=1`
    `{ ascend: 'asc', descend: 'desc' }` 结果 `?name=desc&pi=1` | `{ ascend?: string, descend?: string }` | - | | `[directions]` | 支持的排序方式,取值为 `'ascend'`, `'descend'`, `null` | `Array<'ascend' \| 'descend' \| null>` | `['ascend', 'descend', null]` | ✅ | ### STColumnFilter | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[type]` | 类型,`keyword` 文本框形式 | `default,keyword,number,date,custom` | `default` | | `[menus]` | 表头的筛选菜单项,至少一项才会生效 | `STColumnFilterMenu[]` | - | | `[fn]` | 本地数据的筛选函数 | `(filter: STColumnFilterMenu, record: STData) => boolean` | - | | `[default]` | 标识数据是否经过过滤,筛选图标会高亮 | `boolean` | - | | `[icon]` | 自定义 fiter 图标
    当 `type='default'` 默认 `filter`
    当 `type='keyword'` 默认 `search` | `string | STIcon` | `filter` | | `[multiple]` | 是否多选 | `boolean` | `true` | | `[confirmText]` | filter 确认按钮文本 | `string` | - | | `[clearText]` | filter 清除按钮文本 | `string` | - | | `[key]` | 远程数据的过滤时后端相对应的KEY,默认使用 `index` 属性 | `string` | - | | `[reName]` | 远程数据的过滤时后端相对应的VALUE | `(list: STColumnFilterMenu[], col: STColumn) => Object` | - | | `[custom]` | 自定义模版 | `TemplateRef<{ $implicit: STColumnFilter; col: STColumn; handle: STColumnFilterHandle }>` | - | | `[showOPArea]` | 是否显示操作区域 | `boolean` | `true` | | `[placeholder]` | 在文字框中显示提示讯息 | `boolean` | `true` | | `[number]` | 类型为 `number` 的配置项 | `Object` | - | | `[date]` | 类型为 `date` 的配置项 | `Object` | - | ### STColumnFilterMenu | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[text]` | 文本
    当 `type: 'keyword'` 时表示 `placeholder` | `string` | - | | `[value]` | 值 | `any` | - | | `[checked]` | 是否选中 | `boolean` | - | | `[acl]` | 权限,等同 `can()` 参数值 | `ACLCanType` | - | ### STColumnButton | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[text]` | 文本与图标共存 | `string | (record: T, btn: STColumnButton) => string` | - | | `[icon]` | 图标与文本共存 | `string | STIcon | ((record: T, btn: STColumnButton) => STIcon | null | undefined)` | - | | `[i18n]` | 文本i18n | `string` | - | | `[type]` | 按钮类型 | `none,del,modal,static,drawer,link` | - | | `[click]` | 点击回调;**函数:** `type=modal` 只会在 `确认` 时触发且 `modal` 参数有效
    **reload:** 重新刷新当前页
    **load:** 重新加载数据,并重置页码为:`1` | `(record: STData, modal?: any, instance?: STComponent) => void | reload` | - | | `[pop]` | 是否需要气泡确认框 | `boolean, string, STColumnButtonPop` | `false` | | `[modal]` | 模态框配置 | `STColumnButtonModal` | - | | `[drawer]` | 抽屉配置 | `STColumnButtonDrawer` | - | | `[children]` | 下拉菜单,当存在时以 `dropdown` 形式渲染;只支持一级 | `STColumnButton[]` | - | | `[acl]` | ACL权限,等同 `can()` 参数值 | `ACLCanType` | - | | `[iif]` | 自定义条件表达式 | `(item: STData, btn: STColumnButton, column: STColumn) => boolean` | `() => true` | | `[iifBehavior]` | 表达式 `false` 值时渲染方式 | `hide,disabled` | `hide` | | `[tooltip]` | 按钮文字提示 | `string` | - | | `[className]` | 按钮 `class` 属性值,例如:`text-error` 异常色,更多参考[样式工具类](/theme/tools) | `string | ((record: T, btn: STColumnButton) => NgClassType | null | undefined)` | - | ### STIcon | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[type]` | 图标类型 | `string` | - | - | | `[theme]` | 图标主题风格 | `outline | twotone | fill` | `outline` | ✅ | | `[spin]` | 是否有旋转动画 | `boolean` | `false` | ✅ | | `[twoToneColor]` | 仅适用双色图标,设置双色图标的主要颜色,仅对当前 icon 生效 | `string` | - | ✅ | | `[iconfont]` | 指定来自 IconFont 的图标类型 | `string` | - | ✅ | ### STColumnButtonModal | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[component]` | 目标组件对象 | `any` | - | - | | `[params]` | 目标组件的接收参数对象 | `(record: STData) => Object` | - | - | | `[paramsName]` | 目标组件的接收参数名,若目标组件接收值为空时,检查 [global-config.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/global-config.module.ts#L52) 全局设置 | `string` | `record` | ✅ | | `[size]` | 对话框大小,支持数字类型 | `'sm','md','lg','xl'` | `'lg'` | ✅ | | `[exact]` | 是否精准(默认:`true`),若返回值非空值(`null`或`undefined`)视为成功,否则视为错误 | `boolean` | `true` | ✅ | | `[includeTabs]` | 是否包裹标签页,修复模态包含标签间距问题 | `boolean` | - | - | | `[modalOptions]` | 对话框 [ModalOptions](https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/components/modal/modal-types.ts) 参数 | `any` | - | ✅ | ### STColumnButtonDrawer | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[title]` | 标题 | `any` | - | - | | `[component]` | 目标组件对象 | `any` | - | - | | `[params]` | 目标组件的接收参数对象 | `(record: STData) => Object` | - | - | | `[paramsName]` | 目标组件的接收参数名,若目标组件接收值为空时,检查 [global-config.module.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/global-config.module.ts#L52) 全局设置 | `string` | `record` | ✅ | | `[size]` | 抽屉大小,支持数字类型 | `'sm','md','lg','xl'` | `'md'` | ✅ | | `[drawerOptions]` | 抽屉 [NzDrawerOptions](https://ng.ant.design/components/drawer/zh#nzdraweroptions) 参数 | `any` | - | ✅ | | `[footer]` | 是否包含底部工具条 | `boolean` | `true` | ✅ | | `[footerHeight]` | 底部工具条高度 | `number` | `55` | ✅ | ### STColumnSelection | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[text]` | 文本 | `string` | - | | `[select]` | 选择项点击回调,允许对参数 `data.checked` 进行操作 | `(data: STData[]) => void` | - | | `[acl]` | ACL权限,等同 `can()` 参数值 | `ACLCanType` | - | ### STColumnYn | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[truth]` | 真值条件 | `any` | `true` | | `[yes]` | 徽章 `true` 时文本 | `string` | `是` | | `[no]` | 徽章 `false` 时文本 | `string` | `否` | | `[mode]` | 显示模式 | `full,icon,text` | `icon` | ### STcolumnCurrency | 成员 | 说明 | 类型 | 默认值 | |----------|-------------|------|---------| | `[type]` | 货币渲染类型 | `commas, mega` | `commas` | | `[format]` | 见 [CurrencyService.format](https://ng-alain.com/util/format/zh#format) | `CurrencyFormatOptions` | - | ### STColumnBadge | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[text]` | 文本 | `string` | - | | `[color]` | 徽标颜色值 | `success,processing,default,error,warning` | - | | `[tooltip]` | 文字提示 | `string` | - | ### STColumnTag | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[text]` | 文本 | `string` | - | | `[color]` | Tag颜色值 | `string` | - | | `[tooltip]` | 文字提示 | `string` | - | ### STWidgetColumn | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[type]` | 指定类型名,可通过定义 `STWidgetRegistry` 来定制,[例如](https://github.com/ng-alain/delon/blob/master/src/app/shared/st-widget/st-widget.module.ts) | `string` | - | | `[params]` | 目标组件的参数 | `(options: { record: STData; column: STColumn }) => {}` | - | ### STWidthMode | 成员 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[type]` | 类型 | `strict,default` | `default` | ✅ | | `[strictBehavior]` | `strict` 的行为类型 | `wrap,truncate` | `truncate` | ✅ | ### STStatistical | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[type]` | 统计类型 | `STStatisticalType | STStatisticalFn` | - | | `[digits]` | 保留小数位数 | `number` | `2` | | `[currency]` | 是否需要货币格式化,默认当 `type` 为 `STStatisticalFn`、 `sum`、`average`、`max`、`min` 时为 `true` | `boolean` | - | **STStatisticalFn** ```ts ( values: number[], col: STColumn, list: STData[], rawData?: any, ) => STStatisticalResult ``` ### STResizable | 成员 | 说明 | 类型 | 默认值 | |----------|-------------|------|---------| | `[disabled]` | Disable resize | `boolean` | `true` | | `[bounds]` | 调整尺寸的边界 | `window, parent, ElementRef` | `window` | | `[maxWidth]` | 最大宽度(超过边界部分忽略) | `number` | `360` | | `[minWidth]` | 最小宽度 | `number` | `60` | | `[preview]` | 开启预览 | `boolean` | `true` | ## Theme | 成员 | 说明 | 默认值 |----|----|----| | `@nz-table-img-radius` | 图像圆角大小 | `4px` | | `@nz-table-img-margin-right` | 图像右边距 | `4px` | | `@nz-table-img-max-height` | 图像最大高度 | `32px` | | `@nz-table-img-max-width` | 图像最大宽度 | `32px` | | `@nz-table-even-background` | 行奇偶背景色 | `none` | | `@nz-table-rep-max-width` | 可视区域小于时触发 | `` | | `@nz-table-rep-min-width` | 可视区域大于时触发 | `` | | `@nz-table-rep-header-background` | 响应式下标题背景颜色 | `@border-color-split` | | `@nz-table-rep-even-background` | 响应式下奇偶颜色 | `#f9f9f9` | | `@nz-table-rep-column-name-color` | 响应式下文本颜色 | `rgba(0, 0, 0, 0.5)` | | `@nz-table-rep-column-name-text-align` | 响应式下标题文本对齐方式 | `right` | | `@nz-table-rep-column-name-width` | 响应式下标题宽度 | `100px` | | `@nz-table-rep-column-name-padding-right` | 响应式下标题与内容右边距 | `8px` | | `@table-row-hover-bg` | 行悬停背景色 | `#fafafa` | | `@st-btn-disabled-color` | 禁用按钮的文本颜色 | `rgba(0, 0, 0, 0.25)` | | `@st-title-optional-color` | 标题可选文本颜色 | `rgba(0, 0, 0, 0.35)` | | `@st-resizable-handle-width` | 拖拽宽度 | `1px` | | `@st-resizable-handle-height` | 拖拽高度 | `60%` | | `@st-resizable-handle-color` | 拖拽颜色 | `@border-color-base` | --- --- title: string subtitle: 文本框 type: Widgets order: 3 --- 默认小部件,一般用于字符串元素。 ## API ### schema 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[maxLength]` | 表单最大长度 | `number` | - | | `[readOnly]` | 禁用状态 | `boolean` | - | ### ui 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[type]` | 等同 input 的 `type` 值,例如:`password` | `string` | - | | `[placeholder]` | 在文字框中显示提示讯息 | `string` | - | | `[variant]` | 变体 | `outlined,borderless,filled,underlined` | `outlined` | | `[autocomplete]` | 自动完成功能的表单 | `HTML Attribute` | - | | `[autofocus]` | 当页面加载时获得焦点 | `HTML Attribute` | - | | `[addOnBefore]` | 前置标签,等同 `nzAddOnBefore` | `string` | - | | `[addOnAfter]` | 后置标签,等同 `nzAddOnAfter` | `string` | - | | `[addOnBeforeIcon]` | 前置Icon,等同 `nzAddOnBeforeIcon` | `string` | - | | `[addOnAfterIcon]` | 后置Icon,等同 `nzAddOnAfterIcon` | `string` | - | | `[prefix]` | 带有前缀图标的 input,等同 `nzPrefix` | `string` | - | | `[prefixIcon]` | 前缀图标,等同 `nzPrefixIcon` | `string` | - | | `[suffix]` | 带有后缀图标的 input,等同 `nzSuffix` | `string` | - | | `[suffixIcon]` | 后缀图标,等同 `nzSuffixIcon` | `string` | - | | `[changeDebounceTime]` | `change` 事件节流与顺序控制的阀值 | `number` | - | | `[changeMap]` | 转换数据,相当于 `switchMap` 操作 | `(val: string) => Observable` | - | | `[change]` | 内容变更事件 | `(val: string) => void` | - | | `[focus]` | 焦点事件 | `(e: FocusEvent) => void` | - | | `[blur]` | 失焦事件 | `(e: FocusEvent) => void` | - | | `[enter]` | 回车事件 | `(e: KeyboardEvent) => void` | - | --- --- type: CURD title: sv subtitle: 查看 cols: 1 order: 2 module: import { SVModule } from '@delon/abc/sv'; --- 查看栅格系统是在原 [Grid 栅格](https://ng.ant.design/components/grid/zh) 基础上构建更高阶的组件,用于简化查看页布局。 ## API ### sv-container | 参数 | 说明 | 类型 | 默认值 | 全局配置 | |----|----|----|-----|------| | `[sv-container]` | 指定信息最多分几列展示,最终一行几列由 col 配置结合[响应式规则](/theme/responsive)决定, | `'1','2','3','4','5','6'` | - | - | | `[col]` | 指定信息最多分几列展示,最终一行几列由 col 配置结合[响应式规则](/theme/responsive)决定 | `'1','2','3','4','5','6'` | `3` | ✅ | | `[size]` | 大小 | `'small','large'` | `'large'` | ✅ | | `[layout]` | 布局 | `'horizontal','vertical'` | `'horizontal'` | ✅ | | `[gutter]` | 间距 | `number` | `32` | ✅ | | `[labelWidth]` | 默认标签文本宽度 | `number` | - | ✅ | | `[default]` | 默认是否显示默认文本 | `boolean` | `true` | ✅ | | `[title]` | 标题 | `string,TemplateRef` | - | - | | `[noColon]` | 默认是否不显示 label 后面的冒号 | `boolean` | `false` | - | | `[bordered]` | 是否展示边框 | `boolean` | `false` | - | ### sv | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[col]` | 指定信息最多分几列展示,最终一行几列由 col 配置结合[响应式规则](/theme/responsive)决定,继承 `sv-container` | `'1','2','3','4','5','6'` | - | | `[label]` | 标签 | `string,TemplateRef` | - | | `[unit]` | 单位 | `string,TemplateRef` | - | | `[default]` | 是否显示默认文本,继承 `sv-container` | `boolean` | - | | `[type]` | 类型 | `'primary','success','danger','warning'` | - | | `[optional]` | 标签可选信息 | `string, TemplateRef` | - | | `[optionalHelp]` | 标签可选帮助 | `string, TemplateRef` | - | | `[optionalHelpColor]` | 标签可选帮助背景颜色 | `string` | - | | `[noColon]` | 是否不显示 label 后面的冒号 | `boolean` | `false` | - | | `[hideLabel]` | 是否隐藏当前 `label` | `boolean` | `false` | ### sv-title 用于展示标题,单独一行。 ### sv-value 值展示。 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[prefix]` | 前缀 | `string` | - | | `[unit]` | 单位 | `string` | - | | `[tooltip]` | 文字提示内容 | `string, TemplateRef` | - | | `[size]` | 大小 | `'large','small','default'` | `default` | --- --- title: tag subtitle: 标签 type: Non-built-in widgets --- 进行标记和分类的小标签,**注:** 只支持 `checkable` 标签模式。 ## 如何使用 非内置模块,需要额外在 [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9) 注册 `withTagWidget`。 ## API ### schema 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[enum]` | 数据源 | `SFSchemaEnumType[]` | - | ### ui 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[asyncData]` | 异步数据源 | `() => Observable` | - | | `[mode]` | 设定标签工作的模式 | `'closeable'|'default'|'checkable'` | `'checkable'` | | `[onClose]` | 关闭时的回调,在 `nzMode="closable"` 时可用 | `(e:MouseEvent) => void` | - | | `[checkedChange]` | 设置标签的选中状态的回调 | `(status: boolean) => void` | - | --- --- type: Layout title: tag-select subtitle: 标签选择器 cols: 1 module: import { TagSelectModule } from '@delon/abc/tag-select'; --- 增加标签的展开与收进功能。 ## API ### tag-select | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[expandable]` | 是否启用 `展开与收进` | `boolean` | `true` | | `(change)` | 展开与收进回调函数,参数:`boolean` | `EventEmitter` | - | --- --- title: text subtitle: 文本 type: Widgets --- 一般用于直接显示文本。 ## 规则 - 强制取消 `required` 效果 - 若不指定 `defaultText` 值不存在时自动渲染 `-` ## API ### ui 属性 | 参数 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[defaultText]` | 当值不存在时所指定的文本 | `string` | `-` | | `[html]` | 是否支持HTML | `boolean` | `true` | --- --- title: textarea subtitle: 多行文本框 type: Widgets --- 一般用于多行字符串。 ## API ### schema 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[maxLength]` | 表单最大长度 | `number` | - | | `[readOnly]` | 禁用状态 | `boolean` | - | ### ui 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[size]` | 大小,等同 `nzSize` | `string` | - | | `[placeholder]` | 在文字框中显示提示讯息 | `string` | - | | `[autosize]` | 自适应内容高度,可设置对象:`{ minRows: 2, maxRows: 6 }` | `{ minRows?: number; maxRows?: number }` | `{ minRows: 1, maxRows: 0 }` | | `[variant]` | 变体 | `outlined,borderless,filled,underlined` | `outlined` | | `[maxCharacterCount]` | `textarea` 数字提示显示的最大值 | `number` | - | | `[computeCharacterCount]` | 自定义计算 `characterCount` 的函数 | `(v: string) => number` | `v => v.length` | | `[change]` | 内容变更事件 | `(val: string) => void` | - | | `[focus]` | 焦点事件 | `(e: FocusEvent) => void` | - | | `[blur]` | 失焦事件 | `(e: FocusEvent) => void` | - | --- --- type: Theme title: theme-btn subtitle: 组件-切换样式 cols: 1 order: 1001 module: import { ThemeBtnModule } from '@delon/theme/theme-btn'; --- 用于在运行过程中切换定制样式文件,从而起到换在线换肤功能。 ## API ### layout-default | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[types]` | 类型列表 | `ThemeBtnType[]` | `[ { key: 'default', text: 'Default Theme' }, { key: 'dark', text: 'Dark Theme' }, { key: 'compact', text: 'Compact Theme' }, ]` | | `[devTips]` | 开发提示 | `String` | `When the dark.css file can't be found, you need to run it once: npm run theme` | | `[deployUrl]` | 文件将部署到的 URL,一般到使用 `ng b --deploy-url` 时需要 | `String` | `-` | | `(themeChange)` | 主题变更通知 | `EventEmitter` | `-` | --- --- title: time subtitle: 时间 type: Non-built-in widgets --- 输入或选择时间的控件。 ## 如何使用 非内置模块,需要额外在 [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9) 注册 `withTimeWidget`。 ## 注意事项 - 格式化分为:**数据格式化**表示表单数据和**显示格式化**显示数据(等同 [nzFormat](https://ng.ant.design/components/time-picker/zh#api) 值) - 所有 **数据格式化** 单位,参考 [date-fns format](https://date-fns.org/v1.29.0/docs/format)(国内镜像:[moment format](http://momentjs.cn/docs/#/displaying/format/)) - 指定 `schema.format` 则必须遵守 [RFC3339](https://tools.ietf.org/html/rfc3339#section-5.6) 时间格式,否则都视为格式错误,默认的数据格式化: - `time`、`full-time` 默认 `HH:mm:ss` - 不指定 `schema.format` 根据 `schema.type` 值按以下规则处理(允许通过[全局配置](/docs/global-config)替换)数据格式化: - `string` 默认 `HH:mm:ss` - `number` 默认 `T` 13位 Unix Timestamp - 由于 `disabledHours`、`disabledMinutes`、`disabledSeconds` 组合导致时间格式被破坏,可能会导致无法正常显示或显示不正确时可以指定一个完整的 `Date` 对象给默认值(`schema.default` 或 `formData`) ## API ### schema 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[readOnly]` | 禁用状态 | `boolean` | - | | `[format]` | 数据格式类型 | `string` | - | ### ui 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[size]` | 大小,等同 `nzSize` | `string` | - | | `[placeholder]` | 在文字框中显示提示讯息 | `string` | - | | `[format]` | 数据格式化 | `string` | `HH:mm:ss` | | `[displayFormat]` | 显示格式化,(等同 [nzFormat](https://ng.ant.design/components/time-picker/zh#api) 值) | `string` | `HH:mm:ss` | | `[utcEpoch]` | 是否UTC新纪元(表示从 `1970` 开始计毫秒数),当 `type='number'` 时有效 | `boolean` | `false` | | `[allowEmpty]` | 是否展示清除按钮 | `boolean` | `true` | | `[clearText]` | 清除按钮的提示文案 | `string` | `清除` | | `[defaultOpenValue]` | 设置面板打开时默认选中的值 | `Date` | `new Date()` | | `[disabledHours]` | 禁止选择部分小时选项 | `() => number[]` | - | | `[disabledMinutes]` | 禁止选择部分分钟选项 | `(hour: number) => number[]` | - | | `[disabledSeconds]` | 禁止选择部分秒选项 | `(hour: number, minute: number) => number[]` | - | | `[hideDisabledOptions]` | 隐藏禁止选择的选项 | `boolean` | `false` | | `[hourStep]` | 小时选项间隔 | `number` | `1` | | `[minuteStep]` | 分钟选项间隔 | `number` | `1` | | `[secondStep]` | 秒选项间隔 | `number` | `1` | | `[popupClassName]` | 弹出层类名 | `string` | - | | `[change]` | 时间发生变化的回调 | `(value: Date) => void` | - | | `[openChange]` | 面板打开/关闭时的回调 | `(status: boolean) => void` | - | | `[nowText]` | 此刻按钮文本 | `string` | - | | `[okText]` | 确认按钮文本 | `string` | - | --- --- title: tinymce subtitle: Tinymce富文本 type: Third Widgets --- Tinymce富文本。 ## 如何使用 **安装依赖** `npm i -S ngx-tinymce` - 1、在 `app.config.ts` 下注册 `provideTinymce()` - 2、在 [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9) 注册 `withTinymceWidget`。 > 关于更多 tinymce 配置请参考 [ngx-tinymce](https://github.com/cipchk/ngx-tinymce)。 ## API ### ui 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | config | `any` | | see [configure](https://www.tinymce.com/docs/configure/integration-and-setup/) | | loading | `string,TemplateRef` | - | Loading status of tinymce | | disabled | `boolean` | `false` | Set tinymce mode is `readonly` if `true` | | inline | `boolean` | `false` | Inline editor | | delay | `number` | 0 | Delayed rendering, unit is 'millisecond' | | placeholder | `string` | - | Placeholder for tinymce, **NOTE:** dependent on [tinymce-placeholder](https://github.com/mohan/tinymce-placeholder) | | ready | `EventEmitter` | - | Tinymce ready callback | --- --- order: 4 title: TitleService subtitle: 页面标题服务 type: Service --- 用于设置页面标题,一般监听路由变化并重新刷新标题,例如:[app.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/app.ts#L54);同时标题的默认数据来源于 `MenuService`。 **建议:** 在 Angular 启动服务([startup.service.ts](https://github.com/ng-alain/ng-alain/blob/master/src/app/core/startup/startup.service.ts))过程中设置 `prefix` 或 `suffix` 值来调整统一的标题前后缀。 ## 获取顺序 根据以下顺序获取 `title` 值: 1. 路由配置 `{ data: { title: 'page name', titleI18n: 'page-name' } as RouteTitle }` 2. 根据当前 URL 解析菜单数据 3. 页面 `alain-default__content-title` 或 `page-header__title` 中获取 `h1` 内容 4. 默认标题名 ## API | 名称 | 类型 | 描述 | | ---------------------------------------------- | ---------- | -------------- | | `default` | `property` | 设置默认标题名 | | `selector` | `property` | 设置默认CSS选择器字符串 | | `separator` | `property` | 设置分隔符 | | `prefix` | `property` | 设置前缀 | | `suffix` | `property` | 设置后缀 | | `reverse` | `property` | 设置是否反转 | | `setTitle(title?: string | string[])` | `method` | 设置标题,受限于 [#1261](https://github.com/ng-alain/ng-alain/issues/1261) 会有 `25ms` 的延迟 | | `setTitleByI18n(key: string, params?: Object)` | `method` | 设置国际化标题 | --- --- title: token subtitle: Token type: Tools --- ## WINDOW 访问全局 `window` 对象。 ## PAGE_VISIBILITY 通过 `visibilitychange` 事件来监听浏览器选项卡是否可见,一般用于当用户离开应用时暂时中断后端持续发送请求时。 --- --- title: transfer subtitle: 穿梭框 type: Non-built-in widgets --- 双栏穿梭选择框。 ## 如何使用 非内置模块,需要额外在 [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9) 注册 `withTransferWidget`。 ## 注意事项 - `default` 值被当成 `direction: 'right'` 表示右栏中 ## API ### schema 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[enum]` | 数据源 | `SFSchemaEnumType[]` | - | ### ui 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[asyncData]` | 异步数据源 | `() => Observable` | - | | `[titles]` | 标题集合,顺序从左至右 | `string[]` | `['', '']` | | `[operations]` | 操作文案集合,顺序从下至上 | `string[]` | `['', '']` | | `[listStyle]` | 两个穿梭框的自定义样式,以`ngStyle`写法标题 | `object` | - | | `[itemUnit]` | 单数单位 | `string` | `项目` | | `[itemsUnit]` | 复数单位 | `string` | `项目` | | `[showSearch]` | 是否显示搜索框 | `boolean` | `false` | | `[filterOption]` | 接收 `inputValueoption` 两个参数,当 `option` 符合筛选条件时,应返回 `true`,反之则返回 `false`。 | - | - | | `[searchPlaceholder]` | 搜索框的默认值 | `string` | - | | `[notFoundContent]` | 当列表为空时显示的内容 | `string` | - | | `[canMove]` | 穿梭时二次校验。 | `function` | - | | `[oneWay]` | 展示为单向样式 | `boolean` | `false` | | `(change)` | 选项在两栏之间转移时的回调函数 | `(options: TransferChange) => void` | - | | `(searchChange)` | 搜索框内容时改变时的回调函数 | `(options: TransferSearchChange) => void` | - | | `(selectChange)` | 选中项发生改变时的回调函数 | `(options: TransferSelectChange) => void` | - | --- --- title: tree-select subtitle: 树选择 type: Non-built-in widgets --- 树型选择控件。 **注意:** - `tree-select` 的数据源必须包含 `title`、`key` 键名 ## 如何使用 非内置模块,需要额外在 [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9) 注册 `withTreeSelectWidget`。 ## API ### schema 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[enum]` | 数据源 | `SFSchemaEnumType[]` | - | | `[readOnly]` | 禁用状态 | `boolean` | - | ### ui 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[asyncData]` | 异步数据源 | `() => Observable` | - | | `[size]` | 大小,等同 `nzSize` | `string` | `default` | | `[placeholder]` | 在文字框中显示提示讯息 | `string` | - | | `[notFoundContent]` | 当下拉列表为空时显示的内容 | `string` | - | | `[allowClear]` | 支持清除 | `boolean` | `false` | | `[clearValue]` | 清空时默认值 | `any` | `undefined` | | `[dropdownMatchSelectWidth]` | 下拉菜单和选择器同宽 | `boolean` | `true` | | `[dropdownStyle]` | 下拉菜单的 style 属性 | `object` | - | | `[dropdownClassName]` | 下拉菜单的 className 属性 | `string` | - | | `[multiple]` | 支持多选(当设置 `checkable` 时自动变为true) | `boolean` | `false` | | `[hideUnMatched]` | 搜索隐藏未匹配的节点 | `boolean` | `false` | | `[checkable]` | 节点前添加 Checkbox 复选框 | `boolean` | `false` | | `[checkStrictly]` | checkable 状态下节点选择完全受控(父子节点选中状态不再关联) | `boolean` | `false` | | `[showIcon]` | 是否展示 TreeNode title 前的图标,没有默认样式 | `boolean` | `false` | | `[showExpand]` | 节点前添加展开图标 | `boolean` | `true` | | `[showLine]` | 节点前添加展开图标 | `boolean` | `false` | | `[defaultExpandAll]` | 默认展开所有树节点 | `boolean` | `false` | | `[displayWith]` | 如何在输入框显示所选的节点值的方法 | `(node: NzTreeNode) => string | undefined` | `(node: NzTreeNode) => node.title` | | `[expandedKeys]` | 默认展开指定的树节点 | `string[]` | - | | `[maxTagCount]` | 最多显示多少个 tag | `number` | - | | `[maxTagPlaceholder]` | 隐藏 tag 时显示的内容 | `TemplateRef<{ $implicit: NzTreeNode[] }>` | - | | `[treeTemplate]` | 自定义节点 | `TemplateRef<{ $implicit: NzTreeNode; origin: NzTreeNodeOptions }>` | - | | `[expandChange]` | 点击展开树节点图标调用 | `(e: NzFormatEmitEvent) => Observable` | - | | `[virtualHeight]` | 虚拟滚动的总高度 | `string` | `-` | | `[virtualItemSize]` | 虚拟滚动时每一列的高度,与 [cdk itemSize](https://material.angular.io/cdk/scrolling/api) 相同 | `number` | `28` | | `[virtualMaxBufferPx]` | 缓冲区最大像素高度,与 [cdk maxBufferPx](https://material.angular.io/cdk/scrolling/api) 相同 | `number` | `500` | | `[virtualMinBufferPx]` | 缓冲区最小像素高度,低于该值时将加载新结构,与 [cdk minBufferPx](https://material.angular.io/cdk/scrolling/api) 相同 | `number` | `28` | > 异步数据务必先指定初始化数据(使用 `enum`、`asyncData` 选其一),否则无法触发 `expandChange`。 --- --- type: G2 title: trend subtitle: 趋势标记 cols: 1 module: import { TrendModule } from '@delon/chart/trend'; --- 趋势符号,标记上升和下降趋势。通常用绿色代表“好”,红色代表“不好”,股票涨跌场景除外。 ## API ### trend | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[colorful]` | 是否彩色标记 | `boolean` | `true` | | `[flag]` | 上升下降标识 | `'up','down'` | - | | `[reverseColor]` | 颜色反转 | `boolean` | `false` | --- --- title: upload subtitle: 上传 type: Non-built-in widgets --- 文件选择上传和拖拽上传控件。 ## 如何使用 非内置模块,需要额外在 [json-schema](https://github.com/ng-alain/ng-alain/blob/master/src/app/shared/json-schema/index.ts#L9) 注册 `withUploadWidget`。 ## 注意事项 - **务必** 指定 `resReName` 来获取正确数据 - `multiple` 决定返回数组或者单体数据 - 若指定 `enum` 或 `asyncData` 将被转化成 `fileList` (`nzFileList`) 值,且**务必**初始保证一个 `response` 属性表示远程数据并 `resReName` 能正确获取 - 图像预览:默认使用 `nzModal` 来显示包含文件对象的 `url` 或 `thumbUrl` 值 ## API ### schema 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[readOnly]` | 禁用状态 | `boolean` | - | ### ui 属性 | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[asyncData]` | 异步数据源 | `() => Observable` | - | | `[type]` | 上传类型 | `select,drag` | `select` | | `[text]` | 按钮文本 | `string` | `点击上传` | | `[hint]` | 提醒文本,drag 时有效 | `string` | `支持单个或批量,严禁上传公司数据或其他安全文件` | | `[resReName]` | 重命名返回参数,支持 `a.b.c` 的嵌套写法,若不指定表示整个返回体 | `string` | - | | `[urlReName]` | 重命名预览图像URL返回参数,支持 `a.b.c` 的嵌套写法,若不指定表示使用文件对象的 `url`、`thumbUrl` 值 | `string` | - | | `[action]` | 必选参数, 上传的地址 | `string, ((file: UploadFile) => string, Observable)` | - | | `[accept]` | 接受上传的文件类型, 详见 [input accept Attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-accept) | `string, string[]` | - | | `[limit]` | 限制单次最多上传数量,`multiple` 打开时有效;`0` 表示不限 | `number` | `0` | | `[filter]` | 自定义过滤器 | `UploadFilter[]` | - | | `[fileList]` | 文件列表 | `UploadFile[]` | - | | `[fileSize]` | 限制文件大小,单位:KB;`0` 表示不限 | `number` | `0` | | `[fileType]` | 限制文件类型,例如:`image/png,image/jpeg,image/gif,image/bmp` | `string` | - | | `[headers]` | 设置上传的请求头部 | `Object, (file: UploadFile) => {} | Observable<{}>` | - | | `[listType]` | 上传列表的内建样式 | `text,picture,picture-card` | `text` | | `[showUploadList]` | 是否展示列表, 可设为一个对象,用于单独设定 `showPreviewIcon` 和 `showRemoveIcon` | `boolean` | `true` | | `[multiple]` | 是否支持多选文件,`IE10+` 支持。开启后按住 `ctrl` 可选择多个文件。 | `boolean` | `false` | | `[name]` | 发到后台的文件参数名 | `string` | `file` | | `[data]` | 上传所需参数或返回上传参数的方法 | `Object, (file: UploadFile) => {} | Observable<{}>` | - | | `[withCredentials]` | 上传请求时是否携带 cookie | `boolean` | `false` | | `[directory]` | 支持上传文件夹([caniuse](https://caniuse.com/#feat=input-file-directory)) | `boolean` | `false` | | `[openFileDialogOnClick]` | 点击打开文件对话框 | `boolean` | `true` | | `[beforeUpload]` | 上传文件之前的钩子,参数为上传的文件,若返回 `false` 则停止上传 | `(file: UploadFile, fileList: UploadFile[]) => boolean|Observable` | - | | `[customRequest]` | 通过覆盖默认的上传行为,可以自定义自己的上传实现 | `(item: UploadXHRArgs) => Subscription` | - | | `[remove]` | 点击移除文件时的回调,返回值为 `false` 时不移除 | `(file: UploadFile) => boolean|Observable` | - | | `[preview]` | 点击文件链接或预览图标时的回调 | `(file: UploadFile) => void` | - | | `[previewFile]` | 自定义文件预览逻辑 | `(file: UploadFile) => Observable` | - | | `[download]` | 点击下载文件时的回调,如果没有指定,则默认跳转到文件 url 对应的标签页 | `(file: UploadFile) => void` | - | | `[transformFile]` | 在上传之前转换文件。支持返回一个 Observable 对象 | `(file: UploadFile) => UploadTransformFileType` | - | | `[change]` | 上传文件改变时的状态 | `(args: UploadChangeParam) => void` | - | --- --- title: visibleIf subtitle: 条件表达式 type: Examples --- `sf` 支持两种类型的条件表达式,分别为: - JSON Schema 标准 [if-then-else](https://ajv.js.org/json-schema.html#if-then-else) - 灵活性更强的 `visibleIf` --- --- type: Basic title: xlsx order: 6 subtitle: Excel 操作 cols: 1 module: import { XlsxModule } from '@delon/abc/xlsx'; --- 一个基于 [SheetJS](http://sheetjs.com/) 的Excel文件操作,它是目前在浏览器中包含最全的Excel操作的脚本库。 > 注:你也可以使用 [js-xlsx](https://github.com/protobi/js-xlsx) 是另一个 Fork sheetjs 的类库,它提供包括:图片、样式等额外选项。最后你利用 `callback` 选项重要渲染你的 excel。 ## 依赖 由于 sheetjs 脚本大小以及对 Excel 的操作并不是刚需的原因,因此采用一种延迟加载脚本的形式,可以通过[全局配置](/docs/global-config)配置来改变默认 CDN 路径,默认情况下使用 `https://cdn.jsdelivr.net/npm/xlsx/dist/xlsx.full.min.js`。 **使用本地路径** ```bash yarn add -D xlsx ``` ```json // angular.json { "glob": "**/{xlsx.full.min,cpexcel}.js", "input": "./node_modules/xlsx/dist", "output": "assets/xlsx/" } ``` ```ts // global-config.module.ts const alainConfig: AlainConfig = { xlsx: { url: '/assets/xlsx/xlsx.full.min.js', modules: [`/assets/xlsx/cpexcel.js`] } }; ``` ## API ### LazyService | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `import(fileOrUrl: File | string)` | 导入Excel,返回 JSON | `Promise<{ [key: string]: any[][] }>` | - | | `export(options: XlsxExportOptions)` | 导出Excel | `Promise` | - | | `numberToSchema(val: number)` | 数值转符号名 | `string` | - | ### XlsxExportOptions | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[sheets]` | 数据源 | `{ [sheet: string]: WorkSheet } | XlsxExportSheet[]` | - | | `[filename]` | Excel文件名 | `string` | `export.xlsx` | | `[opts]` | Excel写入选项,见 [WritingOptions](https://github.com/SheetJS/sheetjs/blob/master/docbits/81_writeopts.md) | `WritingOptions` | - | | `[callback]` | 保存前触发 | `(wb: WorkBook) => void` | - | ### [xlsx] xlsx 指令。 ```html
    导出
    ``` --- --- order: 1 title: yn subtitle: 徽章 type: Pipe --- `yn` 将boolean类型徽章化。 ```html Output: ``` --- --- type: Basic title: zip order: 7 subtitle: Zip 操作 cols: 1 --- 一个基于 [jszip](http://stuk.github.io/jszip/) 的Zip文件操作。 ## 依赖 由于 jszip 脚本大小以及对 Zip 的操作并不是刚需的原因,因此采用一种延迟加载脚本的形式,可以通过[全局配置](/docs/global-config)配置来改变默认 CDN 路径,默认情况下使用 `https://cdn.jsdelivr.net/npm/jszip@3/dist/jszip.min.js`。 **使用本地路径** ```json // angular.json { "glob": "**/jszip.min.js", "input": "./node_modules/jszip/dist", "output": "assets/jszip/" } ``` ```ts // global-config.module.ts const alainConfig: AlainConfig = { zip: { url: '/assets/jszip/jszip.min.js' } }; ``` ## API ### ZipService 成员 | 说明 | 类型 | 默认值 ----|------|-----|------ `read(fileOrUrl: File | string, options?: JSZip.JSZipLoadOptions)` | 解压 | `Promise` | - `create()` | 创建 Zip 实例,用于创建压缩文件 | `Promise` | - `pushUrl(zip: JSZip, path: string, url: string)` | 下载URL资源并写入 zip | `Promise` | - `save(zip: JSZip, options?: ZipWriteOptions)` | 保存Zip | `Promise` | - --- --- type: Theme order: 110 title: 空白布局 --- 用于无须任何顶部和侧边区域,一般用于高定制性页面,诸如大屏幕数据等。默认布局所有参数都以 `@alain-blank-` 开头。 ## 使用方式 在 `src/styles.less` 引入: ```less @import '@delon/theme/layout-blank/style/index'; ``` ## 参数 | 名称 | 默认值 | 功能 | | --- | --- | --- | | `@prefix` | `.alain-blank` | 布局样式前缀 | | `@bg` | `#f5f7fa` | 背景色 | | `@content-padding-vertical` | `0` | 垂直内边距 | | `@content-padding-horizontal` | `16px` | 水平内边距 | --- --- type: Theme order: 100 title: 默认布局 --- 默认布局所有参数都以 `@alain-default-` 开头。 ## 使用方式 ### 1、导入样式 在 `src/styles.less` 引入: ```less @import '@delon/theme/layout-default/style/index'; ``` ### 2、使用 `layout-default` 组件 在 `src/app/layout/basic/basic.ts` 创建一个新的布局: ```ts import { Component } from '@angular/core'; import { SettingsService, User } from '@delon/theme'; import { LayoutDefaultOptions } from '@delon/theme/layout-default'; import { environment } from '@env/environment'; @Component({ selector: 'layout-basic', template: `
    • {{ 'menu.account.center' | i18n }}
    • {{ 'menu.account.settings' | i18n }}
    @if (showSettingDrawer) { } `, }) export class LayoutBasic { readonly user = inject(SettingsService).user; protected options: LayoutDefaultOptions = { logoExpanded: `./assets/logo-full.svg`, logoCollapsed: `./assets/logo.svg` }; protected searchToggleStatus = signal(false); protected showSettingDrawer = !environment.production; } ``` 通过 `LayoutDefaultService` 服务可以在运行时动态管理布局。除此之外,在布局的操作都可以通过 `SettingsService.notify` 来订阅布局的变化(例如:侧边栏的展开与收缩等),注意所有布局相关的变化都会通过这个接口,所以需要做好 `filter` 操作。 ## API ### layout-default | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[options]` | 选项 | `LayoutDefaultOptions` | `-` | | `[asideUser]` | 侧边用户信息 | `TemplateRef` | `-` | | `[asideBottom]` | 侧边底部信息 | `TemplateRef` | `-` | | `[nav]` | 导航信息 | `TemplateRef` | `-` | | `[content]` | 内容信息 | `TemplateRef` | `-` | | `[customError]` | 自定义异常路由错误消息,当 `null` 时表示不显示错误消息 | `string, null` | `Could not load ${evt.url} route` | | `[fetchingStrictly]` | 是否完全受控顶部加载动画状态 | `boolean` | `false` | | `[fetching]` | 顶部加载动画状态 | `boolean` | `false` | ### LayoutDefaultOptions | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[logo]` | 自定义 Logo 区域 | `TemplateRef` | - | | `[logoExpanded]` | 展开时 Logo 地址 | `string` | `./assets/logo-full.svg` | | `[logoCollapsed]` | 收缩时 Logo 地址 | `string` | `./assets/logo.svg` | | `[logoFixWidth]` | 指定固定 Logo 宽度 | `number` | - | | `[logoLink]` | 指定 Logo 路由地址 | `string` | `/` | | `[hideAside]` | 隐藏侧边栏,同时不显收缩图标按钮 | `boolean` | `false` | | `[hideHeader]` | 隐藏顶栏 | `boolean` | `false` | | `[showHeaderCollapse]` | 是否在顶栏显示菜单折叠按钮 | `boolean` | `true` | | `[showSiderCollapse]` | 是否在侧边栏底部显示菜单折叠按钮 | `boolean` | `false` | ### layout-default-nav | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[disabledAcl]` | `acl` 校验失败时以 `disabled` 状态显示 | `boolean` | `false` | | `[autoCloseUnderPad]` | 小于Pad宽度时路由切换后自动关闭侧边栏 | `boolean` | `true` | | `[recursivePath]` | 自动向上递归查找,菜单数据源包含 `/ware`,则 `/ware/1` 也视为 `/ware` 项 | `boolean` | `true` | | `[hideEmptyChildren]` | 当所有子项都为隐藏时,是否也隐藏父级 | `boolean` | `true` | | `[openStrictly]` | 展开完全受控,不再自动关闭已展开的项 | `boolean` | `false` | | `[maxLevelIcon]` | Icon最多显示到第几层 | `number` | `3` | | `(select)` | 点击菜单时回调(包含 `disabled`) | `EventEmitter` | - | > 组件的数据来自 `MenuService`(其结构为 [Menu](/theme/menu#Menu)), `MenuService` 的操作会自动同步至该组件。 ### layout-default-header-item | 成员 | 说明 | 类型 | 默认值 | |----|----|----|-----| | `[hidden]` | 隐藏行为 | `pc, mobile, none` | `nones` | | `[direction]` | 方向 | `left, middle, right` | `right` | ### layout-default-header-item-trigger 头部项的触发样式。 ### layout-default-top-menu-item 头部业务菜单项,使用方式请参考 [layout.component.ts](https://github.com/ng-alain/delon/blob/master/src/dev/layout.component.ts#L65-L72)([预览](https://ng-alain.com/dev/home))。 ## 布局说明 按上-左-右布局方式,运用于**业务页**的开发。其规范细节: + 顶部区域高度 `64px`(参数:`@header-hg`) + 侧边区域宽度 `200px`(参数:`@aside-wd`) + 当屏幕低于 `1140px` 宽时隐藏侧边导航 + 当屏幕低于 `1140px` 宽时打开侧边导航为 `fixed` 状态 + 主要包括一个 [layout-default-nav](/theme/layout-default/zh?#layout-default-nav) 组件 > 参数是指可以通过 `src/styles/theme.less` 文件按需要调整。 **顶部区域** 位置:*src/app/layout/base/widgets*。 脚手架默认提供了一些常规顶部区域组件,这些组件都存放于 *components* 目录中。同时 `@delon/abc` 也提供若干顶部组件(例如:[notice-icon](/components/notice-icon) 通知菜单组件)。你可以根据提供的组件自行组合或自行开发。 > 脚手架支持响应式布局,对于顶部区域可能会是在小屏幕下需要隐藏一些组件,因此你可以在对应的DOM节点上加上 `hidden-xs` 表示当屏幕小于 `768px` 时自动隐藏。 **侧边区域** 位置:*src/app/layout/default/sidebar*。 只包括一个用户信息和主菜单。主菜单是一个 [layout-default-nav](/theme/layout-default/zh?#layout-default-nav)。 **内部区域** 内容区域是业务页区域,规范细节: + 内容距离页面标准、侧边、右边滚动条、底部,这四边距依一个标准Dashboard的Gutter宽度 `24px`。 ## 样式参数 | 名称 | 默认值 | 功能 | |----|-----|----| | `@alain-default-prefix` | `.alain-default` | 布局样式前缀 | | `@alain-default-ease` | `cubic-bezier(.25, 0, .15, 1)` | 动画过滤函数 | | `@alain-default-header-hg` | `64px` | 顶部高度 | | `@alain-default-header-bg` | `@primary-color` | 顶部背景色 | | `@alain-default-header-padding` | `@layout-gutter * 2` | 顶部左右内边距 | | `@alain-default-header-search-enabled` | `true` | 是否开启顶部搜索框 | | `@alain-default-header-icon-fs` | `18px` | 顶部 Icon 大小 | | `@alain-default-header-logo-max-height` | `36px` | Logo 图最高高度 | | `@alain-default-aside-wd` | `200px` | 侧边栏宽度 | | `@alain-default-aside-bg` | `#fff` | 侧边栏背景色 | | `@alain-default-aside-scrollbar-width` | `0` | 侧边栏滚动条宽度 | | `@alain-default-aside-scrollbar-height` | `0` | 侧边栏滚动条高度 | | `@alain-default-aside-scrollbar-track-color` | `transparent` | 侧边栏滚动条的轨道颜色 | | `@alain-default-aside-scrollbar-thumb-color` | `transparent` | 侧边栏滚动条小方块颜色 | | `@alain-default-aside-nav-fs` | `14px` | 侧边栏菜单字号 | | `@alain-default-aside-nav-icon-width` | `14px` | 侧边栏菜单 ICON 宽度 | | `@alain-default-aside-nav-img-wh` | `14px` | 侧边栏菜单图像宽高 | | `@alain-default-aside-nav-padding-top-bottom` | `@layout-gutter` | 侧边栏菜单项上下内边距 | | `@alain-default-aside-nav-padding-left-right` | `@layout-gutter * 2` | 侧边栏菜单项左右内边距 | | `@alain-default-aside-nav-text-color` | `rgba(0, 0, 0, 0.65)` | 侧边栏菜单文本颜色 | | `@alain-default-aside-nav-text-hover-color` | `#108ee9` | 侧边栏菜单文本悬停颜色 | | `@alain-default-aside-nav-group-text-color` | `rgba(0, 0, 0, 0.43)` | 侧边栏菜单分组文本颜色 | | `@alain-default-aside-nav-selected-text-color` | `#108ee9` | 侧边栏菜单激活时文本颜色 | | `@alain-default-aside-nav-selected-bg` | `#fcfcfc` | 侧边栏菜单激活时背景颜色 | | `@alain-default-aside-nav-divider-bg` | `#efe3e5` | 分隔线颜色 | | `@alain-default-aside-collapsed-wd` | `@layout-gutter * 8` | 侧边栏收缩后宽度 | | `@alain-default-aside-collapsed-nav-fs` | `24px` | 侧边栏收缩后文本字号 | | `@alain-default-aside-collapsed-nav-img-wh` | `24px` | 侧边栏收缩后图像宽高 | | `@alain-default-content-heading-bg` | `#fafbfc` | 内容区域标题背景色 | | `@alain-default-content-heading-border` | `#efe3e5` | 内容区域标题底部边框色 | | `@alain-default-content-padding` | `@layout-gutter * 3` | 内容区域内边距 | | `@alain-default-content-bg` | `#f5f7fa` | 内容区域背景色 | | `@alain-default-widget-app-icons-enabled` | `true` | 是否 app-icon 小部件样式 | | `@alain-default-aside-user-enabled` | `true` | 是否侧边栏用户信息样式 | ## 常见问题 ### 为什么会有两个快捷菜单 快捷菜单生成规则统一在 `0` 索引下查找,并按以下顺序来获取: 1. 【推荐】 children 存在 `shortcutRoot: true` 则最优先 2. 否则查找带有【dashboard】字样链接,若存在则在此菜单的下方创建快捷入口 3. 否则放在0节点位置 因此,建议在菜单数据的 `0` 索引下保持一个有效的 `shortcutRoot: true` 数据。 ### 常见问题 **隐藏主菜单项** 表示永远不显示菜单,可以在菜单设置 `hide: true`。 **隐藏自动生成导航隐藏面包屑** 表示不显示该节点,可以在菜单设置 `hideInBreadcrumb: true`。 **关于层级** 虽然支持无限层级,但为了用户体验请保持最多不超过四层(含组别)。 **如何更新某个菜单项** 当调用 `MenuService.setItem(key, newValue)` 时会自动重新渲染主菜单,其中 `key` 必须是存在值,请参考 [Menu](/theme/menu#Menu) 的定义。 **如何控制菜单展开** 利用 `LayoutDefaultService.toggleCollapsed()` 来运行时手动控制。 ---