FileManage/web/src/views/testdata/fileSynchronization/index.vue

1130 lines
44 KiB
Vue
Raw Normal View History

2025-02-28 09:11:25 +08:00
<script lang="ts">
export default {
2025-03-19 09:17:05 +08:00
name: "datamanagement",//试验数据管理
2025-02-28 09:11:25 +08:00
};
</script>
<script setup lang="ts">
import { ref, onMounted, nextTick, defineAsyncComponent } from "vue";
import { Search } from '@element-plus/icons-vue'
import { ElMessageBox, ElMessage } from "element-plus";
2025-03-14 09:00:42 +08:00
import { tstaskList, getTsNodesTree, tsFilesPage, deleteTsFilesByIds } from "@/api/datamanagement";
2025-03-07 10:43:38 +08:00
import { listLocalAndBackup, compare, uploadToBackup, downloadToLocal, deleteTsFilesById } from "@/api/fileSynchronization";
2025-03-23 16:55:46 +08:00
//text文件编辑功能
import textEdit from '@/components/textEditing/index.vue'
2025-03-03 11:29:41 +08:00
// 文件预览相关
import useFileUpload from "@/components/file/file/useFileUpload";
import useHeaderStorageList from "@/components/header/useHeaderStorageList";
import useFileData from "@/components/file/file/useFileData";
import Viewfile from '@/views/component/Viewfile.vue'
import useFilePreview from "@/components/file/file/useFilePreview";
import MarkdownViewerAsyncLoading from "@/components/file/preview/MarkdownViewerAsyncLoading.vue";
import VideoPlayerAsyncLoading from "@/components/file/preview/VideoPlayerAsyncLoading.vue";
import TextViewerAsyncLoading from "@/components/file/preview/TextViewerAsyncLoading.vue";
import MarkdownViewerDialogAsyncLoading from "@/components/file/preview/MarkdownViewerDialogAsyncLoading.vue";
const { dialogVideoVisible, dialogTextVisible, dialogPdfVisible, dialogOfficeVisible, dialog3dVisible } = useFilePreview();
const { clearALlFinishedUploadFile } = useFileUpload();
const { currentStorageKey } = useHeaderStorageList();
const { openRow } = useFileData();
2025-02-28 09:11:25 +08:00
//左侧树的选择框
const projectId = ref()
const projectArr = ref([])
//左侧树配置
const pathid = ref()
const treeRef = ref();
const treedata = ref([])
const treeloading = ref(false)
const defaultProps = {
children: "children",
label: "nodeName"
};
const treeForm = ref({
taskId: '',
nodeName: '',
})
//工作空间
const props = {
label: 'fileName',
2025-03-07 10:43:38 +08:00
children: 'children',
2025-02-28 09:11:25 +08:00
}
const workloading = ref(false)
const worktree = ref(false)
const workdata = ref([])
2025-03-07 10:43:38 +08:00
const workall = ref([])
const workref = ref()
2025-03-03 11:29:41 +08:00
//备份空间
const backupsdata = ref([])
// 右键菜单相关
const contextMenuVisible = ref(false)
2025-03-07 10:43:38 +08:00
const contextMenuVisible2 = ref(false)
2025-03-03 11:29:41 +08:00
const contextMenuPosition = ref({ x: 0, y: 0 })
const currentNode = ref<any>(null)
//文件差异性对比
2025-03-03 11:32:37 +08:00
const differential = ref(false)
2025-03-07 10:43:38 +08:00
function diffFile() {
const seen = new Set();
comparearr.value = comparearr.value.filter(item => {
return seen.has(item.id) ? false : seen.add(item.id);
});
2025-03-03 11:32:37 +08:00
differential.value = true
2025-03-07 10:43:38 +08:00
getchayi()
}
//文件差异性对比结果
const comparearr: any = ref([])
const localOnlyFiles: any = ref([])
const md5MismatchedFiles: any = ref([])
const minioOnlyFiles: any = ref([])
const loading = ref(false)
function getchayi() {
loading.value = true
const ids = []
const params: any = {}
if (comparearr.value.length > 0) {
comparearr.value.forEach((item: any) => {
ids.push(item.id)
})
params.id = ids.join(',')
} else {
params.nodeId = pathid.value
params.taskId = projectId.value
}
compare(params).then((res: any) => {
localOnlyFiles.value = res.data.localOnlyFiles
md5MismatchedFiles.value = res.data.md5MismatchedFiles
minioOnlyFiles.value = res.data.minioOnlyFiles
loading.value = false
diffColor()
})
}
//差异染色
function diffColor() {
if (workdata.value.length > 0 && localOnlyFiles.value.length > 0) {
workdata.value = changeColor(workdata.value, localOnlyFiles.value, '1')
}
if (workdata.value.length > 0 && md5MismatchedFiles.value.length > 0) {
workdata.value = changeColor(workdata.value, md5MismatchedFiles.value, '2')
backupsdata.value = changeColor(backupsdata.value, md5MismatchedFiles.value, '2')
}
if (backupsdata.value.length > 0 && minioOnlyFiles.value.length > 0) {
backupsdata.value = changeColor(backupsdata.value, minioOnlyFiles.value, '3')
}
workref.value!.setCheckedKeys([], false)
beifentree.value!.setCheckedKeys([], false)
}
//状态改变递归
function changeColor(data: any, data2: any, type: any) {
data.forEach((item: any) => {
data2.forEach((item2: any) => {
if (item.id == item2.id) {
item.station = type
}
if (item.children.length > 0) {
changeColor(item.children, data2, type)
}
})
})
return data
2025-03-03 11:32:37 +08:00
}
2025-03-03 11:29:41 +08:00
function diffClose() {
differential.value = false
2025-03-07 10:43:38 +08:00
getWorkData()
2025-03-03 11:29:41 +08:00
}
2025-02-28 09:11:25 +08:00
//获取所有项目
function getProject() {
tstaskList().then((res: any) => {
projectArr.value = res.data
projectId.value = projectArr.value[0].id
gettreedata()
})
}
//获取树数据
function gettreedata() {
treeloading.value = true
treeForm.value.taskId = projectId.value
getTsNodesTree(treeForm.value).then((res: any) => {
treedata.value = res.data
treeloading.value = false
pathid.value = treedata.value[0].nodeId
nextTick(() => {
treeRef.value?.setCurrentKey(pathid.value);
});
2025-03-03 11:29:41 +08:00
getWorkData()
2025-02-28 09:11:25 +08:00
})
}
function handleNodeClick(data: any, node: any) {
pathid.value = data.nodeId
2025-03-03 11:29:41 +08:00
getWorkData()
2025-02-28 09:11:25 +08:00
}
//工作空间树点击
const handleCheckChange = (
data: any,
checked: boolean,
indeterminate: boolean
) => {
2025-03-07 10:43:38 +08:00
if (checked == true && indeterminate == false) {
workall.value.push(data)
} else {
workall.value = workall.value.filter(item => item.id !== data.id);
}
comparearr.value = getclickdata(workall.value)
beifentree.value!.setCheckedNodes(comparearr.value, false)
}
//递归整理点击参数
function getclickdata(data: any) {
data.forEach((item: any) => {
if (item.isFile == "FOLDER") {
item.children.forEach((child: any) => {
data = data.filter((items: any) => items.id !== child.id);
if (child.children.length > 0) {
getclickdata(child.children)
}
})
}
});
return data
2025-02-28 09:11:25 +08:00
}
//工作空间树获取
2025-03-03 11:29:41 +08:00
//获取工作空间数据
function getWorkData() {
2025-03-07 10:43:38 +08:00
worktree.value = true
listLocalAndBackup({ taskId: projectId.value, nodeId: pathid.value }).then((res: any) => {
workdata.value = res.data.localTrees
backupsdata.value = res.data.minioTrees
if (workdata.value.length > 0) {
assignment(workdata.value, 'local')
}
if (backupsdata.value.length > 0) {
assignment(backupsdata.value, 'minio')
}
worktree.value = false
diffColor()
2025-02-28 09:11:25 +08:00
})
}
2025-03-07 10:43:38 +08:00
//数据递归赋值
function assignment(data: any, difference: any) {
data.forEach((item: any) => {
item.location = difference
item.station = '0' //0相同1新增2变更3删除
if (item.children.length > 0) {
assignment(item.children, difference)
}
})
}
2025-03-03 11:29:41 +08:00
//备份空间
2025-03-07 10:43:38 +08:00
const beifentree = ref()
2025-03-03 11:29:41 +08:00
function backupsChange(
data: any,
checked: boolean,
indeterminate: boolean
) {
2025-03-07 10:43:38 +08:00
if (checked == true && indeterminate == false) {
workall.value.push(data)
} else {
workall.value = workall.value.filter(item => item.id !== data.id);
}
comparearr.value = getclickdata(workall.value)
workref.value!.setCheckedNodes(comparearr.value, false)
2025-03-03 11:29:41 +08:00
}
//右键菜单
const handleRightClick = (event: Event, data: any, node: any) => {
2025-03-07 10:43:38 +08:00
if (data.station != '0') {
event.preventDefault()
currentNode.value = data
if (data.location == 'local') {
contextMenuVisible.value = true
} else {
contextMenuVisible2.value = true
}
contextMenuPosition.value = {
x: (event as MouseEvent).clientX + 10,
y: (event as MouseEvent).clientY
}
} else {
contextMenuVisible.value = false
contextMenuVisible2.value = false
2025-03-03 11:29:41 +08:00
}
2025-03-07 10:43:38 +08:00
}
function workclick() {
contextMenuVisible.value = false
contextMenuVisible2.value = false
2025-03-03 11:29:41 +08:00
}
const handleMenuClick = (action: string, type: any) => {
contextMenuVisible.value = false
2025-03-07 10:43:38 +08:00
contextMenuVisible2.value = false
2025-03-03 11:29:41 +08:00
switch (action) {
case 'restore':
// 处理恢复逻辑
2025-03-07 10:43:38 +08:00
ElMessageBox.confirm(
'您确定要将该文件/文件夹恢复到工作空间吗?',
'警告',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
const params = [{
path: currentNode.value.path,
name: currentNode.value.fileName,
size: currentNode.value.fileSize,
type: currentNode.value.isFile
}]
downloadToLocal({ parameterLists: params }).then((res: any) => {
if (res.code == '0') {
ElMessage.success("恢复成功")
minioOnlyFiles.value = minioOnlyFiles.value.filter((item: any) => item.id !== currentNode.value.id)
md5MismatchedFiles.value = md5MismatchedFiles.value.filter((item: any) => item.id !== currentNode.value.id)
getWorkData()
}
})
})
2025-03-03 11:29:41 +08:00
break
case 'backups':
// 处理备份逻辑
2025-03-07 10:43:38 +08:00
ElMessageBox.confirm(
'您确定要将该文件/文件夹上传到备份空间吗?',
'警告',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
const params = [{
path: currentNode.value.path,
name: currentNode.value.fileName,
size: currentNode.value.fileSize,
type: currentNode.value.isFile
}]
uploadToBackup({ parameterLists: params }).then((res: any) => {
if (res.code == '0') {
ElMessage.success("上传成功")
localOnlyFiles.value = localOnlyFiles.value.filter((item: any) => item.id !== currentNode.value.id)
md5MismatchedFiles.value = md5MismatchedFiles.value.filter((item: any) => item.id !== currentNode.value.id)
getWorkData()
// getchayi()
}
})
})
2025-03-03 11:29:41 +08:00
break
case 'delete':
// 处理删除逻辑
2025-03-07 10:43:38 +08:00
deleteTsFilesById({ id: currentNode.value.id, type: type }).then((res: any) => {
if (res.code == '0') {
ElMessage.success("删除成功")
getWorkData()
}
})
2025-03-03 11:29:41 +08:00
break
case 'preview':
// 处理预览逻辑
2025-03-07 10:43:38 +08:00
openPreview(currentNode.value)
2025-03-03 11:29:41 +08:00
break
}
}
// 点击其他地方关闭菜单
document.addEventListener('click', () => {
contextMenuVisible.value = false
2025-03-07 10:43:38 +08:00
contextMenuVisible2.value = false
2025-03-03 11:29:41 +08:00
})
2025-03-07 10:43:38 +08:00
//备份操作
function tableBeifen(row: any) {
ElMessageBox.confirm(
'您确定要将该文件/文件夹上传到备份空间吗?',
'警告',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
const params = [{
path: row.path,
name: row.name,
size: row.size,
type: row.type
}]
uploadToBackup({ parameterLists: params }).then((res: any) => {
if (res.code == '0') {
ElMessage.success("上传成功")
localOnlyFiles.value = localOnlyFiles.value.filter((item: any) => item.id !== row.id)
md5MismatchedFiles.value = md5MismatchedFiles.value.filter((item: any) => item.id !== row.id)
}
})
})
}
const beifenArr = ref([])
function bifenChange(row: any) {
beifenArr.value = row
}
function moretableBeifen() {
ElMessageBox.confirm(
'您确定要将这些文件/文件夹上传到备份空间吗?',
'警告',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
uploadToBackup({ parameterLists: beifenArr.value }).then((res: any) => {
if (res.code == '0') {
ElMessage.success("上传成功")
beifenArr.value.forEach((items: any) => {
localOnlyFiles.value = localOnlyFiles.value.filter((item: any) => item.id !== items.id)
md5MismatchedFiles.value = md5MismatchedFiles.value.filter((item: any) => item.id !== items.id)
})
}
})
})
}
//恢复操作
function tablerestore(row: any) {
ElMessageBox.confirm(
'您确定要将该文件/文件夹恢复到工作空间吗?',
'警告',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
const params = [{
path: row.path,
name: row.name,
size: row.size,
type: row.type
}]
downloadToLocal({ parameterLists: params }).then((res: any) => {
if (res.code == '0') {
ElMessage.success("恢复成功")
minioOnlyFiles.value = minioOnlyFiles.value.filter((item: any) => item.id !== row.id)
md5MismatchedFiles.value = md5MismatchedFiles.value.filter((item: any) => item.id !== row.id)
}
})
})
}
const restoreArr = ref([])
function restoreChange(row: any) {
restoreArr.value = row
}
function moretablerestore() {
ElMessageBox.confirm(
'您确定要将这些文件/文件夹恢复到工作空间吗?',
'警告',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
downloadToLocal({ parameterLists: restoreArr.value }).then((res: any) => {
if (res.code == '0') {
ElMessage.success("恢复成功")
2025-03-14 09:00:42 +08:00
restoreArr.value.forEach((items: any) => {
minioOnlyFiles.value = minioOnlyFiles.value.filter((item: any) => item.id !== items.id)
md5MismatchedFiles.value = md5MismatchedFiles.value.filter((item: any) => item.id !== items.id)
})
}
})
})
}
function delhuifu() {
ElMessageBox.confirm(
'您确定要删除这些文件/文件夹吗?(删除后将无法恢复)',
'警告',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
const ids = ref([])
restoreArr.value.forEach((item: any) => {
ids.value.push(item.id)
})
// debugger
deleteTsFilesByIds({ id: ids.value.join(','), type: 'minio' }).then((res: any) => {
if (res.code == '0') {
ElMessage.success("删除成功")
restoreArr.value.forEach((items: any) => {
minioOnlyFiles.value = minioOnlyFiles.value.filter((item: any) => item.id !== items.id)
2025-03-07 10:43:38 +08:00
})
}
})
})
}
//变更
const changeclick = ref([])
function handleChange(row: any) {
changeclick.value = row
}
function bfclick(type: any) {
let tstitle = ''
if (type == 'bf') {
tstitle = '您确定要将这些文件/文件夹上传到备份空间吗?'
} else {
tstitle = '您确定要将这些文件/文件夹恢复到工作空间吗'
}
ElMessageBox.confirm(
tstitle,
'警告',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
if (type == 'bf') {
uploadToBackup({ parameterLists: changeclick.value }).then((res: any) => {
if (res.code == '0') {
ElMessage.success("上传成功")
changeclick.value.forEach((items: any) => {
md5MismatchedFiles.value = md5MismatchedFiles.value.filter((item: any) => item.id !== items.id)
})
}
})
} else {
downloadToLocal({ parameterLists: changeclick.value }).then((res: any) => {
if (res.code == '0') {
ElMessage.success("恢复成功")
changeclick.value.forEach((items: any) => {
md5MismatchedFiles.value = md5MismatchedFiles.value.filter((item: any) => item.id !== items.id)
})
}
})
}
})
}
2025-02-28 09:11:25 +08:00
onMounted(() => {
getProject()
});
//拖动条
const vMove = {
mounted(el: any) {
el.onmousedown = function (e: any) {
var init = e.clientX;
var parent: any = document.getElementById("silderLeft");
const initWidth: any = parent.offsetWidth;
document.onmousemove = function (e) {
var end = e.clientX;
var newWidth = end - init + initWidth;
parent.style.width = newWidth + "px";
};
document.onmouseup = function () {
document.onmousemove = document.onmouseup = null;
};
};
}
}
2025-03-03 11:29:41 +08:00
//预览
// 文件预览相关, 视频、音频、文本、图片
const VideoPlayer = defineAsyncComponent({
loader: () => import("@/components/file/preview/VideoPlayer.vue"),
loadingComponent: VideoPlayerAsyncLoading
})
const TextViewer = defineAsyncComponent({
loader: () => import("@/components/file/preview/TextViewer.vue"),
loadingComponent: TextViewerAsyncLoading
})
const MarkdownViewer = defineAsyncComponent({
loader: () => import("@/components/file/preview/MarkdownViewer.vue"),
loadingComponent: MarkdownViewerDialogAsyncLoading
})
const PdfViewer = defineAsyncComponent({
loader: () => import("@/components/file/preview/PdfViewer.vue"),
loadingComponent: MarkdownViewerDialogAsyncLoading
})
const OfficeViewer = defineAsyncComponent({
loader: () => import("@/components/file/preview/OfficeViewer.vue"),
loadingComponent: MarkdownViewerDialogAsyncLoading
})
const Three3dPreview = defineAsyncComponent({
loader: () => import("@/components/file/preview/Three3dPreview.vue"),
loadingComponent: MarkdownViewerDialogAsyncLoading
})
2025-03-07 10:43:38 +08:00
//单击预览
const filePreview: any = ref({})
const ViewfileUrl = ref("")
const title1 = ref('')
const isViewfile = ref(false)
const fileType = ref('')
function openPreview(row: any) {
2025-03-23 16:55:46 +08:00
// debugger
if (getFileExtension(row.fileName) == 'pdf' || getFileExtension(row.fileName) == 'pptx' || getFileExtension(row.fileName) == 'xlsx' || getFileExtension(row.fileName) == 'xls' || getFileExtension(row.fileName) == 'docx' || getFileExtension(row.fileName) == 'doc' || getFileExtension(row.fileName) == 'bin') {
// debugger
2025-03-07 10:43:38 +08:00
title1.value = row.fileName
ViewfileUrl.value = row.url
isViewfile.value = true
fileType.value = getFileExtension(row.fileName)
2025-03-23 16:55:46 +08:00
} else if (getFileExtension(row.fileName) == 'txt') {
testClick(row)
2025-03-07 10:43:38 +08:00
} else {
row.fileType = getFileType(row.fileName)
filePreview.value = row
localStorage.setItem('videorow', JSON.stringify(row));
openRow(row)
}
}
2025-03-23 16:55:46 +08:00
const textedit = ref(false)
const rowId = ref('')
const title = ref('')
function testClick(row: any) {
rowId.value = row.id
textedit.value = true
title.value = '编辑 - ' + row.fileName
}
function textClose() {
textedit.value = false
}
2025-03-07 10:43:38 +08:00
function getFileExtension(filename: any) {
// 获取点号在文件名中的位置(从后往前查找)
const lastIndex = filename.lastIndexOf('.');
// 如果点号存在且不是文件名的第一个字符,则返回后缀名
if (lastIndex !== -1 && lastIndex !== 0) {
return filename.substring(lastIndex + 1);
} else {
// 如果没有找到点号,则返回空字符串或根据需要返回其他值
return '';
}
}
// 文件分类
const fileTypeMap = {
image: ['gif', 'jpg', 'jpeg', 'png', 'bmp', 'webp', 'ico'],
video: ['mp4', 'webm', 'm3u8', 'rmvb', 'avi', 'swf', '3gp', 'mkv', 'flv'],
audio: ['mp3', 'wav', 'wma', 'ogg', 'aac', 'flac', 'm4a'],
text: ['scss', 'sass', 'kt', 'gitignore', 'bat', 'properties', 'yml', 'css', 'js', 'md', 'xml', 'txt', 'py', 'go', 'html', 'less', 'php', 'rb', 'rust', 'script', 'java', 'sh', 'sql'],
executable: ['exe', 'dll', 'com', 'vbs'],
archive: ['7z', 'zip', 'rar', 'tar', 'gz'],
pdf: ['pdf'],
office: ['doc', 'docx', 'csv', 'xls', 'xlsx', "ppt", 'pptx'],
three3d: ['dae', 'fbx', 'gltf', 'glb', 'obj', 'ply', 'stl'],
document: ['txt', 'pages', 'epub', 'numbers', 'keynote']
};
function getFileType(name: any) {
let fileType: any;
for (let key in fileTypeMap) {
let suffix = getFileSuffix(name);
if (fileTypeMap[key].indexOf(suffix) !== -1) {
fileType = key;
break;
}
}
return fileType;
}
function getFileSuffix(name: any) {
let lastIndex = name.lastIndexOf('.');
if (lastIndex === -1) {
return 'other';
}
return name.substring(lastIndex + 1).toLowerCase();
}
function CloseView() {
isViewfile.value = false
}
2025-02-28 09:11:25 +08:00
</script>
<template>
<div class="faulttemplate-box">
<aside id="silderLeft">
<div>
<el-select v-model="projectId" placeholder="请选择项目" @change="gettreedata()">
<el-option v-for="item in projectArr" :key="item.id" :label="item.taskName" :value="item.id" />
</el-select>
<div class="tree_sou">
<el-input v-model="treeForm.nodeName" style="width:100%;margin:10px 9px 10px 0px;"
placeholder="节点名称" clearable :suffix-icon="Search" @change="gettreedata()" />
</div>
<el-tree ref="treeRef" node-key="nodeId" :data="treedata" :highlight-current="true"
:props="defaultProps" v-loading="treeloading" @node-click="handleNodeClick"
class="silderLeft-default" style="height: calc(100vh - 280px); overflow: auto;margin-top: 10px;">
<template #default="{ data }">
<span class="custom-tree-node">
<span class="text">{{ data.nodeName }}</span>
</span>
</template>
</el-tree>
</div>
<div class="moveBtn" v-move>
<div class="moveBtn-line"></div>
</div>
</aside>
<section class="silderRight">
2025-03-07 10:43:38 +08:00
<div class="tree_button">
<el-button type="primary" @click="diffFile()">文件差异性对比</el-button>
<el-button type="primary" @click="differential = true">查看本次差异性对比</el-button>
</div>
2025-02-28 09:11:25 +08:00
<div class="tree_box">
<div class="tree_left">
<div class="tree_title">工作空间:</div>
2025-03-07 10:43:38 +08:00
<div class="tree_content">
2025-03-03 11:29:41 +08:00
<el-scrollbar height="73vh">
2025-03-07 10:43:38 +08:00
<el-tree ref="workref" style="max-width: 600px" :props="props" :data="workdata"
@node-click="workclick" @node-contextmenu="handleRightClick" default-expand-all
:expand-on-click-node="false" show-checkbox node-key="id" v-loading="worktree"
@check-change="handleCheckChange">
2025-03-03 11:29:41 +08:00
<template #default="{ data }">
2025-03-07 10:43:38 +08:00
<span
:class="data.station == '0' ? 'custom-tree-node' : (data.station == '1' ? 'custom-tree-node1' : (data.station == '2' ? 'custom-tree-node2' : 'custom-tree-node3'))">
<span class="text">
{{ data.fileName }}({{ data.isFile == 'FOLDER' ? data.children.length :
data.fileSize + 'MB' }})
<span v-if="data.station != '0'">({{ data.station == '1' ? '新增' :
(data.station
== '2' ? '已变更' : '已删除') }})</span>
</span>
2025-03-03 11:29:41 +08:00
</span>
</template>
</el-tree>
<div v-show="contextMenuVisible" class="context-menu" :style="{
left: `${contextMenuPosition.x}px`,
top: `${contextMenuPosition.y}px`
}">
<div class="menu-item" @click="handleMenuClick('backups', 'local')">备份</div>
<div class="menu-item" @click="handleMenuClick('delete', 'local')">删除</div>
<div class="menu-item" @click="handleMenuClick('preview', 'local')">预览</div>
</div>
</el-scrollbar>
</div>
</div>
<div class="tree_left">
<div class="tree_title">备份空间:</div>
2025-03-07 10:43:38 +08:00
<div class="tree_content">
2025-03-03 11:29:41 +08:00
<el-scrollbar height="73vh">
2025-03-07 10:43:38 +08:00
<el-tree ref="beifentree" style="max-width: 600px" :props="props" :data="backupsdata"
default-expand-all @node-click="workclick" @node-contextmenu="handleRightClick"
:expand-on-click-node="false" show-checkbox node-key="id" v-loading="worktree"
@check-change="backupsChange">
2025-03-03 11:29:41 +08:00
<template #default="{ data }">
2025-03-07 10:43:38 +08:00
<span
:class="data.station == '0' ? 'custom-tree-node' : (data.station == '1' ? 'custom-tree-node1' : (data.station == '2' ? 'custom-tree-node2' : 'custom-tree-node3'))">
2025-03-03 11:29:41 +08:00
<!-- data.children.length -->
2025-03-07 10:43:38 +08:00
<span class="text">{{ data.fileName }}({{ data.isFile == 'FOLDER' ?
data.children.length :
2025-03-03 11:29:41 +08:00
data.fileSize + 'MB' }})</span>
2025-03-07 10:43:38 +08:00
<span v-if="data.station != '0'">({{ data.station == '1' ? '新增' :
(data.station
== '2' ? '已变更' : '已删除') }})</span>
2025-03-03 11:29:41 +08:00
</span>
</template>
</el-tree>
2025-03-07 10:43:38 +08:00
<div v-show="contextMenuVisible2" class="context-menu" :style="{
2025-03-03 11:29:41 +08:00
left: `${contextMenuPosition.x}px`,
top: `${contextMenuPosition.y}px`
}">
<div class="menu-item" @click="handleMenuClick('restore', 'minio')">恢复</div>
<div class="menu-item" @click="handleMenuClick('delete', 'minio')">删除</div>
<div class="menu-item" @click="handleMenuClick('preview', 'minio')">预览</div>
</div>
</el-scrollbar>
2025-02-28 09:11:25 +08:00
</div>
</div>
2025-03-03 11:29:41 +08:00
<!-- 文件差异性对比 -->
<el-dialog title="文件差异性对比" v-model="differential" width="50%" :before-close="diffClose" top="30px"
draggable destroy-on-close>
<div>
<el-scrollbar height="400px">
<div class="newContent">
<div class="newContent_title"><span class="newContent_title_text">新增内容</span>
2025-03-07 10:43:38 +08:00
<el-button type="primary" :disabled="beifenArr.length == 0"
@click="moretableBeifen()">备份</el-button>
2025-03-03 11:29:41 +08:00
</div>
2025-03-07 10:43:38 +08:00
<el-table v-loading="loading" :data="localOnlyFiles" @selection-change="bifenChange"
2025-03-03 11:29:41 +08:00
:header-cell-style="{ background: 'rgb(250 250 250)', height: '50px' }"
style="width: 100%; height: calc(30vh);margin-bottom: 20px;" border>
<el-table-column type="selection" width="40" />
<!-- <el-table-column type="index" label="序号" width="70" align="center"></el-table-column> -->
2025-03-07 10:43:38 +08:00
<el-table-column prop="name" label="文件名称"></el-table-column>
<el-table-column prop="type" label="文件类型" width="90">
2025-03-03 11:29:41 +08:00
<template #default="scope">
2025-03-07 10:43:38 +08:00
<span v-if="scope.row.type == 'FOLDER'">文件夹</span>
<span v-else>文件</span>
2025-03-03 11:29:41 +08:00
</template>
</el-table-column>
2025-03-07 10:43:38 +08:00
<el-table-column prop="size" label="文件大小" width="100">
2025-03-03 11:29:41 +08:00
<template #default="scope">
2025-03-07 10:43:38 +08:00
<span v-if="scope.row.type != 'FOLDER'">{{ scope.row.size + 'MB' }}</span>
</template>
</el-table-column>
<el-table-column prop="path" label="路径"></el-table-column>
2025-03-14 09:00:42 +08:00
<el-table-column prop="formattedTime" width="170" label="修改日期"></el-table-column>
2025-03-07 10:43:38 +08:00
<el-table-column fixed="right" label="操作" width="60" align="center">
<template #default="scope">
<span style="display: flex;justify-content:center;">
<img @click="tableBeifen(scope.row)" style="cursor: pointer;" title="备份"
src="@/assets/images/beifen.png" alt="">
2025-03-03 11:29:41 +08:00
</span>
</template>
</el-table-column>
</el-table>
</div>
<div class="newContent">
<div class="newContent_title"><span class="newContent_title_text">变更内容</span>
2025-03-07 10:43:38 +08:00
<div>
<el-button type="primary" :disabled="changeclick.length == 0"
@click="bfclick('bei')">备份</el-button>
<el-button type="primary" :disabled="changeclick.length == 0"
@click="bfclick('hui')">恢复</el-button>
</div>
2025-03-03 11:29:41 +08:00
</div>
2025-03-07 10:43:38 +08:00
<el-table v-loading="loading" :data="md5MismatchedFiles"
@selection-change="handleChange"
2025-03-03 11:29:41 +08:00
:header-cell-style="{ background: 'rgb(250 250 250)', height: '50px' }"
style="width: 100%; height: calc(30vh);margin-bottom: 20px;" border>
<el-table-column type="selection" width="40" />
<!-- <el-table-column type="index" label="序号" width="70" align="center"></el-table-column> -->
2025-03-07 10:43:38 +08:00
<el-table-column prop="name" label="文件名称"></el-table-column>
<el-table-column prop="type" label="文件类型" width="90">
2025-03-03 11:29:41 +08:00
<template #default="scope">
2025-03-07 10:43:38 +08:00
<span v-if="scope.row.type == 'FOLDER'">文件夹</span>
<span v-else>文件</span>
2025-03-03 11:29:41 +08:00
</template>
</el-table-column>
2025-03-07 10:43:38 +08:00
<el-table-column prop="size" label="文件大小" width="100">
2025-03-03 11:29:41 +08:00
<template #default="scope">
2025-03-07 10:43:38 +08:00
{{ scope.row.size + 'MB' }}
</template>
</el-table-column>
<el-table-column prop="path" label="路径"></el-table-column>
2025-03-14 09:00:42 +08:00
<el-table-column prop="formattedTime" width="170" label="修改日期"></el-table-column>
2025-03-07 10:43:38 +08:00
<el-table-column label="操作" width="80" align="center">
<template #default="scope">
<span style="display: flex;justify-content:space-around;">
<img @click="tableBeifen(scope.row)" style="cursor: pointer;" title="备份"
src="@/assets/images/beifen.png" alt="">
<img @click="tablerestore(scope.row)" style="cursor: pointer;"
title="恢复" src="@/assets/images/huifu.png" alt="">
2025-03-03 11:29:41 +08:00
</span>
</template>
</el-table-column>
</el-table>
</div>
<div class="newContent">
2025-03-07 10:43:38 +08:00
<div class="newContent_title"><span class="newContent_title_text">缺失内容</span>
2025-03-14 09:00:42 +08:00
<div>
<el-button type="primary" :disabled="restoreArr.length == 0"
@click="moretablerestore()">恢复</el-button>
<el-button type="primary" :disabled="restoreArr.length == 0"
@click="delhuifu()">删除</el-button>
</div>
2025-03-07 10:43:38 +08:00
</div>
<el-table v-loading="loading" :data="minioOnlyFiles" @selection-change="restoreChange"
2025-03-03 11:29:41 +08:00
:header-cell-style="{ background: 'rgb(250 250 250)', height: '50px' }"
style="width: 100%; height: calc(30vh);margin-bottom: 20px;" border>
<el-table-column type="selection" width="40" />
<!-- <el-table-column type="index" label="序号" width="70" align="center"></el-table-column> -->
2025-03-07 10:43:38 +08:00
<el-table-column prop="name" label="文件名称"></el-table-column>
<el-table-column prop="type" label="文件类型" width="90">
2025-03-03 11:29:41 +08:00
<template #default="scope">
2025-03-07 10:43:38 +08:00
<span v-if="scope.row.type == 'FOLDER'">文件夹</span>
<span v-else>文件</span>
2025-03-03 11:29:41 +08:00
</template>
</el-table-column>
2025-03-07 10:43:38 +08:00
<el-table-column prop="size" label="文件大小" width="100">
<template #default="scope">
{{ scope.row.size + 'MB' }}
</template>
</el-table-column>
<el-table-column prop="path" label="路径"></el-table-column>
2025-03-14 09:00:42 +08:00
<el-table-column prop="formattedTime" width="170" label="修改日期"></el-table-column>
<el-table-column fixed="right" label="操作" width="60" align="center">
2025-03-03 11:29:41 +08:00
<template #default="scope">
2025-03-07 10:43:38 +08:00
<span style="display: flex;justify-content:center;">
<img @click="tablerestore(scope.row)" style="cursor: pointer;"
title="恢复" src="@/assets/images/huifu.png" alt="">
2025-03-03 11:29:41 +08:00
</span>
</template>
</el-table-column>
</el-table>
</div>
</el-scrollbar>
</div>
</el-dialog>
<!-- 组件预览 -->
<!-- 视频播放器 -->
2025-03-07 10:43:38 +08:00
<el-dialog draggable class="zfile-video-dialog" :destroy-on-close="true" v-model="dialogVideoVisible">
2025-03-03 11:29:41 +08:00
<video-player v-if="dialogVideoVisible" ref="videoPlayer" />
2025-03-07 10:43:38 +08:00
</el-dialog>
2025-03-03 11:29:41 +08:00
<!-- 文本编辑器 -->
2025-03-07 10:43:38 +08:00
<el-dialog draggable class="zfile-text-dialog zfile-dialog-mini-close" :destroy-on-close="true"
2025-03-03 11:29:41 +08:00
:title="filePreview.fileName" v-model="dialogTextVisible">
<TextViewer :file-name="filePreview.fileName" :file-url="filePreview.url"
v-if="dialogTextVisible && filePreview.fileName.indexOf('.md') === -1" />
<MarkdownViewer :file-name="filePreview.fileName" :file-url="filePreview.url"
v-if="dialogTextVisible && filePreview.fileName.indexOf('.md') !== -1" />
2025-03-07 10:43:38 +08:00
</el-dialog>
2025-03-03 11:29:41 +08:00
<!-- pdf 在线预览 -->
2025-03-07 10:43:38 +08:00
<el-dialog draggable class="zfile-pdf-dialog" :title="filePreview.fileName" v-model="dialogPdfVisible">
2025-03-03 11:29:41 +08:00
<PdfViewer :file-name="filePreview.fileName" :file-url="filePreview.url" v-if="dialogPdfVisible" />
2025-03-07 10:43:38 +08:00
</el-dialog>
2025-03-03 11:29:41 +08:00
<!-- office 在线预览 -->
2025-03-07 10:43:38 +08:00
<el-dialog draggable class="zfile-office-dialog zfile-dialog-mini-close zfile-dialog-hidden-title"
2025-03-03 11:29:41 +08:00
:title="filePreview.fileName" v-model="dialogOfficeVisible">
<OfficeViewer :file-name="filePreview.fileName" :file-url="filePreview.url"
v-if="dialogOfficeVisible" />
2025-03-07 10:43:38 +08:00
</el-dialog>
2025-03-03 11:29:41 +08:00
<!-- 3d 在线预览 -->
2025-03-07 10:43:38 +08:00
<el-dialog draggable class="zfile-3d-dialog" :title="filePreview.fileName" v-model="dialog3dVisible">
2025-03-03 11:29:41 +08:00
<Three3dPreview :file-name="filePreview.fileName" :file-url="filePreview.url"
v-if="dialog3dVisible" />
2025-03-07 10:43:38 +08:00
</el-dialog>
2025-03-03 11:29:41 +08:00
<!-- 音频播放器 -->
2025-03-07 10:43:38 +08:00
<AudioPlayer></AudioPlayer>
<Viewfile v-if="isViewfile" :showTime="true" :title="title1" :url="ViewfileUrl" :type="fileType"
@update="CloseView" />
2025-03-23 16:55:46 +08:00
<el-dialog :title="title" v-model="textedit" :before-close="textClose" top="30px" draggable width="60%"
destroy-on-close>
<textEdit :rowId="rowId" />
<!-- </el-scrollbar> -->
</el-dialog>
2025-02-28 09:11:25 +08:00
</div>
</section>
</div>
</template>
<style scoped lang="scss">
.faulttemplate-box {
height: 100%;
display: flex;
display: -webkit-flex;
background-color: #f2f4f9;
}
#silderLeft {
width: 300px;
padding: 10px;
box-sizing: border-box;
background: #fff;
border-radius: 3px;
position: relative;
.tree_sou {
display: flex;
align-items: center;
justify-content: space-between;
}
&:hover {
.moveBtn {
opacity: 1;
}
}
}
/* 拖动条 */
.moveBtn {
height: 100%;
width: 15px;
padding: 0 6px;
opacity: 0;
position: absolute;
right: -15px;
top: 0;
}
.moveBtn-line {
width: 100%;
height: 100%;
cursor: col-resize;
user-select: none;
background-color: #60bfff;
}
.silderRight {
flex: 1;
width: 100%;
height: calc(100vh - 130px);
overflow: auto;
background-color: rgba(255, 255, 255, 1);
border-radius: 3px;
padding: 15px;
// padding-bottom: 0px;
box-sizing: border-box;
margin-left: 15px;
.tree_button {
2025-03-03 11:32:37 +08:00
// display: flex;
// align-items: center;
// justify-content: flex-end;
2025-02-28 09:11:25 +08:00
margin-bottom: 15px;
}
.tree_box {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
.tree_left {
width: 49%;
height: calc(78vh);
2025-03-03 11:29:41 +08:00
2025-02-28 09:11:25 +08:00
.tree_title {
font-size: 20px;
font-weight: 400;
}
}
}
}
2025-03-07 10:43:38 +08:00
.custom-tree-node1 {
display: inline-block;
width: 100%;
font-size: 14px;
padding: 5px;
background-color: green;
color: #fff;
margin-bottom: 5px !important;
}
.custom-tree-node2 {
display: inline-block;
width: 100%;
font-size: 14px;
padding: 5px;
background-color: yellow;
color: #409eff;
}
.custom-tree-node3 {
display: inline-block;
width: 100%;
font-size: 14px;
padding: 5px;
background-color: red;
color: #fff;
margin-bottom: 5px !important;
}
2025-02-28 09:11:25 +08:00
.custom-tree-node {
2025-03-03 11:29:41 +08:00
display: inline-block;
width: 100%;
2025-02-28 09:11:25 +08:00
font-size: 14px;
padding: 5px;
2025-03-07 10:43:38 +08:00
// margin-bottom: 5px !important;
2025-02-28 09:11:25 +08:00
}
:deep() {
.el-radio-group {
display: block !important;
}
}
//文字换行处理
.text {
overflow-wrap: break-word;
word-break: break-all;
white-space: normal;
// line-height: 1.5;
max-width: 72%;
// border: 1px solid #000;
// padding: 10px;
}
:deep() {
.el-tree-node__content {
height: auto !important;
line-height: 20px !important;
// padding-left: 10px !important;
}
.el-tree-node__content>.el-tree-node__expand-icon {
padding: 0px !important;
}
}
2025-03-03 11:29:41 +08:00
2025-03-07 10:43:38 +08:00
.tree_title {
:deep(.el-tree-node.is-current > .el-tree-node__content) {
background-color: #606266 !important;
color: #606266 !important;
}
}
2025-03-03 11:29:41 +08:00
.context-menu {
position: fixed;
background: #fff;
border: 1px solid #ebeef5;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
z-index: 9999;
min-width: 100px;
border-radius: 10px;
.menu-item {
padding: 8px 16px;
cursor: pointer;
&:hover {
background: #409eff;
color: #fff;
border-radius: 10px;
}
}
}
//文件差异性对比
.newContent {
width: 100%;
.newContent_title {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
.newContent_title_text {
font-size: 20px;
font-weight: 800;
}
}
}
2025-02-28 09:11:25 +08:00
</style>