Ver código fonte

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	apps/practise/practiselog/models.py
jiaweiqi 3 anos atrás
pai
commit
761f14fd8f

+ 13 - 0
apps/api/admin/exampaper/urls.py

@@ -0,0 +1,13 @@
+# coding=utf-8
+
+from django.conf.urls import url, include
+from rest_framework.routers import SimpleRouter
+
+from .views import *
+
+urlpatterns = [
+]
+
+router = SimpleRouter()
+router.register(r'', ExamPaperViewSet)
+urlpatterns += router.urls

+ 42 - 0
apps/api/admin/exampaper/views.py

@@ -0,0 +1,42 @@
+# coding=utf-8
+
+from django.db import transaction
+from rest_framework.views import APIView
+from utils.permission import IsAdministrator
+from utils import response_error, response_ok
+from utils.custom_modelviewset import CustomModelViewSet
+from apps.system.models import SysLog
+
+from apps.examination.exampaper.models import ExamPaper
+from apps.examination.exampaper.filters import ExamPaperFilter
+from apps.examination.exampaper.serializers import ExamPaperSerializer
+
+
+class ExamPaperViewSet(CustomModelViewSet):
+    permission_classes = [IsAdministrator, ]
+    queryset = ExamPaper.objects.filter(delete=False)
+    serializer_class = ExamPaperSerializer
+
+    def filter_queryset(self, queryset):
+        f = ExamPaperFilter(self.request.GET, queryset=queryset)
+        return f.qs
+
+    def perform_create(self, serializer):
+        super(ExamPaperViewSet, self).perform_create(serializer)
+        instance = serializer.instance
+        validated_data = serializer.validated_data
+        SysLog.objects.addnew(self.request.user, SysLog.INSERT, u'添加试卷[%s],id=%d' % (instance.name, instance.id), validated_data)
+
+    def perform_update(self, serializer):
+        super(ExamPaperViewSet, self).perform_update(serializer)
+        instance = serializer.instance
+        validated_data = serializer.validated_data
+        SysLog.objects.addnew(self.request.user, SysLog.UPDATE, u'修改试卷[%s],id=%d' % (instance.name, instance.id), validated_data)
+
+    def destroy(self, request, *args, **kwargs):
+        with transaction.atomic():
+            instance = self.get_object()
+            instance.delete = True
+            instance.save()
+            SysLog.objects.addnew(self.request.user, SysLog.DELETE, u'删除试卷[%s],id=%d' % (instance.name, instance.id))
+        return response_ok()

+ 0 - 1
apps/api/admin/examquestion/views.py

@@ -23,7 +23,6 @@ class ExamQuestionViewSet(CustomModelViewSet):
         return f.qs
 
     def destroy(self, request, *args, **kwargs):
-        print(444444444)
         with transaction.atomic():
             instance = self.get_object()
             instance.delete = True

+ 1 - 0
apps/api/admin/urls.py

@@ -15,4 +15,5 @@ urlpatterns = [
     url(r'^subject/', include('apps.api.admin.subject.urls')),
 
     url(r'^examquestion/', include('apps.api.admin.examquestion.urls')),
+    url(r'^exampaper/', include('apps.api.admin.exampaper.urls')),
 ]

+ 13 - 0
apps/examination/exampaper/filters.py

@@ -0,0 +1,13 @@
+# coding=utf-8
+import django_filters
+
+from .models import ExamPaper
+
+class ExamPaperFilter(django_filters.FilterSet):
+    name = django_filters.CharFilter(field_name="name", lookup_expr="icontains")
+    subject = django_filters.CharFilter(field_name='subject_id')
+    type = django_filters.CharFilter(field_name='type')
+
+    class Meta:
+        model = ExamPaper
+        fields = "__all__"

+ 86 - 0
apps/examination/exampaper/models.py

@@ -1,9 +1,12 @@
 # coding=utf-8
 
+import random
 from django.db import models
 from django.utils import timezone
 from django.conf import settings
 
+from utils.exceptions import CustomError
+
 from apps.foundation.models import Subject
 from apps.examination.examquestion.models import ExamQuestion
 
@@ -64,10 +67,93 @@ class ExamPaper(models.Model):
         verbose_name = u"试卷管理"
         default_permissions = ()
 
+    def clear_detail(self):
+        ExamPaperDetail.objects.filter(main=self).update(delete=True)
+
+    def generate_detail(self):
+        begin_order = 1
+        if self.single_simple_count:
+            self._generate_detail(self.single_simple_count, ExamQuestion.SINGLE, ExamQuestion.SIMPLE, begin_order)
+            begin_order += self.single_simple_count
+        if self.single_mid_count:
+            self._generate_detail(self.single_mid_count, ExamQuestion.SINGLE, ExamQuestion.MID, begin_order)
+            begin_order += self.single_mid_count
+        if self.single_hard_count:
+            self._generate_detail(self.single_hard_count, ExamQuestion.SINGLE, ExamQuestion.HARD, begin_order)
+            begin_order += self.single_hard_count
+        if self.multiple_simple_count:
+            self._generate_detail(self.multiple_simple_count, ExamQuestion.MULTIPLE, ExamQuestion.SIMPLE, begin_order)
+            begin_order += self.multiple_simple_count
+        if self.multiple_mid_count:
+            self._generate_detail(self.multiple_mid_count, ExamQuestion.MULTIPLE, ExamQuestion.MID, begin_order)
+            begin_order += self.multiple_mid_count
+        if self.multiple_hard_count:
+            self._generate_detail(self.multiple_hard_count, ExamQuestion.MULTIPLE, ExamQuestion.HARD, begin_order)
+            begin_order += self.multiple_hard_count
+        if self.fill_simple_count:
+            self._generate_detail(self.fill_simple_count, ExamQuestion.FILL, ExamQuestion.SIMPLE, begin_order)
+            begin_order += self.fill_simple_count
+        if self.fill_mid_count:
+            self._generate_detail(self.fill_mid_count, ExamQuestion.FILL, ExamQuestion.MID, begin_order)
+            begin_order += self.fill_mid_count
+        if self.fill_hard_count:
+            self._generate_detail(self.fill_hard_count, ExamQuestion.FILL, ExamQuestion.HARD, begin_order)
+            begin_order += self.fill_hard_count
+        if self.judgment_simple_count:
+            self._generate_detail(self.judgment_simple_count, ExamQuestion.JUDGMENT, ExamQuestion.SIMPLE, begin_order)
+            begin_order += self.judgment_simple_count
+        if self.judgment_mid_count:
+            self._generate_detail(self.judgment_mid_count, ExamQuestion.JUDGMENT, ExamQuestion.MID, begin_order)
+            begin_order += self.judgment_mid_count
+        if self.judgment_hard_count:
+            self._generate_detail(self.judgment_hard_count, ExamQuestion.JUDGMENT, ExamQuestion.HARD, begin_order)
+            begin_order += self.judgment_hard_count
+
+    def update_count(self):
+        self.single_total_count = self.single_simple_count + self.single_mid_count + self.single_hard_count
+        self.multiple_total_count = self.multiple_simple_count + self.multiple_mid_count + self.multiple_hard_count
+        self.fill_total_count = self.fill_simple_count + self.fill_mid_count + self.fill_hard_count
+        self.judgment_total_count = self.judgment_simple_count + self.judgment_mid_count + self.judgment_hard_count
+        self.single_total_scores = self.single_scores * self.single_total_count
+        self.multiple_total_scores = self.multiple_scores * self.multiple_total_count
+        self.fill_total_scores = self.fill_scores * self.fill_total_count
+        self.judgment_total_scores = self.judgment_scores * self.judgment_total_count
+        self.question_total_count = self.single_total_count + self.multiple_total_count + self.fill_total_count + self.judgment_total_count
+        self.question_total_scores = self.single_total_scores + self.multiple_total_scores + self.fill_total_scores + self.judgment_total_scores
+        return self
+
+    def _generate_detail(self, count, type, difficulty, begin_order):
+        questions = ExamQuestion.objects.filter(
+            chapter__subject=self.subject,
+            difficulty = difficulty,
+            type = type,
+            delete=False
+        )
+        questions_count = questions.count()
+        if questions_count < count:
+            raise CustomError(u'[%s][%s]数量不足!' % (ExamQuestion.DIFFICULTY_CHOICES[difficulty-1][1], ExamQuestion.TYPE_JSON[type-1][1]))
+        question_ids = questions.values_list('id', flat=True)
+
+        rows = random.sample(question_ids, count)
+        random.shuffle(rows)
+
+        data = []
+        for row in rows:
+            item = ExamPaperDetail(
+                main=self,
+                question_id=row,
+                order=begin_order
+            )
+            begin_order += 1
+            data.append(item)
+
+        ExamPaperDetail.objects.bulk_create(data)
+
 class ExamPaperDetail(models.Model):
     main = models.ForeignKey(ExamPaper, verbose_name=u"试卷", on_delete=models.PROTECT)
     question = models.ForeignKey(ExamQuestion, verbose_name=u"试题", on_delete=models.PROTECT)
     order = models.IntegerField(verbose_name=u'序号', editable=False)
+    delete = models.BooleanField(verbose_name=u'删除', default=False, editable=False)
 
     class Meta:
         db_table = "exam_paper_detail"

+ 73 - 0
apps/examination/exampaper/serializers.py

@@ -0,0 +1,73 @@
+# coding=utf-8
+
+from rest_framework import serializers
+from .models import ExamPaper
+from utils.exceptions import CustomError
+
+class ExamPaperSerializer(serializers.ModelSerializer):
+    subject_name = serializers.CharField(source='subject.name', read_only=True)
+
+    class Meta:
+        model = ExamPaper
+        fields = "__all__"
+
+    def validate(self, attrs):
+        if 'subject' in attrs and attrs['subject'].delete:
+            raise CustomError(u'科目[%s]已被删除!' % attrs['subject'].name)
+        return attrs
+
+    def create(self, validated_data):
+        validated_data['create_user'] = self.context['request'].user
+        instance = super(ExamPaperSerializer, self).create(validated_data)
+        instance.update_count()
+        instance.save()
+
+        instance.generate_detail()
+        return instance
+
+    def update(self, instance, validated_data):
+        old_count = []
+        old_count.append(instance.single_simple_count)
+        old_count.append(instance.single_mid_count)
+        old_count.append(instance.single_hard_count)
+        old_count.append(instance.multiple_simple_count)
+        old_count.append(instance.multiple_mid_count)
+        old_count.append(instance.multiple_hard_count)
+        old_count.append(instance.fill_simple_count)
+        old_count.append(instance.fill_mid_count)
+        old_count.append(instance.fill_hard_count)
+        old_count.append(instance.judgment_simple_count)
+        old_count.append(instance.judgment_mid_count)
+        old_count.append(instance.judgment_hard_count)
+
+        if instance.delete:
+            raise CustomError(u'试卷[%s]已经被删除,禁止操作' % instance.name)
+        instance = super(ExamPaperSerializer, self).update(instance, validated_data)
+        instance.update_count()
+        instance.save()
+
+        new_count = []
+        new_count.append(instance.single_simple_count)
+        new_count.append(instance.single_mid_count)
+        new_count.append(instance.single_hard_count)
+        new_count.append(instance.multiple_simple_count)
+        new_count.append(instance.multiple_mid_count)
+        new_count.append(instance.multiple_hard_count)
+        new_count.append(instance.fill_simple_count)
+        new_count.append(instance.fill_mid_count)
+        new_count.append(instance.fill_hard_count)
+        new_count.append(instance.judgment_simple_count)
+        new_count.append(instance.judgment_mid_count)
+        new_count.append(instance.judgment_hard_count)
+
+        change_detail = False
+        for i in range(0, len(old_count)):
+            if old_count[i] != new_count[i]:
+                change_detail = True
+                break
+
+        if change_detail:
+            instance.clear_detail()
+            instance.generate_detail()
+
+        return instance

+ 3 - 1
apps/examination/examquestion/models.py

@@ -50,17 +50,19 @@ class ExamQuestionOption(models.Model):
     main = models.ForeignKey(ExamQuestion, verbose_name=u"试题", on_delete=models.PROTECT)
     content = models.TextField(verbose_name=u"内容")
     right = models.BooleanField(verbose_name=u'正确答案', default=False)
+    delete = models.BooleanField(verbose_name=u'删除', default=False, editable=False)
 
     class Meta:
         db_table = "exam_question_option"
         ordering = ['id']
-        verbose_name = u"试题选项"
+        verbose_name = u"选择题选项" # 单选、多选
         default_permissions = ()
 
 class ExamQuestionFill(models.Model):
     main = models.ForeignKey(ExamQuestion, verbose_name=u"试题", on_delete=models.PROTECT)
     content = models.TextField(verbose_name=u"内容")
     order = models.IntegerField(verbose_name=u'排序')
+    delete = models.BooleanField(verbose_name=u'删除', default=False, editable=False)
 
     class Meta:
         db_table = "exam_question_fill"

+ 89 - 0
apps/examination/examquestion/serializers.py

@@ -1,18 +1,107 @@
 # coding=utf-8
+import json
 from rest_framework import serializers
+from utils.exceptions import CustomError
 from .models import *
 
 class ExamQuestionSerializer(serializers.ModelSerializer):
     subject = serializers.CharField(source='chapter.subject.id', read_only=True)
+    subject_text = serializers.CharField(source='chapter.subject.name', read_only=True)
+    chapter_text = serializers.CharField(source='chapter.name', read_only=True)
     create_user_text = serializers.CharField(source='create_user.username', read_only=True)
     type_text = serializers.CharField(source='get_type_display', read_only=True)
     difficulty_text = serializers.CharField(source='get_difficulty_display', read_only=True)
+    judgment = serializers.SerializerMethodField()
+    items = serializers.SerializerMethodField()
+
+    def get_items(self, obj):
+        if obj.type == ExamQuestion.SINGLE or obj.type == ExamQuestion.MULTIPLE:
+            rows = ExamQuestionOption.objects.filter(main=obj, delete=False)
+            return ExamQuestionOptionSerializer(rows,many=True).data
+        elif obj.type == ExamQuestion.FILL:
+            rows = ExamQuestionFill.objects.filter(main=obj, delete=False)
+            return ExamQuestionFillSerializer(rows, many=True).data
+        else:
+            return []
+
+    def get_judgment(self, obj):
+        if obj.judgment:
+            return "1"
+        return "0"
 
     class Meta:
         model = ExamQuestion
         fields = '__all__'
 
     def create(self, validated_data):
+        if 'judgment' in self.initial_data:
+            validated_data['judgment'] = True if self.initial_data['judgment'] == '1' else False
         validated_data['create_user'] = self.context['request'].user
         instance = super(ExamQuestionSerializer, self).create(validated_data)
+        rows = json.loads(self.initial_data['rows'])
+        for row in rows:
+            if not row['content']:
+                raise CustomError('答案不能为空,请填写答案!')
+            if instance.type == ExamQuestion.SINGLE or instance.type == ExamQuestion.MULTIPLE:
+                ExamQuestionOption.objects.create(
+                    main=instance,
+                    content=row['content'],
+                    right=row['right'],
+                )
+            elif instance.type == ExamQuestion.FILL:
+                ExamQuestionFill.objects.create(
+                    main=instance,
+                    content=row['content'],
+                    order=row['order'],
+                )
+        return instance
+
+    def update(self, instance, validated_data):
+        if 'judgment' in self.initial_data:
+            validated_data['judgment'] = True if self.initial_data['judgment'] == '1' else False
+        instance = super(ExamQuestionSerializer, self).update(instance, validated_data)
+        rows = json.loads(self.initial_data['rows'])
+        ExamQuestionOption.objects.filter(main=instance).update(delete=True, right=False)
+        for row in rows:
+            if not row['content']:
+                raise CustomError('答案不能为空,请填写答案!')
+            if instance.type == ExamQuestion.SINGLE or instance.type == ExamQuestion.MULTIPLE:
+                if row['id']:
+                    # 更新
+                    ExamQuestionOption.objects.filter(main=instance, id=row['id']).update(delete=False,
+                                                                                          content=row['content'],
+                                                                                          right=row['right'])
+                else:
+                    # 新增
+                    ExamQuestionOption.objects.create(
+                        main=instance,
+                        content=row['content'],
+                        right=row['right'],
+                    )
+            elif instance.type == ExamQuestion.FILL:
+                ExamQuestionFill.objects.filter(main=instance).update(delete=True)
+                if row['id']:
+                    # 更新
+                    ExamQuestionFill.objects.filter(main=instance, id=row['id']).update(delete=False,
+                                                                                        content=row['content'],
+                                                                                        order=row['order'])
+                else:
+                    # 新增
+                    ExamQuestionFill.objects.create(
+                        main=instance,
+                        content=row['content'],
+                        order=row['order'],
+                    )
         return instance
+
+class ExamQuestionOptionSerializer(serializers.ModelSerializer):
+
+    class Meta:
+        model = ExamQuestionOption
+        fields = '__all__'
+
+class ExamQuestionFillSerializer(serializers.ModelSerializer):
+
+    class Meta:
+        model = ExamQuestionFill
+        fields = '__all__'

+ 1 - 1
apps/knowledge/models.py

@@ -11,7 +11,7 @@ class KnowledgeBase(models.Model):
     name = models.CharField(max_length=200, verbose_name=u"名称")
     feature = models.TextField(verbose_name=u"识别特征")
     desc = models.TextField(verbose_name=u"备注", null=True, blank=True)
-    create_user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=u'录入人', editable=False, on_delete=models.PROTECT)
+    create_user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=u'录入人',related_name='knowledge_base_user', editable=False, on_delete=models.PROTECT)
     create_time = models.DateTimeField(verbose_name=u"录入时间", default=timezone.now, editable=False)
     delete = models.BooleanField(verbose_name=u'删除', default=False, editable=False)
 

+ 3 - 3
apps/practise/practiselog/models.py

@@ -20,8 +20,8 @@ class PractiseLog(models.Model):
     create_time = models.DateTimeField(verbose_name=u"开始时间", default=timezone.now, editable=False)
     submit_time = models.DateTimeField(verbose_name=u"结束时间", null=True, blank=True)
 
-    begin_answer = models.ForeignKey('PractiseAnswerLog', verbose_name=u"开始习题", null=True, editable=False, on_delete=models.PROTECT, related_name='practise_log_ref_begin_answer_id')
-    end_answer = models.ForeignKey('PractiseAnswerLog', verbose_name=u"结束习题", null=True, editable=False, on_delete=models.PROTECT, related_name='practise_log_ref_end_answer_id')
+    begin_answer = models.ForeignKey('PractiseAnswerLog', verbose_name=u"开始习题", null=True, related_name='practise_log_begin_answer', editable=False, on_delete=models.PROTECT)
+    end_answer = models.ForeignKey('PractiseAnswerLog', verbose_name=u"结束习题", null=True, related_name='practise_log_end_answer', editable=False, on_delete=models.PROTECT)
     right_count = models.IntegerField(verbose_name=u'正确数量', default=0, editable=False)
     wrong_count = models.IntegerField(verbose_name=u'错误数量', default=0, editable=False)
     total_count = models.IntegerField(verbose_name=u'总数量', default=0, editable=False)
@@ -70,4 +70,4 @@ class PractiseAnswerFillLog(models.Model):
         db_table = "practise_answer_fill_log"
         ordering = ['order', 'id']
         verbose_name = u"填空题回答"
-        default_permissions = ()
+        default_permissions = ()

+ 131 - 0
uis/admin/examquestion/details.html

@@ -0,0 +1,131 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <title>试题管理详情</title>
+    <meta name="renderer" content="webkit">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="viewport"
+          content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
+    <link rel="stylesheet" href="../../layuiadmin/layui/css/layui.css" media="all">
+    <link rel="stylesheet" href="../../layuiadmin/style/admin.css" media="all">
+    <style type="text/css">
+        .title {
+            width: 140px;
+            background: #efefef;
+        }
+    </style>
+</head>
+<body>
+
+<div class="layui-fluid">
+    <div class="layui-card">
+        <div class="layui-card-body" pad15>
+            <div class="layui-row layui-col-space15">
+                <div class="layui-col-md12">
+
+                    <div id="print_div">
+                        <table class="layui-table">
+
+                            <tr>
+                                <td class="title">科目:</td>
+                                <td id="subject_text" class="cell"></td>
+                                <td class="title">章节:</td>
+                                <td id="chapter_text" class="cell"></td>
+                            </tr>
+
+                            <tr>
+                                <td class="title">题型:</td>
+                                <td id="type_text" class="cell"></td>
+                                <td class="title">难度:</td>
+                                <td id="difficulty_text" class="cell"></td>
+                            </tr>
+                            <tr>
+                                <td class="title">分数:</td>
+                                <td id="scores" class="cell"></td>
+                                <td class="title"></td>
+                                <td class="cell"></td>
+                            </tr>
+                            <tr>
+                                <td class="title">录入人:</td>
+                                <td id="create_user_text" class="cell"></td>
+                                <td class="title">录入时间:</td>
+                                <td id="create_time" class="cell"></td>
+                            </tr>
+                            <tr>
+                                <td class="title">题目:</td>
+                                <td colspan="3" id="title" class="cell"></td>
+                            </tr>
+
+                            <tr id="id_analysis">
+                                <td class="title">解析:</td>
+                                <td colspan="3" id="analysis" class="cell"></td>
+                            </tr>
+                        </table>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<script src="../../layuiadmin/layui/layui.js?t=1"></script>
+<script>
+    layui.config({
+        base: '../../../layuiadmin/' //静态资源所在路径
+    }).extend({
+        index: 'lib/index' //主入口模块
+    }).use(['index', 'table'], function () {
+        var $ = layui.$;
+        var editdata = JSON.parse(JSON.stringify(parent.layui.table.editdata)); // 框架有Bug所以这么转换
+        $('.cell').each(function (index, element) {
+            element.innerHTML += (editdata[element.id] || '')
+        });
+        var answer = ''
+        if (editdata.type < 4) {
+            // 单选、多选、填空
+            var answers = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
+                'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
+            var items = editdata.items, answer_title = '', right = ''
+            for (var i = 0; i < items.length; i++) {
+                if (editdata.type === 3) {
+                    answer_title = '空白' + (i + 1)
+                } else {
+                    answer_title = answers[i]
+                }
+
+                if (items[i].right) {
+                    right = '<img src="../right.png" width="15px" height="15px" style="margin-right: 3px">'
+                } else {
+                    right = '<span style="margin-right: 17px"></span>'
+                }
+                if (i === 0) {
+                    answer = "<tr>\n" +
+                        "<td class=\"title\">选项/答案:</td>\n" +
+                        "<td colspan=\"3\" class=\"cell\">" + right + answer_title + "、" + items[i].content + "</td>\n" +
+                        "</tr>"
+                } else {
+                    answer = "<tr>\n" +
+                        "<td></td>\n" +
+                        "<td colspan=\"3\" class=\"cell\">" + right + answer_title + "、" + items[i].content + "</td>\n" +
+                        "</tr>"
+                }
+                $('#id_analysis').before(answer)
+            }
+        } else {
+            // 判断
+            if (editdata.judgment === "1") {
+                right = '<img src="../right.png" width="15px" height="15px" style="margin-right: 3px">正确'
+            } else {
+                right = '<img src="../wrong.png" style="margin-right: 3px">错误'
+            }
+            answer = "<tr>\n" +
+                "<td class=\"title\">选项/答案:</td>\n" +
+                "<td colspan=\"3\" class=\"cell\">" + right + "</td>\n" +
+                "</tr>"
+            $('#id_analysis').before(answer)
+        }
+    });
+</script>
+</body>
+</html>

+ 237 - 39
uis/admin/examquestion/edit.html

@@ -28,7 +28,7 @@
                     <div class="layui-inline">
                         <label class="layui-form-label"><font color='red' size="4">*</font>章节:</label>
                         <div class="layui-input-inline">
-                            <select name="chapter" id="id_chapter" lay-verify="required" >
+                            <select name="chapter" id="id_chapter" lay-verify="required">
                                 <option value="">请选择章节</option>
                             </select>
                         </div>
@@ -38,7 +38,7 @@
                     <div class="layui-inline">
                         <label class="layui-form-label"><font color='red' size="4">*</font>题型:</label>
                         <div class="layui-input-inline">
-                            <select name="type" lay-verify="required">
+                            <select name="type" lay-verify="required" lay-filter="typeChange" id="id_type">
                                 <option value="">请选择题型</option>
                                 <option value="1">单选题</option>
                                 <option value="2">多选题</option>
@@ -72,6 +72,22 @@
                         <textarea class="layui-textarea" placeholder="请输内容" id="content_demo"></textarea>
                     </div>
                 </div>
+                <div class="layui-form-item">
+                    <label class="layui-form-label"><font color='red' size="4">*</font>选项:</label>
+                    <div class="layui-input-block" id="id_table">
+                        <div class="layui-btn layui-btn-sm layui-btn-normal" id="add_answer">
+                            <i class="layui-icon layui-icon-add "></i> 添加答案
+                        </div>
+                        <div style="height: 5px"></div>
+                        <div id="tableRes" class="table-overlay" style="align-items: center">
+                            <table id="dataTable" lay-filter="dataTable"></table>
+                        </div>
+                    </div>
+                    <div class="layui-input-block layui-hide" id="id_panduan">
+                        <input type="radio" name="judgment" value="1" title="正确" lay-verify="required">
+                        <input type="radio" name="judgment" value="0" title="错误" lay-verify="required">
+                    </div>
+                </div>
                 <div class="layui-form-item">
                     <label class="layui-form-label">解析:</label>
                     <div class="layui-input-block">
@@ -95,10 +111,10 @@
         base: '../../../layuiadmin/' //静态资源所在路径
     }).extend({
         index: 'lib/index',
-    }).use(['index', 'form', 'utils', 'upload', 'layedit'], function () {
+    }).use(['index', 'form', 'utils', 'table', 'layedit'], function () {
         var $ = layui.$
             , admin = layui.admin
-            , upload = layui.upload
+            , table = layui.table
             , layedit = layui.layedit
             , form = layui.form;
 
@@ -126,6 +142,7 @@
                 layedit.setContent(editIndex, editdata.title, false);
                 chapter_id = editdata.chapter;
                 subjectChange(editdata.subject)
+                typeChange(editdata.type.toString(), editdata.items)
             }
         };
         var subjectChange = function (value) {
@@ -155,49 +172,43 @@
                 , '|', 'fullScreen'
             ],
         });
-
-        // function compareStr(oldStr, newStr){
-        //     //如果字符串相同的话就不在比较
-        //     if(oldStr === newStr) return [];
-        //     //   比较旧的字符串中的src是否还包含在新的字符串中,如果没有表示需要删掉该file
-        //     let srcReg = /src=[\'\"]?([^\'\"]*)[\'\"]?/i // 匹配字符串中的src
-        //     let imgReg = /<img.*?(?:>|\/>)/gi //匹配img标签
-        //     let videoReg = /<video.*?(?:>|\/>)/gi //匹配video标签
-        //     let arrImg = oldStr.match(imgReg);
-        //     let arrVideo = oldStr.match(videoReg);
-        //     let tagArr = [...arrImg, ...arrVideo];
-        //     let srcArr = [];
-        //     //获取旧字符串中的所有src,push进srcArr数组。
-        //     if(tagArr && tagArr.length > 0){
-        //         tagArr.forEach(item=>{
-        //             let src = item.match(srcReg);
-        //             srcArr.push(src[1])
-        //         })
-        //     }
-        //     //遍历srcArr数组,并与新字符串对比,获取需要删除的src(新字符串中不包含旧的路径需要删除)
-        //     let delArr = [];
-        //     if(srcArr && srcArr.length > 0){
-        //         srcArr.forEach(item=>{
-        //             if(newStr.indexOf(item) === -1){
-        //                 delArr.push(item)
-        //             }
-        //         })
-        //     }
-        //     return delArr
-        // }
-
         form.render(null, 'component-form-element');
 
         var url = id ? '/admin/examquestion/' + id + "/" : '/admin/examquestion/',
             method = id ? 'put' : 'post'
 
         form.on('submit(component-form-element)', function (data) {
-            //layer.msg(JSON.stringify(data.field));
-            //富文本中的内容
             data.field.title = layedit.getContent(editIndex);
-            // let newContent = data.field.content;
-            // let delArr = compareStr(oldContent, newContent);
+            var id_type = data.field.type
+            var oldData = table.cache[layTableId], rows = [], item = {}, error_msg = '', checked = 0
+
+            for (var i = 0; i < oldData.length; i++) {
+                //单选题、多选、填空
+                if (!oldData[i].content) {
+                    error_msg = '选项' + oldData[i].answer + '请填写答案'
+                    break
+                }
+                if (oldData[i].LAY_CHECKED) {
+                    checked += 1
+                }
+                item = {
+                    id: oldData[i].id,
+                    content: oldData[i].content,
+                    right: oldData[i].LAY_CHECKED ? 1 : 0,
+                    order: oldData[i].LAY_TABLE_INDEX,
+                }
+                rows.push(item)
 
+            }
+            if (error_msg) {
+                layer.msg(error_msg, {icon: 2});
+                return false
+            }
+            if (!checked && id_type < 3) {
+                layer.msg('请选择一个正确答案!', {icon: 2});
+                return false
+            }
+            data.field.rows = JSON.stringify(rows)
             admin.req({
                 url: url
                 , data: data.field
@@ -226,6 +237,193 @@
         parent.layui.submitChild = function () {
             $("#id_save").click();
         };
+        /////////////////////table
+        var tbWidth = $("#tableRes").width();
+        var cols = [
+            {title: '正确答案', type: 'radio', width: '7%',},
+            {field: 'answer', title: '选项', width: '7%',},
+            {field: 'content', title: '答案', edit: 'text', width: '50%',},
+            {
+                field: 'id', title: '操作', width: '20%', templet: function (d) {
+                    return '<a class="layui-btn layui-btn-xs layui-btn-danger" lay-event="del" lay-id="' + d.id + '"><i class="layui-icon layui-icon-delete"></i>删除</a>';
+                }
+            }
+        ]
+        var layTableId = "layTable";
+        var tableIns = table.render({
+            elem: '#dataTable',
+            id: layTableId,
+            data: [],
+            width: tbWidth,
+            page: false,
+            limit: 100,
+            loading: true,
+            even: true, //不开启隔行背景
+            cols: [cols],
+            done: function (data, date, total) {
+            }
+        });
+
+        var active = {
+            removeItem: function (index) {
+                var oldData = table.cache[layTableId];
+                oldData.splice(index, 1);    //删除一项
+                var id_type = $('#id_type').val()
+                for (var i = 0; i < oldData.length; i++) {
+                    if (id_type === "1" || id_type === "2") {
+                        //单选题、多选
+                        oldData[i].answer = answers[i]
+                    }
+                    else if (id_type === "3") {
+                        //填空题
+                        oldData[i].answer = '空白' + (parseInt(i) + 1)
+                    }
+                }
+                tableIns.reload({
+                    data: oldData,
+                });
+            },
+        }
+        //激活事件
+        var activeByType = function (type, arg) {
+            if (arguments.length === 2) {
+                active[type] ? active[type].call(this, arg) : '';
+            } else {
+                active[type] ? active[type].call(this) : '';
+            }
+        }
+        table.on('tool(dataTable)', function (obj) {
+            var data = obj.data, event = obj.event, tr = obj.tr; //获得当前行 tr 的DOM对象;
+            var data_index = tr.attr("data-index");
+            switch (event) {
+                case "del":
+                    //obj.del(); //删除对应行(tr)的DOM结构,并更新缓存
+                    activeByType('removeItem', data_index);
+                    layer.msg('删除成功', {icon: 6});
+                    break;
+            }
+        });
+        var answers = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
+            'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
+        form.on('select(typeChange)', function (data) {
+            // 更换题型,重新加载table
+            if (!data.value) return;
+            typeChange(data.value)
+        })
+        var typeChange = function (value, items = []) {
+            layui.$('#id_panduan').addClass('layui-hide')
+            layui.$('#id_table').removeClass('layui-hide')
+            var oldData = [], _cols = cols
+            if (value === "1") {
+                //单选题
+                if (items.length > 0) {
+                    for (var i = 0; i < items.length; i++) {
+                        oldData.push(
+                            {
+                                id: items[i].id,
+                                LAY_CHECKED: items[i].right,
+                                answer: answers[i],
+                                content: items[i].content,
+                            }
+                        )
+                    }
+                } else {
+                    for (var i = 0; i < 4; i++) {
+                        oldData.push(
+                            {
+                                id: '',
+                                answer: answers[i],
+                                content: '',
+                            }
+                        )
+                    }
+                }
+                _cols[0] = {title: '正确答案', type: 'radio', width: '7%',}
+            }
+            else if (value === "2") {
+                //多选题
+                if (items.length > 0) {
+                    for (var i = 0; i < items.length; i++) {
+                        oldData.push(
+                            {
+                                id: items[i].id,
+                                LAY_CHECKED: items[i].right,
+                                answer: answers[i],
+                                content: items[i].content,
+                            }
+                        )
+                    }
+                } else {
+                    for (var i = 0; i < 5; i++) {
+                        oldData.push(
+                            {
+                                id: '',
+                                answer: answers[i],
+                                content: '',
+                            }
+                        )
+                    }
+                }
+                _cols[0] = {title: '正确答案', type: 'checkbox', width: '7%',}
+            }
+            else if (value === "3") {
+                //填空题
+                for (var i = 0; i < 3; i++) {
+                    oldData.push(
+                        {
+                            id: '',
+                            answer: '空白' + (i + 1),
+                            content: '',
+                        }
+                    )
+                }
+                _cols = _cols.slice(1, 4)
+                _cols[0] = {field: 'answer', title: '选项', width: '7%',}
+            }
+            else if (value === "4") {
+                //判断题
+                layui.$('#id_panduan').removeClass('layui-hide')
+                layui.$('#id_table').addClass('layui-hide')
+            }
+            tableIns.reload({
+                data: oldData,
+                cols: [_cols],
+            });
+        }
+        $('#add_answer').on('click', function () {
+            var id_type = $('#id_type').val()
+            if (!id_type) {
+                layer.msg("请先选择题型!", {icon: 2})
+                return false
+            }
+
+            var oldData = table.cache[layTableId];
+            var oldData_len = oldData.length
+            if (id_type === "1" || id_type === "2") {
+                //单选题
+                oldData.push(
+                    {
+                        id: '',
+                        answer: answers[oldData_len],
+                        content: '',
+                    }
+                )
+            }
+            else if (id_type === "3") {
+                //填空题
+                oldData.push(
+                    {
+                        id: '',
+                        answer: '空白' + (oldData_len + 1),
+                        content: '',
+                    }
+                )
+            }
+            tableIns.reload({
+                data: oldData,
+            });
+        });
+
     });
 </script>
 </body>

+ 8 - 14
uis/admin/examquestion/index.html

@@ -120,9 +120,11 @@
             , url: '/admin/examquestion/'
             , cols: [[
                 {title: '编号', type: 'numbers'}
-                , {field: 'type_text', title: '难度', width: 100}
+                , {field: 'subject_text', title: '科目', width: 150}
+                , {field: 'chapter_text', title: '章节', width: 150}
+                , {field: 'type_text', title: '题型', width: 100}
                 , {field: 'title', title: '内容', width: 400}
-                , {field: 'difficulty_text', title: '题型', width: 100}
+                , {field: 'difficulty_text', title: '难度', width: 100}
                 , {field: 'create_user_text', title: '录入人', width: 150}
                 , {field: 'create_time', title: '录入时间', width: 200}
                 , {title: '操作', width: 180, align: 'center', fixed: 'right', toolbar: '#exam_question-operate-bar'}
@@ -171,20 +173,12 @@
                     type: 2,
                     title: '查看详情',
                     shadeClose: false,
-                    area: ['80%', '80%'],
-                    btn: ['保存', '取消'],
+                    area: ['70%', '80%'],
+                    btn: ['关闭'],
                     yes: function (index, dom) {
-                        layui.onSubmitChild = function (res) {
-                            layer.close(index);
-                            table.reload('exam_question_datagrid', {});
-                        };
-                        layui.submitChild();
-                    },
-                    btn2: function (index, layero) {
                         layer.close(index);//关闭当前按钮
-                        table.reload('exam_question_datagrid', {});
                     },
-                    content: 'uploadDetailImg.html?id=' + data.id + '&type=' + obj.event
+                    content: 'details.html'
                 });
             }
         });
@@ -202,7 +196,7 @@
             layer.open({
                 type: 2,
                 title: '添加试题',
-                area: ['80%', '80%'],
+                area: ['80%', '90%'],
                 btn: ['保存', '取消'],
                 yes: function (index, dom) {
                     layui.onSubmitChild = function (res) {

BIN
uis/admin/right.png


BIN
uis/admin/wrong.png