zhangjie před 1 rokem
rodič
revize
df87a09ca1
36 změnil soubory, kde provedl 1245 přidání a 1033 odebrání
  1. 12 0
      src/assets/svgs/arrow-bottom.svg
  2. 12 0
      src/assets/svgs/course-college.svg
  3. 12 0
      src/assets/svgs/course-score.svg
  4. 14 0
      src/assets/svgs/data-manage.svg
  5. 12 0
      src/assets/svgs/download.svg
  6. 14 0
      src/assets/svgs/error-log.svg
  7. 12 0
      src/assets/svgs/export-excel.svg
  8. 12 0
      src/assets/svgs/export-report.svg
  9. 14 0
      src/assets/svgs/param-set.svg
  10. 12 0
      src/assets/svgs/report-view.svg
  11. 14 0
      src/assets/svgs/result-list.svg
  12. 2 1
      src/components/ProjectCourseSelect.vue
  13. 13 0
      src/components/StatusTag.vue
  14. 7 1
      src/constants/menu.ts
  15. 61 63
      src/features/allAnalysis/AllAnalysis.vue
  16. 205 162
      src/features/allAnalysis/ScoreRate.vue
  17. 84 63
      src/features/paperAnalysis/DifficultyDistriTable.vue
  18. 117 76
      src/features/paperAnalysis/DiscriminationDistriTable.vue
  19. 67 88
      src/features/paperAnalysis/PaperAnalysis.vue
  20. 9 13
      src/features/paperAnalysis/QuestionAttr.vue
  21. 37 24
      src/features/paperAnalysis/QuestionBianPai.vue
  22. 91 78
      src/features/paperAnalysis/QuestionDifficultyGroup.vue
  23. 58 161
      src/features/paperAnalysis/QuestionTypeDifficulty.vue
  24. 58 161
      src/features/paperAnalysis/QuestionTypeDiscrimination.vue
  25. 79 61
      src/features/projectDataManagement/ProjectDataManagement.vue
  26. 49 46
      src/features/projectManagement/ProjectManagement.vue
  27. 35 16
      src/features/projectParamsManagement/ProjectParamsManagement.vue
  28. 4 1
      src/features/report/ReportBatchDownload.vue
  29. 1 1
      src/features/userManagement/UserManagement.vue
  30. 6 0
      src/filters/index.ts
  31. 3 3
      src/router/index.ts
  32. 15 1
      src/styles/ant-custom.less
  33. 32 5
      src/styles/base.less
  34. 70 0
      src/styles/pages.less
  35. 1 8
      src/styles/reset.less
  36. 1 0
      src/theme.ts

+ 12 - 0
src/assets/svgs/arrow-bottom.svg

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>icon-下拉备份</title>
+    <g id="管理员" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="02.05-项目列表-更多" transform="translate(-436, -96)">
+            <g id="icon-下拉备份" transform="translate(436, 96)">
+                <polygon id="矩形" opacity="0" points="16 0 16 16 0 16 0 0"></polygon>
+                <polygon id="路径-4-(Stroke)" fill="#8C8C8C" points="4.45961356 5.54040527 7.99999428 9.08078575 11.5403748 5.54040527 12.4596138 6.45964408 7.99999428 10.9192634 3.54037476 6.45964408"></polygon>
+            </g>
+        </g>
+    </g>
+</svg>

+ 12 - 0
src/assets/svgs/course-college.svg

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>icon-科目学院属性</title>
+    <g id="管理员" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="02.11-项目列表-参数配置" transform="translate(-154, -265)">
+            <g id="icon-科目学院属性" transform="translate(154, 265)">
+                <rect id="info-circle-(Background)" opacity="0" x="0" y="0" width="16" height="16"></rect>
+                <path d="M7.50561523,6.5 L8.50561523,6.5 L8.50561523,11.9997559 L7.50561523,11.9997559 L7.50561523,6.5 Z M7.39990234,4 L7.39990234,5.19995117 L8.59985352,5.19995117 L8.59985352,4 L7.39990234,4 Z M8,15 C11.8659668,15 15,11.8659668 15,8 C15,4.1340332 11.8659668,1 8,1 C4.1340332,1 1,4.1340332 1,8 C1,11.8659668 4.1340332,15 8,15 Z" id="info-circle" fill="#595959"></path>
+            </g>
+        </g>
+    </g>
+</svg>

+ 12 - 0
src/assets/svgs/course-score.svg

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>icon-科目分数线</title>
+    <g id="管理员" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="02.11-项目列表-参数配置" transform="translate(-40, -265)">
+            <g id="icon-科目分数线" transform="translate(40, 265)">
+                <rect id="chart-(Background)" opacity="0" x="0" y="0" width="16" height="16"></rect>
+                <path d="M4.5,7.49993753 L5.5,7.4999404 L5.5,12 L4.5,12 L4.5,7.49993753 Z M7.5,12 L8.5,12 L8.5,4.5 L7.5,4.5 L7.5,12 Z M10.5,9 L11.5,9 L11.5,12 L10.5,12 L10.5,9 Z M3,2 L13,2 C13.5522871,2 14,2.44771713 14,3 L14,13 C14,13.5522871 13.5522842,14 13,14 L3,14 C2.44771487,14 2,13.5522842 2,13 L2,3 C2,2.44771481 2.44771719,2 3,2 Z" id="chart" fill="#595959"></path>
+            </g>
+        </g>
+    </g>
+</svg>

+ 14 - 0
src/assets/svgs/data-manage.svg

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>icon-数据管理</title>
+    <g id="管理员" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="02.05-项目列表-更多" transform="translate(-1294, -335)">
+            <g id="编组-4" transform="translate(1280, 287)">
+                <g id="icon-数据管理" transform="translate(14, 48)">
+                    <rect id="chart-(Background)" opacity="0" x="0" y="0" width="16" height="16"></rect>
+                    <path d="M4.5,7.49993753 L5.5,7.4999404 L5.5,12 L4.5,12 L4.5,7.49993753 Z M7.5,12 L8.5,12 L8.5,4.5 L7.5,4.5 L7.5,12 Z M10.5,9 L11.5,9 L11.5,12 L10.5,12 L10.5,9 Z M3,2 L13,2 C13.5522871,2 14,2.44771713 14,3 L14,13 C14,13.5522871 13.5522842,14 13,14 L3,14 C2.44771487,14 2,13.5522842 2,13 L2,3 C2,2.44771481 2.44771719,2 3,2 Z M13,3 L13,13 L3,13 L3,3 L13,3 Z" id="chart" fill="#595959"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 12 - 0
src/assets/svgs/download.svg

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>icon-导入</title>
+    <g id="超管" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="06.01-用户管理" transform="translate(-116, -171)">
+            <g id="icon-导入" transform="translate(116, 171)">
+                <rect id="download-(Background)" opacity="0" x="0" y="0" width="16" height="16"></rect>
+                <path d="M8.5,9.57746124 L8.49998093,0.5 L7.49998093,0.5 L7.5,9.57745647 L3.73641205,5.81387091 L3.02930534,6.5209775 L7.64644661,11.1381191 C7.84170875,11.3333813 8.15829124,11.3333813 8.35355339,11.1381192 C8.35355339,11.1381191 8.3535534,11.1381191 8.3535534,11.1381191 L12.9706955,6.52097702 L12.9706955,6.52097702 L12.2635889,5.81386995 L8.5,9.57746124 Z M2,13.0000238 C2,13.5523081 2.44771534,14.0000238 3,14.0000238 L13,14.0000238 C13.5522842,14.0000238 14,13.552309 14,13.0000238 L14,11.0000238 L13,11.0000238 L13,13.0000238 L3,13.0000238 L3,11.0000238 L2,11.0000238 L2,13.0000238 Z" id="download" fill="#595959"></path>
+            </g>
+        </g>
+    </g>
+</svg>

+ 14 - 0
src/assets/svgs/error-log.svg

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>icon-错误报告</title>
+    <g id="管理员" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="02.05-项目列表-更多" transform="translate(-1294, -301)">
+            <g id="编组-4" transform="translate(1280, 287)">
+                <g id="icon-错误报告" transform="translate(14, 14)">
+                    <rect id="file-excel-(Background)" opacity="0" x="0" y="0" width="16" height="16"></rect>
+                    <path d="M10.3363,8.952392 L10.3363,9.792102 C10.3363,9.822342 10.3466,9.851672 10.3656,9.875242 L11.4181,11.185122 L12.4707,9.875242 C12.4896,9.851672 12.5,9.822342 12.5,9.792102 L12.5,8.952392 L13.5,8.952392 L13.5,9.792102 C13.5,10.050122 13.4119,10.300522 13.2502,10.501622 L12.0596,11.983322 L13.2502,13.465022 C13.4119,13.666122 13.5,13.916522 13.5,14.174522 L13.5,15.014222 L12.5,15.014222 L12.5,14.174522 C12.5,14.144322 12.4896,14.114922 12.4707,14.091422 L11.4181,12.781522 L10.3656,14.091422 C10.3466,14.114922 10.3363,14.144322 10.3363,14.174522 L10.3363,15.014222 L9.3363,15.014222 L9.3363,14.174522 C9.3363,13.916522 9.42442,13.666122 9.58607,13.465022 L10.7767,11.983322 L9.58607,10.501622 C9.42442,10.300522 9.3363,10.050122 9.3363,9.792102 L9.3363,8.952392 L10.3363,8.952392 Z M8.86589,1 C9.13442,1 9.39167,1.108002 9.57972,1.299692 L13.2138,5.004002 C13.3973,5.190952 13.5,5.442412 13.5,5.704312 L13.5,7.5 L12.5,7.5 L12.5,6.012752 L8.50008,6.012752 L8.50008,2 L3.5,2 L3.5,14.000022 L8,14.000022 L8,15.000022 L3.49534,15.000022 C3.02005,15.000022 2.5,14.662722 2.5,14.078122 L2.5,1.921912 C2.5,1.337272 3.02005,1 3.49534,1 L8.86589,1 Z M9.50008,2.646452 L9.50008,5.012752 L11.8215,5.012752 L9.50008,2.646452 Z" id="形状结合" fill="#595959"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 12 - 0
src/assets/svgs/export-excel.svg

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>icon-导出Excel</title>
+    <g id="管理员" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="02.14-项目列表-结果查询" transform="translate(-40, -303)" fill-rule="nonzero">
+            <g id="icon-导出Excel" transform="translate(40, 303)">
+                <rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="16" height="16"></rect>
+                <path d="M13.353125,4.5109375 C13.446875,4.6046875 13.5,4.73125 13.5,4.8640625 L13.5,14.5 C13.5,14.7765625 13.2765625,15 13,15 L3,15 C2.7234375,15 2.5,14.7765625 2.5,14.5 L2.5,1.5 C2.5,1.2234375 2.7234375,1 3,1 L9.6359375,1 C9.76875,1 9.896875,1.053125 9.990625,1.146875 L13.353125,4.5109375 Z M12.346875,5.09375 L9.40625,2.153125 L9.40625,5.09375 L12.346875,5.09375 Z M8.98970313,7.46623437 L8.03320312,9.06478125 L7.06696875,7.46554687 C7.0330206,7.40935204 6.97215315,7.375 6.9065,7.375 L6.30592187,7.375 C6.27049191,7.375 6.23578711,7.38503907 6.20582812,7.40395312 C6.11826562,7.45923437 6.09209375,7.57503125 6.147375,7.66259375 L7.43382812,9.7004375 L6.1299375,11.7752344 C6.11115436,11.8051204 6.1011875,11.8397015 6.1011875,11.875 C6.1011875,11.9785469 6.18514062,12.0625 6.2886875,12.0625 L6.82721875,12.0625 C6.89220551,12.0625 6.95255738,12.0288461 6.98671875,11.9735625 L7.96651562,10.3880312 L8.93979687,11.9731094 C8.97390653,12.0286613 9.03442131,12.0625 9.09960938,12.0625 L9.68532812,12.0625 C9.72125003,12.0625 9.75641534,12.0521774 9.78664063,12.0327656 C9.87378125,11.9768281 9.8990625,11.8608281 9.84310938,11.7737031 L8.53325,9.73384375 L9.866,7.664 C9.88548476,7.63373283 9.89584375,7.59849663 9.89584375,7.5625 C9.89584375,7.45895312 9.81190625,7.375 9.70834375,7.375 L9.15060938,7.375 C9.08466726,7.375 9.02357453,7.40964684 8.98971875,7.46623437 L8.98970313,7.46623437 Z" id="形状" fill="#595959"></path>
+            </g>
+        </g>
+    </g>
+</svg>

+ 12 - 0
src/assets/svgs/export-report.svg

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>icon-导出报告</title>
+    <g id="管理员" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="02.14-项目列表-结果查询" transform="translate(-148, -303)" fill-rule="nonzero">
+            <g id="icon-导出报告" transform="translate(148, 303)">
+                <rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="16" height="16"></rect>
+                <path d="M13.353125,4.5109375 C13.446875,4.6046875 13.5,4.73125 13.5,4.8640625 L13.5,14.5 C13.5,14.7765625 13.2765625,15 13,15 L3,15 C2.7234375,15 2.5,14.7765625 2.5,14.5 L2.5,1.5 C2.5,1.2234375 2.7234375,1 3,1 L9.6359375,1 C9.76875,1 9.896875,1.053125 9.990625,1.146875 L13.353125,4.5109375 Z M12.346875,5.09375 L9.40625,2.153125 L9.40625,5.09375 L12.346875,5.09375 Z M5,7.53125 C4.93096441,7.53125 4.875,7.58721441 4.875,7.65625 L4.875,8.40625 C4.875,8.47528559 4.93096441,8.53125 5,8.53125 L11,8.53125 C11.0690356,8.53125 11.125,8.47528559 11.125,8.40625 L11.125,7.65625 C11.125,7.58721441 11.0690356,7.53125 11,7.53125 L5,7.53125 Z M5,9.65625 C4.93096441,9.65625 4.875,9.71221441 4.875,9.78125 L4.875,10.53125 C4.875,10.6002856 4.93096441,10.65625 5,10.65625 L7.875,10.65625 C7.94403559,10.65625 8,10.6002856 8,10.53125 L8,9.78125 C8,9.71221441 7.94403559,9.65625 7.875,9.65625 L5,9.65625 Z" id="形状" fill="#595959"></path>
+            </g>
+        </g>
+    </g>
+</svg>

+ 14 - 0
src/assets/svgs/param-set.svg

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>icon-参数设置</title>
+    <g id="管理员" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="02.05-项目列表-更多" transform="translate(-1294, -369)">
+            <g id="编组-4" transform="translate(1280, 287)">
+                <g id="icon-参数设置" transform="translate(14, 82)">
+                    <rect id="setting-(Background)" opacity="0" x="0" y="0" width="16" height="16"></rect>
+                    <path d="M8.00016594,11 C6.34331179,11 5.00016594,9.65685463 5.00016594,8 C5.00016594,6.34314585 6.34331179,5 8.00016594,5 C9.65702009,5 11.0001659,6.34314585 11.0001659,8 C11.0001659,9.65685463 9.65702009,11 8.00016594,11 Z M8.00016594,10 C6.8955965,10 6.00016594,9.10456944 6.00016594,8 C6.00016594,6.89543056 6.8955965,6 8.00016594,6 C9.10473537,6 10.0001659,6.89543056 10.0001659,8 C10.0001659,9.10456944 9.10473537,10 8.00016594,10 Z M14.0623436,4.625 L14.0623436,11.375 L8.00016594,14.75 L1.93798828,11.375 L1.93798828,4.625 L8.00016594,1.25 L14.0623436,4.625 Z M8.00016594,2.39453006 L13.0623436,5.21279955 L13.0623436,10.7872009 L8.00016594,13.6054697 L2.93798828,10.7872009 L2.93798828,5.21279955 L8.00016594,2.39453006 Z" id="setting" fill="#595959"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 12 - 0
src/assets/svgs/report-view.svg

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>icon-预览报告</title>
+    <g id="管理员" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="02.19-项目列表-结果查询-详情" transform="translate(-1244, -130)" fill-rule="nonzero">
+            <g id="icon-预览报告" transform="translate(1244, 130)">
+                <rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="16" height="16"></rect>
+                <path d="M10.75,4.875 L10.75,4.125 C10.75,4.05625 10.69375,4 10.625,4 L4.625,4 C4.55625,4 4.5,4.05625 4.5,4.125 L4.5,4.875 C4.5,4.94375 4.55625,5 4.625,5 L10.625,5 C10.69375,5 10.75,4.94375 10.75,4.875 Z M4.625,6.25 C4.55625,6.25 4.5,6.30625 4.5,6.375 L4.5,7.125 C4.5,7.19375 4.55625,7.25 4.625,7.25 L7.5,7.25 C7.56875,7.25 7.625,7.19375 7.625,7.125 L7.625,6.375 C7.625,6.30625 7.56875,6.25 7.5,6.25 L4.625,6.25 Z M6.875,13.3125 L3.25,13.3125 L3.25,2.3125 L12,2.3125 L12,7.6875 C12,7.75625 12.05625,7.8125 12.125,7.8125 L13,7.8125 C13.06875,7.8125 13.125,7.75625 13.125,7.6875 L13.125,1.6875 C13.125,1.4109375 12.9015625,1.1875 12.625,1.1875 L2.625,1.1875 C2.3484375,1.1875 2.125,1.4109375 2.125,1.6875 L2.125,13.9375 C2.125,14.2140625 2.3484375,14.4375 2.625,14.4375 L6.875,14.4375 C6.94375,14.4375 7,14.38125 7,14.3125 L7,13.4375 C7,13.36875 6.94375,13.3125 6.875,13.3125 Z M13.8390625,14.1171875 L12.38125,12.659375 C12.7296875,12.1984375 12.9375,11.6234375 12.9375,11 C12.9375,9.48125 11.70625,8.25 10.1875,8.25 C8.66875,8.25 7.4375,9.48125 7.4375,11 C7.4375,12.51875 8.66875,13.75 10.1875,13.75 C10.746875,13.75 11.265625,13.5828125 11.7,13.296875 L13.1796875,14.7765625 C13.2046875,14.8015625 13.2359375,14.8125 13.2671875,14.8125 C13.2984375,14.8125 13.33125,14.8 13.3546875,14.7765625 L13.8390625,14.2921875 C13.8875,14.24375 13.8875,14.165625 13.8390625,14.1171875 Z M10.1875,12.75 C9.2203125,12.75 8.4375,11.9671875 8.4375,11 C8.4375,10.0328125 9.2203125,9.25 10.1875,9.25 C11.1546875,9.25 11.9375,10.0328125 11.9375,11 C11.9375,11.9671875 11.1546875,12.75 10.1875,12.75 Z" id="形状" fill="#FFFFFF"></path>
+            </g>
+        </g>
+    </g>
+</svg>

+ 14 - 0
src/assets/svgs/result-list.svg

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>icon-结果查询</title>
+    <g id="管理员" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="02.05-项目列表-更多" transform="translate(-1294, -403)">
+            <g id="编组-4" transform="translate(1280, 287)">
+                <g id="icon-结果查询" transform="translate(14, 116)">
+                    <rect id="search-(Background)" opacity="0" x="0" y="0" width="16" height="16"></rect>
+                    <path d="M6.75623608,11.5124207 C4.12943935,11.5124207 2,9.38299274 2,6.75621033 C2,4.12942791 4.12943935,2 6.75623608,2 C9.3830328,2 11.5124722,4.12942791 11.5124722,6.75621033 C11.5124722,7.88951063 11.1160965,8.930233 10.4544153,9.7473073 L13.9798489,13.2727222 L13.2727442,13.9798317 L9.74730206,10.4544077 C8.93023014,11.1160622 7.88952065,11.5124207 6.75623608,11.5124207 Z M6.75623608,10.5124207 C4.68171906,10.5124207 3,8.83070278 3,6.75621033 C3,4.68171763 4.68171906,3 6.75623608,3 C8.83075285,3 10.5124722,4.68171763 10.5124722,6.75621033 C10.5124722,8.83070278 8.83075285,10.5124207 6.75623608,10.5124207 Z" id="search" fill="#595959"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 2 - 1
src/components/ProjectCourseSelect.vue

@@ -1,6 +1,7 @@
 <template>
   <a-select
-    placeholder="请选择科目"
+    placeholder="请选择"
+    title="科目"
     allowClear
     showSearch
     :value="valueStr"

+ 13 - 0
src/components/StatusTag.vue

@@ -11,10 +11,21 @@ const configs = {
     themeDict: { true: "success", false: "danger" },
     valFilter: $filters.booleanEnableDisableFilter,
   },
+  projectStatus: {
+    themeDict: {
+      NONE: "",
+      PROCESSING: "",
+      FINISH: "success",
+      FAIL: "danger",
+      STOPING: "",
+    },
+    valFilter: $filters.projectStatusFilter,
+  },
 };
 type ConfigKeyType = keyof typeof configs;
 
 function getConfig(type: ConfigKeyType) {
+  // @ts-ignore
   return configs[type] || { themeDict: {}, valFilter: (val) => val };
 }
 
@@ -26,9 +37,11 @@ const props = defineProps({
 const { themeDict, valFilter } = getConfig(props.type as ConfigKeyType);
 
 const theme = computed(() => {
+  // @ts-ignore
   return themeDict[props.value];
 });
 const label = computed(() => {
+  // @ts-ignore
   return valFilter(props.value as any);
 });
 </script>

+ 7 - 1
src/constants/menu.ts

@@ -49,6 +49,12 @@ export const menuRelative = {
   AuthManagement: ["AuthManagement"],
   UserManagement: ["UserManagement"],
   CourseManagement: ["CourseManagement"],
-  ProjectManagement: ["ProjectManagement"],
+  ProjectManagement: [
+    "ProjectManagement",
+    "ProjectDataManagement",
+    "ProjectParamsManagement",
+    "AllAnalysis",
+    "PaperAnalysis",
+  ],
   // ProjectCompareManagement: ["ProjectCompareManagement"],
 };

+ 61 - 63
src/features/allAnalysis/AllAnalysis2.vue → src/features/allAnalysis/AllAnalysis.vue

@@ -1,62 +1,62 @@
 <template>
   <div>
-    <div class="tw-bg-white tw-p-5 tw-rounded-xl tw-mb-5">
-      <a-input v-model:value="curProject.name" disabled style="width: 200px" />
-      <span class="tw-mr-4"></span>
-      <ProjectCourseSelect v-model:value="courseId" :projectId="projectId" />
-      <span class="tw-mr-4"></span>
-      <a-button class="query-btn" @click="search">查询</a-button>
-      <span class="tw-mr-4"></span>
-      <a-button @click="exportExcel">导出excel</a-button>
-      <span class="tw-mr-4"></span>
-      <ReportBatchDownload v-if="curProject.id" :curProject="curProject" />
-
-      <div class="tw-float-right tw-flex tw-gap-2">
-        <!-- <a-button @click="goProjectPapers(projectId)"> 试卷列表 </a-button> -->
+    <a-card title="结果查询">
+      <template #extra>
         <a-button @click="goBack">返回</a-button>
-      </div>
-    </div>
+      </template>
 
-    <div class="tw-bg-white tw-p-5 tw-rounded-xl">
-      <!-- <a-radio-group v-model:value="activeTab">
-        <a-radio-button value="1">试卷特征量数</a-radio-button>
-        <a-radio-button value="2">科目成绩(总分)频率分布</a-radio-button>
-        <a-radio-button value="3">科目成绩占初试总分权重</a-radio-button>
-      </a-radio-group> -->
-      <div class="tw-mb-4"></div>
-      <div v-if="activeTab === '1'">
-        <a-table
-          style="width: 100%; overflow-x: scroll"
-          rowKey="id"
-          :scroll="{ x: 1200 }"
-          :columns="columns"
-          :data-source="data"
-          :pagination="{
-            pageSize: pageSize,
-            current: pageNo,
-            total: totalElements,
-            showTotal: () => ``,
-          }"
-          @change="tableChange"
-        >
-          <template #bodyCell="{ column, text, record }">
-            <template v-if="column.dataIndex === 'course'">
-              <a>{{ `${record.courseName}(${record.courseCode})` }}</a>
-            </template>
-            <template v-if="column.dataIndex === 'action'">
-              <span>
-                <!-- <a-button @click="openModal">说明</a-button> -->
-                <a-button @click="goPaperAnalysis(record)">详情</a-button>
-              </span>
-            </template>
-          </template>
-        </a-table>
-      </div>
+      <a-form
+        class="tiny"
+        :labelCol="{ style: { width: '72px', marginBottom: '16px' } }"
+      >
+        <a-form-item label="项目名称">
+          <span>{{ curProject.name }}</span>
+        </a-form-item>
+      </a-form>
+      <a-space class="filter-line" :size="12">
+        <ProjectCourseSelect v-model:value="courseId" :projectId="projectId" />
+        <a-button type="primary" @click="search">查询</a-button>
+      </a-space>
+    </a-card>
 
-      <div v-if="activeTab === '2'"><ScoreRate :courseId="courseId" /></div>
-      <div v-if="activeTab === '3'">
-        <ScoreFirstTryRate :courseId="courseId" />
-      </div>
+    <div class="part-box">
+      <a-space class="part-action" :size="6">
+        <a-button type="text" @click="exportExcel">
+          <template #icon> <svg-icon name="export-excel"></svg-icon> </template>
+          导出excel
+        </a-button>
+        <ReportBatchDownload v-if="curProject.id" :curProject="curProject" />
+      </a-space>
+
+      <a-table
+        class="page-table"
+        rowKey="id"
+        :scroll="{ x: 1200 }"
+        :columns="columns"
+        :data-source="data"
+        :pagination="{
+          pageSize: pageSize,
+          current: pageNo,
+          total: totalElements,
+          showQuickJumper:true,
+          showSizeChanger:true,
+          showTotal: (total:number) => `共 ${total} 项数据`,
+        }"
+        @change="tableChange"
+      >
+        <template #bodyCell="{ column, text, record }">
+          <template v-if="column.dataIndex === 'course'">
+            <span>{{ `${record.courseName}(${record.courseCode})` }}</span>
+          </template>
+          <template v-if="column.dataIndex === 'action'">
+            <div class="action-cell">
+              <a-button type="text" @click="goPaperAnalysis(record)"
+                >详情</a-button
+              >
+            </div>
+          </template>
+        </template>
+      </a-table>
     </div>
   </div>
 </template>
@@ -70,8 +70,6 @@ import router from "@/router";
 import { getSasPaperList } from "@/api/allAnalysisPage";
 import { getProjectList } from "@/api/projectManagementPage";
 // import EventBus from "@/plugins/eventBus";
-import ScoreRate from "./ScoreRate.vue";
-import ScoreFirstTryRate from "./ScoreFirstTryRate.vue";
 import ReportBatchDownload from "../report/ReportBatchDownload.vue";
 
 import { SASPaper, Project } from "@/types";
@@ -92,7 +90,7 @@ type SorterType = {
 };
 
 const store = useMainStore();
-store.currentLocation = "项目管理 / 项目列表 / 结果查询";
+store.currentLocation = "项目列表 / 结果查询";
 
 // let rootOrgId = $ref(undefined as unknown as number);
 let courseId = $ref(undefined as unknown as number);
@@ -166,18 +164,18 @@ const columns = [
   {
     title: "科目",
     dataIndex: "course",
-    width: 200,
+    minWidth: 140,
     ellipses: true,
   },
   {
     title: "试卷类型",
     dataIndex: "paperType",
-    width: 100,
+    width: 120,
   },
   {
     title: "起始计算分",
     dataIndex: "startScore",
-    width: 100,
+    width: 140,
   },
   {
     title: "样本数",
@@ -204,7 +202,7 @@ const columns = [
     title: "全距",
     dataIndex: "allRange",
     sorter: true,
-    width: 100,
+    width: 80,
   },
   {
     title: "平均分",
@@ -222,7 +220,7 @@ const columns = [
     title: "差异系数",
     dataIndex: "coefficient",
     sorter: true,
-    width: 100,
+    width: 110,
   },
   {
     title: "信度",
@@ -243,7 +241,7 @@ const columns = [
   {
     title: "操作",
     dataIndex: "action",
-    width: 100,
+    width: 80,
     fixed: "right",
   },
 ];

+ 205 - 162
src/features/allAnalysis/ScoreRate.vue

@@ -1,113 +1,112 @@
 <template>
-  <div>
-    <div class="tw-bg-white tw-rounded-xl">
-      <div class="tw-mt-4"></div>
-      分数间隔
-      <a-select v-model:value="scoreGap">
-        <a-select-option :value="1">1</a-select-option>
-        <a-select-option :value="5">5</a-select-option>
-        <a-select-option :value="10">10</a-select-option>
-        <a-select-option :value="20">20</a-select-option>
-        <a-select-option :value="50">50</a-select-option>
-      </a-select>
-      <div class="tw-mt-4"></div>
+  <div class="score-rate">
+    <div class="score-part">
+      <div class="score-part-head">
+        <h2>等距({{ scoreGap }}分)分组频数分布</h2>
+      </div>
+      <div class="score-part-body">
+        <div class="mb-16">
+          分数间隔
+          <a-select v-model:value="scoreGap">
+            <a-select-option :value="1">1</a-select-option>
+            <a-select-option :value="5">5</a-select-option>
+            <a-select-option :value="10">10</a-select-option>
+            <a-select-option :value="20">20</a-select-option>
+            <a-select-option :value="50">50</a-select-option>
+          </a-select>
+        </div>
 
-      <a-collapse :activeKey="courses[0]?.id">
-        <a-collapse-panel
-          v-for="course in courses"
-          :key="course.id"
-          :header="course.courseName + '(' + course.courseCode + ')'"
-        >
-          <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-            <h3 class="section-title">等距({{ scoreGap }}分)分组频数分布</h3>
-            <!-- <a-button @click="openModal1">说明</a-button> -->
-          </div>
-          <div class="tw-flex tw-gap-4" style="max-height: 500px">
-            <div class="tw-overflow-auto">
-              <table class="custom-table" style="width: 400px">
-                <tr>
-                  <th>分数段</th>
-                  <th>频数</th>
-                  <th>频率(%)</th>
-                  <th>累计频数</th>
-                  <th>累计频数(%)</th>
-                </tr>
-                <tr
-                  v-for="(seg, index2) in course.segements"
-                  :key="index2 + scoreGap * 100000"
-                >
-                  <td>
-                    <span v-if="index2 < course.segements.length - 1"
-                      >{{ scoreGap * index2 }}-{{
-                        scoreGap * (index2 + 1)
-                      }}</span
-                    >
-                    <span v-else>{{ seg[0] }}</span>
-                  </td>
-                  <td>{{ seg[1] }}</td>
-                  <td v-number-to-percent>{{ seg[2] }}%</td>
-                  <td>{{ seg[3] }}</td>
-                  <td v-number-to-percent>{{ seg[4] }}%</td>
-                </tr>
-              </table>
-            </div>
-
-            <div style="flex-grow: 1; min-height: 260px">
+        <a-row v-if="course.segements" :gutter="16">
+          <a-col :span="12">
+            <a-table
+              class="page-table"
+              :scroll="{ y: 453 }"
+              :data-source="course.segements"
+              :pagination="false"
+              bordered
+              size="middle"
+            >
+              <a-table-column key="0" title="分数段" data-index="0">
+                <template #default="{ text, index }">
+                  <span v-if="index < course.segements.length - 1"
+                    >{{ scoreGap * index }}-{{ scoreGap * (index + 1) }}</span
+                  >
+                  <span v-else>{{ text }}</span>
+                </template>
+              </a-table-column>
+              <a-table-column key="1" title="频率" data-index="1" />
+              <a-table-column key="2" title="频率(%)" data-index="2">
+                <template #default="{ text }">{{
+                  $filters.numberToPercentFilter(text)
+                }}</template>
+              </a-table-column>
+              <a-table-column key="3" title="累计频数" data-index="3" />
+              <a-table-column key="4" title="累计频数(%)" data-index="4">
+                <template #default="{ text }">{{
+                  $filters.numberToPercentFilter(text)
+                }}</template>
+              </a-table-column>
+            </a-table>
+          </a-col>
+          <a-col :span="12">
+            <div class="chart-box">
               <v-chart
                 class="chart"
                 :option="segementsLine(course)"
                 :autoresize="true"
               />
             </div>
-          </div>
-
-          <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-            <h3 class="section-title">科目分数线分组的频数分布</h3>
-            <div class="tw-flex tw-gap-2">
-              <a-button @click="openRangeConfigModal(course)">
-                分段设置
-              </a-button>
-              <!-- <a-button @click="openModal2">说明</a-button> -->
-            </div>
-          </div>
-          <div
-            v-if="course.rangeConfig"
-            class="tw-flex tw-gap-4"
-            style="max-height: 500px"
-          >
-            <div class="tw-overflow-auto">
-              <table class="custom-table" style="width: 400px">
-                <tr>
-                  <th>分数段</th>
-                  <th>频数</th>
-                  <th>频率(%)</th>
-                  <th>累计频数</th>
-                  <th>累计频数(%)</th>
-                </tr>
-                <tr
-                  v-for="(seg, index3) in course.rangeSegements"
-                  :key="index3"
-                >
-                  <td>{{ seg[0] }}</td>
-                  <td>{{ seg[1] }}</td>
-                  <td v-number-to-percent>{{ seg[2] }}%</td>
-                  <td>{{ seg[3] }}</td>
-                  <td v-number-to-percent>{{ seg[4] }}%</td>
-                </tr>
-              </table>
-            </div>
+          </a-col>
+        </a-row>
+      </div>
+    </div>
 
-            <div style="flex-grow: 1; min-height: 260px">
+    <div class="score-part">
+      <div class="score-part-head">
+        <h2>科目分数线分组的频数分布</h2>
+      </div>
+      <div class="score-part-body">
+        <div class="mb-16">
+          <a-button @click="openRangeConfigModal(course)"> 分段设置 </a-button>
+        </div>
+        <a-row v-if="course.rangeConfig && course.rangeSegements" :gutter="16">
+          <a-col :span="12">
+            <a-table
+              class="page-table"
+              :scroll="{ y: 235 }"
+              :data-source="course.rangeSegements"
+              :pagination="false"
+              bordered
+              size="middle"
+            >
+              <a-table-column key="0" title="分数段" data-index="0">
+              </a-table-column>
+              <a-table-column key="1" title="频率" data-index="1" />
+              <a-table-column key="2" title="频率(%)" data-index="2">
+                <template #default="{ text }">{{
+                  $filters.numberToPercentFilter(text)
+                }}</template>
+              </a-table-column>
+              <a-table-column key="3" title="累计频数" data-index="3" />
+              <a-table-column key="4" title="累计频数(%)" data-index="4">
+                <template #default="{ text }">{{
+                  $filters.numberToPercentFilter(text)
+                }}</template>
+              </a-table-column>
+            </a-table>
+          </a-col>
+          <a-col :span="12">
+            <div class="chart-box" style="height: 282px">
               <v-chart
                 class="chart"
                 :option="rangeSegementsLine(course)"
                 :autoresize="true"
               />
             </div>
-          </div>
-          <div v-else>请先进行分段设置。</div>
-        </a-collapse-panel>
-      </a-collapse>
+          </a-col>
+        </a-row>
+        <div v-else>请先进行分段设置。</div>
+      </div>
     </div>
 
     <CommonRangeConfig
@@ -145,7 +144,7 @@ const props = defineProps<{
 const route = useRoute();
 const projectId = +route.params.projectId;
 
-let courses = $ref<SasCourse[]>([]);
+let course = $ref<SasCourse>({} as SasCourse);
 async function fetchData() {
   const res = await getSasCourseList({
     projectId,
@@ -180,7 +179,7 @@ async function fetchData() {
     return v;
   });
   // console.log(res.data);
-  courses = res.data;
+  course = res.data[0];
 }
 
 onMounted(async () => {
@@ -192,74 +191,72 @@ watch(() => [props.courseId], fetchData);
 let scoreGap = $ref(10);
 
 watch(
-  () => [scoreGap, courses],
+  () => [scoreGap, course],
   () => {
-    for (const course of courses) {
-      course.segements = [];
-      const validSeg = Math.round(course.totalScore / scoreGap);
-      for (let score = 0; score < validSeg; score++) {
-        const row = [];
-        row[0] = score * scoreGap;
-        let nextScore = score + 1 > validSeg ? course.totalScore : score + 1;
-        // row[1] =
-        //   course.scoreRangeAcc[nextScore * scoreGap - 1] -
-        //   course.scoreRangeAcc[score * scoreGap];
-        row[1] = course.scoreRange
-          .slice(row[0], nextScore * scoreGap)
-          .reduce((p, c) => p + c, 0);
-        row[2] = row[1] / course.scoreRangeAcc[course.totalScore];
-        const endGap =
-          nextScore * scoreGap - 1 >= course.totalScore
-            ? course.totalScore
-            : nextScore * scoreGap - 1;
-        row[3] = course.scoreRangeAcc[endGap];
-        row[4] = row[3] / course.scoreRangeAcc[course.totalScore];
-        course.segements.push(row);
-      }
-      if (validSeg * scoreGap === course.totalScore) {
-        course.segements.push([
-          course.totalScore,
-          course.scoreRange[course.totalScore],
-          course.scoreRange[course.totalScore] /
-            course.scoreRangeAcc[course.totalScore],
+    if (!course || !course.id) return;
+    course.segements = [];
+    const validSeg = Math.round(course.totalScore / scoreGap);
+    for (let score = 0; score < validSeg; score++) {
+      const row = [];
+      row[0] = score * scoreGap;
+      let nextScore = score + 1 > validSeg ? course.totalScore : score + 1;
+      // row[1] =
+      //   course.scoreRangeAcc[nextScore * scoreGap - 1] -
+      //   course.scoreRangeAcc[score * scoreGap];
+      row[1] = course.scoreRange
+        .slice(row[0], nextScore * scoreGap)
+        .reduce((p, c) => p + c, 0);
+      row[2] = row[1] / course.scoreRangeAcc[course.totalScore];
+      const endGap =
+        nextScore * scoreGap - 1 >= course.totalScore
+          ? course.totalScore
+          : nextScore * scoreGap - 1;
+      row[3] = course.scoreRangeAcc[endGap];
+      row[4] = row[3] / course.scoreRangeAcc[course.totalScore];
+      course.segements.push(row);
+    }
+    if (validSeg * scoreGap === course.totalScore) {
+      course.segements.push([
+        course.totalScore,
+        course.scoreRange[course.totalScore],
+        course.scoreRange[course.totalScore] /
           course.scoreRangeAcc[course.totalScore],
-          1,
-        ]);
-      }
-
-      course.rangeSegements = [];
-      for (let i = 0; i < course.rangeConfig.length; i++) {
-        const range = course.rangeConfig[i]!;
-        const nextRange =
-          i === course.rangeConfig.length - 1
-            ? { baseScore: course.totalScore, adjustScore: 0 }
-            : course.rangeConfig[i + 1];
-        const row = [];
-        row[0] = scoreTitle(range);
-        row[1] =
-          course.scoreRange
-            .slice(
-              range.baseScore + range.adjustScore,
-              nextRange.baseScore + nextRange.adjustScore
-            )
-            .reduce((p, c) => p + c, 0) || 0;
-        row[2] = row[1] / course.scoreRangeTotal;
-        row[3] =
-          course.scoreRangeAcc[
-            nextRange.baseScore + nextRange.adjustScore - 1
-          ] || 0;
-        if (
-          nextRange.baseScore + nextRange.adjustScore >=
-          course.scoreRangeAcc.length
-        ) {
-          row[3] = course.scoreRangeAcc[course.scoreRangeAcc.length - 1];
-        }
-        row[4] = row[3] / course.scoreRangeTotal;
+        course.scoreRangeAcc[course.totalScore],
+        1,
+      ]);
+    }
 
-        course.rangeSegements.push(row);
+    course.rangeSegements = [];
+    for (let i = 0; i < course.rangeConfig.length; i++) {
+      const range = course.rangeConfig[i]!;
+      const nextRange =
+        i === course.rangeConfig.length - 1
+          ? { baseScore: course.totalScore, adjustScore: 0 }
+          : course.rangeConfig[i + 1];
+      const row = [];
+      row[0] = scoreTitle(range);
+      row[1] =
+        course.scoreRange
+          .slice(
+            range.baseScore + range.adjustScore,
+            nextRange.baseScore + nextRange.adjustScore
+          )
+          .reduce((p, c) => p + c, 0) || 0;
+      row[2] = row[1] / course.scoreRangeTotal;
+      row[3] =
+        course.scoreRangeAcc[nextRange.baseScore + nextRange.adjustScore - 1] ||
+        0;
+      if (
+        nextRange.baseScore + nextRange.adjustScore >=
+        course.scoreRangeAcc.length
+      ) {
+        row[3] = course.scoreRangeAcc[course.scoreRangeAcc.length - 1];
       }
-      // console.log(course);
+      row[4] = row[3] / course.scoreRangeTotal;
+
+      course.rangeSegements.push(row);
     }
+    // console.log(course);
   },
   {
     immediate: true,
@@ -319,14 +316,37 @@ function segementsLine(course: SasCourse) {
   return {
     title: {
       text: "频数",
-      left: "left",
+      left: 16,
+      top: 16,
+      textStyle: {
+        fontSize: 14,
+        color: "#595959",
+      },
+    },
+    color: ["#4080FF"],
+    grid: {
+      left: 50,
+      right: 70,
+      bottom: 50,
+      top: 60,
     },
     xAxis: {
       type: "category",
       data: course.segements.map((v) => v[0]),
+      name: "分数段",
+      axisLabel: {
+        color: "#8C8C8C",
+        fontSize: 12,
+        fontWeight: 400,
+      },
     },
     yAxis: {
       type: "value",
+      axisLabel: {
+        color: "#8C8C8C",
+        fontSize: 12,
+        fontWeight: 400,
+      },
     },
     series: [
       {
@@ -343,14 +363,37 @@ function rangeSegementsLine(course: SasCourse) {
   return {
     title: {
       text: "频数",
-      left: "left",
+      left: 16,
+      top: 16,
+      textStyle: {
+        fontSize: 14,
+        color: "#595959",
+      },
+    },
+    color: ["#4080FF"],
+    grid: {
+      left: 50,
+      right: 70,
+      bottom: 50,
+      top: 60,
     },
     xAxis: {
       type: "category",
       data: course.rangeSegements.map((v) => v[0]),
+      name: "分数段",
+      axisLabel: {
+        color: "#8C8C8C",
+        fontSize: 12,
+        fontWeight: 400,
+      },
     },
     yAxis: {
       type: "value",
+      axisLabel: {
+        color: "#8C8C8C",
+        fontSize: 12,
+        fontWeight: 400,
+      },
     },
     series: [
       {

+ 84 - 63
src/features/paperAnalysis/DifficultyDistriTable.vue

@@ -1,67 +1,88 @@
 <template>
-  <table class="custom-table">
-    <tr>
-      <td rowspan="2">题型</td>
-      <td rowspan="2">题量</td>
-      <td rowspan="2">满分</td>
-      <td rowspan="2">难度</td>
-      <td colspan="3">高难度</td>
-      <td colspan="3">中等难度</td>
-      <td colspan="3">低难度</td>
-    </tr>
-    <tr>
-      <td>题量</td>
-      <td>分值</td>
-      <td>%</td>
-      <td>题量</td>
-      <td>分值</td>
-      <td>%</td>
-      <td>题量</td>
-      <td>分值</td>
-      <td>%</td>
-    </tr>
-    <tr v-for="(item2, index) in props.questions" :key="index">
-      <td>
-        {{ item2.groupName || "全卷" }}
-      </td>
-      <td>
-        {{ item2.questionCount }}
-      </td>
-      <td>
-        {{ item2.totalScore }}
-      </td>
-      <td v-round-number>
-        {{ item2.difficulty }}
-      </td>
-      <td>
-        {{ item2.difficulityLevel.high.questionCount }}
-      </td>
-      <td>
-        {{ item2.difficulityLevel.high.fullScore }}
-      </td>
-      <td v-number-to-percent>
-        {{ item2.difficulityLevel.high.percent }}
-      </td>
-      <td>
-        {{ item2.difficulityLevel.middle.questionCount }}
-      </td>
-      <td>
-        {{ item2.difficulityLevel.middle.fullScore }}
-      </td>
-      <td v-number-to-percent>
-        {{ item2.difficulityLevel.middle.percent }}
-      </td>
-      <td>
-        {{ item2.difficulityLevel.low.questionCount }}
-      </td>
-      <td>
-        {{ item2.difficulityLevel.low.fullScore }}
-      </td>
-      <td v-number-to-percent>
-        {{ item2.difficulityLevel.low.percent }}
-      </td>
-    </tr>
-  </table>
+  <a-table
+    class="page-table"
+    :data-source="props.questions"
+    :pagination="false"
+    bordered
+    size="middle"
+  >
+    <a-table-column key="groupName" title="题型" data-index="groupName">
+      <template #default="{ text }">{{ text || "全卷" }}</template>
+    </a-table-column>
+    <a-table-column
+      key="questionCount"
+      title="题量"
+      data-index="questionCount"
+    />
+    <a-table-column key="totalScore" title="满分" data-index="totalScore" />
+    <a-table-column key="difficulty" title="难度" data-index="difficulty" />
+    <a-table-column-group>
+      <template #title>高难度</template>
+      <a-table-column
+        key="questionCount"
+        title="题量"
+        data-index="questionCount"
+      >
+        <template #default="{ record }">{{
+          record.difficulityLevel.high.questionCount
+        }}</template>
+      </a-table-column>
+      <a-table-column key="fullScore" title="分值" data-index="fullScore">
+        <template #default="{ record }">{{
+          record.difficulityLevel.high.fullScore
+        }}</template>
+      </a-table-column>
+      <a-table-column key="percent" title="%" data-index="percent">
+        <template #default="{ record }">{{
+          $filters.numberToPercentFilter(record.difficulityLevel.high.percent)
+        }}</template>
+      </a-table-column>
+    </a-table-column-group>
+    <a-table-column-group>
+      <template #title>中等难度</template>
+      <a-table-column
+        key="questionCount"
+        title="题量"
+        data-index="questionCount"
+      >
+        <template #default="{ record }">{{
+          record.difficulityLevel.middle.questionCount
+        }}</template>
+      </a-table-column>
+      <a-table-column key="fullScore" title="分值" data-index="fullScore">
+        <template #default="{ record }">{{
+          record.difficulityLevel.middle.fullScore
+        }}</template>
+      </a-table-column>
+      <a-table-column key="percent" title="%" data-index="percent">
+        <template #default="{ record }">{{
+          $filters.numberToPercentFilter(record.difficulityLevel.middle.percent)
+        }}</template>
+      </a-table-column>
+    </a-table-column-group>
+    <a-table-column-group>
+      <template #title>低难度</template>
+      <a-table-column
+        key="questionCount"
+        title="题量"
+        data-index="questionCount"
+      >
+        <template #default="{ record }">{{
+          record.difficulityLevel.low.questionCount
+        }}</template>
+      </a-table-column>
+      <a-table-column key="fullScore" title="分值" data-index="fullScore">
+        <template #default="{ record }">{{
+          record.difficulityLevel.low.fullScore
+        }}</template>
+      </a-table-column>
+      <a-table-column key="percent" title="%" data-index="percent">
+        <template #default="{ record }">{{
+          $filters.numberToPercentFilter(record.difficulityLevel.low.percent)
+        }}</template>
+      </a-table-column>
+    </a-table-column-group>
+  </a-table>
 </template>
 
 <script setup lang="ts">

+ 117 - 76
src/features/paperAnalysis/DiscriminationDistriTable.vue

@@ -1,80 +1,121 @@
 <template>
-  <table class="custom-table">
-    <tr>
-      <td rowspan="2">题型</td>
-      <td rowspan="2">题量</td>
-      <td rowspan="2">满分</td>
-      <td rowspan="2">差异系数</td>
-      <td colspan="3">优</td>
-      <td colspan="3">良</td>
-      <td colspan="3">可</td>
-      <td colspan="3">差</td>
-    </tr>
-    <tr>
-      <td>题量</td>
-      <td>分值</td>
-      <td>%</td>
-      <td>题量</td>
-      <td>分值</td>
-      <td>%</td>
-      <td>题量</td>
-      <td>分值</td>
-      <td>%</td>
-      <td>题量</td>
-      <td>分值</td>
-      <td>%</td>
-    </tr>
-    <tr v-for="(item2, index) in props.questions" :key="index">
-      <td>
-        {{ item2.groupName || "全卷" }}
-      </td>
-      <td>
-        {{ item2.questionCount }}
-      </td>
-      <td>
-        {{ item2.totalScore }}
-      </td>
-      <td v-round-number>
-        {{ item2.coefficient }}
-      </td>
-      <td>
-        {{ item2.discriminationLevel.excellent.questionCount }}
-      </td>
-      <td>
-        {{ item2.discriminationLevel.excellent.fullScore }}
-      </td>
-      <td v-number-to-percent>
-        {{ item2.discriminationLevel.excellent.percent }}
-      </td>
-      <td>
-        {{ item2.discriminationLevel.good.questionCount }}
-      </td>
-      <td>
-        {{ item2.discriminationLevel.good.fullScore }}
-      </td>
-      <td v-number-to-percent>
-        {{ item2.discriminationLevel.good.percent }}
-      </td>
-      <td>
-        {{ item2.discriminationLevel.general.questionCount }}
-      </td>
-      <td>
-        {{ item2.discriminationLevel.general.fullScore }}
-      </td>
-      <td v-number-to-percent>
-        {{ item2.discriminationLevel.general.percent }}
-      </td>
-      <td>
-        {{ item2.discriminationLevel.bad.questionCount }}
-      </td>
-      <td>
-        {{ item2.discriminationLevel.bad.fullScore }}
-      </td>
-      <td v-number-to-percent>
-        {{ item2.discriminationLevel.bad.percent }}
-      </td>
-    </tr>
-  </table>
+  <a-table
+    class="page-table"
+    :data-source="props.questions"
+    :pagination="false"
+    bordered
+    size="middle"
+  >
+    <a-table-column key="groupName" title="题型" data-index="groupName">
+      <template #default="{ text }">{{ text || "全卷" }}</template>
+    </a-table-column>
+    <a-table-column
+      key="questionCount"
+      title="题量"
+      data-index="questionCount"
+    />
+    <a-table-column key="totalScore" title="满分" data-index="totalScore" />
+    <a-table-column key="coefficient" title="差异系数" data-index="coefficient">
+      <template #default="{ text }">{{
+        $filters.numberRoundFilter(text)
+      }}</template>
+    </a-table-column>
+
+    <a-table-column-group>
+      <template #title>优</template>
+      <a-table-column
+        key="questionCount"
+        title="题量"
+        data-index="questionCount"
+      >
+        <template #default="{ record }">{{
+          record.discriminationLevel.excellent.questionCount
+        }}</template>
+      </a-table-column>
+      <a-table-column key="fullScore" title="分值" data-index="fullScore">
+        <template #default="{ record }">{{
+          record.discriminationLevel.excellent.fullScore
+        }}</template>
+      </a-table-column>
+      <a-table-column key="percent" title="%" data-index="percent">
+        <template #default="{ record }">{{
+          $filters.numberToPercentFilter(
+            record.discriminationLevel.excellent.percent
+          )
+        }}</template>
+      </a-table-column>
+    </a-table-column-group>
+    <a-table-column-group>
+      <template #title>良</template>
+      <a-table-column
+        key="questionCount"
+        title="题量"
+        data-index="questionCount"
+      >
+        <template #default="{ record }">{{
+          record.discriminationLevel.good.questionCount
+        }}</template>
+      </a-table-column>
+      <a-table-column key="fullScore" title="分值" data-index="fullScore">
+        <template #default="{ record }">{{
+          record.discriminationLevel.good.fullScore
+        }}</template>
+      </a-table-column>
+      <a-table-column key="percent" title="%" data-index="percent">
+        <template #default="{ record }">{{
+          $filters.numberToPercentFilter(
+            record.discriminationLevel.good.percent
+          )
+        }}</template>
+      </a-table-column>
+    </a-table-column-group>
+    <a-table-column-group>
+      <template #title>可</template>
+      <a-table-column
+        key="questionCount"
+        title="题量"
+        data-index="questionCount"
+      >
+        <template #default="{ record }">{{
+          record.discriminationLevel.general.questionCount
+        }}</template>
+      </a-table-column>
+      <a-table-column key="fullScore" title="分值" data-index="fullScore">
+        <template #default="{ record }">{{
+          record.discriminationLevel.general.fullScore
+        }}</template>
+      </a-table-column>
+      <a-table-column key="percent" title="%" data-index="percent">
+        <template #default="{ record }">{{
+          $filters.numberToPercentFilter(
+            record.discriminationLevel.general.percent
+          )
+        }}</template>
+      </a-table-column>
+    </a-table-column-group>
+    <a-table-column-group>
+      <template #title>差</template>
+      <a-table-column
+        key="questionCount"
+        title="题量"
+        data-index="questionCount"
+      >
+        <template #default="{ record }">{{
+          record.discriminationLevel.bad.questionCount
+        }}</template>
+      </a-table-column>
+      <a-table-column key="fullScore" title="分值" data-index="fullScore">
+        <template #default="{ record }">{{
+          record.discriminationLevel.bad.fullScore
+        }}</template>
+      </a-table-column>
+      <a-table-column key="percent" title="%" data-index="percent">
+        <template #default="{ record }">{{
+          $filters.numberToPercentFilter(record.discriminationLevel.bad.percent)
+        }}</template>
+      </a-table-column>
+    </a-table-column-group>
+  </a-table>
 </template>
 
 <script setup lang="ts">

+ 67 - 88
src/features/paperAnalysis/PaperAnalysis.vue

@@ -1,88 +1,73 @@
 <template>
-  <div>
-    <div class="tw-bg-white tw-p-5 tw-rounded-xl tw-mb-5">
-      <a-input v-model:value="curProject.name" disabled style="width: 200px" />
-      <span class="tw-mr-4"></span>
-      <ProjectCourseSelect
-        v-model:value="courseId"
-        :projectId="projectId"
-        disabled
-      />
-      <span class="tw-mr-4"></span>
-      <PaperTypeSelect v-model:value="paperType" disabled />
-      <span class="tw-mr-4"></span>
-      <!-- 试卷名称: <a-input disabled :value="paperName" style="width: 120px" />
-      <span class="tw-mr-4"></span> -->
-      <a-button class="query-btn" @click="search">查询</a-button>
-
-      <div class="tw-float-right">
+  <div class="paper-analysis">
+    <a-card>
+      <template #title> {{}} </template>
+      <template #extra>
+        <a-button type="primary" @click="toViewReport">
+          <template #icon>
+            <svg-icon name="report-view" color="#fff"></svg-icon>
+          </template>
+          报告预览
+        </a-button>
         <a-button @click="goBack">返回</a-button>
-      </div>
+      </template>
+
+      <a-form class="tiny" :labelCol="{ style: { width: '72px' } }">
+        <a-form-item label="项目名称">
+          <span>{{ curProject.name }}</span>
+        </a-form-item>
+        <a-form-item label="试卷类型">
+          <span>{{ paperType }}</span>
+        </a-form-item>
+      </a-form>
+    </a-card>
+
+    <div class="part-box">
+      <a-tabs v-model:activeKey="activeTab" type="card">
+        <a-tab-pane key="1" tab="科目频数分布">
+          <ScoreRate v-if="courseId" :courseId="courseId" />
+        </a-tab-pane>
+        <a-tab-pane key="2" tab="试卷题目编排">
+          <QuestionBianPai :questions="paperQuestions" />
+        </a-tab-pane>
+        <a-tab-pane key="3" tab="试题特征量数">
+          <QuestionAttr :questions="paperQuestions" />
+        </a-tab-pane>
+        <a-tab-pane key="4" tab="试题难度分组分布">
+          <QuestionDifficultyGroup
+            :questions="paperQuestions"
+            :totalScore="paper.totalScore"
+            :projectId="projectId"
+            :courseId="courseId"
+            :paperId="paperId"
+            :startScore="startScore"
+            :rangeConfig="paper.difficulityRangeConfig"
+          />
+        </a-tab-pane>
+        <a-tab-pane key="5" tab="题型难度分布">
+          <QuestionTypeDifficulty
+            :projectId="projectId"
+            :questions="paperQuestionGroups"
+          />
+        </a-tab-pane>
+        <a-tab-pane key="6" tab="题型区分度分布">
+          <QuestionTypeDiscrimination
+            :projectId="projectId"
+            :questions="paperQuestionGroups"
+          />
+        </a-tab-pane>
+      </a-tabs>
     </div>
 
-    <div class="tw-bg-white tw-p-5 tw-rounded-xl">
-      <div class="tw-flex tw-items-center tw-justify-between">
-        <a-radio-group v-model:value="activeTab">
-          <a-radio-button value="6">科目频数分布</a-radio-button>
-          <a-radio-button value="1">试卷题目编排</a-radio-button>
-          <a-radio-button value="2">试题特征量数</a-radio-button>
-          <a-radio-button value="5">试题难度分组分布</a-radio-button>
-          <a-radio-button value="3">题型难度分布</a-radio-button>
-          <a-radio-button value="4">题型区分度分布</a-radio-button>
-        </a-radio-group>
-
-        <a-button
-          :type="activeTab === '7' ? 'primary' : 'default'"
-          class="tw-mr"
-          @click="toViewReport"
-          >报告预览</a-button
-        >
-      </div>
-
-      <a-divider />
-
-      <div v-if="activeTab === '1'">
-        <QuestionBianPai :questions="paperQuestions" />
-      </div>
-      <div v-if="activeTab === '2'">
-        <QuestionAttr :questions="paperQuestions" />
-      </div>
-      <div v-if="activeTab === '3'">
-        <QuestionTypeDifficulty
-          :projectId="projectId"
-          :questions="paperQuestionGroups"
-        />
-      </div>
-      <div v-if="activeTab === '4'">
-        <QuestionTypeDiscrimination
-          :projectId="projectId"
-          :questions="paperQuestionGroups"
-        />
-      </div>
-      <div v-if="activeTab === '5'">
-        <QuestionDifficultyGroup
-          :questions="paperQuestions"
-          :totalScore="paper.totalScore"
-          :projectId="projectId"
-          :courseId="courseId"
-          :paperId="paperId"
-          :startScore="startScore"
-          :rangeConfig="paper.difficulityRangeConfig"
-        />
-      </div>
-      <div v-if="activeTab === '6'">
-        <ScoreRate v-if="courseId" :courseId="courseId" />
-      </div>
-      <div v-if="activeTab === '7'">
-        <PaperReport
-          :projectId="projectId"
-          viewType="view"
-          :paperId="paperId"
-          :compareProjectId="compareProjectId"
-        />
-      </div>
+    <div v-if="activeTab === '7'">
+      <!-- TODO: -->
+      <PaperReport
+        :projectId="projectId"
+        viewType="view"
+        :paperId="paperId"
+        :compareProjectId="compareProjectId"
+      />
     </div>
-
     <SelectProject
       ref="selectProjectRef"
       :disableIds="[projectId]"
@@ -96,7 +81,6 @@ import { useMainStore } from "@/store";
 import { goBack } from "@/utils/utils";
 import { watch, onMounted, nextTick } from "vue";
 import { useRoute } from "vue-router";
-// import ProjectSelect from "@/components/ProjectSelect.vue";
 import {
   getPaper,
   getPaperQuestionGroups,
@@ -116,17 +100,12 @@ import { Paper, SASQuestion, SASQuestionGroup, Project } from "@/types";
 import { message } from "ant-design-vue";
 
 const store = useMainStore();
-store.currentLocation = "项目管理 / 项目列表 / 详情";
-
-let activeTab = $ref("6");
+store.currentLocation = "项目列表 / 查询结果 / 详情";
 
-// let rootOrgId = computed(() => {
-//   return store.userInfo.rootOrgId;
-// });
+let activeTab = $ref("5");
 let courseId = $ref(undefined as unknown as number);
 let startScore = $ref(undefined as unknown as number);
 let paperType = $ref(undefined as undefined | string);
-// let paperName = $ref(undefined as undefined | string);
 const route = useRoute();
 const projectId = +route.params.projectId;
 const paperId = +route.params.paperId;

+ 9 - 13
src/features/paperAnalysis/QuestionAttr.vue

@@ -1,17 +1,13 @@
 <template>
-  <div>
-    <!-- <div class="tw-flex tw-justify-end tw-mb-2">
-      <a-button @click="openModal">说明</a-button>
-    </div> -->
-
-    <a-table
-      style="width: 100%; overflow-x: scroll"
-      rowKey="id"
-      :columns="columns"
-      :data-source="props.questions"
-      :pagination="{ pageSize: 200, hideOnSinglePage: true }"
-    ></a-table>
-  </div>
+  <a-table
+    class="page-table"
+    rowKey="id"
+    :columns="columns"
+    :data-source="props.questions"
+    :pagination="false"
+    bordered
+    size="middle"
+  ></a-table>
 </template>
 
 <script setup lang="ts">

+ 37 - 24
src/features/paperAnalysis/QuestionBianPai.vue

@@ -1,31 +1,27 @@
 <template>
-  <div
-    class="question-bianpai tw-flex tw-gap-4"
-    style="background-color: #e1e6f1"
-  >
-    <a-table
-      rowKey="id"
-      :columns="columns"
-      :data-source="props.questions"
-      :pagination="{ pageSize: 200, hideOnSinglePage: true }"
-    ></a-table>
-
-    <div class="tw-flex tw-flex-1 tw-gap-4 tw-pt-4">
-      <div style="flex-grow: 1">
+  <a-row :gutter="16">
+    <a-col :span="6">
+      <a-table
+        class="page-table"
+        rowKey="id"
+        :scroll="{ y: 453 }"
+        :columns="columns"
+        :data-source="props.questions"
+        :pagination="false"
+        bordered
+        size="middle"
+      ></a-table>
+    </a-col>
+    <a-col :span="18">
+      <div class="chart-box">
         <v-chart
           class="chart"
           :option="chartOption(props.questions)"
           :autoresize="true"
         />
       </div>
-
-      <!-- <a-button
-        style="margin-left: -100px; margin-right: 20px"
-        @click="openModal"
-        >说明</a-button
-      > -->
-    </div>
-  </div>
+    </a-col>
+  </a-row>
 </template>
 
 <script setup lang="ts">
@@ -62,20 +58,37 @@ function chartOption(questions: SASQuestion[]) {
   return {
     title: {
       text: "难度",
-      left: "left",
+      left: 16,
+      top: 16,
+      textStyle: {
+        fontSize: 14,
+        color: "#595959",
+      },
     },
+    color: ["#4080FF"],
     grid: {
-      left: 40,
+      left: 50,
+      right: 70,
       bottom: 50,
-      right: 80,
+      top: 60,
     },
     xAxis: {
       type: "category",
       data: questions.map((v) => v.mainNumber + "-" + v.subNumber),
       name: "题号",
+      axisLabel: {
+        color: "#8C8C8C",
+        fontSize: 12,
+        fontWeight: 400,
+      },
     },
     yAxis: {
       type: "value",
+      axisLabel: {
+        color: "#8C8C8C",
+        fontSize: 12,
+        fontWeight: 400,
+      },
     },
     series: [
       {

+ 91 - 78
src/features/paperAnalysis/QuestionDifficultyGroup.vue

@@ -1,69 +1,57 @@
 <template>
   <div>
-    <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-      <h3 class="section-title">按本科目试卷成绩分组</h3>
-      <div class="tw-flex tw-gap-2">
-        <a-button @click="visible = true">图形化展示</a-button>
-        <!-- <a-button @click="openModal1">说明</a-button> -->
-      </div>
+    <div class="box-justify mb-16">
+      <h2 class="h2-title">按本科目试卷成绩分组</h2>
+      <a-button @click="visible = true">图形化展示</a-button>
     </div>
 
-    <div class="tw-overflow-y-scroll">
-      <table class="custom-table">
-        <tr>
-          <td>大题号</td>
-          <td>小题号</td>
-          <template v-for="(item, index) in props.totalScore / 10" :key="index">
-            <td v-if="index >= limitScoreIndex">{{ index * 10 }}-</td>
+    <a-table
+      class="page-table mb-16"
+      :data-source="props.questions"
+      :pagination="false"
+      bordered
+      size="middle"
+    >
+      <a-table-column key="mainNumber" title="大题号" data-index="mainNumber" />
+      <a-table-column key="subNumber" title="小题号" data-index="subNumber" />
+      <template v-for="num in maxScoreStep" :key="num">
+        <a-table-column
+          v-if="num - 1 >= limitScoreIndex"
+          :title="`${(num - 1) * 10}-`"
+          :key="num"
+          data-index="difficulityLevel"
+        >
+          <template #default="{ record }">
+            {{ $filters.numberRoundFilter(record.difficulityLevel[num - 1]) }}
           </template>
-        </tr>
-        <tr v-for="(item, index) in props.questions" :key="index">
-          <td>{{ item.mainNumber }}</td>
-          <td>{{ item.subNumber }}</td>
-          <template
-            v-for="(item2, index2) in item.difficulityLevel"
-            :key="index2"
-          >
-            <td v-if="index2 >= limitScoreIndex" v-round-number>{{ item2 }}</td>
+        </a-table-column>
+      </template>
+    </a-table>
+
+    <a-table
+      v-if="props.rangeConfig"
+      class="page-table mb-16"
+      :data-source="props.questions"
+      :pagination="false"
+      bordered
+      size="middle"
+    >
+      <a-table-column key="mainNumber" title="大题号" data-index="mainNumber" />
+      <a-table-column key="subNumber" title="小题号" data-index="subNumber" />
+      <template v-for="(item, index) in rangeConfigList" :key="index">
+        <a-table-column
+          :title="scoreTitle(item)"
+          data-index="difficulityGroupLevel"
+        >
+          <template #default="{ record }">
+            {{
+              $filters.numberRoundFilter(record.difficulityGroupLevel[index])
+            }}
           </template>
-        </tr>
-      </table>
-    </div>
-
-    <!-- <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-      <h3 class="section-title">按初试总分分组</h3>
-      <div class="tw-flex tw-gap-2">
-        <a-button @click="openRangeConfigModal">分段设置</a-button>
-        <a-button @click="openModal2">说明</a-button>
-      </div>
-    </div> -->
-
-    <div class="tw-overflow-y-scroll">
-      <table v-if="props.rangeConfig" class="custom-table">
-        <tr>
-          <td>大题号</td>
-          <td>小题号</td>
-          <td
-            v-for="(item2, index) in JSON.parse(props.rangeConfig)"
-            :key="index"
-          >
-            {{ scoreTitle(item2) }}
-          </td>
-        </tr>
-        <tr v-for="(item, index) in props.questions" :key="index">
-          <td>{{ item.mainNumber }}</td>
-          <td>{{ item.subNumber }}</td>
-          <td
-            v-for="(item2, index2) in item.difficulityGroupLevel"
-            :key="index2"
-            v-round-number
-          >
-            {{ item2 }}
-          </td>
-        </tr>
-      </table>
-      <div v-else>请先进行分段设置。</div>
-    </div>
+        </a-table-column>
+      </template>
+    </a-table>
+    <!-- <a-alert v-else message="请先进行分段设置。" type="warning" closable /> -->
 
     <CommonRangeConfig
       ref="rangeConfigRef"
@@ -76,21 +64,25 @@
     <a-modal
       v-model:open="visible"
       title="按本科目试卷成绩分组"
-      okText="确定"
-      cancelText="取消"
-      width="600px"
-      :bodyStyle="{ height: '600px' }"
+      :footer="null"
+      :width="704"
       @ok="visible = false"
     >
-      <div style="width: 100%; height: 600px; overflow: scroll">
-        <v-chart
-          v-for="(question, index) in props.questions"
-          :key="index"
-          class="chart"
-          style="height: 300px"
-          :option="questionOptions(question)"
-          :autoresize="true"
-        />
+      <div
+        v-for="(question, index) in props.questions"
+        :key="index"
+        class="diff-chart"
+      >
+        <h4 class="diff-chart-title">
+          {{ question.mainNumber }}-{{ question.subNumber }}
+        </h4>
+        <div class="diff-chart-body">
+          <v-chart
+            class="chart"
+            :option="questionOptions(question)"
+            :autoresize="true"
+          />
+        </div>
       </div>
     </a-modal>
   </div>
@@ -129,10 +121,16 @@ let rangeConfigRef = $ref(null);
 
 let selectedRangeConfig = $ref([]);
 let selectedCourseId = $ref(0);
-console.log(props.rangeConfig);
 
 let limitScoreIndex = computed(() => {
-  return Math.floor(props.startScore / 10);
+  return Math.floor((props.startScore || 0) / 10);
+});
+let maxScoreStep = computed(() => {
+  return Math.floor((props.totalScore || 0) / 10);
+});
+
+const rangeConfigList = computed(() => {
+  return props.rangeConfig ? JSON.parse(props.rangeConfig) : [];
 });
 
 // const openRangeConfigModal = () => {
@@ -174,16 +172,31 @@ function questionOptions(question: SASQuestion) {
     xAxis.push(i * 10 + "-");
   }
   return {
-    title: {
-      text: question.mainNumber + "-" + question.subNumber,
-      left: "left",
+    color: ["#4080FF"],
+    grid: {
+      left: 40,
+      right: 60,
+      bottom: 40,
+      top: 40,
     },
     xAxis: {
       type: "category",
       data: xAxis,
+      name: "分数段",
+      axisLabel: {
+        color: "#8C8C8C",
+        fontSize: 12,
+        fontWeight: 400,
+      },
     },
     yAxis: {
       type: "value",
+      name: "难度",
+      axisLabel: {
+        color: "#8C8C8C",
+        fontSize: 12,
+        fontWeight: 400,
+      },
     },
     series: [
       {

+ 58 - 161
src/features/paperAnalysis/QuestionTypeDifficulty.vue

@@ -1,144 +1,20 @@
 <template>
   <div>
-    <div>
+    <div class="mb-16">
       <span>设置:</span>
       <a-button @click="importModalVisible = true">题型分布设置</a-button>
     </div>
 
-    <div v-if="props.questions.some((q) => q.type === 'TYPE1')">
-      <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-        <h3 class="section-title">题型题目难度等级构成分布(一)</h3>
-        <div class="tw-flex tw-gap-2">
-          <!-- <a-button @click="openModalDESCRIBE070">说明</a-button> -->
+    <template v-for="(item, index) in typeList" :key="index">
+      <div v-if="checkQuestionExist(item)" class="score-part">
+        <div class="score-part-head">
+          <h2>{{ item.title }}</h2>
         </div>
-      </div>
-
-      <DifficultyDistriTable
-        :questions="
-          props.questions.filter((q) => q.type === 'TYPE1' || !q.type)
-        "
-      />
-    </div>
-
-    <div v-if="props.questions.some((q) => q.type === 'TYPE2')">
-      <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-        <h3 class="section-title">题型题目难度等级构成分布(二)</h3>
-        <div class="tw-flex tw-gap-2">
-          <!-- <a-button @click="openModalDESCRIBE080">说明</a-button> -->
-        </div>
-      </div>
-
-      <DifficultyDistriTable
-        :questions="
-          props.questions.filter((q) => q.type === 'TYPE2' || !q.type)
-        "
-      />
-    </div>
-
-    <div v-if="props.questions.some((q) => q.type === 'TYPE3')">
-      <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-        <h3 class="section-title">题型题目难度等级构成分布(三)</h3>
-        <div class="tw-flex tw-gap-2">
-          <!-- <a-button @click="openModalDESCRIBE090">说明</a-button> -->
-        </div>
-      </div>
-
-      <DifficultyDistriTable
-        :questions="
-          props.questions.filter((q) => q.type === 'TYPE3' || !q.type)
-        "
-      />
-    </div>
-
-    <div v-if="props.questions.some((q) => q.type === 'CONTENT1')">
-      <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-        <h3 class="section-title">内容题目难度等级构成分布(一)</h3>
-        <div class="tw-flex tw-gap-2">
-          <!-- <a-button @click="openModalDESCRIBE100">说明</a-button> -->
-        </div>
-      </div>
-
-      <DifficultyDistriTable
-        :questions="
-          props.questions.filter((q) => q.type === 'CONTENT1' || !q.type)
-        "
-      />
-    </div>
-
-    <div v-if="props.questions.some((q) => q.type === 'CONTENT2')">
-      <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-        <h3 class="section-title">内容题目难度等级构成分布(二)</h3>
-        <div class="tw-flex tw-gap-2">
-          <!-- <a-button @click="openModalDESCRIBE110">说明</a-button> -->
-        </div>
-      </div>
-
-      <DifficultyDistriTable
-        :questions="
-          props.questions.filter((q) => q.type === 'CONTENT2' || !q.type)
-        "
-      />
-    </div>
-
-    <div v-if="props.questions.some((q) => q.type === 'CONTENT3')">
-      <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-        <h3 class="section-title">内容题目难度等级构成分布(三)</h3>
-        <div class="tw-flex tw-gap-2">
-          <!-- <a-button @click="openModalDESCRIBE120">说明</a-button> -->
-        </div>
-      </div>
-
-      <DifficultyDistriTable
-        :questions="
-          props.questions.filter((q) => q.type === 'CONTENT3' || !q.type)
-        "
-      />
-    </div>
-
-    <div v-if="props.questions.some((q) => q.type === 'ABILITY1')">
-      <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-        <h3 class="section-title">能力题目难度等级构成分布(一)</h3>
-        <div class="tw-flex tw-gap-2">
-          <!-- <a-button @click="openModalDESCRIBE130">说明</a-button> -->
+        <div class="score-part-body">
+          <DifficultyDistriTable :questions="getQuestions(item)" />
         </div>
       </div>
-
-      <DifficultyDistriTable
-        :questions="
-          props.questions.filter((q) => q.type === 'ABILITY1' || !q.type)
-        "
-      />
-    </div>
-
-    <div v-if="props.questions.some((q) => q.type === 'ABILITY2')">
-      <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-        <h3 class="section-title">能力题目难度等级构成分布(二)</h3>
-        <div class="tw-flex tw-gap-2">
-          <!-- <a-button @click="openModalDESCRIBE140">说明</a-button> -->
-        </div>
-      </div>
-
-      <DifficultyDistriTable
-        :questions="
-          props.questions.filter((q) => q.type === 'ABILITY2' || !q.type)
-        "
-      />
-    </div>
-
-    <div v-if="props.questions.some((q) => q.type === 'ABILITY3')">
-      <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-        <h3 class="section-title">能力题目难度等级构成分布(三)</h3>
-        <div class="tw-flex tw-gap-2">
-          <!-- <a-button @click="openModalDESCRIBE150">说明</a-button> -->
-        </div>
-      </div>
-
-      <DifficultyDistriTable
-        :questions="
-          props.questions.filter((q) => q.type === 'ABILITY3' || !q.type)
-        "
-      />
-    </div>
+    </template>
 
     <a-modal
       v-model:open="importModalVisible"
@@ -175,33 +51,56 @@ const props = defineProps<{
   projectId: number;
 }>();
 
-// function openModalDESCRIBE070() {
-//   EventBus.emit("SHOW_SETTING", "DESCRIBE070");
-// }
-// function openModalDESCRIBE080() {
-//   EventBus.emit("SHOW_SETTING", "DESCRIBE080");
-// }
-// function openModalDESCRIBE090() {
-//   EventBus.emit("SHOW_SETTING", "DESCRIBE090");
-// }
-// function openModalDESCRIBE100() {
-//   EventBus.emit("SHOW_SETTING", "DESCRIBE100");
-// }
-// function openModalDESCRIBE110() {
-//   EventBus.emit("SHOW_SETTING", "DESCRIBE110");
-// }
-// function openModalDESCRIBE120() {
-//   EventBus.emit("SHOW_SETTING", "DESCRIBE120");
-// }
-// function openModalDESCRIBE130() {
-//   EventBus.emit("SHOW_SETTING", "DESCRIBE130");
-// }
-// function openModalDESCRIBE140() {
-//   EventBus.emit("SHOW_SETTING", "DESCRIBE140");
-// }
-// function openModalDESCRIBE150() {
-//   EventBus.emit("SHOW_SETTING", "DESCRIBE150");
-// }
+interface TypeItem {
+  title: string;
+  field: string;
+}
+
+const typeList = [
+  {
+    title: "题型题目难度等级构成分布(一)",
+    field: "TYPE1",
+  },
+  {
+    title: "题型题目难度等级构成分布(二)",
+    field: "TYPE2",
+  },
+  {
+    title: "题型题目难度等级构成分布(三)",
+    field: "TYPE3",
+  },
+  {
+    title: "内容题目难度等级构成分布(一)",
+    field: "CONTENT1",
+  },
+  {
+    title: "内容题目难度等级构成分布(二)",
+    field: "CONTENT2",
+  },
+  {
+    title: "内容题目难度等级构成分布(三)",
+    field: "CONTENT3",
+  },
+  {
+    title: "能力题目难度等级构成分布(一)",
+    field: "ABILITY1",
+  },
+  {
+    title: "能力题目难度等级构成分布(二)",
+    field: "ABILITY2",
+  },
+  {
+    title: "能力题目难度等级构成分布(三)",
+    field: "ABILITY3",
+  },
+];
+
+function getQuestions(item: TypeItem) {
+  return props.questions.filter((q) => q.type === item.field || !q.type);
+}
+function checkQuestionExist(item: TypeItem) {
+  return props.questions.some((q) => q.type === item.field);
+}
 
 /** <handleImport> */
 let importModalVisible = $ref<boolean>(false);
@@ -234,5 +133,3 @@ async function downloadTpl() {
   await downloadFileURL("/api/ess/sasQuestionGroup/template");
 }
 </script>
-
-<style scoped></style>

+ 58 - 161
src/features/paperAnalysis/QuestionTypeDiscrimination.vue

@@ -1,144 +1,20 @@
 <template>
   <div>
-    <div>
+    <div class="mb-16">
       <span>设置:</span>
       <a-button @click="importModalVisible = true">题型分布设置</a-button>
     </div>
 
-    <div v-if="props.questions.some((q) => q.type === 'TYPE1')">
-      <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-        <h3 class="section-title">题型题目区分度等级构成分布(一)</h3>
-        <div class="tw-flex tw-gap-2">
-          <!-- <a-button @click="openModalDESCRIBE160">说明</a-button> -->
+    <template v-for="(item, index) in typeList" :key="index">
+      <div v-if="checkQuestionExist(item)" class="score-part">
+        <div class="score-part-head">
+          <h2>{{ item.title }}</h2>
         </div>
-      </div>
-
-      <DiscriminationDistriTable
-        :questions="
-          props.questions.filter((q) => q.type === 'TYPE1' || !q.type)
-        "
-      />
-    </div>
-
-    <div v-if="props.questions.some((q) => q.type === 'TYPE2')">
-      <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-        <h3 class="section-title">题型题目区分度等级构成分布(二)</h3>
-        <div class="tw-flex tw-gap-2">
-          <!-- <a-button @click="openModalDESCRIBE170">说明</a-button> -->
-        </div>
-      </div>
-
-      <DiscriminationDistriTable
-        :questions="
-          props.questions.filter((q) => q.type === 'TYPE2' || !q.type)
-        "
-      />
-    </div>
-
-    <div v-if="props.questions.some((q) => q.type === 'TYPE3')">
-      <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-        <h3 class="section-title">题型题目区分度等级构成分布(三)</h3>
-        <div class="tw-flex tw-gap-2">
-          <!-- <a-button @click="openModalDESCRIBE180">说明</a-button> -->
-        </div>
-      </div>
-
-      <DiscriminationDistriTable
-        :questions="
-          props.questions.filter((q) => q.type === 'TYPE3' || !q.type)
-        "
-      />
-    </div>
-
-    <div v-if="props.questions.some((q) => q.type === 'CONTENT1')">
-      <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-        <h3 class="section-title">内容题目区分度等级构成分布(一)</h3>
-        <div class="tw-flex tw-gap-2">
-          <!-- <a-button @click="openModalDESCRIBE190">说明</a-button> -->
-        </div>
-      </div>
-
-      <DiscriminationDistriTable
-        :questions="
-          props.questions.filter((q) => q.type === 'CONTENT1' || !q.type)
-        "
-      />
-    </div>
-
-    <div v-if="props.questions.some((q) => q.type === 'CONTENT2')">
-      <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-        <h3 class="section-title">内容题目区分度等级构成分布(二)</h3>
-        <div class="tw-flex tw-gap-2">
-          <!-- <a-button @click="openModalDESCRIBE200">说明</a-button> -->
-        </div>
-      </div>
-
-      <DiscriminationDistriTable
-        :questions="
-          props.questions.filter((q) => q.type === 'CONTENT2' || !q.type)
-        "
-      />
-    </div>
-
-    <div v-if="props.questions.some((q) => q.type === 'CONTENT3')">
-      <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-        <h3 class="section-title">内容题目区分度等级构成分布(三)</h3>
-        <div class="tw-flex tw-gap-2">
-          <!-- <a-button @click="openModalDESCRIBE210">说明</a-button> -->
-        </div>
-      </div>
-
-      <DiscriminationDistriTable
-        :questions="
-          props.questions.filter((q) => q.type === 'CONTENT3' || !q.type)
-        "
-      />
-    </div>
-
-    <div v-if="props.questions.some((q) => q.type === 'ABILITY1')">
-      <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-        <h3 class="section-title">能力题目区分度等级构成分布(一)</h3>
-        <div class="tw-flex tw-gap-2">
-          <!-- <a-button @click="openModalDESCRIBE220">说明</a-button> -->
+        <div class="score-part-body">
+          <DiscriminationDistriTable :questions="getQuestions(item)" />
         </div>
       </div>
-
-      <DiscriminationDistriTable
-        :questions="
-          props.questions.filter((q) => q.type === 'ABILITY1' || !q.type)
-        "
-      />
-    </div>
-
-    <div v-if="props.questions.some((q) => q.type === 'ABILITY2')">
-      <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-        <h3 class="section-title">能力题目区分度等级构成分布(二)</h3>
-        <div class="tw-flex tw-gap-2">
-          <!-- <a-button @click="openModalDESCRIBE230">说明</a-button> -->
-        </div>
-      </div>
-
-      <DiscriminationDistriTable
-        :questions="
-          props.questions.filter((q) => q.type === 'ABILITY2' || !q.type)
-        "
-      />
-    </div>
-
-    <div v-if="props.questions.some((q) => q.type === 'ABILITY3')">
-      <div class="tw-flex tw-justify-between tw-items-center tw-my-4">
-        <h3 class="section-title">能力题目区分度等级构成分布(三)</h3>
-        <div class="tw-flex tw-gap-2">
-          <!-- <a-button @click="openModalDESCRIBE240">说明</a-button> -->
-        </div>
-      </div>
-
-      <DiscriminationDistriTable
-        :questions="
-          props.questions.filter((q) => q.type === 'ABILITY3' || !q.type)
-        "
-      />
-    </div>
+    </template>
 
     <a-modal
       v-model:open="importModalVisible"
@@ -175,33 +51,56 @@ const props = defineProps<{
   projectId: number;
 }>();
 
-// function openModalDESCRIBE160() {
-//   EventBus.emit("SHOW_SETTING", "DESCRIBE160");
-// }
-// function openModalDESCRIBE170() {
-//   EventBus.emit("SHOW_SETTING", "DESCRIBE170");
-// }
-// function openModalDESCRIBE180() {
-//   EventBus.emit("SHOW_SETTING", "DESCRIBE180");
-// }
-// function openModalDESCRIBE190() {
-//   EventBus.emit("SHOW_SETTING", "DESCRIBE190");
-// }
-// function openModalDESCRIBE200() {
-//   EventBus.emit("SHOW_SETTING", "DESCRIBE200");
-// }
-// function openModalDESCRIBE210() {
-//   EventBus.emit("SHOW_SETTING", "DESCRIBE210");
-// }
-// function openModalDESCRIBE220() {
-//   EventBus.emit("SHOW_SETTING", "DESCRIBE220");
-// }
-// function openModalDESCRIBE230() {
-//   EventBus.emit("SHOW_SETTING", "DESCRIBE230");
-// }
-// function openModalDESCRIBE240() {
-//   EventBus.emit("SHOW_SETTING", "DESCRIBE240");
-// }
+interface TypeItem {
+  title: string;
+  field: string;
+}
+
+const typeList = [
+  {
+    title: "题型题目区分度等级构成分布(一)",
+    field: "TYPE1",
+  },
+  {
+    title: "题型题目区分度等级构成分布(二)",
+    field: "TYPE2",
+  },
+  {
+    title: "题型题目区分度等级构成分布(三)",
+    field: "TYPE3",
+  },
+  {
+    title: "内容题目区分度等级构成分布(一)",
+    field: "CONTENT1",
+  },
+  {
+    title: "内容题目区分度等级构成分布(二)",
+    field: "CONTENT2",
+  },
+  {
+    title: "内容题目区分度等级构成分布(三)",
+    field: "CONTENT3",
+  },
+  {
+    title: "能力题目区分度等级构成分布(一)",
+    field: "ABILITY1",
+  },
+  {
+    title: "能力题目区分度等级构成分布(二)",
+    field: "ABILITY2",
+  },
+  {
+    title: "能力题目区分度等级构成分布(三)",
+    field: "ABILITY3",
+  },
+];
+
+function getQuestions(item: TypeItem) {
+  return props.questions.filter((q) => q.type === item.field || !q.type);
+}
+function checkQuestionExist(item: TypeItem) {
+  return props.questions.some((q) => q.type === item.field);
+}
 
 /** <handleImport> */
 let importModalVisible = $ref<boolean>(false);
@@ -234,5 +133,3 @@ async function downloadTpl() {
   await downloadFileURL("/api/ess/sasQuestionGroup/template");
 }
 </script>
-
-<style scoped></style>

+ 79 - 61
src/features/projectDataManagement/ProjectDataManagement.vue

@@ -1,67 +1,83 @@
 <template>
   <div>
-    <div class="tw-bg-white tw-p-5 tw-rounded-xl tw-mb-5">
-      项目名称
-      <a-input
-        v-model:value="project.name"
-        disabled
-        class="tw-mr-4"
-        style="width: 178px"
-        placeholder="项目名称"
-        allowClear
-      ></a-input>
-
-      <span class="tw-mr-4"></span>
-      数据解析状态
-      <a-input
-        disabled
-        :value="$filters.projectStatusFilter(project.status)"
-        class="tw-mr-4"
-        style="width: 178px"
-        placeholder="项目名称"
-        allowClear
-      ></a-input>
-      <span class="tw-mr-4"></span>
-      <a-button @click="goBack">返回</a-button>
-    </div>
-
-    <div class="tw-bg-white tw-p-5 tw-rounded-xl">
-      <a-radio-group
-        v-model:value="dataSourceChannel"
-        :options="[...plainOptions]"
-      />
-      <div class="tw-mb-5"></div>
-      <a-form v-if="dataSourceChannel === '数据上传'">
-        <a-form-item label="文件地址">
-          <input id="file-input" :multiple="false" type="file" />
-        </a-form-item>
-        <a-form-item label="下载模板">
-          <a-button class="download-tpl-btn" @click="downloadTpl">
-            下载模板
-          </a-button>
-        </a-form-item>
-        <a-button type="primary" @click="handleImport">保存</a-button>
-      </a-form>
-      <a-form v-else>
-        <a-form-item label="系统域名">
-          <a-input v-model:value="domain" placeholder="http://192.168.10.100" />
+    <a-card title="数据管理">
+      <template #extra>
+        <a-button @click="goBack">返回</a-button>
+      </template>
+
+      <a-form class="tiny" :labelCol="{ style: { width: '102px' } }">
+        <a-form-item label="项目名称">
+          <span>{{ project.name }}</span>
         </a-form-item>
-        <a-form-item label="考试标识">
-          <a-select v-model:value="examId" placeholder="请选择考试">
-            <a-select-option
-              v-for="(item, index) in exams"
-              :key="index"
-              :value="item.id"
-            >
-              {{ item.name }}({{ item.id }})
-            </a-select-option>
-          </a-select>
+        <a-form-item label="数据解析状态">
+          <status-tag :value="project.status" type="projectStatus"></status-tag>
         </a-form-item>
-        <div class="tw-flex tw-gap-2">
-          <a-button type="primary" @click="handleTestServer">测试</a-button>
-          <a-button type="primary" @click="handleSync">保存</a-button>
-        </div>
       </a-form>
+    </a-card>
+    <div class="part-box">
+      <a-tabs v-model:activeKey="dataSourceChannel" type="card">
+        <a-tab-pane key="资料同步" tab="资料同步">
+          <a-form :labelCol="{ style: { width: '72px' } }" style="width: 400px">
+            <a-form-item label="系统域名">
+              <a-input
+                v-model:value="domain"
+                placeholder="http://192.168.10.100"
+              />
+            </a-form-item>
+            <a-form-item label="考试标识">
+              <a-select v-model:value="examId" placeholder="请选择考试">
+                <a-select-option
+                  v-for="(item, index) in exams"
+                  :key="index"
+                  :value="item.id"
+                >
+                  {{ item.name }}({{ item.id }})
+                </a-select-option>
+              </a-select>
+            </a-form-item>
+            <div>
+              <a-button type="primary" @click="handleTestServer"
+                >接口检测</a-button
+              >
+              <a-button type="primary" @click="handleSync">数据计算</a-button>
+            </div>
+          </a-form>
+        </a-tab-pane>
+        <a-tab-pane key="数据上传" tab="数据上传">
+          <a-form :labelCol="{ style: { width: '72px' } }">
+            <a-form-item label="模板文件">
+              <a-button :loading="loading" @click="downloadTpl">
+                <template #icon>
+                  <svg-icon name="download"></svg-icon>
+                </template>
+                下载模板
+              </a-button>
+            </a-form-item>
+            <a-form-item label="文件地址">
+              <a-upload
+                accept=".lic"
+                :before-upload="() => false"
+                :showUploadList="false"
+              >
+                <a-button>
+                  <template #icon>
+                    <svg-icon name="file"></svg-icon>
+                  </template>
+                  选择文件
+                </a-button>
+              </a-upload>
+            </a-form-item>
+            <a-form-item>
+              <a-button
+                style="margin-left: 72px"
+                type="primary"
+                @click="handleImport"
+                >保存</a-button
+              >
+            </a-form-item>
+          </a-form>
+        </a-tab-pane>
+      </a-tabs>
     </div>
   </div>
 </template>
@@ -80,9 +96,10 @@ import { debounce } from "lodash-es";
 import QueryString from "qs";
 import { onMounted, computed, watch } from "vue";
 import { useRoute } from "vue-router";
+import useLoading from "@/hooks/loading";
 
 const store = useMainStore();
-store.currentLocation = "项目管理 / 项目列表 / 数据管理";
+store.currentLocation = "项目列表 / 数据管理";
 
 let rootOrgId = $ref(undefined as unknown as number);
 
@@ -91,6 +108,7 @@ const projectId = +route.params.projectId;
 let data = $ref<Project[]>([]);
 
 let project = computed(() => data[0] || {});
+const { loading, setLoading } = useLoading();
 
 async function search() {
   await fetchData();
@@ -127,7 +145,7 @@ async function handleImport() {
 }
 
 const plainOptions = ["数据上传", "资料同步"] as const;
-let dataSourceChannel: typeof plainOptions[number] = $ref("数据上传");
+let dataSourceChannel: (typeof plainOptions)[number] = $ref("数据上传");
 
 const domain = $ref("");
 

+ 49 - 46
src/features/projectManagement/ProjectManagement.vue

@@ -101,55 +101,56 @@
               >
                 删除
               </a-button>
-              <a-popover placement="bottom" trigger="click">
+              <a-popover
+                placement="bottomRight"
+                overlayClassName="action-more"
+                trigger="click"
+              >
                 <template #content>
-                  <div class="tw-flex tw-flex-col">
-                    <a-button
-                      v-if="!store.isCourseAdmin"
-                      type="text"
-                      style="color: white"
-                      @click="handleLogsOfProject(record.id)"
-                    >
-                      错误报告
-                    </a-button>
-                    <a-button
-                      v-if="
-                        record.status !== 'PROCESSING' &&
-                        store.isGreaterThanEqualRootOrgAdmin
-                      "
-                      type="text"
-                      style="color: white"
-                      @click="goProjectDataSource(record.id)"
-                    >
-                      数据管理
-                    </a-button>
-                    <a-button
-                      v-if="
-                        record.status !== 'PROCESSING' && !store.isCourseAdmin
-                      "
-                      type="text"
-                      style="color: white"
-                      @click="goProjectParams(record.id)"
-                    >
-                      参数配置
-                    </a-button>
-                    <!-- <a-button
+                  <a-button
+                    v-if="!store.isCourseAdmin"
+                    type="text"
+                    block
+                    @click="handleLogsOfProject(record.id)"
+                  >
+                    <template #icon>
+                      <svg-icon name="error-log"></svg-icon> </template
+                    >错误报告
+                  </a-button>
+                  <a-button
+                    v-if="
+                      record.status !== 'PROCESSING' &&
+                      store.isGreaterThanEqualRootOrgAdmin
+                    "
+                    type="text"
+                    block
+                    @click="goProjectDataSource(record.id)"
+                  >
+                    <template #icon>
+                      <svg-icon name="data-manage"></svg-icon> </template
+                    >数据管理
+                  </a-button>
+                  <a-button
+                    v-if="
+                      record.status !== 'PROCESSING' && !store.isCourseAdmin
+                    "
                     type="text"
-                    style="color: white"
-                    @click="goProjectPapers(record.id)"
+                    block
+                    @click="goProjectParams(record.id)"
                   >
-                    试卷列表
-                  </a-button> -->
-                    <a-button
-                      type="text"
-                      style="color: white"
-                      @click="goAllAnalysis(record.id)"
-                    >
-                      结果查询
-                    </a-button>
-                  </div>
+                    <template #icon>
+                      <svg-icon name="param-set"></svg-icon> </template
+                    >参数配置
+                  </a-button>
+                  <a-button type="text" block @click="goAllAnalysis(record.id)">
+                    <template #icon>
+                      <svg-icon name="result-list"></svg-icon> </template
+                    >结果查询
+                  </a-button>
                 </template>
-                <a-button type="text">更多</a-button>
+                <a-button class="btn-more" type="text">
+                  更多 <svg-icon name="arrow-bottom"></svg-icon>
+                </a-button>
               </a-popover>
             </div>
           </template>
@@ -162,6 +163,7 @@
       title="项目信息页"
       okText="确定"
       cancelText="取消"
+      :width="438"
       @ok="handleOk"
     >
       <a-form :labelCol="{ span: 4 }">
@@ -185,6 +187,7 @@
       title="重新计算"
       okText="确定"
       cancelText="取消"
+      :width="438"
       @ok="handleRestartProject"
     >
       <ul class="tw-ml-4 tw-list-disc">
@@ -307,7 +310,7 @@ const columns = [
   {
     title: "操作",
     dataIndex: "action",
-    width: 240,
+    width: 260,
     fixed: "right",
   },
 ];

+ 35 - 16
src/features/projectParamsManagement/ProjectParamsManagement.vue

@@ -1,19 +1,35 @@
 <template>
   <div>
-    <div class="tw-bg-white tw-p-5 tw-rounded-xl tw-mb-5">
-      <ProjectCourseSelect v-model:value="courseId" :projectId="projectId" />
-      <span class="tw-mr-4"></span>
-      <a-button class="query-btn" @click="clickSearch">查询</a-button>
-    </div>
+    <a-card title="参数配置">
+      <template #extra>
+        <a-button @click="goBack">返回</a-button>
+      </template>
 
-    <div class="tw-my-4 tw-flex tw-gap-2">
-      <a-button @click="importModalVisible = true">批量导入</a-button>
-      <a-button @click="handleExport">批量导出</a-button>
-      <a-button @click="goBack">返回</a-button>
-    </div>
+      <a-space class="filter-line" :size="12">
+        <ProjectCourseSelect v-model:value="courseId" :projectId="projectId" />
+        <a-button type="primary" @click="clickSearch">查询</a-button>
+      </a-space>
+    </a-card>
+
+    <div class="part-box">
+      <a-space class="part-action" :size="6">
+        <!-- TODO:上传下载 -->
+        <a-button type="text" @click="importModalVisible = true">
+          <template #icon>
+            <svg-icon name="course-score"></svg-icon>
+          </template>
+          科目分数线
+        </a-button>
+        <a-button type="text" @click="handleExport">
+          <template #icon>
+            <svg-icon name="course-college"></svg-icon>
+          </template>
+          科目学院属性
+        </a-button>
+      </a-space>
 
-    <div class="tw-bg-white tw-p-5 tw-rounded-xl">
       <a-table
+        class="page-table"
         rowKey="id"
         :columns="columns"
         :data-source="data"
@@ -35,9 +51,9 @@
             <a>{{ `${record.courseName}(${record.courseCode})` }}</a>
           </template>
           <template v-if="column.dataIndex === 'action'">
-            <span>
-              <a-button @click="showModal(record)">编辑</a-button>
-            </span>
+            <div class="action-cell">
+              <a-button type="text" @click="showModal(record)">编辑</a-button>
+            </div>
           </template>
         </template>
       </a-table>
@@ -63,6 +79,9 @@
         <a-form-item label="科目类型">
           <CourseTypeSelect :value="projectObj.courseType" disabled />
         </a-form-item>
+        <a-form-item label="学院">
+          <!-- <a-input v-model:value="projectObj.college"></a-input> -->
+        </a-form-item>
 
         <a-form-item label="起始计算分">
           <a-input-number
@@ -131,7 +150,7 @@ import { watch, onMounted, ref, reactive, toRaw, h } from "vue";
 import { useRoute } from "vue-router";
 
 const store = useMainStore();
-store.currentLocation = "项目管理 / 项目列表 / 参数配置";
+store.currentLocation = "项目列表 / 参数配置";
 
 // let rootOrgId = $ref(undefined as unknown as number);
 let courseId = $ref(undefined as undefined | number);
@@ -200,7 +219,7 @@ const columns = [
   {
     title: "操作",
     dataIndex: "action",
-    width: 120,
+    width: 80,
   },
 ];
 

+ 4 - 1
src/features/report/ReportBatchDownload.vue

@@ -1,5 +1,8 @@
 <template>
-  <a-button @click="exportReport">导出报告</a-button>
+  <a-button type="text" @click="exportReport">
+    <template #icon> <svg-icon name="export-report"></svg-icon> </template>
+    导出报告
+  </a-button>
 
   <!-- card-view-frame -->
   <div v-if="reportPreviewUrl" class="report-preview-frame">

+ 1 - 1
src/features/userManagement/UserManagement.vue

@@ -320,7 +320,7 @@ const columns = [
     title: "操作",
     dataIndex: "action",
     fixed: "right",
-    width: store.isGreaterThanEqualRootOrgAdmin ? 240 : 180,
+    width: store.isGreaterThanEqualRootOrgAdmin ? 260 : 200,
   },
 ];
 

+ 6 - 0
src/filters/index.ts

@@ -22,6 +22,12 @@ const $filters = {
     if (typeof val !== "string") return "无";
     return PROJECT_STATUS.find((p) => p.code === val)?.name;
   },
+  numberToPercentFilter(val: number) {
+    return (Math.round(val * 10000) / 100).toFixed(2) + "%";
+  },
+  numberRoundFilter(val: number) {
+    return Math.round(val * 100) / 100 + "";
+  },
 };
 
 export type FilterType = typeof $filters;

+ 3 - 3
src/router/index.ts

@@ -21,7 +21,7 @@ import ProjectDataManagement from "@/features/projectDataManagement/ProjectDataM
 import ProjectParamsManagement from "@/features/projectParamsManagement/ProjectParamsManagement.vue";
 import ProjectPapersManagement from "@/features/projectPapersManagement/ProjectPapersManagement.vue";
 import PaperAnalysis from "@/features/paperAnalysis/PaperAnalysis.vue";
-import AllAnalysis2 from "@/features/allAnalysis/AllAnalysis2.vue";
+import AllAnalysis from "@/features/allAnalysis/AllAnalysis.vue";
 import ProjectCompareManagement from "@/features/projectCompareManagement/ProjectCompareManagement.vue";
 import ProjectCompareDetail2 from "@/features/projectCompareDetail/ProjectCompareDetail2.vue";
 import TaskManagement from "@/features/taskManagement/TaskManagement.vue";
@@ -108,8 +108,8 @@ const routes = [
       },
       {
         path: "allAnalysis/:projectId",
-        component: AllAnalysis2,
-        name: "AllAnalysis2",
+        component: AllAnalysis,
+        name: "AllAnalysis",
       },
       {
         path: "projectCompareManagement",

+ 15 - 1
src/styles/ant-custom.less

@@ -27,7 +27,7 @@
 }
 
 // ant-btn
-.ant-btn + .ant-btn {
+.ant-btn + .ant-btn:not(.ant-btn-block) {
   margin-left: 8px;
 }
 .ant-btn {
@@ -111,3 +111,17 @@
     }
   }
 }
+
+// ant-table
+.ant-table-wrapper {
+  .ant-table {
+    &.ant-table-bordered {
+      .ant-table-container,
+      table,
+      th,
+      td {
+        border-color: var(--color-border) !important;
+      }
+    }
+  }
+}

+ 32 - 5
src/styles/base.less

@@ -124,24 +124,40 @@
     }
   }
   .action-cell {
+    margin: -5px -10px;
     .ant-btn-text {
-      padding: 0;
-      height: 22px;
+      padding: 5px 10px;
       line-height: 22px;
       border: none;
-      background-color: transparent !important;
+      &:not(.btn-more) {
+        background-color: transparent !important;
+      }
 
       + .ant-btn-text {
-        margin-left: 14px;
+        margin: 0;
       }
 
       &:hover {
-        opacity: 0.8;
+        opacity: 0.5;
       }
     }
   }
 }
 
+// action-more
+.action-more {
+  .ant-btn-block {
+    display: block;
+    padding: 5px 8px;
+  }
+
+  .ant-popover-inner {
+    border-radius: 6px;
+    padding: 6px;
+    box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.12);
+  }
+}
+
 /* table */
 .table {
   width: 100%;
@@ -181,3 +197,14 @@
     }
   }
 }
+
+// common
+.mb-16 {
+  margin-bottom: 16px;
+}
+.h2-title {
+  font-weight: 500;
+  font-size: 16px;
+  color: var(--app-main-text-color);
+  line-height: 24px;
+}

+ 70 - 0
src/styles/pages.less

@@ -0,0 +1,70 @@
+// paper-analysis
+.paper-analysis {
+  .chart-box {
+    height: 500px;
+
+    background: #ffffff;
+    border-radius: var(--border-radius);
+    border: 1px solid var(--color-border);
+  }
+  .ant-table-thead {
+    .ant-table-cell {
+      background-color: #f7f7f7;
+    }
+  }
+
+  .score-part {
+    background-color: #f9f9f9;
+    border-radius: var(--border-radius);
+
+    + .score-part {
+      margin-top: 16px;
+    }
+
+    &-head {
+      padding: 12px 16px;
+      position: relative;
+
+      &::after {
+        content: "";
+        display: block;
+        position: absolute;
+        bottom: 0;
+        left: 16px;
+        right: 16px;
+        border-bottom: 1px solid var(--color-border);
+      }
+
+      > h2 {
+        height: 22px;
+        font-weight: 500;
+        font-size: 14px;
+        color: var(--app-main-text-color);
+        line-height: 22px;
+      }
+    }
+
+    &-body {
+      padding: 16px;
+    }
+  }
+}
+
+// diff-chart
+.diff-chart {
+  margin-bottom: 16px;
+  padding: 12px 16px 16px;
+
+  background: #f0f0f0;
+  border-radius: 6px;
+
+  &-title {
+    margin-bottom: 12px;
+  }
+  &-body {
+    height: 224px;
+    background: #ffffff;
+    border-radius: var(--border-radius);
+    border: 1px solid var(--color-border);
+  }
+}

+ 1 - 8
src/styles/reset.less

@@ -33,23 +33,16 @@ u {
 }
 
 input {
-  font-family: var(--font-family);
   background: rgba(245 245 245 1);
   border: none;
   outline: none;
 }
 
 input:-webkit-input-placeholder {
-  color: var(--color-text-gray-4);
   font-weight: bold;
   font-size: 12px;
 }
 
-button,
-textarea {
-  font-family: var(--font-family);
-}
-
 h1,
 h2,
 h3,
@@ -106,7 +99,7 @@ textarea:focus {
 }
 
 ::-webkit-scrollbar-thumb {
-  background: #7584ac;
+  background: #d9d9d9;
   border-radius: 6px;
 }
 

+ 1 - 0
src/theme.ts

@@ -3,5 +3,6 @@ export const theme = {
     colorPrimary: "#165dff",
     colorSuccess: "#00b42a",
     colorError: "#f53f3f",
+    borderRadius: 4,
   },
 };