提交 e9e3f35f 编写于 作者: J Jason

删除冗余

上级 947de3d0
package dbus
import (
"git.zgwit.com/iot/beeq"
"git.zgwit.com/iot/beeq/packet"
"iot-master/db"
"iot-master/model"
"log"
)
var hive *beeq.Hive
func Start(addr string) error {
hive = beeq.NewHive()
hive.OnConnect(func(connect *packet.Connect, bee *beeq.Bee) bool {
// 验证插件 Key Secret
var plugin model.Plugin
has, err := db.Engine.Where("key=?", connect.UserName()).Get(&plugin)
if !has {
if plugin.Secret == string(connect.Password()) {
return true
} else {
return false
}
} else if err != nil {
log.Println(err)
return false
}
//TODO 验证浏览器
log.Println(bee.ClientId(), "connect", connect)
return true
})
//hive.OnPublish(func(publish *packet.Publish, bee *beeq.Bee) bool {
// log.Println(bee.ClientId(), "publish", publish)
// return true
//})
return hive.ListenAndServe(addr)
}
func Hive() *beeq.Hive {
return hive
}
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support
# You can see what browsers were selected by your queries by running:
# npx browserslist
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR
not IE 9-10 # Angular support for IE 9-10 has been deprecated and will be removed as of Angular v11. To opt-in, remove the 'not' prefix on this line.
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events*.json
speed-measure-plugin*.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
# DtuAdmin
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.0.4.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"MyDTU": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss",
"skipTests": true
},
"@schematics/angular:class": {
"skipTests": true
},
"@schematics/angular:directive": {
"skipTests": true
},
"@schematics/angular:guard": {
"skipTests": true
},
"@schematics/angular:interceptor": {
"skipTests": true
},
"@schematics/angular:module": {
"skipTests": true
},
"@schematics/angular:pipe": {
"skipTests": true
},
"@schematics/angular:service": {
"skipTests": true
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "./dist",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": true,
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/theme.less",
"src/styles.scss"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "MyDTU:build"
},
"configurations": {
"production": {
"browserTarget": "MyDTU:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "MyDTU:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "MyDTU:serve"
},
"configurations": {
"production": {
"devServerTarget": "MyDTU:serve:production"
}
}
}
}
}
},
"defaultProject": "MyDTU"
}
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
browserName: 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({
spec: {
displayStacktrace: StacktraceOption.PRETTY
}
}));
}
};
\ No newline at end of file
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('MyDTU app is running!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo(): Promise<unknown> {
return browser.get(browser.baseUrl) as Promise<unknown>;
}
getTitleText(): Promise<string> {
return element(by.css('app-root .content span')).getText() as Promise<string>;
}
}
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es2018",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/MyDTU'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};
因为 它太大了无法显示 source diff 。你可以改为 查看blob
{
"name": "MyDTU",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve --proxy-config proxy.conf.json -o",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^10.0.14",
"@angular/common": "~10.0.5",
"@angular/compiler": "~10.0.5",
"@angular/core": "~10.0.5",
"@angular/forms": "~10.0.5",
"@angular/platform-browser": "~10.0.5",
"@angular/platform-browser-dynamic": "~10.0.5",
"@angular/router": "~10.0.5",
"@ctrl/ngx-codemirror": "^4.0.2",
"codemirror": "^5.58.3",
"moment": "^2.27.0",
"mqtt": "^4.2.1",
"ng-zorro-antd": "^9.3.0",
"ngx-moment": "^5.0.0",
"ngx-mqtt": "^7.0.14",
"rxjs": "~6.5.5",
"tslib": "^2.0.0",
"zone.js": "~0.10.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.1000.4",
"@angular/cli": "~10.0.4",
"@angular/compiler-cli": "~10.0.5",
"@types/node": "^12.11.1",
"@types/jasmine": "~3.5.0",
"@types/jasminewd2": "~2.0.3",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.5.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~5.0.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~3.3.0",
"karma-jasmine-html-reporter": "^1.5.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~3.9.5"
}
}
{
"/api": {
"target": "http://localhost:8080",
"secure": false
}
}
import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {NzMessageService} from 'ng-zorro-antd';
import {catchError, filter, map} from 'rxjs/operators';
import {Observable, of} from 'rxjs';
import {Router} from '@angular/router';
@Injectable({
providedIn: 'root'
})
export class ApiService {
public base = '/api/'; // environment.host;
constructor(private http: HttpClient, private message: NzMessageService, private route: Router) {
}
request(method: string, uri: string, options: any): Observable<any> {
// 携带Cookie,保持session会话
options.withCredentials = true;
return this.http.request<any>(method, this.base + uri, options).pipe(
// 捕捉异常,数据转换
catchError(err => {
if (err.status === 404) {
return of({error: '无效接口 ' + method + ' ' + uri});
} else if (err.status === 401) {
// window.location.href = '/login';
this.route.navigate(['/login']);
return of({error: '未登录'});
}
return of({error: err.message});
}),
// 统一错误处理
map((ret: any) => {
if (ret && ret.error) {
// 有错误统一显示并不是好的做法
this.message.create('error', ret.error);
// throw new Error(ret.error); //会阻断 complete
}
return ret;
})
);
}
get(uri: string, params?: { [k: string]: any }): Observable<any> {
return this.request('GET', uri, {params});
}
put(uri: string, body: any | null, params?: { [k: string]: any }): Observable<any> {
return this.request('PUT', uri, {params, body});
}
post(uri: string, body: any | null, params?: { [k: string]: any }): Observable<any> {
return this.request('POST', uri, {params, body});
}
patch(uri: string, body: any | null, params?: { [k: string]: any }): Observable<any> {
return this.request('PATCH', uri, {params, body});
}
delete(uri: string, params?: { [k: string]: any }): Observable<any> {
return this.request('DELETE', uri, {params});
}
}
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {LoginComponent} from './login/login.component';
import {PageNotFoundComponent} from './page-not-found/page-not-found.component';
const routes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: '/admin' },
{ path: 'login', pathMatch: 'full', component: LoginComponent},
{ path: 'admin', loadChildren: () => import('./main/main.module').then(m => m.MainModule) },
{ path: '**', component: PageNotFoundComponent},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
}
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {CommonModule} from '@angular/common';
import {AppRoutingModule} from './app-routing.module';
import {AppComponent} from './app.component';
import {HttpClientModule} from '@angular/common/http';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {NZ_I18N} from 'ng-zorro-antd/i18n';
import {zh_CN} from 'ng-zorro-antd/i18n';
import {registerLocaleData} from '@angular/common';
import zh from '@angular/common/locales/zh';
import {LoginComponent} from './login/login.component';
import {
NzButtonModule,
NzCheckboxModule,
NzFormModule,
NzIconModule,
NzInputModule,
NzMessageModule
} from 'ng-zorro-antd';
import {IconsProviderModule} from './icons-provider.module';
import {PageNotFoundComponent} from './page-not-found/page-not-found.component';
registerLocaleData(zh);
@NgModule({
declarations: [
AppComponent,
LoginComponent,
PageNotFoundComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
CommonModule,
FormsModule,
ReactiveFormsModule,
HttpClientModule,
BrowserAnimationsModule,
NzButtonModule,
NzFormModule,
NzInputModule,
NzCheckboxModule,
NzIconModule,
NzMessageModule,
IconsProviderModule,
],
providers: [{provide: NZ_I18N, useValue: zh_CN}],
bootstrap: [AppComponent]
})
export class AppModule {
}
import { NgModule } from '@angular/core';
import { NZ_ICONS, NzIconModule } from 'ng-zorro-antd/icon';
import {
UserOutline,
LockOutline,
} from '@ant-design/icons-angular/icons';
const icons = [UserOutline, LockOutline];
@NgModule({
imports: [NzIconModule],
exports: [NzIconModule],
providers: [
{ provide: NZ_ICONS, useValue: icons }
]
})
export class IconsProviderModule {
}
<div class="login">
<h1>IoT Admin</h1>
<div class="login-form" >
<form nz-form [formGroup]="validateForm" (ngSubmit)="submitForm()">
<nz-form-item>
<nz-form-control nzErrorTip="请输入用户名!">
<nz-input-group nzPrefixIcon="user">
<input type="text" nz-input formControlName="username" placeholder="用户名" />
</nz-input-group>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control nzErrorTip="请输入密码!">
<nz-input-group nzPrefixIcon="lock">
<input type="password" nz-input formControlName="password" placeholder="密码" />
</nz-input-group>
</nz-form-control>
</nz-form-item>
<div nz-row class="login-form-margin">
<div nz-col [nzSpan]="12">
<label nz-checkbox formControlName="remember" nzDisabled>
<span>记住我</span>
</label>
</div>
<div nz-col [nzSpan]="12">
<a class="login-form-forgot">忘记密码</a>
</div>
</div>
<button nz-button class="login-form-button login-form-margin" [nzType]="'primary'">登 录</button>
</form>
</div>
</div>
:host{
display: flex;
justify-content: center;
align-content: center;
width: 100%;
height: 100%;
background-image: url("../../assets/bk.jpg");
background-size: cover;
background-position: center;
}
.login{
margin-top: 100px;
width: 300px;
max-width: 400px;
>h1{
color: white;
text-align: center;
}
}
.login-form {
background-color: white;
padding: 50px;
}
.login-form-margin {
margin-bottom: 16px;
}
.login-form-forgot {
float: right;
}
.login-form-button {
width: 100%;
}
import { Component, OnInit } from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {ApiService} from '../api.service';
import {Router} from '@angular/router';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
validateForm!: FormGroup;
constructor(private fb: FormBuilder, private ca: ApiService, private router: Router) {}
submitForm(): void {
console.log('submit form');
for (const i in this.validateForm.controls) {
this.validateForm.controls[i].markAsDirty();
this.validateForm.controls[i].updateValueAndValidity();
}
if (!this.validateForm.valid) {
return;
}
this.ca.post('login', this.validateForm.value).subscribe(res => {
console.log('res:', res);
this.router.navigate(['/admin/dash']);
}, err => {
console.log('err:', err);
});
}
ngOnInit(): void {
this.validateForm = this.fb.group({
username: [null, [Validators.required]],
password: [null, [Validators.required]],
remember: [false]
});
}
}
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-alert',
templateUrl: './alert.component.html',
styleUrls: ['./alert.component.scss']
})
export class AlertComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-algorithm',
templateUrl: './algorithm.component.html',
styleUrls: ['./algorithm.component.scss']
})
export class AlgorithmComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
<nz-row [nzGutter]="16">
<nz-col [nzSpan]="12">
<nz-statistic [nzValue]="(4 | number)!" nzTitle="通道数量"></nz-statistic>
</nz-col>
<nz-col [nzSpan]="12">
<nz-statistic [nzValue]="(1620 | number)!" nzTitle="连接数量"></nz-statistic>
</nz-col>
</nz-row>
<!-- TODO 流量曲线,数据包曲线,实时 -->
import {Component, OnInit} from '@angular/core';
import {ApiService} from '../../api.service';
import {TabRef} from "../tabs/tabs.component";
@Component({
selector: 'app-dash',
templateUrl: './dash.component.html',
styleUrls: ['./dash.component.scss']
})
export class DashComponent implements OnInit {
constructor(private as: ApiService, private tab: TabRef) {
tab.name = '仪表盘';
}
ngOnInit(): void {
}
}
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-device-detail',
templateUrl: './device-detail.component.html',
styleUrls: ['./device-detail.component.scss']
})
export class DeviceDetailComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-device-log',
templateUrl: './device-log.component.html',
styleUrls: ['./device-log.component.scss']
})
export class DeviceLogComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-device-map',
templateUrl: './device-map.component.html',
styleUrls: ['./device-map.component.scss']
})
export class DeviceMapComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-device',
templateUrl: './device.component.html',
styleUrls: ['./device.component.scss']
})
export class DeviceComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
<p>TODO:元件基本信息</p>
<nz-tabset>
<nz-tab nzTitle="变量">
</nz-tab>
<nz-tab nzTitle="批量">
</nz-tab>
</nz-tabset>
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {TabRef} from '../tabs/tabs.component';
import {ApiService} from '../../api.service';
@Component({
selector: 'app-element-detail',
templateUrl: './element-detail.component.html',
styleUrls: ['./element-detail.component.scss']
})
export class ElementDetailComponent implements OnInit {
element: any = {};
id = 0;
constructor(private as: ApiService, private routeInfo: ActivatedRoute, private tab: TabRef) {
this.id = routeInfo.snapshot.params.id;
tab.name = '元件详情';
}
ngOnInit(): void {
this.as.get('element/' + this.id).subscribe(res => {
if (res.ok) {
this.element = res.data;
this.tab.name = '元件【' + this.element.name + '';
}
// console.log(res);
});
}
}
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
名称
</div>
<div nz-col nzSpan="20">
<input nz-input [(ngModel)]="data.name" placeholder="名称"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
说明
</div>
<div nz-col nzSpan="20">
<textarea nz-input [(ngModel)]="data.description" placeholder="说明"></textarea>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
来源
</div>
<div nz-col nzSpan="20">
<input nz-input [(ngModel)]="data.origin" placeholder="来源"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
生产厂商
</div>
<div nz-col nzSpan="20">
<input nz-input [(ngModel)]="data.manufacturer" placeholder="生产厂商"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
型号
</div>
<div nz-col nzSpan="20">
<input nz-input [(ngModel)]="data.model" placeholder="型号"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
版本号
</div>
<div nz-col nzSpan="20">
<input nz-input [(ngModel)]="data.version" placeholder="版本号"/>
</div>
</div>
<button nz-button nzType="primary" (click)="submit()">保存</button>
import {Component, Input, OnInit} from '@angular/core';
import {ApiService} from '../../api.service';
import {ActivatedRoute} from '@angular/router';
import {TabRef} from '../tabs/tabs.component';
import {NzMessageService, NzModalRef} from 'ng-zorro-antd';
@Component({
selector: 'app-element-edit',
templateUrl: './element-edit.component.html',
styleUrls: ['./element-edit.component.scss']
})
export class ElementEditComponent implements OnInit {
target = 'element';
@Input() id = 0;
data: any = {};
constructor(private as: ApiService, private mr: NzModalRef, private ms: NzMessageService) {
}
ngOnInit(): void {
if (this.id > 0) {
this.as.get(this.target + '/' + this.id).subscribe(res => {
this.data = res.data;
});
}
}
submit(): void {
let uri = this.target;
if (this.data.id) {
uri += '/' + this.data.id;
}
this.as.post(uri, this.data).subscribe(res => {
if (res.ok) {
this.ms.success('保存成功');
this.mr.close(res.data);
}
});
}
}
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-element-variable-edit',
templateUrl: './element-variable-edit.component.html',
styleUrls: ['./element-variable-edit.component.scss']
})
export class ElementVariableEditComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
<nz-space>
<nz-space-item>
<button nz-button (click)="load()" [nzLoading]="loading">
<i nz-icon nzType="reload"></i>
刷新
</button>
</nz-space-item>
<nz-space-item>
<button nz-button (click)="create()">
<i nz-icon nzType="plus"></i>
创建
</button>
</nz-space-item>
<nz-space-item>
<nz-input-group nzSearch nzCompact [nzAddOnAfter]="suffixButton">
<input type="text" nz-input [(ngModel)]="keyword" placeholder="名称、地址"/>
</nz-input-group>
<ng-template #suffixButton>
<button nz-button nzSearch (click)="search()">搜索</button>
</ng-template>
</nz-space-item>
</nz-space>
<nz-table #tbl [nzData]="variables" nzTableLayout="fixed" [nzFrontPagination]="false" [nzPageSize]="pageSize"
[nzPageIndex]="pageIndex" [nzTotal]="total" [nzLoading]="loading" (nzQueryParams)="onTableQuery($event)">
<thead>
<tr>
<th nzColumnKey="ID" [nzSortFn]="true">ID</th>
<th nzColumnKey="ProjectId" [nzSortFn]="true">项目ID</th>
<th nzColumnKey="Name" [nzSortFn]="true">名称</th>
<th nzColumnKey="Slave" [nzSortFn]="true">站号</th>
<th nzColumnKey="Alias" [nzSortFn]="true">别名</th>
<th nzColumnKey="Created" [nzSortFn]="true">创建时间</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of tbl.data">
<td>{{ data.id }}</td>
<td>{{ data.project_id }}</td>
<td>{{ data.name }}</td>
<td>{{ data.slave }}</td>
<td>{{ data.alias }}</td>
<td>{{ data.created | amDateFormat:'YYYY-MM-DD HH:mm:ss' }}</td>
<td>
<a (click)="edit(data)" title="编辑">
<i nz-icon nzType="edit"></i>
</a>
</td>
</tr>
</tbody>
</nz-table>
import {Component, Input, OnInit} from '@angular/core';
import {ApiService} from '../../api.service';
import {NzTableQueryParams} from 'ng-zorro-antd';
import {Router} from '@angular/router';
@Component({
selector: 'app-element-variable',
templateUrl: './element-variable.component.html',
styleUrls: ['./element-variable.component.scss']
})
export class ElementVariableComponent implements OnInit {
@Input() element: any = {};
inited = false;
tableQuery: any;
variables: [];
total = 0;
pageIndex = 1;
pageSize = 10;
sortField = null;
sortOrder = null;
filters = [];
keyword = '';
loading = false;
constructor(private as: ApiService, private router: Router) {
}
ngOnInit(): void {
this.inited = true;
if (this.tableQuery) {
this.onTableQuery(this.tableQuery)
}
}
reload(): void {
this.pageIndex = 1;
this.keyword = '';
this.load();
}
load(): void {
this.loading = true;
this.as.post('element/' + this.element.id + '/variables', {
offset: (this.pageIndex - 1) * this.pageSize,
length: this.pageSize,
sortKey: this.sortField,
sortOrder: this.sortOrder,
filters: this.filters,
keyword: this.keyword,
}).subscribe(res => {
this.variables = res.data;
this.total = res.total;
}, error => {
console.log('error', error);
}, () => {
this.loading = false;
});
}
create(): void {
this.router.navigate(['/admin/element/' + this.element.id + '/variable/create']);
}
edit(c): void {
this.router.navigate(['/admin/element/' + this.element.id + '/variable/' + c.id + '/edit']);
}
onTableQuery(params: NzTableQueryParams): void {
if (!this.inited) {
this.tableQuery = params;
return;
}
const {pageSize, pageIndex, sort, filter} = params;
this.pageSize = pageSize;
this.pageIndex = pageIndex;
const currentSort = sort.find(item => item.value !== null);
this.sortField = (currentSort && currentSort.key) || null;
this.sortOrder = (currentSort && currentSort.value) || null;
this.filters = filter;
this.load();
}
search(): void {
this.pageIndex = 1;
this.load();
}
}
<nz-space>
<nz-space-item>
<button nz-button (click)="load()" [nzLoading]="loading">
<i nz-icon nzType="reload"></i>
刷新
</button>
</nz-space-item>
<nz-space-item>
<button nz-button (click)="edit()">
<i nz-icon nzType="plus"></i>
创建
</button>
</nz-space-item>
<nz-space-item>
<nz-input-group nzSearch nzCompact [nzAddOnAfter]="suffixButton">
<input type="text" nz-input [(ngModel)]="keyword" placeholder="名称、地址"/>
</nz-input-group>
<ng-template #suffixButton>
<button nz-button nzSearch (click)="search()">搜索</button>
</ng-template>
</nz-space-item>
</nz-space>
<nz-table #tbl [nzData]="datum" nzTableLayout="fixed" [nzFrontPagination]="false" [nzPageSize]="pageSize"
[nzPageIndex]="pageIndex" [nzTotal]="total" [nzLoading]="loading" (nzQueryParams)="onTableQuery($event)">
<thead>
<tr>
<th nzColumnKey="ID" [nzSortFn]="true">ID</th>
<th nzColumnKey="Name" [nzSortFn]="true">名称</th>
<th nzColumnKey="UUID" [nzSortFn]="true">UUID</th>
<th nzColumnKey="Manufacturer" [nzSortFn]="true">状态</th>
<th nzColumnKey="Model" [nzSortFn]="true">型号</th>
<th nzColumnKey="Version" [nzSortFn]="true">版本号</th>
<th nzColumnKey="Created" [nzSortFn]="true">创建时间</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of tbl.data" (dblclick)="detail(data)">
<td>{{ data.id }}</td>
<td>{{ data.name }}</td>
<td>{{ data.uuid }}</td>
<td>{{ data.manufacturer }}</td>
<td>{{ data.model }}</td>
<td>{{ data.version }}</td>
<td>{{ data.created | amDateFormat:'YYYY-MM-DD HH:mm:ss' }}</td>
<td>
<a (click)="detail(data)" title="详情">
<i nz-icon nzType="eye"></i>
</a>
<a (click)="edit(data.id)" title="编辑">
<i nz-icon nzType="edit"></i>
</a>
</td>
</tr>
</tbody>
</nz-table>
import {Component, OnInit} from '@angular/core';
import {ApiService} from '../../api.service';
import {NzModalService, NzTableQueryParams} from 'ng-zorro-antd';
import {Router} from '@angular/router';
import {TabRef} from '../tabs/tabs.component';
import {ElementEditComponent} from "../element-edit/element-edit.component";
@Component({
selector: 'app-element',
templateUrl: './element.component.html',
styleUrls: ['./element.component.scss']
})
export class ElementComponent implements OnInit {
datum: any[];
total = 0;
pageIndex = 1;
pageSize = 10;
sortField = null;
sortOrder = null;
filters = [];
keyword = '';
loading = false;
constructor(private as: ApiService, private router: Router, private tab: TabRef, private ms: NzModalService) {
tab.name = '元件管理';
}
ngOnInit(): void {
}
reload(): void {
this.pageIndex = 1;
this.keyword = '';
this.load();
}
load(): void {
this.loading = true;
this.as.post('elements', {
offset: (this.pageIndex - 1) * this.pageSize,
length: this.pageSize,
sortKey: this.sortField,
sortOrder: this.sortOrder,
filters: this.filters,
keywords: [
{key: 'Name', value: this.keyword},
]
}).subscribe(res => {
this.datum = res.data;
this.total = res.total;
}, error => {
console.log('error', error);
}, () => {
this.loading = false;
});
}
edit(id?): void {
const modal = this.ms.create({
nzTitle: id ? '编辑元件' : '创建元件',
nzContent: ElementEditComponent,
nzFooter: null,
nzMaskClosable: false,
// nzViewContainerRef: this.viewContainerRef,
nzComponentParams: {id},
});
// insert/update after close
modal.afterClose.subscribe(data => {
if (!data) {
return;
}
if (id) {
this.datum.forEach((c: any, i, a: any[]) => {
if (c.id === data.id) {
a[i] = data;
}
}
);
} else {
this.datum.unshift(data);
}
});
}
detail(c): void {
this.router.navigate(['/admin/element/' + c.id + '/detail']);
}
onTableQuery(params: NzTableQueryParams): void {
const {pageSize, pageIndex, sort, filter} = params;
this.pageSize = pageSize;
this.pageIndex = pageIndex;
const currentSort = sort.find(item => item.value !== null);
this.sortField = (currentSort && currentSort.key) || null;
this.sortOrder = (currentSort && currentSort.value) || null;
this.filters = filter;
this.load();
}
search(): void {
this.pageIndex = 1;
this.load();
}
}
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-history',
templateUrl: './history.component.html',
styleUrls: ['./history.component.scss']
})
export class HistoryComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
import {NgModule} from '@angular/core';
import {NZ_ICONS, NzIconModule} from 'ng-zorro-antd/icon';
import {
MenuFoldOutline,
MenuUnfoldOutline,
DashboardOutline,
SettingOutline,
LogoutOutline,
ApiOutline,
ReloadOutline,
PlusOutline,
DeleteOutline,
AppstoreOutline,
AimOutline,
SwapOutline,
ClusterOutline,
AlertOutline,
CloudUploadOutline,
ProjectOutline,
BlockOutline,
DatabaseOutline,
EyeOutline,
} from '@ant-design/icons-angular/icons';
import {CommonModule} from '@angular/common';
const icons = [
// 菜单相关
MenuFoldOutline, MenuUnfoldOutline, DashboardOutline, ApiOutline, SettingOutline, AppstoreOutline,
// 表格操作
ReloadOutline, PlusOutline, DeleteOutline, AimOutline, SwapOutline,
LogoutOutline, ClusterOutline, AlertOutline, CloudUploadOutline, ProjectOutline, BlockOutline, DatabaseOutline, EyeOutline
];
@NgModule({
imports: [CommonModule, NzIconModule.forChild(icons)],
exports: [NzIconModule],
providers: [
{provide: NZ_ICONS, useValue: icons}
]
})
export class IconsProviderModule {
}
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
名称
</div>
<div nz-col nzSpan="20">
<input nz-input [(ngModel)]="data.name" placeholder="名称"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
序列号
</div>
<div nz-col nzSpan="20">
<input nz-input [(ngModel)]="data.serial" placeholder="序列号"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
禁用
</div>
<div nz-col nzSpan="20">
<nz-switch [(ngModel)]="data.disabled"></nz-switch>
</div>
</div>
<button nz-button nzType="primary" (click)="submit()">保存</button>
import {Component, OnInit} from '@angular/core';
import {ApiService} from '../../api.service';
import {ActivatedRoute, Router} from '@angular/router';
import {TabRef} from "../tabs/tabs.component";
@Component({
selector: 'app-link-edit',
templateUrl: './link-edit.component.html',
styleUrls: ['./link-edit.component.scss']
})
export class LinkEditComponent implements OnInit {
target = 'link';
id = 0;
data: any = {};
constructor(private as: ApiService, private routeInfo: ActivatedRoute, private tab: TabRef) {
tab.name = '链路编辑';
}
ngOnInit(): void {
this.id = this.routeInfo.snapshot.params.id || 0;
if (this.id > 0) {
this.as.get(this.target + '/' + this.id).subscribe(res => {
this.data = res.data;
});
}
}
submit(): void {
this.as.put(this.target + '/' + this.data.id, this.data).subscribe(res => {
console.log(res);
// TODO 修改成功
this.tab.Close();
});
}
}
<!-- TODO: 工作状态,数据收发量,速度-->
<div class="send-area">
<textarea [(ngModel)]="text" style="resize: vertical"></textarea>
<div class="submit">
<label nz-checkbox [(ngModel)]="isHex">十六进制</label>
<button nz-button nzType="primary" (click)="send()">发送</button>
</div>
</div>
<div nz-row [nzGutter]="10" class="monitor">
<div nz-col nzSpan="12" nzXs="24" nzSm="12">
<div class="title">
数据接收
</div>
<div class="content" #contentRecv>
<div *ngFor="let d of dataRecv">
{{d.time | amDateFormat: 'hh:mm:ss'}} -&gt; {{d.data}}
</div>
</div>
</div>
<div nz-col nzSpan="12" nzXs="24" nzSm="12">
<div class="title">
数据发送
</div>
<div class="content" #contentSend>
<div *ngFor="let d of dataSend">
{{d.time | amDateFormat: 'hh:mm:ss'}} -&gt; {{d.data}}
</div>
</div>
</div>
</div>
.monitor{
.title {
text-align: center;
padding: 5px;
}
.content {
min-height: 200px;
max-height: 400px;
border: 1px solid grey;
overflow-y: auto;
overflow-x: hidden;
word-break: break-all;
}
}
.send-area{
display: flex;
align-items: center;
justify-content: center;
textarea{
flex: 1;
min-height: 40px;
max-height: 100px;
}
.submit{
display: flex;
flex-direction: column;
width: 100px;
padding: 5px;
}
}
import {Component, ElementRef, OnDestroy, OnInit, ViewChild} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
import {ApiService} from '../../api.service';
import {MqttService} from '../../mqtt.service';
import {TabRef} from "../tabs/tabs.component";
@Component({
selector: 'app-link-monitor',
templateUrl: './link-monitor.component.html',
styleUrls: ['./link-monitor.component.scss']
})
export class LinkMonitorComponent implements OnInit, OnDestroy {
@ViewChild('contentRecv')
contentRecv: ElementRef;
@ViewChild('contentSend')
contentSend: ElementRef;
id: number;
link: any;
isHex = false;
text = '';
dataRecv = [];
dataSend = [];
cacheSizeRecv = 500;
cacheSizeSend = 500;
recvSub: any;
sendSub: any;
constructor(private routeInfo: ActivatedRoute, private as: ApiService, private tab: TabRef, private mqtt: MqttService) {
tab.name = '连接监控';
this.id = this.routeInfo.snapshot.params.id;
this.load();
}
ngOnInit(): void {
}
ngOnDestroy(): void {
this.recvSub.unsubscribe();
this.sendSub.unsubscribe();
}
hex_to_buffer(hex: string): Buffer {
hex = hex.replace(/\s*/g, '');
const arr = [];
for (let i = 0; i < hex.length; i += 2) {
arr.push(hex.substr(i, 2));
}
const hexes = arr.map(el => parseInt(el, 16));
return Buffer.from(new Uint8Array(hexes));
}
buffer_to_hex(buf): string {
const arr = Array.prototype.slice.call(buf);
return arr.map(el => Number(el).toString(16)).join(' ');
}
subscribe(): void {
this.recvSub = this.mqtt.subscribe('/link/' + this.link.channel_id + '/' + this.id + '/recv').subscribe(packet => {
this.dataRecv.push({
data: this.buffer_to_hex(packet.payload),
time: new Date(),
});
if (this.dataRecv.length > this.cacheSizeRecv) {
this.dataRecv.splice(0, 1);
}
this.contentRecv.nativeElement.scrollTo(0, this.contentRecv.nativeElement.scrollHeight);
});
this.sendSub = this.mqtt.subscribe('/link/' + this.link.channel_id + '/' + this.id + '/send').subscribe(packet => {
this.dataSend.push({
data: this.buffer_to_hex(packet.payload),
time: new Date(),
});
if (this.dataSend.length > this.cacheSizeSend) {
this.dataSend.splice(0, 1);
}
this.contentSend.nativeElement.scrollTo(0, this.contentSend.nativeElement.scrollHeight);
});
}
load(): void {
this.as.get('link/' + this.id).subscribe(res => {
this.link = res.data;
this.subscribe();
});
}
loadStatus(): void {
// 在线,monitor
}
send(): void {
//console.log('send', this.text);
let content: any = this.text;
// 转换十六进制
if (this.isHex) {
content = this.hex_to_buffer(this.text);
}
this.mqtt.publish('/link/' + this.link.channel_id + '/' + this.id + '/transfer', content);
}
}
<nz-space>
<nz-space-item>
<button nz-button (click)="load()" [nzLoading]="loading">
<i nz-icon nzType="reload"></i>
刷新
</button>
</nz-space-item>
<nz-space-item>
<nz-input-group nzSearch nzCompact [nzAddOnAfter]="suffixButton">
<input type="text" nz-input [(ngModel)]="keyword" placeholder="名称、地址"/>
</nz-input-group>
<ng-template #suffixButton>
<button nz-button nzSearch (click)="search()">搜索</button>
</ng-template>
</nz-space-item>
</nz-space>
<nz-table #tbl [nzData]="links" nzTableLayout="fixed" [nzFrontPagination]="false" [nzPageSize]="pageSize"
[nzPageIndex]="pageIndex" [nzTotal]="total" [nzLoading]="loading" (nzQueryParams)="onTableQuery($event)">
<thead>
<tr>
<th nzColumnKey="ID" [nzSortFn]="true">ID</th>
<th nzColumnKey="ChannelId" [nzSortFn]="true">通道ID</th>
<th nzColumnKey="Name" [nzSortFn]="true">名称</th>
<th nzColumnKey="Serial" [nzSortFn]="true">序列号</th>
<th nzColumnKey="Addr" [nzSortFn]="true">地址</th>
<th nzColumnKey="Active" nzShowFilter [nzFilterFn]="true" [nzFilters]="statusFilters">状态</th>
<th nzColumnKey="Online" [nzSortFn]="true">上线时间</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of tbl.data">
<td>{{ data.id }}</td>
<td>{{ data.tunnel_id }}</td>
<td>{{ data.name }}</td>
<td>{{ data.serial }}</td>
<td>{{ data.addr}}</td>
<td>
{{ data.active ? '活跃' : '-' }}
</td>
<td>{{ data.online | amDateFormat:'YYYY-MM-DD HH:mm:ss' }}</td>
<td>
<a [routerLink]="'/admin/link-monitor/'+data.id" title="监控">
<i nz-icon nzType="aim"></i>
</a>
<nz-divider nzType="vertical"></nz-divider>
<a (click)="transfer(data)" title="透传">
<i nz-icon nzType="swap"></i>
</a>
<nz-divider nzType="vertical"></nz-divider>
<a (click)="edit(data)" title="编辑">
<i nz-icon nzType="edit"></i>
</a>
</td>
</tr>
</tbody>
</nz-table>
import {Component, OnInit} from '@angular/core';
import {ApiService} from '../../api.service';
import {NzTableQueryParams} from 'ng-zorro-antd';
import {Router} from '@angular/router';
import {TabRef} from '../tabs/tabs.component';
@Component({
selector: 'app-link',
templateUrl: './link.component.html',
styleUrls: ['./link.component.scss']
})
export class LinkComponent implements OnInit {
links: [];
total = 0;
pageIndex = 1;
pageSize = 10;
sortField = null;
sortOrder = null;
filters = [];
keyword = '';
loading = false;
statusFilters = [{text: '启动', value: true}];
constructor(private as: ApiService, private router: Router, private tab: TabRef) {
tab.name = '连接管理';
}
ngOnInit(): void {
this.loadFilters();
}
reload(): void {
this.pageIndex = 1;
this.keyword = '';
this.load();
}
load(): void {
this.loading = true;
this.as.post('links', {
offset: (this.pageIndex - 1) * this.pageSize,
length: this.pageSize,
sortKey: this.sortField,
sortOrder: this.sortOrder,
filters: this.filters,
keyword: this.keyword,
}).subscribe(res => {
this.links = res.data;
this.total = res.total;
}, error => {
console.log('error', error);
}, () => {
this.loading = false;
});
}
loadFilters(): void {
// this.as.get('distinct/copy/host').subscribe(res => {
// console.log('res', res);
// this.hosts = res.data.map(h => {
// return {
// text: h.host,
// value: h.host
// };
// });
// }, error => {
// console.log('error', error);
// });
}
edit(c): void {
this.router.navigate(['/admin/link/' + c.id + '/edit/']);
}
onTableQuery(params: NzTableQueryParams): void {
const {pageSize, pageIndex, sort, filter} = params;
this.pageSize = pageSize;
this.pageIndex = pageIndex;
const currentSort = sort.find(item => item.value !== null);
this.sortField = (currentSort && currentSort.key) || null;
this.sortOrder = (currentSort && currentSort.value) || null;
this.filters = filter;
this.load();
}
search(): void {
this.pageIndex = 1;
this.load();
}
}
import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {DashComponent} from './dash/dash.component';
import {MainComponent} from './main.component';
import {TunnelComponent} from './tunnel/tunnel.component';
import {TunnelEditComponent} from './tunnel-edit/tunnel-edit.component';
import {LinkComponent} from './link/link.component';
import {LinkEditComponent} from './link-edit/link-edit.component';
import {LinkMonitorComponent} from './link-monitor/link-monitor.component';
import {PluginComponent} from './plugin/plugin.component';
import {PluginEditComponent} from './plugin-edit/plugin-edit.component';
import {ProjectComponent} from './project/project.component';
import {ProjectEditComponent} from './project-edit/project-edit.component';
import {ProjectDetailComponent} from './project-detail/project-detail.component';
import {ElementComponent} from './element/element.component';
import {ElementDetailComponent} from './element-detail/element-detail.component';
import {ElementEditComponent} from './element-edit/element-edit.component';
import {HistoryComponent} from './history/history.component';
import {AlgorithmComponent} from './algorithm/algorithm.component';
import {AlertComponent} from './alert/alert.component';
import {DeviceComponent} from './device/device.component';
import {DeviceMapComponent} from './device-map/device-map.component';
import {DeviceLogComponent} from './device-log/device-log.component';
import {DeviceDetailComponent} from './device-detail/device-detail.component';
const routes: Routes = [
{
path: '',
component: MainComponent,
children: [
{path: '', redirectTo: 'dash'},
{path: 'dash', component: DashComponent},
{path: 'device', component: DeviceComponent},
{path: 'device/map', component: DeviceMapComponent},
{path: 'device/log', component: DeviceLogComponent},
{path: 'device/:id/detail', component: DeviceDetailComponent},
{path: 'device/:id/log', component: DeviceLogComponent},
{path: 'history', component: HistoryComponent},
{path: 'algorithm', component: AlgorithmComponent},
{path: 'alert', component: AlertComponent},
{path: 'tunnel', component: TunnelComponent},
{path: 'tunnel/create', component: TunnelEditComponent},
{path: 'tunnel/:id/edit', component: TunnelEditComponent},
{path: 'tunnel/:id/link', component: LinkComponent},
{path: 'link', component: LinkComponent},
{path: 'link/:id/edit', component: LinkEditComponent},
{path: 'link/:id/monitor', component: LinkMonitorComponent},
{path: 'project', component: ProjectComponent},
{path: 'project/create', component: ProjectEditComponent},
{path: 'project/:id/edit', component: ProjectEditComponent},
{path: 'project/:id/detail', component: ProjectDetailComponent},
{path: 'element', component: ElementComponent},
{path: 'element/create', component: ElementEditComponent},
{path: 'element/:id/edit', component: ElementEditComponent},
{path: 'element/:id/detail', component: ElementDetailComponent},
{path: 'plugin', component: PluginComponent},
{path: 'plugin/create', component: PluginEditComponent},
{path: 'plugin/:id/edit', component: PluginEditComponent},
{path: '**', redirectTo: 'dash'},
]
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class MainRoutingModule {
}
<nz-layout class="app-layout">
<nz-sider class="menu-sidebar"
nzCollapsible
nzWidth="180px"
nzBreakpoint="md"
[(nzCollapsed)]="isCollapsed"
[nzTrigger]="null">
<div class="sidebar-logo">
<a href="https://github.com/zgwit/MyDTU" target="_blank">
<img src="assets/logo.svg" alt="logo">
<h1>IoT Admin</h1>
</a>
</div>
<ul nz-menu nzTheme="dark" nzMode="inline" [nzInlineCollapsed]="isCollapsed">
<li nz-submenu *ngFor="let menu of menus" [nzTitle]="menu.title" [nzIcon]="menu.icon" [nzOpen]="menu.open" [nzDisabled]="menu.disabled">
<ul>
<li nz-menu-item *ngFor="let m of menu.children" [routerLink]="m.router" [nzDisabled]="m.disabled" nzMatchRouter nzMatchRouterExact>
<i nz-icon [nzType]="m.icon" nzTheme="outline" *ngIf="m.icon"></i>
{{m.title}}
</li>
</ul>
</li>
</ul>
</nz-sider>
<nz-layout>
<nz-header>
<div class="app-header">
<span class="header-trigger" (click)="isCollapsed = !isCollapsed">
<i class="trigger"
nz-icon
[nzType]="isCollapsed ? 'menu-unfold' : 'menu-fold'"
></i>
</span>
</div>
</nz-header>
<nz-content>
<app-tabs></app-tabs>
</nz-content>
</nz-layout>
</nz-layout>
:host {
display: flex;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.app-layout {
height: 100vh;
}
.menu-sidebar {
position: relative;
z-index: 10;
min-height: 100vh;
overflow-y: auto;
box-shadow: 2px 0 6px rgba(0,21,41,.35);
a{
color: inherit;
}
}
.header-trigger {
height: 64px;
padding: 20px 24px;
font-size: 20px;
cursor: pointer;
transition: all .3s,padding 0s;
}
.trigger:hover {
color: #1890ff;
}
.sidebar-logo {
position: relative;
height: 64px;
padding-left: 24px;
overflow: hidden;
line-height: 64px;
background: #001529;
transition: all .3s;
}
.sidebar-logo img {
display: inline-block;
height: 32px;
width: 32px;
vertical-align: middle;
}
.sidebar-logo h1 {
display: inline-block;
margin: 0 0 0 20px;
color: #fff;
font-weight: 600;
font-size: 14px;
font-family: Avenir,Helvetica Neue,Arial,Helvetica,sans-serif;
vertical-align: middle;
}
nz-header {
padding: 0;
width: 100%;
z-index: 2;
}
.app-header {
position: relative;
height: 64px;
padding: 0;
background: #fff;
box-shadow: 0 1px 4px rgba(0,21,41,.08);
}
nz-content {
margin: 10px;
}
.inner-content {
padding: 10px;
background: #fff;
height: 100%;
}
import {
Component,
OnInit,
} from '@angular/core';
@Component({
selector: 'app-main',
templateUrl: './main.component.html',
styleUrls: ['./main.component.scss']
})
export class MainComponent implements OnInit {
isCollapsed = false;
menus = [
{
title: '控制台',
icon: 'dashboard',
open: true,
children: [
{
title: '仪表盘',
router: 'dash'
},
]
},
{
title: '设备中心',
icon: 'block',
children: [
{
title: '设备管理',
router: 'device'
},
{
title: '地图模式',
router: 'device/map'
},
{
title: '操作日志',
router: 'device/log'
},
]
},
{
title: '通道管理',
icon: 'api',
children: [
{
title: '通道管理',
router: 'tunnel'
},
{
title: '连接管理',
router: 'link'
}
]
},
{
title: '项目管理',
icon: 'block',
open: false,
children: [
{
title: '项目管理',
router: 'project'
},
{
title: '模板管理',
router: 'template'
},
{
title: '元件管理',
router: 'element'
},
]
},
{
title: '数据中心',
icon: 'database',
children: [
{
title: '历史记录',
router: 'history'
},
{
title: '算法分析',
router: 'algorithm'
},
]
},
{
title: '报警中心',
icon: 'alert',
children: [
{
title: '报警记录',
router: 'alert'
},
{
title: '微信通知',
router: 'alert-wechat'
},
{
title: '邮件通知',
router: 'alert-email'
},
{
title: '短信通知',
router: 'alert-sms'
}
]
},
{
title: 'OTA升级',
icon: 'cloud-upload',
open: false,
disabled: true,
children: [
{
title: '固件管理',
router: 'firmware'
},
{
title: '升级日志',
router: 'upgrade-log'
},
]
},
{
title: '设置',
icon: 'setting',
open: false,
children: [
{
title: '设置',
router: 'setting'
},
{
title: '邮件发件箱',
router: 'email'
},
{
title: '数据备份',
router: 'backup'
},
{
title: '系统日志',
router: 'logs'
},
]
}
];
constructor() {
}
ngOnInit(): void {
}
}
import {NgModule} from '@angular/core';
import {IconsProviderModule} from './icons-provider.module';
import {NzLayoutModule} from 'ng-zorro-antd/layout';
import {NzMenuModule} from 'ng-zorro-antd/menu';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {CommonModule} from '@angular/common';
import {HttpClientModule} from '@angular/common/http';
import {MainRoutingModule} from './main-routing.module';
import {MainComponent} from './main.component';
import {
NzButtonModule,
NzCheckboxModule, NzCollapseModule, NzDividerModule, NzDrawerModule,
NzFormModule,
NzIconModule,
NzInputModule, NzInputNumberModule,
NzModalModule, NzPopconfirmModule, NzSelectModule, NzStatisticModule, NzSwitchModule,
NzTableModule, NzTabsModule,
NzToolTipModule
} from 'ng-zorro-antd';
import {DashComponent} from './dash/dash.component';
import {MomentModule} from 'ngx-moment';
import {UiModule} from '../ui/ui.module';
import {TabsComponent} from './tabs/tabs.component';
import {TunnelComponent} from './tunnel/tunnel.component';
import {TunnelEditComponent} from './tunnel-edit/tunnel-edit.component';
import {LinkComponent} from './link/link.component';
import {LinkEditComponent} from './link-edit/link-edit.component';
import {LinkMonitorComponent} from './link-monitor/link-monitor.component';
import {PluginComponent} from './plugin/plugin.component';
import {PluginEditComponent} from './plugin-edit/plugin-edit.component';
import {ProjectComponent} from './project/project.component';
import {ProjectEditComponent} from './project-edit/project-edit.component';
import {ProjectDetailComponent} from './project-detail/project-detail.component';
import {TemplateComponent} from './template/template.component';
import {TemplateEditComponent} from './template-edit/template-edit.component';
import {TemplateDetailComponent} from './template-detail/template-detail.component';
import {ElementComponent} from './element/element.component';
import {ElementEditComponent} from './element-edit/element-edit.component';
import {ElementDetailComponent} from './element-detail/element-detail.component';
import {NzSpaceModule} from 'ng-zorro-antd/space';
import {ProjectElementComponent} from './project-element/project-element.component';
import {ProjectElementEditComponent} from './project-element-edit/project-element-edit.component';
import {ProjectJobComponent} from './project-job/project-job.component';
import {ProjectJobEditComponent} from './project-job-edit/project-job-edit.component';
import {ProjectStrategyComponent} from './project-strategy/project-strategy.component';
import {ProjectStrategyEditComponent} from './project-strategy-edit/project-strategy-edit.component';
import {ElementVariableComponent} from './element-variable/element-variable.component';
import {ElementVariableEditComponent} from './element-variable-edit/element-variable-edit.component';
import {ProjectLinkComponent} from './project-link/project-link.component';
import {ProjectLinkEditComponent} from './project-link-edit/project-link-edit.component';
import {ProjectFunctionComponent} from './project-function/project-function.component';
import {ProjectFunctionEditComponent} from './project-function-edit/project-function-edit.component';
import {CodemirrorModule} from "@ctrl/ngx-codemirror";
@NgModule({
declarations: [MainComponent, TabsComponent, DashComponent,
TunnelComponent, TunnelEditComponent,
LinkComponent, LinkEditComponent, LinkMonitorComponent,
PluginComponent, PluginEditComponent,
ProjectComponent, ProjectEditComponent, ProjectDetailComponent,
ProjectElementComponent, ProjectElementEditComponent,
ProjectJobComponent, ProjectJobEditComponent,
ProjectStrategyComponent, ProjectStrategyEditComponent,
ProjectLinkComponent, ProjectLinkEditComponent,
ProjectFunctionComponent, ProjectFunctionEditComponent,
ElementComponent, ElementEditComponent, ElementDetailComponent,
ElementVariableComponent, ElementVariableEditComponent,
],
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
MomentModule,
UiModule,
// IconsProviderModule,
// BrowserModule,
NzIconModule,
NzLayoutModule,
NzMenuModule,
HttpClientModule,
MainRoutingModule,
NzToolTipModule,
NzTableModule,
NzModalModule,
NzFormModule,
NzButtonModule,
NzInputModule,
NzCheckboxModule,
NzSwitchModule,
NzPopconfirmModule,
IconsProviderModule,
NzDividerModule,
NzDrawerModule,
NzSelectModule,
NzSpaceModule,
NzInputNumberModule,
NzStatisticModule,
NzTabsModule,
NzCollapseModule,
CodemirrorModule,
],
bootstrap: [MainComponent]
})
export class MainModule {
}
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
名称
</div>
<div nz-col nzSpan="20">
<input nz-input [(ngModel)]="data.name" placeholder="名称"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
密钥
</div>
<div nz-col nzSpan="20">
<input nz-input [(ngModel)]="data.key" placeholder="密钥"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
密码
</div>
<div nz-col nzSpan="20">
<input nz-input [(ngModel)]="data.secret" placeholder="密码"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
禁用
</div>
<div nz-col nzSpan="20">
<nz-switch [(ngModel)]="data.disabled"></nz-switch>
</div>
</div>
<button nz-button nzType="primary" (click)="submit()">保存</button>
import {Component, OnInit} from '@angular/core';
import {ApiService} from '../../api.service';
import {ActivatedRoute, Router} from '@angular/router';
import {TabRef} from "../tabs/tabs.component";
@Component({
selector: 'app-plugin-edit',
templateUrl: './plugin-edit.component.html',
styleUrls: ['./plugin-edit.component.scss']
})
export class PluginEditComponent implements OnInit {
target = 'plugin';
id = 0;
data: any = {};
constructor(private as: ApiService, private routeInfo: ActivatedRoute, private tab: TabRef) {
tab.name = '插件创建';
}
ngOnInit(): void {
this.id = this.routeInfo.snapshot.params.id || 0;
if (this.id > 0) {
this.as.get(this.target + '/' + this.id).subscribe(res => {
this.data = res.data;
});
}
}
submit(): void {
if (this.data.id) {
this.as.put(this.target + '/' + this.data.id, this.data).subscribe(res => {
console.log(res);
// TODO 修改成功
this.tab.Close();
});
} else {
this.as.post(this.target, this.data).subscribe(res => {
console.log(res);
// TODO 保存成功
this.tab.Close();
});
}
}
}
<nz-space>
<nz-space-item>
<button nz-button (click)="load()" [nzLoading]="loading">
<i nz-icon nzType="reload"></i>
刷新
</button>
</nz-space-item>
<nz-space-item>
<button nz-button (click)="create()">
<i nz-icon nzType="plus"></i>
创建
</button>
</nz-space-item>
<nz-space-item>
<nz-input-group nzSearch nzCompact [nzAddOnAfter]="suffixButton">
<input type="text" nz-input [(ngModel)]="keyword" placeholder="名称、地址"/>
</nz-input-group>
<ng-template #suffixButton>
<button nz-button nzSearch (click)="search()">搜索</button>
</ng-template>
</nz-space-item>
</nz-space>
<nz-table #tbl [nzData]="plugins" nzTableLayout="fixed" [nzFrontPagination]="false" [nzPageSize]="pageSize"
[nzPageIndex]="pageIndex" [nzTotal]="total" [nzLoading]="loading" (nzQueryParams)="onTableQuery($event)">
<thead>
<tr>
<th nzColumnKey="ID" [nzSortFn]="true">ID</th>
<th nzColumnKey="Name" [nzSortFn]="true">名称</th>
<th nzColumnKey="Key" [nzSortFn]="true">密钥</th>
<th nzColumnKey="Secret" [nzSortFn]="true">密码</th>
<th nzColumnKey="Status" nzShowFilter [nzFilterFn]="true" [nzFilters]="statusFilters">状态</th>
<th nzColumnKey="Created" [nzSortFn]="true">创建时间</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of tbl.data">
<td>{{ data.id }}</td>
<td>{{ data.name }}</td>
<td>{{ data.key }}</td>
<td>{{data.secret}}</td>
<td>
{{data.disabled ? '禁用' : ''}}
</td>
<td>{{ data.created | amDateFormat:'YYYY-MM-DD HH:mm:ss' }}</td>
<td>
<a (click)="edit(data)" title="编辑">
<i nz-icon nzType="edit"></i>
</a>
</td>
</tr>
</tbody>
</nz-table>
import { Component, OnInit } from '@angular/core';
import {ApiService} from '../../api.service';
import {PluginEditComponent} from '../plugin-edit/plugin-edit.component';
import {NzTableQueryParams} from 'ng-zorro-antd';
import {Router} from "@angular/router";
import {TabRef} from "../tabs/tabs.component";
@Component({
selector: 'app-plugin',
templateUrl: './plugin.component.html',
styleUrls: ['./plugin.component.scss']
})
export class PluginComponent implements OnInit {
plugins: [];
total = 0;
pageIndex = 1;
pageSize = 10;
sortField = null;
sortOrder = null;
filters = [];
keyword = '';
loading = false;
statusFilters = [{text: '启动', value: 1}];
constructor(private as: ApiService, private router: Router, private tab: TabRef) {
tab.name = '插件管理';
}
ngOnInit(): void {
}
reload(): void {
this.pageIndex = 1;
this.keyword = '';
this.load();
}
load(): void {
this.loading = true;
this.as.post('plugins', {
offset: (this.pageIndex - 1) * this.pageSize,
length: this.pageSize,
sortKey: this.sortField,
sortOrder: this.sortOrder,
filters: this.filters,
keyword: this.keyword,
}).subscribe(res => {
this.plugins = res.data;
this.total = res.total;
}, error => {
console.log('error', error);
}, () => {
this.loading = false;
});
}
create(): void {
this.router.navigate(['/admin/plugin-create']);
}
edit(c): void {
this.router.navigate(['/admin/plugin-edit/' + c.id]);
}
onTableQuery(params: NzTableQueryParams): void {
const {pageSize, pageIndex, sort, filter} = params;
this.pageSize = pageSize;
this.pageIndex = pageIndex;
const currentSort = sort.find(item => item.value !== null);
this.sortField = (currentSort && currentSort.key) || null;
this.sortOrder = (currentSort && currentSort.value) || null;
this.filters = filter;
this.load();
}
search(): void {
this.pageIndex = 1;
this.load();
}
}
<p>TODO:项目基本信息</p>
<p>TODO:链接平铺,包含元件, </p>
<app-project-link [project]="project"></app-project-link>
<app-project-element [project]="project"></app-project-element>
<nz-tabset>
<nz-tab nzTitle="功能脚本">
<app-project-function [project]="project"></app-project-function>
</nz-tab>
<nz-tab nzTitle="定时任务">
<app-project-job [project]="project"></app-project-job>
</nz-tab>
<nz-tab nzTitle="自动策略">
<app-project-strategy [project]="project"></app-project-strategy>
</nz-tab>
</nz-tabset>
import {Component, OnInit} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {TabRef} from '../tabs/tabs.component';
import {ApiService} from '../../api.service';
@Component({
selector: 'app-project-detail',
templateUrl: './project-detail.component.html',
styleUrls: ['./project-detail.component.scss']
})
export class ProjectDetailComponent implements OnInit {
project: any = {};
id = 0;
constructor(private as: ApiService, private routeInfo: ActivatedRoute, private tab: TabRef) {
this.id = routeInfo.snapshot.params.id;
tab.name = '项目详情';
}
ngOnInit(): void {
this.as.get('project/' + this.id).subscribe(res => {
if (res.ok) {
this.project = res.data;
this.tab.name = '项目【' + this.project.name + '';
}
// console.log(res);
});
}
}
<ngx-codemirror
[(ngModel)]="content"
[options]="{
lineNumbers: true,
theme: 'material',
mode: 'yaml'
}"
></ngx-codemirror>
<button nz-button nzType="primary" (click)="submit()">保存</button>
import {Component, Input, OnInit} from '@angular/core';
import {ApiService} from '../../api.service';
import {ActivatedRoute} from '@angular/router';
import {TabRef} from '../tabs/tabs.component';
import {NzMessageService, NzModalRef} from 'ng-zorro-antd';
@Component({
selector: 'app-project-edit',
templateUrl: './project-edit.component.html',
styleUrls: ['./project-edit.component.scss']
})
export class ProjectEditComponent implements OnInit {
target = 'project';
@Input() id = 0;
data: any = {};
content = "";
constructor(private as: ApiService, private mr: NzModalRef, private ms: NzMessageService) {
}
ngOnInit(): void {
if (this.id > 0) {
this.as.get(this.target + '/' + this.id).subscribe(res => {
this.data = res.data;
});
}
}
submit(): void {
let uri = this.target;
if (this.data.id) {
uri += '/' + this.data.id;
}
this.as.post(uri, this.data).subscribe(res => {
if (res.ok) {
this.ms.success('保存成功');
this.mr.close(res.data);
}
});
}
}
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-project-element-edit',
templateUrl: './project-element-edit.component.html',
styleUrls: ['./project-element-edit.component.scss']
})
export class ProjectElementEditComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
<nz-space>
<nz-space-item>
<button nz-button (click)="load()" [nzLoading]="loading">
<i nz-icon nzType="reload"></i>
刷新
</button>
</nz-space-item>
<nz-space-item>
<button nz-button (click)="create()">
<i nz-icon nzType="plus"></i>
创建
</button>
</nz-space-item>
<nz-space-item>
<nz-input-group nzSearch nzCompact [nzAddOnAfter]="suffixButton">
<input type="text" nz-input [(ngModel)]="keyword" placeholder="名称、地址"/>
</nz-input-group>
<ng-template #suffixButton>
<button nz-button nzSearch (click)="search()">搜索</button>
</ng-template>
</nz-space-item>
</nz-space>
<nz-table #tbl [nzData]="elements" nzTableLayout="fixed" [nzFrontPagination]="false" [nzPageSize]="pageSize"
[nzPageIndex]="pageIndex" [nzTotal]="total" [nzLoading]="loading" (nzQueryParams)="onTableQuery($event)">
<thead>
<tr>
<th nzColumnKey="ID" [nzSortFn]="true">ID</th>
<th nzColumnKey="ProjectId" [nzSortFn]="true">项目ID</th>
<th nzColumnKey="Name" [nzSortFn]="true">名称</th>
<th nzColumnKey="Slave" [nzSortFn]="true">站号</th>
<th nzColumnKey="Alias" [nzSortFn]="true">别名</th>
<th nzColumnKey="Created" [nzSortFn]="true">创建时间</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of tbl.data">
<td>{{ data.id }}</td>
<td>{{ data.project_id }}</td>
<td>{{ data.name }}</td>
<td>{{ data.slave }}</td>
<td>{{ data.alias }}</td>
<td>{{ data.created | amDateFormat:'YYYY-MM-DD HH:mm:ss' }}</td>
<td>
<a (click)="edit(data)" title="编辑">
<i nz-icon nzType="edit"></i>
</a>
</td>
</tr>
</tbody>
</nz-table>
import {Component, Input, OnInit} from '@angular/core';
import {ApiService} from '../../api.service';
import {NzTableQueryParams} from 'ng-zorro-antd';
import {Router} from '@angular/router';
@Component({
selector: 'app-project-element',
templateUrl: './project-element.component.html',
styleUrls: ['./project-element.component.scss']
})
export class ProjectElementComponent implements OnInit {
@Input() project: any = {};
inited = false;
tableQuery: any;
elements: [];
total = 0;
pageIndex = 1;
pageSize = 10;
sortField = null;
sortOrder = null;
filters = [];
keyword = '';
loading = false;
constructor(private as: ApiService, private router: Router) {
}
ngOnInit(): void {
this.inited = true;
if (this.tableQuery) {
this.onTableQuery(this.tableQuery)
}
}
reload(): void {
this.pageIndex = 1;
this.keyword = '';
this.load();
}
load(): void {
this.loading = true;
this.as.post('project/' + this.project.id + '/elements', {
offset: (this.pageIndex - 1) * this.pageSize,
length: this.pageSize,
sortKey: this.sortField,
sortOrder: this.sortOrder,
filters: this.filters,
keyword: this.keyword,
}).subscribe(res => {
this.elements = res.data;
this.total = res.total;
}, error => {
console.log('error', error);
}, () => {
this.loading = false;
});
}
create(): void {
this.router.navigate(['/admin/project/' + this.project.id + '/element/create']);
}
edit(c): void {
this.router.navigate(['/admin/project/' + this.project.id + '/element/' + c.id + '/edit']);
}
onTableQuery(params: NzTableQueryParams): void {
if (!this.inited) {
this.tableQuery = params;
return;
}
const {pageSize, pageIndex, sort, filter} = params;
this.pageSize = pageSize;
this.pageIndex = pageIndex;
const currentSort = sort.find(item => item.value !== null);
this.sortField = (currentSort && currentSort.key) || null;
this.sortOrder = (currentSort && currentSort.value) || null;
this.filters = filter;
this.load();
}
search(): void {
this.pageIndex = 1;
this.load();
}
}
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
名称
</div>
<div nz-col nzSpan="20">
<input nz-input [(ngModel)]="data.name" placeholder="名称"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
密钥
</div>
<div nz-col nzSpan="20">
<input nz-input [(ngModel)]="data.key" placeholder="密钥"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
密码
</div>
<div nz-col nzSpan="20">
<input nz-input [(ngModel)]="data.secret" placeholder="密码"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
禁用
</div>
<div nz-col nzSpan="20">
<nz-switch [(ngModel)]="data.disabled"></nz-switch>
</div>
</div>
<button nz-button nzType="primary" (click)="submit()">保存</button>
import {Component, Input, OnInit} from '@angular/core';
import {ApiService} from '../../api.service';
import {NzMessageService, NzModalRef} from 'ng-zorro-antd';
@Component({
selector: 'app-project-function-edit',
templateUrl: './project-function-edit.component.html',
styleUrls: ['./project-function-edit.component.scss']
})
export class ProjectFunctionEditComponent implements OnInit {
target = 'project/function';
@Input() id = 0;
data: any = {};
constructor(private as: ApiService, private mr: NzModalRef, private ms: NzMessageService) {
}
ngOnInit(): void {
if (this.id > 0) {
this.as.get(this.target + '/' + this.id).subscribe(res => {
this.data = res.data;
});
}
}
submit(): void {
let uri = this.target;
if (this.data.id) {
uri += '/' + this.data.id;
}
this.as.post(uri, this.data).subscribe(res => {
if (res.ok) {
this.ms.success('保存成功');
this.mr.close(res.data);
}
});
}
}
<nz-space>
<nz-space-item>
<button nz-button (click)="load()" [nzLoading]="loading">
<i nz-icon nzType="reload"></i>
刷新
</button>
</nz-space-item>
<nz-space-item>
<button nz-button (click)="edit()">
<i nz-icon nzType="plus"></i>
创建
</button>
</nz-space-item>
<nz-space-item>
<nz-input-group nzSearch nzCompact [nzAddOnAfter]="suffixButton">
<input type="text" nz-input [(ngModel)]="keyword" placeholder="名称、地址"/>
</nz-input-group>
<ng-template #suffixButton>
<button nz-button nzSearch (click)="search()">搜索</button>
</ng-template>
</nz-space-item>
</nz-space>
<nz-table #tbl [nzData]="datum" nzTableLayout="fixed" [nzFrontPagination]="false" [nzPageSize]="pageSize"
[nzPageIndex]="pageIndex" [nzTotal]="total" [nzLoading]="loading" (nzQueryParams)="onTableQuery($event)">
<thead>
<tr>
<th nzColumnKey="ID" [nzSortFn]="true">ID</th>
<th nzColumnKey="ProjectId" [nzSortFn]="true">项目ID</th>
<th nzColumnKey="Name" [nzSortFn]="true">名称</th>
<th nzColumnKey="Cron" [nzSortFn]="true">定时</th>
<th nzColumnKey="Script" [nzSortFn]="true">脚本</th>
<th nzColumnKey="Created" [nzSortFn]="true">创建时间</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of tbl.data">
<td>{{ data.id }}</td>
<td>{{ data.project_id }}</td>
<td>{{ data.name }}</td>
<td>{{ data.cron }}</td>
<td>{{ data.script }}</td>
<td>{{ data.created | amDateFormat:'YYYY-MM-DD HH:mm:ss' }}</td>
<td>
<a (click)="edit(data.id)" title="编辑">
<i nz-icon nzType="edit"></i>
</a>
</td>
</tr>
</tbody>
</nz-table>
import {Component, Input, OnInit} from '@angular/core';
import {ApiService} from '../../api.service';
import {NzModalService, NzTableQueryParams} from 'ng-zorro-antd';
import {Router} from '@angular/router';
import {ProjectFunctionEditComponent} from '../project-function-edit/project-function-edit.component';
@Component({
selector: 'app-project-function',
templateUrl: './project-function.component.html',
styleUrls: ['./project-function.component.scss']
})
export class ProjectFunctionComponent implements OnInit {
@Input() project: any = {};
inited = false;
tableQuery: any;
datum: any[];
total = 0;
pageIndex = 1;
pageSize = 10;
sortField = null;
sortOrder = null;
filters = [];
keyword = '';
loading = false;
constructor(private as: ApiService, private router: Router, private ms: NzModalService) {
}
ngOnInit(): void {
this.inited = true;
if (this.tableQuery) {
this.onTableQuery(this.tableQuery);
}
}
reload(): void {
this.pageIndex = 1;
this.keyword = '';
this.load();
}
load(): void {
this.loading = true;
this.as.post('project/' + this.project.id + '/functions', {
offset: (this.pageIndex - 1) * this.pageSize,
length: this.pageSize,
sortKey: this.sortField,
sortOrder: this.sortOrder,
filters: this.filters,
keywords: [
{key: 'Name', value: this.keyword},
]
}).subscribe(res => {
this.datum = res.data;
this.total = res.total;
}, error => {
console.log('error', error);
}, () => {
this.loading = false;
});
}
edit(id?): void {
const modal = this.ms.create({
nzTitle: id ? '编辑功能' : '创建功能',
nzContent: ProjectFunctionEditComponent,
nzFooter: null,
nzMaskClosable: false,
// nzViewContainerRef: this.viewContainerRef,
nzComponentParams: {id},
});
// insert/update after close
modal.afterClose.subscribe(data => {
if (!data) {
return;
}
if (id) {
this.datum.forEach((c: any, i, a: any[]) => {
if (c.id === data.id) {
a[i] = data;
}
}
);
} else {
this.datum.unshift(data);
}
});
}
onTableQuery(params: NzTableQueryParams): void {
if (!this.inited) {
this.tableQuery = params;
return;
}
const {pageSize, pageIndex, sort, filter} = params;
this.pageSize = pageSize;
this.pageIndex = pageIndex;
const currentSort = sort.find(item => item.value !== null);
this.sortField = (currentSort && currentSort.key) || null;
this.sortOrder = (currentSort && currentSort.value) || null;
this.filters = filter;
this.load();
}
search(): void {
this.pageIndex = 1;
this.load();
}
}
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
名称
</div>
<div nz-col nzSpan="20">
<input nz-input [(ngModel)]="data.name" placeholder="名称"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
密钥
</div>
<div nz-col nzSpan="20">
<input nz-input [(ngModel)]="data.key" placeholder="密钥"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
密码
</div>
<div nz-col nzSpan="20">
<input nz-input [(ngModel)]="data.secret" placeholder="密码"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
禁用
</div>
<div nz-col nzSpan="20">
<nz-switch [(ngModel)]="data.disabled"></nz-switch>
</div>
</div>
<button nz-button nzType="primary" (click)="submit()">保存</button>
import {Component, Input, OnInit} from '@angular/core';
import {ApiService} from '../../api.service';
import {NzMessageService, NzModalRef} from 'ng-zorro-antd';
@Component({
selector: 'app-project-job-edit',
templateUrl: './project-job-edit.component.html',
styleUrls: ['./project-job-edit.component.scss']
})
export class ProjectJobEditComponent implements OnInit {
target = 'project/job';
@Input() id = 0;
data: any = {};
constructor(private as: ApiService, private mr: NzModalRef, private ms: NzMessageService) {
}
ngOnInit(): void {
if (this.id > 0) {
this.as.get(this.target + '/' + this.id).subscribe(res => {
this.data = res.data;
});
}
}
submit(): void {
let uri = this.target;
if (this.data.id) {
uri += '/' + this.data.id;
}
this.as.post(uri, this.data).subscribe(res => {
if (res.ok) {
this.ms.success('保存成功');
this.mr.close(res.data);
}
});
}
}
<nz-space>
<nz-space-item>
<button nz-button (click)="load()" [nzLoading]="loading">
<i nz-icon nzType="reload"></i>
刷新
</button>
</nz-space-item>
<nz-space-item>
<button nz-button (click)="edit()">
<i nz-icon nzType="plus"></i>
创建
</button>
</nz-space-item>
<nz-space-item>
<nz-input-group nzSearch nzCompact [nzAddOnAfter]="suffixButton">
<input type="text" nz-input [(ngModel)]="keyword" placeholder="名称、地址"/>
</nz-input-group>
<ng-template #suffixButton>
<button nz-button nzSearch (click)="search()">搜索</button>
</ng-template>
</nz-space-item>
</nz-space>
<nz-table #tbl [nzData]="datum" nzTableLayout="fixed" [nzFrontPagination]="false" [nzPageSize]="pageSize"
[nzPageIndex]="pageIndex" [nzTotal]="total" [nzLoading]="loading" (nzQueryParams)="onTableQuery($event)">
<thead>
<tr>
<th nzColumnKey="ID" [nzSortFn]="true">ID</th>
<th nzColumnKey="ProjectId" [nzSortFn]="true">项目ID</th>
<th nzColumnKey="Name" [nzSortFn]="true">名称</th>
<th nzColumnKey="Cron" [nzSortFn]="true">定时</th>
<th nzColumnKey="Script" [nzSortFn]="true">脚本</th>
<th nzColumnKey="Created" [nzSortFn]="true">创建时间</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of tbl.data">
<td>{{ data.id }}</td>
<td>{{ data.project_id }}</td>
<td>{{ data.name }}</td>
<td>{{ data.cron }}</td>
<td>{{ data.script }}</td>
<td>{{ data.created | amDateFormat:'YYYY-MM-DD HH:mm:ss' }}</td>
<td>
<a (click)="edit(data.id)" title="编辑">
<i nz-icon nzType="edit"></i>
</a>
</td>
</tr>
</tbody>
</nz-table>
import {Component, Input, OnInit} from '@angular/core';
import {ApiService} from '../../api.service';
import {NzModalService, NzTableQueryParams} from 'ng-zorro-antd';
import {Router} from '@angular/router';
import {ProjectJobEditComponent} from '../project-job-edit/project-job-edit.component';
@Component({
selector: 'app-project-job',
templateUrl: './project-job.component.html',
styleUrls: ['./project-job.component.scss']
})
export class ProjectJobComponent implements OnInit {
@Input() project: any = {};
inited = false;
tableQuery: any;
datum: any[];
total = 0;
pageIndex = 1;
pageSize = 10;
sortField = null;
sortOrder = null;
filters = [];
keyword = '';
loading = false;
constructor(private as: ApiService, private router: Router, private ms: NzModalService) {
}
ngOnInit(): void {
this.inited = true;
if (this.tableQuery) {
this.onTableQuery(this.tableQuery);
}
}
reload(): void {
this.pageIndex = 1;
this.keyword = '';
this.load();
}
load(): void {
this.loading = true;
this.as.post('project/' + this.project.id + '/jobs', {
offset: (this.pageIndex - 1) * this.pageSize,
length: this.pageSize,
sortKey: this.sortField,
sortOrder: this.sortOrder,
filters: this.filters,
keywords: [
{key: 'Name', value: this.keyword},
]
}).subscribe(res => {
this.datum = res.data;
this.total = res.total;
}, error => {
console.log('error', error);
}, () => {
this.loading = false;
});
}
edit(id?): void {
const modal = this.ms.create({
nzTitle: id ? '编辑任务' : '创建任务',
nzContent: ProjectJobEditComponent,
nzFooter: null,
nzMaskClosable: false,
// nzViewContainerRef: this.viewContainerRef,
nzComponentParams: {id},
});
// insert/update after close
modal.afterClose.subscribe(data => {
if (!data) {
return;
}
if (id) {
this.datum.forEach((c: any, i, a: any[]) => {
if (c.id === data.id) {
a[i] = data;
}
}
);
} else {
this.datum.unshift(data);
}
});
}
onTableQuery(params: NzTableQueryParams): void {
if (!this.inited) {
this.tableQuery = params;
return;
}
const {pageSize, pageIndex, sort, filter} = params;
this.pageSize = pageSize;
this.pageIndex = pageIndex;
const currentSort = sort.find(item => item.value !== null);
this.sortField = (currentSort && currentSort.key) || null;
this.sortOrder = (currentSort && currentSort.value) || null;
this.filters = filter;
this.load();
}
search(): void {
this.pageIndex = 1;
this.load();
}
}
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-project-link-edit',
templateUrl: './project-link-edit.component.html',
styleUrls: ['./project-link-edit.component.scss']
})
export class ProjectLinkEditComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
import {Component, Input, OnInit} from '@angular/core';
@Component({
selector: 'app-project-link',
templateUrl: './project-link.component.html',
styleUrls: ['./project-link.component.scss']
})
export class ProjectLinkComponent implements OnInit {
@Input() project: any = {};
constructor() { }
ngOnInit(): void {
}
}
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
名称
</div>
<div nz-col nzSpan="20">
<input nz-input [(ngModel)]="data.name" placeholder="名称"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
密钥
</div>
<div nz-col nzSpan="20">
<input nz-input [(ngModel)]="data.key" placeholder="密钥"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
密码
</div>
<div nz-col nzSpan="20">
<input nz-input [(ngModel)]="data.secret" placeholder="密码"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
禁用
</div>
<div nz-col nzSpan="20">
<nz-switch [(ngModel)]="data.disabled"></nz-switch>
</div>
</div>
<button nz-button nzType="primary" (click)="submit()">保存</button>
import {Component, Input, OnInit} from '@angular/core';
import {ApiService} from '../../api.service';
import {NzMessageService, NzModalRef} from 'ng-zorro-antd';
@Component({
selector: 'app-project-strategy-edit',
templateUrl: './project-strategy-edit.component.html',
styleUrls: ['./project-strategy-edit.component.scss']
})
export class ProjectStrategyEditComponent implements OnInit {
target = 'project/strategy';
@Input() id = 0;
data: any = {};
constructor(private as: ApiService, private mr: NzModalRef, private ms: NzMessageService) {
}
ngOnInit(): void {
if (this.id > 0) {
this.as.get(this.target + '/' + this.id).subscribe(res => {
this.data = res.data;
});
}
}
submit(): void {
let uri = this.target;
if (this.data.id) {
uri += '/' + this.data.id;
}
this.as.post(uri, this.data).subscribe(res => {
if (res.ok) {
this.ms.success('保存成功');
this.mr.close(res.data);
}
});
}
}
<nz-space>
<nz-space-item>
<button nz-button (click)="load()" [nzLoading]="loading">
<i nz-icon nzType="reload"></i>
刷新
</button>
</nz-space-item>
<nz-space-item>
<button nz-button (click)="edit()">
<i nz-icon nzType="plus"></i>
创建
</button>
</nz-space-item>
<nz-space-item>
<nz-input-group nzSearch nzCompact [nzAddOnAfter]="suffixButton">
<input type="text" nz-input [(ngModel)]="keyword" placeholder="名称、地址"/>
</nz-input-group>
<ng-template #suffixButton>
<button nz-button nzSearch (click)="search()">搜索</button>
</ng-template>
</nz-space-item>
</nz-space>
<nz-table #tbl [nzData]="datum" nzTableLayout="fixed" [nzFrontPagination]="false" [nzPageSize]="pageSize"
[nzPageIndex]="pageIndex" [nzTotal]="total" [nzLoading]="loading" (nzQueryParams)="onTableQuery($event)">
<thead>
<tr>
<th nzColumnKey="ID" [nzSortFn]="true">ID</th>
<th nzColumnKey="ProjectId" [nzSortFn]="true">项目ID</th>
<th nzColumnKey="Name" [nzSortFn]="true">名称</th>
<th nzColumnKey="Script" [nzSortFn]="true">脚本</th>
<th nzColumnKey="Created" [nzSortFn]="true">创建时间</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of tbl.data">
<td>{{ data.id }}</td>
<td>{{ data.project_id }}</td>
<td>{{ data.name }}</td>
<td>{{ data.script }}</td>
<td>{{ data.created | amDateFormat:'YYYY-MM-DD HH:mm:ss' }}</td>
<td>
<a (click)="edit(data.id)" title="编辑">
<i nz-icon nzType="edit"></i>
</a>
</td>
</tr>
</tbody>
</nz-table>
import {Component, Input, OnInit} from '@angular/core';
import {ApiService} from '../../api.service';
import {NzModalService, NzTableQueryParams} from 'ng-zorro-antd';
import {Router} from '@angular/router';
import {ProjectStrategyEditComponent} from '../project-strategy-edit/project-strategy-edit.component';
@Component({
selector: 'app-project-strategy',
templateUrl: './project-strategy.component.html',
styleUrls: ['./project-strategy.component.scss']
})
export class ProjectStrategyComponent implements OnInit {
@Input() project: any = {};
inited = false;
tableQuery: any;
datum: any[];
total = 0;
pageIndex = 1;
pageSize = 10;
sortField = null;
sortOrder = null;
filters = [];
keyword = '';
loading = false;
constructor(private as: ApiService, private router: Router, private ms: NzModalService) {
}
ngOnInit(): void {
this.inited = true;
if (this.tableQuery) {
this.onTableQuery(this.tableQuery);
}
}
reload(): void {
this.pageIndex = 1;
this.keyword = '';
this.load();
}
load(): void {
this.loading = true;
this.as.post('project/' + this.project.id + '/strategies', {
offset: (this.pageIndex - 1) * this.pageSize,
length: this.pageSize,
sortKey: this.sortField,
sortOrder: this.sortOrder,
filters: this.filters,
keywords: [
{key: 'Name', value: this.keyword},
]
}).subscribe(res => {
this.datum = res.data;
this.total = res.total;
}, error => {
console.log('error', error);
}, () => {
this.loading = false;
});
}
edit(id?): void {
const modal = this.ms.create({
nzTitle: id ? '编辑策略' : '创建策略',
nzContent: ProjectStrategyEditComponent,
nzFooter: null,
nzMaskClosable: false,
// nzViewContainerRef: this.viewContainerRef,
nzComponentParams: {id},
});
// insert/update after close
modal.afterClose.subscribe(data => {
if (!data) {
return;
}
if (id) {
this.datum.forEach((c: any, i, a: any[]) => {
if (c.id === data.id) {
a[i] = data;
}
}
);
} else {
this.datum.unshift(data);
}
});
}
onTableQuery(params: NzTableQueryParams): void {
if (!this.inited) {
this.tableQuery = params;
return;
}
const {pageSize, pageIndex, sort, filter} = params;
this.pageSize = pageSize;
this.pageIndex = pageIndex;
const currentSort = sort.find(item => item.value !== null);
this.sortField = (currentSort && currentSort.key) || null;
this.sortOrder = (currentSort && currentSort.value) || null;
this.filters = filter;
this.load();
}
search(): void {
this.pageIndex = 1;
this.load();
}
}
<nz-space>
<nz-space-item>
<button nz-button (click)="load()" [nzLoading]="loading">
<i nz-icon nzType="reload"></i>
刷新
</button>
</nz-space-item>
<nz-space-item>
<button nz-button (click)="edit()">
<i nz-icon nzType="plus"></i>
创建
</button>
</nz-space-item>
<nz-space-item>
<nz-input-group nzSearch nzCompact [nzAddOnAfter]="suffixButton">
<input type="text" nz-input [(ngModel)]="keyword" placeholder="名称、地址"/>
</nz-input-group>
<ng-template #suffixButton>
<button nz-button nzSearch (click)="search()">搜索</button>
</ng-template>
</nz-space-item>
</nz-space>
<nz-table #tbl [nzData]="datum" nzTableLayout="fixed" [nzFrontPagination]="false" [nzPageSize]="pageSize"
[nzPageIndex]="pageIndex" [nzTotal]="total" [nzLoading]="loading" (nzQueryParams)="onTableQuery($event)">
<thead>
<tr>
<th nzColumnKey="ID" [nzSortFn]="true">ID</th>
<th nzColumnKey="Name" [nzSortFn]="true">名称</th>
<th nzColumnKey="UUID" [nzSortFn]="true">UUID</th>
<th nzColumnKey="Version" [nzSortFn]="true">版本号</th>
<th nzColumnKey="Disabled" [nzSortFn]="true">状态</th>
<th nzColumnKey="Created" [nzSortFn]="true">创建时间</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of tbl.data" (dblclick)="detail(data)">
<td>{{ data.id }}</td>
<td>{{ data.name }}</td>
<td>{{ data.uuid }}</td>
<td>{{ data.version }}</td>
<td>{{ data.disabled ? '禁用' : '启用' }}</td>
<td>{{ data.created | amDateFormat:'YYYY-MM-DD HH:mm:ss' }}</td>
<td>
<a (click)="detail(data)" title="说明">
<i nz-icon nzType="eye"></i>
</a>
<a (click)="edit(data.id)" title="编辑">
<i nz-icon nzType="edit"></i>
</a>
</td>
</tr>
</tbody>
</nz-table>
import {Component, OnInit} from '@angular/core';
import {ApiService} from '../../api.service';
import {NzModalService, NzTableQueryParams} from 'ng-zorro-antd';
import {Router} from '@angular/router';
import {TabRef} from '../tabs/tabs.component';
import {ProjectEditComponent} from '../project-edit/project-edit.component';
@Component({
selector: 'app-project',
templateUrl: './project.component.html',
styleUrls: ['./project.component.scss']
})
export class ProjectComponent implements OnInit {
datum: any[];
total = 0;
pageIndex = 1;
pageSize = 10;
sortField = null;
sortOrder = null;
filters = [];
keyword = '';
loading = false;
constructor(private as: ApiService, private router: Router, private tab: TabRef, private ms: NzModalService) {
tab.name = '项目管理';
}
ngOnInit(): void {
}
reload(): void {
this.pageIndex = 1;
this.keyword = '';
this.load();
}
load(): void {
this.loading = true;
this.as.post('projects', {
offset: (this.pageIndex - 1) * this.pageSize,
length: this.pageSize,
sortKey: this.sortField,
sortOrder: this.sortOrder,
filters: this.filters,
keywords: [
{key: 'Name', value: this.keyword},
]
}).subscribe(res => {
this.datum = res.data;
this.total = res.total;
}, error => {
console.log('error', error);
}, () => {
this.loading = false;
});
}
edit(id?): void {
const modal = this.ms.create({
nzTitle: id ? '编辑项目' : '创建项目',
nzContent: ProjectEditComponent,
nzFooter: null,
nzMaskClosable: false,
// nzViewContainerRef: this.viewContainerRef,
nzComponentParams: {id},
});
// insert/update after close
modal.afterClose.subscribe(data => {
if (!data) {
return;
}
if (id) {
this.datum.forEach((c: any, i, a: any[]) => {
if (c.id === data.id) {
a[i] = data;
}
}
);
} else {
this.datum.unshift(data);
}
});
}
detail(c): void {
this.router.navigate(['/admin/project/' + c.id + '/detail']);
}
onTableQuery(params: NzTableQueryParams): void {
const {pageSize, pageIndex, sort, filter} = params;
this.pageSize = pageSize;
this.pageIndex = pageIndex;
const currentSort = sort.find(item => item.value !== null);
this.sortField = (currentSort && currentSort.key) || null;
this.sortOrder = (currentSort && currentSort.value) || null;
this.filters = filter;
this.load();
}
search(): void {
this.pageIndex = 1;
this.load();
}
}
<!--<nz-tabset nzLinkRouter nzType="card" [nzSelectedIndex]="0" (nzSelectChange)="onTabsChange($event)">-->
<!-- <nz-tab *ngFor="let tab of tabs">-->
<!-- <a nz-tab-link [routerLink]="tab.route">{{tab.name}}</a>-->
<!-- <ng-template #container></ng-template>-->
<!-- </nz-tab>-->
<!--</nz-tabset>-->
<div class="tabs">
<div class="title" *ngFor="let tab of tabs; let i=index"
[class.active]="i==current"
(click)="onTabsChange(i)"
(dblclick)="onTabClose(i)"
>
<a [routerLink]="tab.route">
{{tab.name || tab.route}}
</a>
<i nz-icon nzType="close" nzTheme="outline" (click)="onTabClose(i)" *ngIf="tabs.length>1"></i>
</div>
<ng-container *ngFor="let tab of tabs; let i=index">
<div class="content" [hidden]="i!=current">
<ng-template #container></ng-template>
<div *ngIf="!tab.component">加载中</div>
</div>
</ng-container>
</div>
.tabs{
.title{
display: inline-block;
border: 1px solid #d9d9d9;
border-radius: 2px;
padding: 5px 10px;
margin: 2px;
background-color: #fafafa;
font-size: 14px;
//color: rgba(0, 0, 0, 0.65);
a{
color: rgba(0, 0, 0, 0.65);
text-decoration: none;
}
i {
margin-left: 4px;
transform: scale(0.8);
&:hover{
transform: scale(1.2);
}
}
&.active{
color: white;
a {
color: white;
}
border-color: transparent;
background-color: #1890ff;
}
}
.content{
padding: 15px;
background: #fff;
//height: 100%;
}
}
import {
AfterViewInit,
Component,
ComponentFactoryResolver, ComponentRef,
Injector, OnDestroy,
OnInit,
QueryList, ViewChildren,
ViewContainerRef
} from '@angular/core';
import {ActivatedRoute, ActivationEnd, NavigationEnd, Router, RouterLinkWithHref} from '@angular/router';
import {Subscription} from 'rxjs';
import {MainComponent} from "../main.component";
@Component({
selector: 'app-tabs',
templateUrl: './tabs.component.html',
styleUrls: ['./tabs.component.scss']
})
export class TabsComponent implements OnInit, OnDestroy, AfterViewInit {
@ViewChildren(RouterLinkWithHref) links: QueryList<RouterLinkWithHref>;
@ViewChildren('container', {read: ViewContainerRef}) containers: QueryList<ViewContainerRef>;
current = 0;
tabs: Array<TabRef> = [];
event: any;
sub: Subscription;
constructor(private router: Router, private resolver: ComponentFactoryResolver, private location: ViewContainerRef) {
this.sub = router.events.subscribe((e: any) => {
// console.log('router event', e);
if (e instanceof ActivationEnd && !e.snapshot.firstChild) {
this.event = e;
return;
}
if (e instanceof NavigationEnd) {
this.checkRouter(e.url);
}
});
}
checkRouter(url): void {
if (!this.links) {
return;
}
// TODO 要判断是 /admin/ 起始
const path = url.replace(/^\/admin\//, '');
// 快速查找
let index = this.tabs.findIndex(tab => tab.route === path);
// 通过Angular路由查找
if (index < 0 && this.links) {
index = this.links.toArray().findIndex(link => this.router.isActive(link.urlTree, true));
}
if (index > -1) {
this.current = index;
return;
}
// 创建新标签
this.current = this.tabs.length;
this.tabs.push(new TabRef(path, path, this));
setTimeout(() => {
this.onTabsChange(this.tabs.length - 1);
}, 100);
}
ngAfterViewInit(): void {
this.checkRouter(this.router.url);
}
ngOnInit(): void {
}
ngOnDestroy(): void {
this.sub.unsubscribe();
this.tabs.forEach(tab => {
if (tab.component) {
tab.component.destroy();
}
});
}
onTabsChange(index): void {
this.current = index;
this.loadTab(index);
}
onTabClose(index): void {
if (this.tabs.length === 1) {
// TODO 打开默认页
return;
}
const tab = this.tabs.splice(index, 1)[0];
if (tab.component) {
tab.component.destroy();
}
if (this.current > index) {
this.current--;
} else if (this.current === index) {
if (this.current >= this.tabs.length) {
this.current = this.tabs.length - 1;
}
// 用修改路由的方式触发
this.router.navigate(['/admin/' + this.tabs[this.current].route]);
return;
}
}
loadTab(index): void {
if (this.tabs[index].component) {
// TODO setTitle
return;
}
// this.router.routerState.snapshot.root.firstChild.firstChild.firstChild...
let route: any = this.router.routerState.snapshot.root;
while (route.firstChild) {
route = route.firstChild;
}
// TODO 如果route.component为空,则找不到内容
if (!route.component) {
return;
}
// 避免初次打开,页面一直嵌套的问题,但是页面会一直处于加载中
if (route.component === MainComponent) {
return;
}
const factory = this.resolver.resolveComponentFactory(route.component);
const injector = new TabsInjector(this.event, this.location.injector, this.tabs[index]);
const container = this.containers.toArray()[index];
container.clear();
this.tabs[index].component = container.createComponent(factory, this.location.length, injector);
// TODO setTitle
this.tabs[index].component.instance.closeTab = () => {
this.onTabClose(index);
};
}
}
export class TabRef {
name: string;
route: string;
component: ComponentRef<any>;
tabs: TabsComponent;
constructor(name, route, tabs) {
this.name = name;
this.route = route;
this.tabs = tabs;
}
Close(): void {
const index = this.tabs.tabs.findIndex(v => v === this);
this.tabs.onTabClose(index);
}
}
class TabsInjector implements Injector {
constructor(private route: ActivatedRoute, private parent: Injector, private ref: TabRef) {
}
get(token: any, notFoundValue?: any): any {
if (token === ActivatedRoute) {
return this.route;
}
if (token === TabRef) {
return this.ref;
}
return this.parent.get(token, notFoundValue);
}
}
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-template-detail',
templateUrl: './template-detail.component.html',
styleUrls: ['./template-detail.component.scss']
})
export class TemplateDetailComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-template-edit',
templateUrl: './template-edit.component.html',
styleUrls: ['./template-edit.component.scss']
})
export class TemplateEditComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-template',
templateUrl: './template.component.html',
styleUrls: ['./template.component.scss']
})
export class TemplateComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
名称
</div>
<div nz-col nzSpan="20">
<input nz-input [(ngModel)]="data.name" placeholder="名称"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
网络
</div>
<div nz-col nzSpan="20">
<nz-input-group style="display: flex">
<nz-select [(ngModel)]="data.type" style="width: 120px">
<nz-option nzLabel="TCP服务端" nzValue="tcp-server"></nz-option>
<nz-option nzLabel="TCP客户端" nzValue="tcp-client"></nz-option>
<nz-option nzLabel="UDP服务端" nzValue="udp-server"></nz-option>
<nz-option nzLabel="UDP客户端" nzValue="udp-client"></nz-option>
<nz-option nzLabel="串口" nzValue="serial"></nz-option>
</nz-select>
<input nz-input [(ngModel)]="data.addr" placeholder=":1843" style="flex: 1;">
</nz-input-group>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
禁用
</div>
<div nz-col nzSpan="20">
<nz-switch [(ngModel)]="data.disabled"></nz-switch>
</div>
</div>
<div *ngIf="data.type=='tcp-server' || data.type=='udp-server'">
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
注册包
</div>
<div nz-col nzSpan="20">
<nz-switch [(ngModel)]="data.register_enable"></nz-switch>
</div>
</div>
<div *ngIf="data.register_enable">
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="6" nzOffset="2">
正则表达式
</div>
<div nz-col nzSpan="16">
<input nz-input [(ngModel)]="data.register_regex"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="6" nzOffset="2">
最短
</div>
<div nz-col nzSpan="16">
<nz-input-number [(ngModel)]="data.register_min" [nzMin]="0" [nzMax]="60"
[nzStep]="1"></nz-input-number>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="6" nzOffset="2">
最长
</div>
<div nz-col nzSpan="16">
<nz-input-number [(ngModel)]="data.register_max" [nzMin]="0" [nzMax]="60"
[nzStep]="1"></nz-input-number>
</div>
</div>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="4">
心跳包
</div>
<div nz-col nzSpan="20">
<nz-switch [(ngModel)]="data.heart_beat_enable"></nz-switch>
</div>
</div>
<div *ngIf="data.heart_beat_enable">
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="6" nzOffset="2">
间隔(秒)
</div>
<div nz-col nzSpan="16">
<nz-input-number [(ngModel)]="data.heart_beat_interval" [nzMin]="5" [nzMax]="60"
[nzStep]="1"></nz-input-number>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="6" nzOffset="2">
内容
</div>
<div nz-col nzSpan="16">
<input nz-input [(ngModel)]="data.heart_beat_content"/>
</div>
</div>
<div class="item" nz-row nzAlign="middle">
<div nz-col nzSpan="6" nzOffset="2">
十六进制
</div>
<div nz-col nzSpan="16">
<nz-switch [(ngModel)]="data.heart_beat_is_hex"></nz-switch>
</div>
</div>
</div>
<button nz-button nzType="primary" (click)="submit()">保存</button>
import {Component, Input, OnInit} from '@angular/core';
import {ApiService} from '../../api.service';
import {ActivatedRoute} from '@angular/router';
import {TabRef} from '../tabs/tabs.component';
import {NzMessageService, NzModalRef} from 'ng-zorro-antd';
@Component({
selector: 'app-tunnel-edit',
templateUrl: './tunnel-edit.component.html',
styleUrls: ['./tunnel-edit.component.scss']
})
export class TunnelEditComponent implements OnInit {
target = 'tunnel';
@Input() id = 0;
data: any = {type: 'tcp-server', addr: ':8888'};
constructor(private as: ApiService, private mr: NzModalRef, private ms: NzMessageService) {
}
ngOnInit(): void {
if (this.id > 0) {
this.as.get(this.target + '/' + this.id).subscribe(res => {
this.data = res.data;
});
}
}
submit(): void {
let uri = this.target;
if (this.data.id) {
uri += '/' + this.data.id;
}
this.as.post(uri, this.data).subscribe(res => {
if (res.ok) {
this.ms.success('保存成功');
this.mr.close(res.data);
}
});
}
}
<nz-space>
<nz-space-item>
<button nz-button (click)="load()" [nzLoading]="loading">
<i nz-icon nzType="reload"></i>
刷新
</button>
</nz-space-item>
<nz-space-item>
<button nz-button (click)="edit()">
<i nz-icon nzType="plus"></i>
创建
</button>
</nz-space-item>
<nz-space-item>
<nz-input-group nzSearch nzCompact [nzAddOnAfter]="suffixButton">
<input type="text" nz-input [(ngModel)]="keyword" placeholder="名称、地址"/>
</nz-input-group>
<ng-template #suffixButton>
<button nz-button nzSearch (click)="search()">搜索</button>
</ng-template>
</nz-space-item>
</nz-space>
<nz-table #tbl [nzData]="datum" nzTableLayout="fixed" [nzFrontPagination]="false" [nzPageSize]="pageSize"
[nzPageIndex]="pageIndex" [nzTotal]="total" [nzLoading]="loading" (nzQueryParams)="onTableQuery($event)">
<thead>
<tr>
<th nzColumnKey="ID" [nzSortFn]="true">ID</th>
<th nzColumnKey="Name" [nzSortFn]="true">名称</th>
<th nzColumnKey="Type" nzShowFilter [nzFilterFn]="true" [nzFilters]="netFilters">类型</th>
<th nzColumnKey="Addr" [nzSortFn]="true">地址</th>
<th nzColumnKey="Active" nzShowFilter [nzFilterFn]="true" [nzFilters]="statusFilters">状态</th>
<th nzColumnKey="Created" [nzSortFn]="true">创建时间</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of tbl.data">
<td>{{ data.id }}</td>
<td>{{ data.name }}</td>
<td>{{ data.type }}</td>
<td>{{data.addr}}</td>
<td>
{{data.disabled ? '禁用' : (data.active ? '活跃' : '-')}}
</td>
<td>{{ data.created | amDateFormat:'YYYY-MM-DD HH:mm:ss' }}</td>
<td>
<a [routerLink]="'/admin/tunnel-monitor/'+data.id" title="监控">
<i nz-icon nzType="aim"></i>
</a>
<nz-divider nzType="vertical"></nz-divider>
<a (click)="edit(data.id)" title="编辑">
<i nz-icon nzType="edit"></i>
</a>
</td>
</tr>
</tbody>
</nz-table>
import {Component, OnInit} from '@angular/core';
import {ApiService} from '../../api.service';
import {NzModalService, NzTableQueryParams} from 'ng-zorro-antd';
import {Router} from '@angular/router';
import {TabRef} from '../tabs/tabs.component';
import {TunnelEditComponent} from '../tunnel-edit/tunnel-edit.component';
@Component({
selector: 'app-tunnel',
templateUrl: './tunnel.component.html',
styleUrls: ['./tunnel.component.scss']
})
export class TunnelComponent implements OnInit {
datum: any[];
total = 0;
pageIndex = 1;
pageSize = 10;
sortField = null;
sortOrder = null;
filters = [];
keyword = '';
loading = false;
netFilters = [
{text: '串口', value: 'serial'},
{text: 'TCP服务端', value: 'tcp-server'},
{text: 'TCP客户端', value: 'tcp-client'},
{text: 'UDP服务端', value: 'udp-server'},
{text: 'UDP客户端', value: 'udp-client'},
];
statusFilters = [{text: '启动', value: true}];
constructor(private as: ApiService, private router: Router, private tab: TabRef, private ms: NzModalService) {
tab.name = '通道管理';
}
ngOnInit(): void {
this.loadFilters();
}
reload(): void {
this.pageIndex = 1;
this.keyword = '';
this.load();
}
load(): void {
this.loading = true;
this.as.post('tunnels', {
offset: (this.pageIndex - 1) * this.pageSize,
length: this.pageSize,
sortKey: this.sortField,
sortOrder: this.sortOrder,
filters: this.filters,
keywords: [
{key: 'Name', value: this.keyword},
{key: 'Type', value: this.keyword},
{key: 'Addr', value: this.keyword},
]
}).subscribe(res => {
this.datum = res.data;
this.total = res.total;
}, error => {
console.log('error', error);
}, () => {
this.loading = false;
});
}
loadFilters(): void {
// this.as.get('distinct/copy/host').subscribe(res => {
// console.log('res', res);
// this.hosts = res.data.map(h => {
// return {
// text: h.host,
// value: h.host
// };
// });
// }, error => {
// console.log('error', error);
// });
}
edit(id?): void {
const modal = this.ms.create({
nzTitle: id ? '编辑通道' : '创建通道',
nzContent: TunnelEditComponent,
nzFooter: null,
nzMaskClosable: false,
// nzViewContainerRef: this.viewContainerRef,
nzComponentParams: {id},
});
// insert/update after close
modal.afterClose.subscribe(data => {
if (!data) {
return;
}
if (id) {
this.datum.forEach((c: any, i, a: any[]) => {
if (c.id === data.id) {
a[i] = data;
}
}
);
} else {
this.datum.unshift(data);
}
});
}
onTableQuery(params: NzTableQueryParams): void {
const {pageSize, pageIndex, sort, filter} = params;
this.pageSize = pageSize;
this.pageIndex = pageIndex;
const currentSort = sort.find(item => item.value !== null);
this.sortField = (currentSort && currentSort.key) || null;
this.sortOrder = (currentSort && currentSort.value) || null;
this.filters = filter;
this.load();
}
search(): void {
this.pageIndex = 1;
this.load();
}
}
import {Injectable} from '@angular/core';
import {Observable, Subject, Subscription, Unsubscribable, using, merge} from 'rxjs';
import {filter, publish, refCount} from 'rxjs/operators';
import * as mqtt from 'mqtt';
@Injectable({
providedIn: 'root'
})
export class MqttService {
client: mqtt.MqttClient;
messages: Subject<any> = new Subject();
topics: { [key: string]: Observable<any> } = {};
constructor() {
const client = this.client = mqtt.connect('ws://127.0.0.1:8080/mqtt');
client.on('connect', data => {
console.log('mqtt connect', data);
});
client.on('message', (topic, message, packet) => {
console.log('mqtt message', topic, message);
if (packet.cmd === 'publish') {
this.messages.next(packet);
}
});
client.on('close', () => {
console.log('mqtt close');
});
client.on('offline', () => {
console.log('mqtt offline');
});
client.on('disconnect', (data) => {
console.log('mqtt disconnect');
});
client.on('error', (err) => {
console.log('mqtt error', err);
});
}
match(sub: string, topic: string): boolean {
const fs = sub.split('/');
const ts = topic.split('/');
let i = 0;
for (; i < fs.length && i < ts.length; i++) {
if (fs[i] === '#') {
return true;
} else if (fs[i] === '+' || fs[i] === ts[i]) {
// continue;
} else {
return false;
}
}
return i === fs.length && i === ts.length;
}
publish(topic: string, content: any): void {
this.client.publish(topic, content);
}
subscribe(filterString: string): Observable<any> {
if (!this.topics[filterString]) {
const rejected: Subject<any> = new Subject();
this.topics[filterString] = using(
// topics: Do the actual ref-counting MQTT subscription.
// refcount is decreased on unsubscribe.
() => {
const subscription: Subscription = new Subscription();
this.client.subscribe(filterString, {qos: 1}, (err) => {
});
subscription.add(() => {
delete this.topics[filterString];
this.client.unsubscribe(filterString);
});
return subscription;
},
// observableFactory: Create the observable that is consumed from.
// This part is not executed until the Observable returned by
// `observe` gets actually subscribed.
(subscription: Unsubscribable | void) => merge(rejected, this.messages))
.pipe(
filter((msg: any) => this.match(filterString, msg.topic)),
publish(),
refCount()
) as Observable<Buffer>;
}
return this.topics[filterString];
}
}
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-page-not-found',
templateUrl: './page-not-found.component.html',
styleUrls: ['./page-not-found.component.scss']
})
export class PageNotFoundComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
import {Component, Input, OnInit} from '@angular/core';
@Component({
selector: 'app-form-grid',
templateUrl: './form-grid.component.html',
styleUrls: ['./form-grid.component.scss']
})
export class FormGridComponent implements OnInit {
constructor() {
}
ngOnInit(): void {
}
}
<!--<div>-->
<div class="label">{{label}}</div>
<div class="content">
<ng-content></ng-content>
</div>
<!--</div>-->
<!--TODO 使用Flex,label固定宽度-->
:host {
margin-bottom: 20px;
//display: block;
display: flex;
.label {
width: 80px;
}
.content {
flex: 1;
}
}
import {Component, Input, OnInit} from '@angular/core';
@Component({
selector: 'app-form-item',
templateUrl: './form-item.component.html',
styleUrls: ['./form-item.component.scss']
})
export class FormItemComponent implements OnInit {
@Input() label = '';
constructor() {
}
ngOnInit(): void {
}
}
:host {
padding: 5px 10px;
display: flex;
//border: 1px solid red;
}
import {Component, OnInit} from '@angular/core';
@Component({
selector: 'app-toolbar',
exportAs: 'appToolbar',
template: '<ng-content></ng-content>',
styleUrls: ['./toolbar.component.scss'],
})
export class ToolbarComponent implements OnInit {
constructor() {
}
ngOnInit(): void {
}
}
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {ToolbarComponent} from './toolbar/toolbar.component';
import { FormGridComponent } from './form-grid/form-grid.component';
import {NzGridModule} from 'ng-zorro-antd';
import { FormItemComponent } from './form-item/form-item.component';
@NgModule({
declarations: [
ToolbarComponent,
FormGridComponent,
FormItemComponent,
],
exports: [
ToolbarComponent,
FormGridComponent,
FormItemComponent,
],
imports: [
CommonModule,
// NzLayoutModule,
NzGridModule,
]
})
export class UiModule {
}
<svg width="1024" height="1024" xmlns="http://www.w3.org/2000/svg" class="icon">
<defs>
<style type="text/css"/>
</defs>
<g>
<title>background</title>
<rect fill="none" id="canvas_background" height="402" width="582" y="-1" x="-1"/>
</g>
<g>
<title>Layer 1</title>
<path id="svg_1" fill="#ffffff" d="m514.78607,5.09453c-281.21791,0 -509.45274,228.23482 -509.45274,509.45273s228.23483,509.45274 509.45274,509.45274s509.45274,-228.23483 509.45274,-509.45274s-228.23483,-509.45273 -509.45274,-509.45273zm218.55522,237.40497l-79.98408,480.41393c-1.0189,6.11344 -4.58507,10.69851 -10.18905,14.26468c-3.05672,1.52836 -6.11343,2.54726 -9.6796,2.54726c-2.54727,0 -4.58508,-0.50945 -7.64179,-1.52835l-141.62786,-58.07762l-75.90846,92.21095c-3.56617,4.58507 -8.6607,7.13234 -15.28358,7.13234c-2.54727,0 -5.09453,-0.50946 -7.13234,-1.01891c-4.07562,-1.52836 -7.13234,-4.07562 -9.6796,-7.13234c-2.54727,-3.56617 -3.56617,-7.13233 -3.56617,-11.20796l0,-109.02288l270.00995,-331.14428l-334.201,289.36915l-123.28756,-50.43582c-7.64179,-3.05671 -11.71741,-8.66069 -12.22687,-17.32139c-0.50945,-8.15124 3.05672,-14.26468 10.18906,-18.3403l520.15124,-300.06766c3.05672,-2.03781 6.62289,-2.54726 10.18906,-2.54726c4.07562,0 8.15124,1.0189 11.20796,3.56616c7.64179,3.56617 10.18905,10.18906 8.66069,18.3403z"/>
</g>
</svg>
\ No newline at end of file
export const environment = {
production: true,
host: '/api/'
};
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false,
host: 'http://127.0.0.1:8080/api/'
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>IoT Admin</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import 'codemirror/mode/yaml/yaml';
import 'codemirror/mode/markdown/markdown';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
/* You can add global styles to this file, and also import other style files */
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: {
context(path: string, deep?: boolean, filter?: RegExp): {
keys(): string[];
<T>(id: string): T;
};
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
@import "../node_modules/ng-zorro-antd/ng-zorro-antd.less";
@import '~codemirror/lib/codemirror.css';
@import '~codemirror/theme/material.css';
@theme: default;
// The prefix to use on all css classes from ant.
@ant-prefix: ant;
// An override for the html selector for theme prefixes
@html-selector: html;
// -------- Colors -----------
@primary-color: @blue-6;
@info-color: @blue-6;
@success-color: @green-6;
@processing-color: @blue-6;
@error-color: @red-5;
@highlight-color: @red-5;
@warning-color: @gold-6;
@normal-color: #d9d9d9;
@white: #fff;
@black: #000;
// Color used by default to control hover and active backgrounds and for
// alert info backgrounds.
@primary-1: color(~`colorPalette('@{primary-color}', 1) `); // replace tint(@primary-color, 90%)
@primary-2: color(~`colorPalette('@{primary-color}', 2) `); // replace tint(@primary-color, 80%)
@primary-3: color(~`colorPalette('@{primary-color}', 3) `); // unused
@primary-4: color(~`colorPalette('@{primary-color}', 4) `); // unused
@primary-5: color(
~`colorPalette('@{primary-color}', 5) `
); // color used to control the text color in many active and hover states, replace tint(@primary-color, 20%)
@primary-6: @primary-color; // color used to control the text color of active buttons, don't use, use @primary-color
@primary-7: color(~`colorPalette('@{primary-color}', 7) `); // replace shade(@primary-color, 5%)
@primary-8: color(~`colorPalette('@{primary-color}', 8) `); // unused
@primary-9: color(~`colorPalette('@{primary-color}', 9) `); // unused
@primary-10: color(~`colorPalette('@{primary-color}', 10) `); // unused
// Base Scaffolding Variables
// ---
// Background color for `<body>`
@body-background: #fff;
// Base background color for most components
@component-background: #fff;
// Popover background color
@popover-background: @component-background;
@popover-customize-border-color: @border-color-split;
@font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
@code-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
@text-color: fade(@black, 65%);
@text-color-secondary: fade(@black, 45%);
@text-color-inverse: @white;
@icon-color: inherit;
@icon-color-hover: fade(@black, 75%);
@heading-color: fade(#000, 85%);
@heading-color-dark: fade(@white, 100%);
@text-color-dark: fade(@white, 85%);
@text-color-secondary-dark: fade(@white, 65%);
@text-selection-bg: @primary-color;
@font-variant-base: tabular-nums;
@font-feature-settings-base: 'tnum';
@font-size-base: 14px;
@font-size-lg: @font-size-base + 2px;
@font-size-sm: 12px;
@heading-1-size: ceil(@font-size-base * 2.71);
@heading-2-size: ceil(@font-size-base * 2.14);
@heading-3-size: ceil(@font-size-base * 1.71);
@heading-4-size: ceil(@font-size-base * 1.42);
// https://github.com/ant-design/ant-design/issues/20210
@line-height-base: 1.5715;
@border-radius-base: 2px;
@border-radius-sm: @border-radius-base;
// vertical paddings
@padding-lg: 24px; // containers
@padding-md: 16px; // small containers and buttons
@padding-sm: 12px; // Form controls and items
@padding-xs: 8px; // small items
@padding-xss: 4px; // more small
// vertical padding for all form controls
@control-padding-horizontal: @padding-sm;
@control-padding-horizontal-sm: @padding-xs;
// vertical margins
@margin-lg: 24px; // containers
@margin-md: 16px; // small containers and buttons
@margin-sm: 12px; // Form controls and items
@margin-xs: 8px; // small items
@margin-xss: 4px; // more small
// height rules
@height-base: 32px;
@height-lg: 40px;
@height-sm: 24px;
// The background colors for active and hover states for things like
// list items or table cells.
@item-active-bg: @primary-1;
@item-hover-bg: #f5f5f5;
// ICONFONT
@iconfont-css-prefix: anticon;
// LINK
@link-color: @primary-color;
@link-hover-color: color(~`colorPalette('@{link-color}', 5) `);
@link-active-color: color(~`colorPalette('@{link-color}', 7) `);
@link-decoration: none;
@link-hover-decoration: none;
@link-focus-decoration: none;
@link-focus-outline: 0;
// Animation
@ease-base-out: cubic-bezier(0.7, 0.3, 0.1, 1);
@ease-base-in: cubic-bezier(0.9, 0, 0.3, 0.7);
@ease-out: cubic-bezier(0.215, 0.61, 0.355, 1);
@ease-in: cubic-bezier(0.55, 0.055, 0.675, 0.19);
@ease-in-out: cubic-bezier(0.645, 0.045, 0.355, 1);
@ease-out-back: cubic-bezier(0.12, 0.4, 0.29, 1.46);
@ease-in-back: cubic-bezier(0.71, -0.46, 0.88, 0.6);
@ease-in-out-back: cubic-bezier(0.71, -0.46, 0.29, 1.46);
@ease-out-circ: cubic-bezier(0.08, 0.82, 0.17, 1);
@ease-in-circ: cubic-bezier(0.6, 0.04, 0.98, 0.34);
@ease-in-out-circ: cubic-bezier(0.78, 0.14, 0.15, 0.86);
@ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1);
@ease-in-quint: cubic-bezier(0.755, 0.05, 0.855, 0.06);
@ease-in-out-quint: cubic-bezier(0.86, 0, 0.07, 1);
// Border color
@border-color-base: hsv(0, 0, 85%); // base border outline a component
@border-color-split: hsv(0, 0, 94%); // split border inside a component
@border-color-inverse: @white;
@border-width-base: 1px; // width of the border for a component
@border-style-base: solid; // style of a components border
// Outline
@outline-blur-size: 0;
@outline-width: 2px;
@outline-color: @primary-color;
@outline-fade: 20%;
@background-color-light: hsv(0, 0, 98%); // background of header and selected item
@background-color-base: hsv(0, 0, 96%); // Default grey background color
// Disabled states
@disabled-color: fade(#000, 25%);
@disabled-bg: @background-color-base;
@disabled-color-dark: fade(#fff, 35%);
// Shadow
@shadow-color: rgba(0, 0, 0, 0.15);
@shadow-color-inverse: @component-background;
@box-shadow-base: @shadow-2;
@shadow-1-up: 0 -6px 16px -8px rgba(0, 0, 0, 0.08), 0 -9px 28px 0 rgba(0, 0, 0, 0.05),
0 -12px 48px 16px rgba(0, 0, 0, 0.03);
@shadow-1-down: 0 6px 16px -8px rgba(0, 0, 0, 0.08), 0 9px 28px 0 rgba(0, 0, 0, 0.05),
0 12px 48px 16px rgba(0, 0, 0, 0.03);
@shadow-1-left: -6px 0 16px -8px rgba(0, 0, 0, 0.08), -9px 0 28px 0 rgba(0, 0, 0, 0.05),
-12px 0 48px 16px rgba(0, 0, 0, 0.03);
@shadow-1-right: 6px 0 16px -8px rgba(0, 0, 0, 0.08), 9px 0 28px 0 rgba(0, 0, 0, 0.05),
12px 0 48px 16px rgba(0, 0, 0, 0.03);
@shadow-2: 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 6px 16px 0 rgba(0, 0, 0, 0.08),
0 9px 28px 8px rgba(0, 0, 0, 0.05);
// Buttons
@btn-font-weight: 400;
@btn-border-radius-base: @border-radius-base;
@btn-border-radius-sm: @border-radius-base;
@btn-border-width: @border-width-base;
@btn-border-style: @border-style-base;
@btn-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
@btn-primary-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
@btn-text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12);
@btn-primary-color: #fff;
@btn-primary-bg: @primary-color;
@btn-default-color: @text-color;
@btn-default-bg: @component-background;
@btn-default-border: @border-color-base;
@btn-danger-color: #fff;
@btn-danger-bg: @error-color;
@btn-danger-border: @error-color;
@btn-disable-color: @disabled-color;
@btn-disable-bg: @disabled-bg;
@btn-disable-border: @border-color-base;
@btn-default-ghost-color: @component-background;
@btn-default-ghost-bg: transparent;
@btn-default-ghost-border: @component-background;
@btn-font-size-lg: @font-size-lg;
@btn-font-size-sm: @font-size-base;
@btn-padding-horizontal-base: @padding-md - 1px;
@btn-padding-horizontal-lg: @btn-padding-horizontal-base;
@btn-padding-horizontal-sm: @padding-xs - 1px;
@btn-height-base: @height-base;
@btn-height-lg: @height-lg;
@btn-height-sm: @height-sm;
@btn-circle-size: @btn-height-base;
@btn-circle-size-lg: @btn-height-lg;
@btn-circle-size-sm: @btn-height-sm;
@btn-square-size: @btn-height-base;
@btn-square-size-lg: @btn-height-lg;
@btn-square-size-sm: @btn-height-sm;
@btn-square-only-icon-size: @font-size-base + 2px;
@btn-square-only-icon-size-sm: @font-size-base;
@btn-square-only-icon-size-lg: @btn-font-size-lg + 2px;
@btn-group-border: @primary-5;
@btn-link-ghost-color: @component-background;
// Checkbox
@checkbox-size: 16px;
@checkbox-color: @primary-color;
@checkbox-check-color: #fff;
@checkbox-check-bg: @checkbox-check-color;
@checkbox-border-width: @border-width-base;
// Descriptions
@descriptions-bg: #fafafa;
@descriptions-title-margin-bottom: 20px;
@descriptions-default-padding: @padding-md @padding-lg;
@descriptions-middle-padding: @padding-sm @padding-lg;
@descriptions-small-padding: @padding-xs @padding-md;
// Dropdown
@dropdown-selected-color: @primary-color;
@dropdown-menu-submenu-disabled-bg: @component-background;
// Empty
@empty-font-size: @font-size-base;
// Radio
@radio-size: 16px;
@radio-dot-color: @primary-color;
@radio-dot-disabled-color: fade(@black, 20%);
// solid text-color
@radtio-solid-checked-color: @component-background;
// Radio buttons
@radio-button-bg: @btn-default-bg;
@radio-button-checked-bg: @btn-default-bg;
@radio-button-color: @btn-default-color;
@radio-button-hover-color: @primary-5;
@radio-button-active-color: @primary-7;
@radio-disabled-button-checked-bg: tint(@black, 90%);
@radio-disabled-button-checked-color: @text-color-inverse;
// Media queries breakpoints
// Extra small screen / phone
@screen-xs: 480px;
@screen-xs-min: @screen-xs;
// Small screen / tablet
@screen-sm: 576px;
@screen-sm-min: @screen-sm;
// Medium screen / desktop
@screen-md: 768px;
@screen-md-min: @screen-md;
// Large screen / wide desktop
@screen-lg: 992px;
@screen-lg-min: @screen-lg;
// Extra large screen / full hd
@screen-xl: 1200px;
@screen-xl-min: @screen-xl;
// Extra extra large screen / large desktop
@screen-xxl: 1600px;
@screen-xxl-min: @screen-xxl;
// provide a maximum
@screen-xs-max: (@screen-sm-min - 1px);
@screen-sm-max: (@screen-md-min - 1px);
@screen-md-max: (@screen-lg-min - 1px);
@screen-lg-max: (@screen-xl-min - 1px);
@screen-xl-max: (@screen-xxl-min - 1px);
// Grid system
@grid-columns: 24;
@grid-gutter-width: 0;
// Layout
@layout-body-background: #f0f2f5;
@layout-header-background: #001529;
@layout-header-height: 64px;
@layout-header-padding: 0 50px;
@layout-header-color: @text-color;
@layout-footer-padding: 24px 50px;
@layout-footer-background: @layout-body-background;
@layout-sider-background: @layout-header-background;
@layout-trigger-height: 48px;
@layout-trigger-background: #002140;
@layout-trigger-color: #fff;
@layout-zero-trigger-width: 36px;
@layout-zero-trigger-height: 42px;
// Layout light theme
@layout-sider-background-light: #fff;
@layout-trigger-background-light: #fff;
@layout-trigger-color-light: @text-color;
// z-index list, order by `z-index`
@zindex-badge: auto;
@zindex-table-fixed: auto;
@zindex-affix: 10;
@zindex-back-top: 10;
@zindex-picker-panel: 10;
@zindex-popup-close: 10;
@zindex-modal: 1000;
@zindex-modal-mask: 1000;
@zindex-message: 1010;
@zindex-notification: 1010;
@zindex-popover: 1030;
@zindex-dropdown: 1050;
@zindex-picker: 1050;
@zindex-tooltip: 1060;
// Animation
@animation-duration-slow: 0.3s; // Modal
@animation-duration-base: 0.2s;
@animation-duration-fast: 0.1s; // Tooltip
//CollapsePanel
@collapse-panel-border-radius: @border-radius-base;
//Dropdown
@dropdown-menu-bg: @component-background;
@dropdown-vertical-padding: 5px;
@dropdown-edge-child-vertical-padding: 4px;
@dropdown-font-size: @font-size-base;
@dropdown-line-height: 22px;
// Form
// ---
@label-required-color: @highlight-color;
@label-color: @heading-color;
@form-warning-input-bg: @input-bg;
@form-item-margin-bottom: 24px;
@form-item-trailing-colon: true;
@form-vertical-label-padding: 0 0 8px;
@form-vertical-label-margin: 0;
@form-item-label-font-size: @font-size-base;
@form-item-label-height: @input-height-base;
@form-item-label-colon-margin-right: 8px;
@form-item-label-colon-margin-left: 2px;
@form-error-input-bg: @input-bg;
// Input
// ---
@input-height-base: @height-base;
@input-height-lg: @height-lg;
@input-height-sm: @height-sm;
@input-padding-horizontal: @control-padding-horizontal - 1px;
@input-padding-horizontal-base: @input-padding-horizontal;
@input-padding-horizontal-sm: @control-padding-horizontal-sm - 1px;
@input-padding-horizontal-lg: @input-padding-horizontal;
@input-padding-vertical-base: max(
round((@input-height-base - @font-size-base * @line-height-base) / 2 * 10) / 10 -
@border-width-base,
3px
);
@input-padding-vertical-sm: max(
round((@input-height-sm - @font-size-base * @line-height-base) / 2 * 10) / 10 - @border-width-base,
0
);
@input-padding-vertical-lg: ceil((@input-height-lg - @font-size-lg * @line-height-base) / 2 * 10) /
10 - @border-width-base;
@input-placeholder-color: hsv(0, 0, 75%);
@input-color: @text-color;
@input-icon-color: @input-color;
@input-border-color: @border-color-base;
@input-bg: @component-background;
@input-number-hover-border-color: @input-hover-border-color;
@input-number-handler-active-bg: #f4f4f4;
@input-number-handler-hover-bg: @primary-5;
@input-number-handler-bg: @component-background;
@input-number-handler-border-color: @border-color-base;
@input-addon-bg: @background-color-light;
@input-hover-border-color: @primary-5;
@input-disabled-bg: @disabled-bg;
@input-outline-offset: 0 0;
@input-icon-hover-color: fade(@black, 85%);
// Mentions
// ---
@mentions-dropdown-bg: @component-background;
@mentions-dropdown-menu-item-hover-bg: @mentions-dropdown-bg;
// Select
// ---
@select-border-color: @border-color-base;
@select-item-selected-font-weight: 600;
@select-dropdown-bg: @component-background;
@select-item-selected-bg: @primary-1;
@select-item-active-bg: @item-hover-bg;
@select-dropdown-vertical-padding: @dropdown-vertical-padding;
@select-dropdown-font-size: @dropdown-font-size;
@select-dropdown-line-height: @dropdown-line-height;
@select-dropdown-height: 32px;
@select-background: @component-background;
@select-clear-background: @select-background;
@select-selection-item-bg: @background-color-base;
@select-selection-item-border-color: @border-color-split;
@select-single-item-height-lg: 40px;
@select-multiple-item-height: @input-height-base - @input-padding-vertical-base * 2; // Normal 24px
@select-multiple-item-height-lg: 32px;
@select-multiple-item-spacing-half: ceil(@input-padding-vertical-base / 2);
// Cascader
// ---
@cascader-bg: @component-background;
@cascader-item-selected-bg: @primary-1;
@cascader-menu-bg: @component-background;
@cascader-menu-border-color-split: @border-color-split;
// Cascader
// ----
@cascader-dropdown-vertical-padding: @dropdown-vertical-padding;
@cascader-dropdown-edge-child-vertical-padding: @dropdown-edge-child-vertical-padding;
@cascader-dropdown-font-size: @dropdown-font-size;
@cascader-dropdown-line-height: @dropdown-line-height;
// Anchor
// ---
@anchor-bg: @component-background;
@anchor-border-color: @border-color-split;
// Tooltip
// ---
// Tooltip max width
@tooltip-max-width: 250px;
// Tooltip text color
@tooltip-color: #fff;
// Tooltip background color
@tooltip-bg: rgba(0, 0, 0, 0.75);
// Tooltip arrow width
@tooltip-arrow-width: 5px;
// Tooltip distance with trigger
@tooltip-distance: @tooltip-arrow-width - 1px + 4px;
// Tooltip arrow color
@tooltip-arrow-color: @tooltip-bg;
// Popover
// ---
// Popover body background color
@popover-bg: @component-background;
// Popover text color
@popover-color: @text-color;
// Popover maximum width
@popover-min-width: 177px;
@popover-min-height: 32px;
// Popover arrow width
@popover-arrow-width: 6px;
// Popover arrow color
@popover-arrow-color: @popover-bg;
// Popover outer arrow width
// Popover outer arrow color
@popover-arrow-outer-color: @popover-bg;
// Popover distance with trigger
@popover-distance: @popover-arrow-width + 4px;
@popover-padding-horizontal: @padding-md;
// Modal
// --
@modal-body-padding: @padding-lg;
@modal-header-bg: @component-background;
@modal-header-padding: @padding-md @padding-lg;
@modal-header-border-color-split: @border-color-split;
@modal-header-close-size: 56px;
@modal-content-bg: @component-background;
@modal-heading-color: @heading-color;
@modal-footer-bg: transparent;
@modal-footer-border-color-split: @border-color-split;
@modal-footer-padding-vertical: 10px;
@modal-footer-padding-horizontal: 16px;
@modal-mask-bg: fade(@black, 45%);
@modal-confirm-body-padding: 32px 32px 24px;
// Progress
// --
@progress-default-color: @processing-color;
@progress-remaining-color: @background-color-base;
@progress-text-color: @text-color;
@progress-radius: 100px;
@progress-steps-item-bg: #f3f3f3;
// Menu
// ---
@menu-inline-toplevel-item-height: 40px;
@menu-item-height: 40px;
@menu-item-group-height: @line-height-base;
@menu-collapsed-width: 80px;
@menu-bg: @component-background;
@menu-popup-bg: @component-background;
@menu-item-color: @text-color;
@menu-highlight-color: @primary-color;
@menu-item-active-bg: @primary-1;
@menu-item-active-border-width: 3px;
@menu-item-group-title-color: @text-color-secondary;
@menu-icon-size: @font-size-base;
@menu-icon-size-lg: @font-size-lg;
@menu-item-vertical-margin: 4px;
@menu-item-font-size: @font-size-base;
@menu-item-boundary-margin: 8px;
// dark theme
@menu-dark-color: @text-color-secondary-dark;
@menu-dark-bg: @layout-header-background;
@menu-dark-arrow-color: #fff;
@menu-dark-submenu-bg: #000c17;
@menu-dark-highlight-color: #fff;
@menu-dark-item-active-bg: @primary-color;
@menu-dark-selected-item-icon-color: @white;
@menu-dark-selected-item-text-color: @white;
@menu-dark-item-hover-bg: transparent;
// Spin
// ---
@spin-dot-size-sm: 14px;
@spin-dot-size: 20px;
@spin-dot-size-lg: 32px;
// Table
// --
@table-bg: @component-background;
@table-header-bg: @background-color-light;
@table-header-color: @heading-color;
@table-header-sort-bg: @background-color-base;
@table-body-sort-bg: #fafafa;
@table-row-hover-bg: @item-hover-bg;
@table-selected-row-color: inherit;
@table-selected-row-bg: @primary-1;
@table-body-selected-sort-bg: @table-selected-row-bg;
@table-selected-row-hover-bg: @table-selected-row-bg;
@table-expanded-row-bg: #fbfbfb;
@table-padding-vertical: 16px;
@table-padding-horizontal: 16px;
@table-padding-vertical-md: @table-padding-vertical * 3 / 4;
@table-padding-horizontal-md: @table-padding-horizontal / 2;
@table-padding-vertical-sm: @table-padding-vertical / 2;
@table-padding-horizontal-sm: @table-padding-horizontal / 2;
@table-border-radius-base: @border-radius-base;
@table-footer-bg: @background-color-light;
@table-footer-color: @heading-color;
@table-header-bg-sm: @table-header-bg;
// Sorter
// Legacy: `table-header-sort-active-bg` is used for hover not real active
@table-header-sort-active-bg: darken(@table-header-bg, 3%);
// Filter
@table-header-filter-active-bg: darken(@table-header-sort-active-bg, 5%);
@table-filter-btns-bg: inherit;
@table-filter-dropdown-bg: @component-background;
@table-expand-icon-bg: @component-background;
// Tag
// --
@tag-default-bg: @background-color-light;
@tag-default-color: @text-color;
@tag-font-size: @font-size-sm;
// TimePicker
// ---
@picker-bg: @component-background;
@picker-basic-cell-hover-color: @item-hover-bg;
@picker-basic-cell-active-with-range-color: @primary-1;
@picker-basic-cell-hover-with-range-color: lighten(@primary-color, 35%);
@picker-basic-cell-disabled-bg: @disabled-bg;
@picker-border-color: @border-color-split;
@picker-date-hover-range-border-color: lighten(@primary-color, 20%);
@picker-date-hover-range-color: @picker-basic-cell-hover-with-range-color;
@picker-time-panel-cell-height: 28px;
// Calendar
// ---
@calendar-bg: @component-background;
@calendar-input-bg: @input-bg;
@calendar-border-color: @border-color-inverse;
@calendar-item-active-bg: @item-active-bg;
@calendar-full-bg: @calendar-bg;
@calendar-full-panel-bg: @calendar-full-bg;
// Carousel
// ---
@carousel-dot-width: 16px;
@carousel-dot-height: 3px;
@carousel-dot-active-width: 24px;
// Badge
// ---
@badge-height: 20px;
@badge-dot-size: 6px;
@badge-font-size: @font-size-sm;
@badge-font-weight: normal;
@badge-status-size: 6px;
@badge-text-color: @component-background;
// Rate
// ---
@rate-star-color: @yellow-6;
@rate-star-bg: @border-color-split;
// Card
// ---
@card-head-color: @heading-color;
@card-head-background: transparent;
@card-head-font-size: @font-size-lg;
@card-head-font-size-sm: @font-size-base;
@card-head-padding: 16px;
@card-head-padding-sm: @card-head-padding / 2;
@card-head-height: 48px;
@card-head-height-sm: 36px;
@card-inner-head-padding: 12px;
@card-padding-base: 24px;
@card-padding-base-sm: @card-padding-base / 2;
@card-actions-background: @background-color-light;
@card-actions-li-margin: 12px 0;
@card-skeleton-bg: #cfd8dc;
@card-background: @component-background;
@card-shadow: 0 1px 2px -2px rgba(0, 0, 0, 0.16), 0 3px 6px 0 rgba(0, 0, 0, 0.12),
0 5px 12px 4px rgba(0, 0, 0, 0.09);
@card-radius: @border-radius-base;
@card-head-tabs-margin-bottom: -17px;
// Comment
// ---
@comment-bg: inherit;
@comment-padding-base: @padding-md 0;
@comment-nest-indent: 44px;
@comment-font-size-base: @font-size-base;
@comment-font-size-sm: @font-size-sm;
@comment-author-name-color: @text-color-secondary;
@comment-author-time-color: #ccc;
@comment-action-color: @text-color-secondary;
@comment-action-hover-color: #595959;
@comment-actions-margin-bottom: inherit;
@comment-actions-margin-top: @margin-sm;
@comment-content-detail-p-margin-bottom: inherit;
// Tabs
// ---
@tabs-card-head-background: @background-color-light;
@tabs-card-height: 40px;
@tabs-card-active-color: @primary-color;
@tabs-title-font-size: @font-size-base;
@tabs-title-font-size-lg: @font-size-lg;
@tabs-title-font-size-sm: @font-size-base;
@tabs-ink-bar-color: @primary-color;
@tabs-bar-margin: 0 0 @margin-md 0;
@tabs-horizontal-margin: 0 32px 0 0;
@tabs-horizontal-margin-rtl: 0 0 0 32px;
@tabs-horizontal-padding: @padding-sm 0;
@tabs-horizontal-padding-lg: @padding-md 0;
@tabs-horizontal-padding-sm: @padding-xs 0;
@tabs-vertical-padding: @padding-xs @padding-lg;
@tabs-vertical-margin: 0 0 @margin-md 0;
@tabs-scrolling-size: 32px;
@tabs-highlight-color: @primary-color;
@tabs-hover-color: @primary-5;
@tabs-active-color: @primary-7;
@tabs-card-gutter: 2px;
@tabs-card-tab-active-border-top: 2px solid transparent;
// BackTop
// ---
@back-top-color: #fff;
@back-top-bg: @text-color-secondary;
@back-top-hover-bg: @text-color;
// Avatar
// ---
@avatar-size-base: 32px;
@avatar-size-lg: 40px;
@avatar-size-sm: 24px;
@avatar-font-size-base: 18px;
@avatar-font-size-lg: 24px;
@avatar-font-size-sm: 14px;
@avatar-bg: #ccc;
@avatar-color: #fff;
@avatar-border-radius: @border-radius-base;
// Switch
// ---
@switch-height: 22px;
@switch-sm-height: 16px;
@switch-min-width: 44px;
@switch-sm-min-width: 28px;
@switch-sm-checked-margin-left: -(@switch-sm-height - 3px);
@switch-disabled-opacity: 0.4;
@switch-color: @primary-color;
@switch-bg: @component-background;
@switch-shadow-color: fade(#00230b, 20%);
// Pagination
// ---
@pagination-item-bg: @component-background;
@pagination-item-size: @height-base;
@pagination-item-size-sm: 24px;
@pagination-font-family: Arial;
@pagination-font-weight-active: 500;
@pagination-item-bg-active: @component-background;
@pagination-item-link-bg: @component-background;
@pagination-item-disabled-color-active: @white;
@pagination-item-disabled-bg-active: darken(@disabled-bg, 10%);
@pagination-item-input-bg: @component-background;
@pagination-mini-options-size-changer-top: 0px;
// PageHeader
// ---
@page-header-padding: @padding-lg;
@page-header-padding-vertical: @padding-md;
@page-header-padding-breadcrumb: @padding-sm;
@page-header-content-padding-vertical: @padding-sm;
@page-header-back-color: #000;
@page-header-ghost-bg: inherit;
// Breadcrumb
// ---
@breadcrumb-base-color: @text-color-secondary;
@breadcrumb-last-item-color: @text-color;
@breadcrumb-font-size: @font-size-base;
@breadcrumb-icon-font-size: @font-size-base;
@breadcrumb-link-color: @text-color-secondary;
@breadcrumb-link-color-hover: @primary-5;
@breadcrumb-separator-color: @text-color-secondary;
@breadcrumb-separator-margin: 0 @padding-xs;
// Slider
// ---
@slider-margin: 10px 6px 10px;
@slider-rail-background-color: @background-color-base;
@slider-rail-background-color-hover: #e1e1e1;
@slider-track-background-color: @primary-3;
@slider-track-background-color-hover: @primary-4;
@slider-handle-border-width: 2px;
@slider-handle-background-color: @component-background;
@slider-handle-color: @primary-3;
@slider-handle-color-hover: @primary-4;
@slider-handle-color-focus: tint(@primary-color, 20%);
@slider-handle-color-focus-shadow: fade(@primary-color, 12%);
@slider-handle-color-tooltip-open: @primary-color;
@slider-handle-shadow: 0;
@slider-dot-border-color: @border-color-split;
@slider-dot-border-color-active: tint(@primary-color, 50%);
@slider-disabled-color: @disabled-color;
@slider-disabled-background-color: @component-background;
// Tree
// ---
@tree-bg: @component-background;
@tree-title-height: 24px;
@tree-child-padding: 18px;
@tree-directory-selected-color: #fff;
@tree-directory-selected-bg: @primary-color;
@tree-node-hover-bg: @item-hover-bg;
@tree-node-selected-bg: @primary-2;
// Collapse
// ---
@collapse-header-padding: @padding-sm @padding-md;
@collapse-header-padding-extra: 40px;
@collapse-header-bg: @background-color-light;
@collapse-content-padding: @padding-md;
@collapse-content-bg: @component-background;
// Skeleton
// ---
@skeleton-color: #f2f2f2;
@skeleton-to-color: shade(@skeleton-color, 5%);
@skeleton-paragraph-margin-top: 28px;
@skeleton-paragraph-li-margin-top: @margin-md;
// Transfer
// ---
@transfer-header-height: 40px;
@transfer-item-height: @height-base;
@transfer-disabled-bg: @disabled-bg;
@transfer-list-height: 200px;
@transfer-item-hover-bg: @item-hover-bg;
@transfer-item-padding-vertical: 6px;
@transfer-list-search-icon-top: 12px;
// Message
// ---
@message-notice-content-padding: 10px 16px;
@message-notice-content-bg: @component-background;
// Motion
// ---
@wave-animation-width: 6px;
// Alert
// ---
@alert-success-border-color: ~`colorPalette('@{success-color}', 3) `;
@alert-success-bg-color: ~`colorPalette('@{success-color}', 1) `;
@alert-success-icon-color: @success-color;
@alert-info-border-color: ~`colorPalette('@{info-color}', 3) `;
@alert-info-bg-color: ~`colorPalette('@{info-color}', 1) `;
@alert-info-icon-color: @info-color;
@alert-warning-border-color: ~`colorPalette('@{warning-color}', 3) `;
@alert-warning-bg-color: ~`colorPalette('@{warning-color}', 1) `;
@alert-warning-icon-color: @warning-color;
@alert-error-border-color: ~`colorPalette('@{error-color}', 3) `;
@alert-error-bg-color: ~`colorPalette('@{error-color}', 1) `;
@alert-error-icon-color: @error-color;
@alert-message-color: @heading-color;
@alert-text-color: @text-color;
@alert-close-color: @text-color-secondary;
@alert-close-hover-color: @icon-color-hover;
@alert-no-icon-padding-vertical: @padding-xs;
@alert-with-description-no-icon-padding-vertical: @padding-md - 1px;
// List
// ---
@list-header-background: transparent;
@list-footer-background: transparent;
@list-empty-text-padding: @padding-md;
@list-item-padding: @padding-sm 0;
@list-item-padding-sm: @padding-xs @padding-md;
@list-item-padding-lg: 16px 24px;
@list-item-meta-margin-bottom: @padding-md;
@list-item-meta-avatar-margin-right: @padding-md;
@list-item-meta-title-margin-bottom: @padding-sm;
@list-customize-card-bg: @component-background;
@list-item-meta-description-font-size: @font-size-base;
// Statistic
// ---
@statistic-title-font-size: @font-size-base;
@statistic-content-font-size: 24px;
@statistic-unit-font-size: 16px;
@statistic-font-family: @font-family;
// Drawer
// ---
@drawer-header-padding: @padding-md @padding-lg;
@drawer-body-padding: @padding-lg;
@drawer-bg: @component-background;
@drawer-footer-padding-vertical: @modal-footer-padding-vertical;
@drawer-footer-padding-horizontal: @modal-footer-padding-horizontal;
@drawer-header-close-size: 56px;
// Timeline
// ---
@timeline-width: 2px;
@timeline-color: @border-color-split;
@timeline-dot-border-width: 2px;
@timeline-dot-color: @primary-color;
@timeline-dot-bg: @component-background;
@timeline-item-padding-bottom: 20px;
// Typography
// ---
@typography-title-font-weight: 600;
@typography-title-margin-top: 1.2em;
@typography-title-margin-bottom: 0.5em;
// Upload
// ---
@upload-actions-color: @text-color-secondary;
// Steps
// ---
@process-tail-color: @border-color-split;
@steps-nav-arrow-color: fade(@black, 25%);
@steps-background: @component-background;
@steps-icon-size: 32px;
@steps-icon-custom-size: @steps-icon-size;
@steps-icon-custom-top: 0px;
@steps-icon-custom-font-size: 24px;
@steps-icon-top: -1px;
@steps-icon-font-size: @font-size-lg;
@steps-icon-margin: 0 8px 0 0;
@steps-title-line-height: @height-base;
@steps-small-icon-size: 24px;
@steps-small-icon-margin: 0 8px 0 0;
@steps-dot-size: 8px;
@steps-dot-top: 2px;
@steps-current-dot-size: 10px;
@steps-desciption-max-width: 140px;
@steps-nav-content-max-width: auto;
// Notification
// ---
@notification-bg: @component-background;
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": [
"src/main.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.d.ts"
]
}
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "es2015",
"module": "es2020",
"lib": [
"es2018",
"dom"
]
}
}
/*
This is a "Solution Style" tsconfig.json file, and is used by editors and TypeScript’s language server to improve development experience.
It is not intended to be used to perform a compilation.
To learn more about this file see: https://angular.io/config/solution-tsconfig.
*/
{
"files": [],
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine"
]
},
"files": [
"src/test.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}
{
"extends": "tslint:recommended",
"rules": {
"align": {
"options": [
"parameters",
"statements"
]
},
"array-type": false,
"arrow-return-shorthand": true,
"curly": true,
"deprecation": {
"severity": "warning"
},
"component-class-suffix": true,
"contextual-lifecycle": true,
"directive-class-suffix": true,
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
],
"eofline": true,
"import-blacklist": [
true,
"rxjs/Rx"
],
"import-spacing": true,
"indent": {
"options": [
"spaces"
]
},
"max-classes-per-file": false,
"max-line-length": [
true,
140
],
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-empty": false,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-var-requires": false,
"object-literal-key-quotes": [
true,
"as-needed"
],
"quotemark": [
true,
"single"
],
"semicolon": {
"options": [
"always"
]
},
"space-before-function-paren": {
"options": {
"anonymous": "never",
"asyncArrow": "always",
"constructor": "never",
"method": "never",
"named": "never"
}
},
"typedef": [
true,
"call-signature"
],
"typedef-whitespace": {
"options": [
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
},
{
"call-signature": "onespace",
"index-signature": "onespace",
"parameter": "onespace",
"property-declaration": "onespace",
"variable-declaration": "onespace"
}
]
},
"variable-name": {
"options": [
"ban-keywords",
"check-format",
"allow-pascal-case"
]
},
"whitespace": {
"options": [
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type",
"check-typecast"
]
},
"no-conflicting-lifecycle": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-inputs-metadata-property": true,
"no-output-native": true,
"no-output-on-prefix": true,
"no-output-rename": true,
"no-outputs-metadata-property": true,
"template-banana-in-box": true,
"template-no-negated-async": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true
},
"rulesDirectory": [
"codelyzer"
]
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册