This commit is contained in:
parent
1898d1b730
commit
d5adbd8a1c
|
|
@ -115,3 +115,12 @@ export function saveSharePermissionAPI(data = {}) {
|
|||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 移动和添加副本时获取的树结构
|
||||
export function getMoveAndAddCopyTreeAPI(data = {}) {
|
||||
return request({
|
||||
url: '/screen/document/getRemoveTree',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ export default {
|
|||
methods: {
|
||||
// 获取节点图标
|
||||
getNodeIcon(data) {
|
||||
return 'el-icon-folder'
|
||||
if (data.fileType == 0) {
|
||||
return 'el-icon-folder'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,7 +73,10 @@
|
|||
/>
|
||||
<span class="file-name">{{ scope.row.name }}</span>
|
||||
|
||||
<el-dropdown trigger="click">
|
||||
<el-dropdown
|
||||
trigger="click"
|
||||
v-if="scope.row.fileType == 1"
|
||||
>
|
||||
<span class="el-dropdown-link">
|
||||
<i
|
||||
class="el-icon-setting el-icon--right"
|
||||
|
|
@ -84,7 +87,7 @@
|
|||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item
|
||||
:key="item.label"
|
||||
@click="item.click"
|
||||
@click="item.click(scope.row)"
|
||||
v-for="item in dropdownList"
|
||||
>
|
||||
{{ item.label }}
|
||||
|
|
@ -174,24 +177,6 @@
|
|||
@closeDialogOuter="handleCloseDialogOuter"
|
||||
>
|
||||
<template #outerContent>
|
||||
<!-- <DownloadTags
|
||||
v-if="dialogConfig.outerTitle === '批量下载'"
|
||||
:selectedFiles="selectedFilesForDownload"
|
||||
@remove-file="handleRemoveFile"
|
||||
/>
|
||||
|
||||
<SharePermissionForm
|
||||
v-if="dialogConfig.outerTitle === '共享'"
|
||||
:selectedFiles="selectedFilesForShare"
|
||||
@cancel="handleCloseShareDialog"
|
||||
@confirm="handleShareConfirm"
|
||||
/>
|
||||
|
||||
<AddWord v-if="dialogConfig.outerTitle === '新建文档夹'" />
|
||||
<Upload v-if="dialogConfig.outerTitle === '上传文件'" />
|
||||
<Move v-if="dialogConfig.outerTitle === '移动'" />
|
||||
<AddCopy v-if="dialogConfig.outerTitle === '添加副本'" /> -->
|
||||
|
||||
<component
|
||||
ref="componentRef"
|
||||
:selectedNode="selectedNode"
|
||||
|
|
@ -336,13 +321,15 @@ export default {
|
|||
dropdownList: [
|
||||
{
|
||||
label: '下载',
|
||||
},
|
||||
{
|
||||
label: '移动',
|
||||
click: this.handleMove,
|
||||
},
|
||||
{
|
||||
label: '移动',
|
||||
click: this.handleMove_1,
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
click: this.handleDelete_1,
|
||||
},
|
||||
{
|
||||
label: '共享',
|
||||
|
|
@ -350,10 +337,11 @@ export default {
|
|||
},
|
||||
{
|
||||
label: '重命名',
|
||||
click: this.handleMove,
|
||||
},
|
||||
{
|
||||
label: '添加副本',
|
||||
click: this.handleAddCopy,
|
||||
click: this.handleAddCopy_1,
|
||||
},
|
||||
],
|
||||
|
||||
|
|
@ -461,11 +449,26 @@ export default {
|
|||
|
||||
// 移动
|
||||
handleMove() {
|
||||
// this.$emit('move', this.selectedRows)
|
||||
if (this.selectedRows.length === 0) {
|
||||
this.$modal.msgWarning('请选择要移动的文件')
|
||||
return
|
||||
}
|
||||
this.dialogConfig.outerTitle = '移动'
|
||||
this.dialogConfig.outerVisible = true
|
||||
this.dialogConfig.outerComponent = 'Move'
|
||||
this.dialogConfig.outerComponentProps = {}
|
||||
this.dialogConfig.outerComponentProps = {
|
||||
selectedFiles: this.selectedRows,
|
||||
}
|
||||
},
|
||||
|
||||
// 移动
|
||||
handleMove_1(row) {
|
||||
this.dialogConfig.outerTitle = '移动'
|
||||
this.dialogConfig.outerVisible = true
|
||||
this.dialogConfig.outerComponent = 'Move'
|
||||
this.dialogConfig.outerComponentProps = {
|
||||
selectedFiles: [row],
|
||||
}
|
||||
},
|
||||
|
||||
// 删除
|
||||
|
|
@ -505,6 +508,37 @@ export default {
|
|||
})
|
||||
},
|
||||
|
||||
// 删除
|
||||
async handleDelete_1(row) {
|
||||
this.$confirm(
|
||||
'所选中文档及对应的映射文档将被删除, 且该操作不可恢复, 确认吗?\n该操作会重新配置文档或文档夹的权限, 确认继续吗?',
|
||||
'提示',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
},
|
||||
)
|
||||
.then(async () => {
|
||||
// this.$modal.msgSuccess('删除功能待实现')
|
||||
|
||||
const res = await deleteDocCenterAPI({
|
||||
id: row.id,
|
||||
parentId: this.selectedNode?.id,
|
||||
})
|
||||
|
||||
if (res.code === 200) {
|
||||
this.$modal.msgSuccess('删除成功')
|
||||
this.getTableList()
|
||||
} else {
|
||||
this.$modal.msgError(res.message)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.$modal.msgInfo('已取消删除')
|
||||
})
|
||||
},
|
||||
|
||||
// 批量下载
|
||||
handleBatchDownload() {
|
||||
if (this.selectedRows.length === 0) {
|
||||
|
|
@ -542,11 +576,26 @@ export default {
|
|||
|
||||
// 添加副本
|
||||
handleAddCopy() {
|
||||
// this.$emit('add-copy', this.selectedRows)
|
||||
if (this.selectedRows.length === 0) {
|
||||
this.$modal.msgWarning('请选择要添加副本的文件')
|
||||
return
|
||||
}
|
||||
this.dialogConfig.outerTitle = '添加副本'
|
||||
this.dialogConfig.outerVisible = true
|
||||
this.dialogConfig.outerComponent = 'AddCopy'
|
||||
this.dialogConfig.outerComponentProps = {}
|
||||
this.dialogConfig.outerComponentProps = {
|
||||
selectedFiles: this.selectedRows,
|
||||
}
|
||||
},
|
||||
|
||||
// 添加副本
|
||||
handleAddCopy_1(row) {
|
||||
this.dialogConfig.outerTitle = '添加副本'
|
||||
this.dialogConfig.outerVisible = true
|
||||
this.dialogConfig.outerComponent = 'AddCopy'
|
||||
this.dialogConfig.outerComponentProps = {
|
||||
selectedFiles: [row],
|
||||
}
|
||||
},
|
||||
|
||||
// 查看
|
||||
|
|
@ -603,7 +652,7 @@ export default {
|
|||
this.getTableList()
|
||||
|
||||
// 更新左侧树
|
||||
this.$emit('updateLeftTree')
|
||||
// this.$emit('updateLeftTree')
|
||||
} catch (error) {
|
||||
console.error('操作失败:', error)
|
||||
// 如果是表单验证失败,不显示错误提示(因为 Element UI 已经显示了)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
<template>
|
||||
<!-- 添加副本 -->
|
||||
<div class="move-container">
|
||||
<div class="tree-wrapper" v-loading="loading">
|
||||
<el-tree
|
||||
|
|
@ -11,42 +10,37 @@
|
|||
:props="treeProps"
|
||||
highlight-current
|
||||
default-expand-all
|
||||
draggable
|
||||
:allow-drag="allowDrag"
|
||||
:allow-drop="allowDrop"
|
||||
:expand-on-click-node="false"
|
||||
@node-drop="handleNodeDrop"
|
||||
@node-click="handleNodeClick"
|
||||
>
|
||||
<span class="tree-node" slot-scope="{ data }">
|
||||
<i :class="getNodeIcon(data)" class="node-icon"></i>
|
||||
<i class="el-icon-folder node-icon"></i>
|
||||
<span class="node-label" :title="data.name">
|
||||
{{ data.name }}
|
||||
</span>
|
||||
<el-tag size="mini" v-if="data.fileType === 0" type="info">
|
||||
文件夹
|
||||
</el-tag>
|
||||
</span>
|
||||
</el-tree>
|
||||
<div v-else class="empty-holder">暂无可移动数据</div>
|
||||
<div v-else class="empty-holder">暂无可添加副本的文件夹</div>
|
||||
</div>
|
||||
<div class="tips">
|
||||
<p class="tips-title">操作提示</p>
|
||||
<p>1. 仅文件(fileType ≠ 0)可被拖拽。</p>
|
||||
<p>2. 仅支持拖拽到文件夹节点内。</p>
|
||||
<p>3. 拖拽到目标组后会立即复制副本并同步至后台。</p>
|
||||
<div v-if="selectedFolder" class="selected-info">
|
||||
<span>已选择目标文件夹:</span>
|
||||
<el-tag type="primary">{{ selectedFolder.name }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { copyDocCenterAPI } from '@/api/publicService/docCenter'
|
||||
import {
|
||||
copyDocCenterAPI,
|
||||
getMoveAndAddCopyTreeAPI,
|
||||
} from '@/api/publicService/docCenter'
|
||||
|
||||
export default {
|
||||
name: 'AddCopy',
|
||||
name: 'Move',
|
||||
props: {
|
||||
selectedNode: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
selectedFiles: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
|
|
@ -57,128 +51,100 @@ export default {
|
|||
label: 'name',
|
||||
},
|
||||
loading: false,
|
||||
copyActions: [],
|
||||
selectedFolder: null, // 选中的目标文件夹
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
selectedNode: {
|
||||
handler(newVal) {
|
||||
this.initTree(newVal)
|
||||
},
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
mounted() {
|
||||
this.getTreeData()
|
||||
},
|
||||
methods: {
|
||||
initTree(node) {
|
||||
if (!node || !node.id) {
|
||||
this.treeData = []
|
||||
return
|
||||
}
|
||||
this.treeData = [this.cloneNode(node)]
|
||||
this.copyActions = []
|
||||
},
|
||||
|
||||
cloneNode(node) {
|
||||
const childTree = Array.isArray(node.childTree)
|
||||
? node.childTree.map((child) => this.cloneNode(child))
|
||||
: []
|
||||
return {
|
||||
...node,
|
||||
childTree,
|
||||
}
|
||||
},
|
||||
|
||||
getNodeIcon(data) {
|
||||
// console.log(data.fileType, 'data.fileType')
|
||||
return data.fileType == 0 ? 'el-icon-folder' : 'el-icon-document'
|
||||
},
|
||||
|
||||
// 是否允许拖拽
|
||||
allowDrag(draggingNode) {
|
||||
const data = draggingNode?.data || {}
|
||||
return data.fileType == 1
|
||||
},
|
||||
// 是否允许放下
|
||||
allowDrop(draggingNode, dropNode, type) {
|
||||
if (type !== 'inner') {
|
||||
return false
|
||||
}
|
||||
const dropData = dropNode?.data || {}
|
||||
if (dropData.fileType == 1) {
|
||||
return false
|
||||
}
|
||||
|
||||
const dragData = draggingNode?.data || {}
|
||||
if (!dragData.id || dragData.id === dropData.id) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
|
||||
async handleNodeDrop(draggingNode, dropNode) {
|
||||
const source = draggingNode?.data
|
||||
const target = dropNode?.data
|
||||
|
||||
if (!source?.id || !target?.id) {
|
||||
this.$message.error('无法获取拖拽节点信息')
|
||||
return
|
||||
}
|
||||
|
||||
await this.copyDocument(
|
||||
source.id,
|
||||
target.id,
|
||||
source.parentId,
|
||||
target.parentIds,
|
||||
)
|
||||
},
|
||||
|
||||
async copyDocument(documentId, targetParentId, parentId, parentIds) {
|
||||
// 组装参数
|
||||
const params = {
|
||||
id: documentId, // 自身id
|
||||
toId: targetParentId,
|
||||
newParentId: targetParentId, // 目标i
|
||||
parentId: parentId, // 目标父级id
|
||||
parentIds: parentIds,
|
||||
}
|
||||
// 获取树结构数据
|
||||
async getTreeData() {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await copyDocCenterAPI(params)
|
||||
|
||||
if (res.code === 200) {
|
||||
this.$message.success('复制成功')
|
||||
this.recordAction(documentId, targetParentId)
|
||||
const res = await getMoveAndAddCopyTreeAPI()
|
||||
if (res.code === 200 && res.data) {
|
||||
this.treeData = Array.isArray(res.data) ? res.data : []
|
||||
} else {
|
||||
this.$message.error(res.msg || res.message || '复制失败')
|
||||
this.$message.error(
|
||||
res.msg || res.message || '获取文件夹树失败',
|
||||
)
|
||||
this.treeData = []
|
||||
}
|
||||
} catch (error) {
|
||||
this.$message.error(error.message || '复制失败')
|
||||
this.$message.error(error.message || '获取文件夹树失败')
|
||||
this.treeData = []
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
recordAction(documentId, targetParentId) {
|
||||
const idx = this.copyActions.findIndex(
|
||||
(item) => item.documentId === documentId,
|
||||
)
|
||||
const payload = { documentId, targetParentId }
|
||||
if (idx > -1) {
|
||||
this.$set(this.copyActions, idx, payload)
|
||||
} else {
|
||||
this.copyActions.push(payload)
|
||||
// 点击树节点
|
||||
handleNodeClick(data) {
|
||||
this.selectedFolder = data
|
||||
// 设置树节点为选中状态
|
||||
this.$nextTick(() => {
|
||||
this.$refs.treeRef.setCurrentKey(data.id)
|
||||
})
|
||||
},
|
||||
|
||||
// 添加副本
|
||||
async moveDocument(fileId, targetParentId, parentId, parentIds) {
|
||||
const params = {
|
||||
id: fileId,
|
||||
toId: targetParentId,
|
||||
newParentId: targetParentId,
|
||||
parentId: parentId,
|
||||
parentIds: parentIds,
|
||||
}
|
||||
try {
|
||||
const res = await copyDocCenterAPI(params)
|
||||
if (res.code !== 200) {
|
||||
throw new Error(res.msg || res.message || '添加副本失败')
|
||||
}
|
||||
return res
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
submit() {
|
||||
if (this.copyActions.length === 0) {
|
||||
return Promise.reject(new Error('请先拖拽需要复制的文件'))
|
||||
// 提交添加副本操作
|
||||
async submit() {
|
||||
// 验证选中的文件
|
||||
if (!this.selectedFiles || this.selectedFiles.length === 0) {
|
||||
return Promise.reject(new Error('请先选择要添加副本的文件'))
|
||||
}
|
||||
|
||||
// 验证选中的目标文件夹
|
||||
if (!this.selectedFolder || !this.selectedFolder.id) {
|
||||
return Promise.reject(new Error('请选择要添加副本的目标文件夹'))
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
try {
|
||||
// 批量移动文件
|
||||
const movePromises = this.selectedFiles.map((file) =>
|
||||
this.moveDocument(
|
||||
file.id,
|
||||
this.selectedFolder.id,
|
||||
file.parentId,
|
||||
file.parentIds,
|
||||
),
|
||||
)
|
||||
|
||||
await Promise.all(movePromises)
|
||||
|
||||
this.$message.success('移动成功')
|
||||
return Promise.resolve({
|
||||
success: true,
|
||||
movedCount: this.selectedFiles.length,
|
||||
})
|
||||
} catch (error) {
|
||||
this.$message.error(error.message || '添加副本失败')
|
||||
return Promise.reject(error)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
return Promise.resolve({
|
||||
copied: this.copyActions,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -207,6 +173,7 @@ export default {
|
|||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex: 1;
|
||||
|
||||
.node-icon {
|
||||
font-size: 16px;
|
||||
|
|
@ -214,6 +181,7 @@ export default {
|
|||
}
|
||||
|
||||
.node-label {
|
||||
flex: 1;
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
|
@ -230,18 +198,15 @@ export default {
|
|||
color: #909399;
|
||||
}
|
||||
|
||||
.tips {
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
line-height: 1.8;
|
||||
background: #fdf6ec;
|
||||
border: 1px solid #f5dab1;
|
||||
.selected-info {
|
||||
padding: 12px;
|
||||
background: #f0f9ff;
|
||||
border: 1px solid #bae6fd;
|
||||
border-radius: 6px;
|
||||
|
||||
.tips-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,247 @@
|
|||
<template>
|
||||
<!-- 添加副本 -->
|
||||
<div class="move-container">
|
||||
<div class="tree-wrapper" v-loading="loading">
|
||||
<el-tree
|
||||
v-if="treeData.length"
|
||||
ref="treeRef"
|
||||
node-key="id"
|
||||
class="move-tree"
|
||||
:data="treeData"
|
||||
:props="treeProps"
|
||||
highlight-current
|
||||
default-expand-all
|
||||
draggable
|
||||
:allow-drag="allowDrag"
|
||||
:allow-drop="allowDrop"
|
||||
:expand-on-click-node="false"
|
||||
@node-drop="handleNodeDrop"
|
||||
>
|
||||
<span class="tree-node" slot-scope="{ data }">
|
||||
<i :class="getNodeIcon(data)" class="node-icon"></i>
|
||||
<span class="node-label" :title="data.name">
|
||||
{{ data.name }}
|
||||
</span>
|
||||
<el-tag size="mini" v-if="data.fileType === 0" type="info">
|
||||
文件夹
|
||||
</el-tag>
|
||||
</span>
|
||||
</el-tree>
|
||||
<div v-else class="empty-holder">暂无可移动数据</div>
|
||||
</div>
|
||||
<div class="tips">
|
||||
<p class="tips-title">操作提示</p>
|
||||
<p>1. 仅文件(fileType ≠ 0)可被拖拽。</p>
|
||||
<p>2. 仅支持拖拽到文件夹节点内。</p>
|
||||
<p>3. 拖拽到目标组后会立即复制副本并同步至后台。</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { copyDocCenterAPI } from '@/api/publicService/docCenter'
|
||||
|
||||
export default {
|
||||
name: 'AddCopy',
|
||||
props: {
|
||||
selectedNode: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
treeData: [],
|
||||
treeProps: {
|
||||
children: 'childTree',
|
||||
label: 'name',
|
||||
},
|
||||
loading: false,
|
||||
copyActions: [],
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
selectedNode: {
|
||||
handler(newVal) {
|
||||
this.initTree(newVal)
|
||||
},
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
initTree(node) {
|
||||
if (!node || !node.id) {
|
||||
this.treeData = []
|
||||
return
|
||||
}
|
||||
this.treeData = [this.cloneNode(node)]
|
||||
this.copyActions = []
|
||||
},
|
||||
|
||||
cloneNode(node) {
|
||||
const childTree = Array.isArray(node.childTree)
|
||||
? node.childTree.map((child) => this.cloneNode(child))
|
||||
: []
|
||||
return {
|
||||
...node,
|
||||
childTree,
|
||||
}
|
||||
},
|
||||
|
||||
getNodeIcon(data) {
|
||||
// console.log(data.fileType, 'data.fileType')
|
||||
return data.fileType == 0 ? 'el-icon-folder' : 'el-icon-document'
|
||||
},
|
||||
|
||||
// 是否允许拖拽
|
||||
allowDrag(draggingNode) {
|
||||
const data = draggingNode?.data || {}
|
||||
return data.fileType == 1
|
||||
},
|
||||
// 是否允许放下
|
||||
allowDrop(draggingNode, dropNode, type) {
|
||||
if (type !== 'inner') {
|
||||
return false
|
||||
}
|
||||
const dropData = dropNode?.data || {}
|
||||
if (dropData.fileType == 1) {
|
||||
return false
|
||||
}
|
||||
|
||||
const dragData = draggingNode?.data || {}
|
||||
if (!dragData.id || dragData.id === dropData.id) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
|
||||
async handleNodeDrop(draggingNode, dropNode) {
|
||||
const source = draggingNode?.data
|
||||
const target = dropNode?.data
|
||||
|
||||
if (!source?.id || !target?.id) {
|
||||
this.$message.error('无法获取拖拽节点信息')
|
||||
return
|
||||
}
|
||||
|
||||
await this.copyDocument(
|
||||
source.id,
|
||||
target.id,
|
||||
source.parentId,
|
||||
target.parentIds,
|
||||
)
|
||||
},
|
||||
|
||||
async copyDocument(documentId, targetParentId, parentId, parentIds) {
|
||||
// 组装参数
|
||||
const params = {
|
||||
id: documentId, // 自身id
|
||||
toId: targetParentId,
|
||||
newParentId: targetParentId, // 目标i
|
||||
parentId: parentId, // 目标父级id
|
||||
parentIds: parentIds,
|
||||
}
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await copyDocCenterAPI(params)
|
||||
|
||||
if (res.code === 200) {
|
||||
this.$message.success('复制成功')
|
||||
this.recordAction(documentId, targetParentId)
|
||||
} else {
|
||||
this.$message.error(res.msg || res.message || '复制失败')
|
||||
}
|
||||
} catch (error) {
|
||||
this.$message.error(error.message || '复制失败')
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
recordAction(documentId, targetParentId) {
|
||||
const idx = this.copyActions.findIndex(
|
||||
(item) => item.documentId === documentId,
|
||||
)
|
||||
const payload = { documentId, targetParentId }
|
||||
if (idx > -1) {
|
||||
this.$set(this.copyActions, idx, payload)
|
||||
} else {
|
||||
this.copyActions.push(payload)
|
||||
}
|
||||
},
|
||||
|
||||
submit() {
|
||||
if (this.copyActions.length === 0) {
|
||||
return Promise.reject(new Error('请先拖拽需要复制的文件'))
|
||||
}
|
||||
return Promise.resolve({
|
||||
copied: this.copyActions,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.move-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.tree-wrapper {
|
||||
min-height: 320px;
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.move-tree {
|
||||
max-height: 360px;
|
||||
overflow: auto;
|
||||
|
||||
.tree-node {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.node-icon {
|
||||
font-size: 16px;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.node-label {
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-holder {
|
||||
height: 180px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.tips {
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
line-height: 1.8;
|
||||
background: #fdf6ec;
|
||||
border: 1px solid #f5dab1;
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
|
||||
.tips-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -10,42 +10,37 @@
|
|||
:props="treeProps"
|
||||
highlight-current
|
||||
default-expand-all
|
||||
draggable
|
||||
:allow-drag="allowDrag"
|
||||
:allow-drop="allowDrop"
|
||||
:expand-on-click-node="false"
|
||||
@node-drop="handleNodeDrop"
|
||||
@node-click="handleNodeClick"
|
||||
>
|
||||
<span class="tree-node" slot-scope="{ data }">
|
||||
<i :class="getNodeIcon(data)" class="node-icon"></i>
|
||||
<i class="el-icon-folder node-icon"></i>
|
||||
<span class="node-label" :title="data.name">
|
||||
{{ data.name }}
|
||||
</span>
|
||||
<el-tag size="mini" v-if="data.fileType === 0" type="info">
|
||||
文件夹
|
||||
</el-tag>
|
||||
</span>
|
||||
</el-tree>
|
||||
<div v-else class="empty-holder">暂无可移动数据</div>
|
||||
<div v-else class="empty-holder">暂无可移动的文件夹</div>
|
||||
</div>
|
||||
<div class="tips">
|
||||
<p class="tips-title">操作提示</p>
|
||||
<p>1. 仅文件(fileType ≠ 0)可被拖拽。</p>
|
||||
<p>2. 仅支持拖拽到文件夹节点内。</p>
|
||||
<p>3. 拖拽到目标组后会立即同步至后台。</p>
|
||||
<div v-if="selectedFolder" class="selected-info">
|
||||
<span>已选择目标文件夹:</span>
|
||||
<el-tag type="primary">{{ selectedFolder.name }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { moveDocCenterAPI } from '@/api/publicService/docCenter'
|
||||
import {
|
||||
moveDocCenterAPI,
|
||||
getMoveAndAddCopyTreeAPI,
|
||||
} from '@/api/publicService/docCenter'
|
||||
|
||||
export default {
|
||||
name: 'Move',
|
||||
props: {
|
||||
selectedNode: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
selectedFiles: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
|
|
@ -56,186 +51,100 @@ export default {
|
|||
label: 'name',
|
||||
},
|
||||
loading: false,
|
||||
moveActions: [],
|
||||
selectedFolder: null, // 选中的目标文件夹
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
selectedNode: {
|
||||
handler(newVal) {
|
||||
this.initTree(newVal)
|
||||
},
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
mounted() {
|
||||
this.getTreeData()
|
||||
},
|
||||
methods: {
|
||||
initTree(node) {
|
||||
if (!node || !node.id) {
|
||||
this.treeData = []
|
||||
return
|
||||
}
|
||||
this.treeData = [this.cloneNode(node)]
|
||||
this.moveActions = []
|
||||
},
|
||||
|
||||
cloneNode(node) {
|
||||
const childTree = Array.isArray(node.childTree)
|
||||
? node.childTree.map((child) => this.cloneNode(child))
|
||||
: []
|
||||
return {
|
||||
...node,
|
||||
childTree,
|
||||
}
|
||||
},
|
||||
|
||||
getNodeIcon(data) {
|
||||
return data.fileType == 0 ? 'el-icon-folder' : 'el-icon-document'
|
||||
},
|
||||
|
||||
// 是否允许拖拽
|
||||
allowDrag(draggingNode) {
|
||||
const data = draggingNode?.data || {}
|
||||
return data.fileType == 1
|
||||
},
|
||||
// 是否允许放下
|
||||
allowDrop(draggingNode, dropNode, type) {
|
||||
if (type !== 'inner') {
|
||||
return false
|
||||
}
|
||||
const dropData = dropNode?.data || {}
|
||||
if (dropData.fileType == 1) {
|
||||
return false
|
||||
}
|
||||
|
||||
const dragData = draggingNode?.data || {}
|
||||
if (!dragData.id || dragData.id === dropData.id) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
|
||||
async handleNodeDrop(draggingNode, dropNode) {
|
||||
const source = draggingNode?.data
|
||||
const target = dropNode?.data
|
||||
|
||||
if (!source?.id || !target?.id) {
|
||||
this.$message.error('无法获取拖拽节点信息')
|
||||
return
|
||||
}
|
||||
|
||||
await this.moveDocument(
|
||||
source.id,
|
||||
target.id,
|
||||
source.parentId,
|
||||
target.parentIds,
|
||||
)
|
||||
},
|
||||
|
||||
async moveDocument(documentId, targetParentId, parentId, parentIds) {
|
||||
// 组装参数
|
||||
const params = {
|
||||
id: documentId, // 自身id
|
||||
newParentId: targetParentId, // 目标id
|
||||
parentId: parentId, // 目标父级id
|
||||
parentIds: parentIds,
|
||||
}
|
||||
// 获取树结构数据
|
||||
async getTreeData() {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await moveDocCenterAPI(params)
|
||||
|
||||
if (res.code === 200) {
|
||||
this.$message.success('移动成功')
|
||||
this.recordAction(documentId, targetParentId)
|
||||
this.updateLocalTree(documentId, targetParentId)
|
||||
const res = await getMoveAndAddCopyTreeAPI()
|
||||
if (res.code === 200 && res.data) {
|
||||
this.treeData = Array.isArray(res.data) ? res.data : []
|
||||
} else {
|
||||
this.$message.error(res.msg || res.message || '移动失败')
|
||||
this.$message.error(
|
||||
res.msg || res.message || '获取文件夹树失败',
|
||||
)
|
||||
this.treeData = []
|
||||
}
|
||||
} catch (error) {
|
||||
this.$message.error(error.message || '移动失败')
|
||||
this.$message.error(error.message || '获取文件夹树失败')
|
||||
this.treeData = []
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
recordAction(documentId, targetParentId) {
|
||||
const idx = this.moveActions.findIndex(
|
||||
(item) => item.documentId === documentId,
|
||||
)
|
||||
const payload = { documentId, targetParentId }
|
||||
if (idx > -1) {
|
||||
this.$set(this.moveActions, idx, payload)
|
||||
} else {
|
||||
this.moveActions.push(payload)
|
||||
}
|
||||
},
|
||||
|
||||
updateLocalTree(sourceId, targetId) {
|
||||
const { node: sourceNode, parent } = this.findNodeAndParent(
|
||||
this.treeData,
|
||||
sourceId,
|
||||
)
|
||||
if (!sourceNode) {
|
||||
return
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
const newChildren = (parent.childTree || []).filter(
|
||||
(child) => child.id !== sourceId,
|
||||
)
|
||||
this.$set(parent, 'childTree', newChildren)
|
||||
} else {
|
||||
this.treeData = this.treeData.filter(
|
||||
(item) => item.id !== sourceId,
|
||||
)
|
||||
}
|
||||
|
||||
const targetNode = this.findNode(this.treeData, targetId)
|
||||
if (targetNode) {
|
||||
if (!Array.isArray(targetNode.childTree)) {
|
||||
this.$set(targetNode, 'childTree', [])
|
||||
}
|
||||
targetNode.childTree.push(sourceNode)
|
||||
}
|
||||
},
|
||||
|
||||
findNodeAndParent(nodes, id, parent = null) {
|
||||
for (const node of nodes) {
|
||||
if (node.id === id) {
|
||||
return { node, parent }
|
||||
}
|
||||
const children = node.childTree || []
|
||||
if (children.length > 0) {
|
||||
const result = this.findNodeAndParent(children, id, node)
|
||||
if (result.node) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return { node: null, parent: null }
|
||||
},
|
||||
|
||||
findNode(nodes, id) {
|
||||
for (const node of nodes) {
|
||||
if (node.id === id) {
|
||||
return node
|
||||
}
|
||||
const childResult = this.findNode(node.childTree || [], id)
|
||||
if (childResult) {
|
||||
return childResult
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
|
||||
submit() {
|
||||
if (this.moveActions.length === 0) {
|
||||
return Promise.reject(new Error('请先拖拽需要移动的文件'))
|
||||
}
|
||||
return Promise.resolve({
|
||||
moved: this.moveActions,
|
||||
// 点击树节点
|
||||
handleNodeClick(data) {
|
||||
this.selectedFolder = data
|
||||
// 设置树节点为选中状态
|
||||
this.$nextTick(() => {
|
||||
this.$refs.treeRef.setCurrentKey(data.id)
|
||||
})
|
||||
},
|
||||
|
||||
// 移动文档
|
||||
async moveDocument(fileId, targetParentId, parentId, parentIds) {
|
||||
const params = {
|
||||
id: fileId,
|
||||
newParentId: targetParentId,
|
||||
parentId: parentId,
|
||||
parentIds: parentIds,
|
||||
}
|
||||
try {
|
||||
const res = await moveDocCenterAPI(params)
|
||||
if (res.code !== 200) {
|
||||
throw new Error(res.msg || res.message || '移动失败')
|
||||
}
|
||||
return res
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// 提交移动操作
|
||||
async submit() {
|
||||
// 验证选中的文件
|
||||
if (!this.selectedFiles || this.selectedFiles.length === 0) {
|
||||
return Promise.reject(new Error('请先选择要移动的文件'))
|
||||
}
|
||||
|
||||
// 验证选中的目标文件夹
|
||||
if (!this.selectedFolder || !this.selectedFolder.id) {
|
||||
return Promise.reject(new Error('请选择目标文件夹'))
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
try {
|
||||
// 批量移动文件
|
||||
const movePromises = this.selectedFiles.map((file) =>
|
||||
this.moveDocument(
|
||||
file.id,
|
||||
this.selectedFolder.id,
|
||||
file.parentId,
|
||||
file.parentIds,
|
||||
),
|
||||
)
|
||||
|
||||
await Promise.all(movePromises)
|
||||
|
||||
this.$message.success('移动成功')
|
||||
return Promise.resolve({
|
||||
success: true,
|
||||
movedCount: this.selectedFiles.length,
|
||||
})
|
||||
} catch (error) {
|
||||
this.$message.error(error.message || '移动失败')
|
||||
return Promise.reject(error)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
@ -263,6 +172,7 @@ export default {
|
|||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex: 1;
|
||||
|
||||
.node-icon {
|
||||
font-size: 16px;
|
||||
|
|
@ -270,6 +180,7 @@ export default {
|
|||
}
|
||||
|
||||
.node-label {
|
||||
flex: 1;
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
|
@ -286,18 +197,15 @@ export default {
|
|||
color: #909399;
|
||||
}
|
||||
|
||||
.tips {
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
line-height: 1.8;
|
||||
background: #fdf6ec;
|
||||
border: 1px solid #f5dab1;
|
||||
.selected-info {
|
||||
padding: 12px;
|
||||
background: #f0f9ff;
|
||||
border: 1px solid #bae6fd;
|
||||
border-radius: 6px;
|
||||
|
||||
.tips-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,303 @@
|
|||
<template>
|
||||
<div class="move-container">
|
||||
<div class="tree-wrapper" v-loading="loading">
|
||||
<el-tree
|
||||
v-if="treeData.length"
|
||||
ref="treeRef"
|
||||
node-key="id"
|
||||
class="move-tree"
|
||||
:data="treeData"
|
||||
:props="treeProps"
|
||||
highlight-current
|
||||
default-expand-all
|
||||
draggable
|
||||
:allow-drag="allowDrag"
|
||||
:allow-drop="allowDrop"
|
||||
:expand-on-click-node="false"
|
||||
@node-drop="handleNodeDrop"
|
||||
>
|
||||
<span class="tree-node" slot-scope="{ data }">
|
||||
<i :class="getNodeIcon(data)" class="node-icon"></i>
|
||||
<span class="node-label" :title="data.name">
|
||||
{{ data.name }}
|
||||
</span>
|
||||
<el-tag size="mini" v-if="data.fileType === 0" type="info">
|
||||
文件夹
|
||||
</el-tag>
|
||||
</span>
|
||||
</el-tree>
|
||||
<div v-else class="empty-holder">暂无可移动数据</div>
|
||||
</div>
|
||||
<div class="tips">
|
||||
<p class="tips-title">操作提示</p>
|
||||
<p>1. 仅文件(fileType ≠ 0)可被拖拽。</p>
|
||||
<p>2. 仅支持拖拽到文件夹节点内。</p>
|
||||
<p>3. 拖拽到目标组后会立即同步至后台。</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { moveDocCenterAPI } from '@/api/publicService/docCenter'
|
||||
|
||||
export default {
|
||||
name: 'Move',
|
||||
props: {
|
||||
selectedNode: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
treeData: [],
|
||||
treeProps: {
|
||||
children: 'childTree',
|
||||
label: 'name',
|
||||
},
|
||||
loading: false,
|
||||
moveActions: [],
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
selectedNode: {
|
||||
handler(newVal) {
|
||||
this.initTree(newVal)
|
||||
},
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
initTree(node) {
|
||||
if (!node || !node.id) {
|
||||
this.treeData = []
|
||||
return
|
||||
}
|
||||
this.treeData = [this.cloneNode(node)]
|
||||
this.moveActions = []
|
||||
},
|
||||
|
||||
cloneNode(node) {
|
||||
const childTree = Array.isArray(node.childTree)
|
||||
? node.childTree.map((child) => this.cloneNode(child))
|
||||
: []
|
||||
return {
|
||||
...node,
|
||||
childTree,
|
||||
}
|
||||
},
|
||||
|
||||
getNodeIcon(data) {
|
||||
return data.fileType == 0 ? 'el-icon-folder' : 'el-icon-document'
|
||||
},
|
||||
|
||||
// 是否允许拖拽
|
||||
allowDrag(draggingNode) {
|
||||
const data = draggingNode?.data || {}
|
||||
return data.fileType == 1
|
||||
},
|
||||
// 是否允许放下
|
||||
allowDrop(draggingNode, dropNode, type) {
|
||||
if (type !== 'inner') {
|
||||
return false
|
||||
}
|
||||
const dropData = dropNode?.data || {}
|
||||
if (dropData.fileType == 1) {
|
||||
return false
|
||||
}
|
||||
|
||||
const dragData = draggingNode?.data || {}
|
||||
if (!dragData.id || dragData.id === dropData.id) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
|
||||
async handleNodeDrop(draggingNode, dropNode) {
|
||||
const source = draggingNode?.data
|
||||
const target = dropNode?.data
|
||||
|
||||
if (!source?.id || !target?.id) {
|
||||
this.$message.error('无法获取拖拽节点信息')
|
||||
return
|
||||
}
|
||||
|
||||
await this.moveDocument(
|
||||
source.id,
|
||||
target.id,
|
||||
source.parentId,
|
||||
target.parentIds,
|
||||
)
|
||||
},
|
||||
|
||||
async moveDocument(documentId, targetParentId, parentId, parentIds) {
|
||||
// 组装参数
|
||||
const params = {
|
||||
id: documentId, // 自身id
|
||||
newParentId: targetParentId, // 目标id
|
||||
parentId: parentId, // 目标父级id
|
||||
parentIds: parentIds,
|
||||
}
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await moveDocCenterAPI(params)
|
||||
|
||||
if (res.code === 200) {
|
||||
this.$message.success('移动成功')
|
||||
this.recordAction(documentId, targetParentId)
|
||||
this.updateLocalTree(documentId, targetParentId)
|
||||
} else {
|
||||
this.$message.error(res.msg || res.message || '移动失败')
|
||||
}
|
||||
} catch (error) {
|
||||
this.$message.error(error.message || '移动失败')
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
recordAction(documentId, targetParentId) {
|
||||
const idx = this.moveActions.findIndex(
|
||||
(item) => item.documentId === documentId,
|
||||
)
|
||||
const payload = { documentId, targetParentId }
|
||||
if (idx > -1) {
|
||||
this.$set(this.moveActions, idx, payload)
|
||||
} else {
|
||||
this.moveActions.push(payload)
|
||||
}
|
||||
},
|
||||
|
||||
updateLocalTree(sourceId, targetId) {
|
||||
const { node: sourceNode, parent } = this.findNodeAndParent(
|
||||
this.treeData,
|
||||
sourceId,
|
||||
)
|
||||
if (!sourceNode) {
|
||||
return
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
const newChildren = (parent.childTree || []).filter(
|
||||
(child) => child.id !== sourceId,
|
||||
)
|
||||
this.$set(parent, 'childTree', newChildren)
|
||||
} else {
|
||||
this.treeData = this.treeData.filter(
|
||||
(item) => item.id !== sourceId,
|
||||
)
|
||||
}
|
||||
|
||||
const targetNode = this.findNode(this.treeData, targetId)
|
||||
if (targetNode) {
|
||||
if (!Array.isArray(targetNode.childTree)) {
|
||||
this.$set(targetNode, 'childTree', [])
|
||||
}
|
||||
targetNode.childTree.push(sourceNode)
|
||||
}
|
||||
},
|
||||
|
||||
findNodeAndParent(nodes, id, parent = null) {
|
||||
for (const node of nodes) {
|
||||
if (node.id === id) {
|
||||
return { node, parent }
|
||||
}
|
||||
const children = node.childTree || []
|
||||
if (children.length > 0) {
|
||||
const result = this.findNodeAndParent(children, id, node)
|
||||
if (result.node) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return { node: null, parent: null }
|
||||
},
|
||||
|
||||
findNode(nodes, id) {
|
||||
for (const node of nodes) {
|
||||
if (node.id === id) {
|
||||
return node
|
||||
}
|
||||
const childResult = this.findNode(node.childTree || [], id)
|
||||
if (childResult) {
|
||||
return childResult
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
|
||||
submit() {
|
||||
if (this.moveActions.length === 0) {
|
||||
return Promise.reject(new Error('请先拖拽需要移动的文件'))
|
||||
}
|
||||
return Promise.resolve({
|
||||
moved: this.moveActions,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.move-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.tree-wrapper {
|
||||
min-height: 320px;
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.move-tree {
|
||||
max-height: 360px;
|
||||
overflow: auto;
|
||||
|
||||
.tree-node {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.node-icon {
|
||||
font-size: 16px;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.node-label {
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-holder {
|
||||
height: 180px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.tips {
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
line-height: 1.8;
|
||||
background: #fdf6ec;
|
||||
border: 1px solid #f5dab1;
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
|
||||
.tips-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue