Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
P
pro-layout
Project
Project
Details
Activity
Releases
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
packages
pro-layout
Commits
c6c2a115
Commit
c6c2a115
authored
Oct 03, 2021
by
Sendya
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: FooterToolbar auto margin
parent
0f46541a
Changes
8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
427 additions
and
310 deletions
+427
-310
README.md
README.md
+8
-2
BasicLayout.vue
examples/layouts/BasicLayout.vue
+16
-25
BasicLayout.tsx
src/BasicLayout.tsx
+158
-82
index.tsx
src/FooterToolbar/index.tsx
+53
-51
RouteContext.tsx
src/RouteContext.tsx
+69
-48
SiderMenu.tsx
src/SiderMenu/SiderMenu.tsx
+96
-79
index.less
src/SiderMenu/index.less
+1
-0
index.tsx
src/WaterMark/index.tsx
+26
-23
No files found.
README.md
View file @
c6c2a115
...
@@ -224,8 +224,14 @@ const layoutConf = reactive({
...
@@ -224,8 +224,14 @@ const layoutConf = reactive({
</
template
>
</
template
>
```
```
### Use WaterMark
```
vue
<router-view
v-slot=
"{ Component }"
>
<WaterMark
content=
"Pro Layout"
>
<component
:is=
"Component"
/>
</WaterMark>
</router-view>
```
## Build project
## Build project
...
...
examples/layouts/BasicLayout.vue
View file @
c6c2a115
...
@@ -6,7 +6,7 @@
...
@@ -6,7 +6,7 @@
v-bind=
"state"
v-bind=
"state"
:loading=
"loading"
:loading=
"loading"
:breadcrumb=
"
{ routes: breadcrumb }"
:breadcrumb=
"
{ routes: breadcrumb }"
iconfont-url="//at.alicdn.com/t/font_2804900_
26tw52dc2pl
.js"
iconfont-url="//at.alicdn.com/t/font_2804900_
nzigh7z84gc
.js"
>
>
<template
#
menuHeaderRender
>
<template
#
menuHeaderRender
>
<a>
<a>
...
@@ -39,7 +39,7 @@
...
@@ -39,7 +39,7 @@
</router-link>
</router-link>
</
template
>
</
template
>
<
template
#
menuExtraRender=
"{ collapsed }"
>
<
template
#
menuExtraRender=
"{ collapsed }"
>
<a-input-search
v-if=
"!collapsed"
/>
<a-input-search
v-if=
"!collapsed"
@
search=
"handleSearch"
/>
</
template
>
</
template
>
<
template
#
menuFooterRender
>
<
template
#
menuFooterRender
>
<a
<a
...
@@ -67,26 +67,13 @@
...
@@ -67,26 +67,13 @@
</a>
</a>
</
template
>
</
template
>
<!-- custom menu-item -->
<
template
#
menuItemRender=
"{ item, icon }"
>
<a-menu-item
:key=
"item.path"
:disabled=
"item.meta?.disabled"
:danger=
"item.meta?.danger"
:icon=
"icon"
>
<router-link
:to=
"
{ path: item.path }">
<span
class=
"ant-pro-menu-item"
>
<a-badge
count=
"5"
dot
>
<span
class=
"ant-pro-menu-item-title"
>
{{
item
.
meta
.
title
}}
</span>
</a-badge>
</span>
</router-link>
</a-menu-item>
</
template
>
<!-- content begin -->
<!-- content begin -->
<router-view
/>
<router-view
v-slot=
"{ Component }"
>
<WaterMark
content=
"Pro Layout"
>
<component
:is=
"Component"
/>
</WaterMark>
</router-view>
<!-- content end -->
<!-- content end -->
<FooterToolbar>
<FooterToolbar>
<
template
#
extra
>
<
template
#
extra
>
...
@@ -120,8 +107,8 @@
...
@@ -120,8 +107,8 @@
<
script
lang=
"ts"
>
<
script
lang=
"ts"
>
import
{
computed
,
defineComponent
,
reactive
,
ref
,
watchEffect
}
from
'vue'
;
import
{
computed
,
defineComponent
,
reactive
,
ref
,
watchEffect
}
from
'vue'
;
import
{
useRouter
}
from
'vue-router'
;
import
{
useRouter
}
from
'vue-router'
;
import
{
Button
,
Input
,
Switch
,
Select
,
Avatar
,
Space
,
Badge
,
Menu
}
from
'ant-design-vue'
;
import
{
message
,
Button
,
Input
,
Switch
,
Select
,
Avatar
,
Space
,
Badge
,
Menu
}
from
'ant-design-vue'
;
import
{
getMenuData
,
clearMenuItem
,
FooterToolbar
}
from
'@ant-design-vue/pro-layout'
;
import
{
getMenuData
,
clearMenuItem
,
WaterMark
,
FooterToolbar
}
from
'@ant-design-vue/pro-layout'
;
import
type
{
RouteContextProps
}
from
'@ant-design-vue/pro-layout'
;
import
type
{
RouteContextProps
}
from
'@ant-design-vue/pro-layout'
;
const
i18n
=
(
t
:
string
)
=>
t
;
const
i18n
=
(
t
:
string
)
=>
t
;
...
@@ -130,6 +117,8 @@ export default defineComponent({
...
@@ -130,6 +117,8 @@ export default defineComponent({
name
:
'BasicLayout'
,
name
:
'BasicLayout'
,
components
:
{
components
:
{
FooterToolbar
,
FooterToolbar
,
WaterMark
,
[
Button
.
name
]:
Button
,
[
Button
.
name
]:
Button
,
[
Input
.
name
]:
Input
,
[
Input
.
name
]:
Input
,
[
Input
.
Search
.
name
]:
Input
.
Search
,
[
Input
.
Search
.
name
]:
Input
.
Search
,
...
@@ -137,7 +126,6 @@ export default defineComponent({
...
@@ -137,7 +126,6 @@ export default defineComponent({
[
Select
.
name
]:
Select
,
[
Select
.
name
]:
Select
,
[
Select
.
Option
.
displayName
!
]:
Select
.
Option
,
[
Select
.
Option
.
displayName
!
]:
Select
.
Option
,
[
Space
.
name
]:
Space
,
[
Space
.
name
]:
Space
,
[
Badge
.
name
]:
Badge
,
[
Badge
.
name
]:
Badge
,
[
Avatar
.
name
]:
Avatar
,
[
Avatar
.
name
]:
Avatar
,
[
Menu
.
Item
.
name
]:
Menu
.
Item
,
[
Menu
.
Item
.
name
]:
Menu
.
Item
,
...
@@ -161,7 +149,7 @@ export default defineComponent({
...
@@ -161,7 +149,7 @@ export default defineComponent({
// title: 'ProLayout',
// title: 'ProLayout',
// logo: 'https://alicdn.antdv.com/v2/assets/logo.1ef800a8.svg',
// logo: 'https://alicdn.antdv.com/v2/assets/logo.1ef800a8.svg',
navTheme
:
'dark'
,
navTheme
:
'dark'
,
layout
:
'
mix
'
,
layout
:
'
side
'
,
fixSiderbar
:
true
,
fixSiderbar
:
true
,
});
});
const
breadcrumb
=
computed
(()
=>
const
breadcrumb
=
computed
(()
=>
...
@@ -202,6 +190,9 @@ export default defineComponent({
...
@@ -202,6 +190,9 @@ export default defineComponent({
handlePageLoadingClick
,
handlePageLoadingClick
,
handleCollapsed
,
handleCollapsed
,
handleSearch
:
()
=>
{
message
.
info
(
'search..'
);
},
};
};
},
},
});
});
...
...
src/BasicLayout.tsx
View file @
c6c2a115
This diff is collapsed.
Click to expand it.
src/FooterToolbar/index.tsx
View file @
c6c2a115
import
'./index.less'
;
import
'./index.less'
import
{
computed
,
defineComponent
,
onBeforeUnmount
,
onMounted
,
PropType
}
from
'vue'
;
import
{
import
{
RouteContextProps
,
useRouteContext
}
from
'../RouteContext'
;
computed
,
import
{
getMenuFirstChildren
,
getPropsSlot
,
getPropsSlotfn
}
from
'../utils'
;
defineComponent
,
import
type
{
CustomRender
}
from
'../typings'
;
onBeforeUnmount
,
onMounted
,
unref
,
PropType
,
}
from
'vue'
import
{
RouteContextProps
,
useRouteContext
}
from
'../RouteContext'
import
{
getPropsSlotfn
}
from
'../utils'
import
type
{
CustomRender
}
from
'../typings'
export
interface
FooterToolbarProps
{
export
interface
FooterToolbarProps
{
extra
?:
CustomRender
|
JSX
.
Element
;
extra
?:
CustomRender
|
JSX
.
Element
renderContent
?:
(
renderContent
?:
(
props
:
FooterToolbarProps
&
RouteContextProps
&
{
leftWidth
?:
string
},
props
:
FooterToolbarProps
&
RouteContextProps
&
{
leftWidth
?:
string
},
dom
:
CustomRender
|
JSX
.
Element
,
dom
:
CustomRender
|
JSX
.
Element
)
=>
CustomRender
|
JSX
.
Element
;
)
=>
CustomRender
|
JSX
.
Element
getContainer
?:
(
triggerNode
:
HTMLElement
)
=>
HTMLElement
|
null
;
getContainer
?:
(
triggerNode
:
HTMLElement
)
=>
HTMLElement
|
null
prefixCls
?:
string
;
prefixCls
?:
string
}
}
const
footerToolbarProps
=
{
const
footerToolbarProps
=
{
...
@@ -25,52 +32,47 @@ const footerToolbarProps = {
...
@@ -25,52 +32,47 @@ const footerToolbarProps = {
type
:
[
Function
,
Object
]
as
PropType
<
FooterToolbarProps
[
'getContainer'
]
>
,
type
:
[
Function
,
Object
]
as
PropType
<
FooterToolbarProps
[
'getContainer'
]
>
,
},
},
prefixCls
:
{
type
:
String
as
PropType
<
string
>
},
prefixCls
:
{
type
:
String
as
PropType
<
string
>
},
}
;
}
const
FooterToolbar
=
defineComponent
({
const
FooterToolbar
=
defineComponent
({
name
:
'FooterToolbar'
,
name
:
'FooterToolbar'
,
props
:
footerToolbarProps
,
props
:
footerToolbarProps
,
setup
(
props
,
{
slots
})
{
setup
(
props
,
{
slots
})
{
const
routeContext
=
useRouteContext
();
const
context
=
useRouteContext
()
const
{
getPrefixCls
}
=
routeContext
;
const
baseClassName
=
props
.
prefixCls
||
context
.
getPrefixCls
(
'footer-bar'
)
const
baseClassName
=
props
.
prefixCls
||
getPrefixCls
(
'footer-bar'
);
// matchMenuKeys
const
hasFlatMenu
=
computed
(()
=>
{
const
matchMenuChildrenSize
=
computed
(
return
unref
(
context
.
flatMenuData
).
length
>
0
()
=>
})
(
(
routeContext
.
menuData
&&
getMenuFirstChildren
(
routeContext
.
menuData
,
(
routeContext
.
selectedKeys
&&
routeContext
.
selectedKeys
[
0
])
||
undefined
,
))
||
[]
).
length
,
);
const
hasSide
=
computed
(()
=>
{
return
routeContext
.
layout
===
'mix'
&&
routeContext
.
splitMenus
?
matchMenuChildrenSize
.
value
>
0
:
true
;
});
const
width
=
computed
(()
=>
{
const
width
=
computed
(()
=>
{
const
{
isMobile
,
sideWidth
,
layout
}
=
routeContext
;
const
{
isMobile
,
hasSide
,
siderWidth
,
layout
}
=
context
if
(
!
sideWidth
||
layout
===
'top'
)
{
if
(
!
side
r
Width
||
layout
===
'top'
)
{
return
'100%'
;
return
'100%'
}
}
if
(
!
hasSide
.
value
)
{
console
.
log
(
return
'100%'
;
'x'
,
unref
(
siderWidth
),
'hasFlatMenu'
,
unref
(
hasFlatMenu
),
'hasSide'
,
unref
(
context
.
hasSide
)
)
if
(
!
hasFlatMenu
.
value
&&
!
unref
(
hasSide
))
{
return
'100%'
}
}
return
isMobile
?
'100%'
:
`calc(100% -
${
sideWidth
}
px)`
;
console
.
log
(
'x2'
,
unref
(
context
.
hasSide
))
});
return
isMobile
?
'100%'
:
`calc(100% -
${
siderWidth
}
px)`
})
onMounted
(()
=>
{
onMounted
(()
=>
{
routeContext
.
setHasFooterToolbar
&&
routeContext
.
setHasFooterToolbar
(
true
);
context
.
setHasFooterToolbar
&&
context
.
setHasFooterToolbar
(
true
)
})
;
})
onBeforeUnmount
(()
=>
{
onBeforeUnmount
(()
=>
{
routeContext
.
setHasFooterToolbar
&&
routeContext
.
setHasFooterToolbar
(
false
);
context
.
setHasFooterToolbar
&&
context
.
setHasFooterToolbar
(
false
)
})
;
})
return
()
=>
{
return
()
=>
{
const
extra
=
getPropsSlotfn
(
slots
,
props
,
'extra'
)
;
const
extra
=
getPropsSlotfn
(
slots
,
props
,
'extra'
)
const
dom
=
()
=>
{
const
dom
=
()
=>
{
return
(
return
(
<>
<>
...
@@ -79,24 +81,24 @@ const FooterToolbar = defineComponent({
...
@@ -79,24 +81,24 @@ const FooterToolbar = defineComponent({
</
div
>
</
div
>
<
div
class=
{
`${baseClassName}-right`
}
>
{
slots
.
default
?.()
}
</
div
>
<
div
class=
{
`${baseClassName}-right`
}
>
{
slots
.
default
?.()
}
</
div
>
</>
</>
)
;
)
}
;
}
return
(
return
(
<
div
class=
{
baseClassName
}
style=
{
{
width
:
width
.
value
}
}
>
<
div
class=
{
baseClassName
}
style=
{
{
width
:
width
.
value
}
}
>
{
props
.
renderContent
{
props
.
renderContent
?
props
.
renderContent
(
?
props
.
renderContent
(
{
{
...
props
,
...
props
,
...
routeC
ontext
,
...
c
ontext
,
leftWidth
:
width
.
value
,
leftWidth
:
width
.
value
,
},
},
dom
()
,
dom
()
)
)
:
dom
()
}
:
dom
()
}
</
div
>
</
div
>
)
;
)
}
;
}
},
},
})
;
})
export
default
FooterToolbar
;
export
default
FooterToolbar
src/RouteContext.tsx
View file @
c6c2a115
import
{
InjectionKey
,
provide
,
reactive
,
Ref
,
VNodeChild
,
ComputedRef
}
from
'vue'
;
import
{
import
{
createContext
,
useContext
}
from
'./hooks/context'
;
InjectionKey
,
import
{
MenuDataItem
,
FormatMessage
,
WithFalse
}
from
'./typings'
;
provide
,
import
{
PureSettings
}
from
'./defaultSettings'
;
reactive
,
Ref
,
VNodeChild
,
ComputedRef
,
}
from
'vue'
import
{
createContext
,
useContext
}
from
'./hooks/context'
import
{
MenuDataItem
,
FormatMessage
,
WithFalse
}
from
'./typings'
import
{
PureSettings
}
from
'./defaultSettings'
export
interface
Route
{
export
interface
Route
{
path
:
string
;
path
:
string
breadcrumbName
:
string
;
breadcrumbName
:
string
children
?:
Omit
<
Route
,
'children'
>
[]
;
children
?:
Omit
<
Route
,
'children'
>
[]
}
}
export
interface
BreadcrumbProps
{
export
interface
BreadcrumbProps
{
prefixCls
?:
string
;
prefixCls
?:
string
routes
?:
Route
[]
;
routes
?:
Route
[]
params
?:
any
;
params
?:
any
separator
?:
VNodeChild
;
separator
?:
VNodeChild
itemRender
?:
(
opts
:
{
itemRender
?:
(
opts
:
{
route
:
Route
;
route
:
Route
params
:
any
;
params
:
any
routes
:
Array
<
Route
>
;
routes
:
Array
<
Route
>
paths
:
Array
<
string
>
;
paths
:
Array
<
string
>
})
=>
VNodeChild
;
})
=>
VNodeChild
}
}
export
type
BreadcrumbListReturn
=
Pick
<
export
type
BreadcrumbListReturn
=
Pick
<
BreadcrumbProps
,
BreadcrumbProps
,
Extract
<
keyof
BreadcrumbProps
,
'routes'
|
'itemRender'
>
Extract
<
keyof
BreadcrumbProps
,
'routes'
|
'itemRender'
>
>
;
>
export
interface
MenuState
{
export
interface
MenuState
{
selectedKeys
:
string
[]
;
selectedKeys
:
string
[]
openKeys
:
string
[]
;
openKeys
:
string
[]
}
}
export
interface
RouteContextProps
extends
Partial
<
PureSettings
>
,
MenuState
{
export
interface
RouteContextProps
extends
Partial
<
PureSettings
>
,
MenuState
{
menuData
:
MenuDataItem
[];
menuData
:
MenuDataItem
[]
flatMenuData
?:
MenuDataItem
[]
getPrefixCls
?:
(
suffixCls
?:
string
,
customizePrefixCls
?:
string
)
=>
string
;
getPrefixCls
?:
(
suffixCls
?:
string
,
customizePrefixCls
?:
string
)
=>
string
locale
?:
WithFalse
<
FormatMessage
>
;
locale
?:
WithFalse
<
FormatMessage
>
breadcrumb
?:
BreadcrumbListReturn
|
ComputedRef
<
BreadcrumbListReturn
>
;
breadcrumb
?:
BreadcrumbListReturn
|
ComputedRef
<
BreadcrumbListReturn
>
isMobile
?:
boolean
;
isMobile
?:
boolean
prefixCls
?:
string
;
prefixCls
?:
string
collapsed
?:
boolean
;
collapsed
?:
boolean
hasSideMenu
?:
boolean
;
hasSideMenu
?:
boolean
hasHeader
?:
boolean
;
hasHeader
?:
boolean
sideWidth
?:
number
;
siderWidth
?:
number
headerHeight
?:
number
;
headerHeight
?:
number
hasFooterToolbar
?:
boolean
;
hasFooterToolbar
?:
boolean
hasFooter
?:
boolean
;
hasFooter
?:
boolean
setHasFooterToolbar
?:
(
bool
:
boolean
)
=>
void
;
hasSide
?:
boolean
setHasFooterToolbar
?:
(
bool
:
boolean
)
=>
void
/* 附加属性 */
/* 附加属性 */
[
key
:
string
]:
any
;
[
key
:
string
]:
any
}
}
export
const
defaultPrefixCls
=
'ant-pro'
;
export
const
defaultPrefixCls
=
'ant-pro'
export
const
getPrefixCls
=
(
suffixCls
?:
string
,
customizePrefixCls
?:
string
)
=>
{
export
const
getPrefixCls
=
(
if
(
customizePrefixCls
)
return
customizePrefixCls
;
suffixCls
?:
string
,
return
suffixCls
?
`
${
defaultPrefixCls
}
-
${
suffixCls
}
`
:
defaultPrefixCls
;
customizePrefixCls
?:
string
};
)
=>
{
if
(
customizePrefixCls
)
return
customizePrefixCls
return
suffixCls
?
`
${
defaultPrefixCls
}
-
${
suffixCls
}
`
:
defaultPrefixCls
}
// set default context
// set default context
export
const
defaultRouteContext
=
reactive
({
export
const
defaultRouteContext
=
reactive
({
...
@@ -65,22 +77,31 @@ export const defaultRouteContext = reactive({
...
@@ -65,22 +77,31 @@ export const defaultRouteContext = reactive({
locale
:
(
t
:
string
)
=>
t
,
locale
:
(
t
:
string
)
=>
t
,
contentWidth
:
'Fluid'
,
contentWidth
:
'Fluid'
,
hasFooterToolbar
:
false
,
hasFooterToolbar
:
false
,
})
;
})
const
routeContextInjectKey
:
InjectionKey
<
RouteContextProps
>
=
Symbol
(
'route-context'
);
const
routeContextInjectKey
:
InjectionKey
<
RouteContextProps
>
=
Symbol
(
'route-context'
)
export
const
createRouteContext
=
()
=>
export
const
createRouteContext
=
()
=>
createContext
<
RouteContextProps
>
(
routeContextInjectKey
,
'RouteContext.Provider'
);
createContext
<
RouteContextProps
>
(
routeContextInjectKey
,
'RouteContext.Provider'
)
export
const
provideRouteContext
=
(
value
:
RouteContextProps
|
Ref
<
RouteContextProps
>
)
=>
{
export
const
provideRouteContext
=
(
provide
(
routeContextInjectKey
,
value
);
value
:
RouteContextProps
|
Ref
<
RouteContextProps
>
};
)
=>
{
provide
(
routeContextInjectKey
,
value
)
}
export
const
useRouteContext
=
()
=>
export
const
useRouteContext
=
()
=>
useContext
<
Required
<
RouteContextProps
>>
(
routeContextInjectKey
,
defaultRouteContext
);
useContext
<
Required
<
RouteContextProps
>>
(
routeContextInjectKey
,
defaultRouteContext
)
const
Provider
=
createRouteContext
()
;
const
Provider
=
createRouteContext
()
export
default
{
export
default
{
Provider
,
Provider
,
}
;
}
src/SiderMenu/SiderMenu.tsx
View file @
c6c2a115
This diff is collapsed.
Click to expand it.
src/SiderMenu/index.less
View file @
c6c2a115
...
@@ -197,6 +197,7 @@
...
@@ -197,6 +197,7 @@
.@{ant-prefix}-menu-submenu-title {
.@{ant-prefix}-menu-submenu-title {
.anticon {
.anticon {
transition: none;
transition: none;
font-size: 16px;
}
}
}
}
...
...
src/WaterMark/index.tsx
View file @
c6c2a115
...
@@ -68,6 +68,7 @@ const getPixelRatio = (context: any) => {
...
@@ -68,6 +68,7 @@ const getPixelRatio = (context: any) => {
}
}
const
WaterMark
=
defineComponent
({
const
WaterMark
=
defineComponent
({
name
:
'WaterMark'
,
props
:
waterMarkProps
,
props
:
waterMarkProps
,
setup
(
props
,
{
slots
})
{
setup
(
props
,
{
slots
})
{
const
{
const
{
...
@@ -145,32 +146,34 @@ const WaterMark = defineComponent({
...
@@ -145,32 +146,34 @@ const WaterMark = defineComponent({
}
}
})
})
return
(
return
()
=>
{
<
div
return
(
style=
{
{
position
:
'relative'
,
}
}
class=
{
wrapperCls
.
value
}
>
{
slots
.
default
?.()
}
<
div
<
div
class=
{
waterMakrCls
.
value
}
style=
{
{
style=
{
{
zIndex
,
position
:
'relative'
,
position
:
'absolute'
,
left
:
0
,
top
:
0
,
width
:
'100%'
,
height
:
'100%'
,
backgroundSize
:
`${gapX + width}px`
,
pointerEvents
:
'none'
,
backgroundRepeat
:
'repeat'
,
backgroundImage
:
`url('${base64Url.value}')`
,
...
markStyle
,
}
}
}
}
/>
class=
{
wrapperCls
.
value
}
</
div
>
>
)
{
slots
.
default
?.()
}
<
div
class=
{
waterMakrCls
.
value
}
style=
{
{
zIndex
,
position
:
'absolute'
,
left
:
0
,
top
:
0
,
width
:
'100%'
,
height
:
'100%'
,
backgroundSize
:
`${gapX + width}px`
,
pointerEvents
:
'none'
,
backgroundRepeat
:
'repeat'
,
backgroundImage
:
`url('${base64Url.value}')`
,
...
markStyle
,
}
}
/>
</
div
>
)
}
},
},
})
})
...
...
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