Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
jinli gu
Litemall
Commits
ed9f9462
Commit
ed9f9462
authored
Nov 24, 2019
by
Junling Bu
Browse files
feat[litemall-admin]: 更新vue-element-admin框架版本4.2.1
parent
72406b75
Changes
19
Hide whitespace changes
Inline
Side-by-side
litemall-admin/package.json
View file @
ed9f9462
...
...
@@ -13,8 +13,7 @@
"lint"
:
"eslint --ext .js,.vue src"
,
"test:unit"
:
"jest --clearCache && vue-cli-service test:unit"
,
"test:ci"
:
"npm run lint && npm run test:unit"
,
"svgo"
:
"svgo -f src/icons/svg --config=src/icons/svgo.yml"
,
"new"
:
"plop"
"svgo"
:
"svgo -f src/icons/svg --config=src/icons/svgo.yml"
},
"husky"
:
{
"hooks"
:
{
...
...
@@ -56,6 +55,7 @@
"jszip"
:
"3.2.1"
,
"normalize.css"
:
"7.0.0"
,
"nprogress"
:
"0.2.0"
,
"path-to-regexp"
:
"2.4.0"
,
"screenfull"
:
"4.2.0"
,
"vue"
:
"2.6.10"
,
"vue-count-to"
:
"1.0.13"
,
...
...
litemall-admin/src/components/Breadcrumb/index.vue
View file @
ed9f9462
<
template
>
<el-breadcrumb
class=
"app-breadcrumb"
separator=
"/"
>
<transition-group
name=
"breadcrumb"
>
<el-breadcrumb-item
v-for=
"(item,index) in levelList"
:key=
"item.path"
>
<span
v-if=
"item.redirect==='noredirect'||index==levelList.length
-
1"
class=
"no-redirect"
>
{{
item
.
meta
.
title
}}
</span>
<
router-link
v-else
:to=
"item.redirect||item.path
"
>
{{
item
.
meta
.
title
}}
</
router-link
>
<el-breadcrumb-item
v-for=
"(item,
index) in levelList"
:key=
"item.path"
>
<span
v-if=
"item.redirect
===
'noredirect'
||
index
==
levelList.length
-
1"
class=
"no-redirect"
>
{{
item
.
meta
.
title
}}
</span>
<
a
v-else
@
click.prevent=
"handleLink(item)
"
>
{{
item
.
meta
.
title
}}
</
a
>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
...
...
@@ -19,7 +19,11 @@ export default {
}
},
watch
:
{
$route
()
{
$route
(
route
)
{
// if you go to the redirect page, do not update the breadcrumbs
if
(
route
.
path
.
startsWith
(
'
/redirect/
'
))
{
return
}
this
.
getBreadcrumb
()
}
},
...
...
@@ -28,34 +32,53 @@ export default {
},
methods
:
{
getBreadcrumb
()
{
const
{
params
}
=
this
.
$route
let
matched
=
this
.
$route
.
matched
.
filter
(
item
=>
{
if
(
item
.
name
)
{
// To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
var
toPath
=
pathToRegexp
.
compile
(
item
.
path
)
item
.
path
=
toPath
(
params
)
return
true
}
})
// only show routes with meta.title
let
matched
=
this
.
$route
.
matched
.
filter
(
item
=>
item
.
meta
&&
item
.
meta
.
title
)
const
first
=
matched
[
0
]
if
(
first
&&
first
.
name
.
trim
().
toLocaleLowerCase
()
!==
'
Dashboard
'
.
toLocaleLowerCase
(
))
{
if
(
!
this
.
isDashboard
(
first
))
{
matched
=
[{
path
:
'
/dashboard
'
,
meta
:
{
title
:
'
首页
'
}}].
concat
(
matched
)
}
this
.
levelList
=
matched
this
.
levelList
=
matched
.
filter
(
item
=>
item
.
meta
&&
item
.
meta
.
title
&&
item
.
meta
.
breadcrumb
!==
false
)
},
isDashboard
(
route
)
{
const
name
=
route
&&
route
.
name
if
(
!
name
)
{
return
false
}
return
(
name
.
trim
().
toLocaleLowerCase
()
===
'
Dashboard
'
.
toLocaleLowerCase
())
},
pathCompile
(
path
)
{
// To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
const
{
params
}
=
this
.
$route
var
toPath
=
pathToRegexp
.
compile
(
path
)
return
toPath
(
params
)
},
handleLink
(
item
)
{
const
{
redirect
,
path
}
=
item
if
(
redirect
)
{
this
.
$router
.
push
(
redirect
)
return
}
this
.
$router
.
push
(
this
.
pathCompile
(
path
))
}
}
}
</
script
>
<
style
rel=
"stylesheet/scss"
lang=
"scss"
scoped
>
.app-breadcrumb.el-breadcrumb
{
display
:
inline-block
;
font-size
:
14px
;
line-height
:
50px
;
margin-left
:
10px
;
.no-redirect
{
color
:
#97a8be
;
cursor
:
text
;
}
.app-breadcrumb.el-breadcrumb
{
display
:
inline-block
;
font-size
:
14px
;
line-height
:
50px
;
margin-left
:
10px
;
.no-redirect
{
color
:
#97a8be
;
cursor
:
text
;
}
}
</
style
>
litemall-admin/src/components/SvgIcon/index.vue
View file @
ed9f9462
<
template
>
<svg
:class=
"svgClass"
aria-hidden=
"true"
>
<use
:xlink:href=
"iconName"
/>
<div
v-if=
"isExternal"
:style=
"styleExternalIcon"
class=
"svg-external-icon svg-icon"
v-on=
"$listeners"
/>
<svg
v-else
:class=
"svgClass"
aria-hidden=
"true"
v-on=
"$listeners"
>
<use
:xlink:href=
"iconName"
/>
</svg>
</
template
>
<
script
>
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
import
{
isExternal
}
from
'
@/utils/validate
'
export
default
{
name
:
'
SvgIcon
'
,
props
:
{
...
...
@@ -18,6 +21,9 @@ export default {
}
},
computed
:
{
isExternal
()
{
return
isExternal
(
this
.
iconClass
)
},
iconName
()
{
return
`#icon-
${
this
.
iconClass
}
`
},
...
...
@@ -27,6 +33,12 @@ export default {
}
else
{
return
'
svg-icon
'
}
},
styleExternalIcon
()
{
return
{
mask
:
`url(
${
this
.
iconClass
}
) no-repeat 50% 50%`
,
'
-webkit-mask
'
:
`url(
${
this
.
iconClass
}
) no-repeat 50% 50%`
}
}
}
}
...
...
@@ -40,4 +52,9 @@ export default {
fill
:
currentColor
;
overflow
:
hidden
;
}
.svg-external-icon
{
background-color
:
currentColor
;
mask-size
:
cover
!important
;
display
:
inline-block
;
}
</
style
>
litemall-admin/src/router/index.js
View file @
ed9f9462
...
...
@@ -65,7 +65,7 @@ export const constantRouterMap = [
path
:
'
dashboard
'
,
component
:
()
=>
import
(
'
@/views/dashboard/index
'
),
name
:
'
Dashboard
'
,
meta
:
{
title
:
'
首页
'
,
icon
:
'
dashboard
'
,
noCache
:
true
}
meta
:
{
title
:
'
首页
'
,
icon
:
'
dashboard
'
,
affix
:
true
}
}
]
}
...
...
litemall-admin/src/store/modules/tagsView.js
View file @
ed9f9462
const
tagsView
=
{
state
:
{
visitedViews
:
[],
cachedViews
:
[]
},
mutations
:
{
ADD_VISITED_VIEW
:
(
state
,
view
)
=>
{
if
(
state
.
visitedViews
.
some
(
v
=>
v
.
path
===
view
.
path
))
return
state
.
visitedViews
.
push
(
Object
.
assign
({},
view
,
{
title
:
view
.
meta
.
title
||
'
no-name
'
})
)
},
ADD_CACHED_VIEW
:
(
state
,
view
)
=>
{
if
(
state
.
cachedViews
.
includes
(
view
.
name
))
return
if
(
!
view
.
meta
.
noCache
)
{
state
.
cachedViews
.
push
(
view
.
name
)
}
},
const
state
=
{
visitedViews
:
[],
cachedViews
:
[]
}
DEL_VISITED_VIEW
:
(
state
,
view
)
=>
{
for
(
const
[
i
,
v
]
of
state
.
visitedViews
.
entries
())
{
if
(
v
.
path
===
view
.
path
)
{
state
.
visitedViews
.
splice
(
i
,
1
)
break
}
}
},
DEL_CACHED_VIEW
:
(
state
,
view
)
=>
{
for
(
const
i
of
state
.
cachedViews
)
{
if
(
i
===
view
.
name
)
{
const
index
=
state
.
cachedViews
.
indexOf
(
i
)
state
.
cachedViews
.
splice
(
index
,
1
)
break
}
}
},
const
mutations
=
{
ADD_VISITED_VIEW
:
(
state
,
view
)
=>
{
if
(
state
.
visitedViews
.
some
(
v
=>
v
.
path
===
view
.
path
))
return
state
.
visitedViews
.
push
(
Object
.
assign
({},
view
,
{
title
:
view
.
meta
.
title
||
'
no-name
'
})
)
},
ADD_CACHED_VIEW
:
(
state
,
view
)
=>
{
if
(
state
.
cachedViews
.
includes
(
view
.
name
))
return
if
(
!
view
.
meta
.
noCache
)
{
state
.
cachedViews
.
push
(
view
.
name
)
}
},
DEL_OTHERS_VISITED_VIEWS
:
(
state
,
view
)
=>
{
for
(
const
[
i
,
v
]
of
state
.
visitedViews
.
entries
())
{
if
(
v
.
path
===
view
.
path
)
{
state
.
visitedViews
=
state
.
visitedViews
.
slice
(
i
,
i
+
1
)
break
}
DEL_VISITED_VIEW
:
(
state
,
view
)
=>
{
for
(
const
[
i
,
v
]
of
state
.
visitedViews
.
entries
())
{
if
(
v
.
path
===
view
.
path
)
{
state
.
visitedViews
.
splice
(
i
,
1
)
break
}
},
DEL_OTHERS_CACHED_VIEWS
:
(
state
,
view
)
=>
{
for
(
const
i
of
state
.
cachedViews
)
{
if
(
i
===
view
.
name
)
{
const
index
=
state
.
cachedViews
.
indexOf
(
i
)
state
.
cachedViews
=
state
.
cachedViews
.
slice
(
index
,
index
+
1
)
break
}
}
},
}
},
DEL_CACHED_VIEW
:
(
state
,
view
)
=>
{
const
index
=
state
.
cachedViews
.
indexOf
(
view
.
name
)
index
>
-
1
&&
state
.
cachedViews
.
splice
(
index
,
1
)
},
DEL_ALL_VISITED_VIEWS
:
state
=>
{
state
.
visitedViews
=
[]
},
DEL_ALL_CACHED_VIEWS
:
state
=>
{
DEL_OTHERS_VISITED_VIEWS
:
(
state
,
view
)
=>
{
state
.
visitedViews
=
state
.
visitedViews
.
filter
(
v
=>
{
return
v
.
meta
.
affix
||
v
.
path
===
view
.
path
})
},
DEL_OTHERS_CACHED_VIEWS
:
(
state
,
view
)
=>
{
const
index
=
state
.
cachedViews
.
indexOf
(
view
.
name
)
if
(
index
>
-
1
)
{
state
.
cachedViews
=
state
.
cachedViews
.
slice
(
index
,
index
+
1
)
}
else
{
// if index = -1, there is no cached tags
state
.
cachedViews
=
[]
},
}
},
UPDATE_VISITED_VIEW
:
(
state
,
view
)
=>
{
for
(
let
v
of
state
.
visitedViews
)
{
if
(
v
.
path
===
view
.
path
)
{
v
=
Object
.
assign
(
v
,
view
)
break
}
DEL_ALL_VISITED_VIEWS
:
state
=>
{
// keep affix tags
const
affixTags
=
state
.
visitedViews
.
filter
(
tag
=>
tag
.
meta
.
affix
)
state
.
visitedViews
=
affixTags
},
DEL_ALL_CACHED_VIEWS
:
state
=>
{
state
.
cachedViews
=
[]
},
UPDATE_VISITED_VIEW
:
(
state
,
view
)
=>
{
for
(
let
v
of
state
.
visitedViews
)
{
if
(
v
.
path
===
view
.
path
)
{
v
=
Object
.
assign
(
v
,
view
)
break
}
}
}
}
const
actions
=
{
addView
({
dispatch
},
view
)
{
dispatch
(
'
addVisitedView
'
,
view
)
dispatch
(
'
addCachedView
'
,
view
)
},
addVisitedView
({
commit
},
view
)
{
commit
(
'
ADD_VISITED_VIEW
'
,
view
)
},
addCachedView
({
commit
},
view
)
{
commit
(
'
ADD_CACHED_VIEW
'
,
view
)
},
actions
:
{
addView
({
dispatch
},
view
)
{
dispatch
(
'
addVisitedView
'
,
view
)
dispatch
(
'
addCachedView
'
,
view
)
},
addVisitedView
({
commit
},
view
)
{
commit
(
'
ADD_VISITED_VIEW
'
,
view
)
},
addCachedView
({
commit
},
view
)
{
commit
(
'
ADD_CACHED_VIEW
'
,
view
)
},
delView
({
dispatch
,
state
},
view
)
{
return
new
Promise
(
resolve
=>
{
dispatch
(
'
delVisitedView
'
,
view
)
dispatch
(
'
delCachedView
'
,
view
)
resolve
({
visitedViews
:
[...
state
.
visitedViews
],
cachedViews
:
[...
state
.
cachedViews
]
})
})
},
delVisitedView
({
commit
,
state
},
view
)
{
return
new
Promise
(
resolve
=>
{
commit
(
'
DEL_VISITED_VIEW
'
,
view
)
resolve
([...
state
.
visitedViews
])
delView
({
dispatch
,
state
},
view
)
{
return
new
Promise
(
resolve
=>
{
dispatch
(
'
delVisitedView
'
,
view
)
dispatch
(
'
delCachedView
'
,
view
)
resolve
({
visitedViews
:
[...
state
.
visitedViews
],
cachedViews
:
[...
state
.
cachedViews
]
})
},
delCachedView
({
commit
,
state
},
view
)
{
return
new
Promise
(
resolve
=>
{
commit
(
'
DEL_CACHED_VIEW
'
,
view
)
resolve
([...
state
.
cachedViews
])
})
},
})
},
delVisitedView
({
commit
,
state
},
view
)
{
return
new
Promise
(
resolve
=>
{
commit
(
'
DEL_VISITED_VIEW
'
,
view
)
resolve
([...
state
.
visitedViews
])
})
},
delCachedView
({
commit
,
state
},
view
)
{
return
new
Promise
(
resolve
=>
{
commit
(
'
DEL_CACHED_VIEW
'
,
view
)
resolve
([...
state
.
cachedViews
])
})
},
delOthersViews
({
dispatch
,
state
},
view
)
{
return
new
Promise
(
resolve
=>
{
dispatch
(
'
delOthersVisitedViews
'
,
view
)
dispatch
(
'
delOthersCachedViews
'
,
view
)
resolve
({
visitedViews
:
[...
state
.
visitedViews
],
cachedViews
:
[...
state
.
cachedViews
]
})
})
},
delOthersVisitedViews
({
commit
,
state
},
view
)
{
return
new
Promise
(
resolve
=>
{
commit
(
'
DEL_OTHERS_VISITED_VIEWS
'
,
view
)
resolve
([...
state
.
visitedViews
])
delOthersViews
({
dispatch
,
state
},
view
)
{
return
new
Promise
(
resolve
=>
{
dispatch
(
'
delOthersVisitedViews
'
,
view
)
dispatch
(
'
delOthersCachedViews
'
,
view
)
resolve
({
visitedViews
:
[...
state
.
visitedViews
],
cachedViews
:
[...
state
.
cachedViews
]
})
},
delOthersCachedViews
({
commit
,
state
},
view
)
{
return
new
Promise
(
resolve
=>
{
commit
(
'
DEL_OTHERS_CACHED_VIEWS
'
,
view
)
resolve
([...
state
.
cachedViews
])
})
},
})
},
delOthersVisitedViews
({
commit
,
state
},
view
)
{
return
new
Promise
(
resolve
=>
{
commit
(
'
DEL_OTHERS_VISITED_VIEWS
'
,
view
)
resolve
([...
state
.
visitedViews
])
})
},
delOthersCachedViews
({
commit
,
state
},
view
)
{
return
new
Promise
(
resolve
=>
{
commit
(
'
DEL_OTHERS_CACHED_VIEWS
'
,
view
)
resolve
([...
state
.
cachedViews
])
})
},
delAllViews
({
dispatch
,
state
},
view
)
{
return
new
Promise
(
resolve
=>
{
dispatch
(
'
delAllVisitedViews
'
,
view
)
dispatch
(
'
delAllCachedViews
'
,
view
)
resolve
({
visitedViews
:
[...
state
.
visitedViews
],
cachedViews
:
[...
state
.
cachedViews
]
})
})
},
delAllVisitedViews
({
commit
,
state
})
{
return
new
Promise
(
resolve
=>
{
commit
(
'
DEL_ALL_VISITED_VIEWS
'
)
resolve
([...
state
.
visitedViews
])
delAllViews
({
dispatch
,
state
},
view
)
{
return
new
Promise
(
resolve
=>
{
dispatch
(
'
delAllVisitedViews
'
,
view
)
dispatch
(
'
delAllCachedViews
'
,
view
)
resolve
({
visitedViews
:
[...
state
.
visitedViews
],
cachedViews
:
[...
state
.
cachedViews
]
})
},
delAllCachedViews
({
commit
,
state
})
{
return
new
Promise
(
resolve
=>
{
commit
(
'
DEL_ALL_CACHED_VIEWS
'
)
resolve
([...
state
.
cachedViews
])
})
},
})
},
delAllVisitedViews
({
commit
,
state
})
{
return
new
Promise
(
resolve
=>
{
commit
(
'
DEL_ALL_VISITED_VIEWS
'
)
resolve
([...
state
.
visitedViews
])
})
},
delAllCachedViews
({
commit
,
state
})
{
return
new
Promise
(
resolve
=>
{
commit
(
'
DEL_ALL_CACHED_VIEWS
'
)
resolve
([...
state
.
cachedViews
])
})
},
updateVisitedView
({
commit
},
view
)
{
commit
(
'
UPDATE_VISITED_VIEW
'
,
view
)
}
updateVisitedView
({
commit
},
view
)
{
commit
(
'
UPDATE_VISITED_VIEW
'
,
view
)
}
}
export
default
tagsView
export
default
{
namespaced
:
true
,
state
,
mutations
,
actions
}
litemall-admin/src/styles/btn.scss
View file @
ed9f9462
...
...
@@ -2,8 +2,10 @@
@mixin
colorBtn
(
$color
)
{
background
:
$color
;
&
:hover
{
color
:
$color
;
&
:before
,
&
:after
{
background
:
$color
;
...
...
@@ -49,14 +51,17 @@
transition
:
600ms
ease
all
;
position
:
relative
;
display
:
inline-block
;
&
:hover
{
background
:
#fff
;
&
:before
,
&
:after
{
width
:
100%
;
transition
:
600ms
ease
all
;
}
}
&
:before
,
&
:after
{
content
:
''
;
...
...
@@ -67,6 +72,7 @@
width
:
0
;
transition
:
400ms
ease
all
;
}
&
::after
{
right
:
inherit
;
top
:
inherit
;
...
...
@@ -91,4 +97,3 @@
font-size
:
14px
;
border-radius
:
4px
;
}
litemall-admin/src/styles/element-ui.scss
View file @
ed9f9462
//
覆盖一些
element-ui
样式
//
cover some
element-ui
styles
.el-breadcrumb__inner
,
.el-breadcrumb__inner
a
{
font-weight
:
400
!
important
;
.el-breadcrumb__inner
,
.el-breadcrumb__inner
a
{
font-weight
:
400
!
important
;
}
.el-upload
{
input
[
type
=
"file"
]
{
display
:
none
!
important
;
}
}
.el-upload
{
input
[
type
=
"file"
]
{
display
:
none
!
important
;
}
}
.el-upload__input
{
display
:
none
;
}
.el-upload__input
{
display
:
none
;
}
.cell
{
.el-tag
{
margin-right
:
0px
;
}
}
.cell
{
.el-tag
{
margin-right
:
0px
;
}
}
.small-padding
{
.cell
{
padding-left
:
5px
;
padding-right
:
5px
;
}
}
.small-padding
{
.cell
{
padding-left
:
5px
;
padding-right
:
5px
;
}
}
.fixed-width
{
.el-button--mini
{
.fixed-width
{
.el-button--mini
{
padding
:
7px
10px
;
width
:
60px
;
}
}
}
.status-col
{
.cell
{
padding
:
0
10px
;
text-align
:
center
;
.status-col
{
.cell
{
padding
:
0
10px
;
text-align
:
center
;
.el-tag
{
margin-right
:
0px
;
}
}
}
.el-tag
{
margin-right
:
0px
;
}
}
}
//
暂时性解决dialog 问题
https://github.com/ElemeFE/element/issues/2461
.el-dialog
{
transform
:
none
;
left
:
0
;
position
:
relative
;
margin
:
0
auto
;
}
//
to fixed
https://github.com/ElemeFE/element/issues/2461
.el-dialog
{
transform
:
none
;
left
:
0
;
position
:
relative
;
margin
:
0
auto
;
}
//文章页textarea修改样式
.article-textarea
{
textarea
{
padding-right
:
40px
;
resize
:
none
;
border
:
none
;
border-radius
:
0px
;
border-bottom
:
1px
solid
#bfcbd9
;
}
}
// refine element ui upload
.upload-container
{
.el-upload
{
width
:
100%
;
//element ui upload
.upload-container
{
.el-upload
{
width
:
100%
;
.el-upload-dragger
{
width
:
100%
;
height
:
200px
;
}
}
}
.el-upload-dragger
{
width
:
100%
;
height
:
200px
;
}
}
}
//dropdown
.el-dropdown-menu
{
a
{
//
dropdown
.el-dropdown-menu
{
a
{
display
:
block
}
}
// fix date-picker ui bug in filter-item
.el-range-editor.el-input__inner
{
display
:
inline-flex
!
important
;
}
// to fix el-date-picker css style
.el-range-separator
{
box-sizing
:
content-box
;
}
litemall-admin/src/styles/index.scss
View file @
ed9f9462
...
...
@@ -22,7 +22,7 @@ html {
box-sizing
:
border-box
;
}
#app
{
#app
{
height
:
100%
;
}
...
...
@@ -53,9 +53,9 @@ a:hover {
text-decoration
:
none
;
}
div
:focus
{
div
:focus
{
outline
:
none
;
}
}
.fr
{
float
:
right
;
...
...
@@ -96,36 +96,29 @@ div:focus{
}
}
co
de
{
asi
de
{
background
:
#eef1f6
;
padding
:
15
px
16
px
;
padding
:
8
px
24
px
;
margin-bottom
:
20px
;
border-radius
:
2px
;
display
:
block
;
line-height
:
36px
;
font-size
:
15px
;
font-family
:
"Source Sans Pro"
,
"Helvetica Neue"
,
Arial
,
sans-serif
;
line-height
:
32px
;
font-size
:
16px
;
font-family
:
-
apple-system
,
BlinkMacSystemFont
,
"Segoe UI"
,
Roboto
,
Oxygen
,
Ubuntu
,
Cantarell
,
"Fira Sans"
,
"Droid Sans"
,
"Helvetica Neue"
,
sans-serif
;
color
:
#2c3e50
;
-webkit-font-smoothing
:
antialiased
;
-moz-osx-font-smoothing
:
grayscale
;
a
{
color
:
#337ab7
;
cursor
:
pointer
;
&
:hover
{
color
:
rgb
(
32
,
160
,
255
);
}
}
}
.warn-content
{
background
:
rgba
(
66
,
185
,
131
,.
1
);
border-radius
:
2px
;
padding
:
16px
;
padding
:
1rem
;
line-height
:
1
.6rem
;
word-spacing
:
.05rem
;
a
{
color
:
#42b983
;
font-weight
:
600
;
}
}
//main-container全局样式
.app-container
{
padding
:
20px
;
...
...
@@ -153,13 +146,16 @@ code {
padding-right
:
20px
;
transition
:
600ms
ease
position
;
background
:
linear-gradient
(
90deg
,
rgba
(
32
,
182
,
249
,
1
)
0%
,
rgba
(
32
,
182
,
249
,
1
)
0%
,
rgba
(
33
,
120
,
241
,
1
)
100%
,
rgba
(
33
,
120
,
241
,
1
)
100%
);
.subtitle
{
font-size
:
20px
;
color
:
#fff
;
}
&
.draft
{
background
:
#d0d0d0
;
}
&
.deleted
{
background
:
#d0d0d0
;
}
...
...
@@ -169,6 +165,7 @@ code {
.link-type
:focus
{
color
:
#337ab7
;
cursor
:
pointer
;
&
:hover
{
color
:
rgb
(
32
,
160
,
255
);
}
...
...
@@ -176,6 +173,7 @@ code {
.filter-container
{
padding-bottom
:
10px
;
.filter-item
{
display
:
inline-block
;
vertical-align
:
middle
;
...
...
litemall-admin/src/styles/mixin.scss
View file @
ed9f9462
...
...
@@ -10,9 +10,11 @@
&
::-webkit-scrollbar-track-piece
{
background
:
#d3dce6
;
}
&
::-webkit-scrollbar
{
width
:
6px
;
}
&
::-webkit-scrollbar-thumb
{
background
:
#99a9bf
;
border-radius
:
20px
;
...
...
@@ -37,21 +39,25 @@
$transparent-border-style
:
$width
solid
transparent
;
height
:
0
;
width
:
0
;
@if
$direction
==
up
{
border-bottom
:
$color-border-style
;
border-left
:
$transparent-border-style
;
border-right
:
$transparent-border-style
;
}
@else
if
$direction
==
right
{
border-left
:
$color-border-style
;
border-top
:
$transparent-border-style
;
border-bottom
:
$transparent-border-style
;
}
@else
if
$direction
==
down
{
border-top
:
$color-border-style
;
border-left
:
$transparent-border-style
;
border-right
:
$transparent-border-style
;
}
@else
if
$direction
==
left
{
border-right
:
$color-border-style
;
border-top
:
$transparent-border-style
;
...
...
litemall-admin/src/styles/sidebar.scss
View file @
ed9f9462
#app
{
// 主体区域
.main-container
{
min-height
:
100%
;
transition
:
margin-left
.28s
;
margin-left
:
180px
;
margin-left
:
$sideBarWidth
;
position
:
relative
;
}
// 侧边栏
.sidebar-container
{
transition
:
width
0
.28s
;
width
:
180px
!
important
;
width
:
$sideBarWidth
!
important
;
background-color
:
$menuBg
;
height
:
100%
;
position
:
fixed
;
font-size
:
0px
;
...
...
@@ -18,62 +19,111 @@
left
:
0
;
z-index
:
1001
;
overflow
:
hidden
;
//reset element-ui css
// reset element-ui css
.horizontal-collapse-transition
{
transition
:
0s
width
ease-in-out
,
0s
padding-left
ease-in-out
,
0s
padding-right
ease-in-out
;
}
.scrollbar-wrapper
{
overflow-x
:
hidden
!
important
;
.el-scrollbar__view
{
height
:
100%
;
}
overflow-x
:
hidden
!
important
;
}
.el-scrollbar__bar.is-vertical
{
.el-scrollbar__bar.is-vertical
{
right
:
0px
;
}
.el-scrollbar
{
height
:
100%
;
}
&
.has-logo
{
.el-scrollbar
{
height
:
calc
(
100%
-
50px
);
}
}
.is-horizontal
{
display
:
none
;
}
a
{
display
:
inline-block
;
width
:
100%
;
overflow
:
hidden
;
}
.svg-icon
{
margin-right
:
16px
;
}
.el-menu
{
border
:
none
;
height
:
100%
;
width
:
100%
!
important
;
}
.is-active
>
.el-submenu__title
{
color
:
#f4f4f5
!
important
;
// menu hover
.submenu-title-noDropdown
,
.el-submenu__title
{
&
:hover
{
background-color
:
$menuHover
!
important
;
}
}
.is-active
>
.el-submenu__title
{
color
:
$subMenuActiveText
!
important
;
}
&
.nest-menu
.el-submenu
>
.el-submenu__title
,
&
.el-submenu
.el-menu-item
{
min-width
:
$sideBarWidth
!
important
;
background-color
:
$subMenuBg
!
important
;
&
:hover
{
background-color
:
$subMenuHover
!
important
;
}
}
}
.hideSidebar
{
.sidebar-container
{
width
:
36
px
!
important
;
width
:
54
px
!
important
;
}
.main-container
{
margin-left
:
36
px
;
margin-left
:
54
px
;
}
.submenu-title-noDropdown
{
padding
-left
:
10px
!
important
;
padding
:
0
!
important
;
position
:
relative
;
.el-tooltip
{
padding
:
0
10px
!
important
;
padding
:
0
!
important
;
.svg-icon
{
margin-left
:
20px
;
}
}
}
.el-submenu
{
overflow
:
hidden
;
&
>
.el-submenu__title
{
padding-left
:
10px
!
important
;
padding
:
0
!
important
;
.svg-icon
{
margin-left
:
20px
;
}
.el-submenu__icon-arrow
{
display
:
none
;
}
}
}
.el-menu--collapse
{
.el-submenu
{
&
>
.el-submenu__title
{
...
...
@@ -88,35 +138,33 @@
}
}
}
.sidebar-container
.nest-menu
.el-submenu
>
.el-submenu__title
,
.sidebar-container
.el-submenu
.el-menu-item
{
min-width
:
180px
!
important
;
background-color
:
$subMenuBg
!
important
;
&
:hover
{
background-color
:
$menuHover
!
important
;
}
}
.el-menu--collapse
.el-menu
.el-submenu
{
min-width
:
180px
!
important
;
min-width
:
$sideBarWidth
!
important
;
}
//
适配移动端
//
mobile responsive
.mobile
{
.main-container
{
margin-left
:
0px
;
}
.sidebar-container
{
transition
:
transform
.28s
;
width
:
180px
!
important
;
width
:
$sideBarWidth
!
important
;
}
&
.hideSidebar
{
.sidebar-container
{
pointer-events
:
none
;
transition-duration
:
0
.3s
;
transform
:
translate3d
(
-
180px
,
0
,
0
);
transform
:
translate3d
(
-
$sideBarWidth
,
0
,
0
);
}
}
}
.withoutAnimation
{
.main-container
,
.sidebar-container
{
transition
:
none
;
...
...
@@ -124,10 +172,38 @@
}
}
.el-menu--vertical
{
&
>
.el-menu
{
.svg-icon
{
// when menu collapsed
.el-menu--vertical
{
&
>
.el-menu
{
.svg-icon
{
margin-right
:
16px
;
}
}
.nest-menu
.el-submenu
>
.el-submenu__title
,
.el-menu-item
{
&
:hover
{
// you can use $subMenuHover
background-color
:
$menuHover
!
important
;
}
}
// the scroll bar appears when the subMenu is too long
>
.el-menu--popup
{
max-height
:
100vh
;
overflow-y
:
auto
;
&
::-webkit-scrollbar-track-piece
{
background
:
#d3dce6
;
}
&
::-webkit-scrollbar
{
width
:
6px
;
}
&
::-webkit-scrollbar-thumb
{
background
:
#99a9bf
;
border-radius
:
20px
;
}
}
}
litemall-admin/src/styles/transition.scss
View file @
ed9f9462
//globl transition css
//
glob
a
l transition css
/*fade*/
/*
fade
*/
.fade-enter-active
,
.fade-leave-active
{
transition
:
opacity
0
.28s
;
...
...
@@ -11,21 +11,23 @@
opacity
:
0
;
}
/*fade-transform*/
/*
fade-transform
*/
.fade-transform-leave-active
,
.fade-transform-enter-active
{
transition
:
all
.5s
;
}
.fade-transform-enter
{
opacity
:
0
;
transform
:
translateX
(
-30px
);
}
.fade-transform-leave-to
{
opacity
:
0
;
transform
:
translateX
(
30px
);
}
/*breadcrumb transition*/
/*
breadcrumb transition
*/
.breadcrumb-enter-active
,
.breadcrumb-leave-active
{
transition
:
all
.5s
;
...
...
@@ -44,4 +46,3 @@
.breadcrumb-leave-active
{
position
:
absolute
;
}
litemall-admin/src/styles/variables.scss
View file @
ed9f9462
// base color
$blue
:
#324157
;
$light-blue
:
#3A71A8
;
$red
:
#C03639
;
...
...
@@ -7,7 +8,28 @@ $tiffany: #4AB7BD;
$yellow
:
#FEC171
;
$panGreen
:
#30B08F
;
//sidebar
// sidebar
$menuText
:
#bfcbd9
;
$menuActiveText
:
#409EFF
;
$subMenuActiveText
:
#f4f4f5
;
// https://github.com/ElemeFE/element/issues/12951
$menuBg
:
#304156
;
$menuHover
:
#263445
;
$subMenuBg
:
#1f2d3d
;
$menuHover
:
#001528
;
$subMenuHover
:
#001528
;
$sideBarWidth
:
210px
;
// the :export directive is the magic sauce for webpack
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
:export
{
menuText
:
$menuText
;
menuActiveText
:
$menuActiveText
;
subMenuActiveText
:
$subMenuActiveText
;
menuBg
:
$menuBg
;
menuHover
:
$menuHover
;
subMenuBg
:
$subMenuBg
;
subMenuHover
:
$subMenuHover
;
sideBarWidth
:
$sideBarWidth
;
}
litemall-admin/src/utils/createUniqueString.js
deleted
100644 → 0
View file @
72406b75
/**
* Created by jiachenpan on 17/3/8.
*/
export
default
function
createUniqueString
()
{
const
timestamp
=
+
new
Date
()
+
''
const
randomNum
=
parseInt
((
1
+
Math
.
random
())
*
65536
)
+
''
return
(
+
(
randomNum
+
timestamp
)).
toString
(
32
)
}
litemall-admin/src/utils/validate.js
View file @
ed9f9462
/**
* Created by
j
ia
c
hen
pan
on 16/11/18.
* Created by
PanJ
ia
C
hen on 16/11/18.
*/
/* 合法uri*/
export
function
validateURL
(
textval
)
{
const
urlregex
=
/^
(
https
?
|ftp
)
:
\/\/([
a-zA-Z0-9.-
]
+
(
:
[
a-zA-Z0-9.&%$-
]
+
)
*@
)
*
((
25
[
0-5
]
|2
[
0-4
][
0-9
]
|1
[
0-9
]{2}
|
[
1-9
][
0-9
]?)(\.(
25
[
0-5
]
|2
[
0-4
][
0-9
]
|1
[
0-9
]{2}
|
[
1-9
]?[
0-9
])){3}
|
([
a-zA-Z0-9-
]
+
\.)
*
[
a-zA-Z0-9-
]
+
\.(
com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|
[
a-zA-Z
]{2}))(
:
[
0-9
]
+
)
*
(\/(
$|
[
a-zA-Z0-9.,?'
\\
+&%$#=~_-
]
+
))
*$/
return
urlregex
.
test
(
textval
)
/**
* @param {string} path
* @returns {Boolean}
*/
export
function
isExternal
(
path
)
{
return
/^
(
https
?
:|mailto:|tel:
)
/
.
test
(
path
)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export
function
validUsername
(
str
)
{
const
valid_map
=
[
'
admin
'
,
'
editor
'
]
return
valid_map
.
indexOf
(
str
.
trim
())
>=
0
}
/* 小写字母*/
export
function
validateLowerCase
(
str
)
{
/**
* @param {string} url
* @returns {Boolean}
*/
export
function
validURL
(
url
)
{
const
reg
=
/^
(
https
?
|ftp
)
:
\/\/([
a-zA-Z0-9.-
]
+
(
:
[
a-zA-Z0-9.&%$-
]
+
)
*@
)
*
((
25
[
0-5
]
|2
[
0-4
][
0-9
]
|1
[
0-9
]{2}
|
[
1-9
][
0-9
]?)(\.(
25
[
0-5
]
|2
[
0-4
][
0-9
]
|1
[
0-9
]{2}
|
[
1-9
]?[
0-9
])){3}
|
([
a-zA-Z0-9-
]
+
\.)
*
[
a-zA-Z0-9-
]
+
\.(
com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|
[
a-zA-Z
]{2}))(
:
[
0-9
]
+
)
*
(\/(
$|
[
a-zA-Z0-9.,?'
\\
+&%$#=~_-
]
+
))
*$/
return
reg
.
test
(
url
)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export
function
validLowerCase
(
str
)
{
const
reg
=
/^
[
a-z
]
+$/
return
reg
.
test
(
str
)
}
/* 大写字母*/
export
function
validateUpperCase
(
str
)
{
/**
* @param {string} str
* @returns {Boolean}
*/
export
function
validUpperCase
(
str
)
{
const
reg
=
/^
[
A-Z
]
+$/
return
reg
.
test
(
str
)
}
/* 大小写字母*/
export
function
validateAlphabets
(
str
)
{
/**
* @param {string} str
* @returns {Boolean}
*/
export
function
validAlphabets
(
str
)
{
const
reg
=
/^
[
A-Za-z
]
+$/
return
reg
.
test
(
str
)
}
/**
* validate email
* @param email
* @returns {boolean}
* @param {string} email
* @returns {Boolean}
*/
export
function
validEmail
(
email
)
{
const
reg
=
/^
(([^
<>()
\[\]\\
.,;:
\s
@"
]
+
(\.[^
<>()
\[\]\\
.,;:
\s
@"
]
+
)
*
)
|
(
".+"
))
@
((\[[
0-9
]{1,3}\.[
0-9
]{1,3}\.[
0-9
]{1,3}\.[
0-9
]{1,3}\])
|
(([
a-zA-Z
\-
0-9
]
+
\.)
+
[
a-zA-Z
]{2,}))
$/
return
reg
.
test
(
email
)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export
function
isString
(
str
)
{
if
(
typeof
str
===
'
string
'
||
str
instanceof
String
)
{
return
true
}
return
false
}
/**
* @param {Array} arg
* @returns {Boolean}
*/
export
function
validateEmail
(
email
)
{
const
re
=
/^
(([^
<>()
\[\]\\
.,;:
\s
@"
]
+
(\.[^
<>()
\[\]\\
.,;:
\s
@"
]
+
)
*
)
|
(
".+"
))
@
((\[[
0-9
]{1,3}\.[
0-9
]{1,3}\.[
0-9
]{1,3}\.[
0-9
]{1,3}\])
|
(([
a-zA-Z
\-
0-9
]
+
\.)
+
[
a-zA-Z
]{2,}))
$/
return
re
.
test
(
email
)
export
function
isArray
(
arg
)
{
if
(
typeof
Array
.
isArray
===
'
undefined
'
)
{
return
Object
.
prototype
.
toString
.
call
(
arg
)
===
'
[object Array]
'
}
return
Array
.
isArray
(
arg
)
}
litemall-admin/src/views/layout/components/AppMain.vue
View file @
ed9f9462
...
...
@@ -2,7 +2,7 @@
<section
class=
"app-main"
>
<transition
name=
"fade-transform"
mode=
"out-in"
>
<keep-alive
:include=
"cachedViews"
>
<router-view
:key=
"key"
/>
<router-view
:key=
"key"
/>
</keep-alive>
</transition>
</section>
...
...
@@ -22,13 +22,34 @@ export default {
}
</
script
>
<
style
scoped
>
<
style
lang=
"scss"
scoped
>
.app-main
{
/*
84
= navbar
+ tags-view =
50
+34
*/
min-height
:
calc
(
100vh
-
84
px
);
/*
50
= navbar 50 */
min-height
:
calc
(
100vh
-
50
px
);
width
:
100%
;
position
:
relative
;
overflow
:
hidden
;
}
.fixed-header
+
.app-main
{
padding-top
:
50px
;
}
.hasTagsView
{
.app-main
{
/* 84 = navbar + tags-view = 50 + 34 */
min-height
:
calc
(
100vh
-
84px
);
}
.fixed-header
+
.app-main
{
padding-top
:
84px
;
}
}
</
style
>
<
style
lang=
"scss"
>
// fix css style bug in open el-dialog
.el-popup-parent--hidden
{
.fixed-header
{
padding-right
:
15px
;
}
}
</
style
>
litemall-admin/src/views/layout/components/ScrollPane.vue
0 → 100644
View file @
ed9f9462
<
template
>
<el-scrollbar
ref=
"scrollContainer"
:vertical=
"false"
class=
"scroll-container"
@
wheel.native.prevent=
"handleScroll"
>
<slot
/>
</el-scrollbar>
</
template
>
<
script
>
const
tagAndTagSpacing
=
4
// tagAndTagSpacing
export
default
{
name
:
'
ScrollPane
'
,
data
()
{
return
{
left
:
0
}
},
computed
:
{
scrollWrapper
()
{
return
this
.
$refs
.
scrollContainer
.
$refs
.
wrap
}
},
methods
:
{
handleScroll
(
e
)
{
const
eventDelta
=
e
.
wheelDelta
||
-
e
.
deltaY
*
40
const
$scrollWrapper
=
this
.
scrollWrapper
$scrollWrapper
.
scrollLeft
=
$scrollWrapper
.
scrollLeft
+
eventDelta
/
4
},
moveToTarget
(
currentTag
)
{
const
$container
=
this
.
$refs
.
scrollContainer
.
$el
const
$containerWidth
=
$container
.
offsetWidth
const
$scrollWrapper
=
this
.
scrollWrapper
const
tagList
=
this
.
$parent
.
$refs
.
tag
let
firstTag
=
null
let
lastTag
=
null
// find first tag and last tag
if
(
tagList
.
length
>
0
)
{
firstTag
=
tagList
[
0
]
lastTag
=
tagList
[
tagList
.
length
-
1
]
}
if
(
firstTag
===
currentTag
)
{
$scrollWrapper
.
scrollLeft
=
0
}
else
if
(
lastTag
===
currentTag
)
{
$scrollWrapper
.
scrollLeft
=
$scrollWrapper
.
scrollWidth
-
$containerWidth
}
else
{
// find preTag and nextTag
const
currentIndex
=
tagList
.
findIndex
(
item
=>
item
===
currentTag
)
const
prevTag
=
tagList
[
currentIndex
-
1
]
const
nextTag
=
tagList
[
currentIndex
+
1
]
// the tag's offsetLeft after of nextTag
const
afterNextTagOffsetLeft
=
nextTag
.
$el
.
offsetLeft
+
nextTag
.
$el
.
offsetWidth
+
tagAndTagSpacing
// the tag's offsetLeft before of prevTag
const
beforePrevTagOffsetLeft
=
prevTag
.
$el
.
offsetLeft
-
tagAndTagSpacing
if
(
afterNextTagOffsetLeft
>
$scrollWrapper
.
scrollLeft
+
$containerWidth
)
{
$scrollWrapper
.
scrollLeft
=
afterNextTagOffsetLeft
-
$containerWidth
}
else
if
(
beforePrevTagOffsetLeft
<
$scrollWrapper
.
scrollLeft
)
{
$scrollWrapper
.
scrollLeft
=
beforePrevTagOffsetLeft
}
}
}
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.scroll-container
{
white-space
:
nowrap
;
position
:
relative
;
overflow
:
hidden
;
width
:
100%
;
/
deep
/
{
.el-scrollbar__bar
{
bottom
:
0px
;
}
.el-scrollbar__wrap
{
height
:
49px
;
}
}
}
</
style
>
litemall-admin/src/views/layout/components/Sidebar/SidebarItem.vue
View file @
ed9f9462
<
template
>
<div
v-if=
"!item.hidden&&item.children"
class=
"menu-wrapper"
>
<div
v-if=
"!item.hidden"
class=
"menu-wrapper"
>
<template
v-if=
"hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow"
>
<app-link
:to=
"resolvePath(onlyOneChild.path)"
>
<app-link
v-if=
"onlyOneChild.meta"
:to=
"resolvePath(onlyOneChild.path)"
>
<el-menu-item
:index=
"resolvePath(onlyOneChild.path)"
:class=
"
{'submenu-title-noDropdown':!isNest}">
<item
v-if=
"onlyOneChild.meta"
:icon=
"onlyOneChild.meta.icon||item.meta.icon"
:title=
"onlyOneChild.meta.title"
/>
<item
:icon=
"onlyOneChild.meta.icon||
(item.meta&&
item.meta.icon
)
"
:title=
"onlyOneChild.meta.title"
/>
</el-menu-item>
</app-link>
</
template
>
<el-submenu
v-else
ref=
"submenu"
:index=
"resolvePath(item.path)"
>
<el-submenu
v-else
ref=
"subMenu"
:index=
"resolvePath(item.path)"
popper-append-to-body
>
<
template
slot=
"title"
>
<item
v-if=
"item.meta"
:icon=
"item.meta.icon"
:title=
"item.meta.title"
/>
</
template
>
<
template
v-for=
"child in item.children"
v-if=
"!child.hidden"
>
<sidebar-item
v-if=
"child.children&&child.children.length>0"
:is-nest=
"true"
:item=
"child"
:key=
"child.path"
:base-path=
"resolvePath(child.path)"
class=
"nest-menu"
/>
<app-link
v-else
:to=
"resolvePath(child.path)"
:key=
"child.name"
>
<el-menu-item
:index=
"resolvePath(child.path)"
>
<item
v-if=
"child.meta"
:icon=
"child.meta.icon"
:title=
"child.meta.title"
/>
</el-menu-item>
</app-link>
<item
v-if=
"item.meta"
:icon=
"item.meta && item.meta.icon"
:title=
"item.meta.title"
/>
</
template
>
<sidebar-item
v-for=
"child in item.children"
:key=
"child.path"
:is-nest=
"true"
:item=
"child"
:base-path=
"resolvePath(child.path)"
class=
"nest-menu"
/>
</el-submenu>
</div>
</template>
<
script
>
import
path
from
'
path
'
import
{
isExternal
}
from
'
@/utils
'
import
{
isExternal
}
from
'
@/utils
/validate
'
import
Item
from
'
./Item
'
import
AppLink
from
'
./Link
'
import
FixiOSBug
from
'
./FixiOSBug
'
...
...
@@ -61,12 +50,13 @@ export default {
}
},
data
()
{
return
{
onlyOneChild
:
null
}
// To fix https://github.com/PanJiaChen/vue-admin-template/issues/237
// TODO: refactor with render function
this
.
onlyOneChild
=
null
return
{}
},
methods
:
{
hasOneShowingChild
(
children
,
parent
)
{
hasOneShowingChild
(
children
=
[]
,
parent
)
{
const
showingChildren
=
children
.
filter
(
item
=>
{
if
(
item
.
hidden
)
{
return
false
...
...
@@ -91,13 +81,13 @@ export default {
return
false
},
resolvePath
(
routePath
)
{
if
(
this
.
isExternal
Link
(
routePath
))
{
if
(
isExternal
(
routePath
))
{
return
routePath
}
if
(
isExternal
(
this
.
basePath
))
{
return
this
.
basePath
}
return
path
.
resolve
(
this
.
basePath
,
routePath
)
},
isExternalLink
(
routePath
)
{
return
isExternal
(
routePath
)
}
}
}
...
...
litemall-admin/src/views/layout/components/TagsView.vue
View file @
ed9f9462
<
template
>
<div
class=
"tags-view-container"
>
<div
id=
"tags-view-container"
class=
"tags-view-container"
>
<scroll-pane
ref=
"scrollPane"
class=
"tags-view-wrapper"
>
<router-link
v-for=
"tag in visitedViews"
ref=
"tag"
:key=
"tag.path"
:class=
"isActive(tag)?'active':''"
:to=
"
{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
:key="tag.path"
tag="span"
class="tags-view-item"
@click.middle.native="closeSelectedTag(tag)"
@contextmenu.prevent.native="openMenu(tag,$event)">
@click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"
@contextmenu.prevent.native="openMenu(tag,$event)"
>
{{
tag
.
title
}}
<span
class=
"el-icon-close"
@
click.prevent.stop=
"closeSelectedTag(tag)"
/>
<span
v-if=
"!isAffix(tag)"
class=
"el-icon-close"
@
click.prevent.stop=
"closeSelectedTag(tag)"
/>
</router-link>
</scroll-pane>
<ul
v-show=
"visible"
:style=
"
{left:left+'px',top:top+'px'}" class="contextmenu">
<li
@
click=
"refreshSelectedTag(selectedTag)"
>
刷新
</li>
<li
@
click=
"closeSelectedTag(selectedTag)"
>
关闭
</li>
<li
@
click=
"closeOthersTags"
>
关闭其
它
</li>
<li
@
click=
"closeAllTags"
>
关闭所有
</li>
<li
v-if=
"!isAffix(selectedTag)"
@
click=
"closeSelectedTag(selectedTag)"
>
关闭
</li>
<li
@
click=
"closeOthersTags"
>
关闭其
他
</li>
<li
@
click=
"closeAllTags
(selectedTag)
"
>
关闭所有
</li>
</ul>
</div>
</
template
>
<
script
>
import
ScrollPane
from
'
@/components/ScrollPane
'
import
ScrollPane
from
'
./ScrollPane
'
import
path
from
'
path
'
export
default
{
components
:
{
ScrollPane
},
...
...
@@ -34,17 +36,21 @@ export default {
visible
:
false
,
top
:
0
,
left
:
0
,
selectedTag
:
{}
selectedTag
:
{},
affixTags
:
[]
}
},
computed
:
{
visitedViews
()
{
return
this
.
$store
.
state
.
tagsView
.
visitedViews
},
routes
()
{
return
this
.
$store
.
state
.
permission
.
routers
}
},
watch
:
{
$route
()
{
this
.
add
View
Tags
()
this
.
addTags
()
this
.
moveToCurrentTag
()
},
visible
(
value
)
{
...
...
@@ -56,16 +62,50 @@ export default {
}
},
mounted
()
{
this
.
addViewTags
()
this
.
initTags
()
this
.
addTags
()
},
methods
:
{
isActive
(
route
)
{
return
route
.
path
===
this
.
$route
.
path
},
addViewTags
()
{
isAffix
(
tag
)
{
return
tag
.
meta
&&
tag
.
meta
.
affix
},
filterAffixTags
(
routes
,
basePath
=
'
/
'
)
{
let
tags
=
[]
routes
.
forEach
(
route
=>
{
if
(
route
.
meta
&&
route
.
meta
.
affix
)
{
const
tagPath
=
path
.
resolve
(
basePath
,
route
.
path
)
tags
.
push
({
fullPath
:
tagPath
,
path
:
tagPath
,
name
:
route
.
name
,
meta
:
{
...
route
.
meta
}
})
}
if
(
route
.
children
)
{
const
tempTags
=
this
.
filterAffixTags
(
route
.
children
,
route
.
path
)
if
(
tempTags
.
length
>=
1
)
{
tags
=
[...
tags
,
...
tempTags
]
}
}
})
return
tags
},
initTags
()
{
const
affixTags
=
this
.
affixTags
=
this
.
filterAffixTags
(
this
.
routes
)
for
(
const
tag
of
affixTags
)
{
// Must have tag name
if
(
tag
.
name
)
{
this
.
$store
.
dispatch
(
'
tagsView/addVisitedView
'
,
tag
)
}
}
},
addTags
()
{
const
{
name
}
=
this
.
$route
if
(
name
)
{
this
.
$store
.
dispatch
(
'
addView
'
,
this
.
$route
)
this
.
$store
.
dispatch
(
'
tagsView/
addView
'
,
this
.
$route
)
}
return
false
},
...
...
@@ -75,19 +115,17 @@ export default {
for
(
const
tag
of
tags
)
{
if
(
tag
.
to
.
path
===
this
.
$route
.
path
)
{
this
.
$refs
.
scrollPane
.
moveToTarget
(
tag
)
// when query is different then update
if
(
tag
.
to
.
fullPath
!==
this
.
$route
.
fullPath
)
{
this
.
$store
.
dispatch
(
'
updateVisitedView
'
,
this
.
$route
)
this
.
$store
.
dispatch
(
'
tagsView/
updateVisitedView
'
,
this
.
$route
)
}
break
}
}
})
},
refreshSelectedTag
(
view
)
{
this
.
$store
.
dispatch
(
'
delCachedView
'
,
view
).
then
(()
=>
{
this
.
$store
.
dispatch
(
'
tagsView/
delCachedView
'
,
view
).
then
(()
=>
{
const
{
fullPath
}
=
view
this
.
$nextTick
(()
=>
{
this
.
$router
.
replace
({
...
...
@@ -97,26 +135,40 @@ export default {
})
},
closeSelectedTag
(
view
)
{
this
.
$store
.
dispatch
(
'
delView
'
,
view
).
then
(({
visitedViews
})
=>
{
this
.
$store
.
dispatch
(
'
tagsView/
delView
'
,
view
).
then
(({
visitedViews
})
=>
{
if
(
this
.
isActive
(
view
))
{
const
latestView
=
visitedViews
.
slice
(
-
1
)[
0
]
if
(
latestView
)
{
this
.
$router
.
push
(
latestView
)
}
else
{
this
.
$router
.
push
(
'
/
'
)
}
this
.
toLastView
(
visitedViews
,
view
)
}
})
},
closeOthersTags
()
{
this
.
$router
.
push
(
this
.
selectedTag
)
this
.
$store
.
dispatch
(
'
delOthersViews
'
,
this
.
selectedTag
).
then
(()
=>
{
this
.
$store
.
dispatch
(
'
tagsView/
delOthersViews
'
,
this
.
selectedTag
).
then
(()
=>
{
this
.
moveToCurrentTag
()
})
},
closeAllTags
()
{
this
.
$store
.
dispatch
(
'
delAllViews
'
)
this
.
$router
.
push
(
'
/
'
)
closeAllTags
(
view
)
{
this
.
$store
.
dispatch
(
'
tagsView/delAllViews
'
).
then
(({
visitedViews
})
=>
{
if
(
this
.
affixTags
.
some
(
tag
=>
tag
.
path
===
view
.
path
))
{
return
}
this
.
toLastView
(
visitedViews
,
view
)
})
},
toLastView
(
visitedViews
,
view
)
{
const
latestView
=
visitedViews
.
slice
(
-
1
)[
0
]
if
(
latestView
)
{
this
.
$router
.
push
(
latestView
.
fullPath
)
}
else
{
// now the default is to redirect to the home page if there is no tags-view,
// you can adjust it according to your needs.
if
(
view
.
name
===
'
Dashboard
'
)
{
// to reload home page
this
.
$router
.
replace
({
path
:
'
/redirect
'
+
view
.
fullPath
})
}
else
{
this
.
$router
.
push
(
'
/
'
)
}
}
},
openMenu
(
tag
,
e
)
{
const
menuMinWidth
=
105
...
...
@@ -130,8 +182,8 @@ export default {
}
else
{
this
.
left
=
left
}
this
.
top
=
e
.
clientY
this
.
top
=
e
.
clientY
this
.
visible
=
true
this
.
selectedTag
=
tag
},
...
...
@@ -142,7 +194,7 @@ export default {
}
</
script
>
<
style
rel=
"stylesheet/scss"
lang=
"scss"
scoped
>
<
style
lang=
"scss"
scoped
>
.tags-view-container
{
height
:
34px
;
width
:
100%
;
...
...
@@ -189,7 +241,7 @@ export default {
.contextmenu
{
margin
:
0
;
background
:
#fff
;
z-index
:
1
00
;
z-index
:
30
00
;
position
:
absolute
;
list-style-type
:
none
;
padding
:
5px
0
;
...
...
@@ -210,7 +262,7 @@ export default {
}
</
style
>
<
style
rel=
"stylesheet/scss"
lang=
"scss"
>
<
style
lang=
"scss"
>
//reset element css of el-icon-close
.tags-view-wrapper
{
.tags-view-item
{
...
...
litemall-admin/src/views/redirect/index.vue
View file @
ed9f9462
<
script
>
export
default
{
beforeC
reate
()
{
c
reate
d
()
{
const
{
params
,
query
}
=
this
.
$route
const
{
path
}
=
params
this
.
$router
.
replace
({
path
:
'
/
'
+
path
,
query
})
...
...
Write
Preview
Supports
Markdown
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