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.
500 lines
14 KiB
500 lines
14 KiB
<template>
|
|
<div class="sfcbox">
|
|
<!-- 第一个服务器的文件列表 -->
|
|
<div class="sfcon">
|
|
<div class="sfind">
|
|
<!-- 选择服务器 -->
|
|
<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">
|
|
<ul>
|
|
<li v-for="(item, index) in fsclist" :key="item" :data-index="index">
|
|
<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>
|
|
<!--
|
|
<div class="sflfunc">
|
|
<a>发送</a>
|
|
</div> -->
|
|
</li>
|
|
</ul>
|
|
</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">
|
|
<!-- 选择服务器 -->
|
|
<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">当前路径:{{ 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">
|
|
<div class="fsendcon">
|
|
从到
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { GetFileList, SerlistInUsing } from '@/api/scinfo'
|
|
|
|
export default {
|
|
name: 'Sfilecompare',
|
|
data() {
|
|
return {
|
|
fsip: '',
|
|
ssip: '',
|
|
fsclist: {}, // 第一台文件列表
|
|
ssclist: {}, // 第二台文件列表
|
|
uslist: [], //使用中的服务器
|
|
fspath: '/', // 第一个服务器的路径
|
|
sspath: '/', // 第二个服务器的路径
|
|
|
|
chkfiles: [], // 选中的文件
|
|
|
|
isMenuVisible: false, // 菜单是否可见
|
|
menuTop: 0, // 菜单顶部位置
|
|
menuLeft: 0, // 菜单左侧位置
|
|
}
|
|
},
|
|
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
|
|
})
|
|
},
|
|
// 进入子目录
|
|
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(); // 执行完操作后隐藏菜单
|
|
},
|
|
|
|
// 更新文件
|
|
uploadFile(e){
|
|
//
|
|
console.log(e,"upload")
|
|
|
|
// itname.checked = true
|
|
// let chkbox =document.getElementsByName('ssbox'+index)
|
|
}
|
|
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.sfcbox {
|
|
float: left;
|
|
width: 100%;
|
|
/* overflow-y: auto; */
|
|
margin: 20px auto;
|
|
margin-top: 12px;
|
|
background-color: #fff;
|
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
|
border-radius: 12px 12px 0 0;
|
|
position: relative;
|
|
}
|
|
|
|
.sfcon {
|
|
padding: 20px;
|
|
float: left;
|
|
width: 100%;
|
|
min-height: 560px;
|
|
}
|
|
|
|
.sfcon:first-child {
|
|
margin-bottom: 22px;
|
|
box-shadow: 1px 7px 10px 3px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.sfcon:nth-child(2) {
|
|
margin-top: 22px;
|
|
}
|
|
|
|
.sfcon:nth-child(2) .sflist {
|
|
background-color: #dddcdc5e;
|
|
}
|
|
|
|
.sfind {
|
|
width: 100%;
|
|
/* height: 40px; */
|
|
line-height: 40px;
|
|
padding: 12px;
|
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.sfind .selbox {
|
|
height: 40px;
|
|
border-top: none;
|
|
border-left: none;
|
|
border-right: none;
|
|
|
|
outline: none;
|
|
padding: 0 12px;
|
|
}
|
|
|
|
.sfind .inputbox {
|
|
height: 40px;
|
|
line-height: 40px;
|
|
padding: 12px;
|
|
border-top: none;
|
|
border-left: none;
|
|
border-right: none;
|
|
margin-left: 12px;
|
|
outline: none;
|
|
width: 460px;
|
|
}
|
|
|
|
.sfind button {
|
|
width: 70px;
|
|
height: 40px;
|
|
margin-left: 12px;
|
|
background-color: #f5f5f5;
|
|
border: none;
|
|
}
|
|
|
|
.sfcon .sflist {
|
|
min-height: 460px;
|
|
overflow-y: auto;
|
|
background-color: #f5f5f5;
|
|
}
|
|
|
|
.sfcon .sflist ul {
|
|
margin-top: 20px;
|
|
display: block;
|
|
width: 100%;
|
|
min-height: 500px;
|
|
}
|
|
|
|
.sfcon .sflist ul li {
|
|
list-style: none;
|
|
width: 100%;
|
|
float: left;
|
|
height: 40px;
|
|
line-height: 40px;
|
|
border-bottom: 1px dashed #ccc;
|
|
}
|
|
|
|
.sfcon .sflist ul li:hover {
|
|
background-color: #dad8d8;
|
|
}
|
|
|
|
.sfcon .sflist ul li .sflfunc {
|
|
/* display: none; */
|
|
float: right;
|
|
width: 360px;
|
|
margin-right: 20px;
|
|
}
|
|
|
|
.sfcon .sflist ul li .sfchkbox {
|
|
margin-right: 12px;
|
|
width: 18px;
|
|
height: 18px;
|
|
}
|
|
|
|
.sfcon .sflist ul li span{
|
|
line-height: 30px;
|
|
}
|
|
|
|
.sfcon .sflist .haschild {
|
|
cursor: pointer;
|
|
}
|
|
.sfcon .sflist .haschild i{
|
|
display: inline-block;
|
|
width: 25px;
|
|
height: 25px;
|
|
background-image: url("@/assets/css_sprites.png");
|
|
background-repeat: no-repeat;
|
|
background-size: 25px;
|
|
background-position: 0px -400px;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
.sfcon .sflist .tips {
|
|
width: 100%;
|
|
float: left;
|
|
margin-top: 12px;
|
|
}
|
|
|
|
/* 传输功能区 */
|
|
.sftrans {
|
|
position: fixed;
|
|
width: 200px;
|
|
height: 120px;
|
|
z-index: 99;
|
|
background-color: #f5f5f5;
|
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.sftrans .sfbcon {
|
|
color: #333;
|
|
margin-left: auto;
|
|
margin-right: auto;
|
|
background-color: #fff;
|
|
padding: 10px;
|
|
}
|
|
|
|
.sftrans .sfbcon li {
|
|
list-style: none;
|
|
height: 30px;
|
|
line-height: 30px;
|
|
}
|
|
|
|
.sftrans .sfbcon li:hover {
|
|
background-color: #00bd7e;
|
|
color: #fff;
|
|
cursor: default;
|
|
}
|
|
|
|
.sftrans .sfbcon li span {
|
|
padding-left: 12px;
|
|
}
|
|
.sftrans .sfbcon li.divider{
|
|
height: 1px;
|
|
background-color: #00bd7e;
|
|
margin: 10px 0;
|
|
}
|
|
/** 发送文件 */
|
|
.fsend{
|
|
float: left;
|
|
height: 120px;
|
|
background-color: #f5f5f5;
|
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
|
margin-top: 20px;
|
|
width: 100%;
|
|
}
|
|
.fsend .fsendcon{
|
|
margin-left: auto;
|
|
padding: 12px;
|
|
background-color: #fff;
|
|
}
|
|
</style>
|