tools_att.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. 'use strict';
  2. /**
  3. *
  4. *
  5. * @author Mai
  6. * @date
  7. * @version
  8. */
  9. (function($){
  10. $.ledger_att = function (setting) {
  11. if (!setting.selector) return;
  12. if (!setting.masterKey) setting.masterKey = setting.key;
  13. const obj = $(setting.selector);
  14. const fileInfo = setting.fileInfo || { user_name: 'username', user_id: 'uid', create_time: 'in_time' };
  15. const pageLength = 20;
  16. let curNode = null, curPage = 0;
  17. obj.html(
  18. '<div class="sjs-bar">\n' +
  19. ' <ul class="nav nav-tabs">\n' +
  20. ' <li class="nav-item"><a class="nav-link active" data-toggle="tab" href="#att-cur" role="tab" att-type="cur" id="att-cur-button">当前节点</a></li>\n' +
  21. ' <li class="nav-item"><a class="nav-link" data-toggle="tab" href="#att-all" role="tab" att-type="all">所有附件</a></li>\n' +
  22. ' <li class="nav-item ml-auto pt-1">\n' +
  23. ' <a href="javascript:void(0);" id="batch-download" class="btn btn-sm btn-primary" type="curr">批量下载</a>\n' +
  24. ' <span id="showPage" style="display: none"><a href="javascript:void(0);" class="page-select ml-3" content="pre"><i class="fa fa-chevron-left"></i></a> <span id="att-cur-page">1</span>/<span id="att-total-page">10</span> <a href="javascript:void(0);" class="page-select mr-3" content="next"><i class="fa fa-chevron-right"></i></a></span>\n' +
  25. (setting.readOnly ? '' : ' <a href="#upload" data-toggle="modal" data-target="#upload" class="btn btn-sm btn-outline-primary ml-2">上传</a>\n') +
  26. (setting.readOnly || !setting.bigValid ? '' : ' <a href="#add-big-file" data-toggle="modal" data-target="#add-big-file" class="btn btn-sm btn-outline-primary ml-2">上传大文件</a>\n') +
  27. ' </li>\n' +
  28. ' </ul>\n' +
  29. '</div>\n' +
  30. '<div class="sjs-sh tab-content">\n' +
  31. ' <div class="tab-pane active" id="att-cur" style="height: 100%">\n' +
  32. ' <div style="overflow:auto; overflow-x:hidden; height: 100%">\n' +
  33. ' <div class="mt-1 mb-1" id="att-cur-hint"></div>\n' +
  34. ' <table class="table table-sm table-bordered table-hover" style="word-break:break-all; table-layout: fixed">\n' +
  35. ' <thead><tr><th width="25"><input type="checkbox" class="check-all-file"><th>文件名</th><th width="80">上传</th><th width="80">操作</th></tr></thead>\n' +
  36. ' <tbody id="cur-att-list" class="list-table"></tbody>\n' +
  37. ' </table>\n' +
  38. ' </div>\n' +
  39. ' </div>\n' +
  40. ' <div class="tab-pane" id="att-all" style="height: 100%">\n' +
  41. ' <div style="overflow:auto; overflow-x:hidden; height: 100%">\n' +
  42. ' <table class="table table-sm table-bordered table-hover" style="word-break:break-all; table-layout: fixed">\n' +
  43. ' <thead><tr><th width="25"><input type="checkbox" class="check-all-file"></th><th>文件名</th><th width="80">上传</th><th width="80">操作</th></tr></thead>\n' +
  44. ' <tbody id="all-att-list" class="list-table"></tbody>\n' +
  45. ' </table>\n' +
  46. ' </div>\n' +
  47. ' </div>\n' +
  48. '</div>'
  49. );
  50. autoFlashHeight();
  51. $('#att-cur-button')[0].click();
  52. let allAtts = [], nodeIndexes = {};
  53. const getNodeHint = function(node) {
  54. return setting.getCurHint ? setting.getCurHint(node) : `${node.code || node.b_code || ''}/${node.name || ''}`;
  55. };
  56. const getAttHtml = function(att, tipNode = false) {
  57. const html = [];
  58. html.push('<tr>');
  59. html.push(`<td width="25"><input type="checkbox" class="check-file" file-id=${att.id}></td>`);
  60. let nodeInfo = '';
  61. if (tipNode && att.node) nodeInfo = getNodeHint(att.node);
  62. const tipHtml = nodeInfo ? `${nodeInfo}\n${att[fileInfo.create_time]}` : att[fileInfo.create_time];
  63. const tipType = 'title='; //'data-toggle="tooltip" data-html="true" data-placement="left" data-original-title=';
  64. html.push(`<td><div class="d-flex"><a href="javascript:void(0)" ${tipType}"${tipHtml}" class="pl-0 col-11" file-id=${att.id}>${att.filename}${att.fileext}</a></div></td>`);
  65. html.push(`<td>${att[fileInfo.user_name]}</td>`);
  66. const canDel = setting.readOnly ? false : att[fileInfo.user_id] === userID && (!setting.checked || att.extra_upload);
  67. html.push('<td width="80">',
  68. `<a class="ml-1" href="javascript:void(0)" ${tipType}"定位" name="att-locate" file-id="${att.id}"><i class="fa fa-crosshairs"></i></a>`,
  69. att.viewpath ? `<a class="ml-1" href="${att.viewpath}" ${tipType}"预览" target="_blank"><i class="fa fa-eye"></i></a>` : '',
  70. `<a class="ml-1" href="javascript:void(0)" ${tipType}"下载" onclick="AliOss.downloadFile('${att.filepath}', '${att.filename}${att.fileext}')"><i class="fa fa-download"></i></a>`,
  71. canDel ? `<a class="ml-1 text-danger" href="javascript:void(0)" name="att-delete" file-id="${att.id}"><i class="fa fa-close" ${tipType}"删除"></i></a>` : '',
  72. '</td>');
  73. html.push('</tr>');
  74. return html.join('');
  75. };
  76. const refreshCurAttHtml = function () {
  77. const html = [];
  78. const atts = curNode ? (nodeIndexes[curNode[setting.key]]) || [] : [];
  79. for (const att of atts) {
  80. html.push(getAttHtml(att));
  81. }
  82. $('#cur-att-list').html(html.join());
  83. $('[data-toggle="tooltip"]').tooltip();
  84. };
  85. const refreshAllAttHtml = function () {
  86. let curPage = parseInt($('#att-cur-page').text());
  87. if (allAtts.length === 0 && curPage !== 0) curPage = 0;
  88. if (allAtts.length > 0 && curPage === 0) curPage = 1;
  89. $('#att-cur-page').text(curPage);
  90. const pageNum = Math.ceil(allAtts.length/pageLength);
  91. $('#att-total-page').text(pageNum);
  92. const currPageAttData = allAtts.slice((curPage-1)*pageLength, curPage*pageLength);
  93. const html = [];
  94. for(const att of currPageAttData) {
  95. html.push(getAttHtml(att, true));
  96. }
  97. $('#all-att-list').html(html.join());
  98. $('[data-toggle="tooltip"]').tooltip();
  99. };
  100. const getAllAttHtml = function (page = 1) {
  101. curPage = allAtts.length ? page : 0;
  102. $('#att-cur-page').text(curPage);
  103. refreshAllAttHtml();
  104. };
  105. const getCurAttHtml = function (node) {
  106. curNode = node;
  107. if (curNode) {
  108. $('#att-cur-hint').text(getNodeHint(curNode));
  109. } else {
  110. $('#att-cur-hint').text('');
  111. }
  112. refreshCurAttHtml();
  113. };
  114. const findFile = setting.fileIdType === 'string'
  115. ? function (list, id) { return list.find(item => item.id === id); }
  116. : function (list, id) { return list.find(item => item.id === parseInt(id)); };
  117. const findFileIndex = setting.fileIdType === 'string'
  118. ? function (list, id) { return list.findIndex(item => item.id === id); }
  119. : function (list, id) { return list.findIndex(item => item.id === parseInt(id)); };
  120. // 选中行
  121. $('body').on('click', '#all-att-list tr', function() {
  122. $('#all-att-list tr').removeClass('bg-light');
  123. $(this).addClass('bg-light');
  124. });
  125. $('body').on('click', '#cur-att-list tr', function() {
  126. $('#cur-att-list tr').removeClass('bg-light');
  127. $(this).addClass('bg-light');
  128. });
  129. // 切换 当前节点/所有附件
  130. $('#fujian .nav-link').on('click', function () {
  131. const tabPanel = $(this).attr('att-type');
  132. if (tabPanel !== 'all') {
  133. $('#showPage').hide();
  134. $('#batch-download').prop('type', 'curr');
  135. } else {
  136. $('#showPage').show();
  137. $('#batch-download').prop('type', 'all')
  138. }
  139. });
  140. // 切换页数
  141. $('.page-select').on('click', function () {
  142. const totalPageNum = parseInt($('#att-total-page').text());
  143. const lastPageNum = parseInt($('#att-cur-page').text());
  144. const status = $(this).attr('content');
  145. if (status === 'pre' && lastPageNum > 1) {
  146. getAllAttHtml(lastPageNum-1);
  147. $('#showAttachment').hide();
  148. $('#att-all .check-all-file').prop('checked', false)
  149. } else if (status === 'next' && lastPageNum < totalPageNum) {
  150. getAllAttHtml(lastPageNum+1);
  151. $('#showAttachment').hide();
  152. $('#att-all .check-all-file').prop('checked', false)
  153. }
  154. });
  155. // 批量下载
  156. $('#batch-download').click(function() {
  157. const self = this;
  158. const files = [];
  159. const type = $(this).prop('type');
  160. let node = '';
  161. if (type === 'curr') {
  162. node = '#cur-att-list .check-file:checked'
  163. } else {
  164. node = '#all-att-list .check-file:checked'
  165. }
  166. $(node).each(function() {
  167. const fid = $(this).attr('file-id');
  168. const att = findFile(allAtts, fid);
  169. att && files.push(att);
  170. });
  171. if (files.length === 0) return;
  172. $(self).attr('disabled', 'true');
  173. AliOss.zipFiles(files, setting.zipName, (fails) => {
  174. $(self).removeAttr('disabled');
  175. if (fails.length === 0) {
  176. toastr.success('下载成功');
  177. } else {
  178. toastr.warning(`下载成功(${fails.length}个文件下载失败)`);
  179. }
  180. }, () => {
  181. $(self).removeAttr('disabled');
  182. toastr.error('批量下载失败');
  183. });
  184. });
  185. // 上传附件
  186. $('#upload-file-btn').click(function () {
  187. if (!curNode) {
  188. toastr.error('请先选择台账节点');
  189. return false;
  190. }
  191. const files = $('#upload-file')[0].files;
  192. const formData = new FormData();
  193. formData.append(setting.masterKey, curNode[setting.key]);
  194. for (const file of files) {
  195. if (file === undefined) {
  196. toastr.error('未选择上传文件!');
  197. return false;
  198. }
  199. const filesize = file.size;
  200. if (filesize > 50 * 1024 * 1024) {
  201. toastr.error('存在上传文件大小过大!');
  202. return false;
  203. }
  204. const fileext = '.' + file.name.toLowerCase().split('.').splice(-1)[0];
  205. if (whiteList.indexOf(fileext) === -1) {
  206. toastr.error('只能上传指定格式的附件!');
  207. return false;
  208. }
  209. formData.append('size', filesize);
  210. formData.append('file[]', file);
  211. }
  212. postDataWithFile(setting.uploadUrl, formData, function (data) {
  213. // 插入到attData中
  214. data.forEach(d => {
  215. d.node = curNode;
  216. allAtts.push(d);
  217. _addToNodeIndex(d, true);
  218. });
  219. // 重新生成List
  220. refreshAllAttHtml();
  221. refreshCurAttHtml();
  222. $('#upload').modal('hide');
  223. }, function () {
  224. toastr.error('附件上传失败');
  225. });
  226. $('#upload-file').val('');
  227. });
  228. $('#add-big-file').on('show.bs.modal', function() {
  229. $('#upload-big-file-hint').hide();
  230. $('#upload-big-file')[0].value = '';
  231. if ($('#add-big-file-ok').hasClass('btn-warning')) $('#add-big-file-ok').removeClass('btn-warning').addClass('btn-primary');
  232. });
  233. $('#add-big-file-ok').click(() => {
  234. if (!curNode) {
  235. toastr.error('请先选择台账节点');
  236. return false;
  237. }
  238. const input = $('#upload-big-file');
  239. const file = input[0].files[0];
  240. if (file.size > 500 * 1024 * 1024) {
  241. toastr.error('上传文件大小超过500MB。');
  242. return false;
  243. }
  244. const fileext = '.' + file.name.toLowerCase().split('.').splice(-1)[0];
  245. if (whiteList.indexOf(fileext) === -1) {
  246. toastr.error('仅支持office文档、图片、压缩包格式,请勿上传' + fileext + '格式文件。');
  247. return false;
  248. }
  249. const data = {};
  250. data[setting.masterKey] = curNode[setting.key];
  251. AliOss.uploadBigFile(file, setting.uploadBigUrl, data,
  252. { progressObj: $('#upload-big-file-progress'), resumeObj: $('#add-big-file-resume'), stopObj: $('#add-big-file-stop') },
  253. function(result) {
  254. result.forEach(d => {
  255. d.node = curNode;
  256. allAtts.push(d);
  257. _addToNodeIndex(d, true);
  258. });
  259. refreshAllAttHtml();
  260. refreshCurAttHtml();
  261. $('#upload-big-file').val('');
  262. $('#add-big-file').modal('hide');
  263. });
  264. });
  265. $('body').on('click', 'a[name=att-locate]', function () {
  266. const fid = this.getAttribute('file-id');
  267. const att = findFile(allAtts, fid);
  268. setting.locate && setting.locate(att);
  269. });
  270. $('body').on('click', 'a[name=att-delete]', function () {
  271. const fid = this.getAttribute('file-id');
  272. const data = {id: fid};
  273. postData(setting.deleteUrl, data, function (result) {
  274. // 删除
  275. const att_index = findFileIndex(allAtts, fid);
  276. const att = allAtts[att_index];
  277. allAtts.splice(att_index, 1);
  278. const xi = nodeIndexes[att.node[setting.key]];
  279. xi.splice(findFileIndex(xi, fid), 1);
  280. // 重新生成List
  281. if (allAtts.length === 1) {
  282. getAllAttHtml();
  283. } else {
  284. refreshAllAttHtml();
  285. }
  286. refreshCurAttHtml();
  287. });
  288. });
  289. // 监听附件check是否选中
  290. $('.list-table').on('click', '.check-file', function() {
  291. const checkedList = $(this).parents('.list-table').children().find('input:checked');
  292. const childs = $(this).parents('.list-table').children().length;
  293. const checkBox = $(this).parents('.list-table').parent().find('.check-all-file');
  294. if (checkedList.length === childs) {
  295. checkBox.prop("checked", true);
  296. } else {
  297. checkBox.prop("checked", false);
  298. }
  299. });
  300. $('.check-all-file').click(function() {
  301. const isCheck = $(this).is(':checked');
  302. $(this).parents('table').find('.list-table').each(function() {
  303. $(this).find('input:checkbox').prop("checked", isCheck);
  304. })
  305. });
  306. const _addToNodeIndex = function(att, isTop = false) {
  307. const id = att[setting.masterKey];
  308. if (!nodeIndexes[id]) {
  309. nodeIndexes[id] = [];
  310. }
  311. let xi = nodeIndexes[id];
  312. isTop ? xi.unshift(att) : xi.push(att);
  313. };
  314. const loadDatas = function (datas) {
  315. for (const d of datas) {
  316. allAtts.push(d);
  317. _addToNodeIndex(d);
  318. }
  319. getAllAttHtml();
  320. };
  321. const deleteFileByNodeId = function(nodeIds) {
  322. for (const id of nodeIds) {
  323. const nodeIndex = nodeIndexes[id];
  324. if (!nodeIndex || nodeIndex.length === 0) continue;
  325. for (const att of nodeIndex) {
  326. const att_index = findFileIndex(allAtts, att.id);
  327. allAtts.splice(att_index, 1);
  328. }
  329. delete nodeIndexes[id];
  330. }
  331. if (allAtts.length === 1) {
  332. getAllAttHtml();
  333. } else {
  334. refreshAllAttHtml();
  335. }
  336. refreshCurAttHtml();
  337. };
  338. return { loadDatas, getCurAttHtml, deleteFileByNodeId }
  339. };
  340. })(jQuery);