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
Springboot Plus
Commits
95285c62
Commit
95285c62
authored
Sep 01, 2019
by
一日看尽长安花
Committed by
trumansdo
Sep 01, 2019
Browse files
这是vue版的重构开发分支。
预计工作计划如下: 1、完成路由表 2、重构动态的搜索框组件 3、剩下就是各个接口的重写。
parent
2731ce86
Changes
302
Hide whitespace changes
Inline
Side-by-side
admin-web/src/components/ThemePicker/index.vue
0 → 100644
View file @
95285c62
<
template
>
<el-color-picker
v-model=
"theme"
:predefine=
"['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', ]"
class=
"theme-picker"
popper-class=
"theme-picker-dropdown"
/>
</
template
>
<
script
>
const
version
=
require
(
'
element-ui/package.json
'
).
version
// element-ui version from node_modules
const
ORIGINAL_THEME
=
'
#409EFF
'
// default color
export
default
{
data
()
{
return
{
chalk
:
''
,
// content of theme-chalk css
theme
:
''
}
},
computed
:
{
defaultTheme
()
{
return
this
.
$store
.
state
.
settings
.
theme
}
},
watch
:
{
defaultTheme
:
{
handler
:
function
(
val
,
oldVal
)
{
this
.
theme
=
val
},
immediate
:
true
},
async
theme
(
val
)
{
const
oldVal
=
this
.
chalk
?
this
.
theme
:
ORIGINAL_THEME
if
(
typeof
val
!==
'
string
'
)
return
const
themeCluster
=
this
.
getThemeCluster
(
val
.
replace
(
'
#
'
,
''
))
const
originalCluster
=
this
.
getThemeCluster
(
oldVal
.
replace
(
'
#
'
,
''
))
console
.
log
(
themeCluster
,
originalCluster
)
const
$message
=
this
.
$message
({
message
:
'
Compiling the theme
'
,
customClass
:
'
theme-message
'
,
type
:
'
success
'
,
duration
:
0
,
iconClass
:
'
el-icon-loading
'
})
const
getHandler
=
(
variable
,
id
)
=>
{
return
()
=>
{
const
originalCluster
=
this
.
getThemeCluster
(
ORIGINAL_THEME
.
replace
(
'
#
'
,
''
))
const
newStyle
=
this
.
updateStyle
(
this
[
variable
],
originalCluster
,
themeCluster
)
let
styleTag
=
document
.
getElementById
(
id
)
if
(
!
styleTag
)
{
styleTag
=
document
.
createElement
(
'
style
'
)
styleTag
.
setAttribute
(
'
id
'
,
id
)
document
.
head
.
appendChild
(
styleTag
)
}
styleTag
.
innerText
=
newStyle
}
}
if
(
!
this
.
chalk
)
{
const
url
=
`https://unpkg.com/element-ui@
${
version
}
/lib/theme-chalk/index.css`
await
this
.
getCSSString
(
url
,
'
chalk
'
)
}
const
chalkHandler
=
getHandler
(
'
chalk
'
,
'
chalk-style
'
)
chalkHandler
()
const
styles
=
[].
slice
.
call
(
document
.
querySelectorAll
(
'
style
'
))
.
filter
(
style
=>
{
const
text
=
style
.
innerText
return
new
RegExp
(
oldVal
,
'
i
'
).
test
(
text
)
&&
!
/Chalk Variables/
.
test
(
text
)
})
styles
.
forEach
(
style
=>
{
const
{
innerText
}
=
style
if
(
typeof
innerText
!==
'
string
'
)
return
style
.
innerText
=
this
.
updateStyle
(
innerText
,
originalCluster
,
themeCluster
)
})
this
.
$emit
(
'
change
'
,
val
)
$message
.
close
()
}
},
methods
:
{
updateStyle
(
style
,
oldCluster
,
newCluster
)
{
let
newStyle
=
style
oldCluster
.
forEach
((
color
,
index
)
=>
{
newStyle
=
newStyle
.
replace
(
new
RegExp
(
color
,
'
ig
'
),
newCluster
[
index
])
})
return
newStyle
},
getCSSString
(
url
,
variable
)
{
return
new
Promise
(
resolve
=>
{
const
xhr
=
new
XMLHttpRequest
()
xhr
.
onreadystatechange
=
()
=>
{
if
(
xhr
.
readyState
===
4
&&
xhr
.
status
===
200
)
{
this
[
variable
]
=
xhr
.
responseText
.
replace
(
/@font-face{
[^
}
]
+}/
,
''
)
resolve
()
}
}
xhr
.
open
(
'
GET
'
,
url
)
xhr
.
send
()
})
},
getThemeCluster
(
theme
)
{
const
tintColor
=
(
color
,
tint
)
=>
{
let
red
=
parseInt
(
color
.
slice
(
0
,
2
),
16
)
let
green
=
parseInt
(
color
.
slice
(
2
,
4
),
16
)
let
blue
=
parseInt
(
color
.
slice
(
4
,
6
),
16
)
if
(
tint
===
0
)
{
// when primary color is in its rgb space
return
[
red
,
green
,
blue
].
join
(
'
,
'
)
}
else
{
red
+=
Math
.
round
(
tint
*
(
255
-
red
))
green
+=
Math
.
round
(
tint
*
(
255
-
green
))
blue
+=
Math
.
round
(
tint
*
(
255
-
blue
))
red
=
red
.
toString
(
16
)
green
=
green
.
toString
(
16
)
blue
=
blue
.
toString
(
16
)
return
`#
${
red
}${
green
}${
blue
}
`
}
}
const
shadeColor
=
(
color
,
shade
)
=>
{
let
red
=
parseInt
(
color
.
slice
(
0
,
2
),
16
)
let
green
=
parseInt
(
color
.
slice
(
2
,
4
),
16
)
let
blue
=
parseInt
(
color
.
slice
(
4
,
6
),
16
)
red
=
Math
.
round
((
1
-
shade
)
*
red
)
green
=
Math
.
round
((
1
-
shade
)
*
green
)
blue
=
Math
.
round
((
1
-
shade
)
*
blue
)
red
=
red
.
toString
(
16
)
green
=
green
.
toString
(
16
)
blue
=
blue
.
toString
(
16
)
return
`#
${
red
}${
green
}${
blue
}
`
}
const
clusters
=
[
theme
]
for
(
let
i
=
0
;
i
<=
9
;
i
++
)
{
clusters
.
push
(
tintColor
(
theme
,
Number
((
i
/
10
).
toFixed
(
2
))))
}
clusters
.
push
(
shadeColor
(
theme
,
0.1
))
return
clusters
}
}
}
</
script
>
<
style
>
.theme-message
,
.theme-picker-dropdown
{
z-index
:
99999
!important
;
}
.theme-picker
.el-color-picker__trigger
{
height
:
26px
!important
;
width
:
26px
!important
;
padding
:
2px
;
}
.theme-picker-dropdown
.el-color-dropdown__link-btn
{
display
:
none
;
}
</
style
>
admin-web/src/components/Tinymce/components/EditorImage.vue
0 → 100644
View file @
95285c62
<
template
>
<div
class=
"upload-container"
>
<el-button
:style=
"
{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary" @click=" dialogVisible=true">
upload
</el-button>
<el-dialog
:visible.sync=
"dialogVisible"
>
<el-upload
:multiple=
"true"
:file-list=
"fileList"
:show-file-list=
"true"
:on-remove=
"handleRemove"
:on-success=
"handleSuccess"
:before-upload=
"beforeUpload"
class=
"editor-slide-upload"
action=
"https://httpbin.org/post"
list-type=
"picture-card"
>
<el-button
size=
"small"
type=
"primary"
>
Click upload
</el-button>
</el-upload>
<el-button
@
click=
"dialogVisible = false"
>
Cancel
</el-button>
<el-button
type=
"primary"
@
click=
"handleSubmit"
>
Confirm
</el-button>
</el-dialog>
</div>
</
template
>
<
script
>
// import { getToken } from 'api/qiniu'
export
default
{
name
:
'
EditorSlideUpload
'
,
props
:
{
color
:
{
type
:
String
,
default
:
'
#1890ff
'
}
},
data
()
{
return
{
dialogVisible
:
false
,
listObj
:
{},
fileList
:
[]
}
},
methods
:
{
checkAllSuccess
()
{
return
Object
.
keys
(
this
.
listObj
).
every
(
item
=>
this
.
listObj
[
item
].
hasSuccess
)
},
handleSubmit
()
{
const
arr
=
Object
.
keys
(
this
.
listObj
).
map
(
v
=>
this
.
listObj
[
v
])
if
(
!
this
.
checkAllSuccess
())
{
this
.
$message
(
'
Please wait for all images to be uploaded successfully. If there is a network problem, please refresh the page and upload again!
'
)
return
}
this
.
$emit
(
'
successCBK
'
,
arr
)
this
.
listObj
=
{}
this
.
fileList
=
[]
this
.
dialogVisible
=
false
},
handleSuccess
(
response
,
file
)
{
const
uid
=
file
.
uid
const
objKeyArr
=
Object
.
keys
(
this
.
listObj
)
for
(
let
i
=
0
,
len
=
objKeyArr
.
length
;
i
<
len
;
i
++
)
{
if
(
this
.
listObj
[
objKeyArr
[
i
]].
uid
===
uid
)
{
this
.
listObj
[
objKeyArr
[
i
]].
url
=
response
.
files
.
file
this
.
listObj
[
objKeyArr
[
i
]].
hasSuccess
=
true
return
}
}
},
handleRemove
(
file
)
{
const
uid
=
file
.
uid
const
objKeyArr
=
Object
.
keys
(
this
.
listObj
)
for
(
let
i
=
0
,
len
=
objKeyArr
.
length
;
i
<
len
;
i
++
)
{
if
(
this
.
listObj
[
objKeyArr
[
i
]].
uid
===
uid
)
{
delete
this
.
listObj
[
objKeyArr
[
i
]]
return
}
}
},
beforeUpload
(
file
)
{
const
_self
=
this
const
_URL
=
window
.
URL
||
window
.
webkitURL
const
fileName
=
file
.
uid
this
.
listObj
[
fileName
]
=
{}
return
new
Promise
((
resolve
,
reject
)
=>
{
const
img
=
new
Image
()
img
.
src
=
_URL
.
createObjectURL
(
file
)
img
.
onload
=
function
()
{
_self
.
listObj
[
fileName
]
=
{
hasSuccess
:
false
,
uid
:
file
.
uid
,
width
:
this
.
width
,
height
:
this
.
height
}
}
resolve
(
true
)
})
}
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.editor-slide-upload
{
margin-bottom
:
20px
;
/
deep
/
.el-upload--picture-card
{
width
:
100%
;
}
}
</
style
>
admin-web/src/components/Tinymce/dynamicLoadScript.js
0 → 100644
View file @
95285c62
let
callbacks
=
[]
function
loadedTinymce
()
{
// to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2144
// check is successfully downloaded script
return
window
.
tinymce
}
const
dynamicLoadScript
=
(
src
,
callback
)
=>
{
const
existingScript
=
document
.
getElementById
(
src
)
const
cb
=
callback
||
function
()
{}
if
(
!
existingScript
)
{
const
script
=
document
.
createElement
(
'
script
'
)
script
.
src
=
src
// src url for the third-party library being loaded.
script
.
id
=
src
document
.
body
.
appendChild
(
script
)
callbacks
.
push
(
cb
)
const
onEnd
=
'
onload
'
in
script
?
stdOnEnd
:
ieOnEnd
onEnd
(
script
)
}
if
(
existingScript
&&
cb
)
{
if
(
loadedTinymce
())
{
cb
(
null
,
existingScript
)
}
else
{
callbacks
.
push
(
cb
)
}
}
function
stdOnEnd
(
script
)
{
script
.
onload
=
function
()
{
// this.onload = null here is necessary
// because even IE9 works not like others
this
.
onerror
=
this
.
onload
=
null
for
(
const
cb
of
callbacks
)
{
cb
(
null
,
script
)
}
callbacks
=
null
}
script
.
onerror
=
function
()
{
this
.
onerror
=
this
.
onload
=
null
cb
(
new
Error
(
'
Failed to load
'
+
src
),
script
)
}
}
function
ieOnEnd
(
script
)
{
script
.
onreadystatechange
=
function
()
{
if
(
this
.
readyState
!==
'
complete
'
&&
this
.
readyState
!==
'
loaded
'
)
return
this
.
onreadystatechange
=
null
for
(
const
cb
of
callbacks
)
{
cb
(
null
,
script
)
// there is no way to catch loading errors in IE8
}
callbacks
=
null
}
}
}
export
default
dynamicLoadScript
admin-web/src/components/Tinymce/index.vue
0 → 100644
View file @
95285c62
<
template
>
<div
:class=
"
{fullscreen:fullscreen}" class="tinymce-container" :style="{width:containerWidth}">
<textarea
:id=
"tinymceId"
class=
"tinymce-textarea"
/>
<div
class=
"editor-custom-btn-container"
>
<editorImage
color=
"#1890ff"
class=
"editor-upload-btn"
@
successCBK=
"imageSuccessCBK"
/>
</div>
</div>
</
template
>
<
script
>
/**
* docs:
* https://panjiachen.github.io/vue-element-admin-site/feature/component/rich-editor.html#tinymce
*/
import
editorImage
from
'
./components/EditorImage
'
import
plugins
from
'
./plugins
'
import
toolbar
from
'
./toolbar
'
import
load
from
'
./dynamicLoadScript
'
// why use this cdn, detail see https://github.com/PanJiaChen/tinymce-all-in-one
const
tinymceCDN
=
'
https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.3/tinymce.min.js
'
export
default
{
name
:
'
Tinymce
'
,
components
:
{
editorImage
},
props
:
{
id
:
{
type
:
String
,
default
:
function
()
{
return
'
vue-tinymce-
'
+
+
new
Date
()
+
((
Math
.
random
()
*
1000
).
toFixed
(
0
)
+
''
)
}
},
value
:
{
type
:
String
,
default
:
''
},
toolbar
:
{
type
:
Array
,
required
:
false
,
default
()
{
return
[]
}
},
menubar
:
{
type
:
String
,
default
:
'
file edit insert view format table
'
},
height
:
{
type
:
[
Number
,
String
],
required
:
false
,
default
:
360
},
width
:
{
type
:
[
Number
,
String
],
required
:
false
,
default
:
'
auto
'
}
},
data
()
{
return
{
hasChange
:
false
,
hasInit
:
false
,
tinymceId
:
this
.
id
,
fullscreen
:
false
,
languageTypeList
:
{
'
en
'
:
'
en
'
,
'
zh
'
:
'
zh_CN
'
,
'
es
'
:
'
es_MX
'
,
'
ja
'
:
'
ja
'
}
}
},
computed
:
{
containerWidth
()
{
const
width
=
this
.
width
if
(
/^
[\d]
+
(\.[\d]
+
)?
$/
.
test
(
width
))
{
// matches `100`, `'100'`
return
`
${
width
}
px`
}
return
width
}
},
watch
:
{
value
(
val
)
{
if
(
!
this
.
hasChange
&&
this
.
hasInit
)
{
this
.
$nextTick
(()
=>
window
.
tinymce
.
get
(
this
.
tinymceId
).
setContent
(
val
||
''
))
}
}
},
mounted
()
{
this
.
init
()
},
activated
()
{
if
(
window
.
tinymce
)
{
this
.
initTinymce
()
}
},
deactivated
()
{
this
.
destroyTinymce
()
},
destroyed
()
{
this
.
destroyTinymce
()
},
methods
:
{
init
()
{
// dynamic load tinymce from cdn
load
(
tinymceCDN
,
(
err
)
=>
{
if
(
err
)
{
this
.
$message
.
error
(
err
.
message
)
return
}
this
.
initTinymce
()
})
},
initTinymce
()
{
const
_this
=
this
window
.
tinymce
.
init
({
selector
:
`#
${
this
.
tinymceId
}
`
,
language
:
this
.
languageTypeList
[
'
en
'
],
height
:
this
.
height
,
body_class
:
'
panel-body
'
,
object_resizing
:
false
,
toolbar
:
this
.
toolbar
.
length
>
0
?
this
.
toolbar
:
toolbar
,
menubar
:
this
.
menubar
,
plugins
:
plugins
,
end_container_on_empty_block
:
true
,
powerpaste_word_import
:
'
clean
'
,
code_dialog_height
:
450
,
code_dialog_width
:
1000
,
advlist_bullet_styles
:
'
square
'
,
advlist_number_styles
:
'
default
'
,
imagetools_cors_hosts
:
[
'
www.tinymce.com
'
,
'
codepen.io
'
],
default_link_target
:
'
_blank
'
,
link_title
:
false
,
nonbreaking_force_tab
:
true
,
// inserting nonbreaking space need Nonbreaking Space Plugin
init_instance_callback
:
editor
=>
{
if
(
_this
.
value
)
{
editor
.
setContent
(
_this
.
value
)
}
_this
.
hasInit
=
true
editor
.
on
(
'
NodeChange Change KeyUp SetContent
'
,
()
=>
{
this
.
hasChange
=
true
this
.
$emit
(
'
input
'
,
editor
.
getContent
())
})
},
setup
(
editor
)
{
editor
.
on
(
'
FullscreenStateChanged
'
,
(
e
)
=>
{
_this
.
fullscreen
=
e
.
state
})
}
// 整合七牛上传
// images_dataimg_filter(img) {
// setTimeout(() => {
// const $image = $(img);
// $image.removeAttr('width');
// $image.removeAttr('height');
// if ($image[0].height && $image[0].width) {
// $image.attr('data-wscntype', 'image');
// $image.attr('data-wscnh', $image[0].height);
// $image.attr('data-wscnw', $image[0].width);
// $image.addClass('wscnph');
// }
// }, 0);
// return img
// },
// images_upload_handler(blobInfo, success, failure, progress) {
// progress(0);
// const token = _this.$store.getters.token;
// getToken(token).then(response => {
// const url = response.data.qiniu_url;
// const formData = new FormData();
// formData.append('token', response.data.qiniu_token);
// formData.append('key', response.data.qiniu_key);
// formData.append('file', blobInfo.blob(), url);
// upload(formData).then(() => {
// success(url);
// progress(100);
// })
// }).catch(err => {
// failure('出现未知问题,刷新页面,或者联系程序员')
// console.log(err);
// });
// },
})
},
destroyTinymce
()
{
const
tinymce
=
window
.
tinymce
.
get
(
this
.
tinymceId
)
if
(
this
.
fullscreen
)
{
tinymce
.
execCommand
(
'
mceFullScreen
'
)
}
if
(
tinymce
)
{
tinymce
.
destroy
()
}
},
setContent
(
value
)
{
window
.
tinymce
.
get
(
this
.
tinymceId
).
setContent
(
value
)
},
getContent
()
{
window
.
tinymce
.
get
(
this
.
tinymceId
).
getContent
()
},
imageSuccessCBK
(
arr
)
{
const
_this
=
this
arr
.
forEach
(
v
=>
{
window
.
tinymce
.
get
(
_this
.
tinymceId
).
insertContent
(
`<img class="wscnph" src="
${
v
.
url
}
" >`
)
})
}
}
}
</
script
>
<
style
scoped
>
.tinymce-container
{
position
:
relative
;
line-height
:
normal
;
}
.tinymce-container
>>>
.mce-fullscreen
{
z-index
:
10000
;
}
.tinymce-textarea
{
visibility
:
hidden
;
z-index
:
-1
;
}
.editor-custom-btn-container
{
position
:
absolute
;
right
:
4px
;
top
:
4px
;
/*z-index: 2005;*/
}
.fullscreen
.editor-custom-btn-container
{
z-index
:
10000
;
position
:
fixed
;
}
.editor-upload-btn
{
display
:
inline-block
;
}
</
style
>
admin-web/src/components/Tinymce/plugins.js
0 → 100644
View file @
95285c62
// Any plugins you want to use has to be imported
// Detail plugins list see https://www.tinymce.com/docs/plugins/
// Custom builds see https://www.tinymce.com/download/custom-builds/
const
plugins
=
[
'
advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount
'
]
export
default
plugins
admin-web/src/components/Tinymce/toolbar.js
0 → 100644
View file @
95285c62
// Here is a list of the toolbar
// Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols
const
toolbar
=
[
'
searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample
'
,
'
hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen
'
]
export
default
toolbar
admin-web/src/components/Upload/SingleImage.vue
0 → 100644
View file @
95285c62
<
template
>
<div
class=
"upload-container"
>
<el-upload
:data=
"dataObj"
:multiple=
"false"
:show-file-list=
"false"
:on-success=
"handleImageSuccess"
class=
"image-uploader"
drag
action=
"https://httpbin.org/post"
>
<i
class=
"el-icon-upload"
/>
<div
class=
"el-upload__text"
>
将文件拖到此处,或
<em>
点击上传
</em>
</div>
</el-upload>
<div
class=
"image-preview"
>
<div
v-show=
"imageUrl.length>1"
class=
"image-preview-wrapper"
>
<img
:src=
"imageUrl+'?imageView2/1/w/200/h/200'"
>
<div
class=
"image-preview-action"
>
<i
class=
"el-icon-delete"
@
click=
"rmImage"
/>
</div>
</div>
</div>
</div>
</
template
>
<
script
>
import
{
getToken
}
from
'
@/api/qiniu
'
export
default
{
name
:
'
SingleImageUpload
'
,
props
:
{
value
:
{
type
:
String
,
default
:
''
}
},
data
()
{
return
{
tempUrl
:
''
,
dataObj
:
{
token
:
''
,
key
:
''
}
}
},
computed
:
{
imageUrl
()
{
return
this
.
value
}
},
methods
:
{
rmImage
()
{
this
.
emitInput
(
''
)
},
emitInput
(
val
)
{
this
.
$emit
(
'
input
'
,
val
)
},
handleImageSuccess
()
{
this
.
emitInput
(
this
.
tempUrl
)
},
beforeUpload
()
{
const
_self
=
this
return
new
Promise
((
resolve
,
reject
)
=>
{
getToken
().
then
(
response
=>
{
const
key
=
response
.
data
.
qiniu_key
const
token
=
response
.
data
.
qiniu_token
_self
.
_data
.
dataObj
.
token
=
token
_self
.
_data
.
dataObj
.
key
=
key
this
.
tempUrl
=
response
.
data
.
qiniu_url
resolve
(
true
)
}).
catch
(
err
=>
{
console
.
log
(
err
)
reject
(
false
)
})
})
}
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
@import
"~@/styles/mixin.scss"
;
.upload-container
{
width
:
100%
;
position
:
relative
;
@include
clearfix
;
.image-uploader
{
width
:
60%
;
float
:
left
;
}
.image-preview
{
width
:
200px
;
height
:
200px
;
position
:
relative
;
border
:
1px
dashed
#d9d9d9
;
float
:
left
;
margin-left
:
50px
;
.image-preview-wrapper
{
position
:
relative
;
width
:
100%
;
height
:
100%
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.image-preview-action
{
position
:
absolute
;
width
:
100%
;
height
:
100%
;
left
:
0
;
top
:
0
;
cursor
:
default
;
text-align
:
center
;
color
:
#fff
;
opacity
:
0
;
font-size
:
20px
;
background-color
:
rgba
(
0
,
0
,
0
,
.5
);
transition
:
opacity
.3s
;
cursor
:
pointer
;
text-align
:
center
;
line-height
:
200px
;
.el-icon-delete
{
font-size
:
36px
;
}
}
&
:hover
{
.image-preview-action
{
opacity
:
1
;
}
}
}
}
</
style
>
admin-web/src/components/Upload/SingleImage2.vue
0 → 100644
View file @
95285c62
<
template
>
<div
class=
"singleImageUpload2 upload-container"
>
<el-upload
:data=
"dataObj"
:multiple=
"false"
:show-file-list=
"false"
:on-success=
"handleImageSuccess"
class=
"image-uploader"
drag
action=
"https://httpbin.org/post"
>
<i
class=
"el-icon-upload"
/>
<div
class=
"el-upload__text"
>
Drag或
<em>
点击上传
</em>
</div>
</el-upload>
<div
v-show=
"imageUrl.length>0"
class=
"image-preview"
>
<div
v-show=
"imageUrl.length>1"
class=
"image-preview-wrapper"
>
<img
:src=
"imageUrl"
>
<div
class=
"image-preview-action"
>
<i
class=
"el-icon-delete"
@
click=
"rmImage"
/>
</div>
</div>
</div>
</div>
</
template
>
<
script
>
import
{
getToken
}
from
'
@/api/qiniu
'
export
default
{
name
:
'
SingleImageUpload2
'
,
props
:
{
value
:
{
type
:
String
,
default
:
''
}
},
data
()
{
return
{
tempUrl
:
''
,
dataObj
:
{
token
:
''
,
key
:
''
}
}
},
computed
:
{
imageUrl
()
{
return
this
.
value
}
},
methods
:
{
rmImage
()
{
this
.
emitInput
(
''
)
},
emitInput
(
val
)
{
this
.
$emit
(
'
input
'
,
val
)
},
handleImageSuccess
()
{
this
.
emitInput
(
this
.
tempUrl
)
},
beforeUpload
()
{
const
_self
=
this
return
new
Promise
((
resolve
,
reject
)
=>
{
getToken
().
then
(
response
=>
{
const
key
=
response
.
data
.
qiniu_key
const
token
=
response
.
data
.
qiniu_token
_self
.
_data
.
dataObj
.
token
=
token
_self
.
_data
.
dataObj
.
key
=
key
this
.
tempUrl
=
response
.
data
.
qiniu_url
resolve
(
true
)
}).
catch
(()
=>
{
reject
(
false
)
})
})
}
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.upload-container
{
width
:
100%
;
height
:
100%
;
position
:
relative
;
.image-uploader
{
height
:
100%
;
}
.image-preview
{
width
:
100%
;
height
:
100%
;
position
:
absolute
;
left
:
0px
;
top
:
0px
;
border
:
1px
dashed
#d9d9d9
;
.image-preview-wrapper
{
position
:
relative
;
width
:
100%
;
height
:
100%
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.image-preview-action
{
position
:
absolute
;
width
:
100%
;
height
:
100%
;
left
:
0
;
top
:
0
;
cursor
:
default
;
text-align
:
center
;
color
:
#fff
;
opacity
:
0
;
font-size
:
20px
;
background-color
:
rgba
(
0
,
0
,
0
,
.5
);
transition
:
opacity
.3s
;
cursor
:
pointer
;
text-align
:
center
;
line-height
:
200px
;
.el-icon-delete
{
font-size
:
36px
;
}
}
&
:hover
{
.image-preview-action
{
opacity
:
1
;
}
}
}
}
</
style
>
admin-web/src/components/Upload/SingleImage3.vue
0 → 100644
View file @
95285c62
<
template
>
<div
class=
"upload-container"
>
<el-upload
:data=
"dataObj"
:multiple=
"false"
:show-file-list=
"false"
:on-success=
"handleImageSuccess"
class=
"image-uploader"
drag
action=
"https://httpbin.org/post"
>
<i
class=
"el-icon-upload"
/>
<div
class=
"el-upload__text"
>
将文件拖到此处,或
<em>
点击上传
</em>
</div>
</el-upload>
<div
class=
"image-preview image-app-preview"
>
<div
v-show=
"imageUrl.length>1"
class=
"image-preview-wrapper"
>
<img
:src=
"imageUrl"
>
<div
class=
"image-preview-action"
>
<i
class=
"el-icon-delete"
@
click=
"rmImage"
/>
</div>
</div>
</div>
<div
class=
"image-preview"
>
<div
v-show=
"imageUrl.length>1"
class=
"image-preview-wrapper"
>
<img
:src=
"imageUrl"
>
<div
class=
"image-preview-action"
>
<i
class=
"el-icon-delete"
@
click=
"rmImage"
/>
</div>
</div>
</div>
</div>
</
template
>
<
script
>
import
{
getToken
}
from
'
@/api/qiniu
'
export
default
{
name
:
'
SingleImageUpload3
'
,
props
:
{
value
:
{
type
:
String
,
default
:
''
}
},
data
()
{
return
{
tempUrl
:
''
,
dataObj
:
{
token
:
''
,
key
:
''
}
}
},
computed
:
{
imageUrl
()
{
return
this
.
value
}
},
methods
:
{
rmImage
()
{
this
.
emitInput
(
''
)
},
emitInput
(
val
)
{
this
.
$emit
(
'
input
'
,
val
)
},
handleImageSuccess
(
file
)
{
this
.
emitInput
(
file
.
files
.
file
)
},
beforeUpload
()
{
const
_self
=
this
return
new
Promise
((
resolve
,
reject
)
=>
{
getToken
().
then
(
response
=>
{
const
key
=
response
.
data
.
qiniu_key
const
token
=
response
.
data
.
qiniu_token
_self
.
_data
.
dataObj
.
token
=
token
_self
.
_data
.
dataObj
.
key
=
key
this
.
tempUrl
=
response
.
data
.
qiniu_url
resolve
(
true
)
}).
catch
(
err
=>
{
console
.
log
(
err
)
reject
(
false
)
})
})
}
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
@import
"~@/styles/mixin.scss"
;
.upload-container
{
width
:
100%
;
position
:
relative
;
@include
clearfix
;
.image-uploader
{
width
:
35%
;
float
:
left
;
}
.image-preview
{
width
:
200px
;
height
:
200px
;
position
:
relative
;
border
:
1px
dashed
#d9d9d9
;
float
:
left
;
margin-left
:
50px
;
.image-preview-wrapper
{
position
:
relative
;
width
:
100%
;
height
:
100%
;
img
{
width
:
100%
;
height
:
100%
;
}
}
.image-preview-action
{
position
:
absolute
;
width
:
100%
;
height
:
100%
;
left
:
0
;
top
:
0
;
cursor
:
default
;
text-align
:
center
;
color
:
#fff
;
opacity
:
0
;
font-size
:
20px
;
background-color
:
rgba
(
0
,
0
,
0
,
.5
);
transition
:
opacity
.3s
;
cursor
:
pointer
;
text-align
:
center
;
line-height
:
200px
;
.el-icon-delete
{
font-size
:
36px
;
}
}
&
:hover
{
.image-preview-action
{
opacity
:
1
;
}
}
}
.image-app-preview
{
width
:
320px
;
height
:
180px
;
position
:
relative
;
border
:
1px
dashed
#d9d9d9
;
float
:
left
;
margin-left
:
50px
;
.app-fake-conver
{
height
:
44px
;
position
:
absolute
;
width
:
100%
;
// background: rgba(0, 0, 0, .1);
text-align
:
center
;
line-height
:
64px
;
color
:
#fff
;
}
}
}
</
style
>
admin-web/src/components/UploadExcel/index.vue
0 → 100644
View file @
95285c62
<
template
>
<div>
<input
ref=
"excel-upload-input"
class=
"excel-upload-input"
type=
"file"
accept=
".xlsx, .xls"
@
change=
"handleClick"
>
<div
class=
"drop"
@
drop=
"handleDrop"
@
dragover=
"handleDragover"
@
dragenter=
"handleDragover"
>
Drop excel file here or
<el-button
:loading=
"loading"
style=
"margin-left:16px;"
size=
"mini"
type=
"primary"
@
click=
"handleUpload"
>
Browse
</el-button>
</div>
</div>
</
template
>
<
script
>
import
XLSX
from
'
xlsx
'
export
default
{
props
:
{
beforeUpload
:
Function
,
// eslint-disable-line
onSuccess
:
Function
// eslint-disable-line
},
data
()
{
return
{
loading
:
false
,
excelData
:
{
header
:
null
,
results
:
null
}
}
},
methods
:
{
generateData
({
header
,
results
})
{
this
.
excelData
.
header
=
header
this
.
excelData
.
results
=
results
this
.
onSuccess
&&
this
.
onSuccess
(
this
.
excelData
)
},
handleDrop
(
e
)
{
e
.
stopPropagation
()
e
.
preventDefault
()
if
(
this
.
loading
)
return
const
files
=
e
.
dataTransfer
.
files
if
(
files
.
length
!==
1
)
{
this
.
$message
.
error
(
'
Only support uploading one file!
'
)
return
}
const
rawFile
=
files
[
0
]
// only use files[0]
if
(
!
this
.
isExcel
(
rawFile
))
{
this
.
$message
.
error
(
'
Only supports upload .xlsx, .xls, .csv suffix files
'
)
return
false
}
this
.
upload
(
rawFile
)
e
.
stopPropagation
()
e
.
preventDefault
()
},
handleDragover
(
e
)
{
e
.
stopPropagation
()
e
.
preventDefault
()
e
.
dataTransfer
.
dropEffect
=
'
copy
'
},
handleUpload
()
{
this
.
$refs
[
'
excel-upload-input
'
].
click
()
},
handleClick
(
e
)
{
const
files
=
e
.
target
.
files
const
rawFile
=
files
[
0
]
// only use files[0]
if
(
!
rawFile
)
return
this
.
upload
(
rawFile
)
},
upload
(
rawFile
)
{
this
.
$refs
[
'
excel-upload-input
'
].
value
=
null
// fix can't select the same excel
if
(
!
this
.
beforeUpload
)
{
this
.
readerData
(
rawFile
)
return
}
const
before
=
this
.
beforeUpload
(
rawFile
)
if
(
before
)
{
this
.
readerData
(
rawFile
)
}
},
readerData
(
rawFile
)
{
this
.
loading
=
true
return
new
Promise
((
resolve
,
reject
)
=>
{
const
reader
=
new
FileReader
()
reader
.
onload
=
e
=>
{
const
data
=
e
.
target
.
result
const
workbook
=
XLSX
.
read
(
data
,
{
type
:
'
array
'
})
const
firstSheetName
=
workbook
.
SheetNames
[
0
]
const
worksheet
=
workbook
.
Sheets
[
firstSheetName
]
const
header
=
this
.
getHeaderRow
(
worksheet
)
const
results
=
XLSX
.
utils
.
sheet_to_json
(
worksheet
)
this
.
generateData
({
header
,
results
})
this
.
loading
=
false
resolve
()
}
reader
.
readAsArrayBuffer
(
rawFile
)
})
},
getHeaderRow
(
sheet
)
{
const
headers
=
[]
const
range
=
XLSX
.
utils
.
decode_range
(
sheet
[
'
!ref
'
])
let
C
const
R
=
range
.
s
.
r
/* start in the first row */
for
(
C
=
range
.
s
.
c
;
C
<=
range
.
e
.
c
;
++
C
)
{
/* walk every column in the range */
const
cell
=
sheet
[
XLSX
.
utils
.
encode_cell
({
c
:
C
,
r
:
R
})]
/* find the cell in the first row */
let
hdr
=
'
UNKNOWN
'
+
C
//
<--
replace
with
your
desired
default
if
(
cell
&&
cell
.
t
)
hdr
=
XLSX
.
utils
.
format_cell
(
cell
)
headers
.
push
(
hdr
)
}
return
headers
},
isExcel
(
file
)
{
return
/
\.(
xlsx|xls|csv
)
$/
.
test
(
file
.
name
)
}
}
}
</
script
>
<
style
scoped
>
.excel-upload-input
{
display
:
none
;
z-index
:
-9999
;
}
.drop
{
border
:
2px
dashed
#bbb
;
width
:
600px
;
height
:
160px
;
line-height
:
160px
;
margin
:
0
auto
;
font-size
:
24px
;
border-radius
:
5px
;
text-align
:
center
;
color
:
#bbb
;
position
:
relative
;
}
</
style
>
admin-web/src/directive/clipboard/clipboard.js
0 → 100644
View file @
95285c62
// Inspired by https://github.com/Inndy/vue-clipboard2
const
Clipboard
=
require
(
'
clipboard
'
)
if
(
!
Clipboard
)
{
throw
new
Error
(
'
you should npm install `clipboard` --save at first
'
)
}
export
default
{
bind
(
el
,
binding
)
{
if
(
binding
.
arg
===
'
success
'
)
{
el
.
_v_clipboard_success
=
binding
.
value
}
else
if
(
binding
.
arg
===
'
error
'
)
{
el
.
_v_clipboard_error
=
binding
.
value
}
else
{
const
clipboard
=
new
Clipboard
(
el
,
{
text
()
{
return
binding
.
value
},
action
()
{
return
binding
.
arg
===
'
cut
'
?
'
cut
'
:
'
copy
'
}
})
clipboard
.
on
(
'
success
'
,
e
=>
{
const
callback
=
el
.
_v_clipboard_success
callback
&&
callback
(
e
)
// eslint-disable-line
})
clipboard
.
on
(
'
error
'
,
e
=>
{
const
callback
=
el
.
_v_clipboard_error
callback
&&
callback
(
e
)
// eslint-disable-line
})
el
.
_v_clipboard
=
clipboard
}
},
update
(
el
,
binding
)
{
if
(
binding
.
arg
===
'
success
'
)
{
el
.
_v_clipboard_success
=
binding
.
value
}
else
if
(
binding
.
arg
===
'
error
'
)
{
el
.
_v_clipboard_error
=
binding
.
value
}
else
{
el
.
_v_clipboard
.
text
=
function
()
{
return
binding
.
value
}
el
.
_v_clipboard
.
action
=
function
()
{
return
binding
.
arg
===
'
cut
'
?
'
cut
'
:
'
copy
'
}
}
},
unbind
(
el
,
binding
)
{
if
(
binding
.
arg
===
'
success
'
)
{
delete
el
.
_v_clipboard_success
}
else
if
(
binding
.
arg
===
'
error
'
)
{
delete
el
.
_v_clipboard_error
}
else
{
el
.
_v_clipboard
.
destroy
()
delete
el
.
_v_clipboard
}
}
}
admin-web/src/directive/clipboard/index.js
0 → 100644
View file @
95285c62
import
Clipboard
from
'
./clipboard
'
const
install
=
function
(
Vue
)
{
Vue
.
directive
(
'
Clipboard
'
,
Clipboard
)
}
if
(
window
.
Vue
)
{
window
.
clipboard
=
Clipboard
Vue
.
use
(
install
);
// eslint-disable-line
}
Clipboard
.
install
=
install
export
default
Clipboard
admin-web/src/directive/el-drag-dialog/drag.js
0 → 100644
View file @
95285c62
export
default
{
bind
(
el
,
binding
,
vnode
)
{
const
dialogHeaderEl
=
el
.
querySelector
(
'
.el-dialog__header
'
)
const
dragDom
=
el
.
querySelector
(
'
.el-dialog
'
)
dialogHeaderEl
.
style
.
cssText
+=
'
;cursor:move;
'
dragDom
.
style
.
cssText
+=
'
;top:0px;
'
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
const
getStyle
=
(
function
()
{
if
(
window
.
document
.
currentStyle
)
{
return
(
dom
,
attr
)
=>
dom
.
currentStyle
[
attr
]
}
else
{
return
(
dom
,
attr
)
=>
getComputedStyle
(
dom
,
false
)[
attr
]
}
})()
dialogHeaderEl
.
onmousedown
=
(
e
)
=>
{
// 鼠标按下,计算当前元素距离可视区的距离
const
disX
=
e
.
clientX
-
dialogHeaderEl
.
offsetLeft
const
disY
=
e
.
clientY
-
dialogHeaderEl
.
offsetTop
const
dragDomWidth
=
dragDom
.
offsetWidth
const
dragDomHeight
=
dragDom
.
offsetHeight
const
screenWidth
=
document
.
body
.
clientWidth
const
screenHeight
=
document
.
body
.
clientHeight
const
minDragDomLeft
=
dragDom
.
offsetLeft
const
maxDragDomLeft
=
screenWidth
-
dragDom
.
offsetLeft
-
dragDomWidth
const
minDragDomTop
=
dragDom
.
offsetTop
const
maxDragDomTop
=
screenHeight
-
dragDom
.
offsetTop
-
dragDomHeight
// 获取到的值带px 正则匹配替换
let
styL
=
getStyle
(
dragDom
,
'
left
'
)
let
styT
=
getStyle
(
dragDom
,
'
top
'
)
if
(
styL
.
includes
(
'
%
'
))
{
styL
=
+
document
.
body
.
clientWidth
*
(
+
styL
.
replace
(
/
\%
/g
,
''
)
/
100
)
styT
=
+
document
.
body
.
clientHeight
*
(
+
styT
.
replace
(
/
\%
/g
,
''
)
/
100
)
}
else
{
styL
=
+
styL
.
replace
(
/
\p
x/g
,
''
)
styT
=
+
styT
.
replace
(
/
\p
x/g
,
''
)
}
document
.
onmousemove
=
function
(
e
)
{
// 通过事件委托,计算移动的距离
let
left
=
e
.
clientX
-
disX
let
top
=
e
.
clientY
-
disY
// 边界处理
if
(
-
(
left
)
>
minDragDomLeft
)
{
left
=
-
minDragDomLeft
}
else
if
(
left
>
maxDragDomLeft
)
{
left
=
maxDragDomLeft
}
if
(
-
(
top
)
>
minDragDomTop
)
{
top
=
-
minDragDomTop
}
else
if
(
top
>
maxDragDomTop
)
{
top
=
maxDragDomTop
}
// 移动当前元素
dragDom
.
style
.
cssText
+=
`;left:
${
left
+
styL
}
px;top:
${
top
+
styT
}
px;`
// emit onDrag event
vnode
.
child
.
$emit
(
'
dragDialog
'
)
}
document
.
onmouseup
=
function
(
e
)
{
document
.
onmousemove
=
null
document
.
onmouseup
=
null
}
}
}
}
admin-web/src/directive/el-drag-dialog/index.js
0 → 100644
View file @
95285c62
import
drag
from
'
./drag
'
const
install
=
function
(
Vue
)
{
Vue
.
directive
(
'
el-drag-dialog
'
,
drag
)
}
if
(
window
.
Vue
)
{
window
[
'
el-drag-dialog
'
]
=
drag
Vue
.
use
(
install
);
// eslint-disable-line
}
drag
.
install
=
install
export
default
drag
admin-web/src/directive/el-table/adaptive.js
0 → 100644
View file @
95285c62
import
{
addResizeListener
,
removeResizeListener
}
from
'
element-ui/src/utils/resize-event
'
/**
* How to use
* <el-table height="100px" v-el-height-adaptive-table="{bottomOffset: 30}">...</el-table>
* el-table height is must be set
* bottomOffset: 30(default) // The height of the table from the bottom of the page.
*/
const
doResize
=
(
el
,
binding
,
vnode
)
=>
{
const
{
componentInstance
:
$table
}
=
vnode
const
{
value
}
=
binding
if
(
!
$table
.
height
)
{
throw
new
Error
(
`el-$table must set the height. Such as height='100px'`
)
}
const
bottomOffset
=
(
value
&&
value
.
bottomOffset
)
||
30
if
(
!
$table
)
return
const
height
=
window
.
innerHeight
-
el
.
getBoundingClientRect
().
top
-
bottomOffset
$table
.
layout
.
setHeight
(
height
)
$table
.
doLayout
()
}
export
default
{
bind
(
el
,
binding
,
vnode
)
{
el
.
resizeListener
=
()
=>
{
doResize
(
el
,
binding
,
vnode
)
}
// parameter 1 is must be "Element" type
addResizeListener
(
window
.
document
.
body
,
el
.
resizeListener
)
},
inserted
(
el
,
binding
,
vnode
)
{
doResize
(
el
,
binding
,
vnode
)
},
unbind
(
el
)
{
removeResizeListener
(
window
.
document
.
body
,
el
.
resizeListener
)
}
}
admin-web/src/directive/el-table/index.js
0 → 100644
View file @
95285c62
import
adaptive
from
'
./adaptive
'
const
install
=
function
(
Vue
)
{
Vue
.
directive
(
'
el-height-adaptive-table
'
,
adaptive
)
}
if
(
window
.
Vue
)
{
window
[
'
el-height-adaptive-table
'
]
=
adaptive
Vue
.
use
(
install
);
// eslint-disable-line
}
adaptive
.
install
=
install
export
default
adaptive
admin-web/src/directive/permission/index.js
0 → 100644
View file @
95285c62
import
permission
from
'
./permission
'
const
install
=
function
(
Vue
)
{
Vue
.
directive
(
'
permission
'
,
permission
)
}
if
(
window
.
Vue
)
{
window
[
'
permission
'
]
=
permission
Vue
.
use
(
install
);
// eslint-disable-line
}
permission
.
install
=
install
export
default
permission
admin-web/src/directive/permission/permission.js
0 → 100644
View file @
95285c62
import
store
from
'
@/store
'
export
default
{
inserted
(
el
,
binding
,
vnode
)
{
const
{
value
}
=
binding
const
roles
=
store
.
getters
&&
store
.
getters
.
roles
if
(
value
&&
value
instanceof
Array
&&
value
.
length
>
0
)
{
const
permissionRoles
=
value
const
hasPermission
=
roles
.
some
(
role
=>
{
return
permissionRoles
.
includes
(
role
)
})
if
(
!
hasPermission
)
{
el
.
parentNode
&&
el
.
parentNode
.
removeChild
(
el
)
}
}
else
{
throw
new
Error
(
`need roles! Like v-permission="['admin','editor']"`
)
}
}
}
admin-web/src/directive/sticky.js
0 → 100644
View file @
95285c62
const
vueSticky
=
{}
let
listenAction
vueSticky
.
install
=
Vue
=>
{
Vue
.
directive
(
'
sticky
'
,
{
inserted
(
el
,
binding
)
{
const
params
=
binding
.
value
||
{}
const
stickyTop
=
params
.
stickyTop
||
0
const
zIndex
=
params
.
zIndex
||
1000
const
elStyle
=
el
.
style
elStyle
.
position
=
'
-webkit-sticky
'
elStyle
.
position
=
'
sticky
'
// if the browser support css sticky(Currently Safari, Firefox and Chrome Canary)
// if (~elStyle.position.indexOf('sticky')) {
// elStyle.top = `${stickyTop}px`;
// elStyle.zIndex = zIndex;
// return
// }
const
elHeight
=
el
.
getBoundingClientRect
().
height
const
elWidth
=
el
.
getBoundingClientRect
().
width
elStyle
.
cssText
=
`top:
${
stickyTop
}
px; z-index:
${
zIndex
}
`
const
parentElm
=
el
.
parentNode
||
document
.
documentElement
const
placeholder
=
document
.
createElement
(
'
div
'
)
placeholder
.
style
.
display
=
'
none
'
placeholder
.
style
.
width
=
`
${
elWidth
}
px`
placeholder
.
style
.
height
=
`
${
elHeight
}
px`
parentElm
.
insertBefore
(
placeholder
,
el
)
let
active
=
false
const
getScroll
=
(
target
,
top
)
=>
{
const
prop
=
top
?
'
pageYOffset
'
:
'
pageXOffset
'
const
method
=
top
?
'
scrollTop
'
:
'
scrollLeft
'
let
ret
=
target
[
prop
]
if
(
typeof
ret
!==
'
number
'
)
{
ret
=
window
.
document
.
documentElement
[
method
]
}
return
ret
}
const
sticky
=
()
=>
{
if
(
active
)
{
return
}
if
(
!
elStyle
.
height
)
{
elStyle
.
height
=
`
${
el
.
offsetHeight
}
px`
}
elStyle
.
position
=
'
fixed
'
elStyle
.
width
=
`
${
elWidth
}
px`
placeholder
.
style
.
display
=
'
inline-block
'
active
=
true
}
const
reset
=
()
=>
{
if
(
!
active
)
{
return
}
elStyle
.
position
=
''
placeholder
.
style
.
display
=
'
none
'
active
=
false
}
const
check
=
()
=>
{
const
scrollTop
=
getScroll
(
window
,
true
)
const
offsetTop
=
el
.
getBoundingClientRect
().
top
if
(
offsetTop
<
stickyTop
)
{
sticky
()
}
else
{
if
(
scrollTop
<
elHeight
+
stickyTop
)
{
reset
()
}
}
}
listenAction
=
()
=>
{
check
()
}
window
.
addEventListener
(
'
scroll
'
,
listenAction
)
},
unbind
()
{
window
.
removeEventListener
(
'
scroll
'
,
listenAction
)
}
})
}
export
default
vueSticky
admin-web/src/directive/waves/index.js
0 → 100644
View file @
95285c62
import
waves
from
'
./waves
'
const
install
=
function
(
Vue
)
{
Vue
.
directive
(
'
waves
'
,
waves
)
}
if
(
window
.
Vue
)
{
window
.
waves
=
waves
Vue
.
use
(
install
);
// eslint-disable-line
}
waves
.
install
=
install
export
default
waves
Prev
1
2
3
4
5
6
7
8
9
…
16
Next
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