Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
CivManage
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ReactWeb5
CivManage
Commits
d18f2d51
Commit
d18f2d51
authored
2 years ago
by
涂伟
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of
https://g.civnet.cn:8443/ReactWeb5/maintenance
# Conflicts: # src/pages/bsmanager/workOrder/workflowEdit/workFlowComponents/FlowChartRt.jsx
parents
79f350de
5953834c
Pipeline
#72050
passed with stages
Changes
14
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
348 additions
and
236 deletions
+348
-236
FlowChartRt.jsx
...workOrder/workflowEdit/workFlowComponents/FlowChartRt.jsx
+94
-67
SortModal.jsx
...tformCenter/gis/schemeConfig/projectMessage/SortModal.jsx
+2
-2
AddModal.jsx
src/pages/platformCenter/hostmanager/gateWay/AddModal.jsx
+15
-49
gateWay.jsx
src/pages/platformCenter/hostmanager/gateWay/gateWay.jsx
+65
-56
HomeConfigModal.jsx
...es/productCenter/webConfig/components/HomeConfigModal.jsx
+6
-2
siteConfigDrawer.js
...es/productCenter/webConfig/components/siteConfigDrawer.js
+6
-3
AddForm.jsx
src/pages/productCenter/webConfig/menuconfig/AddForm.jsx
+37
-5
ParmarsModal.jsx
...pages/productCenter/webConfig/menuconfig/ParmarsModal.jsx
+44
-16
ParmarsModal.less
...ages/productCenter/webConfig/menuconfig/ParmarsModal.less
+1
-1
TreeSelect.jsx
src/pages/productCenter/webConfig/menuconfig/TreeSelect.jsx
+3
-3
editConfigFileWrapper.js
...oductCenter/webConfig/menuconfig/editConfigFileWrapper.js
+25
-22
editForm.jsx
src/pages/productCenter/webConfig/menuconfig/editForm.jsx
+43
-10
hostmanager.js
src/services/hostmanager/hostmanager.js
+5
-0
api.js
src/services/webConfig/api.js
+2
-0
No files found.
src/pages/bsmanager/workOrder/workflowEdit/workFlowComponents/FlowChartRt.jsx
View file @
d18f2d51
...
...
@@ -78,17 +78,17 @@ const FlowChart = props => {
}
},
[
treeVisible
]);
// 监听删除,给删除数组里添加删除id
useEffect
(()
=>
{
if
(
deleteLine
)
{
setDeleteLines
([...
DeleteLines
,
deleteLine
]);
}
},
[
deleteLine
]);
useEffect
(()
=>
{
if
(
deleteNode
)
{
setDeleteNodes
([...
DeleteNodes
,
deleteNode
]);
}
},
[
deleteNode
]);
//
//
监听删除,给删除数组里添加删除id
//
useEffect(() => {
//
if (deleteLine) {
//
setDeleteLines([...DeleteLines, deleteLine]);
//
}
//
}, [deleteLine]);
//
useEffect(() => {
//
if (deleteNode) {
//
setDeleteNodes([...DeleteNodes, deleteNode]);
//
}
//
}, [deleteNode]);
// 初始化
useEffect
(()
=>
{
// 初始化流程图
...
...
@@ -96,52 +96,66 @@ const FlowChart = props => {
initPalette
();
myOverview
=
objGo
(
go
.
Overview
,
'myOverviewDiv'
,
{
observed
:
diagram
});
// 监听节点或线的删除事件
diagram
.
addDiagramListener
(
'SelectionDeleted'
,
e
=>
{
let
delNodes
=
[];
let
delLinks
=
[];
e
.
subject
.
each
(
n
=>
{
if
(
n
.
data
.
LineId
)
{
delLinks
.
push
(
n
.
data
.
LineId
);
}
if
(
n
.
data
.
ActivityId
)
{
delNodes
.
push
(
n
.
data
.
ActivityId
);
}
//
diagram.addDiagramListener('SelectionDeleted', e => {
//
let delNodes = [];
//
let delLinks = [];
//
e.subject.each(n => {
//
if (n.data.LineId) {
//
delLinks.push(n.data.LineId);
//
}
//
if (n.data.ActivityId) {
//
delNodes.push(n.data.ActivityId);
//
}
// 如果删除得节点不是新增得就给id放入到删除节点数组中
if
(
n
.
data
.
NodeId
&&
!
AddNodes
.
some
(
item
=>
item
===
n
.
data
.
NodeId
))
{
setTimeout
(()
=>
{
setDeleteNode
(
n
.
data
.
NodeId
);
},
0
);
}
if
(
n
.
data
.
LineKey
)
{
setTimeout
(()
=>
{
setDeleteLine
(
n
.
data
.
LineId
);
},
0
);
// // 如果删除得节点不是新增得就给id放入到删除节点数组中
// if (n.data.NodeId && !AddNodes.some(item => item === n.data.NodeId)) {
// setTimeout(() => {
// setDeleteNode(n.data.NodeId);
// }, 0);
// }
// if (n.data.LineKey) {
// setTimeout(() => {
// setDeleteLine(n.data.LineId);
// }, 0);
// }
// });
// if (delNodes.length === 0) {
// return;
// }
// DeleteFlowNodes({ ActivityIds: delNodes, LineIds: delLinks }).then(res => {
// if (res.code === 0) {
// message.success('删除成功');
// } else {
// // message.error(res.msg);
// message.error({
// content: <div style={{ whiteSpace: 'pre-line', textAlign: 'justify' }}>{res.msg}</div>,
// });
// }
// });
// console.log(delNodes, delLinks, 'fffff');
// });
// 监听节点或线的删除前事件
diagram
.
commandHandler
.
canDeleteSelection
=
()
=>
{
let
delNodes
=
new
Set
();
let
delNodeIds
=
new
Set
();
let
delLinks
=
new
Set
();
diagram
.
selection
.
toArray
().
forEach
(
item
=>
{
if
(
item
.
data
.
ActivityId
)
{
delNodes
.
add
(
item
.
data
.
ActivityId
);
delNodeIds
.
add
(
item
.
data
.
NodeId
);
item
.
findLinksConnected
().
each
(
link
=>
{
if
(
link
.
data
.
LineId
)
{
delLinks
.
add
(
link
.
data
.
LineId
);
}
});
if
(
delNodes
.
length
===
0
)
{
return
;
}
DeleteFlowNodes
({
ActivityIds
:
delNodes
,
LineIds
:
delLinks
}).
then
(
res
=>
{
if
(
res
.
code
===
0
)
{
message
.
success
(
'删除成功'
);
}
else
{
// message.error(res.msg);
message
.
error
({
content
:
<
div
style=
{
{
whiteSpace
:
'pre-line'
,
textAlign
:
'justify'
}
}
>
{
res
.
msg
}
</
div
>,
});
if
(
item
.
data
.
LineId
)
{
delLinks
.
add
(
item
.
data
.
LineId
);
}
});
console
.
log
(
delNodes
,
delLinks
,
'fffff'
);
});
// 监听节点或线的删除前事件
diagram
.
commandHandler
.
canDeleteSelection
=
()
=>
// 用例获取选中的节点或线
diagram
.
selection
.
all
(
e
=>
{
// 判断是否存在不允许删除的节点或线
showDeleteConfirm
(
e
.
data
);
showDeleteConfirm
([...
delNodeIds
],
[...
delNodes
],
[...
delLinks
]);
return
false
;
})
;
}
;
// 监听线,连接线的时候加上text属性
diagram
.
addDiagramListener
(
'LinkDrawn'
,
e
=>
{
// e.subject.data.text = '';
...
...
@@ -309,7 +323,7 @@ const FlowChart = props => {
};
},
[
currentFlowData
]);
// 删除提醒
const
showDeleteConfirm
=
val
=>
{
const
showDeleteConfirm
=
(
delNodeIds
,
delNodes
,
delLinks
)
=>
{
confirm
({
title
:
'确定要删除所选中的节点吗?'
,
icon
:
<
ExclamationCircleOutlined
/>,
...
...
@@ -318,29 +332,31 @@ const FlowChart = props => {
okType
:
'danger'
,
cancelText
:
'否'
,
onOk
()
{
delNode
(
val
);
delNode
(
delNodeIds
,
delNodes
,
delLinks
);
},
onCancel
()
{},
});
};
// 删除节点
const
delNode
=
val
=>
{
const
delNode
=
(
delNodeIds
,
delNodes
,
delLinks
)
=>
{
setShowLeaveTip
(
true
);
// leaveCallBack(true);
if
(
delNodes
.
length
===
0
)
{
diagram
.
commandHandler
.
deleteSelection
();
// if (val.LineId) {
// diagram.commandHandler.deleteSelection();
// return;
// }
// DeleteFlowNode({ activityId: val.ActivityId }).then(res => {
// if (res.code === 0) {
// message.success('删除成功');
// diagram.commandHandler.deleteSelection();
// } else {
return
;
}
DeleteFlowNodes
({
ActivityIds
:
delNodes
,
LineIds
:
delLinks
}).
then
(
res
=>
{
if
(
res
.
code
===
0
)
{
diagram
.
commandHandler
.
deleteSelection
();
setDeleteNodes
([...
DeleteNodes
,
...
delNodeIds
]);
setDeleteLines
([...
DeleteLines
,
...
delLinks
]);
message
.
success
(
'删除成功'
);
}
else
{
// message.error(res.msg);
// }
// });
message
.
error
({
content
:
<
div
style=
{
{
whiteSpace
:
'pre-line'
,
textAlign
:
'justify'
}
}
>
{
res
.
msg
}
</
div
>,
});
}
});
};
const
animateFadeDown
=
e
=>
{
...
...
@@ -1108,8 +1124,13 @@ const FlowChart = props => {
// return;
// }
let
list
=
new
Set
([]);
let
errorList
=
new
Set
();
diagramObj
.
nodeDataArray
.
forEach
(
item
=>
{
if
(
item
.
NodeType
===
'1'
||
item
.
NodeType
===
'0'
||
item
.
NodeType
===
'1'
)
{
if
(
!
item
.
TableName
||
!
item
.
Fields
)
{
errorList
.
add
(
item
.
NodeName
);
}
}
if
((
item
.
NodeType
===
'20'
||
item
.
NodeType
===
'21'
)
&&
item
.
RuleList
)
{
item
.
RuleList
.
forEach
(
ele
=>
{
if
(
!
ele
.
RuleName
)
{
...
...
@@ -1134,6 +1155,12 @@ const FlowChart = props => {
}
item
.
CarbonCopyPeopleList
=
item
.
CarbonCopyPeopleList
.
map
(
ele
=>
Number
(
ele
.
value
));
});
if
([...
errorList
].
length
>
0
)
{
errorList
.
forEach
(
item
=>
{
message
.
error
(
`请检查
${
item
}
节点存在未配置项`
);
});
return
;
}
if
([...
list
].
length
>
0
)
{
list
.
forEach
(
item
=>
{
message
.
error
(
`请检查
${
item
}
规则配置`
);
...
...
This diff is collapsed.
Click to expand it.
src/pages/platformCenter/gis/schemeConfig/projectMessage/SortModal.jsx
View file @
d18f2d51
...
...
@@ -13,14 +13,14 @@ import { Modal, notification } from 'antd';
import
Sortable
from
'sortablejs'
;
import
styles
from
'./SortModal.less'
;
import
DragTable
from
'@/components/DragTable/DragTable'
;
import
{
SortScheme
}
from
'@/services/webConfig/api'
;
import
{
SortScheme
,
SortSchemePost
}
from
'@/services/webConfig/api'
;
const
SortModal
=
props
=>
{
const
{
callBackSubmit
=
()
=>
{},
title
,
visible
,
onCancel
,
sortData
}
=
props
;
const
[
orderTable
,
setOrderTable
]
=
useState
([]);
const
[
flowIDs
,
setFlowIDs
]
=
useState
([]);
const
onSumbit
=
()
=>
{
SortScheme
({
sortName
:
flowIDs
.
toString
()
}).
then
(
res
=>
{
SortScheme
Post
({
sortNames
:
flowIDs
}).
then
(
res
=>
{
if
(
res
.
code
===
'0'
)
{
callBackSubmit
();
onCancel
();
...
...
This diff is collapsed.
Click to expand it.
src/pages/platformCenter/hostmanager/gateWay/AddModal.jsx
View file @
d18f2d51
...
...
@@ -28,8 +28,7 @@ const AddModal = props => {
useEffect
(()
=>
{
if
(
visible
)
{
if
(
type
===
'edit'
)
{
let
data
=
[
'CityServer'
,
'IOT'
,
'CivData'
,
'GIS'
];
if
(
data
.
indexOf
(
pickItem
.
key
)
!=
-
1
)
{
if
(
pickItem
.
remark
)
{
setHidden
(
true
);
}
let
aa
=
pickItem
.
methods
.
replace
(
/
\s
/g
,
''
);
...
...
@@ -80,7 +79,7 @@ const AddModal = props => {
setCurrent
(
true
);
}
}
else
{
form
.
setFieldsValue
({
IsAuthentication
:
true
});
form
.
setFieldsValue
({
IsAuthentication
:
true
,
Priority
:
1
,
Methods
:
[
'GET'
,
'POST'
]
});
}
}
else
{
setHidden
(
false
);
...
...
@@ -95,11 +94,8 @@ const AddModal = props => {
if
(
validate
)
{
setLoading
(
true
);
let
obj
=
form
.
getFieldsValue
();
console
.
log
(
obj
.
IsAuthentication
);
console
.
log
(
obj
.
Priority
);
let
aa
=
obj
.
Methods
.
toString
();
obj
.
Methods
=
aa
.
replace
(
/
\s
/g
,
''
);
console
.
log
(
obj
.
Methods
);
let
data
=
{
UpstreamPathTemplate
:
obj
.
UpstreamPathTemplate
||
null
,
DownstreamPathTemplate
:
obj
.
DownstreamPathTemplate
||
null
,
...
...
@@ -125,8 +121,7 @@ const AddModal = props => {
ReRouteIsCaseSensitive
:
obj
.
ReRouteIsCaseSensitive
||
null
,
DownstreamHttpMethod
:
obj
.
DownstreamHttpMethod
||
null
,
};
console
.
log
(
data
);
if
(
type
===
'add'
)
{
let
str
=
type
===
'add'
?
'新增'
:
'编辑'
;
SaveRoutes
([
{
...
data
,
...
...
@@ -141,49 +136,19 @@ const AddModal = props => {
notification
.
success
({
message
:
'提示'
,
duration
:
3
,
description
:
res
.
msg
||
'新增成功'
,
description
:
res
.
msg
||
`
${
str
}
成功`
,
});
}
else
{
notification
.
error
({
message
:
'提示'
,
duration
:
3
,
description
:
res
.
msg
||
'新增失败'
,
description
:
res
.
msg
||
`
${
str
}
失败`
,
});
}
})
.
catch
(
err
=>
{
setLoading
(
false
);
});
}
else
{
SaveRoutes
([
{
id
:
pickItem
.
id
,
...
data
,
IsEnable
:
1
,
},
])
.
then
(
res
=>
{
setLoading
(
false
);
if
(
res
.
code
===
0
)
{
onCancel
();
callBackSubmit
();
notification
.
success
({
message
:
'提示'
,
duration
:
3
,
description
:
res
.
msg
||
'编辑成功'
,
});
}
else
{
notification
.
error
({
message
:
'提示'
,
duration
:
3
,
description
:
res
.
msg
||
'编辑失败'
,
});
}
})
.
catch
(
err
=>
{
setLoading
(
false
);
});
}
}
});
};
...
...
@@ -274,7 +239,7 @@ const AddModal = props => {
},
]
}
>
<
Input
allowClear
placeholder=
"示例:/{url}"
disabled=
{
hidden
}
/>
<
Input
allowClear
placeholder=
"示例:/{url}"
/>
</
Item
>
<
Item
label=
"上游请求方式"
...
...
@@ -286,7 +251,7 @@ const AddModal = props => {
},
]
}
>
<
Checkbox
.
Group
options=
{
plainOptions
}
style=
{
{
display
:
'flex'
}
}
disabled=
{
hidden
}
/>
<
Checkbox
.
Group
options=
{
plainOptions
}
style=
{
{
display
:
'flex'
}
}
/>
</
Item
>
<
Item
label=
"下游服务地址"
...
...
@@ -302,12 +267,8 @@ const AddModal = props => {
</
Item
>
<
Item
label=
"身份认证"
name=
"IsAuthentication"
>
<
Radio
.
Group
>
<
Radio
value=
{
true
}
disabled=
{
hidden
}
>
开启
</
Radio
>
<
Radio
value=
{
false
}
disabled=
{
hidden
}
>
关闭
</
Radio
>
<
Radio
value=
{
true
}
>
开启
</
Radio
>
<
Radio
value=
{
false
}
>
关闭
</
Radio
>
</
Radio
.
Group
>
</
Item
>
<
Item
...
...
@@ -360,7 +321,12 @@ const AddModal = props => {
name=
"Priority"
labelCol=
{
{
span
:
12
}
}
>
<
InputNumber
min=
{
0
}
max=
{
10
}
defaultValue=
{
0
}
disabled=
{
hidden
}
/>
<
InputNumber
min=
{
0
}
max=
{
10
}
defaultValue=
{
0
}
disabled=
{
pickItem
.
remark
===
'万能模板'
}
/>
</
Item
>
</
Col
>
<
Col
span=
{
16
}
>
...
...
This diff is collapsed.
Click to expand it.
src/pages/platformCenter/hostmanager/gateWay/gateWay.jsx
View file @
d18f2d51
/* eslint-disable indent */
/* eslint-disable no-unused-expressions */
/* eslint-disable no-else-return */
import
React
,
{
useEffect
,
useState
}
from
'react'
;
...
...
@@ -28,12 +29,15 @@ import {
}
from
'@ant-design/icons'
;
import
{
useHistory
}
from
'react-router-dom'
;
import
styles
from
'./gateWay.less'
;
import
axios
from
'axios'
;
import
{
GetGateWay
,
UpdateGeteWay
,
GetReRoutes
,
GetReRoutesFirst
,
DelRoutes
,
DelRouteByUpUrl
,
GetWayHealthCheck
,
}
from
'@/services/hostmanager/hostmanager'
;
import
{
get
,
PUBLISH_SERVICE
}
from
'@/services/index'
;
import
configuration
from
'../../../../assets/images/icons/消息.svg'
;
...
...
@@ -43,6 +47,7 @@ import CheckModal from './CheckModal';
const
GateConfig
=
()
=>
{
const
[
loading
,
setLoading
]
=
useState
(
false
);
// 加载
const
[
allLoading
,
setAllLoading
]
=
useState
(
false
);
// 加载
const
[
form
]
=
Form
.
useForm
();
const
[
flag
,
setFlag
]
=
useState
(
1
);
...
...
@@ -63,6 +68,17 @@ const GateConfig = () => {
localStorage
.
setItem
(
'panda-publish'
,
''
);
console
.
log
(
checked
);
if
(
checked
)
{
setAllLoading
(
true
);
// axios({
// method: 'get',
// url: `${tableData[0].url}/PandaOMS/OMS/health/get`,
// }).then(res => {
// console.log(res);
// debugger;
// });
GetWayHealthCheck
().
then
(
resData
=>
{
setAllLoading
(
false
);
if
(
resData
.
code
===
0
)
{
UpdateGeteWay
({
isUsed
:
checked
}).
then
(
res
=>
{
if
(
res
.
code
===
0
)
{
console
.
log
(
localStorage
.
getItem
(
'token'
));
...
...
@@ -127,6 +143,20 @@ const GateConfig = () => {
}
});
},
0
);
}
else
{
message
.
warning
({
content
:
(
<>
万能模板下游服务 [
<
span
style=
{
{
color
:
'#1890ff'
}
}
>
{
tableData
[
0
].
url
}
</
span
>
]
检测不通过,请修复后再开启!
</>
),
style
:
{
marginTop
:
'10vh'
,
},
});
}
});
}
else
{
UpdateGeteWay
({
isUsed
:
checked
}).
then
(
res
=>
{
if
(
res
.
code
===
0
)
{
...
...
@@ -213,46 +243,13 @@ const GateConfig = () => {
align
:
'center'
,
width
:
150
,
render
:
(
text
,
record
)
=>
{
if
(
record
.
key
===
'CityServer'
)
{
if
(
record
.
remark
)
{
return
(
<
Tooltip
placement=
"top"
title=
{
text
}
>
<
span
>
{
searchStyle1
(
text
)
}{
' '
}
<
Tag
color=
"cyan"
style=
{
{
marginRight
:
'0px'
}
}
>
万能模板
</
Tag
>
</
span
>
</
Tooltip
>
);
}
else
if
(
record
.
key
===
'IOT'
)
{
return
(
<
Tooltip
placement=
"top"
title=
{
text
}
>
<
span
>
{
searchStyle1
(
text
)
}{
' '
}
<
Tag
color=
"purple"
style=
{
{
marginRight
:
'0px'
}
}
>
物联
</
Tag
>
</
span
>
</
Tooltip
>
);
}
else
if
(
record
.
key
===
'CivData'
)
{
return
(
<
Tooltip
placement=
"top"
title=
{
text
}
>
<
span
>
{
searchStyle1
(
text
)
}{
' '
}
<
Tag
color=
"green"
style=
{
{
marginRight
:
'0px'
}
}
>
中台
</
Tag
>
</
span
>
</
Tooltip
>
);
}
else
if
(
record
.
key
===
'GIS'
)
{
return
(
<
Tooltip
placement=
"top"
title=
{
text
}
>
<
span
>
{
searchStyle1
(
text
)
}{
' '
}
<
Tag
color=
"volcano"
style=
{
{
marginRight
:
'0px'
}
}
>
GIS
{
record
.
remark
}
</
Tag
>
</
span
>
</
Tooltip
>
...
...
@@ -273,13 +270,24 @@ const GateConfig = () => {
dataIndex
:
'upstreamPathTemplate'
,
key
:
'upstreamPathTemplate'
,
align
:
'center'
,
render
:
(
text
,
record
)
=>
(
render
:
(
text
,
record
)
=>
{
let
str
=
window
.
location
.
origin
+
`/PandaCore/GateWay
${
text
}
`
;
let
list
=
str
.
split
(
':'
);
list
[
0
]
=
record
.
downstreamScheme
;
let
data
=
record
.
downstreamScheme
;
list
.
forEach
((
i
,
j
)
=>
{
if
(
j
>
0
)
{
data
=
data
+
':'
+
i
;
}
});
return
(
<
span
>
<
Tooltip
placement=
"top"
title=
{
`上游路由模板:${text}`
}
>
{
window
.
location
.
origin
+
`/PandaCore/GateWay${text}`
}
{
data
}
</
Tooltip
>
</
span
>
),
);
},
},
// {
// title: '上游路由模板',
...
...
@@ -377,13 +385,13 @@ const GateConfig = () => {
align
:
'center'
,
fixed
:
'right'
,
render
:
record
=>
{
if
(
record
.
key
!=
'CityServer'
)
{
//
if (record.key != 'CityServer') {
return
(
<
Space
size=
"middle"
>
<
Tooltip
title=
"编辑"
>
<
EditTwoTone
onClick=
{
()
=>
edit
(
record
)
}
style=
{
{
fontSize
:
'16px'
}
}
/>
</
Tooltip
>
{
record
.
key
!=
'IOT'
&&
record
.
key
!=
'CivData'
&&
record
.
key
!=
'GIS'
&&
(
{
!
record
.
remark
&&
(
<
Tooltip
title=
"删除"
>
<
Popconfirm
placement=
"bottomRight"
...
...
@@ -403,15 +411,15 @@ const GateConfig = () => {
)
}
</
Space
>
);
}
else
{
return
(
<
Space
size=
"middle"
>
<
Tooltip
title=
"查看"
>
<
SearchOutlined
onClick=
{
()
=>
look
(
record
)
}
style=
{
{
fontSize
:
'16px'
}
}
/>
</
Tooltip
>
</
Space
>
);
}
//
} else {
//
return (
//
<Space size="middle">
//
<Tooltip title="查看">
//
<SearchOutlined onClick={() => look(record)} style={{ fontSize: '16px' }} />
//
</Tooltip>
//
</Space>
//
);
//
}
},
},
];
...
...
@@ -508,8 +516,8 @@ const GateConfig = () => {
description
:
'默认数据无法删除'
,
});
}
else
{
DelRoute
s
({
Ids
:
e
.
id
,
DelRoute
ByUpUrl
({
upTemplate
:
e
.
upstreamPathTemplate
,
}).
then
(
res
=>
{
if
(
res
.
code
===
0
)
{
GetReRoutes
({
...
...
@@ -549,6 +557,7 @@ const GateConfig = () => {
return
(
<
div
className=
{
styles
.
gateWay_container
}
>
<
Spin
spinning=
{
allLoading
}
tip=
"loading"
>
<
Card
style=
{
{
width
:
'100%'
,
height
:
'calc(100vh - 130px)'
}
}
>
<
div
style=
{
{
display
:
'flex'
,
alignItems
:
'center'
,
marginTop
:
'10px'
}
}
>
<
img
src=
{
configuration
}
style=
{
{
height
:
'16px'
}
}
alt=
""
/>
...
...
@@ -633,13 +642,12 @@ const GateConfig = () => {
scroll=
{
{
y
:
'calc(100vh - 380px)'
,
x
:
'max-content'
}
}
onRow=
{
record
=>
({
onDoubleClick
:
event
=>
{
console
.
log
(
record
);
event
.
stopPropagation
();
if
(
record
.
key
==
'CityServer'
)
{
look
(
record
);
}
else
{
//
if (record.key == 'CityServer')
{
//
look(record);
//
}
else
{
edit
(
record
);
}
//
}
},
// 双击
})
}
pagination=
{
{
...
...
@@ -667,6 +675,7 @@ const GateConfig = () => {
onCancel=
{
()
=>
setCheckVisible
(
false
)
}
/>
</
Card
>
</
Spin
>
</
div
>
);
};
...
...
This diff is collapsed.
Click to expand it.
src/pages/productCenter/webConfig/components/HomeConfigModal.jsx
View file @
d18f2d51
...
...
@@ -42,13 +42,17 @@ const HomeConfigModal = props => {
let
obj
=
{
homePage
:
form
.
getFieldsValue
().
homePage
,
productType
:
''
};
let
arrUrl
=
obj
.
homePage
.
split
(
'/'
);
// 用const声明常量
const
product
=
allProductList
.
find
(
item
=>
item
.
PackageName
.
includes
(
arrUrl
[
0
]));
let
allProList
=
JSON
.
parse
(
JSON
.
stringify
(
allProductList
));
allProList
.
push
({
PackageName
:
'civ_base'
});
const
product
=
allProList
.
find
(
item
=>
item
.
PackageName
.
includes
(
arrUrl
[
0
]));
if
(
!
product
)
{
// arrUrl.shift();
obj
.
homePage
=
`civweb4/
${
obj
.
homePage
}
`
;
}
obj
.
productType
=
product
?.
PackageName
||
'civweb4'
;
if
(
!
productList
.
some
(
item
=>
item
.
PackageName
===
obj
.
productType
))
{
let
proList
=
JSON
.
parse
(
JSON
.
stringify
(
productList
));
proList
.
push
({
PackageName
:
'civ_base'
});
if
(
!
proList
.
some
(
item
=>
item
.
PackageName
===
obj
.
productType
))
{
message
.
error
(
`
${
obj
.
productType
}
未授权,不能使用该功能当主页`
);
return
;
}
...
...
This diff is collapsed.
Click to expand it.
src/pages/productCenter/webConfig/components/siteConfigDrawer.js
View file @
d18f2d51
...
...
@@ -272,15 +272,18 @@ export default props => {
console
.
log
(
validate
);
if
(
validate
.
homePage
)
{
let
arr
=
validate
.
homePage
.
split
(
'/'
);
// 用const声明常量
const
product
=
allProductList
.
find
(
item
=>
item
.
PackageName
.
includes
(
arr
[
0
]));
let
allProList
=
JSON
.
parse
(
JSON
.
stringify
(
allProductList
));
allProList
.
push
({
PackageName
:
'civ_base'
});
const
product
=
allProList
.
find
(
item
=>
item
.
PackageName
.
includes
(
arr
[
0
]));
if
(
product
)
{
arr
.
shift
();
validate
.
homePage
=
arr
.
join
(
'/'
);
}
console
.
log
(
product
,
'product'
);
validate
.
productType
=
product
?.
PackageName
||
'civweb4'
;
if
(
!
productList
.
some
(
item
=>
item
.
PackageName
===
validate
.
productType
))
{
let
proList
=
JSON
.
parse
(
JSON
.
stringify
(
productList
));
proList
.
push
({
PackageName
:
'civ_base'
});
if
(
!
proList
.
some
(
item
=>
item
.
PackageName
===
validate
.
productType
))
{
message
.
error
(
`
${
validate
.
productType
}
未授权,不能使用该功能当主页`
);
return
;
}
...
...
This diff is collapsed.
Click to expand it.
src/pages/productCenter/webConfig/menuconfig/AddForm.jsx
View file @
d18f2d51
import
React
,
{
useState
}
from
'react'
;
import
{
getConfigContent
}
from
'@/services/webConfig/api'
;
import
{
Form
,
Input
,
Button
,
Select
,
Radio
,
message
}
from
'antd'
;
import
styles
from
'./addForm.less'
;
import
PicturesWall
from
'@/components/Upload/index'
;
...
...
@@ -20,6 +21,7 @@ const AddForm = props => {
const
[
curretnMenu
,
setCurrentMenu
]
=
useState
({});
// 当前选中菜单数据
const
[
form
]
=
Form
.
useForm
();
const
[
otherForm
]
=
Form
.
useForm
();
const
[
configContent
,
setConfigContent
]
=
useState
(
''
);
// 配置文件内容
const
layout
=
{
layout
:
'horizontal'
,
labelCol
:
{
span
:
4
,
offset
:
1
},
...
...
@@ -29,19 +31,31 @@ const AddForm = props => {
if
(
addType
===
1
)
{
let
obj
=
form
.
getFieldsValue
();
let
arr
=
obj
.
pageUrl
.
split
(
'/'
);
// 用const声明常量
const
product
=
allProductList
.
find
(
item
=>
item
.
PackageName
.
includes
(
arr
[
0
]));
if
(
!
arr
[
0
])
{
arr
.
shift
();
}
let
allProList
=
JSON
.
parse
(
JSON
.
stringify
(
allProductList
));
allProList
.
push
({
PackageName
:
'civ_base'
});
const
product
=
allProList
.
find
(
item
=>
item
.
PackageName
.
includes
(
arr
[
0
]));
if
(
product
)
{
if
(
arr
.
length
>
1
)
{
arr
.
shift
();
}
obj
.
pageUrl
=
arr
.
join
(
'/'
);
}
console
.
log
(
product
,
'product'
);
obj
.
product
=
product
?.
PackageName
||
'civweb4'
;
if
(
!
productList
.
some
(
item
=>
item
.
PackageName
===
obj
.
product
))
{
let
proList
=
JSON
.
parse
(
JSON
.
stringify
(
productList
));
proList
.
push
({
PackageName
:
'civ_base'
});
if
(
!
proList
.
some
(
item
=>
item
.
PackageName
===
obj
.
product
))
{
message
.
error
(
`
${
obj
.
product
}
未授权,不能使用该功能`
);
return
;
}
obj
.
codeParam
=
JSON
.
stringify
(
curretnMenu
.
param
);
obj
.
code
=
curretnMenu
.
code
;
obj
.
configContent
=
configContent
;
console
.
log
(
obj
,
'obj'
);
submitCallback
(
obj
,
nodeObj
);
}
if
(
addType
===
2
)
{
...
...
@@ -62,7 +76,24 @@ const AddForm = props => {
menuName
:
val
?.
function
,
shortName
:
val
?.
shortName
,
imageUrl
:
val
?.
icon
,
config
:
val
?.
configName
,
configName
:
val
?.
configName
,
});
setConfigContent
(
val
.
configContent
);
};
const
wrapperFinish
=
val
=>
{
setConfigContent
(
val
);
};
// 选择配置文件获取到配置文件内容
const
selectConfig
=
val
=>
{
console
.
log
(
val
);
getConfigContent
(
val
)
.
then
(
res
=>
{
if
(
res
.
code
===
0
)
{
setConfigContent
(
res
.
data
);
}
})
.
catch
(
e
=>
{
console
.
error
(
e
);
});
};
return
(
...
...
@@ -133,11 +164,12 @@ const AddForm = props => {
{
/* <Input placeholder="请输入功能路径" /> */
}
<
TreeSelect
menuChange=
{
val
=>
menuChange
(
val
)
}
/>
</
Item
>
<
Item
label=
"配置文件"
name=
"config"
>
<
EditeConfigWrapper
>
<
Item
label=
"配置文件"
name=
"config
Name
"
>
<
EditeConfigWrapper
onFinish=
{
wrapperFinish
}
configContent=
{
configContent
}
>
<
Select
allowClear
showSearch
onSelect=
{
selectConfig
}
filterOption=
{
(
input
,
option
)
=>
option
.
children
.
toLowerCase
().
indexOf
(
input
.
toLowerCase
())
>=
0
}
...
...
This diff is collapsed.
Click to expand it.
src/pages/productCenter/webConfig/menuconfig/ParmarsModal.jsx
View file @
d18f2d51
import
React
,
{
useState
,
useRef
,
useContext
,
useEffect
}
from
'react'
;
import
{
Modal
,
Form
,
Table
,
Input
,
Button
,
message
}
from
'antd'
;
import
{
Modal
,
Form
,
Table
,
Input
,
Button
,
message
,
Select
}
from
'antd'
;
import
{
DeleteOutlined
}
from
'@ant-design/icons'
;
import
styles
from
'./ParmarsModal.less'
;
const
EditableContext
=
React
.
createContext
(
null
);
...
...
@@ -67,6 +67,37 @@ const EditableCell = ({
},
];
let
rules
=
dataIndex
===
'field'
?
keyNameRule
:
[];
const
rendeFrom
=
()
=>
{
if
(
dataIndex
===
'defaultValue'
&&
record
.
type
===
'选择型'
)
{
let
options
=
record
.
valueDesc
.
split
(
';'
)
.
map
(
item
=>
({
label
:
item
.
split
(
':'
)[
1
],
value
:
item
.
split
(
':'
)[
0
]
}));
return
(
<
Select
// showSearch
placeholder=
"选择值"
optionFilterProp=
"children"
optionLabelProp=
"value"
// filterOption={(input, option) =>
// (option?.label ?? '').toLowerCase().includes(input.toLowerCase())
// }
options=
{
options
}
ref=
{
inputRef
}
onPressEnter=
{
save
}
onBlur=
{
save
}
/>
);
}
return
(
<
Input
readOnly=
{
record
.
releaseDate
&&
dataIndex
!==
'defaultValue'
}
ref=
{
inputRef
}
onPressEnter=
{
save
}
onBlur=
{
save
}
/>
);
};
if
(
editable
)
{
childNode
=
editing
?
(
<
Form
.
Item
...
...
@@ -76,12 +107,7 @@ const EditableCell = ({
name=
{
dataIndex
}
rules=
{
rules
}
>
<
Input
readOnly=
{
record
.
name
&&
dataIndex
!==
'defaultValue'
}
ref=
{
inputRef
}
onPressEnter=
{
save
}
onBlur=
{
save
}
/>
{
rendeFrom
()
}
</
Form
.
Item
>
)
:
(
<
div
className=
"editable-cell-value-wrap"
onClick=
{
toggleEdit
}
>
...
...
@@ -173,7 +199,7 @@ const ParmarsModal = props => {
field
:
item
.
field
,
defaultValue
:
item
.
defaultValue
,
paramDesc
:
item
.
paramDesc
,
valueDesc
:
item
.
valueDesc
,
name
:
item
.
name
,
}));
if
(
parma
)
{
parmarCallBack
(
`
${
pageUrl
.
split
(
'|'
)[
0
]}
|
${
parma
}
`
,
parmas
);
...
...
@@ -195,30 +221,32 @@ const ParmarsModal = props => {
width
:
50
,
render
:
(
text
,
record
,
index
)
=>
<
span
>
{
index
+
1
}
</
span
>,
},
{
title
:
'配置项'
,
dataIndex
:
'name'
,
width
:
250
,
editable
:
true
,
},
{
title
:
'参数名'
,
dataIndex
:
'field'
,
width
:
150
,
editable
:
true
,
},
{
title
:
'参数值'
,
dataIndex
:
'defaultValue'
,
width
:
150
,
editable
:
true
,
},
{
title
:
'参数
名描述
'
,
title
:
'参数
配置说明
'
,
dataIndex
:
'paramDesc'
,
width
:
250
,
editable
:
true
,
},
{
title
:
'参数值描述'
,
dataIndex
:
'valueDesc'
,
width
:
250
,
editable
:
true
,
},
{
title
:
'操作'
,
...
...
@@ -246,7 +274,7 @@ const ParmarsModal = props => {
field
:
''
,
defaultValue
:
''
,
paramDesc
:
''
,
valueDesc
:
''
,
name
:
''
,
};
console
.
log
(
newData
,
'newData'
);
setDataSource
([...
dataSource
,
newData
]);
...
...
This diff is collapsed.
Click to expand it.
src/pages/productCenter/webConfig/menuconfig/ParmarsModal.less
View file @
d18f2d51
...
...
@@ -43,7 +43,7 @@
// width: 180px;
padding: 0 5px;
background-color: #ff4d4f;
top:
40
px;
top:
-45
px;
left: 50%;
transform: translateX(-50%);
border-radius: 5px;
...
...
This diff is collapsed.
Click to expand it.
src/pages/productCenter/webConfig/menuconfig/TreeSelect.jsx
View file @
d18f2d51
...
...
@@ -2,7 +2,7 @@ import React, { useState, useEffect, forwardRef, useImperativeHandle } from 'rea
import
{
GetWebMenuInfo
}
from
'@/services/webConfig/api'
;
import
{
message
,
Input
,
Tree
,
Empty
,
Tooltip
}
from
'antd'
;
import
lodash
from
'lodash'
;
import
{
FolderFilled
,
FileOutlined
,
InfoCircleOutlined
}
from
'@ant-design/icons'
;
import
{
FolderFilled
,
FileOutlined
,
InfoCircleOutlined
,
HomeOutlined
}
from
'@ant-design/icons'
;
import
styles
from
'./TreeSelect.less'
;
const
TreeSelect
=
(
props
,
ref
)
=>
{
const
{
value
,
onChange
,
menuChange
,
code
,
initCurrentMenu
}
=
props
;
...
...
@@ -25,7 +25,7 @@ const TreeSelect = (props, ref) => {
setTreeList
(
tree
);
setMenuWebList
(
res
.
data
.
map
(
item
=>
{
let
obj
=
{
...
item
,
path
:
`
${
item
.
pa
ckName
}
/
${
item
.
pa
th
}
`
};
let
obj
=
{
...
item
,
path
:
`
${
item
.
path
}
`
};
if
(
item
.
code
===
code
)
{
setCurrentMenuParmar
(
obj
);
initCurrentMenu
(
obj
);
...
...
@@ -97,7 +97,7 @@ const TreeSelect = (props, ref) => {
</
Tooltip
>
</
div
>
),
icon
:
<
FileOutlined
/>,
icon
:
item
.
isHomePage
?
<
HomeOutlined
/>
:
<
FileOutlined
/>,
})),
})),
});
...
...
This diff is collapsed.
Click to expand it.
src/pages/productCenter/webConfig/menuconfig/editConfigFileWrapper.js
View file @
d18f2d51
...
...
@@ -7,7 +7,7 @@ import 'ace-builds/src-noconflict/mode-json';
import
'ace-builds/src-noconflict/theme-solarized_light'
;
const
ConfigWrapper
=
props
=>
{
const
{
value
,
children
,
onChange
,
id
,
...
rest
}
=
props
;
const
{
value
,
children
,
onChange
,
id
,
onFinish
,
configContent
,
...
rest
}
=
props
;
const
[
text
,
setText
]
=
useState
(
''
);
const
[
visible
,
setVisible
]
=
useState
(
false
);
...
...
@@ -19,15 +19,16 @@ const ConfigWrapper = props => {
});
return
;
}
getConfigContent
(
value
)
.
then
(
res
=>
{
if
(
res
.
code
===
0
)
{
setText
(
res
.
data
);
}
})
.
catch
(
e
=>
{
console
.
error
(
e
);
});
setText
(
configContent
);
// getConfigContent(value)
// .then(res => {
// if (res.code === 0) {
// setText(res.data);
// }
// })
// .catch(e => {
// console.error(e);
// });
setVisible
(
true
);
};
...
...
@@ -44,20 +45,22 @@ const ConfigWrapper = props => {
err
=
true
;
}
if
(
!
err
)
{
saveConfigContent
(
value
,
JSON
.
stringify
(
JSON
.
parse
(
text
),
null
,
4
),
id
)
.
then
(
res
=>
{
if
(
res
.
code
===
0
)
{
notification
.
success
({
message
:
'保存成功'
,
duration
:
3
,
});
// saveConfigContent(value, JSON.stringify(JSON.parse(text), null, 4), id)
// .then(res => {
// if (res.code === 0) {
// notification.success({
// message: '保存成功',
// duration: 3,
// });
// setVisible(false);
// }
// })
// .catch(error => {
// console.error(error);
// });
onFinish
(
JSON
.
stringify
(
JSON
.
parse
(
text
),
null
,
4
));
setVisible
(
false
);
}
})
.
catch
(
error
=>
{
console
.
error
(
error
);
});
}
};
const
handleChange
=
v
=>
{
...
...
This diff is collapsed.
Click to expand it.
src/pages/productCenter/webConfig/menuconfig/editForm.jsx
View file @
d18f2d51
...
...
@@ -22,7 +22,8 @@ import {
FolderFilled
,
FileOutlined
,
}
from
'@ant-design/icons'
;
import
{
GetWebMenuInfo
}
from
'@/services/webConfig/api'
;
import
{
GetWebMenuInfo
,
getConfigContent
}
from
'@/services/webConfig/api'
;
import
classnames
from
'classnames'
;
import
styles
from
'./addForm.less'
;
import
PicturesWall
from
'@/components/Upload/index'
;
...
...
@@ -59,6 +60,7 @@ const EditForm = props => {
const
[
menuParmar
,
setMenuParmar
]
=
useState
([]);
const
[
codeParmar
,
setCodeParmar
]
=
useState
([]);
const
[
curretnMenu
,
setCurrentMenu
]
=
useState
({});
// 当前选中菜单数据
const
[
configContent
,
setConfigContent
]
=
useState
(
''
);
// 配置文件内容
const
treeSelectRef
=
useRef
();
const
layout
=
{
layout
:
'horizontal'
,
...
...
@@ -74,6 +76,7 @@ const EditForm = props => {
setMenuParmar
([]);
}
setCodeParmar
(
infoAll
.
codeParam
?
JSON
.
parse
(
infoAll
.
codeParam
)
:
[]);
setConfigContent
(
infoAll
.
configContent
);
setCurrentMenu
({});
form
.
resetFields
();
otherForm
.
resetFields
();
...
...
@@ -150,22 +153,31 @@ const EditForm = props => {
}
let
arr
=
obj
.
pageUrl
.
split
(
'/'
);
// 用const声明常量
const
product
=
allProductList
.
find
(
item
=>
item
.
PackageName
.
includes
(
arr
[
0
]));
if
(
!
arr
[
0
])
{
arr
.
shift
();
}
let
allProList
=
JSON
.
parse
(
JSON
.
stringify
(
allProductList
));
allProList
.
push
({
PackageName
:
'civ_base'
});
const
product
=
allProList
.
find
(
item
=>
item
.
PackageName
.
includes
(
arr
[
0
]));
console
.
log
(
product
,
'product'
);
if
(
product
)
{
arr
.
shift
();
obj
.
pageUrl
=
arr
.
join
(
'/'
);
}
obj
.
product
=
product
?.
PackageName
||
'civweb4'
;
if
(
!
productList
.
some
(
item
=>
item
.
PackageName
===
obj
.
product
))
{
console
.
log
(
productList
,
'productList'
);
let
proList
=
JSON
.
parse
(
JSON
.
stringify
(
productList
));
proList
.
push
({
PackageName
:
'civ_base'
});
if
(
!
proList
.
some
(
item
=>
item
.
PackageName
===
obj
.
product
))
{
message
.
error
(
`
${
obj
.
product
}
未授权,不能使用该功能`
);
return
;
}
}
obj
.
codeParam
=
JSON
.
stringify
(
curretnMenu
.
param
);
obj
.
code
=
curretnMenu
.
code
;
obj
.
configContent
=
configContent
;
submitCallback
(
obj
);
};
...
...
@@ -332,14 +344,30 @@ const EditForm = props => {
menuName
:
val
?.
function
,
shortName
:
val
?.
shortName
,
imageUrl
:
val
?.
icon
,
config
:
val
?.
configName
,
config
Name
:
val
?.
configName
,
pageUrl
:
val
?.
pageUrl
,
});
setConfigContent
(
val
.
configContent
);
setCurrentMenu
(
val
);
setMenuParmar
(
val
?.
param
);
};
console
.
log
(
curretnMenu
?.
param
,
'curretnMenu.param'
);
// 编辑完配置文件后回调
const
wrapperFinish
=
val
=>
{
setConfigContent
(
val
);
};
// 选择配置文件获取到配置文件内容
const
selectConfig
=
val
=>
{
console
.
log
(
val
);
getConfigContent
(
val
)
.
then
(
res
=>
{
if
(
res
.
code
===
0
)
{
setConfigContent
(
res
.
data
);
}
})
.
catch
(
e
=>
{
console
.
error
(
e
);
});
};
return
(
<
div
style=
{
{
marginTop
:
'10px'
}
}
>
{
nodeType
===
1
&&
(
...
...
@@ -426,11 +454,16 @@ const EditForm = props => {
<
Button
onClick=
{
addParama
}
>
参数管理
</
Button
>
</
div
>
</
Item
>
<
Item
label=
"配置文件"
name=
"config"
style=
{
{
marginLeft
:
'11px'
}
}
>
<
EditeConfigWrapper
id=
{
infoAll
.
menuID
}
>
<
Item
label=
"配置文件"
name=
"configName"
style=
{
{
marginLeft
:
'11px'
}
}
>
<
EditeConfigWrapper
id=
{
infoAll
.
menuID
}
configContent=
{
configContent
}
onFinish=
{
wrapperFinish
}
>
<
Select
allowClear
showSearch
onSelect=
{
selectConfig
}
filterOption=
{
(
input
,
option
)
=>
option
.
children
.
toLowerCase
().
indexOf
(
input
.
toLowerCase
())
>=
0
}
...
...
This diff is collapsed.
Click to expand it.
src/services/hostmanager/hostmanager.js
View file @
d18f2d51
...
...
@@ -80,9 +80,14 @@ export const GetGateWay = param => get(`${PUBLISH_SERVICE}/HostManager/GetGateWa
export
const
UpdateGeteWay
=
param
=>
get
(
`
${
PUBLISH_SERVICE
}
/HostManager/UpdateGeteWay`
,
param
);
// 网关配置
// export const GetReRoutesFirst = param => get(`/OcelotSettings/GetReRoutes`, param);
export
const
GetWayHealthCheck
=
param
=>
get
(
`/PandaCore/GateWay/OcelotSettings/HealthCheck`
,
param
);
export
const
GetReRoutes
=
param
=>
get
(
`/PandaCore/GateWay/OcelotSettings/GetReRoutes`
,
param
);
export
const
SaveRoutes
=
param
=>
post
(
`/PandaCore/GateWay/OcelotSettings/SaveRoutes`
,
param
);
export
const
DelRoutes
=
param
=>
get
(
`/PandaCore/GateWay/OcelotSettings/DelRoutes`
,
param
);
export
const
DelRouteByUpUrl
=
param
=>
get
(
`/PandaCore/GateWay/OcelotSettings/DelRouteByUpUrl`
,
param
);
// 代理服务老接口
export
const
GetNginxConfigInfoOLD
=
param
=>
...
...
This diff is collapsed.
Click to expand it.
src/services/webConfig/api.js
View file @
d18f2d51
...
...
@@ -337,3 +337,5 @@ export const GetRoleGroups = param => get(`${PUBLISH_SERVICE}/UserCenter/GetRole
export
const
GetWebMenuInfo
=
param
=>
get
(
`
${
PUBLISH_SERVICE
}
/WebSite/GetWebMenuInfo`
,
param
);
export
const
SortScheme
=
param
=>
get
(
`
${
PANDA_GIS
}
/MapLayer/SortScheme`
,
param
);
export
const
SortSchemePost
=
param
=>
post
(
`
${
PANDA_GIS
}
/MapLayer/SortSchemePost`
,
param
);
This diff is collapsed.
Click to expand it.
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