├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .storybook ├── block │ ├── ImportInfo.tsx │ └── docPage.tsx ├── main.js ├── manager.js ├── nzx-antd.theme.js ├── preview.js ├── tsconfig.json └── typings.d.ts ├── .vscode ├── extensions.json ├── launch.json └── tasks.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── angular.json ├── lib ├── README.md ├── base.less ├── between-datetime │ ├── BetweenDatetime.stories.ts │ ├── between-datetime.component.html │ ├── between-datetime.component.ts │ ├── between-datetime.module.ts │ ├── datetime-utils.ts │ ├── index.ts │ ├── ng-package.json │ └── public-api.ts ├── between-input │ ├── BetweenInput.stories.ts │ ├── between-input.component.html │ ├── between-input.component.ts │ ├── between-input.module.ts │ ├── index.ts │ ├── ng-package.json │ └── public-api.ts ├── between-time │ ├── BetweenTime.stories.ts │ ├── between-time.component.html │ ├── between-time.component.ts │ ├── between-time.module.ts │ ├── index.ts │ ├── ng-package.json │ └── public-api.ts ├── between │ ├── Between.stories.ts │ ├── between.component.html │ ├── between.component.less │ ├── between.component.ts │ ├── between.module.ts │ ├── index.ts │ ├── ng-package.json │ └── public-api.ts ├── button │ ├── Button.stories.ts │ ├── button.directive.ts │ ├── button.less │ ├── button.module.ts │ ├── index.ts │ ├── mixin.less │ ├── ng-package.json │ └── public-api.ts ├── checkbox │ ├── Checkbox.stories.ts │ ├── checkbox.component.html │ ├── checkbox.component.ts │ ├── checkbox.module.ts │ ├── index.ts │ ├── ng-package.json │ └── public-api.ts ├── directive │ ├── auth.directive.ts │ ├── auth.not.directive.ts │ ├── click-outside.directive.ts │ ├── click.once.directive.ts │ ├── directive.module.ts │ ├── down-file.directive.ts │ ├── fa-icon.directive.ts │ ├── index.ts │ ├── let.directive.ts │ ├── named-template.ts │ ├── ng-package.json │ ├── ngx-for.directive.ts │ └── public-api.ts ├── http-interceptor │ ├── HttpInterceptor.stories.mdx │ ├── http-custom-server-error.interceptor.ts │ ├── http-default-encoder.ts │ ├── http-error.interceptor.ts │ ├── http-header.interceptor.ts │ ├── http-interceptor.config.ts │ ├── http-interceptor.module.ts │ ├── http-loading.interceptor.ts │ ├── http-loading.service.ts │ ├── http-params.interceptor.ts │ ├── http-response-parse.interceptor.ts │ ├── http-url.interceptor.ts │ ├── http.model.ts │ ├── index.ts │ ├── logout.service.ts │ ├── ng-package.json │ ├── public-api.ts │ └── xhr.ts ├── index.less ├── karma.conf.js ├── layout-page │ ├── content.component.ts │ ├── header.component.ts │ ├── index.ts │ ├── is-content-empty.ts │ ├── layout-page.less │ ├── layout-page.module.ts │ ├── ng-package.json │ ├── page.component.ts │ └── public-api.ts ├── modal │ ├── index.ts │ ├── modal-drag.directive.ts │ ├── modal-drag.service.ts │ ├── modal.module.ts │ ├── ng-package.json │ ├── nzx-modal.service.ts │ └── public-api.ts ├── ng-package.json ├── nzx-antd.less ├── nzx-antd.module.ts ├── nzx-antd.service.ts ├── package.json ├── pipe │ ├── defaultify.pipe.ts │ ├── dic.pipe.ts │ ├── filter.pipe.ts │ ├── index.ts │ ├── math.pipe.ts │ ├── ng-package.json │ ├── path-value.pipe.ts │ ├── pipe.module.ts │ ├── public-api.ts │ ├── time-unit.pipe.ts │ ├── to-async.pipe.ts │ └── trust-resource.pipe.ts ├── public-api.ts ├── repeat │ ├── index.ts │ ├── ng-package.json │ ├── public-api.ts │ ├── repeat.component.html │ ├── repeat.component.less │ ├── repeat.component.ts │ └── repeat.module.ts ├── service │ ├── auth-guard.service.ts │ ├── dic.service.ts │ ├── download.service.ts │ ├── fetcher.service.ts │ ├── index.ts │ ├── loading.service.ts │ ├── ng-package.json │ ├── public-api.ts │ ├── service.module.ts │ └── storage.service.ts ├── switch │ ├── index.ts │ ├── ng-package.json │ ├── public-api.ts │ ├── switch.component.ts │ └── switch.module.ts ├── table │ ├── Table.stories.ts │ ├── const.ts │ ├── header │ │ ├── column-setting │ │ │ ├── column-setting.component.html │ │ │ ├── column-setting.component.less │ │ │ └── column-setting.component.ts │ │ └── table-header │ │ │ ├── table-header.component.html │ │ │ └── table-header.component.ts │ ├── index.less │ ├── index.ts │ ├── ng-package.json │ ├── public-api.ts │ ├── table-widget.directive.ts │ ├── table-widget.service.ts │ ├── table-widget │ │ ├── table-button │ │ │ └── table-button.component.ts │ │ ├── table-input │ │ │ └── table-input.component.ts │ │ ├── table-link │ │ │ └── table-link.component.ts │ │ ├── table-tag │ │ │ └── table-tag.component.ts │ │ └── table-widget.module.ts │ ├── table.component.html │ ├── table.component.less │ ├── table.component.ts │ ├── table.module.ts │ ├── table.type.ts │ └── transform │ │ ├── col-button-visible.pipe.ts │ │ ├── col-buttons.pipe.ts │ │ ├── col-format.pipe.ts │ │ ├── col-span.pipe.ts │ │ ├── has-auth.pipe.ts │ │ └── link-href.pipe.ts ├── test.ts ├── tsconfig.lib.json ├── tsconfig.lib.prod.json ├── tsconfig.spec.json ├── upload │ ├── index.ts │ ├── ng-package.json │ ├── public-api.ts │ ├── upload.component.html │ ├── upload.component.ts │ └── upload.module.ts └── util │ ├── base-control.ts │ ├── form-utils.ts │ ├── index.ts │ ├── ng-package.json │ ├── public-api.ts │ ├── utils-fn.ts │ └── utils.ts ├── package-lock.json ├── package.json ├── stories ├── @types │ └── index.ts ├── Introduction.stories.mdx ├── index.ts └── styles.less ├── tsconfig.json └── version.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": [ 4 | "dist/**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "parserOptions": { 12 | "project": [ 13 | "./lib/tsconfig.lib.json" 14 | ], 15 | "createDefaultProgram": true 16 | }, 17 | "extends": [ 18 | "plugin:@angular-eslint/recommended", 19 | "eslint:recommended", 20 | "plugin:@typescript-eslint/recommended", 21 | "plugin:@typescript-eslint/recommended-requiring-type-checking", 22 | "plugin:@angular-eslint/template/process-inline-templates" 23 | ], 24 | "rules": { 25 | "@typescript-eslint/no-empty-function": 0, 26 | "@typescript-eslint/no-explicit-any": 0, 27 | "@typescript-eslint/no-unsafe-assignment": 0, 28 | "@typescript-eslint/no-unsafe-call": 0, 29 | "@typescript-eslint/no-unsafe-member-access": 0, 30 | "prefer-arrow/prefer-arrow-functions": 0, 31 | "@angular-eslint/directive-selector": 0, 32 | "@angular-eslint/component-selector": [ 33 | "error", 34 | { 35 | "type": "element", 36 | "prefix": "nzx,app", 37 | "style": "kebab-case" 38 | } 39 | ], 40 | "jsdoc/newline-after-description": 0 41 | } 42 | }, 43 | { 44 | "files": [ 45 | "*.html" 46 | ], 47 | "extends": [ 48 | "plugin:@angular-eslint/template/recommended" 49 | ], 50 | "rules": { 51 | } 52 | } 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | 44 | storybook-static 45 | documentation.json 46 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/*.md 2 | **/*.svg 3 | # **/*.html 4 | **/test.ts 5 | src/index.html 6 | 7 | .stylelintrc 8 | .prettierrc 9 | 10 | _nginx/ 11 | _screenshot/ 12 | node_modules/ 13 | dist/ 14 | package.json 15 | src/templates/ 16 | assets/ 17 | **/*.min.* 18 | package-lock.json 19 | documentation 20 | e2e 21 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "useTabs": false, 4 | "printWidth": 120, 5 | "tabWidth": 2, 6 | "semi": true, 7 | "htmlWhitespaceSensitivity": "ignore", 8 | "arrowParens": "avoid", 9 | "bracketSpacing": true, 10 | "proseWrap": "preserve", 11 | "trailingComma": "none", 12 | "endOfLine": "lf", 13 | "jsxBracketSameLine": true, 14 | "overrides": [ 15 | { 16 | "files": ".prettierrc", 17 | "options": { 18 | "parser": "json" 19 | } 20 | }, 21 | { 22 | "files": ["*.html"], 23 | "options": { 24 | "printWidth": 120, 25 | "htmlWhitespaceSensitivity": "ignore" 26 | } 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /.storybook/block/ImportInfo.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import { DocsContext } from '@storybook/addon-docs'; 3 | import { SyntaxHighlighter } from '@storybook/components'; 4 | 5 | export const ImportInfo = () => { 6 | const context = useContext(DocsContext); 7 | console.log(context); 8 | // @ts-ignore 9 | const componentName = context.component?.name; 10 | if (!componentName) { 11 | return null; 12 | } 13 | const moduleName = componentName.replace(/(Component|Directive|Service|Pipe)$/, 'Module'); 14 | const importName = componentName 15 | .replace(/(^Nzx)|((Component|Directive|Service|Pipe)$)/g, '') 16 | .replace(/\w([A-Z])/g, '-$1') 17 | .toLowerCase(); 18 | const importStatement = `import { ${moduleName} } from '@xmagic/nzx-antd/${importName}';`; 19 | return ( 20 | 21 | {importStatement} 22 | 23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /.storybook/block/docPage.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { PRIMARY_STORY, ArgsTable, Title, Subtitle, Primary, Description, Stories } from '@storybook/addon-docs'; 3 | import { H2 } from '@storybook/components'; 4 | import { ImportInfo } from './ImportInfo'; 5 | 6 | export const page = () => ( 7 | <> 8 | 9 | <Subtitle /> 10 | <Description /> 11 | <ImportInfo /> 12 | <Primary /> 13 | <H2>参数定义</H2> 14 | <ArgsTable story={PRIMARY_STORY} /> 15 | <Stories title="所有示例" includePrimary={false} /> 16 | </> 17 | ); 18 | -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: [ 3 | '../stories/**/*.stories.mdx', 4 | '../stories/**/*.stories.@(js|jsx|ts|tsx)', 5 | '../lib/**/*.stories.mdx', 6 | '../lib/**/*.stories.@(js|jsx|ts|tsx)' 7 | ], 8 | addons: ['@storybook/addon-links', '@storybook/addon-essentials'], 9 | framework: '@storybook/angular', 10 | core: { 11 | builder: '@storybook/builder-webpack5' 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /.storybook/manager.js: -------------------------------------------------------------------------------- 1 | import { addons } from '@storybook/addons'; 2 | import { nzxAntdTheme } from './nzx-antd.theme'; 3 | 4 | /** 5 | * 此文件为固定名称, 管理storybook配置 6 | */ 7 | // 更多配置参考 https://storybook.js.org/docs/angular/configure/features-and-behavior 8 | addons.setConfig({ 9 | theme: nzxAntdTheme 10 | }); 11 | -------------------------------------------------------------------------------- /.storybook/nzx-antd.theme.js: -------------------------------------------------------------------------------- 1 | import { create } from '@storybook/theming'; 2 | // 定制主题参考 https://storybook.js.org/docs/react/configure/theming 3 | export const nzxAntdTheme = create({ 4 | brandTitle: 'Nzx Antd', 5 | brandUrl: 'https://github.com/m310851010/nzx-antd', 6 | // brandImage: './static/media/stories/assets/colors.svg', 7 | brandTarget: '_blank' 8 | }); 9 | -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import { setCompodocJson } from '@storybook/addon-docs/angular'; 2 | import docJson from '../documentation.json'; 3 | import { CommonModule, registerLocaleData } from '@angular/common'; 4 | import zh from '@angular/common/locales/zh'; 5 | import { moduleMetadata } from '@storybook/angular'; 6 | 7 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 8 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 9 | import { NZ_I18N, zh_CN } from 'ng-zorro-antd/i18n'; 10 | import { BrowserModule } from '@angular/platform-browser'; 11 | import { EXCLUDE_PARAMS } from '@stories'; 12 | import { page } from './block/docPage'; 13 | setCompodocJson(docJson); 14 | 15 | registerLocaleData(zh); 16 | 17 | export const parameters = { 18 | actions: { argTypesRegex: '^on[A-Z].*' }, 19 | controls: { 20 | matchers: { 21 | color: /(background|color)$/i, 22 | date: /(Date|Time)$/ 23 | }, 24 | exclude: EXCLUDE_PARAMS 25 | }, 26 | docs: { 27 | inlineStories: true, 28 | source: { 29 | language: 'typescript', 30 | format: true 31 | }, 32 | page 33 | }, 34 | options: { 35 | storySort: { 36 | // order: ['介绍', '组件', '指令', '管道', '服务', '工具类'] 37 | } 38 | } 39 | }; 40 | 41 | // 全局模块配置 42 | export const decorators = [ 43 | moduleMetadata({ 44 | imports: [CommonModule, BrowserModule, BrowserAnimationsModule, FormsModule, ReactiveFormsModule], 45 | providers: [{ provide: NZ_I18N, useValue: zh_CN }] 46 | }) 47 | // (Story, { id, kind, name, story, parameters, hooks, args, argTypes, globals, viewMode, loaded }) => { 48 | // console.log('=================='); 49 | // } 50 | ]; 51 | -------------------------------------------------------------------------------- /.storybook/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig", 3 | "compilerOptions": { 4 | "types": [ 5 | "node" 6 | ], 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "exclude": [ 10 | "../lib/test.ts", 11 | "../lib/**/*.spec.ts", 12 | "../projects/**/*.spec.ts", 13 | ], 14 | "include": [ 15 | "../lib/**/*", 16 | "../projects/**/*", 17 | "../stories/**/*", 18 | "./block/**/*" 19 | ], 20 | "files": [ 21 | "./typings.d.ts" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /.storybook/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.md' { 2 | const content: string; 3 | export default content; 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "ng serve", 7 | "type": "pwa-chrome", 8 | "request": "launch", 9 | "preLaunchTask": "npm: start", 10 | "url": "http://localhost:4200/" 11 | }, 12 | { 13 | "name": "ng test", 14 | "type": "chrome", 15 | "request": "launch", 16 | "preLaunchTask": "npm: test", 17 | "url": "http://localhost:9876/debug.html" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 3 | "version": "2.0.0", 4 | "tasks": [ 5 | { 6 | "type": "npm", 7 | "script": "start", 8 | "isBackground": true, 9 | "problemMatcher": { 10 | "owner": "typescript", 11 | "pattern": "$tsc", 12 | "background": { 13 | "activeOnStart": true, 14 | "beginsPattern": { 15 | "regexp": "(.*?)" 16 | }, 17 | "endsPattern": { 18 | "regexp": "bundle generation complete" 19 | } 20 | } 21 | } 22 | }, 23 | { 24 | "type": "npm", 25 | "script": "test", 26 | "isBackground": true, 27 | "problemMatcher": { 28 | "owner": "typescript", 29 | "pattern": "$tsc", 30 | "background": { 31 | "activeOnStart": true, 32 | "beginsPattern": { 33 | "regexp": "(.*?)" 34 | }, 35 | "endsPattern": { 36 | "regexp": "bundle generation complete" 37 | } 38 | } 39 | } 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 2022/8/8 2 | 3 | 增加 storybook支持 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 m310851010 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # NzxAntd 3 | 4 | `NzxAntd`是一个`angular`组件库,基于`ng-zorro-antd`进行二次扩展,并加入开发常用功能。全部代码开源并遵循 `MIT` 协议,任何企业、组织及个人均可免费使用。 5 | 6 | [![npm version](https://img.shields.io/npm/v/@xmagic/nzx-antd/latest.svg)](https://npmjs.com/package/@xmagic/nzx-antd) 7 | ![License](https://img.shields.io/badge/License-MIT-blue.svg) 8 | [![Angular](https://img.shields.io/badge/Build%20with-Angular%20CLI-red?logo=angular)](https://www.github.com/angular/angular) 9 | [![Storybook](https://cdn.jsdelivr.net/gh/storybookjs/brand@main/badge/badge-storybook.svg)](https://m310851010.github.io/nzx-antd) 10 | 11 | ## ✨特性 12 | 13 | - 扩展`HttpInterceptor`拦截器,简化通用业务处理 14 | - 封装常用组件 使之支持`FormControl`和`NgModal` 15 | - 封装表格组件, 简单易用, 功能强大 16 | - 常用工具类, 服务, 指令,管道 17 | - 集中化配置,统一配置入口 18 | 19 | ## 文档和示例 20 | 21 | 有关文档与示例,请访问 [https://m310851010.github.io/nzx-antd](https://m310851010.github.io/nzx-antd) 22 | 23 | 24 | ## 🖥使用环境 25 | 26 | - [Angular](https://angular.io) >= v16.0.0 27 | - [ng-zorro-antd](https://ng.ant.design) >= v16.0.0 28 | 29 | ## 📦安装 30 | 31 | ```shell 32 | npm i @xmagic/nzx-antd --save 33 | ``` 34 | 35 | ## 🔨使用 36 | 37 | 38 | ## 🍏引入样式 39 | 40 | > 有两种方式引入样式, 在 `angular.json` 中 或者 `style.less`中, 任选其一 41 | 42 | - 在 `angular.json` 中引入 43 | 44 | ```json 45 | { 46 | "styles": [ 47 | "node_modules/@xmagic/nzx-antd/nzx-antd.less" 48 | ] 49 | } 50 | ``` 51 | 52 | - 在 `style.less` 中引入 `less` 样式文件 53 | 54 | ```css 55 | @import "node_modules/@xmagic/nzx-antd/nzx-antd.less"; 56 | ``` 57 | 58 | ## 🍎引入模块 59 | 60 | 1. 配置`NzxAntdService` 61 | 62 | ```ts 63 | // nzx-antd-config.service.ts 64 | 65 | import { Injectable } from '@angular/core'; 66 | import { NzxAntdService } from '@xmagic/nzx-antd'; 67 | import { environment } from '../environments/environment'; 68 | 69 | @Injectable({ 70 | providedIn: 'root' 71 | }) 72 | export class NzxAntdConfigService extends NzxAntdService { 73 | override basePath = environment.basePath; 74 | override response = { data: 'data' }; 75 | constructor() { 76 | super(); 77 | } 78 | } 79 | 80 | ``` 81 | 82 | 2. 修改`AppModule` 83 | 84 | ```diff 85 | // app.module.ts 86 | 87 | import { NgModule } from '@angular/core'; 88 | import { AppComponent } from './app.component'; 89 | import { NzxModalModule } from '@xmagic/nzx-antd/modal'; 90 | import { NzxHttpInterceptorModule } from '@xmagic/nzx-antd/http-interceptor'; 91 | +import { NzxAntdService } from '@xmagic/nzx-antd'; 92 | +import { NzxAntdConfigService } from './nzx-antd-config.service'; 93 | 94 | @NgModule({ 95 | imports: [ 96 | NzxModalModule, 97 | NzxHttpInterceptorModule 98 | ], 99 | providers: [ 100 | + { provide: NzxAntdService, useExisting: NzxAntdConfigService } 101 | ], 102 | bootstrap: [AppComponent] 103 | }) 104 | export class AppComponent {} 105 | ``` 106 | 107 | 3. 修改`AppComponent` 108 | 109 | ```ts 110 | //app.component.ts 111 | 112 | import { Component, OnInit } from '@angular/core'; 113 | import { HttpLoadingService, LogoutService } from '@xmagic/nzx-antd/http-interceptor'; 114 | import { NzMessageService } from 'ng-zorro-antd/message'; 115 | import { NzxModalWrapService } from '@xmagic/nzx-antd/modal'; 116 | import { loadingService } from '@xmagic/nzx-antd/service'; 117 | 118 | @Component({ 119 | selector: 'app-root', 120 | template: '<router-outlet></router-outlet>', 121 | }) 122 | export class AppComponent implements OnInit { 123 | constructor( 124 | protected loading: HttpLoadingService, 125 | protected notifyService: LogoutService, 126 | protected modalService: NzxModalWrapService, 127 | protected message: NzMessageService, 128 | ) {} 129 | 130 | ngOnInit(): void { 131 | this.loading.subscribe(status => loadingService.loading(status)); 132 | 133 | this.notifyService.onLogout(error => { 134 | this.modalService.closeAll(); 135 | if (error.timeout) { 136 | this.message.info(error.message || '登录超时,请重新登录'); 137 | } 138 | window.top!.location.href = error?.url || '#/login'; 139 | }); 140 | } 141 | } 142 | ``` 143 | 144 | ## 🏴授权协议 145 | 146 | [MIT](https://raw.githubusercontent.com/m310851010/nzx-antd/main/LICENSE) 147 | 148 | ## 👍支持 149 | 150 | 为该项目点个免费的星⭐ 151 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "nzx-antd": { 7 | "projectType": "library", 8 | "root": "lib", 9 | "sourceRoot": "lib", 10 | "prefix": "nzx", 11 | "architect": { 12 | "build": { 13 | "builder": "@angular-devkit/build-angular:ng-packagr", 14 | "options": { 15 | "project": "lib/ng-package.json" 16 | }, 17 | "configurations": { 18 | "production": { 19 | "tsConfig": "lib/tsconfig.lib.prod.json" 20 | }, 21 | "development": { 22 | "tsConfig": "lib/tsconfig.lib.json" 23 | } 24 | }, 25 | "defaultConfiguration": "production" 26 | }, 27 | "test": { 28 | "builder": "@angular-devkit/build-angular:karma", 29 | "options": { 30 | "main": "lib/test.ts", 31 | "tsConfig": "lib/tsconfig.spec.json", 32 | "karmaConfig": "lib/karma.conf.js" 33 | } 34 | } 35 | } 36 | }, 37 | "storybook": { 38 | "projectType": "application", 39 | "root": "", 40 | "sourceRoot": "stories", 41 | "architect": { 42 | "build": { 43 | "builder": "@angular-devkit/build-angular:browser", 44 | "options": { 45 | "outputPath": "", 46 | "index": "", 47 | "main": "", 48 | "tsConfig": "tsconfig.json", 49 | "styles": ["stories/styles.less"], 50 | "scripts": [], 51 | "assets": [ 52 | { 53 | "glob": "**/*", 54 | "ignore": ["fill/*", "twotone/*"], 55 | "input": "./node_modules/@ant-design/icons-angular/src/inline-svg/", 56 | "output": "/assets/" 57 | } 58 | ], 59 | "stylePreprocessorOptions": { 60 | "includePaths": ["node_modules"] 61 | } 62 | } 63 | }, 64 | 65 | "storybook": { 66 | "builder": "@storybook/angular:start-storybook", 67 | "options": { 68 | "configDir": ".storybook", 69 | "browserTarget": "storybook:build", 70 | "compodoc": true, 71 | "compodocArgs": ["-e", "json", "-d", "."], 72 | "port": 6006 73 | } 74 | }, 75 | "build-storybook": { 76 | "builder": "@storybook/angular:build-storybook", 77 | "options": { 78 | "configDir": ".storybook", 79 | "browserTarget": "storybook:build", 80 | "compodoc": true, 81 | "compodocArgs": ["-e", "json", "-d", "."], 82 | "outputDir": "storybook-static" 83 | } 84 | } 85 | 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/README.md: -------------------------------------------------------------------------------- 1 | # NzxAntd 2 | 3 | `NzxAntd`是一个`angular`组件库,基于`ng-zorro-antd`进行二次扩展,并加入开发常用功能。全部代码开源并遵循 `MIT` 协议,任何企业、组织及个人均可免费使用。 4 | 5 | [![npm version](https://img.shields.io/npm/v/@xmagic/nzx-antd/latest.svg)](https://npmjs.com/package/@xmagic/nzx-antd) 6 | ![License](https://img.shields.io/badge/License-MIT-blue.svg) 7 | [![Angular](https://img.shields.io/badge/Build%20with-Angular%20CLI-red?logo=angular)](https://www.github.com/angular/angular) 8 | [![Storybook](https://cdn.jsdelivr.net/gh/storybookjs/brand@main/badge/badge-storybook.svg)](https://m310851010.github.io/nzx-antd) 9 | 10 | ## ✨ 特性 11 | 12 | - 扩展`HttpInterceptor`拦截器,简化通用业务处理 13 | - 封装常用组件 使之支持`FormControl`和`NgModal` 14 | - 封装表格组件, 简单易用, 功能强大 15 | - 常用工具类, 服务, 指令,管道 16 | - 集中化配置,统一配置入口 17 | 18 | ## 文档和示例 19 | 20 | 有关文档与示例,请访问 [https://m310851010.github.io/nzx-antd](https://m310851010.github.io/nzx-antd) 21 | 22 | ## 🖥 使用环境 23 | 24 | - [Angular](https://angular.io) >= v16.0.0 25 | - [ng-zorro-antd](https://ng.ant.design) >= v16.0.0 26 | 27 | ## 📦 安装 28 | 29 | ```shell 30 | npm i @xmagic/nzx-antd --save 31 | ``` 32 | 33 | ## 🔨 使用 34 | 35 | ## 🍏 引入样式 36 | 37 | > 有两种方式引入样式, 在 `angular.json` 中 或者 `style.less`中, 任选其一 38 | 39 | - 在 `angular.json` 中引入 40 | 41 | ```json 42 | { 43 | "styles": ["node_modules/@xmagic/nzx-antd/nzx-antd.less"] 44 | } 45 | ``` 46 | 47 | - 在 `style.less` 中引入 `less` 样式文件 48 | 49 | ```css 50 | @import 'node_modules/@xmagic/nzx-antd/nzx-antd.less'; 51 | ``` 52 | 53 | ## 🍎 引入模块 54 | 55 | 1. 配置`NzxAntdService` 56 | 57 | ```ts 58 | // nzx-antd-config.service.ts 59 | 60 | import { Injectable } from '@angular/core'; 61 | import { NzxAntdService } from '@xmagic/nzx-antd'; 62 | import { environment } from '../environments/environment'; 63 | 64 | @Injectable({ 65 | providedIn: 'root' 66 | }) 67 | export class NzxAntdConfigService extends NzxAntdService { 68 | override basePath = environment.basePath; 69 | override response = { data: 'data' }; 70 | constructor() { 71 | super(); 72 | } 73 | } 74 | ``` 75 | 76 | 2. 修改`AppModule` 77 | 78 | ```diff 79 | // app.module.ts 80 | 81 | import { NgModule } from '@angular/core'; 82 | import { AppComponent } from './app.component'; 83 | import { NzxModalModule } from '@xmagic/nzx-antd/modal'; 84 | import { NzxHttpInterceptorModule } from '@xmagic/nzx-antd/http-interceptor'; 85 | +import { NzxAntdService } from '@xmagic/nzx-antd'; 86 | +import { NzxAntdConfigService } from './nzx-antd-config.service'; 87 | 88 | @NgModule({ 89 | imports: [ 90 | NzxModalModule, 91 | NzxHttpInterceptorModule 92 | ], 93 | providers: [ 94 | + { provide: NzxAntdService, useExisting: NzxAntdConfigService } 95 | ], 96 | bootstrap: [AppComponent] 97 | }) 98 | export class AppComponent {} 99 | ``` 100 | 101 | 3. 修改`AppComponent` 102 | 103 | ```ts 104 | //app.component.ts 105 | 106 | import { Component, OnInit } from '@angular/core'; 107 | import { HttpLoadingService, LogoutService } from '@xmagic/nzx-antd/http-interceptor'; 108 | import { NzMessageService } from 'ng-zorro-antd/message'; 109 | import { NzxModalWrapService } from '@xmagic/nzx-antd/modal'; 110 | import { loadingService } from '@xmagic/nzx-antd/service'; 111 | 112 | @Component({ 113 | selector: 'app-root', 114 | template: '<router-outlet></router-outlet>' 115 | }) 116 | export class AppComponent implements OnInit { 117 | constructor( 118 | protected loading: HttpLoadingService, 119 | protected notifyService: LogoutService, 120 | protected modalService: NzxModalWrapService, 121 | protected message: NzMessageService 122 | ) {} 123 | 124 | ngOnInit(): void { 125 | this.loading.subscribe(status => loadingService.loading(status)); 126 | 127 | this.notifyService.onLogout(error => { 128 | this.modalService.closeAll(); 129 | if (error.timeout) { 130 | this.message.info(error.message || '登录超时,请重新登录'); 131 | } 132 | window.top!.location.href = error?.url || '#/login'; 133 | }); 134 | } 135 | } 136 | ``` 137 | 138 | ## 🏴 授权协议 139 | 140 | [MIT](https://raw.githubusercontent.com/m310851010/nzx-antd/main/LICENSE) 141 | 142 | ## 👍 支持 143 | 144 | 为该项目点个免费的星 ⭐ 145 | -------------------------------------------------------------------------------- /lib/base.less: -------------------------------------------------------------------------------- 1 | // Config global less under antd 2 | @nzx-prefix: nzx; 3 | 4 | .nowrap { 5 | white-space: nowrap; 6 | } 7 | -------------------------------------------------------------------------------- /lib/between-datetime/BetweenDatetime.stories.ts: -------------------------------------------------------------------------------- 1 | import { moduleMetadata, Story, Meta } from '@storybook/angular'; 2 | import { NzxBetweenDatetimeComponent } from './between-datetime.component'; 3 | import { NzFormModule } from 'ng-zorro-antd/form'; 4 | import { NzDatePickerModule } from 'ng-zorro-antd/date-picker'; 5 | import { NzxBetweenModule } from '@xmagic/nzx-antd/between'; 6 | import { EXCLUDE_PARAMS, hideControlArgType, SIZE_ARG_TYPE, storyFactory } from '@stories'; 7 | import { action } from '@storybook/addon-actions'; 8 | 9 | export default { 10 | title: '组件/BetweenDatetime 日期区间', 11 | component: NzxBetweenDatetimeComponent, 12 | decorators: [ 13 | moduleMetadata({ 14 | declarations: [NzxBetweenDatetimeComponent], 15 | imports: [NzFormModule, NzxBetweenModule, NzDatePickerModule] 16 | }) 17 | ], 18 | argTypes: { 19 | nzxSize: SIZE_ARG_TYPE, 20 | nzMode: { control: 'select', options: ['date', 'week', 'month', 'year'] }, 21 | defaultDisabledTime: { table: { disable: true } }, 22 | nzShowTime: { control: { type: 'boolean' }, type: 'boolean' }, 23 | nzxStartShowTime: { control: { type: 'boolean' }, type: 'boolean' }, 24 | nzxEndShowTime: { control: { type: 'boolean' }, type: 'boolean' }, 25 | nzLocale: { control: false }, 26 | ...hideControlArgType<NzxBetweenDatetimeComponent>( 27 | 'nzxStartOnOpenChange', 28 | 'nzxEndOnOpenChange', 29 | 'nzxStartOnOk', 30 | 'nzxEndOnOk', 31 | 'nzxStartOnCalendarChange', 32 | 'nzxEndOnCalendarChange', 33 | 'nzxStartOnPanelChange', 34 | 'nzxEndOnPanelChange' 35 | ) 36 | }, 37 | args: { 38 | nzxStartOnOpenChange: action('nzxStartOnOpenChange'), 39 | nzxEndOnOpenChange: action('nzxEndOnOpenChange'), 40 | nzxStartOnOk: action('nzxStartOnOk'), 41 | nzxEndOnOk: action('nzxEndOnOk'), 42 | nzxStartOnCalendarChange: action('nzxStartOnCalendarChange'), 43 | nzxEndOnCalendarChange: action('nzxEndOnCalendarChange'), 44 | nzxStartOnPanelChange: action('nzxStartOnPanelChange'), 45 | nzxEndOnPanelChange: action('nzxEndOnPanelChange') 46 | }, 47 | parameters: { 48 | controls: { 49 | exclude: [ 50 | 'nzxStartDisabledDate', 51 | 'nzxEndDisabledDate', 52 | 'nzxStartDisabledTime', 53 | 'nzxEndDisabledTime', 54 | 'getDisabledMaxDate', 55 | 'getDisabledMinDate', 56 | 'getDisabledTime', 57 | ...EXCLUDE_PARAMS 58 | ] 59 | } 60 | } 61 | } as Meta; 62 | 63 | const Template: (props?: Partial<NzxBetweenDatetimeComponent>) => Story<NzxBetweenDatetimeComponent> = storyFactory; 64 | 65 | export const Default = Template(); 66 | 67 | export const NzxSize = Template({ nzxSize: 'large' }); 68 | 69 | export const NzxDisabled = Template({ nzxDisabled: true }); 70 | 71 | export const NzxStartDisabled = Template({ nzxStartDisabled: true }); 72 | 73 | export const NzxEndDisabled = Template({ nzxEndDisabled: true }); 74 | 75 | const start = new Date(); 76 | start.setDate(start.getDate() - 2); 77 | export const StartMinDate = Template({ startMinDateTime: start }); 78 | 79 | export const EndMaxDateTime = Template({ endMaxDateTime: new Date() }); 80 | 81 | export const ShowTimeStart = Template({ nzxStartShowTime: true }); 82 | 83 | export const ShowTimeEnd = Template({ nzxEndShowTime: true }); 84 | 85 | export const ShowTime = Template({ nzShowTime: true }); 86 | 87 | export const ShowToday = Template({ nzShowToday: true }); 88 | 89 | export const ShowTodayStart = Template({ nzxStartShowToday: true }); 90 | 91 | export const ShowTodayEnd = Template({ nzxEndShowToday: true }); 92 | 93 | export const NzMode = Template({ nzMode: 'week' }); 94 | 95 | export const NzAllowClear = Template({ nzAllowClear: true }); 96 | -------------------------------------------------------------------------------- /lib/between-datetime/between-datetime.component.html: -------------------------------------------------------------------------------- 1 | <nzx-between 2 | [nzxDisabled]="nzxDisabled" 3 | [nzxStartDisabled]="nzxStartDisabled" 4 | [nzxEndDisabled]="nzxEndDisabled" 5 | [nzxSize]="nzxSize" 6 | > 7 | <ng-container 8 | start 9 | *ngTemplateOutlet=" 10 | typeTemplate; 11 | context: { 12 | $implicit: { 13 | dir: nzxStartReName, 14 | disabled: nzxStartDisabled != null ? nzxStartDisabled : nzxDisabled, 15 | placeholder: nzxStartPlaceholder, 16 | nzSuffixIcon: nzxStartSuffixIcon, 17 | nzAllowClear: nzxStartAllowClear, 18 | nzRenderExtraFooter: nzxStartRenderExtraFooter, 19 | nzBackdrop: nzxStartBackdrop, 20 | nzAutoFocus: nzxStartAutoFocus, 21 | nzDateRender: nzxStartDateRender, 22 | nzDefaultPickerValue: nzxStartDefaultPickerValue, 23 | nzDisabledDate: nzxStartDisabledDate, 24 | nzDisabledTime: nzxStartDisabledTime, 25 | nzDropdownClassName: nzxStartDropdownClassName, 26 | nzId: nzxStartId, 27 | nzInputReadOnly: nzxStartInputReadOnly, 28 | nzLocale: nzLocale, 29 | nzPopupStyle: nzxStartPopupStyle, 30 | nzShowNow: nzxStartShowNow, 31 | nzShowTime: nzxStartShowTime, 32 | nzShowToday: nzxStartShowToday, 33 | nzOnOpenChange: nzxStartOnOpenChange, 34 | nzOnOk: nzxStartOnOk, 35 | nzOnCalendarChange: nzxStartOnCalendarChange, 36 | nzOnPanelChange: nzxStartOnPanelChange 37 | } 38 | } 39 | " 40 | ></ng-container> 41 | <ng-container 42 | end 43 | *ngTemplateOutlet=" 44 | typeTemplate; 45 | context: { 46 | $implicit: { 47 | dir: nzxEndReName, 48 | disabled: nzxEndDisabled != null ? nzxEndDisabled : nzxDisabled, 49 | placeholder: nzxEndPlaceholder, 50 | nzSuffixIcon: nzxEndSuffixIcon, 51 | nzAllowClear: nzxEndAllowClear, 52 | nzRenderExtraFooter: nzxEndRenderExtraFooter, 53 | nzBackdrop: nzxEndBackdrop, 54 | nzAutoFocus: nzxEndAutoFocus, 55 | nzDateRender: nzxEndDateRender, 56 | nzDefaultPickerValue: nzxEndDefaultPickerValue, 57 | nzDisabledDate: nzxEndDisabledDate, 58 | nzDisabledTime: nzxEndDisabledTime, 59 | nzDropdownClassName: nzxEndDropdownClassName, 60 | nzId: nzxEndId, 61 | nzInputReadOnly: nzxEndInputReadOnly, 62 | nzLocale: nzLocale, 63 | nzPopupStyle: nzxEndPopupStyle, 64 | nzShowNow: nzxEndShowNow, 65 | nzShowTime: nzxEndShowTime, 66 | nzShowToday: nzxEndShowToday, 67 | nzOnOpenChange: nzxEndOnOpenChange, 68 | nzOnOk: nzxEndOnOk, 69 | nzOnCalendarChange: nzxEndOnCalendarChange, 70 | nzOnPanelChange: nzxEndOnPanelChange 71 | } 72 | } 73 | " 74 | ></ng-container> 75 | </nzx-between> 76 | 77 | <ng-template #typeTemplate let-data> 78 | <nz-date-picker 79 | [(ngModel)]="nzxValue[data.dir]" 80 | (ngModelChange)="ngModelChange()" 81 | [nzDisabled]="data.disabled" 82 | [nzPlaceHolder]="data.placeholder" 83 | [nzSize]="nzxSize" 84 | [nzMode]="nzMode" 85 | [nzSuffixIcon]="data.nzSuffixIcon || 'calendar'" 86 | [nzAllowClear]="nzAllowClear || data.nzAllowClear" 87 | [nzRenderExtraFooter]="data.nzRenderExtraFooter" 88 | [nzBackdrop]="data.nzBackdrop" 89 | [nzAutoFocus]="data.nzAutoFocus" 90 | [nzDateRender]="data.nzDateRender" 91 | [nzDefaultPickerValue]="data.nzDefaultPickerValue" 92 | [nzDisabledDate]="data.nzDisabledDate" 93 | [nzDisabledTime]="data.nzDisabledTime" 94 | [nzDropdownClassName]="data.nzDropdownClassName" 95 | [nzFormat]="nzFormat" 96 | [nzId]="data.nzId" 97 | [nzInputReadOnly]="data.nzInputReadOnly" 98 | [nzLocale]="nzLocale" 99 | [nzPopupStyle]="data.nzPopupStyle" 100 | [nzShowNow]="data.nzShowNow" 101 | [nzShowTime]="data.nzShowTime || nzShowTime" 102 | [nzShowToday]="nzShowToday || data.nzShowToday" 103 | (nzOnOpenChange)="data.nzOnOpenChange.emit($event)" 104 | (nzOnOk)="data.nzOnOk.emit($event)" 105 | (nzOnCalendarChange)="data.nzOnCalendarChange.emit($event)" 106 | (nzOnPanelChange)="data.nzOnPanelChange.emit($event)" 107 | ></nz-date-picker> 108 | </ng-template> 109 | -------------------------------------------------------------------------------- /lib/between-datetime/between-datetime.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NzxBetweenDatetimeComponent } from './between-datetime.component'; 4 | import { FormsModule } from '@angular/forms'; 5 | import { NzDatePickerModule } from 'ng-zorro-antd/date-picker'; 6 | import { NzxBetweenModule } from '@xmagic/nzx-antd/between'; 7 | 8 | @NgModule({ 9 | declarations: [NzxBetweenDatetimeComponent], 10 | imports: [CommonModule, FormsModule, NzxBetweenModule, NzDatePickerModule], 11 | exports: [NzxBetweenDatetimeComponent] 12 | }) 13 | export class NzxBetweenDatetimeModule {} 14 | -------------------------------------------------------------------------------- /lib/between-datetime/datetime-utils.ts: -------------------------------------------------------------------------------- 1 | import { NzxUtils } from '@xmagic/nzx-antd/util'; 2 | 3 | export function getStartDate(date: Date) { 4 | const time = new Date(date.getTime()); 5 | time.setHours(0, 0, 0, 0); 6 | return time.getTime(); 7 | } 8 | 9 | export function getEndDate(date: Date) { 10 | const time = new Date(date.getTime()); 11 | time.setHours(23, 59, 59, 999); 12 | return time.getTime(); 13 | } 14 | 15 | export function getStartWeekDate(date: Date) { 16 | const time = new Date(date.getTime()); 17 | const day = time.getDay(); 18 | time.setDate(time.getDate() - (day === 0 ? 7 : day)); 19 | time.setHours(0, 0, 0, 0); 20 | return time.getTime(); 21 | } 22 | 23 | export function getEndWeekDate(date: Date) { 24 | const time = new Date(date.getTime()); 25 | const day = time.getDay(); 26 | time.setDate(time.getDate() + 7 - (day === 0 ? 7 : day)); 27 | time.setHours(23, 59, 59, 999); 28 | return time.getTime(); 29 | } 30 | 31 | export function getStartMonthDate(date: Date) { 32 | const time = new Date(date.getTime()); 33 | time.setDate(1); 34 | time.setHours(0, 0, 0, 0); 35 | return time.getTime(); 36 | } 37 | 38 | export function getEndMonthDate(date: Date) { 39 | const time = new Date(date.getTime()); 40 | time.setDate(0); 41 | time.setHours(23, 59, 59, 999); 42 | return time.getTime(); 43 | } 44 | 45 | export function getStartYearDate(date: Date) { 46 | const time = new Date(date.getTime()); 47 | time.setMonth(1, 1); 48 | time.setHours(0, 0, 0, 0); 49 | return time.getTime(); 50 | } 51 | 52 | export function getEndYearDate(date: Date) { 53 | const time = new Date(date.getTime()); 54 | time.setMonth(12, 0); 55 | time.setHours(23, 59, 59, 999); 56 | return time.getTime(); 57 | } 58 | 59 | export function getTimeValue(date: Date, value?: Date | null): { hour: number; minute: number; second: number } | null { 60 | // 检查是否在同一天, 不在同一天不禁用时间 61 | if (!value || getStartDate(value) !== getStartDate(date)) { 62 | return null; 63 | } 64 | return { hour: value.getHours(), minute: value.getMinutes(), second: value.getSeconds() }; 65 | } 66 | 67 | /** 68 | * 获取Datetime 真实值 69 | * @param date, 70 | * @param disabledDateType 71 | */ 72 | export function getRealDateTime(date: Date, disabledDateType?: DisabledDateType): Date | null | undefined { 73 | if (!disabledDateType) { 74 | return null; 75 | } 76 | if (NzxUtils.isFunction(disabledDateType)) { 77 | return disabledDateType(date); 78 | } 79 | if (NzxUtils.isDate(disabledDateType)) { 80 | return disabledDateType; 81 | } 82 | return new Date(disabledDateType); 83 | } 84 | 85 | export type DisabledDateType = Date | null | ((current: Date) => Date | null); 86 | export type DatetimeValueType = Record<string, Date | null>; 87 | -------------------------------------------------------------------------------- /lib/between-datetime/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Use of this source code is governed by an MIT-style license that can be 3 | * 4 | */ 5 | 6 | export * from './public-api'; 7 | -------------------------------------------------------------------------------- /lib/between-datetime/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib": { 3 | "entryFile": "public-api.ts" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/between-datetime/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './between-datetime.component'; 2 | export * from './datetime-utils'; 3 | export * from './between-datetime.module'; 4 | -------------------------------------------------------------------------------- /lib/between-input/BetweenInput.stories.ts: -------------------------------------------------------------------------------- 1 | import { moduleMetadata, Story, Meta } from '@storybook/angular'; 2 | import { NzxBetweenInputComponent } from './between-input.component'; 3 | import { NzxBetweenModule } from '@xmagic/nzx-antd/between'; 4 | import { EXCLUDE_PARAMS, SIZE_ARG_TYPE, storyFactory } from '@stories'; 5 | import { NzInputModule } from 'ng-zorro-antd/input'; 6 | import { NzInputNumberModule } from 'ng-zorro-antd/input-number'; 7 | 8 | export default { 9 | title: '组件/BetweenInput 输入框区间', 10 | component: NzxBetweenInputComponent, 11 | decorators: [ 12 | moduleMetadata({ 13 | declarations: [NzxBetweenInputComponent], 14 | imports: [NzxBetweenModule, NzInputModule, NzInputNumberModule] 15 | }) 16 | ], 17 | args: { 18 | nzxStartFormatter: (v: number | string) => v, 19 | nzxEndFormatter: (v: number | string) => v, 20 | nzxStartMax: Infinity, 21 | nzxEndMax: Infinity, 22 | nzxStartMin: -Infinity, 23 | nzxEndMin: -Infinity 24 | }, 25 | argTypes: { 26 | nzxSize: SIZE_ARG_TYPE, 27 | nzxType: { control: 'inline-radio' }, 28 | nzxStartMax: { control: 'number' }, 29 | nzxEndMax: { control: 'number' }, 30 | nzxStartMin: { control: 'number' }, 31 | nzxEndMin: { control: 'number' } 32 | }, 33 | parameters: { 34 | controls: { 35 | exclude: EXCLUDE_PARAMS 36 | } 37 | } 38 | } as Meta; 39 | 40 | const Template: (props?: Partial<NzxBetweenInputComponent>) => Story<NzxBetweenInputComponent> = storyFactory; 41 | 42 | export const Default = Template(); 43 | 44 | export const NzxSize = Template({ nzxSize: 'large' }); 45 | 46 | export const NzxDisabled = Template({ nzxDisabled: true }); 47 | 48 | export const NzxStartDisabled = Template({ nzxStartDisabled: true }); 49 | 50 | export const NzxEndDisabled = Template({ nzxEndDisabled: true }); 51 | 52 | export const NzxType = Template({ nzxType: 'number' }); 53 | -------------------------------------------------------------------------------- /lib/between-input/between-input.component.html: -------------------------------------------------------------------------------- 1 | <nzx-between 2 | [nzxDisabled]="nzxDisabled" 3 | [nzxStartDisabled]="nzxStartDisabled" 4 | [nzxEndDisabled]="nzxEndDisabled" 5 | [nzxSize]="nzxSize" 6 | > 7 | <ng-container 8 | start 9 | *ngTemplateOutlet=" 10 | typeTemplate; 11 | context: { 12 | $implicit: { 13 | dir: nzxStartReName || 'start', 14 | disabled: nzxStartDisabled != null ? nzxStartDisabled : nzxDisabled, 15 | placeholder: nzxStartPlaceholder, 16 | step: nzxStarStep, 17 | id: nzxStarId, 18 | formatter: nzxStartFormatter, 19 | max: nzxStartMax, 20 | min: nzxStartMin 21 | } 22 | } 23 | " 24 | ></ng-container> 25 | <ng-container 26 | end 27 | *ngTemplateOutlet=" 28 | typeTemplate; 29 | context: { 30 | $implicit: { 31 | dir: nzxEndReName || 'end', 32 | disabled: nzxEndDisabled != null ? nzxEndDisabled : nzxDisabled, 33 | placeholder: nzxEndPlaceholder, 34 | step: nzxEndStep, 35 | id: nzxEndId, 36 | formatter: nzxEndFormatter, 37 | max: nzxEndMax, 38 | min: nzxEndMin 39 | } 40 | } 41 | " 42 | ></ng-container> 43 | </nzx-between> 44 | 45 | <ng-template #typeTemplate let-data> 46 | <ng-container [ngSwitch]="nzxType"> 47 | <input 48 | *ngSwitchCase="'input'" 49 | nz-input 50 | [disabled]="data.disabled" 51 | [nzSize]="nzxSize" 52 | [(ngModel)]="nzxValue[data.dir]" 53 | [attr.placeholder]="data.placeholder" 54 | (ngModelChange)="ngModelChange()" 55 | /> 56 | <nz-input-number 57 | *ngSwitchCase="'number'" 58 | [nzDisabled]="data.disabled" 59 | [(ngModel)]="nzxValue[data.dir]" 60 | [nzPlaceHolder]="data.placeholder" 61 | [nzSize]="nzxSize" 62 | [nzStep]="data.step" 63 | [nzPrecision]="data.precision" 64 | [nzId]="data.id" 65 | [nzFormatter]="data.formatter || defaultFormat" 66 | [nzMax]="data.max" 67 | [nzMin]="data.min" 68 | (ngModelChange)="ngModelChange()" 69 | ></nz-input-number> 70 | </ng-container> 71 | </ng-template> 72 | -------------------------------------------------------------------------------- /lib/between-input/between-input.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ChangeDetectionStrategy, 3 | ChangeDetectorRef, 4 | Component, 5 | forwardRef, 6 | Input, 7 | ViewEncapsulation 8 | } from '@angular/core'; 9 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; 10 | import { NzxBetweenComponent } from '@xmagic/nzx-antd/between'; 11 | import { NzInputNumberComponent } from 'ng-zorro-antd/input-number'; 12 | 13 | @Component({ 14 | selector: 'nzx-between-input', 15 | templateUrl: './between-input.component.html', 16 | preserveWhitespaces: false, 17 | encapsulation: ViewEncapsulation.None, 18 | changeDetection: ChangeDetectionStrategy.OnPush, 19 | providers: [ 20 | { 21 | provide: NG_VALUE_ACCESSOR, 22 | useExisting: forwardRef(() => NzxBetweenInputComponent), 23 | multi: true 24 | } 25 | ] 26 | }) 27 | export class NzxBetweenInputComponent extends NzxBetweenComponent implements ControlValueAccessor { 28 | nzxValue: InputValueType = {}; 29 | 30 | /** 31 | * 控件类型, 输入框 或 数字框 32 | */ 33 | @Input() nzxType: 'input' | 'number' = 'input'; 34 | /** 35 | * 开始字段Placeholder 36 | */ 37 | @Input() nzxStartPlaceholder = '起始值'; 38 | /** 39 | * 结束字段Placeholder 40 | */ 41 | @Input() nzxEndPlaceholder = '结束值'; 42 | 43 | /** 44 | * 最大值-开始 45 | */ 46 | @Input() nzxStartMax = Infinity; 47 | /** 48 | * 最大值-结束 49 | */ 50 | @Input() nzxEndMax = Infinity; 51 | /** 52 | * 最小值-开始 53 | */ 54 | @Input() nzxStartMin = -Infinity; 55 | /** 56 | * 最小值-结束 57 | */ 58 | @Input() nzxEndMin = -Infinity; 59 | /** 60 | * 数值精度-开始 61 | */ 62 | @Input() nzxStarPrecision?: number; 63 | /** 64 | * 数值精度-结束 65 | */ 66 | @Input() nzxEndPrecision?: number; 67 | /** 68 | * 每次改变步数,可以为小数-开始 69 | */ 70 | @Input() nzxStarStep = 1; 71 | /** 72 | * 每次改变步数,可以为小数-结束 73 | */ 74 | @Input() nzxEndStep = 1; 75 | 76 | /** 77 | * 组件内部 input 的 id 值-开始 78 | */ 79 | @Input() nzxStarId?: string; 80 | /** 81 | * 组件内部 input 的 id 值-结束 82 | */ 83 | @Input() nzxEndId?: string; 84 | /** 85 | * 开始字段重命名 86 | */ 87 | @Input() nzxStartReName?: string; 88 | /** 89 | * 结束字段重命名 90 | */ 91 | @Input() nzxEndReName?: string; 92 | 93 | /** 94 | * 指定输入框展示值的格式-开始 95 | */ 96 | @Input() nzxStartFormatter?: NzInputNumberComponent['nzFormatter']; 97 | /** 98 | * 指定输入框展示值的格式-结束 99 | */ 100 | @Input() nzxEndFormatter?: NzInputNumberComponent['nzFormatter']; 101 | 102 | defaultFormat = (v: number) => v; 103 | constructor(protected cdr: ChangeDetectorRef) { 104 | super(); 105 | } 106 | 107 | ngModelChange() { 108 | this.onChange(this.nzxValue); 109 | } 110 | 111 | writeValue(value: InputValueType): void { 112 | this.nzxValue = value || {}; 113 | this.cdr.markForCheck(); 114 | } 115 | 116 | setDisabledState(isDisabled: boolean): void { 117 | this.nzxDisabled = isDisabled; 118 | } 119 | 120 | registerOnChange(fn: (_: InputValueType) => void): void { 121 | this.onChange = fn; 122 | } 123 | 124 | registerOnTouched(fn: () => void): void { 125 | this.onTouched = fn; 126 | } 127 | 128 | onChange: (value: InputValueType) => void = () => null; 129 | onTouched: () => void = () => null; 130 | } 131 | 132 | export type InputValueType = Record<string, string | number | null> | null; 133 | -------------------------------------------------------------------------------- /lib/between-input/between-input.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NzxBetweenInputComponent } from './between-input.component'; 4 | import { NzInputModule } from 'ng-zorro-antd/input'; 5 | import { FormsModule } from '@angular/forms'; 6 | import { NzInputNumberModule } from 'ng-zorro-antd/input-number'; 7 | import { NzxBetweenModule } from '@xmagic/nzx-antd/between'; 8 | 9 | @NgModule({ 10 | declarations: [NzxBetweenInputComponent], 11 | imports: [CommonModule, FormsModule, NzxBetweenModule, NzInputModule, NzInputNumberModule], 12 | exports: [NzxBetweenInputComponent] 13 | }) 14 | export class NzxBetweenInputModule {} 15 | -------------------------------------------------------------------------------- /lib/between-input/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Use of this source code is governed by an MIT-style license that can be 3 | * 4 | */ 5 | 6 | export * from './public-api'; 7 | -------------------------------------------------------------------------------- /lib/between-input/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib": { 3 | "entryFile": "public-api.ts" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/between-input/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './between-input.component'; 2 | export * from './between-input.module'; 3 | -------------------------------------------------------------------------------- /lib/between-time/BetweenTime.stories.ts: -------------------------------------------------------------------------------- 1 | import { moduleMetadata, Story, Meta } from '@storybook/angular'; 2 | import { NzxBetweenTimeComponent } from './between-time.component'; 3 | import { NzxBetweenModule } from '@xmagic/nzx-antd/between'; 4 | import { EXCLUDE_PARAMS, hideControlArgType, SIZE_ARG_TYPE, storyFactory } from '@stories'; 5 | import { NzTimePickerModule } from 'ng-zorro-antd/time-picker'; 6 | 7 | export default { 8 | title: '组件/BetweenDatetime 时间区间', 9 | component: NzxBetweenTimeComponent, 10 | decorators: [ 11 | moduleMetadata({ 12 | declarations: [NzxBetweenTimeComponent], 13 | imports: [NzTimePickerModule, NzxBetweenModule] 14 | }) 15 | ], 16 | argTypes: { 17 | nzxSize: SIZE_ARG_TYPE, 18 | nzxStartStatus: { control: 'inline-radio', options: ['', 'error', 'warning'], defaultValue: '' }, 19 | nzxEndStatus: { control: 'inline-radio', options: ['', 'error', 'warning'], defaultValue: '' }, 20 | ...hideControlArgType<NzxBetweenTimeComponent>('nzxEndOpenChange', 'nzxStartOpenChange') 21 | }, 22 | parameters: { 23 | controls: { 24 | exclude: [ 25 | ...EXCLUDE_PARAMS, 26 | 'nzxStartDisabledHours', 27 | 'nzxStartDisabledMinutes', 28 | 'nzxStartDisabledSeconds', 29 | 'nzxEndDisabledHours', 30 | 'nzxEndDisabledMinutes', 31 | 'nzxEndDisabledSeconds', 32 | 'getDefaultMinValue', 33 | 'getDefaultMaxValue', 34 | 'getDisabledHour', 35 | 'getDisabledMinutes', 36 | 'getDisabledSeconds', 37 | 'nzDefaultOpenValue' 38 | ] 39 | } 40 | } 41 | } as Meta; 42 | 43 | const Template: (props?: Partial<NzxBetweenTimeComponent>) => Story<NzxBetweenTimeComponent> = storyFactory; 44 | 45 | export const Default = Template(); 46 | 47 | export const NzxSize = Template({ nzxSize: 'large' }); 48 | 49 | export const NzxDisabled = Template({ nzxDisabled: true }); 50 | 51 | export const NzxStartDisabled = Template({ nzxStartDisabled: true }); 52 | 53 | export const NzxEndDisabled = Template({ nzxEndDisabled: true }); 54 | -------------------------------------------------------------------------------- /lib/between-time/between-time.component.html: -------------------------------------------------------------------------------- 1 | <nzx-between 2 | [nzxDisabled]="nzxDisabled" 3 | [nzxStartDisabled]="nzxStartDisabled" 4 | [nzxEndDisabled]="nzxEndDisabled" 5 | [nzxSize]="nzxSize" 6 | > 7 | <ng-container 8 | start 9 | *ngTemplateOutlet=" 10 | typeTemplate; 11 | context: { 12 | $implicit: { 13 | dir: nzxStartReName, 14 | disabled: nzxStartDisabled != null ? nzxStartDisabled : nzxDisabled, 15 | placeholder: nzxStartPlaceholder, 16 | nzSuffixIcon: nzxStartSuffixIcon, 17 | nzBackdrop: nzxStartBackdrop, 18 | nzAutoFocus: nzxStartAutoFocus, 19 | nzId: nzxStartId, 20 | nzAddOn: nzxStartAddOn, 21 | nzAllowEmpty: nzxStartAllowEmpty, 22 | nzClearText: nzxStartClearText, 23 | nzDefaultOpenValue: nzxStartDefaultOpenValue, 24 | nzDisabledHours: nzxStartDisabledHours, 25 | nzDisabledMinutes: nzxStartDisabledMinutes, 26 | nzDisabledSeconds: nzxStartDisabledSeconds, 27 | nzHideDisabledOptions: nzxStartHideDisabledOptions, 28 | nzHourStep: nzxStartHourStep, 29 | nzMinuteStep: nzxStartMinuteStep, 30 | nzNowText: nzxStartNowText, 31 | nzOkText: nzxStartOkText, 32 | nzPopupClassName: nzxStartPopupClassName, 33 | nzSecondStep: nzxStartSecondStep, 34 | nzUse12Hours: nzxStartUse12Hours, 35 | nzOpenChange: nzxStartOpenChange, 36 | nzStatus: nzxStartStatus 37 | } 38 | } 39 | " 40 | ></ng-container> 41 | <ng-container 42 | end 43 | *ngTemplateOutlet=" 44 | typeTemplate; 45 | context: { 46 | $implicit: { 47 | dir: nzxEndReName, 48 | disabled: nzxEndDisabled != null ? nzxEndDisabled : nzxDisabled, 49 | placeholder: nzxEndPlaceholder, 50 | nzSuffixIcon: nzxEndSuffixIcon, 51 | nzBackdrop: nzxEndBackdrop, 52 | nzAutoFocus: nzxEndAutoFocus, 53 | nzId: nzxEndId, 54 | nzAddOn: nzxEndAddOn, 55 | nzAllowEmpty: nzxEndAllowEmpty, 56 | nzClearText: nzxEndClearText, 57 | nzDefaultOpenValue: nzxEndDefaultOpenValue, 58 | nzDisabledHours: nzxEndDisabledHours, 59 | nzDisabledMinutes: nzxEndDisabledMinutes, 60 | nzDisabledSeconds: nzxEndDisabledSeconds, 61 | nzHideDisabledOptions: nzxEndHideDisabledOptions, 62 | nzHourStep: nzxEndHourStep, 63 | nzMinuteStep: nzxEndMinuteStep, 64 | nzNowText: nzxEndNowText, 65 | nzOkText: nzxEndOkText, 66 | nzPopupClassName: nzxEndPopupClassName, 67 | nzSecondStep: nzxEndSecondStep, 68 | nzUse12Hours: nzxEndUse12Hours, 69 | nzOpenChange: nzxEndOpenChange, 70 | nzStatus: nzxEndStatus 71 | } 72 | } 73 | " 74 | ></ng-container> 75 | </nzx-between> 76 | 77 | <ng-template #typeTemplate let-data> 78 | <nz-time-picker 79 | [(ngModel)]="nzxValue[data.dir]" 80 | (ngModelChange)="ngModelChange()" 81 | [nzDisabled]="data.disabled" 82 | [nzPlaceHolder]="data.placeholder" 83 | [nzSize]="nzxSize" 84 | [nzSuffixIcon]="data.nzSuffixIcon || 'clock-circle'" 85 | [nzBackdrop]="data.nzBackdrop" 86 | [nzAutoFocus]="data.nzAutoFocus" 87 | [nzFormat]="nzFormat == null ? 'HH:mm:ss' : nzFormat" 88 | [nzId]="data.nzId" 89 | [nzAddOn]="data.nzAddOn" 90 | [nzAllowEmpty]="data.nzAllowEmpty == null ? true : data.nzAllowEmpty" 91 | [nzClearText]="data.nzClearText == null ? '清空' : data.nzClearText" 92 | [nzDefaultOpenValue]="data.nzDefaultOpenValue || nzDefaultOpenValue" 93 | [nzDisabledHours]="data.nzDisabledHours" 94 | [nzDisabledMinutes]="data.nzDisabledMinutes" 95 | [nzDisabledSeconds]="data.nzDisabledSeconds" 96 | [nzHideDisabledOptions]="data.nzHideDisabledOptions" 97 | [nzHourStep]="data.nzHourStep == null ? 1 : data.nzHourStep" 98 | [nzMinuteStep]="data.nzMinuteStep == null ? 1 : data.nzMinuteStep" 99 | [nzNowText]="data.nzNowText" 100 | [nzOkText]="data.nzOkText" 101 | [nzPopupClassName]="data.nzPopupClassName" 102 | [nzSecondStep]="data.nzSecondStep == null ? 1 : data.nzSecondStep" 103 | [nzUse12Hours]="data.nzUse12Hours" 104 | (nzOpenChange)="data.nzOpenChange.emit($event)" 105 | [nzStatus]="data.nzStatus" 106 | ></nz-time-picker> 107 | </ng-template> 108 | -------------------------------------------------------------------------------- /lib/between-time/between-time.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NzxBetweenTimeComponent } from './between-time.component'; 4 | import { NzTimePickerModule } from 'ng-zorro-antd/time-picker'; 5 | import { FormsModule } from '@angular/forms'; 6 | import { NzxBetweenModule } from '@xmagic/nzx-antd/between' 7 | 8 | @NgModule({ 9 | declarations: [NzxBetweenTimeComponent], 10 | imports: [CommonModule, NzTimePickerModule, FormsModule, NzxBetweenModule], 11 | exports: [NzxBetweenTimeComponent] 12 | }) 13 | export class NzxBetweenTimeModule {} 14 | -------------------------------------------------------------------------------- /lib/between-time/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Use of this source code is governed by an MIT-style license that can be 3 | * 4 | */ 5 | 6 | export * from './public-api'; 7 | -------------------------------------------------------------------------------- /lib/between-time/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib": { 3 | "entryFile": "public-api.ts" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/between-time/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './between-time.component'; 2 | export * from './between-time.module'; 3 | -------------------------------------------------------------------------------- /lib/between/Between.stories.ts: -------------------------------------------------------------------------------- 1 | import { moduleMetadata, Story, Meta } from '@storybook/angular'; 2 | import { NzxBetweenComponent } from './between.component'; 3 | import { NzInputModule } from 'ng-zorro-antd/input'; 4 | import { NzSelectModule } from 'ng-zorro-antd/select'; 5 | import { NzDatePickerModule } from 'ng-zorro-antd/date-picker'; 6 | import { NzFormModule } from 'ng-zorro-antd/form'; 7 | import { SIZE_ARG_TYPE } from '@stories'; 8 | 9 | export default { 10 | title: '组件/Between 区间', 11 | component: NzxBetweenComponent, 12 | decorators: [ 13 | moduleMetadata({ 14 | declarations: [NzxBetweenComponent], 15 | imports: [NzInputModule, NzSelectModule, NzDatePickerModule, NzFormModule] 16 | }) 17 | ], 18 | argTypes: { 19 | nzxSize: SIZE_ARG_TYPE, 20 | }, 21 | parameters: { 22 | docs: { 23 | // 传给 24 | moduleName: '', 25 | importName: '' 26 | } 27 | } 28 | } as Meta; 29 | 30 | const Template: Story<NzxBetweenComponent> = args => { 31 | return { 32 | props: args, 33 | template: ` 34 | <nzx-between 35 | [nzxSize]="nzxSize" 36 | [nzxDisabled]="nzxDisabled" 37 | [nzxEndDisabled]="nzxEndDisabled" 38 | [nzxStartDisabled]="nzxStartDisabled"> 39 | <input nz-input start placeholder="开始控件" [disabled]="nzxDisabled || nzxStartDisabled"> 40 | <input nz-input end placeholder="结束控件" [disabled]="nzxDisabled || nzxEndDisabled"> 41 | </nzx-between> 42 | ` 43 | }; 44 | }; 45 | 46 | export const NzxSize = Template.bind({}); 47 | NzxSize.storyName = 'Nzx Size'; 48 | NzxSize.args = { 49 | nzxSize: 'small' 50 | }; 51 | 52 | export const NzxDisabled = Template.bind({}); 53 | NzxDisabled.args = { 54 | nzxDisabled: true 55 | }; 56 | 57 | export const NzxStartDisabled = Template.bind({}); 58 | NzxStartDisabled.args = { 59 | nzxStartDisabled: true 60 | }; 61 | 62 | export const NzxEndDisabled = Template.bind({}); 63 | NzxEndDisabled.args = { 64 | nzxEndDisabled: true 65 | }; 66 | 67 | // select控件 68 | export const SelectControl: Story = args => { 69 | return { 70 | props: args, 71 | template: ` 72 | <nzx-between [nzxSize]="nzxSize" 73 | [nzxDisabled]="nzxDisabled" 74 | [nzxEndDisabled]="nzxEndDisabled" 75 | [nzxStartDisabled]="nzxStartDisabled"> 76 | <nz-select start nzPlaceHolder="开始控件" [nzOptions]="nzOptions" [nzDisabled]="nzxDisabled || nzxStartDisabled"></nz-select> 77 | <nz-select end nzPlaceHolder="结束控件" [nzOptions]="nzOptions" [nzDisabled]="nzxDisabled || nzxEndDisabled"></nz-select> 78 | </nzx-between> 79 | ` 80 | }; 81 | }; 82 | SelectControl.args = { 83 | nzOptions: Array.from({ length: 10 }).map((it, i) => ({ label: `选项-${i + 1}`, value: i })) 84 | }; 85 | 86 | // DatePicker控件 87 | export const DatePickerControl: Story<NzxBetweenComponent> = args => { 88 | return { 89 | props: args, 90 | template: ` 91 | <nzx-between [nzxSize]="nzxSize" 92 | [nzxDisabled]="nzxDisabled" 93 | [nzxEndDisabled]="nzxEndDisabled" 94 | [nzxStartDisabled]="nzxStartDisabled"> 95 | <nz-date-picker start nzPlaceHolder="开始控件" [nzSize]="nzxSize" [nzDisabled]="nzxDisabled || nzxStartDisabled"></nz-date-picker> 96 | <nz-date-picker end nzPlaceHolder="结束控件" [nzSize]="nzxSize" [nzDisabled]="nzxDisabled || nzxEndDisabled"></nz-date-picker> 97 | </nzx-between> 98 | ` 99 | }; 100 | }; 101 | 102 | // WidthFormItem 103 | export const WidthFormItem: Story<NzxBetweenComponent> = args => { 104 | return { 105 | props: args, 106 | template: ` 107 | <nz-form-item> 108 | <nz-form-label>label1</nz-form-label> 109 | <nz-form-control> 110 | <nzx-between [nzxSize]="nzxSize" 111 | [nzxDisabled]="nzxDisabled" 112 | [nzxEndDisabled]="nzxEndDisabled" 113 | [nzxStartDisabled]="nzxStartDisabled"> 114 | <input nz-input start placeholder="开始控件" [disabled]="nzxDisabled || nzxStartDisabled"> 115 | <input nz-input end placeholder="结束控件" [disabled]="nzxDisabled || nzxEndDisabled"> 116 | </nzx-between> 117 | </nz-form-control> 118 | </nz-form-item> 119 | ` 120 | }; 121 | }; 122 | -------------------------------------------------------------------------------- /lib/between/between.component.html: -------------------------------------------------------------------------------- 1 | <nz-input-group 2 | nzCompact 3 | class="nzx-between" 4 | [class.nzx-between-disabled]="nzxDisabled || (nzxStartDisabled && nzxEndDisabled)" 5 | [nzSize]="nzxSize" 6 | > 7 | <ng-content select="[start]"></ng-content> 8 | <input type="text" disabled nz-input placeholder="~" class="nzx-between-placeholder" /> 9 | <ng-content select="[end]"></ng-content> 10 | </nz-input-group> 11 | -------------------------------------------------------------------------------- /lib/between/between.component.less: -------------------------------------------------------------------------------- 1 | @import '../base.less'; 2 | @nzx-between-prefix: ~'@{nzx-prefix}-between'; 3 | 4 | .@{nzx-between-prefix}{ 5 | display: flex !important; 6 | 7 | > :first-child, .between-start { 8 | border-right-color: transparent; 9 | flex: 1; 10 | .ant-select-selector{ 11 | border-right-color: transparent; 12 | } 13 | } 14 | 15 | > :last-child, .between-end { 16 | border-left-color: transparent; 17 | flex: 1; 18 | .ant-select-selector{ 19 | border-left-color: transparent; 20 | } 21 | } 22 | 23 | &:not(.@{nzx-between-prefix}-disabled) { 24 | .@{nzx-between-prefix}-placeholder{ 25 | background-color: #fff !important; 26 | } 27 | } 28 | &-placeholder { 29 | width: 20px !important; 30 | border-left: none; 31 | border-right: none; 32 | pointer-events: none; 33 | padding-left: 0 !important; 34 | padding-right: 0 !important; 35 | text-align: center; 36 | } 37 | } 38 | 39 | .ant-form-item-has-error .@{nzx-between-prefix}-placeholder{ 40 | border-top-color: @error-color; 41 | border-bottom-color: @error-color; 42 | } 43 | -------------------------------------------------------------------------------- /lib/between/between.component.ts: -------------------------------------------------------------------------------- 1 | import { ChangeDetectionStrategy, Component, Input, ViewEncapsulation } from '@angular/core'; 2 | import { NzSizeLDSType } from 'ng-zorro-antd/core/types'; 3 | 4 | /** 5 | * 包含开始和结束两个控件,子组件使用`start`和`end`属性来插入到对应的容易内。 6 | */ 7 | @Component({ 8 | selector: 'nzx-between', 9 | exportAs: 'nzxBetween', 10 | templateUrl: './between.component.html', 11 | preserveWhitespaces: false, 12 | encapsulation: ViewEncapsulation.None, 13 | changeDetection: ChangeDetectionStrategy.OnPush 14 | }) 15 | export class NzxBetweenComponent { 16 | /** 17 | * 是否禁用 18 | */ 19 | @Input() nzxDisabled = false; 20 | /** 21 | * 控件大小 22 | */ 23 | @Input() nzxSize: NzSizeLDSType = 'default'; 24 | /** 25 | * 开始字段禁用 26 | */ 27 | @Input() nzxStartDisabled?: boolean; 28 | /** 29 | * 结束字段禁用 30 | */ 31 | @Input() nzxEndDisabled?: boolean; 32 | constructor() {} 33 | } 34 | -------------------------------------------------------------------------------- /lib/between/between.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NzxBetweenComponent } from './between.component'; 4 | import { NzInputModule } from 'ng-zorro-antd/input'; 5 | 6 | @NgModule({ 7 | declarations: [NzxBetweenComponent], 8 | imports: [CommonModule, NzInputModule], 9 | exports: [NzxBetweenComponent] 10 | }) 11 | export class NzxBetweenModule {} 12 | -------------------------------------------------------------------------------- /lib/between/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Use of this source code is governed by an MIT-style license that can be 3 | * 4 | */ 5 | 6 | export * from './public-api'; 7 | -------------------------------------------------------------------------------- /lib/between/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib": { 3 | "entryFile": "public-api.ts" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/between/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './between.component'; 2 | export * from './between.module'; 3 | -------------------------------------------------------------------------------- /lib/button/button.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, Input, OnChanges, OnInit, Renderer2, SimpleChange, SimpleChanges } from '@angular/core'; 2 | 3 | /** 4 | * 按钮指令,增强ng-zorro-antd按钮颜色, 和nz-button组件配合使用, 只增加`nzxColor`属性 5 | * 6 | * 颜色值取自 https://ant.design/docs/spec/colors-cn 7 | */ 8 | @Directive({ 9 | selector: '[nz-button]', 10 | exportAs: 'nzxButton' 11 | }) 12 | export class NzxButtonDirective implements OnInit, OnChanges { 13 | /** 14 | * 按钮颜色名称 15 | */ 16 | @Input() nzxColor?: NzxColorType; 17 | constructor(protected renderer: Renderer2, protected elementRef: ElementRef<HTMLElement>) {} 18 | 19 | ngOnInit(): void { 20 | this.addButtonClass(this.nzxColor); 21 | } 22 | 23 | /** 24 | * 添加按钮class 名称 25 | * @param className class 名称 26 | * @protected 27 | */ 28 | protected addButtonClass(className?: NzxColorType): void { 29 | const element = this.elementRef.nativeElement; 30 | const classList = element.classList; 31 | classList.forEach(cls => { 32 | if (cls.indexOf('nzx-button') === 0) { 33 | classList.remove(cls); 34 | } 35 | }); 36 | 37 | if (className) { 38 | this.renderer.addClass(element, `nzx-button-${className}`); 39 | } 40 | } 41 | 42 | ngOnChanges(changes: { [K in keyof this]?: SimpleChange } & SimpleChanges): void { 43 | if (changes.nzxColor && !changes.nzxColor.isFirstChange()) { 44 | this.addButtonClass(this.nzxColor); 45 | } 46 | } 47 | } 48 | 49 | export type NzxColorType = 'success' | 'warning' | 'info' | 'error' | 'gray' | 'teal' | 'cyan' | string; 50 | -------------------------------------------------------------------------------- /lib/button/button.less: -------------------------------------------------------------------------------- 1 | @import 'ng-zorro-antd/style/themes/index.less'; 2 | @import 'ng-zorro-antd/button/style/mixin.less'; 3 | @import '../base.less'; 4 | @import './mixin.less'; 5 | 6 | // 颜色值取自 https://ant.design/docs/spec/colors-cn 7 | 8 | @nzx-button-prefix-cls: ~'@{nzx-prefix}-button'; 9 | @btn-prefix-cls: ~'@{ant-prefix}-btn'; 10 | @btn-ghost-prefix-cls: ~'@{btn-prefix-cls}-background-ghost'; 11 | 12 | @info-color: #0dcaf0; 13 | @info-color-hover: color(~`colorPalette('@{info-color}', 5) `); 14 | @info-color-active: color(~`colorPalette('@{info-color}', 7) `); 15 | 16 | @gray-color: #bfbfbf; 17 | @gray-color-hover: color(~`colorPalette('@{gray-color}', 5) `); 18 | @gray-color-active: color(~`colorPalette('@{gray-color}', 7) `); 19 | 20 | @teal-color: #20c997; 21 | @teal-color-hover: color(~`colorPalette('@{teal-color}', 5) `); 22 | @teal-color-active: color(~`colorPalette('@{teal-color}', 7) `); 23 | 24 | @cyan-color: #13c2c2; 25 | @cyan-color-hover: color(~`colorPalette('@{cyan-color}', 5) `); 26 | @cyan-color-active: color(~`colorPalette('@{cyan-color}', 7) `); 27 | 28 | .@{nzx-button-prefix-cls} { 29 | &-success { 30 | .btn-color(@success-color); 31 | } 32 | &-success.@{btn-prefix-cls}-text { 33 | .button-variant-text(@success-color); 34 | } 35 | &-success.@{btn-prefix-cls}-link { 36 | .button-variant-link(@success-color); 37 | } 38 | .@{btn-ghost-prefix-cls}&-success { 39 | .button-variant-ghost(@success-color; @success-color; @success-color-hover; @success-color-active); 40 | } 41 | 42 | &-warning { 43 | .btn-color(@warning-color); 44 | } 45 | &-warning.@{btn-prefix-cls}-text { 46 | .button-variant-text(@warning-color); 47 | } 48 | &-warning.@{btn-prefix-cls}-link { 49 | .button-variant-link(@warning-color); 50 | } 51 | .@{btn-ghost-prefix-cls}&-warning { 52 | .button-variant-ghost(@warning-color; @warning-color; @warning-color-hover; @warning-color-active); 53 | } 54 | 55 | &-error { 56 | .btn-color(@error-color); 57 | } 58 | &-error.@{btn-prefix-cls}-text { 59 | .button-variant-text(@error-color); 60 | } 61 | &-error.@{btn-prefix-cls}-link { 62 | .button-variant-link(@error-color); 63 | } 64 | .@{btn-ghost-prefix-cls}&-error { 65 | .button-variant-ghost(@error-color; @error-color; @error-color-hover; @error-color-active); 66 | } 67 | 68 | &-info { 69 | .btn-color(@info-color); 70 | } 71 | &-info.@{btn-prefix-cls}-text { 72 | .button-variant-text(@info-color); 73 | } 74 | &-info.@{btn-prefix-cls}-link { 75 | .button-variant-link(@info-color); 76 | } 77 | .@{btn-ghost-prefix-cls}&-info { 78 | .button-variant-ghost(@info-color; @info-color; @info-color-hover; @info-color-active); 79 | } 80 | 81 | &-gray { 82 | .btn-color(@gray-color); 83 | } 84 | &-gray.@{btn-prefix-cls}-text { 85 | .button-variant-text(@gray-color); 86 | } 87 | &-gray.@{btn-prefix-cls}-link { 88 | .button-variant-link(@gray-color); 89 | } 90 | .@{btn-ghost-prefix-cls}&-gray { 91 | .button-variant-ghost(@gray-color; @gray-color; @gray-color-hover; @gray-color-active); 92 | } 93 | 94 | &-teal { 95 | .btn-color(@teal-color); 96 | } 97 | &-teal.@{btn-prefix-cls}-text { 98 | .button-variant-text(@teal-color); 99 | } 100 | &-teal.@{btn-prefix-cls}-link { 101 | .button-variant-link(@teal-color); 102 | } 103 | .@{btn-ghost-prefix-cls}&-teal { 104 | .button-variant-ghost(@teal-color; @teal-color; @teal-color-hover; @teal-color-active); 105 | } 106 | 107 | &-cyan { 108 | .btn-color(@cyan-color); 109 | } 110 | &-cyan.@{btn-prefix-cls}-text { 111 | .button-variant-text(@cyan-color); 112 | } 113 | &-cyan.@{btn-prefix-cls}-link { 114 | .button-variant-link(@cyan-color); 115 | } 116 | .@{btn-ghost-prefix-cls}&-cyan { 117 | .button-variant-ghost(@cyan-color; @cyan-color; @cyan-color-hover; @cyan-color-active); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /lib/button/button.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NzxButtonDirective } from './button.directive'; 4 | import { NzButtonModule } from 'ng-zorro-antd/button'; 5 | 6 | @NgModule({ 7 | declarations: [NzxButtonDirective], 8 | imports: [CommonModule, NzButtonModule], 9 | exports: [NzxButtonDirective, NzButtonModule] 10 | }) 11 | export class NzxButtonModule {} 12 | -------------------------------------------------------------------------------- /lib/button/index.ts: -------------------------------------------------------------------------------- 1 | export * from './public-api'; 2 | -------------------------------------------------------------------------------- /lib/button/mixin.less: -------------------------------------------------------------------------------- 1 | 2 | .active-btn-color(@color) { 3 | &:focus, 4 | &.focus { 5 | box-shadow: 0 0 0 2px fade(@color, 20%); 6 | } 7 | } 8 | 9 | .btn-color(@color) { 10 | .button-variant(@btn-primary-color; @color; @color); 11 | 12 | &:hover, 13 | &:active, 14 | &.active, 15 | &:focus, 16 | &.focus { 17 | color: @btn-primary-color; 18 | } 19 | 20 | .active-btn-color(@color); 21 | } 22 | 23 | .button-variant(@color; @background; @border) { 24 | .button-color(@color; @background; @border); 25 | 26 | &:hover 27 | { 28 | .button-color(tint(@color, 20%); tint(@background, 20%); tint(@border, 20%)); 29 | } 30 | &:active, 31 | &.active , 32 | &:focus, 33 | &.focus{ 34 | .button-color(shade(@color, 5%); shade(@background, 5%); shade(@background, 5%)); 35 | } 36 | 37 | .button-disabled(); 38 | } 39 | 40 | 41 | .button-variant-text(@color;) { 42 | .button-variant-other(@color, transparent, transparent); 43 | box-shadow: none; 44 | 45 | &:hover, 46 | &:focus { 47 | .button-color(~`colorPalette('@{color}', 5) `; @btn-text-hover-bg; transparent); 48 | } 49 | 50 | &:active { 51 | .button-color(~`colorPalette('@{color}', 7) `; fadein(@btn-text-hover-bg, 1%); transparent); 52 | } 53 | .button-disabled(@disabled-color; transparent; transparent); 54 | } 55 | 56 | 57 | .button-variant-link(@color) { 58 | .button-variant-other(@color, transparent, transparent); 59 | box-shadow: none; 60 | 61 | &:hover, 62 | &:focus { 63 | .button-color(~`colorPalette('@{color}', 5) `; transparent; transparent); 64 | } 65 | 66 | &:active { 67 | .button-color(~`colorPalette('@{color}', 7) `; transparent; transparent); 68 | } 69 | .button-disabled(@disabled-color; transparent; transparent); 70 | } 71 | -------------------------------------------------------------------------------- /lib/button/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib": { 3 | "entryFile": "public-api.ts" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/button/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './button.directive'; 2 | export * from './button.module'; 3 | -------------------------------------------------------------------------------- /lib/checkbox/Checkbox.stories.ts: -------------------------------------------------------------------------------- 1 | import { moduleMetadata, Story, Meta, componentWrapperDecorator } from '@storybook/angular'; 2 | import { NzxCheckboxComponent, NzxCheckboxOption } from './checkbox.component'; 3 | import { EXCLUDE_PARAMS, hideControlArgType, storyFactory } from '@stories'; 4 | import { action } from '@storybook/addon-actions'; 5 | import { NzCheckboxModule } from 'ng-zorro-antd/checkbox'; 6 | import { NzOutletModule } from 'ng-zorro-antd/core/outlet'; 7 | import { Component, Input, OnInit, Output, TemplateRef, ViewChild } from '@angular/core'; 8 | 9 | export default { 10 | title: '组件/Checkbox 复选框', 11 | component: NzxCheckboxComponent, 12 | decorators: [ 13 | moduleMetadata({ 14 | declarations: [NzxCheckboxComponent], 15 | imports: [NzCheckboxModule, NzOutletModule] 16 | }) 17 | ], 18 | argTypes: { 19 | ...hideControlArgType<NzxCheckboxComponent>('nzxBlur', 'nzxFocus') 20 | }, 21 | args: { 22 | nzxBlur: action('nzxBlur'), 23 | nzxFocus: action('nzxFocus'), 24 | nzxValue: [] 25 | }, 26 | parameters: { 27 | controls: { 28 | exclude: EXCLUDE_PARAMS 29 | } 30 | } 31 | } as Meta; 32 | 33 | const Template = (props: Partial<NzxCheckboxComponent>): Story<NzxCheckboxComponent> => { 34 | return storyFactory( 35 | props, 36 | ` 37 | <nzx-checkbox 38 | [(ngModel)]="nzxValue" 39 | (nzxBlur)="nzxBlur($event)" 40 | (nzxFocus)="nzxFocus($event)" 41 | [nzxDisabled]="nzxDisabled" 42 | [nzxLayout]="nzxLayout" 43 | [nzxOptions]="nzxOptions" > 44 | </nzx-checkbox>` 45 | ); 46 | }; 47 | 48 | function getNzxOptions(): NzxCheckboxOption[] { 49 | return Array(5) 50 | .fill(0) 51 | .map((v, i) => ({ label: `label-${i}`, value: `value-${i}` })); 52 | } 53 | 54 | export const Default = Template({ nzxOptions: getNzxOptions() }); 55 | 56 | export const Checked = Template({ nzxValue: ['value-1'], nzxOptions: getNzxOptions() }); 57 | 58 | export const NzxDisabled = Template({ nzxOptions: getNzxOptions(), nzxDisabled: true }); 59 | 60 | export const HideOption = Template({ 61 | nzxOptions: getNzxOptions().map((v, i) => { 62 | if (i < 3) { 63 | v.hide = true; 64 | } 65 | return v; 66 | }) 67 | }); 68 | 69 | export const DisabledOption = Template({ 70 | nzxOptions: getNzxOptions().map((v, i) => { 71 | if (i == 1) { 72 | v.disabled = true; 73 | } 74 | return v; 75 | }) 76 | }); 77 | 78 | export const IndeterminateOption = Template({ 79 | nzxOptions: getNzxOptions().map((v, i) => { 80 | if (i < 3) { 81 | v.indeterminate = true; 82 | } 83 | return v; 84 | }) 85 | }); 86 | 87 | export const NgModelChangeOption = Template({ 88 | nzxOptions: getNzxOptions().map((v, i) => { 89 | v.ngModelChange = action('ngModelChange'); 90 | return v; 91 | }) 92 | }); 93 | 94 | @Component({ 95 | selector: 'test', 96 | template: ` 97 | <ng-template #hello>Hello</ng-template> 98 | ` 99 | }) 100 | export class FakeComponent implements OnInit { 101 | @ViewChild('hello', { static: true }) hello!: TemplateRef<any>; 102 | @Input() nzxValue: any; 103 | @Input() nzxBlur: (evt: NzxCheckboxOption) => void = () => void 0; 104 | @Input() nzxFocus: (evt: NzxCheckboxOption) => void = () => void 0; 105 | @Input() nzxDisabled!: boolean; 106 | @Input() nzxOptions: NzxCheckboxOption[] = []; 107 | 108 | ngOnInit(): void { 109 | this.nzxOptions.forEach((v, i) => { 110 | if (i === 1) { 111 | v.label = this.hello; 112 | } 113 | }); 114 | } 115 | } 116 | 117 | export const LabelOption = Template({ 118 | nzxOptions: getNzxOptions() 119 | }); 120 | 121 | LabelOption.decorators = [ 122 | moduleMetadata({ 123 | declarations: [FakeComponent], 124 | imports: [NzCheckboxModule, NzOutletModule] 125 | }), 126 | componentWrapperDecorator(FakeComponent) 127 | ]; 128 | -------------------------------------------------------------------------------- /lib/checkbox/checkbox.component.html: -------------------------------------------------------------------------------- 1 | <nz-checkbox-wrapper (nzOnChange)="ngModelChange($event)"> 2 | <ng-container *ngIf="nzxLayout === 'vertical'; else horizontalTpl"> 3 | <div *ngFor="let item of nzxOptions" [ngStyle]="item.wrapperNStyle" [ngClass]="item.wrapperNgClass"> 4 | <ng-container *ngTemplateOutlet="labelTpl; context: { $implicit: item }"></ng-container> 5 | </div> 6 | </ng-container> 7 | 8 | <ng-template #horizontalTpl> 9 | <ng-container *ngFor="let item of nzxOptions"> 10 | <ng-container *ngTemplateOutlet="labelTpl; context: { $implicit: item }"></ng-container> 11 | </ng-container> 12 | </ng-template> 13 | 14 | <ng-template #labelTpl let-item> 15 | <label 16 | *ngIf="item.hide !== true" 17 | nz-checkbox 18 | [ngStyle]="item.ngStyle" 19 | [ngClass]="item.ngClass" 20 | (focus)="nzxFocus.emit(item)" 21 | (blur)="nzxBlur.emit(item)" 22 | [nzValue]="item.value" 23 | [(ngModel)]="item.checked" 24 | [nzDisabled]="$any(nzxDisabled || item.disabled)" 25 | [nzIndeterminate]="item.indeterminate" 26 | (ngModelChange)="onItemChange($event, item)" 27 | > 28 | <ng-container 29 | *nzStringTemplateOutlet="nzxLabelTemplate || item.label; context: { $implicit: item, options: nzxOptions }" 30 | > 31 | {{ nzxLabelTemplate || item.label }} 32 | </ng-container> 33 | </label> 34 | </ng-template> 35 | </nz-checkbox-wrapper> 36 | -------------------------------------------------------------------------------- /lib/checkbox/checkbox.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NzCheckboxModule } from 'ng-zorro-antd/checkbox'; 4 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 5 | import { NzxCheckboxComponent } from './checkbox.component'; 6 | import { NzOutletModule } from 'ng-zorro-antd/core/outlet'; 7 | 8 | @NgModule({ 9 | declarations: [NzxCheckboxComponent], 10 | imports: [CommonModule, FormsModule, ReactiveFormsModule, NzCheckboxModule, NzOutletModule], 11 | exports: [NzxCheckboxComponent] 12 | }) 13 | export class NzxCheckboxModule {} 14 | -------------------------------------------------------------------------------- /lib/checkbox/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Use of this source code is governed by an MIT-style license that can be 3 | * 4 | */ 5 | 6 | export * from './public-api'; 7 | -------------------------------------------------------------------------------- /lib/checkbox/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib": { 3 | "entryFile": "public-api.ts" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/checkbox/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './checkbox.component'; 2 | export * from './checkbox.module'; 3 | -------------------------------------------------------------------------------- /lib/directive/auth.not.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'; 2 | import { NzxAntdService } from '@xmagic/nzx-antd'; 3 | import { AuthContext, AuthDirective } from './auth.directive'; 4 | 5 | /** 6 | * 权限指令, 无权限渲染模板,否则渲染else模板 7 | * @example 8 | * <button *auth.not="['code1', 'code2']; else hasAuth">没有有权限时显示</button> 9 | * <button *auth.not="'code1'; else hasAuth">没有有权限时显示</button> 10 | * <ng-template #hasAuth>有权限时显示</ng-template> 11 | */ 12 | @Directive({ 13 | selector: '[auth.not]' 14 | }) 15 | export class AuthNotDirective<T = unknown> extends AuthDirective { 16 | /** @internal */ 17 | static authNotUseIfTypeGuard: void; 18 | static ngTemplateGuard_authNot: 'binding'; 19 | 20 | /** 21 | * 布尔表达式,将其作为显示模板的条件进行计算。 22 | * 23 | */ 24 | @Input('auth.not') 25 | set authNot(condition: T) { 26 | super.auth = condition; 27 | } 28 | 29 | /** 30 | * 当此条件表达式计算为 true 时要显示的模板。 31 | */ 32 | @Input('auth.notThen') 33 | set authNotThen(templateRef: TemplateRef<AuthContext<T>> | null) { 34 | super.authThen = templateRef; 35 | } 36 | 37 | /** 38 | * 当此条件表达式计算为 false 时要显示的模板。 39 | */ 40 | @Input('auth.notElse') 41 | set authNotElse(templateRef: TemplateRef<AuthContext<T>> | null) { 42 | super.authElse = templateRef; 43 | } 44 | 45 | /** 46 | * 是否允许渲染, 没有权限的时候渲染 47 | * @param hasAuth 是否有权限 48 | * @param value 权限码 49 | * @protected 50 | */ 51 | protected override canRender(hasAuth: boolean, value: T) { 52 | return !hasAuth; 53 | } 54 | 55 | constructor( 56 | protected override _viewContainer: ViewContainerRef, 57 | public override templateRef: TemplateRef<AuthContext<T>>, 58 | public override antdService: NzxAntdService 59 | ) { 60 | super(_viewContainer, templateRef, antdService); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/directive/click.once.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, EventEmitter, HostListener, Input, Output } from '@angular/core'; 2 | import { InputBoolean } from 'ng-zorro-antd/core/util'; 3 | 4 | /** 5 | * 防止重复触发点击 6 | * @example 7 | * <button (click.once)="myMethod($event)" [delay]="1000" (originClick)="method($event)">按钮</button> 8 | */ 9 | @Directive({ 10 | selector: '[click.once]' 11 | }) 12 | export class ClickOnceDirective { 13 | constructor() {} 14 | 15 | /** 16 | * 点击延迟时间(单位:ms) 17 | */ 18 | @Input() delay = 500; 19 | @Input() @InputBoolean() disabled = false; 20 | /** 21 | * neClick事件 22 | */ 23 | @Output('click.once') clickEvent = new EventEmitter<MouseEvent>(); 24 | /** 25 | * 原始点击事件 26 | * delay - 是否处于延迟状态 27 | */ 28 | @Output() originClick = new EventEmitter<{ target: MouseEvent; delay: boolean }>(); 29 | private _delayDisabled = false; 30 | 31 | @HostListener('click', ['$event']) 32 | clickEventHandle(event: MouseEvent): void { 33 | if (this.disabled) { 34 | return; 35 | } 36 | 37 | this.originClick.emit({ target: event, delay: this._delayDisabled }); 38 | if (this._delayDisabled) { 39 | event.preventDefault(); 40 | event.stopPropagation(); 41 | return; 42 | } 43 | 44 | this._delayDisabled = true; 45 | setTimeout(() => (this._delayDisabled = false), this.delay); 46 | this.clickEvent.emit(event); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /lib/directive/directive.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NamedTemplate } from './named-template'; 4 | import { ClickOnceDirective } from './click.once.directive'; 5 | import { FaIconDirective } from './fa-icon.directive'; 6 | import { ClickOutsideDirective } from './click-outside.directive'; 7 | import { LetDirective } from './let.directive'; 8 | import { DownFileDirective } from './down-file.directive'; 9 | import { AuthDirective } from './auth.directive'; 10 | import { AuthNotDirective } from './auth.not.directive'; 11 | import { NzxServiceModule } from '@xmagic/nzx-antd/service'; 12 | import { NgxFor } from './ngx-for.directive'; 13 | 14 | const DIRECTIVE = [ 15 | NamedTemplate, 16 | ClickOnceDirective, 17 | FaIconDirective, 18 | ClickOutsideDirective, 19 | LetDirective, 20 | DownFileDirective, 21 | AuthDirective, 22 | AuthNotDirective, 23 | NgxFor 24 | ]; 25 | @NgModule({ 26 | declarations: [DIRECTIVE], 27 | imports: [CommonModule, NzxServiceModule], 28 | exports: [DIRECTIVE] 29 | }) 30 | export class NzxDirectiveModule {} 31 | -------------------------------------------------------------------------------- /lib/directive/down-file.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, HostListener, Input } from '@angular/core'; 2 | import { DownloadOption, NzxDownloadService, FetchOptions } from '@xmagic/nzx-antd/service'; 3 | 4 | @Directive({ 5 | selector: '[down-file]', 6 | exportAs: 'downFile' 7 | }) 8 | export class DownFileDirective { 9 | /** 10 | * 下载文件的url 11 | */ 12 | @Input('down-file') url!: string; 13 | /** 14 | * 是否禁用点击下载 15 | */ 16 | @Input() disabled = false; 17 | /** 18 | * 请求方式 19 | */ 20 | @Input() method: FetchOptions['method'] = 'get'; 21 | /** 22 | * 请求参数 23 | */ 24 | @Input() data?: FetchOptions['data']; 25 | /** 26 | * 发送之前的回调函数 27 | */ 28 | @Input() beforeSend?: FetchOptions['beforeSend']; 29 | 30 | /** 31 | * 请求完成后的回调 32 | */ 33 | @Input() afterDownload?: DownloadOption['afterDownload']; 34 | 35 | /** 36 | * 下载并保存完成的回调 37 | */ 38 | @Input() downloadDone?: DownloadOption['downloadDone']; 39 | /** 40 | * 下载发生错误回调 41 | * @param error 42 | */ 43 | @Input() downloadError?: DownloadOption['downloadError']; 44 | 45 | /** 46 | * 获取下载文件名函数 47 | */ 48 | @Input() getFileName?: DownloadOption['getFileName']; 49 | 50 | constructor(private downloadService: NzxDownloadService) {} 51 | 52 | @HostListener('click', ['$event']) 53 | clickEventHandler(event: MouseEvent) { 54 | event.preventDefault(); 55 | event.stopPropagation(); 56 | 57 | if (this.disabled) { 58 | return; 59 | } 60 | this.downloadService.download({ 61 | url: this.url, 62 | method: this.method, 63 | data: this.data, 64 | beforeSend: this.beforeSend, 65 | afterDownload: this.afterDownload, 66 | downloadDone: this.downloadDone, 67 | getFileName: this.getFileName 68 | }); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/directive/fa-icon.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, Input, OnChanges, OnInit, Renderer2, SimpleChange, SimpleChanges } from '@angular/core'; 2 | 3 | /** 4 | * font-awesome 使用指令 5 | * @ 6 | * <i 7 | */ 8 | @Directive({ 9 | selector: '[fa-icon]', 10 | exportAs: 'faIcon', 11 | host: { 12 | '[class.fa]': `type === 'fa'`, 13 | '[class.far]': `type === 'far'`, 14 | '[class.fa-spin]': 'spin === true', 15 | '[class.fa-pulse]': 'pulse === true', 16 | '[class.fa-fw]': 'fixedWidth === true', 17 | '[class.fa-border]': 'border === true', 18 | '[class.fa-inverse]': 'inverse === true', 19 | '[class.fa-flip-horizontal]': `flip === 'horizontal'`, 20 | '[class.fa-flip-vertical]': `flip === 'vertical'`, 21 | '[class.fa-flip-both]': `flip === 'both'`, 22 | '[class.fa-pull-left]': `pull === 'left'`, 23 | '[class.fa-pull-right]': `pull === 'right'`, 24 | '[class.fa-rotate-90]': `rotate === 90`, 25 | '[class.fa-rotate-180]': `rotate === 180`, 26 | '[class.fa-rotate-270]': `rotate === 270`, 27 | '[class.fa-stack-1x]': `stackItemSize === '1x'`, 28 | '[class.fa-stack-2x]': `stackItemSize === '2x'` 29 | } 30 | }) 31 | export class FaIconDirective implements OnInit, OnChanges { 32 | @Input('fa-icon') icon!: string; 33 | @Input() type: 'far' | 'fa' = 'fa'; 34 | @Input() spin?: boolean; 35 | @Input() pulse?: boolean; 36 | @Input() flip?: 'horizontal' | 'vertical' | 'both'; 37 | @Input() pull?: 'left' | 'right'; 38 | @Input() border?: boolean; 39 | @Input() inverse?: boolean; 40 | @Input() symbol?: string | boolean; 41 | @Input() rotate?: 90 | 180 | 270; 42 | @Input() fixedWidth?: boolean; 43 | @Input() stackItemSize?: '1x' | '2x'; 44 | @Input() size?: IconSizeType; 45 | 46 | constructor(public render: Renderer2, public element: ElementRef) {} 47 | 48 | ngOnInit(): void { 49 | this.renderIcon(this.icon); 50 | this.renderSize(this.size); 51 | } 52 | 53 | renderIcon(newIcon: string | null, oldIcon?: string | null) { 54 | if (oldIcon) { 55 | this.getIconClass(oldIcon).forEach(cls => this.render.removeClass(this.element.nativeElement, cls)); 56 | } 57 | if (newIcon) { 58 | this.getIconClass(newIcon).forEach(cls => this.render.addClass(this.element.nativeElement, cls)); 59 | } 60 | } 61 | 62 | getIconClass(icon: string): string[] { 63 | return icon ? icon.split(/\s/) : []; 64 | } 65 | 66 | renderSize(newSize?: IconSizeType, oldSize?: IconSizeType) { 67 | const newSizeCls = newSize ? `fa-${newSize}` : null; 68 | const oldSizeCls = oldSize ? `fa-${oldSize}` : null; 69 | this.renderIcon(newSizeCls, oldSizeCls); 70 | } 71 | 72 | ngOnChanges(changes: { [K in keyof this]?: SimpleChange } & SimpleChanges): void { 73 | if (changes.icon && !changes.icon.isFirstChange()) { 74 | this.renderIcon(changes.icon.currentValue, changes.icon.previousValue); 75 | } 76 | 77 | if (changes.size && !changes.size.isFirstChange()) { 78 | this.renderSize(changes.size.currentValue, changes.size.previousValue); 79 | } 80 | } 81 | } 82 | 83 | export type IconSizeType = 'xs' | 'lg' | 'sm' | '1x' | '2x' | '3x' | '4x' | '5x' | '6x' | '7x' | '8x' | '9x' | '10x'; 84 | -------------------------------------------------------------------------------- /lib/directive/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Use of this source code is governed by an MIT-style license that can be 3 | * 4 | */ 5 | 6 | export * from './public-api'; 7 | -------------------------------------------------------------------------------- /lib/directive/let.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Inject, Input, TemplateRef, ViewContainerRef } from '@angular/core'; 2 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 3 | 4 | /** 5 | * 允许在模板内复用计算值(包含异步),避免重复重新计算。 6 | * @example 7 | * 8 | * <div *let="value1 as v"> 9 | * <p>{{ v }}</p> 10 | * </div> 11 | * <div *let="time$ | async as time"> 12 | * <p>{{ time }}</p> 13 | * </div> 14 | */ 15 | @Directive({ selector: '[let]' }) 16 | export class LetDirective<T> { 17 | @Input() let!: T; 18 | 19 | constructor(@Inject(ViewContainerRef) vc: ViewContainerRef, @Inject(TemplateRef) ref: TemplateRef<LetContext<T>>) { 20 | vc.createEmbeddedView(ref, new LetContext<T>(this)); 21 | } 22 | 23 | static ngTemplateContextGuard<T>(_dir: LetDirective<T>, _ctx: NzSafeAny): _ctx is LetDirective<T> { 24 | return true; 25 | } 26 | } 27 | 28 | export class LetContext<T> { 29 | constructor(private readonly directive: LetDirective<T>) {} 30 | 31 | get $implicit(): T { 32 | return this.directive.let; 33 | } 34 | 35 | get let(): T { 36 | return this.directive.let; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/directive/named-template.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Input, OnInit, TemplateRef } from '@angular/core'; 2 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 3 | 4 | /** 5 | * 获取模板名称 6 | * @example 7 | * ``` html 8 | * <ng-template named="test"></ng-template> 9 | * <ng-template #test named></ng-template> 10 | * 11 | * ``` 12 | * ``` javascript 13 | * @Component(...) 14 | * export class TestComponent { 15 | * @ViewChildren(NamedTemplate) list!: QueryList<NamedTemplate>; 16 | * 17 | * trace() { 18 | * this.list.forEach(it => { 19 | * console.log(it.named); 20 | * console.log(it.template); 21 | * }); 22 | * } 23 | * } 24 | * ``` 25 | */ 26 | @Directive({ 27 | selector: 'ng-template[named]', 28 | exportAs: 'namedTemplate' 29 | }) 30 | export class NamedTemplate<T> implements OnInit { 31 | /** 32 | * 模板名称 33 | */ 34 | @Input() named!: string; 35 | constructor(public template: TemplateRef<T>) {} 36 | 37 | ngOnInit(): void { 38 | this.resolveName(); 39 | } 40 | 41 | resolveName() { 42 | if (!this.named && this.template) { 43 | const tplRef = this.template as NzSafeAny; 44 | // localNames为数组, 如果没有name则为null 45 | this.named = tplRef._declarationTContainer.localNames?.[0]; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/directive/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib": { 3 | "entryFile": "public-api.ts" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/directive/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './click.once.directive'; 2 | export * from './named-template'; 3 | export * from './fa-icon.directive'; 4 | export * from './click-outside.directive'; 5 | export * from './let.directive'; 6 | export * from './auth.directive'; 7 | export * from './auth.not.directive'; 8 | export * from './down-file.directive'; 9 | export * from './ngx-for.directive'; 10 | export * from './directive.module'; 11 | -------------------------------------------------------------------------------- /lib/http-interceptor/http-custom-server-error.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; 3 | import { Observable, catchError, throwError } from 'rxjs'; 4 | import { HttpError } from './http.model'; 5 | import { LogoutService } from './logout.service'; 6 | import { DEFAULT_RESPONSE_SETTING, NzxAntdService, ResponseSetting } from '@xmagic/nzx-antd'; 7 | import { NzxUtils } from '@xmagic/nzx-antd/util'; 8 | 9 | /** 10 | * 在 http-error-interceptor 后,进行自定义错误处理。 11 | */ 12 | @Injectable() 13 | export class HttpCustomServerErrorInterceptor implements HttpInterceptor { 14 | protected readonly settings: Required<ResponseSetting>; 15 | 16 | constructor( 17 | protected logoutNotify: LogoutService, 18 | protected antdService: NzxAntdService 19 | ) { 20 | this.settings = NzxUtils.extend( 21 | {}, 22 | DEFAULT_RESPONSE_SETTING, 23 | this.antdService.response 24 | ) as Required<ResponseSetting>; 25 | } 26 | 27 | intercept(req: HttpRequest<HttpError>, next: HttpHandler): Observable<HttpEvent<HttpError>> { 28 | return next.handle(req).pipe(catchError((error, caught) => this.handleError(req, error, caught))); 29 | } 30 | 31 | /** 32 | * 只抛出自定义异常, 对于http内部异常由拦截器统一处理 33 | * @param req 34 | * @param error 35 | * @param caught 原始异常 36 | */ 37 | handleError(req: HttpRequest<HttpError>, error: HttpError, caught: Observable<HttpEvent<HttpError>>) { 38 | if (error.httpError) { 39 | return this.settings.handleError(req, error); 40 | // 登录超时 强制下线 41 | } else if (this.settings.timeout(error) || this.settings.forceLogout(error)) { 42 | this.logoutNotify.notifyLogin(error); 43 | return throwError(() => error); 44 | } 45 | return this.settings.handleError(req, error); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/http-interceptor/http-default-encoder.ts: -------------------------------------------------------------------------------- 1 | import { HttpParameterCodec } from '@angular/common/http'; 2 | 3 | export const HttpDefaultCodec: HttpParameterCodec = { 4 | decodeKey: (key: string): string => { 5 | return key; 6 | }, 7 | 8 | decodeValue(value: string): string { 9 | return value; 10 | }, 11 | 12 | encodeKey(key: string): string { 13 | return key; 14 | }, 15 | 16 | encodeValue(value: string): string { 17 | return value; 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /lib/http-interceptor/http-error.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; 3 | import { Observable, throwError } from 'rxjs'; 4 | import { catchError } from 'rxjs/operators'; 5 | import { HttpError } from './http.model'; 6 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 7 | import { NzxAntdService } from '@xmagic/nzx-antd'; 8 | 9 | /** 10 | * http 请求发生错误后进行处理 11 | */ 12 | @Injectable() 13 | export class HttpErrorInterceptor implements HttpInterceptor { 14 | constructor(protected antdService: NzxAntdService) {} 15 | 16 | intercept(req: HttpRequest<NzSafeAny>, next: HttpHandler): Observable<HttpEvent<NzSafeAny>> { 17 | return next.handle(req).pipe(catchError(error => this.handleError(req, error))); 18 | } 19 | 20 | /** 21 | * 将错误响应包装为统一格式 22 | * @param req 23 | * @param errorResponse 24 | */ 25 | handleError(req: HttpRequest<NzSafeAny>, errorResponse: HttpErrorResponse) { 26 | if (this.antdService.handleHttpError) { 27 | return this.antdService.handleHttpError(req, errorResponse) as Observable<never>; 28 | } 29 | 30 | const { status: code, error, message } = errorResponse; 31 | return throwError(() => new HttpError(true, code, message, error)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/http-interceptor/http-header.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { NzxAntdService } from '@xmagic/nzx-antd'; 5 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 6 | 7 | /** 8 | * 对请求头进行处理 9 | */ 10 | @Injectable() 11 | export class HttpHeaderInterceptor implements HttpInterceptor { 12 | constructor(protected antdService: NzxAntdService) {} 13 | 14 | intercept(req: HttpRequest<NzSafeAny>, next: HttpHandler): Observable<HttpEvent<NzSafeAny>> { 15 | const headers: Record<string, string> = { 'X-Requested-With': 'XMLHttpRequest' }; 16 | if ( 17 | !req.headers.has('Content-Type') && 18 | !(req.body instanceof FormData || req.body instanceof ArrayBuffer || req.body instanceof Blob) 19 | ) { 20 | headers['Content-Type'] = 21 | this.antdService.contentType === 'form' 22 | ? 'application/x-www-form-urlencoded' 23 | : 'application/json;charset=utf-8'; 24 | } 25 | const clone = req.clone({ 26 | setHeaders: headers 27 | }); 28 | return next.handle(clone); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/http-interceptor/http-interceptor.config.ts: -------------------------------------------------------------------------------- 1 | import { HTTP_INTERCEPTORS } from '@angular/common/http'; 2 | import { HttpLoadingInterceptor } from './http-loading.interceptor'; 3 | import { HttpUrlInterceptor } from './http-url.interceptor'; 4 | import { HttpHeaderInterceptor } from './http-header.interceptor'; 5 | import { HttpParamsInterceptor } from './http-params.interceptor'; 6 | import { HttpCustomServerErrorInterceptor } from './http-custom-server-error.interceptor'; 7 | import { HttpResponseParseInterceptor } from './http-response-parse.interceptor'; 8 | import { HttpErrorInterceptor } from './http-error.interceptor'; 9 | 10 | /** 11 | * 注意,中间件是有序的,谨慎调整下列中间件的顺序 12 | */ 13 | export const httpInterceptors = [ 14 | { provide: HTTP_INTERCEPTORS, useClass: HttpLoadingInterceptor, multi: true }, 15 | { provide: HTTP_INTERCEPTORS, useClass: HttpUrlInterceptor, multi: true }, 16 | { provide: HTTP_INTERCEPTORS, useClass: HttpHeaderInterceptor, multi: true }, 17 | { provide: HTTP_INTERCEPTORS, useClass: HttpParamsInterceptor, multi: true }, 18 | { provide: HTTP_INTERCEPTORS, useClass: HttpCustomServerErrorInterceptor, multi: true }, 19 | { provide: HTTP_INTERCEPTORS, useClass: HttpResponseParseInterceptor, multi: true }, 20 | { provide: HTTP_INTERCEPTORS, useClass: HttpErrorInterceptor, multi: true } 21 | ]; 22 | -------------------------------------------------------------------------------- /lib/http-interceptor/http-interceptor.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { HttpBackend, HttpClientModule } from '@angular/common/http'; 3 | import { httpInterceptors } from './http-interceptor.config'; 4 | import { AsyncHttpXhrBackend } from './xhr'; 5 | 6 | @NgModule({ 7 | imports: [HttpClientModule], 8 | providers: [httpInterceptors, AsyncHttpXhrBackend, { provide: HttpBackend, useExisting: AsyncHttpXhrBackend }] 9 | }) 10 | export class NzxHttpInterceptorModule {} 11 | -------------------------------------------------------------------------------- /lib/http-interceptor/http-loading.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; 3 | import { Observable, Subject } from 'rxjs'; 4 | import { filter, finalize } from 'rxjs/operators'; 5 | import { HttpLoadingService } from './http-loading.service'; 6 | import { LOADING_ENABLED } from '@xmagic/nzx-antd/service'; 7 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 8 | 9 | /** 10 | * http 请求发送时,调用 loading service 显示加载中图标或者做一些其它处理。 11 | */ 12 | @Injectable() 13 | export class HttpLoadingInterceptor implements HttpInterceptor { 14 | private showStatusChange = new Subject<boolean>(); 15 | private requestCount = 0; 16 | 17 | constructor(private loadingService: HttpLoadingService) { 18 | this.loadingService.init(this.showStatusChange.pipe(filter(() => this.requestCount === 0))); 19 | } 20 | 21 | intercept(req: HttpRequest<NzSafeAny>, next: HttpHandler): Observable<HttpEvent<NzSafeAny>> { 22 | const show = req.context.get(LOADING_ENABLED); 23 | if (show) { 24 | this.show(); 25 | } 26 | return next.handle(req).pipe(finalize(() => show && this.hide())); 27 | } 28 | 29 | /** 30 | * 显示loading 31 | */ 32 | show() { 33 | this.showStatusChange.next(true); 34 | this.requestCount++; 35 | } 36 | 37 | /** 38 | * 隐藏loading 39 | */ 40 | hide() { 41 | this.requestCount--; 42 | this.showStatusChange.next(false); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/http-interceptor/http-loading.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Observable, Subject, Subscription } from 'rxjs'; 3 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 4 | 5 | @Injectable({ providedIn: 'root' }) 6 | export class HttpLoadingService { 7 | private httpLoadingStatus = new Subject<boolean>(); 8 | 9 | constructor() {} 10 | 11 | /** 12 | * HttpLoadingService needs a subject to tell the next loading status. 13 | * @param statusChangeObservable The subject who tells the loading status. 14 | */ 15 | init(statusChangeObservable: Observable<boolean>) { 16 | statusChangeObservable.subscribe(this.httpLoadingStatus); 17 | } 18 | 19 | /** 20 | * You have to subscribe the httpLoadingStatus$ in application to set custom loading behaviors through the method. 21 | * 22 | * @example 23 | * `app.component.ts` 24 | * ```ts 25 | * constructor(private loading: HttpLoadingService){} 26 | * ngOnInit() { 27 | * this.loading.subscribe(status => { 28 | * if(status){ 29 | * this.modal.show('loading...'); 30 | * }else{ 31 | * this.modal.hide(); 32 | * } 33 | * }) 34 | * } 35 | * ``` 36 | * @param next 37 | * @param error 38 | * @param complete 39 | */ 40 | subscribe(next?: (value: boolean) => void, error?: (error: NzSafeAny) => void, complete?: () => void): Subscription { 41 | return this.httpLoadingStatus.subscribe({ next, error, complete }); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/http-interceptor/http-params.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpEvent, HttpHandler, HttpInterceptor, HttpParams, HttpRequest } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { HttpDefaultCodec } from './http-default-encoder'; 5 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 6 | import { NzxUtils } from '@xmagic/nzx-antd/util'; 7 | 8 | /** 9 | * 请求参数处理 10 | */ 11 | @Injectable() 12 | export class HttpParamsInterceptor implements HttpInterceptor { 13 | constructor() {} 14 | 15 | intercept(req: HttpRequest<NzSafeAny>, next: HttpHandler): Observable<HttpEvent<NzSafeAny>> { 16 | return next.handle(this.processParameters(req)); 17 | } 18 | 19 | processParameters(req: HttpRequest<NzSafeAny>) { 20 | if (req.params == null) { 21 | return req; 22 | } 23 | return req.clone({ 24 | params: this.processGetParams(req.params) 25 | }); 26 | } 27 | 28 | processGetParams(params: HttpParams) { 29 | const result: Record<string, NzSafeAny> = { r: Math.random() }; 30 | 31 | params.keys().reduce((acc, key) => { 32 | const values = params.getAll(key); 33 | if (values) { 34 | acc[key] = values.length > 1 ? values : values[0]; 35 | return acc; 36 | } 37 | return acc; 38 | }, result); 39 | 40 | return new HttpParams({ fromString: NzxUtils.serialize(result), encoder: HttpDefaultCodec }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/http-interceptor/http-response-parse.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { 3 | HttpEvent, 4 | HttpEventType, 5 | HttpHandler, 6 | HttpInterceptor, 7 | HttpRequest, 8 | HttpResponse 9 | } from '@angular/common/http'; 10 | import { Observable, Subscriber } from 'rxjs'; 11 | import { switchMap } from 'rxjs/operators'; 12 | import { HttpError, ResponseModel } from './http.model'; 13 | import { DEFAULT_RESPONSE_SETTING, NzxAntdService, ResponseSetting } from '@xmagic/nzx-antd'; 14 | import { NzxUtils } from '@xmagic/nzx-antd/util'; 15 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 16 | 17 | /** 18 | * 处理服务器返回的数据,改变其结构。 19 | */ 20 | @Injectable() 21 | export class HttpResponseParseInterceptor implements HttpInterceptor { 22 | protected readonly settings: Required<ResponseSetting>; 23 | constructor(protected antdService: NzxAntdService) { 24 | this.settings = NzxUtils.extend( 25 | {}, 26 | DEFAULT_RESPONSE_SETTING, 27 | this.antdService.response 28 | ) as Required<ResponseSetting>; 29 | } 30 | 31 | intercept<T>(req: HttpRequest<T>, next: HttpHandler): Observable<HttpEvent<T>> { 32 | return next.handle(req).pipe( 33 | switchMap(response => { 34 | return new Observable<HttpEvent<T>>(observ => { 35 | if (response.type !== HttpEventType.Response) { 36 | observ.next(response); 37 | return; 38 | } 39 | this.processData<T>(observ, req, response); 40 | }); 41 | }) 42 | ); 43 | } 44 | 45 | protected processData<T>( 46 | subscriber: Subscriber<HttpEvent<T>>, 47 | req: HttpRequest<T>, 48 | response: HttpResponse<ResponseModel> 49 | ) { 50 | const contentType = (response.headers.get('content-type') || '').toLowerCase(); 51 | 52 | const isJsonResponse = contentType.indexOf('application/json') !== -1; 53 | if (!isJsonResponse) { 54 | subscriber.next(response as HttpResponse<T>); 55 | subscriber.complete(); 56 | return; 57 | } 58 | 59 | const { data: dataProp, code: codeProp, message: messageProp, success: successProp } = this.settings; 60 | 61 | // 处理下载文件错误 62 | if (isJsonResponse && response.body instanceof Blob) { 63 | const reader = new FileReader(); 64 | reader.onload = () => { 65 | const err = JSON.parse(reader.result as string) as ResponseModel<T>; 66 | const httpError = new HttpError( 67 | false, 68 | NzxUtils.get(err, codeProp, 0), 69 | this.getBodyAttr(req, response, err, messageProp), 70 | err 71 | ); 72 | subscriber.error(httpError); 73 | }; 74 | reader.readAsText(response.body); 75 | return; 76 | } 77 | 78 | const body = response.body || {}; 79 | if (successProp(req, response)) { 80 | const resp = response.clone({ body: this.getBodyAttr(req, response, body, dataProp) }); 81 | subscriber.next(resp); 82 | subscriber.complete(); 83 | } else { 84 | const httpError = new HttpError( 85 | false, 86 | NzxUtils.get(body, codeProp, 0), 87 | this.getBodyAttr(req, response, body, messageProp), 88 | body 89 | ); 90 | subscriber.error(httpError); 91 | } 92 | } 93 | 94 | private getBodyAttr( 95 | req: HttpRequest<NzSafeAny>, 96 | response: HttpResponse<NzSafeAny>, 97 | body: NzSafeAny, 98 | attrOrFn: string | ((req: HttpRequest<NzSafeAny>, response: HttpResponse<NzSafeAny>) => NzSafeAny) 99 | ) { 100 | return attrOrFn ? (NzxUtils.isFunction(attrOrFn) ? attrOrFn(req, response) : NzxUtils.get(body, attrOrFn)) : body; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /lib/http-interceptor/http-url.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; 3 | import { Observable } from 'rxjs'; 4 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 5 | import { HttpRequestOptions, NzxAntdService } from '@xmagic/nzx-antd'; 6 | 7 | /** 8 | * 处理 URL 9 | */ 10 | @Injectable() 11 | export class HttpUrlInterceptor implements HttpInterceptor { 12 | constructor(protected antdService: NzxAntdService) {} 13 | 14 | intercept(req: HttpRequest<NzSafeAny>, next: HttpHandler): Observable<HttpEvent<NzSafeAny>> { 15 | return next.handle(this.processUrl(req)); 16 | } 17 | 18 | processUrl(req: HttpRequest<NzSafeAny>): HttpRequest<NzSafeAny> { 19 | let url = req.url; 20 | if (!/^http/i.test(url)) { 21 | if (!/^\//.test(url)) { 22 | url = '/' + url; 23 | } 24 | url = (this.antdService.basePath || '') + url; 25 | } 26 | 27 | // 使用自定义请求处理器 28 | if (this.antdService.handleRequest) { 29 | const newReq = this.antdService.handleRequest(req, url); 30 | if (newReq) { 31 | if ((newReq as HttpRequest<NzSafeAny>).clone) { 32 | return newReq as HttpRequest<NzSafeAny>; 33 | } 34 | const option = newReq as HttpRequestOptions; 35 | if (!option.url) { 36 | option.url = url; 37 | } 38 | return req.clone(option); 39 | } 40 | } 41 | return req.clone({ url }); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/http-interceptor/http.model.ts: -------------------------------------------------------------------------------- 1 | import { NzSafeAny } from 'ng-zorro-antd/core/types' 2 | 3 | /** 4 | * 服务端响应实体, 可以通过设置 http请求参数 5 | * {observe: 'response'} 拿到完整数据 6 | */ 7 | export interface ResponseModel<T = NzSafeAny> { 8 | /** 9 | * 服务端返回编码 10 | */ 11 | code?: number; 12 | message?: string; 13 | data?: T; 14 | } 15 | 16 | /** 17 | * http 请求出错后,在中间件中封闭为统一的格式。 18 | */ 19 | export class HttpError<T = NzSafeAny> { 20 | /** 21 | * 22 | * @param httpError 是否是HTTP原始异常 23 | * @param code 错误码, 如果是HTTP原始异常,则为status code 24 | * @param message 错误消息 25 | * @param body 返回数据 26 | */ 27 | constructor(public httpError: boolean, public code: number, public message: string, public body: T) {} 28 | } 29 | -------------------------------------------------------------------------------- /lib/http-interceptor/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Use of this source code is governed by an MIT-style license that can be 3 | * 4 | */ 5 | 6 | export * from './public-api'; 7 | -------------------------------------------------------------------------------- /lib/http-interceptor/logout.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpError, ResponseModel } from './http.model'; 3 | import { Subject, Subscription } from 'rxjs'; 4 | import { throttleTime } from 'rxjs/operators'; 5 | import { NzxUtils } from '@xmagic/nzx-antd/util'; 6 | import { DEFAULT_RESPONSE_SETTING, NzxAntdService, ResponseSetting } from '@xmagic/nzx-antd' 7 | 8 | /** 9 | * 退出通知 10 | */ 11 | @Injectable({ providedIn: 'root' }) 12 | export class LogoutService { 13 | protected readonly timeoutFn: (error: HttpError) => boolean; 14 | private loginNotify$ = new Subject<LogoutType>(); 15 | 16 | constructor(protected antdService: NzxAntdService) { 17 | this.timeoutFn = NzxUtils.extend<ResponseSetting>({}, DEFAULT_RESPONSE_SETTING, this.antdService.response).timeout!; 18 | } 19 | 20 | notifyLogin(error: HttpError<ResponseModel>): void { 21 | this.loginNotify$.next({ 22 | message: error.message, 23 | url: (error.body as { url?: string })?.url, 24 | timeout: this.timeoutFn(error) 25 | }); 26 | } 27 | 28 | /** 29 | * 静默通知退出 30 | * @param message 提示消息 31 | * @param code 错误码 -1 不显示提示信息 32 | */ 33 | notifyLogout(message: string = '', code = 0): void { 34 | this.notifyLogin(new HttpError(false, code, message, {})); 35 | } 36 | 37 | /** 38 | * 执行退出登录Observable 39 | * @param logoutType 40 | */ 41 | logout(logoutType: LogoutType): void { 42 | this.loginNotify$.next(logoutType); 43 | } 44 | 45 | onLogout(fn: (logoutType: LogoutType) => void): Subscription { 46 | return this.loginNotify$.asObservable().pipe(throttleTime(2000)).subscribe(fn); 47 | } 48 | } 49 | 50 | export interface LogoutType { 51 | /** 52 | * url 53 | */ 54 | url?: string; 55 | /** 56 | * 是否超时 57 | */ 58 | timeout?: boolean; 59 | /** 60 | * 错误信息 61 | */ 62 | message?: string; 63 | } 64 | -------------------------------------------------------------------------------- /lib/http-interceptor/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib": { 3 | "entryFile": "public-api.ts" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/http-interceptor/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './http-custom-server-error.interceptor'; 2 | export * from './http-error.interceptor'; 3 | export * from './http.model'; 4 | export * from './http-loading.interceptor'; 5 | export * from './http-loading.service'; 6 | export * from './logout.service'; 7 | export * from './http-params.interceptor'; 8 | export * from './http-response-parse.interceptor'; 9 | export * from './http-url.interceptor'; 10 | export * from './http-default-encoder'; 11 | export * from './http-interceptor.config'; 12 | export * from './http-interceptor.module'; 13 | export * from './xhr'; 14 | -------------------------------------------------------------------------------- /lib/index.less: -------------------------------------------------------------------------------- 1 | @import 'ng-zorro-antd/style/entry.less'; 2 | @import 'ng-zorro-antd/components.less'; 3 | 4 | @import './between/between.component.less'; 5 | @import './layout-page/layout-page.less'; 6 | @import './table/index.less'; 7 | @import './button/button.less'; 8 | -------------------------------------------------------------------------------- /lib/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | jasmine: { 17 | // you can add configuration options for Jasmine here 18 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html 19 | // for example, you can disable the random execution with `random: false` 20 | // or set a specific seed with `seed: 4321` 21 | }, 22 | clearContext: false // leave Jasmine Spec Runner output visible in browser 23 | }, 24 | jasmineHtmlReporter: { 25 | suppressAll: true // removes the duplicated traces 26 | }, 27 | coverageReporter: { 28 | dir: require('path').join(__dirname, '../../coverage/nzx-antd'), 29 | subdir: '.', 30 | reporters: [ 31 | { type: 'html' }, 32 | { type: 'text-summary' } 33 | ] 34 | }, 35 | reporters: ['progress', 'kjhtml'], 36 | port: 9876, 37 | colors: true, 38 | logLevel: config.LOG_INFO, 39 | autoWatch: true, 40 | browsers: ['Chrome'], 41 | singleRun: false, 42 | restartOnFileChange: true 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /lib/layout-page/content.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, HostBinding, Input, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'nzx-content', 5 | template: '<ng-content></ng-content>', 6 | host: { 7 | '[class.nzx-page-content]': 'true' 8 | } 9 | }) 10 | export class NzxContentComponent { 11 | /** 12 | * 显示验证占位符(是否显示form-item底部的空白) 13 | */ 14 | @Input() formItemBottomVisible = true; 15 | /** 16 | * margin 17 | */ 18 | @Input() margin?: string; 19 | /** 20 | * margin top 21 | */ 22 | @Input() marginTop = '10px'; 23 | 24 | @HostBinding('class.hide-form-item-bottom') get hideFormItemBottom() { 25 | return !this.formItemBottomVisible; 26 | } 27 | @HostBinding('style.margin') get contentMargin() { 28 | return this.margin; 29 | } 30 | @HostBinding('style.margin-top') get contentMarginTop() { 31 | return this.marginTop; 32 | } 33 | constructor() {} 34 | } 35 | -------------------------------------------------------------------------------- /lib/layout-page/header.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, HostBinding, Input } from '@angular/core'; 2 | import { isContentEmpty } from './is-content-empty'; 3 | 4 | @Component({ 5 | selector: 'nzx-header', 6 | template: ` 7 | <div class="page-header_row"> 8 | <div class="page-header_col"> 9 | <ng-content></ng-content> 10 | </div> 11 | <div 12 | #divElement 13 | (cdkObserveContent)="contentChanged(divElement)" 14 | [debounce]="200" 15 | [class.header-content-wrapper]="hasContent" 16 | > 17 | <ng-content select="[buttons]"></ng-content> 18 | </div> 19 | </div> 20 | `, 21 | host: { 22 | '[class.nzx-page-header]': 'true' 23 | } 24 | }) 25 | export class NzxHeaderComponent { 26 | hasContent = false; 27 | /** 28 | * 显示验证占位符(是否显示form-item底部的空白) 29 | */ 30 | @Input() hasBottom = false; 31 | /** 32 | * 是否显示按钮区域 33 | */ 34 | @Input() buttonsVisible = true; 35 | @HostBinding('class.hide-form-item-bottom') get hideFormItemBottom() { 36 | return !this.hasBottom; 37 | } 38 | 39 | constructor() {} 40 | 41 | contentChanged(element: HTMLElement) { 42 | this.hasContent = !isContentEmpty(element); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/layout-page/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Use of this source code is governed by an MIT-style license that can be 3 | * 4 | */ 5 | 6 | export * from './public-api'; 7 | -------------------------------------------------------------------------------- /lib/layout-page/is-content-empty.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 用于校验 `<ng-content></ng-content>` 是否为空,自定义组件时蛮有用。 4 | */ 5 | export function isContentEmpty(element: HTMLElement): boolean { 6 | const nodes = element.childNodes; 7 | for (let i = 0; i < nodes.length; i++) { 8 | const node = nodes.item(i); 9 | if (node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).outerHTML.trim().length !== 0) { 10 | return false; 11 | } else if (node.nodeType === Node.TEXT_NODE && node.textContent!.trim().length !== 0) { 12 | return false; 13 | } 14 | } 15 | return true; 16 | } 17 | -------------------------------------------------------------------------------- /lib/layout-page/layout-page.less: -------------------------------------------------------------------------------- 1 | @import '../base.less'; 2 | @nzx-page-prefix: ~'@{nzx-prefix}-page'; 3 | @nzx-page-header-prefix: ~'@{nzx-prefix}-page-header'; 4 | @nzx-page-content-prefix: ~'@{nzx-prefix}-page-content'; 5 | @nzx-page-background-color: #f0f2f5; 6 | 7 | .@{nzx-page-prefix}{ 8 | flex-direction: column; 9 | display: flex; 10 | height: 100%; 11 | background: @nzx-page-background-color; 12 | } 13 | 14 | .@{nzx-page-content-prefix}{ 15 | padding: 6px; 16 | flex: 1; 17 | background-color: #fff; 18 | border-radius: 2px; 19 | 20 | &.hide-form-item-bottom .ant-form-item { 21 | margin-bottom: 3px; 22 | } 23 | } 24 | 25 | .@{nzx-page-header-prefix}{ 26 | background-color: #fff; 27 | padding: 12px 10px; 28 | border-radius: 2px; 29 | 30 | &.hide-form-item-bottom .ant-form-item { 31 | margin-bottom: 3px; 32 | } 33 | .page-header_row { 34 | display: flex; 35 | } 36 | .page-header_col { 37 | flex: 1; 38 | } 39 | .header-content-wrapper { 40 | padding-left: 10px; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/layout-page/layout-page.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NzxContentComponent } from './content.component'; 4 | import { NzxPageComponent } from './page.component'; 5 | import { NzxHeaderComponent } from './header.component'; 6 | import { ObserversModule } from '@angular/cdk/observers'; 7 | 8 | const COMPONENTS = [NzxContentComponent, NzxPageComponent, NzxHeaderComponent]; 9 | 10 | @NgModule({ 11 | declarations: [COMPONENTS], 12 | imports: [CommonModule, ObserversModule], 13 | exports: [COMPONENTS] 14 | }) 15 | export class NzxLayoutPageModule {} 16 | -------------------------------------------------------------------------------- /lib/layout-page/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib": { 3 | "entryFile": "public-api.ts" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/layout-page/page.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'nzx-page', 5 | template: '<ng-content></ng-content>', 6 | host: { 7 | '[class.nzx-page]': 'true' 8 | } 9 | }) 10 | export class NzxPageComponent { 11 | constructor() {} 12 | } 13 | -------------------------------------------------------------------------------- /lib/layout-page/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './content.component'; 2 | export * from './page.component'; 3 | export * from './header.component'; 4 | export * from './layout-page.module'; 5 | export * from './is-content-empty'; 6 | -------------------------------------------------------------------------------- /lib/modal/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Use of this source code is governed by an MIT-style license that can be 3 | * 4 | */ 5 | 6 | export * from './public-api'; 7 | -------------------------------------------------------------------------------- /lib/modal/modal-drag.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Host } from '@angular/core'; 2 | import { NzModalComponent } from 'ng-zorro-antd/modal'; 3 | import { NzxModalDragService } from './modal-drag.service'; 4 | 5 | /** 6 | * 可拖动的对话框 7 | * @example 8 | * ``` html 9 | * <nz-modal nzxModalDrag ></nz-modal> 10 | ``` 11 | */ 12 | @Directive({ 13 | selector: 'nz-modal[nzxModalDrag]' 14 | }) 15 | export class NzxModalDragDirective { 16 | constructor(@Host() protected modal: NzModalComponent, public modalDragService: NzxModalDragService) { 17 | const wrapCls = this.modalDragService.getRandomCls(); 18 | modal.afterOpen.subscribe(() => { 19 | const modelElement = modal.getElement()!; 20 | if (!modelElement || modelElement.className.indexOf(NzxModalDragService.DRAG_CLS_PREFIX) !== -1) { 21 | return; 22 | } 23 | 24 | modelElement.classList.add(wrapCls); 25 | const drag = this.modalDragService.createDragHandler(wrapCls, modal.nzMask, modal.nzModalType); 26 | modal.afterClose.subscribe(() => { 27 | if (drag && !drag.dropped) { 28 | drag.dispose(); 29 | } 30 | }); 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/modal/modal-drag.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { DragDrop, DragRef } from '@angular/cdk/drag-drop'; 3 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 4 | import { ModalTypes, NzModalService } from 'ng-zorro-antd/modal'; 5 | 6 | /** 7 | * 对话框拖动服务 8 | */ 9 | @Injectable() 10 | export class NzxModalDragService { 11 | static readonly DRAG_CLS_PREFIX = 'NZ-MODAL-WRAP-CLS-'; 12 | constructor(public modal: NzModalService, public dragDrop: DragDrop) {} 13 | 14 | /** 15 | * 创建拖拽手柄 16 | * @param wrapCls 类名 17 | * @param nzMask 是否有遮罩 18 | * @param nzModalType 对话框类型 19 | */ 20 | createDragHandler<T = NzSafeAny>(wrapCls: string, nzMask?: boolean, nzModalType?: ModalTypes): DragRef<T> { 21 | const wrapElement = document.querySelector<HTMLDivElement>(`.${wrapCls}`)!; 22 | const rootElement = wrapElement.querySelector<HTMLDivElement>(`.ant-modal-content`)!; 23 | const handle = 24 | nzModalType === 'confirm' 25 | ? rootElement.querySelector<HTMLDivElement>('.ant-modal-body')! 26 | : rootElement.querySelector<HTMLDivElement>('.ant-modal-header')!; 27 | this.fixedWrapElementStyle(wrapElement); 28 | if (!(nzMask == null || nzMask)) { 29 | this.setMaxZIndex(rootElement, wrapElement); 30 | } 31 | return this.dragDrop.createDrag(handle).withHandles([handle]).withRootElement(rootElement); 32 | } 33 | 34 | /** 35 | * 获取随机类名 36 | */ 37 | getRandomCls() { 38 | return NzxModalDragService.DRAG_CLS_PREFIX + Date.now() + Math.random().toString().replace('0.', ''); 39 | } 40 | 41 | /** 42 | * 解决wrap的样式, 设置鼠标可以穿透 43 | * @param wrapElement 44 | * @protected 45 | */ 46 | protected fixedWrapElementStyle(wrapElement: HTMLElement): void { 47 | wrapElement.style.pointerEvents = 'none'; 48 | } 49 | 50 | /** 51 | * 当前对话框点击时,设置当前对话框z-index为最大 52 | * @param rootElement 当前对话框 53 | * @param wrapElement 待修改z-index 容器 54 | * @protected 55 | */ 56 | protected setMaxZIndex(rootElement: HTMLElement, wrapElement: HTMLElement): void { 57 | rootElement.addEventListener( 58 | 'mousedown', 59 | () => { 60 | const maxZIndex = this.getModalMaxZIndex(wrapElement); 61 | if (maxZIndex) { 62 | wrapElement.style.zIndex = maxZIndex + 1 + ''; 63 | } 64 | }, 65 | false 66 | ); 67 | } 68 | 69 | /** 70 | * 获取所有对话框最大值,并确定是否需要修改 71 | * @param wrapElement 待修改z-index 容器 72 | */ 73 | protected getModalMaxZIndex(wrapElement: HTMLElement): number | null { 74 | const wrapZIndex = this.getZIndex(wrapElement); 75 | const maxZIndex = this.modal.openModals.reduce<number>((prev, modal) => { 76 | // @ts-ignore 77 | const element = (modal.containerInstance.host || modal.containerInstance.elementRef).nativeElement; 78 | if (wrapElement === element) { 79 | return prev; 80 | } 81 | const zIndex = this.getZIndex(element); 82 | return zIndex > prev ? zIndex : prev; 83 | }, 0); 84 | return maxZIndex >= wrapZIndex ? maxZIndex : null; 85 | } 86 | 87 | protected getZIndex(element: HTMLElement): number { 88 | return +getComputedStyle(element, null).zIndex; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /lib/modal/modal.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { NzxModalService } from './nzx-modal.service'; 3 | import { NzModalModule } from 'ng-zorro-antd/modal'; 4 | import { NzxModalDragDirective } from './modal-drag.directive'; 5 | import { NzxModalDragService } from './modal-drag.service'; 6 | 7 | @NgModule({ 8 | declarations: [NzxModalDragDirective], 9 | imports: [NzModalModule], 10 | exports: [NzxModalDragDirective], 11 | providers: [NzxModalService, NzxModalDragService] 12 | }) 13 | export class NzxModalModule {} 14 | -------------------------------------------------------------------------------- /lib/modal/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib": { 3 | "entryFile": "public-api.ts" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/modal/nzx-modal.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { Subject } from 'rxjs'; 4 | import { ConfirmType, ModalOptions, NzModalRef, NzModalService } from 'ng-zorro-antd/modal'; 5 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 6 | import { NzxModalDragService } from './modal-drag.service'; 7 | 8 | @Injectable() 9 | export class NzxModalService { 10 | constructor( 11 | public modal: NzModalService, 12 | public modalDragService: NzxModalDragService 13 | ) {} 14 | 15 | /** 16 | * 创建对话框, 增加可拖拽功能 17 | * @param config NzxModalOptions 18 | */ 19 | create<T, R = NzSafeAny>(config: NzxModalOptions<T, R>): NzModalRef<T, R> { 20 | return this.createModalWidthDrag(config, c => this.modal.create(c)); 21 | } 22 | 23 | get openModals(): NzModalRef[] { 24 | return this.modal.openModals; 25 | } 26 | 27 | get afterAllClosed(): Subject<void> { 28 | return this.modal._afterAllClosed; 29 | } 30 | 31 | closeAll(): void { 32 | this.modal.closeAll(); 33 | } 34 | 35 | confirm<T>(options: NzxModalOptions<T>, confirmType?: ConfirmType): NzModalRef<T> { 36 | return this.createModalWidthDrag(options, c => this.modal.confirm(c, confirmType)); 37 | } 38 | 39 | info<T>(options: NzxModalOptions<T>): NzModalRef<T> { 40 | return this.createModalWidthDrag(options, c => this.modal.info(c)); 41 | } 42 | 43 | success<T>(options: NzxModalOptions<T>): NzModalRef<T> { 44 | return this.createModalWidthDrag(options, c => this.modal.success(c)); 45 | } 46 | 47 | error<T>(options: NzxModalOptions<T>): NzModalRef<T> { 48 | return this.createModalWidthDrag(options, c => this.modal.error(c)); 49 | } 50 | 51 | warning<T>(options: NzxModalOptions<T>): NzModalRef<T> { 52 | return this.createModalWidthDrag(options, c => this.modal.warning(c)); 53 | } 54 | 55 | protected createModalWidthDrag<T, R = NzSafeAny>( 56 | config: NzxModalOptions<T, R>, 57 | create: (newConfig: NzxModalOptions<T, R>) => NzModalRef<T, R> 58 | ) { 59 | const wrapCls = this.modalDragService.getRandomCls(); 60 | const newConfig = this.createModalConfig(config, wrapCls); 61 | const modalRef = create(newConfig); 62 | 63 | modalRef.afterOpen.subscribe(() => { 64 | if (config.draggable !== false) { 65 | const drag = this.modalDragService.createDragHandler(wrapCls, config.nzMask, newConfig.nzModalType); 66 | modalRef.afterClose.subscribe(() => { 67 | if (drag && !drag.dropped) { 68 | drag.dispose(); 69 | } 70 | }); 71 | } 72 | }); 73 | return modalRef; 74 | } 75 | 76 | protected createModalConfig<T, R = NzSafeAny>(config: NzxModalOptions<T, R>, wrapCls: string): NzxModalOptions<T, R> { 77 | const defaultConfig: NzxModalOptions = { 78 | nzMaskClosable: false, 79 | nzTitle: '提示' 80 | }; 81 | const maskStyle = config.nzMask === false ? { nzMaskStyle: { display: 'none' } } : {}; 82 | const newConfig = Object.assign(defaultConfig, config, maskStyle); 83 | newConfig.nzWrapClassName = (newConfig.nzWrapClassName || '') + ' ' + wrapCls; 84 | return newConfig; 85 | } 86 | } 87 | 88 | export interface NzxModalOptions<T = NzSafeAny, D = NzSafeAny, R = NzSafeAny> extends ModalOptions<T, D, R> { 89 | /** 90 | * 是否允许拖拽 91 | */ 92 | draggable?: boolean; 93 | /** 94 | * 允许改变窗口大小 95 | */ 96 | resizable?: boolean; 97 | /** 98 | * 允许最大值 99 | */ 100 | maximum?: boolean; 101 | /** 102 | * 允许最小值 103 | */ 104 | minimum?: boolean; 105 | } 106 | -------------------------------------------------------------------------------- /lib/modal/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './modal.module'; 2 | export * from './nzx-modal.service'; 3 | export * from './modal-drag.service'; 4 | export * from './modal-drag.directive'; 5 | -------------------------------------------------------------------------------- /lib/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../dist/nzx-antd", 4 | "lib": { 5 | "entryFile": "./public-api.ts", 6 | "styleIncludePaths": ["node_modules"] 7 | }, 8 | "assets": [ 9 | "./**/*.less" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /lib/nzx-antd.less: -------------------------------------------------------------------------------- 1 | @import 'ng-zorro-antd/style/default.less'; 2 | @import './index.less'; 3 | -------------------------------------------------------------------------------- /lib/nzx-antd.module.ts: -------------------------------------------------------------------------------- 1 | // import { NgModule } from '@angular/core'; 2 | // import { NzxModalModule } from '@xmagic/nzx-antd/modal'; 3 | // import { NzxDirectiveModule } from '@xmagic/nzx-antd/directive'; 4 | // import { NzxTableModule } from '@xmagic/nzx-antd/table'; 5 | // import { NzxPipeModule } from '@xmagic/nzx-antd/pipe'; 6 | // import { NzxSwitchModule } from '@xmagic/nzx-antd/switch'; 7 | // import { NzxServiceModule } from '@xmagic/nzx-antd/service'; 8 | // import { NzxBetweenModule } from '@xmagic/nzx-antd/between'; 9 | // import { NzxBetweenInputModule } from '@xmagic/nzx-antd/between-input'; 10 | // import { NzxCheckboxModule } from '@xmagic/nzx-antd/checkbox'; 11 | // import { NzxUploadModule } from '@xmagic/nzx-antd/upload'; 12 | // import { NzxBetweenDatetimeModule } from '@xmagic/nzx-antd/between-datetime'; 13 | // import { NzxBetweenTimeModule } from '@xmagic/nzx-antd/between-time'; 14 | // import { NzxHttpInterceptorModule } from '@xmagic/nzx-antd/http-interceptor'; 15 | // import { NzxRepeatModule } from '@xmagic/nzx-antd/repeat'; 16 | // import { NzxLayoutPageModule } from '@xmagic/nzx-antd/layout-page'; 17 | // 18 | // @NgModule({ 19 | // imports: [], 20 | // exports: [ 21 | // NzxBetweenModule, 22 | // NzxBetweenInputModule, 23 | // NzxCheckboxModule, 24 | // NzxModalModule, 25 | // NzxDirectiveModule, 26 | // NzxTableModule, 27 | // NzxPipeModule, 28 | // NzxSwitchModule, 29 | // NzxServiceModule, 30 | // NzxUploadModule, 31 | // NzxBetweenDatetimeModule, 32 | // NzxBetweenTimeModule, 33 | // NzxHttpInterceptorModule, 34 | // NzxLayoutPageModule, 35 | // NzxRepeatModule 36 | // ], 37 | // declarations: [] 38 | // }) 39 | // export class NzxAntdModule {} 40 | -------------------------------------------------------------------------------- /lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@xmagic/nzx-antd", 3 | "version": "17.0.13", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/m310851010/nzx-antd.git" 7 | }, 8 | "author": "m310851010", 9 | "publishConfig": { 10 | "access": "public" 11 | }, 12 | "license": "MIT" 13 | } 14 | -------------------------------------------------------------------------------- /lib/pipe/defaultify.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 3 | import { NzxAntdService } from '@xmagic/nzx-antd'; 4 | 5 | @Pipe({ 6 | name: 'defaultify', 7 | pure: true 8 | }) 9 | export class DefaultifyPipe implements PipeTransform { 10 | constructor(protected antdService: NzxAntdService) {} 11 | transform<T = NzSafeAny>(value: T | undefined | null, defaultValue?: T | string): T | string { 12 | return value == null ? ((defaultValue == null ? this.antdService.defaultify || '--' : defaultValue) as T) : value; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/pipe/dic.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { DicItem, DicService } from '@xmagic/nzx-antd/service'; 3 | import { map, Observable, of } from 'rxjs'; 4 | import { NzxUtils } from '@xmagic/nzx-antd/util'; 5 | 6 | /** 7 | * 字典管道 8 | * @example 9 | * 10 | * ``` html 11 | * {{1 | dic: 'status' | async}} // 'label' 12 | * 13 | * {{'A' | dic: 'status' | async}} // 'label' 14 | * 15 | * {{'A' | dic: statusObservable$ | async}} // 'label' 16 | * 17 | * {{'A' | dic: statusObservable$ : true | async}} // dic item 18 | * 19 | * 20 | * ``` 21 | */ 22 | @Pipe({ 23 | name: 'dic', 24 | pure: true 25 | }) 26 | export class DicPipe implements PipeTransform { 27 | constructor(protected dicService: DicService) {} 28 | 29 | /** 30 | * 字典管道, 获取字典项label 31 | * @param key 字典项key 32 | * @param dicItemsOrKey 字典名称 33 | */ 34 | transform( 35 | key: string | number | null, 36 | dicItemsOrKey: Observable<DicItem[] | undefined | null> | string 37 | ): Observable<string>; 38 | 39 | /** 40 | * 字典管道, 获取字典项label 41 | * @param key 字典项key 42 | * @param dicItemsOrKey 字典名称 43 | * @param isGetItem 获取字典项label 44 | */ 45 | transform( 46 | key: string | number | null, 47 | dicItemsOrKey: Observable<DicItem[] | undefined | null> | string, 48 | isGetItem: false 49 | ): Observable<string>; 50 | 51 | /** 52 | * 字典管道, 获取字典项 53 | * @param key 字典项key 54 | * @param dicItemsOrKey 字典名称 55 | * @param isGetItem 获取字典项 56 | */ 57 | transform( 58 | key: string | number | null, 59 | dicItemsOrKey: Observable<DicItem[] | undefined | null> | string, 60 | isGetItem: true 61 | ): Observable<DicItem>; 62 | 63 | /** 64 | * 字典管道 65 | * @param key 字典项key 66 | * @param dicItemsOrKey 字典名称 67 | * @param isGetItem 是否获取字典项 68 | */ 69 | transform( 70 | key: string | number | null, 71 | dicItemsOrKey: Observable<DicItem[] | undefined | null> | string, 72 | isGetItem?: boolean 73 | ): Observable<string | null | DicItem> { 74 | if (key == null) { 75 | return of(null); 76 | } 77 | const dic$ = NzxUtils.isString(dicItemsOrKey) ? this.dicService.getDic(dicItemsOrKey) : dicItemsOrKey; 78 | return dic$.pipe(map(list => NzxUtils.listToMap(list, 'value', isGetItem ? (v: DicItem) => v : 'label')[key])); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/pipe/filter.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 3 | import { NzxUtils } from '@xmagic/nzx-antd/util'; 4 | 5 | @Pipe({ 6 | name: 'filter', 7 | pure: true 8 | }) 9 | export class FilterPipe implements PipeTransform { 10 | transform<T = NzSafeAny>(array: T[], matcher: string, searchText?: string): T[]; 11 | transform<T = NzSafeAny>(array: T[], matcher: FilterMatcherFn<T>): T[]; 12 | 13 | /** 14 | * Filter array 15 | * 16 | * 过滤数组 17 | */ 18 | transform<T = NzSafeAny>(array: T[], matcher: FilterMatcherFn<T> | string, searchText?: string): T[] { 19 | if (typeof matcher === 'string') { 20 | if (NzxUtils.isEmpty(searchText)) { 21 | return array; 22 | } 23 | 24 | // @ts-ignore 25 | const text = searchText.toLowerCase(); 26 | return (array || []).filter(v => NzxUtils.get(v, matcher, '').toLowerCase().indexOf(text) !== -1); 27 | } 28 | return (array || []).filter(v => matcher(v)); 29 | } 30 | } 31 | 32 | export type FilterMatcherFn<T> = (item: T) => boolean; 33 | -------------------------------------------------------------------------------- /lib/pipe/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Use of this source code is governed by an MIT-style license that can be 3 | * 4 | */ 5 | 6 | export * from './public-api'; 7 | -------------------------------------------------------------------------------- /lib/pipe/math.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | /** 4 | * 调用数学函数 如{{ 5.1 | math: 'ceil' }} => 6 5 | */ 6 | @Pipe({ 7 | name: 'math', 8 | pure: true 9 | }) 10 | export class MathPipe implements PipeTransform { 11 | transform(value: string | number, fnName: keyof Math, fixed?: number): string | null { 12 | if (value == null) { 13 | return null; 14 | } 15 | const n: number = typeof value === 'string' ? Number(value) : value; 16 | // @ts-ignore 17 | const result = Math[fnName](n); 18 | return fixed == null ? result : n.toFixed(fixed); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/pipe/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib": { 3 | "entryFile": "public-api.ts" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/pipe/path-value.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { NzxUtils } from '@xmagic/nzx-antd/util'; 3 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 4 | 5 | /** 6 | * 根据路径获取数据 7 | * @example 8 | * {{ {a: {b: 1}} | pathValue: 'a.b'}} // 1 9 | */ 10 | @Pipe({ 11 | name: 'pathValue', 12 | pure: true 13 | }) 14 | export class PathValuePipe implements PipeTransform { 15 | transform<T>(value: NzSafeAny, path: string, defaultValue?: NzSafeAny): T { 16 | return NzxUtils.get(value, path, defaultValue); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/pipe/pipe.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { MathPipe } from './math.pipe'; 4 | import { DefaultifyPipe } from './defaultify.pipe'; 5 | import { TrustHtmlPipe, TrustScriptPipe, TrustStylePipe, TrustUrlPipe } from './trust-resource.pipe'; 6 | import { TimeUnitPipe } from './time-unit.pipe'; 7 | import { ToAsyncPipe } from './to-async.pipe'; 8 | import { FilterPipe } from './filter.pipe'; 9 | import { DicPipe } from './dic.pipe'; 10 | import { PathValuePipe } from './path-value.pipe'; 11 | 12 | const PIPE = [ 13 | MathPipe, 14 | DefaultifyPipe, 15 | TrustUrlPipe, 16 | TrustHtmlPipe, 17 | TrustScriptPipe, 18 | TrustStylePipe, 19 | TimeUnitPipe, 20 | ToAsyncPipe, 21 | FilterPipe, 22 | DicPipe, 23 | PathValuePipe 24 | ]; 25 | @NgModule({ 26 | declarations: [PIPE], 27 | imports: [CommonModule], 28 | exports: [PIPE] 29 | }) 30 | export class NzxPipeModule {} 31 | -------------------------------------------------------------------------------- /lib/pipe/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './defaultify.pipe'; 2 | export * from './filter.pipe'; 3 | export * from './trust-resource.pipe'; 4 | export * from './math.pipe'; 5 | export * from './time-unit.pipe'; 6 | export * from './to-async.pipe'; 7 | export * from './dic.pipe'; 8 | export * from './path-value.pipe'; 9 | export * from './pipe.module'; 10 | -------------------------------------------------------------------------------- /lib/pipe/time-unit.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | 3 | @Pipe({ 4 | name: 'timeUnit', 5 | pure: true 6 | }) 7 | export class TimeUnitPipe implements PipeTransform { 8 | transform(value: number | undefined | null, unit: 's' | 'ms'): string | null { 9 | if (value == null) { 10 | return null; 11 | } 12 | 13 | const second = unit === 's' ? value : Math.floor(value / 1000); 14 | const days = Math.floor(second / 86400); 15 | const hours = Math.floor((second % 86400) / 3600); 16 | const minutes = Math.floor(((second % 86400) % 3600) / 60); 17 | const seconds = Math.floor(((second % 86400) % 3600) % 60); 18 | 19 | const units = ['天', '小时', '分', '秒']; 20 | const format = [days, hours, minutes, seconds].reduce((str, v, i) => (v ? str + v + units[i] : str), ''); 21 | return format || '0秒'; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/pipe/to-async.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { from, map, Observable, of } from 'rxjs'; 3 | import { FetcherService, FetchOptions } from '@xmagic/nzx-antd/service'; 4 | import { NzxUtils } from '@xmagic/nzx-antd/util'; 5 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 6 | 7 | /** 8 | * 把请求信息转换为异步对象 9 | */ 10 | @Pipe({ 11 | name: 'toAsync', 12 | pure: true 13 | }) 14 | export class ToAsyncPipe implements PipeTransform { 15 | constructor(private fetcher: FetcherService) {} 16 | 17 | /** 18 | * 转换为Observable 19 | * @param value 可以是url或者Observable, Promise 20 | * @param option 配置项 21 | */ 22 | transform<T = NzSafeAny, U = NzSafeAny>( 23 | value: string | Observable<T> | Promise<T> | T, 24 | option?: AsyncOption 25 | ): Observable<U> | null { 26 | if (value == null) { 27 | return of(option?.defaultValue as U); 28 | } 29 | 30 | const opt = option || ({} as AsyncOption); 31 | const mapFn = NzxUtils.isFunction(opt.map) ? opt.map : (data: T) => data as unknown as U; 32 | if (typeof value === 'string') { 33 | return this.fetcher.fetch<T>({ ...opt, url: value }).pipe( 34 | map(v => NzxUtils.defaultIfy(v, option?.defaultValue)), 35 | map<T, U>(mapFn) 36 | ); 37 | } 38 | 39 | if (NzxUtils.isPromise(value) || NzxUtils.isObservable(value)) { 40 | return from(value).pipe( 41 | map(v => NzxUtils.defaultIfy(v, option?.defaultValue)), 42 | map<T, U>(mapFn) 43 | ); 44 | } 45 | 46 | return of(value as T).pipe(map<T, U>(mapFn)); 47 | } 48 | } 49 | 50 | /** 51 | * 异步请求信息 52 | */ 53 | export type AsyncOption = Omit<FetchOptions, 'url'> & { 54 | /** 55 | * 映射数据 56 | * @param data 57 | * @param index 58 | */ 59 | map?: (data: NzSafeAny) => NzSafeAny; 60 | /** 61 | * 默认值 62 | */ 63 | defaultValue?: NzSafeAny; 64 | }; 65 | -------------------------------------------------------------------------------- /lib/pipe/trust-resource.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { DomSanitizer, SafeHtml, SafeResourceUrl, SafeScript, SafeStyle } from '@angular/platform-browser'; 3 | 4 | /** 5 | * 非安全加载URL,比如加载iframe url 6 | */ 7 | @Pipe({ 8 | name: 'trustUrl', 9 | pure: true 10 | }) 11 | export class TrustUrlPipe implements PipeTransform { 12 | constructor(private sanitizer: DomSanitizer) {} 13 | transform(url: string): SafeResourceUrl { 14 | return this.sanitizer.bypassSecurityTrustResourceUrl(url); 15 | } 16 | } 17 | 18 | /** 19 | * 非安全加载HTML 20 | */ 21 | @Pipe({ 22 | name: 'trustHtml', 23 | pure: true 24 | }) 25 | export class TrustHtmlPipe implements PipeTransform { 26 | constructor(private sanitizer: DomSanitizer) {} 27 | transform(html: string, enabled = true): SafeHtml { 28 | return enabled ? this.sanitizer.bypassSecurityTrustHtml(html) : html; 29 | } 30 | } 31 | 32 | /** 33 | * 非安全调用Script 34 | */ 35 | @Pipe({ 36 | name: 'trustScript', 37 | pure: true 38 | }) 39 | export class TrustScriptPipe implements PipeTransform { 40 | constructor(private sanitizer: DomSanitizer) {} 41 | transform(script: string, enabled = true): SafeScript { 42 | return enabled ? this.sanitizer.bypassSecurityTrustScript(script) : script; 43 | } 44 | } 45 | 46 | /** 47 | * 非安全调用Style 48 | */ 49 | @Pipe({ 50 | name: 'trustStyle', 51 | pure: true 52 | }) 53 | export class TrustStylePipe implements PipeTransform { 54 | constructor(private sanitizer: DomSanitizer) {} 55 | transform(style: string, enabled = true): SafeStyle { 56 | return enabled ? this.sanitizer.bypassSecurityTrustStyle(style) : style; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of nzx-antd 3 | */ 4 | 5 | // export default void 0; 6 | export * from './nzx-antd.service'; 7 | // export * from './nzx-antd.module'; 8 | // 9 | // export * from './lib/between/public_api'; 10 | // export * from './lib/between-input/public_api'; 11 | // export * from './lib/checkbox/public_api'; 12 | // export * from './lib/switch/public_api'; 13 | // export * from './lib/table/public_api'; 14 | // export * from './lib/upload/public_api'; 15 | // export * from './lib/between-datetime/public_api'; 16 | // export * from './lib/between-time/public_api'; 17 | // export * from './lib/directive/public_api'; 18 | // export * from './lib/modal/public_api'; 19 | // export * from './lib/repeat/public_api'; 20 | // export * from './lib/layout-page/public_api'; 21 | // 22 | // export * from './lib/pipe/public_api'; 23 | // export * from './lib/service/public_api'; 24 | // export * from './lib/util/public_api'; 25 | // export * from './lib/http-interceptor/public_api'; 26 | -------------------------------------------------------------------------------- /lib/repeat/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Use of this source code is governed by an MIT-style license that can be 3 | * 4 | */ 5 | 6 | export * from './public-api'; 7 | -------------------------------------------------------------------------------- /lib/repeat/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib": { 3 | "entryFile": "public-api.ts" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/repeat/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './repeat.component'; 2 | export * from './repeat.module'; 3 | -------------------------------------------------------------------------------- /lib/repeat/repeat.component.html: -------------------------------------------------------------------------------- 1 | <p>repeat works!</p> 2 | -------------------------------------------------------------------------------- /lib/repeat/repeat.component.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m310851010/nzx-antd/6089df3a3fbe55f3c9b406b8f0b9d4a5d302b1df/lib/repeat/repeat.component.less -------------------------------------------------------------------------------- /lib/repeat/repeat.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'nzx-repeat', 5 | templateUrl: './repeat.component.html', 6 | styleUrls: ['./repeat.component.less'] 7 | }) 8 | export class RepeatComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit(): void { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/repeat/repeat.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { RepeatComponent } from './repeat.component'; 4 | 5 | @NgModule({ 6 | declarations: [RepeatComponent], 7 | imports: [CommonModule], 8 | exports: [RepeatComponent] 9 | }) 10 | export class NzxRepeatModule {} 11 | -------------------------------------------------------------------------------- /lib/service/auth-guard.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Injector } from '@angular/core'; 2 | import { ActivatedRouteSnapshot, Data, Route, Router, RouterStateSnapshot } from '@angular/router'; 3 | import { Observable, of, tap } from 'rxjs'; 4 | import { NzxUtils } from '@xmagic/nzx-antd/util'; 5 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 6 | import { NzxAntdService } from '@xmagic/nzx-antd'; 7 | 8 | /** 9 | * 权限路由守卫服务, 具体数据格式,由NzxAntdService.hasAuth决定处理 10 | * @example 11 | * const routes: Routes = [ 12 | * { 13 | * path: 'xxx', 14 | * canActivate: [ NzxAuthGuardService ], 15 | * data: { guard: 'user1', noAuthUrl: '/no-permisseion' } 16 | * }, 17 | * { 18 | * path: 'xxx', 19 | * canActivate: [ NzxAuthGuardService ], 20 | * data: { guard: { auth: ['user1', 'user2'], noAuthUrl: '/no-permisseion'} }} 21 | * }, 22 | * { 23 | * path: 'yyy', 24 | * canActivate: [ NzxAuthGuardService ], 25 | * data: { guard: ((router, injector, antdService) => of(true)) as AuthGuardType, noAuthUrl: '/no-permisseion' } 26 | * } 27 | * ]; 28 | */ 29 | @Injectable({ 30 | providedIn: 'root' 31 | }) 32 | export class NzxAuthGuardService { 33 | protected hasAuth: Required<NzxAntdService>['hasAuth'] = () => of(true); 34 | 35 | constructor(private antdService: NzxAntdService, private router: Router, private injector: Injector) { 36 | if (this.antdService.hasAuth) { 37 | this.hasAuth = this.antdService.hasAuth; 38 | } 39 | } 40 | 41 | private process(data: Data): Observable<boolean> { 42 | const guard: AuthGuardType = data.guard; 43 | 44 | if (NzxUtils.isFunction(guard)) { 45 | return guard(this.router, this.injector, this.antdService); 46 | } 47 | 48 | return this.hasAuth(guard.auth).pipe( 49 | tap(auth => { 50 | const url = guard.noAuthUrl || this.antdService.noAuthUrl; 51 | if (!auth && url) { 52 | this.router.navigateByUrl(url); 53 | } 54 | }) 55 | ); 56 | } 57 | 58 | // lazy loading 59 | canLoad(route: Route): Observable<boolean> { 60 | return this.process(route.data!); 61 | } 62 | 63 | // all children route 64 | canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> { 65 | return this.canActivate(childRoute, state); 66 | } 67 | 68 | // route 69 | canActivate(route: ActivatedRouteSnapshot, _state: RouterStateSnapshot | null): Observable<boolean> { 70 | return this.process(route.data); 71 | } 72 | } 73 | 74 | /** 75 | * 权限路由守卫 使用函数处理 76 | */ 77 | export type AuthGuardFnType = (router: Router, injector: Injector, antdService: NzxAntdService) => Observable<boolean>; 78 | /** 79 | * 权限路由守卫配置 80 | */ 81 | export type AuthGuardType<T = NzSafeAny> = AuthGuardFnType | T; 82 | -------------------------------------------------------------------------------- /lib/service/download.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpResponse } from '@angular/common/http'; 3 | import { FetcherService, FetchOptions } from './fetcher.service'; 4 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 5 | import { HttpErrorBean } from '@xmagic/nzx-antd'; 6 | 7 | @Injectable() 8 | export class NzxDownloadService { 9 | constructor(protected fetcher: FetcherService) {} 10 | 11 | /** 12 | * 下载文件 13 | * @param options 下载文件配置信息 14 | */ 15 | download(options: DownloadOption) { 16 | this.fetcher.fetch<HttpResponse<Blob>>({ ...options, responseType: 'blob', observe: 'response' }).subscribe({ 17 | next: resp => { 18 | const fn = options.getFileName || this.getFilename.bind(this); 19 | const filename = fn(resp, options.url); 20 | if (options.afterDownload && options.afterDownload(resp, filename) === false) { 21 | return; 22 | } 23 | 24 | this.saveAs(resp.body!, filename); 25 | if (options.downloadDone) { 26 | options.downloadDone(resp, filename); 27 | } 28 | }, 29 | error: error => options.downloadError && options.downloadError(error), 30 | complete: () => options.downloadComplete && options.downloadComplete() 31 | }); 32 | } 33 | 34 | /** 35 | * 文件另存为 36 | * @param body 二进制内容 37 | * @param filename 文件名 38 | */ 39 | saveAs(body: Blob, filename: string) { 40 | if (typeof (window.navigator as NzSafeAny).msSaveBlob !== 'undefined') { 41 | (window.navigator as NzSafeAny).msSaveBlob(body, filename); 42 | return; 43 | } 44 | 45 | const blobURL = window.URL.createObjectURL(body as Blob); 46 | const tempLink = document.createElement('a'); 47 | tempLink.style.display = 'none'; 48 | tempLink.href = blobURL; 49 | tempLink.setAttribute('download', filename); 50 | if (typeof tempLink.download === 'undefined') { 51 | tempLink.setAttribute('target', '_blank'); 52 | } 53 | document.body.appendChild(tempLink); 54 | tempLink.click(); 55 | document.body.removeChild(tempLink); 56 | window.URL.revokeObjectURL(blobURL); 57 | } 58 | 59 | /** 60 | * 获取文件名称 61 | * @param resp 62 | * @param url 63 | * @protected 64 | */ 65 | getFilename(resp: HttpResponse<Blob>, url: string): string { 66 | const headers = resp.headers; 67 | const disposition = headers.get('content-disposition'); 68 | const filename = headers.get('filename'); 69 | if (filename) { 70 | return decodeURIComponent(filename.trim()); 71 | } else if (disposition) { 72 | return disposition 73 | .split(';') 74 | .filter(v => v.indexOf('filename=') >= 0)[0] 75 | .split('=')[1] 76 | .replace(/(^")|("$)/g, '') 77 | .trim(); 78 | } else { 79 | const start = url.lastIndexOf('/') + 1; 80 | const endIndex = url.lastIndexOf('?'); 81 | const end = endIndex === -1 ? url.length : endIndex; 82 | return url.substring(start, end); 83 | } 84 | } 85 | } 86 | 87 | /** 88 | * 下载文件配置信息 89 | */ 90 | export type DownloadOption = Omit<FetchOptions, 'observe'> & { 91 | /** 92 | * 请求完成后的回调 93 | * @param resp 94 | * @param filename 95 | */ 96 | afterDownload?: (resp: HttpResponse<Blob>, filename: string) => boolean | void; 97 | 98 | /** 99 | * 下载并保存完成的回调 100 | * @param resp 101 | * @param filename 102 | */ 103 | downloadDone?: (resp: HttpResponse<Blob>, filename: string) => void; 104 | /** 105 | * 下载发生错误回调 106 | * @param error 107 | */ 108 | downloadError?: (error: HttpErrorBean) => void; 109 | /** 110 | * 下载结束回调, 不管成功还是失败 111 | */ 112 | downloadComplete?: () => void; 113 | 114 | /** 115 | * 获取文件名 116 | * @param resp 响应对象 117 | * @param url 请求的url 118 | */ 119 | getFileName?: (resp: HttpResponse<Blob>, url: string) => string; 120 | }; 121 | -------------------------------------------------------------------------------- /lib/service/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Use of this source code is governed by an MIT-style license that can be 3 | * 4 | */ 5 | 6 | export * from './public-api'; 7 | -------------------------------------------------------------------------------- /lib/service/loading.service.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 全局loading 3 | */ 4 | import { Injectable } from '@angular/core'; 5 | 6 | @Injectable({ 7 | providedIn: 'root' 8 | }) 9 | export class NzxLoadingService { 10 | readonly loadingId = '__MESSAGE_LOADING__'; 11 | 12 | /** 13 | * 显示loading 14 | * @param type loading类型 15 | * @param message 消息 16 | */ 17 | show(type: 'message' | 'spin' = 'message', message: string = '加载中...') { 18 | this.showx(this.loadingId, type, () => { 19 | if (type === 'spin') { 20 | return ` 21 | <div class="ant-modal-wrap" style="position: fixed; z-index: 999998; background-color: rgba(255, 255, 255, 0.35)"></div> 22 | <div 23 | class="ant-spin ant-spin-spinning ant-spin-show-text" 24 | style="top: 45%; transform: translate(-50%); z-index: 999999; position: fixed; left: 50%" 25 | > 26 | <span class="ant-spin-dot ant-spin-dot-spin"> 27 | <i class="ant-spin-dot-item"></i> 28 | <i class="ant-spin-dot-item"></i> 29 | <i class="ant-spin-dot-item"></i> 30 | <i class="ant-spin-dot-item"></i> 31 | </span> 32 | <div class="ant-spin-text">${message}</div> 33 | </div>`; 34 | } 35 | 36 | return ` 37 | <div class="ant-modal-wrap" style="z-index: 999998;background-color: rgba(255, 255, 255,.35);"></div> 38 | <div class="ant-message-notice ant-message" style="top: 45%; z-index: 999999;"> 39 | <div class="ant-message-notice-content" style="min-width: 150px;"> 40 | <div class="ant-message-loading"> 41 | <i class="anticon anticon-loading"> 42 | <svg viewBox="0 0 1024 1024" focusable="false" fill="currentColor" width="1em" height="1em" 43 | class="anticon-spin"> 44 | <path 45 | d="M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 00-94.3-139.9 437.71 437.71 0 00-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"></path> 46 | </svg> 47 | </i> 48 | <span>${message}</span> 49 | </div> 50 | </div> 51 | </div>`; 52 | }); 53 | } 54 | 55 | private showx(id: string, type: 'message' | 'spin', html: () => string) { 56 | const loading = document.getElementById(id); 57 | if (loading) { 58 | if (loading.getAttribute('type') !== type) { 59 | loading.setAttribute('type', type); 60 | loading.innerHTML = html(); 61 | } 62 | loading.style.display = 'block'; 63 | return; 64 | } 65 | const div = document.createElement('div'); 66 | div.setAttribute('type', type); 67 | div.setAttribute('id', id); 68 | div.innerHTML = html(); 69 | document.body.append(div); 70 | } 71 | 72 | /** 73 | * 显示或隐藏loading 74 | * @param show 是否显示 75 | */ 76 | loading(show: boolean = true): void { 77 | show ? this.show() : this.hide(); 78 | } 79 | 80 | /** 81 | * 隐藏loading 82 | */ 83 | hide() { 84 | const loading = document.getElementById(this.loadingId); 85 | if (loading) { 86 | loading.style.display = 'none'; 87 | } 88 | } 89 | } 90 | 91 | export const loadingService = new NzxLoadingService(); 92 | -------------------------------------------------------------------------------- /lib/service/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib": { 3 | "entryFile": "public-api.ts" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/service/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './loading.service'; 2 | export * from './download.service'; 3 | export * from './fetcher.service'; 4 | export * from './storage.service'; 5 | export * from './auth-guard.service'; 6 | export * from './dic.service'; 7 | export * from './service.module'; 8 | -------------------------------------------------------------------------------- /lib/service/service.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { FetcherService } from './fetcher.service'; 4 | import { HttpClientModule } from '@angular/common/http'; 5 | import { NzxDownloadService } from './download.service'; 6 | 7 | @NgModule({ 8 | declarations: [], 9 | imports: [CommonModule, HttpClientModule], 10 | providers: [FetcherService, NzxDownloadService] 11 | }) 12 | export class NzxServiceModule {} 13 | -------------------------------------------------------------------------------- /lib/service/storage.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Optional } from '@angular/core'; 2 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 3 | 4 | @Injectable({ 5 | providedIn: 'root' 6 | }) 7 | export class NzxStorageService { 8 | /** 9 | * 获取长度 10 | */ 11 | get length() { 12 | return this.storage.length; 13 | } 14 | 15 | constructor(@Optional() public storage: Storage) { 16 | if (!storage) { 17 | this.storage = localStorage; 18 | } 19 | } 20 | 21 | /** 22 | * 清除所有数据 23 | */ 24 | clear() { 25 | this.storage.clear(); 26 | } 27 | 28 | /** 29 | * 获取值 30 | * @param key key 31 | */ 32 | getItem<T = NzSafeAny>(key: string): T | null { 33 | const value = this.storage.getItem(key); 34 | return value == null ? null : (JSON.parse(value) as T); 35 | } 36 | 37 | /** 38 | * 获取key 39 | * @param index 索引 40 | */ 41 | key(index: number): string | null { 42 | return this.storage.key(index); 43 | } 44 | 45 | /** 46 | * 移除值 47 | * @param key key 48 | */ 49 | removeItem(key: string): void { 50 | this.storage.removeItem(key); 51 | } 52 | 53 | /** 54 | * 设置值 55 | * @param key 56 | * @param value 57 | */ 58 | setItem<T = NzSafeAny>(key: string, value: T): void { 59 | this.storage.setItem(key, JSON.stringify(value)); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/switch/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Use of this source code is governed by an MIT-style license that can be 3 | * 4 | */ 5 | 6 | export * from './public-api'; 7 | -------------------------------------------------------------------------------- /lib/switch/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib": { 3 | "entryFile": "public-api.ts" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/switch/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './switch.component'; 2 | export * from './switch.module'; 3 | -------------------------------------------------------------------------------- /lib/switch/switch.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AfterViewInit, 3 | ChangeDetectionStrategy, 4 | ChangeDetectorRef, 5 | Component, 6 | forwardRef, 7 | Input, 8 | TemplateRef, 9 | ViewChild 10 | } from '@angular/core'; 11 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; 12 | import { NzSafeAny, NzSizeDSType } from 'ng-zorro-antd/core/types'; 13 | import { BaseControl } from '@xmagic/nzx-antd/util'; 14 | import { NzSwitchComponent } from 'ng-zorro-antd/switch'; 15 | 16 | @Component({ 17 | selector: 'nzx-switch', 18 | template: ` 19 | <nz-switch 20 | #nzSwitch 21 | [(ngModel)]="nzxValue" 22 | [nzCheckedChildren]="nzCheckedChildren" 23 | [nzUnCheckedChildren]="nzUnCheckedChildren" 24 | [nzDisabled]="nzDisabled" 25 | [nzSize]="nzSize" 26 | [nzLoading]="nzLoading" 27 | [nzControl]="nzControl" 28 | (ngModelChange)="ngModelChange($event)" 29 | ></nz-switch> 30 | `, 31 | changeDetection: ChangeDetectionStrategy.OnPush, 32 | providers: [ 33 | { 34 | provide: NG_VALUE_ACCESSOR, 35 | useExisting: forwardRef(() => NzxSwitchComponent), 36 | multi: true 37 | } 38 | ] 39 | }) 40 | export class NzxSwitchComponent extends BaseControl<NzSafeAny> implements ControlValueAccessor, AfterViewInit { 41 | @ViewChild('nzSwitch', { static: true }) nzSwitch!: NzSwitchComponent; 42 | nzxValue!: boolean; 43 | /** 44 | * 选中时的值 45 | */ 46 | @Input() nzxCheckedValue: NzSafeAny = true; 47 | /** 48 | * 非选中时的值 49 | */ 50 | @Input() nzxUnCheckedValue: NzSafeAny = false; 51 | /** 52 | * disable 状态 53 | */ 54 | @Input() nzDisabled?: boolean; 55 | /** 56 | * 加载中的开关 57 | */ 58 | @Input() nzLoading?: boolean; 59 | /** 60 | * 是否完全由用户控制状态, Switch 的状态完全由用户接管,不再自动根据点击事件改变数据。 61 | */ 62 | @Input() nzControl = false; 63 | /** 64 | * 选中时的内容 65 | */ 66 | @Input() nzCheckedChildren?: string | TemplateRef<void>; 67 | /** 68 | * 非选中时的内容 69 | */ 70 | @Input() nzUnCheckedChildren?: string | TemplateRef<void>; 71 | /** 72 | * 开关大小 73 | */ 74 | @Input() nzSize?: NzSizeDSType; 75 | 76 | constructor(private cdr: ChangeDetectorRef) { 77 | super(); 78 | } 79 | 80 | ngAfterViewInit(): void { 81 | const touched = this.nzSwitch.onTouched; 82 | this.nzSwitch.onTouched = () => { 83 | touched.call(this.nzSwitch); 84 | this.onTouched(); 85 | }; 86 | } 87 | 88 | ngModelChange(val: boolean) { 89 | this.onChange(val ? this.nzxCheckedValue : this.nzxUnCheckedValue); 90 | } 91 | 92 | writeValue(value: NzSafeAny | null): void { 93 | this.nzxValue = value === this.nzxCheckedValue; 94 | this.cdr.markForCheck(); 95 | } 96 | 97 | override setDisabledState(isDisabled: boolean): void { 98 | this.nzDisabled = isDisabled; 99 | this.cdr.markForCheck(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /lib/switch/switch.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NzxSwitchComponent } from './switch.component'; 4 | import { NzSwitchModule } from 'ng-zorro-antd/switch'; 5 | import { FormsModule } from '@angular/forms'; 6 | 7 | @NgModule({ 8 | declarations: [NzxSwitchComponent], 9 | imports: [CommonModule, FormsModule, NzSwitchModule], 10 | exports: [NzxSwitchComponent] 11 | }) 12 | export class NzxSwitchModule {} 13 | -------------------------------------------------------------------------------- /lib/table/const.ts: -------------------------------------------------------------------------------- 1 | import { FetchSetting } from './table.type'; 2 | 3 | export const FETCH_SETTING: FetchSetting = { 4 | pageIndexField: 'pageIndex', 5 | pageSizeField: 'pageSize', 6 | listField: 'list', 7 | totalField: 'total', 8 | method: 'post', 9 | responseType: 'json' 10 | }; 11 | -------------------------------------------------------------------------------- /lib/table/header/column-setting/column-setting.component.html: -------------------------------------------------------------------------------- 1 | <i 2 | nz-icon 3 | nzType="setting" 4 | nzTheme="outline" 5 | tabindex="-1" 6 | nz-popover 7 | nzPopoverTrigger="click" 8 | nzPopoverOverlayClassName="nzx-column-setting__cloumn-box" 9 | [nzPopoverTitle]="titleTemplate" 10 | [nzPopoverPlacement]="'bottomRight'" 11 | [nzPopoverContent]="contentTemplate" 12 | nz-tooltip 13 | nzTooltipTitle="列设置" 14 | ></i> 15 | 16 | <ng-template #titleTemplate> 17 | <div class="nzx-column-setting__popover-title"> 18 | <div class="nzx-column-setting__flex" style="flex: 1"> 19 | <label 20 | *ngIf="columnNameVisible !== false" 21 | nz-checkbox 22 | [(ngModel)]="_columnNameChecked" 23 | [nzIndeterminate]="_indeterminate" 24 | (ngModelChange)="columnNameChange($event)" 25 | > 26 | 展示列 27 | </label> 28 | </div> 29 | </div> 30 | </ng-template> 31 | 32 | <ng-template #contentTemplate> 33 | <ul 34 | style="min-width: 315px" 35 | cdkDropList 36 | (cdkDropListDropped)="dropColumn($event, _nzxColumns)" 37 | class="nzx-column-setting__column-list" 38 | > 39 | <ng-container *ngFor="let item of _nzxColumns"> 40 | <li 41 | *ngIf="item.settingVisible !== false" 42 | class="nzx-column-setting__check-item" 43 | cdkDrag 44 | cdkDragLockAxis="y" 45 | cdkDragBoundary=".nzx-column-setting__column-list" 46 | (cdkDragStarted)="cdkDragStarted($event)" 47 | (cdkDragReleased)="cdkDragReleased($event)" 48 | > 49 | <div class="drag-box"> 50 | <i cdkDragHandle nz-icon nzType="drag" class="nzx-column-setting__drag-icon"></i> 51 | <label 52 | nz-checkbox 53 | [nzDisabled]="item.settingDisabled === true" 54 | [(ngModel)]="item.visible" 55 | (ngModelChange)="columnVisible(item, $event)" 56 | > 57 | {{ item.settingText || item.thText }} 58 | </label> 59 | </div> 60 | 61 | <div class="nzx-column-setting__flex"> 62 | <i 63 | nz-icon 64 | nzType="vertical-align-top" 65 | class="nzx-column-setting__fixed-left" 66 | [class.active]="item.fixed === 'left'" 67 | [class.disabled]="item.visible === false" 68 | (click)="fixedColumn(item, item.fixed == 'left' ? null : 'left')" 69 | nz-tooltip 70 | nzTooltipTitle="固定到左侧" 71 | ></i> 72 | <nz-divider nzType="vertical"></nz-divider> 73 | <i 74 | nz-icon 75 | nzType="vertical-align-bottom" 76 | class="nzx-column-setting__fixed-right" 77 | [class.active]="item.fixed === 'right'" 78 | [class.disabled]="item.visible === false" 79 | (click)="fixedColumn(item, item.fixed == 'right' ? null : 'right')" 80 | nz-tooltip 81 | nzTooltipTitle="固定到右侧" 82 | ></i> 83 | </div> 84 | </li> 85 | </ng-container> 86 | </ul> 87 | </ng-template> 88 | -------------------------------------------------------------------------------- /lib/table/header/column-setting/column-setting.component.less: -------------------------------------------------------------------------------- 1 | @import '../../../base.less'; 2 | @nzx-column-setting-prefix-cls: ~'@{nzx-prefix}-column-setting'; 3 | 4 | .@{nzx-column-setting-prefix-cls} { 5 | &__popover-title { 6 | display: flex; 7 | align-items: center; 8 | } 9 | 10 | &__flex { 11 | display: flex; 12 | align-items: center; 13 | } 14 | 15 | &__cloumn-box { 16 | .ant-popover-inner-content { 17 | padding-right: 0; 18 | padding-left: 0; 19 | } 20 | 21 | .cdk-drop-list-dragging .cdk-drag { 22 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 23 | } 24 | 25 | .cdk-drag-placeholder { 26 | opacity: 0; 27 | } 28 | } 29 | 30 | &__drag-preview { 31 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); 32 | } 33 | 34 | &__column-list { 35 | padding: 0; 36 | } 37 | 38 | &__check-item { 39 | display: flex; 40 | align-items: center; 41 | padding: 6px 16px 6px 0; 42 | justify-content: space-between; 43 | 44 | .drag-box { 45 | display: flex; 46 | align-items: center; 47 | flex: 1; 48 | } 49 | 50 | .ant-checkbox-wrapper { 51 | flex: 1; 52 | &:hover { 53 | color: @primary-color; 54 | } 55 | } 56 | } 57 | 58 | &__fixed-left, 59 | &__fixed-right { 60 | color: #666; 61 | cursor: pointer; 62 | font-size: 16px; 63 | transform: rotate(-90deg); 64 | 65 | &.active, 66 | &:hover { 67 | color: @primary-color; 68 | } 69 | 70 | &.disabled { 71 | color: @disabled-color; 72 | cursor: not-allowed; 73 | } 74 | } 75 | 76 | &__drag-icon { 77 | display: inline-block; 78 | width: 16px; 79 | height: 16px; 80 | margin: 0 5px; 81 | font-size: 16px; 82 | cursor: move; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/table/header/column-setting/column-setting.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ChangeDetectionStrategy, 3 | Component, 4 | ElementRef, 5 | EventEmitter, 6 | Input, 7 | OnInit, 8 | Output, 9 | Renderer2, 10 | ViewEncapsulation 11 | } from '@angular/core'; 12 | import { CdkDragDrop, CdkDragRelease, CdkDragStart, moveItemInArray } from '@angular/cdk/drag-drop'; 13 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 14 | import { NzxColumn } from '../../table.type'; 15 | 16 | /** 17 | * 列设置 18 | * 注意: 只在表头不分组的情况下有效 19 | */ 20 | @Component({ 21 | selector: 'nzx-column-setting', 22 | templateUrl: './column-setting.component.html', 23 | preserveWhitespaces: false, 24 | encapsulation: ViewEncapsulation.None, 25 | changeDetection: ChangeDetectionStrategy.OnPush 26 | }) 27 | export class NzxColumnSettingComponent<T> implements OnInit /*, OnChanges*/ { 28 | _nzxColumns: NzxColumn<T>[] = []; 29 | _columnNameChecked!: boolean | null; 30 | _indeterminate!: boolean | null; 31 | 32 | /** 33 | * 拖拽预览样式 34 | */ 35 | @Input() dragPreviewClass = 'nzx-column-setting__drag-preview'; 36 | /** 37 | * 显示列名 38 | */ 39 | @Input() columnNameVisible: boolean | null = true; 40 | 41 | @Input() set nzxColumns(value: NzxColumn<T>[]) { 42 | if (value) { 43 | this._nzxColumns = value; 44 | this.refreshNameCheckedStatus(); 45 | } 46 | } 47 | get nzxColumns() { 48 | return this._nzxColumns; 49 | } 50 | 51 | /** 52 | * 当个列选中事件 53 | */ 54 | @Output() columnCheckedChange = new EventEmitter<NzxColumn<T>>(); 55 | /** 56 | * 排序列触发 57 | */ 58 | @Output() sortedColumn = new EventEmitter<CdkDragDrop<NzxColumn<T>, NzSafeAny>>(); 59 | /** 60 | * 列名选择Change 61 | */ 62 | @Output() columnNameCheckedChange = new EventEmitter<boolean>(); 63 | 64 | /** 65 | * 固定列 66 | */ 67 | @Output() fixedClick = new EventEmitter<NzxColumn<T>>(); 68 | 69 | constructor(protected renderer: Renderer2) {} 70 | 71 | ngOnInit(): void {} 72 | 73 | columnVisible(item: NzxColumn<T>, checked: boolean) { 74 | this.refreshNameCheckedStatus(); 75 | this.columnCheckedChange.emit(item); 76 | } 77 | 78 | /** 79 | * 固定列 80 | * @param column 81 | * @param fixed 82 | */ 83 | fixedColumn(column: NzxColumn<T>, fixed?: 'left' | 'right') { 84 | if (column.fixed === fixed) { 85 | return; 86 | } 87 | column.fixed = fixed; 88 | this.fixedClick.emit(column); 89 | } 90 | 91 | /** 92 | * 拖动列排序 93 | * @param event 94 | * @param list 排序数组 95 | */ 96 | dropColumn(event: CdkDragDrop<NzxColumn<T>, NzSafeAny>, list: NzxColumn<T>[]) { 97 | moveItemInArray(list, event.previousIndex, event.currentIndex); 98 | this.sortedColumn.emit(event); 99 | } 100 | 101 | /** 102 | * 开始拖动 103 | * @param event 104 | */ 105 | cdkDragStarted(event: CdkDragStart) { 106 | const preview = new ElementRef<HTMLElement>(document.querySelector('.cdk-drag.cdk-drag-preview')!); 107 | this.renderer.addClass(preview.nativeElement, this.dragPreviewClass); 108 | } 109 | 110 | /** 111 | * 结束拖动 112 | * @param event 113 | */ 114 | cdkDragReleased(event: CdkDragRelease) { 115 | const preview = new ElementRef<HTMLElement>(document.querySelector('.cdk-drag.cdk-drag-preview')!); 116 | this.renderer.removeClass(preview.nativeElement, this.dragPreviewClass); 117 | } 118 | 119 | /** 120 | * 展示列复选框 checked 121 | * @param checked 122 | */ 123 | columnNameChange(checked: boolean) { 124 | if (this._nzxColumns && this._nzxColumns.length) { 125 | this._columnNameChecked = checked; 126 | this._indeterminate = false; 127 | this._nzxColumns.filter(v => v.settingVisible != false && v.settingDisabled !== true).forEach(v => (v.visible = checked)); 128 | this.columnNameCheckedChange.emit(checked); 129 | } 130 | } 131 | 132 | /** 133 | * 更新展示列状态 134 | */ 135 | refreshNameCheckedStatus(): void { 136 | if (this._nzxColumns && this._nzxColumns.length) { 137 | const list = this._nzxColumns.filter(v => v.settingVisible != false && v.settingDisabled !== true); 138 | this._columnNameChecked = list.every(item => item.visible); 139 | this._indeterminate = !this._columnNameChecked && list.some(item => item.visible); 140 | } else { 141 | this._columnNameChecked = false; 142 | this._indeterminate = false; 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /lib/table/header/table-header/table-header.component.html: -------------------------------------------------------------------------------- 1 | <div class="nzx-table__header"> 2 | <div class="nzx-table__title"> 3 | <ng-content></ng-content> 4 | </div> 5 | 6 | <div class="nzx-table__toolbar"> 7 | <ng-content select="[setting-before]"></ng-content> 8 | <ng-container *ngIf="actionVisible !== false"> 9 | <i 10 | *ngIf="refreshVisible" 11 | (click)="refreshClick.emit()" 12 | nz-icon 13 | nzType="reload" 14 | nzTheme="outline" 15 | nz-tooltip 16 | nzTooltipTitle="刷新" 17 | tabindex="-1" 18 | ></i> 19 | <i 20 | *ngIf="resizeVisible" 21 | nzTrigger="click" 22 | nz-dropdown 23 | [nzDropdownMenu]="tableSizeMenu" 24 | nz-icon 25 | nzType="column-height" 26 | nzTheme="outline" 27 | nz-tooltip 28 | tabindex="-1" 29 | nzTooltipTitle="密度" 30 | ></i> 31 | 32 | <ng-content select="[setting]"></ng-content> 33 | </ng-container> 34 | 35 | <ng-content select="[toolbar]"></ng-content> 36 | </div> 37 | </div> 38 | 39 | <nz-dropdown-menu #tableSizeMenu> 40 | <ul nz-menu> 41 | <li 42 | nz-menu-item 43 | (click)="item.value !== tableSize && tableSizeChange.emit(item.value)" 44 | [nzSelected]="item.selected" 45 | *ngFor="let item of tableSizeOptions" 46 | > 47 | <span>{{ item.sizeName }}</span> 48 | </li> 49 | </ul> 50 | </nz-dropdown-menu> 51 | -------------------------------------------------------------------------------- /lib/table/header/table-header/table-header.component.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ChangeDetectionStrategy, 3 | Component, 4 | EventEmitter, 5 | Input, 6 | OnChanges, 7 | OnInit, 8 | Output, 9 | SimpleChange, 10 | SimpleChanges, 11 | ViewEncapsulation 12 | } from '@angular/core'; 13 | import { NzTableSize } from 'ng-zorro-antd/table'; 14 | import { NzxTableSize } from '../../table.type'; 15 | 16 | @Component({ 17 | selector: 'nzx-table-header', 18 | templateUrl: './table-header.component.html', 19 | styles: [':host{display: block}'], 20 | preserveWhitespaces: false, 21 | encapsulation: ViewEncapsulation.None, 22 | changeDetection: ChangeDetectionStrategy.OnPush 23 | }) 24 | export class NzxTableHeaderComponent implements OnInit, OnChanges { 25 | @Input() tableSize: NzxTableSize = 'small'; 26 | /** 27 | * 是否显示操作按钮小图标 28 | */ 29 | @Input() actionVisible?: boolean; 30 | @Input() refreshVisible?: boolean; 31 | @Input() resizeVisible?: boolean; 32 | 33 | @Output() tableSizeChange = new EventEmitter<NzTableSize>(); 34 | @Output() refreshClick = new EventEmitter<void>(); 35 | 36 | readonly tableSizeOptions = [ 37 | { sizeName: '大号', selected: false, value: 'default' }, 38 | { sizeName: '中等', selected: false, value: 'middle' }, 39 | { sizeName: '紧凑', selected: true, value: 'small' }, 40 | { sizeName: '迷你', selected: true, value: 'mini' } 41 | ]; 42 | 43 | constructor() {} 44 | 45 | ngOnInit(): void { 46 | this.tableSizeOptions.forEach(v => (v.selected = v.value === this.tableSize)); 47 | } 48 | 49 | ngOnChanges(changes: { [K in keyof this]?: SimpleChange } & SimpleChanges): void { 50 | if (changes.tableSize && !changes.tableSize.isFirstChange()) { 51 | this.ngOnInit(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/table/index.less: -------------------------------------------------------------------------------- 1 | @import 'table.component.less'; 2 | @import 'header/column-setting/column-setting.component.less'; 3 | -------------------------------------------------------------------------------- /lib/table/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Use of this source code is governed by an MIT-style license that can be 3 | * 4 | */ 5 | 6 | export * from './public-api'; 7 | -------------------------------------------------------------------------------- /lib/table/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib": { 3 | "entryFile": "public-api.ts" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/table/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './table.component'; 2 | export * from './table.type'; 3 | export * from './header/table-header/table-header.component'; 4 | export * from './header/column-setting/column-setting.component'; 5 | export * from './table-widget.service'; 6 | export * from './table.module'; 7 | -------------------------------------------------------------------------------- /lib/table/table-widget.directive.ts: -------------------------------------------------------------------------------- 1 | import { ComponentRef, Directive, Input, OnInit, Renderer2, ViewContainerRef } from '@angular/core'; 2 | import { IndexAttr, NzxColumn, NzxWidget } from './table.type'; 3 | import { TableWidgetService } from './table-widget.service'; 4 | import { NzxUtils } from '@xmagic/nzx-antd/util'; 5 | import { of } from 'rxjs'; 6 | 7 | @Directive({ 8 | selector: '[table-widget]' 9 | }) 10 | export class TableWidgetDirective<T> implements OnInit { 11 | @Input() row: T = {} as T; 12 | @Input() indexAttr!: IndexAttr; 13 | @Input() colIndex!: IndexAttr; 14 | @Input() column!: NzxColumn<T>; 15 | @Input() nzData: T[] = []; 16 | @Input() nzPageData: T[] = []; 17 | 18 | @Input() widget!: NzxWidget<T>; 19 | 20 | constructor( 21 | private widgetService: TableWidgetService, 22 | private viewContainerRef: ViewContainerRef, 23 | private renderer: Renderer2 24 | ) {} 25 | 26 | ngOnInit(): void { 27 | this.viewContainerRef.clear(); 28 | if (!this.widget) { 29 | return; 30 | } 31 | const componentType = this.widgetService.get(this.widget.type); 32 | if (!componentType) { 33 | console.warn(`组件类型“${this.widget.type}”未注册`); 34 | return; 35 | } 36 | 37 | const params = { 38 | row: this.row, 39 | nzData: this.nzData, 40 | nzPageData: this.nzPageData, 41 | column: this.column, 42 | indexAttr: this.indexAttr, 43 | colIndex: this.colIndex 44 | }; 45 | 46 | const componentRef = this.viewContainerRef.createComponent(componentType); 47 | const callbackParams = { 48 | instance: componentRef.instance, 49 | componentRef, 50 | ...params 51 | }; 52 | 53 | const result = NzxUtils.isFunction(this.widget.props) 54 | ? resolveType(this.widget.props(this.row, params)) 55 | : resolveType(this.widget.props); 56 | result.subscribe((props: Record<string, any>) => { 57 | console.log(props); 58 | const values = Object.assign(params, { props }, props); 59 | Object.assign(componentRef.instance, values); 60 | 61 | for (const key in values) { 62 | // eslint-disable-next-line 63 | // @ts-ignore 64 | const tNode = componentRef._tNode; 65 | // 检查属性是否有属于inputs, _tNode是私有属性,不应该直接访问,所以做个检查,对实际运行不影响 66 | if (tNode && tNode.inputs && tNode.inputs[key]) { 67 | componentRef.setInput(key, values[key]); 68 | } else { 69 | componentRef.instance[key] = values[key]; 70 | } 71 | } 72 | this.widget.onInit?.(callbackParams); 73 | }); 74 | 75 | this.mergeElementProperty(componentRef, params); 76 | if (this.widget.onDestroy) { 77 | componentRef.onDestroy(() => 78 | this.widget.onDestroy!({ 79 | ...callbackParams 80 | }) 81 | ); 82 | } 83 | } 84 | 85 | private mergeElementProperty( 86 | componentRef: ComponentRef<any>, 87 | params: { 88 | indexAttr: IndexAttr; 89 | column: NzxColumn<T>; 90 | colIndex: IndexAttr; 91 | nzData: T[]; 92 | row: T; 93 | nzPageData: T[]; 94 | } 95 | ) { 96 | const element = componentRef.location.nativeElement; 97 | if (this.widget.style) { 98 | Object.assign(element.style, this.widget.style); 99 | } 100 | if (this.widget.className) { 101 | element.className = this.widget.className; 102 | } 103 | if (this.widget.click) { 104 | this.renderer.listen(element, 'click', (event: MouseEvent) => this.widget.click!(this.row, params, event)); 105 | } 106 | } 107 | } 108 | 109 | function resolveType<T>(type: T) { 110 | if (!type) { 111 | return of({}); 112 | } 113 | if (NzxUtils.isObservable(type) || NzxUtils.isPromise(type)) { 114 | return type; 115 | } 116 | return of(type); 117 | } 118 | -------------------------------------------------------------------------------- /lib/table/table-widget.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, InjectionToken, Type } from '@angular/core'; 2 | 3 | export const TABLE_WIDGET = new InjectionToken<TableWidget[]>('TABLE_WIDGET'); 4 | 5 | export interface TableWidget { 6 | name: string; 7 | component: Type<any>; 8 | } 9 | 10 | export type TableWidgetMap = Record<string, Type<any>>; 11 | 12 | @Injectable({ 13 | providedIn: 'root' 14 | }) 15 | export class TableWidgetService { 16 | private widgetMap: TableWidgetMap = {}; 17 | 18 | get widgets(): TableWidgetMap { 19 | return this.widgetMap; 20 | } 21 | 22 | register(widgets?: TableWidget[]): void { 23 | if (!widgets?.length) { 24 | return; 25 | } 26 | 27 | for (const widget of widgets) { 28 | this.widgetMap[widget.name] = widget.component; 29 | } 30 | } 31 | 32 | has(type: string): boolean { 33 | return Object.hasOwn(this.widgetMap, type); 34 | } 35 | 36 | get(type: string): Type<any> { 37 | return this.widgetMap[type]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/table/table-widget/table-button/table-button.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { IndexAttr, NzxColumn } from '../../table.type'; 3 | 4 | @Component({ 5 | selector: 'nzx-table-button', 6 | template: ` 7 | <button 8 | type="button" 9 | nz-button 10 | [type]="props.type || 'button'" 11 | [nzBlock]="props.nzBlock" 12 | [nzDanger]="props.nzDanger" 13 | [nzGhost]="props.nzGhost" 14 | [nzSize]="props.nzSize || 'small'" 15 | [nzLoading]="props.nzLoading" 16 | [nzSearch]="props.nzSearch" 17 | [nzShape]="props.nzShape" 18 | [nzType]="props.nzType" 19 | [disabled]="props.disabled" 20 | [ngClass]="props.ngClass" 21 | [ngStyle]="props.ngStyle" 22 | (click)="props.click && props.click(row, { row, nzData, nzPageData, colIndex, column, indexAttr }, $event)" 23 | > 24 | <i *ngIf="props.icon" nz-icon [nzType]="props.icon"></i> 25 | {{ props.text }} 26 | </button> 27 | ` 28 | }) 29 | export class TableButtonComponent<T> { 30 | @Input() props: Record<string, any> = {}; 31 | @Input() row: T = {} as T; 32 | @Input() indexAttr!: IndexAttr; 33 | @Input() colIndex!: IndexAttr; 34 | @Input() column!: NzxColumn<T>; 35 | @Input() nzData: T[] = []; 36 | @Input() nzPageData: T[] = []; 37 | } 38 | -------------------------------------------------------------------------------- /lib/table/table-widget/table-input/table-input.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { IndexAttr, NzxColumn } from '../../table.type'; 3 | 4 | @Component({ 5 | selector: 'nzx-table-input', 6 | template: ` 7 | <input 8 | nz-input 9 | [(ngModel)]="props.text" 10 | (ngModelChange)="props.modelChange && props.modelChange($event, params)" 11 | autocomplete="off" 12 | [type]="props.type || 'text'" 13 | [ngStyle]="props.ngStyle" 14 | [ngClass]="props.ngClass" 15 | [nzBorderless]="props.nzBorderless" 16 | [nzStatus]="props.nzStatus" 17 | [nzSize]="props.nzSize" 18 | [disabled]="props.nzDisabled || props.disabled" 19 | (click)="props.onClick && props.onClick($event, params)" 20 | (focus)="props.onFocus && props.onFocus($event, params)" 21 | (blur)="props.onBlur && props.onBlur($event, params)" 22 | maxlength="" 23 | /> 24 | ` 25 | }) 26 | export class TableInputComponent<T> { 27 | @Input() props: Record<string, any> = {}; 28 | @Input() row: T = {} as T; 29 | @Input() indexAttr!: IndexAttr; 30 | @Input() colIndex!: IndexAttr; 31 | @Input() column!: NzxColumn<T>; 32 | @Input() nzData: T[] = []; 33 | @Input() nzPageData: T[] = []; 34 | 35 | get params() { 36 | return { 37 | row: this.row, 38 | nzData: this.nzData, 39 | nzPageData: this.nzPageData, 40 | colIndex: this.colIndex, 41 | column: this.column, 42 | indexAttr: this.indexAttr 43 | }; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/table/table-widget/table-link/table-link.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { IndexAttr, NzxColumn, NzxColumnButton } from '../../table.type'; 3 | 4 | @Component({ 5 | selector: 'nzx-table-link', 6 | template: ` 7 | <a 8 | [attr.href]="props | linkHref: row : params" 9 | [attr.target]="props.target" 10 | [ngClass]="props.ngClass" 11 | [ngStyle]="props.ngStyle" 12 | (click)="props.click && !props.disabled && props.click(row, params, $event)" 13 | > 14 | {{ props.text }} 15 | </a> 16 | ` 17 | }) 18 | export class TableLinkComponent<T> { 19 | @Input() props!: NzxColumnButton; 20 | @Input() nzData: T[] = []; 21 | @Input() nzPageData: T[] = []; 22 | @Input() row: T = {} as T; 23 | @Input() indexAttr!: IndexAttr; 24 | @Input() colIndex!: IndexAttr; 25 | @Input() column!: NzxColumn<T>; 26 | 27 | get params() { 28 | return { 29 | row: this.row, 30 | nzData: this.nzData, 31 | nzPageData: this.nzPageData, 32 | colIndex: this.colIndex, 33 | column: this.column, 34 | indexAttr: this.indexAttr 35 | }; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/table/table-widget/table-tag/table-tag.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { IndexAttr, NzxColumn } from '../../table.type'; 3 | 4 | @Component({ 5 | selector: 'nzx-table-tag', 6 | template: ` 7 | <nz-tag 8 | #instance 9 | [nzColor]="props.nzColor" 10 | [nzChecked]="props.nzChecked" 11 | [nzMode]="props.nzMode" 12 | (nzOnClose)="props.nzOnClose?.($event, row, params)" 13 | (nzCheckedChange)="props.nzCheckedChange?.($event, row, params)" 14 | (click)="props.click && props.click(row, params, $event)" 15 | [ngClass]="props.ngClass" 16 | [ngStyle]="props.ngStyle" 17 | > 18 | {{ props.text }} 19 | </nz-tag> 20 | ` 21 | }) 22 | export class TableTagComponent<T> { 23 | @Input() props: Record<string, any> = {}; 24 | @Input() row: T = {} as T; 25 | @Input() indexAttr!: IndexAttr; 26 | @Input() colIndex!: IndexAttr; 27 | @Input() column!: NzxColumn<T>; 28 | @Input() nzData: T[] = []; 29 | @Input() nzPageData: T[] = []; 30 | 31 | get params() { 32 | return { 33 | row: this.row, 34 | nzData: this.nzData, 35 | nzPageData: this.nzPageData, 36 | colIndex: this.colIndex, 37 | column: this.column, 38 | indexAttr: this.indexAttr 39 | }; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/table/table-widget/table-widget.module.ts: -------------------------------------------------------------------------------- 1 | import { Inject, NgModule, Optional } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { TableInputComponent } from './table-input/table-input.component'; 4 | import { FormsModule } from '@angular/forms'; 5 | import { NzInputModule } from 'ng-zorro-antd/input'; 6 | import { TableButtonComponent } from './table-button/table-button.component'; 7 | import { NzButtonModule } from 'ng-zorro-antd/button'; 8 | import { NzIconModule } from 'ng-zorro-antd/icon'; 9 | import { TableLinkComponent } from './table-link/table-link.component'; 10 | import { LinkHrefPipe } from '../transform/link-href.pipe'; 11 | import { TableTagComponent } from './table-tag/table-tag.component'; 12 | import { NzTagModule } from 'ng-zorro-antd/tag'; 13 | import { NzxSwitchComponent, NzxSwitchModule } from '@xmagic/nzx-antd/switch'; 14 | import { NzInputNumberComponent, NzInputNumberModule } from 'ng-zorro-antd/input-number'; 15 | import { TableWidgetService, TABLE_WIDGET, TableWidget } from '../table-widget.service'; 16 | 17 | export function defaultTableWidget(): TableWidget[] { 18 | return [ 19 | { name: 'switch', component: NzxSwitchComponent }, 20 | { name: 'input', component: TableInputComponent }, 21 | { name: 'number', component: NzInputNumberComponent }, 22 | { name: 'tag', component: TableTagComponent }, 23 | { name: 'button', component: TableButtonComponent }, 24 | { name: 'link', component: TableLinkComponent } 25 | ]; 26 | } 27 | 28 | @NgModule({ 29 | declarations: [TableInputComponent, TableButtonComponent, TableLinkComponent, TableTagComponent, LinkHrefPipe], 30 | imports: [ 31 | CommonModule, 32 | FormsModule, 33 | NzInputModule, 34 | NzButtonModule, 35 | NzIconModule, 36 | NzTagModule, 37 | NzInputNumberModule, 38 | NzxSwitchModule 39 | ], 40 | providers: [{ provide: TABLE_WIDGET, useFactory: defaultTableWidget, multi: true }], 41 | exports: [TableInputComponent, TableButtonComponent, TableLinkComponent, TableTagComponent] 42 | }) 43 | export class TableWidgetModule { 44 | constructor( 45 | public service: TableWidgetService, 46 | @Optional() @Inject(TABLE_WIDGET) widgets: TableWidget[][] = [] 47 | ) { 48 | if (!widgets) { 49 | return; 50 | } 51 | widgets.forEach(c => service.register(c)); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/table/table.component.less: -------------------------------------------------------------------------------- 1 | @import '../base'; 2 | @table-padding-vertical-mini: 4px; 3 | @table-padding-horizontal-mini: 4px; 4 | @table-font-size-mini: 14px; 5 | @table-striped-bgcolor: @table-row-hover-bg; 6 | 7 | .table-size(~'mini', @table-padding-vertical-mini, @table-padding-horizontal-mini, @table-font-size-mini); 8 | 9 | .@{table-prefix-cls}-mini { 10 | .@{table-prefix-cls}-thead > tr > th { 11 | background-color: @table-header-bg-sm; 12 | } 13 | .@{table-prefix-cls}-selection-column { 14 | width: 46px; 15 | min-width: 46px; 16 | } 17 | } 18 | 19 | @nzx-table-prefix-cls: ~'@{nzx-prefix}-table'; 20 | 21 | .@{nzx-table-prefix-cls} { 22 | &-striped-tbody > tr:nth-of-type(even):not(.ant-table-placeholder) > td { 23 | background: @table-striped-bgcolor; 24 | } 25 | 26 | &-fit { 27 | height: 100%; 28 | position: relative; 29 | display: flex; 30 | flex-direction: column; 31 | 32 | .nzx-inner-table{ 33 | width: 100%; 34 | flex: 1; 35 | display: flex; 36 | 37 | .ant-spin-nested-loading{ 38 | width: 100%; 39 | } 40 | .ant-spin-container{ 41 | display: flex; 42 | flex-direction: column; 43 | height: 100%; 44 | 45 | >.ant-table { 46 | width: 100%; 47 | flex: 1; 48 | position: relative; 49 | 50 | .ant-table-container{ 51 | top: 0; 52 | left: 0; 53 | right: 0; 54 | bottom: 0; 55 | } 56 | } 57 | } 58 | } 59 | } 60 | 61 | .ant-table-column-title { 62 | position: initial; 63 | } 64 | .nz-resizable-handle-right { 65 | width: 8px; 66 | right: 0; 67 | } 68 | 69 | .ant-table:not(.ant-table-bordered) .ant-table-thead > tr > th:not(:last-child) .nz-resizable-handle-right:after { 70 | content: ' '; 71 | position: absolute; 72 | width: 1px; 73 | background: #e8e8e8; 74 | right: 0; 75 | height: 18px; 76 | top: 50%; 77 | margin-top: -9px; 78 | } 79 | 80 | .ant-table.ant-table-bordered .ant-table-thead > tr > th:not(:last-child) .nz-resizable-handle-right:after { 81 | content: ' '; 82 | position: absolute; 83 | width: 1px; 84 | right: 0; 85 | height: 18px; 86 | top: 50%; 87 | margin-top: -9px; 88 | } 89 | 90 | &__header { 91 | display: flex; 92 | align-items: center; 93 | padding-bottom: 8px; 94 | } 95 | &__title { 96 | display: flex; 97 | flex: 1; 98 | align-items: center; 99 | > * { 100 | margin-right: 8px; 101 | } 102 | } 103 | 104 | &__toolbar { 105 | display: flex; 106 | align-items: center; 107 | justify-content: flex-end; 108 | 109 | > * { 110 | margin-right: 8px; 111 | } 112 | .anticon[tabindex] { 113 | cursor: pointer; 114 | font-size: 18px; 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /lib/table/table.module.ts: -------------------------------------------------------------------------------- 1 | import { Inject, ModuleWithProviders, NgModule, Optional } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NzTableModule } from 'ng-zorro-antd/table'; 4 | import { NzxTableComponent } from './table.component'; 5 | import { NzxTableHeaderComponent } from './header/table-header/table-header.component'; 6 | import { NzxColumnSettingComponent } from './header/column-setting/column-setting.component'; 7 | import { NzResizableModule } from 'ng-zorro-antd/resizable'; 8 | import { NzIconModule } from 'ng-zorro-antd/icon'; 9 | import { NzPopoverModule } from 'ng-zorro-antd/popover'; 10 | import { NzCheckboxModule } from 'ng-zorro-antd/checkbox'; 11 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 12 | import { DragDropModule } from '@angular/cdk/drag-drop'; 13 | import { NzDividerModule } from 'ng-zorro-antd/divider'; 14 | import { NzDropDownModule } from 'ng-zorro-antd/dropdown'; 15 | import { NzToolTipModule } from 'ng-zorro-antd/tooltip'; 16 | import { NzxPipeModule } from '@xmagic/nzx-antd/pipe'; 17 | import { NzxServiceModule } from '@xmagic/nzx-antd/service'; 18 | import { NzOutletModule } from 'ng-zorro-antd/core/outlet'; 19 | import { NzButtonModule } from 'ng-zorro-antd/button'; 20 | import { ColFormatPipe } from './transform/col-format.pipe'; 21 | import { NzxDirectiveModule } from '@xmagic/nzx-antd/directive'; 22 | import { ColSpanPipe } from './transform/col-span.pipe'; 23 | import { ColButtonVisiblePipe } from './transform/col-button-visible.pipe'; 24 | import { HasAuthPipe } from './transform/has-auth.pipe'; 25 | import { ColButtonsPipe } from './transform/col-buttons.pipe'; 26 | import { TableWidgetService, TABLE_WIDGET, TableWidget } from './table-widget.service'; 27 | 28 | import { TableWidgetModule } from './table-widget/table-widget.module'; 29 | import { TableWidgetDirective } from './table-widget.directive'; 30 | 31 | const COMPONENT = [NzxTableComponent, NzxTableHeaderComponent, NzxColumnSettingComponent]; 32 | @NgModule({ 33 | declarations: [ 34 | COMPONENT, 35 | ColFormatPipe, 36 | ColSpanPipe, 37 | ColButtonVisiblePipe, 38 | HasAuthPipe, 39 | TableWidgetDirective, 40 | ColButtonsPipe 41 | ], 42 | imports: [ 43 | CommonModule, 44 | FormsModule, 45 | ReactiveFormsModule, 46 | NzTableModule, 47 | NzResizableModule, 48 | NzIconModule, 49 | NzPopoverModule, 50 | NzCheckboxModule, 51 | DragDropModule, 52 | NzDividerModule, 53 | NzDropDownModule, 54 | NzToolTipModule, 55 | NzxPipeModule, 56 | NzxServiceModule, 57 | NzOutletModule, 58 | NzButtonModule, 59 | NzxDirectiveModule, 60 | TableWidgetModule 61 | ], 62 | exports: [COMPONENT] 63 | }) 64 | export class NzxTableModule { 65 | constructor( 66 | public service: TableWidgetService, 67 | @Optional() @Inject(TABLE_WIDGET) widgets: TableWidget[][] = [] 68 | ) { 69 | if (!widgets) { 70 | return; 71 | } 72 | widgets.forEach(c => service.register(c)); 73 | } 74 | 75 | static forChild(config: TableWidget[] = []): ModuleWithProviders<NzxTableModule> { 76 | return { 77 | ngModule: NzxTableModule, 78 | providers: [{ provide: TABLE_WIDGET, useValue: config, multi: true }] 79 | }; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/table/transform/col-button-visible.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { CellArgType, NzxColumnButton } from '../table.type'; 3 | import { NzxUtils } from '@xmagic/nzx-antd/util'; 4 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 5 | 6 | @Pipe({ 7 | name: 'colButtonVisible' 8 | }) 9 | export class ColButtonVisiblePipe implements PipeTransform { 10 | transform( 11 | value?: NzxColumnButton['visible'] 12 | ): (row: NzSafeAny, params: CellArgType<NzSafeAny>) => boolean | undefined | null | void { 13 | if (NzxUtils.isFunction(value)) { 14 | return value; 15 | } 16 | return () => value !== false; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/table/transform/col-buttons.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { CellArgType } from '../table.type'; 3 | import { of } from 'rxjs'; 4 | import { NzxUtils } from '@xmagic/nzx-antd/util'; 5 | 6 | @Pipe({ 7 | name: 'colButtons' 8 | }) 9 | export class ColButtonsPipe<T> implements PipeTransform { 10 | transform( 11 | buttons: T[] | ((row: T, params: CellArgType<T> & { parentRow?: T }) => T[]), 12 | row: T, 13 | params: CellArgType<T>, 14 | parentRow?: T 15 | ) { 16 | if (!buttons) { 17 | return of([]); 18 | } 19 | 20 | if (NzxUtils.isFunction(buttons)) { 21 | return resolveButton(buttons(row, { ...params, parentRow })); 22 | } 23 | return resolveButton(buttons); 24 | } 25 | } 26 | 27 | function resolveButton<T>(buttons: T[]) { 28 | if (!buttons) { 29 | return of([]); 30 | } 31 | 32 | if (NzxUtils.isArray(buttons)) { 33 | return of(buttons); 34 | } 35 | 36 | if (NzxUtils.isObservable(buttons) || NzxUtils.isPromise(buttons)) { 37 | return buttons; 38 | } 39 | return of([]); 40 | } 41 | -------------------------------------------------------------------------------- /lib/table/transform/col-format.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 3 | import { CellArgType, NzxColumn } from '../table.type'; 4 | import { Observable } from 'rxjs'; 5 | import { NzxUtils } from '@xmagic/nzx-antd/util'; 6 | 7 | @Pipe({ 8 | name: 'colFormat' 9 | }) 10 | export class ColFormatPipe<T extends Record<string, NzSafeAny>> implements PipeTransform { 11 | transform(row: T, col: NzxColumn<T>, params: CellArgType<T>): Observable<T> | Promise<T> { 12 | const nameData = col.name ? NzxUtils.get(row, col.name as string) : null; 13 | if (!col.format) { 14 | return Promise.resolve(nameData); 15 | } 16 | const result = col.format(nameData, row, params); 17 | if (NzxUtils.isPromise(result) || NzxUtils.isObservable(result)) { 18 | return result; 19 | } 20 | return Promise.resolve(result); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/table/transform/col-span.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { IndexAttr, NzxColumn, SpanFunc } from '../table.type'; 3 | 4 | @Pipe({ 5 | name: 'colSpan', 6 | pure: true 7 | }) 8 | export class ColSpanPipe implements PipeTransform { 9 | transform<T>( 10 | row: T, 11 | spanFunc: SpanFunc<T>, 12 | nzData: T[], 13 | nzPageData: T[], 14 | column: NzxColumn<T>, 15 | indexAttr: IndexAttr, 16 | colIndex: IndexAttr 17 | ): unknown { 18 | let rowspan: number | null | void = 1; 19 | let colspan: number | null | void = 1; 20 | 21 | if (spanFunc) { 22 | const result = spanFunc(row, { 23 | row, 24 | nzData, 25 | nzPageData, 26 | column, 27 | indexAttr, 28 | colIndex 29 | }); 30 | if (Array.isArray(result)) { 31 | rowspan = result[0]; 32 | colspan = result[1]; 33 | } else if (result != null) { 34 | rowspan = result.rowspan; 35 | colspan = result.colspan; 36 | } 37 | } 38 | 39 | return { rowspan, colspan }; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/table/transform/has-auth.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 3 | import { NzxAntdService } from '@xmagic/nzx-antd'; 4 | import { Observable, of } from 'rxjs'; 5 | 6 | @Pipe({ 7 | name: 'hasAuth' 8 | }) 9 | export class HasAuthPipe implements PipeTransform { 10 | protected hasAuth: Required<NzxAntdService>['hasAuth'] = () => of(true); 11 | constructor(public antdService: NzxAntdService) { 12 | if (this.antdService.hasAuth) { 13 | this.hasAuth = this.antdService.hasAuth; 14 | } 15 | } 16 | 17 | transform(value: NzSafeAny): Observable<boolean> { 18 | return this.hasAuth(value); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/table/transform/link-href.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import { CellArgType, NzxColumnButton } from '../table.type'; 3 | import { NzSafeAny } from 'ng-zorro-antd/core/types'; 4 | import { NzxUtils } from '@xmagic/nzx-antd/util'; 5 | 6 | /** 7 | * 处理链接的href属性 8 | */ 9 | @Pipe({ 10 | name: 'linkHref' 11 | }) 12 | export class LinkHrefPipe implements PipeTransform { 13 | transform(btn: NzxColumnButton, row: NzSafeAny, params: CellArgType<any>): unknown { 14 | if (!btn.href) { 15 | return undefined; 16 | } 17 | if (typeof btn.href === 'string') { 18 | return btn.href; 19 | } 20 | 21 | if (NzxUtils.isFunction(btn.href)) { 22 | return btn.href(row, params); 23 | } 24 | 25 | if (row?.buttons && params.column.name) { 26 | return row.buttons[params.column.name]?.href; 27 | } 28 | return null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js'; 4 | import 'zone.js/testing'; 5 | import { getTestBed } from '@angular/core/testing'; 6 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; 7 | 8 | new Date().toLocaleString('', { dateStyle: 'medium' }); 9 | 10 | // First, initialize the Angular testing environment. 11 | getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), { 12 | teardown: { destroyAfterEach: true } 13 | }); 14 | -------------------------------------------------------------------------------- /lib/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../out-tsc/lib", 6 | "declaration": true, 7 | "declarationMap": true, 8 | "inlineSources": true, 9 | "types": [] 10 | }, 11 | "exclude": [ 12 | "src/test.ts", 13 | "**/*.spec.ts", 14 | "**/*.stories.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /lib/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.lib.json", 4 | "compilerOptions": { 5 | "declarationMap": false 6 | }, 7 | "angularCompilerOptions": { 8 | "compilationMode": "partial" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /lib/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../out-tsc/spec", 6 | "types": ["jasmine"] 7 | }, 8 | "files": [ 9 | "test.ts" 10 | ], 11 | "include": ["**/*.spec.ts", "**/*.d.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /lib/upload/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Use of this source code is governed by an MIT-style license that can be 3 | * 4 | */ 5 | 6 | export * from './public-api'; 7 | -------------------------------------------------------------------------------- /lib/upload/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib": { 3 | "entryFile": "public-api.ts" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/upload/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './upload.component'; 2 | export * from './upload.module'; 3 | -------------------------------------------------------------------------------- /lib/upload/upload.component.html: -------------------------------------------------------------------------------- 1 | <nz-upload 2 | [nzType]="nzType" 3 | [nzAccept]="nzAccept" 4 | [nzAction]="nzAction" 5 | [nzDirectory]="nzDirectory" 6 | [nzOpenFileDialogOnClick]="nzOpenFileDialogOnClick" 7 | [nzBeforeUpload]="_nzBeforeUpload" 8 | [nzCustomRequest]="nzCustomRequest" 9 | [nzData]="nzData" 10 | [nzFilter]="nzFilter" 11 | [(nzFileList)]="nzFileList" 12 | [nzDisabled]="nzDisabled" 13 | [nzHeaders]="nzHeaders" 14 | [nzListType]="nzListType" 15 | [nzMultiple]="nzMultiple" 16 | [nzName]="nzName" 17 | [nzShowUploadList]="nzShowUploadList" 18 | [nzShowButton]="nzShowButton" 19 | [nzWithCredentials]="nzWithCredentials" 20 | [nzRemove]="nzRemove" 21 | [nzPreview]="nzPreview" 22 | [nzPreviewFile]="nzPreviewFile" 23 | [nzPreviewIsImage]="nzPreviewIsImage" 24 | [nzTransformFile]="nzTransformFile" 25 | [nzDownload]="nzDownload" 26 | [nzIconRender]="nzIconRender" 27 | [nzFileListRender]="nzFileListRender" 28 | (nzChange)="onNzChange($event)" 29 | (nzFileListChange)="nzFileListChange.emit($event)" 30 | > 31 | <ng-container *nzStringTemplateOutlet="nzxUploadButton; context: { $implicit: this, nzFileList: nzFileList }"> 32 | <button 33 | nz-button 34 | [disabled]="nzDisabled" 35 | type="button" 36 | *ngIf="nzType === 'select' && nzxShowUploadButtonIcon !== false" 37 | > 38 | <i *ngIf="nzxUploadButtonIcon" nz-icon [nzType]="nzxUploadButtonIcon"></i> 39 | {{ nzxUploadButton || '上传' }} 40 | </button> 41 | </ng-container> 42 | 43 | <ng-container *ngIf="nzxShowUploadButtonIcon !== false && nzType === 'drag'"> 44 | <p class="ant-upload-drag-icon"> 45 | <i *ngIf="nzxUploadButtonIcon" nz-icon [nzType]="nzxUploadButtonIcon"></i> 46 | </p> 47 | <p class="ant-upload-text">{{nzxUploadText}}</p> 48 | <p class="ant-upload-hint">{{ nzxHint }}</p> 49 | </ng-container> 50 | </nz-upload> 51 | -------------------------------------------------------------------------------- /lib/upload/upload.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { NzxUploadComponent } from './upload.component'; 4 | import { NzUploadModule } from 'ng-zorro-antd/upload'; 5 | import { HttpClientModule } from '@angular/common/http'; 6 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 7 | import { NzButtonModule } from 'ng-zorro-antd/button'; 8 | import { NzIconModule } from 'ng-zorro-antd/icon'; 9 | import { NzMessageModule } from 'ng-zorro-antd/message'; 10 | import { NzOutletModule } from 'ng-zorro-antd/core/outlet'; 11 | 12 | const COMPONENT = [NzxUploadComponent]; 13 | 14 | @NgModule({ 15 | declarations: [COMPONENT], 16 | imports: [ 17 | CommonModule, 18 | HttpClientModule, 19 | FormsModule, 20 | ReactiveFormsModule, 21 | NzUploadModule, 22 | NzMessageModule, 23 | NzButtonModule, 24 | NzIconModule, 25 | NzOutletModule 26 | ], 27 | exports: [COMPONENT] 28 | }) 29 | export class NzxUploadModule {} 30 | -------------------------------------------------------------------------------- /lib/util/base-control.ts: -------------------------------------------------------------------------------- 1 | export abstract class BaseControl<T> { 2 | protected nzxDisabled?: boolean; 3 | onChange: (value: T) => void = () => null; 4 | onTouched: () => void = () => null; 5 | 6 | registerOnChange(fn: (_: T) => void): void { 7 | this.onChange = fn; 8 | } 9 | 10 | registerOnTouched(fn: () => void): void { 11 | this.onTouched = fn; 12 | } 13 | 14 | setDisabledState?(isDisabled: boolean): void { 15 | this.nzxDisabled = isDisabled; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/util/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Use of this source code is governed by an MIT-style license that can be 3 | * 4 | */ 5 | 6 | export * from './public-api'; 7 | -------------------------------------------------------------------------------- /lib/util/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "lib": { 3 | "entryFile": "public-api.ts" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/util/public-api.ts: -------------------------------------------------------------------------------- 1 | export * from './utils'; 2 | export * from './utils-fn'; 3 | export * from './form-utils'; 4 | export * from './base-control'; 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nzx-antd", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "watch": "ng build --watch --configuration development", 9 | "test": "ng test", 10 | "build:nzx-antd": "ng build nzx-antd && node version path=./dist/nzx-antd/version", 11 | "publish:nzx-antd": "cd dist/nzx-antd && npm publish --access public", 12 | "docs:json": "compodoc -p ./tsconfig.json -e json -d .", 13 | "storybook": "ng run storybook:storybook", 14 | "build-storybook": "ng run storybook:build-storybook" 15 | }, 16 | "private": true, 17 | "dependencies": { 18 | "@angular/animations": "^17.0.2", 19 | "@angular/common": "^17.0.2", 20 | "@angular/compiler": "^17.0.2", 21 | "@angular/core": "^17.0.2", 22 | "@angular/forms": "^17.0.2", 23 | "@angular/platform-browser": "^17.0.2", 24 | "@angular/platform-browser-dynamic": "^17.0.2", 25 | "@angular/router": "^17.0.2", 26 | "rxjs": "~7.8.0", 27 | "tslib": "^2.3.0", 28 | "zone.js": "~0.14.2", 29 | "ng-zorro-antd": "^17.0.0" 30 | }, 31 | "devDependencies": { 32 | "@angular-devkit/build-angular": "^17.0.0", 33 | "@angular/cli": "^17.0.0", 34 | "@angular/compiler-cli": "^17.0.2", 35 | "@babel/core": "^7.21.8", 36 | "@compodoc/compodoc": "^1.1.19", 37 | "ng-packagr": "^17.0.0", 38 | "@angular-eslint/builder": "^17.0.0", 39 | "@angular-eslint/eslint-plugin": "^17.0.0", 40 | "@angular-eslint/eslint-plugin-template": "^17.0.0", 41 | "@angular-eslint/schematics": "^17.0.0", 42 | "@angular-eslint/template-parser": "^17.0.0", 43 | "@commitlint/cli": "^17.6.3", 44 | "@commitlint/config-conventional": "^17.6.3", 45 | "@storybook/addon-actions": "^7.0.9", 46 | "@storybook/addon-essentials": "^7.0.9", 47 | "@storybook/addon-interactions": "^7.0.9", 48 | "@storybook/addon-links": "^7.0.9", 49 | "@storybook/angular": "^7.0.9", 50 | "@storybook/builder-webpack5": "^7.0.9", 51 | "@storybook/manager-webpack5": "^6.5.16", 52 | "@storybook/testing-library": "0.1.0", 53 | "@types/jasmine": "~4.3.0", 54 | "@types/node": "18.16.9", 55 | "@types/prettier": "^3.0.0", 56 | "@typescript-eslint/eslint-plugin": "5.59.2", 57 | "@typescript-eslint/eslint-plugin-tslint": "5.59.2", 58 | "@typescript-eslint/parser": "5.59.2", 59 | "eslint": "8.43.0", 60 | "eslint-config-prettier": "^8.8.0", 61 | "eslint-plugin-import": "2.27.5", 62 | "eslint-plugin-jsdoc": "46.4.2", 63 | "eslint-plugin-prefer-arrow": "1.2.3", 64 | "eslint-plugin-storybook": "^0.6.12", 65 | "jasmine-core": "~4.6.0", 66 | "karma": "~6.4.0", 67 | "karma-chrome-launcher": "~3.2.0", 68 | "karma-coverage": "~2.2.0", 69 | "karma-jasmine": "~5.1.0", 70 | "karma-jasmine-html-reporter": "~2.0.0", 71 | "prettier": "^3.0.0", 72 | "react-textarea-autosize": "8.5.2", 73 | "typescript": "~5.2.2" 74 | }, 75 | "engines": { 76 | "node": ">= 16.14.0 || >= 18.10.0" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /stories/@types/index.ts: -------------------------------------------------------------------------------- 1 | declare module '*.mdx' { 2 | const mdxMeta: { 3 | parameters: { 4 | docs: { 5 | page: () => any; 6 | }; 7 | }; 8 | [key: string]: any; 9 | }; 10 | export default mdxMeta; 11 | } 12 | -------------------------------------------------------------------------------- /stories/Introduction.stories.mdx: -------------------------------------------------------------------------------- 1 | import { Meta } from '@storybook/addon-docs'; 2 | 3 | <Meta title="介绍" /> 4 | 5 | # NzxAntd 6 | 7 | `NzxAntd`是一个`angular`组件库,基于`ng-zorro-antd`进行二次扩展,并加入开发常用功能。全部代码开源并遵循 `MIT` 协议,任何企业、组织及个人均可免费使用。 8 | 9 | [![npm version](https://img.shields.io/npm/v/ngx-fluent-form/latest.svg)](https://npmjs.com/package/@xmagic/nzx-antd) 10 | ![License](https://img.shields.io/badge/License-MIT-blue.svg) 11 | [![Angular](https://img.shields.io/badge/Build%20with-Angular%20CLI-red?logo=angular)](https://www.github.com/angular/angular) 12 | [![Storybook](https://cdn.jsdelivr.net/gh/storybookjs/brand@main/badge/badge-storybook.svg)](https://m310851010.github.io/nzx-antd) 13 | 14 | ## ✨特性 15 | 16 | - 扩展`HttpInterceptor`拦截器,简化通用业务处理 17 | - 封装常用组件 使之支持`FormControl`和`NgModal` 18 | - 封装表格组件, 简单易用, 功能强大 19 | - 常用工具类, 服务, 指令,管道 20 | - 集中化配置,统一配置入口 21 | 22 | ## 🖥使用环境 23 | 24 | - [Angular](https://angular.io) >= v13.0.0 25 | - [ng-zorro-antd](https://ng.ant.design) >= v13.0.0 26 | 27 | ## 📦安装 28 | 29 | ```shell 30 | npm i @xmagic/nzx-antd --save 31 | ``` 32 | 33 | ## 🔨使用 34 | 35 | 36 | ## 🍏引入样式 37 | 38 | > 有两种方式引入样式, 在 `angular.json` 中 或者 `style.less`中, 任选其一 39 | 40 | - 在 `angular.json` 中引入 41 | 42 | ```json 43 | { 44 | "styles": [ 45 | "node_modules/@xmagic/nzx-antd/nzx-antd.less" 46 | ] 47 | } 48 | ``` 49 | 50 | - 在 `style.less` 中引入 `less` 样式文件 51 | 52 | ```css 53 | @import "~@xmagic/nzx-antd/nzx-antd.less"; 54 | ``` 55 | 56 | ## 🍎引入模块 57 | 58 | 1. 配置`NzxAntdService` 59 | 60 | ```ts 61 | // nzx-antd-config.service.ts 62 | 63 | import { Injectable } from '@angular/core'; 64 | import { NzxAntdService } from '@xmagic/nzx-antd'; 65 | import { environment } from '../environments/environment'; 66 | 67 | @Injectable({ 68 | providedIn: 'root' 69 | }) 70 | export class NzxAntdConfigService extends NzxAntdService { 71 | override basePath = environment.basePath; 72 | override response = { data: 'data' }; 73 | constructor() { 74 | super(); 75 | } 76 | } 77 | 78 | ``` 79 | 80 | 2. 修改`AppModule` 81 | 82 | ```ts 83 | // app.module.ts 84 | 85 | import { NgModule } from '@angular/core'; 86 | import { AppComponent } from './app.component'; 87 | import { NzxModalModule } from '@xmagic/nzx-antd/modal'; 88 | import { NzxHttpInterceptorModule } from '@xmagic/nzx-antd/http-interceptor'; 89 | import { NzxAntdService } from '@xmagic/nzx-antd'; 90 | import { NzxAntdConfigService } from './nzx-antd-config.service'; 91 | 92 | @NgModule({ 93 | imports: [ 94 | NzxModalModule, 95 | NzxHttpInterceptorModule 96 | ], 97 | providers: [ 98 | { provide: NzxAntdService, useExisting: NzxAntdConfigService } 99 | ], 100 | bootstrap: [AppComponent] 101 | }) 102 | export class AppComponent {} 103 | ``` 104 | 105 | 3. 修改`AppComponent` 106 | 107 | ```ts 108 | //app.component.ts 109 | 110 | import { Component, OnInit } from '@angular/core'; 111 | import { HttpLoadingService, LogoutService } from '@xmagic/nzx-antd/http-interceptor'; 112 | import { NzMessageService } from 'ng-zorro-antd/message'; 113 | import { NzxModalWrapService } from '@xmagic/nzx-antd/modal'; 114 | import { loadingService } from '@xmagic/nzx-antd/service'; 115 | 116 | @Component({ 117 | selector: 'app-root', 118 | template: '<router-outlet></router-outlet>', 119 | }) 120 | export class AppComponent implements OnInit { 121 | constructor( 122 | protected loading: HttpLoadingService, 123 | protected notifyService: LogoutService, 124 | protected modalService: NzxModalWrapService, 125 | protected message: NzMessageService, 126 | ) {} 127 | 128 | ngOnInit(): void { 129 | this.loading.subscribe(status => loadingService.loading(status)); 130 | 131 | this.notifyService.onLogout(error => { 132 | this.modalService.closeAll(); 133 | if (error.timeout) { 134 | this.message.info(error.message || '登录超时,请重新登录'); 135 | } 136 | window.top!.location.href = error?.url || '#/login'; 137 | }); 138 | } 139 | } 140 | ``` 141 | 142 | ## 🏴授权协议 143 | 144 | [MIT](https://raw.githubusercontent.com/m310851010/nzx-antd/main/LICENSE) 145 | 146 | ## 👍支持 147 | 148 | <div className="doc-tip-wrapper"> 149 | <span className="doc-tip">提示</span> 为该项目点个免费的星⭐ 150 | </div> 151 | -------------------------------------------------------------------------------- /stories/index.ts: -------------------------------------------------------------------------------- 1 | import { InputType } from '@storybook/csf'; 2 | import { Story } from '@storybook/angular'; 3 | // 控件类型 4 | // https://storybook.js.org/docs/angular/essentials/controls 5 | export const EXCLUDE_PARAMS = [ 6 | 'setDisabledState', 7 | 'registerOnChange', 8 | 'registerOnTouched', 9 | 'onChange', 10 | 'onTouched', 11 | 'writeValue', 12 | 'ngModelChange' 13 | ]; 14 | 15 | export const SIZE_ARG_TYPE = { 16 | control: 'inline-radio', 17 | options: ['large', 'default', 'small'], 18 | defaultValue: 'default' 19 | }; 20 | 21 | export const HIDE_CONTROL = { 22 | table: { defaultValue: { summary: null } }, 23 | control: false 24 | }; 25 | 26 | /** 27 | * storybook 模板工程 28 | * @param props 传递的参数 29 | * @param template 模板 30 | */ 31 | export function storyFactory<T>(props?: Partial<T>, template?: string): Story<T> { 32 | // @ts-ignore 33 | const fn: Story<T> = args => { 34 | return { 35 | props: args, 36 | template 37 | }; 38 | }; 39 | if (props) { 40 | fn.args = props; 41 | } 42 | return fn; 43 | } 44 | 45 | /** 46 | * 隐藏指定属性的control, 属性不隐藏 47 | * @param props 属性名称列表 48 | */ 49 | export function hideControlArgType<T>(...props: (keyof T)[]): Record<string, InputType> { 50 | return props.reduce((prev, curr) => { 51 | prev[curr] = { ...HIDE_CONTROL }; 52 | return prev; 53 | }, {} as Record<keyof T, InputType>); 54 | } 55 | 56 | /** 57 | * 隐藏属性 58 | */ 59 | export const HIDE_CONTROL_COMMONS = hideControlArgType('nzxValue'); 60 | -------------------------------------------------------------------------------- /stories/styles.less: -------------------------------------------------------------------------------- 1 | @import '../lib/nzx-antd.less'; 2 | @ant-btn-primary: #451; 3 | 4 | .doc-tip { 5 | display: inline-block; 6 | border-radius: 1em; 7 | font-size: 11px !important; 8 | line-height: 12px; 9 | font-weight: 700; 10 | background: #e7fdd8; 11 | color: #66bf3c; 12 | padding: 4px 12px; 13 | margin-right: 10px !important; 14 | vertical-align: top; 15 | } 16 | 17 | .doc-tip-wrapper { 18 | font-size: 13px !important; 19 | line-height: 20px; 20 | margin-top: 40px !important; 21 | margin-bottom: 40px !important; 22 | } 23 | 24 | .doc-tip-wrapper code { 25 | font-size: 12px !important; 26 | display: inline-block; 27 | } 28 | 29 | .doc-label{ 30 | width: 120px; 31 | display: inline-block; 32 | text-align: right; 33 | padding-right: 10px; 34 | } 35 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "sourceMap": true, 12 | "declaration": false, 13 | "downlevelIteration": true, 14 | "experimentalDecorators": true, 15 | "moduleResolution": "node", 16 | "importHelpers": true, 17 | "skipLibCheck": true, 18 | "noImplicitAny": true, 19 | "strictNullChecks": true, 20 | "noImplicitOverride": true, 21 | "noImplicitThis": true, 22 | "target": "ES2022", 23 | "module": "es2020", 24 | "lib": ["ESNext", "dom"], 25 | "resolveJsonModule": true, 26 | "allowJs": true, 27 | "jsx": "react-jsx", 28 | "allowSyntheticDefaultImports": true, 29 | "paths": { 30 | "@xmagic/nzx-antd": ["lib/public-api"], 31 | "@xmagic/nzx-antd/*": ["lib/*"], 32 | "@stories": ["stories"], 33 | "@stories/*": ["stories/*"] 34 | }, 35 | "typeRoots": ["./stories/@types", "./node_modules/@types"] 36 | }, 37 | "angularCompilerOptions": { 38 | "enableI18nLegacyMessageIdFormat": false, 39 | "strictInjectionParameters": true, 40 | "strictInputAccessModifiers": true, 41 | "strictTemplates": false 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /version.js: -------------------------------------------------------------------------------- 1 | const exec = require('child_process').execSync; 2 | const process = require('process'); 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | 6 | function getCommand() { 7 | const [, , ...argv] = process.argv; 8 | return argv.reduce((prev, v) => { 9 | const kv = v.split('='); 10 | prev[kv[0]] = kv[1]; 11 | return prev; 12 | }, {}); 13 | } 14 | 15 | function getDateStr(date) { 16 | const prefixZero = v => (v < 10 ? '0' + v : v); 17 | return ( 18 | date.getFullYear() + 19 | '-' + 20 | prefixZero(date.getMonth() + 1) + 21 | '-' + 22 | prefixZero(date.getDate()) + 23 | ' ' + 24 | prefixZero(date.getHours()) + 25 | ':' + 26 | prefixZero(date.getMinutes()) + 27 | ':' + 28 | prefixZero(date.getSeconds()) 29 | ); 30 | } 31 | 32 | const cmd = getCommand(); 33 | const versionPath = path.resolve(cmd.path || './dist/version'); 34 | const commitId = exec('git log -1 --format="COMMIT ID: %HDATE: %cd" --date=format:"%Y-%m-%d %H:%M:%S"\n') 35 | .toString() 36 | .replace('DATE:', '\nCOMMIT TIME:'); 37 | const branch = exec('git name-rev --name-only HEAD'); 38 | 39 | const dir = path.dirname(versionPath); 40 | if (!fs.existsSync(dir)) { 41 | fs.mkdirSync(dir, { recursive: true }); 42 | } 43 | 44 | const buildTime = 'BUILD TIME: ' + getDateStr(new Date()) + '\n'; 45 | fs.writeFileSync(versionPath, `BRANCH: ${branch}${commitId}${buildTime}`); 46 | --------------------------------------------------------------------------------