lsiten 6 years ago
parent
commit
d0c1b6b106
7 changed files with 530 additions and 1016 deletions
  1. 2 2
      src/data.json
  2. 3 22
      src/index.js
  3. 9 0
      src/js/util/question.js
  4. 0 932
      src/js/util/questionPrev.js
  5. 483 59
      src/js/yzPage.js
  6. 32 0
      src/js/yzPageManager.js
  7. 1 1
      src/less/main.less

+ 2 - 2
src/data.json

@@ -2,8 +2,8 @@
 	"schoolId": "108",
 	"id": "459",
 	"subjectId": "1635",
-	"paperSize": "A4",
-	"layoutType": "1",
+	"paperSize": "A3",
+	"layoutType": "2",
 	"sourceType": "2",
 	"prohibitType": "1",
 	"noMode": "1",

+ 3 - 22
src/index.js

@@ -104,31 +104,12 @@ let editor = new yzWebeditor({dom:document.getElementById('pages-box'), data: da
 //   asynExecCommand(0, commondsLength);
 // }
 
-// function checkoutCententChange() {
-//   return new Promise((resolve, reject) => {
-//     while (editor.page._checkCrossing || editor.page._addParagraphStatus || editor.page._contentChangeStatus) {}    
-//     resolve(true);
-//   });
-// }
 // async function asynExecCommand (i, length) {
 //   if (i < length) {
-//     let result = await checkoutCententChange();
-//     if (result) {
+//     setTimeout(() => {
 //       editor.cmd.do(conmands[i].exc, conmands[i].params);
-//       if (conmands[i].exc === 'border' && conmands[i].params.type === 1) {
-//         editor.isDoObject = true;
-//       } else if (conmands[i].exc === 'questiontype' && conmands[i].params.type === 1){
-//         editor.isDoObject = true;
-//       } else if (conmands[i].exc === 'questiontype' && conmands[i].params.type === 2) {
-//         editor.isDoObject = true;
-//       } else {
-//         editor.isDoObject = false;
-//       }
-//       i++;
-//     }
-//     asynExecCommand(i, length);
-//   } else {
-//     editor.page._checkContentOutThrottle();
+//       asynExecCommand(++i, length);
+//     }, 0)
 //   }
 // }
 

+ 9 - 0
src/js/util/question.js

@@ -201,6 +201,10 @@ questionUtils.prototype = {
         }
         index--;
       }
+      
+      if (isEmptyElement($content[0])) {
+        $border.remove();
+      }
       if ($nextContent.childNodes().length > 0) {
         $nextBorder.append($nextContent);
         $border.addClass('js-split-border');
@@ -209,6 +213,7 @@ questionUtils.prototype = {
       } else {
         return false;
       }
+
     } else {
       $border.remove();
       return false;
@@ -367,6 +372,10 @@ questionUtils.prototype = {
       $nextWritting.addClass('js-split-question');
       $item.addClass('js-split-question');
     }
+
+    if (isEmptyElement($item[0])) {
+      $item.remove();
+    }
   }
 }
 

+ 0 - 932
src/js/util/questionPrev.js

@@ -1,932 +0,0 @@
-import $ from './dom-core';
-import {getParentNodeByClass, isEmptyElement} from './util.js'
-let questionPrevUtils = function () {
-  this.currentParaghSize = {};
-}
-
-questionPrevUtils.prototype = {
-  constructor: questionPrevUtils,
-  /**
-  * function 客观题获取最后一行【不在框内】
-  * @param {DomElement}  $lastparagraph
-  */
-  _getLastNodeByLastparagraphNormal: function ($lastparagraph) {
-    let lastchild = $lastparagraph[0].lastChild;
-    if (lastchild.nodeType === 3) {
-      return {
-        type: 1,
-        dom: lastchild
-      };
-    } else if (lastchild.tagName.toLowerCase() === 'br'){
-      $(lastchild).remove();
-      return this._getLastNodeByLastparagraphNormal($lastparagraph);
-    } else {
-      if (lastchild.className) {
-        if (lastchild.className.indexOf('js-lsiten-border') > -1) {
-          return {
-            type: 0,
-            dom: null
-          };
-        }
-      } else {
-        return {
-          type: 2,
-          dom: lastchild
-        };
-      }
-    }
-  },
-   /**
-   * funciton 处理回退文字【不在框内】
-   * @param {Node} item 文字节点
-   * @param {Object} sizes 尺寸对象
-   * @param {Boolean} isMerge 是否需要合并段落
-   * @param {DomElement} $lastparagraph 该栏最后一段
-   * @param {ndoe} currentParagh 当前编辑的段落
-   */
-  handerText: function (item, sizes, isMerge, $lastparagraph, currentParagh) {
-    if (isMerge) {
-      let lastNode = this._getLastNodeByLastparagraphNormal($lastparagraph);
-      if (lastNode.type === 1 || lastNode.type === 2) {
-        let textValue = item.nodeValue;
-        let textLength = textValue.length;
-        let parent = lastNode.dom.parentNode;
-        let i = 0;
-        while (i < textLength) {
-          let tempHtml = parent.innerHTML;
-          parent.innerHTML += textValue[i];
-          let lastSize = $lastparagraph.getSizeData();
-          if (lastSize.bottom > sizes.csize.bottom) {
-            parent.innerHTML = tempHtml;
-            break;
-          }
-  
-          i++;
-        }
-        let currentText = textValue.substring(i);
-        item.nodeValue = currentText;
-      } else {
-        console.log('%cerror:系统错误,请联系管理员!', 'color:red')
-        return  false;
-      }
-    } else {
-      let tempParagraph = currentParagh.cloneNode(false);
-      let $tempParagraph = $(tempParagraph);
-      $tempParagraph.insertAfter($lastparagraph);
-      let textValue = item.nodeValue;
-      let textLength = textValue.length;
-      let i = 0;
-      while (i < textLength) {
-        let tempHtml = tempParagraph.innerHTML;
-        tempParagraph.innerHTML += textValue[i];
-        let lastSize = $tempParagraph.getSizeData();
-        if (lastSize.bottom > sizes.csize.bottom) {
-          tempParagraph.innerHTML = tempHtml;
-          break;
-        }
-        i++;
-      }
-      let currentText = textValue.substring(i);
-      item.nodeValue = currentText;
-      if (isEmptyElement(tempParagraph)) {
-        $tempParagraph.remove();
-      }
-    }
-  },
-  /**
-   * funciton 处理回退div【不在框内】
-   * @param {Node} item 文字节点
-   * @param {Object} sizes 尺寸对象
-   * @param {DomElement} $lastparagraph 该栏最后一段
-   * @param {Boolean} isMerge 是否需要合并段落
-   */
-  handerNormalDiv: function ($item, sizes, $lastparagraph, isMerge) {
-    let currentParagh = getParentNodeByClass($item[0], 'js-paragraph-view');
-    if (currentParagh) {
-      this.currentParaghSize = $(currentParagh).getSizeData();
-    } else {
-      console.log('error');
-      return '';
-    }
-    let $prevDivSub = $item.clone(false);
-    $item.childNodes().forEach(node => {
-      if (node.nodeType === 3) {
-        this.handerText(node, sizes, isMerge, $lastparagraph, currentParagh);
-      } else if (node.tagName.toLowerCase() === 'br' || node.tagName.toLowerCase() === 'img') {
-        let brSize = $(node).getSizeData();
-        if (isMerge) {
-          if (brSize.height < sizes.diff) {
-            sizes.diff -= brSize.height;
-            $lastparagraph.append($(node));
-          } else {
-            return false;
-          }
-        } else {
-          let diff = brSize.bottom - this.currentParaghSize.top;
-          if (diff < sizes.diff) {
-            sizes.diff -= diff;
-            $lastparagraph.append($(node));
-          } else {
-            return false;
-          }
-        }
-      } else {
-        this._handerNormalDiv($(node), sizes, $prevDivSub, $lastparagraph, isMerge);
-        if (!isEmptyElement($prevDivSub[0])) {
-          if (isMerge) {
-            $lastparagraph[0].innerHTML += $prevDivSub.html();
-          } else {
-            $prevDivSub.insertAfter($lastparagraph);
-          }
-        }
-      }
-    })
-  },
-  /**
-   * function 计算字体是否可以回退【不在框内】
-   * @param {node} text 计算文字节点
-   * @param {object} sizes 参考尺寸
-   */
-  _checkoutNodeOut: function (text, sizes) {
-    let nextNode = text.nextSibling;
-    let parentNode = text.parentNode;
-    let span = document.createElement('span');
-    span.appendChild(text);
-    if (nextNode) {
-      parentNode.insertBefore(span, nextNode);
-    } else {
-      parentNode.appendChild(span);
-    }
-    let $span = $(span);
-    let currentParaghSize = this.currentParaghSize;
-    let textValue = text.nodeValue;
-    let length = textValue.length;
-    let i = length;
-    let prev = '';
-    while (i >= 0) {
-      let textTemp = textValue.substr(0, i--);
-      text.nodeValue = textTemp;
-      let textSize = $span.getSizeData();
-      let diff = textSize.bottom - currentParaghSize.top;
-      if (diff < sizes.diff) {
-        prev = textTemp;
-        span.outerHTML = textValue.substring(i);
-        sizes.diff -= diff;
-        break;
-      }
-    }
-    return prev;
-  },
-  /**
-   * function 插入上一段文字是否可以回退【不在框内】
-   * @param {node} lastNode 上一段最后一个节点
-   * @param {node} subNode 子节点
-   * @param {Object} sizes 尺寸对象
-   * @param {DomElement} $lastparagraph 该栏最后一段
-   */
-  _checkInsertText: function (lastNode, subNode, sizes, $lastparagraph) {
-    let textValue = subNode.nodeValue;
-    let length = textValue.length;
-    let i = 0;
-    while (i < length) {
-      let tempHtml = lastNode.innerHTML;
-      lastNode.innerHTML += textValue[i];
-      let size = $lastparagraph.getSizeData();
-      if (size.bottom > sizes.csize.bottom) {
-        lastNode.innerHTML = tempHtml;
-        break;
-      }
-      i++;
-    }
-    subNode.nodeValue = textValue.substring(i);
-  },
-  /**
-   * funciton 处理回退div【不在框内】
-   * @param {DomElement} $item 处理的DOM
-   * @param {Object} sizes 尺寸对象
-   * @param {DomElement} $prevDiv 该栏前移的div
-   * @param {DomElement} $lastparagraph 该栏最后一段
-   * @param {Boolean} isMerge 是否需要合并段落
-   */
-  _handerNormalDiv: function ($item, sizes, $prevDiv, $lastparagraph, isMerge) {
-    let $prevDivSub = $item.clone(false);
-    let currentParaghSize = this.currentParaghSize;
-    $item.childNodes().forEach(subNode => {
-      if (subNode.nodeType === 3) {
-        if (isMerge) {
-          let lastNode = this._getLastNodeByLastparagraphNormal($lastparagraph);
-          if (lastNode.type === 2) {
-            if (lastNode.dom.tagName === $item[0].tagName) {
-             this._checkInsertText(lastNode.dom, subNode, sizes, $lastparagraph);             
-            }
-          }
-        } else {
-          let prevText = this._checkoutNodeOut(subNode, sizes);
-          if (prevText.length > 0) {
-            let textNode = document.createTextNode(prevText);
-            $prevDivSub[0].appendChild(textNode);
-          }
-        }
-      } else if (subNode.tagName.toLowerCase() === 'br') {
-        let $br = $(subNode);
-        let brSize = $br.getSizeData();
-        let diff = brSize.bottom - currentParaghSize.top + 11;
-        if (diff < sizes.diff) {
-          $prevDivSub.append($br);
-          sizes.diff -= diff;
-        }
-      } else {
-        let $subnode = $(subNode);
-        let $prevTemp = $subnode.clone(false);
-        this._handerNormalDiv($subnode, sizes, $prevTemp, $lastparagraph, isMerge);
-        let html = $prevTemp.html();
-        if (html.length > 0) {
-          $prevDivSub.append($prevTemp);
-        }
-      }
-    });
-
-    let html2 = $prevDivSub.html();
-    if (html2.length > 0) {
-      $prevDiv.append($prevDivSub);
-    }
-
-    if (!$item.html().length) {
-      $item.remove();
-    }
-  },
-  /**
-   * funciton 处理回退框
-   * @param {DomElement} $border 题框
-   * @param {Object} sizes 尺寸对象
-   * @param {Boolean} isMerge 是否需要合并段落
-   * @param {DomElement} $lastparagraph 该栏最后一段
-   */
-  handerBorder: function ($border, sizes, isMerge, $lastparagraph) {
-    // border有10px的margin-bottom
-    sizes.diff -= 10;
-    let currentParagh = getParentNodeByClass($border[0], 'js-paragraph-view');
-    if (currentParagh) {
-      this.currentParaghSize = $(currentParagh).getSizeData();
-    } else {
-      console.log('error');
-      return '';
-    }
-    let borderType = parseInt($border.attr('data-type'));
-    this.borderType = borderType;
-    if (borderType === 1) {
-      // 客观题
-      this._handerObjectBorder($border, sizes, isMerge, $lastparagraph, currentParagh);
-    } else {
-      // 主观题
-      this._handerSubjectBorder($border, sizes, isMerge, $lastparagraph);
-    }
-  },
-  /**
-   * function 客观题获取最后一行
-   * @param {DomElement}  $lastparagraph
-   */
-  _getLastRowByLastparagraph ($lastparagraph) {
-    let border = $lastparagraph[0].lastChild;
-    if (border.className && border.className.indexOf('lsiten-question-object') > -1) {
-      let lastRow = border.lastChild;
-      if (lastRow.className && lastRow.className.indexOf('js-options-row') > -1) {
-        return lastRow;
-      } else {
-        return false;
-      }
-    } else {
-      return false;
-    }
-  },
-  /**
-   * funciton 处理主观题回退框
-   * @param {DomElement} $border 题框
-   * @param {Object} sizes 尺寸对象
-   * @param {Boolean} isMerge 是否需要合并段落
-   * @param {DomElement} $lastparagraph 该栏最后一段
-   * @param {Node} currentParagh 当前处理的段落
-   */
-  _handerObjectBorder: function ($border, sizes, isMerge, $lastparagraph, currentParagh) {
-    if (isMerge) {
-      let lastRow = this._getLastRowByLastparagraph($lastparagraph);
-      let $lastRow = $(lastRow);
-      let splitClass = '';
-      let splitColumn = [];
-      if (lastRow.className) {
-        let splits = lastRow.className.match(/js-split-row[0-9]+/);
-        if (splits) {
-          splitClass = splits[0];
-          splitColumn = $lastRow.childNodes();
-        }
-      }
-      if (lastRow) {
-        $border.childNodes().forEach(row => {
-          let $row = $(row);
-          let $rowTemp = $row.clone(false);
-          let columns = $row.childNodes();
-          let rowClass = $row.attr('class');
-          if (!columns.length) {
-            return false;
-          } else {
-            let $firstColumn = $(columns[0].firstChild);
-            $firstColumn.childNodes().forEach(question => {
-              let size = $(question).getSizeData();
-              if (splitClass.length > 0 && rowClass.indexOf(splitClass) > -1) {
-                if (size.height + 16 < sizes.diff) {
-                  let j = 0;
-                  splitColumn.forEach(column => {
-                    let questions = $(columns[j++].firstChild).childNodes();
-                    questions[0] && column.firstChild.appendChild(questions[0]);
-                  })
-
-                  sizes.diff -= size.height;
-                }
-              } else {
-                let diff = size.height + 24 + 16;
-                if (diff < sizes.diff) {
-                  let j = 0;
-                  let columnTemps = $rowTemp.find('.js-option-column');  
-                  columns.forEach(column => {
-                    let columnTemp = columnTemps[j];
-                    let columnContentTemp = null;
-                    if (!columnTemp) {
-                      columnTemp = column.cloneNode(false);
-                      columnContentTemp = column.firstChild.cloneNode(false);
-                      columnTemp.appendChild(columnContentTemp);
-                      $rowTemp[0].appendChild(columnTemp);
-                    } else {
-                      columnContentTemp = columnTemp.firstChild;
-                    }
-                    let questions = $(columns[j++].firstChild).childNodes();
-                    questions[0] && columnContentTemp.appendChild(questions[0]);
-                  })
-                  sizes.diff -= size.height;
-                }
-              }
-            })
-
-            if (isEmptyElement($firstColumn[0])) {
-              $row.remove();
-            }
-
-            if (!isEmptyElement($rowTemp[0])) {
-              $lastparagraph[0].firstChild.appendChild($rowTemp[0]);
-              $lastparagraph[0].firstChild.childNodes.length > 0 && $rowTemp.css('border-top', '1px solid #000');
-            }
-          }
-        })
-        if (isEmptyElement($border[0])) {
-          $border.remove();
-        }
-      } else {
-        console.log("%cerror", "color:red")
-      } 
-    } else {
-      let $borderTemp = $border.clone(false);
-      $border.childNodes().forEach(row => {
-        let $row = $(row);
-        let firstColumn = row.firstChild;
-        let $rowTemp = $row.clone(false);
-        let columns = $row.childNodes();
-        let $columnContent = $(firstColumn.firstChild);
-        $columnContent.childNodes().forEach(question => {
-          let  size = $(question).getSizeData();
-          let diff = size.height + 24 + 16;
-          if (diff < sizes.diff) {
-            let j = 0;
-            let columnTemps = $rowTemp.find('.js-option-column');  
-            columns.forEach(column => {
-              let columnTemp = columnTemps[j];
-              let columnContentTemp = null;
-              if (!columnTemp) {
-                columnTemp = column.cloneNode(false);
-                columnContentTemp = column.firstChild.cloneNode(false);
-                columnTemp.appendChild(columnContentTemp);
-                $rowTemp[0].appendChild(columnTemp);
-              } else {
-                columnContentTemp = columnTemp.firstChild;
-              }
-              let questions = $(columns[j++].firstChild).childNodes();
-              questions[0] && columnContentTemp.appendChild(questions[0]);
-            })
-            sizes.diff -= size.height;
-          }
-        })
-        if (!isEmptyElement($rowTemp[0])) {
-          $borderTemp.append($rowTemp);
-        }
-
-        if (isEmptyElement($columnContent[0])) {
-          $row.remove();
-        }
-      })
-
-      if (isEmptyElement($border[0])) {
-        $border.remove();
-      }
-
-      if (!isEmptyElement($borderTemp[0])) {
-        let tempParagraph = currentParagh.cloneNode(false);
-        tempParagraph.appendChild($borderTemp[0]);
-        $(tempParagraph).insertAfter($lastparagraph);
-        if (isEmptyElement(currentParagh)) {
-          $(tempParagraph).remove();
-        }
-      }
-    }
-  },
-  /**
-   * funciton 处理主观题回退框
-   * @param {DomElement} $border 题框
-   * @param {Object} sizes 尺寸对象
-   * @param {Boolean} isMerge 是否需要合并段落
-   * @param {DomElement} $lastparagraph 该栏最后一段
-   */
-  _handerSubjectBorder: function ($border, sizes, isMerge, $lastparagraph) {
-    let borderContent = $border[0].firstChild;
-    if (!borderContent || borderContent.nodeType !== 1) {
-      console.log("%cerror border", "color:red");   
-      return false;
-    } else {
-      let $borderContent = $(borderContent);
-      let $prevDiv = $borderContent.clone(false);  
-      $borderContent.childNodes().forEach(item => {
-        if (item.nodeType === 3) {
-          this._moveTxtToPrev(item, sizes, $prevDiv, $lastparagraph, isMerge);
-        } else {
-          let $item = $(item);
-          let className = $item.attr('class');
-          if (className && className.indexOf('js-lsiten-question') > -1) {
-            let type = parseInt($item.attr('data-type'));
-            switch (type) {
-              case 1:
-              case 2:
-              this._handerObjectContent($item, sizes, $prevDiv, $lastparagraph, isMerge);
-              break;
-              case 3:
-              this._handersubjectContent($item, sizes, $prevDiv, $lastparagraph, isMerge);
-              break;
-              case 4:
-              this._handerwrittingContent($item, sizes, $prevDiv, $lastparagraph, isMerge);
-              break;
-            }
-          } else {
-            this._handerNormal($item, sizes, $prevDiv, $lastparagraph, isMerge);
-          }
-        }
-      });
-      if (!isEmptyElement($prevDiv[0])) {
-        if (isMerge) {
-          let lastBorder = $lastparagraph[0].lastChild;
-          if (lastBorder) {
-            lastBorder.firstChild.innerHTML += $prevDiv[0].innerHTML;
-          }
-        } else {
-          let tempBorder = $border[0].cloneNode(false);
-          tempBorder.append($prevDiv[0]);
-          let tempPrevParagraph = $border[0].parentNode.cloneNode(false);
-          tempPrevParagraph.appendChild(tempBorder);
-          $lastparagraph[0].parentNode.appendChild(tempPrevParagraph);
-        }
-      }
-      if (isEmptyElement($borderContent[0])) {
-        $border.remove();
-      }
-    }
-  },
-  /**
-   * funciton 处理回退框内客观题
-   * @param {DomElement} $item 文字节点
-   * @param {Object} sizes 尺寸对象
-   * @param {DomElement} $prev 向前移动的容器
-   * @param {DomElement} $lastparagraph 该栏最后一段
-   * @param {Boolean} isMerge 是否需要合并段落
-   */
-  _handerObjectContent: function ($item, sizes, $prevDiv, $lastparagraph, isMerge) {
-    let itemSize = $item.getSizeData();
-    if (isMerge) {
-      let diff = itemSize.height;
-      if (sizes.diff > diff) {
-        sizes.diff -= diff;
-        let borderContent = this.getBorderContentByParagh($lastparagraph);
-        borderContent.appendChild($item[0]);
-      }
-    } else {
-      let diff = itemSize.bottom - this.currentParaghSize.top + 11;
-      if (sizes.diff > diff) {
-        sizes.diff -= diff;
-        $prevDiv.append($item);
-      }
-    }
-  },
-  /**
-   * funciton 处理回退框内主观题
-   * @param {DomElement} $item 文字节点
-   * @param {Object} sizes 尺寸对象
-   * @param {DomElement} $prev 向前移动的容器
-   * @param {DomElement} $lastparagraph 该栏最后一段
-   * @param {Boolean} isMerge 是否需要合并段落
-   */
-  _handersubjectContent: function ($item, sizes, $prevDiv, $lastparagraph, isMerge) {
-    let $prevDivSub = $item.clone(false);
-    $item.childNodes().forEach(node => {
-      if (node.nodeType === 3) {
-        this._moveTxtToPrev(node, sizes, $prevDivSub, $lastparagraph, isMerge);
-      } else {
-        this._handerNormal($(node), sizes, $prevDivSub, $lastparagraph, isMerge);
-
-        if (node.innerHTML === '') {
-          $(node).remove();
-        }
-      }
-
-    })
-    // console.log($prevDivSub[0], isMerge, sizes);
-    if(!isEmptyElement($prevDivSub[0])) {
-      let lastRow = this.getLastQestionByParagh($lastparagraph);
-      if (lastRow) {
-        if (isMerge) {
-          let type = parseInt(lastRow.getAttribute('data-type'));
-          if (type === 3) {   
-            if (lastRow.className && lastRow.className.indexOf('js-split-problem') > -1) {
-              lastRow.innerHTML += $prevDivSub.html();
-            } else {
-              $prevDiv.append($prevDivSub);
-            }
-          } 
-        } else {
-          $prevDiv.append($prevDivSub);
-        }
-      } else {
-        $prevDiv.append($prevDivSub);
-      }
-
-      if (isEmptyElement($item[0])) {
-        $(lastRow).removeClass('js-split-problem');
-        $item.remove();
-      }
-    }
-  },
-  /**
-   * function 获取最后一段最后一个div
-   * @param {DomElement} $lastparagraph 该栏最后一段
-   */
-  getLastQestionByParagh: function ($lastparagraph) {
-    let lastChild = $lastparagraph[0].lastChild;
-    if (lastChild.className && lastChild.className.indexOf('js-lsiten-border') > -1) {
-      let borderType = parseInt(lastChild.getAttribute('data-type'));
-      if (borderType === this.borderType) {
-        if (borderType === 2) {
-          // 主观题
-          let content = lastChild.lastChild;
-          if (content.className && content.className.indexOf('border-content') > -1) {
-            let lastcontent = content.lastChild;
-            if (lastcontent.className && lastcontent.className.indexOf('js-lsiten-question') > -1) {
-              return lastcontent;
-            }
-          }
-        } else {
-          // 客观题
-        }
-      }
-    }
-    return null;
-  },
-  /**
-   * funciton 处理回退框内作文题
-   * @param {DomElement} $item 文字节点
-   * @param {Object} sizes 尺寸对象
-   * @param {DomElement} $prev 向前移动的容器
-   * @param {DomElement} $lastparagraph 该栏最后一段
-   * @param {Boolean} isMerge 是否需要合并段落
-   */
-  _handerwrittingContent: function ($item, sizes, $prevDiv, $lastparagraph, isMerge) {
-    let $questionDivSub = $item.clone(false);
-    $item.childNodes().forEach(nodeItem => {
-      if (nodeItem.nodeType === 3) {
-        nodeItem.parentNode.removeChild(nodeItem);
-      } else {
-        let $nodeItem = $(nodeItem);
-        let className = $nodeItem.attr('class');
-        if (className && className.indexOf('lsiten-title') > -1) {
-          // 作文题目区
-          let $prevDivSub = $nodeItem.clone(false);      
-          $nodeItem.childNodes().forEach(node => {
-            if (node.nodeType === 3) {
-              this._moveTxtToPrev(node, sizes, $prevDivSub, $lastparagraph, isMerge);
-            } else {
-              this._handerNormal($(node), sizes, $prevDivSub, $lastparagraph, isMerge);
-            }
-          })
-    
-          if (!isEmptyElement($prevDivSub[0])) {
-            $questionDivSub.append($prevDivSub);
-          }
-        } else if (className && className.indexOf('lsiten-border-box') > -1) {
-          let $prevDivSub = $nodeItem.clone(false);   
-          let lastRow = null;
-          if (isMerge) {
-            lastRow = this.getLastContentByParagh($lastparagraph);
-          }
-          if (lastRow) {
-            // 作文网格区
-            $nodeItem.childNodes().forEach(row => {
-              let rsize = $(row).getSizeData();
-              if (this._checkIsNeedPrev(rsize, sizes, isMerge)) {
-                if (isMerge) {
-                  if (lastRow.className && lastRow.className.indexOf('lsiten-title') > -1) {
-                    let $prevDivSubTemp = $nodeItem.clone(false);   
-                    $prevDivSubTemp.append($(row));
-                    lastRow.parentNode.appendChild($prevDivSubTemp[0]);
-                  } else {
-                    lastRow.parentNode.appendChild(row);
-                  }
-                } else {
-                  $prevDivSub.append($(row));
-                }
-              }
-            })
-          }
-    
-          if (!isEmptyElement($prevDivSub[0])) {
-            $questionDivSub.append($prevDivSub);
-          }
-
-        }
-        if (isEmptyElement($nodeItem[0])) {
-          $nodeItem.remove();
-        }
-      }
-    })
-
-    if (!isEmptyElement($questionDivSub[0])) {
-      $prevDiv.append($questionDivSub);
-    }
-
-    if (isEmptyElement($item[0])) {
-      $item.remove();
-    }
-  },
-  /**
-   * function 判断是否可以回退回去
-   * @param {Object} osize 需要判断的尺寸
-   * @param {Object} sizes 参考的尺寸
-   * @param {Boolean} isMerge 是否合并段落
-   */
-  _checkIsNeedPrev: function (osize, sizes, isMerge) {
-    if (isMerge) {
-      if (osize.height < sizes.diff) {
-        sizes.diff -= osize.height;
-        return true;
-      } else {
-        return false;
-      }
-    } else {
-      let diff = osize.bottom - sizes.csize.bottom;
-      if (diff < sizes.diff) {
-        sizes.diff -= diff;
-        return true;
-      } else {
-        return false;
-      }
-    }
-  },
-  /**
-   * @param {node} item 需要处理的节点
-   * @param {object} sizes 参考的尺寸
-   * @param {DomElement} $prevDivSub 上移的div容器
-   * @param {DomElement} $lastparagraph 上一栏最后一段
-   */
-  _checkNormalText: function (item, sizes, $prevDivSub, $lastparagraph) {
-    let nextNode = item.nextSibling;
-    let parentNode = item.parentNode;
-    let span = document.createElement('span');
-    span.appendChild(item);
-    if (nextNode) {
-      parentNode.insertBefore(span, nextNode);
-    } else {
-      parentNode.appendChild(span);
-    }
-    let $span =  $(span);
-    if (!this.currentParaghSize.top) {
-      $span[0].outerHTML = $span[0].innerHTML;
-      return false;
-    }
-    let textValue = item.nodeValue;
-    let length = textValue.length;
-    let i = length;
-    while (i > 0) {
-      let textTemp = textValue.substring(0, length);
-      item.nodeValue = textTemp;
-      let textSize = $span.getSizeData();
-      let diff = textSize.bottom - this.currentParaghSize.top + 11;
-      if (diff < sizes.diff) {
-        $prevDivSub[0].appendChild(document.createTextNode(textTemp));
-        break;
-      }
-      i--;
-    }
-    item.nodeValue = textValue.substring(i);
-    $span[0].outerHTML = $span[0].innerHTML;
-  },
-  /**
-   * funciton 处理回退框内普通文字
-   * @param {DomElement} $item 文字节点
-   * @param {Object} sizes 尺寸对象
-   * @param {DomElement} $prev 向前移动的容器
-   * @param {DomElement} $lastparagraph 该栏最后一段
-   * @param {Boolean} isMerge 是否需要合并段落
-   */
-  _handerNormal: function ($item, sizes, $prevDiv, $lastparagraph, isMerge) {
-    let $prevDivSub = $item.clone(false);
-    $item.childNodes().forEach(node => {
-      if (node.nodeType === 3) {
-        if (isMerge) {
-          this._moveTxtToPrev(node, sizes, $prevDivSub, $lastparagraph, isMerge);
-        } else {
-          this._checkNormalText(node, sizes, $prevDivSub, $lastparagraph);
-        }
-      } else if (node.tagName.toLowerCase() === 'br' || node.tagName.toLowerCase() === 'img') {
-        let brSize = $(node).getSizeData();
-        if (isMerge) {
-          let diff = brSize.bottom - this.currentParaghSize.top - 11;
-          if (diff < sizes.diff) {
-            sizes.diff -= brSize.height;
-            $prevDivSub.append($(node));
-          } else {
-             return false;
-          }
-        } else {
-          let diff = brSize.bottom - this.currentParaghSize.top + 11;
-          if (diff < sizes.diff) {
-            sizes.diff -= diff;
-            $prevDivSub.append($(node));
-          } else {
-            return false;
-          }
-        }
-      } else {
-        this._handerNormal($(node), sizes, $prevDivSub, $lastparagraph, isMerge);
-      }
-    })
-    if($prevDivSub[0].innerHTML !== '') {
-      $prevDiv.append($prevDivSub);
-    }
-  },
-  /**
-   * function 获取最后一段最后一个问题
-   * @param {DomElement} $lastparagraph 该栏最后一段
-   */
-  getBorderContentByParagh: function ($lastparagraph) {
-    let lastChild = $lastparagraph[0].lastChild;
-    if (lastChild.className && lastChild.className.indexOf('js-lsiten-border') > -1) {
-      let borderType = parseInt(lastChild.getAttribute('data-type'));
-      if (borderType === this.borderType) {
-        if (borderType === 2) {
-          // 主观题
-          let content = lastChild.lastChild;
-          if (content.className && content.className.indexOf('border-content') > -1) {
-            return content;
-          } else {
-            return null;
-          }
-        } else {
-          // 客观题
-        }
-      }
-    }
-    return null;
-  },
-  /**
-   * function 获取最后一段最后一个div
-   * @param {DomElement} $lastparagraph 该栏最后一段
-   */
-  getLastContentByParagh: function ($lastparagraph) {
-    let lastChild = $lastparagraph[0].lastChild;
-    if (lastChild.className && lastChild.className.indexOf('js-lsiten-border') > -1) {
-      let borderType = parseInt(lastChild.getAttribute('data-type'));
-      if (borderType === this.borderType) {
-        if (borderType === 2) {
-          // 主观题
-          let content = lastChild.lastChild;
-          if (content.className && content.className.indexOf('border-content') > -1) {
-            let lastcontent = content.lastChild;
-            let type = parseInt(lastcontent.getAttribute('data-type'));
-            if (type === 3) {
-              return lastcontent.lastChild;
-            } else if (type === 4) {
-              lastcontent = lastcontent.lastChild;
-              if (lastcontent.className && lastcontent.className.indexOf('lsiten-title') > -1) {
-                return lastcontent;
-              } else if (lastcontent.className && lastcontent.className.indexOf('lsiten-border-box') > -1) {
-                return lastcontent.lastChild;
-              }
-            }
-          }
-        } else {
-          // 客观题
-        }
-      }
-    } else {
-      return lastChild;
-    }
-  },
-    /**
-   * funciton 处理回退框 仅仅适合客观题
-   * @param {Node} item 文字节点
-   * @param {Object} sizes 尺寸对象
-   * @param {DomElement} $prev 向前移动的容器
-   * @param {DomElement} $lastparagraph 该栏最后一段
-   * @param {Boolean} isMerge 是否需要合并段落
-   * */
-  _moveTxtToPrev (item, sizes, $prev, $lastparagraph, isMerge) {
-    let nextNode = item.nextSibling;
-    let parentNode = item.parentNode;
-    let span = document.createElement('span');
-    span.appendChild(item);
-    if (nextNode) {
-      parentNode.insertBefore(span, nextNode);
-    } else {
-      parentNode.appendChild(span);
-    }
-    let $span =  $(span);
-    let textSize = $span.getSizeData();
-    if (!this.currentParaghSize.top) {
-      $span[0].outerHTML = $span[0].innerHTML;
-      return false;
-    }
-    let diff = textSize.bottom - this.currentParaghSize.top + 11;
-    if (diff) {
-      if (sizes.diff - diff > 0) {
-        $prev[0].appendChild(item);
-        $span.remove();
-      } else {
-        if (isMerge) {
-          let lastContent = this.getLastContentByParagh($lastparagraph);
-          if (lastContent.className && lastContent.className.indexOf('js-split-item') > -1) {
-            let content = lastContent.lastChild;
-            if (content.nodeType === 3) {
-              let prevText = this._textInsertTest(content, item, $lastparagraph, sizes);
-              $prev[0].innerHTML += prevText;
-            }
-          } else if ( lastContent.className && lastContent.className.indexOf('js-split-writting-title') > -1) {
-            let content = lastContent.lastChild;
-            if (content.nodeType === 3) {
-              let prevText = this._textInsertTest(content, item, $lastparagraph, sizes);
-              $prev[0].innerHTML += prevText;
-            }
-          } else {
-           diff = textSize.bottom - this.currentParaghSize.top - 11;
-           if ( diff < sizes.diff ) {
-            sizes.diff -= textSize.height;
-            $prev[0].appendChild(item);
-           }
-          }
-        }
-        $span[0].outerHTML = $span[0].innerHTML;
-      }
-    } else {
-      $span[0].outerHTML = $span[0].innerHTML;
-      console.log('error');
-    }
-  },
-  /**
-   * 
-   * @param {node} content 需要插入测试的文本节点node
-   * @param {node} insert  被插入测试的文本节点node
-   * @param {DomElement} $lastparagraph 该栏最后一段
-   * @param {Object} sizes 尺寸对象
-   */
-  _textInsertTest (content, insert, $lastparagraph, sizes) {
-    let nextNode = content.nextSibling;
-    let parentNode = content.parentNode;
-    let span = document.createElement('span');
-    span.appendChild(content);
-    if (nextNode) {
-      parentNode.insertBefore(span, nextNode);
-    } else {
-      parentNode.appendChild(span);
-    }
-
-    let textValue = insert.nodeValue;
-    let length = textValue.length;
-    let columnSize = sizes.csize;
-    let prev = '';
-    let i = 0;
-    for ( ;i < length; i++) {
-      let tempHtml = span.innerHTML;
-      span.innerHTML += textValue[i];
-      let psize = $lastparagraph.getSizeData();
-      if (psize.bottom > columnSize.bottom) {
-        span.outerHTML = tempHtml;
-        break;
-      } else {
-        prev += textValue[i];
-      }
-    }
-    insert.nodeValue = textValue.substring(i);
-    return prev;
-  }
-}
-export default questionPrevUtils;

+ 483 - 59
src/js/yzPage.js

@@ -1,7 +1,6 @@
 import $ from './util/dom-core.js';
 import {getParentByClassname, getParentNodeByClass, isEmptyElement, trim} from './util/util.js';
 import QuestionTools from './util/question';
-import QuestionPrevTools from './util/questionPrev';
 import JQuery from 'jquery';
 let yzPage = function (manage, $page) {
   this.manage = manage;
@@ -23,10 +22,9 @@ yzPage.prototype = {
   _init: function () {
     this._initStyles();
     this._initColumn();
-    // this._initPositionPlaceholder();
-    // this._initPAndNPlaceholder();
+    this._initPositionPlaceholder();
+    this._initPAndNPlaceholder();
     this.questionTools = new QuestionTools();
-    this.questionPrevTools = new QuestionPrevTools();
   },
   /**
    * function 初始化样式
@@ -119,10 +117,21 @@ yzPage.prototype = {
     $postionDom.css('height', '16px');
     return $postionDom;
   },
-  // 生成正反面定位符
+  /**
+   * function 生成正反面定位符
+   */
   _initPAndNPlaceholder: function () {
+    let count = this.pageIndex + 2;
+    for (let i = 0; i < count; i++) {
+      let template = this._placeholderTemplate();
+      let top = i * 22 - 16;
+      template.css('width', '25px').css('height', '16px').css('left', '-25px').css('top', top + 'px');
+      this.$textElem.append(template);
+    }
   },
-  // 初始化占位符
+  /**
+   * function 初始化占位符
+   */
   _initPositionPlaceholder: function () {
     let $columns = this.$colums;
     let clength = $columns.length;
@@ -151,10 +160,88 @@ yzPage.prototype = {
       }
     }
   },
-  // 更新跨栏符
+  /**
+   * function 更新跨栏符
+   */
   _updateCrossColumn: function ($column) {
+    let index =parseInt($column.attr('data-column-i'));
+    if (index === 0) {
+      return '';
+    }
+    let column = $column[0];
+    let firstParagh = column.firstChild;
+    firstParagh = this._cycleFindParagraph(firstParagh);
+    if (firstParagh) {
+      let page = column.parentNode;
+      if (page) {
+        let $page = $(page);
+        let columnIndex = parseInt(column.getAttribute('data-column-i'));
+        let crossPlaceholder = '.js-cross-placeholder-' + columnIndex;
+        let classname = 'js-cross-placeholder-' + columnIndex;
+        let $placeholder = $page.find(crossPlaceholder);
+        let firstBorder = firstParagh.firstChild;
+        if (firstParagh.className && firstParagh.className.indexOf('js-split-paragraph') > -1) {
+          if (firstBorder && firstBorder.className && firstBorder.className.indexOf('js-lsiten-border') > -1) {
+            if (!$placeholder.length) {
+              // 添加跨栏符
+              // 判断是否有该栏跨栏符
+              let $template = this._placeholderTemplate();
+              let cwidth = this._pagesWidth[this.editor.pageSize] / parseInt(this.editor.columnNumber);
+              let left = columnIndex * cwidth + 20;
+              $template.css('left', left + 'px').css('top', '-15px').css('width', '12px').css('height', '12px');
+              $template.addClass(classname);
+              $page.append($template);
+            }
+          }
+        } else {
+          if (firstBorder && firstBorder.className && firstBorder.className.indexOf('js-lsiten-border') > -1) {
+            if ($placeholder.length > 0) {
+              // 删除跨栏符
+              $placeholder.remove();
+            }
+          }
+        }
+      }
+    } else {
+      let page = column.parentNode;
+      if (page) {
+        let $page = $(page);
+        let columnIndex = parseInt(column.getAttribute('data-column-i'));
+        let crossPlaceholder = '.js-cross-placeholder-' + columnIndex;
+        let classname = 'js-cross-placeholder-' + columnIndex;
+        let $placeholder = $page.find(crossPlaceholder);
+        if ($placeholder.length > 0) {
+          // 删除跨栏符
+          $placeholder.remove();
+        }
+      }
+    }
   },
   
+  /**
+   * function 循环查找段落 
+   */
+  _cycleFindParagraph: function (paragraph) {
+    if (!paragraph) {
+      return null;
+    }
+    let i = 0;
+    while (i < 100 && (!paragraph.className || paragraph.className.indexOf('js-paragraph-view') < 0)) {
+      let temp = paragraph.nextSibling;
+      paragraph.parentNode.removeChild(paragraph);
+      paragraph = temp;
+       i++;
+    }
+
+
+    let firstContent = paragraph.firstChild;
+    if (firstContent && firstContent.className && firstContent.className.indexOf('js-answer-header') > -1) {
+      let temp = paragraph.nextSibling;
+      paragraph = temp; 
+      return this._cycleFindParagraph(paragraph);
+    }
+     return paragraph || null;
+  },
   /** 
    * @param {DomElement} $column 栏dom
   */
@@ -162,9 +249,6 @@ yzPage.prototype = {
     let $paragraphs = $column.find('.js-paragraph-view');
       let cSize = $column.getSizeData();
       let i = $paragraphs.length - 1;
-      if(!i || i < 0) {
-        return false;
-      }
       let $moves = [];
       while (i >=0 ) {
         let $paragraph = $($paragraphs[i]);
@@ -192,7 +276,11 @@ yzPage.prototype = {
             }
           }
           nextColumn.page._checkColumnOut(nextColumn.column);
+          // 跨栏符检测
+          this._updateCrossColumn(nextColumn.column);          
         }
+        // 跨栏符检测
+        this._updateCrossColumn($column);
       }
   },
   /**
@@ -235,7 +323,7 @@ yzPage.prototype = {
       this._insertDomBeforeByFirst($Allparagraph, firstFirst);
     } else if (insertLast.className && insertLast.className.indexOf('js-lsiten-border') > -1) {
       // 框处理
-      if (firstFirst.className && firstFirst.className.indexOf('js-lsiten-border') > -1) {
+      if (firstFirst && firstFirst.className && firstFirst.className.indexOf('js-lsiten-border') > -1) {
         // 如果下一段第一个dom是框
         let borderId = insertLast.getAttribute('data-id');
         let nextBorderId = firstFirst.getAttribute('data-id');
@@ -360,12 +448,16 @@ yzPage.prototype = {
     $columns.forEach($column => {
       this._checkColumnBack($column);   
     });
+    this.manage._checkRemovePage();
   },
   /**
    * function 检测该段落是否为答题卡头
    * @param {DomElement} 需要检测的段落
    */
   isHeadParagraph: function ($paragraph) {
+    if (!$paragraph || !$paragraph[0]) {
+      return false;
+    }
     let firstChild = $paragraph[0].firstChild;
     return firstChild && firstChild.className && firstChild.className.indexOf('js-answer-header') > -1;
   },
@@ -377,10 +469,10 @@ yzPage.prototype = {
     let $paragraphs = $column.find('.js-paragraph-view');
     let cSize = $column.getSizeData();
     let i = $paragraphs.length - 1;
-    if(!i || i < 0) {
+    let $moves = [];
+    if (!$paragraphs[i]) {
       return false;
     }
-    let $moves = [];
     let $lastParagraph = $($paragraphs[i]);
     let lastSize = $lastParagraph.getSizeData();
     let diff = cSize.bottom - lastSize.bottom;
@@ -390,17 +482,129 @@ yzPage.prototype = {
       if (nextColumn.column) {
         let $nextParagraphs = nextColumn.column.find('.js-paragraph-view');
         while (i >=0 ) {
-          let $paragraph = $($nextParagraphs[i]);
-          if (!this.isHeadParagraph($paragraph)) {
-            let pSize = $paragraph.getSizeData();
-            if (pSize.height < diff) {
-              $moves.push($paragraph);
-            } else {
-              this._prevParagraph($moves, $paragraph, diff, $lastParagraph);
+          if ($nextParagraphs[i]) {
+            let $paragraph = $($nextParagraphs[i]);
+            if ($paragraph && !this.isHeadParagraph($paragraph)) {
+              let pSize = $paragraph.getSizeData();
+              if (pSize.height < diff) {
+                $moves.push($paragraph);
+              } else {
+                let size = {
+                  diff: diff
+                }
+                this._prevParagraph($moves, $paragraph, size, $lastParagraph);
+              }
             }
           }
           i--;
         }
+        let lastPid = $lastParagraph.attr('data-index');
+        let lastBorderItem = $lastParagraph[0].lastChild;
+        let lastIsBorder = lastBorderItem.className && lastBorderItem.className.indexOf('js-lsiten-border') > -1;
+        $moves.forEach($paragraphITem => {
+          let currentPid = $paragraphITem.attr('data-index');
+          if (lastPid === currentPid) {
+            $paragraphITem.childNodes().forEach(item => {
+              if (item.nodeType === 3) {
+                $paragraph[0].removeChild(item);
+              } else {
+                let $item = $(item);
+                let itemClass = $item.attr('class');
+                if (itemClass && itemClass.indexOf('js-lsiten-line') > -1) {
+                  // 普通行
+                  $lastParagraph.append($item);
+                } else if (itemClass && itemClass.indexOf('js-lsiten-border') > -1) {
+                  // 框处理
+                  if (lastIsBorder) {
+                    let borderId = lastBorderItem.getAttribute('data-id');
+                    let currentBorderId = $item.attr('data-id');
+
+                    if (currentBorderId === borderId) {
+                      $($item[0].firstChild).childNodes().forEach(borderItem => {
+                        if (borderItem.className) {
+                          if (borderItem.className.indexOf('js-lsiten-question') > -1) {
+                            let qustionType = parseInt(borderItem.getAttribute('data-type'));
+                            let lastQuestionItem = lastBorderItem.lastChild;
+                            let lastQuestionId = lastQuestionItem.getAttribute('data-id');
+                            let currentQuestionId = borderItem.getAttribute('data-id');
+                            if (lastQuestionId && currentQuestionId && currentQuestionId === lastQuestionId) {
+                              if (qustionType === 3) {
+                                $(borderItem).childNodes().forEach(subjectItem => {
+                                  lastQuestionItem.appendChild(subjectItem);
+                                })
+
+                              } else if (qustionType === 4) {
+                                $(borderItem).childNodes().forEach(writtingItem => {
+                                  if (writtingItem.className.indexOf('lsiten-title') > -1) {
+                                    let $prevWrittingTitleItem = $(lastQuestionItem).find('.lsiten-title');
+                                    let needMergeItem = true;
+                                    if (!$prevWrittingTitleItem.length) {
+                                      needMergeItem = false;
+                                      $prevWrittingTitleItem = $($prevWrittingTitleItem);
+                                    } else {
+                                      needMergeItem = false;
+                                      $prevWrittingTitleItem = $(writtingItem).clone(false);
+                                    }
+                                    $(writtingItem).childNodes().forEach(writtingTitleItem => {
+                                      $prevWrittingTitleItem[0].appendChild(writtingTitleItem);
+                                    })
+
+                                    if (needMergeItem && !isEmptyElement($prevWrittingTitleItem[0])) {
+                                      lastQuestionItem.appendChild($prevWrittingTitleItem[0]);
+                                    }
+                                  } else if (writtingItem.className.indexOf('lsiten-border-box') > -1) {
+                                    let $prevWrittingRowsItem = $(lastQuestionItem).find('.lsiten-border-box');
+                                    let needMergeItem = true;
+                                    if (!$prevWrittingRowsItem.length) {
+                                      needMergeItem = false;
+                                      $prevWrittingRowsItem = $($prevWrittingRowsItem);
+                                    } else {
+                                      needMergeItem = false;
+                                      $prevWrittingRowsItem = $(writtingItem).clone(false);
+                                    }
+                                    $(writtingItem).childNodes().forEach(writtingRowItem => {
+                                      $prevWrittingRowsItem[0].appendChild(writtingRowItem);
+                                    })
+
+                                    if (needMergeItem && !isEmptyElement($prevWrittingRowsItem[0])) {
+                                      lastQuestionItem.appendChild($prevWrittingRowsItem[0]);
+                                    }
+                                  }
+                                })
+                              } else {
+                                $(borderItem).remove();
+                              }
+                              $(borderItem).remove();
+                            } else {
+                              lastBorderItem.firstChild.appendChild(borderItem);
+                            }
+                          } else if (borderItem.className.indexOf('js-lsiten-line') > -1) {
+                            lastBorderItem.firstChild.appendChild(borderItem);
+                          } else {
+                            $(borderItem).remove();
+                          }
+                        }
+                      })
+                      $item.remove();
+                    } else {
+                      $lastParagraph.append($item);                      
+                    }
+                  } else {
+                    $lastParagraph.append($item);
+                  }
+                } else {
+                  $item.remove();
+                }
+              }
+            })
+            $paragraphITem.remove();
+          } else {
+            $paragraphITem.insertAfter($lastParagraph);
+          }
+        })
+        // 跨栏符检测
+        this._updateCrossColumn($column);
+        this._updateCrossColumn(nextColumn.column);
       }
     }
   },
@@ -409,10 +613,10 @@ yzPage.prototype = {
    * function 检测段落是否需要上移
    * @param {array} $moves 需要移动的段落数组
    * @param {DomElement} $paragraph  需要检测的段落
-   * @param {number} diff  可以上移的空间
+   * @param {object} size  可以上移的空间
    * @param {DomElement} $lastParagraph  该段最后一段
    */
-  _prevParagraph: function ($moves, $paragraph, diff, $lastParagraph) {
+  _prevParagraph: function ($moves, $paragraph, size, $lastParagraph) {
     let lastClass = $lastParagraph.attr('class');
     let currentClass = $paragraph.attr('class');
     let lastPid = $lastParagraph.attr('data-index');
@@ -420,7 +624,7 @@ yzPage.prototype = {
     if (lastClass && lastClass.indexOf('js-split-paragraph') > -1) {
       if (currentClass && currentClass.indexOf('js-split-paragraph') > -1 && lastPid === currentPid) {
         // 如果两个段落需要合并
-        let $prevParagraph = $paragraph.clone(false);
+        let $prevParagraph = $lastParagraph;
         $paragraph.childNodes().forEach(item => {
           if (item.nodeType === 3) {
             $paragraph[0].removeChild(item);
@@ -430,68 +634,175 @@ yzPage.prototype = {
             if (itemClass && itemClass.indexOf('js-lsiten-line') > -1) {
               // 普通行
               let height = $item.getSizeData().height;
-              if (height < diff) {
+              if (height < size.diff) {
+                $prevParagraph.append($item);
+                size.diff -= height;
+              }
+            } else if (itemClass && itemClass.indexOf('js-lsiten-border') > -1) {
+              // 框处理
+              this._prevBorder($prevParagraph, size, $item, $lastParagraph, true);
+            } else {
+              $item.remove();
+            }
+          }
+        })
+      } else {
+        // 两段落不需要合并
+        let $prevParagraph = $paragraph.clone(false);
+        $paragraph.childNodes().forEach(item => {
+          if (item.nodeType === 3) {
+            $paragraph[0].removeChild(item);
+          } else {
+            let $item = $(item);
+            let itemClass = $item.attr('class');
+            if (itemClass && itemClass.indexOf('js-lsiten-line') > -1) {
+              let height = $item.getSizeData().height;
+              if (height < size.diff) {
                 $prevParagraph.append($item);
-                diff -= height;
+                size.diff -= height;
               }
             } else if (itemClass && itemClass.indexOf('js-lsiten-border') > -1) {
               // 框处理
-              this._prevBorder($prevParagraph, diff, $item, $lastParagraph);
+              this._prevBorder($prevParagraph, size, $item, $lastParagraph, false);
             } else {
               $item.remove();
             }
           }
         })
+
         if (!isEmptyElement($prevParagraph[0])) {
           $moves.push($prevParagraph);
         }
-      } else {
-
       }
     } else {
+      // 两段落不需要合并
+      let $prevParagraph = $paragraph.clone(false);
+      $paragraph.childNodes().forEach(item => {
+        if (item.nodeType === 3) {
+          $paragraph[0].removeChild(item);
+        } else {
+          let $item = $(item);
+          let itemClass = $item.attr('class');
+          if (itemClass && itemClass.indexOf('js-lsiten-line') > -1) {
+            let height = $item.getSizeData().height;
+            if (height < size.diff) {
+              $prevParagraph.append($item);
+              size.diff -= height;
+            }
+          } else if (itemClass && itemClass.indexOf('js-lsiten-border') > -1) {
+            // 框处理
+            this._prevBorder($prevParagraph, size, $item, $lastParagraph, false);
+          } else {
+            $item.remove();
+          }
+        }
+      })
 
+      if (!isEmptyElement($prevParagraph[0])) {
+        $moves.push($prevParagraph);
+      }
     }
   },
 
   /**
    * function 处理需要上移的框
    * @param {DomElement} $prevParagraph 前移段落暂存
-   * @param {number} diff 可移空间
+   * @param {object} size 可移空间
    * @param {DomElement} $border 需要处理的框
    * @param {DomElement} $lastParagraph 最后一段dom
+   * @param {boolean} 是否需要合并
    */
-  _prevBorder: function ($prevParagraph, diff, $border, $lastParagraph) {
+  _prevBorder: function ($prevParagraph, size, $border, $lastParagraph, isMerge) {   
     let lastCentent = $lastParagraph[0].lastChild;
     if (lastCentent.className && lastCentent.className.indexOf('js-lsiten-border') > -1) {
       let prevBid = $border.attr('data-id');
       let bid = $(lastCentent).attr('data-id');
       if (prevBid === bid) {
         let $prevBorder = $border.clone(false);
-        this._prevQuestions($prevBorder, diff, $border, $(lastCentent));
+        let $prevBorderContent = $($border[0].firstChild).clone(false);
+        $prevBorder.append($prevBorderContent);
+        if (isMerge) {
+          $prevBorder = $($prevParagraph[0].lastChild);
+          $prevBorderContent = $prevBorder[0].firstChild;
+        }
+        this._prevQuestions($prevBorder, size, $border, $(lastCentent));
+        if (!isMerge && !isEmptyElement($prevBorderContent[0])) {
+          $prevParagraph.append($prevBorder);
+        }
       } else {
         let borderSize= $border.getSizeData();
-        if (borderSize.height < diff) {
+        if (borderSize.height < size.diff) {
           $prevParagraph.append($border);
-          diff -= borderSize.height;          
+          size.diff -= borderSize.height;          
+        } else {
+          // 框的padding+margin+border
+          let borderOutSize = 22;
+          let $prevBorder = $border.clone(false);
+          let $borderContent = $($border[0].firstChild);
+          let $prevBorderContent = $borderContent.clone(false);
+          $prevBorder.append($prevBorderContent);
+          size.diff -= borderOutSize;
+          $borderContent.childNodes().forEach(item => {
+            if (item.className) {
+              if (item.className.indexOf('js-lsiten-question') > -1) {
+                let qustionType = parseInt(item.getAttribute('data-type'));
+                if (qustionType === 3) {
+                  this._prevSubjectQuestion($prevSubject, size, $(item), false);
+                  if (!isEmptyElement($prevSubject)) {
+                    $prevBorderContent.appendChild($prevSubject[0]);
+                  }
+                } else if (qustionType === 4) {
+                  let $prevWritting = $(item).clone(false);                 
+                  this._prevWrittingQuestion($prevWritting, size, $(item), false);
+                  if (!isEmptyElement($prevWritting)) {
+                    $prevBorderContent.appendChild($prevWritting[0]);
+                  }
+                } else {
+                  let height = $(item).getSizeData().height;
+                  if (height < size.diff) {
+                    $prevBorderContent.appendChild(item);
+                    size.diff -= height;
+                  }
+                }
+              } else if (item.className.indexOf('js-lsiten-line') > -1) {
+                let height = $(item).getSizeData().height;
+                if (height < size.diff) {
+                  $prevBorderContent.appendChild(item);
+                  size.diff -= height;
+                }
+              } else {
+                $(item).remove();
+              }
+            } else {
+              item && item.parentNode.removeChild(item);
+            }
+          })
+          size.diff += borderOutSize;
+          if (!isEmptyElement($prevBorderContent[0])) {
+            $prevParagraph.append($prevBorder);
+          }
         }
       }
     } else {
       let borderSize= $border.getSizeData();
-      if (borderSize.height < diff) {
+      if (borderSize.height < size.diff) {
         $prevParagraph.append($border);
-        diff -= borderSize.height;
+        size.diff -= borderSize.height;
       }
     }
+    if (isEmptyElement($border[0].firstChild)) {
+      $border.remove();
+    }
   },
   /**
    * function 前移问题
    * @param {DomElement} $prevBorder 前移框暂存
-   * @param {number} diff 前移段落暂存
+   * @param {object} size 前移段落暂存
    * @param {DomElement} $border 前移段落暂存
    * @param {DomElement} $lastBorder 前段落最后一个框
    * 
    */
-  _prevQuestions: function ($prevBorder, diff, $border, $lastBorder) {
+  _prevQuestions: function ($prevBorder, size, $border, $lastBorder) {
     let lastQuestion = $lastBorder[0].firstChild.lastChild;
     let $borderContent = $($border[0].firstChild);
     $borderContent.childNodes().forEach(item => {
@@ -500,33 +811,68 @@ yzPage.prototype = {
           let qustionType = parseInt(item.getAttribute('data-type'));
           let questionId = item.getAttribute('data-id');
           if (lastQuestion.className && lastQuestion.className.indexOf('js-lsiten-question') > -1) {
-
+            let lastQuestionId = lastQuestion.getAttribute('data-id');
+            if (questionId && lastQuestionId && lastQuestionId === questionId) {
+              // 需要合并2个小问
+              if (qustionType === 3) {
+                this._prevSubjectQuestion($(lastQuestion), size, $(item), true);
+              } else if (qustionType === 4) {
+                this._prevWrittingQuestion($(lastQuestion), size, $(item), true);
+              } else {
+                let height = $(item).getSizeData().height;
+                if (height < size.diff) {
+                  $prevBorder[0].firstChild.appendChild(item);
+                  size.diff -= height;
+                }
+              }
+            } else {
+              if (qustionType === 3) {
+                let $prevSubject = $(item).clone(false);
+                this._prevSubjectQuestion($prevSubject, size, $(item), false);
+                if (!isEmptyElement($prevSubject[0])) {
+                  $prevBorder[0].firstChild.appendChild($prevSubject[0]);
+                }
+              } else if (qustionType === 4) {
+                let $prevWritting = $(item).clone(false);
+                this._prevWrittingQuestion($prevWritting, size, $(item), false);
+                if (!isEmptyElement($prevWritting[0])) {
+                  $prevBorder[0].firstChild.appendChild($prevWritting[0]);
+                }
+              } else {
+                let height = $(item).getSizeData().height;
+                if (height < size.diff) {
+                  $prevBorder[0].firstChild.appendChild(item);
+                  size.diff -= height;
+                }
+              }
+            }
+            
           } else {
             if (qustionType === 3) {
               let $prevSubject = $(item).clone(false);
-              this._prevSubjectQuestion($prevSubject, diff, $(item));
-              if (!isEmptyElement($prevSubject)) {
+              this._prevSubjectQuestion($prevSubject, size, $(item), false);
+              if (!isEmptyElement($prevSubject[0])) {
                 $prevBorder[0].firstChild.appendChild($prevSubject[0]);
               }
             } else if (qustionType === 4) {
               let $prevWritting = $(item).clone(false);
-              this._prevWrittingQuestion($prevWritting, diff, $(item));
-              if (!isEmptyElement($prevWritting)) {
+              this._prevWrittingQuestion($prevWritting, size, $(item), false);
+              if (!isEmptyElement($prevWritting[0])) {
                 $prevBorder[0].firstChild.appendChild($prevWritting[0]);
               }
             } else {
-              let height = $(item).getSizeData();
-              if (height < diff) {
+              let height = $(item).getSizeData().height;
+              if (height < size.diff) {
                 $prevBorder[0].firstChild.appendChild(item);
-                diff -= height;
+                size.diff -= height;
               }
             }
           }
         } else if (item.className.indexOf('js-lsiten-line') > -1) {
-          let height = $(item).getSizeData();
-          if (height < diff) {
+          let height = $(item).getSizeData().height;
+          if (height < size.diff) {
             $prevBorder[0].firstChild.appendChild(item);
-            diff -= height;
+            size.diff -= height;
           }
         } else {
           $(item).remove();
@@ -539,34 +885,109 @@ yzPage.prototype = {
   /**
    * function 前移主观题框
    * @param {DomElement} $prevSubject  前移框暂存
-   * @param {number} diff  可移动空间
+   * @param {object} size  可移动空间
    * @param {DomElement} $subject  需要前移客观题处理
+   * @param {boolean} isMerge  是否需要合并
    */
-  _prevSubjectQuestion: function ($prevSubject, diff, $subject) {
+  _prevSubjectQuestion: function ($prevSubject, size, $subject, isMerge) {
     $subject.childNodes().forEach(item => {
-      let height = $(item).getSizeData();
-      if (height < diff) {
+      let height = $(item).getSizeData().height;
+      if (height < size.diff) {
         $prevSubject[0].appendChild(item);
-        diff -= height;
+        size.diff -= height;
+      }
+
+      if (isEmptyElement($subject[0])) {
+        $subject.remove();
       }
     })
   },
   /**
    * function 前移主观题框
-   * @param {DomElement} $prevSubject  前移框暂存
-   * @param {number} diff  可移动空间
-   * @param {DomElement} $subject  需要前移客观题处理
+   * @param {DomElement} $prevWritting  前移框暂存
+   * @param {number} size  可移动空间
+   * @param {DomElement} $Writting  需要前移客观题处理
+   * @param {boolean} isMerge  是否需要合并
    */
-  _prevWrittingQuestion: function ($prevWritting, diff, $Writting) {
+  _prevWrittingQuestion: function ($prevWritting, size, $Writting, isMerge) {
     $Writting.childNodes().forEach(item => {
       if (item.className) {
         if (item.className.indexOf('lsiten-title') > -1) {
-  
+          let $prevWrittingTitle = null;
+          let needApppend = true;
+          if (isMerge) {
+            $prevWrittingTitle = $prevWritting.find('.lsiten-title');
+            if ($prevWrittingTitle.length) {
+              $prevWrittingTitle = $($prevWrittingTitle);
+              needApppend = false;
+            } else {
+              $prevWrittingTitle = null;
+              let needApppend = true;              
+            }
+          }
+          let $writtingTitle = $(item);
+          $prevWrittingTitle = $prevWrittingTitle || $writtingTitle.clone(false);
+          $writtingTitle.childNodes().forEach(titleItem => {
+            if (titleItem.className && titleItem.className.indexOf('js-lsiten-line') > -1) {
+              let height = $(titleItem).getSizeData().height;
+              if (height < size.diff) {
+                $prevWrittingTitle[0].appendChild(titleItem);
+                size.diff -= height;
+              }
+            } else {
+              titleItem && item.removeChild(titleItem);
+            }
+          })
+          if (needApppend && !isEmptyElement($prevWrittingTitle[0])) {
+            $prevWritting.append($prevWrittingTitle);
+          }
+
+          if (isEmptyElement($writtingTitle[0])) {
+            $writtingTitle.remove();
+          }
         } else if (item.className.indexOf('lsiten-border-box') > -1) {
-  
+          let $prevWrittingRows = null;
+          let needApppend = true;
+          if (isMerge) {
+            $prevWrittingRows = $prevWritting.find('.lsiten-border-box');
+            if ($prevWrittingRows.length) {
+              $prevWrittingRows = $($prevWrittingRows);
+              needApppend = false;
+            } else {
+              $prevWrittingRows = null;
+              needApppend = true;
+            }
+          }
+          let $writtingRows = $(item);
+          $prevWrittingRows = $prevWrittingRows || $writtingRows.clone(false);
+          // 行的margin值
+          let rowOutSize = 8;
+          $writtingRows.childNodes().forEach(rowItem => {
+            if (rowItem.className && rowItem.className.indexOf('js-lsiten-writting-row') > -1) {
+              let height = $(rowItem).getSizeData().height + rowOutSize;
+              if (height < size.diff) {
+                $prevWrittingRows[0].appendChild(rowItem);
+                size.diff -= height;
+              }
+            } else {
+              rowItem && item.removeChild(rowItem);
+            }
+          })
+
+          if (needApppend && !isEmptyElement($prevWrittingRows[0])) {
+            $prevWritting.append($prevWrittingRows);
+          }
+
+          if (isEmptyElement($writtingRows[0])) {
+            $writtingRows.remove();
+          }
         }
       }
     })
+    
+    if (isEmptyElement($Writting[0])) {
+      $Writting.remove();
+    }
   },
   /**
    * function 检测内容是否超出
@@ -585,6 +1006,9 @@ yzPage.prototype = {
   _getFirstPargraph: function (column) {
     let $paragraphs = column.find('.js-paragraph-view');
     let firstParagraph = $paragraphs[0];
+    if (!firstParagraph) {
+      return false;
+    }
     let firstChild = firstParagraph.firstChild;
     if (firstChild && firstChild.className && firstChild.className.indexOf('js-answer-header') > -1) {
       firstParagraph = $paragraphs[1];

+ 32 - 0
src/js/yzPageManager.js

@@ -114,6 +114,9 @@ yzPageManager.prototype = {
     this._checkPageOutStatus = false;
   },
 
+  /**
+   * function 检测是否有空页并删除
+   */
   _checkEveryPageBack: function () {
     if (this._checkPageBackStatus) {
       return false;
@@ -125,6 +128,35 @@ yzPageManager.prototype = {
     });
     this._checkPageBackStatus = false;
   },
+
+  _checkRemovePage() {
+    let pagesTemp = this.pages.slice(0);
+    let i = pagesTemp.length - 1;
+    while(i>=0) {
+      let page = pagesTemp[i];
+      let currentParagraphs = page.$textElem.find('.js-paragraph-view');
+      if (currentParagraphs.length && currentParagraphs.length === 1 && currentParagraphs[0].firstChild.className && currentParagraphs[0].firstChild.className.indexOf('js-answer-header') > -1) {
+        this.removePage(page.pageIndex);
+      }
+      i--;
+    }
+  },
+  /**
+   * function 移除某一页
+   * @param {int} 页面id
+   */
+  removePage: function (index) {
+    this.$el.find('.js-page-break-' + index).remove();
+    this.pages[index].$textElem.remove();
+    this.pageIndex--;
+    this.pages.splice(index,1);
+    let i = 0;
+    this.pages.forEach(page => {
+      page.$textElem.find('.js-column').attr('data-column-page', i++);
+    })
+    let height = (this.pages.length - 1) * this._pagesBreakHeight[this.editor.pageSize] + parseInt(this._pagesHeight[this.editor.pageSize]);
+    this.$el.css('height', height + 'px');
+  },
   /**
    * function 添加事件监听 
    */

+ 1 - 1
src/less/main.less

@@ -26,7 +26,7 @@
   .lsiten-editor-toolbar {
     height: 40px;
     background: #ffffff;
-    border-bottom: 1px #bfbfbfbf solid;  
+    border-bottom: 1px #ece8e8 solid;  
     box-shadow: 0 3px 7px rgba(0,0,0,.1);
     * {
       width: auto;