plugs.upfile.html 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <title>{:sysconf('app_name')} {:sysconf('app_version')}</title>
  5. <meta charset="utf-8">
  6. <link rel="icon" href="../image/favicon.ico">
  7. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  8. <meta name="viewport" content="width=device-width, initial-scale=0">
  9. <link href="__STATIC__/plugs/uploader/webuploader.css" rel="stylesheet" type="text/css"/>
  10. <link href="__STATIC__/plugs/uploader/theme/uploader.css" rel="stylesheet" type="text/css"/>
  11. <script src="__STATIC__/plugs/jquery/jquery.min.js" type="text/javascript"></script>
  12. <script src="__STATIC__/plugs/uploader/webuploader.min.js" type="text/javascript"></script>
  13. <script src="__STATIC__/plugs/uploader/theme/upload.js" type="text/javascript"></script>
  14. </head>
  15. <body>
  16. <div id="uploader">
  17. <div class="queueList">
  18. <div id="dndArea" class="placeholder">
  19. <div id="filePicker"></div>
  20. </div>
  21. </div>
  22. <div class="statusBar" style="display:none;">
  23. <div class="progress">
  24. <span class="text">0%</span>
  25. <span class="percentage"></span>
  26. </div>
  27. <div class="info"></div>
  28. <div class="btns">
  29. {if $mode!=='one'}
  30. <div id="filePicker2"></div>
  31. {/if}
  32. <div class="uploadBtn">开始上传</div>
  33. </div>
  34. </div>
  35. </div>
  36. <script>
  37. /* global WebUploader */
  38. /**
  39. * 每个文件上传成功调用
  40. * @type Function
  41. */
  42. function uploaded(ret, file) {
  43. var url = ret.url || ret.site_url;
  44. $('#' + file.id).attr('data-md5', file.md5).attr('data-src', url);
  45. /*{if $mode === 'one'}*/
  46. top.$('[name="{$field}"]').map(function () {
  47. top.$(this).attr('data-srcs', ret.url).attr('data-md5', file.md5).val(url).trigger('change');
  48. });
  49. var index = top.layer.getFrameIndex(window.name);
  50. top.layer.close(index);
  51. /*{/if} {$mode}*/
  52. }
  53. function confirmSelected() {
  54. var srcs = new Array(), md5s = new Array();
  55. $('[data-md5] .success').map(function () {
  56. var $li = $(this).parents('[data-md5]');
  57. md5s.push($li.attr('data-md5'));
  58. srcs.push($li.attr('data-src'));
  59. });
  60. if (srcs.length < 1) {
  61. return top.$.msg.tips('还没有选择文件,请勾选需要使用的文件!');
  62. }
  63. top.$('[name="{$field}"]').map(function () {
  64. top.$(this).attr('data-srcs', srcs.join('|')).attr('data-md5', md5s.join('|')).val(srcs.join('|')).trigger('change');
  65. });
  66. var index = top.layer.getFrameIndex(window.name);
  67. top.layer.close(index);
  68. }
  69. function completed() {
  70. var btnHTML = '完成上传';
  71. $('.uploadBtn').on('click', function () {
  72. if (this.innerHTML === btnHTML) {
  73. confirmSelected.call(this);
  74. }
  75. }).html(btnHTML);
  76. }
  77. // 当domReady的时候开始初始化
  78. $(function () {
  79. var $wrap = $('#uploader'),
  80. /*文件容器*/
  81. $queue = $('<ul class="filelist"></ul>').appendTo($wrap.find('.queueList')),
  82. /*状态栏,包括进度和控制按钮*/
  83. $statusBar = $wrap.find('.statusBar'),
  84. /*文件总体选择信息。*/
  85. $info = $statusBar.find('.info'),
  86. /*上传按钮*/
  87. $upload = $wrap.find('.uploadBtn'),
  88. /*没选择文件之前的内容。*/
  89. $placeHolder = $wrap.find('.placeholder'),
  90. $progress = $statusBar.find('.progress').hide(),
  91. /* 添加的文件数量*/
  92. fileCount = 0,
  93. /*添加的文件总大小*/
  94. fileSize = 0,
  95. /*优化retina, 在retina下这个值是2*/
  96. ratio = window.devicePixelRatio || 1,
  97. /*缩略图大小*/
  98. thumbnailWidth = 110 * ratio, thumbnailHeight = 110 * ratio,
  99. /*可能有pedding, ready, uploading, confirm, done.*/
  100. state = 'pedding',
  101. /*所有文件的进度信息,key为file id*/
  102. percentages = {},
  103. /*判断浏览器是否支持文件的base64*/
  104. isSupportBase64 = (function () {
  105. var data = new Image();
  106. var support = true;
  107. data.onload = data.onerror = function () {
  108. if (this.width != 1 || this.height != 1) {
  109. support = false;
  110. }
  111. };
  112. data.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
  113. return support;
  114. }.call(this)),
  115. /*检测是否已经安装flash,检测flash的版本*/
  116. flashVersion = (function () {
  117. var version;
  118. try {
  119. version = navigator.plugins[ 'Shockwave Flash' ];
  120. version = version.description;
  121. } catch (ex) {
  122. try {
  123. version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
  124. } catch (ex2) {
  125. version = '0.0';
  126. }
  127. }
  128. version = version.match(/\d+/g);
  129. return parseFloat(version[ 0 ] + '.' + version[ 1 ], 10);
  130. })(),
  131. supportTransition = (function () {
  132. var s = document.createElement('p').style, r = 'transition' in s || 'WebkitTransition' in s || 'MozTransition' in s || 'msTransition' in s || 'OTransition' in s;
  133. return (s = null), r;
  134. })(),
  135. uploader;
  136. if (!WebUploader.Uploader.support('flash') && WebUploader.browser.ie) {
  137. /*flash 安装了但是版本过低*/
  138. if (flashVersion) {
  139. (function (container) {
  140. window['expressinstallcallback'] = function (state) {
  141. switch (state) {
  142. case 'Download.Cancelled':
  143. alert('您取消了更新!');
  144. break;
  145. case 'Download.Failed':
  146. alert('安装失败');
  147. break;
  148. default:
  149. alert('安装已成功,请刷新!');
  150. break;
  151. }
  152. delete window['expressinstallcallback'];
  153. };
  154. var swf = '__STATIC__/plugs/uploader/expressInstall.swf';
  155. var html = '<object type="application/' + 'x-shockwave-flash" data="' + swf + '" ';
  156. if (WebUploader.browser.ie) {
  157. html += 'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ';
  158. }
  159. html += 'width="100%" height="100%" style="outline:0">' +
  160. '<param name="movie" value="' + swf + '" />' +
  161. '<param name="wmode" value="transparent" />' +
  162. '<param name="allowscriptaccess" value="always" />' +
  163. '</object>';
  164. container.html(html);
  165. })($wrap);
  166. } else {
  167. $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>');
  168. }
  169. return;
  170. } else if (!WebUploader.Uploader.support()) {
  171. alert('Web Uploader 不支持您的浏览器!');
  172. return;
  173. }
  174. WebUploader.Uploader.register({'before-send-file': 'preupload'}, {preupload: function (file) {
  175. var me = this, owner = this.owner, deferred = WebUploader.Deferred();
  176. owner.md5File(file.source).fail(function () {
  177. deferred.reject();
  178. }).then(function (md5) {
  179. file.md5 = md5;
  180. var data = {id: file.id, md5: md5, uptype: '{$uptype}', filename: file.name};
  181. $.ajax("{:url('admin/plugs/upstate')}", {dataType: 'json', method: 'post', data: data, success: function (ret) {
  182. if (ret.code !== 'NOT_FOUND') {
  183. owner.skipFile(file);
  184. uploaded.call(uploader, ret.data, file);
  185. percentages[file.id] = [file.size, 1];
  186. updateTotalProgress();
  187. console.log('文件秒传成功 --> ' + file.name);
  188. } else {
  189. file.md5 = md5;
  190. file.token = ret.data.token || '';
  191. file.key = ret.data.file_url || '';
  192. file.site_url = ret.data.site_url || '';
  193. me.options.formData.OSSAccessKeyId = ret.data.OSSAccessKeyId || ''; // OSS
  194. me.options.formData.signature = ret.data.signature; // OSS
  195. me.options.formData.policy = ret.data.policy; // OSS
  196. me.options.formData.success_action_status = '200'; // OSS
  197. me.options.server = ret.data.server;
  198. }
  199. deferred.resolve();
  200. }
  201. });
  202. });
  203. return deferred.promise();
  204. }
  205. });
  206. // 实例化
  207. uploader = WebUploader.create({
  208. pick: {
  209. id: '#filePicker',
  210. label: '点击选择文件',
  211. /*{if $mode === 'one'}*/
  212. multiple: false,
  213. /*{else}*/
  214. multiple: true,
  215. /*{/if}*/
  216. },
  217. accept: {
  218. title: '选择文件',
  219. extensions: '{$types}',
  220. mimeTypes: '{$mimes}'
  221. },
  222. formData: {},
  223. /*{if $mode === 'one'}*/
  224. auto: true,
  225. fileNumLimit: 1,
  226. /*{else}*/
  227. auto: false,
  228. fileNumLimit: 300,
  229. /*{/if}*/
  230. server: '{:url("admin/plugs/upload")}',
  231. swf: '__STATIC__/plugs/uploader/Uploader.swf',
  232. chunked: false,
  233. dnd: '#dndArea',
  234. paste: document.body,
  235. chunkSize: 512 * 1024,
  236. disableGlobalDnd: true,
  237. fileSizeLimit: 200 * 1024 * 1024, // 200 M
  238. fileSingleSizeLimit: 200 * 1024 * 1024, // 200 M
  239. compress: {
  240. width: 1600,
  241. height: 16000,
  242. crop: false, // 是否允许裁剪
  243. quality: 90, // 图片质量(只有type为`image/jpeg`的时候才有效)
  244. allowMagnify: false, // 是否允许放大(如果想要生成小图的时候不失真,此选项应该设置为false).
  245. preserveHeaders: true, // 是否保留头部meta信息
  246. noCompressIfLarger: false, // 如果发现压缩后文件大小比原来还大,则使用原来图片
  247. compressSize: 1024 * 512, // 单位字节(如果图片大小小于此值,不会采用压缩)
  248. }
  249. });
  250. /* 上传开始前的处理 */
  251. uploader.on('uploadBeforeSend', function (file, data, header) {
  252. header['X_Requested_With'] = 'XMLHttpRequest';
  253. data['allowed_types'] = this.options.accept[0].extensions.split(',').join('|');
  254. data['token'] = file.file.token;
  255. data['md5'] = file.file.md5;
  256. data['key'] = file.file.key;
  257. });
  258. /* 处理上传后的结果 */
  259. uploader.on('uploadAccept', function (fieldata, ret) {
  260. // Qiniu or Local 上传
  261. if (ret.code === 'SUCCESS') {
  262. uploaded.call(uploader, ret.data, fieldata.file);
  263. return true;
  264. }
  265. // AliOSS 上传
  266. if (fieldata.file.site_url) {
  267. uploaded.call(uploader, {'site_url': fieldata.file.site_url}, fieldata.file);
  268. return true;
  269. }
  270. return false;
  271. });
  272. // 拖拽时不接受 js, txt 文件。
  273. uploader.on('dndAccept', function (items) {
  274. var denied = false, len = items.length, unAllowed = 'text/plain;application/javascript ';
  275. for (var i = 0; i < len; i++) {
  276. if (~unAllowed.indexOf(items[ i ].type)) {
  277. denied = true;
  278. break;
  279. }
  280. }
  281. return !denied;
  282. });
  283. // 添加“添加文件”的按钮,
  284. uploader.addButton({id: '#filePicker2', label: '继续添加'});
  285. uploader.on('ready', function () {
  286. window.uploader = uploader;
  287. });
  288. // 当有文件添加进来时执行,负责view的创建
  289. function addFile(file) {
  290. var $li = $('<li id="' + file.id + '">' + '<p class="title">' + file.name + '</p>' +
  291. '<p class="imgWrap"></p>' +
  292. '<p class="progress"><span></span></p>' +
  293. '</li>'),
  294. $btns = $('<div class="file-panel">' +
  295. '<span class="cancel">删除</span>' +
  296. '<span class="rotateRight">向右旋转</span>' +
  297. '<span class="rotateLeft">向左旋转</span></div>').appendTo($li),
  298. $prgress = $li.find('p.progress span'),
  299. $wrap = $li.find('p.imgWrap'),
  300. $info = $('<p class="error"></p>'),
  301. showError = function (code) {
  302. var text = '';
  303. switch (code) {
  304. case 'exceed_size':
  305. text = '文件大小超出';
  306. break;
  307. case 'interrupt':
  308. text = '上传暂停';
  309. break;
  310. default:
  311. text = '上传失败,请重试';
  312. break;
  313. }
  314. $info.text(text).appendTo($li);
  315. };
  316. if (file.getStatus() === 'invalid') {
  317. showError(file.statusText);
  318. } else {
  319. // @todo lazyload
  320. $wrap.text('预览中');
  321. uploader.makeThumb(file, function (error, src) {
  322. if (error) {
  323. $wrap.text('不能预览');
  324. return;
  325. }
  326. var img;
  327. if (isSupportBase64) {
  328. img = $('<img src="' + src + '">');
  329. $wrap.empty().append(img);
  330. } else {
  331. $.ajax('{"plugs/file/preview"|url}', {method: 'POST', data: src, dataType: 'json'}).done(function (response) {
  332. if (response.result) {
  333. img = $('<img src="' + response.result + '">');
  334. $wrap.empty().append(img);
  335. } else {
  336. $wrap.text("预览出错");
  337. }
  338. });
  339. }
  340. }, thumbnailWidth, thumbnailHeight);
  341. percentages[ file.id ] = [file.size, 0];
  342. file.rotation = 0;
  343. $upload.html('开始上传');
  344. }
  345. file.on('statuschange', function (cur, prev) {
  346. if (prev === 'progress') {
  347. $prgress.hide().width(0);
  348. } else if (prev === 'queued') {
  349. $li.off('mouseenter mouseleave');
  350. $btns.remove();
  351. }
  352. // 成功
  353. if (cur === 'error' || cur === 'invalid') {
  354. showError(file.statusText);
  355. percentages[ file.id ][ 1 ] = 1;
  356. } else if (cur === 'interrupt') {
  357. showError('interrupt');
  358. } else if (cur === 'queued') {
  359. percentages[ file.id ][ 1 ] = 0;
  360. } else if (cur === 'progress') {
  361. $info.remove();
  362. $prgress.css('display', 'block');
  363. } else if (cur === 'complete') {
  364. $li.append('<span class="success"></span>');
  365. }
  366. $li.removeClass('state-' + prev).addClass('state-' + cur);
  367. });
  368. $li.on('mouseenter', function () {
  369. $btns.stop().animate({height: 30});
  370. }).on('mouseleave', function () {
  371. $btns.stop().animate({height: 0});
  372. });
  373. $btns.on('click', 'span', function () {
  374. var index = $(this).index(), deg;
  375. switch (index) {
  376. case 0:
  377. return uploader.removeFile(file);
  378. case 1:
  379. file.rotation += 90;
  380. break;
  381. case 2:
  382. file.rotation -= 90;
  383. break;
  384. }
  385. if (supportTransition) {
  386. deg = 'rotate(' + file.rotation + 'deg)';
  387. $wrap.css({'-webkit-transform': deg, '-mos-transform': deg, '-o-transform': deg, 'transform': deg});
  388. } else {
  389. $wrap.css('filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation=' + (~~((file.rotation / 90) % 4 + 4) % 4) + ')');
  390. }
  391. });
  392. $li.appendTo($queue);
  393. }
  394. // 负责view的销毁
  395. function removeFile(file) {
  396. delete percentages[ file.id ];
  397. updateTotalProgress();
  398. $('#' + file.id).off().find('.file-panel').off().end().remove();
  399. }
  400. function updateTotalProgress() {
  401. var loaded = 0, total = 0, spans = $progress.children(), percent;
  402. $.each(percentages, function (k, v) {
  403. total += v[ 0 ];
  404. loaded += v[ 0 ] * v[ 1 ];
  405. });
  406. percent = total ? loaded / total : 0;
  407. spans.eq(0).text(Math.round(percent * 100) + '%');
  408. spans.eq(1).css('width', Math.round(percent * 100) + '%');
  409. updateStatus();
  410. }
  411. function updateStatus() {
  412. var text = '', stats;
  413. if (state === 'ready') {
  414. text = '选中' + fileCount + '个文件,共' + WebUploader.formatSize(fileSize) + '。';
  415. } else if (state === 'confirm') {
  416. stats = uploader.getStats();
  417. if (stats.uploadFailNum) {
  418. text = '已成功上传' + stats.successNum + '个文件,' + stats.uploadFailNum + '个文件上传失败,<a class="retry" href="#">重新上传</a>失败文件'
  419. }
  420. } else {
  421. stats = uploader.getStats();
  422. text = '共' + fileCount + '个(' + WebUploader.formatSize(fileSize) + '),已上传' + stats.successNum + '个';
  423. if (stats.uploadFailNum) {
  424. text += ',失败' + stats.uploadFailNum + '个';
  425. }
  426. }
  427. $info.html(text);
  428. }
  429. function setState(val) {
  430. var stats;
  431. if (val === state) {
  432. return;
  433. }
  434. $upload.removeClass('state-' + state);
  435. $upload.addClass('state-' + val);
  436. state = val;
  437. switch (state) {
  438. case 'pedding':
  439. $placeHolder.removeClass('element-invisible');
  440. $queue.hide();
  441. $statusBar.addClass('element-invisible');
  442. uploader.refresh();
  443. break;
  444. case 'ready':
  445. $placeHolder.addClass('element-invisible');
  446. $('#filePicker2').removeClass('element-invisible');
  447. $queue.show();
  448. $statusBar.removeClass('element-invisible');
  449. uploader.refresh();
  450. break;
  451. case 'uploading':
  452. $('#filePicker2').addClass('element-invisible');
  453. $progress.show();
  454. $upload.text('暂停上传');
  455. break;
  456. case 'paused':
  457. $progress.show();
  458. $upload.text('继续上传');
  459. break;
  460. case 'confirm':
  461. $progress.hide();
  462. $('#filePicker2').removeClass('element-invisible');
  463. $upload.text('开始上传');
  464. stats = uploader.getStats();
  465. if (stats.successNum && !stats.uploadFailNum) {
  466. setState('finish');
  467. return;
  468. }
  469. break;
  470. case 'finish':
  471. stats = uploader.getStats();
  472. if (stats.successNum) {
  473. completed.call(this);
  474. } else {
  475. state = 'done';
  476. location.reload();
  477. }
  478. break;
  479. }
  480. updateStatus();
  481. }
  482. uploader.onUploadProgress = function (file, percentage) {
  483. var $li = $('#' + file.id), $percent = $li.find('.progress span');
  484. $percent.css('width', percentage * 100 + '%');
  485. percentages[ file.id ][ 1 ] = percentage;
  486. updateTotalProgress();
  487. };
  488. uploader.onFileQueued = function (file) {
  489. fileCount++;
  490. fileSize += file.size;
  491. if (fileCount === 1) {
  492. $placeHolder.addClass('element-invisible');
  493. $statusBar.show();
  494. }
  495. addFile(file);
  496. setState('ready');
  497. updateTotalProgress();
  498. };
  499. uploader.onfieldequeued = function (file) {
  500. fileCount--;
  501. fileSize -= file.size;
  502. !fileCount && setState('pedding');
  503. removeFile(file);
  504. updateTotalProgress();
  505. };
  506. uploader.on('all', function (type) {
  507. switch (type) {
  508. case 'uploadFinished':
  509. setState('confirm');
  510. break;
  511. case 'startUpload':
  512. setState('uploading');
  513. break;
  514. case 'stopUpload':
  515. setState('paused');
  516. break;
  517. }
  518. });
  519. uploader.onError = function (code) {
  520. //alert('Eroor: ' + code);
  521. };
  522. $upload.on('click', function () {
  523. if ($(this).hasClass('disabled')) {
  524. return false;
  525. }
  526. if (state === 'ready') {
  527. uploader.upload();
  528. } else if (state === 'paused') {
  529. uploader.upload();
  530. } else if (state === 'uploading') {
  531. uploader.stop();
  532. }
  533. });
  534. $info.on('click', '.retry', function () {
  535. uploader.retry();
  536. });
  537. $upload.addClass('state-' + state);
  538. updateTotalProgress();
  539. });
  540. </script>
  541. </body>
  542. </html>