diff --git a/IoTSharp.Contracts/Enums.cs b/IoTSharp.Contracts/Enums.cs index 1b027e0982dcc904c801c94f6f4c73443882ed63..81d383809a800199ca35e8c2fbb6ae5542982a37 100644 --- a/IoTSharp.Contracts/Enums.cs +++ b/IoTSharp.Contracts/Enums.cs @@ -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, diff --git a/IoTSharp/ClientApp/src/app/routes/devices/deviceform/deviceform.component.html b/IoTSharp/ClientApp/src/app/routes/devices/deviceform/deviceform.component.html index 77088d8e40629c76f6a83f16dd3c2e3e543bc0fb..ea66ea73be3681537ec48ac7401db6521ddfcfd0 100644 --- a/IoTSharp/ClientApp/src/app/routes/devices/deviceform/deviceform.component.html +++ b/IoTSharp/ClientApp/src/app/routes/devices/deviceform/deviceform.component.html @@ -11,7 +11,13 @@ - + + + + + + + @@ -32,8 +38,6 @@ - - diff --git a/IoTSharp/ClientApp/src/app/routes/devices/deviceform/deviceform.component.ts b/IoTSharp/ClientApp/src/app/routes/devices/deviceform/deviceform.component.ts index e0a6272b79031bc6b1a0cc5f640e782b298f9e69..c3c428677634d5fa2aeff5df89916161bf0e17de 100644 --- a/IoTSharp/ClientApp/src/app/routes/devices/deviceform/deviceform.component.ts +++ b/IoTSharp/ClientApp/src/app/routes/devices/deviceform/deviceform.component.ts @@ -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>('api/Devices/' + this.params.id).pipe( diff --git a/IoTSharp/ClientApp/src/app/routes/produce/produce.module.ts b/IoTSharp/ClientApp/src/app/routes/produce/produce.module.ts index b48295a66f2000f72fa425ad3d44ea49cc3f889c..d7b616cb89584b173e9606b6b7a24f7904c73013 100644 --- a/IoTSharp/ClientApp/src/app/routes/produce/produce.module.ts +++ b/IoTSharp/ClientApp/src/app/routes/produce/produce.module.ts @@ -1,10 +1,12 @@ 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 {} diff --git a/IoTSharp/ClientApp/src/app/routes/produce/producedatadictionaryform/producedatadictionaryform.component.html b/IoTSharp/ClientApp/src/app/routes/produce/producedatadictionaryform/producedatadictionaryform.component.html new file mode 100644 index 0000000000000000000000000000000000000000..0ef7a68d4bb01dc2c3ca3e1596678c361df709e2 --- /dev/null +++ b/IoTSharp/ClientApp/src/app/routes/produce/producedatadictionaryform/producedatadictionaryform.component.html @@ -0,0 +1,100 @@ + + + + + + + + + + {{ item.keyName }} + + + + + {{ item.displayName }} + + + + + {{ item.unit }} + + + + + {{ item.unitExpression }} + + + + + + {{ item.unitConvert }} + + + + + + + {{ item.keyDesc }} + + + + {{ item.defaultValue }} + + + + + + + + {{ item.display }} + + + + + + {{ item.place0 }} + + + + + + + {{ item.tag }} + + + + + + + + + + + + + + + + {{ item.dataType}} + + + + \ No newline at end of file diff --git a/IoTSharp/ClientApp/src/app/routes/produce/producedatadictionaryform/producedatadictionaryform.component.less b/IoTSharp/ClientApp/src/app/routes/produce/producedatadictionaryform/producedatadictionaryform.component.less new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/IoTSharp/ClientApp/src/app/routes/produce/producedatadictionaryform/producedatadictionaryform.component.spec.ts b/IoTSharp/ClientApp/src/app/routes/produce/producedatadictionaryform/producedatadictionaryform.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..651f943bb989270dce6703ee5e7ffe935953b866 --- /dev/null +++ b/IoTSharp/ClientApp/src/app/routes/produce/producedatadictionaryform/producedatadictionaryform.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ProducedatadictionaryformComponent } from './producedatadictionaryform.component'; + +describe('ProducedatadictionaryformComponent', () => { + let component: ProducedatadictionaryformComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ProducedatadictionaryformComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ProducedatadictionaryformComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/IoTSharp/ClientApp/src/app/routes/produce/producedatadictionaryform/producedatadictionaryform.component.ts b/IoTSharp/ClientApp/src/app/routes/produce/producedatadictionaryform/producedatadictionaryform.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..d5ab8f306940caeef8951f41968b7fc70a7340bd --- /dev/null +++ b/IoTSharp/ClientApp/src/app/routes/produce/producedatadictionaryform/producedatadictionaryform.component.ts @@ -0,0 +1,127 @@ +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>('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: () => { } + } + ); + } + +} diff --git a/IoTSharp/ClientApp/src/app/routes/produce/producedataform/producedataform.component.html b/IoTSharp/ClientApp/src/app/routes/produce/producedataform/producedataform.component.html new file mode 100644 index 0000000000000000000000000000000000000000..808e1886be81a8623048a237a8807d7c51eac6ad --- /dev/null +++ b/IoTSharp/ClientApp/src/app/routes/produce/producedataform/producedataform.component.html @@ -0,0 +1,46 @@ + + + + + + + {{ item.keyName }} + + + + + + + + + + + {{ item.dataSide }} + + + + + + + + + + + + + + + {{ item.type}} + + + + \ No newline at end of file diff --git a/IoTSharp/ClientApp/src/app/routes/produce/producedataform/producedataform.component.less b/IoTSharp/ClientApp/src/app/routes/produce/producedataform/producedataform.component.less new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/IoTSharp/ClientApp/src/app/routes/produce/producedataform/producedataform.component.spec.ts b/IoTSharp/ClientApp/src/app/routes/produce/producedataform/producedataform.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..91c14906c231bbd225a9115c966e8243708e1fc5 --- /dev/null +++ b/IoTSharp/ClientApp/src/app/routes/produce/producedataform/producedataform.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ProducedataformComponent } from './producedataform.component'; + +describe('ProducedataformComponent', () => { + let component: ProducedataformComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ProducedataformComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ProducedataformComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/IoTSharp/ClientApp/src/app/routes/produce/producedataform/producedataform.component.ts b/IoTSharp/ClientApp/src/app/routes/produce/producedataform/producedataform.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..b03fb2e953a58f804b2ce5089de279eccff7e39d --- /dev/null +++ b/IoTSharp/ClientApp/src/app/routes/produce/producedataform/producedataform.component.ts @@ -0,0 +1,102 @@ +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>('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 => { }, () => { }); + } + + + + +} diff --git a/IoTSharp/ClientApp/src/app/routes/produce/produceform/produceform.component.html b/IoTSharp/ClientApp/src/app/routes/produce/produceform/produceform.component.html index 89f00de83b7119e190152c567ae793a3beca5db6..b902b552dc6ce37803797eac5fc543355adcf604 100644 --- a/IoTSharp/ClientApp/src/app/routes/produce/produceform/produceform.component.html +++ b/IoTSharp/ClientApp/src/app/routes/produce/produceform/produceform.component.html @@ -5,19 +5,45 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + - - - + + @@ -28,6 +54,9 @@ + + + \ No newline at end of file diff --git a/IoTSharp/ClientApp/src/app/routes/produce/produceform/produceform.component.less b/IoTSharp/ClientApp/src/app/routes/produce/produceform/produceform.component.less index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e399a368d839e3ebde4bb843d14d520401610d03 100644 --- a/IoTSharp/ClientApp/src/app/routes/produce/produceform/produceform.component.less +++ b/IoTSharp/ClientApp/src/app/routes/produce/produceform/produceform.component.less @@ -0,0 +1,19 @@ +.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 diff --git a/IoTSharp/ClientApp/src/app/routes/produce/produceform/produceform.component.ts b/IoTSharp/ClientApp/src/app/routes/produce/produceform/produceform.component.ts index 3125b8784c8b4b539bd5a3a88b462b20f42d2835..96436ac048f9b21286d176d39e8405845d3e83eb 100644 --- a/IoTSharp/ClientApp/src/app/routes/produce/produceform/produceform.component.ts +++ b/IoTSharp/ClientApp/src/app/routes/produce/produceform/produceform.component.ts @@ -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 - ) {} + ) { } 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: () => { } + + } + ); } } diff --git a/IoTSharp/ClientApp/src/app/routes/produce/producelist/producelist.component.html b/IoTSharp/ClientApp/src/app/routes/produce/producelist/producelist.component.html index a80b5c67c6fbc0484347bb3cef0a5180e9cae6b3..a3352ac8ff6d0d9bf4a4074127cc6c37ae11b79a 100644 --- a/IoTSharp/ClientApp/src/app/routes/produce/producelist/producelist.component.html +++ b/IoTSharp/ClientApp/src/app/routes/produce/producelist/producelist.component.html @@ -35,8 +35,14 @@ {{ 'button.new' | translate }} --> - - + + + + + + + + diff --git a/IoTSharp/ClientApp/src/app/routes/produce/producelist/producelist.component.ts b/IoTSharp/ClientApp/src/app/routes/produce/producelist/producelist.component.ts index 9c7f5a85a680b10ecfe04940397125a46a38f76d..1620da045fa452cba64610b29873c07c5835a9df 100644 --- a/IoTSharp/ClientApp/src/app/routes/produce/producelist/producelist.component.ts +++ b/IoTSharp/ClientApp/src/app/routes/produce/producelist/producelist.component.ts @@ -1,11 +1,13 @@ 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({ + 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({ + 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<{}>) { } diff --git a/IoTSharp/ClientApp/src/app/routes/routes-routing.module.ts b/IoTSharp/ClientApp/src/app/routes/routes-routing.module.ts index 8619f96ab10af6dfd328ee8716d5b31672b7195d..2f2f65f1f40ad71e66200ae3083a43075c4754d3 100644 --- a/IoTSharp/ClientApp/src/app/routes/routes-routing.module.ts +++ b/IoTSharp/ClientApp/src/app/routes/routes-routing.module.ts @@ -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) } diff --git a/IoTSharp/ClientApp/src/style-icons.ts b/IoTSharp/ClientApp/src/style-icons.ts index 11785b4fa742d5f50ecddcdbea4dbe4581ccb5c5..f2574b903e297dcb9cfa10173d9f2b624afdf86c 100644 --- a/IoTSharp/ClientApp/src/style-icons.ts +++ b/IoTSharp/ClientApp/src/style-icons.ts @@ -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 ]; diff --git a/IoTSharp/Controllers/MenuController.cs b/IoTSharp/Controllers/MenuController.cs index 5c51a7f819748a5be1f6533ea598c223fd5883b9..5069037671972994de6d30958f5b151bb554c9fe 100644 --- a/IoTSharp/Controllers/MenuController.cs +++ b/IoTSharp/Controllers/MenuController.cs @@ -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 = "", diff --git a/IoTSharp/Controllers/ProducesController.cs b/IoTSharp/Controllers/ProducesController.cs index 0f69b48cb39f4fe270dc96af2ad256ebeb18d579..ef4385ef9b41884583eda3d9a07ea00b23bb41cd 100644 --- a/IoTSharp/Controllers/ProducesController.cs +++ b/IoTSharp/Controllers/ProducesController.cs @@ -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; } + /// /// 产品列表 /// @@ -49,16 +51,47 @@ namespace IoTSharp.Controllers condition = condition.And(x => x.Name.Contains(m.Name)); } - return new ApiResult>(ApiCode.Success, "OK", new PagedData + + if (m.limit > 1) + { + return new ApiResult>(ApiCode.Success, "OK", new PagedData + { + 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>(ApiCode.Success, "OK", new PagedData + { + 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() - }); + }); + } + + } + /// /// /// @@ -67,9 +100,11 @@ namespace IoTSharp.Controllers [HttpGet] public async Task>> 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>(ApiCode.Success, "OK", result); } + /// /// 获取产品 /// @@ -84,10 +119,7 @@ namespace IoTSharp.Controllers { return new ApiResult(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 } } + /// /// 删除产品 /// @@ -126,6 +159,7 @@ namespace IoTSharp.Controllers await _context.SaveChangesAsync(); return new ApiResult(ApiCode.Success, "OK", true); } + return new ApiResult(ApiCode.CantFindObject, "Produce is not found", false); } catch (Exception e) @@ -140,7 +174,7 @@ namespace IoTSharp.Controllers /// /// /// - [HttpGet] + [HttpPost] public async Task> 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(ApiCode.Success, "OK", true); @@ -175,7 +209,7 @@ namespace IoTSharp.Controllers /// /// /// - [HttpGet] + [HttpPut] public async Task> 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(ApiCode.CantFindObject, "Produce is not found", false); } catch (Exception e) { return new ApiResult(ApiCode.Exception, e.Message, false); } - + + } + + + + + /// + /// 获取产品属性 + /// + /// + /// + [HttpGet] + public async Task>> 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>(ApiCode.Success, "Ok", result); + } + return new ApiResult>(ApiCode.CantFindObject, "Produce is not found", null); } + + + + + /// + /// 修改属性 + /// + /// + /// + [HttpPost] + public async Task> 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(ApiCode.Success, "Ok", true); + } + return new ApiResult(ApiCode.CantFindObject, "Produce data is not found", false); + } + return new ApiResult(ApiCode.CantFindObject, "Produce is not found", false); + } + catch (Exception e) + { + return new ApiResult(ApiCode.Exception, e.Message, false); + } + + } + + + + + /// + /// 获取产品字典 + /// + /// + /// + [HttpGet] + public async Task>> GetProduceDictionary(Guid produceId) + { + var produce = await _context.Produces.Include(c => c.Dictionaries) + .SingleOrDefaultAsync(c => c.Id == produceId); + if (produce != null) + { + return new ApiResult>(ApiCode.Success, "Ok", produce.Dictionaries); + } + return new ApiResult>(ApiCode.CantFindObject, "Produce is not found", null); + } + + + /// + /// 修改字典 + /// + /// + /// + [HttpPost] + public async Task> 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(ApiCode.Success, "Ok", true); + } + return new ApiResult(ApiCode.CantFindObject, "Produce is not found", false); + } + catch (Exception e) + { + return new ApiResult(ApiCode.Exception, e.Message, false); + } + + } + + + } } diff --git a/IoTSharp/Dtos/ProduceAddDto.cs b/IoTSharp/Dtos/ProduceAddDto.cs index 8f1a1ab01da3b20c77bd8cc1b608a7ec3df0e76f..af80a7a6bef93f39f81590417c4283d613342a96 100644 --- a/IoTSharp/Dtos/ProduceAddDto.cs +++ b/IoTSharp/Dtos/ProduceAddDto.cs @@ -24,6 +24,7 @@ namespace IoTSharp.Dtos /// /// 网关类型 根据不通网关来处理相关配置 /// + [EnumDataType(typeof(GatewayType))] public GatewayType GatewayType { get; set; } = GatewayType.Unknow; /// diff --git a/IoTSharp/Dtos/ProduceDataEditDto.cs b/IoTSharp/Dtos/ProduceDataEditDto.cs new file mode 100644 index 0000000000000000000000000000000000000000..4bc16019fe08e3b70e8ee81ac67cf9d2b80961e6 --- /dev/null +++ b/IoTSharp/Dtos/ProduceDataEditDto.cs @@ -0,0 +1,25 @@ +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; } + + } + +} diff --git a/IoTSharp/Dtos/ProduceDictionaryEditDto.cs b/IoTSharp/Dtos/ProduceDictionaryEditDto.cs new file mode 100644 index 0000000000000000000000000000000000000000..90cdb911eeaad1b5cc3f0309f8f47b61f6e240a8 --- /dev/null +++ b/IoTSharp/Dtos/ProduceDictionaryEditDto.cs @@ -0,0 +1,91 @@ +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; } + + /// + /// 字段名称 + /// + public string KeyName { get; set; } + + /// + /// 字段显示名称 + /// + public string DisplayName { get; set; } + + + + /// + /// 单位 + /// + public string Unit { get; set; } + + /// + /// 单位转换表达式 + /// + public string UnitExpression { get; set; } + + /// + /// + /// + public bool UnitConvert { get; set; } + /// + /// 字段备注 + /// + public string? KeyDesc { get; set; } + /// + /// 默认值 + /// + public string? DefaultValue { get; set; } + + /// + /// 是否显示 + /// + public bool Display { get; set; } + + /// + /// 位置名称 + /// + public string Place0 { get; set; } + /// + /// 此位置顺序 + /// + 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; } + /// + /// 数据类型 + /// + public DataType DataType { get; set; } + + + public string? Tag { get; set; } + + } + +} diff --git a/IoTSharp/Dtos/ProduceDto.cs b/IoTSharp/Dtos/ProduceDto.cs index 4c39ef46a3ace850907aba22bdceee3a4ad46846..f6de973886ad2fba20140ff7bf8fe066a0700165 100644 --- a/IoTSharp/Dtos/ProduceDto.cs +++ b/IoTSharp/Dtos/ProduceDto.cs @@ -21,5 +21,8 @@ namespace IoTSharp.Dtos public IdentityType DefaultIdentityType { get; set; } = IdentityType.AccessToken; public string Description { get; set; } + + public List Devices { get; set; } + } } diff --git a/IoTSharp/IoTSharp.xml b/IoTSharp/IoTSharp.xml index a3addda38e0601bf9e114e7e647d51d9608f8523..928746b495dd8256b10f00ea6eccd20f5ab7f633 100644 --- a/IoTSharp/IoTSharp.xml +++ b/IoTSharp/IoTSharp.xml @@ -556,6 +556,34 @@ + + + 获取产品属性 + + + + + + + 修改属性 + + + + + + + 获取产品字典 + + + + + + + 修改字典 + + + + 更新节点的条件表达式 @@ -838,6 +866,61 @@ 默认设备类型 + + + 字段名称 + + + + + 字段显示名称 + + + + + 单位 + + + + + 单位转换表达式 + + + + + + + + + + 字段备注 + + + + + 默认值 + + + + + 是否显示 + + + + + 位置名称 + + + + + 此位置顺序 + + + + + 数据类型 + + 查询历史遥测数据请求结构体 diff --git a/IoTSharp/Models/IPageParam.cs b/IoTSharp/Models/IPageParam.cs index 2f41d4b7409e90b82b77ce69e0f21e8dbf51f3ec..1554149d38e3525029e66dd79dee1c9e29379c13 100644 --- a/IoTSharp/Models/IPageParam.cs +++ b/IoTSharp/Models/IPageParam.cs @@ -57,6 +57,7 @@ namespace IoTSharp.Controllers.Models public bool OnlyActive { get; set; } public string Name { get; set; } + } public class RulePageParam : IPageParam