@focus-teach/ui 1.0.37 → 1.0.39

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/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.37",
5
+ "version": "1.0.39",
6
6
  "private": false,
7
7
  "main": "lib/ui.umd.min.js",
8
8
  "scripts": {
@@ -26,6 +26,7 @@
26
26
  "sass": "^1.26.5",
27
27
  "sass-loader": "^10.1.1",
28
28
  "underscore": "^1.13.1",
29
+ "v-viewer": "^1.7.4",
29
30
  "vue": "^2.6.10",
30
31
  "vue-infinite-scroll": "^2.0.2",
31
32
  "vue-loader": "^15.9.6",
package/utils/common.js CHANGED
@@ -1,3 +1,13 @@
1
+ const TABLE_PC = 647; // PC所占宽度
2
+ const TABLE_MISP = 959; // 错打所占宽度
3
+ const TABLE_MINIP = 320; // 小程序所占宽度
4
+ const TABLE_DRAWER = 470; // 抽屉所占宽度
5
+ const TABLE_CLASS = 509; // 上课所占宽度
6
+ const TABLE_MISD = 457; // 错打下载所占宽度
7
+ const TABLE_MINI110 = 408; // 小程序110mm所占宽度
8
+ const MAX_HEIGHT = 1024 //一页a4的高度
9
+
10
+
1
11
  function getFileTypeSuffix(fileName) {
2
12
  var index = fileName.lastIndexOf(".");
3
13
  if (index > -1) {
@@ -167,4 +177,284 @@ export function getKlLeafNode(tree){
167
177
  }
168
178
  deep(tree);
169
179
  return result;
180
+ }
181
+
182
+ function ptToPx(pt) {
183
+ var dpi = window.devicePixelRatio * 96;
184
+ return pt * dpi / 72;
185
+ }
186
+
187
+ function getStringWidthNoImage(text, type, fontSize = '14px',fontFamily='Times New Roman, SimSun, 宋体 ,Segoe UI Symbol') {
188
+ var span = document.createElement("span");
189
+ var result = {};
190
+ result.width = span.offsetWidth;
191
+ result.height = span.offsetHeight;
192
+ span.style.visibility = "hidden";
193
+ span.style.fontSize = fontSize;
194
+ span.style.fontFamily = fontFamily
195
+ span.style.display = "inline-block";
196
+ span.style.whiteSpace = 'break-spaces'
197
+ document.body.appendChild(span);
198
+ if(type == 'text'){
199
+ if (typeof span.textContent != "undefined") {
200
+ span.textContent = text;
201
+ } else {
202
+ span.innerText = text;
203
+ }
204
+ }else{
205
+ span.innerHTML = text
206
+ }
207
+
208
+ result.width = parseFloat(window.getComputedStyle(span).width) - result.width;
209
+ result.height = parseFloat(window.getComputedStyle(span).height) - result.height;
210
+ document.body.removeChild(span);
211
+ return result.width;
212
+ }
213
+
214
+ function getImageWidth(child, maxWidth, maxHeight) {
215
+ let rect = child.getBoundingClientRect()
216
+ let width = rect.width,height = rect.height;
217
+ if(!child.getAttribute('width') || !child.getAttribute('height')){
218
+ child.setAttribute('width',width)
219
+ child.setAttribute('height',height < maxHeight ? height : maxHeight)
220
+ let computedWidth = width > maxWidth ? maxWidth : width
221
+ let computedHeight = (computedWidth * child.naturalHeight / child.naturalWidth)
222
+ if(width >= maxWidth) {
223
+ height = computedWidth
224
+ child.style.width = computedWidth + 'px'//超过高度之后 按原图片等比例缩放的高度设置
225
+ }
226
+ if(height >= maxHeight) {
227
+ height = computedHeight
228
+ child.style.height = computedHeight + 'px'//超过高度之后 按原图片等比例缩放的高度设置
229
+ }
230
+ }else{
231
+ width = rect.width;
232
+ height = rect.height;
233
+ }
234
+ return {width,height}
235
+ }
236
+ function loadImage({child,maxHeight,maxWidth}){//等待图片加载完成
237
+ return new Promise(async (resolve)=>{
238
+ try{
239
+ const promiseArr = [];
240
+ let imgLoadCount = 0;
241
+ if(child.nodeName === 'IMG'){
242
+ promiseArr.push(new Promise(resolve=>{
243
+ let timer = setInterval(function() {
244
+ if(child.complete) {
245
+ clearInterval(timer)
246
+ let { width,height } = getImageWidth(child,maxWidth,maxHeight)
247
+ resolve({width,height})
248
+ }
249
+ if(child.onerror){
250
+ imgLoadCount++
251
+ if(imgLoadCount > 60){
252
+ clearInterval(timer)
253
+ imgLoadCount = 0
254
+ let { width,height } = getImageWidth(child,maxWidth,maxHeight)
255
+ resolve({width,height})
256
+ }
257
+ }
258
+ }, 50)
259
+ }))
260
+ }
261
+ if(!promiseArr.length){
262
+ resolve('other')
263
+ }else{
264
+ try{
265
+ let res = await Promise.all(promiseArr)
266
+ console.log('res is...',res)
267
+ resolve({width:res?.[0]?.width,height:res?.[0]?.height})
268
+ }catch(err){
269
+ setTimeout(()=>{
270
+ resolve('loadError')
271
+ },2000)
272
+ }
273
+ }
274
+ }catch(error){
275
+ setTimeout(()=>{
276
+ resolve('loadError')
277
+ },2000)
278
+ }
279
+ })
280
+ }
281
+
282
+ // 计算当前选项的列数
283
+ async function getCols(optionArr,TABLE_WIDTH,className){
284
+ let optionCount = optionArr.length, factors = [], cols = 1;
285
+
286
+ // 计算选项公约数
287
+ factors = [...Array(optionCount).keys()].map(item=>item+1).filter(item=> optionCount % item === 0).reverse();
288
+ let widthList = [];
289
+ // let widthList = optionArr.map(async item=>{
290
+ for(let i = 0; i < optionArr.length; i++){
291
+ let item = optionArr[i]
292
+ if(item.includes('img')){
293
+ let div = document.createElement('div')
294
+ div.id = 'preRenderImgContainer'
295
+ div.style.visibility = 'hidden'
296
+ div.innerHTML = item
297
+ document.body.appendChild(div)
298
+ let width = 0
299
+ // Array.from(div.childNodes).forEach((childNode)=>{
300
+ for(let j = 0; j < div.childNodes.length; j++){
301
+ let childNode = div.childNodes[j]
302
+ if(childNode.nodeName == 'IMG'){
303
+ let tempWidth = childNode?.style?.width || childNode.width
304
+ console.log(tempWidth,className,'图片宽度');
305
+ let realWidth = String(tempWidth || '').includes('pt') ? ptToPx(parseFloat(tempWidth)) : parseFloat(tempWidth)
306
+ // 若图片不存在宽度,则随便给一个值,超过编辑器宽度即可,这里给了1000
307
+ if(!realWidth) {
308
+ let { width,height } = await loadImage({child:childNode,maxHeight:MAX_HEIGHT,maxWidth:TABLE_WIDTH})
309
+ realWidth = width
310
+ }
311
+ width += realWidth || MAX_HEIGHT
312
+ }else{
313
+ if(childNode.nodeName == '#text'){
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{
317
+ width += getStringWidthNoImage(childNode.outerHTML,'',className == 'options-mistake-print' ? '16px' : '14px',className == 'options-mistake-print' ? 'Avenir, Helvetica, Arial, sans-serif' : undefined)
318
+ console.log(width,'其他标签宽度');
319
+ }
320
+ }
321
+ }
322
+ div?.remove?.()
323
+ console.log("带图片识别出来的宽",className,width);
324
+ // return width
325
+ widthList.push(width)
326
+ }else{
327
+ let eleWidth = getStringWidthNoImage(item.trim(),'',className == 'options-mistake-print' ? '16px' : '14px',className == 'options-mistake-print' ? 'Avenir, Helvetica, Arial, sans-serif' : undefined)
328
+ widthList.push(eleWidth)
329
+ }
330
+ }
331
+ //当前选项的长度列表
332
+ let maxWidth = Math.max(...widthList); // 当前选项的最大长度
333
+
334
+ let find = factors.find(item=> maxWidth <= TABLE_WIDTH / item)
335
+ find && (cols = find);
336
+ console.log('计算出来的表格列数=====>',cols,maxWidth);
337
+ return cols
338
+ }
339
+
340
+
341
+ async function getOptionArr(optionArr,TABLE_WIDTH,className){
342
+ let cols = await getCols(optionArr,TABLE_WIDTH,className);
343
+ let rows = Math.ceil(optionArr.length / cols);
344
+ optionArr = Array.from({ length: rows }, (_, i) => optionArr.slice(i * cols, i * cols + cols));
345
+ return optionArr
346
+ }
347
+
348
+ export async function generateTable(optionArr,option) {
349
+
350
+ let table = '<table border="0" class="ckeditor-table cke_show_border option-table" style="border-collapse: collapse; width: 100%;;border:none"><tbody>';
351
+ // 后续有新增类型只需要添加宽度和对应的类名即可
352
+ // !!!!!!!!!!!!!!!!
353
+ // !!!!!!!!!!!!!!!!
354
+ // !!!!!!!!!!!!!!!! 新增类型需要去ckeditor.js源码中在表格转换功能处过滤新增的类型
355
+ // !!!!!!!!!!!!!!!!
356
+ // !!!!!!!!!!!!!!!!
357
+ let tableList = [
358
+ // PC
359
+ {
360
+ className: 'options-origin',
361
+ width: TABLE_PC
362
+ },
363
+ // 错打
364
+ {
365
+ className: 'options-mistake-print',
366
+ width: TABLE_MISP
367
+ },
368
+ // 小程序
369
+ {
370
+ className: 'options-miniprogram',
371
+ width: TABLE_MINIP
372
+ },
373
+ // 抽屉
374
+ {
375
+ className: 'options-drawer',
376
+ width: TABLE_DRAWER
377
+ },
378
+ // 上课
379
+ {
380
+ className: 'options-class',
381
+ width: TABLE_CLASS
382
+ },
383
+ // 错打下载
384
+ {
385
+ className: 'options-mistake-download',
386
+ width: TABLE_MISD
387
+ },
388
+ // 小程序110mm
389
+ {
390
+ className: 'options-mini110',
391
+ width: TABLE_MINI110
392
+ },
393
+ ]
394
+ // [[A,B],[C,D]]
395
+ for(let i = 0; i < tableList.length; i++){
396
+ let item = tableList[i]
397
+ let optionsTemp = await getOptionArr(optionArr,item.width,item.className);
398
+ optionsTemp.forEach((optionCols)=>{
399
+ table += `<tr class="options-tr ${item.className}">`
400
+ optionCols.forEach((item)=>{
401
+ table += '<td>'+item+'</td>'
402
+ })
403
+ table += '</tr>'
404
+ })
405
+
406
+ }
407
+ table += '</tbody></table>'
408
+ return table
409
+ }
410
+
411
+ // 把选项转换成表格展示
412
+ function optionToTable(stem) {
413
+ let content = [] // 存储题干内容
414
+ let option = '' // 存储选项内容
415
+ let optionPosition = -1 // 选项在题干中的位置
416
+ let div = document.createElement('div');
417
+ div.innerHTML = stem;
418
+ if(div?.childNodes.length){
419
+ div.childNodes.forEach((item,index)=>{
420
+ if(item?.innerHTML.match(/^((\n|\s|&nbsp;)*([A-H]\s*)+[.|、|.])/g) || item?.innerHTML.match(/\n?(\s|&nbsp;)+([A-H])[.|、|.]\s*/g)){
421
+ // 有选项的节点,从中取出选项
422
+ let matchs = item?.innerHTML.match(/^((\n|\s|&nbsp;)*([A-H]\s*)+[.|、|.])/g) || item?.innerHTML.match(/\n?(\s|&nbsp;)+([A-H])[.|、|.]\s*/g);
423
+ let optionIndex = item.innerHTML.indexOf(matchs[0])
424
+
425
+ if(optionIndex > 0){
426
+ // 表示选项前面有其他内容
427
+ content.push(`<p>${item.innerHTML.slice(0,optionIndex)}</p>`)
428
+ option += item?.innerHTML.slice(optionIndex)
429
+ optionPosition < 0 && (optionPosition = index + 1)
430
+ }else{
431
+ option += item?.innerHTML
432
+ // 只记录第一个选项的位置
433
+ optionPosition < 0 && (optionPosition = index)
434
+ }
435
+ }else{
436
+ // content += item?.outerHTML
437
+ content.push(item?.outerHTML)
438
+ }
439
+ })
440
+ }
441
+ console.log(option,'option======');
442
+ if(!option) return stem
443
+ const reg = /([A-H])[.|、|.]\s*(.+?)(?=[A-H][.|、|.]|\s*$)/g;
444
+ let optionArr = option.match(reg);
445
+ if(!optionArr || !optionArr.length) return stem // 没有匹配到选项,直接返回
446
+ // optionArr 肯定是以选项A-H开头的,如果超过一个A开头的选项,说明不符合单选题的规则,直接返回
447
+ // 若匹配到的选项不以A开头,则直接返回
448
+ if(optionArr?.filter(item=>item.startsWith('A'))?.length > 1 || optionArr?.filter(item=>item.startsWith('A'))?.length == 0 ||optionArr?.length > 4) return stem
449
+ optionArr = optionArr.map(item=> item.trim())
450
+ let table = generateTable(optionArr,option);
451
+
452
+ content.splice(optionPosition,0,table)
453
+ return content.join('')
454
+ }
455
+
456
+ export function getUpdateTime(timeStr) {
457
+ var oneTime = new Date(timeStr).getTime();
458
+ var date = new Date(oneTime);
459
+ return formatDate(date, "yyyy-MM-dd hh:mm");
170
460
  }