提交 f8e4bed3 编写于 作者: 麦壳饼's avatar 麦壳饼

Merge branch 'master' of https://github.com/IoTSharp/IoTSharp

......@@ -152,6 +152,9 @@ namespace IoTSharp.Contracts
Device = 0,
Gateway = 1
}
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))]
[JsonConverter(typeof(StringEnumConverter))]
public enum GatewayType
{
Unknow=0,
......
......@@ -11,7 +11,13 @@
<nz-option nzValue="Gateway" nzLabel="网关"></nz-option>
</nz-select>
</se>
<se label="所属产品" error="所属产品" >
<nz-select formControlName="produceId" placeholder="所属产品" >
<nz-option *ngFor="let o of deviceproduce" [nzValue]="o.id" [nzLabel]="o.name"></nz-option>
</nz-select>
</se>
<se label="超时" error="超时" required>
<nz-input-number formControlName="timeout" [nzMin]="0" [nzStep]="1"></nz-input-number>
</se>
......@@ -32,8 +38,6 @@
<button nz-button type="reset" (click)="close()">取消</button>
<button nz-button nzType="primary" [disabled]="form.invalid" type="submit" [nzLoading]="submitting">保存</button>
<button nz-button nzType="primary" type="button" (click)="createcert($event)" *ngIf="data.id!=='00000000-0000-0000-0000-000000000000'&&data.identityType==='X509Certificate'" > <i nz-icon nzType="download"></i>证书生成</button>
</se>
</form>
</nz-card>
......
......@@ -32,7 +32,7 @@ export class DeviceformComponent implements OnInit {
) {}
form!: FormGroup;
submitting = false;
deviceproduce = [];
devicemodel: devicemodel[] = [];
data: deviceitem = {
......@@ -47,20 +47,26 @@ export class DeviceformComponent implements OnInit {
name: [null, [Validators.required, Validators.pattern(/^(\s+\S+\s*)*(?!\s).*$/)]],
deviceType: [null, [Validators.required]],
customerId: [null, []],
// deviceModelId: [null, []],
produceId: [null, []],
timeout: [300, []],
id: [Guid.EMPTY, []],
identityType: [Guid.EMPTY, []]
});
this._httpClient.post('api/deviceModel/index', { offset: 0, limit: 100 }).subscribe({
// this._httpClient.post('api/deviceModel/index', { offset: 0, limit: 100 }).subscribe({
// next: next => {
// this.devicemodel = next.data.rows;
// },
// error: error => {},
// complete: () => {}
// });
this._httpClient.get('api/produces/list', { offset: 0, limit: -1 }).subscribe({
next: next => {
this.devicemodel = next.data.rows;
this.deviceproduce = next.data.rows;
},
error: error => {},
complete: () => {}
});
if (this.params.id !== Guid.EMPTY) {
concat(
this._httpClient.get<appmessage<deviceitem>>('api/Devices/' + this.params.id).pipe(
......
import { NgModule } from '@angular/core';
import { SharedModule } from '@shared';
import { ProduceRoutingModule } from './produce-routing.module';
import { ProducedatadictionaryformComponent } from './producedatadictionaryform/producedatadictionaryform.component';
import { ProducedataformComponent } from './producedataform/producedataform.component';
const COMPONENTS = [
ProducedataformComponent,ProducedatadictionaryformComponent
];
@NgModule({
......@@ -12,4 +14,4 @@ const COMPONENTS = [
providers: [],
declarations: COMPONENTS
})
export class SettingsModule {}
export class ProduceModule {}
<button nz-button [nzType]="'primary'" (click)="newRow()" acl [acl-ability]="102">
<i nz-icon nzType="plus"></i>
<span>{{ 'button.new' | i18n }}</span>
</button>
<button nz-button [nzType]="'primary'" nzDanger="true" (click)="saveRow()" acl [acl-ability]="102">
<i nz-icon nzType="save"></i>
<span>{{ 'button.save' | i18n }}</span>
</button>
<st #st [data]="dictionaryData" [columns]="columns">
<ng-template st-row="keyNameTpl" let-item let-index="index">
<input *ngIf="item.edit" nz-input [ngModel]="item.keyName"
(ngModelChange)="st.setRow(index, { keyName: $event })" />
<ng-container *ngIf="!item.edit">{{ item.keyName }}</ng-container>
</ng-template>
<ng-template st-row="displayNameTpl" let-item let-index="index">
<input *ngIf="item.edit" nz-input [ngModel]="item.displayName"
(ngModelChange)="st.setRow(index, { displayName: $event })" />
<ng-container *ngIf="!item.edit">{{ item.displayName }}</ng-container>
</ng-template>
<ng-template st-row="unitTpl" let-item let-index="index">
<input *ngIf="item.edit" nz-input [ngModel]="item.unit"
(ngModelChange)="st.setRow(index, { unit: $event })" />
<ng-container *ngIf="!item.edit">{{ item.unit }}</ng-container>
</ng-template>
<ng-template st-row="unitExpressionTpl" let-item let-index="index">
<input *ngIf="item.edit" nz-input [ngModel]="item.unitExpression"
(ngModelChange)="st.setRow(index, { unitExpression: $event })" />
<ng-container *ngIf="!item.edit">{{ item.unitExpression }}</ng-container>
</ng-template>
<ng-template st-row="unitConvertTpl" let-item let-index="index">
<nz-switch [ngModel]="item.unitConvert" *ngIf="item.edit"
(ngModelChange)="st.setRow(index, { unitConvert: $event })"></nz-switch>
<ng-container *ngIf="!item.edit">{{ item.unitConvert }}</ng-container>
</ng-template>
<ng-template st-row="keyDescTpl" let-item let-index="index">
<input *ngIf="item.edit" nz-input [ngModel]="item.keyDesc"
(ngModelChange)="st.setRow(index, { keyDesc: $event })" />
<ng-container *ngIf="!item.edit">{{ item.keyDesc }}</ng-container>
</ng-template>
<ng-template st-row="defaultValueTpl" let-item let-index="index">
<input *ngIf="item.edit" nz-input [ngModel]="item.defaultValue"
(ngModelChange)="st.setRow(index, { defaultValue: $event })" />
<ng-container *ngIf="!item.edit">{{ item.defaultValue }}</ng-container>
</ng-template>
<ng-template st-row="displayTpl" let-item let-index="index">
<nz-switch [ngModel]="item.display" *ngIf="item.edit"
(ngModelChange)="st.setRow(index, { display: $event })"></nz-switch>
<ng-container *ngIf="!item.edit">{{ item.display }}</ng-container>
</ng-template>
<ng-template st-row="place0Tpl" let-item let-index="index">
<input *ngIf="item.edit" nz-input [ngModel]="item.place0"
(ngModelChange)="st.setRow(index, { place0: $event })" />
<ng-container *ngIf="!item.edit">{{ item.place0 }}</ng-container>
</ng-template>
<ng-template st-row="tagtpl" let-item let-index="index">
<input *ngIf="item.edit" nz-input [ngModel]="item.tag"
(ngModelChange)="st.setRow(index, { tag: $event })" />
<ng-container *ngIf="!item.edit">{{ item.tag }}</ng-container>
</ng-template>
<ng-template st-row="dataTypeTpl" let-item let-index="index">
<nz-select *ngIf="item.edit" [ngModel]="item.dataType"
(ngModelChange)="st.setRow(index, { dataType: $event })">
<nz-option nzValue="Boolean" nzLabel="Boolean"></nz-option>
<nz-option nzValue="String" nzLabel="String"></nz-option>
<nz-option nzValue="Long" nzLabel="Long"></nz-option>
<nz-option nzValue="Double" nzLabel="Double"></nz-option>
<nz-option nzValue="Json" nzLabel="Json"></nz-option>
<nz-option nzValue="XML" nzLabel="XML"></nz-option>
<nz-option nzValue="Binary" nzLabel="Binary"></nz-option>
<nz-option nzValue="DateTime" nzLabel="DateTime"></nz-option>
</nz-select>
<ng-container *ngIf="!item.edit">{{ item.dataType}}</ng-container>
</ng-template>
</st>
\ No newline at end of file
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ProducedatadictionaryformComponent } from './producedatadictionaryform.component';
describe('ProducedatadictionaryformComponent', () => {
let component: ProducedatadictionaryformComponent;
let fixture: ComponentFixture<ProducedatadictionaryformComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ProducedatadictionaryformComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ProducedatadictionaryformComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { STComponent, STData, STColumn } from '@delon/abc/st';
import { _HttpClient } from '@delon/theme';
import { Guid } from 'guid-typescript';
import { NzMessageService } from 'ng-zorro-antd/message';
import { appmessage } from 'src/app/models/appmessage';
@Component({
selector: 'app-producedatadictionaryform',
templateUrl: './producedatadictionaryform.component.html',
styleUrls: ['./producedatadictionaryform.component.less']
})
export class ProducedatadictionaryformComponent implements OnInit {
@Input() id: string = Guid.EMPTY;
@ViewChild('st') private st!: STComponent;
dictionaryData: STData[] = []
columns: STColumn[] = [
{ title: '字段名称', index: 'keyName', render: 'keyNameTpl' },
{ title: '字段显示名称', index: 'displayName', render: 'displayNameTpl' },
{ title: '单位', index: 'unit', render: 'unitTpl' },
{ title: '单位转换表达式', index: 'unitExpression', render: 'unitExpressionTpl' },
{ title: 'UnitConvert', index: 'unitConvert', render: 'unitConvertTpl' },
{ title: '字段备注', index: 'keyDesc', render: 'keyDescTpl' },
{ title: '默认值', index: 'defaultValue', render: 'defaultValueTpl' },
{ title: '是否显示', index: 'display', render: 'displayTpl' },
{ title: '位置名称', index: 'place0', render: 'place0Tpl' },
{ title: 'Tag', index: 'tag', render: 'tagtpl' },
{ title: '数据类型', index: 'dataType', render: 'dataTypeTpl' },
{
title: '操作',
buttons: [
{
text: `编辑`,
iif: i => !i.edit,
click: i => this.updateEdit(i, true),
},
{
text: `删除`,
iif: i => !i.edit, pop: {
title: '确认删除属性?',
okType: 'danger',
icon: 'warning',
},
click: i => this.remove(i),
},
{
text: `保存`,
iif: i => i.edit,
click: (i, v) => {
console.log(i)
this.submit(i);
},
},
{
text: `取消`,
iif: i => i.edit,
click: i => this.updateEdit(i, false),
},
],
},
];
constructor(private msg: NzMessageService, private http: _HttpClient,) {
}
ngOnInit(): void {
this.http.get<appmessage<any>>('api/produces/getProduceDictionary?produceId=' + this.id)
.subscribe({
next: next => {
this.dictionaryData = next.data;
},
error: error => {
},
complete: () => { }
});
}
private submit(i: STData): void {
this.updateEdit(i, false)
this.dictionaryData = this.st.list;
}
private remove(i: STData) {
this.st.removeRow(i); this.dictionaryData = this.st.list;
}
private updateEdit(i: STData, edit: boolean): void {
this.st.setRow(i, { edit }, { refreshSchema: true });
}
_id = 0;
newRow() {
this.dictionaryData = [{ edit: true }, ...this.dictionaryData]
}
saveRow() {
this.http.post('api/produces/editProduceDictionary', { produceId: this.id, produceDictionaryData: this.dictionaryData }).subscribe(
{
next: next => {
if (next.code === 10000) {
this.msg.success('保存成功')
}else{
this.msg.warning('保存失败:'+next.msg)
}
},
error: error => { },
complete: () => { }
}
);
}
}
<button nz-button [nzType]="'primary'" (click)="newRow()" acl [acl-ability]="102">
<i nz-icon nzType="plus"></i>
<span>{{ 'button.new' | i18n }}</span>
</button>
<button nz-button [nzType]="'primary'" nzDanger="true" (click)="saveRow()" acl [acl-ability]="102">
<i nz-icon nzType="save"></i>
<span>{{ 'button.save' | i18n }}</span>
</button>
<st #st [data]="produceData" [columns]="columns">
<ng-template st-row="ColumnTitleTpl" let-item let-index="index">
<input *ngIf="item.edit" nz-input [ngModel]="item.keyName"
(ngModelChange)="st.setRow(index, { keyName: $event })" />
<ng-container *ngIf="!item.edit">{{ item.keyName }}</ng-container>
</ng-template>
<ng-template st-row="ColumnTypeTpl" let-item let-index="index">
<nz-select *ngIf="item.edit" [ngModel]="item.dataSide"
(ngModelChange)="st.setRow(index, { dataSide: $event })">
<nz-option nzValue="AnySide" nzLabel="AnySide"></nz-option>
<nz-option nzValue="ServerSide" nzLabel="ServerSide"></nz-option>
<nz-option nzValue="ClientSide" nzLabel="ClientSide"></nz-option>
</nz-select>
<ng-container *ngIf="!item.edit">{{ item.dataSide }}</ng-container>
</ng-template>
<ng-template st-row="ColumnDataTypeTpl" let-item let-index="index">
<nz-select *ngIf="item.edit" [ngModel]="item.type"
(ngModelChange)="st.setRow(index, { type: $event })">
<nz-option nzValue="Boolean" nzLabel="Boolean"></nz-option>
<nz-option nzValue="String" nzLabel="String"></nz-option>
<nz-option nzValue="Long" nzLabel="Long"></nz-option>
<nz-option nzValue="Double" nzLabel="Double"></nz-option>
<nz-option nzValue="Json" nzLabel="Json"></nz-option>
<nz-option nzValue="XML" nzLabel="XML"></nz-option>
<nz-option nzValue="Binary" nzLabel="Binary"></nz-option>
<nz-option nzValue="DateTime" nzLabel="DateTime"></nz-option>
</nz-select>
<ng-container *ngIf="!item.edit">{{ item.type}}</ng-container>
</ng-template>
</st>
\ No newline at end of file
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ProducedataformComponent } from './producedataform.component';
describe('ProducedataformComponent', () => {
let component: ProducedataformComponent;
let fixture: ComponentFixture<ProducedataformComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ProducedataformComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ProducedataformComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { STComponent, STData, STColumn } from '@delon/abc/st';
import { _HttpClient } from '@delon/theme';
import { Guid } from 'guid-typescript';
import { NzMessageService } from 'ng-zorro-antd/message';
import { appmessage } from 'src/app/models/appmessage';
@Component({
selector: 'app-producedataform',
templateUrl: './producedataform.component.html',
styleUrls: ['./producedataform.component.less']
})
export class ProducedataformComponent implements OnInit {
@Input() id: string = Guid.EMPTY;
@ViewChild('st') private st!: STComponent;
produceData: STData[] = []
columns: STColumn[] = [
{ title: 'KeyName', index: 'keyName', render: 'ColumnTitleTpl' },
{ title: '数据侧', index: 'dataSide', render: 'ColumnTypeTpl' },
{ title: '数据类型', index: 'type', render: 'ColumnDataTypeTpl' },
{
title: '操作',
buttons: [
{
text: `编辑`,
iif: i => !i.edit,
click: i => this.updateEdit(i, true),
},
{
text: `删除`,
iif: i => !i.edit, pop: {
title: '确认删除属性?',
okType: 'danger',
icon: 'warning',
},
click: i => this.remove(i),
},
{
text: `保存`,
iif: i => i.edit,
click: (i, v) => {
console.log(i)
this.submit(i);
},
},
{
text: `取消`,
iif: i => i.edit,
click: i => this.updateEdit(i, false),
},
],
},
];
constructor(private msg: NzMessageService, private http: _HttpClient,) {
}
ngOnInit(): void {
this.http.get<appmessage<any>>('api/produces/GetProduceData?produceId=' + this.id).subscribe(next => {
this.produceData = next.data;
}, error => {
}, () => { });
}
private submit(i: STData): void {
this.updateEdit(i, false)
this.produceData = this.st.list;
}
private remove(i: STData) {
this.st.removeRow(i); this.produceData = this.st.list;
}
private updateEdit(i: STData, edit: boolean): void {
this.st.setRow(i, { edit }, { refreshSchema: true });
}
_id = 0;
newRow() {
this.produceData = [{ edit: true }, ...this.produceData]
}
saveRow() {
this.http.post('api/produces/editProduceData', { produceId: this.id, produceData: this.produceData }).subscribe(next => {
if (next.code === 10000) {
this.msg.success('保存成功')
}else{
this.msg.warning('保存失败:'+next.msg)
}}, error => { }, () => { });
}
}
......@@ -5,19 +5,45 @@
<se label="名称" error="名称不能为空或者空白字符" required>
<input nz-input formControlName="name" placeholder="名称" />
</se>
<se label="设备类型" error="设备类型" required>
<nz-select formControlName="deviceType" placeholder="设备类型">
<nz-option nzValue="Device" nzLabel="设备"></nz-option>
<nz-option nzValue="Gateway" nzLabel="网关"></nz-option>
<se label="名称" error="图标" required>
<input nz-input formControlName="icon" placeholder="图标" />
</se>
<se label="默认网关类型" error="默认网关类型" required>
<nz-select formControlName="gatewayType" placeholder="默认网关类型">
<nz-option nzValue="Unknow" nzLabel="Unknow"></nz-option>
<nz-option nzValue="Customize" nzLabel="Customize"></nz-option>
<nz-option nzValue="Modbus" nzLabel="Modbus"></nz-option>
<nz-option nzValue="Bacnet" nzLabel="Bacnet"></nz-option>
<nz-option nzValue="OPC_UA" nzLabel="OPC_UA"></nz-option>
<nz-option nzValue="CanOpen" nzLabel="CanOpen"></nz-option>
</nz-select>
</se>
<se label="默认网关类型" error="默认网关类型" *ngIf="this.form.value.gatewayType==='Customize'">
<nz-code-editor formControlName="gatewayConfigurationJson" [class.full-screen]="fullScreen"
style="width: 100%; height: 500px; padding-top: 1rem" class="editor"
[nzEditorOption]="{ theme: 'vs',language: 'json' }">
</nz-code-editor>
</se>
<se label="默认网关类型" error="默认网关类型" *ngIf="this.form.value.gatewayType!=='Unknow'&&this.form.value.gatewayType!=='Customize'">
<input nz-input formControlName="gatewayConfigurationName" placeholder="名称" />
</se>
<se label="默认超时" error="默认超时" required>
<nz-select formControlName="defaultDeviceType" placeholder="默认网关类型">
<nz-option nzValue="Device" nzLabel="Device"></nz-option>
<nz-option nzValue="Gateway" nzLabel="Gateway"></nz-option>
</nz-select>
</se>
<se label="超时" error="超时" required>
<nz-input-number formControlName="timeout" [nzMin]="0" [nzStep]="1"></nz-input-number>
<se label="默认超时" error="默认超时" required>
<nz-input-number formControlName="defaultTimeout" [nzMin]="0" [nzStep]="1"></nz-input-number>
</se>
<se label="认证方式" error="认证方式" required>
<nz-select formControlName="identityType" placeholder="设备类型">
<se label="默认认证方式" error="认证方式" required>
<nz-select formControlName="defaultIdentityType" placeholder="默认认证方式">
<nz-option nzValue="AccessToken" nzLabel="AccessToken"></nz-option>
<nz-option nzValue="X509Certificate" nzLabel="X509Certificate"></nz-option>
</nz-select>
......@@ -28,6 +54,9 @@
<button nz-button nzType="primary" [disabled]="form.invalid" type="submit" [nzLoading]="submitting">保存</button>
</se>
</form>
</nz-card>
</page-header>
\ No newline at end of file
.editor {
height: 100%;
}
.full-screen {
position: fixed;
z-index: 999;
height: 100%;
left: 0;
top: 0;
bottom: 0;
right: 0;
}
nz-select{
width: 100%;
}
.exgroup{
width: 100%;
}
\ No newline at end of file
......@@ -14,10 +14,10 @@ import { devicemodel, deviceitem } from 'src/app/models/deviceitem';
styleUrls: ['./produceform.component.less']
})
export class ProduceformComponent implements OnInit {
fullScreen = false;
isManufactorLoading: Boolean = false;
optionList: any;
@Input() id=Guid.EMPTY;
@Input() id = Guid.EMPTY;
nodes = [];
title: string = '';
loading = false;
......@@ -27,67 +27,98 @@ export class ProduceformComponent implements OnInit {
private fb: FormBuilder,
private msg: NzMessageService,
private drawerRef: NzDrawerRef<string>
) {}
) { }
form!: FormGroup;
submitting = false;
devicemodel: devicemodel[] = [];
data: deviceitem = {
name: '',
deviceType: '',
customerId: '',
id: Guid.EMPTY,
identityType: ''
};
ngOnInit() {
this.form = this.fb.group({
name: [null, [Validators.required ]],
description: ['', []],
defaultTimeout: [300, []],
id: [Guid.EMPTY, []],
defaultIdentityType: ['', []]
name: [null, [Validators.required]],
icon: ['', []],
gatewayType: ['Unknow', []],
defaultTimeout: [300, []],
gatewayConfigurationJson: ['', []],
gatewayConfigurationName: ['', []],
gatewayConfiguration: ['', []],
defaultIdentityType: ['', []],
defaultDeviceType: ['', []],
description: ['', []],
});
this._httpClient.get('api/produce/get/' + this.id).pipe(
map(x => {
this.form.patchValue(this.data);
})
).subscribe();
if (this.id != Guid.EMPTY) {
this._httpClient.get('api/produces/get?id=' + this.id).pipe(
map(x => {
this.form.patchValue(x.data);
if (x.data.gatewayType === 'Customize') {
this.form.patchValue({ gatewayConfigurationJson: this.form.value.gatewayConfiguration });
} else if (x.data.gatewayType === 'Unknow') {
this.form.patchValue({ gatewayConfigurationName: '' });
this.form.patchValue({ gatewayConfigurationJson: '' });
} else {
this.form.patchValue({ gatewayConfigurationName: this.form.value.gatewayConfiguration });
}
})
).subscribe();
}
}
submit() {
this.submitting = true;
if (this.form.value.gatewayType === 'Customize') {
this.form.patchValue({ gatewayConfiguration: this.form.value.gatewayConfigurationJson });
} else if (this.form.value.gatewayType === 'Unknow') {
this.form.patchValue({ gatewayConfiguration: '' });
} else {
this.form.patchValue({ gatewayConfiguration: this.form.value.gatewayConfigurationName });
}
if (this.id == Guid.EMPTY) {
this._httpClient.post('api/produce', this.form.value).subscribe(
() => {
this.submitting = false;
this.msg.create('success', '产品新增成功');
this.close();
},
() => {
this.msg.create('error', '产品新增失败');
this.close();
},
() => {}
this._httpClient.post('api/produces/save', this.form.value).subscribe(
{
next: next => {
this.submitting = false;
if (next.code === 10000) {
this.msg.create('success', '产品新增成功');
this.close();
}
},
error: error => {
this.msg.create('error', '产品新增失败');
this.close();
},
complete: () => { }
}
);
} else {
this._httpClient.put('api/produce/' + this.id, this.form.value).subscribe(
() => {
this.submitting = false;
this.msg.create('success', '产品修改成功');
this.close();
},
() => {
this.msg.create('error', '产品修改失败');
this.close();
},
() => {}
this._httpClient.put('api/produces/update', this.form.value).subscribe(
{
next: next => {
this.submitting = false;
if (next.code === 10000) {
this.msg.create('success', '产品修改成功');
this.close();
}
},
error: error => {
this.msg.create('error', '产品修改失败');
this.close();
},
complete: () => { }
}
);
}
}
......
......@@ -35,8 +35,14 @@
<span>{{ 'button.new' | translate }}</span>
</button>-->
<st #st [columns]="columns" [data]="url" ps="20" [page]="page" [req]="req" [res]="res">
<st #st [columns]="columns" [data]="url" ps="20" [page]="page" [req]="req" [res]="res" [expand]="expand" expandAccordion (change)="onchange($event)">
<ng-template #expand let-item let-index="index" let-column="column">
<st [data]="item.devices" [columns]="devicecolumns" >
</st>
</ng-template>
</st>
</nz-card>
</page-header>
import { ChangeDetectorRef, Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { Router } from '@angular/router';
import { STPage, STReq, STRes, STComponent, STColumn, STData } from '@delon/abc/st';
import { STPage, STReq, STRes, STComponent, STColumn, STData, STChange, STColumnTag, STColumnBadge } from '@delon/abc/st';
import { ModalHelper, SettingsService, _HttpClient } from '@delon/theme';
import { Guid } from 'guid-typescript';
import { NzDrawerService } from 'ng-zorro-antd/drawer';
import { NzMessageService } from 'ng-zorro-antd/message';
import { appmessage } from 'src/app/models/appmessage';
import { ProducedatadictionaryformComponent } from '../producedatadictionaryform/producedatadictionaryform.component';
import { ProducedataformComponent } from '../producedataform/producedataform.component';
import { ProduceformComponent } from '../produceform/produceform.component';
@Component({
......@@ -66,8 +68,8 @@ export class ProducelistComponent implements OnInit {
}
];
url = 'api/produce/list';
req: STReq = { method: 'POST', allInBody: true, reName: { pi: 'offset', ps: 'limit' }, params: this.q };
url = 'api/produces/list';
req: STReq = { method: 'GET', allInBody: true, reName: { pi: 'offset', ps: 'limit' }, params: this.q };
// 定义返回的参数
res: STRes = {
......@@ -76,6 +78,36 @@ export class ProducelistComponent implements OnInit {
list: 'data.rows'
}
};
BADGE: STColumnBadge = {
true: { text: '在线', color: 'success' },
false: { text: '离线', color: 'error' }
};
TAG: STColumnTag = {
AccessToken: { text: 'AccessToken', color: 'green' },
X509Certificate: { text: 'X509Certificate', color: 'blue' }
};
DeviceTAG: STColumnTag = {
Device: { text: '设备', color: 'green' },
Gateway: { text: '网关', color: 'blue' }
};
devicecolumns:STColumn[] = [
{ title: 'id', index: 'id', },
{title: '名称',
index: 'name',
render: 'name',
type: 'link',
},
{ title: '设备类型', index: 'deviceType', type: 'tag', tag: this.DeviceTAG },
{ title: '在线状态', index: 'active', type: 'badge', badge: this.BADGE, sort: true },
{ title: '最后活动时间', index: 'lastActivityDateTime', type: 'date' },
{ title: '认证方式', index: 'identityType', type: 'tag', tag: this.TAG},
]
@ViewChild('st', { static: true })
st!: STComponent;
......@@ -106,7 +138,21 @@ export class ProducelistComponent implements OnInit {
this.openComponent(item.id);
}
},
{
text: '属性',
// i18n: 'common.edit',
acl: 56,
click: (item: any) => {
this.editattr(item.id);
}
}, {
text: '字典',
// i18n: 'common.edit',
acl: 56,
click: (item: any) => {
this.editdic(item.id);
}
},
{
text: '删除',
pop: {
......@@ -125,7 +171,7 @@ export class ProducelistComponent implements OnInit {
selectedRows: STData[] = [];
ngOnInit() {
}
openComponent(id: string): void {
var { nzMaskClosable, width } = this.settingService.getData('drawerconfig');
......@@ -142,6 +188,88 @@ export class ProducelistComponent implements OnInit {
drawerRef.afterOpen.subscribe(() => { });
drawerRef.afterClose.subscribe(data => {
if (typeof data === 'string') {
}
this.getData();
});
}
editattr(id: string): void {
var { nzMaskClosable, width } = this.settingService.getData('drawerconfig');
var title = '属性编辑';
const drawerRef = this.drawerService.create<ProducedataformComponent, { id: string }, string>({
nzTitle: title,
nzContent: ProducedataformComponent,
nzWidth: width,
nzMaskClosable: nzMaskClosable,
nzContentParams: {
id: id
}
});
drawerRef.afterOpen.subscribe(() => { });
drawerRef.afterClose.subscribe(data => {
if (typeof data === 'string') {
}
this.getData();
});
}
onchange($events: STChange): void {
switch ($events.type) {
case 'expand':
if ($events.expand.expand) {
}
break;
}
}
getdevices() {
this.http.post('', {}).subscribe({
next: next => {
}, error: error => {
}, complete: () => {
}
})
}
editdic(id: string): void {
var { nzMaskClosable, width } = this.settingService.getData('drawerconfig');
var title = '字典编辑';
const drawerRef = this.drawerService.create<ProducedatadictionaryformComponent, { id: string }, string>({
nzTitle: title,
nzContent: ProducedatadictionaryformComponent,
nzWidth: "100%",
nzMaskClosable: nzMaskClosable,
nzContentParams: {
id: id
}
});
drawerRef.afterOpen.subscribe(() => { });
drawerRef.afterClose.subscribe(data => {
if (typeof data === 'string') {
}
......@@ -150,23 +278,17 @@ export class ProducelistComponent implements OnInit {
});
}
r;
getData() {
this.st.req = this.req;
this.st.load(1);
}
delete(id: number) {
this.http.get('api/produce/delete?id=' + id).subscribe(() => {
this.http.get('api/produces/delete?id=' + id).subscribe(() => {
this.st.load(this.st.pi);
});
}
setstatus(id: number) {
this.http.get('api/produce/setstatus?id=' + id).subscribe(() => {
this.st.load(this.st.pi);
});
}
add(tpl: TemplateRef<{}>) { }
......
......@@ -39,6 +39,7 @@ const routes: Routes = [
children: [
{ path: 'assets', loadChildren: () => import('./assets/assets.module').then(m => m.AssetsModule) },
{ path: 'devices', loadChildren: () => import('./devices/devices.module').then(m => m.DevicesModule) },
{ path: 'produce', loadChildren: () => import('./produce/produce.module').then(m => m.ProduceModule) },
{ path: 'settings', loadChildren: () => import('./settings/settings.module').then(m => m.SettingsModule) },
{ path: 'alarms', loadChildren: () => import('./alarms/alarms.module').then(m => m.AlarmsModule) },
{ path: 'rules', loadChildren: () => import('./rules/rules.module').then(m => m.RulesModule) }
......
......@@ -12,7 +12,9 @@ import {
AlertOutline,
GoldFill,
GoldTwoTone,
GoldOutline
GoldOutline,
SaveOutline,
MedicineBoxOutline
} from '@ant-design/icons-angular/icons';
export const ICONS = [
......@@ -27,5 +29,7 @@ export const ICONS = [
AlertOutline,
GoldFill,
GoldTwoTone,
GoldOutline
GoldOutline,
SaveOutline,
MedicineBoxOutline
];
......@@ -87,7 +87,19 @@ namespace IoTSharp.Controllers
{
new { text = "用户列表", i18n = "", link = "/iot/settings/userlist" }
}
}, new
},
new
{
text = "产品管理",
i18n = "",
icon = "medicinebox",
children = new[]
{
new { text = "产品列表", i18n = "", link = "/iot/produce/producelist" }
}
},
new
{
text = "设备管理",
i18n = "",
......
......@@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Scripting.Utils;
using ShardingCore.Extensions;
namespace IoTSharp.Controllers
......@@ -31,6 +32,7 @@ namespace IoTSharp.Controllers
_context = context;
_logger = logger;
}
/// <summary>
/// 产品列表
/// </summary>
......@@ -49,16 +51,47 @@ namespace IoTSharp.Controllers
condition = condition.And(x => x.Name.Contains(m.Name));
}
return new ApiResult<PagedData<ProduceDto>>(ApiCode.Success, "OK", new PagedData<ProduceDto>
if (m.limit > 1)
{
return new ApiResult<PagedData<ProduceDto>>(ApiCode.Success, "OK", new PagedData<ProduceDto>
{
total = await _context.Produces.CountAsync(condition),
rows = _context.Produces.Include(c=>c.Devices).Where(condition).Skip((m.offset) * m.limit).Take(m.limit)
.ToList().Select(c => new ProduceDto
{
Id = c.Id,
DefaultIdentityType = c.DefaultIdentityType,
DefaultTimeout = c.DefaultTimeout,
Description = c.Description,
Name = c.Name, Devices = c.Devices
}).ToList()
});
}
else
{
total = await _context.Produces.CountAsync(condition),
rows = _context.Produces.Where(condition).Where(condition).Skip((m.offset) * m.limit).Take(m.limit)
.ToList().Select(c => new ProduceDto
{ Id = c.Id, DefaultIdentityType = c.DefaultIdentityType, DefaultTimeout = c.DefaultTimeout, Description = c.Description, Name = c.Name }).ToList()
return new ApiResult<PagedData<ProduceDto>>(ApiCode.Success, "OK", new PagedData<ProduceDto>
{
total = await _context.Produces.CountAsync(),
rows = _context.Produces.Where(condition)
.ToList().Select(c => new ProduceDto
{
Id = c.Id,
DefaultIdentityType = c.DefaultIdentityType,
DefaultTimeout = c.DefaultTimeout,
Description = c.Description,
Name = c.Name
}).ToList()
});
});
}
}
/// <summary>
///
/// </summary>
......@@ -67,9 +100,11 @@ namespace IoTSharp.Controllers
[HttpGet]
public async Task<ApiResult<List<ProduceData>>> ProduceDatas(Guid produceid)
{
var result = await _context.ProduceDatas.Include(c => c.Owner).Where(p => p.Owner.Id == produceid).AsSplitQuery().ToListAsync();
var result = await _context.ProduceDatas.Include(c => c.Owner).Where(p => p.Owner.Id == produceid)
.AsSplitQuery().ToListAsync();
return new ApiResult<List<ProduceData>>(ApiCode.Success, "OK", result);
}
/// <summary>
/// 获取产品
/// </summary>
......@@ -84,10 +119,7 @@ namespace IoTSharp.Controllers
{
return new ApiResult<ProduceAddDto>(ApiCode.Success, "OK", new ProduceAddDto
{
Id = result.Id,
Customer = result.Customer.Id,
Tenant = result.Tenant.Id,
Icon = result.Icon,
DefaultDeviceType = result.DefaultDeviceType,
DefaultIdentityType = result.DefaultIdentityType,
......@@ -105,6 +137,7 @@ namespace IoTSharp.Controllers
}
}
/// <summary>
/// 删除产品
/// </summary>
......@@ -126,6 +159,7 @@ namespace IoTSharp.Controllers
await _context.SaveChangesAsync();
return new ApiResult<bool>(ApiCode.Success, "OK", true);
}
return new ApiResult<bool>(ApiCode.CantFindObject, "Produce is not found", false);
}
catch (Exception e)
......@@ -140,7 +174,7 @@ namespace IoTSharp.Controllers
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpGet]
[HttpPost]
public async Task<ApiResult<bool>> Save(ProduceAddDto dto)
{
......@@ -151,14 +185,14 @@ namespace IoTSharp.Controllers
produce.Customer = _context.Customer.Find(profile.Customer);
produce.Tenant = _context.Tenant.Find(profile.Tenant);
produce.DefaultDeviceType = dto.DefaultDeviceType;
produce.DefaultIdentityType=dto.DefaultIdentityType;
produce.DefaultTimeout=dto.DefaultTimeout;
produce.Description=dto.Description;
produce.Name=
produce.DefaultIdentityType = dto.DefaultIdentityType;
produce.DefaultTimeout = dto.DefaultTimeout;
produce.Description = dto.Description;
produce.Name =
dto.Name;
produce.GatewayConfiguration=dto.GatewayConfiguration;
produce.Icon=dto.Icon;
produce.GatewayType=dto.GatewayType;
produce.GatewayConfiguration = dto.GatewayConfiguration;
produce.Icon = dto.Icon;
produce.GatewayType = dto.GatewayType;
_context.Produces.Add(produce);
await _context.SaveChangesAsync();
return new ApiResult<bool>(ApiCode.Success, "OK", true);
......@@ -175,7 +209,7 @@ namespace IoTSharp.Controllers
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpGet]
[HttpPut]
public async Task<ApiResult<bool>> Update(ProduceAddDto dto)
{
......@@ -184,23 +218,210 @@ namespace IoTSharp.Controllers
var produce = await _context.Produces.SingleOrDefaultAsync(c => c.Id == dto.Id);
if (produce != null)
{
produce.DefaultIdentityType = dto.DefaultIdentityType;
produce.DefaultIdentityType = dto.DefaultIdentityType;
produce.DefaultTimeout = dto.DefaultTimeout;
produce.Description = dto.Description;
produce.GatewayType = dto.GatewayType;
produce.GatewayConfiguration = dto.GatewayConfiguration;
produce.Name = dto.Name;
produce.Icon = dto.Icon;
_context.Produces.Update(produce);
await _context.SaveChangesAsync();
}
return new ApiResult<bool>(ApiCode.CantFindObject, "Produce is not found", false);
}
catch (Exception e)
{
return new ApiResult<bool>(ApiCode.Exception, e.Message, false);
}
}
/// <summary>
/// 获取产品属性
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpGet]
public async Task<ApiResult<List<ProduceDataItemDto>>> GetProduceData(Guid produceId)
{
var produce = await _context.Produces
.SingleOrDefaultAsync(c => c.Id == produceId);
if (produce != null)
{
//var result = _context.ProduceDatas.Where(c => c.Owner.Id == produceId).Select(c => new ProduceDataItemDto
//{ KeyName = c.KeyName, DataSide = c.DataSide, Type = c.Type }).ToList();
var result = _context.DataStorage.Where(c => c.DeviceId == Guid.Empty).Select(c =>
new ProduceDataItemDto
{ KeyName = c.KeyName, DataSide = c.DataSide, Type = c.Type }).ToList();
return new ApiResult<List<ProduceDataItemDto>>(ApiCode.Success, "Ok", result);
}
return new ApiResult<List<ProduceDataItemDto>>(ApiCode.CantFindObject, "Produce is not found", null);
}
/// <summary>
/// 修改属性
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost]
public async Task<ApiResult<bool>> EditProduceData(ProduceDataEditDto dto)
{
try
{
var produce = await _context.Produces.Include(c => c.DefaultAttributes).SingleOrDefaultAsync(c => c.Id == dto.produceId);
if (produce != null)
{
var pds = _context.ProduceDatas.Where(c => c.Owner == produce).ToList();
if (dto.ProduceData != null && dto.ProduceData.Length > 0)
{
var data = dto.ProduceData.Select(c => new ProduceData { KeyName = c.KeyName, DataSide = c.DataSide, Type = c.Type, Owner = produce, DateTime = DateTime.Now }).ToList();
foreach (var item in data)
{
var pd = pds.FirstOrDefault(c => c.KeyName.ToLower() == item.KeyName.ToLower());
if (pd != null)
{
pd.DataSide = item.DataSide;
pd.Type = item.Type;
_context.ProduceDatas.Update(pd);
}
else
{
_context.ProduceDatas.Add(item);
}
}
await _context.SaveChangesAsync();
var delete_keynames = pds.Select(c => c.KeyName.ToLower())
.Except(dto.ProduceData.Select(c => c.KeyName.ToLower())).ToArray();
if (delete_keynames.Length > 0)
{
var deleteattr = pds.Join(delete_keynames, x => x.KeyName.ToLower(), y => y, (x, y) => x).ToArray();
_context.ProduceDatas.RemoveRange(deleteattr);
await _context.SaveChangesAsync();
}
return new ApiResult<bool>(ApiCode.Success, "Ok", true);
}
return new ApiResult<bool>(ApiCode.CantFindObject, "Produce data is not found", false);
}
return new ApiResult<bool>(ApiCode.CantFindObject, "Produce is not found", false);
}
catch (Exception e)
{
return new ApiResult<bool>(ApiCode.Exception, e.Message, false);
}
}
/// <summary>
/// 获取产品字典
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpGet]
public async Task<ApiResult<List<ProduceDictionary>>> GetProduceDictionary(Guid produceId)
{
var produce = await _context.Produces.Include(c => c.Dictionaries)
.SingleOrDefaultAsync(c => c.Id == produceId);
if (produce != null)
{
return new ApiResult<List<ProduceDictionary>>(ApiCode.Success, "Ok", produce.Dictionaries);
}
return new ApiResult<List<ProduceDictionary>>(ApiCode.CantFindObject, "Produce is not found", null);
}
/// <summary>
/// 修改字典
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost]
public async Task<ApiResult<bool>> EditProduceDictionary(ProduceDictionaryEditDto dto)
{
var profile = this.GetUserProfile();
try
{
var produce = await _context.Produces.Include(c => c.Dictionaries).SingleOrDefaultAsync(c => c.Id == dto.produceId);
if (produce != null)
{
foreach (var item in dto.ProduceDictionaryData)
{
if (item.Id == Guid.Empty)
{
var produceDictionary = new ProduceDictionary();
produceDictionary.KeyName = item.KeyName;
produceDictionary.DataType = item.DataType;
produceDictionary.Customer = profile.Customer;
produceDictionary.DefaultValue = item.DefaultValue;
produceDictionary.Display = item.Display;
produceDictionary.DisplayName = item.DisplayName;
produceDictionary.KeyDesc = item.KeyDesc;
produceDictionary.Tag = item.Tag;
produceDictionary.UnitConvert = item.UnitConvert;
produceDictionary.Unit = item.Unit;
produceDictionary.UnitExpression = item.UnitExpression;
produce.Dictionaries.Add(produceDictionary);
await _context.SaveChangesAsync();
}
else
{
var produceDictionary = await _context.ProduceDictionaries.SingleOrDefaultAsync(c => c.Id == item.Id);
if (produceDictionary != null)
{
produceDictionary.KeyName = item.KeyName;
produceDictionary.DataType = item.DataType;
produceDictionary.Customer = profile.Customer;
produceDictionary.DefaultValue = item.DefaultValue;
produceDictionary.Display = item.Display;
produceDictionary.DisplayName = item.DisplayName;
produceDictionary.KeyDesc = item.KeyDesc;
produceDictionary.Tag = item.Tag;
produceDictionary.UnitConvert = item.UnitConvert;
produceDictionary.Unit = item.Unit;
produceDictionary.UnitExpression = item.UnitExpression;
_context.ProduceDictionaries.Update(produceDictionary);
await _context.SaveChangesAsync();
}
}
}
var deletedic = produce.Dictionaries.Select(c => c.Id).Except(dto.ProduceDictionaryData.Select(c => c.Id)).ToList();
_context.ProduceDictionaries.RemoveRange(produce.Dictionaries.Where(c => deletedic.Any(p => p == c.Id)).ToList());
await _context.SaveChangesAsync();
return new ApiResult<bool>(ApiCode.Success, "Ok", true);
}
return new ApiResult<bool>(ApiCode.CantFindObject, "Produce is not found", false);
}
catch (Exception e)
{
return new ApiResult<bool>(ApiCode.Exception, e.Message, false);
}
}
}
}
......@@ -24,6 +24,7 @@ namespace IoTSharp.Dtos
/// <summary>
/// 网关类型 根据不通网关来处理相关配置
/// </summary>
[EnumDataType(typeof(GatewayType))]
public GatewayType GatewayType { get; set; } = GatewayType.Unknow;
/// <summary>
......
using System;
using IoTSharp.Contracts;
namespace IoTSharp.Dtos
{
public class ProduceDataEditDto
{
public Guid produceId { get; set; }
public ProduceDataItemDto[] ProduceData { get; set; }
}
public class ProduceDataItemDto
{
public string KeyName { get; set; }
public DataSide DataSide { get; set; }
public DataType Type { get; set; }
}
}
using System;
using IoTSharp.Contracts;
namespace IoTSharp.Dtos
{
public class ProduceDictionaryEditDto
{
public Guid produceId { get; set; }
public ProduceDictionaryItemDto[] ProduceDictionaryData { get; set; }
}
public class ProduceDictionaryItemDto
{
public Guid Id { get; set; }
/// <summary>
/// 字段名称
/// </summary>
public string KeyName { get; set; }
/// <summary>
/// 字段显示名称
/// </summary>
public string DisplayName { get; set; }
/// <summary>
/// 单位
/// </summary>
public string Unit { get; set; }
/// <summary>
/// 单位转换表达式
/// </summary>
public string UnitExpression { get; set; }
/// <summary>
///
/// </summary>
public bool UnitConvert { get; set; }
/// <summary>
/// 字段备注
/// </summary>
public string? KeyDesc { get; set; }
/// <summary>
/// 默认值
/// </summary>
public string? DefaultValue { get; set; }
/// <summary>
/// 是否显示
/// </summary>
public bool Display { get; set; }
/// <summary>
/// 位置名称
/// </summary>
public string Place0 { get; set; }
/// <summary>
/// 此位置顺序
/// </summary>
public string PlaceOrder0 { get; set; }
public string Place1 { get; set; }
public string PlaceOrder1 { get; set; }
public string Place2 { get; set; }
public string PlaceOrder2 { get; set; }
public string Place3 { get; set; }
public string PlaceOrder3 { get; set; }
public string Place4 { get; set; }
public string PlaceOrder4 { get; set; }
public string Place5 { get; set; }
public string PlaceOrder5 { get; set; }
/// <summary>
/// 数据类型
/// </summary>
public DataType DataType { get; set; }
public string? Tag { get; set; }
}
}
......@@ -21,5 +21,8 @@ namespace IoTSharp.Dtos
public IdentityType DefaultIdentityType { get; set; } = IdentityType.AccessToken;
public string Description { get; set; }
public List<Device> Devices { get; set; }
}
}
......@@ -556,6 +556,34 @@
<param name="dto"></param>
<returns></returns>
</member>
<member name="M:IoTSharp.Controllers.ProducesController.GetProduceData(System.Guid)">
<summary>
获取产品属性
</summary>
<param name="dto"></param>
<returns></returns>
</member>
<member name="M:IoTSharp.Controllers.ProducesController.EditProduceData(IoTSharp.Dtos.ProduceDataEditDto)">
<summary>
修改属性
</summary>
<param name="dto"></param>
<returns></returns>
</member>
<member name="M:IoTSharp.Controllers.ProducesController.GetProduceDictionary(System.Guid)">
<summary>
获取产品字典
</summary>
<param name="dto"></param>
<returns></returns>
</member>
<member name="M:IoTSharp.Controllers.ProducesController.EditProduceDictionary(IoTSharp.Dtos.ProduceDictionaryEditDto)">
<summary>
修改字典
</summary>
<param name="dto"></param>
<returns></returns>
</member>
<member name="M:IoTSharp.Controllers.RulesController.UpdateFlowExpression(IoTSharp.Dtos.UpdateFlowExpression)">
<summary>
更新节点的条件表达式
......@@ -838,6 +866,61 @@
默认设备类型
</summary>
</member>
<member name="P:IoTSharp.Dtos.ProduceDictionaryItemDto.KeyName">
<summary>
字段名称
</summary>
</member>
<member name="P:IoTSharp.Dtos.ProduceDictionaryItemDto.DisplayName">
<summary>
字段显示名称
</summary>
</member>
<member name="P:IoTSharp.Dtos.ProduceDictionaryItemDto.Unit">
<summary>
单位
</summary>
</member>
<member name="P:IoTSharp.Dtos.ProduceDictionaryItemDto.UnitExpression">
<summary>
单位转换表达式
</summary>
</member>
<member name="P:IoTSharp.Dtos.ProduceDictionaryItemDto.UnitConvert">
<summary>
</summary>
</member>
<member name="P:IoTSharp.Dtos.ProduceDictionaryItemDto.KeyDesc">
<summary>
字段备注
</summary>
</member>
<member name="P:IoTSharp.Dtos.ProduceDictionaryItemDto.DefaultValue">
<summary>
默认值
</summary>
</member>
<member name="P:IoTSharp.Dtos.ProduceDictionaryItemDto.Display">
<summary>
是否显示
</summary>
</member>
<member name="P:IoTSharp.Dtos.ProduceDictionaryItemDto.Place0">
<summary>
位置名称
</summary>
</member>
<member name="P:IoTSharp.Dtos.ProduceDictionaryItemDto.PlaceOrder0">
<summary>
此位置顺序
</summary>
</member>
<member name="P:IoTSharp.Dtos.ProduceDictionaryItemDto.DataType">
<summary>
数据类型
</summary>
</member>
<member name="T:IoTSharp.Dtos.TelemetryDataQueryDto">
<summary>
查询历史遥测数据请求结构体
......
......@@ -57,6 +57,7 @@ namespace IoTSharp.Controllers.Models
public bool OnlyActive { get; set; }
public string Name { get; set; }
}
public class RulePageParam : IPageParam
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册