diff --git a/package.json b/package.json index ebfab99..f76fe7a 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/ShowFile/ShowFile.vue b/src/components/ShowFile/ShowFile.vue index 0028e7b..d4bce98 100644 --- a/src/components/ShowFile/ShowFile.vue +++ b/src/components/ShowFile/ShowFile.vue @@ -10,7 +10,7 @@ - -
-
    - -
  • - -
  • -
-
+
-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 +3553,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 > -1 && !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 +3600,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 +3626,7 @@ export default { }, // 鼠标右击 rowContextmenu(row) { + this.$refs.homeContextmenu.hide() if (this.selectFile) { return } @@ -3653,7 +3637,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 +3645,36 @@ export default { this.menusIsMultiple = false this.setMenus(row) } + + // 设置分割线 + this.setFileContextMenusDivider() + event.preventDefault() + event.stopPropagation() this.menuTriangle = '' 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 +3708,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 +4269,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 }, diff --git a/src/components/VContextmenu/components/Contextmenu.vue b/src/components/VContextmenu/components/Contextmenu.vue new file mode 100644 index 0000000..599d7cd --- /dev/null +++ b/src/components/VContextmenu/components/Contextmenu.vue @@ -0,0 +1,180 @@ + + + diff --git a/src/components/VContextmenu/components/ContextmenuGroup.vue b/src/components/VContextmenu/components/ContextmenuGroup.vue new file mode 100644 index 0000000..c9cdd22 --- /dev/null +++ b/src/components/VContextmenu/components/ContextmenuGroup.vue @@ -0,0 +1,33 @@ + + + diff --git a/src/components/VContextmenu/components/ContextmenuItem.vue b/src/components/VContextmenu/components/ContextmenuItem.vue new file mode 100644 index 0000000..a7ad8c8 --- /dev/null +++ b/src/components/VContextmenu/components/ContextmenuItem.vue @@ -0,0 +1,73 @@ + + + diff --git a/src/components/VContextmenu/components/ContextmenuSubmenu.vue b/src/components/VContextmenu/components/ContextmenuSubmenu.vue new file mode 100644 index 0000000..bbef54a --- /dev/null +++ b/src/components/VContextmenu/components/ContextmenuSubmenu.vue @@ -0,0 +1,110 @@ + + + + diff --git a/src/components/VContextmenu/directive.js b/src/components/VContextmenu/directive.js new file mode 100644 index 0000000..f76087c --- /dev/null +++ b/src/components/VContextmenu/directive.js @@ -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 }) + } + }, +} diff --git a/src/components/VContextmenu/file-contextmenu.vue b/src/components/VContextmenu/file-contextmenu.vue new file mode 100644 index 0000000..2a4a9f2 --- /dev/null +++ b/src/components/VContextmenu/file-contextmenu.vue @@ -0,0 +1,139 @@ + + + + + diff --git a/src/components/VContextmenu/index.js b/src/components/VContextmenu/index.js new file mode 100644 index 0000000..7c2a56c --- /dev/null +++ b/src/components/VContextmenu/index.js @@ -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, +} diff --git a/src/components/VContextmenu/styles/contextmenu.styl b/src/components/VContextmenu/styles/contextmenu.styl new file mode 100644 index 0000000..937a8ee --- /dev/null +++ b/src/components/VContextmenu/styles/contextmenu.styl @@ -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 diff --git a/src/components/VContextmenu/styles/fonts/iconfont.eot b/src/components/VContextmenu/styles/fonts/iconfont.eot new file mode 100644 index 0000000..c053677 Binary files /dev/null and b/src/components/VContextmenu/styles/fonts/iconfont.eot differ diff --git a/src/components/VContextmenu/styles/fonts/iconfont.svg b/src/components/VContextmenu/styles/fonts/iconfont.svg new file mode 100644 index 0000000..9477beb --- /dev/null +++ b/src/components/VContextmenu/styles/fonts/iconfont.svg @@ -0,0 +1,36 @@ + + + + + +Created by iconfont + + + + + + + + + + + + + + + + + + diff --git a/src/components/VContextmenu/styles/fonts/iconfont.ttf b/src/components/VContextmenu/styles/fonts/iconfont.ttf new file mode 100644 index 0000000..1b82fc5 Binary files /dev/null and b/src/components/VContextmenu/styles/fonts/iconfont.ttf differ diff --git a/src/components/VContextmenu/styles/fonts/iconfont.woff b/src/components/VContextmenu/styles/fonts/iconfont.woff new file mode 100644 index 0000000..b0584c9 Binary files /dev/null and b/src/components/VContextmenu/styles/fonts/iconfont.woff differ diff --git a/src/components/VContextmenu/styles/iconfont.styl b/src/components/VContextmenu/styles/iconfont.styl new file mode 100644 index 0000000..5607636 --- /dev/null +++ b/src/components/VContextmenu/styles/iconfont.styl @@ -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 diff --git a/src/components/VContextmenu/styles/index.css b/src/components/VContextmenu/styles/index.css new file mode 100644 index 0000000..20698c2 --- /dev/null +++ b/src/components/VContextmenu/styles/index.css @@ -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} diff --git a/src/components/VContextmenu/styles/index.styl b/src/components/VContextmenu/styles/index.styl new file mode 100644 index 0000000..eb3deda --- /dev/null +++ b/src/components/VContextmenu/styles/index.styl @@ -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" diff --git a/src/components/select/icon.vue b/src/components/select/icon.vue index 21bf234..d587284 100644 --- a/src/components/select/icon.vue +++ b/src/components/select/icon.vue @@ -53,7 +53,7 @@ export default { props: { value: { type: String, - default: 'sdfsd' + default: '' } }, data() { diff --git a/src/layout/components/Sidebar/index.vue b/src/layout/components/Sidebar/index.vue index 523ad91..6d18080 100644 --- a/src/layout/components/Sidebar/index.vue +++ b/src/layout/components/Sidebar/index.vue @@ -45,7 +45,7 @@
- +
  • @@ -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; diff --git a/src/main.js b/src/main.js index b8848a0..3dd7873 100644 --- a/src/main.js +++ b/src/main.js @@ -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 diff --git a/src/styles/home-index.scss b/src/styles/home-index.scss index 1bd4208..268551a 100644 --- a/src/styles/home-index.scss +++ b/src/styles/home-index.scss @@ -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); diff --git a/src/styles/index.scss b/src/styles/index.scss index 63ac178..f55dc9c 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -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; } } diff --git a/src/utils/file-operations.js b/src/utils/file-operations.js index 9964d6c..ee53f6f 100644 --- a/src/utils/file-operations.js +++ b/src/utils/file-operations.js @@ -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 = [