upload.js 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231
  1. (function (w, $) {
  2. function Uploader(opts) {
  3. opts = $.extend({
  4. wrapper: '.web-uploader', // 图片显示容器选择器
  5. addFileButton: '.add-file-button', // 继续添加按钮选择器
  6. inputSelector: '',
  7. isImage: false,
  8. preview: [], // 数据预览
  9. server: '',
  10. updateServer: '',
  11. sortable: false,
  12. deleteUrl: '',
  13. deleteData: {},
  14. thumbHeight: 160,
  15. disabled: false, // 禁止任何上传编辑
  16. autoUpdateColumn: false,
  17. disableRemove: false, // 禁止删除图片,允许替换
  18. dimensions: {
  19. // width: 100, // 图片宽限制
  20. // height: 100, // 图片高限制
  21. // min_width: 100, //
  22. // min_height: 100,
  23. // max_width: 100,
  24. // max_height: 100,
  25. // ratio: 3/2, // 宽高比
  26. },
  27. lang: {
  28. exceed_size: '文件大小超出',
  29. interrupt: '上传暂停',
  30. upload_failed: '上传失败,请重试',
  31. selected_files: '选中:num个文件,共:size。',
  32. selected_has_failed: '已成功上传:success个文件,:fail个文件上传失败,<a class="retry" href="javascript:"";">重新上传</a>失败文件或<a class="ignore" href="javascript:"";">忽略</a>',
  33. selected_success: '共:num个(:size),已上传:success个。',
  34. dot: ',',
  35. failed_num: '失败:fail个。',
  36. pause_upload: '暂停上传',
  37. go_on_upload: '继续上传',
  38. start_upload: '开始上传',
  39. upload_success_message: '已成功上传:success个文件',
  40. go_on_add: '继续添加',
  41. Q_TYPE_DENIED: '对不起,不允许上传此类型文件',
  42. Q_EXCEED_NUM_LIMIT: '对不起,已超出文件上传数量限制,最多只能上传:num个文件',
  43. F_EXCEED_SIZE: '对不起,当前选择的文件过大',
  44. Q_EXCEED_SIZE_LIMIT: '对不起,已超出文件大小限制',
  45. F_DUPLICATE: '文件重复',
  46. confirm_delete_file: '您确定要删除这个文件吗?',
  47. },
  48. upload: { // web-uploader配置
  49. formData: {
  50. _id: null, // 唯一id
  51. },
  52. thumb: {
  53. width: 160,
  54. height: 160,
  55. quality: 70,
  56. allowMagnify: true,
  57. crop: true,
  58. preserveHeaders: false,
  59. // 为空的话则保留原有图片格式。
  60. // 否则强制转换成指定的类型。
  61. // IE 8下面 base64 大小不能超过 32K 否则预览失败,而非 jpeg 编码的图片很可
  62. // 能会超过 32k, 所以这里设置成预览的时候都是 image/jpeg
  63. type: 'image/jpeg'
  64. },
  65. }
  66. }, opts);
  67. var $selector = $(opts.selector),
  68. updateColumn = opts.upload.formData.upload_column || ('webup' + Math.floor(Math.random()*10000)),
  69. relation = opts.upload.formData._relation, // 一对多关联关系名称
  70. elementName = opts.elementName;
  71. if (typeof opts.upload.formData._id == "undefined" || !opts.upload.formData._id) {
  72. opts.upload.formData._id = updateColumn + Math.floor(Math.random()*10000);
  73. }
  74. let Dcat = w.Dcat,
  75. $wrap,
  76. // 展示图片
  77. showImg = opts.isImage,
  78. // 图片容器
  79. $queue,
  80. // 状态栏,包括进度和控制按钮
  81. $statusBar,
  82. // 文件总体选择信息。
  83. $info,
  84. // 上传按钮
  85. $upload,
  86. // 没选择文件之前的内容。
  87. $placeHolder,
  88. $progress,
  89. // 已上传文件数量
  90. originalFilesNum = Dcat.helpers.len(opts.preview),
  91. // 上传表单
  92. $input = $selector.find(opts.inputSelector),
  93. // 获取文件视图选择器
  94. getFileViewSelector = function (fileId) {
  95. return elementName.replace(/[\[\]]*/g, '_')+'-'+fileId;
  96. },
  97. getFileView = function (fileId) {
  98. return $('#' + getFileViewSelector(fileId));
  99. },
  100. // 继续添加按钮选择器
  101. addFileButtonSelector = opts.addFileButton,
  102. // 临时存储上传失败的文件,key为file id
  103. faildFiles = {},
  104. // 临时存储添加到form表单的文件
  105. formFiles = {},
  106. // 添加的文件数量
  107. fileCount = 0,
  108. // 添加的文件总大小
  109. fileSize = 0,
  110. // 可能有pedding, ready, uploading, confirm, done.
  111. state = 'pedding',
  112. // 所有文件的进度信息,key为file id
  113. percentages = {},
  114. // 判断浏览器是否支持图片的base64
  115. isSupportBase64 = (function () {
  116. var data = new Image();
  117. var support = true;
  118. data.onload = data.onerror = function () {
  119. if (this.width != 1 || this.height != 1) {
  120. support = false;
  121. }
  122. };
  123. data.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
  124. return support;
  125. })(),
  126. // 检测是否已经安装flash,检测flash的版本
  127. flashVersion = (function () {
  128. var version;
  129. try {
  130. version = navigator.plugins['Shockwave Flash'];
  131. version = version.description;
  132. } catch (ex) {
  133. try {
  134. version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash')
  135. .GetVariable('$version');
  136. } catch (ex2) {
  137. version = '0.0';
  138. }
  139. }
  140. version = version.match(/\d+/g);
  141. return parseFloat(version[0] + '.' + version[1], 10);
  142. })(),
  143. // 判断是否是图片
  144. isImage = function (file) {
  145. return file.type.match(/^image/);
  146. },
  147. // 翻译
  148. lang = Dcat.Translator(opts.lang),
  149. __ = lang.trans.bind(lang),
  150. // WebUploader实例
  151. uploader,
  152. // 已上传的文件
  153. uploadedFiles = [],
  154. initImg;
  155. // 当有文件添加进来时执行,负责view的创建
  156. function addFile(file) {
  157. var size = WebUploader.formatSize(file.size), $li, $btns, fileName = file.name || null;
  158. if (showImg) {
  159. $li = $(`<li id="${getFileViewSelector(file.id)}" title="${fileName}" >
  160. <p class="file-type">${(file.ext.toUpperCase() || 'FILE')}</p>
  161. <p class="imgWrap "></p>
  162. <p class="title" style="">${file.name}</p>
  163. <p class="title" style="margin-bottom:20px;">(<b>${size}</b>)</p>
  164. </li>`);
  165. $btns = $(`<div class="file-panel">
  166. <a class="btn btn-sm btn-white" data-file-act="cancel"><i class="feather icon-x red-dark" style="font-size:13px"></i></a>
  167. <a class="btn btn-sm btn-white" data-file-act="delete" style="display: none">
  168. <i class="feather icon-trash red-dark" style="font-size:13px"></i></a>
  169. <a class="btn btn-sm btn-white" data-file-act="preview" ><i class="feather icon-zoom-in"></i></a>
  170. <a class='btn btn-sm btn-white' data-file-act='order' data-order="1" style="display: none"><i class='feather icon-arrow-up'></i></a>
  171. <a class='btn btn-sm btn-white' data-file-act='order' data-order="0" style="display: none"><i class='feather icon-arrow-down'></i></a>
  172. </div>`).appendTo($li);
  173. } else {
  174. $li = $(`
  175. <li id="${getFileViewSelector(file.id)}" title="${file.nam}">
  176. <p class="title" style="display:block">
  177. <i class="feather icon-check green _success icon-success"></i>
  178. ${file.name} (${size})
  179. </p>
  180. </li>
  181. `);
  182. $btns = $(`
  183. <span style="right: 45px;" class="file-action d-none" data-file-act='order' data-order="1"><i class='feather icon-arrow-up'></i></span>
  184. <span style="right: 25px;" class="file-action d-none" data-file-act='order' data-order="0"><i class='feather icon-arrow-down'></i></span>
  185. <span data-file-act="cancel" class="file-action" style="font-size:13px">
  186. <i class="feather icon-x red-dark"></i>
  187. </span>
  188. <span data-file-act="delete" class="file-action" style="display:none">
  189. <i class="feather icon-trash red-dark"></i>
  190. </span>
  191. `).appendTo($li);
  192. }
  193. var $wrap = $li.find('p.imgWrap'),
  194. $info = $('<p class="error"></p>'),
  195. showError = function (code, file) {
  196. var text = '';
  197. switch (code) {
  198. case 'exceed_size':
  199. text = __('exceed_size');
  200. break;
  201. case 'interrupt':
  202. text = __('interrupt');
  203. break;
  204. default:
  205. text = __('upload_failed');
  206. break;
  207. }
  208. faildFiles[file.id] = file;
  209. $info.text(text).appendTo($li);
  210. };
  211. $li.appendTo($queue);
  212. setTimeout(function () {
  213. $li.css({margin: '5px'});
  214. }, 50);
  215. if (file.getStatus() === 'invalid') {
  216. showError(file.statusText, file);
  217. } else {
  218. if (showImg) {
  219. var image = uploader.makeThumb(file, function (error, src) {
  220. var img;
  221. $wrap.empty();
  222. if (error) {
  223. $li.find('.title').show();
  224. $li.find('.file-type').show();
  225. return;
  226. }
  227. if (isSupportBase64) {
  228. img = $('<img src="' + src + '">');
  229. $wrap.append(img);
  230. } else {
  231. $li.find('.file-type').show();
  232. }
  233. });
  234. try {
  235. image.once('load', function () {
  236. file._info = file._info || image.info();
  237. file._meta = file._meta || image.meta();
  238. var width = file._info.width,
  239. height = file._info.height;
  240. if (!validateDimensions(file)) {
  241. Dcat.error('The image dimensions is invalid.');
  242. uploader.removeFile(file);
  243. return false;
  244. }
  245. image.resize(width, height);
  246. });
  247. } catch (e) {
  248. // 不是图片
  249. return setTimeout(function () {
  250. uploader.removeFile(file);
  251. }, 10);
  252. }
  253. }
  254. percentages[file.id] = [file.size, 0];
  255. file.rotation = 0;
  256. }
  257. file.on('statuschange', function (cur, prev) {
  258. if (prev === 'progress') {
  259. // $prgress.hide().width(0);
  260. } else if (prev === 'queued') {
  261. $btns.find('[data-file-act="cancel"]').hide();
  262. $btns.find('[data-file-act="delete"]').show();
  263. }
  264. // 成功
  265. if (cur === 'error' || cur === 'invalid') {
  266. showError(file.statusText, file);
  267. percentages[file.id][1] = 1;
  268. } else if (cur === 'interrupt') {
  269. showError('interrupt', file);
  270. } else if (cur === 'queued') {
  271. percentages[file.id][1] = 0;
  272. } else if (cur === 'progress') {
  273. $info.remove();
  274. // $prgress.css('display', 'block');
  275. } else if (cur === 'complete') {
  276. if (showImg) {
  277. $li.append('<span class="success"><em></em><i class="feather icon-check"></i></span>');
  278. } else {
  279. $li.find('._success').show();
  280. }
  281. }
  282. $li.removeClass('state-' + prev).addClass('state-' + cur);
  283. });
  284. var $act = showImg ? $btns.find('a') : $btns;
  285. $act.on('click', function () {
  286. var index = $(this).data('file-act');
  287. switch (index) {
  288. case 'cancel':
  289. uploader.removeFile(file);
  290. return;
  291. case 'deleteurl':
  292. case 'delete':
  293. if (opts.disableRemove) {
  294. deleteInput(file.serverId);
  295. return uploader.removeFile(file);
  296. }
  297. Dcat.confirm(__('confirm_delete_file'), file.serverId, function () {
  298. var post = opts.deleteData;
  299. post.key = file.serverId;
  300. if (!post.key) {
  301. deleteInput(file.serverId);
  302. return uploader.removeFile(file);
  303. }
  304. post._column = updateColumn;
  305. post._relation = relation;
  306. Dcat.loading();
  307. $.post(opts.deleteUrl, post, function (result) {
  308. Dcat.loading(false);
  309. if (result.status) {
  310. deleteInput(file.serverId);
  311. uploader.removeFile(file);
  312. return;
  313. }
  314. Dcat.error(result.message || 'Remove file failed.');
  315. });
  316. });
  317. break;
  318. case 'preview':
  319. Dcat.helpers.previewImage($wrap.find('img').attr('src'), null, file.name);
  320. break;
  321. case 'order':
  322. $(this).attr('data-id', file.serverId);
  323. orderFiles.apply(this);
  324. break;
  325. }
  326. });
  327. }
  328. // 图片宽高验证
  329. function validateDimensions(file) {
  330. // The image dimensions is invalid.
  331. if (!showImg || !isImage(file) || !Dcat.helpers.len(opts.dimensions)) return true;
  332. var dimensions = opts.dimensions,
  333. width = file._info.width,
  334. height = file._info.height,
  335. isset = Dcat.helpers.isset;
  336. if (
  337. (isset(dimensions, 'width') && dimensions['width'] != width) ||
  338. (isset(dimensions, 'min_width') && dimensions['min_width'] > width)||
  339. (isset(dimensions, 'max_width') && dimensions['max_width'] < width) ||
  340. (isset(dimensions, 'height') && dimensions['height'] != height) ||
  341. (isset(dimensions, 'min_height') && dimensions['min_height'] > height) ||
  342. (isset(dimensions, 'max_height') && dimensions['max_height'] < height) ||
  343. (isset(dimensions, 'ratio') && dimensions['ratio'] != (width / height))
  344. ) {
  345. return false;
  346. }
  347. return true;
  348. }
  349. // 负责view的销毁
  350. function removeUploadFile(file) {
  351. var $li = getFileView(file.id);
  352. delete percentages[file.id];
  353. updateTotalProgress();
  354. $li.off().find('.file-panel').off().end().remove();
  355. }
  356. function updateTotalProgress() {
  357. var loaded = 0,
  358. total = 0,
  359. $bar = $progress.find('.progress-bar'),
  360. percent;
  361. $.each(percentages, function (k, v) {
  362. total += v[0];
  363. loaded += v[0] * v[1];
  364. });
  365. percent = total ? loaded / total : 0;
  366. percent = Math.round(percent * 100) + '%';
  367. $bar.text(percent);
  368. $bar.css('width', percent);
  369. updateStatusText();
  370. }
  371. function updateStatusText() {
  372. var text = '', stats;
  373. if (!uploader) {
  374. return;
  375. }
  376. if (state === 'ready') {
  377. stats = uploader.getStats();
  378. if (fileCount) {
  379. text = __('selected_files', {num: fileCount, size: WebUploader.formatSize(fileSize)});
  380. } else {
  381. showSuccess();
  382. }
  383. } else if (state === 'confirm') {
  384. stats = uploader.getStats();
  385. if (stats.uploadFailNum) {
  386. text = __('selected_has_failed', {success: stats.successNum, fail: stats.uploadFailNum});
  387. }
  388. } else {
  389. showSuccess();
  390. }
  391. function showSuccess() {
  392. stats = uploader.getStats();
  393. if (stats.successNum) {
  394. text = __('selected_success', {num: fileCount, size: WebUploader.formatSize(fileSize), success: stats.successNum});
  395. }
  396. if (stats.uploadFailNum) {
  397. text += (text ? __('dot') : '') + __('failed_num', {fail: stats.uploadFailNum});
  398. }
  399. }
  400. $info.html(text);
  401. }
  402. // 上传文件后修改字段值
  403. function updateFileColumn() {
  404. var values = getInput(),
  405. num = uploader.getStats().successNum,
  406. form = $.extend({}, opts.formData);
  407. if (!num || !values || !opts.autoUpdateColumn) {
  408. return;
  409. }
  410. if (relation) {
  411. if (! relation[1]) {
  412. // 新增子表记录,则不调用update接口
  413. return;
  414. }
  415. form[relation[0]] = {};
  416. form[relation[0]][relation[1]] = {};
  417. form[relation[0]][relation[1]][updateColumn] = values.join(',');
  418. } else {
  419. form[updateColumn] = values.join(',');
  420. }
  421. delete form['_relation'];
  422. delete form['upload_column'];
  423. $.post(opts.updateServer, form);
  424. }
  425. function setState(val, args) {
  426. var stats;
  427. args = args || {};
  428. if (val === state) {
  429. return;
  430. }
  431. if ($upload) {
  432. $upload.removeClass('state-' + state);
  433. $upload.addClass('state-' + val);
  434. }
  435. state = val;
  436. switch (state) {
  437. case 'pedding':
  438. if (opts.disabled) return;
  439. $placeHolder.removeClass('element-invisible');
  440. $queue.hide();
  441. $statusBar.addClass('element-invisible');
  442. if (showImg) {
  443. $wrap.removeAttr('style');
  444. $wrap.find('.queueList').removeAttr('style');
  445. }
  446. refreshButton();
  447. break;
  448. case 'ready':
  449. $placeHolder.addClass('element-invisible');
  450. $selector.find(addFileButtonSelector).removeClass('element-invisible');
  451. $queue.show();
  452. if (!opts.disabled) {
  453. $statusBar.removeClass('element-invisible');
  454. }
  455. refreshButton();
  456. if (showImg) {
  457. $wrap.find('.queueList').css({'border': '1px solid #d3dde5', 'padding':'5px'});
  458. // $wrap.find('.queueList').removeAttr('style');
  459. }
  460. break;
  461. case 'uploading':
  462. $selector.find(addFileButtonSelector).addClass('element-invisible');
  463. $progress.show();
  464. $upload.text(__('pause_upload'));
  465. break;
  466. case 'paused':
  467. $progress.show();
  468. $upload.text(__('go_on_upload'));
  469. break;
  470. case 'confirm':
  471. if (uploader) {
  472. $progress.hide();
  473. $selector.find(addFileButtonSelector).removeClass('element-invisible');
  474. $upload.text(__('start_upload'));
  475. stats = uploader.getStats();
  476. if (stats.successNum && !stats.uploadFailNum) {
  477. setState('finish');
  478. return;
  479. }
  480. }
  481. break;
  482. case 'finish':
  483. if (uploader) {
  484. stats = uploader.getStats();
  485. if (stats.successNum) {
  486. Dcat.success(__('upload_success_message', {success: stats.successNum}));
  487. setTimeout(function () {
  488. if (opts.upload.fileNumLimit == 1) {
  489. // 单文件上传,需要重置文件上传个数
  490. uploader.request('get-stats').numOfSuccess = 0;
  491. }
  492. }, 10);
  493. } else {
  494. // 没有成功的图片,重设
  495. state = 'done';
  496. location.reload();
  497. }
  498. }
  499. break;
  500. case 'decrOriginalFileNum':
  501. if (originalFilesNum > 0) originalFilesNum --;
  502. break;
  503. case 'incrOriginalFileNum':
  504. originalFilesNum ++;
  505. break;
  506. case 'decrFileNumLimit': // 减少上传文件数量限制
  507. if (!uploader) {
  508. return;
  509. }
  510. var fileLimit = uploader.option('fileNumLimit'),
  511. num = args.num || 1;
  512. if (fileLimit == '-1') fileLimit = 0;
  513. num = fileLimit >= num ? fileLimit - num : 0;
  514. if (num == 0) num = '-1';
  515. uploader.option('fileNumLimit', num);
  516. break;
  517. case 'incrFileNumLimit': // 增加上传文件数量限制
  518. if (!uploader) {
  519. return;
  520. }
  521. var fileLimit = uploader.option('fileNumLimit'),
  522. num = args.num || 1;
  523. if (fileLimit == '-1') fileLimit = 0;
  524. num = fileLimit + num;
  525. uploader.option('fileNumLimit', num);
  526. break;
  527. case 'init': // 初始化
  528. $upload.addClass('state-' + state);
  529. updateTotalProgress();
  530. if (originalFilesNum || opts.disabled) {
  531. $placeHolder.addClass('element-invisible');
  532. if (!opts.disabled) {
  533. $statusBar.show();
  534. } else {
  535. $wrap.addClass('disabled');
  536. }
  537. setState('ready');
  538. } else if (showImg) {
  539. $wrap.removeAttr('style');
  540. $wrap.find('.queueList').css('margin', '0');
  541. }
  542. refreshButton();
  543. break;
  544. }
  545. updateStatusText();
  546. }
  547. // 移除form表单的文件
  548. function removeFormFile(fileId) {
  549. if (!fileId) return;
  550. var file = formFiles[fileId];
  551. deleteInput(fileId);
  552. delete formFiles[fileId];
  553. if (uploader && !file.fake) {
  554. uploader.removeFile(file);
  555. }
  556. setState('decrOriginalFileNum');
  557. setState('incrFileNumLimit');
  558. if (!Dcat.helpers.len(formFiles) && !Dcat.helpers.len(percentages)) {
  559. setState('pedding');
  560. }
  561. }
  562. // 获取表单值
  563. function getInput() {
  564. var val = $input.val();
  565. return val ? val.split(',') : [];
  566. }
  567. // 新增表单值
  568. function addInput(id) {
  569. var val = getInput();
  570. val.push(id);
  571. setInput(val);
  572. }
  573. // 设置表单值
  574. function setInput(arr) {
  575. arr = arr.filter(function(v, k, self) {
  576. return self.indexOf(v) === k;
  577. }).filter(function (v) {
  578. return v ? true : false;
  579. });
  580. $input.val(arr.join(','));
  581. }
  582. // 删除表单值
  583. function deleteInput(id) {
  584. deleteUploadedFile(id);
  585. if (!id) {
  586. return $input.val('');
  587. }
  588. setInput(getInput().filter(function (v) {
  589. return v != id;
  590. }));
  591. }
  592. // 添加已上传文件
  593. function appendUploadedFile(file) {
  594. if (! file.serverId || searchUploadedFile(file.serverId) !== -1) {
  595. return;
  596. }
  597. uploadedFiles.push(file)
  598. }
  599. function syncUploadedFiles() {
  600. var files = [];
  601. for (var i in uploadedFiles) {
  602. if (uploadedFiles[i]) {
  603. files.push(uploadedFiles[i].serverId);
  604. }
  605. }
  606. setInput(files);
  607. }
  608. function deleteUploadedFile(fileId) {
  609. uploadedFiles = uploadedFiles.filter(function (v) {
  610. return v.serverId != fileId;
  611. });
  612. }
  613. // 查找文件位置
  614. function searchUploadedFile(fileId) {
  615. for (var i in uploadedFiles) {
  616. if (uploadedFiles[i].serverId === fileId) {
  617. return i;
  618. }
  619. }
  620. return -1;
  621. }
  622. // 交换文件排序
  623. function swrapUploadedFile(fileId, order) {
  624. var index = parseInt(searchUploadedFile(fileId)),
  625. currentFile = uploadedFiles[index],
  626. prevFile = uploadedFiles[index - 1],
  627. nextFile = uploadedFiles[index + 1];
  628. if (order) {
  629. if (index === 0) {
  630. return;
  631. }
  632. uploadedFiles[index - 1] = currentFile;
  633. uploadedFiles[index] = prevFile;
  634. } else {
  635. if (! nextFile) {
  636. return;
  637. }
  638. uploadedFiles[index + 1] = currentFile;
  639. uploadedFiles[index] = nextFile;
  640. }
  641. syncUploadedFiles();
  642. }
  643. // 重新渲染已上传文件
  644. function rerenderUploadedFiles() {
  645. $queue.html('');
  646. for (var i in uploadedFiles) {
  647. if (uploadedFiles[i]) {
  648. appendUploadedFileForm(uploadedFiles[i])
  649. }
  650. }
  651. }
  652. // 重新计算按钮定位
  653. function refreshButton() {
  654. uploader.refresh();
  655. }
  656. // 文件排序
  657. function orderFiles() {
  658. var $this = $(this),
  659. $li = $this.parents('li').first(),
  660. fileId = $this.data('id'),
  661. order = $this.data('order'),
  662. $prev = $li.prev(),
  663. $next = $li.next();
  664. if (order) {
  665. // 升序
  666. if (! $prev.length) {
  667. return;
  668. }
  669. swrapUploadedFile(fileId, order);
  670. rerenderUploadedFiles();
  671. return;
  672. }
  673. if (! $next.length) {
  674. return;
  675. }
  676. swrapUploadedFile(fileId, order);
  677. rerenderUploadedFiles();
  678. }
  679. // 添加上传成功文件到表单区域
  680. function appendUploadedFileForm(file) {
  681. var html = "";
  682. html += "<li title='" + file.serverPath + "'>";
  683. if (! showImg && opts.sortable) {
  684. // 文件排序
  685. html += `
  686. <p style="right: 45px" class="file-action" data-file-act='order' data-order="1" data-id='${file.serverId}'><i class='feather icon-arrow-up'></i></p>
  687. <p style="right: 25px" class="file-action" data-file-act='order' data-order="0" data-id='${file.serverId}'><i class='feather icon-arrow-down'></i></p>
  688. `;
  689. }
  690. if (showImg) {
  691. html += `<p class='imgWrap'><img src='${file.serverUrl}'></p>`
  692. } else if (! opts.disabled) {
  693. html += `<p class="file-action" data-file-act="delete" data-id="${file.serverId}"><i class="feather icon-trash red-dark"></i></p>`;
  694. }
  695. html += "<p class='title' style=''><i class='feather icon-check text-white icon-success text-white'></i>";
  696. html += file.serverPath;
  697. html += "</p>";
  698. if (showImg) {
  699. html += "<p class='title' style='margin-bottom:20px;'>&nbsp;</p>";
  700. html += "<div class='file-panel' >";
  701. if (! opts.disabled) {
  702. html += `<a class='btn btn-sm btn-white' data-file-act='deleteurl' data-id='${file.serverId}'><i class='feather icon-trash red-dark' style='font-size:13px'></i></a>`;
  703. }
  704. html += `<a class='btn btn-sm btn-white' data-file-act='preview' data-url='${file.serverUrl}' ><i class='feather icon-zoom-in'></i></a>`;
  705. if (opts.sortable) {
  706. // 文件排序
  707. html += `
  708. <a class='btn btn-sm btn-white' data-file-act='order' data-order="1" data-id='${file.serverId}'><i class='feather icon-arrow-up'></i></a>
  709. <a class='btn btn-sm btn-white' data-file-act='order' data-order="0" data-id='${file.serverId}'><i class='feather icon-arrow-down'></i></a>
  710. `;
  711. }
  712. html += "</div>";
  713. } else {
  714. }
  715. html += "</li>";
  716. html = $(html);
  717. if (!showImg) {
  718. html.find('.file-type').show();
  719. html.find('.title').show();
  720. $wrap.css('background', 'transparent');
  721. }
  722. var deleteFile = function () {
  723. var fileId = $(this).data('id'), post = opts.deleteData;
  724. if (opts.disableRemove) {
  725. html.remove();
  726. return removeFormFile(fileId);
  727. }
  728. Dcat.confirm(__('confirm_delete_file'), file.serverId, function () {
  729. post.key = fileId;
  730. post._column = updateColumn;
  731. post._relation = relation;
  732. Dcat.loading();
  733. $.post(opts.deleteUrl, post, function (result) {
  734. Dcat.loading(false);
  735. if (result.status) {
  736. // 移除
  737. html.remove();
  738. removeFormFile(fileId);
  739. return;
  740. }
  741. Dcat.error(result.message || 'Remove file failed.')
  742. });
  743. });
  744. };
  745. // 删除按钮点击事件
  746. html.find('[data-file-act="deleteurl"]').click(deleteFile);
  747. html.find('[data-file-act="delete"]').click(deleteFile);
  748. // 文件排序
  749. if (opts.sortable) {
  750. html.find('[data-file-act="order"').click(orderFiles);
  751. }
  752. // 放大图片
  753. html.find('[data-file-act="preview"]').click(function () {
  754. var url = $(this).data('url');
  755. Dcat.helpers.previewImage(url);
  756. });
  757. formFiles[file.serverId] = file;
  758. addInput(file.serverId);
  759. $queue.append(html);
  760. if (showImg) {
  761. setTimeout(function () {
  762. html.css('margin', '5px');
  763. }, initImg ? 0 : 400);
  764. initImg = 1;
  765. }
  766. }
  767. // 初始化web-uploader
  768. function build() {
  769. $wrap = $selector.find(opts.wrapper);
  770. // 图片容器
  771. $queue = $('<ul class="filelist"></ul>').appendTo($wrap.find('.queueList'));
  772. // 状态栏,包括进度和控制按钮
  773. $statusBar = $wrap.find('.statusBar');
  774. // 文件总体选择信息。
  775. $info = $statusBar.find('.info');
  776. // 上传按钮
  777. $upload = $wrap.find('.upload-btn');
  778. // 没选择文件之前的内容。
  779. $placeHolder = $wrap.find('.placeholder');
  780. $progress = $statusBar.find('.upload-progress').hide();
  781. // IE;
  782. supportIe();
  783. // 实例化
  784. this.uploader = uploader = WebUploader.create(opts.upload);
  785. // 拖拽时不接受 js, txt 文件。
  786. uploader.on('dndAccept', function (items) {
  787. var denied = false,
  788. len = items.length,
  789. i = 0,
  790. // 修改js类型
  791. unAllowed = 'text/plain;application/javascript ';
  792. for (; i < len; i++) {
  793. // 如果在列表里面
  794. if (~unAllowed.indexOf(items[i].type)) {
  795. denied = true;
  796. break;
  797. }
  798. }
  799. return !denied;
  800. });
  801. if (opts.upload.fileNumLimit > 1 && !opts.disabled) {
  802. // 添加“添加文件”的按钮,
  803. uploader.addButton({
  804. id: addFileButtonSelector,
  805. label: '<i class="feather icon-folder"></i> &nbsp;' + __('go_on_add')
  806. });
  807. }
  808. uploader.onUploadProgress = function (file, percentage) {
  809. percentages[file.id][1] = percentage;
  810. updateTotalProgress();
  811. };
  812. uploader.onBeforeFileQueued = function (file) {
  813. };
  814. // 添加文件
  815. uploader.onFileQueued = function (file) {
  816. fileCount++;
  817. fileSize += file.size;
  818. if (fileCount === 1) {
  819. $placeHolder.addClass('element-invisible');
  820. $statusBar.show();
  821. }
  822. addFile(file);
  823. setState('ready');
  824. updateTotalProgress();
  825. };
  826. // 删除文件事件监听
  827. uploader.onFileDequeued = function (file) {
  828. fileCount--;
  829. fileSize -= file.size;
  830. if (!fileCount && !Dcat.helpers.len(formFiles)) {
  831. setState('pedding');
  832. }
  833. removeUploadFile(file);
  834. };
  835. uploader.on('all', function (type, obj, reason) {
  836. switch (type) {
  837. case 'uploadFinished':
  838. setState('confirm');
  839. updateFileColumn();
  840. break;
  841. case 'startUpload':
  842. setState('uploading');
  843. break;
  844. case 'stopUpload':
  845. setState('paused');
  846. break;
  847. case 'uploadAccept':
  848. // 上传失败,返回false
  849. if (reason && reason.error) {
  850. Dcat.error(reason.error.message);
  851. faildFiles[obj.file.id] = obj.file;
  852. return false;
  853. }
  854. if (reason.merge) {
  855. // 分片上传
  856. return;
  857. }
  858. // 上传成功,保存新文件名和路径到file对象
  859. obj.file.serverId = reason.id;
  860. obj.file.serverName = reason.name;
  861. obj.file.serverPath = reason.path;
  862. obj.file.serverUrl = reason.url || null;
  863. appendUploadedFile(obj.file);
  864. addInput(reason.id);
  865. var $li = getFileView(obj.file.id);
  866. if (!showImg) {
  867. $li.find('.file-action').hide();
  868. $li.find('[data-file-act="delete"]').show();
  869. }
  870. if (opts.sortable) {
  871. $li.find('[data-file-act="order"]').removeClass('d-none').show();
  872. }
  873. break;
  874. }
  875. });
  876. uploader.onError = function (code) {
  877. switch (code) {
  878. case 'Q_TYPE_DENIED':
  879. Dcat.error(__('Q_TYPE_DENIED'));
  880. break;
  881. case 'Q_EXCEED_NUM_LIMIT':
  882. Dcat.error(__('Q_EXCEED_NUM_LIMIT', {num: opts.upload.fileNumLimit}));
  883. break;
  884. case 'F_EXCEED_SIZE':
  885. Dcat.error(__('F_EXCEED_SIZE'));
  886. break;
  887. case 'Q_EXCEED_SIZE_LIMIT':
  888. Dcat.error(__('Q_EXCEED_SIZE_LIMIT'));
  889. break;
  890. case 'F_DUPLICATE':
  891. Dcat.warning(__('F_DUPLICATE'));
  892. break;
  893. default:
  894. Dcat.error('Error: ' + code);
  895. }
  896. };
  897. $upload.on('click', function () {
  898. if ($(this).hasClass('disabled')) {
  899. return false;
  900. }
  901. if (state === 'ready') {
  902. uploader.upload();
  903. } else if (state === 'paused') {
  904. uploader.upload();
  905. } else if (state === 'uploading') {
  906. uploader.stop();
  907. }
  908. });
  909. $info.on('click', '.retry', function () {
  910. uploader.retry();
  911. });
  912. $info.on('click', '.ignore', function () {
  913. for (var i in faildFiles) {
  914. uploader.removeFile(i, true);
  915. delete faildFiles[i];
  916. }
  917. });
  918. setState('init');
  919. }
  920. // 预览
  921. function preview() {
  922. for (var i in opts.preview) {
  923. var path = opts.preview[i].path, ext;
  924. if (path.indexOf('.')) {
  925. ext = path.split('.').pop();
  926. }
  927. var file = {
  928. serverId: opts.preview[i].id,
  929. serverUrl: opts.preview[i].url,
  930. serverPath: path,
  931. ext: ext,
  932. fake: 1,
  933. };
  934. setState('incrOriginalFileNum');
  935. setState('decrFileNumLimit');
  936. appendUploadedFileForm(file);
  937. appendUploadedFile(file);
  938. }
  939. }
  940. this.uploader = uploader;
  941. this.options = opts;
  942. this.build = build;
  943. this.preview = preview;
  944. this.setState = setState;
  945. this.refreshButton = refreshButton;
  946. this.getFileView = getFileView;
  947. this.getFileViewSelector = getFileViewSelector;
  948. this.addFileView = addFile;
  949. this.removeUploadFileView = removeUploadFile;
  950. this.isImage = isImage;
  951. this.getColumn = function () {
  952. return updateColumn;
  953. };
  954. function supportIe() {
  955. if (!WebUploader.Uploader.support('flash') && WebUploader.browser.ie) {
  956. // flash 安装了但是版本过低。
  957. if (flashVersion) {
  958. (function (container) {
  959. window['expressinstallcallback'] = function (state) {
  960. switch (state) {
  961. case 'Download.Cancelled':
  962. break;
  963. case 'Download.Failed':
  964. Dcat.error('Install failed!');
  965. break;
  966. default:
  967. Dcat.success('Install Success!');
  968. break;
  969. }
  970. delete window['expressinstallcallback'];
  971. };
  972. var swf = './expressInstall.swf';
  973. // insert flash object
  974. var html = `<object type="application/x-shockwave-flash" data="${swf}" `;
  975. if (WebUploader.browser.ie) {
  976. html += 'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ';
  977. }
  978. html += `width="100%" height="100%" style="outline:0">
  979. <param name="movie" value="${swf}" />
  980. <param name="wmode" value="transparent" />
  981. <param name="allowscriptaccess" value="always" />
  982. </object>`;
  983. container.html(html);
  984. })($wrap);
  985. } else {
  986. $wrap.html('<a href="http://www.adobe.com/go/getflashplayer" target="_blank" border="0"><img alt="get flash player" src="http://www.adobe.com/macromedia/style_guide/images/160x41_Get_Flash_Player.jpg" /></a>');
  987. }
  988. return;
  989. } else if (!WebUploader.Uploader.support()) {
  990. Dcat.error('您的浏览器不支持Web Uploader!');
  991. return;
  992. }
  993. }
  994. return this;
  995. }
  996. Dcat.Uploader = function (options) {
  997. return new Uploader(options)
  998. };
  999. })(window, jQuery);