558 lines
18 KiB
JavaScript
558 lines
18 KiB
JavaScript
//
|
||
// 文件管理 模块
|
||
//
|
||
|
||
const LANG_T = antSword['language']['toastr'];
|
||
const LANG = antSword['language']['filemanager']['files'];
|
||
|
||
class Files {
|
||
|
||
// 需要参数
|
||
// 1.cell: 左侧layout.cell对象
|
||
// 2.manager 主对象(index.jsx)
|
||
constructor(cell, manager) {
|
||
const self = this;
|
||
cell.setText(`<i class="fa fa-file-o"></i> ${LANG['title']}`);
|
||
// 创建toolbar
|
||
const toolbar = cell.attachToolbar();
|
||
// 加载本地缓存书签栏
|
||
let bookmark = JSON.parse(manager.storage('bookmarks').get('{}'));
|
||
// 重新加载书签
|
||
this.reloadToolbar = () => {
|
||
let bookmark_opts = [{
|
||
id: 'bookmark_add',
|
||
type: 'button',
|
||
icon: 'plus-circle',
|
||
text: LANG['bookmark']['add'],
|
||
enabled: !bookmark[manager.path]
|
||
}];
|
||
if (!$.isEmptyObject(bookmark)) {
|
||
bookmark_opts.push({ type: 'separator' });
|
||
};
|
||
for (let _ in bookmark) {
|
||
bookmark_opts.push({
|
||
id: 'bookmark_' + _,
|
||
text: bookmark[_],
|
||
icon: 'bookmark-o',
|
||
type: 'button',
|
||
enabled: manager.path !== _
|
||
});
|
||
}
|
||
// 添加清除按钮
|
||
if (bookmark_opts.length > 2) {
|
||
bookmark_opts.push({
|
||
type: 'separator'
|
||
});
|
||
bookmark_opts.push({
|
||
id: 'bookmark_remove',
|
||
icon: 'remove',
|
||
text: LANG['bookmark']['del'],
|
||
type: 'button',
|
||
enabled: !!bookmark[manager.path]
|
||
});
|
||
bookmark_opts.push({
|
||
id: 'bookmark_clear',
|
||
icon: 'trash-o',
|
||
text: LANG['bookmark']['clear'],
|
||
type: 'button'
|
||
});
|
||
};
|
||
|
||
toolbar.clearAll();
|
||
toolbar.loadStruct([
|
||
{
|
||
id: 'new', type: 'buttonSelect', icon: 'plus-circle', text: LANG['toolbar']['new'], openAll: true,
|
||
options: [
|
||
{ id: 'new_folder', icon: 'folder-o', type: 'button', text: LANG['toolbar']['folder'] },
|
||
{ id: 'new_file', icon: 'file-o', type: 'button', text: LANG['toolbar']['file'] },
|
||
{ type: 'separator' },
|
||
{ id: 'new_wget', icon: 'cloud-download', type: 'button', text: LANG['toolbar']['wget'] },
|
||
{ id: 'new_upload', icon: 'cloud-upload', type: 'button', text: LANG['toolbar']['upload'] }
|
||
]
|
||
},
|
||
{ type: 'separator' },
|
||
{ id: 'up', type: 'button', icon: 'arrow-up', text: LANG['toolbar']['up'] },
|
||
{ type: 'separator' },
|
||
{ id: 'refresh', type: 'button', icon: 'refresh', text: LANG['toolbar']['refresh'] },
|
||
{ type: 'separator' },
|
||
{ id: 'home', type: 'button', icon: 'home', text: LANG['toolbar']['home'] },
|
||
{ type: 'separator' },
|
||
{
|
||
id: 'bookmark', type: 'buttonSelect', icon: 'bookmark', text: LANG['toolbar']['bookmark'], openAll: true,
|
||
options: bookmark_opts
|
||
},
|
||
{ type: 'separator' },
|
||
{ id: 'path', width: 300, type: 'buttonInput', value: manager.path || 'loading..' },
|
||
{ id: 'read_path', type: 'button', icon: 'arrow-right', text: LANG['toolbar']['read'] },
|
||
{ type: 'separator' }
|
||
]);
|
||
}
|
||
|
||
this.reloadToolbar();
|
||
// reloadToolbar();
|
||
// this.reloadToolbar = reloadToolbar;
|
||
// toolbar点击事件
|
||
toolbar.attachEvent('onClick', (id) => {
|
||
switch(id) {
|
||
case 'up':
|
||
self.gotoPath('..');
|
||
break;
|
||
case 'refresh':
|
||
self.refreshPath();
|
||
break;
|
||
case 'read_path':
|
||
let pwd = toolbar.getInput('path').value;
|
||
self.gotoPath(pwd);
|
||
break;
|
||
case 'home':
|
||
self.gotoPath(manager.home);
|
||
break;
|
||
case 'new_folder':
|
||
manager.createFolder();
|
||
break;
|
||
case 'new_file':
|
||
manager.createFile();
|
||
break;
|
||
case 'new_wget':
|
||
manager.wgetFile();
|
||
break;
|
||
case 'new_upload':
|
||
manager.uploadFile();
|
||
break;
|
||
case 'bookmark_add':
|
||
// 添加书签
|
||
layer.prompt({
|
||
value: self.manager.path,
|
||
title: LANG['prompt']['add']['title']
|
||
}, (value, i, e) => {
|
||
bookmark[self.manager.path] = value;
|
||
self.manager.storage('bookmarks').set(JSON.stringify(bookmark));
|
||
toastr.success(LANG['prompt']['add']['success'](self.manager.path), LANG_T['success']);
|
||
self.reloadToolbar();
|
||
layer.close(i);
|
||
});
|
||
break;
|
||
case 'bookmark_remove':
|
||
layer.confirm(
|
||
LANG['prompt']['remove']['confirm']
|
||
, {
|
||
icon: 2, shift: 6,
|
||
title: `<i class="fa fa-remove"></i> ${LANG['prompt']['remove']['title']}`,
|
||
}
|
||
, (_) => {
|
||
// 删除书签并刷新
|
||
delete bookmark[self.manager.path];
|
||
self.manager.storage('bookmarks').set(JSON.stringify(bookmark));
|
||
self.reloadToolbar();
|
||
toastr.success(LANG['prompt']['remove']['success'], LANG_T['success']);
|
||
layer.close(_);
|
||
}
|
||
)
|
||
break;
|
||
case 'bookmark_clear':
|
||
layer.confirm(
|
||
LANG['prompt']['clear']['confirm']
|
||
, {
|
||
icon: 2, shift: 6,
|
||
title: `<i class="fa fa-trash-o"></i> ${LANG['prompt']['clear']['title']}`
|
||
}
|
||
, (_) => {
|
||
bookmark = {};
|
||
self.manager.storage('bookmarks').set('{}');
|
||
self.reloadToolbar();
|
||
toastr.success(LANG['prompt']['clear']['success'], LANG_T['success']);
|
||
layer.close(_);
|
||
}
|
||
)
|
||
break;
|
||
default:
|
||
let arr = id.split('_');
|
||
if (arr.length === 2 && arr[0] === 'bookmark') {
|
||
self.gotoPath(arr[1]);
|
||
};
|
||
}
|
||
});
|
||
toolbar.attachEvent('onEnter', (id, value) => {
|
||
switch(id) {
|
||
case 'path':
|
||
self.gotoPath(value);
|
||
break;
|
||
}
|
||
});
|
||
// 创建grid
|
||
let grid = cell.attachGrid();
|
||
|
||
grid.setHeader(`
|
||
,
|
||
${LANG['grid']['header']['name']},
|
||
${LANG['grid']['header']['time']},
|
||
${LANG['grid']['header']['size']},
|
||
${LANG['grid']['header']['attr']}
|
||
`);
|
||
grid.setColTypes("ro,ro,ro,ro,ro");
|
||
grid.setColSorting('str,str,str,str,str');
|
||
grid.setInitWidths("40,*,150,100,100");
|
||
grid.setColAlign("center,left,left,right,center");
|
||
grid.enableMultiselect(true);
|
||
// grid.enableDragAndDrop(true);
|
||
// grid.enableMultiline(true);
|
||
|
||
// grid右键
|
||
// 空白数据右键fix
|
||
$('.objbox').on('contextmenu', (e) => {
|
||
(e.target.nodeName === 'DIV' && grid.callEvent instanceof Function && antSword['tabbar'].getActiveTab().startsWith('tab_filemanager_')) ? grid.callEvent('onRightClick', [-1, -1, e]) : null;
|
||
});
|
||
$('.objbox').on('click', (e) => {
|
||
bmenu.hide();
|
||
});
|
||
grid.attachEvent('onRightClick', function(id, lid, event) {
|
||
|
||
// 获取选中ID列表
|
||
let _ids = (this.getSelectedId() || '').split(',');
|
||
// 如果是空白右键
|
||
if (id === -1) {
|
||
_ids = [];
|
||
}
|
||
// 如果没有选中?则选中右键对应选项
|
||
else if (_ids.length === 1) {
|
||
this.selectRowById(id);
|
||
_ids = [id];
|
||
};
|
||
let ids = [];
|
||
_ids.map((_) => {
|
||
ids.push(this.getRowAttribute(_, 'fname'));
|
||
});
|
||
id = ids[0] || '';
|
||
|
||
// 获取剪贴板内容
|
||
let _Clipboard = [];
|
||
let _Clipboard_num = 0;
|
||
for (let c in self.Clipboard) {
|
||
_Clipboard.push({
|
||
text: antSword.noxss(c),
|
||
icon: 'fa fa-' + (c.endsWith('/') ? 'folder-o' : 'file-o'),
|
||
action: ( (source, name) => {
|
||
return () => {
|
||
manager.pasteFile(source, name);
|
||
}
|
||
})(self.Clipboard[c], c)
|
||
});
|
||
_Clipboard_num ++;
|
||
if (!(_Clipboard_num % 5)) {
|
||
_Clipboard.push({ divider: true });
|
||
};
|
||
}
|
||
// 清除最后的divider
|
||
// if (_Clipboard.length % 5 && _Clipboard_num > 5) { _Clipboard.pop() };
|
||
// all item
|
||
if (_Clipboard.length > 0) {
|
||
_Clipboard.unshift({ divider: true });
|
||
_Clipboard.unshift({
|
||
text: LANG['grid']['contextmenu']['paste']['all'],
|
||
count: _Clipboard_num,
|
||
icon: 'fa fa-th-list',
|
||
action: () => {
|
||
_Clipboard.map( (c) => {
|
||
if (c['divider'] || c['count'] || c['text'] === LANG['grid']['contextmenu']['paste']['clear']['title']) { return };
|
||
|
||
let name = c['text'];
|
||
let source = self.Clipboard[name];
|
||
|
||
manager.pasteFile(source, name);
|
||
|
||
} );
|
||
}
|
||
});
|
||
// 清空剪贴板
|
||
!(_Clipboard[_Clipboard.length-1].divider) ? _Clipboard.push({ divider: true }) : 0;
|
||
_Clipboard.push({
|
||
text: LANG['grid']['contextmenu']['paste']['clear']['title'],
|
||
icon: 'fa fa-trash-o',
|
||
action: () => {
|
||
self.Clipboard = {};
|
||
toastr.info(LANG['grid']['contextmenu']['paste']['clear']['info'], LANG_T['info']);
|
||
}
|
||
});
|
||
};
|
||
|
||
let isFolder = id.endsWith('/');
|
||
// 可编辑文件后缀
|
||
let isEdited = false;
|
||
'php,asp,aspx,jsp,cfm,js,css,html,py,sh,bat,txt,log,ini,conf,sql'.split(',').map(
|
||
(e) => {
|
||
id.toLowerCase().endsWith(`.${e}`) ? isEdited = true : 0;
|
||
}
|
||
);
|
||
|
||
let menu = [
|
||
{ text: LANG['grid']['contextmenu']['refresh'], icon: 'fa fa-refresh', action: () => { self.refreshPath(); } },
|
||
{ divider: true },
|
||
{ text: LANG['grid']['contextmenu']['wget'], icon: 'fa fa-cloud-download', action: manager.wgetFile.bind(manager) },
|
||
{ text: LANG['grid']['contextmenu']['upload'], icon: 'fa fa-upload', action: manager.uploadFile.bind(manager) },
|
||
{ text: LANG['grid']['contextmenu']['download'], icon: 'fa fa-download', disabled: isFolder || !id || ids.length > 1, action: () => {
|
||
manager.downloadFile(id, this.getRowAttribute(_ids[0], 'fsize'));
|
||
} },
|
||
{ divider: true },
|
||
{ text: LANG['grid']['contextmenu']['copy']['title'], icon: 'fa fa-copy', disabled: !id, action: () => {
|
||
// 如果只有一个id,则显示id名称,否则显示ids数量
|
||
ids.map( (id) => {
|
||
let path = manager.path + id;
|
||
// 判断是否已经复制
|
||
if (id in self.Clipboard) {
|
||
return toastr.warning(LANG['grid']['contextmenu']['copy']['warning'](id), LANG_T['warning']);
|
||
};
|
||
self.Clipboard[id] = path;
|
||
toastr.info(LANG['grid']['contextmenu']['copy']['info'](id), LANG_T['info']);
|
||
} );
|
||
} },
|
||
{ text: LANG['grid']['contextmenu']['paste']['title'], icon: 'fa fa-paste', disabled: _Clipboard_num === 0, subMenu: _Clipboard },
|
||
{ text: LANG['grid']['contextmenu']['preview'], icon: 'fa fa-eye', disabled: !id || ids.length > 1 || !self.checkPreview(id), action: () => {
|
||
manager.previewFile(id, this.getRowAttribute(_ids[0], 'fsize'));
|
||
} },
|
||
{ divider: true },
|
||
{ text: LANG['grid']['contextmenu']['edit'], icon: 'fa fa-edit', disabled: /*!isEdited || */!id || ids.length > 1 || isFolder, action: () => {
|
||
manager.editFile(id);
|
||
} },
|
||
{ text: LANG['grid']['contextmenu']['delete'], icon: 'fa fa-trash-o', disabled: !id, action: () => {
|
||
manager.deleteFile(ids);
|
||
} },
|
||
{ text: LANG['grid']['contextmenu']['rename'], icon: 'fa fa-font', disabled: !id || ids.length > 1, action: () => {
|
||
manager.renameFile(id);
|
||
} },
|
||
{ text: LANG['grid']['contextmenu']['modify'], icon: 'fa fa-clock-o', disabled: !id || ids.length > 1, action: () => {
|
||
// manager.retimeFile(id, this.rowsAr[id]['cells'][2].innerText);
|
||
manager.retimeFile(id, this.getRowAttribute(_ids[0], 'data')[2]);
|
||
} },
|
||
{ divider: true },
|
||
{ text: LANG['grid']['contextmenu']['create']['title'], icon: 'fa fa-plus-circle', subMenu: [
|
||
{ text: LANG['grid']['contextmenu']['create']['folder'], icon: 'fa fa-folder-o', action: manager.createFolder.bind(manager) },
|
||
{ text: LANG['grid']['contextmenu']['create']['file'], icon: 'fa fa-file-o', action: manager.createFile.bind(manager) }
|
||
] }
|
||
];
|
||
|
||
bmenu(menu, event);
|
||
|
||
return true;
|
||
});
|
||
|
||
// 选择事件
|
||
grid.attachEvent('onRowSelect', (id, lid, event) => {
|
||
bmenu.hide();
|
||
});
|
||
|
||
// 双击文件
|
||
// :如果可预览并且小于 1MB,则进行预览
|
||
// :如果size < 100kb,则进行编辑,否则进行下载
|
||
grid.attachEvent('onRowDblClicked', (id, lid, event) => {
|
||
const fname = grid.getRowAttribute(id, 'fname');
|
||
const fsize = grid.getRowAttribute(id, 'fsize');
|
||
if (!fname.endsWith('/')) {
|
||
if(self.checkPreview(fname) && fsize <= 1000 * 1024){
|
||
manager.previewFile(fname, fsize);
|
||
}else if(fsize <= 100 * 1024){
|
||
// 双击编辑size < 100kb 文件
|
||
manager.editFile(fname);
|
||
}else{
|
||
manager.downloadFile(fname, fsize);
|
||
}
|
||
}else{
|
||
self.gotoPath(fname);
|
||
}
|
||
});
|
||
|
||
// 键盘按下事件
|
||
grid.attachEvent('onKeyPress', (code, cFlag, sFlag)=>{
|
||
switch (true){
|
||
case (code <= 90 && code >= 65)||(code<=57 && code >= 48):
|
||
// A-Z 0-9
|
||
var input = String.fromCharCode(code);
|
||
input = input.toLowerCase();
|
||
var sid = 0;
|
||
var ids = grid.getAllRowIds().split(",");
|
||
for (var i=0; i<ids.length; i++) {
|
||
var _id = parseInt(ids[i]);
|
||
var fname = grid.getRowAttribute(_id, "fname");
|
||
if(fname.startsWith(input)){
|
||
sid = _id - 1;
|
||
grid.selectRow(sid);
|
||
break;
|
||
}
|
||
}
|
||
break
|
||
case (code == 38):
|
||
//up
|
||
var ids = grid.getAllRowIds().split(",");
|
||
var sid = grid.getSelectedRowId().toString();
|
||
var cid = ids.indexOf(sid) <= 0 ? parseInt(ids[0]) - 1 : parseInt(ids[ids.indexOf(sid)-1])-1;
|
||
grid.selectRow(cid);
|
||
break;
|
||
case (code == 40):
|
||
//down
|
||
var ids = grid.getAllRowIds().split(",");
|
||
var sid = grid.getSelectedRowId().toString();
|
||
var cid = ids.indexOf(sid) >= ids.length-1 ? parseInt(ids[ids.length-1])-1 : parseInt(ids[ids.indexOf(sid) + 1])-1;
|
||
grid.selectRow(cid);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
});
|
||
|
||
grid.init();
|
||
|
||
this.grid = grid;
|
||
this.cell = cell;
|
||
this.toolbar = toolbar;
|
||
this.manager = manager;
|
||
|
||
// 剪贴板
|
||
this.Clipboard = {};
|
||
|
||
// 文件拖拽上传
|
||
$(this.cell.cell).on({
|
||
dragleave: (e) => { e.preventDefault() },
|
||
drop: (e) => {
|
||
e.preventDefault();
|
||
let filePaths = [];
|
||
let files = e.originalEvent['dataTransfer']['files'] || {};
|
||
for (let i = 0; i < files.length; i++) {
|
||
let f = files.item(i);
|
||
filePaths.push(f['path']);
|
||
}
|
||
this.manager.uploadFile(filePaths);
|
||
},
|
||
dragenter: (e) => { e.preventDefault() },
|
||
dragover: (e) => { e.preventDefault() }
|
||
});
|
||
}
|
||
|
||
checkPreview(name) {
|
||
// 可预览文件后缀
|
||
let isPreviewed = false;
|
||
'jpeg,jpg,png,gif,bmp,ico'.split(',').map(
|
||
(e) => {
|
||
name.toLowerCase().endsWith(`.${e}`) ? isPreviewed = true : 0;
|
||
}
|
||
);
|
||
return isPreviewed;
|
||
}
|
||
|
||
// 刷新当前目录
|
||
// 如果传递路径参数,则刷新该路径下的文件,不跳转,否则刷新&&跳转
|
||
refreshPath(p) {
|
||
let path = p || this.manager.path;
|
||
// delete this.manager.cache[path];
|
||
this.manager.cache.del('filemanager-files-' + new Buffer(path).toString('base64'));
|
||
// 删除文件夹缓存
|
||
for (let _ in this.manager.folder.cache) {
|
||
if (_.indexOf(path) === 0 && _ != path) {
|
||
delete this.manager.folder.cache[_];
|
||
}
|
||
}
|
||
if (!p) { this.gotoPath('.') };
|
||
}
|
||
|
||
// 跳转目录
|
||
gotoPath(path) {
|
||
let self = this;
|
||
this.cell.progressOn();
|
||
try{
|
||
this.manager.getFiles(path, (files) => {
|
||
self.parse(files);
|
||
self.manager.folder.parse(files);
|
||
// self.cell.progressOff();
|
||
});
|
||
}catch(e) {
|
||
toastr.error(e, LANG_T['error']);
|
||
self.cell.progressOff();
|
||
}
|
||
}
|
||
|
||
// 解析数据
|
||
parse(files) {
|
||
|
||
let data = [];
|
||
let self = this;
|
||
let _id = 1;
|
||
files.map( (file) => {
|
||
if (!file['name'] || ['./', '../'].indexOf(file['name']) != -1) {return};
|
||
data.push({
|
||
id: _id,
|
||
fname: file['name'],
|
||
fsize: parseInt(file['size']),
|
||
// 如果是可执行文件(exe、dll..),则设置为红色字体
|
||
style: /\.exe$|\.dll$|\.bat$|\.sh$|\.com$/.test(file['name']) ? 'color:red' : '',
|
||
data: [
|
||
self.fileIcon(file['name']),
|
||
antSword.noxss(file['name'].replace(/\/$/, '')),
|
||
antSword.noxss(file['time']),
|
||
antSword.noxss(self.fileSize(parseInt(file['size']))),
|
||
antSword.noxss(file['attr'])
|
||
]
|
||
});
|
||
_id ++;
|
||
} );
|
||
|
||
|
||
this.cell.setText(`<i class="fa fa-file-o"></i> ${LANG['title']} (${data.length})`);
|
||
this.grid.clearAll();
|
||
this.grid.parse({
|
||
'rows': data
|
||
}, 'json');
|
||
|
||
// 设置path路径
|
||
this.toolbar.getInput('path').value = this.manager.path;
|
||
|
||
this.cell.progressOff();
|
||
this.reloadToolbar();
|
||
}
|
||
|
||
// 文件大小计算
|
||
fileSize(t) {
|
||
let i = false;
|
||
let b = ["b","Kb","Mb","Gb","Tb","Pb","Eb"];
|
||
for (let q=0; q<b.length; q++) if (t > 1024) t = t / 1024; else if (i === false) i = q;
|
||
if (i === false) i = b.length-1;
|
||
return Math.round(t*100)/100+" "+b[i];
|
||
}
|
||
|
||
// 返回文件图标
|
||
fileIcon(name) {
|
||
let icons = {};
|
||
const _icons = {
|
||
'exe,dll': 'file',
|
||
'jpg,png,gif,ico,bmp': 'file-image-o',
|
||
'mp4,mpeg,avi,rm,rmvb': 'file-movie-o',
|
||
'mp3,mid,wav': 'file-sound-o',
|
||
'pdf': 'file-pdf-o',
|
||
'ppt': 'file-powerpoint-o',
|
||
'xls,xlsx': 'file-excel-o',
|
||
'doc,docx': 'file-word-o',
|
||
'zip,tar,7z,gz,rar': 'file-archive-o',
|
||
'txt,ttf,tiff,ini,log,chm,conf,cfg': 'file-text-o',
|
||
'php,asp,jsp,sql,cfm,aspx,html,js,py,rb,pl,go,css,less,jsx,sass,xml,sh,bat,h,cpp,c,m': 'file-code-o'
|
||
}
|
||
for (let _ in _icons) {
|
||
let _arr = _.split(',');
|
||
_arr.map( (a) => {
|
||
icons[a] = _icons[_];
|
||
});
|
||
}
|
||
// 默认图标
|
||
let icon = 'file-o';
|
||
// 判断是否为文件夹
|
||
if (name.endsWith('/')) {
|
||
icon = 'folder-o';
|
||
}else{
|
||
let _ = name.split('.');
|
||
let ext = _[_.length - 1].toLowerCase();
|
||
icon = icons[ext] || icon;
|
||
};
|
||
return `<i class="fa fa-${icon} fa-lg"></i>`;
|
||
}
|
||
|
||
}
|
||
|
||
// export default Files;
|
||
module.exports = Files;
|