jPreview.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  1. let jPreview={
  2. config:{
  3. container:"", // 容器id
  4. staticPath:"./static", // 静态资源路径
  5. url:"", // 预览资源路径
  6. ext:"", // 资源后缀
  7. name:"", // 资源名称
  8. watermarkTxt:"", // 水印文字
  9. watermarkSize:"16px", // 水印文字大小
  10. priority:1, // 优先级 1:使用插件预览 2:使用office在线预览
  11. oburl:"https://view.officeapps.live.com/op/embed.aspx?src=", // office在线预览地址
  12. },
  13. preview(opts){
  14. this.config = $.extend({}, this.config, opts);
  15. if(this.config.url == ''){
  16. var url=this.parseUrl('src');
  17. this.config.url=url;
  18. }else{
  19. url=this.config.url;
  20. }
  21. // 检测url中是否有优先级参数
  22. let pri=this.parseUrl('pri');
  23. if(pri){
  24. this.config.priority=pri;
  25. }
  26. var spl = url.split(".");
  27. var exts=spl[spl.length-1];
  28. var ext=exts.toLowerCase();
  29. this.config.ext=ext;
  30. let name=this.parseUrl('name');
  31. if(!name){
  32. var decodedUrl = decodeURIComponent(url);
  33. var paths = decodedUrl.split("/");
  34. var fileName=paths[paths.length-1];
  35. name=fileName.replace('.'+ext, "");
  36. }
  37. this.config.name=name;
  38. // 如果url为空,或者后缀为空,则提示资源不存在
  39. if(!url || !ext){
  40. this.error('资源不存在!');
  41. return;
  42. }
  43. let self=this;
  44. // 增加文字水印
  45. if(self.config.watermarkTxt != ''){
  46. dynamicLoadJs(this.config.staticPath+"/common/js/watermark.js",function(){
  47. watermark.init({
  48. watermark_txt: self.config.watermarkTxt,
  49. watermark_x: 0,
  50. watermark_y: 0,
  51. watermark_rows: 0,
  52. watermark_cols: 0,
  53. watermark_x_space: 30,
  54. watermark_y_space: 30,
  55. watermark_font: '微软雅黑',
  56. watermark_fontsize: self.config.watermarkSize,
  57. watermark_color:'black',
  58. watermark_alpha: 0.2,
  59. watermark_width: 180,
  60. watermark_height: 80,
  61. watermark_angle: 10,
  62. });
  63. })
  64. }
  65. this.startPreviw();
  66. },
  67. startPreviw(){
  68. let self=this;
  69. let url=this.config.url;
  70. let ext=this.config.ext;
  71. let static=this.config.staticPath;
  72. let videoExt=['mp4','avi','3gp','rmvb','rm','flv','wmv','mkv','mov','mpeg','mpg','m4v','f4v','m4v'];
  73. let imgExt=['jpeg','jpg','gif','png','bmp','ico','webp'];
  74. let pdfExt=['pdf'];
  75. let txtExt=['txt'];
  76. let audioExt=['mp3','wav','ogg','aac','flac','ape','m4a','mid','ram','amr','ac3','aiff','au','m4p','mmf','mpc','tta','vqf','wv','wma'];
  77. let docExt=['docx'];
  78. let pptExt=['pptx'];
  79. let xlsExt=['xls','xlsx','csv'];
  80. let olExt=["doc","docx","docm","dot","dotx","dotm","rtf","xls","xlsx","xlt","xlsb","xlsm","csv","ppt","pptx","pps","ppsx","pptm","potm","ppam","potx","ppsm","odt","ods","odp","ott","ots","otp","wps","wpt"];
  81. if($.inArray(ext,imgExt)>=0){
  82. dynamicLoadJs(static+"/viewer/viewer.js",function(){
  83. self.imgView(url);
  84. })
  85. }else if($.inArray(ext,pdfExt)>=0){
  86. self.pdfView(url);
  87. }else if($.inArray(ext,audioExt)>=0){
  88. dynamicLoadJs(static+"/common/js/audio.js",function(){
  89. self.audioView(url);
  90. })
  91. }else if($.inArray(ext,videoExt)>=0){
  92. dynamicLoadJs(static+"/common/js/superVideo.js",function(){
  93. self.videoView(url);
  94. })
  95. }else if($.inArray(ext,docExt)>=0 && this.config.priority == 1){
  96. dynamicLoadJs(static+"/docxjs/js/jszip.min.js",function(){
  97. dynamicLoadJs(static+"/docxjs/js/docx-preview.js",function(){
  98. self.docView(url,ext);
  99. })
  100. })
  101. }else if($.inArray(ext,pptExt)>=0 && this.config.priority == 1){
  102. dynamicLoadJs(static+"/pptxjs/js/jszip.min.js",function(){
  103. dynamicLoadJs(static+"/pptxjs/js/filereader.js",function(){
  104. dynamicLoadJs(static+"/pptxjs/js/d3.min.js",function(){
  105. dynamicLoadJs(static+"/pptxjs/js/nv.d3.min.js",function(){
  106. dynamicLoadJs(static+"/pptxjs/js/divs2slides.min.js",function(){
  107. dynamicLoadJs(static+"/pptxjs/js/pptxjs.min.js",function(){
  108. self.pptView(url);
  109. })
  110. })
  111. })
  112. })
  113. })
  114. })
  115. }else if($.inArray(ext,xlsExt)>=0 && this.config.priority == 1){
  116. dynamicLoadJs(static+"/luckysheet/js/plugin.js",function(){
  117. dynamicLoadJs(static+"/luckysheet/js/luckysheet.umd.js",function(){
  118. dynamicLoadJs(static+"/luckysheet/js/luckyexcel.umd.js",function(){
  119. dynamicLoadJs(static+"/luckysheet/js/xlsx.core.min.js",function(){
  120. self.xlsView(url,ext);
  121. })
  122. })
  123. })
  124. })
  125. }else if($.inArray(ext,olExt)>=0){
  126. self.olView(url);
  127. }else if($.inArray(ext,txtExt)>=0){
  128. self.txtView(url);
  129. }else{
  130. self.error('不支持的文件类型!');
  131. }
  132. },
  133. txtView(url){
  134. $("body").html("<div class='text-preview'><pre id='file-content'></pre><div>");
  135. // 使用fetch API获取文件内容
  136. fetch(url)
  137. .then(response => {
  138. if (!response.ok) {
  139. throw new Error('网络响应错误');
  140. }
  141. return response.text();
  142. })
  143. .then(text => {
  144. // 将获取到的文本内容放入<pre>元素中
  145. document.getElementById('file-content').textContent = text;
  146. })
  147. .catch(error => {
  148. console.error('获取文件内容时出错:', error);
  149. document.getElementById('file-content').textContent = '无法加载文件内容,请检查URL或网络连接。';
  150. });
  151. },
  152. olView(url){
  153. $("body").css({overflow:'hidden'});
  154. // 如果是ppt,doc文档,直接使用office在线预览
  155. $("body").html("<iframe src='"+this.config.oburl+url+"' style='width:100%;height:100%;position:absolute;left:0;top:0'></iframe>");return;
  156. },
  157. getFileInfo(url,callback){
  158. let self=this;
  159. var xhr = new XMLHttpRequest();
  160. xhr.open('GET', url);
  161. xhr.responseType = "arraybuffer";
  162. xhr.onload = function (e) {
  163. var data = xhr.response;
  164. if(!data){loading.close();loading = false;return;};
  165. var file = {name: self.config.name, ext: self.config.ext, content: data};
  166. callback(file);
  167. };
  168. xhr.send();
  169. xhr.onreadystatechange = function(){ // 请求状态
  170. if(xhr.readyState==4){
  171. if (xhr.status < 200 || (xhr.status > 300 && xhr.status != 304)) {
  172. console.log('error');
  173. }
  174. }
  175. };
  176. },
  177. parseUrl:(field,urlstr)=>{
  178. if(typeof (urlstr)==='undefined'){
  179. urlstr=window.location.href;
  180. }
  181. var param=urlstr.substring(urlstr.indexOf("?")+1);
  182. var paramArr=param.split("&");
  183. var urlArr={};
  184. for(var i=0;i<paramArr.length;i++){
  185. var str=paramArr[i];
  186. var itemArr=str.split("=");
  187. urlArr[itemArr[0]]=itemArr[1];
  188. }
  189. if(typeof (urlArr[field])==='undefined'){
  190. return '';
  191. }
  192. return urlArr[field];
  193. },
  194. loadLuckySheet(exportJson){
  195. // 获得转化后的表格数据后,使用luckysheet初始化,或者更新已有的luckysheet工作簿
  196. // 注:luckysheet需要引入依赖包和初始化表格容器才可以使用
  197. luckysheet.create({
  198. container: this.config.container, // 容器id
  199. data:exportJson.sheets,
  200. // plugins: ['chart'], // luckyexcel暂不支持导入图表——解析的数据没有chart相关内容
  201. lang: 'zh',
  202. // title:exportJson.info.name,
  203. // userInfo:exportJson.info.name.creator,
  204. // showinfobar: false,
  205. allowCopy: true, // 是否允许拷贝
  206. showtoolbar: false, // 是否显示工具栏——edit
  207. showinfobar: false, // 是否显示顶部信息栏
  208. // showsheetbar: false, // 是否显示底部sheet页按钮
  209. // showstatisticBar: false, // 是否显示底部计数栏
  210. sheetBottomConfig: false, // sheet页下方的添加行按钮和回到顶部按钮配置
  211. allowEdit: false, // 是否允许前台编辑——edit
  212. enableAddRow: false, // 允许增加行
  213. enableAddCol: false, // 允许增加列
  214. // userInfo: false, // 右上角的用户信息展示样式
  215. // showRowBar: false, // 是否显示行号区域
  216. // showColumnBar: false, // 是否显示列号区域
  217. // sheetFormulaBar: false, // 是否显示公式栏
  218. enableAddBackTop: false,//返回头部按钮
  219. // functionButton: '<button id="" class="btn btn-primary" style="padding:3px 6px;font-size: 12px;margin-right: 10px;">下载</button>', // 需要显示信息栏
  220. });
  221. },
  222. setLuckySheet : (data, callback)=>{
  223. try{
  224. callback(data);
  225. }catch(err){
  226. console.error(err);
  227. }
  228. },
  229. docView(url,ext){
  230. let container = this.config.container;
  231. try{
  232. this.getFileInfo(url,function(file){
  233. docx.renderAsync(file.content, document.getElementById(container)).then(x => console.log("docx: finished"));
  234. });
  235. // 如果预览失败,则转为线上预览
  236. window.onerror = function (message, urls, line, column, error) {
  237. self.olView(url);
  238. }
  239. }catch(err){
  240. this.error('文件已经损坏!');
  241. }
  242. },
  243. pptView(url){
  244. let container = this.config.container;
  245. let self = this;
  246. try{
  247. $('#'+container).addClass(this.isWap() ? 'is-in-wap' : 'not-in-wap');
  248. $('#'+container).addClass("pptview");
  249. $("#"+container).pptxToHtml({
  250. pptxFileUrl: url,
  251. fileInputId: "",
  252. slideMode: false,
  253. keyBoardShortCut: false,
  254. mediaProcess: false,
  255. slideModeConfig: { //on slide mode (slideMode: true)
  256. first: 1,
  257. nav: true, /** true,false : show or not nav buttons*/
  258. // nav: true, /** true,false : show or not nav buttons*/
  259. navTxtColor: "white", /** color */
  260. navNextTxt:"&#8250;", //">"
  261. navPrevTxt: "&#8249;", //"<"
  262. showPlayPauseBtn: true,/** true,false */
  263. keyBoardShortCut: false, /** true,false */
  264. showSlideNum: true, /** true,false */
  265. showTotalSlideNum: true, /** true,false */
  266. autoSlide: 2, /** false or seconds (the pause time between slides) , F8 to active(keyBoardShortCut: true) */
  267. // randomAutoSlide: false, /** true,false ,autoSlide:true */
  268. // loop: false, /** true,false */
  269. background: false, /** false or color*/
  270. transition: "fade", /** transition type: "slid","fade","default","random" , to show transition efects :transitionTime > 0.5 */
  271. transitionTime: 0 /** transition time in seconds */
  272. }
  273. });
  274. // 如果预览失败,则转为线上预览
  275. window.onerror = function (message, urls, line, column, error) {
  276. self.olView(url);
  277. }
  278. }catch(err){
  279. this.error('文件已经损坏!');
  280. }
  281. },
  282. xlsView(url,ext){
  283. let self=this;
  284. try{
  285. $('#'+this.config.container).css({height:'100vh'});
  286. this.getFileInfo(url,function(file){
  287. // 1.xlsx,直接luckyexcel读取
  288. if(ext == 'xlsx') {
  289. self.setLuckySheet(file.content, function(content){
  290. LuckyExcel.transformExcelToLucky(content, function(exportJson, luckysheetfile){
  291. self.setLuckySheet(exportJson, function(exportJson){
  292. self.loadLuckySheet(exportJson);
  293. });
  294. });
  295. });
  296. return;
  297. }
  298. var sheet = utils.getLuckySheet();
  299. // 2.csv以字符串方式读取,区分编码
  300. if(file.ext == 'csv'){
  301. var data = new Uint8Array(file.content);
  302. var code = utils.isUTF8(data) ? 'utf-8' : 'gbk';
  303. var str = new TextDecoder(code).decode(data);
  304. var wb = XLSX.read(str, { type: "string" });
  305. }
  306. // 3.xls通过SheetJs获取数据
  307. if(_.isUndefined(wb)) {
  308. var wb = XLSX.read(file.content, {type: 'buffer', cellStyles: true}); // XLSX/XLS
  309. }
  310. var sheets = [];
  311. for(var i in wb.SheetNames) {
  312. var name = wb.SheetNames[i];
  313. var _sheet = JSON.parse(JSON.stringify(sheet));
  314. _sheet.name = name;
  315. _sheet.index = _sheet.order = parseInt(i);
  316. _sheet.data = utils.xlsToLuckySheet(wb.Sheets[name], _sheet);
  317. sheets.push(_sheet);
  318. }
  319. self.setLuckySheet({sheets: sheets}, function(exportJson){
  320. self.loadLuckySheet(exportJson);
  321. if(loading){loading.close();loading = false;}
  322. });
  323. })
  324. // 如果预览失败,则转为线上预览
  325. window.onerror = function (message, urls, line, column, error) {
  326. self.olView(url);
  327. }
  328. }catch(err){
  329. this.error('文件已经损坏!');
  330. }
  331. },
  332. pdfView(url){
  333. $("body").html("<iframe src='./static/pdfjs/web/viewer.html?file="+url+"' style='width:100%;height:100vh;border:none;display:block'></iframe>");
  334. },
  335. imgView(url){
  336. $('#'+this.config.container).html('<div style="height:100vh"><img id="image" style="display:none" src="'+url+'" alt="Picture"></div>');
  337. var image = $('#image');
  338. image.viewer({
  339. inline: true,
  340. button: false,
  341. viewed: function() {
  342. viewer.zoomTo(1);
  343. }
  344. });
  345. },
  346. videoView(url){
  347. let container = this.config.container;
  348. var nextControl = new Super.NextControl() // 实例化“下一个”按钮控件
  349. var Dbspeen = new Super.DbspeenControl() // 倍速控件
  350. // var BarrageControl = new Super.BarrageControl() // 弹幕控件
  351. var fullScreenControl = new Super.FullScreenControl() // 实例化“全屏”控件
  352. var video = new Super.Svideo(container, {
  353. source: new Super.VideoSource({ // 引入视频资源
  354. src: url
  355. }),
  356. leftControls: [nextControl], // 控件栏左槽插入控件
  357. rightControls: [Dbspeen, fullScreenControl], // 控件栏右槽插入控件
  358. // centerControls: [BarrageControl] // 弹幕控件插入中间插槽
  359. })
  360. $('#'+container).addClass("videoContainer");
  361. video.addEventListener('change', function(event) { // 监听video各属性变化
  362. // console.log(event)
  363. })
  364. nextControl.addEventListener('click', function(event) { // 监听“下一个”按钮控件点击事件
  365. alert('click next menu !!!')
  366. })
  367. fullScreenControl.addEventListener('fullscreen', function(event) { // 监听进入全屏
  368. console.log('is fullscreen !!!')
  369. })
  370. fullScreenControl.addEventListener('cancelfullscreen', function(event) { // 监听退出全屏
  371. console.log('cancel fullscreen !!!')
  372. })
  373. video.addEventListener('fullscreen', function(event) {
  374. console.log('is fullscreen !!!')
  375. })
  376. },
  377. audioView(url){
  378. $('#'+this.config.container).html('<div class="yAudio" id="yAudio"></div>');
  379. new YAudio({
  380. element: document.querySelector('#yAudio'),
  381. audio: {
  382. "title": this.config.name,
  383. "url": url
  384. }
  385. })
  386. },
  387. isWap(){
  388. return $(window.document).width() < 768;
  389. },
  390. error(msg){
  391. $('#'+this.config.container).css({height:'100vh',display:'flex',justifyContent: 'center',alignItems:'center',fontSize:'66px'}).text(msg);
  392. },
  393. isMobile() {
  394. const mobileRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
  395. return mobileRegex.test(navigator.userAgent);
  396. }
  397. }
  398. $(function(){
  399. var isWap = function(){
  400. return $(window.document).width() < 768;
  401. }
  402. let container = jPreview.config.container;
  403. let isTrue = false;
  404. // 文件加载完成,重置页面尺寸样式
  405. utils.functionHook($,'attr',false,function(res,args){
  406. var id = args[0].id || '';
  407. if(id != 'all_slides_warpper' || isTrue == true) {
  408. return res;
  409. } // convertToHtml结束
  410. isTrue =true;
  411. // 隐藏<#>
  412. $("#all_slides_warpper .slide .block.v-mid.content .text-block").each(function(){
  413. if ($(this).text() == '‹#›') $(this).addClass('hidden');
  414. });
  415. $("#"+container+" .slide").wrap('<div class="slide-box"></div>');
  416. $('#all_slides_warpper').height('auto');
  417. // 4.初始化主区域子节点尺寸
  418. utils.initPageSize(pageRatio(false));
  419. });
  420. // 页面尺寸随窗口变化
  421. $(window).resize(function(){
  422. var wap = isWap();
  423. // 这里可以改成阶段性变化,而不是实时变
  424. var ratio = pageRatio(wap);
  425. utils.changePageSize(ratio, 'all_slides_warpper');
  426. });
  427. var pageRatio = function(wap){
  428. // 左侧栏
  429. dfWidth = $("#all_slides_warpper .slide").first().width();
  430. dfHeight = $("#all_slides_warpper .slide").first().height();
  431. if( arguments[1] !== undefined) {
  432. return 225 / dfWidth;
  433. }
  434. // 移动端,固定以宽为基准
  435. if(wap) {
  436. return $("#"+jPreview.config.container).width() / dfWidth;
  437. }
  438. var pgWidth = $("#all_slides_warpper").width();
  439. var pgHeight = $("#all_slides_warpper").height();
  440. // 当前宽高比>原始宽高比,说明宽度较大,以高度为基准,否则相反
  441. if((pgWidth / pgHeight) > (dfWidth / dfHeight)) {
  442. return pgHeight / dfHeight;
  443. }
  444. return pgWidth / dfWidth;
  445. }
  446. });
  447. function dynamicLoadJs(url, callback) {
  448. var head = document.getElementsByTagName('head')[0]
  449. var script = document.createElement('script')
  450. script.type = 'text/javascript'
  451. script.src = url
  452. if (typeof (callback) === 'function') {
  453. script.onload = script.onreadystatechange = function () {
  454. if (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') {
  455. callback()
  456. script.onload = script.onreadystatechange = null
  457. }
  458. }
  459. }
  460. head.appendChild(script)
  461. }
  462. // 工具函数
  463. var utils = {
  464. isUTF8: function (bytes) {
  465. var i = 0;
  466. while (i < bytes.length) {
  467. if (( // ASCII
  468. bytes[i] == 0x09 ||
  469. bytes[i] == 0x0A ||
  470. bytes[i] == 0x0D ||
  471. (0x20 <= bytes[i] && bytes[i] <= 0x7E)
  472. )) {
  473. i += 1;
  474. continue;
  475. }
  476. if ((// non-overlong 2-byte
  477. (0xC2 <= bytes[i] && bytes[i] <= 0xDF) &&
  478. (0x80 <= bytes[i + 1] && bytes[i + 1] <= 0xBF)
  479. )) {
  480. i += 2;
  481. continue;
  482. }
  483. if (( // excluding overlongs
  484. bytes[i] == 0xE0 &&
  485. (0xA0 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) &&
  486. (0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF)
  487. ) || ( // straight 3-byte
  488. ((0xE1 <= bytes[i] && bytes[i] <= 0xEC) ||
  489. bytes[i] == 0xEE ||
  490. bytes[i] == 0xEF) &&
  491. (0x80 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) &&
  492. (0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF)
  493. ) || ( // excluding surrogates
  494. bytes[i] == 0xED &&
  495. (0x80 <= bytes[i + 1] && bytes[i + 1] <= 0x9F) &&
  496. (0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF)
  497. )) {
  498. i += 3;
  499. continue;
  500. }
  501. if (( // planes 1-3
  502. bytes[i] == 0xF0 &&
  503. (0x90 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) &&
  504. (0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) &&
  505. (0x80 <= bytes[i + 3] && bytes[i + 3] <= 0xBF)
  506. ) || ( // planes 4-15
  507. (0xF1 <= bytes[i] && bytes[i] <= 0xF3) &&
  508. (0x80 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) &&
  509. (0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) &&
  510. (0x80 <= bytes[i + 3] && bytes[i + 3] <= 0xBF)
  511. ) || ( // plane 16
  512. bytes[i] == 0xF4 &&
  513. (0x80 <= bytes[i + 1] && bytes[i + 1] <= 0x8F) &&
  514. (0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) &&
  515. (0x80 <= bytes[i + 3] && bytes[i + 3] <= 0xBF)
  516. )) {
  517. i += 4;
  518. continue;
  519. }
  520. return false;
  521. }
  522. return true;
  523. },
  524. // 读取cell的数字或字母
  525. getCellNum: function(str){
  526. var n = '';
  527. var isNum = !arguments[1];
  528. for(var i in str) {
  529. var val = parseInt(str[i]);
  530. var _isNaN = isNum ? !isNaN(val) : isNaN(val);
  531. if(_isNaN) n += str[i];
  532. }
  533. return isNum ? parseInt(n) : n;
  534. },
  535. // 表头字母转数字
  536. stringToNum: function(str){
  537. str=str.toLowerCase().split("");
  538. var al = str.length;
  539. var getCharNumber = function(charx){
  540. return charx.charCodeAt() -96;
  541. };
  542. var numout = 0;
  543. var charnum = 0;
  544. for(var i = 0; i < al; i++){
  545. charnum = getCharNumber(str[i]);
  546. numout += charnum * Math.pow(26, al-i-1);
  547. };
  548. return numout;
  549. },
  550. // 数字转字母
  551. numToString: function(numm){
  552. var stringArray = [];
  553. stringArray.length = 0;
  554. var numToStringAction = function(nnum){
  555. var num = nnum - 1;
  556. var a = parseInt(num / 26);
  557. var b = num % 26;
  558. stringArray.push(String.fromCharCode(64 + parseInt(b+1)));
  559. if(a>0){
  560. numToStringAction(a);
  561. }
  562. }
  563. numToStringAction(numm);
  564. return stringArray.reverse().join("");
  565. },
  566. // sheetjs.data转luckysheet.data
  567. xlsToLuckySheet: function(sheet, _sheet){
  568. var arr = (_.get(sheet, '!ref') || ':').split(':');
  569. var cols = this.getCellNum(arr[1], true);
  570. cols = this.stringToNum(cols);
  571. cols = cols > 26 ? cols : 26; // 列,字母,不足的填充
  572. var rows = this.getCellNum(arr[1]);
  573. rows = rows > 84 ? rows : 84; // 行,数字
  574. // 表格样式
  575. var _cols = _.get(sheet, '!cols') || {};
  576. var _rows = _.get(sheet, '!rows') || {};
  577. var _merges = _.get(sheet, '!merges') || {};
  578. var obj = [];
  579. var self = this;
  580. for(var i=1; i<=rows; i++) {
  581. var row = [];
  582. for(var j=1; j<=cols; j++) {
  583. var key = self.numToString(j) + i;
  584. var cell = null;
  585. if(sheet[key]) {
  586. // https://mengshukeji.github.io/LuckysheetDocs/zh/guide/cell.html#基本单元格
  587. var value = sheet[key].v || '';
  588. var style = sheet[key].s || {};
  589. var bgColor = _.get(style, 'fgColor.rgb'); // 前景色
  590. // var ftColor = _.get(style, 'ftColor.rgb');
  591. cell = {
  592. m: value, // 显示值
  593. v: value, // 原始值
  594. ct: {fa: sheet[key].z || 'General', t: sheet[key].t || 'g'},
  595. // bg: bgColor ? '#'+bgColor : '',
  596. // bl: _.get(style, 'patternType') == 'bold' ? 1 : 0,
  597. tb: 2, // 0:截断;1:溢出;2:换行
  598. }
  599. if (bgColor) cell.bg = '#'+bgColor;
  600. }
  601. row.push(cell);
  602. _sheet.config.columnlen[j-1] = _cols[j-1] ? _cols[j-1].wpx : 73; // 默认列宽73px
  603. }
  604. obj.push(row)
  605. _sheet.config.rowlen[i-1] = _rows[i-1] ? _rows[i-1].hpt * 4 / 3 : 19; // 本来有参数hpx,但其值和hpt一样;默认值行高19px
  606. }
  607. // 合并单元格
  608. // https://mengshukeji.github.io/LuckysheetDocs/zh/guide/sheet.html#初始化配置
  609. _.each(_merges, function(opt){
  610. var r = opt.s.r; // sheet[!merges] = [{e:{r:,c:},s:{r:,c:}}]
  611. var c = opt.s.c; // s:start,e:end
  612. _sheet.config.merge[r+'_'+c] = {
  613. r: r,
  614. c: c,
  615. rs: opt.e.r - r + 1,
  616. cs: opt.e.c - c + 1,
  617. };
  618. });
  619. return obj;
  620. },
  621. // 单个sheet初始配置
  622. getLuckySheet: function(){
  623. return {
  624. "name": "Sheet1",
  625. "color": "",
  626. "status": 1,
  627. "order": 0,
  628. "data": [ // data直接替换,这里就不写null填充了
  629. [null],
  630. [null],
  631. ],
  632. "config": {
  633. rowlen: {}, // 表格行高
  634. columnlen: {}, // 表格行宽
  635. merge: {}, // 合并单元格
  636. },
  637. "index": 0,
  638. // "jfgird_select_save": [],
  639. "luckysheet_select_save": [],
  640. "visibledatarow": [],
  641. "visibledatacolumn": [],
  642. // "ch_width": 4560,
  643. // "rh_height": 1760,
  644. "luckysheet_selection_range": [],
  645. "zoomRatio": 1,
  646. "celldata": [],
  647. // "load": "1",
  648. "scrollLeft": 0,
  649. "scrollTop": 0
  650. };
  651. },
  652. functionHook: function(target,method,beforeFunc,afterFunc){
  653. var context = target || window;
  654. var _theMethod = context[method];
  655. if(!context || !_theMethod) return console.error('method error!',method);
  656. context[method] = function(){
  657. var args = arguments;
  658. if(beforeFunc){
  659. var newArgs = beforeFunc.apply(this,args);
  660. if( newArgs === false ) return;
  661. args = newArgs === undefined ? args : newArgs; //没有返回值则使用结果;
  662. }
  663. var result = _theMethod.apply(this,args);
  664. if( afterFunc ){
  665. var newResult = afterFunc.apply(this,[result,args]);
  666. result = newResult === undefined ? result : newResult;//没有返回值则使用结果
  667. }
  668. return result;
  669. }
  670. },
  671. // 初始化主页面尺寸
  672. initPageSize: function(ratio){
  673. var divId = arguments[1] === undefined ? 'all_slides_warpper' : 'left_slides_bar';
  674. return this.changePageSize(ratio, divId);
  675. },
  676. // 变更主页面尺寸
  677. changePageSize: function(ratio, divId){
  678. $('#'+divId+' .slide').css({'-webkit-transform': 'scale('+ratio+')'});
  679. var width = $('#'+divId+' .slide').width() * ratio + 'px';
  680. var height = $('#'+divId+' .slide').height() * ratio + 'px'; // 使用scale后获取到的是原始尺寸,因此需要*ratio
  681. $('#'+divId+' .slide-box').css({'width': width, 'height': height});
  682. },
  683. // 前后翻页
  684. nextSlide: function(type){
  685. if(!$('#left_slides_bar').length) return;
  686. var index = parseInt($('.slide-page-toolbar .page-cur-num').text());
  687. var total = parseInt($('.slide-page-toolbar .page-total-num').text());
  688. if((index == 1 && type == 'sub') || (index == total && type == 'add')) return;
  689. var page = type == 'sub' ? index - 1 : index + 1;
  690. this.gotoSlide(page);
  691. },
  692. // 页码变更
  693. gotoSlide: function(page){
  694. // 0.设置页码显示
  695. $('.slide-page-toolbar .page-cur-num').html(page);
  696. // 1.主区域显示
  697. $('#all_slides_warpper .slide-box').hide();
  698. $('#all_slides_warpper .slide-box').eq(page - 1).show();
  699. // 2.左右翻页图标显示
  700. this.setLtRtIcon();
  701. // 3.左侧选中样式变更
  702. $('#left_slides_bar .slide-box').removeClass('total-page-point');
  703. $('#left_slides_bar .slide-box').eq(page - 1).addClass('total-page-point');
  704. // 左侧选中项滚动到当前区域,滚轮停止后计算
  705. setTimeout(function(){
  706. if (!$(".total-page-point").length) return;
  707. var top = $(".total-page-point").offset().top;
  708. var height = $(".total-page-point").height();
  709. // 选中区在可视范围内时不滚动。margin+padding=8+10
  710. if(top >= 18 && (top + height - 8) < $("#left_slides_bar").height()) {
  711. return false;
  712. }
  713. // 滚动高度为选中区前所有兄弟元素的(高+mb)之和,理论上应该再+18
  714. var prevTop = (height + 10) * (page - 1);
  715. $("#left_slides_bar").scrollTop(prevTop + 10);
  716. }, 200);
  717. },
  718. // 左右翻页图标显示和隐藏
  719. setLtRtIcon: function(){
  720. var index = parseInt($('.slide-page-toolbar .page-cur-num').text());
  721. var total = parseInt($('.slide-page-toolbar .page-total-num').text());
  722. var funcLt = index == 1 ? 'hide' : 'show';
  723. var funcRt = index == total ? 'hide' : 'show';
  724. $('.slide-left-icon.btn')[funcLt]();
  725. $('.slide-right-icon.btn')[funcRt]();
  726. }
  727. }