Browse Source

科组长新增去掉长度限制;仲裁去掉准考号长度限制;考试新增多媒体类型;多媒体评卷新增部分

ting.yin 5 years ago
parent
commit
93ca9eeca0
19 changed files with 1174 additions and 27 deletions
  1. 19 4
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/Exam.java
  2. 15 3
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/TaskServiceImpl.java
  3. 2 1
      stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/TaskService.java
  4. 34 0
      stmms-common/src/main/java/cn/com/qmth/stmms/common/enums/ExamType.java
  5. 16 4
      stmms-common/src/main/java/cn/com/qmth/stmms/common/utils/PictureUrlBuilder.java
  6. 2 0
      stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ExamController.java
  7. 31 12
      stmms-web/src/main/java/cn/com/qmth/stmms/common/controller/BaseController.java
  8. 15 1
      stmms-web/src/main/java/cn/com/qmth/stmms/mark/MarkController.java
  9. 1 0
      stmms-web/src/main/webapp/WEB-INF/application.properties
  10. 1 1
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/arbitrateList.jsp
  11. 8 0
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/examEdit.jsp
  12. 10 0
      stmms-web/src/main/webapp/WEB-INF/views/modules/exam/examForm.jsp
  13. 98 0
      stmms-web/src/main/webapp/WEB-INF/views/modules/mark/markJson.jsp
  14. 1 1
      stmms-web/src/main/webapp/WEB-INF/views/modules/user/userEdit.jsp
  15. 45 0
      stmms-web/src/main/webapp/static/mark-json/css/rich-text.css
  16. 56 0
      stmms-web/src/main/webapp/static/mark-json/css/style.css
  17. 77 0
      stmms-web/src/main/webapp/static/mark-json/js/json-builder.js
  18. 725 0
      stmms-web/src/main/webapp/static/mark-json/js/mark-control.js
  19. 18 0
      stmms-web/src/main/webapp/static/mark-json/js/rich-text-builder.js

+ 19 - 4
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/exam/model/Exam.java

@@ -16,6 +16,7 @@ import javax.persistence.TemporalType;
 
 import cn.com.qmth.stmms.biz.mark.model.PictureConfigItem;
 import cn.com.qmth.stmms.common.enums.ExamStatus;
+import cn.com.qmth.stmms.common.enums.ExamType;
 
 @Entity
 @Table(name = "eb_exam")
@@ -97,13 +98,20 @@ public class Exam implements Serializable {
      */
     @Column(name = "sas_config", nullable = true)
     private String sasConfig;
-    
+
     /**
      * 原图遮盖配置
      */
     @Column(name = "sheet_config", nullable = true)
     private String sheetConfig;
 
+    /**
+     * 考试类型
+     */
+    @Enumerated(EnumType.STRING)
+    @Column(name = "type", length = 16, nullable = false)
+    private ExamType type;
+
     public Integer getId() {
         return id;
     }
@@ -236,17 +244,24 @@ public class Exam implements Serializable {
         this.sasConfig = sasConfig;
     }
 
-    
     public String getSheetConfig() {
         return sheetConfig;
     }
-    
+
     public void setSheetConfig(String sheetConfig) {
         this.sheetConfig = sheetConfig;
     }
-    
+
     public List<PictureConfigItem> getSheetConfigList() {
         return PictureConfigItem.parse(sheetConfig);
     }
 
+    public ExamType getType() {
+        return type;
+    }
+
+    public void setType(ExamType type) {
+        this.type = type;
+    }
+
 }

+ 15 - 3
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/Impl/TaskServiceImpl.java

@@ -12,10 +12,12 @@ import org.springframework.transaction.annotation.Transactional;
 
 import cn.com.qmth.stmms.biz.campus.model.Campus;
 import cn.com.qmth.stmms.biz.campus.service.CampusService;
+import cn.com.qmth.stmms.biz.exam.model.Exam;
 import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
 import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
 import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
 import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
+import cn.com.qmth.stmms.biz.exam.service.ExamService;
 import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
 import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
 import cn.com.qmth.stmms.biz.exam.service.MarkerService;
@@ -39,6 +41,7 @@ import cn.com.qmth.stmms.biz.mark.service.MarkSpecialTagService;
 import cn.com.qmth.stmms.biz.mark.service.MarkTrackService;
 import cn.com.qmth.stmms.biz.mark.service.TaskService;
 import cn.com.qmth.stmms.biz.mark.service.TrialService;
+import cn.com.qmth.stmms.common.enums.ExamType;
 import cn.com.qmth.stmms.common.enums.LibraryStatus;
 import cn.com.qmth.stmms.common.enums.MarkStatus;
 import cn.com.qmth.stmms.common.utils.PictureUrlBuilder;
@@ -81,13 +84,17 @@ public class TaskServiceImpl implements TaskService {
     @Autowired
     private TrialService trialService;
 
+    @Autowired
+    private ExamService examService;
+
     @Override
     public List<Task> findByQuery(MarkLibrarySearchQuery query) {
         List<Task> list = new LinkedList<Task>();
         query = libraryService.findByQuery(query);
         if (query.getCurrentCount() > 0) {
+            Exam exam = examService.findById(query.getExamId());
             for (MarkLibrary library : query.getResult()) {
-                list.add(build(library));
+                list.add(build(library, exam.getType()));
             }
         }
         return list;
@@ -132,7 +139,7 @@ public class TaskServiceImpl implements TaskService {
     }
 
     @Override
-    public Task build(MarkLibrary library) {
+    public Task build(MarkLibrary library, ExamType examType) {
         ExamStudent student = studentService.findByExamIdAndExamNumber(library.getExamId(), library.getExamNumber());
         MarkGroup group = groupService.findOne(library.getExamId(), library.getSubjectCode(), library.getGroupNumber());
         Task task = new Task();
@@ -148,7 +155,12 @@ public class TaskServiceImpl implements TaskService {
         task.setPictureConfig(group.getPictureConfigList());
         task.setSheetUrls(PictureUrlBuilder.getSheetUrls(library.getExamId(), library.getCampusId(),
                 library.getSubjectCode(), library.getExamNumber(), student.getSheetCount()));
-        task.setAnswerUrl(PictureUrlBuilder.getAnswerUrl(library.getExamId(), library.getSubjectCode()));
+        if (examType.equals(ExamType.MULTI_MEDIA)) {
+            task.setAnswerUrl(PictureUrlBuilder.getAnswerJson(library.getExamId(), library.getSubjectCode(),
+                    student.getPaperType(), student.getExamNumber()));
+        } else {
+            task.setAnswerUrl(PictureUrlBuilder.getAnswerUrl(library.getExamId(), library.getSubjectCode()));
+        }
         task.setPaperUrl(PictureUrlBuilder.getPaperUrl(library.getExamId(), library.getSubjectCode()));
         task.setObjectiveScore(student != null ? student.getObjectiveScore() : 0);
         task.setMarkTime(library.getMarkerTime());

+ 2 - 1
stmms-biz/src/main/java/cn/com/qmth/stmms/biz/mark/service/TaskService.java

@@ -9,6 +9,7 @@ import cn.com.qmth.stmms.biz.mark.model.Task;
 import cn.com.qmth.stmms.biz.mark.model.TrialHistory;
 import cn.com.qmth.stmms.biz.mark.model.TrialLibrary;
 import cn.com.qmth.stmms.biz.mark.query.MarkLibrarySearchQuery;
+import cn.com.qmth.stmms.common.enums.ExamType;
 
 public interface TaskService {
 
@@ -18,7 +19,7 @@ public interface TaskService {
 
     Task build(TrialLibrary library, TrialHistory history);
 
-    Task build(MarkLibrary library);
+    Task build(MarkLibrary library, ExamType examType);
 
     Task build(Integer studentId);
 

+ 34 - 0
stmms-common/src/main/java/cn/com/qmth/stmms/common/enums/ExamType.java

@@ -0,0 +1,34 @@
+package cn.com.qmth.stmms.common.enums;
+
+public enum ExamType {
+
+    SCAN_IMAGE("扫描图片类型", 1), MULTI_MEDIA("多媒体类型", 2);
+
+    private String name;
+
+    private int value;
+
+    private ExamType(String name, int value) {
+        this.name = name;
+        this.value = value;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getValue() {
+        return value;
+    }
+
+    public static ExamType findByValue(int value) {
+        ExamType status = null;
+        for (ExamType s : ExamType.values()) {
+            if (s.getValue() == value) {
+                status = s;
+                break;
+            }
+        }
+        return status;
+    }
+}

+ 16 - 4
stmms-common/src/main/java/cn/com/qmth/stmms/common/utils/PictureUrlBuilder.java

@@ -26,10 +26,16 @@ public class PictureUrlBuilder {
 
     private static final String PACKAGE_URL_TEMPLATE = "/{0}/{1}/{2}.{3}";
 
+    private static final String JSON_PAPER_TYPE_URL_TEMPLATE = "/{0}/{1}-{2}/{3}.{4}";
+
+    private static final String JSON_URL_TEMPLATE = "/{0}/{1}/{2}.{3}";
+
     private static final String DEFAULT_SUFFIX = "jpg";
 
     private static final String DOCUMENT_SUFFIX = "pdf";
 
+    private static final String JSON_SUFFIX = "json";
+
     public static List<String> getInnerSheetUrls(int examId, String examNumber, int count) {
         List<String> list = new LinkedList<String>();
         if (StringUtils.isNotBlank(examNumber) && count > 0) {
@@ -41,8 +47,7 @@ public class PictureUrlBuilder {
         return list;
     }
 
-    public static List<String> getSheetUrls(int examId, int campusId, String subjectCode, String examNumber,
-            int count) {
+    public static List<String> getSheetUrls(int examId, int campusId, String subjectCode, String examNumber, int count) {
         List<String> list = new LinkedList<String>();
         if (StringUtils.isNotEmpty(subjectCode) && count > 0) {
             for (int i = 1; i <= count; i++) {
@@ -63,8 +68,7 @@ public class PictureUrlBuilder {
                 examNumber, String.valueOf(index), DEFAULT_SUFFIX);
     }
 
-    public static List<String> getSliceUrls(int examId, int campusId, String subjectCode, String examNumber,
-            int count) {
+    public static List<String> getSliceUrls(int examId, int campusId, String subjectCode, String examNumber, int count) {
         List<String> list = new LinkedList<String>();
         if (StringUtils.isNotEmpty(subjectCode) && count > 0) {
             for (int i = 1; i <= count; i++) {
@@ -105,4 +109,12 @@ public class PictureUrlBuilder {
         }
         return list;
     }
+
+    public static String getAnswerJson(Integer examId, String subjectCode, String paperType, String examNumber) {
+        if (StringUtils.isNotEmpty(paperType)) {
+            return MessageFormat.format(JSON_PAPER_TYPE_URL_TEMPLATE, String.valueOf(examId), subjectCode, paperType,
+                    examNumber, JSON_SUFFIX);
+        }
+        return MessageFormat.format(JSON_URL_TEMPLATE, String.valueOf(examId), subjectCode, examNumber, JSON_SUFFIX);
+    }
 }

+ 2 - 0
stmms-web/src/main/java/cn/com/qmth/stmms/admin/exam/ExamController.java

@@ -41,6 +41,7 @@ import cn.com.qmth.stmms.biz.user.model.User;
 import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
 import cn.com.qmth.stmms.common.domain.WebUser;
 import cn.com.qmth.stmms.common.enums.ExamStatus;
+import cn.com.qmth.stmms.common.enums.ExamType;
 import cn.com.qmth.stmms.common.enums.Role;
 import cn.com.qmth.stmms.common.utils.Paginator;
 import cn.com.qmth.stmms.common.utils.PictureUrlBuilder;
@@ -91,6 +92,7 @@ public class ExamController extends BaseExamController {
     public String add(Exam exam, Model model) {
         model.addAttribute("exam", exam);
         model.addAttribute("statusList", ExamStatus.values());
+        model.addAttribute("typeList", ExamType.values());
         return "modules/exam/examForm";
     }
 

+ 31 - 12
stmms-web/src/main/java/cn/com/qmth/stmms/common/controller/BaseController.java

@@ -22,6 +22,7 @@ import cn.com.qmth.stmms.biz.utils.ScoreCalculateUtil;
 import cn.com.qmth.stmms.biz.utils.ScoreInfo;
 import cn.com.qmth.stmms.common.enums.CheckType;
 import cn.com.qmth.stmms.common.enums.ExamSubjectStatus;
+import cn.com.qmth.stmms.common.enums.ExamType;
 import cn.com.qmth.stmms.common.enums.HistoryStatus;
 import cn.com.qmth.stmms.common.enums.LibraryStatus;
 import cn.com.qmth.stmms.common.enums.MarkMode;
@@ -43,7 +44,7 @@ public class BaseController {
 
     @Autowired
     private ExamQuestionService questionService;
-    
+
     @Autowired
     private MarkGroupService groupService;
 
@@ -143,7 +144,7 @@ public class BaseController {
                 }
             }
         });
-        
+
         // HistoryStatus 类型转换
         binder.registerCustomEditor(HistoryStatus.class, new PropertyEditorSupport() {
 
@@ -181,20 +182,34 @@ public class BaseController {
                 }
             }
         });
-        
+        // ExamType 类型转换
+        binder.registerCustomEditor(ExamType.class, new PropertyEditorSupport() {
+
+            @Override
+            public void setAsText(String text) {
+                try {
+                    setValue(ExamType.findByValue(Integer.valueOf(text)));
+                } catch (Exception e) {
+                    setValue(null);
+                }
+            }
+        });
+
         binder.registerCustomEditor(Boolean.class, new CustomBooleanEditor(true));
     }
 
     protected boolean saveUploadStudent(ExamStudent student) {
         ExamStudent old = studentService.findById(student.getId());
-        if(!student.isAbsent()){//正考
-            List<MarkGroup> groupList = groupService.findByExamAndSubjectAndStatus(student.getExamId(), student.getSubjectCode(), MarkStatus.FINISH);
+        if (!student.isAbsent()) {// 正考
+            List<MarkGroup> groupList = groupService.findByExamAndSubjectAndStatus(student.getExamId(),
+                    student.getSubjectCode(), MarkStatus.FINISH);
             for (MarkGroup markGroup : groupList) {
-                groupService.updateStatus(student.getExamId(), student.getSubjectCode(), markGroup.getNumber(), MarkStatus.FORMAL, MarkStatus.FINISH);
+                groupService.updateStatus(student.getExamId(), student.getSubjectCode(), markGroup.getNumber(),
+                        MarkStatus.FORMAL, MarkStatus.FINISH);
             }
         }
         calculateObjectiveScore(student);
-        if(!old.isAbsent() && student.isAbsent()){//正考转缺考
+        if (!old.isAbsent() && student.isAbsent()) {// 正考转缺考
             student.setObjectiveScore(0d);
             student.setObjectiveScoreList(null);
             student.setSubjectiveScore(0d);
@@ -202,9 +217,12 @@ public class BaseController {
             studentService.save(student);
         }
         boolean success = studentService.updateScanInfo(student);
-        if(success){
-        	subjectService.updateUploadCount(student.getExamId(), student.getSubjectCode(), (int) studentService
-        			.countUploadedByExamIdAndSubjectCode(student.getExamId(), student.getSubjectCode()));
+        if (success) {
+            subjectService.updateUploadCount(
+                    student.getExamId(),
+                    student.getSubjectCode(),
+                    (int) studentService.countUploadedByExamIdAndSubjectCode(student.getExamId(),
+                            student.getSubjectCode()));
         }
         return success;
     }
@@ -212,8 +230,9 @@ public class BaseController {
     private void calculateObjectiveScore(ExamStudent student) {
         ScoreCalculateUtil util = ScoreCalculateUtil.instance(student);
 
-        ScoreInfo info = util.calculate(questionService.findByExamAndSubjectAndObjectiveAndPaperType(
-                student.getExamId(), student.getSubjectCode(), true, student.getPaperType()), null);
+        ScoreInfo info = util.calculate(
+                questionService.findByExamAndSubjectAndObjectiveAndPaperType(student.getExamId(),
+                        student.getSubjectCode(), true, student.getPaperType()), null);
 
         student.setObjectiveScore(info.getObjectiveScore());
         student.setScoreList(info.getScoreList(), true);

+ 15 - 1
stmms-web/src/main/java/cn/com/qmth/stmms/mark/MarkController.java

@@ -48,6 +48,7 @@ import cn.com.qmth.stmms.biz.mark.service.MarkService;
 import cn.com.qmth.stmms.biz.mark.service.TaskService;
 import cn.com.qmth.stmms.biz.mark.service.TrialService;
 import cn.com.qmth.stmms.common.controller.BaseController;
+import cn.com.qmth.stmms.common.enums.ExamType;
 import cn.com.qmth.stmms.common.enums.LibraryStatus;
 import cn.com.qmth.stmms.common.enums.LockType;
 import cn.com.qmth.stmms.common.enums.MarkMode;
@@ -104,6 +105,9 @@ public class MarkController extends BaseController {
     @Value("${marker.forceMode}")
     private String forceMarkMode;
 
+    @Value("${answer.server}")
+    private String answerServer;
+
     @RequestMapping(value = "/reset", method = RequestMethod.GET)
     public ModelAndView reset(HttpServletRequest request) {
         Marker marker = RequestUtils.getWebUser(request).getMarker();
@@ -132,6 +136,14 @@ public class MarkController extends BaseController {
     }
 
     private ModelAndView getMarkModeView(Marker marker, MarkMode mode) {
+        // 多媒体阅卷
+        Exam exam = examService.findById(marker.getExamId());
+        if (ExamType.MULTI_MEDIA.equals(exam.getType())) {
+            ModelAndView view = new ModelAndView("modules/mark/markJson");
+            view.addObject("forceMode", false);
+            view.addObject("sheetView", false);
+            return view;
+        }
         boolean forceMode = false;
         MarkMode sysMode = MarkMode.findByName(forceMarkMode);
         MarkGroup group = groupService.findOne(marker.getExamId(), marker.getSubjectCode(), marker.getGroupNumber());
@@ -181,6 +193,7 @@ public class MarkController extends BaseController {
         modelAndView.addObject("sliceServer", sliceServer);
         modelAndView.addObject("sheetServer", sheetServer);
         modelAndView.addObject("cardServer", cardServer);
+        modelAndView.addObject("answerServer", answerServer);
         modelAndView.addObject("marker", marker);
         ExamSubject subject = subjectService.find(marker.getExamId(), marker.getSubjectCode());
         modelAndView.addObject("subject", subject);
@@ -295,6 +308,7 @@ public class MarkController extends BaseController {
     private Task getFormalTask(Marker marker) {
         int retry = 1;
         Task task = null;
+        Exam exam = examService.findById(marker.getExamId());
         while (task == null) {
             List<MarkLibrary> list = new ArrayList<MarkLibrary>();
             // 需要判断评卷员是否绑定了班级
@@ -305,7 +319,7 @@ public class MarkController extends BaseController {
             }
             for (MarkLibrary library : list) {
                 if (markService.applyLibrary(library, marker)) {
-                    task = taskService.build(library);
+                    task = taskService.build(library, exam.getType());
                     break;
                 }
             }

+ 1 - 0
stmms-web/src/main/webapp/WEB-INF/application.properties

@@ -15,6 +15,7 @@ slice.image.server=https://ft-slice.markingcloud.com
 sheet.image.server=https://ft-sheet.markingcloud.com
 package.image.server=http://ft-package.markingcloud.com
 card.server=http://ft-card.markingcloud.com
+answer.server=http://ft-card.markingcloud.com
 ##slice.image.server=http://${local.ip}:9000/gx-slice
 ##sheet.image.server=http://${local.ip}:9000/gx-sheet
 ##package.image.server=http://${local.ip}:9000/gx-package

+ 1 - 1
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/arbitrateList.jsp

@@ -40,7 +40,7 @@
                 </c:forEach>
             </select>
             <label>准考证号</label>
-            <input type="text" name="examNumber" value="${query.examNumber}" maxlength="10" class="input-medium"/>
+            <input type="text" name="examNumber" value="${query.examNumber}" maxlength="20" class="input-medium"/>
 			&nbsp;
 			<input id="btnSubmit" class="btn btn-primary" type="button" value="查询" onclick="goSearch()"/>
 			

+ 8 - 0
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/examEdit.jsp

@@ -42,6 +42,14 @@
 				<form:input path="name" htmlEscape="false" maxlength="200" class="required"/>
 			</div>
 		</div>
+		<div class="control-group">
+			<label class="control-label">类型</label>
+			<div class="controls">
+				<select class="input-small" name="type" disabled="disabled">
+                	 <option value="${exam.type.value}">${exam.type.name}</option>
+            	</select>
+			</div>
+		</div>
 		<div class="control-group">
 			<label class="control-label">考试日期</label>
 			<div class="controls">

+ 10 - 0
stmms-web/src/main/webapp/WEB-INF/views/modules/exam/examForm.jsp

@@ -39,6 +39,16 @@
 				<form:input path="name" htmlEscape="false" maxlength="200" class="required"/>
 			</div>
 		</div>
+		<div class="control-group">
+			<label class="control-label">类型</label>
+			<div class="controls">
+				<select class="input-small" name="type">
+                <c:forEach items="${typeList}" var="item">
+                	 <option value="${item.value}" <c:if test="${item.value==exam.type.value}">selected</c:if>>${item.name}</option>
+                </c:forEach>
+            </select>
+			</div>
+		</div>
 		<div class="control-group">
 			<label class="control-label">考试日期</label>
 			<div class="controls">

+ 98 - 0
stmms-web/src/main/webapp/WEB-INF/views/modules/mark/markJson.jsp

@@ -0,0 +1,98 @@
+<%@ page language="java" pageEncoding="utf-8"%>
+<%@ include file="/WEB-INF/views/include/taglib.jsp"%>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>云阅卷高校版</title>
+<link href="${ctxStatic}/mark-new/css/bootstrap.css" rel="stylesheet" type="text/css" />
+<link href="${ctxStatic}/mark-new/css/style.css" rel="stylesheet" type="text/css" />
+<link href="${ctxStatic}/mark-json/css/style.css" rel="stylesheet" type="text/css" />
+<link href="${ctxStatic}/mark-json/css/rich-text.css" rel="stylesheet" type="text/css" />
+
+<script type="text/javascript" src="${ctxStatic}/mark-new/js/json2.js"></script>
+
+<script type="text/javascript" src="${ctxStatic}/mark-new/js/jquery.min.js"></script>
+<script type="text/javascript" src="${ctxStatic}/mark-new/js/jquery-ui.min.js "></script>
+<script type="text/javascript" src="${ctxStatic}/mark-new/js/jquery.mousewheel.min.js"></script>
+
+<script type="text/javascript" src="${ctxStatic}/iviewer/jquery.iviewer.js"></script>
+<link rel="stylesheet" href="${ctxStatic}/iviewer/jquery.iviewer.css" rel="stylesheet" />
+
+<script src="${ctxStatic}/perfect-scrollbar/min/perfect-scrollbar.min.js"></script>
+<link href="${ctxStatic}/perfect-scrollbar/min/perfect-scrollbar.min.css" rel="stylesheet">
+
+<script type="text/javascript" src="${ctxStatic}/mark-json/js/mark-control.js"></script>
+<script type="text/javascript" src="${ctxStatic}/mark-json/js/json-builder.js"></script>
+<script type="text/javascript" src="${ctxStatic}/mark-new/js/task-control.js"></script>
+<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/paper-view.js"></script>
+<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/image-builder.js"></script>
+<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/mark-board.js"></script>
+<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/mark-history.js"></script>
+<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/mark-status.js"></script>
+<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/warning-info.js"></script>
+<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/change-name.js"></script>
+<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/tag-process.js"></script>
+<script type="text/javascript" src="${ctxStatic}/mark-new/js/modules/view-sidebar.js"></script>
+
+</head>
+<body>
+	<div class="container-fluid" id="container"></div>
+	<script type="text/javascript">
+		$(document).ready(function() {
+			var mc = new MarkControl({
+				container : $('#container'),
+				staticServer : '${ctxStatic}',
+				imageServer : '${sliceServer}',
+				userId: '${marker.id}',
+				userName : '${web_user.name}',
+				logoutUrl: '${ctx}/mark/logout',
+				//clearUrl: '${ctx}/mark/clear',
+			/* 	<c:if test="${forceMode==false}">
+				switchTrackUrl: '${ctx}/mark/index?mode=track',
+				</c:if>
+				forceSpecialTag : eval('${forceSpecialTag}'.toLowerCase()), */
+				<c:if test="${defaultSetting!=null}">
+				defaultSetting: '${defaultSetting}',
+				</c:if>
+				settingSyncUrl: '${ctx}/mark/update-setting',
+				modules : {
+					'json-builder': {
+						answerServer : '${answerServer}'
+					},
+					'mark-status': {
+						simple : false,
+						subjectName : '${subject.displayName}'
+					},
+					'mark-history':{
+						pageSize:10
+					},
+					'mark-board' : {
+						showScoreBoard : false,
+						autoSubmit : false,
+						needConfirm : false,
+					},
+					'warning-info': {
+					},
+					'change-name':{
+						url : '${ctx}/mark/change-name'
+					},
+					'view-sidebar':{
+						list: [
+							{title:'试卷',  url:'<c:if test="${subject.hasPaper==true}">${cardServer}${subject.paperUrl}</c:if>'},
+							{title:'答案',  url:'<c:if test="${subject.hasAnswer==true}">${cardServer}${subject.answerUrl}</c:if>'}
+						]
+					}
+				}
+			});
+			mc.start({
+				mode : 'loop',
+				statusUrl : '${ctx}/mark/status',
+				getUrl : '${ctx}/mark/gettask',
+				historyUrl : '${ctx}/mark/gethistory',
+				submitUrl : '${ctx}/mark/savetask'
+				//clearUrl : '${ctx}/mark/clear'
+			});
+		});
+	</script>
+</body>
+</html>

+ 1 - 1
stmms-web/src/main/webapp/WEB-INF/views/modules/user/userEdit.jsp

@@ -74,7 +74,7 @@
 		<div class="control-group" id="subject-code-div">
             <label class="control-label">绑定科目</label>
             <div class="controls">
-                <form:input path="subjectCode" htmlEscape="false" maxlength="10"/>
+                <form:input path="subjectCode" htmlEscape="false" maxlength="30"/>
             </div>
         </div>
 		<div class="control-group">

+ 45 - 0
stmms-web/src/main/webapp/static/mark-json/css/rich-text.css

@@ -0,0 +1,45 @@
+/*rich-text*/
+* {
+    font-family: "微软雅黑", "苹方", Arial, Helvetica, sans-serif;
+}
+
+.rich-text {
+    padding: 10px 0;
+}
+
+.rich-text p {
+    font-size: 16px;
+    line-height: 2em;
+    margin: 10px 0;
+    word-break: break-all;
+}
+
+.rich-text img.inline {
+    width: auto;
+    max-height: 2em;
+}
+
+.rich-text p span.sup {
+    vertical-align: super;
+    font-size: 9px;
+    font-family: Arial, Helvetica, sans-serif;
+    margin: 10px 0px 0px 0px
+}
+
+.rich-text p span.sub {
+    vertical-align: sub;
+    font-size: 9px;
+    font-family: Arial, Helvetica, sans-serif;
+}
+
+.rich-text p span.bold {
+    font-weight: 700;
+}
+
+.rich-text p span.underline {
+    text-decoration: underline;
+}
+
+.rich-text p audio {
+    height: 2em;
+}

+ 56 - 0
stmms-web/src/main/webapp/static/mark-json/css/style.css

@@ -0,0 +1,56 @@
+
+/*======common======*/
+* {
+	font-family: "微软雅黑","苹方";
+	margin: 0;
+	padding: 0;
+	list-style: none;
+	outline: none;
+	-webkit-tap-highlight-color: transparent;
+	-webkit-box-sizing: border-box;
+	box-sizing: border-box;
+}
+html, body {
+	height: 100%;
+	background: #f5f5f5;
+	color: #444;
+}
+a {
+	text-decoration: none;
+}
+.ellipsis {
+	text-overflow: ellipsis;
+	overflow: hidden;
+	white-space: nowrap;
+}
+.cl:after {
+	content: ".";
+	display: block;
+	height: 0;
+	clear: both;
+	visibility: hidden;
+}
+.cl {
+	zoom: 1;
+}
+.wp {
+	width: 1000px;
+	overflow: hidden;
+	margin: 50px auto;
+	padding: 20px 40px;
+	background: #FFF;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 77 - 0
stmms-web/src/main/webapp/static/mark-json/js/json-builder.js

@@ -0,0 +1,77 @@
+//多媒体显示模块
+var json_builder = function(option, success) {
+    var object = new JsonBuilder(option);
+    success();
+    return object;
+}
+
+function JsonBuilder(option) {
+    this.markControl = option.markControl;
+    this.answerServer = option.answerServer;
+    this.init();
+    this.markControl.on('step.board.show', this, function(event, context, eventObject) {
+        this.container.removeClass('span12');
+        this.container.addClass('span10');
+    });
+    this.markControl.on('step.board.hide', this, function(event, context, eventObject) {
+        this.container.removeClass('span10');
+        this.container.addClass('span12');
+    });
+    this.markControl.on('task.get.before', this, function(event, context, eventObject) {
+        this.task = undefined;
+        this.json = undefined;
+    });
+    this.markControl.on('task.get.success', this, function(event, context, eventObject) {
+        this.task = context.task;
+        this.json = undefined;
+    });
+    this.markControl.on('task.get.none', this, function(event, context, eventObject) {
+        this.task = undefined;
+        this.json = undefined;
+    });
+    this.markControl.on('image.position.change', this, function(event, context, topPercent) {
+        if (this.task != undefined) {
+            this.updateScrollTop(topPercent);
+        }
+    });
+}
+
+JsonBuilder.prototype.init = function() {
+    var self = this;
+    this.container = this.markControl.container.imageContent;
+    this.container.height(this.markControl.container.centerContent.height());
+    this.container.css('overflow', 'scroll');
+    
+    this.container = getDom(this.control_dom, this.markControl).appendTo(this.markControl.container);
+    this.container.hide();
+
+}
+
+JsonBuilder.prototype.build = function(task,callback) {
+    var self = this;
+    if (this.task != undefined && this.task.answerUrl != undefined) {
+    	var url = this.answerServer + this.task.answerUrl;
+    	$.get(url, function(result) {
+    		
+    		self.container.show();
+        	self.markControl.trigger('task.load.finish');
+        }).error(function() {
+        	 callback('image load error');
+        });
+    }
+}
+
+JsonBuilder.prototype.updateScrollTop = function(scrollTopPercent) {
+    var height = this.canvas.height;
+    var minHeight = this.container.height();
+    if (scrollTopPercent != undefined && scrollTopPercent >= 0 && scrollTopPercent <= 1 && height > minHeight) {
+        var left = height * (1 - scrollTopPercent);
+        var scrollTop = left > minHeight ? (height - left) : (height - minHeight);
+        this.container.scrollTop(scrollTop);
+    } else {
+        this.container.scrollTop(0);
+    }
+}
+
+JsonBuilder.prototype.control_dom = '<div class="wp"><div class="rich-text"></div></div>';
+JsonBuilder.prototype.rich_text_dom = '<div class="rich-text"></div>';

+ 725 - 0
stmms-web/src/main/webapp/static/mark-json/js/mark-control.js

@@ -0,0 +1,725 @@
+function MarkControl(option) {
+    this.option = option;
+
+    this.maxPrefetchCount = parseInt(option.prefetchCount);
+    this.maxPrefetchCount = this.maxPrefetchCount != undefined && this.maxPrefetchCount > 2 ? this.maxPrefetchCount : 2;
+
+    this.container = option.container;
+    this.context = {
+        imageServer: option.imageServer,
+        staticServer: option.staticServer,
+        isFinish: false,
+        prefetching: false,
+        prefetchTask: []
+    };
+    //初始化容器结构
+    this.initContainer();
+    //初始化事件监听
+    this.initTriggers(option);
+    //初始化功能模块
+    this.initModules(option);
+    //初始化评卷配置
+    this.initSetting(option);
+    //初始化成功回调方法
+    //console.log('MarkControl init success!');
+    //if (option.success != undefined && typeof(option.success) == 'function') {
+    //    option.success();
+    //}
+}
+
+MarkControl.prototype.initContainer = function() {
+
+    var height = this.option.height;
+    if (height == undefined) {
+        height = $(window).height();
+    }
+    this.container = getDom(this.main_row_dom, this).appendTo(this.container);
+
+    if (this.option.enableSidear != false) {
+        this.container.sidebar = getDom(this.sidebar_dom, this).appendTo(this.container);
+        this.container.sidebar.css('max-height', height);
+    }
+    this.container.center = getDom(this.center_dom, this).appendTo(this.container);
+    this.container.header = getDom(this.center_header_dom, this).appendTo(this.container.center).find('.header');
+    if (this.option.switchTrackUrl != undefined && this.option.switchTrackUrl.length > 0) {
+        var switchButton = this.container.header.find('#switch-track-button');
+        switchButton.attr('href', this.option.switchTrackUrl);
+        switchButton.show();
+    }
+    this.container.centerContent = getDom(this.center_content_dom, this).appendTo(this.container.center);
+    this.container.imageContent = getDom(this.image_content_dom, this).appendTo(this.container.centerContent);
+
+    this.container.height(height);
+    this.container.centerContent.css('height', height - this.container.header.parent().height());
+
+    this.initHeaderAndAssistant();
+
+    this.container.centerList = [];
+    this.navNumber = 0;
+}
+
+MarkControl.prototype.initHeaderAndAssistant = function() {
+    var self = this;
+    this.container.header.find('#mark-user-name').html(this.option.userName);
+    if (this.option.logoutTitle != undefined) {
+        this.container.header.find('#logout-title').html(this.option.logoutTitle);
+    }
+    this.container.header.find('#logout-link').click(function() {
+        self.trigger('logout.link.click');
+        return true;
+    })
+
+    this.container.assistant = getDom(this.assistant_dom, this).appendTo(this.container);
+    this.container.assistant.positionSet = false;
+    this.container.assistantButton = this.container.header.find('#assistant-button');
+
+    this.container.assistantButton.click(this, function(event) {
+        if (self.container.assistant.positionSet == false) {
+            self.container.assistant.offset({
+                top: self.container.assistantButton.position().top + self.container.assistantButton.height() + 5,
+                left: self.container.assistantButton.position().left - 85
+            });
+            self.container.assistant.positionSet = true;
+        }
+        self.container.assistant.toggle();
+    });
+
+}
+
+MarkControl.prototype.initMarkFunction = function() {
+    var functionList = this.container.assistant.functionList;
+    if (functionList == undefined) {
+        functionList = getDom(this.mark_function_dom, this).appendTo(this.container.assistant).find('#function-list');
+        this.container.assistant.functionList = functionList;
+    }
+}
+
+MarkControl.prototype.initSetting = function(option) {
+    this.userId = option.userId;
+    this.settingSyncUrl = option.settingSyncUrl;
+    this.settingSyncing = false;
+    this.localStore = window.localStorage;
+    this.setting = {};
+
+    if (option.defaultSetting != undefined) {
+        //读取外部初始化配置
+        this.setting = JSON.parse(option.defaultSetting);
+    } else if (this.localStore != undefined) {
+        //读取本地存储并判断用户标识
+        var store = JSON.parse(this.localStore.getItem('mark.setting'));
+        if (store != undefined && store.userId != undefined && store.userId == this.userId) {
+            this.setting = store.setting;
+        }
+    }
+    //强制刷新本地存储
+    this.localSettingSave();
+    //广播通知所有模块初始化设置
+    this.trigger('mark.setting.init', this.setting);
+}
+
+MarkControl.prototype.localSettingSave = function() {
+    if (this.localStore != undefined) {
+        this.localStore.setItem('mark.setting', JSON.stringify({
+            userId: this.userId,
+            setting: this.setting
+        }));
+    }
+}
+
+MarkControl.prototype.updateSetting = function(item) {
+    if (item == undefined) {
+        return;
+    }
+    for (var key in item) {
+        this.setting[key] = item[key];
+    }
+    this.localSettingSave();
+    if (this.settingSyncUrl != undefined && this.settingSyncing == false) {
+        this.settingSyncing = true;
+        var self = this;
+        setTimeout(function() {
+            //同步个性化设置
+            $.post(self.settingSyncUrl, {
+                setting: JSON.stringify(self.setting)
+            });
+            self.settingSyncing = false;
+        }, 1000);
+    }
+}
+
+//增加某个事件的监听方法
+MarkControl.prototype.on = function(eventName, caller, callback, async) {
+    if (eventName && callback && eventName.length > 0 && typeof(callback) == 'function') {
+        if (async) {
+            this.container.bind(eventName, callback);
+        } else {
+            if (this.triggers[eventName] == undefined) {
+                this.triggers[eventName] = new Array();
+            }
+            this.triggers[eventName].push({
+                caller: caller,
+                callback: callback
+            });
+        }
+    }
+}
+
+//触发某个事件,并传递事件相关内容
+MarkControl.prototype.trigger = function(eventName, eventObject) {
+    var result = true;
+    if (eventName && eventName.length > 0) {
+        var array = this.triggers[eventName];
+        if (array != undefined && array.length > 0) {
+            var event = {
+                name: eventName
+            }
+            for (var i in array) {
+                result = result & array[i].callback.call(array[i].caller, event, this.context, eventObject);
+            }
+        }
+        this.container.trigger(eventName, this.context, eventObject);
+    }
+    return result;
+}
+
+//初始化事件监听
+MarkControl.prototype.initTriggers = function(option) {
+    if (this.triggers == undefined) {
+        this.triggers = {};
+    }
+    if (option != undefined && option.on != undefined) {
+        for (var name in option.on) {
+            this.on(name, option.on[name]);
+        }
+    }
+
+    var self = this;
+    this.on('view.sidebar.open', this, function(event, context, eventObject) {
+        this.container.assistant.hide();
+        if (this.container.center.hasClass('span12')) {
+            this.container.center.removeClass('span12');
+            this.container.center.addClass('span7');
+        } else if (this.container.center.hasClass('span10')) {
+            this.container.center.removeClass('span10');
+            this.container.center.addClass('span5');
+        }
+        this.trigger('center.width.change');
+    });
+    this.on('view.sidebar.close', this, function(event, context, eventObject) {
+        this.container.assistant.hide();
+        if (this.container.center.hasClass('span7')) {
+            this.container.center.removeClass('span7');
+            this.container.center.addClass('span12');
+        } else if (this.container.center.hasClass('span5')) {
+            this.container.center.removeClass('span5');
+            this.container.center.addClass('span10');
+        }
+        this.trigger('center.width.change');
+    });
+    this.on('mark.sidebar.open', this, function(event, context, eventObject) {
+        this.container.assistant.hide();
+        if (this.container.center.hasClass('span12')) {
+            this.container.center.removeClass('span12');
+            this.container.center.addClass('span10');
+        } else if (this.container.center.hasClass('span7')) {
+            this.container.center.removeClass('span7');
+            this.container.center.addClass('span5');
+        }
+        this.trigger('center.width.change');
+    });
+    this.on('mark.sidebar.close', this, function(event, context, eventObject) {
+        this.container.assistant.hide();
+        if (this.container.center.hasClass('span10')) {
+            this.container.center.removeClass('span10');
+            this.container.center.addClass('span12');
+        } else if (this.container.center.hasClass('span5')) {
+            this.container.center.removeClass('span5');
+            this.container.center.addClass('span7');
+        }
+        this.trigger('center.width.change');
+    });
+    this.on('task.load.finish', this, function(event, context, eventObject) {
+        if (context.task != undefined) {
+            context.task.spent = new Date().getTime();
+        }
+    });
+    this.on('mark.focus.change', this, function(event, context, eventObject) {});
+    this.on('task.get.finish', this, function(event, context, eventObject) {
+        context.prefetchCallback = undefined;
+    });
+    this.on('task.get.none', this, function(event, context, eventObject) {
+        self.getStatus();
+        if (context.task == undefined && self.option.clearUrl != undefined) {
+            $.post(self.option.clearUrl, {}, function() {});
+        }
+        context.prefetchCallback = function() {
+            context.prefetchCallback = undefined;
+            if (self.context.task == undefined && self.context.prefetchTask.length > 0) {
+                self.getTask();
+            }
+        }
+    });
+    this.on('task.prefetch.success', this, function(event, context, eventObject) {
+        if (context.prefetchCallback != undefined) {
+            context.prefetchCallback();
+        }
+        setTimeout(function() {
+            self.prefetch();
+        }, 500);
+    });
+    this.on('task.prefetch.error', this, function(event, context, eventObject) {
+        setTimeout(function() {
+            self.prefetch();
+        }, 500);
+    });
+    this.on('task.prefetch.none', this, function(event, context, eventObject) {
+        if (context.prefetchCallback != undefined) {
+            context.prefetchCallback();
+        }
+        if (context.isFinish != true) {
+            setTimeout(function() {
+                self.prefetch();
+            }, 1000);
+        }
+    });
+    this.on('task.prefetch.finish', this, function(event, context, eventObject) {
+        self.getTask();
+    });
+    this.on('task.submit.before', this, function(event, context, eventObject) {
+        context.submitting = true;
+    });
+    this.on('history.submit.success', this, function(event, context, eventObject) {
+        context.submitting = false;
+    });
+    this.on('task.submit.success', this, function(event, context, eventObject) {
+        context.submitting = false;
+        context.task = undefined;
+        self.getTask();
+    });
+    this.on('task.submit.error', this, function(event, context, eventObject) {
+        context.submitting = false;
+    });
+    this.on('task.pass.success', this, function(event, context, eventObject) {
+        if (context.task != undefined && self.option.clearUrl != undefined) {
+            $.post(self.option.clearUrl, {
+                libraryId: context.task.libraryId
+            });
+        }
+        context.task = undefined;
+        self.getTask();
+    });
+    this.on('mark.setting.change', this, function(event, context, eventObject) {
+        self.updateSetting(eventObject);
+    });
+    $(document).keypress(this, function(event) {
+        if (self.context.listenKeyboard != false) {
+            return self.trigger('key.press', event);
+        }
+    });
+    $(document).keydown(this, function(event) {
+        if (self.context.listenKeyboard != false) {
+            return self.trigger('key.down', event);
+        }
+    });
+    $(document).keyup(this, function(event) {
+        if (self.context.listenKeyboard != false) {
+            return self.trigger('key.up', event);
+        }
+    });
+
+    window.onbeforeunload = function(e) {
+        if (self.option.clearUrl != undefined) {
+            $.post(self.option.clearUrl);
+        }
+    }
+}
+
+//初始化功能模块
+MarkControl.prototype.initModules = function(option) {
+    if (this.modules == undefined) {
+        this.modules = {};
+    }
+    var names = [];
+    var options = [];
+    for (var name in this.defaultModules) {
+        names.push(name);
+        options[name] = this.defaultModules[name];
+    }
+
+    if (option.modules != undefined) {
+        for (var name in option.modules) {
+            if (options[name] == undefined) {
+                names.push(name);
+                options[name] = {};
+            }
+            $.extend(options[name], option.modules[name]);
+        }
+    }
+
+    this.initModule(names, options, this.option.success);
+    //initModule(this, names, 0, options);
+}
+
+//指定初始化某个名称的模块
+MarkControl.prototype.initModule = function(names, options, success) {
+    for (var i in names) {
+        var name = names[i];
+        var option = options[name];
+        var moduleInit = name.replace(/-/g, '_');
+        if (option == undefined || typeof(option) != 'object') {
+            option = {};
+        }
+        option.markControl = this;
+        eval('this.modules[name]=' + moduleInit + '(option, function(){})');
+    }
+    if (success != undefined && typeof(success) == 'function') {
+        success();
+    }
+}
+
+function initModuleAsync(markControl, names, index, option) {
+    if (index < names.length) {
+        var name = names[index];
+        var moduleOption = option[name];
+        var moduleUrl = 'modules/' + name + '.js';
+        var moduleInit = name.replace(/-/g, '_');
+        var modules = markControl.modules;
+        if (modules[name] == undefined) {
+            if (typeof(moduleOption) != 'object') {
+                moduleOption = {};
+            }
+            moduleOption.markControl = markControl;
+            $.getScript(moduleUrl, function() {
+                var success = function() {
+                    initModule(markControl, names, index + 1, option);
+                }
+                eval('modules[name]=' + moduleInit + '(moduleOption, success)');
+            });
+        } else {
+            initModule(markControl, names, index + 1, option);
+        }
+    } else {
+        if (markControl.option.success != undefined && typeof(markControl.option.success) == 'function') {
+            markControl.option.success();
+        }
+    }
+}
+
+MarkControl.prototype.start = function(taskOption) {
+    taskOption.markControl = this;
+    var markControl = this;
+    taskOption.success = function() {
+        markControl.context.prefetchCallback = function() {
+            markControl.context.prefetchCallback = undefined;
+            markControl.getTask();
+        }
+        markControl.context.statusCallback = function() {
+            markControl.context.statusCallback = undefined;
+            markControl.prefetch();
+        }
+        markControl.getStatus();
+    };
+    taskOption.error = function(message) {
+        alert('初始化失败,请刷新页面重新加载');
+    };
+    this.taskControl = new TaskControl(taskOption);
+    this.taskControl.init();
+}
+
+//task预加载
+MarkControl.prototype.prefetch = function() {
+    var taskControl = this.taskControl;
+    var markControl = this;
+    var context = this.context;
+    var jsonBuilder = this.modules['json-builder'];
+
+    if (context.isFinish != true) {
+        if (taskControl.isFinish()) {
+            context.isFinish = true;
+        } else if (context.prefetchTask.length >= markControl.maxPrefetchCount) {
+            markControl.trigger('task.prefetch.success');
+        } else if (context.prefetching == false) {
+            context.prefetching = true;
+            markControl.trigger('task.prefetch.before');
+
+            taskControl.fetch(function(task) {
+                if (jsonBuilder != undefined) {
+                	jsonBuilder.build(task, function(error) {
+                        if (error) {
+                            context.prefetching = false;
+                            markControl.trigger('task.prefetch.error');
+                        } else {
+                            context.prefetchTask.push(task);
+                            context.prefetchStatus = undefined;
+                            context.prefetching = false;
+                            markControl.trigger('task.prefetch.success');
+                        }
+                    });
+                } else {
+                    context.prefetchTask.push(task);
+                    context.prefetchStatus = undefined;
+                    context.prefetching = false;
+                    markControl.trigger('task.prefetch.success');
+                }
+            }, function(task) {
+                context.prefetchStatus = task.message;
+                context.prefetching = false;
+                markControl.trigger('task.prefetch.none');
+            }, function() {
+                context.prefetching = false;
+                markControl.trigger('task.prefetch.none');
+            });
+        }
+    } else {
+        markControl.trigger('task.prefetch.finish');
+    }
+}
+
+MarkControl.prototype.getStatus = function() {
+    if (this.taskControl == undefined) {
+        return;
+    }
+    var self = this;
+    this.taskControl.status(function(status) {
+        self.context.status = status;
+        if (status != undefined) {
+            self.context.isFinish = status.blockTotalCount > 0 && status.blockTotalCount == (status.blockMarkedCount + status.blockExceptionCount);
+        }
+        if (self.context.statusCallback != undefined) {
+            self.context.statusCallback();
+        }
+        self.trigger('mark.status.change', status);
+    })
+}
+
+MarkControl.prototype.getTask = function() {
+    if (this.taskControl == undefined) {
+        return;
+    }
+    var markControl = this;
+    var context = this.context;
+    markControl.trigger('task.get.before');
+
+    if (context.isFinish == true) {
+        markControl.trigger('task.get.finish');
+        return;
+    } else if (context.task != undefined) {
+        markControl.trigger('task.get.success');
+        return;
+    } else if (context.waitTask != undefined) {
+        //优先选择因回评等操作处于等待状态的任务
+        context.task = context.waitTask;
+        context.waitTask = undefined;
+        markControl.trigger('task.get.success');
+    } else if (context.prefetchTask.length > 0) {
+        //判断是否有任务已预加载完毕
+        context.task = context.prefetchTask.shift();
+        markControl.trigger('task.get.success');
+    } else if (context.prefetchStatus != undefined) {
+        //判断是否有在无预加载任务的情况下的消息提示
+        markControl.trigger('task.get.none', context.prefetchStatus);
+    } else {
+        markControl.trigger('task.get.none');
+    }
+}
+
+MarkControl.prototype.getHistory = function(data) {
+    if (this.taskControl == undefined) {
+        return;
+    }
+    this.taskControl.history(data, function(result) {
+        data.result = result;
+        this.option.markControl.trigger('history.get.success', data);
+    }, function(message) {
+        data.message = message;
+        this.option.markControl.trigger('history.get.error', data);
+    });
+}
+
+MarkControl.prototype.setTask = function(task) {
+    var imageBuilder = this.modules['image-builder'];
+    var self = this;
+
+    if (this.context.task != undefined && !this.context.task.previous && this.context.waitTask == undefined) {
+        this.context.waitTask = this.context.task;
+    }
+    this.trigger('task.get.before');
+    if (imageBuilder != undefined && task != undefined) {
+        imageBuilder.build(task, function(error) {
+            self.context.task = task;
+            self.trigger('task.get.success');
+        });
+    } else {
+        self.context.task = task;
+        if (task != undefined) {
+            this.trigger('task.get.success');
+        }
+    }
+}
+
+MarkControl.prototype.submitTask = function(submitUrl) {
+    var task = this.context.task;
+    var markControl = this;
+    var submitUrl = submitUrl != undefined && submitUrl.length > 0 ? submitUrl : this.option.submitUrl;
+
+    if (task != undefined && this.context.submitting != true) {
+        //开启强制标记
+        if (this.option.forceSpecialTag === true) {
+            if (task.tagList == undefined || task.tagList == null || task.tagList.length <= 0) {
+                markControl.trigger('task.submit.forceSpecialTag');
+                return;
+            }
+        }
+
+        var submitObj = {
+            statusValue: task.statusValue,
+            studentId: task.studentId,
+            libraryId: task.libraryId,
+            totalScore: task.totalScore,
+            scoreList: task.scoreList,
+            trackList: [],
+            tagList: task.tagList,
+            spent: new Date().getTime() - task.spent
+        }
+
+        this.trigger('task.submit.before');
+
+        if (this.taskControl != undefined) {
+            //已定义任务引擎
+            this.taskControl.submit(submitObj, function(status) {
+                if (status != undefined && status.valid == true) {
+                    markControl.context.status = status;
+                    markControl.trigger('mark.status.change', status);
+                }
+                if (task.previous == true) {
+                    markControl.trigger('history.submit.success', task);
+                } else {
+                    markControl.trigger('task.submit.success');
+                }
+            }, function(message) {
+                markControl.trigger('task.submit.error', message);
+            });
+        } else if (submitUrl != undefined && submitUrl.length > 0) {
+            //未定义任务引擎,依赖定义/传入的提交地址
+            $.ajax({
+                url: submitUrl,
+                type: 'POST',
+                data: JSON.stringify(submitObj),
+                dataType: "json",
+                contentType: 'application/json;charset=utf-8',
+                success: function(result) {
+                    if (result.success == true) {
+                        markControl.trigger('task.submit.success');
+                    } else {
+                        markControl.trigger('task.submit.error', result.message);
+                    }
+                },
+                error: function(message) {
+                    markControl.trigger('task.submit.error', message);
+                }
+            });
+        } else {
+            markControl.trigger('task.submit.success');
+        }
+    }
+}
+MarkControl.prototype.addNavGroup = function(title, content) {
+    var self = this;
+    self.navNumber++;
+    var nav = $('<a href="#"><span>' + title + '</span></a>').appendTo(self.container.centerNavbar);
+    nav.attr('data-number', self.navNumber);
+    content.attr('data-number', self.navNumber);
+    nav.click(function() {
+        self.container.centerNavbar.find('a').removeClass('selected');
+        $(this).addClass('selected');
+
+        for (var i in self.container.centerList) {
+            var dom = self.container.centerList[i];
+            if (dom.attr('data-number') == $(this).attr('data-number')) {
+                dom.show();
+            } else {
+                dom.hide();
+            }
+        }
+    });
+    self.container.centerList.push(content);
+    return nav;
+}
+
+//默认要初始化的模块名称
+MarkControl.prototype.defaultModules = {
+    'json-builder': {
+    	answerServer : '${answerServer}'
+    }
+};
+
+MarkControl.prototype.main_row_dom = '<div class="row-fluid" style="background-image:url({staticServer}/mark-new/images/background.jpg)"></div>';
+
+MarkControl.prototype.sidebar_dom = '<div class="mark-sidebar span2 hide"></div>';
+
+MarkControl.prototype.center_dom = '<div class="center-content span12"></div>';
+
+MarkControl.prototype.center_header_dom = '<div class="row-fluid"><div class="header"><p class="tips">\
+<em>\
+<a href="javascript:void(0)" id="assistant-button" class="btn"><i class="icon-wrench"></i> 小助手</a></em>\
+<a class="useinfo" href="#"><i class="icon-user icon-white"></i><i id="mark-user-name"></i></a>\
+<a class="logout" id="logout-link" href="{logoutUrl}"><i class="icon-off icon-white"></i> <i id="logout-title">退出</i></a>\
+</p></div></div>';
+
+MarkControl.prototype.center_content_dom = '<div class="row-fluid"></div>';
+
+MarkControl.prototype.image_content_dom = '<div class="image-content span12"></div>';
+
+MarkControl.prototype.assistant_dom = '<div class="popover bottom assistant"><div class="arrow"></div></div>';
+
+MarkControl.prototype.mark_function_dom = '<h3 class="popover-title">评卷功能</h3>\
+<div class="popover-content"><p id="function-list" class="popover-list">\
+</p></div>';
+
+//其他通用方法
+String.prototype.startWith = function(prefix) {
+    return this.indexOf(prefix) === 0;
+}
+String.prototype.endWith = function(suffix) {
+    return this.match(suffix + "$") == suffix;
+};
+
+//日期格式化
+Date.prototype.format = function(fmt) { //author: meizz 
+    var o = {
+        "M+": this.getMonth() + 1, //月份 
+        "d+": this.getDate(), //日 
+        "h+": this.getHours(), //小时 
+        "m+": this.getMinutes(), //分 
+        "s+": this.getSeconds(), //秒 
+        "q+": Math.floor((this.getMonth() + 3) / 3), //季度 
+        "S": this.getMilliseconds() //毫秒 
+    };
+    if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
+    for (var k in o) {
+        if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
+    }
+    return fmt;
+}
+
+function parseNumber(number) {
+    return Math.round(parseFloat(number) * 10) / 10;
+}
+
+function numberAdd(n1, n2) {
+    return (n1 * 10 + n2 * 10) / 10;
+}
+
+function getDom(content, markControl) {
+    if (markControl != undefined && markControl.option.staticServer != undefined) {
+        content = content.replace(/{staticServer}/g, markControl.option.staticServer);
+    }
+    if (markControl != undefined && markControl.option.logoutUrl != undefined) {
+        content = content.replace(/{logoutUrl}/g, markControl.option.logoutUrl);
+    }
+    return $(content);
+}
+
+function isArray(obj) {
+    return obj != undefined && Object.prototype.toString.call(obj) === '[object Array]';
+}

+ 18 - 0
stmms-web/src/main/webapp/static/mark-json/js/rich-text-builder.js

@@ -0,0 +1,18 @@
+//富文本json解析模块
+var rich_text_dom = '<div class="wp"><div class="rich-text"></div></div>';
+var sectionsch_text_dom = '<p></p>';
+function RichTextBuilder(jsonText) {
+	var dom = rich_text_dom;
+	var questionArray = JSON.parse(jsonText);
+	 for (var i = 0; i < questionArray.length; i++) {
+		 var question = questionArray[i];
+		 var title = '<span>题号:'+ question.mainNumber+'-'+question.subNumber +'<span><br/>';
+		 var body = question.body.sections;
+		 for(var j = 0; j < body.length; j++){
+			 
+		 }
+		 
+	 }
+	 return dom;
+}
+