Merge pull request #456 from jamebal/dev

refactor: 重构ShowFile.vue
This commit is contained in:
jmal 2025-08-07 11:07:20 +08:00 committed by GitHub
commit 8d4162426a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 4110 additions and 3989 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,48 @@
import store from '@/store'
import fileConfig from '@/utils/file-config'
export default {
computed: {
fileClipboard() {
return store.getters.fileClipboard
},
getClipboardFileIdList() {
return store.getters.fileClipboard.map(file => file.id)
},
},
props: {
showClipboard: {
type: Boolean,
default: false,
}
},
methods: {
// 复制下载链接
copyDownloadLink(row, elName) {
let url = window.location.origin + fileConfig.previewUrl(this.$store.getters.name, row, undefined, this.shareToken)
if (row.isFolder) {
url = fileConfig.packageDownloadUrl(row.id, row.name + '.zip', this.shareToken)
}
let clipboard = new Clipboard(elName, {
text: function() {
return url
},
})
clipboard.on('success', () => {
this.$message({ message: '复制成功', type: 'success', duration: 1000 })
// 释放内存
clipboard.destroy()
})
clipboard.on('error', () => {
// 不支持复制
this.$message({
message: '该浏览器不支持自动复制',
type: 'warning',
duration: 1000,
})
clipboard.destroy()
})
},
}
}

View File

@ -0,0 +1,23 @@
import { formatSize, formatTime } from '@/utils/number'
export default {
computed: {
imageUrl() {
return `${process.env.VUE_APP_BASE_API}/view/thumbnail?jmal-token=${this.$store.state.user.token}&name=${this.$store.state.user.name}&id=`
},
audioCoverUrl() {
return `${process.env.VUE_APP_BASE_API}/view/cover?jmal-token=${this.$store.state.user.token}&name=${this.$store.state.user.name}&id=`
}
},
methods: {
// 格式化最近时间
formatTime(time) {
return formatTime(time)
},
// 格式化文件大小
formatSize(size) {
return formatSize(size)
},
}
}

View File

@ -0,0 +1,452 @@
import api from '@/api/file-api'
import fileConfig from '@/utils/file-config'
import { directLinkSubMenus, fileOperations } from '@/utils/file-operations'
import { suffix } from '@/utils/file-type'
export default {
props: {
singleMenus: {
type: Array,
default: function() {
return [
fileOperations.open,
{ iconClass: 'link', label: '直链', operation: 'directLink', child: directLinkSubMenus },
fileOperations.download,
{ iconClass: 'share', label: '分享...', operation: 'share' },
{ iconClass: 'tag', label: '标签...' , operation: 'tag'},
{ iconClass: 'menu-favorite', label: '收藏', operation: 'favorite' },
fileOperations.detail,
fileOperations.copyOnly,
fileOperations.rename,
fileOperations.copy,
fileOperations.remove,
]
},
},
multipleRightMenus: {
type: Array,
default: function() {
return [
{
iconClass: 'menu-deselect',
label: '取消选定',
operation: 'deselect',
},
fileOperations.download,
{ iconClass: 'tag', label: '标签', operation: 'tag' },
fileOperations.copyOnly,
fileOperations.copy,
fileOperations.remove,
]
},
},
},
data() {
return {
isJustHideMenus: false,
menusIsMultiple: false,
menus: [],
rowContextData: {},
}
},
methods: {
contextmenu(e) {
e.preventDefault()
e.stopPropagation()
},
show() {
const that = this
this.contextmenuDisabled = true
setTimeout(function() {
that.contextmenuDisabled = false
}, 1000)
},
hide() {
const that = this
this.isJustHideMenus = true
setTimeout(function() {
that.isJustHideMenus = false
}, 100)
this.cellMouseIndex = -1
},
setMenus(row) {
this.menus = JSON.parse(JSON.stringify(this.singleMenus))
// 挂载的文件
const owner = localStorage.getItem('mountFileOwner')
const notSelf = row.userId && row.userId !== this.$store.getters.userId
if ((this.$route.query.folder && owner) || notSelf) {
// 根据权限设置菜单
this.setMenusByPermission(row)
} else {
if (row.suffix && row.suffix === 'md' && this.queryFileType !== 'trash') {
this.menus.splice(this.getIndexOfFileContextMenus(fileOperations.copyOnly.operation), 0, {
iconClass: 'menu-edit1',
label: '编辑',
operation: 'edit',
})
}
if (row.isFolder && row.mountFileId) {
const reservations = ['open', 'tag', 'favorite', 'remove']
// 删除this.menus中不要的菜单, 仅保留reservations中的菜单
this.menus = this.menus.filter(item =>
reservations.includes(item.operation)
)
}
if (!row.isFolder && this.queryFileType !== 'trash') {
// 创建副本
const copyIndex = this.getIndexOfFileContextMenus(fileOperations.copy.operation)
if (copyIndex > -1) {
this.menus.splice(copyIndex, 0, fileOperations.duplicate)
}
}
if (row.ossFolder) {
const reservations = ['open', 'tag']
// 删除this.menus中不要的菜单, 仅保留reservations中的菜单
this.menus = this.menus.filter(item =>
reservations.includes(item.operation)
)
}
this.setMenusCopyDownLoadLinks(row)
}
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的索引
const downloadIndex = this.menus.findIndex(
item => item.operation === 'download'
)
this.shareToken = undefined
// 在download之前添加复制下载链接选项
this.addMenusCopyDownLoadLinks(downloadIndex)
if (row.isPrivacy) {
// 如果是私密分享需要先获取shareToken
api.generateShareToken({fileId: row.id}).then(res => {
this.shareToken = res.data
}).catch(() => {
this.menus.splice(downloadIndex, 1)
})
}
}
},
addMenusCopyDownLoadLinks(index) {
this.menus.splice(index, 0, {
iconClass: 'menu-fuzhi',
label: '复制下载链接',
operation: 'copyDownloadLink',
})
},
setMenusByPermission(file) {
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, 0, fileOperations.rename)
}
if (file.operationPermissionList.indexOf('UPLOAD') > -1) {
if (file.operationPermissionList.indexOf('DELETE') > -1) {
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, 0, fileOperations.duplicate)
}
}
if (file.operationPermissionList.indexOf('DELETE') > -1) {
this.menus.splice(this.menus.length, 0, fileOperations.remove)
}
}
this.setMenusCopyDownLoadLinks(file)
},
// 鼠标右击
rowContextmenu(row) {
this.$refs.homeContextmenu.hide()
if (this.selectFile) {
return
}
if (
this.$refs.fileListTable.tableSelectData.length > 1 &&
this.$refs.fileListTable.tableSelectData.findIndex(item => item.index === row.index) > -1
) {
this.menusIsMultiple = true
this.menus = JSON.parse(JSON.stringify(this.multipleRightMenus))
this.highlightFavorite(false, false)
} else {
this.fileListTableClearSelection()
this.$refs.fileListTable.toggleRowSelection([{ row: row }])
this.menusIsMultiple = false
this.setMenus(row)
}
// 设置分割线
this.setFileContextMenusDivider()
event.preventDefault()
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 + 2
const dividerSize = this.menus.filter(item => item.divider).length
const containerWidth = 180 + 10
const containerHeight = (this.menus.length - dividerSize) * 35 + (dividerSize * 11) + 18 + 10
const distanceToBottom = document.documentElement.clientHeight - event.clientY
const distanceToRight = document.documentElement.clientWidth - event.clientX
if (distanceToBottom < containerHeight) {
e.top = event.clientY - (containerHeight - distanceToBottom)
}
if (distanceToRight < containerWidth) {
e.left = event.clientX - containerWidth + 8
}
this.$refs.contextShow.showMenu(e)
this.cellMouseIndex = -1
},
// 列表右键菜单操作
menusOperations(operation, event) {
switch (operation) {
case 'share':
// 分配标签
this.share()
break
case 'tag':
// 分配标签
this.allocateTag()
break
case 'favorite':
// 收藏
this.favoriteOperating(true)
break
case 'edit':
// 编辑
window.open(
`/setting/website/manager-articles?operation=editor&id=${this.rowContextData.id}`,
'_blank'
)
break
case 'open':
// 打开
this.fileClick(this.rowContextData, event)
break
case 'deselect':
// 取消选定
this.fileListTableClearSelection()
break
case 'unFavorite':
// 取消收藏
this.favoriteOperating(false)
break
case 'details':
this.drawer = true
break
case 'rename':
// 重命名
this.renameFileName = this.rowContextData.name
this.editingIndex = this.rowContextData.index
break
case 'duplicate':
// 创建副本
this.duplicate()
break
case 'copy':
// 移动或复制
this.moveOrCopy()
break
case 'copyOnly':
this.copyOperation()
break
case 'download':
// 下载
this.downloadFile()
break
case 'copyDownloadLink':
// 复制下载链接
this.copyDownloadLink(this.rowContextData, '.file-contextmenu')
break
case 'remove':
// 删除
this.removeOperation()
break
case 'sweep':
// 清空回收站
this.sweepFile()
break
case 'restore':
// 返回原处
this.restoreFile()
break
case 'manageDirectLink':
// 管理直链
this.directLinkDialogVisible = true
this.directLinkDialogGetDirectLink(this.rowContextData)
break
case 'copyDirectLink':
// 复制直链
this.directLinkDialogGetDirectLink(this.rowContextData, true)
break
}
this.$refs.contextShow.hideMenu()
},
// 点击文件或文件夹
fileClick(row, event) {
this.drawer = false
if (this.queryFileType === 'trash') {
return
}
if (this.editingIndex === row.index) {
return
}
this.openingFile = row
if (row.isFolder) {
this.editingIndex = -1
const queryTagId = this.$route.query.tagId ? `&tagId=${this.$route.query.tagId}` : ''
const keyword = this.$route.query.keyword ? `&keyword=${this.$route.query.keyword}` : ''
// 打开文件夹
if (this.listModeSearch) {
const item = {}
item['folder'] = row.name
item['search'] = true
item['row'] = row
this.pathList.push(item)
this.pagination.pageIndex = 1
const searchOpenFolder = row.id ? `&searchOpenFolder=${row.id}` : ''
const path = this.$route.query.path ? `&path=${this.$route.query.path}` : ''
const folder = this.$route.query.folder ? `&folder=${this.$route.query.folder}` : ''
const basePath = this.getBasePath()
this.$router.push(
`?vmode=${
this.vmode
}${path}${keyword}${searchOpenFolder}${folder}${queryTagId}${basePath}`
)
this.searchFileAndOpenDir(row.id)
} else {
let notHomePage = this.$route.path.length > 1
if (
notHomePage &&
this.path + '/' !== row.path &&
this.basePath.length === 1
) {
this.basePath = row.path
}
this.path += '/' + row.name
this.path = this.path.replace(/\\/g, '/')
this.path = this.path.replace(/\/\//g, '/')
// 去掉this.path开头的this.basePath
this.path = this.path.replace(this.basePath, '/')
const path = encodeURIComponent(this.path)
const item = { folder: row.name, shareBase: row.shareBase }
this.pathList.push(item)
this.pagination.pageIndex = 1
if (this.$store.getters.userId !== row.userId) {
row.mountFileId = row.id
}
if (row.mountFileId) {
localStorage.setItem(this.path, row.mountFileId)
}
const basePath = this.basePath && this.basePath.length > 1 ? `&basePath=${this.basePath}` : ''
const searchOpenFolder = this.$route.query.searchOpenFolder ? `&searchOpenFolder=${this.$route.query.searchOpenFolder}` : ''
this.$router.push(`?vmode=${this.vmode}&path=${path}${row.mountFileId ? '&folder=' + row.mountFileId : ''}${queryTagId}${basePath}${keyword}${searchOpenFolder}`)
this.openDir(row)
}
} else {
if (this.selectFile) {
let selectFile = row
const selectData = this.$refs.fileListTable.tableSelectData
if (selectData.length < 1 || selectData[0].id !== row.id) {
this.fileListTableClearSelection()
this.$refs.fileListTable.toggleRowSelection([{ row: row }])
this.pinSelect(row, event)
} else {
this.fileListTableClearSelection()
selectFile = {}
}
this.$emit('selectedFile', selectFile)
return
}
const fileHandler = fileConfig.hasIframePreview(row.suffix, this.$store.getters.iframePreviewConfig)
if (fileHandler !== null) {
// iframe 预览
this.iframePreviewVisible = true
this.iframePreviewRow = row
this.fileHandler = fileHandler
return
}
if (row.contentType.startsWith('image')) {
// 图片
this.imagePreviewVisible = true
this.imagePreviewRow = row
return
}
if (suffix.simText.includes(row.suffix)) {
// 文本文件
this.textPreviewRow = row
this.textPreviewVisible = true
return
}
if (row.contentType.indexOf('video') > -1) {
// 视频文件
this.videoPreviewVisible = true
this.videoPreviewRow = row
return
}
if (row.contentType.indexOf('audio') > -1) {
// 音频文件
this.$store.dispatch('updateMessage', {
event: 'onAddAudio',
data: { row: row, audioCoverUrl: this.audioCoverUrl },
})
return
}
if (suffix.compressedFile.includes(row.suffix)) {
// 压缩文件
this.openCompressionVisible = true
return
}
if (row.contentType.indexOf('office') > -1 || suffix.iframePreviewFile.includes(row.suffix)) {
// iframe 预览
this.iframePreviewVisible = true
this.iframePreviewRow = row
this.fileHandler = {}
return
}
if (row.contentType.indexOf('utf-8') > -1) {
// 文本文件
this.textPreviewRow = row
this.textPreviewVisible = true
return
}
this.notPreviewDialogVisible = true
}
},
}
}

View File

@ -0,0 +1,740 @@
import { getElementToPageLeft } from '@/utils/dom'
export default {
data() {
return {
dragElementList: [],
drawFlag: false,
draging: 0, // 是否正在拖拽中,0:没有拖拽,1:拖拽中,
selectRowData: [],
selectOrigin: -1, // 选择起点(主要用于按住shift键多选)
selectEnd: -1, // 选择终点
inputting: false, // 是否正在输入
fileListScrollTop: 0,
initFileListScrollTop: 0,
}
},
methods: {
// 画矩形选区
drawRectangle() {
let scrollDiv = this.$refs.fileListTableContainer.querySelector('.el-table__body-wrapper')
if (this.grid) {
scrollDiv = this.$refs.gridDiv.querySelector('.van-grid')
}
// 添加scroll事件
scrollDiv.onscroll = e => {
this.tableBodyScroll(null, e)
}
if (this.selectFile) {
return
}
const _this = this
let $$ = function(id) {
return document.getElementById(id)
}
let draw = $$('v-draw-rectangle')
let wId = 'rectangle1'
let startX = 0,
startY = 0
let retcLeft = 0,
retcTop = 0,
retcHeight = 0,
retcWidth = 0
_this.drawFlag = false
let itemClassName = 'el-table__row'
if (_this.grid) {
itemClassName =
'grid-item van-grid-item__content van-grid-item__content--center van-grid-item__content--square'
}
draw.onmousedown = null
draw.onmousedown = function(e) {
let evt = window.event || e
const elPath = e.path || (e.composedPath && e.composedPath())
// 列表模式下点击表头,阻止点击事件
if (!_this.grid && _this.selectRowData.length > 0) {
const findIndex = elPath.findIndex(path => {
if (path.className === 'el-table__header-wrapper') {
return path
}
})
if (findIndex > -1) {
_this.stopSortChange = true
}
}
// 点击的区域是否为文件, throughRow 不为空就证明点到了文件
let throughRow = elPath.find(path => {
if (path.className === itemClassName || path.className === 'el-table__row el-table__row--striped') {
return path
}
})
if (throughRow) {
// 鼠标按下时就选中文件
if (!_this.selectRowData.includes(_this.fileList[throughRow.rowIndex])) {
_this.editingIndex = -1
if (!_this.checkCmdKey(e) && !e.shiftKey) {
_this.fileListTableClearSelection()
_this.$refs.fileListTable.toggleRowSelection([
{ row: _this.fileList[throughRow.rowIndex], selected: true },
])
}
}
return
} else {
_this.editingIndex = -1
}
if (evt.button !== 0) {
return
}
if (!_this.checkCmdKey(e) && !e.shiftKey) {
const index = elPath.findIndex(
el =>
el.className === itemClassName ||
el.className === 'el-table__row el-table__row--striped'
)
if (index < 0) {
_this.fileListTableClearSelection()
}
}
let scrollTop = draw.scrollTop
let scrollLeft = draw.scrollLeft || draw.scrollLeft
startX = evt.clientX + scrollLeft
startY = evt.clientY + scrollTop
let div = document.createElement('div')
div.id = wId
div.className = 'draw-rectangle'
div.style.left = startX + 'px'
div.style.top = startY + 'px'
div.style.position = 'fixed'
div.style.border = '1px dashed #2898ff'
div.style.width = '0px'
div.style.height = '0px'
div.style.left = '0px'
div.style.top = '0px'
div.style.overflow = 'hidden'
draw.appendChild(div)
document.onmousemove = function(e) {
let evt = window.event || e
let scrollTop =
document.body.scrollTop || document.documentElement.scrollTop
let scrollLeft =
document.body.scrollLeft || document.documentElement.scrollLeft
retcLeft =
startX - evt.clientX - scrollLeft > 0
? evt.clientX + scrollLeft
: startX
retcTop =
startY - evt.clientY - scrollTop > 0
? evt.clientY + scrollTop
: startY
retcHeight = Math.abs(startY - evt.clientY - scrollTop)
retcWidth = Math.abs(startX - evt.clientX - scrollLeft)
_this.drawFlag = true
if (retcHeight + retcWidth < 4) {
return
}
const drawRectangle = document.getElementById(wId)
if (drawRectangle) {
drawRectangle.style.left = retcLeft + 'px'
drawRectangle.style.top = retcTop + 'px'
drawRectangle.style.width = retcWidth + 'px'
drawRectangle.style.height = retcHeight + 'px'
drawRectangle.style.backgroundColor = '#f2f5fa55'
}
if (_this.drawFlag && retcHeight + retcWidth > 4) {
if (!drawSelecting) {
drawSelect({
x: retcLeft,
y: retcTop + _this.fileListScrollTop,
w: retcWidth,
h: retcHeight,
})
}
}
}
document.onmouseup = function(e) {
document.onmousemove = null
document.onmouseup = null
if (_this.stopSortChange) {
_this.stopSortChange = false
setTimeout(() => {
_this.changeSelectedStyle(_this.selectRowData)
}, 200)
}
setTimeout(function() {
_this.drawFlag = false
}, 50)
const rectangle = document.getElementById(wId)
if (rectangle) {
draw.removeChild(rectangle)
}
if (!_this.grid) {
draw = document.getElementById('drag-table')
}
const dragingDivs = Array.prototype.slice.call(
draw.getElementsByClassName('dragingDiv')
)
dragingDivs.forEach(el => draw.removeChild(el))
}
}
let drawSelecting = false
let drawSelect = function(drawNode) {
drawSelecting = true
_this.dragElementList.forEach(element => {
if (checkTouch(element, drawNode)) {
_this.$refs.fileListTable.toggleRowSelection([
{ row: _this.fileList[element.rowIndex], selected: true },
])
} else {
_this.$refs.fileListTable.toggleRowSelection([
{ row: _this.fileList[element.rowIndex], selected: false },
])
}
})
setTimeout(() => (drawSelecting = false), 10)
}
//检查两个DIV是否有接触
let checkTouch = function(item, draw) {
//得到左上角的绝对坐标
let x1 = item.x
let y1 = item.y
let x2 = draw.x
let y2 = draw.y
let w1 = item.w
let h1 = item.h
let w2 = draw.w
let h2 = draw.h
return (
((x1 - x2 <= 0 && x2 - x1 < w1) || (x1 - x2 >= 0 && x1 - x2 < w2)) &&
((y1 - y2 <= 0 && y2 - y1 < h1) || (y1 - y2 >= 0 && y1 - y2 < h2))
)
}
},
// 行拖拽
rowDrop() {
if (this.selectFile) {
return
}
// 目标元素的背景颜色
let dragEnterBackCorlor = null
// 被拖拽元素的背景色
let dragBackCorlor = null
const _this = this
// 被拖动的元素
let dragged = null
// 被拖动的元素的索引
let draggedIndex = -1
let parentClassName = 'van-grid'
let itemClassName = 'van-grid-item van-grid-item--square'
let gridItemChildenClassName =
'grid-item van-grid-item__content van-grid-item__content--center van-grid-item__content--square'
if (!_this.grid) {
itemClassName = 'el-table__row'
parentClassName = 'el-table__body'
}
// 正在拖动的元素
let dragingDiv = null
let container = document.querySelector('.dashboard-container')
// 目标元素
let target = document.querySelector('.el-table__body-wrapper tbody')
if (this.grid) {
target = document.querySelector('.van-checkbox-group .van-grid')
}
let draw = document.getElementById('v-draw-rectangle')
if (!this.grid) {
draw = document.getElementById('drag-table')
}
let rows = 0 //行数
let drawOffsetLeft = getElementToPageLeft(draw)
let firstOver = 0 // 是否刚开始拖动
let moveTitle = ''
this.$nextTick(() => {
rows = target.childElementCount
_this.dragElementList = []
for (let i = 0; i < target.childElementCount; i++) {
let child = target.children[i]
// 设置索引,表格自带rowIndex,这里我们设置grid的
if (_this.grid) {
if (child.rowIndex !== i) {
child.rowIndex = i
}
child.children[0].children[0].rowIndex = i
child = child.children[0].children[0]
}
// 为画矩形选取准备数据
let pos = getObjPos(child)
child.w = child.offsetWidth
child.h = child.offsetHeight
child.x = pos.x
child.y = pos.y
pos.rowIndex = child.rowIndex
_this.dragElementList.push(child)
// 使元素可拖动
child.draggable = true
// 给能拖动的元素加上标识,只有加上此标识才能被拖动,否则即使draggable = true,也无法拖动(在全局的ondragstart里拦截)
child.slot = 'jmal'
let childOfImg = child.querySelector('.el-avatar > img')
if (_this.grid) {
childOfImg = child.querySelector('.el-image > img')
}
if (childOfImg) {
childOfImg.draggable = false
}
}
})
// 被拖动的元素正在那个容器里
let dragIndex = -1
// 判断经过了那个元素
let judgThroughDom = function(e, d) {
const elPath = e.path || (e.composedPath && e.composedPath())
if (d === 'enter') {
// 这里进入其他容器后 清除上次进入的容器的状态
let node = null
const className = e.toElement.className
if (_this.grid) {
if (className === itemClassName) {
node = e.toElement
}
if (className === parentClassName) {
node = e.fromElement
}
} else {
// 列表模式
if (
elPath[0].id === 'v-draw-rectangle' ||
elPath[0].className === 'el-table__virtual-wrapper'
) {
// 超出列表底部
node = e.toElement
} else {
// 超出列表顶部
node = elPath.find(path => {
if (path.className === 'el-table__header-wrapper') {
return path
}
})
}
}
if (node) {
// console.log(d,e,node,node.rowIndex)
if (dragIndex > -1) {
// 清除上次进入的容器的状态
const last = target.children[dragIndex]
clearClass(last)
}
dragIndex = -1
}
}
let throughRow = null
if (_this.grid) {
if (elPath[0].className === gridItemChildenClassName) {
// throughRow 表示被拖动的元素正在哪一行上
return throughRow
} else {
throughRow = elPath.find(path => {
if (path.className === gridItemChildenClassName) {
return path
}
})
}
return throughRow
} else {
if (elPath[0].tagName === 'TD') {
// throughRow 表示被拖动的元素正在哪一行上
throughRow = elPath.find(path => {
if (
path.className === 'el-table__row el-table__row--striped' ||
path.className === 'el-table__row'
) {
return path
}
})
}
return throughRow
}
}
/***
* 复原拖拽的dom
* @param animation 是否显示动画
*/
let recoverDragDom = function(animation) {
if (animation) {
_this.selectRowData.forEach(row => {
let dragingDiv = document.getElementById('dragingDiv' + row.index)
dragingDiv.style.transition = 'all 0.3s'
dragingDiv.style.top =
dragingDiv.original.top -
(_this.fileListScrollTop - _this.initFileListScrollTop) +
'px'
dragingDiv.style.left = dragingDiv.original.left
})
setTimeout(() => {
_this.selectRowData.forEach(row => {
draw.removeChild(
document.getElementById('dragingDiv' + row.index)
)
})
}, 300)
} else {
_this.selectRowData.forEach(row => {
draw.removeChild(document.getElementById('dragingDiv' + row.index))
})
}
setTimeout(() => {
if (!_this.grid) {
document
.getElementsByClassName('el-table')[0]
.classList.add('el-table--enable-row-hover')
} else {
target.querySelectorAll('.grid-hover-back').forEach(e => {
e.classList.add('grid-hover')
})
}
}, 350)
_this.draging = 0
}
container.ondragend = function(e) {
_this.$store.dispatch('updateMessage', {
event: 'onDragStart',
data: false,
})
e.dataTransfer.effectAllowed = 'none'
// 清除上次进入的容器的状态
const last = target.children[dragIndex]
clearClass(last)
dragged.style.cursor = 'default'
e.target.parentNode.parentNode.title = moveTitle
}
// 开始拖拽
container.ondragstart = e => {
if (this.queryFileType === 'trash') {
e.preventDefault()
e.stopPropagation()
return false
}
// 正在选区获取按住关键键时禁止拖拽
if (_this.drawFlag || _this.checkCmdKey(e) || e.shiftKey) {
e.preventDefault()
e.stopPropagation()
return
}
// 判断被拖拽dom是否有slot属性并且等于'jmal'
if (!e.target.slot || e.target.slot !== 'jmal') {
return true
}
// 该文件正在重命名
if (e.target.rowIndex === _this.editingIndex) {
e.preventDefault()
e.stopPropagation()
return
}
e.target.style.cursor = 'no-drop'
// 复制被拖拽dom的title, 拖拽过程中移除, 拖拽完后还原
moveTitle = e.target.parentNode.parentNode.title
e.target.parentNode.parentNode.title = ''
// 创建拖拽时的dom, 克隆自被拖拽dom
_this.selectRowData.forEach((row, index) => {
const element = _this.dragElementList[row.index]
const rowIndex = element.rowIndex
dragingDiv = element.cloneNode(true)
dragingDiv.id = 'dragingDiv' + rowIndex
dragingDiv.classList.add('dragingDiv')
dragingDiv.classList.remove('el-table_row')
dragingDiv.style.transition = 'all 0.3s'
dragingDiv.style.zIndex = -1
dragingDiv.style.position = 'absolute'
const pos = _this.dragElementList[rowIndex]
dragingDiv.style.width = pos.w + 'px'
dragingDiv.style.height = pos.h + 'px'
dragingDiv.style.left = pos.x - drawOffsetLeft + 'px'
let dragingDivStyleTop = 0
if (!_this.grid) {
dragingDiv.firstChild.style.textAlign = 'center'
let tds = Array.prototype.slice.call(dragingDiv.childNodes)
tds.forEach((node, index) => {
if (index === 0) {
node.firstChild.style.margin = '0 20px'
}
if (index === 1) {
node.firstChild.style.width = '280px'
node.firstChild.style.marginRight = '20px'
}
if (index === 2) {
node.style.borderRadius = '0 12px 12px 0'
node.style.borderRight = 'solid 1px var(--apple-shadow-color)'
node.firstChild.style.height = '44px'
node.firstChild.style.lineHeight = '44px'
node.firstChild.style.width = '80px'
return true
}
if (index === 3) {
dragingDiv.removeChild(node)
}
})
dragingDivStyleTop = pos.y - _this.fileListScrollTop - 51.5
} else {
dragingDivStyleTop = pos.y - _this.fileListScrollTop - pos.h / 2 + 10
}
dragingDiv.style.top = dragingDivStyleTop + 'px'
if (index === 0) {
let numberFilesCopy = document
.getElementById('numberFiles')
.cloneNode(true)
numberFilesCopy.id = 'numberFilesCopy'
numberFilesCopy.querySelector('.number').innerHTML =
_this.selectRowData.length + '个文件'
dragingDiv.appendChild(numberFilesCopy)
}
dragingDiv.original = {
top: dragingDivStyleTop,
left: dragingDiv.style.left,
}
_this.initFileListScrollTop = _this.fileListScrollTop
draw.appendChild(dragingDiv)
})
firstOver = 0
let dragImage = document.getElementById('dragImage')
e.dataTransfer.setDragImage(dragImage, 10, 10)
_this.$store.dispatch('updateMessage', {
event: 'onDragStart',
data: true,
})
// 避免和画矩形选区冲突
_this.drawFlag = false
let rectangle = document.getElementById('rectangle1')
if (rectangle) {
document.getElementById('v-draw-rectangle').removeChild(rectangle)
}
dragged = e.target
draggedIndex = dragged.rowIndex
// 只有选中的才能拖拽
_this.cellMouseIndex = -1
dragBackCorlor = dragged.style.backgroundColor
if (!_this.grid) {
document
.getElementsByClassName('el-table--enable-row-hover')[0]
.classList.remove('el-table--enable-row-hover')
} else {
target.querySelectorAll('.grid-hover-back').forEach(e => {
e.classList.remove('grid-hover')
})
}
_this.draging = 1
}
container.ondragenter = function(e) {
clearTimeout(loop)
// 由于被拖动的元素 经过区域内中的每一元素都会触发该事件, 但是我们只需要它正在那一行上就行了
let throughRow = judgThroughDom(e, 'enter')
if (throughRow) {
if (dragIndex !== throughRow.rowIndex) {
if (dragIndex > -1) {
// 清除上次进入的容器的状态
const last = target.children[dragIndex]
clearClass(last)
}
// console.log('拖动进入目标元素'+throughRow.rowIndex,'dragIndex:',dragIndex);
// 不是自己或为文件夹时才改变状态
if (
draggedIndex !== throughRow.rowIndex &&
_this.fileList[throughRow.rowIndex].isFolder &&
_this.selectRowData.findIndex(
item => item.index === throughRow.rowIndex
) === -1
) {
// 改变本次进入的容器的状态
dragged.style.cursor = 'copy'
let numberFilesCopy = document.getElementById('numberFilesCopy')
numberFilesCopy.style.backgroundColor = '#40a9ffc9'
numberFilesCopy.querySelector('.number').style.display = 'none'
numberFilesCopy.querySelector('.icon').style.display = 'inline'
numberFilesCopy.querySelector('.operate').style.display = 'inline'
let targetFolder = numberFilesCopy.querySelector(
'.target .folder'
)
targetFolder.style.display = 'inline'
targetFolder.innerHTML = _this.fileList[throughRow.rowIndex].name
dragEnterBackCorlor = throughRow.style.backgroundColor
// 当拖拽文件夹上时,文件夹当背景色
const color = '#9fcdfc99'
if (_this.grid) {
throughRow.style.backgroundColor = color
} else {
throughRow.childNodes.forEach(
node => (node.style.backgroundColor = color)
)
}
}
dragIndex = throughRow.rowIndex
}
leaveIndex = -1
}
}
container.ondragover = function(e) {
_this.selectRowData.forEach((row, index) => {
const drawRectangle = document.getElementById(
'dragingDiv' + row.index
)
if (drawRectangle) {
drawRectangle.style.left =
e.clientX - drawOffsetLeft + index * 3 + 10 + 'px'
drawRectangle.style.top = e.clientY - 50 + index * 3 + 10 + 'px'
if (firstOver === 0) {
drawRectangle.style.zIndex = 999
setTimeout(() => {
drawRectangle.style.transition = ''
}, 300)
}
}
})
e.preventDefault()
leaveIndex = -1
firstOver++
}
let loop = null
let leaveIndex = -1 // 是否拖出了整个table, -1表示还在table内
container.ondragleave = function(e) {
clearTimeout(loop)
let throughRow = judgThroughDom(e, 'leave')
if (throughRow) {
if (!_this.grid) {
if (throughRow.rowIndex === 0 || throughRow.rowIndex === rows - 1) {
// 离开第一行或最后一行
leaveIndex = throughRow.rowIndex
loop = setTimeout(function() {
if (leaveIndex > -1) {
const leave = target.children[leaveIndex]
clearClass(leave)
dragIndex = -1
}
}, 100)
}
}
}
}
container.ondrop = function() {
const form = _this.fileList[draggedIndex]
const to = _this.fileList[dragIndex]
if (
form &&
to &&
form.id !== to.id &&
to.isFolder &&
!_this.selectRowData.includes(to)
) {
// 移动文件/文件夹
let forms = []
_this.selectRowData.forEach(row => {
forms.push(row.id)
})
_this
.$confirm(
`是否将选中的${_this.selectRowData.length}项移动到 ${to.name}?`,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'info',
}
)
.then(() => {
_this.checkCopyOrMoveApi('move', forms, to.id, to.name)
recoverDragDom(false)
})
.catch(() => {
recoverDragDom(true)
})
} else {
recoverDragDom(true)
}
}
// 清除之前的样式
let clearClass = function(node) {
if (!dragged) {
return
}
if (node) {
if (_this.grid) {
node = node.children[0].children[0]
// #9fcdfc99
if (node.style.backgroundColor === 'rgba(159, 205, 252, 0.6)') {
node.style.backgroundColor = null
}
} else {
// #9fcdfc99
if (
node.firstChild.style.backgroundColor ===
'rgba(159, 205, 252, 0.6)'
) {
node.childNodes.forEach(node => {
node.style.backgroundColor = null
})
}
}
dragged.style.cursor = 'default'
let numberFilesCopy = document.getElementById('numberFilesCopy')
if (numberFilesCopy) {
numberFilesCopy.style.backgroundColor = '#d2eefa66'
numberFilesCopy.querySelector('.number').style.display = 'inline'
numberFilesCopy.querySelector('.icon').style.display = 'none'
numberFilesCopy.querySelector('.operate').style.display = 'none'
numberFilesCopy.querySelector('.target .folder').style.display =
'none'
}
}
dragged.style.backgroundColor = dragBackCorlor
}
function getObjPos(obj) {
let pos = { x: 0, y: 0 }
while (obj) {
pos.x += obj.offsetLeft
pos.y += obj.offsetTop
obj = obj.offsetParent
}
return pos
}
},
tableBodyScroll(table, e) {
this.fileListScrollTop = e.target.scrollTop
let scrollBottom = e.target.scrollHeight - e.target.clientHeight - e.target.scrollTop
if (scrollBottom < (e.target.scrollHeight * 0.2)) {
if (!this.finished) {
if (!this.pageLoadCompleteList[this.pagination.pageIndex]) {
return
}
if (this.listModeSearch) {
if (this.listModeSearchOpenDir) {
this.searchFileAndOpenDir(this.listModeSearchOpenDir, true)
} else {
this.searchFile(this.searchFileName, true)
}
} else {
this.getFileListEnter(true)
}
}
}
},
}
}

View File

@ -0,0 +1,882 @@
import api from '@/api/file-api'
import fileConfig from '@/utils/file-config'
import path from 'path'
export default {
props: {
showUploadButton: {
type: Boolean,
default: true,
},
},
data() {
return {
newCreateFileDialog: false,
newCreateFileName: '',
newCreateFileDialogTitle: '',
createFileLoading: false,
renameFileName: '',
newFolderLoading: false,
renameLoading: false,
cellMouseIndex: -1,
editingIndex: -1,
titlePrefix: '',
unzipOperating: false,
dialogMoveOrCopyVisible: false,
fileTreeAndNewFolderDisabled: false,
directoryTreeData: [],
compressedFileVisible: false,
compressedFileData: [],
compressedFileName: '',
compressedFileTempDir: false,
selectTreeNode: {},
directoryTreeProps: {
label: 'name',
children: 'children',
isLeaf: 'isLeaf',
},
existsFileList: [], // 移动或复制存在的文件列表
copyOrMoveConfirmVisible: false, // 移动或复制警告弹出框
copyOrMoveParams: {
operating: 'copy',
froms: [],
to: '',
targetPath: ''
}, // 移动或复制要传递的参数
copyOrMoveToName: '', // 移动或复制到的文件夹名称
deleteConfirmVisible: false, // 删除确认弹窗
permanentDelete: false, // 是否永久删除
permanentDeleteDisable: false, // 是否禁用永久删除选项
}
},
methods: {
// 新建文件夹
newFolderNameClick() {
if (this.newFolderName) {
if (/[\[\]\/\\"<>\?\*]/gi.test(this.newFolderName)) {
this.$message({
message: '文件名不能包含以下字符:<,>,|,*,?,,/,[,]',
type: 'warning',
})
return
}
this.newFolderLoading = true
this.createFileLoading = true
api.uploadFolder({
isFolder: true,
filename: encodeURIComponent(this.newFolderName),
currentDirectory: this.getQueryPath(),
folder: this.$route.query.searchOpenFolder || this.$route.query.folder,
username: this.$store.state.user.name,
userId: this.$store.state.user.userId,
})
.then(res => {
if (res.data === 1) {
this.newFolderLoading = false
this.$message({
message: '该文件夹已存在',
type: 'warning',
})
} else {
this.createFileLoading = false
this.newCreateFileDialog = false
this.newFolderLoading = false
this.showNewFolder = false
this.isShowNewFolder = false
this.$notify({
title: '新建文件夹成功',
type: 'success',
duration: 1000,
})
this.getFileListEnter()
}
})
.catch(() => {
this.newFolderLoading = false
this.createFileLoading = false
})
} else {
this.newFolderLoading = false
this.$message({
message: '请输入文件夹名称',
type: 'warning',
})
}
},
// 新建文件
createFile(newFileName) {
if (this.newCreateFileDialogTitle === '新建文件夹') {
this.newFolderName = newFileName
this.newFolderNameClick()
return
}
if (newFileName) {
if (/[\[\]\/\\"<>\?\*]/gi.test(newFileName)) {
this.$message({
message: '文件名不能包含以下字符:<,>,|,*,?,,/,[,]',
type: 'warning',
})
return
}
this.createFileLoading = true
let parentPath = '/'
if (this.path) {
if (this.path.length > 0) {
parentPath = this.path
}
}
let suffix = newFileName.substring(newFileName.lastIndexOf('.') + 1)
api.addFile({
fileName: encodeURIComponent(newFileName),
isFolder: false,
folder: this.$route.query.searchOpenFolder || this.$route.query.folder,
username: this.$store.state.user.name,
parentPath: encodeURIComponent(parentPath),
})
.then(res => {
this.createFileLoading = false
switch (suffix && !this.$route.query.folder) {
case 'txt':
// 打开编辑器
this.textPreviewRow = res.data
this.textPreviewVisible = true
break
case 'drawio':
case 'mind':
case 'docx':
case 'xlsx':
case 'pptx':
this.iframePreviewRow = res.data
this.iframePreviewVisible = true
break
}
const that = this
this.setOnCreateFilename(newFileName)
setTimeout(function() {
that.newCreateFileDialog = false
}, 200)
})
.catch(() => {
this.createFileLoading = false
})
}
},
// 新建文件
createNewFile(suffix) {
if (this.newCreateFileDialogTitle === '新建文件夹') {
this.newCreateFileName = `新建文件夹`
} else {
this.newCreateFileName = `未命名文件.${suffix}`
}
this.newCreateFileName = this.getNewFileName(
this.fileList,
this.newCreateFileName
)
this.newCreateFileDialog = true
this.$nextTick(() => {
let newFileNameInput = this.$refs.newCreateFileName.$el.querySelector(
'.el-input__inner'
)
this.renameInputFocus(newFileNameInput, suffix)
})
},
newFolder() {
this.newFolderName = this.getNewFileName(this.fileList, '新建文件夹')
this.showNewFolder = true
this.$nextTick(() => {
this.$refs.newFolderName.focus()
this.$refs.newFolderName.select()
})
},
// 统计文件和文件夹
getShowSumFileAndFolder(fileList) {
let folderSize = 0
let fileSize = 0
fileList.forEach(fileInfo => {
if (fileInfo.isFolder) {
folderSize += 1
} else {
fileSize += 1
}
})
let folderSum = ''
if (folderSize > 0) {
folderSum = folderSize + '个文件夹'
}
let fileSum = ''
if (fileSize > 0) {
fileSum = fileSize + '个文件'
}
const stand = folderSize > 0 && fileSize > 0 ? '、' : ''
return folderSum + stand + fileSum
},
// 计算总大小
getShowSumSize(totalSize) {
let sizeSum = ''
if (totalSize < 1024) {
sizeSum += totalSize + 'B'
} else if (totalSize >= 1024 && totalSize < 1024 * 1024) {
sizeSum += (totalSize / 1024).toFixed(2) + 'K'
} else if (totalSize >= 1024 * 1024 && totalSize < 1024 * 1024 * 1024) {
sizeSum += (totalSize / (1024 * 1024)).toFixed(2) + 'M'
} else {
sizeSum += (totalSize / (1024 * 1024 * 1024)).toFixed(2) + 'G'
}
return sizeSum
},
// 重命名
rowRename(newFileName, row) {
//去掉回车换行
newFileName = newFileName.replace(/[\r\n]/g, '')
if (newFileName) {
if (/[\[\]\/\\"<>\?\*]/gi.test(newFileName)) {
this.$message({
message: '文件名不能包含以下字符:<,>,|,*,?,,/,[,]',
type: 'warning',
})
return
}
let strFileName = newFileName.replace(/(.*\/)*([^.]+).*/gi, '$2')
let newExt = newFileName.replace(/.+\./, '')
if (!row.isFolder) {
if (row.suffix !== newExt) {
this.$confirm(
`您确定要将扩展名“.${row.suffix}”更改为“.${newExt}”吗?`,
'提示',
{
type: 'warning',
showClose: false,
closeOnClickModal: false,
confirmButtonText: `保持.${row.suffix}`,
cancelButtonText: `使用.${newExt}`,
}
).then(() => {
const finalFileName = strFileName + '.' + row.suffix
this.rename(row, finalFileName)
}).catch(() => {
this.rename(row, newFileName)
})
} else {
this.rename(row, newFileName)
}
} else {
this.rename(row, newFileName)
}
} else {
this.editingIndex = -1
}
},
rename(row, newFileName) {
if (row.name === newFileName) {
this.editingIndex = -1
return
}
this.renameLoading = true
const findIndex = this.fileList.findIndex(item => {
if (newFileName === item.name) {
return item
}
})
if (findIndex > -1) {
let msg = '该文件已存在'
if (row.isFolder) {
msg = '该文件夹已存在'
}
this.$message({
message: msg,
type: 'warning',
})
this.renameLoading = false
return
}
api.rename({
newFileName: encodeURIComponent(newFileName),
username: this.$store.state.user.name,
folder: this.$route.query.searchOpenFolder || this.$route.query.folder,
id: row.id,
}).then(() => {
this.renameLoading = false
row.name = newFileName
row.suffix = newFileName.replace(/.+\./, '')
this.fileList[row.index] = row
this.editingIndex = -1
}).then(() => {
this.fileListTableClearSelection()
this.setOnCreateFilename(newFileName)
}).catch(() => {
this.renameLoading = false
this.editingIndex = -1
})
},
// 选取输入框部分内容
renameInputFocus(doc, suffix) {
this.setInputFocus()
doc.focus()
doc.selectionStart = 0
doc.selectionEnd = doc.value.length
if (suffix) {
doc.selectionEnd -= suffix.length + 1
}
},
setOnCreateFilename(newFileName) {
if (this.$route.query.folder) {
this.onCreateFilename = newFileName
this.getFileListEnter()
this.clearOnCreateFilename()
}
},
removeOperation() {
this.permanentDelete = false
this.selectFileList = this.getSelectFileList()
this.deleteConfirmVisible = true
},
onCopy(fileIdList, targetPath, targetFolder) {
this.checkCopyOrMoveApi('copy', fileIdList, targetFolder, undefined, targetPath)
},
onMove(fileIdList, targetPath, targetFolder) {
this.checkCopyOrMoveApi('move', fileIdList, targetFolder, undefined, targetPath)
},
copyOperation() {
this.$store.dispatch('updateFileClipboard', this.getSelectFileList())
},
// 加载下一级文件树
directoryTreeLoadNode(node, resolve) {
let fileId = null
if (node.level === 0) {
const that = this
setTimeout(function() {
that.$refs.directoryTree.setCurrentKey('0')
}, 0)
return resolve([{ id: '0', name: '根目录' }])
}
if (node.level > 1) {
fileId = node.data.mountFileId || node.data.id
}
api.queryFileTree({
userId: this.$store.state.user.userId,
username: this.$store.state.user.name,
fileId: fileId,
}).then(res => {
const nextNodes = res.data
return resolve(nextNodes)
})
},
// 点击文件树
treeNodeClick(row, node, event) {
this.fileTreeAndNewFolderDisabled = row.hasOwnProperty('newFolder')
this.selectTreeNode = row
this.selectTreeNode.showName = ' "' + row.name + '"'
},
// 节点被展开时触发
treeNodeExpand(row, node, event) {},
// 文件树里新建文件夹
fileTreeAndNewFolder() {
let newNodeId = 'newFolderNodeKey'
let node = this.$refs.directoryTree.getNode(newNodeId)
if (node !== null) {
this.$refs.directoryTree.remove(node)
}
let childNodes = this.$refs.directoryTree.store.currentNode.childNodes
let newFolderName = this.getNewFileName(childNodes, '新建文件夹')
let newNode = {
id: newNodeId,
newFolder: true,
name: newFolderName,
showName: newFolderName,
isLeaf: true,
}
this.$refs.directoryTree.append(newNode, this.selectTreeNode)
setTimeout(function() {
let treeInput = document.getElementById('treeInput')
if (treeInput) {
treeInput.value = newFolderName
treeInput.focus()
treeInput.select()
}
}, 100)
},
// 移动文件
moveFileTree() {
this.copyOrMove('move')
},
// 复制文件
copyFileTree() {
this.copyOrMove('copy')
},
// 解压文件
confirmUnzip() {
this.unzip(this.openingFile, this.selectTreeNode.id, false)
},
showDialogMoveOrCopyVisible() {
this.dialogMoveOrCopyVisible = true
},
/**
* 创建副本
*/
duplicate() {
const newFilename = this.getDuplicateFileName(
this.fileList,
this.rowContextData.name
)
api
.duplicateFile({
fileId: this.rowContextData.id,
newFilename: newFilename,
})
.then(() => {
this.$message.success('创建副本成功')
this.setOnCreateFilename(newFilename)
})
},
moveOrCopy() {
this.showDialogMoveOrCopyVisible()
this.titlePrefix = '移动或复制到: '
this.unzipOperating = false
const that = this
setTimeout(function() {
that.selectTreeNode = that.$refs.directoryTree.getCurrentNode()
that.selectTreeNode.showName = ' "' + that.selectTreeNode.name + '"'
}, 100)
},
copyOrMove(operating) {
let operation = '复制'
if (operating === 'move') {
operation = '移动'
}
let selectNodePath = '/'
if (this.selectTreeNode.path) {
selectNodePath =
this.selectTreeNode.path + this.selectTreeNode.name + '/'
}
let fileIds = []
if (this.menusIsMultiple || this.selectRowData.length > 1) {
const exits = this.$refs.fileListTable.tableSelectData.some(value => {
fileIds.push(value.id)
const thisParentPath = value.path
if (thisParentPath === selectNodePath) {
this.$message({
message: '不能将文件' + operation + '到自身或其子目录下',
type: 'warning',
})
return true
}
})
if (exits) {
return
}
} else {
if (this.rowContextData.id) {
fileIds.push(this.rowContextData.id)
} else {
fileIds.push(this.rowContextData[0].id)
}
}
this.checkCopyOrMoveApi(operating, fileIds, this.selectTreeNode.id, this.selectTreeNode.name)
},
checkCopyOrMoveApi(operating, froms, to, toName, targetPath) {
if (!to && !targetPath) {
to = '0'
toName = '根目录'
}
if (targetPath) {
toName = path.basename(targetPath)
}
api['checkMoveOrCopy']({
userId: this.$store.state.user.userId,
username: this.$store.state.user.name,
froms: froms,
to: to,
targetPath: targetPath
}).then((res) => {
if (res.count > 0) {
this.copyOrMoveConfirmVisible = true
this.copyOrMoveParams = {operating: operating, froms: froms, to: to, targetPath: targetPath}
this.copyOrMoveToName = `${(operating === 'copy' ? '复制' : '移动')}到: "${toName}"`
this.existsFileList = res.data
} else {
this.copyOrMoveApi(operating, froms, to, false, targetPath)
}
})
},
copyOrMoveApi(operating, froms, to, isOverride, targetPath) {
this.copyOrMoveConfirmVisible = false
let operation = '复制'
if (operating === 'move') {
operation = '移动'
}
let copying = this.$message({
iconClass: 'el-icon-loading',
type: 'info',
duration: 0,
dangerouslyUseHTMLString: true,
message: operation + '中...',
})
this.dialogMoveOrCopyVisible = false
api[operating]({
userId: this.$store.state.user.userId,
username: this.$store.state.user.name,
froms: froms,
to: to,
targetPath: targetPath,
isOverride: isOverride
}).then(() => {
this.$refs.fileClipboard.clear()
copying.iconClass = null
copying.type = 'success'
copying.message = operation + '中...'
if (this.rowContextData.isFolder) {
this.$refs.directoryTree.append(this.rowContextData, to)
}
if (operating === 'move') {
// 移除列表
this.getFileListEnter()
}
setTimeout(function() {
copying.close()
}, 1000)
}).catch(() => {
copying.close()
})
},
renderContent(h, { node, data, store }) {
if (data.newFolder) {
return (
<span class="custom-tree-node">
<span>
<svg-icon icon-class="folder" />
</span>
<span>
<div class="el-input el-input--mini el-input-tree">
<input
type="text"
autocomplete="on"
value="新建文件夹"
id="treeInput"
class="el-input__inner"
/>
</div>
<button
type="button"
on-click={() => {
let path = '/'
let parentData = node.parent.data
if (parentData.path) {
path = parentData.path + parentData.name + path
}
let newFolderName = document.getElementById('treeInput').value
api
.newFolder({
isFolder: true,
filename: encodeURIComponent(newFolderName),
currentDirectory: this.getQueryPath(),
folder: this.$route.query.searchOpenFolder || this.$route.query.folder,
username: this.$store.state.user.name,
userId: this.$store.state.user.userId,
})
.then(res => {
data.newFolder = false
data.name = newFolderName
data.id = res.data
})
.catch(() => {
window.event.preventDefault()
window.event.stopPropagation()
})
}}
class="el-button el-icon-check el-button--mini el-input-tree-button"
element-loading-spinner="el-icon-loading"
element-loading-background="#f6f7fa88"
/>
<button
type="button"
on-click={() => {
this.$refs.directoryTree.remove(node)
window.event.preventDefault()
window.event.stopPropagation()
}}
class="el-button el-icon-close el-button--mini el-input-tree-button"
element-loading-spinner="el-icon-loading"
element-loading-background="#f6f7fa88"
/>
</span>
</span>
)
}
if (node.expanded) {
return (
<span class="custom-tree-node">
<svg-icon icon-class="open-folder" />
<span style="margin-left: 5px;">{node.label}</span>
<span />
</span>
)
} else {
return (
<span class="custom-tree-node">
<svg-icon icon-class="folder" />
<span style="margin-left: 5px;">{node.label}</span>
<span />
</span>
)
}
},
allocateTag() {
// 将this.$refs.fileListTable.tableSelectData拷贝给tagDialogObjectList而不是赋值
this.tagDialogObjectList = JSON.parse(
JSON.stringify(this.$refs.fileListTable.tableSelectData)
)
this.tagDialogVisible = true
},
allocateTagSuccess() {
this.getFileListEnter()
},
share(row) {
if (!row || !row.id) {
if (this.rowContextData.id) {
row = this.rowContextData
} else {
row = this.$refs.fileListTable.tableSelectData[0]
}
}
this.shareDialogObject = row
this.shareDialogObject.fileId = row.id
this.shareDialogObject.shareBase = row.shareBase
this.shareDialogObject.subShare = row.subShare
this.shareDialogVisible = true
},
shareSuccess(shareBase, subShare) {
this.updateRowContextData()
this.rowContextData.shareBase = shareBase
this.rowContextData.subShare = subShare
this.rowContextData.isShare = true
},
onCancelShare() {
this.updateRowContextData()
delete this.rowContextData.shareBase
delete this.rowContextData.subShare
delete this.rowContextData.isShare
},
updateRowContextData() {
// 这3行代码是为了让vue刷新数据
const isFavorite = this.rowContextData.isFavorite
this.rowContextData.isFavorite = !isFavorite
this.rowContextData.isFavorite = isFavorite
},
downloadFile() {
let fileIds = []
if (this.$refs.fileListTable.tableSelectData.length > 0) {
this.$refs.fileListTable.tableSelectData.forEach(value => {
fileIds.push(value.id)
})
} else {
fileIds.push(this.rowContextData.id)
}
if (fileIds.length > 1 || this.rowContextData.isFolder) {
fileConfig.packageDownload(fileIds)
return
}
fileConfig.download(
this.$store.state.user.name,
this.rowContextData,
this.$store.getters.token
)
},
// 收藏/取消收藏
favoriteOperating(isFavorite) {
const fileIds = this.getSelectIdList()
this.rowContextData.isFavorite = isFavorite
this.highlightFavorite(isFavorite, true)
api
.favoriteUrl({
fileIds: fileIds,
isFavorite: isFavorite,
})
.then(() => {
// 收藏页面
const homePage =
!this.$route.query.path || this.$route.query.length <= 1
if (!isFavorite && this.isCollectView && homePage) {
// 移除列表
this.removeSelectItem()
}
})
.catch(() => {
this.rowContextData.isFavorite = !isFavorite
})
},
// 移动至回收站
moveToRecycle() {
this.permanentDelete = false
this.deleteFile()
},
// 彻底删除
sweepDeleteFile() {
this.permanentDelete = true
this.deleteFile()
},
// 删除
deleteFile() {
// 提取出selectFileList中的id
const fileIds = this.selectFileList.map(item => item.id)
this.deleteLoading = true
api.delete({
currentDirectory: this.getQueryPath(),
username: this.$store.state.user.name,
fileIds: fileIds,
sweep: this.permanentDelete,
}).then(() => {
this.deleteLoading = false
this.deleteConfirmVisible = false
// 刷新列表
if (this.$route.query.folder) {
this.getFileListEnter()
}
}).catch(() => {
this.deleteLoading = false
this.deleteConfirmVisible = false
})
},
restoreFile() {
const fileIds = this.getSelectIdList()
api.restore({fileIds: fileIds}).then()
},
sweepFile() {
const fileIds = this.getSelectIdList()
this.$confirm(`此操作将永久删除 ${fileIds.length}个文件, 是否继续?`, '提示', {
confirmButtonText: '彻底删除',
confirmButtonClass: 'el-button--danger',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
api.sweep({fileIds: fileIds}).then()
})
},
clearTrash() {
this.$confirm(`是否清空回收站? 此操作将无法还原!!!`, '提示', {
confirmButtonText: '清空',
confirmButtonClass: 'el-button--danger',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
api.clearTrash().then()
})
},
// 移除选中项
removeSelectItem() {
let removeFileIndexList = []
if (this.$refs.fileListTable.tableSelectData.length > 0) {
this.$refs.fileListTable.tableSelectData.forEach(item => {
let fileIndex = this.fileList.findIndex(file => file.id === item.id)
if (fileIndex > -1) {
removeFileIndexList.push(fileIndex)
}
})
}
// 先清空之前选择的数据
this.$refs.fileListTable.doLayout()
this.fileListTableClearSelection()
this.$refs.fileListTable.tableSelectData = []
// 倒序
removeFileIndexList = removeFileIndexList.sort((a, b) => b - a)
setTimeout(() => {
// 再执行移除
for (let i = 0; i < removeFileIndexList.length; i++) {
this.fileList.splice(removeFileIndexList[i], 1)
}
this.fileList.map((item, index) => {
item.index = index
})
// 改变拖拽目标
this.rowDrop()
// 画矩形选取
this.drawRectangle()
}, 300)
},
// 获取选中项id列表
getSelectIdList() {
const fileIds = []
if (this.selectRowData.length > 1 || this.menusIsMultiple) {
this.$refs.fileListTable.tableSelectData.forEach(value => {
fileIds.push(value.id)
})
} else {
fileIds.push(this.rowContextData.id)
}
return fileIds
},
getSelectFileList() {
this.permanentDeleteDisable = false;
let selectedSource = [];
if (this.selectRowData.length > 1 || this.menusIsMultiple) {
selectedSource = this.$refs.fileListTable.tableSelectData;
} else if (this.rowContextData) {
selectedSource = [this.rowContextData];
}
if (selectedSource.length === 0) {
return [];
}
const fileList = selectedSource.map(({ id, suffix, name, mountFileId, contentType, isFolder, music, video }) => {
return { id, suffix, name, mountFileId, contentType, isFolder, music, video };
});
if (fileList.length > 0) {
this.checkPermanentDelete(fileList[0]);
}
return fileList;
},
checkPermanentDelete(file) {
if (/\//.test(file.id) || file.mountFileId) {
this.permanentDeleteDisable = true
this.permanentDelete = true
}
},
// 解压文件
unzip(file, destFileId, tempDir) {
let status = '解压'
let decompressing = this.$message({
iconClass: 'el-icon-loading',
type: 'info',
duration: 0,
dangerouslyUseHTMLString: true,
message: '<span>&nbsp;&nbsp;正在' + status + '</span>',
})
api.unzip({
fileId: file.id,
destFileId: destFileId,
})
.then(res => {
decompressing.iconClass = null
decompressing.type = 'success'
decompressing.message = status + '成功'
if (tempDir) {
this.compressedFileData = res.data
this.compressedFileVisible = true
this.compressedFileName = file.name
this.compressedFileTempDir = tempDir
}
const that = this
setTimeout(function() {
decompressing.close()
that.openCompressionVisible = false
if (file.id === destFileId) {
that.getFileListEnter()
}
if (destFileId && file.id !== destFileId && !tempDir) {
that.dialogMoveOrCopyVisible = false
}
}, 1000)
})
.catch(() => {
decompressing.close()
})
},
},
}

View File

@ -0,0 +1,477 @@
import api from '@/api/file-api'
import _ from 'lodash'
export default {
data() {
return {
debounceGetFileList: null,// 获取文件列表防抖
fileList: [],
pageLoadCompleteList: [],
pagination: {
pageIndex: 1,
pageSize: 50,
total: 0,
pageSizes: [10, 20, 30, 40, 50],
},
clientHeight: 500,
tableHeadNameWidth: 500,
// 表头数据
tableHead: [
{
name: '',
label: '',
index: 0,
},
{
name: '',
label: '',
index: 1,
},
{
name: 'name',
label: '名称',
sortable: true,
index: 2,
},
{
name: 'size',
label: '大小',
sortable: true,
index: 3,
},
{
name: 'updateDate',
label: '修改日期',
sortable: true,
index: 4,
},
],
tableLoading: true,
finished: false,
grid: this.defaultGrid,
vmode: this.defaultGrid ? 'grid' : 'list',
gridColumnNum: -1,
gridPaddingRight: 0,
gridItemWidth: 125,
gridMinHeight: 125,
onCreateFilename: '',
}
},
created() {
this.debounceGetFileList = _.debounce((onLoad) => {
this.getFileList(onLoad)
}, 200)
},
mounted() {
// 加载布局
if (this.$route.query.vmode) {
this.vmode = this.$route.query.vmode
} else {
if (this.defaultGrid) {
this.vmode = 'grid'
} else {
this.vmode = 'list'
}
}
if (this.vmode === 'list') {
this.grid = false
if (!this.selectFile) {
this.lessClientHeight = 140
}
} else {
this.grid = true
if (!this.selectFile) {
this.lessClientHeight = 106
}
this.containerResize()
}
// 加载url上的path
if (this.$route.query.path !== '/') {
const path = decodeURI(this.$route.query.path)
this.pathList.splice(1, 1)
path.split('/').forEach((pathName, index) => {
if (index > 0) {
const item = {}
item['folder'] = pathName
this.pathList.push(item)
}
})
}
if (this.$route.query.searchOpenFolder && this.path && !this.$route.query.keyword) {
localStorage.setItem(this.path, this.$route.query.searchOpenFolder)
}
if (this.$route.query.highlight) {
this.onCreateFilename = this.$route.query.highlight
this.clearOnCreateFilename()
}
// remove query.basePath
if (this.$route.query.basePath) {
this.basePath = this.$route.query.basePath
}
// remove query.folder
if (this.$route.query.searchOpenFolder && this.$route.query.keyword) {
const query = { ...this.$route.query }
delete query.searchOpenFolder
this.$router.replace({ query })
}
if (this.$route.query.keyword) {
this.filterOption = localStorage.getItem('searchFilterOption') ? JSON.parse(localStorage.getItem('searchFilterOption')) : {}
this.$refs.searchOption.setFilterOption(this.filterOption)
}
let that = this
window.onresize = function() {
that.clientHeight = document.documentElement.clientHeight - that.lessClientHeight
}
setTimeout(() => {
if (!this.getFileListed) {
this.getFileListEnter()
}
}, 50)
},
methods: {
onmessage(msg) {
let fileDoc = msg.body
const url = msg.url
let index = this.fileList.findIndex(file => file.id === fileDoc.id)
if ('updateFile' === url) {
if (index > -1) {
if (fileDoc.m3u8) {
this.fileList[index].m3u8 = fileDoc.m3u8
}
this.fileList[index].size = fileDoc.size
this.fileList[index].agoTime = 1
this.fileList[index].updateDate = fileDoc.updateDate
}
}
let thisPath = this.path
if (this.listModeSearchOpenDir) {
const row = this.pathList[this.pathList.length - 1]
thisPath = `${row.row.path}${row.folder}`
}
const isCurrentPath = (thisPath + '/') === fileDoc
if ('deleteFile' === url && (isCurrentPath || this.$route.path.startsWith('/recently'))) {
this.getFileListEnter()
}
if ('createFile' === url) {
if (fileDoc.$set) {
this.onCreateFilename = fileDoc.$set.name
}
if (fileDoc && fileDoc.name) {
this.onCreateFilename = fileDoc.name
}
if (!thisPath) {
thisPath = ''
}
if (fileDoc) {
if (fileDoc.$set) {
let path = fileDoc.$set.path
path = path.replace(/\\/g, '/')
if (thisPath + '/' === path) {
this.getFileListEnter()
}
} else {
if (thisPath + '/' === fileDoc.path) {
this.getFileListEnter()
}
}
}
this.clearOnCreateFilename()
}
if (this.$route.path.startsWith('/trash') && msg.url === 'operationTips') {
this.getFileListEnter()
}
},
// 延时清空onCreateFilename
clearOnCreateFilename() {
setTimeout(() => {
this.onCreateFilename = ''
}, 2000)
},
gridItemClick(row, event) {
if (this.selectFile) {
this.fileClick(row, event)
}
if (this.checkCmdKey(event)) {
this.pinSelect(row, event)
this.$refs.fileListTable.toggleRowSelection([{ row: row }])
return
}
this.pinSelect(row, event)
},
containerResize() {
// gird视图
const container = document.querySelector('.dashboard-container')
let clientWidth = container.clientWidth
this.clientHeight = document.documentElement.clientHeight - this.lessClientHeight
this.gridItemWidth = 125
if (this.queryFileType === 'image') {
this.gridItemWidth = 165
this.gridColumnNum = Math.trunc((clientWidth - 10) / this.gridItemWidth)
} else {
this.gridColumnNum = Math.trunc((clientWidth - 10) / this.gridItemWidth)
}
const gridWidth = (clientWidth - 10) / this.gridColumnNum
this.gridPaddingRight = gridWidth - this.gridItemWidth + 10
this.showUpdateDateItem = clientWidth >= 900;
this.showSizeItem = clientWidth >= 500;
let gridRowNum = Math.round(
this.clientHeight / (clientWidth / this.gridColumnNum)
)
if (this.finished && this.pagination.pageIndex === 1) {
const actualRows = Math.ceil(this.fileList.length / this.gridColumnNum)
const gridMinHeight = actualRows * this.gridItemWidth + (actualRows - 1) * 10
if (gridMinHeight < this.clientHeight) {
this.gridMinHeight = gridMinHeight
}
}
const lastPageSize = this.pagination.pageSize
const thisPageSize = gridRowNum * this.gridColumnNum + this.gridColumnNum
if (thisPageSize !== lastPageSize) {
this.pagination.pageSize = thisPageSize
if (this.$route.query.tagId) {
this.queryCondition.tagId = this.$route.query.tagId
}
this.getFileListEnter()
}
this.$nextTick(() => {
// list 视图
const fileListTableWidth = this.$refs.fileListTableContainer.offsetWidth
this.tableHeadNameWidth = fileListTableWidth - (80 + 200 + 250) - 90
})
// 使列表可拖拽
this.rowDrop()
this.drawRectangle()
},
// 切换布局
changeVmode() {
this.grid = !this.grid
this.vmode = 'list'
if (this.grid) {
this.vmode = 'grid'
this.lessClientHeight = 106
} else {
this.lessClientHeight = 140
this.$refs.fileListTable.setHeight()
}
this.clientHeight =
document.documentElement.clientHeight - this.lessClientHeight
if (!this.path) {
this.path = ''
}
this.editingIndex = -1
const queryTagId = this.$route.query.tagId ? `&tagId=${this.$route.query.tagId}` : ''
const basePath = this.getBasePath().replace(/#/g, '%23')
const keyword = this.$route.query.keyword ? `&keyword=${this.$route.query.keyword}` : ''
const folder = this.$route.query.folder ? `&folder=${this.$route.query.folder}` : ''
const searchOpenFolder = this.$route.query.searchOpenFolder ? `&searchOpenFolder=${this.$route.query.searchOpenFolder}` : ''
this.$router.push(`?vmode=${this.vmode}&path=${this.path.replace(/#/g, '%23')}${folder}${queryTagId}${basePath}${keyword}${searchOpenFolder}`)
// 改变拖拽目标
this.rowDrop()
// 画矩形选取
this.drawRectangle()
this.loadContextMenus()
// 使列表滑到顶部
if (!this.grid) {
if (this.fileListScrollTop > 0) {
this.$refs.fileListTable.pagingScrollTopLeft()
}
}
this.fileListScrollTop = 0
},
// 请求之前的准备
beforeLoadData(onLoad) {
if (onLoad) {
this.pagination.pageIndex++
} else {
this.pagination.pageIndex = 1
}
this.currentDirectory = decodeURIComponent(this.getQueryPath())
this.pageLoadCompleteList[this.pagination.pageIndex] = false
this.tableLoading = true
this.finished = false
},
// 填充数据
loadData(res, onLoad) {
if (!this.$refs.fileListTable) {
return
}
if (onLoad) {
res.data.forEach((file, number) => {
file['index'] =
(this.pagination.pageIndex - 1) * this.pagination.pageSize + number
this.fileList.push(file)
})
} else {
this.fileList = res.data
this.fileList.map((item, index) => {
item.index = index
})
this.$refs.fileListTable.reloadData(this.fileList)
setTimeout(() => {
if (this.$refs.fileListTable) {
this.$refs.fileListTable.reloadData(this.fileList)
}
}, 0)
}
// 数据全部加载完成
if (this.fileList.length >= res.count) {
this.finished = true
}
this.tableLoading = false
this.clientHeight =
document.documentElement.clientHeight - this.lessClientHeight
this.listModeSearch = false
this.pagination['total'] = res.count
this.$nextTick(() => {
this.containerResize()
this.tableLoading = false
this.pageLoadCompleteList[this.pagination.pageIndex] = true
})
// 加载菜单状态
this.loadContextMenus()
// 高亮新增的文件
this.highlightNewFile()
// 设置挂载文件的用户名(文件的所有者)
this.fileUsername = ''
this.setMountFileOwner(res.props)
const path = this.$route.query.path ? this.$route.query.path : '/'
const basePath = this.$route.query.basePath
? this.$route.query.basePath
: '/'
this.path = basePath + path
this.path = this.path.replace(/\\/g, '/')
this.path = this.path.replace(/\/\//g, '/')
if (this.path === '/') {
this.path = ''
}
},
// 设置挂载文件的用户名(文件的所有者)
setMountFileOwner(props) {
localStorage.removeItem('mountFileOwner')
if (this.$route.query.folder) {
if (props && props.fileUsername && props.fileUsername !== this.$store.getters.name) {
this.fileUsername = props.fileUsername
localStorage.setItem('mountFileOwner', props.fileUsername)
}
}
},
// 高亮新增文件
highlightNewFile() {
if (this.onCreateFilename) {
let index = this.fileList.findIndex(
item => item.name === this.onCreateFilename
)
if (index > -1) {
let row = this.fileList[index]
setTimeout(() => {
this.fileListTableClearSelection()
this.$refs.fileListTable.toggleRowSelection([
{ row: row, selected: true },
])
}, 0)
}
}
},
getFileListEnter(onLoad) {
this.debounceGetFileList(onLoad)
},
getFileList(onLoad) {
if (this.$route.query.keyword) {
if (this.$route.query.keyword !== 'undefined') {
this.searchFileName = this.$route.query.keyword
}
this.searchInputBlur()
const searchPathIndex = this.pathList.findIndex(item => item.search)
if (this.$route.query.searchOpenFolder && searchPathIndex > -1) {
this.searchFileAndOpenDir(this.$route.query.searchOpenFolder, onLoad)
} else {
this.searchFile(this.searchFileName)
}
} else {
this.searchFileName = ''
this.getFileListed = true
this.beforeLoadData(onLoad)
api.fileList({
userId: this.$store.state.user.userId,
username: this.$store.state.user.name,
currentDirectory: this.getQueryPath(),
folder: this.$route.query.folder,
queryFileType: this.queryFileType,
sortableProp: this.sortable.prop,
order: this.sortable.order,
isFolder: this.queryCondition.isFolder,
isFavorite: this.queryCondition.isFavorite,
isMount: this.queryCondition.isMount,
isTrash: this.queryCondition.isTrash,
tagId: this.queryCondition.tagId,
queryCondition: this.queryCondition,
pageIndex: this.pagination.pageIndex,
pageSize: this.pagination.pageSize,
})
.then(res => {
this.loadData(res, onLoad)
}).catch(() => {
this.tableLoading = false
})
}
},
pinSelect(row, event) {
if (event.shiftKey && this.selectOrigin > -1) {
const origin = this.selectOrigin
this.selectEnd = row.index
let diff = this.selectEnd - origin
// 先清除选中
this.fileListTableClearSelection()
if (diff === 0) {
this.selectOrigin = -1
}
if (diff > 0) {
for (let i = origin; i <= this.selectEnd; i++) {
this.$refs.fileListTable.toggleRowSelection([{ row: this.fileList[i], selected: true }])
}
}
if (diff < 0) {
for (let i = this.selectEnd; i <= origin; i++) {
this.$refs.fileListTable.toggleRowSelection([{ row: this.fileList[i], selected: true }])
}
}
}
this.changeSelectedStyle(this.$refs.fileListTable.tableSelectData)
},
//双击
dblclick(row, column, cell, event) {
this.fileClick(row, event)
},
// 单元格点击事件
cellClick(row, column, cell, event) {
if (this.selectFile) {
this.fileClick(row, event)
return
}
clearTimeout(this.Loop)
if (this.editingIndex === -1) {
const columnIndex = column.index
if (columnIndex === 2) {
if (this.selectRowData.length < 1) {
if (row.index !== this.editingIndex) {
this.editingIndex = -1
}
}
}
if (this.checkCmdKey(event)) {
this.pinSelect(row, event)
this.$refs.fileListTable.toggleRowSelection([{ row: row }])
return
}
this.pinSelect(row, event)
}
},
}
}

View File

@ -0,0 +1,56 @@
import { formatExif, formatVideo } from '@/utils/media'
export default {
methods: {
extendedInfo(file) {
if (!file.exif && !file.video) {
return ''
}
return formatExif(file.exif, '<br>') + formatVideo(file.video, '<br>')
},
tooltipConfig(item) {
// 使用模板字符串构建更清晰的 HTML
const contentHTML = `
<div class="apple-tooltip-content">
<div class="tooltip-row">
<span class="tooltip-label">大小:</span>
<span class="tooltip-value">${this.formatSize(item.size)}</span>
</div>
${item.w && item.h ? `
<div class="tooltip-row">
<span class="tooltip-label">尺寸:</span>
<span class="tooltip-value">${item.w}×${item.h}</span>
</div>` : ''}
<div class="tooltip-row">
<span class="tooltip-label">名称:</span>
<span class="tooltip-value tooltip-value-name">${item.name}</span>
</div>
<div class="tooltip-row">
<span class="tooltip-label">上传时间:</span>
<span class="tooltip-value tooltip-value-date">${item.uploadDate}</span>
</div>
<div class="tooltip-row">
<span class="tooltip-label">修改时间:</span>
<span class="tooltip-value tooltip-value-date">${item.updateDate}</span>
</div>
<div class="tooltip-row">
<span class="tooltip-label">路径:</span>
<span class="tooltip-value tooltip-value-path">${item.path}</span>
</div>
${this.extendedInfo(item)}
</div>
`;
return {
content: contentHTML,
// trigger: 'click',
delay: 0,
placement: 'left',
allowHTML: true,
theme: 'light',
// appendTo: 'parent',
arrow: true
};
}
}
}

View File

@ -0,0 +1,159 @@
export default {
computed: {
gridFilename() {
// 优化文件名,如果文件名过长,则进行截取
return function(item) {
let filename = item.name;
// 如果是文件夹,直接返回文件夹名
if (item.isFolder || !this.grid) {
return filename;
}
const verticalImage = item.w && item.h && (item.w / item.h < 1.6)
const verticalVideo = item.video && (item.video.width / item.video.height < 1.6)
const singleLine = verticalImage || verticalVideo || item.showCover
const gridFilenameLength = singleLine ? 13 : 28
// 分离文件名和后缀
let parts = filename.split('.');
let suffix = parts.length > 1 ? parts.pop() : '';
let base = parts.join('.');
// 获取文件名的有效长度
let effectiveLength = this.getEffectiveLength(base, suffix);
// 如果有效长度小于或等于规定的长度,则直接返回文件名
if (effectiveLength <= gridFilenameLength) {
return filename;
}
// 根据是否有后缀来确定需要截取的长度
let sliceLength = gridFilenameLength - (suffix ? suffix.length + 2 : 1); // +2 是为了“…”和分隔符
let prev = '';
let currentLength = 0;
// 截取字符串,确保不会在中文字符中间断开
for (let char of Array.from(base)) {
let charLength = this.getCharLength(char);
if (currentLength + charLength > sliceLength) {
break;
}
currentLength += charLength;
prev += char;
}
// 根据是否有后缀返回相应的格式
if (suffix) {
return prev + '…' + suffix;
}
return prev + '…';
};
},
getSummaries3() {
let totalSize = 0
this.fileList.forEach(file => {
totalSize += file.size
})
return totalSize > 0
? this.fileList.length + '项 ' + this.getShowSumSize(totalSize)
: ''
},
},
methods: {
// 获取字符的长度。中文字符长度为2,其他字符长度为1
getCharLength(char) {
// 优化的字符长度计算函数
// 使用 Unicode 范围来判断字符宽度,更准确且性能更好
if (!char) return 0; // 处理空字符或 undefined
const code = char.codePointAt(0);
// 全角字符范围判断(长度为2)
if (
// 中日韩统一表意文字 (CJK Unified Ideographs)
(code >= 0x4E00 && code <= 0x9FFF) ||
// 中日韩扩展A区
(code >= 0x3400 && code <= 0x4DBF) ||
// 中日韩符号和标点
(code >= 0x3000 && code <= 0x303F) ||
// 全角ASCII、全角标点
(code >= 0xFF00 && code <= 0xFFEF) ||
// 平假名
(code >= 0x3040 && code <= 0x309F) ||
// 片假名
(code >= 0x30A0 && code <= 0x30FF) ||
// 韩文字母
(code >= 0xAC00 && code <= 0xD7AF) ||
// 其他可能的宽字符
(code >= 0x1100 && code <= 0x11FF) || // 韩文字母扩展
(code >= 0x2E80 && code <= 0x2EFF) || // 中日韩部首补充
(code >= 0x2F00 && code <= 0x2FDF) || // 康熙部首
(code >= 0x31C0 && code <= 0x31EF) || // 中日韩笔画
(code >= 0xF900 && code <= 0xFAFF) || // 中日韩兼容表意文字
(code >= 0xFE30 && code <= 0xFE4F) || // 中日韩兼容形式
// 表情符号和其他宽字符
(code >= 0x1F300 && code <= 0x1F9FF) ||
// 全角空格
code === 0x3000
) {
return 2;
}
// 半角字符(包括普通空格、ASCII字符等)
return 1;
},
// 获取有效长度。如果有后缀,则包括后缀和点的长度;否则是基础名称和后7位的长度
getEffectiveLength(base, suffix) {
let chineseLength = Array.from(base).reduce((count, char) => count + this.getCharLength(char), 0)
return suffix ? chineseLength + suffix.length + 1 : chineseLength
},
getNewFileName(fileList, newFileName) {
let append = 0
let filenameList = []
fileList.forEach(file => {
let fileName = file.name || file.label
filenameList.push(fileName)
})
const newName = newFileName
while (filenameList.includes(newFileName)) {
append += 1
if (newName.indexOf('.') > 0) {
const name = newName.substring(0, newName.lastIndexOf('.'))
const suffix = newName.substring(newName.lastIndexOf('.'))
newFileName = `${name}${append}${suffix}`
} else {
newFileName = `${newName}${append}`
}
}
return newFileName
},
getDuplicateFileName(fileList, filename) {
let append = 0
let filenameList = []
fileList.forEach(file => {
let fileName = file.name || file.label
filenameList.push(fileName)
})
let newName = filename
while (filenameList.includes(filename)) {
append += 1
if (newName.indexOf('.') > 0) {
let name = newName.substring(0, newName.lastIndexOf('.'))
if (name.indexOf(' 副本') > 0) {
name = name.substring(0, name.lastIndexOf(' 副本'))
}
const suffix = newName.substring(newName.lastIndexOf('.'))
if (append === 1) {
filename = `${name} 副本${suffix}`
} else {
filename = `${name} 副本${append}${suffix}`
}
} else {
if (newName.indexOf(' 副本') > 0) {
newName = newName.substring(0, newName.lastIndexOf(' 副本'))
}
if (append === 1) {
filename = `${newName} 副本`
} else {
filename = `${newName} 副本${append}`
}
}
}
return filename
},
}
}

View File

@ -0,0 +1,144 @@
import api from '@/api/file-api'
export default {
data() {
return {
basePath: '/',
pathList: [{ folder: '' }],
currentDirectory: '/', // 当前路径
}
},
mounted() {
// 监听返回
if (window.history && window.history.pushState) {
history.pushState(null, null, document.URL)
window.addEventListener('popstate', this.goBack, false)
}
},
beforeDestroy() {
window.removeEventListener('popstate', this.goBack, false)
},
methods: {
// 浏览器的返回事件
goBack() {
if (this.pathList.length <= 1) {
const queryTagId = this.$route.query.tagId
? `&tagId=${this.$route.query.tagId}`
: ''
const keyword = this.$route.query.keyword
? `&keyword=${this.$route.query.keyword}`
: ''
const basePath = this.getBasePath()
this.$router.push(
`/?vmode=${this.vmode}&path=${encodeURIComponent(
this.path
)}${queryTagId}${basePath}${keyword}`
)
return
}
this.lastLink()
},
lastLink() {
let keywordQuery = ''
const searchPathIndex = this.pathList.findIndex(item => item.search)
if (searchPathIndex === this.pathList.length - 1) {
keywordQuery = '&keyword='
}
this.handleLink(
this.pathList[this.pathList.length - 2],
this.pathList.length - 2,
undefined,
undefined,
keywordQuery
).then(r => {})
},
async handleLink(item, index, unPushLink, unRefresh, keywordQuery) {
this.pathList.splice(
this.pathList.findIndex((v, i) => i === index + 1),
this.pathList.length - (index + 1)
)
if (item && item.search) {
if (item.searchKey) {
this.searchFileByKeyword(item.searchKey)
} else if (item.row) {
this.searchFileAndOpenDir(item.row.id)
}
} else {
this.pathList.forEach((p, number) => {
if (number === 0) {
this.path = ''
} else if (number === this.pathList.length) {
} else {
this.path += '/' + this.pathList[number].folder
}
this.path = this.path.replace(/\\/g, '/')
})
let queryFolder = localStorage.getItem('mountFileOwner') ? localStorage.getItem(this.path) : this.$route.query.folder
if (localStorage.getItem('mountFileOwner') && !queryFolder) {
queryFolder = await this.getMountFolderId(this.$route.query.folder, localStorage.getItem('mountFileOwner'), this.path)
}
let searchOpenFolder = this.$route.query.searchOpenFolder ? `&searchOpenFolder=${this.$route.query.searchOpenFolder}` : ''
if (!unPushLink) {
const queryTagId = this.$route.query.tagId
? `&tagId=${this.$route.query.tagId}`
: ''
const basePath = this.getBasePath()
if (keywordQuery !== '&keyword=') {
keywordQuery = this.$route.query.keyword
? `&keyword=${this.$route.query.keyword}`
: ''
} else {
keywordQuery = ''
}
const searchPathIndex = this.pathList.findIndex(item => item.search)
if (searchPathIndex < 0) {
keywordQuery = ''
searchOpenFolder = ''
}
this.$router.push(`?vmode=${this.vmode}&path=${encodeURIComponent(this.path)}${queryFolder ? '&folder=' + queryFolder : ''}${queryTagId}${basePath}${keywordQuery}${searchOpenFolder}`)
}
if (!unRefresh) {
this.pagination.pageIndex = 1
this.getFileListEnter()
}
}
},
getMountFolderId(currentFolder, fileUsername, path) {
return new Promise((resolve, reject) => {
api.getMountFolderId({
otherFileId: currentFolder,
fileUsername: fileUsername,
path: path
})
.then(res => {
if (res.data) {
resolve(res.data)
} else {
resolve('')
}
})
.catch(error => {
reject('')
})
})
},
getBasePath() {
let basePath = this.$route.query.basePath ? `&basePath=${this.$route.query.basePath}` : ''
if (!this.path || this.path.length < 2) {
this.basePath = '/'
return ''
}
return basePath
},
getQueryPath() {
// 去掉this.$route.query.basePath最后的/
let basePath = this.$route.query.basePath ? this.$route.query.basePath : '/'
if (basePath) {
if (basePath.lastIndexOf('/') === basePath.length - 1) {
basePath = basePath.substring(0, basePath.length - 1)
}
}
return encodeURIComponent(basePath + this.$route.query.path)
},
}
}

View File

@ -0,0 +1,140 @@
// 全局右键菜单操作
export default {
props: {
contextMenus: {
type: Array,
default: () => [],
},
},
data() {
return {
contextmenuDisabled: false,
}
},
methods: {
contextmenuClick(operation) {
this.fileListTableClearSelection()
if (operation.startsWith('order')) {
const [prop, order] = operation.split('-')
this.handleSortChange(prop, order)
return
}
switch (operation) {
case 'vmode-list':
this.grid = true
this.changeVmode()
break
case 'vmode-grid':
this.grid = false
this.changeVmode()
break
case 'refresh':
this.getFileListEnter()
break
case 'createTextFile':
this.newCreateFileDialogTitle = '新建文本文件'
this.createNewFile('txt')
break
case 'createFolder':
this.newCreateFileDialogTitle = '新建文件夹'
this.createNewFile('')
break
case 'createDrawioFile':
this.newCreateFileDialogTitle = '新建流程图'
this.createNewFile('drawio')
break
case 'createMinderFile':
this.newCreateFileDialogTitle = '新建思维导图'
this.createNewFile('mind')
break
case 'createWordFile':
this.newCreateFileDialogTitle = '新建Word文档'
this.createNewFile('docx')
break
case 'createExcalidrawFile':
this.newCreateFileDialogTitle = '新建白板'
this.createNewFile('excalidraw')
break
case 'createExcelFile':
this.newCreateFileDialogTitle = '新建Excel工作表'
this.createNewFile('xlsx')
break
case 'createPPTFile':
this.newCreateFileDialogTitle = '新建PPT演示文档'
this.createNewFile('pptx')
break
case 'createMarkdownFile':
this.newDocument()
break
case 'clearTrash':
this.clearTrash()
break
}
},
// 加载菜单查看状态
loadContextMenus() {
if (this.contextMenus.length < 1) {
this.contextmenuDisabled = true
return
}
let container = document.querySelector('.dashboard-container')
if (this.$refs.homeContextmenu.references.length === 0) {
this.$refs.homeContextmenu.addRef({ el: container, vnode: container })
}
const viewModeIndex = this.contextMenus.findIndex(
item => item.operation === 'viewMode'
)
const arrangementModeIndex = this.contextMenus.findIndex(
item => item.operation === 'arrangement'
)
if (viewModeIndex > -1) {
const child = this.contextMenus[viewModeIndex].child
if (this.grid) {
child[0].iconClass = 'menu-empty'
child[1].iconClass = 'menu-point'
} else {
child[0].iconClass = 'menu-point'
child[1].iconClass = 'menu-empty'
}
}
if (arrangementModeIndex > -1) {
const child = this.contextMenus[arrangementModeIndex].child
const prop = this.sortable.prop
child.forEach(item => {
const orderProp = item.orderProp
if (orderProp === prop) {
child.map(item => {
if (orderProp === item.orderProp && this.sortable.order === item.order) {
item.iconClass = 'menu-' + this.sortable.order
} else {
item.iconClass = 'menu-null'
}
return item
})
}
})
}
},
// 新建文档
newDocument() {
window.open(`/setting/website/manager-articles?operation=new`, '_blank')
},
handleSortChange(command, order) {
this.sortable.order = order
switch (command) {
case 'orderName':
this.sortChangeOfMenu('name', 1)
break
case 'orderSize':
this.sortChangeOfMenu('size', 2)
break
case 'orderUpdateDate':
this.sortChangeOfMenu('updateDate', 3)
break
case 'orderUploadDate':
this.sortChangeOfMenu('uploadDate', -1)
break
}
},
},
}

View File

@ -0,0 +1,316 @@
import api from '@/api/file-api'
import _ from 'lodash'
export default {
data() {
return {
showNewFolder: false,
isShowNewFolder: false,
listModeSearch: false,
listModeSearchOpenDir: false,
newFolderName: '新建文件夹',
searchFileName: '',
debounceSearch: null,// 搜索防抖
filterOption: {}, // 搜索选项
searchSuggestions: [],// 搜索历史列表
_isSuggestionHandledBySelect: false,
_originalSearchFileNameBeforeSelect: '',
hasSearchFilterOption: false, // 是否有搜索选项
fileUsername: '', // 一般用于挂载文件
searchInputClass: 'search-input' // 搜索输入框样式
}
},
computed: {
searchOptionBtnClass() {
return this.hasSearchFilterOption ? 'search-option-btn search-option-btn-active' : 'search-option-btn'
},
},
watch: {
searchFileName(newValue) {
if (!newValue) {
this.hasSearchFilterOption = false
this.$refs.searchOption.clearFilterOption()
}
}
},
created() {
this.debounceSearch = _.debounce((key, onLoad) => {
this.searchFile(key, onLoad)
}, 200)
},
methods: {
hideNewFolderName() {
this.showNewFolder = false
this.isShowNewFolder = false
},
showNewFolderClick() {
this.isShowNewFolder = true
},
upload() {
// 打开文件选择框
this.$store.dispatch('updateMessage', {event: 'openUploader'})
},
uploadFolder() {
if (window.uploader.supportDirectory) {
this.$store.dispatch('updateMessage', {event: 'uploadFolder'})
} else {
this.$message({
message: '该浏览器不支持上传文件夹',
type: 'warning',
})
}
},
getUploadParams() {
return {
folder: this.$route.query.searchOpenFolder || this.$route.query.folder,
currentDirectory: this.getQueryPath(),
username: this.$store.state.user.name,
userId: this.$store.state.user.userId,
}
},
newFolderNameClickEnter() {
this.newFolderNameClick()
},
searchFileByKeyword(key) {
this.searchFile(key)
},
searchFileEnter() {
if (this._isSuggestionHandledBySelect) {
return
}
this.debounceSearch(this.searchFileName)
this.$refs.searchInput.close()
},
searchFile(key, onLoad) {
if (key) {
this.beforeLoadData(onLoad)
// this.pathList = [{ folder: "" }]
// 查找this.pathList中是否已经有搜索的路径
const item = {}
item['folder'] = `搜索: ${key}`
item['search'] = true
item['searchKey'] = key
let searchOpenFolder = this.$route.query.searchOpenFolder ? `&searchOpenFolder=${this.$route.query.searchOpenFolder}` : ''
const searchPathIndex = this.pathList.findIndex(item => item.search)
if (searchPathIndex < 0) {
this.pathList.push(item)
} else {
this.pathList.splice(searchPathIndex,this.pathList.length - searchPathIndex)
this.pathList.push(item)
searchOpenFolder = ''
}
const queryTagId = this.$route.query.tagId ? `&tagId=${this.$route.query.tagId}` : ''
const folder = this.$route.query.folder ? `&folder=${this.$route.query.folder}` : ''
const basePath = this.getBasePath()
const keyword = key ? `&keyword=${key}` : ''
const path = this.path ? encodeURIComponent(this.path.replace(this.basePath, '/')) : '/'
this.$router.push(`?vmode=${this.vmode}&path=${path}${keyword}${searchOpenFolder}${queryTagId}${basePath}${folder}`)
api.searchFile({
userId: this.$store.state.user.userId,
username: this.$store.state.user.name,
keyword: key,
sortableProp: this.sortable.prop,
order: this.sortable.order,
currentDirectory: this.getQueryPath(),
folder: this.$route.query.folder,
tagId: this.$route.query.tagId,
isFolder: this.queryCondition.isFolder,
isFavorite: this.queryCondition.isFavorite,
queryFileType: this.filterOption.type || this.queryFileType,
pageIndex: this.pagination.pageIndex,
pageSize: this.pagination.pageSize,
type: this.filterOption.type,
queryModifyStart: this.filterOption.modifyStart,
queryModifyEnd: this.filterOption.modifyEnd,
querySizeMin: this.filterOption.sizeMin,
querySizeMax: this.filterOption.sizeMax,
searchMount: this.filterOption.searchMount,
isMount: this.queryCondition.isMount,
searchOverall: this.filterOption.searchOverall,
exactSearch: this.filterOption.exactSearch,
includeTagName: this.filterOption.includeTagName,
includeFileName: this.filterOption.includeFileName,
includeFileContent: this.filterOption.includeFileContent,
}).then(res => {
this.loadData(res, onLoad)
this.listModeSearch = true
this.listModeSearchOpenDir = false
}).catch(() => {
this.tableLoading = false
})
} else {
if (this.listModeSearch) {
this.listModeSearch = false
this.lastLink()
}
}
},
searchFileAndOpenDir(fileId, onLoad) {
this.beforeLoadData(onLoad)
const queryTagId = this.$route.query.tagId ? `&tagId=${this.$route.query.tagId}` : ''
const path = this.$route.query.path ? `&path=${this.$route.query.path}`.replace(/#/g, '%23') : ''
const keyword = this.$route.query.keyword ? `&keyword=${this.$route.query.keyword}` : ''
const basePath = this.getBasePath()
const searchOpenFolder = `&searchOpenFolder=${fileId}`
const folder = this.$route.query.folder ? `&folder=${this.$route.query.folder}` : ''
this.$router.push(`?vmode=${this.vmode}${path}${keyword}${searchOpenFolder}${queryTagId}${basePath}${folder}`)
api.searchFileAndOpenDir({
userId: this.$store.state.user.userId,
username: this.$store.getters.name,
id: fileId,
currentDirectory: this.getQueryPath(),
pageIndex: this.pagination.pageIndex,
pageSize: this.pagination.pageSize,
folder: this.$route.query.folder,
})
.then(res => {
this.loadData(res, onLoad)
this.listModeSearch = true
this.listModeSearchOpenDir = fileId
}).catch(() => {
this.tableLoading = false
})
},
openDir(row, onLoad) {
this.beforeLoadData(onLoad)
api.searchFileAndOpenDir({
userId: this.$store.state.user.userId,
username: this.$store.getters.name,
id: row.mountFileId || row.id,
currentDirectory: this.getQueryPath(),
pageIndex: this.pagination.pageIndex,
pageSize: this.pagination.pageSize,
folder: this.$route.query.folder,
})
.then(res => {
this.loadData(res, onLoad)
}).catch(() => {
this.tableLoading = false
})
},
searchClose() {
this.searchFileName = ''
this.searchInputBlur()
this.searchFileEnter()
},
searchInputFocus() {
this.searchInputClass = 'search-input search-input-focus'
this.setInputFocus()
},
searchInputBlur() {
this.searchInputClass = this.searchFileName ? 'search-input search-input-focus' : 'search-input'
this.setInputBlur()
},
searchFilterChange() {
this.debounceSearch(this.searchFileName, false)
},
showSearchOption() {
},
async fetchDynamicSuggestions(queryString, cb) {
this._originalSearchFileNameBeforeSelect = queryString
this.searchSuggestions = []
const recentlySearch =
{
sugType: 'clearAllHistory',
keyword: '',
value: `__READONLY_DIVIDER_${Date.now()}__`,
id: 'recently_search_id'
}
if (!queryString) {
this.queryRecentlySearchHistory(queryString, recentlySearch, cb)
} else {
const fixedActionItems = [
{
sugType: 'action_current_path_search',
keyword: queryString,
value: queryString,
id: 'action_current_path_search_id'
},
{
sugType: 'action_global_search',
keyword: queryString,
value: queryString,
id: 'action_global_search_id'
},
]
this.searchSuggestions.push(...fixedActionItems)
this.queryRecentlySearchHistory(queryString, recentlySearch, cb)
}
},
queryRecentlySearchHistory(queryString = '', recentlySearch, cb) {
api.getRecentlySearchHistory({keyword: queryString}).then(res => {
const historyItems = res.data.map(item => ({
...item,
value: item.keyword,
sugType: 'history'
}))
if (historyItems.length > 0) {
this.searchSuggestions.push(recentlySearch)
}
this.searchSuggestions.push(...historyItems)
cb(this.searchSuggestions)
}).catch(error => {
console.error("Error fetching search history:", error)
cb(this.searchSuggestions.length > 0 ? this.searchSuggestions : [])
})
},
handleSelectSuggestion(item) {
this._isSuggestionHandledBySelect = true
if (item.id === "recently_search_id") {
this.$nextTick(() => {
if (this.searchFileName !== this._originalSearchFileNameBeforeSelect) {
this.searchFileName = this._originalSearchFileNameBeforeSelect
}
this._isSuggestionHandledBySelect = false
})
return
}
if ( item.sugType === 'history') {
this.searchFileName = item.keyword
}
if ( item.sugType === 'action_global_search') {
item.searchOverall = true
}
if ( item.sugType === 'action_current_path_search') {
item.searchOverall = false
}
this.debounceSearch(this.searchFileName, false)
this.$refs.searchOption.setFilterOption(item)
this.$nextTick(() => {
this._isSuggestionHandledBySelect = false
})
},
clearSearchHistoryForItem(itemToDelete, event) {
event.stopPropagation();
event.preventDefault();
const idToDelete = itemToDelete.id
api.removeSearchHistory({id: idToDelete}).then(() => {
const index = this.searchSuggestions.findIndex(item => item.id === idToDelete)
this.searchSuggestions.splice(index, 1)
})
},
clearAllSearchHistory(event) {
event.stopPropagation();
event.preventDefault();
this.$confirm('确定要清除所有搜索记录吗', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
api.removeAllSearchHistory({userId: this.$store.getters.userId}).then(() => {
this.searchSuggestions = this.searchSuggestions.filter(
item => item.sugType === 'action_current_path_search' || item.sugType === 'action_global_search'
);
})
}).catch(() => {
})
},
}
}

View File

@ -0,0 +1,109 @@
// 快捷键处理
export default {
data() {
return {
drawer: false,
drawerShowTime: 0,
}
},
mounted() {
// 获取键盘事件
window.addEventListener('keydown', this.keydown)
window.addEventListener('keyup', this.keyup)
},
beforeDestroy() {
// 移除事件监听
window.removeEventListener('keydown', this.keydown, false)
window.removeEventListener('keyup', this.keyup, false)
},
computed: {
cmdKey() {
return navigator.platform.startsWith('Mac') ? '⌘' : 'Ctrl'
},
},
methods: {
checkCmdKey(event) {
const isMac = navigator.platform.startsWith('Mac')
const { ctrlKey, metaKey } = event
return (isMac && metaKey) || (!isMac && ctrlKey)
},
keydown(event) {
const { keyCode } = event
const isCmd = this.checkCmdKey(event)
const checkPreviewVisible = this.checkPreviewVisible()
// space
if (keyCode === 32 && this.selectRowData.length > 0 && !checkPreviewVisible) {
if (!this.drawer) {
this.drawer = true
this.drawerShowTime = Date.now()
} else {
this.drawer = false
}
event.preventDefault()
event.stopPropagation()
}
// F2
if (keyCode === 113 && this.selectRowData.length > 0 && !checkPreviewVisible) {
this.renameFileName = this.rowContextData.name
this.editingIndex = this.rowContextData.index
event.preventDefault()
event.stopPropagation()
}
// Del
if (keyCode === 8 && !this.inputting && this.selectRowData.length > 0 && !checkPreviewVisible) {
this.removeOperation()
event.preventDefault()
event.stopPropagation()
}
// ctrl + A / cmd + A
if (isCmd && keyCode === 65 && !checkPreviewVisible) {
if (this.inputting || this.editingIndex !== -1) {
event.target.select()
} else {
this.$nextTick(() => {
if (this.$refs.fileListTable) {
this.$refs.fileListTable.toggleAllSelection()
}
})
event.preventDefault()
event.stopPropagation()
}
}
// ctrl + C / cmd + C
if (isCmd && keyCode === 67 && !checkPreviewVisible && !this.inputting && this.selectRowData.length > 0) {
this.copyOperation()
event.preventDefault()
event.stopPropagation()
}
// ctrl + V / cmd + V
if (isCmd && keyCode === 86 && !checkPreviewVisible && !this.inputting && this.fileClipboard.length > 0) {
// copy
this.onCopy(this.getClipboardFileIdList, this.path, this.currentFolder)
event.preventDefault()
event.stopPropagation()
}
// ctrl + X / cmd + X
if (isCmd && keyCode === 88 && !checkPreviewVisible && !this.inputting && this.fileClipboard.length > 0) {
// move
this.onMove(this.getClipboardFileIdList, this.path, this.currentFolder)
event.preventDefault()
event.stopPropagation()
}
// ctrl + P / cmd + P
if (isCmd && keyCode === 80 && !checkPreviewVisible) {
this.$refs.searchInput.focus()
event.preventDefault()
event.stopPropagation()
}
},
keyup(event) {
const { keyCode } = event
// space
if (keyCode === 32) {
if (this.drawer && Date.now() - this.drawerShowTime >= 500) {
this.drawer = false
}
}
},
},
}

View File

@ -0,0 +1,179 @@
export default {
props: {
lessClientHeight: {
type: Number,
default: 106,
},
showSearchButton: {
type: Boolean,
default: true,
},
isCollectView: {
type: Boolean,
default: false,
},
showNavigation: {
type: Boolean,
default: true,
},
},
data() {
return {
selectedRowIndices: new Set()
}
},
methods: {
sortChangeOfMenu(prop, headerIndex) {
let tableHeader = document.querySelector('.el-table__header thead tr')
// 去掉table-header上所有排序高亮
tableHeader.childNodes.forEach(el => {
if (el.className.indexOf('is-sortable') > -1) {
this.removeClass(el, 'descending')
this.removeClass(el, 'ascending')
}
})
// 重新加上排序高亮
let order = this.sortable.order
if (headerIndex > -1) {
this.addClass(tableHeader.children[headerIndex], order)
}
this.orderCustom = true
this.sortChange({ prop: prop, order: order })
},
sortChange(column) {
let { prop, order } = column
if (this.orderCustom || this.listModeSearch) {
this.sortable.prop = prop
this.sortable.order = order
this.pagination.pageIndex = 1
if (this.listModeSearch) {
this.searchFile(this.searchFileName)
} else {
this.getFileListEnter()
}
}
},
removeClass(el, className) {
const str = el.className
if (str.indexOf(className) > -1) {
el.className = str.replace(className, '')
}
},
addClass(el, className) {
if (el) {
el.classList.add(className)
}
},
// 收集选中的index值作为数组 传递给rowRed判断变换样式
handleSelectionChange(rows) {
// 起点
if (rows.length > 0) {
this.selectOrigin = rows[0].index
this.rowContextData = rows[0]
}
this.selectedRowIndices = new Set(rows.map(row => row.index));
this.$refs.fileListTable.tableSelectData = rows
this.selectRowData = rows
this.changeSelectedStyle(rows)
},
changeSelectedStyle(rows) {
if (this.stopSortChange) {
return
}
let selectTotalSize = 0
rows.forEach(item => {
selectTotalSize += item.size
})
const item_name = this.tableHead[2]
const item_size = this.tableHead[3]
const item_date = this.tableHead[4]
if (rows.length > 0) {
const sumFileAndFolder = this.getShowSumFileAndFolder(rows)
const sizeSum = this.getShowSumSize(selectTotalSize)
item_name.label = sumFileAndFolder
item_name.sortable = false
item_size.label = sizeSum
item_size.sortable = false
item_date.label = ''
item_date.sortable = false
} else {
item_name.label = '名称'
item_name.sortable = true
item_size.label = '大小'
item_size.sortable = true
item_date.label = '修改日期'
item_date.sortable = true
}
},
// cell-style 通过返回值可以实现样式变换利用传递过来的数组index循环改变样式
rowStyle({ row, column, rowIndex, columnIndex }) {
if (this.selectedRowIndices.has(rowIndex)) {
if (columnIndex === 0) {
return {
backgroundColor: '#e0f3fc !important',
borderRadius: '12px 0 0 12px',
borderLeft: 'solid 1px var(--apple-shadow-color)',
borderTop: 'solid 1px var(--apple-shadow-color)',
borderBottom: 'solid 1px var(--apple-shadow-color)',
}
}
if (columnIndex === 3) {
return {
backgroundColor: '#e0f3fc !important',
borderRadius: '0 12px 12px 0',
borderRight: 'solid 1px var(--apple-shadow-color)',
borderTop: 'solid 1px var(--apple-shadow-color)',
borderBottom: 'solid 1px var(--apple-shadow-color)',
}
}
return {
backgroundColor: '#e0f3fc !important',
borderTop: 'solid 1px var(--apple-shadow-color)',
borderBottom: 'solid 1px var(--apple-shadow-color)',
}
} else {
if (columnIndex === 0) {
return {
borderRadius: '12px 0 0 12px',
}
}
if (columnIndex === 3) {
return {
borderRadius: '0 12px 12px 0',
}
}
}
},
// 选择某行预备数据
preliminaryRowData(row) {
if (row) {
this.rowContextData = row
}
const isFavorite = this.rowContextData.isFavorite
this.highlightFavorite(isFavorite, false)
},
// 是否高亮收藏图标
highlightFavorite(isFavorite, isHover) {
const item_menu = this.menus.find(item => {
if (item.operation === 'favorite' || item.operation === 'unFavorite') {
return item
}
})
if (item_menu) {
if (isFavorite) {
item_menu.label = '取消收藏'
item_menu.iconClass = 'menu-unfavorite-hover'
item_menu.operation = 'unFavorite'
} else {
if (isHover) {
item_menu.iconClass = 'menu-favorite-hover'
} else {
item_menu.iconClass = 'menu-favorite'
}
item_menu.label = '收藏'
item_menu.operation = 'favorite'
}
}
},
}
}

View File

@ -0,0 +1,343 @@
.dashboard-container {
min-width: 498px;
height: 100%;
}
>>> .app-wrapper {
overflow-y: hidden;
}
>>> :focus {
outline: 0;
}
.list-item {
height: 50px;
}
.table-file-name:hover {
cursor: default;
}
>>> .plTableBox .el-table .el-table__header {
th {
background-color: #ffffff;
}
.is-sortable:hover {
background-color: #e0f3fc;
border-radius: 12px;
}
}
>>> table {
border-collapse: separate;
border-spacing: 0 1px;
}
>>> .el-table td {
padding: 0;
height: 50px !important;
border: 0;
}
.home-link:hover {
color: #409eff;
}
.info-statistics {
padding: 5px 15px;
float: right;
width: 30%;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: end;
-ms-flex-pack: end;
justify-content: flex-end;
span {
font-size: 12px;
line-height: 16px;
color: #666;
}
}
>>> .el-input-tree {
width: 50% !important;
}
>>> .el-input-tree-button {
margin-left: 5px !important;
}
>>> .open-file-dialog {
.el-dialog {
width: 420px;
}
.svg-icon {
font-size: 20px;
}
.dialog-msg {
margin-left: 10px;
}
}
>>> .v-contextmenu-item {
.svg-icon {
font-size: 14px;
}
&:hover {
border-radius: 4px;
}
}
>>> .new-text-file-dialog {
height: 350px;
top: calc(50% - 175px);
.el-dialog {
width: 420px;
.el-dialog__header {
padding: 15px 20px 15px;
}
.dialog-footer {
.el-loading-spinner {
margin-top: -13px;
.circular {
height: 26px;
width: 26px;
}
}
}
}
}
.dialog-footer-delete {
display: flex;
justify-content: space-between;
}
.shadow-container {
position: relative;
width: 100%;
overflow: hidden;
background: white;
}
.shadow-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(to bottom, rgba(0, 0, 0, 0.05), transparent 4px);
pointer-events: none;
z-index: 10;
}
>>> .van-grid-item__content {
background-size: cover;
background-position: center;
border-radius: $dialogBorderRadius !important;
padding: 0;
}
.vmode {
padding: 5px 10px;
margin-left: -5px;
}
.number-files {
position: absolute;
top: -42px;
left: 0;
height: 40px;
line-height: 40px;
-webkit-backdrop-filter: saturate(180%) blur(20px);
backdrop-filter: saturate(180%) blur(20px);
background: #d2eefa66;
border-radius: 40px;
display: flex;
.icon {
padding: 5px;
width: 40px;
}
span {
font-weight: 500;
}
.number {
padding: 0 15px 0 15px;
}
.target {
.folder {
background-color: #1d8cff;
color: #ffffff;
padding: 8px;
border-radius: 32px;
margin-right: 5px;
font-weight: 600;
}
}
}
>>> .el-table--enable-row-hover {
.el-table__body tr:hover > td {
background-color: #e0f3fc;
}
}
>>> .el-table::before {
height: 0;
}
>>> .el-table {
padding-left: 40px;
th.gutter {
display: table-cell !important;
}
}
.dialog-file-list {
padding: 10px 0;
}
.delete-attention {
padding: 10px 10px;
border-radius: 12px;
margin-bottom: 10px;
.el-checkbox {
color: #606266;
font-weight: 500;
font-size: 14px;
cursor: pointer;
white-space: nowrap;
user-select: none;
}
}
.mt-5 {
margin-top: 5px;
}
.search-input {
width: 250px;
transition: width 0.3s ease-in-out;
>>> .el-input--suffix .el-input__inner {
padding-right: 60px;
}
}
.search-input-focus {
width: 250px;
}
.search-close-btn {
padding: 4px;
color: #C0C4CC;
:hover {
color: #409EFF;
}
}
.search-option-btn {
padding: 4px;
margin-right: 4px;
color: #606266;
:hover {
color: #409EFF;
}
}
.search-option-btn-active {
background: #409EFF;
color: #fff;
:hover {
color: #fff;
}
}
>>> .el-autocomplete-suggestion {
margin-top: 0 !important;
.popper__arrow {
display: none;
}
li {
padding: 0 5px 0 15px;
}
.suggestion-item {
display: flex;
justify-content: space-between;
align-items: center;
.suggestion-item-title {
display: flex;
gap: 15px;
justify-content: flex-start;
flex-direction: row;
align-items: center;
.suggestion-item-text {
width: 168px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.clear-search-history-btn {
padding: 8px;
display: none;
}
&:hover {
.clear-search-history-btn {
display: block;
}
}
}
.suggestion-item.action-item {
display: flex;
justify-content: space-between;
align-items: center;
.suggestion-action-item-title {
display: flex;
gap: 15px;
justify-content: flex-start;
flex-direction: row;
align-items: center;
height: 40px;
line-height: 40px;
.suggestion-action-item-text {
display: flex;
flex-wrap: wrap;
}
.suggestion-item-keyword {
line-height: normal;
color: #303133;
font-weight: 500;
margin-right: 10px;
}
.suggestion-item-hint {
color: #c8c9cc;
line-height: normal;
}
}
}
}

View File

@ -322,7 +322,6 @@ export default {
return {
prompt: '文件分享已被撤销',// 文件分享已被撤销
isLoading: true,
fileMenuActive: '',
path: this.$route.query.path,
showNewFolder: false,
isShowNewFolder: false,
@ -401,10 +400,6 @@ export default {
children: 'children',
isLeaf: 'isLeaf'
},
dragLoop: null,
positionX: 0,
positionY: 0,
grid: false,
vmode: 'list',
gridColumnNum: -1,
@ -415,7 +410,6 @@ export default {
summaries: '',
shareDialog: false,
shareLink: '',
shareFileName: '',
generateShareLinkLoading: true,
shareId: '',
shortId: this.$route.params.id,
@ -430,7 +424,6 @@ export default {
iframePreviewRow: {},
iframePreviewVisible: false,
fileHandler: {},
audioPreviewRow: {},
audioPreviewVisible: false,
showUpdateDateItem: this.$pc,// 列表模式下是否显示修改时间
showSizeItem: this.$pc,// 列表模式下是否显示文件大小

View File

@ -215,7 +215,6 @@
return {
imageUrl: `${process.env.VUE_APP_BASE_API}/view/thumbnail?jmal-token=${this.$store.state.user.token}&name=${this.$store.state.user.name}&id=`,
audioCoverUrl: `${process.env.VUE_APP_BASE_API}/view/cover?jmal-token=${this.$store.state.user.token}&name=${this.$store.state.user.name}&id=`,
fileMenuActive: '',
path: this.$route.query.path,
pathList: [
{ 'folder': '', index: 0 },
@ -265,7 +264,6 @@
finished: false,
cellMouseIndex: -1,
shareDialog: false,
shareFileName: '',
sumFileAndFolder: '',
shareDialogVisible: false,
shareDialogObject: {},