自动更新管控端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

447 lines
16 KiB

<template>
<div class="sfcbox">
<!-- 第一个服务器的文件列表 -->
<div class="sfcon">
<div class="sfind">
<span class="scdesc">源服务器</span>
<!-- 选择服务器 -->
<select class="selbox" name="fsip" ref="fsip" @change="changeSer">
<option value="0">请选择源服务器</option>
<option v-for="item in uslist" :value="item.addr + ':' + item.port">{{ item.addr + ':' + item.port
}}
</option>
</select>
<input name="path" class="inputbox" type="text" placeholder="/" autocomplete="off" v-model="fspath">
<button @click="getFlist">Go</button>
<button @click="goParent">🔙</button>
</div>
<div class="sflist">
<form method="post" ref="fsform">
<ul>
<li v-for="(item, index) in fsclist" :key="item" :data-index="index" :id="'fs_'+rc32(item.path)" :class="{'diff': item.path in diffFiles}">
<input type="checkbox" :value="item.path" class="sfchkbox" :name="'fsbox' + index" :ref="'fsbox' + index"/>
<a class="haschild" @click="goIntoDir(item.path, 1)" v-if="item.isdir"><i></i>{{ item.path }}</a>
<span v-else><i :class="'tb tb-'+item.suffix"></i>{{ item.path }}</span>
</li>
</ul>
<div class="fbdiv" v-if="fsclist.length > 0">
<input type="hidden" name="curpath" :value="fspath" />
<!-- <button class="fbtn">同步选中的文件</button> -->
</div>
</form>
</div>
<div class="tips">
<span v-if="fsclist.length > 0"> 服务器:{{ this.$refs.fsip.value }} 路径:{{ fspath }} ,文件数量:{{ fsclist.length
}}</span>
<span v-else>当前服务器:{{ fsip }}</span>
</div>
</div>
<!-- 第二个服务器的文件列表 -->
<div class="sfcon">
<div class="sfind">
<span class="scdesc">目标服务器:</span>
<!-- 选择服务器 -->
<select class="selbox" name="ssip" ref="ssip">
<option value="0">请选择目标服务器</option>
<option v-for="item in uslist" :value="item.addr + ':' + item.port">{{ item.addr + ':' + item.port}}</option>
</select>
<input name="path" class="inputbox" type="text" placeholder="/" autocomplete="off" v-model="sspath">
<button @click="getSflist">GO</button>
<button @click="goParent">🔙</button>
</div>
<div class="sflist">
<ul>
<li v-for="item in ssclist" :key="item" :data-index="index">
<input type="checkbox" :value="item.path" class="sfchkbox" :name="'ssbox' + index" :ref="'fsbox' + index"/>
<a class="haschild" @click="goIntoDir(item.path, 2)" v-if="item.isdir "><i></i>{{ item.path }}</a>
<span v-else><i :class="'tb tb-'+item.suffix"></i>{{ item.path }}</span>
</li>
</ul>
</div>
<div class="tips">
<span v-if="ssclist.length > 0">服务器:{{ this.$refs.fsip.value }} 路径:{{ sspath }} ,文件数量:{{ ssclist.length }}</span>
<span v-else>当前服务器:{{ ssip }}</span>
</div>
</div>
<!-- 右键菜单 -->
<div class="sftrans" v-if="isMenuVisible" @contextmenu.prevent
:style="{ top: menuTop + 'px', left: menuLeft + 'px' }">
<ul class="sfbcon">
<li><span href="javascript:void(0)" @click="handleMenuAction('upload')">同步文件</span></li>
<li><span href="javascript:void(0)" @click="handleMenuAction('download')">压缩下载</span></li>
<li class="divider"></li>
<li><span href="javascript:void(0)" @click="handleMenuAction('delete')">删除文件</span></li>
</ul>
</div>
<!-- 待发送文件 -->
<div class="fsend" v-if="hasTwolist">
<div class="fsendcon">
<!-- 暂未想好放啥 -->
<button class="fbtn" @click="submitForm">同步选中的文件</button>
</div>
</div>
<!-- 弹框提示 -->
<div class="dialog" v-if="dialogShow">
<div class="alert alert-success">
同步成功
</div>
</div>
</div>
</template>
<script>
import { GetFileList, SerlistInUsing,SendZipFile } from '@/api/scinfo'
export default {
name: 'Sfilecompare',
data() {
return {
fsip: '',
ssip: '',
fsclist: {}, // 第一台文件列表
ssclist: {}, // 第二台文件列表
uslist: [], //使用中的服务器
fspath: '/', // 第一个服务器的路径
sspath: '/', // 第二个服务器的路径
chkfiles: [], // 选中的文件
selectedFiles: [], // 选中的文件
isMenuVisible: false, // 菜单是否可见
menuTop: 0, // 菜单顶部位置
menuLeft: 0, // 菜单左侧位置
dialogShow: false, // 对话框是否可见
isShowDir: false, // 只显示目录
hasTwolist: false, // 是否有两个服务器的文件列表
diffFiles: [], // 差异文件
}
},
mounted() {
// 点击文档其他区域隐藏菜单
document.addEventListener('click', (event) => {
//避免快速点击时的冲突
if (this.isMenuVisible) {
this.hideMenu()
}
})
// 使用中的服务器
SerlistInUsing().then(res => {
this.uslist = res.data;
})
},
beforeMount() {
// 移除事件监听
document.removeEventListener('click', this.hideMenu);
},
methods: {
// 获取第一个服务器的文件列表
getFlist() {
// 获取第一个服务器
let fsip = this.$refs.fsip.value
let sstr = fsip.split(":")
let sport = sstr[1]
let scip = btoa(sstr[0])
// 获取文件列表
GetFileList({ srcip: scip, path: this.fspath, sport: sport }).then(res => {
this.fsclist = res.data.list
})
},
// 获取第二个服务器的文件列表
getSflist() {
let ssip = this.$refs.ssip.value
let sstr = ssip.split(":")
let sport = sstr[1]
let scip = btoa(sstr[0])
// 获取文件列表
GetFileList({ srcip: scip, path: this.sspath, sport: sport }).then(res => {
this.ssclist = res.data.list
})
// 3s 后执行对比
setTimeout(() => {
this.diffFlist()
}, 3000)
// 如果存在第一条
if (this.fsclist.length > 0 && this.ssclist.length > 0) {
this.hasTwolist=true
}
},
// 对比web端文件列表
diffFlist(){
let alist = this.fsclist;
let blist = this.ssclist;
// 对比alist 与 blist 的差异
let diff = [...alist].filter(item =>
!blist.some(i =>
// i.hash == item.hash
i.path == item.path
)
)
// diff 转为一维数组
// diff = diff.flatMap(item => item.hash)
diff = diff.map(item => item.path)
//
this.diffFiles = diff
},
// 计算rc32的结果,剔除负数的情况
rc32(str) {
// 转换为utf-8编码
str = encodeURIComponent(str)
let rc = 0xffff
for (let i = 0; i < str.length; i++) {
rc = (rc << 5) + rc + str.charCodeAt(i)
// 使用掩码确保结果为非负数
rc = rc & 0x7FFFFFFF
}
return rc
},
// 对比两个list
compareList(){
let alist = this.fsclist;
let blist = this.ssclist;
// 对比alist 与 blist 的差异
let diff = [...alist].filter(item =>
!blist.some(i =>
// i.hash == item.hash
i.path == item.path
)
)
// diff 转为一维数组
// diff = diff.flatMap(item => item.hash)
diff = diff.map(item => item.path)
//
this.diffFiles = diff
},
// 进入子目录
goIntoDir(path, nflag) {
// 根据nflag 来判断
let npath, sel
if (nflag == 1) {
sel = this.$refs.fsip.value
// 拼装url
if (this.fspath == '/') {
npath = '/' + path
} else {
npath = this.fspath + '/' + path
}
}
if (nflag == 2) {
sel = this.$refs.ssip.value
if (this.sspath == path) {
this.sspath = '/'
}
if (this.sspath == '/') {
npath = '/' + path
} else {
npath = this.sspath + '/' + path
}
}
let rrarr = this.preIp(sel)
// ip转为base64
let ip = btoa(rrarr[0])
this.queryFlist(ip, rrarr[1], npath, nflag)
},
// 查询内容
// nflag 1 第一个服务器 2 第二个服务器
async queryFlist(ip, port, npath, nflag) {
let rlist = []
await GetFileList({
srcip: ip, path: npath, sport: port
}).then(res => {
var vlist = res.data.list
if (vlist) {
// 绑定数据
if (nflag == 1) {
this.fspath = npath
this.fsclist = vlist
}
if (nflag == 2) {
this.sspath = npath
this.ssclist = vlist
}
} else {
npath = "/"
}
})
return rlist;
},
// 拆分组装ip
preIp(ssip) {
// 返回数组
let iparr = []
let sstr = ssip.split(":")
iparr.push(sstr[0])
iparr.push(sstr[1])
return iparr
},
// 选择服务器
changeSer() {
// 清空路径
this.fspath = '/'
this.sspath = '/'
},
// 上一级目录
// 移除最后一个文件
goParent() {
// 第一个服务器
if (this.fspath != '/') {
let npath = this.fspath.split('/')
// 弹出最后一个元素
npath.pop()
this.fspath = npath.join('/')
// 调用方法
this.getFlist()
}
// 第二个服务器
if (this.sspath != '/') {
let npath = this.sspath.split('/')
npath.pop()
this.sspath = npath.join('/')
// 调用方法
this.getSflist()
}
},
// 右键菜单内容
showMenu(event) {
// 计算菜单位置(基于点击坐标)
this.menuTop = event.clientY;
this.menuLeft = event.clientX;
// 确保菜单不会超出视口
this.adjustMenuPosition();
// 显示菜单
this.isMenuVisible = true;
// 获取到当前行的索引值
let index = event.target.dataset.index
// 对应的box 也要选中
let chkbox = document.getElementsByName('fsbox' + index)
chkbox[0].checked = true
console.log(chkbox,"chkbox")
// 第二个服务器
// let schkbox = document.getElementsByName('ssbox'+index)
// schkbox[0].checked = true
},
// 隐藏右键菜单
hideMenu() {
this.isMenuVisible = false;
},
// 调整菜单位置,避免超出视口
adjustMenuPosition() {
const menuWidth = 200; // 菜单宽度(px)
const menuHeight = 160; // 菜单高度(px)
// 右侧超出视口时向左调整
if (this.menuLeft + menuWidth > window.innerWidth) {
this.menuLeft = window.innerWidth - menuWidth;
}
// 底部超出视口时向上调整
if (this.menuTop + menuHeight > window.innerHeight) {
this.menuTop = window.innerHeight - menuHeight;
}
},
// 处理菜单操作
handleMenuAction(action) {
console.log(`执行操作: ${action}`);
switch(action){
case 'upload':
this.uploadFile()
break;
}
// 这里可以添加具体的业务逻辑
this.hideMenu(); // 执行完操作后隐藏菜单
},
// 提交表单
submitForm(){
// checkbox选中的数值
let chkbox = document.getElementsByClassName('sfchkbox')
for(let i=0;i<chkbox.length;i++){
if(chkbox[i].checked){
// 加上当前的路径
let rv = chkbox[i].value
this.selectedFiles.push(rv)
}
}
// build to string
// let srt = this.selectedFiles.join(',')
// 当前路径
let curpath = this.fspath
// 提交post请求
let data = {
sfiles: this.selectedFiles,
serverip: this.$refs.fsip.value,
curpath: curpath,
remoteaddr: this.$refs.ssip.value,
remotepath: this.sspath,
}
// build to schema
const pdata = new URLSearchParams(data)
//
SendZipFile(pdata).then(res=>{
console.log(res,"res")
// alert(res.status)
if(res.status == "200"){
// 显示对话框
this.dialogShow = true
// 3s后隐藏
setTimeout(() => {
this.dialogShow = false
}, 3000);
// 第二台服务器列表更新
this.getSflist()
}
// alert(res.data.reply)
// 清空数组
this.selectedFiles = []
// 清空pdata
pdata.forEach((value, key) => {
pdata.delete(key)
})
})
return
},
// 更新文件
uploadFile(e){
//
console.log(e,"upload")
// itname.checked = true
// let chkbox =document.getElementsByName('ssbox'+index)
}
}
}
</script>
<style scoped>
@import url('@/assets/sfilecompare.css');
</style>