init.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. jQuery(function() {
  2. var $ = jQuery;
  3. $.fn.webupload_fileinput = function (config) {
  4. $('body').append(renderModal());
  5. var _modal = $('#' + config['modal_id']),
  6. chooseObject; // 点击选择图片的按钮
  7. _modal.on("shown.bs.modal", init);
  8. function init () {
  9. var $wrap = $('#uploader'),
  10. // 图片容器
  11. $queue = $('<ul class="filelist"></ul>').appendTo( $wrap.find('.queueList') ),
  12. // 状态栏,包括进度和控制按钮
  13. $statusBar = $wrap.find('.statusBar'),
  14. // 文件总体选择信息。
  15. $info = $statusBar.find('.info'),
  16. // 上传按钮
  17. $upload = $wrap.find('.uploadBtn'),
  18. // 没选择文件之前的内容。
  19. $placeHolder = $wrap.find('.placeholder'),
  20. // 总体进度条
  21. $progress = $statusBar.find('.progress').hide(),
  22. // 添加的文件数量
  23. fileCount = 0,
  24. // 添加的文件总大小
  25. fileSize = 0,
  26. // 优化retina, 在retina下这个值是2
  27. ratio = window.devicePixelRatio || 1,
  28. // 缩略图大小
  29. thumbnailWidth = 110 * ratio,
  30. thumbnailHeight = 110 * ratio,
  31. // 可能有pedding, ready, uploading, confirm, done.
  32. state = 'pedding',
  33. // 所有文件的进度信息,key为file id
  34. percentages = {},
  35. supportTransition = (function(){
  36. var s = document.createElement('p').style,
  37. r = 'transition' in s ||
  38. 'WebkitTransition' in s ||
  39. 'MozTransition' in s ||
  40. 'msTransition' in s ||
  41. 'OTransition' in s;
  42. s = null;
  43. return r;
  44. })(),
  45. uploadedFiles = [], // 成功上传的图片信息
  46. k = 0,
  47. $r = $('<li class="fileinput-button js-add-image" id="filePicker2" style="display:none;"> <a href="javascript:;" class="fileinput-button-icon">+</a></li>').appendTo($wrap.find('.filelist')),
  48. // WebUploader实例
  49. uploader;
  50. if ( !WebUploader.Uploader.support() ) {
  51. alert( 'Web Uploader 不支持您的浏览器!如果你使用的是IE浏览器,请尝试升级 flash 播放器');
  52. throw new Error( 'WebUploader does not support the browser you are using.' );
  53. }
  54. if (config.compress == undefined) {
  55. config.compress = {};
  56. }
  57. // 实例化
  58. uploader = WebUploader.create({
  59. pick: {
  60. id: '#filePicker',
  61. label: tips.webuploader.clickSelectImage,
  62. multiple: config.pick.multiple
  63. },
  64. dnd: '#uploader .queueList',
  65. paste: document.body,
  66. accept: config.accept,
  67. swf: './webuploader/Uploader.swf',
  68. server: config.server,
  69. formData: config.formData,
  70. disableGlobalDnd: config.disableGlobalDnd,
  71. chunked: config.chunked,
  72. fileNumLimit: config.pick.multiple ? config.fileNumLimit : 1,
  73. fileSizeLimit: config.fileSizeLimit,
  74. fileSingleSizeLimit: config.fileSingleSizeLimit,
  75. compress: {
  76. width: config.compress.width,
  77. height: config.compress.height,
  78. quality: config.compress.quality,
  79. allowMagnify: config.compress.allowMagnify,
  80. crop: config.compress.crop,
  81. preserveHeaders: config.compress.preserveHeaders,
  82. noCompressIfLarger: config.compress.noCompressIfLarger,
  83. compressSize: config.compress.compressSize
  84. }
  85. });
  86. // 添加“添加文件”的按钮,
  87. uploader.addButton({
  88. id: '#filePicker2',
  89. label: '+',
  90. multiple: config.pick.multiple
  91. });
  92. // 当有文件添加进来时执行,负责view的创建
  93. function addFile( file ) {
  94. var $li = $( '<li id="' + file.id + '">' +
  95. '<p class="title">' + file.name + '</p>' +
  96. '<p class="imgWrap"></p>'+
  97. '</li>' ),
  98. $btns = $('<div class="file-panel">' +
  99. '<span class="cancel">删除</span>' +
  100. '<span class="rotateRight">向右旋转</span>' +
  101. '<span class="rotateLeft">向左旋转</span></div>').appendTo( $li ),
  102. $prgress = $li.find('p.progress span'),
  103. $wrap = $li.find( 'p.imgWrap' ),
  104. $info = $('<p class="error"></p>'),
  105. showError = function( code ) {
  106. switch( code ) {
  107. case 'exceed_size':
  108. text = '文件大小超出';
  109. break;
  110. case 'interrupt':
  111. text = '上传暂停';
  112. break;
  113. default:
  114. text = '上传失败,请重试';
  115. break;
  116. }
  117. $info.text( text ).appendTo( $li );
  118. };
  119. if ( file.getStatus() === 'invalid' ) {
  120. showError( file.statusText );
  121. } else {
  122. // @todo lazyload
  123. $wrap.text( '预览中' );
  124. uploader.makeThumb( file, function( error, src ) {
  125. if ( error ) {
  126. $wrap.text( '不能预览' );
  127. return;
  128. }
  129. var img = $('<img src="'+src+'">');
  130. $wrap.empty().append( img );
  131. }, thumbnailWidth, thumbnailHeight );
  132. percentages[ file.id ] = [ file.size, 0 ];
  133. file.rotation = 0;
  134. }
  135. file.on('statuschange', function( cur, prev ) {
  136. if ( prev === 'progress' ) {
  137. $prgress.hide().width(0);
  138. } else if ( prev === 'queued' ) {
  139. $li.off( 'mouseenter mouseleave' );
  140. $btns.remove();
  141. }
  142. // 成功
  143. if ( cur === 'error' || cur === 'invalid' ) {
  144. showError( file.statusText );
  145. percentages[ file.id ][ 1 ] = 1;
  146. } else if ( cur === 'interrupt' ) {
  147. showError( 'interrupt' );
  148. } else if ( cur === 'queued' ) {
  149. percentages[ file.id ][ 1 ] = 0;
  150. } else if ( cur === 'progress' ) {
  151. $info.remove();
  152. $prgress.css('display', 'block');
  153. } else if ( cur === 'complete' ) {
  154. $li.append( '<span class="success"></span>' );
  155. }
  156. $li.removeClass( 'state-' + prev ).addClass( 'state-' + cur );
  157. });
  158. $li.on( 'mouseenter', function() {
  159. $btns.stop().animate({height: 30});
  160. });
  161. $li.on( 'mouseleave', function() {
  162. $btns.stop().animate({height: 0});
  163. });
  164. $btns.on( 'click', 'span', function() {
  165. var index = $(this).index(),
  166. deg;
  167. switch ( index ) {
  168. case 0:
  169. uploader.removeFile( file );
  170. return;
  171. case 1:
  172. file.rotation += 90;
  173. break;
  174. case 2:
  175. file.rotation -= 90;
  176. break;
  177. }
  178. if ( supportTransition ) {
  179. deg = 'rotate(' + file.rotation + 'deg)';
  180. $wrap.css({
  181. '-webkit-transform': deg,
  182. '-mos-transform': deg,
  183. '-o-transform': deg,
  184. 'transform': deg
  185. });
  186. } else {
  187. $wrap.css( 'filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation='+ (~~((file.rotation/90)%4 + 4)%4) +')');
  188. }
  189. });
  190. config.pick.multiple && $r.find(".fileinput-button").show(), $li.insertBefore($('#filePicker2'));
  191. // $li.appendTo( $queue );
  192. }
  193. // 负责view的销毁
  194. function removeFile( file ) {
  195. var $li = $('#'+file.id);
  196. delete percentages[ file.id ];
  197. updateTotalProgress();
  198. $li.off().find('.file-panel').off().end().remove();
  199. }
  200. function resetUploader() {
  201. uploadedFiles = [];
  202. k = 0;
  203. }
  204. function updateTotalProgress() {
  205. var loaded = 0,
  206. total = 0,
  207. spans = $progress.children(),
  208. percent;
  209. $.each( percentages, function( k, v ) {
  210. total += v[ 0 ];
  211. loaded += v[ 0 ] * v[ 1 ];
  212. } );
  213. percent = total ? loaded / total : 0;
  214. spans.eq( 0 ).text( Math.round( percent * 100 ) + '%' );
  215. spans.eq( 1 ).css( 'width', Math.round( percent * 100 ) + '%' );
  216. updateStatus();
  217. }
  218. function updateStatus() {
  219. var text = '', stats;
  220. if ( state === 'ready' ) {
  221. text = '选中' + fileCount + '张图片,共' +
  222. WebUploader.formatSize( fileSize ) + '。';
  223. } else if ( state === 'confirm' ) {
  224. stats = uploader.getStats();
  225. if ( stats.uploadFailNum ) {
  226. text = '已成功上传' + stats.successNum+ '张图片,'+
  227. stats.uploadFailNum + '张图片上传失败,<a class="retry" href="#">重新上传</a>失败图片或<a class="ignore" href="#">忽略</a>'
  228. }
  229. } else {
  230. stats = uploader.getStats();
  231. text = '共' + fileCount + '张(' +
  232. WebUploader.formatSize( fileSize ) +
  233. '),已上传' + stats.successNum + '张';
  234. if ( stats.uploadFailNum ) {
  235. text += ',失败' + stats.uploadFailNum + '张';
  236. }
  237. }
  238. $info.html( text );
  239. }
  240. function setState( val ) {
  241. var file, stats;
  242. if ( val === state ) {
  243. return;
  244. }
  245. $upload.removeClass( 'state-' + state );
  246. $upload.addClass( 'state-' + val );
  247. state = val;
  248. switch ( state ) {
  249. case 'pedding':
  250. $placeHolder.removeClass( 'element-invisible' );
  251. $queue.parent().removeClass('filled');
  252. $queue.hide();
  253. $statusBar.addClass( 'element-invisible' );
  254. uploader.refresh();
  255. $r.hide();
  256. break;
  257. case 'ready':
  258. $placeHolder.addClass( 'element-invisible' );
  259. $( '#filePicker2' ).removeClass( 'element-invisible');
  260. $queue.parent().addClass('filled');
  261. $queue.show();
  262. $statusBar.removeClass('element-invisible');
  263. uploader.refresh();
  264. config.pick.multiple && $r.show();
  265. break;
  266. case 'uploading':
  267. $( '#filePicker2' ).addClass( 'element-invisible' );
  268. $progress.show();
  269. $upload.text( tips.webuploader.pauseUploading );
  270. break;
  271. case 'paused':
  272. $progress.show();
  273. $upload.text( tips.webuploader.continueUploading );
  274. break;
  275. case 'confirm':
  276. $progress.hide();
  277. $upload.text( tips.webuploader.startUploading ).addClass( 'disabled' );
  278. stats = uploader.getStats();
  279. if ( stats.successNum && !stats.uploadFailNum ) {
  280. setState( 'finish' );
  281. return;
  282. }
  283. break;
  284. case 'finish':
  285. stats = uploader.getStats();
  286. if ( stats.successNum ) {
  287. if (uploadedFiles.length > 0) {
  288. if (!config.pick.multiple) {
  289. uploadedFiles = uploadedFiles[0];
  290. if (uploadedFiles.code == 0) {
  291. var btn = chooseObject,
  292. ipt = btn.parent().prev(),
  293. val = ipt.val(),
  294. img = ipt.parent().next().children();
  295. if(img.length > 0){
  296. img.get(0).src = uploadedFiles.url;
  297. }
  298. ipt.val(uploadedFiles.attachment);
  299. } else {
  300. alert(uploadedFiles.msg);
  301. }
  302. } else {
  303. var name = chooseObject.parent().children().eq(0).children("input[type=hidden]").attr('name');
  304. var multierr = false;
  305. $.each(uploadedFiles, function(idx, url) {
  306. if (url.code == 0) {
  307. chooseObject.parent().children(".multi-img-details").append('<div class="multi-item"><img src="'+url.url+'" class="img-responsive img-thumbnail cus-img"><input type="hidden" name="'+name+'[]" value="'+url.attachment+'"><em class="close delMultiImage" title="删除这张图片">×</em></div>');
  308. } else {
  309. if (!multierr) {
  310. multierr = true;
  311. alert(url.msg);
  312. } else {
  313. console.log(url.msg);
  314. }
  315. }
  316. });
  317. }
  318. _modal.modal('hide');
  319. resetUploader();
  320. }
  321. } else {
  322. // 没有成功的图片,重设
  323. state = 'done';
  324. location.reload();
  325. }
  326. break;
  327. }
  328. updateStatus();
  329. }
  330. uploader.onUploadProgress = function( file, percentage ) {
  331. var $li = $('#'+file.id),
  332. $percent = $li.find('.progress span');
  333. $percent.css( 'width', percentage * 100 + '%' );
  334. percentages[ file.id ][ 1 ] = percentage;
  335. updateTotalProgress();
  336. };
  337. uploader.onFileQueued = function( file ) {
  338. fileCount++;
  339. fileSize += file.size;
  340. if ( fileCount === 1 ) {
  341. $placeHolder.addClass( 'element-invisible' );
  342. $statusBar.show();
  343. }
  344. addFile( file );
  345. setState( 'ready' );
  346. updateTotalProgress();
  347. };
  348. uploader.onFileDequeued = function( file ) {
  349. fileCount--;
  350. fileSize -= file.size;
  351. if ( !fileCount ) {
  352. setState( 'pedding' );
  353. }
  354. removeFile( file );
  355. updateTotalProgress();
  356. };
  357. uploader.on( 'all', function( type ) {
  358. var stats;
  359. switch( type ) {
  360. case 'uploadFinished':
  361. setState( 'confirm' );
  362. break;
  363. case 'startUpload':
  364. setState( 'uploading' );
  365. break;
  366. case 'stopUpload':
  367. setState( 'paused' );
  368. break;
  369. }
  370. });
  371. uploader.onError = function( code ) {
  372. var msg = code;
  373. switch (code) {
  374. case 'Q_EXCEED_NUM_LIMIT':
  375. msg = '添加的文件数量超出 fileNumLimit 的设置';
  376. break;
  377. case 'Q_EXCEED_SIZE_LIMIT':
  378. msg = '添加的文件总大小超出了 fileSizeLimit 的设置';
  379. break;
  380. case 'Q_TYPE_DENIED':
  381. msg = '添加的文件类型错误';
  382. break;
  383. case 'P_DUPLICATE':
  384. msg = '添加的文件重复了';
  385. break;
  386. }
  387. alert( 'Error: ' + msg );
  388. };
  389. uploader.onUploadSuccess = function (b, c) {
  390. return (k++, uploadedFiles.push(c))
  391. }
  392. $upload.on('click', function() {
  393. if ( $(this).hasClass( 'disabled' ) ) {
  394. return false;
  395. }
  396. if ( state === 'ready' ) {
  397. uploader.upload();
  398. } else if ( state === 'paused' ) {
  399. uploader.upload();
  400. } else if ( state === 'uploading' ) {
  401. uploader.stop();
  402. }
  403. });
  404. $info.on( 'click', '.retry', function() {
  405. uploader.retry();
  406. } );
  407. $info.on( 'click', '.ignore', function() {
  408. alert( 'todo' );
  409. } );
  410. $upload.addClass( 'state-' + state );
  411. updateTotalProgress();
  412. $('#filePicker2').mouseenter(function(){
  413. uploader.refresh();
  414. });;
  415. }
  416. function buildModalBody () {
  417. return '<div role="tabpanel" class="tab-pane upload active" id="upload">' +
  418. '<div id="uploader" class="uploader">' +
  419. '<div class="queueList">' +
  420. '<div id="dndArea" class="placeholder">' +
  421. '<div id="filePicker"></div>' +
  422. '<p id="">' + tips.webuploader.dragHere + '</p>' +
  423. ' </div>' +
  424. '</div>' +
  425. '<div class="statusBar">' +
  426. '<div class="infowrap">' +
  427. '<div class="progress" style="display: none;">' +
  428. '<span class="text">0%</span>' +
  429. '<span class="percentage" style="width: 0%;"></span>' +
  430. '</div>' +
  431. '<div class="info">共0张(0B),已上传0张</div>' +
  432. '<div class="accept"></div>' +
  433. '</div>' +
  434. '<div class="btns">' +
  435. '<div class="uploadBtn btn btn-primary state-pedding" style="margin-top: 4px;">' + tips.webuploader.confirmUse + '</div>' +
  436. '<div class="modal-button-upload" style="float: right; margin-left: 5px;">' +
  437. '<button type="button" class="btn btn-default" data-dismiss="modal">' + tips.cancel + '</button>' +
  438. '</div>' +
  439. '</div>' +
  440. '</div>' +
  441. '</div>' +
  442. '</div>';
  443. }
  444. function renderModal () {
  445. var modal_id = config['modal_id'];
  446. if ($('#' + modal_id).length == 0) {
  447. return '<div id="' + config['modal_id'] + '" class="fade modal modal-c" role="dialog" tabindex="-1">' +
  448. '<div class="modal-dialog cus-size">' +
  449. '<div class="modal-content">' +
  450. '<div class="modal-header">' +
  451. '<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>' +
  452. '<h4 class="modal-title">' + tips.webuploader.uploadImage + '</h4>' +
  453. '</div>' +
  454. '<div class="modal-body">' +
  455. '</div>' +
  456. '</div>' +
  457. '</div>' +
  458. '</div>';
  459. } else {
  460. return false;
  461. }
  462. }
  463. // =====================================================================================
  464. $('.' + config['modal_id']).on('click', function () {
  465. chooseObject = $(this);
  466. _modal.modal('show');
  467. _modal.find('.modal-body').html('');
  468. _modal.find('.modal-body').html(buildModalBody());
  469. });
  470. $(document).on('click', '.delImage', function () {
  471. var _this = $(this);
  472. _this.prev().attr("src", config.defaultImage);
  473. _this.parent().prev().find("input").val("");
  474. });
  475. $(document).on('click', '.delMultiImage', function () {
  476. $(this).parent().remove();
  477. });
  478. // 解决多modal下滚动以及filePicker失效问题
  479. $(document).on('hidden.bs.modal', '.modal', function () {
  480. if($('.modal:visible').length) {
  481. $(document.body).addClass('modal-open');
  482. }
  483. $('.modal-c').find('.modal-body').html('');
  484. });
  485. };
  486. });