@focus-teach/ui 1.0.54 → 1.0.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api/index.js +48 -1
- package/lib/img/ico_xkw.d6be8fa4.png +0 -0
- package/lib/ui.common.js +73589 -51007
- package/lib/ui.css +1 -1
- package/lib/ui.umd.js +73589 -51007
- package/lib/ui.umd.min.js +40 -5
- package/package.json +3 -1
- package/utils/common.js +217 -13
- package/utils/index.js +1181 -0
- package/utils/xkw/util.js +588 -0
- package/utils/xkw/xkw-sdk.js +296 -0
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@focus-teach/ui",
|
|
3
3
|
"packageName": "ui",
|
|
4
4
|
"publishName": "@focus-teach/ui",
|
|
5
|
-
"version": "1.0.
|
|
5
|
+
"version": "1.0.55",
|
|
6
6
|
"private": false,
|
|
7
7
|
"main": "lib/ui.umd.min.js",
|
|
8
8
|
"scripts": {
|
|
@@ -22,7 +22,9 @@
|
|
|
22
22
|
"babel-loader": "^8.0.0",
|
|
23
23
|
"core-js": "^3.6.5",
|
|
24
24
|
"element-ui": "^2.15.1",
|
|
25
|
+
"html2canvas": "^1.4.1",
|
|
25
26
|
"lodash": "^4.17.21",
|
|
27
|
+
"md5": "^2.3.0",
|
|
26
28
|
"qs": "^6.10.1",
|
|
27
29
|
"sass": "^1.26.5",
|
|
28
30
|
"sass-loader": "^10.1.1",
|
package/utils/common.js
CHANGED
|
@@ -132,7 +132,7 @@ export function getFirstNumber(itemNumber, type) {
|
|
|
132
132
|
let match = itemNumber.match(/(\d+)(?:-(\d+))?/);
|
|
133
133
|
let num1 = match[1];
|
|
134
134
|
let num2 = match[2];
|
|
135
|
-
console.log(resultmatchchineseNum, num1, num2)
|
|
135
|
+
// console.log(resultmatchchineseNum, num1, num2)
|
|
136
136
|
return resultmatchchineseNum ? num1 == num2 ? resultmatchchineseNum + num1 : itemNumber : num1 == num2 ? num1 : itemNumber
|
|
137
137
|
}
|
|
138
138
|
}
|
|
@@ -263,7 +263,7 @@ function loadImage({child,maxHeight,maxWidth}){//等待图片加载完成
|
|
|
263
263
|
}else{
|
|
264
264
|
try{
|
|
265
265
|
let res = await Promise.all(promiseArr)
|
|
266
|
-
|
|
266
|
+
// console.log('res is...',res)
|
|
267
267
|
resolve({width:res?.[0]?.width,height:res?.[0]?.height})
|
|
268
268
|
}catch(err){
|
|
269
269
|
setTimeout(()=>{
|
|
@@ -301,7 +301,7 @@ async function getCols(optionArr,TABLE_WIDTH,className){
|
|
|
301
301
|
let childNode = div.childNodes[j]
|
|
302
302
|
if(childNode.nodeName == 'IMG'){
|
|
303
303
|
let tempWidth = childNode?.style?.width || childNode.width
|
|
304
|
-
console.log(tempWidth,className,'图片宽度');
|
|
304
|
+
// console.log(tempWidth,className,'图片宽度');
|
|
305
305
|
let realWidth = String(tempWidth || '').includes('pt') ? ptToPx(parseFloat(tempWidth)) : parseFloat(tempWidth)
|
|
306
306
|
// 若图片不存在宽度,则随便给一个值,超过编辑器宽度即可,这里给了1000
|
|
307
307
|
if(!realWidth) {
|
|
@@ -312,15 +312,27 @@ async function getCols(optionArr,TABLE_WIDTH,className){
|
|
|
312
312
|
}else{
|
|
313
313
|
if(childNode.nodeName == '#text'){
|
|
314
314
|
width += getStringWidthNoImage(childNode.textContent,'text',className == 'options-mistake-print' ? '16px' : '14px',className == 'options-mistake-print' ? 'Avenir, Helvetica, Arial, sans-serif' : undefined)
|
|
315
|
-
console.log(width,'文本节点宽度');
|
|
316
|
-
}else{
|
|
315
|
+
// console.log(width,'文本节点宽度');
|
|
316
|
+
}else{//对应其他标签 优先处理是否里面有内嵌的图片
|
|
317
|
+
let imgList = childNode.querySelectorAll('img')
|
|
318
|
+
for(let k = 0; k < imgList.length; k++){
|
|
319
|
+
let img = imgList[k]
|
|
320
|
+
let tempWidth = img?.style?.width || img.width
|
|
321
|
+
let realWidth = String(tempWidth || '').includes('pt') ? ptToPx(parseFloat(tempWidth)) : parseFloat(tempWidth)
|
|
322
|
+
// 若图片不存在宽度,则随便给一个值,超过编辑器宽度即可,这里给了1000
|
|
323
|
+
if(!realWidth) {
|
|
324
|
+
let { width,height } = await loadImage({child:img,maxHeight:MAX_HEIGHT,maxWidth:TABLE_WIDTH})
|
|
325
|
+
realWidth = width
|
|
326
|
+
}
|
|
327
|
+
width += realWidth || MAX_HEIGHT
|
|
328
|
+
}
|
|
317
329
|
width += getStringWidthNoImage(childNode.outerHTML,'',className == 'options-mistake-print' ? '16px' : '14px',className == 'options-mistake-print' ? 'Avenir, Helvetica, Arial, sans-serif' : undefined)
|
|
318
|
-
console.log(width,'其他标签宽度');
|
|
330
|
+
// console.log(width,'其他标签宽度');
|
|
319
331
|
}
|
|
320
332
|
}
|
|
321
333
|
}
|
|
322
334
|
div?.remove?.()
|
|
323
|
-
console.log("带图片识别出来的宽",className,width);
|
|
335
|
+
// console.log("带图片识别出来的宽",className,width);
|
|
324
336
|
// return width
|
|
325
337
|
widthList.push(width)
|
|
326
338
|
}else{
|
|
@@ -333,19 +345,22 @@ async function getCols(optionArr,TABLE_WIDTH,className){
|
|
|
333
345
|
|
|
334
346
|
let find = factors.find(item=> maxWidth <= TABLE_WIDTH / item)
|
|
335
347
|
find && (cols = find);
|
|
336
|
-
console.log('计算出来的表格列数=====>',cols,maxWidth);
|
|
348
|
+
// console.log('计算出来的表格列数=====>',cols,maxWidth);
|
|
337
349
|
return cols
|
|
338
350
|
}
|
|
339
351
|
|
|
340
352
|
|
|
341
|
-
async function getOptionArr(optionArr,TABLE_WIDTH,className){
|
|
353
|
+
async function getOptionArr(optionArr,TABLE_WIDTH,className,optionCount){
|
|
342
354
|
let cols = await getCols(optionArr,TABLE_WIDTH,className);
|
|
355
|
+
if(optionCount){
|
|
356
|
+
cols = Math.min(cols,optionCount)
|
|
357
|
+
}
|
|
343
358
|
let rows = Math.ceil(optionArr.length / cols);
|
|
344
359
|
optionArr = Array.from({ length: rows }, (_, i) => optionArr.slice(i * cols, i * cols + cols));
|
|
345
360
|
return optionArr
|
|
346
361
|
}
|
|
347
362
|
|
|
348
|
-
export async function generateTable(optionArr,
|
|
363
|
+
export async function generateTable(optionArr,optionCount) {
|
|
349
364
|
|
|
350
365
|
let table = '<table border="0" class="ckeditor-table cke_show_border option-table" style="border-collapse: collapse; width: 100%;;border:none"><tbody>';
|
|
351
366
|
// 后续有新增类型只需要添加宽度和对应的类名即可
|
|
@@ -394,7 +409,7 @@ export async function generateTable(optionArr,option) {
|
|
|
394
409
|
// [[A,B],[C,D]]
|
|
395
410
|
for(let i = 0; i < tableList.length; i++){
|
|
396
411
|
let item = tableList[i]
|
|
397
|
-
let optionsTemp = await getOptionArr(optionArr,item.width,item.className);
|
|
412
|
+
let optionsTemp = await getOptionArr(optionArr,item.width,item.className,optionCount);
|
|
398
413
|
optionsTemp.forEach((optionCols)=>{
|
|
399
414
|
table += `<tr class="options-tr ${item.className}">`
|
|
400
415
|
optionCols.forEach((item)=>{
|
|
@@ -438,7 +453,7 @@ function optionToTable(stem) {
|
|
|
438
453
|
}
|
|
439
454
|
})
|
|
440
455
|
}
|
|
441
|
-
console.log(option,'option======');
|
|
456
|
+
// console.log(option,'option======');
|
|
442
457
|
if(!option) return stem
|
|
443
458
|
const reg = /([A-H])[.|、|.]\s*(.+?)(?=[A-H][.|、|.]|\s*$)/g;
|
|
444
459
|
let optionArr = option.match(reg);
|
|
@@ -457,4 +472,193 @@ export function getUpdateTime(timeStr) {
|
|
|
457
472
|
var oneTime = new Date(timeStr).getTime();
|
|
458
473
|
var date = new Date(oneTime);
|
|
459
474
|
return formatDate(date, "yyyy-MM-dd hh:mm");
|
|
460
|
-
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function extractTextAndImages(html) {
|
|
478
|
+
// 第一步:将img标签替换为特殊标记
|
|
479
|
+
const imgPlaceholder = '___IMG___';
|
|
480
|
+
const imgTags = [];
|
|
481
|
+
|
|
482
|
+
const htmlWithPlaceholders = html.replace(/<img[^>]+>/gi, match => {
|
|
483
|
+
imgTags.push(match);
|
|
484
|
+
return imgPlaceholder;
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
// 第二步:移除其他HTML标签
|
|
488
|
+
const textWithPlaceholders = htmlWithPlaceholders
|
|
489
|
+
.replace(/<[^>]+>/g, '') // 移除所有HTML标签
|
|
490
|
+
.replace(/ /g, ' ') // 替换HTML实体
|
|
491
|
+
.replace(/</g, '<')
|
|
492
|
+
.replace(/>/g, '>')
|
|
493
|
+
.replace(/&/g, '&')
|
|
494
|
+
.replace(/"/g, '"')
|
|
495
|
+
.replace(/\s+/g, ' '); // 合并多个空格
|
|
496
|
+
|
|
497
|
+
// 第三步:还原img标签
|
|
498
|
+
let index = 0;
|
|
499
|
+
const result = textWithPlaceholders.replace(new RegExp(imgPlaceholder, 'g'), () => {
|
|
500
|
+
return imgTags[index++] || '';
|
|
501
|
+
});
|
|
502
|
+
let dom = new DOMParser().parseFromString(result,'text/html')
|
|
503
|
+
let contentList = []
|
|
504
|
+
Array.from(dom?.body?.childNodes).map(item=>{
|
|
505
|
+
if(item.nodeName == 'IMG'){
|
|
506
|
+
contentList.push({type:'image',content:item.outerHTML,url:item.src})
|
|
507
|
+
}else{
|
|
508
|
+
contentList.push({type:'text',content:item.textContent})
|
|
509
|
+
}
|
|
510
|
+
})
|
|
511
|
+
return contentList;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
export async function textWithImageToBase64(text, options = {}) {
|
|
515
|
+
let contentList = extractTextAndImages(text)
|
|
516
|
+
// 默认配置
|
|
517
|
+
const config = {
|
|
518
|
+
width: 762,
|
|
519
|
+
fontSize: 14,
|
|
520
|
+
fontFamily: 'Arial',
|
|
521
|
+
textColor: '#000000',
|
|
522
|
+
backgroundColor: '#ffffff',
|
|
523
|
+
padding: 16,
|
|
524
|
+
lineHeight: 1.5,
|
|
525
|
+
imageHeight: 100, // 默认图片高度
|
|
526
|
+
imageWidth: 100, // 默认图片宽度
|
|
527
|
+
imageMargin: 10, // 图片之间的间距
|
|
528
|
+
...options
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
// 创建canvas
|
|
532
|
+
const canvas = document.createElement('canvas');
|
|
533
|
+
const ctx = canvas.getContext('2d');
|
|
534
|
+
|
|
535
|
+
// 加载图片函数
|
|
536
|
+
const loadImage = (src) => {
|
|
537
|
+
return new Promise((resolve, reject) => {
|
|
538
|
+
const img = new Image();
|
|
539
|
+
img.crossOrigin = 'anonymous'; // 处理跨域
|
|
540
|
+
img.onload = () => resolve(img);
|
|
541
|
+
img.onerror = reject;
|
|
542
|
+
img.src = src;
|
|
543
|
+
});
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
// 计算文本换行
|
|
547
|
+
function getTextLines(text, maxWidth) {
|
|
548
|
+
ctx.font = `${config.fontSize}px ${config.fontFamily}`;
|
|
549
|
+
const words = text.split('');
|
|
550
|
+
const lines = [];
|
|
551
|
+
let currentLine = '';
|
|
552
|
+
|
|
553
|
+
for (let i = 0; i < words.length; i++) {
|
|
554
|
+
const testLine = currentLine + words[i];
|
|
555
|
+
const metrics = ctx.measureText(testLine);
|
|
556
|
+
|
|
557
|
+
if (metrics.width > maxWidth && i > 0) {
|
|
558
|
+
lines.push(currentLine);
|
|
559
|
+
currentLine = words[i];
|
|
560
|
+
} else {
|
|
561
|
+
currentLine = testLine;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
lines.push(currentLine);
|
|
565
|
+
return lines;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// 计算总高度
|
|
569
|
+
async function calculateTotalHeight(content) {
|
|
570
|
+
let totalHeight = config.padding;
|
|
571
|
+
const maxWidth = config.width - (config.padding * 2);
|
|
572
|
+
|
|
573
|
+
for (const item of content) {
|
|
574
|
+
if (item.type === 'text') {
|
|
575
|
+
const lines = getTextLines(item.content, maxWidth);
|
|
576
|
+
totalHeight += lines.length * config.fontSize * config.lineHeight;
|
|
577
|
+
} else if (item.type === 'image') {
|
|
578
|
+
const img = await loadImage(item.url)
|
|
579
|
+
totalHeight += img.height + config.imageMargin;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
return totalHeight + config.padding;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// 绘制内容
|
|
587
|
+
async function drawContent() {
|
|
588
|
+
let currentY = config.padding;
|
|
589
|
+
const maxWidth = config.width - (config.padding * 2);
|
|
590
|
+
|
|
591
|
+
for (const item of contentList) {
|
|
592
|
+
if (item.type === 'text') {
|
|
593
|
+
ctx.font = `${config.fontSize}px ${config.fontFamily}`;
|
|
594
|
+
ctx.fillStyle = config.textColor;
|
|
595
|
+
|
|
596
|
+
const lines = getTextLines(item.content, maxWidth);
|
|
597
|
+
for (const line of lines) {
|
|
598
|
+
ctx.fillText(line, config.padding, currentY);
|
|
599
|
+
currentY += config.fontSize * config.lineHeight;
|
|
600
|
+
}
|
|
601
|
+
} else if (item.type === 'image') {
|
|
602
|
+
try {
|
|
603
|
+
const img = await loadImage(item.url);
|
|
604
|
+
// 居中显示图片
|
|
605
|
+
const x = (config.width - img.width) / 2;
|
|
606
|
+
ctx.drawImage(img, config.imageMargin, currentY, img.width, img.height);
|
|
607
|
+
currentY += img.height + config.imageMargin;
|
|
608
|
+
} catch (error) {
|
|
609
|
+
console.error('Failed to load image:', error);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// 计算画布高度
|
|
616
|
+
const totalHeight = await calculateTotalHeight(contentList);
|
|
617
|
+
|
|
618
|
+
// 设置画布尺寸
|
|
619
|
+
canvas.width = config.width;
|
|
620
|
+
canvas.height = totalHeight;
|
|
621
|
+
|
|
622
|
+
// 绘制背景
|
|
623
|
+
ctx.fillStyle = config.backgroundColor;
|
|
624
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
625
|
+
|
|
626
|
+
// 绘制内容
|
|
627
|
+
await drawContent();
|
|
628
|
+
|
|
629
|
+
return {
|
|
630
|
+
base64: canvas.toDataURL('image/png'),
|
|
631
|
+
size: Math.ceil((canvas.toDataURL('image/png').length * 3/4) / (1024 * 1024))
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* 计算 base64 字符串大小
|
|
636
|
+
* @param {string} base64String base64字符串
|
|
637
|
+
* @returns {object} 包含字节数和格式化后大小的对象
|
|
638
|
+
*/
|
|
639
|
+
export function getBase64Size(base64String,unit = 'MB') {
|
|
640
|
+
// 去掉 base64 字符串头部标识和空格
|
|
641
|
+
const strLength = base64String.replace(/^data:image\/\w+;base64,/, '').length;
|
|
642
|
+
|
|
643
|
+
// base64 字符串解码后的字节数,大约为原长度的 3/4
|
|
644
|
+
const fileSize = Math.floor((strLength - (strLength / 8) * 2) / 4) * 3;
|
|
645
|
+
// 格式化文件大小
|
|
646
|
+
const formatSize = (size) => {
|
|
647
|
+
if (size < 1024 && unit == 'B') {
|
|
648
|
+
return Math.ceil(size);
|
|
649
|
+
} else if (size < 1024 * 1024 && unit == 'KB') {
|
|
650
|
+
return Math.ceil(size / 1024);
|
|
651
|
+
} else if (size < 1024 * 1024 && unit == 'MB') {
|
|
652
|
+
return Math.ceil((size / (1024 * 1024)));
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
return formatSize(fileSize);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
|