Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
jobily
IoTSharp
提交
f8e4bed3
I
IoTSharp
项目概览
jobily
/
IoTSharp
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
I
IoTSharp
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
f8e4bed3
编写于
10月 14, 2022
作者:
麦壳饼
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'master' of
https://github.com/IoTSharp/IoTSharp
上级
a65bbd4e
50b24fb4
变更
27
显示空白变更内容
内联
并排
Showing
27 changed file
with
1193 addition
and
104 deletion
+1193
-104
IoTSharp.Contracts/Enums.cs
IoTSharp.Contracts/Enums.cs
+3
-0
IoTSharp/ClientApp/src/app/routes/devices/deviceform/deviceform.component.html
...c/app/routes/devices/deviceform/deviceform.component.html
+7
-3
IoTSharp/ClientApp/src/app/routes/devices/deviceform/deviceform.component.ts
...src/app/routes/devices/deviceform/deviceform.component.ts
+11
-5
IoTSharp/ClientApp/src/app/routes/produce/produce.module.ts
IoTSharp/ClientApp/src/app/routes/produce/produce.module.ts
+4
-2
IoTSharp/ClientApp/src/app/routes/produce/producedatadictionaryform/producedatadictionaryform.component.html
...tadictionaryform/producedatadictionaryform.component.html
+100
-0
IoTSharp/ClientApp/src/app/routes/produce/producedatadictionaryform/producedatadictionaryform.component.less
...tadictionaryform/producedatadictionaryform.component.less
+0
-0
IoTSharp/ClientApp/src/app/routes/produce/producedatadictionaryform/producedatadictionaryform.component.spec.ts
...ictionaryform/producedatadictionaryform.component.spec.ts
+25
-0
IoTSharp/ClientApp/src/app/routes/produce/producedatadictionaryform/producedatadictionaryform.component.ts
...datadictionaryform/producedatadictionaryform.component.ts
+127
-0
IoTSharp/ClientApp/src/app/routes/produce/producedataform/producedataform.component.html
...es/produce/producedataform/producedataform.component.html
+46
-0
IoTSharp/ClientApp/src/app/routes/produce/producedataform/producedataform.component.less
...es/produce/producedataform/producedataform.component.less
+0
-0
IoTSharp/ClientApp/src/app/routes/produce/producedataform/producedataform.component.spec.ts
...produce/producedataform/producedataform.component.spec.ts
+25
-0
IoTSharp/ClientApp/src/app/routes/produce/producedataform/producedataform.component.ts
...utes/produce/producedataform/producedataform.component.ts
+102
-0
IoTSharp/ClientApp/src/app/routes/produce/produceform/produceform.component.html
...app/routes/produce/produceform/produceform.component.html
+39
-10
IoTSharp/ClientApp/src/app/routes/produce/produceform/produceform.component.less
...app/routes/produce/produceform/produceform.component.less
+19
-0
IoTSharp/ClientApp/src/app/routes/produce/produceform/produceform.component.ts
...c/app/routes/produce/produceform/produceform.component.ts
+77
-46
IoTSharp/ClientApp/src/app/routes/produce/producelist/producelist.component.html
...app/routes/produce/producelist/producelist.component.html
+8
-2
IoTSharp/ClientApp/src/app/routes/produce/producelist/producelist.component.ts
...c/app/routes/produce/producelist/producelist.component.ts
+134
-12
IoTSharp/ClientApp/src/app/routes/routes-routing.module.ts
IoTSharp/ClientApp/src/app/routes/routes-routing.module.ts
+1
-0
IoTSharp/ClientApp/src/style-icons.ts
IoTSharp/ClientApp/src/style-icons.ts
+6
-2
IoTSharp/Controllers/MenuController.cs
IoTSharp/Controllers/MenuController.cs
+13
-1
IoTSharp/Controllers/ProducesController.cs
IoTSharp/Controllers/ProducesController.cs
+242
-21
IoTSharp/Dtos/ProduceAddDto.cs
IoTSharp/Dtos/ProduceAddDto.cs
+1
-0
IoTSharp/Dtos/ProduceDataEditDto.cs
IoTSharp/Dtos/ProduceDataEditDto.cs
+25
-0
IoTSharp/Dtos/ProduceDictionaryEditDto.cs
IoTSharp/Dtos/ProduceDictionaryEditDto.cs
+91
-0
IoTSharp/Dtos/ProduceDto.cs
IoTSharp/Dtos/ProduceDto.cs
+3
-0
IoTSharp/IoTSharp.xml
IoTSharp/IoTSharp.xml
+83
-0
IoTSharp/Models/IPageParam.cs
IoTSharp/Models/IPageParam.cs
+1
-0
未找到文件。
IoTSharp.Contracts/Enums.cs
浏览文件 @
f8e4bed3
...
...
@@ -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
,
...
...
IoTSharp/ClientApp/src/app/routes/devices/deviceform/deviceform.component.html
浏览文件 @
f8e4bed3
...
...
@@ -12,6 +12,12 @@
</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>
...
...
IoTSharp/ClientApp/src/app/routes/devices/deviceform/deviceform.component.ts
浏览文件 @
f8e4bed3
...
...
@@ -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
,
[]],
// deviceModel
Id: [null, []],
produce
Id
:
[
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
.
device
model
=
next
.
data
.
rows
;
this
.
device
produce
=
next
.
data
.
rows
;
},
error
:
error
=>
{},
complete
:
()
=>
{}
});
if
(
this
.
params
.
id
!==
Guid
.
EMPTY
)
{
concat
(
this
.
_httpClient
.
get
<
appmessage
<
deviceitem
>>
(
'
api/Devices/
'
+
this
.
params
.
id
).
pipe
(
...
...
IoTSharp/ClientApp/src/app/routes/produce/produce.module.ts
浏览文件 @
f8e4bed3
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
Settings
Module
{}
export
class
Produce
Module
{}
IoTSharp/ClientApp/src/app/routes/produce/producedatadictionaryform/producedatadictionaryform.component.html
0 → 100644
浏览文件 @
f8e4bed3
<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
IoTSharp/ClientApp/src/app/routes/produce/producedatadictionaryform/producedatadictionaryform.component.less
0 → 100644
浏览文件 @
f8e4bed3
IoTSharp/ClientApp/src/app/routes/produce/producedatadictionaryform/producedatadictionaryform.component.spec.ts
0 → 100644
浏览文件 @
f8e4bed3
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
();
});
});
IoTSharp/ClientApp/src/app/routes/produce/producedatadictionaryform/producedatadictionaryform.component.ts
0 → 100644
浏览文件 @
f8e4bed3
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
:
()
=>
{
}
}
);
}
}
IoTSharp/ClientApp/src/app/routes/produce/producedataform/producedataform.component.html
0 → 100644
浏览文件 @
f8e4bed3
<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
IoTSharp/ClientApp/src/app/routes/produce/producedataform/producedataform.component.less
0 → 100644
浏览文件 @
f8e4bed3
IoTSharp/ClientApp/src/app/routes/produce/producedataform/producedataform.component.spec.ts
0 → 100644
浏览文件 @
f8e4bed3
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
();
});
});
IoTSharp/ClientApp/src/app/routes/produce/producedataform/producedataform.component.ts
0 → 100644
浏览文件 @
f8e4bed3
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
=>
{
},
()
=>
{
});
}
}
IoTSharp/ClientApp/src/app/routes/produce/produceform/produceform.component.html
浏览文件 @
f8e4bed3
...
...
@@ -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=
"超时"
required
>
<nz-input-number
formControlName=
"timeout"
[nzMin]=
"0"
[nzStep]=
"1"
></nz-input-number>
<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=
"identityType"
placeholder=
"设备类型"
>
<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=
"defaultTimeout"
[nzMin]=
"0"
[nzStep]=
"1"
></nz-input-number>
</se>
<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
IoTSharp/ClientApp/src/app/routes/produce/produceform/produceform.component.less
浏览文件 @
f8e4bed3
.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
IoTSharp/ClientApp/src/app/routes/produce/produceform/produceform.component.ts
浏览文件 @
f8e4bed3
...
...
@@ -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
(
if
(
this
.
id
!=
Guid
.
EMPTY
)
{
this
.
_httpClient
.
get
(
'
api/produces/get?id=
'
+
this
.
id
).
pipe
(
map
(
x
=>
{
this
.
form
.
patchValue
(
this
.
data
);
})
).
subscribe
();
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
.
_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
.
_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
:
()
=>
{
}
}
);
}
}
...
...
IoTSharp/ClientApp/src/app/routes/produce/producelist/producelist.component.html
浏览文件 @
f8e4bed3
...
...
@@ -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>
IoTSharp/ClientApp/src/app/routes/produce/producelist/producelist.component.ts
浏览文件 @
f8e4bed3
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
:
'
POS
T
'
,
allInBody
:
true
,
reName
:
{
pi
:
'
offset
'
,
ps
:
'
limit
'
},
params
:
this
.
q
};
url
=
'
api/produce
s
/list
'
;
req
:
STReq
=
{
method
:
'
GE
T
'
,
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
:
{
...
...
@@ -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/produce
s
/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
<
{}
>
)
{
}
...
...
IoTSharp/ClientApp/src/app/routes/routes-routing.module.ts
浏览文件 @
f8e4bed3
...
...
@@ -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
)
}
...
...
IoTSharp/ClientApp/src/style-icons.ts
浏览文件 @
f8e4bed3
...
...
@@ -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
];
IoTSharp/Controllers/MenuController.cs
浏览文件 @
f8e4bed3
...
...
@@ -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
=
""
,
...
...
IoTSharp/Controllers/ProducesController.cs
浏览文件 @
f8e4bed3
...
...
@@ -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
));
}
if
(
m
.
limit
>
1
)
{
return
new
ApiResult
<
PagedData
<
ProduceDto
>>(
ApiCode
.
Success
,
"OK"
,
new
PagedData
<
ProduceDto
>
{
total
=
await
_context
.
Produces
.
CountAsync
(
condition
),
rows
=
_context
.
Produces
.
Where
(
condition
).
Where
(
condition
).
Skip
((
m
.
offset
)
*
m
.
limit
).
Take
(
m
.
limit
)
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
{
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
()
{
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>
[
Http
Ge
t
]
[
Http
Pos
t
]
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>
[
Http
Ge
t
]
[
Http
Pu
t
]
public
async
Task
<
ApiResult
<
bool
>>
Update
(
ProduceAddDto
dto
)
{
...
...
@@ -184,16 +218,17 @@ 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
)
...
...
@@ -202,5 +237,191 @@ namespace IoTSharp.Controllers
}
}
/// <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
);
}
}
}
}
IoTSharp/Dtos/ProduceAddDto.cs
浏览文件 @
f8e4bed3
...
...
@@ -24,6 +24,7 @@ namespace IoTSharp.Dtos
/// <summary>
/// 网关类型 根据不通网关来处理相关配置
/// </summary>
[
EnumDataType
(
typeof
(
GatewayType
))]
public
GatewayType
GatewayType
{
get
;
set
;
}
=
GatewayType
.
Unknow
;
/// <summary>
...
...
IoTSharp/Dtos/ProduceDataEditDto.cs
0 → 100644
浏览文件 @
f8e4bed3
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
;
}
}
}
IoTSharp/Dtos/ProduceDictionaryEditDto.cs
0 → 100644
浏览文件 @
f8e4bed3
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
;
}
}
}
IoTSharp/Dtos/ProduceDto.cs
浏览文件 @
f8e4bed3
...
...
@@ -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
;
}
}
}
IoTSharp/IoTSharp.xml
浏览文件 @
f8e4bed3
...
...
@@ -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>
查询历史遥测数据请求结构体
...
...
IoTSharp/Models/IPageParam.cs
浏览文件 @
f8e4bed3
...
...
@@ -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.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录