fileChoose.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. /**
  2. * 文件选择扩展模块
  3. * date:2019-08-03 License By http://easyweb.vip
  4. */
  5. layui.define(['jquery', 'layer', 'dropdown', 'contextMenu', 'form', 'upload', 'notice', 'util','laypage'], function (exports) {
  6. var $ = layui.jquery;
  7. var layer = layui.layer;
  8. var form = layui.form;
  9. var upload = layui.upload;
  10. var util = layui.util;
  11. var notice = layui.notice;
  12. var contextMenu = layui.contextMenu;
  13. var laypage = layui.laypage;
  14. /** 文件后缀对应图标 */
  15. var fileIcons = [{
  16. suffix: ['ppt', 'pptx'],
  17. icon: 'ppt'
  18. }, {
  19. suffix: ['doc', 'docx'],
  20. icon: 'doc'
  21. }, {
  22. suffix: ['xls', 'xlsx'],
  23. icon: 'xls'
  24. }, {
  25. suffix: ['pdf'],
  26. icon: 'pdf'
  27. }, {
  28. suffix: ['html', 'htm'],
  29. icon: 'htm'
  30. }, {
  31. suffix: ['txt'],
  32. icon: 'txt'
  33. }, {
  34. suffix: ['swf'],
  35. icon: 'flash'
  36. }, {
  37. suffix: ['zip', 'rar', '7z'],
  38. icon: 'zip'
  39. }, {
  40. suffix: ['mp3', 'wav'],
  41. icon: 'mp3'
  42. }, {
  43. suffix: ['mp4', '3gp', 'rmvb', 'avi', 'flv'],
  44. icon: 'mp4'
  45. }, {
  46. suffix: ['psd'],
  47. icon: 'psd'
  48. }, {
  49. suffix: ['ttf'],
  50. icon: 'ttf'
  51. }, {
  52. suffix: ['apk'],
  53. icon: 'apk'
  54. }, {
  55. suffix: ['exe'],
  56. icon: 'exe'
  57. }, {
  58. suffix: ['torrent'],
  59. icon: 'bt'
  60. }, {
  61. suffix: ['gif', 'png', 'jpeg', 'jpg', 'bmp'],
  62. icon: 'img'
  63. }, {
  64. // 20200321 新增代码图标
  65. suffix: ['html', 'css', 'js', 'php'],
  66. icon: 'code'
  67. }];
  68. var fileChoose = {};
  69. /** 打开选择文件弹窗 */
  70. fileChoose.open = function (param) {
  71. var fileUrl = param.fileUrl; // 文件查看url
  72. var listUrl = param.listUrl; // 文件列表url
  73. var uploadUrl = param.uploadUrl; // 上传url
  74. var operaUrl = param.operaUrl; // 操作url
  75. var where = param.where; // 文件列表请求参数
  76. var headers = param.headers; // 请求头部参数
  77. var chooseNum = param.num; // 文件选择的数量
  78. var onChoose = param.onChoose; // 选择回调
  79. var uploadOption = param.upload; // 文件上传配置
  80. var dialogOption = param.dialog; // 弹窗配置
  81. var operMenu = param.menu; // 操作菜单
  82. var operMenuClick = param.menuClick; // 操作菜单事件处理
  83. var response = param.response ? param.response : {}; // 返回数据格式
  84. var dirName = response.dir; // 文件列表请求参数文件夹名称
  85. var okCode = response.code; // 成功码
  86. var urlName = response.url; // url名称
  87. var smUrlName = response.smUrl; // 缩略图名称
  88. var isDirName = response.isDir; // 是否是文件夹名称
  89. var titleName = response.name; // 文件名称字段名
  90. var method = response.method; // 文件名称字段名
  91. var parseData = response.parseData; // 数据格式化
  92. var dataList = []; // 当前文件列表
  93. // 设置默认参数
  94. where || (where = {});
  95. (fileUrl == undefined) && (fileUrl = '');
  96. (chooseNum !== undefined) || (chooseNum = 1);
  97. uploadOption || (uploadOption = {});
  98. dialogOption || (dialogOption = {});
  99. dirName || (dirName = 'dir');
  100. (okCode !== undefined) || (okCode = 200);
  101. urlName || (urlName = 'url');
  102. smUrlName || (smUrlName = 'smUrl');
  103. isDirName || (isDirName = 'isDir');
  104. titleName || (titleName = 'name');
  105. method || (method = 'get');
  106. // 显示弹窗
  107. dialogOption.id = 'file-choose-dialog';
  108. dialogOption.type = 1;
  109. (dialogOption.title != undefined) || (dialogOption.title = '选择文件');
  110. dialogOption.content = '';
  111. dialogOption.area || (dialogOption.area = ['565px', '420px']);
  112. (dialogOption.shade != undefined) || (dialogOption.shade = .1);
  113. dialogOption.fixed || (dialogOption.fixed = false);
  114. dialogOption.skin || (dialogOption.skin = 'layer-file-choose');
  115. var sCallBack = param.success;
  116. dialogOption.success = function (layero, dIndex) {
  117. $(layero).children('.layui-layer-content').load(layui.cache.base + 'fileChoose/fileChoose.html', function () {
  118. init(); // 渲染页面
  119. sCallBack && sCallBack(layero, index);
  120. });
  121. };
  122. layer.open(dialogOption);
  123. // 获取文件列表
  124. function loadList(dir, page = 1, limit = 10) {
  125. dir || (dir = $('#fc-current-position').text());
  126. $('.file-choose-dialog .file-choose-loading-group').removeClass('layui-hide');
  127. where[dirName] = dir;
  128. where['page'] = page;
  129. where['limit'] = limit;
  130. $('#file-choose-list').html('');
  131. $.ajax({
  132. url: listUrl,
  133. type: method,
  134. data: where,
  135. headers: headers,
  136. dataType: 'json',
  137. success: function (res) {
  138. parseData && (res = parseData(res));
  139. if (res.code == okCode) {
  140. dataList = res.data;
  141. $('#fc-btn-ok-sel').text('完成选择');
  142. $('#file-choose-list').html(fileChoose.renderList({
  143. fileUrl: fileUrl,
  144. data: dataList.list,
  145. multi: chooseNum > 1,
  146. menu: operMenu,
  147. response: response
  148. }));
  149. // 渲染分页
  150. laypage.render({
  151. elem: "file-choose-page",
  152. count: dataList.count, // 总数据
  153. curr: page, // 当前页
  154. limit: limit,
  155. limits: 10 || [10, 20, 30, 40, 50, 60, 70, 80, 90],
  156. groups: 3,
  157. layout: ["prev", "page", "next"],
  158. prev: '<i class="layui-icon">&#xe603;</i>',
  159. next: '<i class="layui-icon">&#xe602;</i>',
  160. jump: function (t, e) {
  161. !e && loadList(dir, t.curr, t.limit);
  162. }
  163. });
  164. form.render('checkbox');
  165. } else {
  166. layer.msg(res.msg, {icon: 2, anim: 6});
  167. $('#file-choose-list').html(fileChoose.getErrorHtml('加载失败', 'layui-icon-face-cry'));
  168. }
  169. setTimeout(function () {
  170. $('.file-choose-dialog .file-choose-loading-group').addClass('layui-hide');
  171. }, 200);
  172. }
  173. });
  174. }
  175. // 事件处理
  176. function init() {
  177. (chooseNum > 1) || ($('.file-choose-dialog').addClass('hide-bottom'));
  178. loadList();
  179. // 刷新
  180. $('#fc-btn-refresh').click(function () {
  181. loadList();
  182. });
  183. // 返回上级
  184. $('#fc-btn-back').click(function () {
  185. var cDir = $('#fc-current-position').text();
  186. if (cDir != '/') {
  187. cDir = cDir.substring(0, cDir.lastIndexOf('/'));
  188. cDir || (cDir = '/');
  189. $('#fc-current-position').text(cDir);
  190. loadList(cDir);
  191. }
  192. });
  193. // 上传文件事件
  194. uploadOption.elem = '#fc-btn-upload';
  195. uploadOption.data || (uploadOption.data = {});
  196. // 2020/03/10 请求头
  197. uploadOption.headers = headers;
  198. uploadOption.url = uploadUrl;
  199. uploadOption.accept = 'file';
  200. // 限制大小
  201. uploadOption.size = 2048;
  202. uploadOption.data.dir = function () {
  203. return $('#fc-current-position').text();
  204. };
  205. uploadOption.before = function () {
  206. layer.load(2);
  207. };
  208. console.log('uploadOption', uploadOption)
  209. uploadOption.before = function (res, index, upload) {
  210. console.log('uploadOption.before' ,res,index,upload)
  211. },
  212. uploadOption.done = function (res, index, upload) {
  213. layer.closeAll('loading');
  214. if (res.code != okCode) {
  215. layer.msg(res.message, {icon: 2});
  216. } else {
  217. layer.msg(res.message, {icon: 1});
  218. // 目录直接刷新
  219. // 2020701 修改 许祖兴 zuxing.xu@lettered.cn
  220. //var dir = res.dir ? res.dir : util.toDateString(new Date(), '/yyyy/MM/dd');
  221. //$('#fc-current-position').text(dir);
  222. loadList();
  223. }
  224. };
  225. uploadOption.error = function () {
  226. layer.closeAll('loading');
  227. layer.msg('上传失败', {icon: 2});
  228. };
  229. upload.render(uploadOption);
  230. // 完成选择按钮
  231. $('#fc-btn-ok-sel').click(function () {
  232. var urls = [];
  233. $('input[lay-filter="file-choose-item-ck"]:checked').each(function () {
  234. var dataIndex = $(this).parents('.file-choose-list-item').data('index');
  235. urls.push(dataList.list[dataIndex]);
  236. });
  237. if (urls.length <= 0) {
  238. layer.msg('请选择', {icon: 2, anim: 6});
  239. } else if (urls.length > chooseNum) {
  240. layer.msg('最多只能选择' + chooseNum + '个', {icon: 2, anim: 6});
  241. } else {
  242. okChoose(urls);
  243. }
  244. });
  245. // 列表点击事件
  246. $(document).off('click.fcli').on('click.fcli', '.file-choose-dialog .file-choose-list-item', function (e) {
  247. console.log('列表点击事件')
  248. var item = dataList.list[$(this).data('index')];
  249. if (item[isDirName]) { // 是否是文件夹
  250. var cDir = $('#fc-current-position').text();
  251. cDir += (cDir == '/' ? item[titleName] : ('/' + item[titleName]));
  252. $('#fc-current-position').text(cDir);
  253. loadList(cDir);
  254. } else {
  255. var $cMenu = $(this).find('.file-choose-oper-menu');
  256. $('.file-choose-dialog .file-choose-oper-menu').not($cMenu).removeClass('show');
  257. $cMenu.toggleClass('show');
  258. e.stopPropagation();
  259. }
  260. });
  261. // 点击空白隐藏下拉框
  262. $(document).off('click.fclom').on('click.fclom', '.file-choose-dialog', function (e) {
  263. $('.file-choose-dialog .file-choose-oper-menu').removeClass('show');
  264. $('.ctxMenu').css({'display': 'none'});
  265. e.stopPropagation();
  266. });
  267. // 监听复选框选中
  268. form.on('checkbox(file-choose-item-ck)', function (data) {
  269. var ckSize = $('.file-choose-dialog input[lay-filter="file-choose-item-ck"]:checked').length;
  270. if (data.elem.checked) {
  271. if (ckSize > chooseNum) {
  272. layer.msg('最多只能选择' + chooseNum + '个', {icon: 2, anim: 6});
  273. $(data.elem).prop('checked', false);
  274. form.render('checkbox');
  275. return;
  276. }
  277. $(data.elem).parents('.file-choose-list-item').addClass('active');
  278. } else {
  279. $(data.elem).parents('.file-choose-list-item').removeClass('active');
  280. }
  281. $('#fc-btn-ok-sel').text('完成选择' + (ckSize > 0 ? ('(' + ckSize + ')') : ''));
  282. });
  283. // 点击多选框阻止列表事件
  284. $(document).off('click.fclic').on('click.fclic', '.file-choose-dialog .file-choose-list-item-ck', function (e) {
  285. e.stopPropagation();
  286. });
  287. // 菜单事件监听
  288. $(document).off('click.fclomi').on('click.fclomi', '.file-choose-dialog .file-choose-oper-menu-item', function () {
  289. var event = $(this).data('event');
  290. console.log('菜单事件监听');
  291. var dataIndex = $(this).parent().parent().data('index');
  292. if ('choose' == event) {
  293. if (chooseNum > 1) {
  294. $(this).parent().parent().find('.layui-form-checkbox').trigger('click');
  295. } else {
  296. okChoose([dataList.list[dataIndex]]);
  297. }
  298. } else if ('preview' == event) {
  299. var url = (fileUrl + dataList.list[dataIndex][urlName]);
  300. if ('img' == fileChoose.getFileType(url)) {
  301. var imgUrls = [], start = 0;
  302. for (var i = 0; i < dataList.list.length; i++) {
  303. var tUrl = fileUrl + dataList.list[i][urlName];
  304. if ('img' == fileChoose.getFileType(tUrl)) {
  305. imgUrls.push({src: tUrl, alt: dataList.list[i][titleName]});
  306. }
  307. if (url == tUrl) {
  308. start = imgUrls.length - 1;
  309. }
  310. }
  311. layer.photos({photos: {start: start, data: imgUrls}, shade: .1, closeBtn: true});
  312. } else {
  313. layer.confirm('这不是图片类型,可能需要下载才能预览,确定要打开吗?', {
  314. title: '温馨提示',
  315. area: '260px',
  316. shade: .1
  317. }, function (index) {
  318. layer.close(index);
  319. window.open(url);
  320. });
  321. }
  322. } else {
  323. operMenuClick && operMenuClick(event, dataList.list[dataIndex]);
  324. }
  325. });
  326. $(document).on('contextmenu', '#file-choose-list .file-choose-list-item', function (e) {
  327. var t = $(this),
  328. cx = e.clientX,
  329. cy = e.clientY,
  330. data = dataList.list[t.data('index')];
  331. contextMenu.show([{
  332. name: '<span style="color: red;">删除</span>',
  333. click: function () {
  334. operation({name: data.name, folder: (data.type === 'dir')}, 'delete');
  335. }
  336. }], cx, cy);
  337. e.stopPropagation();
  338. return false;
  339. });
  340. $(document).on('click', '.create-btn', function (e) {
  341. var t = $(this),
  342. d = t.attr('data-value');
  343. // 弹窗输入
  344. layer.prompt({
  345. title: '新建' + (d === 'file'? '文件' : '目录'),
  346. }, function(value, index, elem) {
  347. // todo 验证
  348. if (d === 'file' && !(/^\w+\.{1}[a-z]+$/.test(value))){
  349. notice.msg('文件名称错误', {icon: 2});
  350. return false;
  351. }
  352. // operation
  353. operation({name: value, folder: (d !== 'file')}, 'post');
  354. layer.close(index);
  355. });
  356. $('.layui-layer-input').attr('placeholder', (d === 'file'? '文件' : '目录') + '名称,请勿使用中文名');
  357. e.stopPropagation();
  358. return false;
  359. });
  360. }
  361. // 完成选择
  362. function okChoose(urls) {
  363. onChoose && onChoose(urls);
  364. layer.close($('#fc-btn-ok-sel').parents('.layui-layer').attr('id').substring(11));
  365. }
  366. // 统一操作
  367. function operation(data, method = 'get') {
  368. var cDir = $('#tv-current-position').text(),
  369. f = function () {
  370. // operation
  371. $.ajax({
  372. url: operaUrl,
  373. type: method,
  374. data: data,
  375. headers: headers,
  376. dataType: 'json',
  377. success: function (res) {
  378. if (res.code === 10000) {
  379. notice.msg('操作成功', {icon: 1});
  380. loadList();
  381. } else {
  382. notice.msg(res.message, {icon: 2});
  383. }
  384. }
  385. })
  386. // admin.req('system/annex/operation', $.extend({}, data, {dir: cDir}), function (res) {
  387. // if (res.code === 10000) {
  388. // notice.msg('操作成功', {icon: 1});
  389. // renderList(cDir);
  390. // } else {
  391. // notice.msg(res.message, {icon: 2});
  392. // }
  393. // }, method);
  394. };
  395. if (method === 'get'){
  396. layer.confirm('确定要删除吗?', {title: false, closeBtn: 0, shade: .1}, function (index) {
  397. f();
  398. layer.close(index);
  399. });
  400. }else {
  401. f();
  402. }
  403. }
  404. };
  405. // 渲染文件列表
  406. fileChoose.renderList = function (param) {
  407. var fileUrl = param.fileUrl; // 文件服务器地址
  408. var dataList = param.data; // 数据
  409. var multi = param.multi; // 是否多选
  410. var operMenu = param.menu; // 操作菜单
  411. var response = param.response ? param.response : {}; // 返回数据格式
  412. var urlName = response.url; // url名称
  413. var smUrlName = response.smUrl; // 缩略图名称
  414. var isDirName = response.isDir; // 是否是文件夹名称
  415. var titleName = response.name; // 文件名称字段名
  416. (fileUrl == undefined) && (fileUrl = '');
  417. (dataList == undefined) && (dataList = []);
  418. (multi == undefined) && (multi = false);
  419. urlName || (urlName = 'url');
  420. smUrlName || (smUrlName = 'smUrl');
  421. isDirName || (isDirName = 'isDir');
  422. titleName || (titleName = 'name');
  423. var html = '';
  424. if (dataList.length <= 0) {
  425. html += fileChoose.getErrorHtml('没有文件');
  426. } else {
  427. for (var i = 0; i < dataList.length; i++) {
  428. var item = dataList[i];
  429. html += '<div class="file-choose-list-item" data-index="' + i + '">';
  430. var imgUrl = fileUrl + item[smUrlName], fileImgIcon = '';
  431. if (!item[smUrlName]) {
  432. fileImgIcon = ' img-icon';
  433. imgUrl = fileChoose.getFileIcon(item[urlName], item[isDirName]);
  434. }
  435. var bgStyle = "background-image: url('" + imgUrl + "')";
  436. html += ' <div class="file-choose-list-item-img' + fileImgIcon + '" style="' + bgStyle + '"></div>';
  437. html += ' <div class="file-choose-list-item-name" title="' + item[titleName] + '">' + item[titleName] + '</div>';
  438. if (!item[isDirName] && multi) {
  439. html += ' <div class="file-choose-list-item-ck layui-form">';
  440. html += ' <input type="checkbox" lay-skin="primary" lay-filter="file-choose-item-ck"/>';
  441. html += ' </div>';
  442. }
  443. if (!operMenu) {
  444. html += '<div class="file-choose-oper-menu">';
  445. html += ' <div class="file-choose-oper-menu-item" data-event="preview">预览</div>';
  446. html += ' <div class="file-choose-oper-menu-item" data-event="choose">选择</div>';
  447. html += '</div>';
  448. } else if (operMenu.length > 0) {
  449. html += '<div class="file-choose-oper-menu">';
  450. // 20200320 新增允许操作
  451. if (['txt','php','html','css','js'].indexOf(item.type) !== -1) {
  452. html += '<div class="file-choose-oper-menu-item" data-event="edit">编辑</div>';
  453. }
  454. for (var mi = 0; mi < operMenu.length; mi++) {
  455. var menuItem = operMenu[mi];
  456. html += '<div class="file-choose-oper-menu-item" data-event="' + menuItem.event + '">' + menuItem.name + '</div>';
  457. }
  458. html += '</div>';
  459. }
  460. html += '</div>';
  461. }
  462. }
  463. return html;
  464. };
  465. // 显示空布局或错误布局
  466. fileChoose.getErrorHtml = function (msg, icon) {
  467. icon || (icon = 'layui-icon-face-surprised');
  468. var html = '';
  469. html += '<div class="file-choose-empty">';
  470. html += ' <i class="layui-icon ' + icon + '"></i>';
  471. html += ' <p>' + msg + '</p>';
  472. html += '</div>';
  473. return html;
  474. };
  475. // 根据文件后缀获取图标
  476. fileChoose.getFileIcon = function (url, isDir) {
  477. var type = isDir ? 'dir' : fileChoose.getFileType(url);
  478. return layui.cache.base + 'fileChoose/img/' + type + '.png';
  479. };
  480. // 根据文件后缀获取文件类型
  481. fileChoose.getFileType = function ( url = 'unknown.file') {
  482. var type = 'file';
  483. var suffix = url.substring(url.lastIndexOf('.') + 1);
  484. for (var i = 0; i < fileIcons.length; i++) {
  485. for (var j = 0; j < fileIcons[i].suffix.length; j++) {
  486. if (suffix.toLowerCase() == fileIcons[i].suffix[j]){
  487. type = fileIcons[i].icon;
  488. break;
  489. }
  490. }
  491. }
  492. return type;
  493. };
  494. $('body').append('<style>.layer-file-choose { max-width: 100%;}@media screen and (max-width:768px){.layer-file-choose{max-width:98%;max-width:-moz-calc(100% - 30px);max-width:-webkit-calc(100% - 30px);max-width:calc(100% - 30px);left:0!important;right:0!important;margin:auto!important;margin-bottom:15px!important}}</style>');
  495. exports("fileChoose", fileChoose);
  496. });