commit
5eea0e51a1
@ -52,7 +52,6 @@
|
||||
"spark-md5": "^3.0.0",
|
||||
"stompjs": "^2.3.3",
|
||||
"tippy.js": "^6.3.7",
|
||||
"v-contextmenu": "^2.9.0",
|
||||
"v-viewer": "^1.5.1",
|
||||
"vditor": "3.11.0",
|
||||
"vue-aplayer": "^1.6.1",
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<!-- 一级菜单 -->
|
||||
<v-contextmenu-item
|
||||
v-if="!item.child"
|
||||
:divider="item.divider ? true : false"
|
||||
:divider="!!item.divider"
|
||||
@click="contextmenuClick(item.operation)"
|
||||
>
|
||||
<svg-icon
|
||||
@ -28,7 +28,7 @@
|
||||
<div v-for="itemSecond of item.child" :key="itemSecond.operation">
|
||||
<v-contextmenu-item
|
||||
v-if="!itemSecond.child"
|
||||
:divider="itemSecond.divider ? true : false"
|
||||
:divider="!!itemSecond.divider"
|
||||
@click="contextmenuClick(itemSecond.operation)"
|
||||
>
|
||||
<svg-icon
|
||||
@ -367,45 +367,7 @@
|
||||
</div>
|
||||
|
||||
<!--右键菜单-->
|
||||
<e-vue-contextmenu
|
||||
ref="contextShow"
|
||||
class="newFileMenu"
|
||||
:class="menuTriangle"
|
||||
@ctx-show="show"
|
||||
@ctx-hide="hide"
|
||||
>
|
||||
<div class="popper-arrow"></div>
|
||||
<ul v-for="(item, index) in menus" :key="item.label">
|
||||
<li v-if="item.operation === 'unFavorite' || item.operation === 'favorite'"
|
||||
class="menu-option"
|
||||
@click="menusOperations(item.operation, $event)"
|
||||
@mouseover.prevent.stop="
|
||||
menuFavoriteOver(index, rowContextData.isFavorite)
|
||||
"
|
||||
@mouseleave.prevent.stop="
|
||||
menuFavoriteLeave(index, rowContextData.isFavorite)
|
||||
"
|
||||
>
|
||||
<div class="menuitem home-contextmenu">
|
||||
<svg-icon :icon-class="item.iconClass" />
|
||||
<div class="home-contextmenu-title">
|
||||
<span class="menuitem text">{{ item.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li v-else @click="menusOperations(item.operation, $event)">
|
||||
<div class="menuitem home-contextmenu">
|
||||
<svg-icon :icon-class="item.iconClass" />
|
||||
<div class="home-contextmenu-title">
|
||||
<span class="menuitem text">{{ item.label }}</span>
|
||||
<span v-if="item.shortcut">
|
||||
<kbd v-for="key in item.shortcut" :style="{fontSize: key === '⌘' ? '14px' : '12px'}">{{ key }}</kbd>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</e-vue-contextmenu>
|
||||
<file-contextmenu ref="contextShow" :menus="menus" @menusOperations="menusOperations" @show="show" @hide="hide" @contextmenu="contextmenu" />
|
||||
|
||||
<!--list布局-->
|
||||
<div
|
||||
@ -914,6 +876,7 @@ import ShareDialog from '@/components/ShareDialog/index.vue'
|
||||
import DialogFileList from '@/components/ShowFile/DialogFileList.vue'
|
||||
import FileClipboard from '@/components/ShowFile/FileClipboard.vue'
|
||||
import TagDialog from '@/components/TagDialog/index.vue'
|
||||
import FileContextmenu from '@/components/VContextmenu/file-contextmenu.vue'
|
||||
import store from '@/store'
|
||||
import { getElementToPageLeft } from '@/utils/dom'
|
||||
|
||||
@ -932,6 +895,7 @@ import { mapGetters, mapState } from 'vuex'
|
||||
export default {
|
||||
name: 'ShowFile',
|
||||
components: {
|
||||
FileContextmenu,
|
||||
FileClipboard,
|
||||
DialogFileList,
|
||||
SearchOption,
|
||||
@ -1018,19 +982,14 @@ export default {
|
||||
default: function() {
|
||||
return [
|
||||
fileOperations.open,
|
||||
{ iconClass: 'share', label: '分享', operation: 'share' },
|
||||
{ iconClass: 'tag', label: '标签', operation: 'tag' },
|
||||
fileOperations.download,
|
||||
{ iconClass: 'share', label: '分享...', operation: 'share' },
|
||||
{ iconClass: 'tag', label: '标签...' , operation: 'tag'},
|
||||
{ iconClass: 'menu-favorite', label: '收藏', operation: 'favorite' },
|
||||
{
|
||||
iconClass: 'menu-details',
|
||||
label: '详情',
|
||||
shortcut: ["space"],
|
||||
operation: 'details',
|
||||
},
|
||||
fileOperations.detail,
|
||||
fileOperations.copyOnly,
|
||||
fileOperations.rename,
|
||||
fileOperations.copy,
|
||||
fileOperations.copyOnly,
|
||||
fileOperations.download,
|
||||
fileOperations.remove,
|
||||
]
|
||||
},
|
||||
@ -1044,10 +1003,10 @@ export default {
|
||||
label: '取消选定',
|
||||
operation: 'deselect',
|
||||
},
|
||||
{ iconClass: 'tag', label: '标签', operation: 'tag' },
|
||||
fileOperations.copy,
|
||||
fileOperations.copyOnly,
|
||||
fileOperations.download,
|
||||
{ iconClass: 'tag', label: '标签', operation: 'tag' },
|
||||
fileOperations.copyOnly,
|
||||
fileOperations.copy,
|
||||
fileOperations.remove,
|
||||
]
|
||||
},
|
||||
@ -1134,7 +1093,6 @@ export default {
|
||||
finished: false,
|
||||
newFolderLoading: false,
|
||||
renameLoading: false,
|
||||
menuTriangle: '', // 三角菜单
|
||||
cellMouseIndex: -1,
|
||||
editingIndex: -1,
|
||||
titlePrefix: '',
|
||||
@ -3563,7 +3521,7 @@ export default {
|
||||
this.setMenusByPermission(row)
|
||||
} else {
|
||||
if (row.suffix && row.suffix === 'md' && this.queryFileType !== 'trash') {
|
||||
this.menus.splice(2, 0, {
|
||||
this.menus.splice(this.getIndexOfFileContextMenus(fileOperations.copyOnly.operation), 0, {
|
||||
iconClass: 'menu-edit1',
|
||||
label: '编辑',
|
||||
operation: 'edit',
|
||||
@ -3577,11 +3535,15 @@ export default {
|
||||
)
|
||||
}
|
||||
if (!row.isFolder && this.queryFileType !== 'trash') {
|
||||
this.menus.splice(-2, 0, fileOperations.duplicate)
|
||||
// 创建副本
|
||||
const copyIndex = this.getIndexOfFileContextMenus(fileOperations.copy.operation)
|
||||
if (copyIndex > -1) {
|
||||
this.menus.splice(copyIndex, 0, fileOperations.duplicate)
|
||||
}
|
||||
}
|
||||
if (row.ossFolder) {
|
||||
// 删除分享选项
|
||||
let index = this.menus.findIndex(item => item.operation === 'share')
|
||||
let index = this.getIndexOfFileContextMenus('share')
|
||||
if (index > -1) {
|
||||
this.menus.splice(index, 1)
|
||||
}
|
||||
@ -3590,6 +3552,26 @@ export default {
|
||||
}
|
||||
this.preliminaryRowData(row)
|
||||
},
|
||||
setFileContextMenusDivider() {
|
||||
// 下载栏下面添加分隔符
|
||||
const downloadIndex = this.getIndexOfFileContextMenus(fileOperations.download.operation)
|
||||
if (downloadIndex > -1 && this.menus.length - 1 > downloadIndex && !this.menus[downloadIndex + 1].divider) {
|
||||
this.menus.splice(downloadIndex + 1, 0, { divider: true, operation: 'divider' })
|
||||
}
|
||||
// 删除栏上面添加分隔符
|
||||
const removeIndex = this.getIndexOfFileContextMenus(fileOperations.remove.operation)
|
||||
if (removeIndex > 0 && !this.menus[removeIndex - 1].divider) {
|
||||
this.menus.splice(removeIndex, 0, { divider: true, operation: 'divider' })
|
||||
}
|
||||
// 详情栏下面添加分隔符
|
||||
const detailIndex = this.getIndexOfFileContextMenus(fileOperations.detail.operation)
|
||||
if (detailIndex > -1 && this.menus.length - 1 > detailIndex && !this.menus[detailIndex + 1].divider) {
|
||||
this.menus.splice(detailIndex + 1, 0, { divider: true, operation: 'divider' })
|
||||
}
|
||||
},
|
||||
getIndexOfFileContextMenus(operation) {
|
||||
return this.menus.findIndex(item => item.operation === operation)
|
||||
},
|
||||
setMenusCopyDownLoadLinks(row) {
|
||||
if (row.isShare) {
|
||||
// 获取this.menus中download的索引
|
||||
@ -3617,22 +3599,22 @@ export default {
|
||||
})
|
||||
},
|
||||
setMenusByPermission(file) {
|
||||
const reservations = ['open', 'download']
|
||||
const reservations = ['open', 'download', 'details']
|
||||
// 删除this.menus中不要的菜单, 仅保留reservations中的菜单
|
||||
this.menus = this.menus.filter(item =>
|
||||
reservations.includes(item.operation)
|
||||
)
|
||||
if (file.operationPermissionList && file.operationPermissionList.length > 0) {
|
||||
if (file.operationPermissionList.indexOf('PUT') > -1) {
|
||||
this.menus.splice(this.menus.length - 1, 0, fileOperations.rename)
|
||||
this.menus.splice(this.menus.length, 0, fileOperations.rename)
|
||||
}
|
||||
if (file.operationPermissionList.indexOf('UPLOAD') > -1) {
|
||||
if (file.operationPermissionList.indexOf('DELETE') > -1) {
|
||||
this.menus.splice(this.menus.length - 1, 0, fileOperations.copy)
|
||||
this.menus.splice(this.menus.length - 1, 0, fileOperations.copyOnly)
|
||||
this.menus.splice(this.menus.length, 0, fileOperations.copy)
|
||||
this.menus.splice(this.menus.length, 0, fileOperations.copyOnly)
|
||||
}
|
||||
if (!file.isFolder) {
|
||||
this.menus.splice(this.menus.length - 1, 0, fileOperations.duplicate)
|
||||
this.menus.splice(this.menus.length, 0, fileOperations.duplicate)
|
||||
}
|
||||
}
|
||||
if (file.operationPermissionList.indexOf('DELETE') > -1) {
|
||||
@ -3643,6 +3625,7 @@ export default {
|
||||
},
|
||||
// 鼠标右击
|
||||
rowContextmenu(row) {
|
||||
this.$refs.homeContextmenu.hide()
|
||||
if (this.selectFile) {
|
||||
return
|
||||
}
|
||||
@ -3653,7 +3636,7 @@ export default {
|
||||
) > -1
|
||||
) {
|
||||
this.menusIsMultiple = true
|
||||
this.menus = this.multipleRightMenus
|
||||
this.menus = JSON.parse(JSON.stringify(this.multipleRightMenus))
|
||||
this.highlightFavorite(false, false)
|
||||
} else {
|
||||
this.$refs.fileListTable.clearSelection()
|
||||
@ -3661,13 +3644,35 @@ export default {
|
||||
this.menusIsMultiple = false
|
||||
this.setMenus(row)
|
||||
}
|
||||
|
||||
// 设置分割线
|
||||
this.setFileContextMenusDivider()
|
||||
|
||||
event.preventDefault()
|
||||
this.menuTriangle = ''
|
||||
event.stopPropagation()
|
||||
const e = {}
|
||||
e.pageX = event.pageX + 5
|
||||
e.pageY = event.pageY + 20
|
||||
e.clientX = event.clientX + 50
|
||||
e.clientY = event.clientY + 2
|
||||
e.top = event.clientY
|
||||
e.left = event.clientX
|
||||
|
||||
const dividerSize = this.menus.filter(item => item.divider).length
|
||||
|
||||
const containerWidth = 180 + 5
|
||||
const containerHeight = (this.menus.length - dividerSize) * 35 + (dividerSize * 10) + 21
|
||||
const distanceToBottom = document.documentElement.clientHeight - event.clientY
|
||||
const distanceToRight = document.documentElement.clientWidth - event.clientX
|
||||
|
||||
if (distanceToBottom < containerHeight) {
|
||||
e.top = event.clientY - containerHeight
|
||||
}
|
||||
if (distanceToRight < containerWidth) {
|
||||
e.left = event.clientX - containerWidth
|
||||
}
|
||||
|
||||
|
||||
this.$refs.contextShow.showMenu(e)
|
||||
this.cellMouseIndex = -1
|
||||
},
|
||||
@ -3701,6 +3706,11 @@ export default {
|
||||
// this.$set(this.menus, 0, item_menu)
|
||||
}
|
||||
},
|
||||
contextmenu(e) {
|
||||
console.log('contextmenu', e)
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
},
|
||||
show() {
|
||||
const that = this
|
||||
this.contextmenuDisabled = true
|
||||
@ -4257,7 +4267,7 @@ export default {
|
||||
if (row.isFolder) {
|
||||
url = fileConfig.packageDownloadUrl(row.id, row.name + '.zip', this.shareToken)
|
||||
}
|
||||
let clipboard = new Clipboard('.newFileMenu', {
|
||||
let clipboard = new Clipboard('.file-contextmenu', {
|
||||
text: function() {
|
||||
return url
|
||||
},
|
||||
|
||||
180
src/components/VContextmenu/components/Contextmenu.vue
Normal file
180
src/components/VContextmenu/components/Contextmenu.vue
Normal file
@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<ul
|
||||
v-show="visible"
|
||||
ref="contextmenu"
|
||||
:class="contextmenuCls"
|
||||
:style="style"
|
||||
>
|
||||
<slot />
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'VContextmenu',
|
||||
|
||||
provide () {
|
||||
return {
|
||||
$$contextmenu: this,
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
eventType: {
|
||||
type: String,
|
||||
default: 'contextmenu',
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
default: 'default',
|
||||
},
|
||||
autoPlacement: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
disabled: Boolean,
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
visible: false,
|
||||
references: [],
|
||||
style: {
|
||||
top: 0,
|
||||
left: 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
clickOutsideHandler () {
|
||||
return this.visible ? this.hide : () => {}
|
||||
},
|
||||
isClick () {
|
||||
return this.eventType === 'click'
|
||||
},
|
||||
contextmenuCls () {
|
||||
return [
|
||||
'v-contextmenu',
|
||||
`v-contextmenu--${this.theme}`,
|
||||
]
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
visible (value) {
|
||||
if (value) {
|
||||
this.$emit('show', this)
|
||||
|
||||
document.body.addEventListener('click', this.handleBodyClick)
|
||||
} else {
|
||||
this.$emit('hide', this)
|
||||
|
||||
document.body.removeEventListener('click', this.handleBodyClick)
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
document.body.appendChild(this.$el)
|
||||
|
||||
if (window.$$VContextmenu) {
|
||||
window.$$VContextmenu[this.$contextmenuId] = this
|
||||
} else {
|
||||
window.$$VContextmenu = { [this.$contextmenuId]: this }
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
document.body.removeChild(this.$el)
|
||||
|
||||
delete window.$$VContextmenu[this.$contextmenuId]
|
||||
|
||||
this.references.forEach((ref) => {
|
||||
ref.el.removeEventListener(this.eventType, this.handleReferenceContextmenu)
|
||||
})
|
||||
|
||||
document.body.removeEventListener('click', this.handleBodyClick)
|
||||
},
|
||||
|
||||
methods: {
|
||||
addRef (ref) {
|
||||
// FIXME: 如何处理 removeRef?
|
||||
this.references.push(ref)
|
||||
|
||||
ref.el.addEventListener(this.eventType, this.handleReferenceContextmenu)
|
||||
},
|
||||
handleReferenceContextmenu (event) {
|
||||
event.preventDefault()
|
||||
|
||||
if (this.disabled) return
|
||||
|
||||
const reference = this.references.find(ref => ref.el.contains(event.target))
|
||||
|
||||
this.$emit('contextmenu', reference ? reference.vnode : null)
|
||||
|
||||
const eventX = event.pageX
|
||||
const eventY = event.pageY
|
||||
|
||||
this.show()
|
||||
|
||||
this.$nextTick(() => {
|
||||
const contextmenuPosition = {
|
||||
top: eventY,
|
||||
left: eventX,
|
||||
}
|
||||
|
||||
if (this.autoPlacement) {
|
||||
const contextmenuWidth = this.$refs.contextmenu.clientWidth
|
||||
const contextmenuHeight = this.$refs.contextmenu.clientHeight
|
||||
|
||||
if (contextmenuHeight + eventY >= window.innerHeight) {
|
||||
contextmenuPosition.top -= contextmenuHeight
|
||||
}
|
||||
|
||||
if (contextmenuWidth + eventX >= window.innerWidth) {
|
||||
contextmenuPosition.left -= contextmenuWidth
|
||||
}
|
||||
}
|
||||
|
||||
this.style = {
|
||||
top: `${contextmenuPosition.top}px`,
|
||||
left: `${contextmenuPosition.left}px`,
|
||||
}
|
||||
})
|
||||
},
|
||||
handleBodyClick (event) {
|
||||
const notOutside = this.$el.contains(event.target) || (
|
||||
this.isClick && this.references.some(ref => ref.el.contains(event.target))
|
||||
)
|
||||
|
||||
if (!notOutside) {
|
||||
this.visible = false
|
||||
}
|
||||
},
|
||||
show (position) {
|
||||
Object.keys(window.$$VContextmenu)
|
||||
.forEach((key) => {
|
||||
if (key !== this.$contextmenuId) {
|
||||
window.$$VContextmenu[key].hide()
|
||||
}
|
||||
})
|
||||
|
||||
if (position) {
|
||||
this.style = {
|
||||
top: `${position.top}px`,
|
||||
left: `${position.left}px`,
|
||||
}
|
||||
}
|
||||
|
||||
this.visible = true
|
||||
},
|
||||
hide () {
|
||||
this.visible = false
|
||||
},
|
||||
hideAll () {
|
||||
Object.keys(window.$$VContextmenu)
|
||||
.forEach((key) => {
|
||||
window.$$VContextmenu[key].hide()
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
33
src/components/VContextmenu/components/ContextmenuGroup.vue
Normal file
33
src/components/VContextmenu/components/ContextmenuGroup.vue
Normal file
@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<li class="v-contextmenu-group">
|
||||
<ul
|
||||
:style="menusStyle"
|
||||
class="v-contextmenu-group__menus"
|
||||
>
|
||||
<slot />
|
||||
</ul>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'VContextmenuGroup',
|
||||
|
||||
props: {
|
||||
maxWidth: [Number, String],
|
||||
},
|
||||
|
||||
computed: {
|
||||
menusStyle () {
|
||||
if (!this.maxWidth) return null
|
||||
|
||||
const style = {
|
||||
'max-width': typeof this.maxWidth === 'number' ? `${this.maxWidth}px` : this.maxWidth,
|
||||
'overflow-x': 'auto',
|
||||
}
|
||||
|
||||
return style
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
73
src/components/VContextmenu/components/ContextmenuItem.vue
Normal file
73
src/components/VContextmenu/components/ContextmenuItem.vue
Normal file
@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<li
|
||||
v-if="divider"
|
||||
class="v-contextmenu-divider"
|
||||
/>
|
||||
|
||||
<li
|
||||
v-else
|
||||
:class="classname"
|
||||
@click="handleClick"
|
||||
@mouseenter="handleMouseenter"
|
||||
@mouseleave="handleMouseleave"
|
||||
>
|
||||
<slot />
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'VContextmenuItem',
|
||||
|
||||
inject: ['$$contextmenu'],
|
||||
|
||||
props: {
|
||||
divider: Boolean,
|
||||
disabled: Boolean,
|
||||
autoHide: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
hover: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
classname () {
|
||||
return {
|
||||
'v-contextmenu-item': !this.divider,
|
||||
'v-contextmenu-item--hover': this.hover,
|
||||
'v-contextmenu-item--disabled': this.disabled,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleMouseenter (event) {
|
||||
if (this.disabled) return
|
||||
|
||||
this.hover = true
|
||||
|
||||
this.$emit('mouseenter', this, event)
|
||||
},
|
||||
handleMouseleave (event) {
|
||||
if (this.disabled) return
|
||||
|
||||
this.hover = false
|
||||
|
||||
this.$emit('mouseleave', this, event)
|
||||
},
|
||||
|
||||
handleClick (event) {
|
||||
if (this.disabled) return
|
||||
|
||||
this.$emit('click', this, event)
|
||||
|
||||
this.autoHide && this.$$contextmenu.hide()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
110
src/components/VContextmenu/components/ContextmenuSubmenu.vue
Normal file
110
src/components/VContextmenu/components/ContextmenuSubmenu.vue
Normal file
@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<li
|
||||
:class="classname"
|
||||
@mouseenter="handleMouseenter"
|
||||
@mouseleave="handleMouseleave"
|
||||
>
|
||||
<svg-icon v-if="iconClass" :icon-class="iconClass"></svg-icon>
|
||||
<!-- <div class="v-contextmenu-submenu__title home-contextmenu">-->
|
||||
<!-- <svg-icon v-if="iconClass" :icon-class="iconClass"></svg-icon>-->
|
||||
<!-- <div class="home-contextmenu-title">-->
|
||||
<!-- <span class="menuitem text">{{ title }}</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- <span class="v-contextmenu-iconfont v-contextmenu-submenu__icon" />-->
|
||||
<!-- </div>-->
|
||||
|
||||
<div class="v-contextmenu-submenu__title home-contextmenu-title">
|
||||
<span class="menuitem text">{{ title }}</span>
|
||||
<span class="v-contextmenu-iconfont v-contextmenu-submenu__icon" />
|
||||
</div>
|
||||
|
||||
<ul
|
||||
v-show="hover"
|
||||
ref="submenu"
|
||||
:class="submenuCls"
|
||||
>
|
||||
<slot />
|
||||
</ul>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'VContextmenuSubmenu',
|
||||
|
||||
props: {
|
||||
title: String,
|
||||
disabled: Boolean,
|
||||
iconClass: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
hover: false,
|
||||
submenuPlacement: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
classname () {
|
||||
return {
|
||||
'v-contextmenu-item': true,
|
||||
'v-contextmenu-submenu': true,
|
||||
'v-contextmenu-item--hover': this.hover,
|
||||
'v-contextmenu-item--disabled': this.disabled,
|
||||
}
|
||||
},
|
||||
submenuCls () {
|
||||
return [
|
||||
'v-contextmenu',
|
||||
'v-contextmenu-sub',
|
||||
...this.submenuPlacement,
|
||||
]
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleMouseenter (event) {
|
||||
if (this.disabled) return
|
||||
|
||||
const { target } = event
|
||||
const targetDimension = target.getBoundingClientRect()
|
||||
|
||||
this.hover = true
|
||||
|
||||
this.$emit('mouseenter', this, event)
|
||||
|
||||
this.$nextTick(() => {
|
||||
const submenuWidth = this.$refs.submenu.clientWidth
|
||||
const submenuHeight = this.$refs.submenu.clientHeight
|
||||
const submenuPlacement = []
|
||||
|
||||
if (targetDimension.right + submenuWidth >= window.innerWidth) {
|
||||
submenuPlacement.push('left')
|
||||
} else {
|
||||
submenuPlacement.push('right')
|
||||
}
|
||||
|
||||
if (targetDimension.bottom + submenuHeight >= window.innerHeight) {
|
||||
submenuPlacement.push('bottom')
|
||||
} else {
|
||||
submenuPlacement.push('top')
|
||||
}
|
||||
|
||||
this.submenuPlacement = submenuPlacement
|
||||
})
|
||||
},
|
||||
handleMouseleave (event) {
|
||||
if (this.disabled) return
|
||||
|
||||
this.hover = false
|
||||
|
||||
this.$emit('mouseleave', this, event)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
19
src/components/VContextmenu/directive.js
Normal file
19
src/components/VContextmenu/directive.js
Normal file
@ -0,0 +1,19 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
export default {
|
||||
// 在组件 mount 前,设置 $contextmenuId,修复内存泄漏的问题
|
||||
bind (el, binding, vnode) {
|
||||
const node = vnode.context.$refs[binding.arg] || vnode.context.$refs[binding.value]
|
||||
const contextmenu = Object.prototype.toString.call(node) === '[object Array]' ? node[0] : node
|
||||
if (contextmenu) {
|
||||
contextmenu.$contextmenuId = el.id || contextmenu._uid // eslint-disable-line no-underscore-dangle
|
||||
}
|
||||
},
|
||||
// 之所以用 inserted 而不是 bind,是需要确保 contentmenu mounted 后才进行 addRef 操作
|
||||
inserted (el, binding, vnode) {
|
||||
const node = vnode.context.$refs[binding.arg] || vnode.context.$refs[binding.value]
|
||||
const contextmenu = Object.prototype.toString.call(node) === '[object Array]' ? node[0] : node
|
||||
if (contextmenu) {
|
||||
contextmenu.addRef({ el, vnode })
|
||||
}
|
||||
},
|
||||
}
|
||||
142
src/components/VContextmenu/file-contextmenu.vue
Normal file
142
src/components/VContextmenu/file-contextmenu.vue
Normal file
@ -0,0 +1,142 @@
|
||||
<template>
|
||||
|
||||
<v-contextmenu ref="contextShow" class="file-contextmenu" :class="className" @show="show" @hide="hide" @contextmenu="contextmenu">
|
||||
<div v-for="item of menus" :key="item.label">
|
||||
<!-- 一级菜单 -->
|
||||
<v-contextmenu-item v-if="!item.child" :divider="!!item.divider" @click="menusOperations(item.operation, $event)"
|
||||
class="menuitem home-contextmenu">
|
||||
<svg-icon v-if="item.iconClass" :icon-class="item.iconClass"></svg-icon>
|
||||
<div class="home-contextmenu-title">
|
||||
<span class="menuitem text">{{ item.label }}</span>
|
||||
<span v-if="item.shortcut">
|
||||
<kbd v-for="key in item.shortcut" :style="{fontSize: key === '⌘' ? '14px' : '12px'}">{{ key }}</kbd>
|
||||
</span>
|
||||
</div>
|
||||
</v-contextmenu-item>
|
||||
<v-contextmenu-submenu class="home-contextmenu" v-else :icon-class="item.iconClass" :title="item.label">
|
||||
<!-- 二级菜单 -->
|
||||
<div v-for="itemSecond of item.child" :key="itemSecond.operation">
|
||||
<v-contextmenu-item
|
||||
v-if="!itemSecond.child"
|
||||
:divider="!!itemSecond.divider"
|
||||
@click="menusOperations(itemSecond.operation, $event)"
|
||||
>
|
||||
<svg-icon
|
||||
v-if="itemSecond.iconClass"
|
||||
:icon-class="itemSecond.iconClass"
|
||||
></svg-icon>
|
||||
{{ itemSecond.label }}
|
||||
</v-contextmenu-item>
|
||||
<v-contextmenu-submenu
|
||||
v-if="itemSecond.child"
|
||||
:icon-class="itemSecond.iconClass"
|
||||
:title="itemSecond.label"
|
||||
>
|
||||
</v-contextmenu-submenu>
|
||||
</div>
|
||||
</v-contextmenu-submenu>
|
||||
</div>
|
||||
</v-contextmenu>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'file-contextmenu',
|
||||
props: {
|
||||
menus: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
menusOperations() {
|
||||
this.$emit('menusOperations', ...arguments)
|
||||
},
|
||||
showMenu(e) {
|
||||
this.$refs.contextShow.show(e)
|
||||
},
|
||||
hideMenu(e) {
|
||||
this.$refs.contextShow.hide(e)
|
||||
},
|
||||
show() {
|
||||
this.$emit('show')
|
||||
},
|
||||
hide() {
|
||||
this.$emit('hide')
|
||||
},
|
||||
contextmenu() {
|
||||
this.$emit('contextmenu')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "~@/styles/variables.scss";
|
||||
>>> .v-contextmenu.v-contextmenu-sub {
|
||||
border-radius: $dialogBorderRadius;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.file-contextmenu.v-contextmenu {
|
||||
min-width: 180px;
|
||||
border-radius: $dialogBorderRadius;
|
||||
padding: 8px;
|
||||
background-color: rgba(255, 255, 255, .75);
|
||||
-webkit-backdrop-filter: blur(16px) saturate(180%);
|
||||
backdrop-filter: blur(16px) saturate(180%);
|
||||
|
||||
.v-contextmenu-divider {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
li {
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
font-size: 16px;
|
||||
min-width: 136px;
|
||||
line-height: 35px;
|
||||
&:hover {
|
||||
border-radius: 10px;
|
||||
background-color: #409eff30;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
li > .text {
|
||||
font-weight: normal;
|
||||
}
|
||||
li > .svg-icon {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.v-contextmenu-item {
|
||||
padding: 0 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
>>> .svg-icon {
|
||||
margin-right: 10px;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
>>> .home-contextmenu-title {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
32
src/components/VContextmenu/index.js
Normal file
32
src/components/VContextmenu/index.js
Normal file
@ -0,0 +1,32 @@
|
||||
import directive from './directive'
|
||||
|
||||
import Contextmenu from './components/Contextmenu.vue'
|
||||
import ContextmenuItem from './components/ContextmenuItem.vue'
|
||||
import ContextmenuSubmenu from './components/ContextmenuSubmenu.vue'
|
||||
import ContextmenuGroup from './components/ContextmenuGroup.vue'
|
||||
|
||||
const install = (Vue) => {
|
||||
Vue.directive('contextmenu', directive)
|
||||
|
||||
Vue.component(Contextmenu.name, Contextmenu)
|
||||
Vue.component(ContextmenuItem.name, ContextmenuItem)
|
||||
Vue.component(ContextmenuSubmenu.name, ContextmenuSubmenu)
|
||||
Vue.component(ContextmenuGroup.name, ContextmenuGroup)
|
||||
}
|
||||
|
||||
export {
|
||||
directive,
|
||||
|
||||
Contextmenu,
|
||||
ContextmenuItem,
|
||||
ContextmenuSubmenu,
|
||||
ContextmenuGroup,
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined' && window.Vue) {
|
||||
install(window.Vue)
|
||||
}
|
||||
|
||||
export default {
|
||||
install,
|
||||
}
|
||||
82
src/components/VContextmenu/styles/contextmenu.styl
Normal file
82
src/components/VContextmenu/styles/contextmenu.styl
Normal file
@ -0,0 +1,82 @@
|
||||
.v-contextmenu
|
||||
position: absolute
|
||||
padding: padding-vertical 0
|
||||
margin: 0
|
||||
background-color: #fff
|
||||
border: 1px solid border-color
|
||||
border-radius: 4px
|
||||
box-shadow: 2px 2px 8px 0px shadow-color
|
||||
list-style: none
|
||||
font-size: 14px
|
||||
white-space: nowrap
|
||||
cursor: pointer
|
||||
z-index: 2800
|
||||
-webkit-tap-highlight-color: transparent
|
||||
|
||||
.v-contextmenu-item
|
||||
padding: padding-vertical contextmenu-item-padding-horizonal
|
||||
line-height: 1
|
||||
color: #333
|
||||
|
||||
&.v-contextmenu-item--hover
|
||||
color: #fff
|
||||
|
||||
&.v-contextmenu-item--disabled
|
||||
color: #ccc
|
||||
cursor: not-allowed
|
||||
|
||||
.v-contextmenu-divider
|
||||
height: 0
|
||||
margin: padding-vertical 0
|
||||
border-bottom: 1px solid border-color
|
||||
|
||||
.v-contextmenu-group__menus
|
||||
padding: 0 5px
|
||||
margin: 0
|
||||
list-style: none
|
||||
|
||||
.v-contextmenu-item
|
||||
display: inline-block
|
||||
padding: padding-vertical (contextmenu-item-padding-horizonal - 5)
|
||||
|
||||
.v-contextmenu-submenu
|
||||
position: relative
|
||||
|
||||
& > .v-contextmenu
|
||||
position: absolute
|
||||
|
||||
&.left
|
||||
left: 0
|
||||
transform: translateX(-100%)
|
||||
|
||||
&.right
|
||||
right: 0
|
||||
transform: translateX(100%)
|
||||
|
||||
&.top
|
||||
top: -(padding-vertical) - 1
|
||||
|
||||
&.bottom
|
||||
bottom: -(padding-vertical) - 1
|
||||
|
||||
.v-contextmenu-submenu__title
|
||||
margin-right: padding-vertical * 2
|
||||
|
||||
.v-contextmenu-submenu__icon
|
||||
position: absolute
|
||||
right: padding-vertical
|
||||
|
||||
&::before
|
||||
content: "\e622"
|
||||
|
||||
.v-contextmenu--default
|
||||
.v-contextmenu-item--hover
|
||||
background-color: active-color
|
||||
|
||||
.v-contextmenu--bright
|
||||
.v-contextmenu-item--hover
|
||||
background-color: active-color-bright
|
||||
|
||||
.v-contextmenu--dark
|
||||
.v-contextmenu-item--hover
|
||||
background-color: active-color-dark
|
||||
BIN
src/components/VContextmenu/styles/fonts/iconfont.eot
Normal file
BIN
src/components/VContextmenu/styles/fonts/iconfont.eot
Normal file
Binary file not shown.
36
src/components/VContextmenu/styles/fonts/iconfont.svg
Normal file
36
src/components/VContextmenu/styles/fonts/iconfont.svg
Normal file
@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<!--
|
||||
2013-9-30: Created.
|
||||
-->
|
||||
<svg>
|
||||
<metadata>
|
||||
Created by iconfont
|
||||
</metadata>
|
||||
<defs>
|
||||
|
||||
<font id="iconfont" horiz-adv-x="1024" >
|
||||
<font-face
|
||||
font-family="iconfont"
|
||||
font-weight="500"
|
||||
font-stretch="normal"
|
||||
units-per-em="1024"
|
||||
ascent="896"
|
||||
descent="-128"
|
||||
/>
|
||||
<missing-glyph />
|
||||
|
||||
<glyph glyph-name="x" unicode="x" horiz-adv-x="1001"
|
||||
d="M281 543q-27 -1 -53 -1h-83q-18 0 -36.5 -6t-32.5 -18.5t-23 -32t-9 -45.5v-76h912v41q0 16 -0.5 30t-0.5 18q0 13 -5 29t-17 29.5t-31.5 22.5t-49.5 9h-133v-97h-438v97zM955 310v-52q0 -23 0.5 -52t0.5 -58t-10.5 -47.5t-26 -30t-33 -16t-31.5 -4.5q-14 -1 -29.5 -0.5
|
||||
t-29.5 0.5h-32l-45 128h-439l-44 -128h-29h-34q-20 0 -45 1q-25 0 -41 9.5t-25.5 23t-13.5 29.5t-4 30v167h911zM163 247q-12 0 -21 -8.5t-9 -21.5t9 -21.5t21 -8.5q13 0 22 8.5t9 21.5t-9 21.5t-22 8.5zM316 123q-8 -26 -14 -48q-5 -19 -10.5 -37t-7.5 -25t-3 -15t1 -14.5
|
||||
t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q-5 19 -11 39h-368zM336 498v228q0 11 2.5 23t10 21.5t20.5 15.5t34 6h188q31 0 51.5 -14.5t20.5 -52.5v-227h-327z" />
|
||||
|
||||
|
||||
|
||||
<glyph glyph-name="arrow-right" unicode="" d="M725.3399071604938 383.8342991012345a41.425224691358025 41.425224691358025 0 0 0-12.924670103703681-30.074713125925918l-343.82936493827134-325.2708642765434a41.425224691358025 41.425224691358025 0 0 0-56.91825872592596 60.23227670123452l312.0976428246912 295.1961511506175-312.0976428246914 295.4447024987654a41.50807514074074 41.50807514074074 0 0 0-1.6570089876543506 58.575267713580246c15.74158538271605 16.570089876543207 42.00517783703699 17.315743920987664 58.575267713580246 1.6570089876543506l343.8293649382716-325.6022660740737a41.425224691358025 41.425224691358025 0 0 0 12.924670103703713-30.15756357530862z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
|
||||
|
||||
</font>
|
||||
</defs></svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
BIN
src/components/VContextmenu/styles/fonts/iconfont.ttf
Normal file
BIN
src/components/VContextmenu/styles/fonts/iconfont.ttf
Normal file
Binary file not shown.
BIN
src/components/VContextmenu/styles/fonts/iconfont.woff
Normal file
BIN
src/components/VContextmenu/styles/fonts/iconfont.woff
Normal file
Binary file not shown.
14
src/components/VContextmenu/styles/iconfont.styl
Normal file
14
src/components/VContextmenu/styles/iconfont.styl
Normal file
@ -0,0 +1,14 @@
|
||||
@font-face
|
||||
font-family: "v-contextmenu-iconfont"
|
||||
src: url("./fonts/iconfont.eot")
|
||||
src: url("./fonts/iconfont.eot#iefix") format("embedded-opentype"),
|
||||
url("./fonts/iconfont.woff") format("woff"),
|
||||
url("./fonts/iconfont.ttf") format("truetype"),
|
||||
url("./fonts/iconfont.svg") format("svg")
|
||||
|
||||
.v-contextmenu-iconfont
|
||||
font-family: "v-contextmenu-iconfont" !important
|
||||
font-size: inherit
|
||||
font-style: normal
|
||||
-webkit-font-smoothing: antialiased
|
||||
-moz-osx-font-smoothing: grayscale
|
||||
1
src/components/VContextmenu/styles/index.css
Normal file
1
src/components/VContextmenu/styles/index.css
Normal file
@ -0,0 +1 @@
|
||||
@font-face{font-family:"v-contextmenu-iconfont";src:url("./fonts/iconfont.eot");src:url("./fonts/iconfont.eot#iefix") format("embedded-opentype"),url("./fonts/iconfont.woff") format("woff"),url("./fonts/iconfont.ttf") format("truetype"),url("./fonts/iconfont.svg") format("svg")}.v-contextmenu-iconfont{font-family:"v-contextmenu-iconfont" !important;font-size:inherit;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.v-contextmenu{position:absolute;padding:5px 0;margin:0;background-color:#fff;border:1px solid #e8e8e8;border-radius:4px;box-shadow:2px 2px 8px 0 rgba(150,150,150,0.2);list-style:none;font-size:14px;white-space:nowrap;cursor:pointer;z-index:2800;-webkit-tap-highlight-color:transparent}.v-contextmenu .v-contextmenu-item{padding:5px 14px;line-height:1;color:#333}.v-contextmenu .v-contextmenu-item.v-contextmenu-item--hover{color:#fff}.v-contextmenu .v-contextmenu-item.v-contextmenu-item--disabled{color:#ccc;cursor:not-allowed}.v-contextmenu .v-contextmenu-divider{height:0;margin:5px 0;border-bottom:1px solid #e8e8e8}.v-contextmenu .v-contextmenu-group__menus{padding:0 5px;margin:0;list-style:none}.v-contextmenu .v-contextmenu-group__menus .v-contextmenu-item{display:inline-block;padding:5px 9px}.v-contextmenu .v-contextmenu-submenu{position:relative}.v-contextmenu .v-contextmenu-submenu > .v-contextmenu{position:absolute}.v-contextmenu .v-contextmenu-submenu > .v-contextmenu.left{left:0;transform:translateX(-100%)}.v-contextmenu .v-contextmenu-submenu > .v-contextmenu.right{right:0;transform:translateX(100%)}.v-contextmenu .v-contextmenu-submenu > .v-contextmenu.top{top:-6px}.v-contextmenu .v-contextmenu-submenu > .v-contextmenu.bottom{bottom:-6px}.v-contextmenu .v-contextmenu-submenu .v-contextmenu-submenu__title{margin-right:10px}.v-contextmenu .v-contextmenu-submenu .v-contextmenu-submenu__icon{position:absolute;right:5px}.v-contextmenu .v-contextmenu-submenu .v-contextmenu-submenu__icon::before{content:"\e622"}.v-contextmenu--default .v-contextmenu-item--hover{background-color:#46a0fc}.v-contextmenu--bright .v-contextmenu-item--hover{background-color:#ef5350}.v-contextmenu--dark .v-contextmenu-item--hover{background-color:#2d3035}
|
||||
11
src/components/VContextmenu/styles/index.styl
Normal file
11
src/components/VContextmenu/styles/index.styl
Normal file
@ -0,0 +1,11 @@
|
||||
active-color = #46a0fc
|
||||
border-color = #e8e8e8
|
||||
shadow-color = rgba(150, 150, 150, 0.2)
|
||||
padding-vertical = 5px
|
||||
contextmenu-item-padding-horizonal = 14px
|
||||
|
||||
active-color-bright = #ef5350
|
||||
active-color-dark = #2d3035
|
||||
|
||||
@import "./iconfont.styl"
|
||||
@import "./contextmenu.styl"
|
||||
@ -53,7 +53,7 @@ export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: 'sdfsd'
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
||||
@ -427,9 +427,9 @@ export default {
|
||||
$tagMenuBorderRadius: 8px;
|
||||
|
||||
>>> .ctx-menu-container {
|
||||
border: 0 solid rgba(0, 0, 0, 0);
|
||||
min-width: unset;
|
||||
border-radius: $dialogBorderRadius;
|
||||
border: 0 solid rgba(0, 0, 0, 0) !important;
|
||||
min-width: unset !important;
|
||||
border-radius: $dialogBorderRadius !important;
|
||||
}
|
||||
|
||||
.tag-menu ul {
|
||||
@ -446,7 +446,7 @@ $tagMenuBorderRadius: 8px;
|
||||
min-width: 110px;
|
||||
}
|
||||
.tag-menu {
|
||||
padding: 5px 5px;
|
||||
padding: 3px;
|
||||
}
|
||||
.tag-menu li:hover {
|
||||
cursor: pointer;
|
||||
|
||||
@ -19,8 +19,8 @@ setFavicon()
|
||||
import { Affix } from 'ant-design-vue';
|
||||
Vue.use(Affix)
|
||||
|
||||
import contentmenu from 'v-contextmenu'
|
||||
import 'v-contextmenu/dist/index.css'
|
||||
import contentmenu from '@/components/VContextmenu'
|
||||
import '@/components/VContextmenu/styles/index.css'
|
||||
Vue.use(contentmenu)
|
||||
|
||||
import '@/icons' // icon
|
||||
|
||||
@ -131,31 +131,6 @@
|
||||
line-height: 35px;
|
||||
}
|
||||
|
||||
>>> .ctx-menu-container {
|
||||
border: 0 solid rgba(0, 0, 0, 0);
|
||||
min-width: 160px;
|
||||
border-radius: $dialogBorderRadius;
|
||||
padding: 10px 10px;
|
||||
box-shadow: 0 5px 12px #ccc;
|
||||
|
||||
.home-contextmenu {
|
||||
padding: 0 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.svg-icon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.home-contextmenu-title {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.menu-triangle-bottom {
|
||||
border: 0 solid rgba(0, 0, 0, 0);
|
||||
|
||||
@ -40,9 +40,9 @@ body {
|
||||
|
||||
.el-popover.el-popper.upload-file {
|
||||
border-radius: 16px;
|
||||
padding: 10px 10px;
|
||||
padding: 8px;
|
||||
li {
|
||||
padding: 0 10px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,9 +3,15 @@ export const fileOperations = {
|
||||
remove: { iconClass: 'menu-remove', label: '删除', operation: 'remove', shortcut: ['Del'] },
|
||||
duplicate: { iconClass: 'duplicate', label: '创建副本', operation: 'duplicate' },
|
||||
download: { iconClass: 'menu-download', label: '下载', operation: 'download' },
|
||||
copy: { iconClass: 'menu-copy', label: '移动或复制', operation: 'copy' },
|
||||
copy: { iconClass: 'menu-copy', label: '移动或复制...', operation: 'copy' },
|
||||
rename: { iconClass: 'menu-rename', label: '重命名', operation: 'rename', shortcut: ['F2'] },
|
||||
copyOnly: { iconClass: 'menu-copy', label: '复制', operation: 'copyOnly', shortcut: [navigator.platform.startsWith('Mac') ? '⌘' : 'Ctrl', 'C'] },
|
||||
detail: {
|
||||
iconClass: 'menu-details',
|
||||
label: '详情...',
|
||||
shortcut: ["space"],
|
||||
operation: 'details',
|
||||
}
|
||||
}
|
||||
|
||||
export const fileArrangements = [
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user