Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
admin
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
c1109e90
authored
Oct 28, 2024
by
GoldenZqqq
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 发起流程-填写表单页面单独抽取子组件,总体布局调整与样式绘制
parent
bd4ac52a
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
271 additions
and
154 deletions
+271
-154
src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue
+247
-0
src/views/bpm/processInstance/create/index_new.vue
+10
-149
src/views/bpm/processInstance/create/mock.js
+14
-5
No files found.
src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue
0 → 100644
View file @
c1109e90
<
template
>
<ContentWrap
:bodyStyle=
"
{ padding: '10px 20px 0' }">
<div
class=
"processInstance-wrap-main"
>
<el-scrollbar>
<div
class=
"text-#878c93 h-15px"
>
编号:
{{
selectProcessDefinition
.
id
}}
</div>
<el-divider
class=
"!my-8px"
/>
<div
class=
"flex items-center justify-between gap-5 mb-10px h-40px"
>
<div
class=
"text-26px font-bold mb-5px"
>
{{
selectProcessDefinition
.
name
}}
</div>
<el-button
style=
"float: right"
type=
"primary"
@
click=
"handleCancel"
>
<Icon
icon=
"ep:delete"
/>
选择其它流程
</el-button>
</div>
<!-- 中间主要内容tab栏 -->
<el-tabs
v-model=
"activeTab"
>
<!-- 表单信息 -->
<el-tab-pane
label=
"表单填写"
name=
"form"
>
<div
class=
"form-scroll-area"
>
<el-scrollbar>
<el-row>
<el-col
:span=
"17"
>
<form-create
:rule=
"detailForm.rule"
v-model:api=
"fApi"
v-model=
"detailForm.value"
:option=
"detailForm.option"
@
submit=
"submitForm"
>
<template
#
type-startUserSelect
>
<el-col
:span=
"24"
>
<el-card
class=
"mb-10px"
>
<template
#
header
>
指定审批人
</
template
>
<el-form
:model=
"startUserSelectAssignees"
:rules=
"startUserSelectAssigneesFormRules"
ref=
"startUserSelectAssigneesFormRef"
>
<el-form-item
v-for=
"userTask in startUserSelectTasks"
:key=
"userTask.id"
:label=
"`任务【${userTask.name}】`"
:prop=
"userTask.id"
>
<el-select
v-model=
"startUserSelectAssignees[userTask.id]"
multiple
placeholder=
"请选择审批人"
>
<el-option
v-for=
"user in userList"
:key=
"user.id"
:label=
"user.nickname"
:value=
"user.id"
/>
</el-select>
</el-form-item>
</el-form>
</el-card>
</el-col>
</template>
</form-create>
</el-col>
<el-col
:span=
"7"
>
<!-- 审批记录时间线 -->
<!-- <ProcessInstanceTimeline ref="timelineRef" :approve-nodes="approveNodes" /> -->
</el-col>
</el-row>
</el-scrollbar>
</div>
</el-tab-pane>
<!-- 流程图 -->
<el-tab-pane
label=
"流程图"
name=
"diagram"
>
<div
class=
"form-scroll-area"
>
<!-- 流程图预览 -->
<ProcessInstanceBpmnViewer
:bpmn-xml=
"bpmnXML"
/>
</div>
</el-tab-pane>
</el-tabs>
<!-- 底部操作栏 -->
<div
class=
"b-t-solid border-t-1px border-[var(--el-border-color)]"
>
<!-- 操作栏按钮 -->
<div
class=
"h-50px bottom-10 text-14px flex items-center color-#32373c dark:color-#fff font-bold btn-container"
>
<el-button
plain
type=
"success"
@
click=
"submitForm"
>
<Icon
icon=
"ep:select"
/>
发起
</el-button>
<el-button
plain
type=
"danger"
@
click=
"handleCancel"
>
<Icon
icon=
"ep:close"
/>
取消
</el-button>
</div>
</div>
</el-scrollbar>
</div>
</ContentWrap>
</template>
<
script
lang=
"ts"
setup
>
import
{
setConfAndFields2
}
from
'@/utils/formCreate'
import
ProcessInstanceBpmnViewer
from
'../detail/ProcessInstanceBpmnViewer.vue'
import
type
{
ApiAttrs
}
from
'@form-create/element-ui/types/config'
import
{
useTagsViewStore
}
from
'@/store/modules/tagsView'
import
*
as
ProcessInstanceApi
from
'@/api/bpm/processInstance'
import
*
as
DefinitionApi
from
'@/api/bpm/definition'
import
*
as
UserApi
from
'@/api/system/user'
defineOptions
({
name
:
'ProcessDefinitionDetail'
})
const
props
=
defineProps
<
{
selectProcessDefinition
:
any
}
>
()
const
{
push
,
currentRoute
}
=
useRouter
()
// 路由
const
message
=
useMessage
()
// 消息弹窗
const
{
delView
}
=
useTagsViewStore
()
// 视图操作
const
detailForm
:
any
=
ref
({
rule
:
[],
option
:
{},
value
:
{}
})
// 流程表单详情
const
fApi
=
ref
<
ApiAttrs
>
()
// 指定审批人
const
startUserSelectAssigneesFormRef
=
ref
()
// 发起人选择审批人的表单 Ref
const
startUserSelectTasks
:
any
=
ref
([])
// 发起人需要选择审批人的用户任务列表
const
startUserSelectAssignees
=
ref
({})
// 发起人选择审批人的数据
const
startUserSelectAssigneesFormRules
=
ref
({})
// 发起人选择审批人的表单 Rules
const
userList
=
ref
<
any
[]
>
([])
// 用户列表
const
bpmnXML
:
any
=
ref
(
null
)
// BPMN 数据
/** 当前的Tab */
const
activeTab
=
ref
(
'form'
)
const
emit
=
defineEmits
([
'cancel'
])
/** 处理选择流程的按钮操作 **/
const
initProcessInfo
=
async
(
row
,
formVariables
?)
=>
{
// 重置指定审批人
startUserSelectTasks
.
value
=
[]
startUserSelectAssignees
.
value
=
{}
startUserSelectAssigneesFormRules
.
value
=
{}
// 情况一:流程表单
if
(
row
.
formType
==
10
)
{
// 设置表单
setConfAndFields2
(
detailForm
,
row
.
formConf
,
row
.
formFields
,
formVariables
)
// 加载流程图
const
processDefinitionDetail
=
await
DefinitionApi
.
getProcessDefinition
(
row
.
id
)
if
(
processDefinitionDetail
)
{
bpmnXML
.
value
=
processDefinitionDetail
.
bpmnXml
startUserSelectTasks
.
value
=
processDefinitionDetail
.
startUserSelectTasks
// 设置指定审批人
if
(
startUserSelectTasks
.
value
?.
length
>
0
)
{
detailForm
.
value
.
rule
.
push
({
type
:
'startUserSelect'
,
props
:
{
title
:
'指定审批人'
}
})
// 设置校验规则
for
(
const
userTask
of
startUserSelectTasks
.
value
)
{
startUserSelectAssignees
.
value
[
userTask
.
id
]
=
[]
startUserSelectAssigneesFormRules
.
value
[
userTask
.
id
]
=
[
{
required
:
true
,
message
:
'请选择审批人'
,
trigger
:
'blur'
}
]
}
// 加载用户列表
userList
.
value
=
await
UserApi
.
getSimpleUserList
()
}
}
// 情况二:业务表单
}
else
if
(
row
.
formCustomCreatePath
)
{
await
push
({
path
:
row
.
formCustomCreatePath
})
// 这里暂时无需加载流程图,因为跳出到另外个 Tab;
}
}
/** 提交按钮 */
const
submitForm
=
async
(
formData
)
=>
{
if
(
!
fApi
.
value
||
props
.
selectProcessDefinition
)
{
return
}
// 如果有指定审批人,需要校验
if
(
startUserSelectTasks
.
value
?.
length
>
0
)
{
await
startUserSelectAssigneesFormRef
.
value
.
validate
()
}
// 提交请求
fApi
.
value
.
btn
.
loading
(
true
)
try
{
await
ProcessInstanceApi
.
createProcessInstance
({
processDefinitionId
:
props
.
selectProcessDefinition
.
id
,
variables
:
formData
||
detailForm
.
value
.
value
,
startUserSelectAssignees
:
startUserSelectAssignees
.
value
})
// 提示
message
.
success
(
'发起流程成功'
)
// 跳转回去
delView
(
unref
(
currentRoute
))
await
push
({
name
:
'BpmProcessInstanceMy'
})
}
finally
{
fApi
.
value
.
btn
.
loading
(
false
)
}
}
const
handleCancel
=
()
=>
{
emit
(
'cancel'
)
}
defineExpose
({
initProcessInfo
})
</
script
>
<
style
lang=
"scss"
scoped
>
$
wrap-padding-height
:
20px
;
$
wrap-margin-height
:
15px
;
$
button-height
:
51px
;
$
process-header-height
:
194px
;
.processInstance-wrap-main
{
height
:
calc
(
100vh
-
var
(
--top-tool-height
)
-
var
(
--tags-view-height
)
-
var
(
--app-footer-height
)
-
35px
);
max-height
:
calc
(
100vh
-
var
(
--top-tool-height
)
-
var
(
--tags-view-height
)
-
var
(
--app-footer-height
)
-
35px
);
overflow
:
auto
;
.form-scroll-area
{
height
:
calc
(
100vh
-
var
(
--top-tool-height
)
-
var
(
--tags-view-height
)
-
var
(
--app-footer-height
)
-
35px
-
$
process-header-height
-
40px
);
max-height
:
calc
(
100vh
-
var
(
--top-tool-height
)
-
var
(
--tags-view-height
)
-
var
(
--app-footer-height
)
-
35px
-
$
process-header-height
-
40px
);
overflow
:
auto
;
}
}
.form-box
{
:deep(.el-card)
{
border
:
none
;
}
}
</
style
>
src/views/bpm/processInstance/create/index_new.vue
View file @
c1109e90
...
@@ -43,86 +43,31 @@
...
@@ -43,86 +43,31 @@
</ContentWrap>
</ContentWrap>
<!-- 第二步,填写表单,进行流程的提交 -->
<!-- 第二步,填写表单,进行流程的提交 -->
<ContentWrap
v-else
>
<ProcessDefinitionDetail
<el-card
class=
"box-card"
>
v-else
<div
class=
"clearfix"
>
ref=
"processDefinitionDetailRef"
<span
class=
"el-icon-document"
>
申请信息【{{ selectProcessDefinition.name }}】
</span>
:selectProcessDefinition=
"selectProcessDefinition"
<el-button
style=
"float: right"
type=
"primary"
@
click=
"selectProcessDefinition = undefined"
>
@
cancel=
"selectProcessDefinition = undefined"
<Icon
icon=
"ep:delete"
/>
选择其它流程
</el-button>
</div>
<el-col
:span=
"16"
:offset=
"6"
style=
"margin-top: 20px"
>
<form-create
:rule=
"detailForm.rule"
v-model:api=
"fApi"
v-model=
"detailForm.value"
:option=
"detailForm.option"
@
submit=
"submitForm"
>
<
template
#
type-startUserSelect
>
<el-col
:span=
"24"
>
<el-card
class=
"mb-10px"
>
<template
#
header
>
指定审批人
</
template
>
<el-form
:model=
"startUserSelectAssignees"
:rules=
"startUserSelectAssigneesFormRules"
ref=
"startUserSelectAssigneesFormRef"
>
<el-form-item
v-for=
"userTask in startUserSelectTasks"
:key=
"userTask.id"
:label=
"`任务【${userTask.name}】`"
:prop=
"userTask.id"
>
<el-select
v-model=
"startUserSelectAssignees[userTask.id]"
multiple
placeholder=
"请选择审批人"
>
<el-option
v-for=
"user in userList"
:key=
"user.id"
:label=
"user.nickname"
:value=
"user.id"
/>
/>
</el-select>
</el-form-item>
</el-form>
</el-card>
</el-col>
</template>
</form-create>
</el-col>
</el-card>
<!-- 流程图预览 -->
<ProcessInstanceBpmnViewer
:bpmn-xml=
"bpmnXML"
/>
</ContentWrap>
</template>
</template>
<
script
lang=
"ts"
setup
>
<
script
lang=
"ts"
setup
>
import
*
as
DefinitionApi
from
'@/api/bpm/definition'
import
*
as
DefinitionApi
from
'@/api/bpm/definition'
import
*
as
ProcessInstanceApi
from
'@/api/bpm/processInstance'
import
*
as
ProcessInstanceApi
from
'@/api/bpm/processInstance'
import
{
setConfAndFields2
}
from
'@/utils/formCreate'
import
type
{
ApiAttrs
}
from
'@form-create/element-ui/types/config'
import
ProcessInstanceBpmnViewer
from
'../detail/ProcessInstanceBpmnViewer.vue'
import
{
CategoryApi
}
from
'@/api/bpm/category'
import
{
CategoryApi
}
from
'@/api/bpm/category'
import
{
useTagsViewStore
}
from
'@/store/modules/tagsView'
import
ProcessDefinitionDetail
from
'./ProcessDefinitionDetail.vue'
import
*
as
UserApi
from
'@/api/system/user'
import
{
categoryList
as
cl
,
processDefinitionList
as
pl
}
from
'./mock'
import
{
categoryList
as
cl
,
processDefinitionList
as
pl
}
from
'./mock'
defineOptions
({
name
:
'BpmProcessInstanceCreate'
})
defineOptions
({
name
:
'BpmProcessInstanceCreate'
})
const
route
=
useRoute
()
// 路由
const
route
=
useRoute
()
// 路由
const
{
push
,
currentRoute
}
=
useRouter
()
// 路由
const
message
=
useMessage
()
// 消息
const
message
=
useMessage
()
// 消息
const
{
delView
}
=
useTagsViewStore
()
// 视图操作
const
processInstanceId
:
any
=
route
.
query
.
processInstanceId
const
processInstanceId
:
any
=
route
.
query
.
processInstanceId
const
loading
=
ref
(
true
)
// 加载中
const
loading
=
ref
(
true
)
// 加载中
const
categoryList
:
any
=
ref
([])
// 分类的列表
const
categoryList
:
any
=
ref
([])
// 分类的列表
const
categoryActive
:
any
=
ref
({})
// 选中的分类
const
categoryActive
:
any
=
ref
({})
// 选中的分类
const
processDefinitionList
=
ref
([])
// 流程定义的列表
const
processDefinitionList
=
ref
([])
// 流程定义的列表
/** 查询列表 */
/** 查询列表 */
const
getList
=
async
()
=>
{
const
getList
=
async
()
=>
{
loading
.
value
=
true
loading
.
value
=
true
...
@@ -134,7 +79,6 @@ const getList = async () => {
...
@@ -134,7 +79,6 @@ const getList = async () => {
if
(
categoryList
.
value
.
length
>
0
)
{
if
(
categoryList
.
value
.
length
>
0
)
{
categoryActive
.
value
=
categoryList
.
value
[
0
]
categoryActive
.
value
=
categoryList
.
value
[
0
]
}
}
console
.
log
(
'[ categoryActive.value ] >'
,
categoryActive
.
value
)
// 流程定义
// 流程定义
processDefinitionList
.
value
=
await
DefinitionApi
.
getProcessDefinitionList
({
processDefinitionList
.
value
=
await
DefinitionApi
.
getProcessDefinitionList
({
suspensionState
:
1
suspensionState
:
1
...
@@ -171,100 +115,17 @@ const categoryProcessDefinitionList: any = computed(() => {
...
@@ -171,100 +115,17 @@ const categoryProcessDefinitionList: any = computed(() => {
})
})
// ========== 表单相关 ==========
// ========== 表单相关 ==========
const
fApi
=
ref
<
ApiAttrs
>
()
const
detailForm
:
any
=
ref
({
rule
:
[],
option
:
{},
value
:
{}
})
// 流程表单详情
const
selectProcessDefinition
=
ref
()
// 选择的流程定义
const
selectProcessDefinition
=
ref
()
// 选择的流程定义
const
processDefinitionDetailRef
=
ref
()
// 指定审批人
const
bpmnXML
:
any
=
ref
(
null
)
// BPMN 数据
const
startUserSelectTasks
:
any
=
ref
([])
// 发起人需要选择审批人的用户任务列表
const
startUserSelectAssignees
=
ref
({})
// 发起人选择审批人的数据
const
startUserSelectAssigneesFormRef
=
ref
()
// 发起人选择审批人的表单 Ref
const
startUserSelectAssigneesFormRules
=
ref
({})
// 发起人选择审批人的表单 Rules
const
userList
=
ref
<
any
[]
>
([])
// 用户列表
/** 处理选择流程的按钮操作 **/
/** 处理选择流程的按钮操作 **/
const
handleSelect
=
async
(
row
,
formVariables
?)
=>
{
const
handleSelect
=
async
(
row
,
formVariables
?)
=>
{
// 设置选择的流程
// 设置选择的流程
selectProcessDefinition
.
value
=
row
selectProcessDefinition
.
value
=
row
// 初始化流程定义详情
// 重置指定审批人
await
nextTick
()
startUserSelectTasks
.
value
=
[]
processDefinitionDetailRef
.
value
?.
initProcessInfo
(
row
,
formVariables
)
startUserSelectAssignees
.
value
=
{}
startUserSelectAssigneesFormRules
.
value
=
{}
// 情况一:流程表单
if
(
row
.
formType
==
10
)
{
// 设置表单
setConfAndFields2
(
detailForm
,
row
.
formConf
,
row
.
formFields
,
formVariables
)
// 加载流程图
const
processDefinitionDetail
=
await
DefinitionApi
.
getProcessDefinition
(
row
.
id
)
if
(
processDefinitionDetail
)
{
bpmnXML
.
value
=
processDefinitionDetail
.
bpmnXml
startUserSelectTasks
.
value
=
processDefinitionDetail
.
startUserSelectTasks
// 设置指定审批人
if
(
startUserSelectTasks
.
value
?.
length
>
0
)
{
detailForm
.
value
.
rule
.
push
({
type
:
'startUserSelect'
,
props
:
{
title
:
'指定审批人'
}
})
// 设置校验规则
for
(
const
userTask
of
startUserSelectTasks
.
value
)
{
startUserSelectAssignees
.
value
[
userTask
.
id
]
=
[]
startUserSelectAssigneesFormRules
.
value
[
userTask
.
id
]
=
[
{
required
:
true
,
message
:
'请选择审批人'
,
trigger
:
'blur'
}
]
}
// 加载用户列表
userList
.
value
=
await
UserApi
.
getSimpleUserList
()
}
}
// 情况二:业务表单
}
else
if
(
row
.
formCustomCreatePath
)
{
await
push
({
path
:
row
.
formCustomCreatePath
})
// 这里暂时无需加载流程图,因为跳出到另外个 Tab;
}
}
}
/** 提交按钮 */
const
submitForm
=
async
(
formData
)
=>
{
if
(
!
fApi
.
value
||
!
selectProcessDefinition
.
value
)
{
return
}
// 如果有指定审批人,需要校验
if
(
startUserSelectTasks
.
value
?.
length
>
0
)
{
await
startUserSelectAssigneesFormRef
.
value
.
validate
()
}
// 提交请求
fApi
.
value
.
btn
.
loading
(
true
)
try
{
await
ProcessInstanceApi
.
createProcessInstance
({
processDefinitionId
:
selectProcessDefinition
.
value
.
id
,
variables
:
formData
,
startUserSelectAssignees
:
startUserSelectAssignees
.
value
})
// 提示
message
.
success
(
'发起流程成功'
)
// 跳转回去
delView
(
unref
(
currentRoute
))
await
push
({
name
:
'BpmProcessInstanceMy'
})
}
finally
{
fApi
.
value
.
btn
.
loading
(
false
)
}
}
// 左侧分类切换
// 左侧分类切换
const
handleCategoryClick
=
(
val
)
=>
{
const
handleCategoryClick
=
(
val
)
=>
{
categoryActive
.
value
=
val
categoryActive
.
value
=
val
...
...
src/views/bpm/processInstance/create/mock.js
View file @
c1109e90
...
@@ -13,8 +13,12 @@ export const processDefinitionList = [
...
@@ -13,8 +13,12 @@ export const processDefinitionList = [
category
:
'approval'
,
category
:
'approval'
,
icon
:
'https://picsum.photos/200?r=1'
,
icon
:
'https://picsum.photos/200?r=1'
,
formType
:
10
,
formType
:
10
,
formConf
:
{},
// 根据需要定义表单配置
formConf
:
formFields
:
[],
// 根据需要定义表单字段
'{"form":{"inline":false,"hideRequiredAsterisk":false,"labelPosition":"right","size":"default","labelWidth":"100px"},"resetBtn":{"show":false,"innerText":"重置"},"submitBtn":{"show":true,"innerText":"提交"}}'
,
formFields
:
[
'{"type":"input","field":"F1yrm2sosxgeabc","title":"请假原因","info":"","$required":false,"props":{"type":"text","placeholder":"请输入123"},"_fc_id":"id_Fhrbm2sosxgeacc","name":"ref_Festm2sosxgeadc","display":true,"hidden":false,"_fc_drag_tag":"input"}'
,
'{"type":"radio","field":"F9r3m2sp1b34aec","title":"请假类型","info":"","$required":false,"props":{"_optionType":2},"_fc_id":"id_F4nwm2sp1b34afc","name":"ref_Fkodm2sp1b34agc","display":true,"hidden":false,"_fc_drag_tag":"radio","options":[{"label":"事假","value":"1"},{"label":"婚假","value":"2"},{"label":"丧假","value":"3"}]}'
],
startUserSelectTasks
:
[
startUserSelectTasks
:
[
{
id
:
'task1'
,
name
:
'部门经理'
},
{
id
:
'task1'
,
name
:
'部门经理'
},
{
id
:
'task2'
,
name
:
'人力资源'
}
{
id
:
'task2'
,
name
:
'人力资源'
}
...
@@ -27,8 +31,12 @@ export const processDefinitionList = [
...
@@ -27,8 +31,12 @@ export const processDefinitionList = [
category
:
'approval'
,
category
:
'approval'
,
icon
:
'https://picsum.photos/200?r=2'
,
icon
:
'https://picsum.photos/200?r=2'
,
formType
:
10
,
formType
:
10
,
formConf
:
{},
formConf
:
formFields
:
[],
'{"form":{"inline":false,"hideRequiredAsterisk":false,"labelPosition":"right","size":"default","labelWidth":"100px"},"resetBtn":{"show":false,"innerText":"重置"},"submitBtn":{"show":true,"innerText":"提交"}}'
,
formFields
:
[
'{"type":"input","field":"F1yrm2sosxgeabc","title":"请假原因","info":"","$required":false,"props":{"type":"text","placeholder":"请输入123"},"_fc_id":"id_Fhrbm2sosxgeacc","name":"ref_Festm2sosxgeadc","display":true,"hidden":false,"_fc_drag_tag":"input"}'
,
'{"type":"radio","field":"F9r3m2sp1b34aec","title":"请假类型","info":"","$required":false,"props":{"_optionType":2},"_fc_id":"id_F4nwm2sp1b34afc","name":"ref_Fkodm2sp1b34agc","display":true,"hidden":false,"_fc_drag_tag":"radio","options":[{"label":"事假","value":"1"},{"label":"婚假","value":"2"},{"label":"丧假","value":"3"}]}'
],
startUserSelectTasks
:
[{
id
:
'task3'
,
name
:
'财务'
}]
startUserSelectTasks
:
[{
id
:
'task3'
,
name
:
'财务'
}]
},
},
{
{
...
@@ -55,7 +63,8 @@ export const processDefinitionList = [
...
@@ -55,7 +63,8 @@ export const processDefinitionList = [
formConf
:
{},
formConf
:
{},
formFields
:
[],
formFields
:
[],
startUserSelectTasks
:
[{
id
:
'task3'
,
name
:
'财务'
}]
startUserSelectTasks
:
[{
id
:
'task3'
,
name
:
'财务'
}]
},{
},
{
id
:
'5'
,
id
:
'5'
,
key
:
'process_1'
,
key
:
'process_1'
,
name
:
'请假申请'
,
name
:
'请假申请'
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment