Commit 77ebf04dafe61c3505d47fe0a739b13991559eb4

Authored by 梁保满
1 parent 6e63ea51

个人版

Showing 57 changed files with 11140 additions and 58 deletions
src/api/apis/apis.js
... ... @@ -34,6 +34,14 @@ export default {
34 34 data,
35 35 });
36 36 },
  37 + // 个人版首页数据
  38 + personalIndex(data) {
  39 + return service({
  40 + url: setUpUrls.personalIndex,
  41 + method: "POST",
  42 + data,
  43 + });
  44 + },
37 45 //班主任-查询管理的班级
38 46 cTClassList(data) {
39 47 return service({
... ...
src/api/axios.js
... ... @@ -63,7 +63,6 @@ service.interceptors.response.use(
63 63 return Promise.resolve(res);
64 64 },
65 65 (error) => {
66   - debugger
67 66 const { data, status } = error.response;
68 67 if (error.response == undefined) {
69 68 return Promise.reject(error);
... ...
src/api/urls/apis.js
... ... @@ -199,4 +199,8 @@ export default {
199 199 exportGradeContrast: "/api_html/tenant/exportGradeContrast",
200 200 // 同步教师账号
201 201 syncUser: "/api_html/tenant/syncUser",
  202 +
  203 +
  204 + // 个人版首页统计数据
  205 + personalIndex:"/api_html/personal/classList"
202 206 }
... ...
src/components/charts/lineChart.vue 0 → 100644
  1 +<template>
  2 + <div class="chart" :id="id"></div>
  3 +</template>
  4 +
  5 +<script>
  6 +export default {
  7 + name: "lineChart",
  8 + props: {
  9 + id: String,
  10 + params: Array,
  11 + xAxis: Array,
  12 + },
  13 + watch: {
  14 + params: {
  15 + handler: function (val) {
  16 + if (val.length) {
  17 + this.initData();
  18 + }
  19 + },
  20 + deep: true,
  21 + },
  22 + },
  23 + data() {
  24 + return {
  25 + chart: null,
  26 + };
  27 + },
  28 + created() {},
  29 + mounted() {
  30 + this.initData();
  31 + },
  32 + methods: {
  33 + setOption() {
  34 + const that = this;
  35 + const options = {
  36 + color: this.colors || ["#4472c4", "#ed7d32", "#a5a5a5"],
  37 + backgroundColor: "#fff",
  38 + tooltip: {
  39 + trigger: "item",
  40 + confine: true,
  41 + },
  42 + legend: {
  43 + show: true,
  44 + bottom: 0,
  45 + itemHeight: 2,
  46 + },
  47 + xAxis: {
  48 + type: "category",
  49 + data: this.xAxis,
  50 + axisLine: { show: true, lineStyle: { color: "#e2e2e2" } },
  51 + axisTick: {
  52 + show: false,
  53 + },
  54 + axisLabel: {
  55 + color: "#666",
  56 + },
  57 + },
  58 + yAxis: {
  59 + type: "value",
  60 + axisLine: { show: false },
  61 + splitLine: {
  62 + show: true,
  63 + },
  64 + axisLabel: {
  65 + color: "#666",
  66 + },
  67 + },
  68 + grid: {
  69 + top:28,
  70 + left: "10%",
  71 + right: "10%",
  72 + bottom: 30,
  73 + containLabel: true,
  74 + },
  75 + series: that.params.map((item) => {
  76 + return {
  77 + name: item.name,
  78 + type: "line",
  79 + symbol: "circle",
  80 + symbolSize: "8",
  81 + lineStyle: {
  82 + width: 3,
  83 + },
  84 + data: item.value,
  85 + };
  86 + }),
  87 + };
  88 + return options;
  89 + },
  90 + initData() {
  91 + if (!this.chart) {
  92 + const div = document.getElementById(this.id);
  93 + this.chart = this.$echarts.init(div);
  94 + }
  95 + const options = this.setOption();
  96 + this.chart?.clear();
  97 + this.chart.setOption(options, true);
  98 + this.chart.off("click");
  99 + this.chart.on("click", "series", (params) => {
  100 + // this.$emit("clickPieChart", params);
  101 + });
  102 + },
  103 + },
  104 +};
  105 +</script>
  106 +
  107 +<style lang="scss" scoped>
  108 +.chart {
  109 + height: 100%;
  110 +}
  111 +</style>
... ...
src/components/charts/radarChart.vue 0 → 100644
  1 +<template>
  2 + <div class="chart" :id="id"></div>
  3 +</template>
  4 +
  5 +<script>
  6 +export default {
  7 + name: "radarChart",
  8 + props: {
  9 + id: String,
  10 + params: Object,
  11 + },
  12 + watch: {
  13 + params: {
  14 + handler: function (val) {
  15 + if (!!val.indicator) {
  16 + this.initData();
  17 + }
  18 + },
  19 + deep: true,
  20 + },
  21 + },
  22 + data() {
  23 + return {
  24 + chart: null,
  25 + };
  26 + },
  27 + created() {},
  28 + mounted() {
  29 + this.initData();
  30 + },
  31 + methods: {
  32 + setOption() {
  33 + const option = {
  34 + color: this.colors || ["#4472c4", "#ed7d32", "#a5a5a5"],
  35 + backgroundColor: "#fff",
  36 + tooltip: {
  37 + trigger: "item",
  38 + confine: true,
  39 + },
  40 + legend: {
  41 + show:true,
  42 + bottom:0,
  43 + itemHeight:2
  44 + },
  45 + radar: {
  46 + indicator: [...this.params.indicator],
  47 + splitNumber: 5,
  48 + center:['50%', '48%'],
  49 + radius:"70%",
  50 + shape: "polygon",
  51 + nameGap:10,
  52 + axisLine:{
  53 + show:false
  54 + },
  55 + splitLine:{
  56 + lineStyle: {
  57 + width: 0.5,
  58 + color:'#e2e2e2'
  59 + },
  60 + },
  61 + splitArea:{
  62 + areaStyle:{
  63 + color:['rgba(250,250,250,0)','rgba(200,200,200,0)']
  64 + }
  65 + }
  66 + },
  67 + series: [
  68 + {
  69 + type: "radar",
  70 + lineStyle: {
  71 + width: 1,
  72 + },
  73 + symbolSize: 4,
  74 + areaStyle: {
  75 + color: "transparent",
  76 + },
  77 + data: this.params.num,
  78 + },
  79 + ],
  80 + };
  81 + return option;
  82 + },
  83 + initData() {
  84 + if (!this.chart) {
  85 + const div = document.getElementById(this.id);
  86 + this.chart = this.$echarts.init(div);
  87 + }
  88 + const options = this.setOption();
  89 + this.chart?.clear();
  90 + this.chart.setOption(options, true);
  91 + this.chart.off("click");
  92 + this.chart.on("click", "series", (params) => {
  93 + // this.$emit("clickPieChart", params);
  94 + });
  95 + },
  96 + },
  97 +};
  98 +</script>
  99 +
  100 +<style lang="scss" scoped>
  101 +.chart {
  102 + height: 100%;
  103 +}
  104 +</style>
... ...
src/main.js
... ... @@ -11,7 +11,7 @@ import permission from &quot;./directive/permission/button&quot;
11 11 import NProgress from "nprogress"
12 12 import "babel-polyfill"
13 13 import '@/components/globalComponents.js'
14   -
  14 +import '@/plugins/echarts.js'
15 15  
16 16 import "nprogress/nprogress.css"
17 17 import "font-awesome/css/font-awesome.css"
... ...
src/plugins/echarts.js 0 → 100644
  1 +import Vue from 'vue'
  2 +// 引入 echarts 核心模块,核心模块提供了 echarts 使用必须要的接口。
  3 +import * as echarts from 'echarts/core';
  4 +// 引入柱状图图表,图表后缀都为 Chart
  5 +import {
  6 + LineChart,
  7 + PieChart,
  8 + ScatterChart,
  9 + RadarChart
  10 +} from 'echarts/charts';
  11 +// 引入提示框,标题,直角坐标系组件,组件后缀都为 Component
  12 +import {
  13 + LabelLayout,
  14 + UniversalTransition
  15 +} from 'echarts/features'
  16 +import {
  17 + TitleComponent,
  18 + TooltipComponent,
  19 + GridComponent,
  20 + GraphicComponent,
  21 + SingleAxisComponent,
  22 + LegendComponent
  23 +} from 'echarts/components';
  24 +
  25 +// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
  26 +import {
  27 + CanvasRenderer
  28 +} from 'echarts/renderers';
  29 +
  30 +// 注册必须的组件
  31 +echarts.use(
  32 + [LineChart,RadarChart, ScatterChart, PieChart, SingleAxisComponent, TitleComponent, TooltipComponent, GridComponent, CanvasRenderer, GraphicComponent, UniversalTransition, LabelLayout,LegendComponent]
  33 +);
  34 +
  35 +Vue.prototype.$echarts = echarts
0 36 \ No newline at end of file
... ...
src/router/index.js
... ... @@ -8,27 +8,46 @@ import HomeMain from &quot;@/views/index/mainIndex&quot;
8 8  
9 9 // 不是必须加载的组件使用懒加载
10 10 const NotFound = () => import("@/views/page404")
11   -const ExaminationPaper = () => import("@/views/examinationPaper/index")
12   -const ExaminationPaperAdd = () => import("@/views/examinationPaper/add")
13   -const ExaminationPaperEdit = () => import("@/views/examinationPaper/edit")
14   -const ExaminationPaperRecycle = () => import("@/views/examinationPaper/recycle")
15   -const Ask = () => import("@/views/ask/index")
16   -const AskAnalysis = () => import("@/views/ask/analysis")
17   -const Test = () => import("@/views/test/index")
18   -const TestAnalysis = () => import("@/views/test/analysis")
19   -const DataSync = () => import("@/views/dataSync/index")
20   -const Portrait = () => import("@/views/portrait/index")
21   -const Card = () => import("@/views/card/index")
22   -const Analysis = () => import("@/views/analysis/index")
23   -const Device = () => import("@/views/device/index")
24   -const DeviceLog = () => import("@/views/device/log")
25   -const Down = () => import("@/views/down/index")
26   -const DownClient = () => import("@/views/down/client")
27   -const SetUpAccount = () => import("@/views/setUp/account")
28   -const SetUpConglomerate = () => import("@/views/setUp/conglomerate")
29   -const SetUpSchool = () => import("@/views/setUp/school")
30   -const SetUpStudent = () => import("@/views/setUp/student")
31   -const SetUpTeacher = () => import("@/views/setUp/teacher")
  11 +const ExaminationPaper = () => import("@/views/standard/examinationPaper/index")
  12 +const ExaminationPaperAdd = () => import("@/views/standard/examinationPaper/add")
  13 +const ExaminationPaperEdit = () => import("@/views/standard/examinationPaper/edit")
  14 +const ExaminationPaperRecycle = () => import("@/views/standard/examinationPaper/recycle")
  15 +const Ask = () => import("@/views/standard/ask/index")
  16 +const AskAnalysis = () => import("@/views/standard/ask/analysis")
  17 +const Test = () => import("@/views/standard/test/index")
  18 +const TestAnalysis = () => import("@/views/standard/test/analysis")
  19 +const DataSync = () => import("@/views/standard/dataSync/index")
  20 +const Card = () => import("@/views/standard/card/index")
  21 +const Analysis = () => import("@/views/standard/analysis/index")
  22 +const Device = () => import("@/views/standard/device/index")
  23 +const DeviceLog = () => import("@/views/standard/device/log")
  24 +const Down = () => import("@/views/standard/down/index")
  25 +const DownClient = () => import("@/views/standard/down/client")
  26 +const SetUpAccount = () => import("@/views/standard/setUp/account")
  27 +const SetUpConglomerate = () => import("@/views/standard/setUp/conglomerate")
  28 +const SetUpSchool = () => import("@/views/standard/setUp/school")
  29 +const SetUpStudent = () => import("@/views/standard/setUp/student")
  30 +const SetUpTeacher = () => import("@/views/standard/setUp/teacher")
  31 +
  32 +const PersonalExaminationPaper = () => import("@/views/personal/examinationPaper/index")
  33 +const PersonalExaminationPaperAdd = () => import("@/views/personal/examinationPaper/add")
  34 +const PersonalExaminationPaperEdit = () => import("@/views/personal/examinationPaper/edit")
  35 +const PersonalExaminationPaperRecycle = () => import("@/views/personal/examinationPaper/recycle")
  36 +const PersonalAsk = () => import("@/views/personal/ask/index")
  37 +const PersonalAskAnalysis = () => import("@/views/personal/ask/analysis")
  38 +const PersonalTest = () => import("@/views/personal/test/index")
  39 +const PersonalTestAnalysis = () => import("@/views/personal/test/analysis")
  40 +const PersonalDataSync = () => import("@/views/personal/dataSync/index")
  41 +const PersonalPortrait = () => import("@/views/personal/portrait/index")
  42 +const PersonalPortraitDetail = () => import("@/views/personal/portrait/detail")
  43 +const PersonalSetUpStudent = () => import("@/views/personal/setUp/student")
  44 +const PersonalDown = () => import("@/views/personal/down/index")
  45 +const PersonalUserInfo = () => import("@/views/personal/userInfo/index")
  46 +
  47 +const AdminDevice = () => import("@/views/admin/device/index")
  48 +const AdminDeviceLog = () => import("@/views/admin/device/log")
  49 +const AdminAccount = () => import("@/views/admin/account/index")
  50 +const AdminClientVersion = () => import("@/views/admin/clientVersion/index")
32 51  
33 52 /**
34 53 * 重写路由的push方法
... ... @@ -70,6 +89,22 @@ let defaultRouter = [
70 89 ]
71 90 },
72 91 {
  92 + path: "/userInfo",
  93 + iconCls: "fa fa-user", // 图标样式class
  94 + name: "个人信息",
  95 + component: Layout,
  96 + hidden: true,
  97 + children: [
  98 + {
  99 + path: "/userInfo",
  100 + iconCls: "fa fa-user", // 图标样式class
  101 + name: "个人信息",
  102 + component: PersonalUserInfo,
  103 + children: []
  104 + }
  105 + ]
  106 + },
  107 + {
73 108 path: "/404",
74 109 component: NotFound,
75 110 name: "404",
... ... @@ -176,23 +211,6 @@ let addrouters = [ //测试用,后续后端获取
176 211  
177 212 ]
178 213 },
179   - // {
180   - // path: "/portrait",
181   - // iconCls: "fa fa-users", // 图标样式class
182   - // name: "学生画像",
183   - // component: Layout,
184   - // alone: true,
185   - // children: [
186   - // {
187   - // path: "/portrait",
188   - // iconCls: "fa fa-users", // 图标样式class
189   - // name: "",
190   - // component: Portrait,
191   - // children: []
192   - // }
193   - // ]
194   - // },
195   -
196 214 {
197 215 path: "/setUpConglomerate",
198 216 iconCls: "fa fa-building", // 图标样式class
... ... @@ -355,7 +373,247 @@ let addrouters = [ //测试用,后续后端获取
355 373 },
356 374 ]
357 375  
  376 +const addroutersPersonal = [
  377 + {
  378 + path: "/examinationPaper",
  379 + iconCls: "fa fa-file-text", // 图标样式class
  380 + name: "备题组卷",
  381 + component: Layout,
  382 + alone: true,
  383 + children: [
  384 + {
  385 + path: "/examinationPaper",
  386 + iconCls: "fa fa-file-text", // 图标样式class
  387 + name: "examinationPaper",
  388 + component: PersonalExaminationPaper,
  389 + children: []
  390 + },
  391 + {
  392 + path: "/examinationPaperAdd",
  393 + iconCls: "", // 图标样式class
  394 + name: "examinationPaperAdd",
  395 + component: PersonalExaminationPaperAdd,
  396 + parent: "examinationPaper",
  397 + children: []
  398 + },
  399 + {
  400 + path: "/examinationPaperEdit",
  401 + iconCls: "", // 图标样式class
  402 + name: "修改答题卡",
  403 + component: PersonalExaminationPaperEdit,
  404 + parent: "examinationPaper",
  405 + children: []
  406 + },
  407 + {
  408 + path: "/examinationPaperRecycle",
  409 + iconCls: "", // 图标样式class
  410 + name: "已归档答题卡",
  411 + component: PersonalExaminationPaperRecycle,
  412 + parent: "examinationPaper",
  413 + children: []
  414 + },
  415 + ]
  416 + },
  417 + {
  418 + path: "/ask",
  419 + iconCls: "fa fa-bar-chart", // 图标样式class
  420 + name: "随堂问报表",
  421 + component: Layout,
  422 + alone: true,
  423 + children: [
  424 + {
  425 + path: "/ask",
  426 + iconCls: "fa fa-bar-chart", // 图标样式class
  427 + name: "ask",
  428 + name: "随堂问报表",
  429 + component: PersonalAsk,
  430 + meta: {
  431 + keepAlive: true,
  432 + },
  433 + children: []
  434 +
  435 + },
  436 + {
  437 + path: "/askAnalysis",
  438 + iconCls: "", // 图标样式class
  439 + name: "随堂问报表分析",
  440 + component: PersonalAskAnalysis,
  441 + parent: "ask",
  442 + children: []
  443 + }
  444 + ]
  445 + },
  446 + {
  447 + path: "/test",
  448 + iconCls: "fa fa-pie-chart", // 图标样式class
  449 + name: "即时测报表",
  450 + component: Layout,
  451 + alone: true,
  452 + children: [
  453 + {
  454 + path: "/test",
  455 + iconCls: "fa fa-pie-chart", // 图标样式class
  456 + name: "",
  457 + component: PersonalTest,
  458 + meta: {
  459 + keepAlive: true,
  460 + },
  461 + children: []
  462 + },
  463 + {
  464 + path: "/testAnalysis",
  465 + iconCls: "", // 图标样式class
  466 + name: "即时测报表分析",
  467 + component: PersonalTestAnalysis,
  468 + parent: "test",
  469 + children: []
  470 + }
  471 +
  472 + ]
  473 + },
  474 + {
  475 + path: "/portrait",
  476 + iconCls: "fa fa-users", // 图标样式class
  477 + name: "学生画像",
  478 + component: Layout,
  479 + alone: true,
  480 + children: [
  481 + {
  482 + path: "/portrait",
  483 + iconCls: "fa fa-users", // 图标样式class
  484 + name: "",
  485 + component: PersonalPortrait,
  486 + meta: {
  487 + keepAlive: true,
  488 + },
  489 + children: []
  490 + },
  491 + {
  492 + path: "/portraitDetail",
  493 + iconCls: "", // 图标样式class
  494 + name: "授课端软件",
  495 + component: PersonalPortraitDetail,
  496 + parent: "down",
  497 + children: []
  498 + }
  499 + ]
  500 + },
  501 + {
  502 + path: "/setUpStudent",
  503 + iconCls: "fa fa-mortar-board",
  504 + name: '班级名单',
  505 + component: Layout,
  506 + alone: true,
  507 + children: [
  508 + {
  509 + path: "/setUpStudent",
  510 + iconCls: "a fa-mortar-board",
  511 + name: '',
  512 + component: PersonalSetUpStudent,
  513 + children: []
  514 + },
  515 + ]
  516 + },
  517 + {
  518 + path: "/down",
  519 + iconCls: "fa fa-download", // 图标样式class
  520 + name: "软件下载",
  521 + component: Layout,
  522 + alone: true,
  523 + children: [
  524 + {
  525 + path: "/down",
  526 + iconCls: "fa fa-download", // 图标样式class
  527 + name: "发卡软件",
  528 + component: PersonalDown,
  529 + children: []
  530 + }
  531 + ]
  532 + },
  533 + {
  534 + path: "/dataSync",
  535 + iconCls: "fa fa-random", // 图标样式class
  536 + name: "数据同步",
  537 + component: Layout,
  538 + alone: true,
  539 + children: [
  540 + {
  541 + path: "/dataSync",
  542 + iconCls: "fa fa-random", // 图标样式class
  543 + name: "",
  544 + component: PersonalDataSync,
  545 + children: []
  546 + }
  547 + ]
  548 + },
  549 +
  550 +
  551 +
  552 +]
  553 +
  554 +const addRoutersAdmin = [
  555 + {
  556 + path: "/account",
  557 + iconCls: "fa fa-id-card-o", // 图标样式class
  558 + name: "账号管理",
  559 + component: Layout,
  560 + alone: true,
  561 + children: [
  562 + {
  563 + path: "/account",
  564 + iconCls: "fa fa-id-card-o",
  565 + name: '',
  566 + component: AdminAccount,
  567 + children: []
  568 + },
  569 + ]
  570 + },
  571 + {
  572 + path: "/device",
  573 + iconCls: "fa fa-dashboard", // 图标样式class
  574 + name: "设备状态",
  575 + component: Layout,
  576 + alone: true,
  577 + children: [
  578 + {
  579 + path: "/device",
  580 + iconCls: "fa fa-dashboard", // 图标样式class
  581 + name: "",
  582 + component: AdminDevice,
  583 + meta: {
  584 + keepAlive: true,
  585 + },
  586 + children: []
  587 + },
  588 + {
  589 + path: "/deviceLog",
  590 + iconCls: "fa fa-list-alt", // 图标样式class
  591 + name: "",
  592 + component: AdminDeviceLog,
  593 + parent: "device",
  594 + children: []
  595 + }
  596 + ]
  597 + },
  598 + {
  599 + path: "/clientVersion",
  600 + iconCls: "fa fa-cogs", // 图标样式class
  601 + name: "版本管理",
  602 + component: Layout,
  603 + alone: true,
  604 + children: [
  605 + {
  606 + path: "/clientVersion",
  607 + iconCls: "fa fa-id-card-o",
  608 + name: '',
  609 + component: AdminClientVersion,
  610 + children: []
  611 + },
  612 + ]
  613 + },
  614 +]
  615 +
358 616 export default new Router({
359 617 routes: defaultRouter
360 618 })
361   -export { defaultRouter, addrouters }
  619 +export { defaultRouter, addrouters,addroutersPersonal,addRoutersAdmin }
... ...
src/router/permission.js
... ... @@ -18,7 +18,25 @@ router.beforeEach((to, from, next) =&gt; {
18 18 return item.roleName == userInfo.showRoleName;
19 19 });
20 20 console.log([...authorityRouterObj[0]?.authorityRouter])
21   - store.commit("setRouters", [...authorityRouterObj[0]?.authorityRouter]);
  21 + // store.commit("setRouters", [...authorityRouterObj[0]?.authorityRouter]);
  22 +
  23 + //start 开发用,测试删除
  24 + store.commit("setRouters", [
  25 + "setUpStudent",
  26 + "examinationPaper",
  27 + "examinationPaperAdd",
  28 + "examinationPaperEdit",
  29 + "examinationPaperRecycle",
  30 + "ask",
  31 + "askAnalysis",
  32 + "test",
  33 + "testAnalysis",
  34 + "portrait",
  35 + "dataSync",
  36 + "down",
  37 + ]);
  38 + //end
  39 +
22 40 store.commit("setInfo", { ...userInfo });
23 41 store.getters.addRouters.forEach((res) => {
24 42 router.addRoute(res);
... ...
src/store/index.js
... ... @@ -8,7 +8,7 @@ import { Message } from &quot;element-ui&quot;;
8 8  
9 9 import request from "@/api/index";
10 10 import router from "@/router/index";
11   -import { addrouters } from "@/router/index";
  11 +import { addrouters,addRoutersAdmin ,addroutersPersonal} from "@/router/index";
12 12 Vue.use(Vuex);
13 13  
14 14 const store = new Vuex.Store({
... ... @@ -39,7 +39,15 @@ const store = new Vuex.Store({
39 39 localStorage.setItem("info", JSON.stringify(data));
40 40 },
41 41 setRouters: (state, routers) => {
42   - let aRouters = addrouters.filter((item) => {
  42 + let addrouterList = []
  43 + if(state.info.showRole == 'ROLE_PINGTAI'){
  44 + addrouterList = [...addroutersPersonal]
  45 + }else if(state.info.showRole == 'ROLE_ADMIN'){
  46 + addrouterList = [...addRoutersAdmin]
  47 + }else{
  48 + addrouterList = [...addrouters]
  49 + }
  50 + let aRouters = addrouterList.filter((item) => {
43 51 let path = item.children[0]?.path.replace("/", "");
44 52 return routers?.includes(path);
45 53 });
... ... @@ -72,11 +80,35 @@ const store = new Vuex.Store({
72 80 const userInfo = { ...response.data };
73 81 if (userInfo.permissions && userInfo.permissions.length) {
74 82 userInfo.showRoleName = response.data.permissions[0]?.roleName;
  83 + // userInfo.showRole = response.data.permissions[0]?.role;
  84 + // 开发用,测试删除
  85 + userInfo.showRole = "ROLE_PINGTAI";
75 86 commit("setToken", "isLogin");
76 87 commit("setInfo", { ...userInfo });
77   - commit("setRouters", [
78   - ...userInfo.permissions[0]?.authorityRouter,
  88 + // commit("setRouters", [
  89 + // ...userInfo.permissions[0]?.authorityRouter,
  90 + // ]);
  91 + //start 开发用,测试删除
  92 + commit("setRouters", [
  93 + "setUpStudent",
  94 + "examinationPaper",
  95 + "examinationPaperAdd",
  96 + "examinationPaperEdit",
  97 + "examinationPaperRecycle",
  98 + "ask",
  99 + "askAnalysis",
  100 + "test",
  101 + "testAnalysis",
  102 + "portrait",
  103 + "dataSync",
  104 + "down",
  105 +
  106 + "account",
  107 + "device",
  108 + "clientVersion"
79 109 ]);
  110 + //end
  111 +
80 112 state.addRouters.forEach((res) => {
81 113 router.addRoute(res);
82 114 });
... ... @@ -117,6 +149,7 @@ const store = new Vuex.Store({
117 149 const userInfo = { ...response.data };
118 150 if (userInfo.permissions && userInfo.permissions.length) {
119 151 userInfo.showRoleName = response.data.permissions[0]?.roleName;
  152 + userInfo.showRole = response.data.permissions[0]?.role;
120 153 commit("setToken", "isLogin");
121 154 commit("setCode", code);
122 155 commit("setInfo", { ...userInfo });
... ... @@ -158,6 +191,7 @@ const store = new Vuex.Store({
158 191 return item.role == role;
159 192 });
160 193 userInfo.showRoleName = authorityRouterObj[0]?.roleName;
  194 + userInfo.showRole = authorityRouterObj[0]?.role;
161 195 commit("setInfo", userInfo);
162 196 commit("setRouters", [...authorityRouterObj[0]?.authorityRouter]);
163 197 state.addRouters.forEach((res) => {
... ...
src/utils/global.js
1 1 import rules from "./rules"
2 2 import request from "@/api"
3   -import * as echarts from "echarts"
4 3  
5 4 export default {
6 5 install (Vue, options) {
7   - Vue.prototype.$echarts = echarts
8 6 Vue.prototype.$request = request
9 7 Vue.prototype.$rules = rules
10 8 }
... ...
src/utils/index.js
... ... @@ -744,3 +744,44 @@ export function formatGradeNameClass(data) {
744 744 });
745 745 return gradeNameArr;
746 746 }
  747 +
  748 +export function tablePrint(id) {
  749 + let divs = document.getElementById(id);
  750 + let awin = window.open("", "_blank");
  751 + awin.document.getElementsByTagName(
  752 + "head"
  753 + )[0].innerHTML = `<style> @media print {@page {size:A4 landscape;margin:10mm}
  754 + body{margin:5mm;font-size:8px;}}
  755 + .table-box,.el-table,.el-table__body-wrapper,.el-table--border{max-height:99999999px!important;height:auto;width:auto!important}
  756 + .el-table{width:100%}
  757 + .el-table,.el-table__body-wrapper{max-height:auto}
  758 + .el-table .el-table__cell{padding:12px 0}
  759 + .el-table thead tr:first-child th.el-table__cell{border-top: 1px solid #EBEEF5;}
  760 + .el-table thead tr:first-child th.el-table__cell:first-child{border-left: 1px solid #EBEEF5;}
  761 + .el-table tbody tr td.el-table__cell:first-child{border-left: 1px solid #EBEEF5;}
  762 + .el-table td.el-table__cell{border-bottom: 1px solid #EBEEF5;}
  763 + .el-table--border .el-table__cell{border-right: 1px solid #EBEEF5;}
  764 + .el-table--border th.el-table__cell, .el-table__fixed-right-patch{border-bottom: 1px solid #EBEEF5;}
  765 + .el-table .el-table__cell.gutter{border:none!important;}
  766 + .el-table__fixed{display:none!important}
  767 + .el-table .el-table__cell.is-center{text-align:center!important}
  768 + .el-table .el-table__cell.is-hidden>*{visibility: inherit;}
  769 + ul,li{margin:0;padding:0;list-style:none}
  770 + .hui-box{display: flex;text-align: center;}
  771 + .hui-box .s-txt{width: 60px;line-height: 144px;background: #e2e2e2;font-size: 16px;color: #fff;font-weight: 700;border:1px solid #e2e2e2;box-sizing:border-box}
  772 + .hui-ul{border-top: 1px solid #e2e2e2;}
  773 + .hui-li{display: flex;}
  774 + .hui-s{height: 48px;line-height: 48px;border-right: 1px solid #e2e2e2;border-bottom: 1px solid #e2e2e2;box-sizing: border-box;}
  775 + .hui-s.s1{width: 87.5px;}
  776 + .hui-s.s2{ width: 97.5px;}
  777 + .hui-s.s3{width: 108px;}
  778 + </style>`;
  779 + let aDom = divs.cloneNode(true);
  780 + let aTbody = aDom
  781 + .getElementsByClassName("el-table__body")[0]
  782 + .getElementsByTagName("tbody")[0];
  783 + aDom.getElementsByClassName("el-table__header")[0].append(aTbody);
  784 + awin.document.body.append(aDom);
  785 + awin.print();
  786 + awin.close()
  787 +}
747 788 \ No newline at end of file
... ...
src/views/admin/account/index.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <back-box>
  4 + <template slot="title">
  5 + <span>账号管理</span>
  6 + </template>
  7 + </back-box>
  8 + <div class="page-content">
  9 + 功能开发中。
  10 + </div>
  11 + </div>
  12 +</template>
  13 +
  14 +<script>
  15 +export default {
  16 +
  17 +}
  18 +</script>
  19 +
  20 +<style>
  21 +
  22 +</style>
0 23 \ No newline at end of file
... ...
src/views/admin/clientVersion/index.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <back-box>
  4 + <template slot="title">
  5 + <span>授课端版本管理</span>
  6 + </template>
  7 + </back-box>
  8 + <div class="page-content">
  9 + 功能开发中。
  10 + </div>
  11 + </div>
  12 +</template>
  13 +
  14 +<script>
  15 +export default {
  16 +
  17 +}
  18 +</script>
  19 +
  20 +<style>
  21 +
  22 +</style>
0 23 \ No newline at end of file
... ...
src/views/admin/device/index.vue 0 → 100644
  1 +<template>
  2 + <div ref="main" class="page-content">
  3 + <back-box>
  4 + <template slot="title">
  5 + <span>设备管理</span>
  6 + </template>
  7 + </back-box>
  8 + <div>
  9 + <div class="tab-box">
  10 + <el-radio-group v-model="type">
  11 + <el-radio-button :label="1">基站管理</el-radio-button>
  12 + <el-radio-button :label="2">答题器管理</el-radio-button>
  13 + <el-radio-button :label="3">授课端管理</el-radio-button>
  14 + </el-radio-group>
  15 + </div>
  16 + <div class="content">
  17 + <div v-show="type == 1">
  18 + <div class="chart-box" v-if="count">
  19 + <div class="device-num">
  20 + <p class="p1">{{ count }}</p>
  21 + <p class="p2">基站数量</p>
  22 + </div>
  23 + <div class="chart">
  24 + <pie-chart
  25 + id="pieChart"
  26 + :params="chartData"
  27 + @clickPieChart="clickPieChart"
  28 + ></pie-chart>
  29 + </div>
  30 + </div>
  31 + <div class="table-box">
  32 + <div class="answer-header">
  33 + <div class="sel-box">
  34 + <el-cascader
  35 + size="small"
  36 + class="sel sel2"
  37 + clearable
  38 + placeholder="选择班级"
  39 + v-model="query.classId"
  40 + :options="gradeList"
  41 + :props="props"
  42 + collapse-tags
  43 + :show-all-levels="false"
  44 + ></el-cascader>
  45 + <el-select
  46 + class="sel"
  47 + v-model="query.onlineStatus"
  48 + placeholder="选择状态"
  49 + @change="_QueryData(true)"
  50 + >
  51 + <el-option
  52 + v-for="item in statusList"
  53 + :key="item.value"
  54 + :label="item.label"
  55 + :value="item.value"
  56 + >
  57 + </el-option>
  58 + </el-select>
  59 + <el-input
  60 + placeholder="请输入设备编码"
  61 + v-model="query.sn"
  62 + class="input-with-select"
  63 + @keyup.enter.native="_QueryData(true)"
  64 + >
  65 + <el-button
  66 + slot="append"
  67 + icon="el-icon-search"
  68 + @click="_QueryData(true)"
  69 + ></el-button>
  70 + </el-input>
  71 + <el-button class="serach-box" round @click="_QueryData(true)"
  72 + >筛选</el-button
  73 + >
  74 + </div>
  75 + </div>
  76 + <el-table :data="tableData" border style="width: 100%">
  77 + <el-table-column
  78 + prop="sn"
  79 + label="设备编码"
  80 + align="center"
  81 + ></el-table-column>
  82 + <el-table-column
  83 + prop="frequency"
  84 + label="频点"
  85 + align="center"
  86 + ></el-table-column>
  87 + <el-table-column
  88 + prop="pairingCode"
  89 + label="配对码"
  90 + align="center"
  91 + ></el-table-column>
  92 + <el-table-column
  93 + prop="roomName"
  94 + label="所在教室"
  95 + align="center"
  96 + ></el-table-column>
  97 + <el-table-column label="关联班级" align="center">
  98 + <template slot-scope="scoped">
  99 + <p v-for="(item, index) in scoped.row.classList" :key="index">
  100 + {{ item.className }}
  101 + </p>
  102 + </template>
  103 + </el-table-column>
  104 + <el-table-column
  105 + prop="otaVersionName"
  106 + label="固件版本号"
  107 + align="center"
  108 + ></el-table-column>
  109 + <el-table-column
  110 + prop="onlineTime"
  111 + label="最近上报"
  112 + align="center"
  113 + ></el-table-column>
  114 + <el-table-column label="状态" align="center"
  115 + ><template slot-scope="scope">
  116 + {{
  117 + scope.row.onlineStatus == 1
  118 + ? "在线"
  119 + : scope.row.onlineStatus == 2
  120 + ? "异常"
  121 + : "离线"
  122 + }}
  123 + </template></el-table-column
  124 + >
  125 + <el-table-column label="操作" align="center"
  126 + ><template slot-scope="scoped">
  127 + <el-tooltip effect="dark" content="日志" placement="top">
  128 + <el-button
  129 + type="warning"
  130 + circle
  131 + size="mini"
  132 + icon="fa fa-eye"
  133 + @click="linkTo(scoped.row, 1)"
  134 + ></el-button>
  135 + </el-tooltip> </template
  136 + ></el-table-column>
  137 + </el-table>
  138 + </div>
  139 + </div>
  140 + <div v-show="type == 2">
  141 + <div class="chart-box" v-if="count">
  142 + <div class="device-num">
  143 + <p class="p1">{{ count }}</p>
  144 + <p class="p2">答题器数量</p>
  145 + </div>
  146 + <div class="chart">
  147 + <scatter-chart
  148 + id="scatterChart"
  149 + :params="chartData2"
  150 + @clickScatterChart="clickScatterChart"
  151 + ></scatter-chart>
  152 + </div>
  153 + </div>
  154 + <div class="table-box">
  155 + <div class="answer-header">
  156 + <div class="sel-box">
  157 + <el-cascader
  158 + size="small"
  159 + class="sel sel2"
  160 + clearable
  161 + placeholder="选择班级"
  162 + v-model="query.classId"
  163 + :options="gradeList"
  164 + :props="props"
  165 + collapse-tags
  166 + :show-all-levels="false"
  167 + ></el-cascader>
  168 + <el-select
  169 + class="sel"
  170 + v-model="query.type"
  171 + placeholder="选择状态"
  172 + @change="_QueryData(true)"
  173 + >
  174 + <el-option
  175 + v-for="item in typeList"
  176 + :key="item.value"
  177 + :label="item.label"
  178 + :value="item.value"
  179 + >
  180 + </el-option>
  181 + </el-select>
  182 + <el-input
  183 + placeholder="请输入设备编码"
  184 + v-model="query.sn"
  185 + class="input-with-select"
  186 + @keyup.enter.native="_QueryData(true)"
  187 + >
  188 + <el-button
  189 + slot="append"
  190 + icon="el-icon-search"
  191 + @click="_QueryData(true)"
  192 + ></el-button>
  193 + </el-input>
  194 + <el-button class="serach-box" round @click="_QueryData(true)"
  195 + >筛选</el-button
  196 + >
  197 + </div>
  198 + </div>
  199 + <el-table :data="tableData" border style="width: 100%">
  200 + <el-table-column
  201 + prop="sn"
  202 + label="设备编码"
  203 + align="center"
  204 + ></el-table-column>
  205 + <el-table-column label="学生信息" align="center"
  206 + ><template slot-scope="scoped"
  207 + ><p
  208 + v-for="(item, index) in scoped.row.studentList"
  209 + :key="index"
  210 + >
  211 + {{ item.studentName }}
  212 + </p></template
  213 + ></el-table-column
  214 + >
  215 + <el-table-column
  216 + prop="electricity"
  217 + label="电量"
  218 + align="center"
  219 + ></el-table-column>
  220 + <el-table-column prop="class" label="关联班级" align="center">
  221 + <template slot-scope="scoped">
  222 + <p v-for="(item, index) in scoped.row.classList" :key="index">
  223 + {{ item.className }}
  224 + </p>
  225 + </template></el-table-column
  226 + >
  227 + <el-table-column
  228 + prop="pairingCode"
  229 + label="配对码"
  230 + align="center"
  231 + ></el-table-column>
  232 + <el-table-column
  233 + prop="answerTimes"
  234 + label="答题次数"
  235 + align="center"
  236 + ></el-table-column>
  237 + <el-table-column
  238 + prop="latestReportTime"
  239 + label="最后答题时间"
  240 + align="center"
  241 + ></el-table-column>
  242 + <el-table-column label="操作" align="center"
  243 + ><template slot-scope="scoped">
  244 + <el-tooltip effect="dark" content="日志" placement="top">
  245 + <el-button
  246 + type="warning"
  247 + circle
  248 + size="mini"
  249 + icon="fa fa-eye"
  250 + @click="linkTo(scoped.row, 2)"
  251 + ></el-button>
  252 + </el-tooltip> </template
  253 + ></el-table-column>
  254 + </el-table>
  255 + </div>
  256 + </div>
  257 + <div class="pagination-box">
  258 + <el-pagination
  259 + small=""
  260 + layout="total,prev, pager, next"
  261 + :hide-on-single-page="true"
  262 + :total="total"
  263 + @current-change="changePage"
  264 + :current-page="page"
  265 + :page-size="size"
  266 + >
  267 + </el-pagination>
  268 + </div>
  269 + </div>
  270 + </div>
  271 +
  272 +
  273 + </div>
  274 +</template>
  275 +
  276 +<script>
  277 +import pieChart from "@/components/charts/pieChart";
  278 +import scatterChart from "@/components/charts/scatterChart";
  279 +import _ from "lodash";
  280 +import { downloadFile, getBlob, formatGradeNameClass } from "@/utils";
  281 +import api from "@/api/apis/apis";
  282 +import BusEvent from "@/utils/busEvent";
  283 +export default {
  284 + components: { pieChart, scatterChart },
  285 + watch: {
  286 + type: function (val) {
  287 + this.page = 1;
  288 + this.total = 0;
  289 + this.count = 0;
  290 + this.query.classId = [];
  291 + this.query.onlineStatus = "";
  292 + this.query.sn = "";
  293 + this.query.type = "";
  294 + if (val == 1) {
  295 + this.stationReport();
  296 + } else if (val == 2) {
  297 + this.keyboardReport();
  298 + }
  299 + this._QueryData();
  300 + },
  301 + },
  302 + data() {
  303 + return {
  304 + code: "",
  305 + loading: false,
  306 + gradeList: [],
  307 + gradeListAll: [],
  308 + schoolAll: [],
  309 + school: {}, //校园账号所属学校信息
  310 + props: {
  311 + multiple: true,
  312 + checkStrictly: true,
  313 + },
  314 + type: 1,
  315 + query: {
  316 + classId: [],
  317 + onlineStatus: "",
  318 + sn: "",
  319 + type: "",
  320 + },
  321 + statusList: [
  322 + { label: "全部", value: "" },
  323 + { label: "离线", value: 0 },
  324 + { label: "在线", value: 1 },
  325 + { label: "异常", value: 2 },
  326 + ],
  327 + typeList: [
  328 + { label: "全部", value: 0 },
  329 + { label: "1日内", value: 1 },
  330 + { label: "3日内", value: 2 },
  331 + { label: "7日内", value: 3 },
  332 + { label: "1月内", value: 4 },
  333 + { label: "3月内", value: 5 },
  334 + { label: "3月以上", value: 6 },
  335 + ],
  336 + tableData: [],
  337 + total: 0,
  338 + count: 0,
  339 + chartData: [],
  340 + chartData2: [],
  341 + page: 1,
  342 + size: 20,
  343 + };
  344 + },
  345 + created() {
  346 + this.code = localStorage.getItem("csCode") || "";
  347 +
  348 + (this.props.lazy = true),
  349 + (this.props.lazyLoad = function (node, resolve) {
  350 + const { level } = node;
  351 + if (level == 2) {
  352 + console.log(node);
  353 + api
  354 + .tenantClassList({
  355 + schoolId: node.data.value,
  356 + })
  357 + .then((res) => {
  358 + let children = formatGradeNameClass(res.data?.list).sort(
  359 + (a, b) => {
  360 + return a.grade - b.grade;
  361 + }
  362 + );
  363 + console.log();
  364 +
  365 + const nodes = [...children];
  366 + // 通过调用resolve将子节点数据返回,通知组件数据加载完成
  367 + resolve(nodes);
  368 + });
  369 + } else {
  370 + resolve(node);
  371 + }
  372 + });
  373 +
  374 + this.stationReport();
  375 + this._QueryGradeList();
  376 + this._QueryData();
  377 + this.showSchool();
  378 + },
  379 + activated() {
  380 + const that = this;
  381 + BusEvent.$on("keepAlive", async function () {
  382 + that.type = 1;
  383 + that.page = 1;
  384 + that.total = 0;
  385 + that.count = 0;
  386 + that.query.classId = [];
  387 + that.query.onlineStatus = "";
  388 + that.query.sn = "";
  389 + that.query.type = "";
  390 + that.stationReport();
  391 + that._QueryData();
  392 + });
  393 + },
  394 + methods: {
  395 +
  396 + linkTo(obj, type) {
  397 + this.$router.push({
  398 + path: "/deviceLog",
  399 + query: {
  400 + id: obj.id,
  401 + type: type,
  402 + },
  403 + });
  404 + },
  405 + clickPieChart(obj) {
  406 + this.query.onlineStatus =
  407 + obj.name == "在线" ? 1 : obj.name == "离线" ? 0 : 2;
  408 + this.query.sn = "";
  409 + this.query.classId = [];
  410 + this.page = 1;
  411 + this._QueryData(false);
  412 + },
  413 + clickScatterChart(obj) {
  414 + this.query.type =
  415 + obj.name == "1日内"
  416 + ? 1
  417 + : obj.name == "3日内"
  418 + ? 2
  419 + : obj.name == "7日内"
  420 + ? 3
  421 + : obj.name == "1月内"
  422 + ? 4
  423 + : obj.name == "3月内"
  424 + ? 5
  425 + : 6;
  426 + this.query.sn = "";
  427 + this.query.classId = [];
  428 + this.page = 1;
  429 + this._QueryData(false);
  430 + },
  431 + changePage(page) {
  432 + this.page = page;
  433 + this._QueryData(false);
  434 + },
  435 +
  436 + async showSchool() {
  437 + const { data, status, info } = await this.$request.schoolList();
  438 + if (status === 0) {
  439 + let gradeListAll = data.list?.map((item) => {
  440 + return {
  441 + value: item.id,
  442 + label: item.schoolName,
  443 + };
  444 + });
  445 + this.schoolAll = [
  446 + {
  447 + value: 0,
  448 + label: "全部",
  449 + children: [...gradeListAll],
  450 + },
  451 + ];
  452 + this.gradeList = [...this.schoolAll, ...this.gradeList];
  453 + } else {
  454 + this.$message.error(info);
  455 + }
  456 + },
  457 +
  458 +
  459 + // 查找班级
  460 + async _QueryGradeList() {
  461 + this.loading = true;
  462 + const { data, status, info } = await this.$request.gradeList();
  463 + if (status === 0) {
  464 + if (!!data.list) {
  465 + this.gradeList =
  466 + data.list?.map((item) => {
  467 + let gradeList = {
  468 + value: item.id,
  469 + label: item.regionName,
  470 + };
  471 + gradeList.children =
  472 + item.schoolList?.map((items) => {
  473 + return {
  474 + value: items.id,
  475 + label: items.schoolName,
  476 + };
  477 + }) || [];
  478 + return gradeList;
  479 + }) || [];
  480 + this.gradeList = [...this.schoolAll, ...this.gradeList];
  481 + }
  482 + } else {
  483 + this.$message.error(info);
  484 + }
  485 + },
  486 + // 基站统计数据
  487 + async stationReport() {
  488 + const { data, status, info } = await this.$request.stationReport();
  489 + if (status === 0) {
  490 + this.chartData =
  491 + data.list?.map((item) => {
  492 + return {
  493 + name:
  494 + item.onlineStatus == "1"
  495 + ? "在线"
  496 + : item.onlineStatus == 0
  497 + ? "离线"
  498 + : "异常",
  499 + value: item.total,
  500 + rate: item.rate,
  501 + };
  502 + }) || [];
  503 + this.count = data.total || 0;
  504 + } else {
  505 + this.$message.error(info);
  506 + }
  507 + },
  508 + // 答题器统计数据
  509 + async keyboardReport() {
  510 + const { data, status, info } = await this.$request.keyboardReport();
  511 + if (status === 0) {
  512 + this.chartData2 =
  513 + data.list?.map((item) => {
  514 + return {
  515 + name:
  516 + item.type == 1
  517 + ? "1日内"
  518 + : item.type == 2
  519 + ? "3日内"
  520 + : item.type == 3
  521 + ? "7日内"
  522 + : item.type == 4
  523 + ? "1月内"
  524 + : item.type == 5
  525 + ? "3月内"
  526 + : "3月以上",
  527 + count: item.total,
  528 + value: item.rate * 100,
  529 + };
  530 + }) || [];
  531 + this.count = data.total || 0;
  532 + } else {
  533 + this.$message.error(info);
  534 + }
  535 + },
  536 + setQuery() {
  537 + //整理传参
  538 + let query = {};
  539 + if (this.query.sn) {
  540 + query.sn = this.query.sn;
  541 + } else {
  542 + if (this.type == 1) {
  543 + this.query.onlineStatus !== ""
  544 + ? (query.onlineStatus = this.query.onlineStatus)
  545 + : "";
  546 + } else if (this.type == 2) {
  547 + this.query.type !== "" ? (query.type = this.query.type) : "";
  548 + }
  549 + query.regionIds = [];
  550 + query.schoolIds = [];
  551 + query.gradeNames = [];
  552 + query.classIds = [];
  553 + this.query.classId?.map((item) => {
  554 + if (item.length == 1) {
  555 + if (!query.regionIds.includes(item[0])) {
  556 + query.regionIds.push(item[0]);
  557 + }
  558 + } else if (item.length == 2) {
  559 + if (!query.schoolIds.includes(item[1])) {
  560 + query.schoolIds.push(item[1]);
  561 + }
  562 + query.regionIds.includes(item[0])
  563 + ? query.regionIds.remove(item[0])
  564 + : "";
  565 + } else if (item.length == 3) {
  566 + if (!query.schoolIds.includes(item[1])) {
  567 + query.schoolIds.push(item[1]);
  568 + }
  569 + if (!query.gradeNames.includes(item[2])) {
  570 + query.gradeNames.push(item[2]);
  571 + }
  572 + query.regionIds.includes(item[0])
  573 + ? query.regionIds.remove(item[0])
  574 + : "";
  575 + } else if (item.length == 4) {
  576 + if (!query.classIds.includes(item[3])) {
  577 + query.classIds.push(item[3]);
  578 + }
  579 + query.regionIds.includes(item[0])
  580 + ? query.regionIds.remove(item[0])
  581 + : "";
  582 + query.schoolIds.includes(item[1])
  583 + ? query.schoolIds.remove(item[1])
  584 + : "";
  585 + query.gradeNames.includes(item[2])
  586 + ? query.gradeNames.remove(item[2])
  587 + : "";
  588 + }
  589 + });
  590 +
  591 + delete query.classId;
  592 + }
  593 + return query;
  594 + },
  595 + // 设备列表信息
  596 + async _QueryData(isRef) {
  597 + this.loading = true;
  598 + let query = this.setQuery();
  599 + if (isRef) {
  600 + this.page = 1;
  601 + }
  602 + this.loading = true;
  603 + this.tableData = [];
  604 + const { data, status, info } = await this.$request.deviceList({
  605 + ...query,
  606 + deviceType: this.type,
  607 + page: this.page,
  608 + size: this.size,
  609 + });
  610 + this.loading = false;
  611 + if (status == 0) {
  612 + this.tableData =
  613 + (data?.list &&
  614 + data?.list.map((item) => {
  615 + item.upgradeFlag = item.upgradeFlag == 1 ? true : false;
  616 + return item;
  617 + })) ||
  618 + [];
  619 +
  620 + this.total = data.count;
  621 + this.$nextTick(function () {
  622 + this.$refs.main.scrollTop = 0;
  623 + if (this.query.classId.length) {
  624 + this.selectionTabIds = this.tableData.map((item) => {
  625 + this.$refs.multipleTable.toggleRowSelection(item);
  626 + return item.id;
  627 + });
  628 + }
  629 + });
  630 + } else {
  631 + this.$message.error(info);
  632 + }
  633 + },
  634 + },
  635 +};
  636 +</script>
  637 +
  638 +<style lang="scss" scoped>
  639 +.page-content {
  640 + padding: 0 20px;
  641 + height: 100%;
  642 + overflow-y: auto;
  643 +}
  644 +.tab-box {
  645 + padding: 20px 0 12px;
  646 +}
  647 +.content {
  648 + background: #f8f8f8;
  649 + border: 1px solid #e2e2e2;
  650 + border-radius: 10px;
  651 + overflow: hidden;
  652 + :deep(.fa-edit) {
  653 + width: 12px;
  654 + height: 12px;
  655 + &::before {
  656 + margin-left: 2px;
  657 + }
  658 + }
  659 + :deep(.fa-eye) {
  660 + width: 12px;
  661 + height: 12px;
  662 + &::before {
  663 + margin-left: 1px;
  664 + }
  665 + }
  666 + .chart-box {
  667 + display: flex;
  668 + overflow: hidden;
  669 + height: 240px;
  670 + border-bottom: 0.5px solid #e2e2e2;
  671 + .device-num {
  672 + width: 280px;
  673 + border-right: 0.5px solid #e2e2e2;
  674 + display: flex;
  675 + flex-direction: column;
  676 + justify-content: center;
  677 + align-items: center;
  678 + .p1 {
  679 + font-size: 28px;
  680 + }
  681 + }
  682 + .chart {
  683 + flex: 1;
  684 + height: 100%;
  685 + }
  686 + }
  687 + .table-box {
  688 + padding: 20px;
  689 + .answer-header {
  690 + padding: 0;
  691 + margin-bottom: 12px;
  692 + }
  693 + }
  694 +}
  695 +</style>
0 696 \ No newline at end of file
... ...
src/views/device/log.vue renamed to src/views/admin/device/log.vue
src/views/index/mainIndex.vue
... ... @@ -158,6 +158,93 @@
158 158 </li>
159 159 </template>
160 160 </ul>
  161 + <ul class="nav-list" v-if="type == 'ROLE_PINGTAI'">
  162 + <li class="nav-item item1" @click="links('/examinationPaper')">
  163 + <img class="icon" src="../../assets/nav/setUpAccount.png" alt="" />
  164 + <div class="text">
  165 + <p class="p1">备题组卷</p>
  166 + <p class="p2">
  167 + 管理
  168 + <template v-if="dataInfo.paperCount">
  169 + {{ dataInfo.paperCount }}套答题卡,
  170 + </template>
  171 + </p>
  172 + </div>
  173 + </li>
  174 + <li class="nav-item item1" @click="links('/setUpStudent')">
  175 + <img class="icon" src="../../assets/nav/setUpSchool.png" alt="" />
  176 + <div class="text">
  177 + <p class="p1">班级名单</p>
  178 + <p class="p2">
  179 + 管理
  180 + <template v-if="dataInfo.classCount">
  181 + {{ dataInfo.classCount }}个班级的学生名单,
  182 + </template>
  183 + </p>
  184 + </div>
  185 + </li>
  186 + <li class="nav-item item2" @click="links('/portrait')">
  187 + <img class="icon" src="../../assets/nav/device.png" alt="" />
  188 + <div class="text">
  189 + <p class="p1">学生画像</p>
  190 + <p class="p2">
  191 + 共分析
  192 + <template v-if="dataInfo.stationCount"
  193 + >{{ dataInfo.stationCount }}名学生成绩</template
  194 + >
  195 + </p>
  196 + </div>
  197 + </li>
  198 + <li class="item3">
  199 + <div class="nav-item item1 item-child2" @click="links('/ask')">
  200 + <img class="icon" src="../../assets/nav/card.png" alt="" />
  201 + <p class="p1">随堂问报表</p>
  202 + <p class="p2">
  203 + 对
  204 + <template v-if="dataInfo.classPeriodCount"
  205 + >{{ dataInfo.classPeriodCount }}套随堂问答题记录分析</template
  206 + >
  207 + </p>
  208 + </div>
  209 + <div class="nav-item item1 item-child2" @click="links('/test')">
  210 + <img class="icon" src="../../assets/nav/analysis.png" alt="" />
  211 + <p class="p1">即时测报表</p>
  212 + <p class="p2">
  213 + 对<template v-if="dataInfo.examCount"
  214 + >{{ dataInfo.examCount }}套即时测答题记录分析</template
  215 + >
  216 + </p>
  217 + </div>
  218 + <div class="nav-item item1 item-child1" @click="links('/down')">
  219 + <img class="icon" src="../../assets/nav/down.png" alt="" />
  220 + <div class="text">
  221 + <p class="p1">软件下载</p>
  222 + <p class="p2">授课端软件最新版本1.2.0。</p>
  223 + </div>
  224 + </div>
  225 + </li>
  226 +
  227 + </ul>
  228 + <ul class="nav-list" v-if="type == 'ROLE_ADMIN'">
  229 + <li class="nav-item item1" @click="links('/account')">
  230 + <img class="icon" src="../../assets/nav/setUpAccount.png" alt="" />
  231 + <div class="text">
  232 + <p class="p1">账号管理</p>
  233 + </div>
  234 + </li>
  235 + <li class="nav-item item1" @click="links('/device')">
  236 + <img class="icon" src="../../assets/nav/device.png" alt="" />
  237 + <div class="text">
  238 + <p class="p1">设备管理</p>
  239 + </div>
  240 + </li>
  241 + <li class="nav-item item2" @click="links('/clientVersion')">
  242 + <img class="icon" src="../../assets/nav/down.png" alt="" />
  243 + <div class="text">
  244 + <p class="p1">授课端版本管理</p>
  245 + </div>
  246 + </li>
  247 + </ul>
161 248 </div>
162 249 </template>
163 250  
... ... @@ -198,6 +285,7 @@ export default {
198 285 }
199 286 });
200 287 this.type = type ? type : this.$store.getters.info.permissions[0].role;
  288 + this.type = 'ROLE_PINGTAI'
201 289 this.navList = this.$store.getters.addRouters.map((item) => {
202 290 return {
203 291 name: item.name,
... ... @@ -214,6 +302,8 @@ export default {
214 302 this.tenantIndex();
215 303 } else if (this.type == "ROLE_BANZHUREN") {
216 304 this.classIndex();
  305 + } else if (this.type == "ROLE_PINGTAI") {
  306 + this.personalIndex();
217 307 }
218 308 },
219 309 async teacherIndex() {
... ... @@ -248,6 +338,14 @@ export default {
248 338 this.$message.error(info);
249 339 }
250 340 },
  341 + async personalIndex() {
  342 + const { data, status, info } = await this.$request.personalIndex();
  343 + if (status === 0) {
  344 + this.dataInfo = { ...data };
  345 + } else {
  346 + this.$message.error(info);
  347 + }
  348 + },
251 349 },
252 350 };
253 351 </script>
... ... @@ -324,7 +422,6 @@ export default {
324 422 }
325 423 &.item-child2 {
326 424 height: calc(50% - 8px);
327   - margin-right: 20px;
328 425 flex-wrap: wrap;
329 426 padding: 12px 0 12px 30px;
330 427 .icon {
... ...
src/views/layout/header/header.vue
... ... @@ -9,7 +9,7 @@
9 9 <!-- <li>
10 10 <langSelect></langSelect>
11 11 </li> -->
12   - <li class="dropdown-box">
  12 + <li class="dropdown-box" v-if="role != 'ROLE_PINGTAI'">
13 13 <div class="userInfo">
14 14 <img :src="avatar" />
15 15 <div class="txt">
... ... @@ -34,6 +34,15 @@
34 34 </el-dropdown-menu>
35 35 </el-dropdown>
36 36 </li>
  37 + <li class="dropdown-box" v-else @click="linkToUserInfo">
  38 + <div class="userInfo">
  39 + <img :src="avatar" />
  40 + <div class="txt">
  41 + <p>{{ `${this.$store.getters.info.name}` }}</p>
  42 + <p>个人版</p>
  43 + </div>
  44 + </div>
  45 + </li>
37 46 <li class="fullScreen" @click="fullScreen">
38 47 <el-tooltip
39 48 class="item"
... ...
src/views/personal/ask/analysis.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <back-box>
  4 + <template slot="title">
  5 + <span>单课分析</span>
  6 + </template>
  7 + </back-box>
  8 + <div class="page-content">
  9 + <div class="tab-box">
  10 + <span
  11 + class="tab-item"
  12 + :class="type == 1 ? 'active' : ''"
  13 + @click="setType(1)"
  14 + >答题表现</span
  15 + >
  16 + <span
  17 + class="tab-item"
  18 + :class="type == 2 ? 'active' : ''"
  19 + @click="setType(2)"
  20 + >学生问答表现</span
  21 + >
  22 + <span
  23 + class="tab-item"
  24 + :class="type == 3 ? 'active' : ''"
  25 + @click="setType(3)"
  26 + >学生互动表现</span
  27 + >
  28 + <span
  29 + class="tab-item"
  30 + :class="type == 4 ? 'active' : ''"
  31 + @click="setType(4)"
  32 + >签到明细</span
  33 + >
  34 + </div>
  35 + <div v-loading="loading">
  36 + <ul class="info" v-if="type == 1">
  37 + <li class="info-item">科目:{{ detail.subjectName }}</li>
  38 + <li class="info-item">课时:{{ detail.title }}</li>
  39 + <li class="info-item">上课时间:{{ detail.startTime }}</li>
  40 + <li class="info-item">下课时间:{{ detail.endTime }}</li>
  41 + <li class="info-item">签到人数:{{ detail.answeredNum }}</li>
  42 + <li class="info-item">题目总数:{{ detail.questionNum }}</li>
  43 + <li class="info-item">答题总数:{{ detail.totalAnswersNum }}</li>
  44 + <li class="info-item">课时时长:{{ detail.duration }}分钟</li>
  45 + <li class="info-item">总参与度::{{ detail.participationRate }}%</li>
  46 + <li class="info-item">
  47 + 班级总正确率:{{ detail.classCorrectRate }}%
  48 + </li>
  49 + <li class="info-item">
  50 + 已答总正确率:{{ detail.answerCorrectRate }}%
  51 + </li>
  52 + <li class="info-item">
  53 + 反馈时长:{{ setDuration(detail.consumingDuration) }}
  54 + </li>
  55 + </ul>
  56 + <div id="print-content">
  57 + <el-table
  58 + v-if="type == 1"
  59 + :data="tableData"
  60 + border
  61 + style="width: 100%"
  62 + >
  63 + <el-table-column prop="questionIndex" label="题号" align="center"
  64 + ><template slot-scope="scoped"
  65 + >Q{{ scoped.row.questionIndex }}</template
  66 + ></el-table-column
  67 + >
  68 + <el-table-column prop="questionType" label="题型" align="center">
  69 + <template slot-scope="scoped">{{
  70 + setSubPro(scoped.row.questionType)
  71 + }}</template>
  72 + </el-table-column>
  73 + <el-table-column
  74 + prop="answeredNum"
  75 + label="答题人数"
  76 + sortable
  77 + align="center"
  78 + ></el-table-column>
  79 + <el-table-column
  80 + prop="correctAnswerNum"
  81 + label="答对人数"
  82 + sortable
  83 + align="center"
  84 + ></el-table-column>
  85 + <el-table-column
  86 + prop="participationRate"
  87 + label="班级参与度"
  88 + sortable
  89 + align="center"
  90 + ><template slot-scope="scoped"
  91 + >{{ scoped.row.participationRate }}%</template
  92 + ></el-table-column
  93 + >
  94 + <el-table-column
  95 + prop="classCorrectRate"
  96 + label="班级正确率"
  97 + sortable
  98 + align="center"
  99 + ><template slot-scope="scoped"
  100 + >{{ scoped.row.classCorrectRate }}%</template
  101 + ></el-table-column
  102 + >
  103 + <el-table-column
  104 + prop="answerCorrectRate"
  105 + label="已答正确率"
  106 + sortable
  107 + align="center"
  108 + ><template slot-scope="scoped"
  109 + >{{ scoped.row.answerCorrectRate }}%</template
  110 + ></el-table-column
  111 + >
  112 + <el-table-column
  113 + prop="correctAnswer"
  114 + label="正确答案"
  115 + align="center"
  116 + >
  117 + <template slot-scope="scoped">{{
  118 + scoped.row.correctAnswer == 1
  119 + ? "✓"
  120 + : scoped.row.correctAnswer == 2
  121 + ? "✗"
  122 + : scoped.row.correctAnswer
  123 + }}</template></el-table-column
  124 + >
  125 + <el-table-column prop="fallible" label="干扰答案" align="center"
  126 + ><template slot-scope="scoped">{{
  127 + scoped.row.fallible == 1
  128 + ? "✓"
  129 + : scoped.row.fallible == 2
  130 + ? "✗"
  131 + : scoped.row.fallible
  132 + }}</template></el-table-column
  133 + >
  134 + <!-- <el-table-column prop="screenshot" label="题干" align="center">
  135 + <template slot-scope="scoped">
  136 + <el-image
  137 + v-if="scoped.row.screenshot"
  138 + style="width: 60px; height: 40px"
  139 + :src="scoped.row.screenshot"
  140 + :preview-src-list="[scoped.row.screenshot]"
  141 + >
  142 + </el-image><span v-else>暂无</span></template
  143 + ></el-table-column> -->
  144 + </el-table>
  145 + <el-table
  146 + v-if="type == 2"
  147 + :data="tableData"
  148 + border
  149 + style="width: 100%"
  150 + >
  151 + <el-table-column
  152 + prop="studentCode"
  153 + label="学号"
  154 + align="center"
  155 + ></el-table-column>
  156 + <el-table-column
  157 + prop="studentName"
  158 + label="姓名"
  159 + align="center"
  160 + ></el-table-column>
  161 + <el-table-column
  162 + prop="answerTimes"
  163 + label="答题次数"
  164 + align="center"
  165 + ></el-table-column>
  166 + <el-table-column
  167 + prop="consumingDuration"
  168 + label="答题耗时"
  169 + align="center"
  170 + ><template slot-scope="scoped">{{
  171 + setDuration(scoped.row.consumingDuration)
  172 + }}</template></el-table-column
  173 + >
  174 + <el-table-column
  175 + prop="correctAnswerTimes"
  176 + label="答对次数"
  177 + align="center"
  178 + ></el-table-column>
  179 + <el-table-column
  180 + prop="participationRate"
  181 + label="参与度"
  182 + sortable
  183 + align="center"
  184 + ><template slot-scope="scoped"
  185 + >{{ scoped.row.participationRate }}%</template
  186 + ></el-table-column
  187 + >
  188 + <el-table-column
  189 + prop="correctRate"
  190 + label="正确率"
  191 + sortable
  192 + align="center"
  193 + ><template slot-scope="scoped"
  194 + >{{ scoped.row.correctRate }}%</template
  195 + ></el-table-column
  196 + >
  197 + <el-table-column
  198 + prop="answerCorrectRate"
  199 + label="已答正确率"
  200 + sortable
  201 + align="center"
  202 + ><template slot-scope="scoped"
  203 + >{{ scoped.row.answerCorrectRate }}%</template
  204 + ></el-table-column
  205 + >
  206 + <el-table-column
  207 + v-for="(item, index) in optionsList"
  208 + :key="index"
  209 + :label="'Q' + (index + 1)"
  210 + align="center"
  211 + ><template slot-scope="scoped">
  212 + <span :class="scoped.row['isRight' + index] ? '' : 'red'">{{
  213 + scoped.row["answer" + index]
  214 + }}</span>
  215 + </template>
  216 + </el-table-column>
  217 + </el-table>
  218 + <el-table
  219 + v-if="type == 3"
  220 + :data="tableData"
  221 + border
  222 + style="width: 100%"
  223 + >
  224 + <el-table-column
  225 + prop="studentCode"
  226 + label="学号"
  227 + align="center"
  228 + ></el-table-column>
  229 + <el-table-column
  230 + prop="studentName"
  231 + label="姓名"
  232 + align="center"
  233 + ></el-table-column>
  234 + <el-table-column
  235 + prop="rushAnswerTimes"
  236 + label="抢答成功次数"
  237 + sortable
  238 + align="center"
  239 + ></el-table-column>
  240 + <el-table-column
  241 + prop="rushAnswerCorrectTimes"
  242 + label="答对次数"
  243 + sortable
  244 + align="center"
  245 + ></el-table-column>
  246 + <el-table-column
  247 + prop="checkAnswerTimes"
  248 + label="抽答次数"
  249 + sortable
  250 + align="center"
  251 + ></el-table-column>
  252 + <el-table-column
  253 + prop="checkAnswerCorrectTimes"
  254 + label="抽答答对次数"
  255 + sortable
  256 + align="center"
  257 + ></el-table-column>
  258 + <el-table-column
  259 + prop="interactionsNum"
  260 + label="参与得分"
  261 + sortable
  262 + align="center"
  263 + ></el-table-column>
  264 + <el-table-column
  265 + prop="interactionsCorrectNum"
  266 + label="对错得分"
  267 + sortable
  268 + align="center"
  269 + ></el-table-column>
  270 + </el-table>
  271 + <el-table
  272 + v-if="type == 4"
  273 + :data="tableData"
  274 + border
  275 + style="width: 100%"
  276 + >
  277 + <el-table-column
  278 + prop="studentName"
  279 + label="姓名"
  280 + align="center"
  281 + ></el-table-column>
  282 + <el-table-column
  283 + prop="checkInTime"
  284 + label="签到时间"
  285 + sortable
  286 + align="center"
  287 + ></el-table-column>
  288 + <el-table-column
  289 + prop="makeUpTime"
  290 + label="补签时间"
  291 + sortable
  292 + align="center"
  293 + ></el-table-column>
  294 + </el-table>
  295 + </div>
  296 + <div class="pagination-box" v-show="type == 1">
  297 + <el-pagination
  298 + small=""
  299 + layout="total,prev, pager, next"
  300 + :hide-on-single-page="true"
  301 + :total="total"
  302 + @current-change="changePage"
  303 + :current-page="page"
  304 + :page-size="size"
  305 + >
  306 + </el-pagination>
  307 + </div>
  308 + <div class="down">
  309 + <p>
  310 + <el-button
  311 + @click="exportData"
  312 + type="primary"
  313 + plain
  314 + round
  315 + icon="fa fa-cloud-download"
  316 + >导出报表</el-button
  317 + >
  318 + <el-button
  319 + @click="print"
  320 + type="primary"
  321 + plain
  322 + round
  323 + icon="el-icon-printer"
  324 + >打印</el-button
  325 + >
  326 + </p>
  327 + <!-- <el-button @click="edit" type="primary" round>修改答案</el-button> -->
  328 + </div>
  329 + </div>
  330 + </div>
  331 + </div>
  332 +</template>
  333 +
  334 +<script>
  335 +import { downloadFile,tablePrint } from "@/utils";
  336 +export default {
  337 + data() {
  338 + return {
  339 + loading: false,
  340 + id: "",
  341 + title: "",
  342 + type: 1,
  343 + form: {
  344 + id: "",
  345 + questionList: [],
  346 + },
  347 + detail: {},
  348 + tableData: [],
  349 + optionsList: [],
  350 + page: 1,
  351 + size: 20,
  352 + total: 0,
  353 + };
  354 + },
  355 + created() {
  356 + this.id = this.$route.query.id;
  357 + this.title = this.$route.query.title;
  358 + this._QueryData();
  359 + this.periodDetail();
  360 + },
  361 + methods: {
  362 + print() {
  363 + tablePrint("print-content");
  364 + },
  365 + setType(type) {
  366 + this.type = type;
  367 + this.page = 1;
  368 + this._QueryData();
  369 + },
  370 + setDuration(times) {
  371 + let m = parseInt(times / 1000 / 60);
  372 + let s = parseInt((times / 1000) % 60);
  373 + let ms = times;
  374 + let aTime;
  375 + if (times == 0) {
  376 + aTime = `0`;
  377 + } else {
  378 + if (m == 0 && s == 0) {
  379 + aTime = `${ms}毫秒`;
  380 + } else if (m == 0 && s != 0) {
  381 + aTime = `${s}秒`;
  382 + } else if (m != 0 && s != 0) {
  383 + aTime = `${m}分${s}秒`;
  384 + }
  385 + }
  386 + return aTime;
  387 + },
  388 + setSubPro(type) {
  389 + let tit;
  390 + switch (type) {
  391 + case 2:
  392 + tit = "单选题";
  393 + break;
  394 + case 3:
  395 + tit = "多选题";
  396 + break;
  397 + case 4:
  398 + tit = "判断题";
  399 + break;
  400 + case 5:
  401 + tit = "主观题";
  402 + break;
  403 + default:
  404 + tit = "其他";
  405 + }
  406 + return tit;
  407 + },
  408 + async edit() {
  409 + this.$router.push({
  410 + path: "/examinationPaperEdit",
  411 + query: {
  412 + paperId: this.id,
  413 + title: this.title,
  414 + type: 3,
  415 + },
  416 + });
  417 + },
  418 + changePage(page) {
  419 + this.page = page;
  420 + this._QueryData();
  421 + },
  422 + async periodDetail() {
  423 + let { data, info, status } = await this.$request.periodDetail({
  424 + periodId: this.id,
  425 + });
  426 + if (status == 0) {
  427 + this.detail = { ...data };
  428 + this.detail.duration = this.detail.duration
  429 + ? (this.detail.duration / 60).toFixed(2)
  430 + : 0;
  431 + this.detail.consumingDuration = this.detail.consumingDuration
  432 + ? (this.detail.consumingDuration / 60).toFixed(2)
  433 + : 0;
  434 + } else {
  435 + this.$message.error(info);
  436 + }
  437 + },
  438 + async _QueryData() {
  439 + const queryData =
  440 + this.type == 1
  441 + ? this.$request.periodQuestionReport
  442 + : this.$request.periodStudentReport;
  443 + let query = {};
  444 + if (this.type == 2) {
  445 + query.type = 1;
  446 + } else if (this.type == 3) {
  447 + query.type = 2;
  448 + } else if (this.type == 4) {
  449 + query.type = 3;
  450 + }
  451 + this.loading = true;
  452 + let { data, info, status } = await queryData({
  453 + periodId: this.id,
  454 + page: this.page,
  455 + size: this.size,
  456 + ...query,
  457 + });
  458 + this.loading = false;
  459 + if (status === 0) {
  460 + if (this.type == 2) {
  461 + let optionsList = [];
  462 + this.tableData = data?.list.map((item) => {
  463 + let params = {};
  464 + const detail = JSON.parse(item.detail);
  465 + if (detail.length > optionsList.length) {
  466 + optionsList = [...detail];
  467 + }
  468 + detail.map((items, index) => {
  469 + params["isRight" + index] = items.isRight;
  470 + params["answer" + index] =
  471 + items.answer == 1
  472 + ? "✓"
  473 + : items.answer == 2
  474 + ? "✗"
  475 + : items.answer;
  476 + });
  477 + return {
  478 + ...item,
  479 + ...params,
  480 + };
  481 + });
  482 + this.optionsList = [...optionsList];
  483 + } else {
  484 + this.tableData = data?.list.sort((a, b) => {
  485 + return a.questionIndex - b.questionIndex;
  486 + });
  487 + }
  488 + this.total = data.count;
  489 + } else {
  490 + this.$message.error(info);
  491 + }
  492 + },
  493 + //导出
  494 + async exportData() {
  495 + if (this.exportLoading == true) return;
  496 + this.exportLoading = true;
  497 + const data = await this.$request.exportPeriodReport({
  498 + periodId: this.id,
  499 + });
  500 + this.exportLoading = false;
  501 + if (data) {
  502 + let blob = new Blob([data], {
  503 + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  504 + });
  505 + downloadFile("随堂问-单课时报表.xlsx", blob);
  506 + } else {
  507 + this.$message.error("下载失败");
  508 + }
  509 + },
  510 + },
  511 +};
  512 +</script>
  513 +<style>
  514 +div::-webkit-scrollbar {
  515 + width: 3px;
  516 + height: 10px;
  517 +}
  518 +div::-webkit-scrollbar-thumb {
  519 + border-radius: 10px;
  520 + background-color: #ccc;
  521 +}
  522 +</style>
  523 +<style lang="scss" scoped>
  524 +.down {
  525 + padding-top: 20px;
  526 + width: 100%;
  527 + display: flex;
  528 + justify-content: space-between;
  529 +}
  530 +.red {
  531 + color: #f30;
  532 +}
  533 +.page-content {
  534 + padding: 20px 20px 0;
  535 +}
  536 +.tab-box {
  537 + width: 800px;
  538 + margin: 0 auto 12px;
  539 + background: #f8f8f8;
  540 + border-radius: 20px;
  541 + display: flex;
  542 + .tab-item {
  543 + flex: 1;
  544 + height: 40px;
  545 + line-height: 40px;
  546 + text-align: center;
  547 + font-size: 16px;
  548 + color: #666;
  549 + font-weight: 500;
  550 + background: transparent;
  551 + border-radius: 20px;
  552 + cursor: pointer;
  553 + &.active {
  554 + background: #667ffd;
  555 + color: #fff;
  556 + }
  557 + }
  558 +}
  559 +.info {
  560 + display: flex;
  561 + flex-wrap: wrap;
  562 + border-left: 1px solid #e2e2e2;
  563 + border-top: 1px solid #e2e2e2;
  564 + margin-bottom: 12px;
  565 + .info-item {
  566 + width: 25%;
  567 + height: 50px;
  568 + box-sizing: border-box;
  569 + flex-shrink: 0;
  570 + background: #f8f8f8;
  571 + border-right: 1px solid #e2e2e2;
  572 + border-bottom: 1px solid #e2e2e2;
  573 + line-height: 50px;
  574 + text-align: center;
  575 + }
  576 +}
  577 +</style>
0 578 \ No newline at end of file
... ...
src/views/personal/ask/index.vue 0 → 100644
  1 +<template>
  2 + <div class="main" ref="main">
  3 + <back-box>
  4 + <template slot="title">
  5 + <span>问答-数据报表</span>
  6 + </template>
  7 + </back-box>
  8 + <div class="answer-header">
  9 + <div class="sel-box">
  10 + <el-select
  11 + class="sel"
  12 + v-model="query.classId"
  13 + placeholder="选择班级"
  14 + @change="changeclass"
  15 + >
  16 + <el-option
  17 + v-for="item in classList"
  18 + :key="item.value"
  19 + :label="item.label"
  20 + :value="item.value"
  21 + >
  22 + </el-option>
  23 + </el-select>
  24 + <el-select
  25 + class="sel"
  26 + multiple
  27 + collapse-tags
  28 + v-model="query.subjectNames"
  29 + placeholder="选择科目"
  30 + @change="changeSub"
  31 + >
  32 + <el-option
  33 + v-for="item in subjectList"
  34 + :key="item.value"
  35 + :label="item.label"
  36 + :value="item.value"
  37 + >
  38 + </el-option>
  39 + </el-select>
  40 + <div class="d1">
  41 + <el-date-picker
  42 + v-model="query.startDay"
  43 + type="date"
  44 + @change="handleChangeTimeStart"
  45 + placeholder="选择日期时间"
  46 + value-format="yyyy-MM-dd"
  47 + >
  48 + </el-date-picker>
  49 + ~
  50 + <el-date-picker
  51 + v-model="query.endDay"
  52 + type="date"
  53 + placeholder="选择日期时间"
  54 + @change="handleChangeTimeEnd"
  55 + value-format="yyyy-MM-dd"
  56 + >
  57 + </el-date-picker>
  58 + </div>
  59 + <p class="p1">
  60 + <span @click="setDate(1)" :class="[date == 1 ? 'active' : '', 's1']"
  61 + >今天</span
  62 + >
  63 + <span @click="setDate(2)" :class="[date == 2 ? 'active' : '', 's1']"
  64 + >本周</span
  65 + >
  66 + <span @click="setDate(3)" :class="[date == 3 ? 'active' : '', 's1']"
  67 + >本月</span
  68 + >
  69 + <span @click="setDate(4)" :class="[date == 4 ? 'active' : '', 's1']"
  70 + >本季度</span
  71 + >
  72 + </p>
  73 + <el-button type="primary" round @click="_QueryData()">筛选</el-button>
  74 + </div>
  75 + </div>
  76 + <div class="table-box">
  77 + <el-radio-group
  78 + v-model="tabIndex"
  79 + @change="tabChange"
  80 + style="margin-bottom: 20px"
  81 + >
  82 + <el-radio-button :label="1">单课时报表</el-radio-button>
  83 + <el-radio-button :label="2" v-if="query.startDay != query.endDay"
  84 + >阶段问答报表</el-radio-button
  85 + >
  86 + <el-radio-button :label="3" v-if="query.startDay != query.endDay"
  87 + >阶段互动报表</el-radio-button
  88 + >
  89 + </el-radio-group>
  90 + <div class="table-cont" v-loading="loading">
  91 + <div v-show="tabIndex == 1">
  92 + <el-table
  93 + :data="tableData"
  94 + border
  95 + style="width: 100%"
  96 + @sort-change="sortChange"
  97 + >
  98 + <el-table-column
  99 + prop="title"
  100 + label="课时"
  101 + align="center"
  102 + ></el-table-column>
  103 + <el-table-column
  104 + prop="questionNum"
  105 + label="题目总数"
  106 + align="center"
  107 + width="100"
  108 + ></el-table-column>
  109 + <el-table-column
  110 + prop="startTime"
  111 + label="上课时间"
  112 + align="center"
  113 + ></el-table-column>
  114 + <el-table-column
  115 + prop="participationRate"
  116 + label="参与度"
  117 + sortable="custom"
  118 + align="center"
  119 + >
  120 + <template slot-scope="scoped"
  121 + >{{ scoped.row.participationRate }}%</template
  122 + ></el-table-column
  123 + >
  124 + <el-table-column
  125 + prop="answerCorrectRate"
  126 + label="已答总正确率"
  127 + sortable="custom"
  128 + align="center"
  129 + >
  130 + <template slot-scope="scoped"
  131 + >{{ scoped.row.answerCorrectRate }}%</template
  132 + >
  133 + </el-table-column>
  134 + <el-table-column
  135 + prop="classCorrectRate"
  136 + label="班级总正确率"
  137 + sortable="custom"
  138 + align="center"
  139 + ><template slot-scope="scoped"
  140 + >{{ scoped.row.classCorrectRate }}%</template
  141 + ></el-table-column
  142 + >
  143 + <el-table-column label="操作" align="center">
  144 + <template slot-scope="scoped">
  145 + <el-tooltip
  146 + effect="dark"
  147 + v-if="scoped.row.answerNum == 0"
  148 + content="设置答案"
  149 + placement="top"
  150 + >
  151 + <el-button
  152 + type="primary"
  153 + circle
  154 + size="mini"
  155 + icon="fa fa-file-text"
  156 + @click="edit(scoped.row)"
  157 + ></el-button>
  158 + </el-tooltip>
  159 + <el-tooltip v-else effect="dark" content="详情" placement="top">
  160 + <el-button
  161 + type="primary"
  162 + circle
  163 + size="mini"
  164 + icon="fa fa-arrow-right"
  165 + @click="linkTo(scoped.row)"
  166 + ></el-button>
  167 + </el-tooltip>
  168 + </template>
  169 + </el-table-column>
  170 + </el-table>
  171 + </div>
  172 + <div v-show="tabIndex == 2">
  173 + <el-table
  174 + id="print-content2"
  175 + :max-height="tableMaxHeight"
  176 + :data="tableData"
  177 + border
  178 + style="width: 100%"
  179 + >
  180 + <el-table-column
  181 + prop="studentCode"
  182 + label="学号"
  183 + align="center"
  184 + fixed
  185 + ></el-table-column>
  186 + <el-table-column
  187 + prop="studentName"
  188 + label="姓名"
  189 + align="center"
  190 + fixed
  191 + width="100"
  192 + ></el-table-column>
  193 + <el-table-column
  194 + v-for="(item, index) in phaseOption"
  195 + :key="index"
  196 + :label="item"
  197 + align="center"
  198 + >
  199 + <el-table-column
  200 + align="center"
  201 + :label="index == 0 ? '总课时数' : '课时数'"
  202 + :prop="'periodCount' + item"
  203 + >
  204 + </el-table-column>
  205 + <el-table-column
  206 + align="center"
  207 + :label="index == 0 ? '总出题数' : '出题数'"
  208 + :prop="'questionNum' + item"
  209 + >
  210 + </el-table-column>
  211 + <el-table-column
  212 + align="center"
  213 + :label="index == 0 ? '总参与度' : '参与度'"
  214 + :prop="'participationRate' + item"
  215 + ><template slot-scope="scoped"
  216 + >{{ scoped.row["participationRate" + item] }}%</template
  217 + >
  218 + </el-table-column>
  219 + <el-table-column
  220 + align="center"
  221 + :label="index == 0 ? '总正确率' : '正确率'"
  222 + :prop="'correctRate' + item"
  223 + ><template slot-scope="scoped"
  224 + >{{ scoped.row["correctRate" + item] }}%</template
  225 + >
  226 + </el-table-column>
  227 + </el-table-column>
  228 + </el-table>
  229 + </div>
  230 + <div v-show="tabIndex == 3">
  231 + <el-table
  232 + id="print-content3"
  233 + :max-height="tableMaxHeight"
  234 + :data="tableData"
  235 + border
  236 + style="width: 100%"
  237 + >
  238 + <el-table-column
  239 + prop="studentCode"
  240 + label="学号"
  241 + align="center"
  242 + ></el-table-column>
  243 + <el-table-column
  244 + prop="studentName"
  245 + label="姓名"
  246 + align="center"
  247 + width="100"
  248 + ></el-table-column>
  249 + <el-table-column
  250 + v-for="(item, index) in phaseInter"
  251 + :key="index"
  252 + :label="item"
  253 + align="center"
  254 + >
  255 + <el-table-column
  256 + align="center"
  257 + v-if="index == 0"
  258 + label="参与分"
  259 + sortable
  260 + :prop="'interactionsNum' + item"
  261 + >
  262 + </el-table-column>
  263 + <el-table-column
  264 + v-else
  265 + align="center"
  266 + label="互动数"
  267 + :prop="'interactionsNum' + item"
  268 + >
  269 + </el-table-column>
  270 + <el-table-column
  271 + v-if="index == 0"
  272 + align="center"
  273 + label="对错分"
  274 + sortable
  275 + :prop="'interactionsCorrectNum' + item"
  276 + >
  277 + </el-table-column>
  278 + <el-table-column
  279 + v-else
  280 + align="center"
  281 + label="参与数"
  282 + :prop="'interactionsCorrectNum' + item"
  283 + >
  284 + </el-table-column>
  285 + </el-table-column>
  286 + </el-table>
  287 + </div>
  288 + <div class="pagination-box" v-show="tabIndex == 1">
  289 + <el-pagination
  290 + small=""
  291 + layout="total,prev, pager, next"
  292 + :hide-on-single-page="true"
  293 + :total="total"
  294 + @current-change="changePage"
  295 + :current-page="page"
  296 + :page-size="size"
  297 + >
  298 + </el-pagination>
  299 + </div>
  300 + <p
  301 + class="down"
  302 + v-if="(tabIndex == 3 || tabIndex == 2) && tableData.length"
  303 + >
  304 + <el-button
  305 + @click="exportData"
  306 + type="primary"
  307 + plain
  308 + round
  309 + icon="fa fa-cloud-download"
  310 + >导出报表</el-button
  311 + >
  312 + <el-button
  313 + @click="print"
  314 + type="primary"
  315 + plain
  316 + round
  317 + icon="el-icon-printer"
  318 + >打印</el-button
  319 + >
  320 + </p>
  321 + </div>
  322 + </div>
  323 + </div>
  324 +</template>
  325 +
  326 +<script>
  327 +import { formatDate, tablePrint, downloadFile } from "utils";
  328 +import BusEvent from "@/utils/busEvent";
  329 +export default {
  330 + data() {
  331 + return {
  332 + tableMaxHeight: 300,
  333 + loading: false,
  334 + form: { questionList: [] },
  335 + date: "", //今天-昨天-本周
  336 + query: {
  337 + //搜索条件
  338 + classId: "",
  339 + subjectNames: [],
  340 + startDay: "",
  341 + endDay: "",
  342 + day: "",
  343 + },
  344 + custom: {
  345 + //单课时排序
  346 + orderField: null,
  347 + orderType: null,
  348 + },
  349 + classList: [], //班级
  350 + subjectList: [], //科目
  351 + tabIndex: 1, //选项卡
  352 + tableData: [],
  353 + phaseOption: [], //问答补充数据
  354 + phaseInter: [], //互动补充数据
  355 + page: 1,
  356 + size: 20,
  357 + total: 0,
  358 + };
  359 + },
  360 + async created() {
  361 + await this._QueryClassList();
  362 + await this._QuerySubjectList();
  363 + await this.setDate(1);
  364 + let startDay = this.query?.startDay;
  365 + if (!startDay) {
  366 + this.query.startDay = new Date();
  367 + this.query.endDay = new Date();
  368 + }
  369 + },
  370 + activated() {
  371 + const that = this;
  372 + BusEvent.$on("keepAlive", async function () {
  373 + await that._QueryClassList();
  374 + await that._QuerySubjectList();
  375 + await that.setDate(1);
  376 + let startDay = that.query?.startDay;
  377 + if (!startDay) {
  378 + that.query.startDay = new Date();
  379 + that.query.endDay = new Date();
  380 + }
  381 + });
  382 + },
  383 + methods: {
  384 + print() {
  385 + tablePrint("print-content");
  386 + },
  387 + changeSub(val) {
  388 + let sub;
  389 + if (val && val.length) {
  390 + let leng = val.length - 1;
  391 + sub = val[leng];
  392 + }
  393 + console.log(val);
  394 + this.query.subjectNames = val.filter((item) => {
  395 + return sub != "全部" ? item != "全部" : item == "全部";
  396 + });
  397 + },
  398 + linkTo(obj) {
  399 + //去详情
  400 + this.$router.push({
  401 + path: "/askAnalysis",
  402 + query: {
  403 + id: obj.id,
  404 + title: obj.title,
  405 + },
  406 + });
  407 + },
  408 + setDate(index) {
  409 + const that = this;
  410 + this.date = index == this.date ? "" : index;
  411 + let aYear = new Date().getFullYear();
  412 + let aMonth = new Date().getMonth() + 1;
  413 + that.query.day = "";
  414 + that.query.startDay = "";
  415 + that.query.endDay = "";
  416 + switch (index) {
  417 + case 1:
  418 + that.query.day = formatDate(new Date(), "yyyy-MM-dd");
  419 + that.query.startDay = that.query.day;
  420 + that.query.endDay = that.query.day;
  421 + that.tabIndex = 1;
  422 + break;
  423 + case 2:
  424 + let day = new Date().getDay();
  425 + if (day == 0) {
  426 + //中国式星期天是一周的最后一天
  427 + day = 7;
  428 + }
  429 + day--;
  430 + let aTime = new Date().getTime() - 24 * 60 * 60 * 1000 * day;
  431 + that.query.startDay = formatDate(new Date(aTime), "yyyy-MM-dd");
  432 + that.query.endDay = formatDate(new Date(), "yyyy-MM-dd");
  433 + break;
  434 + case 3:
  435 + aMonth = aMonth < 10 ? "0" + aMonth : aMonth;
  436 + that.query.startDay = `${aYear}-${aMonth}-01`;
  437 + that.query.endDay = formatDate(new Date(), "yyyy-MM-dd");
  438 + break;
  439 + case 4:
  440 + if (aMonth > 0 && aMonth < 4) {
  441 + aMonth = "1";
  442 + } else if (aMonth > 3 && aMonth < 7) {
  443 + aMonth = "4";
  444 + } else if (aMonth > 6 && aMonth < 10) {
  445 + aMonth = "7";
  446 + } else {
  447 + aMonth = "10";
  448 + }
  449 +
  450 + aMonth = aMonth < 10 ? "0" + aMonth : aMonth;
  451 + that.query.startDay = `${aYear}-${aMonth}-01`;
  452 + that.query.endDay = formatDate(new Date(), "yyyy-MM-dd");
  453 + break;
  454 + }
  455 + this.page = 1;
  456 + this._QueryData();
  457 + },
  458 + handleChangeTimeStart(val) {
  459 + this.query.day = "";
  460 + this.date = "";
  461 + if (this.query.endDay) {
  462 + if (new Date(val).getTime() > new Date(this.query.endDay).getTime()) {
  463 + this.$message.error("任务结束时间不能任务开始时间前面,请重新设置");
  464 + this.query.startDay = "";
  465 + }
  466 + }
  467 + },
  468 + handleChangeTimeEnd(val) {
  469 + this.query.day = "";
  470 + this.date = "";
  471 + if (this.query.startDay) {
  472 + if (new Date(val).getTime() < new Date(this.query.startDay).getTime()) {
  473 + this.$message.error("任务结束时间不能任务开始时间前面,请重新设置");
  474 + this.query.endDay = "";
  475 + }
  476 + }
  477 + },
  478 + edit(item) {
  479 + this.$router.push({
  480 + path: "/examinationPaperEdit",
  481 + query: {
  482 + paperId: item.id,
  483 + title: item.title,
  484 + type: 3,
  485 + },
  486 + });
  487 + },
  488 + tabChange() {
  489 + this.tableMaxHeight = this.$refs.main.offsetHeight;
  490 + this.page = 1;
  491 + this.tableData = [];
  492 + this._QueryData();
  493 + },
  494 + sortChange(obj) {
  495 + this.custom.orderField =
  496 + obj.prop == "participationRate"
  497 + ? 1
  498 + : obj.prop == "answerCorrectRate"
  499 + ? 2
  500 + : 3;
  501 + this.custom.orderType = obj.order == "ascending" ? 0 : 1;
  502 + this.page = 1;
  503 + this._QueryData();
  504 + },
  505 + changePage(page) {
  506 + this.page = page;
  507 + this._QueryData();
  508 + },
  509 + async changeclass() {
  510 + await this._QuerySubjectList();
  511 + this.page = 1;
  512 + this._QueryData();
  513 + },
  514 + async changClazz() {
  515 + await this._QuerySubjectList();
  516 + // await this.setDate(1);
  517 + this._QueryData();
  518 + },
  519 + async _QueryClassList() {
  520 + const { data, status, info } = await this.$request.tClassList();
  521 + if (status === 0) {
  522 + this.classList = data.list.map((item) => {
  523 + return {
  524 + value: item.classId,
  525 + label: item.className,
  526 + };
  527 + });
  528 + this.query.classId = this.classList[0]?.value;
  529 + } else {
  530 + this.$message.error(info);
  531 + }
  532 + },
  533 + async _QuerySubjectList() {
  534 + const { data, status, info } = await this.$request.tSubjectList({
  535 + classId: this.query.classId,
  536 + });
  537 + if (status === 0) {
  538 + this.query.subjectNames = [];
  539 + this.subjectList =
  540 + data.subjectNames?.map((item) => {
  541 + return {
  542 + value: item,
  543 + label: item,
  544 + };
  545 + }) || [];
  546 + this.subjectList.unshift({
  547 + value: "全部",
  548 + label: "全部",
  549 + });
  550 + this.query.subjectNames.push(this.subjectList[0]?.value);
  551 + } else {
  552 + this.$message.error(info);
  553 + }
  554 + },
  555 + async _QueryData() {
  556 + if (this.tabIndex == 1) {
  557 + this.periodReportList();
  558 + } else if (this.tabIndex == 2) {
  559 + this.phaseAnswerReport();
  560 + } else if (this.tabIndex == 3) {
  561 + this.phaseInteractiveReport();
  562 + }
  563 + },
  564 + //分页查询课时报表列表
  565 + async periodReportList() {
  566 + this.loading = true;
  567 + let query = {};
  568 + for (let key in this.query) {
  569 + if (this.query[key] != "") {
  570 + query[key] = this.query[key];
  571 + }
  572 + }
  573 + if (this.custom.orderField !== null) {
  574 + query = { ...query, ...this.custom };
  575 + }
  576 +
  577 + if (
  578 + query["subjectNames"] &&
  579 + query["subjectNames"].length == 1 &&
  580 + query["subjectNames"][0] == "全部"
  581 + ) {
  582 + query["subjectNames"] = this.subjectList.map((item) => {
  583 + return item.value;
  584 + });
  585 + query["subjectNames"].shift();
  586 + }
  587 + if (!query["subjectNames"]) {
  588 + this.$message.warning("请选择科目");
  589 + return;
  590 + }
  591 + const { data, status, info } = await this.$request.periodReportList({
  592 + ...query,
  593 + page: this.page,
  594 + size: this.size,
  595 + });
  596 + this.loading = false;
  597 + if (status === 0) {
  598 + this.tableData = (data?.list && [...data?.list]) || [];
  599 + this.total = data.count;
  600 + } else {
  601 + this.$message.error(info);
  602 + }
  603 + },
  604 + //查询阶段问答报表
  605 + async phaseAnswerReport() {
  606 + this.loading = true;
  607 + let query = {};
  608 + for (let key in this.query) {
  609 + if (this.query[key] != "") {
  610 + query[key] = this.query[key];
  611 + }
  612 + }
  613 + if (
  614 + query["subjectNames"] &&
  615 + query["subjectNames"].length == 1 &&
  616 + query["subjectNames"][0] == "全部"
  617 + ) {
  618 + query["subjectNames"] = this.subjectList.map((item) => {
  619 + return item.value;
  620 + });
  621 + query["subjectNames"].shift();
  622 + }
  623 + if (!query["subjectNames"]) {
  624 + this.$message.warning("请选择科目");
  625 + return;
  626 + }
  627 + const { data, status, info } = await this.$request.phaseAnswerReport({
  628 + ...query,
  629 + });
  630 + this.loading = false;
  631 + if (status === 0) {
  632 + let subjectName = [];
  633 + let tableData = data?.list.map((item) => {
  634 + let params = {};
  635 + item.dataList.map((items, index) => {
  636 + if (!subjectName.includes(items.subjectName)) {
  637 + subjectName.push(items.subjectName);
  638 + }
  639 + params["answerCorrectRate" + items.subjectName] =
  640 + items.answerCorrectRate;
  641 + params["correctRate" + items.subjectName] = items.correctRate;
  642 + params["participationRate" + items.subjectName] =
  643 + items.participationRate;
  644 + params["periodCount" + items.subjectName] = items.periodCount;
  645 + params["questionNum" + items.subjectName] = items.questionNum;
  646 + });
  647 + return {
  648 + ...item,
  649 + ...params,
  650 + };
  651 + });
  652 + this.phaseOption = [...subjectName];
  653 + this.tableData = tableData.sort((a, b) => {
  654 + return a.studentCode - b.studentCode;
  655 + });
  656 +
  657 + this.total = data.count;
  658 + } else {
  659 + this.$message.error(info);
  660 + }
  661 + },
  662 + //查询阶段互动报表
  663 + async phaseInteractiveReport() {
  664 + this.loading = true;
  665 + let query = {};
  666 + for (let key in this.query) {
  667 + if (this.query[key] != "") {
  668 + query[key] = this.query[key];
  669 + }
  670 + }
  671 +
  672 + if (
  673 + query["subjectNames"] &&
  674 + query["subjectNames"].length == 1 &&
  675 + query["subjectNames"][0] == "全部"
  676 + ) {
  677 + query["subjectNames"] = this.subjectList.map((item) => {
  678 + return item.value;
  679 + });
  680 + query["subjectNames"].shift();
  681 + }
  682 + if (!query["subjectNames"]) {
  683 + this.$message.warning("请选择科目");
  684 + return;
  685 + }
  686 + const { data, status, info } = await this.$request.phaseInteractiveReport(
  687 + {
  688 + ...query,
  689 + }
  690 + );
  691 + this.loading = false;
  692 + if (status === 0) {
  693 + let subjectName = [];
  694 + let tableData = data?.list.map((item) => {
  695 + let params = {};
  696 + item.dataList.map((items, index) => {
  697 + if (!subjectName.includes(items.subjectName)) {
  698 + subjectName.push(items.subjectName);
  699 + }
  700 + params["interactionsNum" + items.subjectName] =
  701 + items.interactionsNum;
  702 + params["interactionsCorrectNum" + items.subjectName] =
  703 + items.interactionsCorrectNum;
  704 + });
  705 + return {
  706 + ...item,
  707 + ...params,
  708 + };
  709 + });
  710 + this.phaseInter = [...subjectName];
  711 + this.tableData = tableData.sort((a, b) => {
  712 + return a.studentCode - b.studentCode;
  713 + });
  714 +
  715 + this.total = data.count;
  716 + } else {
  717 + this.$message.error(info);
  718 + }
  719 + },
  720 + setDownQuery() {},
  721 + //导出
  722 + async exportData() {
  723 + if (this.exportLoading == true) return;
  724 + let query = {};
  725 + for (let key in this.query) {
  726 + if (this.query[key] != "") {
  727 + query[key] = this.query[key];
  728 + }
  729 + }
  730 +
  731 + if (
  732 + query["subjectNames"] &&
  733 + query["subjectNames"].length == 1 &&
  734 + query["subjectNames"][0] == "全部"
  735 + ) {
  736 + query["subjectNames"] = this.subjectList.map((item) => {
  737 + return item.value;
  738 + });
  739 + query["subjectNames"].shift();
  740 + }
  741 + if (!query["subjectNames"]) {
  742 + this.$message.warning("请选择科目");
  743 + return;
  744 + }
  745 + this.exportLoading = true;
  746 + const data = await this.$request.exportPhaseAnswerReport({ ...query });
  747 + this.exportLoading = false;
  748 + if (data) {
  749 + let blob = new Blob([data], {
  750 + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  751 + });
  752 + downloadFile("随堂问-阶段报表.xlsx", blob);
  753 + } else {
  754 + this.$message.error("下载失败");
  755 + }
  756 + },
  757 + },
  758 +};
  759 +</script>
  760 +<style>
  761 +div::-webkit-scrollbar {
  762 + width: 3px;
  763 + height: 10px;
  764 +}
  765 +div::-webkit-scrollbar-thumb {
  766 + border-radius: 10px;
  767 + background-color: #ccc;
  768 +}
  769 +</style>
  770 +<style lang="scss" scoped>
  771 +.main {
  772 + height: 100%;
  773 +}
  774 +.table-box {
  775 + margin: 0 20px;
  776 + padding: 16px;
  777 + background: #f8f8f8;
  778 + border-radius: 5px;
  779 + .table-cont {
  780 + min-height: 300px;
  781 + }
  782 + :deep(.fa-arrow-right) {
  783 + padding-left: 2px;
  784 + }
  785 + :deep(.fa-file-text) {
  786 + padding-left: 2px;
  787 + }
  788 +}
  789 +
  790 +.fa-exchange {
  791 + color: #667ffd;
  792 + cursor: pointer;
  793 + font-size: 16px;
  794 + margin-left: 10px;
  795 +}
  796 +.dia-btn {
  797 + border-radius: 20px;
  798 + margin: 0 20px;
  799 + padding: 10px 20px;
  800 +}
  801 +.dia-tips {
  802 + padding-bottom: 10px;
  803 +}
  804 +.dia-question-box {
  805 + padding: 16px 16px 1px;
  806 + background: #f8f8f8;
  807 + border-radius: 10px;
  808 +
  809 + .answer-s {
  810 + width: 36px;
  811 + height: 28px;
  812 + cursor: pointer;
  813 + }
  814 +}
  815 +.set-questions {
  816 + display: flex;
  817 + margin-bottom: 12px;
  818 + width: 100%;
  819 + .qs-num {
  820 + flex-shrink: 0;
  821 + margin-right: 10px;
  822 + }
  823 + .qs-options {
  824 + flex: 1;
  825 + .ipt {
  826 + margin-bottom: 5px;
  827 + }
  828 + }
  829 + .delButton {
  830 + border-color: #ff6868;
  831 + background: #ff6868 url("../../../assets/images/arrow.png") no-repeat center;
  832 + background-size: 19px;
  833 + color: transparent;
  834 + }
  835 + .ac {
  836 + border-color: #ff6868;
  837 + background: #ff6868;
  838 + color: #fff;
  839 + }
  840 +}
  841 +.down {
  842 + padding-top: 16px;
  843 +}
  844 +</style>
0 845 \ No newline at end of file
... ...
src/views/card/index.vue renamed to src/views/personal/card/index.vue
src/views/dataSync/index.vue renamed to src/views/personal/dataSync/index.vue
src/views/personal/down/index.vue 0 → 100644
  1 +<template>
  2 + <div class="content-box">
  3 + <back-box>
  4 + <template slot="title">
  5 + <span>软件下载</span>
  6 + </template>
  7 + </back-box>
  8 + <div class="down-box" v-loading="loading">
  9 + <img class="logo" src="" alt="" />
  10 + <div class="txt">
  11 + <p class="p1">中天易教授课端 V1.3.4</p>
  12 + <p class="p2">文件大小:35M</p>
  13 + <p class="p2">最近更新:2022-12-03 13:30</p>
  14 + </div>
  15 + <el-button type="primary" @click="downCard">立即下载</el-button>
  16 + </div>
  17 + </div>
  18 +</template>
  19 +
  20 +<script>
  21 +export default {
  22 + data() {
  23 + return {
  24 + loading: false,
  25 + };
  26 + },
  27 + methods: {
  28 + async downCard() {
  29 + if (this.loading == true) return;
  30 + this.loading = true;
  31 + const { data, status, info } = await this.$request.latestClickersApp();
  32 + this.loading = false;
  33 + if (status == 0) {
  34 + const a = document.createElement("a");
  35 + a.href = data.downloadUrl;
  36 + a.download = data.appName;
  37 + document.body.appendChild(a);
  38 + a.click();
  39 + a.remove();
  40 + } else {
  41 + this.$message.error(info);
  42 + }
  43 + },
  44 + },
  45 +};
  46 +</script>
  47 +
  48 +<style lang="scss" scoped>
  49 +.content-box {
  50 + width: 100%;
  51 +}
  52 +
  53 +.down-box {
  54 + padding:20px 50px;
  55 + display: flex;
  56 + align-items: center;
  57 +}
  58 +.logo {
  59 + width: 145px;
  60 + height: 85px;
  61 + margin-right: 20px;
  62 +}
  63 +.txt {
  64 + width: 300px;
  65 + margin-right: 50px;
  66 + .p1 {
  67 + font-size: 18px;
  68 + color: #333;
  69 + font-weight: 600;
  70 + padding-bottom: 16px;
  71 + }
  72 + .p2 {
  73 + color: #7f7f7f;
  74 + line-height: 20px;
  75 + }
  76 +}
  77 +</style>
0 78 \ No newline at end of file
... ...
src/views/personal/examinationPaper/add.vue 0 → 100644
  1 +<template>
  2 + <div ref="content" class="content-box">
  3 + <back-box>
  4 + <template slot="title">
  5 + <span>{{ "创建答题卡" }}</span>
  6 + </template>
  7 + </back-box>
  8 + <div class="content">
  9 + <el-steps
  10 + :active="step"
  11 + finish-status="success"
  12 + simple
  13 + style="margin: 20px 0"
  14 + >
  15 + <el-step title="基础信息" icon="el-icon-edit"></el-step>
  16 + <el-step title="题目编辑" icon="el-icon-tickets"></el-step>
  17 + <el-step title="设置答案" icon="el-icon-edit-outline"></el-step>
  18 + </el-steps>
  19 + <div v-show="step == 0">
  20 + <el-form
  21 + ref="forms"
  22 + :model="form"
  23 + :rules="formRules"
  24 + label-width="140px"
  25 + >
  26 + <el-form-item label="答题卡名称:" prop="title">
  27 + <el-input
  28 + class="sel2"
  29 + type="text"
  30 + placeholder="请输入答题卡名称"
  31 + v-model.trim="form.title"
  32 + maxlength="30"
  33 + size="45"
  34 + show-word-limit
  35 + >
  36 + </el-input>
  37 + </el-form-item>
  38 + <el-form-item label="测验类型:">
  39 + <el-select v-model="form.tagId" placeholder="选择测验类型">
  40 + <el-option label="--" value=""> </el-option>
  41 + <el-option
  42 + v-for="item in answerTypeList"
  43 + :key="item.id"
  44 + :label="item.typeName"
  45 + :value="item.id"
  46 + >{{ item.typeName }}</el-option
  47 + >
  48 + </el-select>
  49 + <el-button
  50 + class="ml-20"
  51 + type="primary"
  52 + round
  53 + circle
  54 + icon="el-icon-edit"
  55 + @click="openTagDia"
  56 + ></el-button>
  57 + </el-form-item>
  58 + <el-form-item label="科目:" prop="subjectName">
  59 + <el-select class="sel" v-model="form.subjectName" placeholder="">
  60 + <el-option
  61 + v-for="item in subjectList"
  62 + :key="item.value"
  63 + :label="item.label"
  64 + :value="item.value"
  65 + >{{ item.label }}
  66 + </el-option>
  67 + </el-select>
  68 + </el-form-item>
  69 + <el-form-item label="考试时长:">
  70 + <el-input-number
  71 + size="medium"
  72 + :min="1"
  73 + :max="140"
  74 + :step-strictly="true"
  75 + :step="1"
  76 + v-model="form.examsDuration"
  77 + label="考试时长"
  78 + ></el-input-number>
  79 + </el-form-item>
  80 + </el-form>
  81 + <div class="btn-box">
  82 + <el-button type="danger" plain round @click="linkBack"
  83 + >取消</el-button
  84 + >
  85 + <el-button type="primary" round @click="setStep1">下一步</el-button>
  86 + </div>
  87 + <el-dialog
  88 + title="设置测验类型"
  89 + :visible.sync="dialogVisible"
  90 + width="500px"
  91 + >
  92 + <div class="dia-content">
  93 + <p class="add-type" v-for="item in tagList" :key="item.id">
  94 + <el-row :gutter="10">
  95 + <el-col :span="18"
  96 + ><el-input
  97 + v-model="item.typeName"
  98 + :maxlength="10"
  99 + placeholder="请输入答题卡类型名称"
  100 + ></el-input
  101 + ></el-col>
  102 + <el-col :span="6">
  103 + <el-tooltip effect="dark" content="保存" placement="top">
  104 + <el-button
  105 + class="js-set"
  106 + type="primary"
  107 + size="small"
  108 + round
  109 + circle
  110 + icon="el-icon-check"
  111 + @click="editTypeName(item)"
  112 + ></el-button>
  113 + </el-tooltip>
  114 + <el-tooltip effect="dark" content="删除" placement="right">
  115 + <el-popconfirm
  116 + title="确定删除这道大题吗?"
  117 + @confirm="removeTypeName(item)"
  118 + >
  119 + <el-button
  120 + class="js-set"
  121 + type="danger"
  122 + size="small"
  123 + round
  124 + circle
  125 + icon="el-icon-delete"
  126 + slot="reference"
  127 + ></el-button>
  128 + </el-popconfirm>
  129 + </el-tooltip>
  130 + </el-col>
  131 + </el-row>
  132 + </p>
  133 + <p class="add-type">
  134 + <el-row :gutter="10">
  135 + <el-col :span="18">
  136 + <el-input
  137 + type="text"
  138 + placeholder="请输入答题卡类型名称"
  139 + v-model.trim="answerTypeName"
  140 + :maxlength="10"
  141 + >
  142 + </el-input>
  143 + </el-col>
  144 + <el-col :span="6">
  145 + <el-tooltip effect="dark" content="添加" placement="right">
  146 + <el-button
  147 + class="js-set"
  148 + type="primary"
  149 + size="small"
  150 + round
  151 + circle
  152 + icon="el-icon-plus"
  153 + @click="addPaperType"
  154 + ></el-button>
  155 + </el-tooltip>
  156 + </el-col>
  157 + </el-row>
  158 + </p>
  159 + </div>
  160 + <div class="" slot="footer">
  161 + <el-button @click="dialogVisible = false">关 闭</el-button>
  162 + </div>
  163 + </el-dialog>
  164 + </div>
  165 + <div v-show="step == 1">
  166 + <div
  167 + class="question-box"
  168 + v-for="(question, index) in form.questionList"
  169 + :key="index"
  170 + >
  171 + <p class="question-title">
  172 + <el-tooltip
  173 + effect="dark"
  174 + :content="question.show ? '收起' : '展开'"
  175 + placement="left"
  176 + >
  177 + <i
  178 + class="el-icon-caret-right"
  179 + :class="question.show ? 'active' : ''"
  180 + @click="question.show = !question.show"
  181 + ></i>
  182 + </el-tooltip>
  183 + <span>{{ setBigNum(index) }}、</span>
  184 + <el-input
  185 + class="ipt"
  186 + v-model.trim="question.questionTitle"
  187 + maxlength="30"
  188 + placeholder="填写大题名称"
  189 + ></el-input>
  190 + <el-popconfirm
  191 + title="确定删除这道大题吗?"
  192 + @confirm="form.questionList.splice(index, 1)"
  193 + >
  194 + <el-button
  195 + slot="reference"
  196 + class="delete"
  197 + type="danger"
  198 + size="mini"
  199 + circle
  200 + icon="el-icon-delete"
  201 + ></el-button>
  202 + </el-popconfirm>
  203 + <span class="m20">共:{{ question.subQuestions.length }}题</span>
  204 + <span>共:{{ setScore(question) }}分</span>
  205 + </p>
  206 + <transition name="el-zoom-in-top">
  207 + <ul v-show="question.show" class="questions-ul">
  208 + <li class="sub-questions">
  209 + <div class="qs-num">题号</div>
  210 + <div class="qs-type">题型</div>
  211 + <div class="qs-score">分数</div>
  212 + <div class="qs-partScore">漏选得分</div>
  213 + <div class="qs-options">选项设置</div>
  214 + <div class="qs-set">操作</div>
  215 + </li>
  216 + <li
  217 + class="sub-questions"
  218 + v-for="(subQuestions, indexs) in question.subQuestions"
  219 + :key="indexs"
  220 + >
  221 + <div class="qs-num">{{ setNum(index, indexs) }}</div>
  222 + <div class="qs-type">
  223 + <el-select
  224 + v-model="subQuestions.questionType"
  225 + placeholder="选择题目类型"
  226 + @change="changeSubQuestions($event, subQuestions)"
  227 + >
  228 + <el-option label="单选题" :value="2"></el-option>
  229 + <el-option label="多选题" :value="3"></el-option>
  230 + <el-option label="判断题" :value="4"></el-option>
  231 + <el-option label="主观题" :value="5"></el-option>
  232 + </el-select>
  233 + </div>
  234 + <div class="qs-score">
  235 + <el-input-number
  236 + class="number-ipt"
  237 + size="medium"
  238 + :min="1"
  239 + :max="200"
  240 + :precision="2"
  241 + :step="1"
  242 + v-model="subQuestions.score"
  243 + label="单题分值"
  244 + ></el-input-number>
  245 + </div>
  246 + <div class="qs-partScore">
  247 + <p v-if="subQuestions.questionType != 3">--</p>
  248 + <el-input-number
  249 + class="number-ipt"
  250 + v-else
  251 + size="medium"
  252 + :min="0"
  253 + :precision="2"
  254 + :max="subQuestions.score"
  255 + :step="0.5"
  256 + v-model="subQuestions.partScore"
  257 + label="漏选得分"
  258 + ></el-input-number>
  259 + </div>
  260 + <div class="qs-options">
  261 + <p v-if="subQuestions.questionType == 5">--</p>
  262 + <p v-if="subQuestions.questionType == 4" class="answer-box">
  263 + <span
  264 + class="answer-s"
  265 + :class="subQuestions.correctAnswer == 1 ? 'active' : ''"
  266 + @click="subQuestions.correctAnswer = 1"
  267 + >✓</span
  268 + >
  269 + <span
  270 + class="answer-s"
  271 + :class="subQuestions.correctAnswer == 2 ? 'active' : ''"
  272 + @click="subQuestions.correctAnswer = 2"
  273 + >✗</span
  274 + >
  275 + </p>
  276 + <p v-if="subQuestions.questionType == 3" class="answer-box">
  277 + <span
  278 + class="answer-s"
  279 + v-for="option in subQuestions.answerOptions.split(',')"
  280 + :class="
  281 + subQuestions.correctAnswer?.includes(option)
  282 + ? 'active'
  283 + : ''
  284 + "
  285 + :key="option"
  286 + @click="changAnswer(subQuestions, option)"
  287 + >{{ option }}</span
  288 + >
  289 + </p>
  290 + <p v-if="subQuestions.questionType == 2" class="answer-box">
  291 + <span
  292 + class="answer-s"
  293 + v-for="option in subQuestions.answerOptions.split(',')"
  294 + :class="
  295 + subQuestions.correctAnswer == option ? 'active' : ''
  296 + "
  297 + :key="option"
  298 + @click="subQuestions.correctAnswer = option"
  299 + >{{ option }}</span
  300 + >
  301 + </p>
  302 + <p
  303 + v-if="
  304 + subQuestions.questionType == 3 ||
  305 + subQuestions.questionType == 2
  306 + "
  307 + class="answer-box"
  308 + >
  309 + <el-button
  310 + size="mini"
  311 + type="primary"
  312 + icon="el-icon-plus"
  313 + circle
  314 + @click="addOptions(subQuestions)"
  315 + ></el-button>
  316 + <el-button
  317 + size="mini"
  318 + type="primary"
  319 + icon="el-icon-minus"
  320 + round
  321 + circle
  322 + @click="removeOptions(subQuestions)"
  323 + ></el-button>
  324 + </p>
  325 + </div>
  326 + <div class="qs-set">
  327 + <el-tooltip effect="dark" content="上传提干" placement="top">
  328 + <el-button
  329 + type="primary"
  330 + circle
  331 + size="mini"
  332 + icon="el-icon-tickets"
  333 + @click="openStem(subQuestions, 1)"
  334 + ></el-button>
  335 + </el-tooltip>
  336 + <el-tooltip effect="dark" content="题目打标" placement="top">
  337 + <el-button
  338 + type="primary"
  339 + circle
  340 + size="mini"
  341 + icon="el-icon-price-tag"
  342 + @click="openTag(subQuestions)"
  343 + ></el-button>
  344 + </el-tooltip>
  345 + <el-tooltip
  346 + effect="dark"
  347 + content="上传题目解析"
  348 + placement="top"
  349 + >
  350 + <el-button
  351 + type="primary"
  352 + circle
  353 + size="mini"
  354 + icon="el-icon-upload"
  355 + @click="openStem(subQuestions, 2)"
  356 + ></el-button>
  357 + </el-tooltip>
  358 + <el-popconfirm
  359 + title="确定删除这道题吗?"
  360 + @confirm="delTabData(indexs, index)"
  361 + >
  362 + <el-button
  363 + slot="reference"
  364 + class="delete"
  365 + type="danger"
  366 + size="mini"
  367 + circle
  368 + icon="el-icon-delete"
  369 + ></el-button>
  370 + </el-popconfirm>
  371 + </div>
  372 + </li>
  373 + <li class="sub-questions">
  374 + <div class="qs-num">添加</div>
  375 + <div class="qs-type">
  376 + <el-select
  377 + v-model="addSubQuestionsType"
  378 + placeholder="选择题目类型"
  379 + @change="changeAddSubQuestions($event, question)"
  380 + >
  381 + <el-option label="单选题" :value="2"></el-option>
  382 + <el-option label="多选题" :value="3"></el-option>
  383 + <el-option label="判断题" :value="4"></el-option>
  384 + <el-option label="主观题" :value="5"></el-option>
  385 + </el-select>
  386 + </div>
  387 + <div class="qs-score"></div>
  388 + <div class="qs-partScore"></div>
  389 + <div class="qs-options"></div>
  390 + <div class="qs-set"></div>
  391 + </li>
  392 + </ul>
  393 + </transition>
  394 + </div>
  395 + <div class="add-box">
  396 + <p class="add-question" @click="openQuestion">
  397 + <el-button
  398 + size="mini"
  399 + type="primary"
  400 + icon="el-icon-plus"
  401 + circle
  402 + ></el-button>
  403 + <span class="s1">添加大题</span>
  404 + </p>
  405 + </div>
  406 + <div class="btn-box">
  407 + <el-button type="danger" plain round @click="linkBack"
  408 + >取消</el-button
  409 + >
  410 + <el-button round @click="step = 0">上一步</el-button>
  411 + <el-button
  412 + :type="form.questionList.length == 0 ? 'info' : 'primary'"
  413 + round
  414 + @click="setStep2"
  415 + >下一步</el-button
  416 + >
  417 + </div>
  418 + <el-dialog
  419 + title="添加大题"
  420 + :visible.sync="addQuestionVisible"
  421 + :close-on-click-modal="false"
  422 + width="600px"
  423 + >
  424 + <div class="dia-content">
  425 + <el-form
  426 + ref="form"
  427 + :model="questionForm"
  428 + :rules="questionFormRules"
  429 + label-width="100px"
  430 + >
  431 + <el-form-item label="标题:">
  432 + <el-col :span="20">
  433 + <el-input
  434 + v-model.trim="questionForm.questionTitle"
  435 + maxlength="30"
  436 + placeholder="输入大题名称"
  437 + ></el-input>
  438 + </el-col>
  439 + </el-form-item>
  440 + <el-form-item label="选择题型:">
  441 + <el-select
  442 + v-model="questionForm.questionType"
  443 + placeholder=""
  444 + @change="setQuestionForm"
  445 + >
  446 + <el-option
  447 + v-for="item in questionOptions"
  448 + :key="item.value"
  449 + :label="item.label"
  450 + :value="item.value"
  451 + >
  452 + </el-option>
  453 + <el-option label="混合题" :value="6"> </el-option>
  454 + </el-select>
  455 + </el-form-item>
  456 + <el-form-item label="题数:">
  457 + <el-input-number
  458 + v-model="questionForm.number"
  459 + @change="changeQesNum"
  460 + :min="1"
  461 + :max="100"
  462 + :step-strictly="true"
  463 + :step="1"
  464 + label="label"
  465 + ></el-input-number>
  466 + </el-form-item>
  467 + <el-form-item
  468 + label="选项个数:"
  469 + v-show="
  470 + questionForm.questionType != 4 &&
  471 + questionForm.questionType != 5
  472 + "
  473 + >
  474 + <el-input-number
  475 + v-model="questionForm.selectNum"
  476 + :min="3"
  477 + :max="7"
  478 + :step-strictly="true"
  479 + :step="1"
  480 + label="label"
  481 + ></el-input-number>
  482 + </el-form-item>
  483 + <el-form-item label="单题分数:">
  484 + <el-input-number
  485 + v-model="questionForm.score"
  486 + :min="1"
  487 + :max="200"
  488 + :precision="2"
  489 + :step="1"
  490 + label="label"
  491 + ></el-input-number>
  492 + </el-form-item>
  493 + <el-form-item
  494 + label="漏选得分:"
  495 + v-if="questionForm.questionType == 3"
  496 + >
  497 + <el-input-number
  498 + v-model="questionForm.partScore"
  499 + :min="0"
  500 + :max="questionForm.score"
  501 + :precision="2"
  502 + :step="0.5"
  503 + label="label"
  504 + ></el-input-number>
  505 + </el-form-item>
  506 + <el-form-item
  507 + label="设置答案:"
  508 + v-show="questionForm.questionType != 5"
  509 + >
  510 + <div class="qs-options">
  511 + <p class="ipt">
  512 + <el-input
  513 + v-if="
  514 + questionForm.questionType == 2 ||
  515 + questionForm.questionType == 3 ||
  516 + questionForm.questionType == 6
  517 + "
  518 + ref="formAnsIpt2"
  519 + v-model="questionForm.answerList"
  520 + @keydown.native="
  521 + keydownAnswer($event, questionForm.questionType, 1)
  522 + "
  523 + @input="
  524 + setAllAnswer($event, questionForm.questionType, 1)
  525 + "
  526 + ></el-input>
  527 + <el-input
  528 + v-if="questionForm.questionType == 4"
  529 + v-model="questionForm.answerList"
  530 + readonly=""
  531 + ></el-input>
  532 + </p>
  533 + <p class="answer-box">
  534 + <template v-if="questionForm.questionType == 4">
  535 + <span
  536 + class="answer-s active"
  537 + @click="
  538 + questionForm.answerList.length < questionForm.number
  539 + ? (questionForm.answerList += '✓')
  540 + : ''
  541 + "
  542 + >✓</span
  543 + >
  544 + <span
  545 + class="answer-s active"
  546 + @click="
  547 + questionForm.answerList.length < questionForm.number
  548 + ? (questionForm.answerList += '✗')
  549 + : ''
  550 + "
  551 + >✗</span
  552 + >
  553 + </template>
  554 + <template v-if="questionForm.questionType == 3">
  555 + <template v-for="(option, opIdx) in rightOptions">
  556 + <span
  557 + v-if="opIdx < questionForm.selectNum"
  558 + class="answer-s active"
  559 + :key="option"
  560 + @click="setMultiple(questionForm, option, 1)"
  561 + >{{ option }}</span
  562 + >
  563 + </template>
  564 + <span
  565 + class="answer-s active"
  566 + @click="setMultiple(questionForm, ',', 1)"
  567 + >,</span
  568 + >
  569 + </template>
  570 + <template
  571 + v-if="
  572 + questionForm.questionType == 2 ||
  573 + questionForm.questionType == 6
  574 + "
  575 + class="answer-box"
  576 + >
  577 + <template v-for="(option, opIdx) in rightOptions">
  578 + <span
  579 + v-if="opIdx < questionForm.selectNum"
  580 + class="answer-s active"
  581 + :key="option"
  582 + @click="
  583 + questionForm.answerList.length < questionForm.number
  584 + ? (questionForm.answerList += option)
  585 + : ''
  586 + "
  587 + >{{ option }}</span
  588 + >
  589 + </template>
  590 + </template>
  591 + <span
  592 + class="answer-s delButton"
  593 + @click="
  594 + questionForm.answerList = questionForm.answerList.slice(
  595 + 0,
  596 + -1
  597 + )
  598 + "
  599 + >x</span
  600 + >
  601 + <span
  602 + class="answer-s ac"
  603 + @click="questionForm.answerList = ''"
  604 + >ac</span
  605 + >
  606 + </p>
  607 + </div>
  608 + </el-form-item>
  609 + </el-form>
  610 + </div>
  611 + <div class="dialog-footer" slot="footer">
  612 + <el-button type="primary" @click="addQuestion">确 定</el-button>
  613 + <el-button @click="addQuestionVisible = false">取 消</el-button>
  614 + </div>
  615 + </el-dialog>
  616 + </div>
  617 + <div v-show="step == 2">
  618 + <div class="answer-title">
  619 + <p class="name">{{ form.title }}</p>
  620 + <p class="totals">卷面总分:{{ allScore }}分</p>
  621 + </div>
  622 + <div
  623 + class="question-box"
  624 + v-for="(question, index) in form.questionList"
  625 + :key="index"
  626 + >
  627 + <p class="question-title">
  628 + <span>{{ setBigNum(index) }}、</span>
  629 + <span class="title-txt">{{ question.questionTitle }}</span>
  630 + <span class="m20">共:{{ setNums(question.subQuestions) }}题</span>
  631 + <span>共:{{ setScore(question) }} 分</span>
  632 + </p>
  633 + <ul class="questions-ul">
  634 + <li class="sub-questions">
  635 + <div class="qs-num">题号</div>
  636 + <div class="qs-type">题型</div>
  637 + <div class="qs-score">分数</div>
  638 + <div class="qs-partScore">漏选得分</div>
  639 + <div class="qs-options qs-options2">选项设置</div>
  640 + </li>
  641 + <li
  642 + v-for="(subQuestions, indexs) in question.subQuestions"
  643 + :key="indexs"
  644 + >
  645 + <p
  646 + class="set-ans-btn"
  647 + v-if="
  648 + subQuestions.qusType &&
  649 + subQuestions.subNum &&
  650 + subQuestions.subNum > 4
  651 + "
  652 + >
  653 + <el-button type="primary" @click="setFormAns(indexs, index)"
  654 + >批量设置答案</el-button
  655 + >
  656 + </p>
  657 + <div v-else class="sub-questions">
  658 + <div class="qs-num">
  659 + {{ setNum(index, indexs, subQuestions) }}
  660 + </div>
  661 + <div class="qs-type">
  662 + {{ setSubPro(subQuestions.questionType) }}
  663 + </div>
  664 + <div class="qs-score">
  665 + <el-input-number
  666 + class="number-ipt"
  667 + size="medium"
  668 + :min="1"
  669 + :max="200"
  670 + :precision="2"
  671 + v-model="subQuestions.score"
  672 + label="单题分值"
  673 + ></el-input-number>
  674 + </div>
  675 + <div class="qs-partScore">
  676 + <p v-if="subQuestions.questionType != 3">--</p>
  677 + <el-input-number
  678 + class="number-ipt"
  679 + v-else
  680 + size="medium"
  681 + :min="0"
  682 + :precision="2"
  683 + :max="subQuestions.score"
  684 + :step="0.5"
  685 + v-model="subQuestions.partScore"
  686 + label="漏选得分"
  687 + ></el-input-number>
  688 + </div>
  689 + <div class="qs-options qs-options2">
  690 + <p v-if="subQuestions.questionType == 5">--</p>
  691 + <p v-if="subQuestions.questionType == 4" class="answer-box">
  692 + <span
  693 + class="answer-s"
  694 + :class="subQuestions.correctAnswer == 1 ? 'active' : ''"
  695 + @click="subQuestions.correctAnswer = 1"
  696 + >✓</span
  697 + >
  698 + <span
  699 + class="answer-s"
  700 + :class="subQuestions.correctAnswer == 2 ? 'active' : ''"
  701 + @click="subQuestions.correctAnswer = 2"
  702 + >✗</span
  703 + >
  704 + </p>
  705 + <p v-if="subQuestions.questionType == 3" class="answer-box">
  706 + <span
  707 + class="answer-s"
  708 + v-for="option in subQuestions.answerOptions.split(',')"
  709 + :class="
  710 + subQuestions.correctAnswer?.includes(option)
  711 + ? 'active'
  712 + : ''
  713 + "
  714 + :key="option"
  715 + @click="changAnswer(subQuestions, option)"
  716 + >{{ option }}</span
  717 + >
  718 + </p>
  719 + <p v-if="subQuestions.questionType == 2" class="answer-box">
  720 + <span
  721 + class="answer-s"
  722 + v-for="option in subQuestions.answerOptions.split(',')"
  723 + :class="
  724 + subQuestions.correctAnswer == option ? 'active' : ''
  725 + "
  726 + :key="option"
  727 + @click="subQuestions.correctAnswer = option"
  728 + >{{ option }}</span
  729 + >
  730 + </p>
  731 + </div>
  732 + </div>
  733 + </li>
  734 + </ul>
  735 + </div>
  736 + <el-dialog
  737 + title="批量设置答案"
  738 + :visible.sync="diaSetAns"
  739 + :close-on-click-modal="false"
  740 + width="400"
  741 + :modal-append-to-body="false"
  742 + >
  743 + <div class="qs-options">
  744 + <p class="dia-tips">
  745 + 请点击选项按钮设置答案,多选题题目之间用“,”隔开,若添加5道题:“AC,AD,BD,AC,CD”
  746 + </p>
  747 + <p>{{ setSubPro(formAns.qusType) }}:</p>
  748 + <p class="ipt">
  749 + <el-input
  750 + v-if="formAns.qusType == 2 || formAns.qusType == 3"
  751 + ref="formAnsIpt"
  752 + v-model="formAns.answerList"
  753 + @keydown.native="keydownAnswer($event, formAns.qusType)"
  754 + @input="setAllAnswer($event, formAns.qusType)"
  755 + ></el-input>
  756 + <el-input
  757 + v-if="formAns.qusType == 4"
  758 + v-model="formAns.answerList"
  759 + readonly=""
  760 + ></el-input>
  761 + </p>
  762 + <p class="answer-box">
  763 + <template v-if="formAns.qusType == 4">
  764 + <span
  765 + class="answer-s active"
  766 + @click="
  767 + formAns.answerList.length < formAns.subNum
  768 + ? (formAns.answerList += '✓')
  769 + : ''
  770 + "
  771 + >✓</span
  772 + >
  773 + <span
  774 + class="answer-s active"
  775 + @click="
  776 + formAns.answerList.length < formAns.subNum
  777 + ? (formAns.answerList += '✗')
  778 + : ''
  779 + "
  780 + >✗</span
  781 + >
  782 + </template>
  783 + <template v-if="formAns.qusType == 3">
  784 + <span
  785 + class="answer-s active"
  786 + v-for="option in formAns.answerOptions.split(',')"
  787 + :key="option"
  788 + @click="setMultiple(formAns, option, 2)"
  789 + >{{ option }}</span
  790 + >
  791 + <span
  792 + class="answer-s active"
  793 + @click="setMultiple(formAns, ',', 2)"
  794 + >,</span
  795 + >
  796 + </template>
  797 + <template v-if="formAns.qusType == 2">
  798 + <span
  799 + class="answer-s active"
  800 + v-for="option in formAns.answerOptions.split(',')"
  801 + :key="option"
  802 + @click="
  803 + formAns.answerList.length < formAns.subNum
  804 + ? (formAns.answerList += option)
  805 + : ''
  806 + "
  807 + >{{ option }}</span
  808 + >
  809 + </template>
  810 + <span
  811 + class="answer-s delButton"
  812 + @click="formAns.answerList = formAns.answerList.slice(0, -1)"
  813 + >x</span
  814 + >
  815 + <span class="answer-s ac" @click="formAns.answerList = ''"
  816 + >ac</span
  817 + >
  818 + </p>
  819 + </div>
  820 + <div class="dialog-footer" slot="footer">
  821 + <el-button @click="saveFormAns">确 定</el-button>
  822 + <el-button @click="diaSetAns = false">取 消</el-button>
  823 + </div>
  824 + </el-dialog>
  825 + <div class="btn-box">
  826 + <el-button type="danger" plain round @click="linkBack"
  827 + >取消</el-button
  828 + >
  829 + <el-button round @click="backStep1">上一步</el-button>
  830 + <el-button type="primary" round @click="save">保存</el-button>
  831 + </div>
  832 + </div>
  833 + <el-dialog
  834 + :title="upLoadType == 1 ? '上传题干' : '上传题目解析'"
  835 + :visible.sync="dialogStem"
  836 + width="500"
  837 + >
  838 + <div>
  839 + <img class="stem-pic" :src="stem.src" alt="" />
  840 + <el-upload
  841 + class="upload-demo"
  842 + action="https://jsonplaceholder.typicode.com/posts/"
  843 + :limit="1"
  844 + :with-credentials="true"
  845 + :on-success="upSuccess"
  846 + :on-error="upError"
  847 + accept="image/*"
  848 + >
  849 + <el-button size="small" type="primary">{{
  850 + stem.src ? "重新上传" : "选择照片"
  851 + }}</el-button>
  852 + </el-upload>
  853 + </div>
  854 + <div slot="footer">
  855 + <el-button @click="dialogStem = false">关闭弹窗</el-button>
  856 + </div>
  857 + </el-dialog>
  858 + <el-dialog title="题目打标" :visible.sync="dialogTag" width="500">
  859 + <div>
  860 + <el-form ref="form" :model="stem" label-width="160px">
  861 + <el-form-item label="题目难度:">
  862 + <el-select v-model="stem.type" placeholder="选择题目难度">
  863 + <el-option label="简单" :value="0"></el-option>
  864 + <el-option label="正常" :value="1"></el-option>
  865 + <el-option label="困难" :value="2"></el-option>
  866 + </el-select>
  867 + </el-form-item>
  868 + <el-form-item label="知识点:">
  869 + <el-select v-model="stem.tag" placeholder="选择知识点">
  870 + <el-option label="生字积累" :value="0"></el-option>
  871 + <el-option label="字词运用" :value="1"></el-option>
  872 + <el-option label="阅读理解" :value="2"></el-option>
  873 + <el-option label="作文" :value="2"></el-option>
  874 + </el-select>
  875 + </el-form-item>
  876 + </el-form>
  877 + </div>
  878 + <div slot="footer">
  879 + <el-button @click="dialogTag = false">取 消</el-button>
  880 + <el-button type="primary" @click="dialogTag = false">确 定</el-button>
  881 + </div>
  882 + </el-dialog>
  883 + </div>
  884 + </div>
  885 +</template>
  886 +
  887 +<script>
  888 +import { deepClone, checkAnswer } from "utils";
  889 +const questionForm = {
  890 + questionTitle: "",
  891 + questionType: 2,
  892 + number: 10,
  893 + selectNum: 4,
  894 + score: 1,
  895 + partScore: 0,
  896 + answerList: "",
  897 +};
  898 +const subQuesOptions = {
  899 + questionType: 2,
  900 + score: 1,
  901 + partScore: 0,
  902 + selectNum: 4,
  903 + answerOptions: "A,B,C,D",
  904 + correctAnswer: "",
  905 +};
  906 +export default {
  907 + computed: {
  908 + allScore: function () {
  909 + let score = 0;
  910 + this.form.questionList.map((item) => {
  911 + score += item.subQuestions.reduce((a, b) => {
  912 + return a + Number(b.score ? b.score : 0);
  913 + }, 0);
  914 + }, 0);
  915 + return Number(score).toFixed(2);
  916 + },
  917 + },
  918 + watch: {
  919 + step: function () {
  920 + this.$nextTick(function () {
  921 + this.$refs.content.scrollTop = 0;
  922 + });
  923 + },
  924 + },
  925 + data() {
  926 + return {
  927 + dialogStem: false,
  928 + dialogTag: false,
  929 + upLoadType: 1,
  930 + stem: {
  931 + src: "", //题干图片地址
  932 + type: 0, //题目难度
  933 + tag: 0, //知识点
  934 + },
  935 + type: 1, //1-创建,2-复制答题卡
  936 + dialogVisible: false, //测验类型设置弹窗
  937 + addQuestionVisible: false, //添加大题弹窗
  938 + questionForm: { ...questionForm },
  939 + questionFormRules: {
  940 + questionTitle: [
  941 + { required: true, message: "请输入大题名称", trigger: "blur" },
  942 + {
  943 + min: 1,
  944 + max: 30,
  945 + message: "长度在 1 到 30 个字符",
  946 + trigger: "blur",
  947 + },
  948 + ],
  949 + },
  950 + questionOptions: [
  951 + { label: "单选题", value: 2 },
  952 + { label: "多选题", value: 3 },
  953 + { label: "判断题", value: 4 },
  954 + { label: "主观题", value: 5 },
  955 + ],
  956 + rightOptions: ["A", "B", "C", "D", "E", "F", "G"],
  957 + addSubQuestionsType: "",
  958 + step: 0, //步骤
  959 + gradeList: [], //年级
  960 + subjectList: [], //科目
  961 + answerTypeList: [], //测验类型
  962 + answerTypeName: "", //测验类型名称
  963 + form: {
  964 + //答题卡详情
  965 + title: "",
  966 + tagId: "",
  967 + subjectName: "",
  968 + examsDuration: 90,
  969 + questionList: [],
  970 + },
  971 + formRules: {
  972 + //答题卡验证
  973 + title: [
  974 + { required: true, message: "请输入答题卡名称", trigger: "blur" },
  975 + {
  976 + min: 1,
  977 + max: 30,
  978 + message: "长度在 1 到 30 个字符",
  979 + trigger: "blur",
  980 + },
  981 + ],
  982 + subjectName: [
  983 + { required: true, message: "请选择科目", trigger: "blur" },
  984 + ],
  985 + examsDuration: [
  986 + { required: true, message: "请设置考试时长", trigger: "blur" },
  987 + ],
  988 + },
  989 + tagList: [],
  990 + diaSetAns: false,
  991 + formAns: {
  992 + listIndex: 0, //大题位置
  993 + endIndex: 0, //相同题目最后一位题目的questionIndex
  994 + index: 0, //相同题目最后一位题目的位置
  995 + qusType: "", //题目类型
  996 + subNum: 0, //数量
  997 + answerOptions: [], //答案选项
  998 + answerList: "", //答案列表-字符串
  999 + },
  1000 + };
  1001 + },
  1002 + async created() {
  1003 + this.type = this.$route.query.type ? this.$route.query.type : 1;
  1004 + await this._QuerySubjectList(this.gradeList[0]);
  1005 + await this._TypeList();
  1006 + if (this.type == 2) {
  1007 + this._QueryDetail();
  1008 + }
  1009 + },
  1010 + methods: {
  1011 + openStem(obj, type) {
  1012 + this.upLoadType = type;
  1013 + this.stem = { ...this.stem, obj };
  1014 + this.dialogStem = true;
  1015 + },
  1016 + openTag(obj) {
  1017 + this.stem = { ...this.stem, obj };
  1018 + this.dialogTag = true;
  1019 + },
  1020 + upSuccess(res) {
  1021 + if (res && res.status == 0) {
  1022 + this.$message.success("上传成功");
  1023 + } else {
  1024 + this.$message.error(res.info);
  1025 + }
  1026 + },
  1027 + upError(res) {
  1028 + if (res && res.status == 0) {
  1029 + this.$message.error("上传失败");
  1030 + } else {
  1031 + this.$message.error(res.message);
  1032 + }
  1033 + },
  1034 + linkBack() {
  1035 + this.$confirm(
  1036 + (this.type == 2 ? "修改复制的" : "组建的") +
  1037 + "答题卡未保存,确认退出吗?",
  1038 + "提示",
  1039 + {
  1040 + confirmButtonText: "取消",
  1041 + cancelButtonText: "确定",
  1042 + confirmButtonClass: "el-button--danger1",
  1043 + cancelButtonClass: "el-button--primary",
  1044 + showClose: false,
  1045 + roundButton: true,
  1046 + center: true,
  1047 + type: "warning",
  1048 + }
  1049 + )
  1050 + .then(() => {})
  1051 + .catch(() => {
  1052 + this.$router.push({
  1053 + path: "/examinationPaper",
  1054 + });
  1055 + });
  1056 + },
  1057 + setSubPro(type) {
  1058 + let tit;
  1059 + switch (type) {
  1060 + case 2:
  1061 + tit = "单选题";
  1062 + break;
  1063 + case 3:
  1064 + tit = "多选题";
  1065 + break;
  1066 + case 4:
  1067 + tit = "判断题";
  1068 + break;
  1069 + case 5:
  1070 + tit = "主观题";
  1071 + break;
  1072 + }
  1073 + return tit;
  1074 + },
  1075 + setNum(index, indexs, sub) {
  1076 + let lengths = 0;
  1077 + let subIndex = 0;
  1078 + for (let i = 0; i < index; i++) {
  1079 + let subArr = this.form.questionList[i].subQuestions.filter((item) => {
  1080 + return !!item.questionType;
  1081 + });
  1082 + lengths += subArr.length;
  1083 + }
  1084 +
  1085 + for (let i = 0; i < indexs; i++) {
  1086 + if (!!this.form.questionList[index].subQuestions[i].questionType) {
  1087 + subIndex += 1;
  1088 + }
  1089 + }
  1090 + return lengths + subIndex + 1;
  1091 + },
  1092 + setNums(ques) {
  1093 + let lengths = 0;
  1094 + let subArr = ques.filter((item) => {
  1095 + return !!item.questionType;
  1096 + });
  1097 + lengths = subArr.length;
  1098 + return lengths;
  1099 + },
  1100 + setBigNum(num) {
  1101 + let txt = "";
  1102 + let bigNum = [
  1103 + "一",
  1104 + "二",
  1105 + "三",
  1106 + "四",
  1107 + "五",
  1108 + "六",
  1109 + "七",
  1110 + "八",
  1111 + "九",
  1112 + "十",
  1113 + "十一",
  1114 + "十二",
  1115 + "十三",
  1116 + "十四",
  1117 + "十五",
  1118 + "十六",
  1119 + "十七",
  1120 + "十八",
  1121 + "十九",
  1122 + "二十",
  1123 + ];
  1124 + txt = bigNum[num];
  1125 +
  1126 + return txt;
  1127 + },
  1128 + setQuestionForm(val) {
  1129 + //切换题型清空答案
  1130 + this.questionForm.answerList = "";
  1131 + },
  1132 + changeQesNum(val) {
  1133 + //减少题数设置答案
  1134 + if (
  1135 + this.questionForm.questionType == 2 ||
  1136 + this.questionForm.questionType == 4
  1137 + ) {
  1138 + this.questionForm.answerList = this.questionForm.answerList.substring(
  1139 + 0,
  1140 + val
  1141 + );
  1142 + } else {
  1143 + console.log(this.questionForm.answerList.split(","));
  1144 + this.questionForm.answerList = this.questionForm.answerList
  1145 + .split(",")
  1146 + .splice(0, val)
  1147 + .join(",");
  1148 + }
  1149 + },
  1150 + setFormAns(indexs, index) {
  1151 + //初始化要修改的答案
  1152 + this.formAns = { ...this.form.questionList[index].subQuestions[indexs] };
  1153 + this.formAns.listIndex = index;
  1154 + let startIndex = this.formAns.index + 1 - this.formAns.subNum; //批量设置大难开始位置
  1155 + this.formAns.answerList = [];
  1156 + let answerList = "";
  1157 + this.form.questionList[index].subQuestions.map((item, subIdx) => {
  1158 + if (subIdx >= startIndex) {
  1159 + answerList += this.setAnswer(item.questionType, item.correctAnswer);
  1160 + if (subIdx != indexs) {
  1161 + if (!!item.qusType) {
  1162 + answerList = "";
  1163 + }
  1164 + } else {
  1165 + if (item.qusType == 3) {
  1166 + answerList = answerList.slice(0, -1);
  1167 + }
  1168 + this.formAns.answerList = answerList;
  1169 + }
  1170 + }
  1171 + });
  1172 +
  1173 + this.diaSetAns = true;
  1174 + },
  1175 + insertTxtAndSetcursor(element, answerList, str) {
  1176 + let startPos = element.selectionStart; // 获取光标开始的位置
  1177 + if (startPos === undefined) {
  1178 + // 如果没有光标位置 不操作
  1179 + return answerList;
  1180 + } else {
  1181 + return {
  1182 + text:
  1183 + answerList.substring(0, startPos) +
  1184 + str +
  1185 + answerList.substring(startPos), // 将文本插入
  1186 + startPos: startPos + str.length,
  1187 + };
  1188 + }
  1189 + },
  1190 + setMultiple(obj, answer, type) {
  1191 + //多选答案设置
  1192 + let elements =
  1193 + type == 1
  1194 + ? this.$refs["formAnsIpt2"].$el.children[0]
  1195 + : this.$refs["formAnsIpt"].$el.children[0];
  1196 + let resault = this.insertTxtAndSetcursor(
  1197 + elements,
  1198 + obj.answerList || "",
  1199 + answer
  1200 + );
  1201 + obj.answerList = resault.text;
  1202 + let str = obj.answerList;
  1203 + let str2;
  1204 + if (!!obj.answerOptions) {
  1205 + str2 = checkAnswer(
  1206 + str,
  1207 + 3,
  1208 + obj.answerOptions.split(",").length,
  1209 + obj.subNum
  1210 + );
  1211 + } else {
  1212 + str2 = checkAnswer(str, 3, obj.selectNum, obj.number);
  1213 + }
  1214 + obj.answerList = str2;
  1215 + elements.focus();
  1216 + elements.selectionStart = resault.startPos;
  1217 + },
  1218 + saveFormAns() {
  1219 + //批量修改答案
  1220 + let EndIndex;
  1221 + let subNum = this.formAns.subNum - 1;
  1222 + this.form.questionList[this.formAns.listIndex].subQuestions.some(
  1223 + (item, index) => {
  1224 + if (this.formAns.endIndex == item.questionIndex) {
  1225 + EndIndex = index;
  1226 + return;
  1227 + }
  1228 + }
  1229 + );
  1230 +
  1231 + for (let i = 0; i <= subNum; i++) {
  1232 + let correctAnswer = "";
  1233 + if (this.formAns.qusType == 2) {
  1234 + correctAnswer = this.formAns.answerList[subNum - i] || "";
  1235 + } else if (this.formAns.qusType == 3) {
  1236 + correctAnswer = this.formAns.answerList.split(",")[subNum - i] || "";
  1237 + } else if (this.formAns.qusType == 4) {
  1238 + correctAnswer =
  1239 + this.formAns.answerList[subNum - i] == "✓"
  1240 + ? 1
  1241 + : this.formAns.answerList[subNum - i] == "✗"
  1242 + ? 2
  1243 + : "";
  1244 + }
  1245 + this.form.questionList[this.formAns.listIndex].subQuestions[
  1246 + EndIndex - i
  1247 + ].correctAnswer = correctAnswer;
  1248 + }
  1249 + this.diaSetAns = false;
  1250 + },
  1251 + keydownAnswer(event, type, isAddBig) {
  1252 + let answerA = "ABCDEFG";
  1253 + let answer_a = "abcdefg";
  1254 + if (isAddBig) {
  1255 + answerA = answerA.substring(0, this.questionForm.selectNum);
  1256 + answer_a = answer_a.substring(0, this.questionForm.selectNum);
  1257 + } else {
  1258 + answerA = answerA.substring(0, this.formAns.subNum);
  1259 + answer_a = answer_a.substring(0, this.formAns.subNum);
  1260 + }
  1261 + answerA += answer_a;
  1262 + answerA = type == 2 ? answerA : answerA + ",";
  1263 + if (
  1264 + event.key == "Meta" ||
  1265 + event.key == "CapsLock" ||
  1266 + event.key == "Shift" ||
  1267 + event.key == "Enter" ||
  1268 + event.key == "Alt" ||
  1269 + event.key == "Backspace" ||
  1270 + event.key == "Delete" ||
  1271 + event.key == "ArrowUp" ||
  1272 + event.key == "ArrowDown" ||
  1273 + event.key == "ArrowLeft" ||
  1274 + event.key == "v" ||
  1275 + event.key == "V" ||
  1276 + event.key == "ArrowRight"
  1277 + )
  1278 + return;
  1279 + if (!answerA.includes(event.key)) {
  1280 + event.returnValue = "";
  1281 + }
  1282 + },
  1283 + setAllAnswer(event, type, isAddBig) {
  1284 + if (isAddBig) {
  1285 + let str = this.questionForm.answerList;
  1286 + let str2 = checkAnswer(
  1287 + str,
  1288 + type,
  1289 + this.questionForm.selectNum,
  1290 + this.questionForm.number
  1291 + );
  1292 + this.questionForm.answerList = str2;
  1293 + } else {
  1294 + let str = this.formAns.answerList;
  1295 + let str2 = checkAnswer(
  1296 + str,
  1297 + type,
  1298 + this.formAns.answerOptions.split(",").length,
  1299 + this.formAns.subNum
  1300 + );
  1301 + this.formAns.answerList = str2;
  1302 + }
  1303 + },
  1304 + setAnswer(type, ans) {
  1305 + let txt = "";
  1306 + if (type == 2) {
  1307 + txt = ans;
  1308 + } else if (type == 3) {
  1309 + txt = ans + ",";
  1310 + } else if (type == 4) {
  1311 + txt = ans == 1 ? "✓" : ans == 2 ? "✗" : "";
  1312 + }
  1313 + return txt;
  1314 + },
  1315 + backStep1() {
  1316 + this.formatQuestionList();
  1317 + this.step = 1;
  1318 + },
  1319 + setStep1() {
  1320 + this.$refs["forms"].validate((valid) => {
  1321 + // 验证通过:保存
  1322 + if (valid) {
  1323 + this.step = 1;
  1324 + } else {
  1325 + this.$message.error("数据有误,请检查!");
  1326 + return false;
  1327 + }
  1328 + });
  1329 + },
  1330 + setStep2() {
  1331 + if (!this.form.questionList.length) {
  1332 + this.$message.warning("请添加题目!");
  1333 + return;
  1334 + }
  1335 + //添加题目ID、序号
  1336 + this.form.questionList.map((item, index) => {
  1337 + item.questionType = 0;
  1338 + item.subQuestions.map((items, indexs) => {
  1339 + items.questionId = this.setNum(index, indexs);
  1340 + items.questionIndex = this.setNum(index, indexs);
  1341 + });
  1342 + });
  1343 + //整理问题
  1344 + this.form.questionList?.map((item) => {
  1345 + let types = [{}];
  1346 + let addndex = 0;
  1347 + item.subQuestions.map((sub, index) => {
  1348 + if (!!sub.questionType) {
  1349 + if (
  1350 + sub.questionType == types[addndex].qusType &&
  1351 + sub.questionType != 5
  1352 + ) {
  1353 + //同类型批量答案+1
  1354 + types[addndex].subNum += 1;
  1355 + if (
  1356 + types[addndex].answerOptions.length < sub.answerOptions.length
  1357 + ) {
  1358 + types[addndex].answerOptions = sub.answerOptions;
  1359 + }
  1360 +
  1361 + // types[addndex].answerList += this.setAnswer(
  1362 + // sub.questionType,
  1363 + // sub.correctAnswer
  1364 + // );
  1365 + types[addndex].answerList = "";
  1366 + if (index == item.subQuestions.length - 1) {
  1367 + //循环最后类型数量大于等于5,保存批量答案
  1368 + if (types[addndex].subNum && types[addndex].subNum >= 5) {
  1369 + types[addndex].endIndex = sub.questionIndex;
  1370 + types[addndex].index = index;
  1371 + }
  1372 + }
  1373 + } else {
  1374 + if (types[addndex].subNum && types[addndex].subNum >= 5) {
  1375 + //不同类型时如果原有类型数量大于等于5,保存批量答案
  1376 + types[addndex].endIndex =
  1377 + item.subQuestions[index - 1].questionIndex;
  1378 + types[addndex].index = index - 1;
  1379 + addndex += 1;
  1380 + types[addndex] = {};
  1381 + }
  1382 + //不同类型初始化批量答案
  1383 + types[addndex].qusType = sub.questionType;
  1384 + types[addndex].subNum = 1;
  1385 + types[addndex].answerOptions = sub.answerOptions;
  1386 + types[addndex].answerList = "";
  1387 + }
  1388 + }
  1389 + });
  1390 + for (let i = 0; i < types.length; i++) {
  1391 + if (types[i].subNum >= 5) {
  1392 + item.subQuestions.splice(
  1393 + types[i].index + i + 1,
  1394 + 0,
  1395 + deepClone(types[i])
  1396 + );
  1397 + }
  1398 + }
  1399 + console.log(types);
  1400 + });
  1401 + this.step = 2;
  1402 + return;
  1403 + },
  1404 + formatQuestionList() {
  1405 + for (let i = 0; i < this.form.questionList.length; i++) {
  1406 + for (
  1407 + let j = 0;
  1408 + j < this.form.questionList[i].subQuestions.length;
  1409 + j++
  1410 + ) {
  1411 + if (this.form.questionList[i].subQuestions[j].qusType) {
  1412 + this.form.questionList[i].subQuestions.splice(j, 1);
  1413 + }
  1414 + }
  1415 + }
  1416 + },
  1417 + openQuestion() {
  1418 + this.questionForm = { ...questionForm };
  1419 + this.addQuestionVisible = true;
  1420 + },
  1421 + addQuestion() {
  1422 + //添加大题
  1423 + let subQuestions = [];
  1424 + let questionsOptions = {
  1425 + ...subQuesOptions,
  1426 + selectNum: this.questionForm.selectNum,
  1427 + score: this.questionForm.score,
  1428 + partScore: this.questionForm.partScore,
  1429 + questionType:
  1430 + this.questionForm.questionType == 6
  1431 + ? 2
  1432 + : this.questionForm.questionType,
  1433 + };
  1434 + switch (questionsOptions.questionType) {
  1435 + case 2:
  1436 + questionsOptions.answerOptions = this.rightOptions
  1437 + .slice(0, questionsOptions.selectNum)
  1438 + .join(",");
  1439 + break;
  1440 + case 3:
  1441 + questionsOptions.answerOptions = this.rightOptions
  1442 + .slice(0, questionsOptions.selectNum)
  1443 + .join(",");
  1444 + break;
  1445 + case 4:
  1446 + questionsOptions.selectNum = 0;
  1447 + questionsOptions.answerOptions = "1,2";
  1448 + break;
  1449 + case 5:
  1450 + questionsOptions.selectNum = 0;
  1451 + break;
  1452 + }
  1453 + for (let i = 0; i < this.questionForm.number; i++) {
  1454 + let answer = "";
  1455 + if (questionsOptions.questionType == 4) {
  1456 + answer =
  1457 + this.questionForm.answerList[i] == "✓"
  1458 + ? 1
  1459 + : this.questionForm.answerList[i] == "✗"
  1460 + ? 2
  1461 + : "";
  1462 + } else if (questionsOptions.questionType == 3) {
  1463 + answer = this.questionForm.answerList.split(",")[i] || "";
  1464 + } else if (questionsOptions.questionType == 2) {
  1465 + answer = this.questionForm.answerList[i] || "";
  1466 + }
  1467 + questionsOptions.correctAnswer = answer;
  1468 + subQuestions.push({ ...questionsOptions });
  1469 + }
  1470 + this.form.questionList.push({
  1471 + questionTitle: this.questionForm.questionTitle,
  1472 + number: this.questionForm.number,
  1473 + source: 10,
  1474 + subQuestions: [...subQuestions],
  1475 + show: false,
  1476 + });
  1477 + this.addQuestionVisible = false;
  1478 + },
  1479 + delTabData(subIndex, index) {
  1480 + //删除小题
  1481 + this.form.questionList[index].subQuestions.splice(subIndex, 1);
  1482 + },
  1483 + setScore(question) {
  1484 + let score = question.subQuestions.reduce((a, b) => {
  1485 + return a + (b.score ? b.score : 0);
  1486 + }, 0);
  1487 + return Number(score).toFixed(2);
  1488 + },
  1489 + changeAddSubQuestions(val, question) {
  1490 + if (val) {
  1491 + let questionsOptions = {
  1492 + ...subQuesOptions,
  1493 + questionType: val,
  1494 + };
  1495 + switch (questionsOptions.questionType) {
  1496 + case 2:
  1497 + questionsOptions.answerOptions = this.rightOptions
  1498 + .slice(0, questionsOptions.selectNum)
  1499 + .join(",");
  1500 + break;
  1501 + case 3:
  1502 + questionsOptions.answerOptions = this.rightOptions
  1503 + .slice(0, questionsOptions.selectNum)
  1504 + .join(",");
  1505 + questionsOptions.partScore = 0;
  1506 + break;
  1507 + case 4:
  1508 + questionsOptions.selectNum = 0;
  1509 + questionsOptions.answerOptions = "1,2";
  1510 + break;
  1511 + case 5:
  1512 + questionsOptions.selectNum = 0;
  1513 + break;
  1514 + }
  1515 + question.subQuestions.push(questionsOptions);
  1516 + this.addSubQuestionsType = "";
  1517 + }
  1518 + },
  1519 + changeSubQuestions(val, subQuestions) {
  1520 + //切换多题型-小题题型
  1521 + const that = this;
  1522 + subQuestions.score = 1;
  1523 + subQuestions.partScore = 0;
  1524 + subQuestions.correctAnswer = "";
  1525 + subQuestions.selectNum = 4;
  1526 + switch (val) {
  1527 + case 2:
  1528 + subQuestions.answerOptions = that.rightOptions
  1529 + .slice(0, subQuestions.selectNum)
  1530 + .join(",");
  1531 + break;
  1532 + case 3:
  1533 + subQuestions.answerOptions = that.rightOptions
  1534 + .slice(0, subQuestions.selectNum)
  1535 + .join(",");
  1536 + break;
  1537 + case 4:
  1538 + subQuestions.selectNum = 0;
  1539 + subQuestions.answerOptions = "1,2";
  1540 + break;
  1541 + case 5:
  1542 + subQuestions.selectNum = 0;
  1543 + break;
  1544 + }
  1545 + },
  1546 + addOptions(subQuestions) {
  1547 + //添加选项
  1548 + let length = subQuestions.answerOptions.split(",").length;
  1549 + if (length > 6) return;
  1550 + subQuestions.selectNum = length + 1;
  1551 + subQuestions.answerOptions = this.rightOptions
  1552 + .slice(0, subQuestions.selectNum)
  1553 + .join(",");
  1554 + },
  1555 + removeOptions(subQuestions) {
  1556 + //删除选项
  1557 + let length = subQuestions.answerOptions.split(",").length;
  1558 + if (length < 3) return;
  1559 + subQuestions.selectNum = length - 1;
  1560 + subQuestions.answerOptions = this.rightOptions
  1561 + .slice(0, subQuestions.selectNum)
  1562 + .join(",");
  1563 + subQuestions.correctAnswer = subQuestions.correctAnswer.slice(
  1564 + 0,
  1565 + subQuestions.selectNum
  1566 + );
  1567 + },
  1568 + changAnswer(sub, option) {
  1569 + //设置多选答案
  1570 + let str = new RegExp(option, "g");
  1571 + if (sub.correctAnswer?.includes(option)) {
  1572 + sub.correctAnswer = sub.correctAnswer.replace(str, "");
  1573 + } else {
  1574 + let arrs = (sub.correctAnswer && sub.correctAnswer.split("")) || [];
  1575 + arrs.push(option);
  1576 + sub.correctAnswer = arrs.sort().join("");
  1577 + }
  1578 + },
  1579 + openTagDia() {
  1580 + this.tagList = deepClone(this.answerTypeList);
  1581 + this.dialogVisible = true;
  1582 + },
  1583 + async editTypeName(obj) {
  1584 + let isRepeat = false;
  1585 + this.answerTypeList.map((item) => {
  1586 + if (item.typeName == obj.typeName) {
  1587 + isRepeat = true;
  1588 + }
  1589 + });
  1590 + if (isRepeat) {
  1591 + this.$message.error("类型已存在请重新填写!");
  1592 + return;
  1593 + }
  1594 + //修改测验类型
  1595 + const { data, status, info } = await this.$request.editPaperType({
  1596 + tagId: obj.id,
  1597 + tag: obj.typeName,
  1598 + });
  1599 + if (status == 0) {
  1600 + await this._TypeList();
  1601 + this.tagList = deepClone(this.answerTypeList);
  1602 + this.$message.success("修改成功");
  1603 + } else {
  1604 + this.$message.error(info);
  1605 + }
  1606 + },
  1607 + async removeTypeName(obj) {
  1608 + //删除测验类型
  1609 + const { data, status, info } = await this.$request.delPaperType({
  1610 + tagId: obj.id,
  1611 + });
  1612 + if (status == 0) {
  1613 + await this._TypeList();
  1614 + this.tagList = deepClone(this.answerTypeList);
  1615 + this.$message.success("删除成功");
  1616 + } else {
  1617 + this.$message.error(info);
  1618 + }
  1619 + },
  1620 + async addPaperType() {
  1621 + //保存测验类型
  1622 + if (!this.answerTypeName) {
  1623 + this.$message.error("请填写测验名称!");
  1624 + return;
  1625 + }
  1626 + let isRepeat = false;
  1627 + this.answerTypeList.map((item) => {
  1628 + if (item.typeName == this.answerTypeName) {
  1629 + isRepeat = true;
  1630 + }
  1631 + });
  1632 + if (isRepeat) {
  1633 + this.$message.error("类型已存在请重新填写!");
  1634 + return;
  1635 + }
  1636 + //添加测验类型
  1637 + const { data, status, info } = await this.$request.addPaperType({
  1638 + tag: this.answerTypeName,
  1639 + });
  1640 + if (status == 0) {
  1641 + await this._TypeList();
  1642 + this.tagList = deepClone(this.answerTypeList);
  1643 + this.form.tagId = data || "";
  1644 + this.answerTypeName = "";
  1645 + this.$message.success("添加成功");
  1646 + } else {
  1647 + this.$message.error(info);
  1648 + }
  1649 + },
  1650 + async save() {
  1651 + if (this.saveLoading) return;
  1652 + this.saveLoading = true;
  1653 + this.formatQuestionList();
  1654 + let formDatas = deepClone(this.form);
  1655 + for (let i = 0; i < formDatas.questionList.length; i++) {
  1656 + delete formDatas.questionList[i].show;
  1657 + }
  1658 + const { data, status, info } = await this.$request.addPaper({
  1659 + ...formDatas,
  1660 + });
  1661 + this.saveLoading = false;
  1662 + if (status == 0) {
  1663 + this.$router.push({
  1664 + path: "/examinationPaper",
  1665 + });
  1666 + } else {
  1667 + this.$message.error(info);
  1668 + }
  1669 + },
  1670 + async _TypeList() {
  1671 + //测验类型查询
  1672 + const { data, status, info } = await this.$request.fetchTypeNames({
  1673 + type: 1,
  1674 + });
  1675 + if (status == 0) {
  1676 + this.answerTypeList =
  1677 + data.list?.map((item) => {
  1678 + return {
  1679 + typeName: item.tag,
  1680 + id: item.tagId,
  1681 + };
  1682 + }) || [];
  1683 + if (this.type != 2) {
  1684 + this.form.tagId = this.answerTypeList[0]?.id || "";
  1685 + }
  1686 + } else {
  1687 + this.$message.error(info);
  1688 + }
  1689 + },
  1690 +
  1691 + async _QuerySubjectList() {
  1692 + //查询科目列表
  1693 + const { data, status, info } = await this.$request.fetchSubjectList();
  1694 + if (status === 0) {
  1695 + this.subjectList =
  1696 + data.subjectNames?.map((item) => {
  1697 + return {
  1698 + value: item,
  1699 + label: item,
  1700 + };
  1701 + }) || [];
  1702 + console.log(this.subjectList);
  1703 + if (this.subjectList.length) {
  1704 + this.form.subjectName = this.subjectList[0].value;
  1705 + }
  1706 + console.log(this.form);
  1707 + } else {
  1708 + this.$message.error(info);
  1709 + }
  1710 + },
  1711 + async _QueryDetail() {
  1712 + //查询年级列表
  1713 + const { data, status, info } = await this.$request.fetchPaperDetail({
  1714 + paperId: this.$route.query.paperId,
  1715 + });
  1716 + if (status == 0) {
  1717 + this.form.title = data.title + "_副本";
  1718 + this.form.tagId = data.tagId === 0 ? "" : data.tagId;
  1719 + this.form.subjectName = data.subjectName;
  1720 + this.form.examsDuration = data.examsDuration;
  1721 + this.form.questionList = data.questionList?.map((item) => {
  1722 + let subQuestions =
  1723 + item.subQuestions?.map((items) => {
  1724 + return {
  1725 + questionType: items.questionType,
  1726 + score: items.score,
  1727 + partScore: items.partScore,
  1728 + selectNum: items.answerOptions.split(",").length,
  1729 + answerOptions: items.answerOptions || "A,B,C,D",
  1730 + correctAnswer: items.correctAnswer,
  1731 + };
  1732 + }) || [];
  1733 + return {
  1734 + questionTitle: item.questionTitle,
  1735 + subQuestions: subQuestions,
  1736 + show: false,
  1737 + };
  1738 + });
  1739 + } else {
  1740 + this.$message.error(info);
  1741 + }
  1742 + },
  1743 + },
  1744 +};
  1745 +</script>
  1746 +
  1747 +<style lang="scss" scoped>
  1748 +.red {
  1749 + color: #f30;
  1750 +}
  1751 +.qs-options {
  1752 + flex: 1;
  1753 + .ipt {
  1754 + margin-bottom: 5px;
  1755 + }
  1756 + .answer-box {
  1757 + .answer-s {
  1758 + cursor: pointer;
  1759 + user-select: none;
  1760 + &:first-of-type {
  1761 + margin-left: 0;
  1762 + }
  1763 + }
  1764 + }
  1765 + .delButton {
  1766 + text-indent: -9999999px;
  1767 + border-color: #ff6868;
  1768 + background: #ff6868 url("../../../assets/images/arrow.png") no-repeat center;
  1769 + background-size: 19px;
  1770 + color: transparent;
  1771 + }
  1772 + .answer-s.ac {
  1773 + border: none;
  1774 + }
  1775 + .ac {
  1776 + border-color: #ff6868;
  1777 + background: #ff6868;
  1778 + color: #fff !important;
  1779 + }
  1780 +}
  1781 +
  1782 +.sel2 {
  1783 + width: 480px;
  1784 +}
  1785 +.set-ans-btn {
  1786 + width: 100%;
  1787 + padding: 10px 0 10px 630px;
  1788 + box-sizing: border-box;
  1789 + border-right: 1px solid #e2e2e2;
  1790 + border-bottom: 1px solid #e2e2e2;
  1791 +}
  1792 +.content-box {
  1793 + width: 100%;
  1794 + height: 100%;
  1795 + overflow-y: auto;
  1796 +}
  1797 +.content {
  1798 + width: 100%;
  1799 + box-sizing: border-box;
  1800 + padding: 0 24px;
  1801 + .ml-20 {
  1802 + margin-left: 20px;
  1803 + }
  1804 + .btn-box {
  1805 + text-align: right;
  1806 + margin-left: 140px;
  1807 + }
  1808 +}
  1809 +.dia-content {
  1810 + .dia-tit {
  1811 + font-size: 20px;
  1812 + color: #333;
  1813 + font-weight: 700;
  1814 + text-align: center;
  1815 + }
  1816 + .add-type {
  1817 + width: 100%;
  1818 + margin-bottom: 10px;
  1819 + .js-set {
  1820 + margin: 2.5px 10px 0 0;
  1821 + font-size: 14px;
  1822 + }
  1823 + }
  1824 + .add-btn {
  1825 + margin-left: 20px;
  1826 + }
  1827 +}
  1828 +.el-message-box {
  1829 + :deep(.el-button--default) {
  1830 + color: #fff;
  1831 + }
  1832 +}
  1833 +.add-box {
  1834 + display: flex;
  1835 + justify-content: center;
  1836 + align-items: center;
  1837 + .add-question {
  1838 + display: flex;
  1839 + justify-content: center;
  1840 + align-items: center;
  1841 + font-size: 16px;
  1842 + font-weight: bolder;
  1843 + padding: 2px;
  1844 + cursor: pointer;
  1845 + .s1 {
  1846 + margin-left: 6px;
  1847 + }
  1848 + :deep(.el-icon-plus) {
  1849 + font-size: 24px;
  1850 + font-weight: 900;
  1851 + }
  1852 + :deep(.el-button--mini.is-circle) {
  1853 + padding: 3px;
  1854 + }
  1855 + }
  1856 +}
  1857 +.dialog-footer {
  1858 + text-align: center;
  1859 + :deep(.el-button) {
  1860 + border-radius: 20px;
  1861 + padding: 8px 20px 7px;
  1862 + margin: 0 12px;
  1863 + }
  1864 +}
  1865 +.question-box {
  1866 + margin-bottom: 20px;
  1867 +}
  1868 +.question-title {
  1869 + line-height: 40px;
  1870 + display: flex;
  1871 + align-items: center;
  1872 + margin-bottom: 12px;
  1873 + .m20 {
  1874 + margin: 0 20px;
  1875 + }
  1876 + .ipt {
  1877 + width: 300px;
  1878 + margin: 0 16px 0 10px;
  1879 + :deep(.el-input__inner) {
  1880 + border-radius: 20px;
  1881 + border-color: #667ffd;
  1882 + background: rgba($color: #667ffd, $alpha: 0.05);
  1883 + }
  1884 + }
  1885 + .delete {
  1886 + margin-right: 8px;
  1887 + }
  1888 + .title-txt {
  1889 + margin-right: 20px;
  1890 + font-size: 16px;
  1891 + font-weight: 700;
  1892 + }
  1893 + .el-icon-caret-right {
  1894 + font-size: 24px;
  1895 + color: #888;
  1896 + transition: all 0.4s;
  1897 + margin-right: 12px;
  1898 + cursor: pointer;
  1899 + &.active {
  1900 + transform: rotate(90deg);
  1901 + }
  1902 + }
  1903 +}
  1904 +.questions-ul {
  1905 + border-left: 1px solid #e2e2e2;
  1906 + border-top: 1px solid #e2e2e2;
  1907 +}
  1908 +.el-input-number {
  1909 + width: 140px;
  1910 +}
  1911 +.answer-title {
  1912 + text-align: center;
  1913 + font-size: 20px;
  1914 + color: #333;
  1915 + font-weight: 700;
  1916 + padding-bottom: 20px;
  1917 + .totals {
  1918 + font-size: 16px;
  1919 + color: #888;
  1920 + font-weight: normal;
  1921 + }
  1922 +}
  1923 +.answer-box {
  1924 + .answer-s {
  1925 + display: inline-block;
  1926 + width: 30px;
  1927 + height: 30px;
  1928 + border: 1px solid #e2e2e2;
  1929 + border-radius: 3px;
  1930 + margin: 0 6px;
  1931 + font-size: 16px;
  1932 + color: #333;
  1933 + text-align: center;
  1934 + line-height: 30px;
  1935 + &.active {
  1936 + background: #5e78fa;
  1937 + border-color: #5e78fa;
  1938 + color: #fff;
  1939 + }
  1940 + }
  1941 +}
  1942 +.qs-options {
  1943 + .answer-s {
  1944 + display: inline-block;
  1945 + width: 30px;
  1946 + height: 30px;
  1947 + border: 1px solid #e2e2e2;
  1948 + border-radius: 3px;
  1949 + margin: 0 6px;
  1950 + font-size: 16px;
  1951 + color: #333;
  1952 + text-align: center;
  1953 + line-height: 30px;
  1954 + cursor: pointer;
  1955 + &.active {
  1956 + background: #5e78fa;
  1957 + border-color: #5e78fa;
  1958 + color: #fff;
  1959 + }
  1960 + }
  1961 +}
  1962 +.sub-questions {
  1963 + width: 100%;
  1964 + display: flex;
  1965 + border-bottom: 1px solid #e2e2e2;
  1966 + & > div {
  1967 + min-height: 40px;
  1968 + padding: 5px;
  1969 + flex-shrink: 0;
  1970 + border-right: 1px solid #e2e2e2;
  1971 + display: flex;
  1972 + justify-content: center;
  1973 + align-items: center;
  1974 + }
  1975 + .qs-num {
  1976 + width: 70px;
  1977 + }
  1978 + .qs-type {
  1979 + width: 160px;
  1980 + }
  1981 + .qs-score,
  1982 + .qs-partScore {
  1983 + width: 160px;
  1984 + }
  1985 + .qs-options {
  1986 + flex: 1;
  1987 + }
  1988 + .qs-set {
  1989 + width: 190px;
  1990 + .delete{
  1991 + margin-left:9px;
  1992 + }
  1993 + }
  1994 + .qs-options2 {
  1995 + text-align: left;
  1996 + justify-content: flex-start;
  1997 + padding-left: 20px;
  1998 + .answer-s {
  1999 + cursor: pointer;
  2000 + }
  2001 + }
  2002 + :deep(.el-select) {
  2003 + .el-input__inner {
  2004 + border-radius: 20px;
  2005 + border-color: #667ffd;
  2006 + width: 150px;
  2007 + height: 32px;
  2008 + line-height: 32px;
  2009 + background: rgba($color: #667ffd, $alpha: 0.05);
  2010 + }
  2011 + .el-input__icon {
  2012 + line-height: 32px;
  2013 + }
  2014 + }
  2015 +}
  2016 +.upload-demo{
  2017 + text-align: center;
  2018 +}
  2019 +.stem-pic {
  2020 + width: 100%;
  2021 +}
  2022 +</style>
0 2023 \ No newline at end of file
... ...
src/views/examinationPaper/detail.vue renamed to src/views/personal/examinationPaper/detail.vue
src/views/personal/examinationPaper/edit.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <back-box>
  4 + <template slot="title">
  5 + <span>修改答案</span>
  6 + </template>
  7 + </back-box>
  8 + <div class="content">
  9 + <template v-if="type == 1"
  10 + ><p class="tips" v-if="paperModifyLog.modifiedTime">
  11 + <i class="fa fa-bell-o"></i>
  12 + {{
  13 + `${paperModifyLog.modifiedTime} ${paperModifyLog.realName}`
  14 + }}老师修改了答案
  15 + </p></template
  16 + >
  17 + <div class="answer-title">
  18 + <p class="name">{{ form.title || title }}</p>
  19 + <p class="totals">卷面总分:{{ allScore }}分</p>
  20 + </div>
  21 + <template v-if="questionList[0]?.subQuestions">
  22 + <div v-for="(question, index) in questionList" :key="index">
  23 + <p class="question-title">
  24 + <span>{{ setBigNum(index) }}、</span>
  25 + <el-input
  26 + class="ipt"
  27 + v-model.trim="question.questionTitle"
  28 + maxlength="30"
  29 + placeholder="填写大题名称"
  30 + ></el-input>
  31 + <span>共 {{ setScore(question) }} 分</span>
  32 + </p>
  33 + <ul class="questions-ul">
  34 + <li class="sub-questions">
  35 + <div class="qs-num">题号</div>
  36 + <div class="qs-type">题型</div>
  37 + <div class="qs-score">分数</div>
  38 + <div class="qs-partScore">漏选得分</div>
  39 + <div class="qs-options qs-options2">选项设置</div>
  40 + <div class="qs-set">操作</div>
  41 + </li>
  42 + <li
  43 + v-for="(subQuestions, indexs) in question.subQuestions"
  44 + :key="indexs"
  45 + >
  46 + <p
  47 + class="set-ans-btn"
  48 + v-if="
  49 + subQuestions.qusType &&
  50 + subQuestions.subNum &&
  51 + subQuestions.subNum > 4
  52 + "
  53 + >
  54 + <el-button type="primary" @click="setFormAns(indexs, index)"
  55 + >批量设置答案</el-button
  56 + >
  57 + </p>
  58 + <div v-else class="sub-questions">
  59 + <div class="qs-num">{{ subQuestions.questionIndex }}</div>
  60 + <div class="qs-type">
  61 + {{ setSubPro(subQuestions.questionType) }}
  62 + </div>
  63 + <div class="qs-score">
  64 + <el-input-number
  65 + class="number-ipt"
  66 + size="medium"
  67 + :min="1"
  68 + :max="200"
  69 + :precision="2"
  70 + :step="1"
  71 + v-model="subQuestions.score"
  72 + label="单题分值"
  73 + ></el-input-number>
  74 + </div>
  75 + <div class="qs-partScore">
  76 + <p v-if="subQuestions.questionType != 3">--</p>
  77 + <el-input-number
  78 + class="number-ipt"
  79 + v-else
  80 + size="medium"
  81 + :min="0"
  82 + :precision="2"
  83 + :max="subQuestions.score"
  84 + :step="0.5"
  85 + v-model="subQuestions.partScore"
  86 + label="漏选得分"
  87 + ></el-input-number>
  88 + </div>
  89 + <div class="qs-options qs-options2">
  90 + <p v-if="subQuestions.questionType == 5">--</p>
  91 + <p v-if="subQuestions.questionType == 4" class="answer-box">
  92 + <span
  93 + class="answer-s"
  94 + :class="subQuestions.correctAnswer == 1 ? 'active' : ''"
  95 + @click="subQuestions.correctAnswer = 1"
  96 + >✓</span
  97 + >
  98 + <span
  99 + class="answer-s"
  100 + :class="subQuestions.correctAnswer == 2 ? 'active' : ''"
  101 + @click="subQuestions.correctAnswer = 2"
  102 + >✗</span
  103 + >
  104 + </p>
  105 + <p v-if="subQuestions.questionType == 3" class="answer-box">
  106 + <template
  107 + v-for="option in subQuestions.answerOptions.split(',')"
  108 + >
  109 + <span
  110 + v-if="option"
  111 + class="answer-s"
  112 + :class="
  113 + subQuestions.correctAnswer.includes(option)
  114 + ? 'active'
  115 + : ''
  116 + "
  117 + :key="option"
  118 + @click="changAnswer(subQuestions, option)"
  119 + >{{ option }}</span
  120 + >
  121 + </template>
  122 + </p>
  123 + <p v-if="subQuestions.questionType == 2" class="answer-box">
  124 + <template
  125 + v-for="option in subQuestions.answerOptions.split(',')"
  126 + >
  127 + <span
  128 + class="answer-s"
  129 + v-if="option"
  130 + :class="
  131 + subQuestions.correctAnswer == option ? 'active' : ''
  132 + "
  133 + :key="option"
  134 + @click="subQuestions.correctAnswer = option"
  135 + >{{ option }}</span
  136 + >
  137 + </template>
  138 + </p>
  139 + </div>
  140 + <div class="qs-set">
  141 + <el-tooltip effect="dark" content="上传提干" placement="top">
  142 + <el-button
  143 + type="primary"
  144 + circle
  145 + size="small"
  146 + icon="el-icon-tickets"
  147 + @click="openStem(subQuestions,1)"
  148 + ></el-button>
  149 + </el-tooltip>
  150 + <el-tooltip effect="dark" content="题目打标" placement="top">
  151 + <el-button
  152 + type="primary"
  153 + circle
  154 + size="small"
  155 + icon="el-icon-price-tag"
  156 + @click="openTag(subQuestions)"
  157 + ></el-button>
  158 + </el-tooltip>
  159 + <el-tooltip
  160 + effect="dark"
  161 + content="上传题目解析"
  162 + placement="top"
  163 + >
  164 + <el-button
  165 + type="primary"
  166 + circle
  167 + size="small"
  168 + icon="el-icon-upload"
  169 + @click="openStem(subQuestions,2)"
  170 + ></el-button>
  171 + </el-tooltip>
  172 + </div>
  173 + </div>
  174 + </li>
  175 + </ul>
  176 + </div>
  177 + </template>
  178 + <ul class="questions-ul" v-else>
  179 + <li class="sub-questions">
  180 + <div class="qs-num">题号</div>
  181 + <div class="qs-type">题型</div>
  182 + <div class="qs-score">分数</div>
  183 + <div class="qs-partScore">漏选得分</div>
  184 + <div class="qs-options qs-options2">答案</div>
  185 + <div class="qs-set">操作</div>
  186 + </li>
  187 + <li v-for="(subQuestions, indexs) in questionList" :key="indexs">
  188 + <p
  189 + class="set-ans-btn"
  190 + v-if="
  191 + subQuestions.qusType &&
  192 + subQuestions.subNum &&
  193 + subQuestions.subNum > 4
  194 + "
  195 + >
  196 + <el-button type="primary" @click="setFormAns(indexs)"
  197 + >批量设置答案</el-button
  198 + >
  199 + </p>
  200 + <div v-else class="sub-questions">
  201 + <div class="qs-num">{{ subQuestions.questionIndex }}</div>
  202 + <div class="qs-type">
  203 + {{ setSubPro(subQuestions.questionType) }}
  204 + </div>
  205 + <div class="qs-score">
  206 + {{ subQuestions.score }}
  207 + </div>
  208 + <div class="qs-partScore">
  209 + <p v-if="subQuestions.questionType != 3">--</p>
  210 + <el-input-number
  211 + class="number-ipt"
  212 + v-else
  213 + size="medium"
  214 + :min="0"
  215 + :precision="2"
  216 + :max="subQuestions.score"
  217 + :step="0.5"
  218 + v-model="subQuestions.partScore"
  219 + label="漏选得分"
  220 + ></el-input-number>
  221 + </div>
  222 + <div class="qs-options qs-options2">
  223 + <p v-if="subQuestions.questionType == 5">--</p>
  224 + <p v-if="subQuestions.questionType == 4" class="answer-box">
  225 + <span
  226 + class="answer-s"
  227 + :class="subQuestions.correctAnswer == 1 ? 'active' : ''"
  228 + @click="subQuestions.correctAnswer = 1"
  229 + >✓</span
  230 + >
  231 + <span
  232 + class="answer-s"
  233 + :class="subQuestions.correctAnswer == 2 ? 'active' : ''"
  234 + @click="subQuestions.correctAnswer = 2"
  235 + >✗</span
  236 + >
  237 + </p>
  238 + <p v-if="subQuestions.questionType == 3" class="answer-box">
  239 + <template
  240 + v-for="option in subQuestions.answerOptions.split(',')"
  241 + >
  242 + <span
  243 + v-if="option"
  244 + class="answer-s"
  245 + :class="
  246 + subQuestions.correctAnswer.includes(option)
  247 + ? 'active'
  248 + : ''
  249 + "
  250 + :key="option"
  251 + @click="changAnswer(subQuestions, option)"
  252 + >{{ option }}</span
  253 + >
  254 + </template>
  255 + </p>
  256 + <p v-if="subQuestions.questionType == 2" class="answer-box">
  257 + <template
  258 + v-for="option in subQuestions.answerOptions.split(',')"
  259 + >
  260 + <span
  261 + class="answer-s"
  262 + v-if="option"
  263 + :class="
  264 + subQuestions.correctAnswer == option ? 'active' : ''
  265 + "
  266 + :key="option"
  267 + @click="subQuestions.correctAnswer = option"
  268 + >{{ option }}</span
  269 + >
  270 + </template>
  271 + </p>
  272 + </div>
  273 + </div>
  274 + </li>
  275 + </ul>
  276 + <div class="btn-box">
  277 + <el-button type="danger" plain round @click="linkBack">取消</el-button>
  278 + <el-button type="primary" round @click="save">保存</el-button>
  279 + </div>
  280 + <el-dialog
  281 + title="批量设置答案"
  282 + :close-on-click-modal="false"
  283 + :visible.sync="diaSetAns"
  284 + width="400"
  285 + :modal-append-to-body="false"
  286 + >
  287 + <div class="qs-options set-questions">
  288 + <p class="dia-tips">
  289 + 请点击选项按钮设置答案,多选题题目之间用“,”隔开,若添加5道题:“AC,AD,BD,AC,CD”
  290 + </p>
  291 + <p>{{ setSubPro(formAns.qusType) }}:</p>
  292 + <p class="ipt">
  293 + <el-input
  294 + ref="formAnsIpt"
  295 + v-if="formAns.qusType == 2 || formAns.qusType == 3"
  296 + v-model="formAns.answerList"
  297 + @keydown.native="keydownAnswer($event, formAns.qusType)"
  298 + @input="setAllAnswer($event, formAns.qusType)"
  299 + ></el-input>
  300 + <el-input
  301 + v-if="formAns.qusType == 4"
  302 + v-model="formAns.answerList"
  303 + readonly=""
  304 + ></el-input>
  305 + </p>
  306 + <p class="answer-box">
  307 + <template v-if="formAns.qusType == 4">
  308 + <span
  309 + class="answer-s active"
  310 + @click="
  311 + formAns.answerList.length < formAns.subNum
  312 + ? (formAns.answerList += '✓')
  313 + : ''
  314 + "
  315 + >✓</span
  316 + >
  317 + <span
  318 + class="answer-s active"
  319 + @click="
  320 + formAns.answerList.length < formAns.subNum
  321 + ? (formAns.answerList += '✗')
  322 + : ''
  323 + "
  324 + >✗</span
  325 + >
  326 + </template>
  327 + <template v-if="formAns.qusType == 3">
  328 + <span
  329 + class="answer-s active"
  330 + v-for="option in formAns.answerOptions.split(',')"
  331 + :key="option"
  332 + @click="setMultiple(formAns, option)"
  333 + >{{ option }}</span
  334 + >
  335 + <span class="answer-s active" @click="setMultiple(formAns, ',')"
  336 + >,</span
  337 + >
  338 + </template>
  339 + <template v-if="formAns.qusType == 2" class="answer-box">
  340 + <span
  341 + class="answer-s active"
  342 + v-for="option in formAns.answerOptions.split(',')"
  343 + :key="option"
  344 + @click="
  345 + formAns.answerList.length < formAns.subNum
  346 + ? (formAns.answerList += option)
  347 + : ''
  348 + "
  349 + >{{ option }}</span
  350 + >
  351 + </template>
  352 + <span
  353 + class="answer-s delButton"
  354 + @click="formAns.answerList = formAns.answerList.slice(0, -1)"
  355 + >x</span
  356 + >
  357 + <span class="answer-s ac" @click="formAns.answerList = ''">ac</span>
  358 + </p>
  359 + </div>
  360 + <div class="dialog-footer" slot="footer">
  361 + <el-button @click="saveFormAns">确 定</el-button>
  362 + <el-button @click="diaSetAns = false">取 消</el-button>
  363 + </div>
  364 + </el-dialog>
  365 + <el-dialog :title="upLoadType==1?'上传题干':'上传题目解析'" :visible.sync="dialogStem" width="500">
  366 + <div>
  367 + <img class="stem-pic" :src="stem.src" alt="" />
  368 + <el-upload
  369 + class="upload-demo"
  370 + action="https://jsonplaceholder.typicode.com/posts/"
  371 + :limit="1"
  372 + :with-credentials="true"
  373 + :on-success="upSuccess"
  374 + :on-error="upError"
  375 + accept="image/*"
  376 + >
  377 + <el-button size="small" type="primary">{{stem.src?"重新上传":"选择照片"}}</el-button>
  378 + </el-upload>
  379 + </div>
  380 + <div slot="footer">
  381 + <el-button @click="dialogStem = false">关闭弹窗</el-button>
  382 + </div>
  383 + </el-dialog>
  384 + <el-dialog title="题目打标" :visible.sync="dialogTag" width="500">
  385 + <div>
  386 + <el-form ref="form" :model="stem" label-width="160px">
  387 + <el-form-item label="题目难度:">
  388 + <el-select v-model="stem.type" placeholder="选择题目难度">
  389 + <el-option label="简单" :value="0"></el-option>
  390 + <el-option label="正常" :value="1"></el-option>
  391 + <el-option label="困难" :value="2"></el-option>
  392 + </el-select>
  393 + </el-form-item>
  394 + <el-form-item label="知识点:">
  395 + <el-select v-model="stem.tag" placeholder="选择知识点">
  396 + <el-option label="生字积累" :value="0"></el-option>
  397 + <el-option label="字词运用" :value="1"></el-option>
  398 + <el-option label="阅读理解" :value="2"></el-option>
  399 + <el-option label="作文" :value="2"></el-option>
  400 + </el-select>
  401 + </el-form-item>
  402 + </el-form>
  403 + </div>
  404 + <div slot="footer">
  405 + <el-button @click="dialogTag = false">取 消</el-button>
  406 + <el-button type="primary" @click="dialogTag = false"
  407 + >确 定</el-button
  408 + >
  409 + </div>
  410 + </el-dialog>
  411 + </div>
  412 + </div>
  413 +</template>
  414 +
  415 +<script>
  416 +import { deepClone, checkAnswer } from "utils";
  417 +export default {
  418 + data() {
  419 + return {
  420 + dialogStem: false,
  421 + dialogTag: false,
  422 + upLoadType:1,
  423 + stem: {
  424 + src: "", //题干图片地址
  425 + type: 0, //题目难度
  426 + tag: 0, //知识点
  427 + },
  428 + title: "",
  429 + type: 1,
  430 + questionList: [],
  431 + form: {
  432 + //答题卡详情
  433 + title: "",
  434 + tag: "",
  435 + subjectId: "",
  436 + examsDuration: 90,
  437 + gradeName: "",
  438 + share: 1,
  439 + questionList: [],
  440 + },
  441 + paperModifyLog: {
  442 + realName: "",
  443 + modifiedTime: "",
  444 + },
  445 + diaSetAns: false,
  446 + formAns: {
  447 + listIndex: 0, //大题位置
  448 + endIndex: 0, //相同题目最后一位题目的questionIndex
  449 + index: 0,
  450 + qusType: "", //题目类型
  451 + subNum: 0, //数量
  452 + answerOptions: [], //答案选项
  453 + answerList: "", //答案列表-字符串
  454 + },
  455 + };
  456 + },
  457 + computed: {
  458 + allScore: function () {
  459 + let score = 0;
  460 + this.questionList?.map((item) => {
  461 + if (item.subQuestions) {
  462 + score += item.subQuestions.reduce((a, b) => {
  463 + return a + (Number(b.score) || 0);
  464 + }, 0);
  465 + } else {
  466 + score += item.score;
  467 + }
  468 + }, 0);
  469 + return Number(score).toFixed(2);
  470 + },
  471 + },
  472 + created() {
  473 + this.type = this.$route.query.type || 1;
  474 + this.title = this.$route.query.title || "";
  475 + this._QueryDetail();
  476 + },
  477 + methods: {
  478 + openStem(obj,type) {
  479 + this.upLoadType = type
  480 + this.stem = { ...this.stem, obj };
  481 + this.dialogStem = true;
  482 + },
  483 + openTag(obj) {
  484 + this.stem = { ...this.stem, obj };
  485 + this.dialogTag = true;
  486 + },
  487 + upSuccess(res) {
  488 + if (res && res.status == 0) {
  489 + this.$message.success("上传成功");
  490 + } else {
  491 + this.$message.error(res.info);
  492 + }
  493 + },
  494 + upError(res) {
  495 + if (res && res.status == 0) {
  496 + this.$message.error("上传失败");
  497 + } else {
  498 + this.$message.error(res.message);
  499 + }
  500 + },
  501 + linkBack() {
  502 + this.$router.go(-1);
  503 + },
  504 + setSubPro(type) {
  505 + let tit;
  506 + switch (type) {
  507 + case 2:
  508 + tit = "单选题";
  509 + break;
  510 + case 3:
  511 + tit = "多选题";
  512 + break;
  513 + case 4:
  514 + tit = "判断题";
  515 + break;
  516 + case 5:
  517 + tit = "主观题";
  518 + break;
  519 + }
  520 + return tit;
  521 + },
  522 + setBigNum(num) {
  523 + let txt = "";
  524 + let bigNum = [
  525 + "一",
  526 + "二",
  527 + "三",
  528 + "四",
  529 + "五",
  530 + "六",
  531 + "七",
  532 + "八",
  533 + "九",
  534 + "十",
  535 + "十一",
  536 + "十二",
  537 + "十三",
  538 + "十四",
  539 + "十五",
  540 + "十六",
  541 + "十七",
  542 + "十八",
  543 + "十九",
  544 + "二十",
  545 + ];
  546 + txt = bigNum[num];
  547 +
  548 + return txt;
  549 + },
  550 + setScore(question) {
  551 + let score = question.subQuestions.reduce((a, b) => {
  552 + return a + (b.score || 0);
  553 + }, 0);
  554 + return Number(score).toFixed(2);
  555 + },
  556 + changAnswer(sub, option) {
  557 + //设置多选答案
  558 + let str = new RegExp(option, "g");
  559 + if (sub.correctAnswer?.includes(option)) {
  560 + sub.correctAnswer = sub.correctAnswer.replace(str, "");
  561 + } else {
  562 + let arrs = (sub.correctAnswer && sub.correctAnswer.split("")) || [];
  563 + arrs.push(option);
  564 + sub.correctAnswer = arrs.sort().join("");
  565 + }
  566 + },
  567 + keydownAnswer(event, type) {
  568 + let answerA = "ABCDEFG";
  569 + let answer_a = "abcdefg";
  570 + answerA = answerA.substring(0, this.formAns.subNum);
  571 + answer_a = answer_a.substring(0, this.formAns.subNum);
  572 + answerA += answer_a;
  573 + answerA = type == 2 ? answerA : answerA + ",";
  574 + if (
  575 + event.key == "Meta" ||
  576 + event.key == "CapsLock" ||
  577 + event.key == "Shift" ||
  578 + event.key == "Enter" ||
  579 + event.key == "Alt" ||
  580 + event.key == "Backspace" ||
  581 + event.key == "Delete" ||
  582 + event.key == "ArrowUp" ||
  583 + event.key == "ArrowDown" ||
  584 + event.key == "ArrowLeft" ||
  585 + event.key == "v" ||
  586 + event.key == "V" ||
  587 + event.key == "ArrowRight"
  588 + )
  589 + return;
  590 + if (!answerA.includes(event.key)) {
  591 + event.returnValue = "";
  592 + }
  593 + },
  594 + setAllAnswer(event, type) {
  595 + let str = this.formAns.answerList;
  596 + let str2 = checkAnswer(
  597 + str,
  598 + type,
  599 + this.formAns.answerOptions.split(",").length,
  600 + this.formAns.subNum
  601 + );
  602 + this.formAns.answerList = str2;
  603 + },
  604 + setAnswer(type, ans) {
  605 + let txt = "";
  606 + if (type == 2) {
  607 + txt = ans;
  608 + } else if (type == 3) {
  609 + txt = ans + ",";
  610 + } else if (type == 4) {
  611 + txt = ans == 1 ? "✓" : ans == 2 ? "✗" : "";
  612 + }
  613 + return txt;
  614 + },
  615 + insertTxtAndSetcursor(answerList, str) {
  616 + let element = this.$refs.formAnsIpt.$el.children[0]; // 获取到指定标签
  617 + let startPos = element.selectionStart; // 获取光标开始的位置
  618 + if (startPos === undefined) {
  619 + // 如果没有光标位置 不操作
  620 + return answerList;
  621 + } else {
  622 + return {
  623 + text:
  624 + answerList.substring(0, startPos) +
  625 + str +
  626 + answerList.substring(startPos), // 将文本插入
  627 + startPos: startPos + str.length,
  628 + };
  629 + }
  630 + },
  631 + setMultiple(obj, answer) {
  632 + //多选答案设置
  633 + let resault = this.insertTxtAndSetcursor(obj.answerList || "", answer);
  634 + obj.answerList = resault.text;
  635 + let str = obj.answerList;
  636 + let str2 = checkAnswer(
  637 + str,
  638 + 3,
  639 + obj.answerOptions.split(",").length,
  640 + obj.subNum
  641 + );
  642 + obj.answerList = str2;
  643 + this.$refs.formAnsIpt.$el.children[0].focus();
  644 + this.$refs.formAnsIpt.$el.children[0].selectionStart = resault.startPos;
  645 + },
  646 + setFormAns(indexs, index) {
  647 + //初始化要修改的答案
  648 + if (this.questionList[0].subQuestions) {
  649 + this.formAns = { ...this.questionList[index].subQuestions[indexs] };
  650 + this.formAns.listIndex = index;
  651 + let answerList = "";
  652 + let startIndex = this.formAns.index + 1 - this.formAns.subNum; //批量设置大难开始位置
  653 + this.questionList[index].subQuestions.map((item, subIdx) => {
  654 + if (subIdx >= startIndex) {
  655 + answerList += this.setAnswer(item.questionType, item.correctAnswer);
  656 + if (subIdx != indexs) {
  657 + if (!!item.qusType) {
  658 + answerList = "";
  659 + }
  660 + } else {
  661 + if (item.qusType == 3) {
  662 + answerList = answerList.slice(0, -1);
  663 + }
  664 + this.formAns.answerList = answerList;
  665 + }
  666 + }
  667 + });
  668 + } else {
  669 + this.formAns = { ...this.questionList[indexs] };
  670 + let startIndex = this.formAns.endIndex - this.formAns.subNum;
  671 + this.formAns.listIndex = indexs;
  672 + let answerList = "";
  673 + this.questionList.map((item, subIdx) => {
  674 + if (subIdx >= startIndex) {
  675 + answerList += this.setAnswer(item.questionType, item.correctAnswer);
  676 + if (subIdx != indexs) {
  677 + if (!!item.qusType) {
  678 + answerList = "";
  679 + }
  680 + } else {
  681 + if (item.qusType == 3) {
  682 + answerList = answerList.slice(0, -1);
  683 + }
  684 + this.formAns.answerList = answerList;
  685 + }
  686 + }
  687 + });
  688 + }
  689 + this.diaSetAns = true;
  690 + },
  691 + saveFormAns() {
  692 + //批量修改答案
  693 + let EndIndex;
  694 + let subNum = this.formAns.subNum - 1;
  695 + if (this.questionList[this.formAns.listIndex].subQuestions) {
  696 + this.questionList[this.formAns.listIndex].subQuestions.some(
  697 + (item, index) => {
  698 + if (this.formAns.endIndex == item.questionIndex) {
  699 + EndIndex = index;
  700 + return;
  701 + }
  702 + }
  703 + );
  704 + } else {
  705 + this.questionList.some((item, index) => {
  706 + if (this.formAns.endIndex == item.questionIndex) {
  707 + EndIndex = index;
  708 + return;
  709 + }
  710 + });
  711 + }
  712 +
  713 + for (let i = 0; i <= subNum; i++) {
  714 + let correctAnswer = "";
  715 + if (this.formAns.qusType == 2) {
  716 + correctAnswer = this.formAns.answerList[subNum - i] || "";
  717 + } else if (this.formAns.qusType == 3) {
  718 + correctAnswer = this.formAns.answerList.split(",")[subNum - i] || "";
  719 + } else if (this.formAns.qusType == 4) {
  720 + correctAnswer =
  721 + this.formAns.answerList[subNum - i] == "✓"
  722 + ? 1
  723 + : this.formAns.answerList[subNum - i] == "✗"
  724 + ? 2
  725 + : "";
  726 + }
  727 + if (this.questionList[0].subQuestions) {
  728 + this.questionList[this.formAns.listIndex].subQuestions[
  729 + EndIndex - i
  730 + ].correctAnswer = correctAnswer;
  731 + } else {
  732 + this.questionList[EndIndex - i].correctAnswer = correctAnswer;
  733 + }
  734 + }
  735 + this.diaSetAns = false;
  736 + },
  737 + async save() {
  738 + for (let i = 0; i < this.questionList.length; i++) {
  739 + if (this.questionList[0].subQuestions) {
  740 + for (let j = 0; j < this.questionList[i].subQuestions.length; j++) {
  741 + if (this.questionList[i].subQuestions[j].qusType) {
  742 + this.questionList[i].subQuestions.splice(j, 1);
  743 + }
  744 + }
  745 + } else {
  746 + if (this.questionList[i].qusType) {
  747 + this.questionList.splice(i, 1);
  748 + i--;
  749 + }
  750 + }
  751 + }
  752 + let questionList = this.questionList.map((item) => {
  753 + item.score = null;
  754 + // item.questionId = "";
  755 + // item.questionIndex = "";
  756 + return item;
  757 + });
  758 + //更新答题卡
  759 + let modifyPaper, params;
  760 + if (this.type == 1) {
  761 + modifyPaper = this.$request.modifyPaper;
  762 + params = {
  763 + paperId: this.$route.query.paperId,
  764 + questionList: questionList,
  765 + };
  766 + } else if (this.type == 2) {
  767 + modifyPaper = this.$request.setExamAnswer;
  768 + params = {
  769 + examId: this.$route.query.paperId,
  770 + questionList: questionList,
  771 + };
  772 + } else {
  773 + modifyPaper = this.$request.setPeriodAnswer;
  774 + params = {
  775 + periodId: this.$route.query.paperId,
  776 + questionList: questionList,
  777 + };
  778 + }
  779 + const { data, status, info } = await modifyPaper(params);
  780 + if (status == 0) {
  781 + this.$router.go(-1);
  782 + } else {
  783 + this.$message.error(message);
  784 + }
  785 + },
  786 + async _QueryDetail() {
  787 + //查询详情
  788 + let detail =
  789 + this.type == 1
  790 + ? this.$request.fetchPaperDetail
  791 + : this.type == 2
  792 + ? this.$request.examQuestionList
  793 + : this.$request.periodQuestionList;
  794 + let params =
  795 + this.type == 1
  796 + ? { paperId: this.$route.query.paperId }
  797 + : this.type == 2
  798 + ? { examId: this.$route.query.paperId }
  799 + : {
  800 + periodId: this.$route.query.paperId,
  801 + };
  802 + const { data, status, info } = await detail(params);
  803 + if (status == 0) {
  804 + if (this.type == 1) {
  805 + this.paperModifyLog = {
  806 + ...this.paperModifyLog,
  807 + ...this.form.paperModifyLog,
  808 + };
  809 + this.form = deepClone(data);
  810 + this.questionList = deepClone(this.form.questionList);
  811 + } else {
  812 + this.questionList = data.list.sort((a, b) => {
  813 + return a.questionIndex - b.questionIndex;
  814 + });
  815 + }
  816 + if (!!this.questionList[0]?.subQuestions) {
  817 + this.questionList?.map((item) => {
  818 + let types = [{}];
  819 + let addndex = 0;
  820 + item.subQuestions.map((sub, index) => {
  821 + if (!!sub.questionType) {
  822 + if (
  823 + sub.questionType == types[addndex].qusType &&
  824 + sub.questionType != 5
  825 + ) {
  826 + //同类型批量答案+1
  827 + types[addndex].subNum += 1;
  828 + if (
  829 + types[addndex].answerOptions.length <
  830 + sub.answerOptions.length
  831 + ) {
  832 + types[addndex].answerOptions = sub.answerOptions;
  833 + }
  834 + types[addndex].answerList += this.setAnswer(
  835 + sub.questionType,
  836 + sub.correctAnswer
  837 + );
  838 + if (index == item.subQuestions.length - 1) {
  839 + //循环最后类型数量大于等于5,保存批量答案
  840 + if (types[addndex].subNum && types[addndex].subNum >= 5) {
  841 + types[addndex].endIndex = sub.questionIndex;
  842 + types[addndex].index = index;
  843 + }
  844 + }
  845 + } else {
  846 + if (types[addndex].subNum && types[addndex].subNum >= 5) {
  847 + //不同类型时如果原有类型数量大于等于5,保存批量答案
  848 + types[addndex].endIndex =
  849 + item.subQuestions[index - 1].questionIndex;
  850 + types[addndex].index = index - 1;
  851 + addndex += 1;
  852 + types[addndex] = {};
  853 + }
  854 + //不同类型初始化批量答案
  855 + types[addndex].qusType = sub.questionType;
  856 + types[addndex].subNum = 1;
  857 + types[addndex].answerOptions = sub.answerOptions;
  858 + types[addndex].answerList = this.setAnswer(
  859 + sub.questionType,
  860 + sub.correctAnswer
  861 + );
  862 + }
  863 + }
  864 + });
  865 + for (let i = 0; i < types.length; i++) {
  866 + if (types[i].qusType == 3) {
  867 + types[i].answerList = types[i].answerList.slice(0, -1);
  868 + }
  869 + if (types[i].subNum >= 5) {
  870 + item.subQuestions.splice(
  871 + types[i].index + i + 1,
  872 + 0,
  873 + deepClone(types[i])
  874 + );
  875 + }
  876 + }
  877 + });
  878 + } else {
  879 + let types = [{}];
  880 + let addndex = 0;
  881 + this.questionList?.map((sub, index) => {
  882 + if (!!sub.questionType) {
  883 + if (
  884 + sub.questionType == types[addndex].qusType &&
  885 + sub.questionType != 5
  886 + ) {
  887 + //同类型批量答案+1
  888 + types[addndex].subNum += 1;
  889 + if (
  890 + types[addndex].answerOptions.length < sub.answerOptions.length
  891 + ) {
  892 + types[addndex].answerOptions = sub.answerOptions;
  893 + }
  894 + types[addndex].answerList += this.setAnswer(
  895 + sub.questionType,
  896 + sub.correctAnswer
  897 + );
  898 + if (index == this.questionList.length - 1) {
  899 + //循环最后类型数量大于等于5,保存批量答案
  900 + if (types[addndex].subNum && types[addndex].subNum >= 5) {
  901 + types[addndex].endIndex = sub.questionIndex;
  902 + types[addndex].index = index;
  903 + }
  904 + }
  905 + } else {
  906 + if (types[addndex].subNum && types[addndex].subNum >= 5) {
  907 + //不同类型时如果原有类型数量大于等于5,保存批量答案
  908 + types[addndex].endIndex =
  909 + this.questionList[index - 1].questionIndex;
  910 + types[addndex].index = index - 1;
  911 + addndex += 1;
  912 + types[addndex] = {};
  913 + }
  914 + //不同类型初始化批量答案
  915 + types[addndex].qusType = sub.questionType;
  916 + types[addndex].subNum = 1;
  917 + types[addndex].answerOptions = sub.answerOptions;
  918 + types[addndex].answerList = this.setAnswer(
  919 + sub.questionType,
  920 + sub.correctAnswer
  921 + );
  922 + }
  923 + }
  924 + });
  925 + for (let i = 0; i < types.length; i++) {
  926 + if (types[i].qusType == 3) {
  927 + types[i].answerList = types[i].answerList.slice(0, -1);
  928 + }
  929 + if (types[i].subNum >= 5) {
  930 + this.questionList.splice(
  931 + types[i].index + i + 1,
  932 + 0,
  933 + deepClone(types[i])
  934 + );
  935 + }
  936 + }
  937 + }
  938 + console.log(this.questionList);
  939 + } else {
  940 + this.$message.error(info);
  941 + }
  942 + },
  943 + },
  944 +};
  945 +</script>
  946 +
  947 +<style lang="scss" scoped>
  948 +.content {
  949 + width: 100%;
  950 + box-sizing: border-box;
  951 + padding: 0 50px;
  952 + .ml-20 {
  953 + margin-left: 20px;
  954 + }
  955 + .btn-box {
  956 + text-align: right;
  957 + margin-left: 140px;
  958 + }
  959 + .tips {
  960 + height: 48px;
  961 + line-height: 48px;
  962 + padding: 0 16px;
  963 + border: 1px solid #fac7cc;
  964 + background-color: #ffebec;
  965 + font-size: 14px;
  966 + color: #fd9795;
  967 + margin: 10px 0 20px 0;
  968 + .fa-bell-o {
  969 + font-size: 18px;
  970 + margin-right: 5px;
  971 + }
  972 + }
  973 +}
  974 +.answer-title {
  975 + text-align: center;
  976 + font-size: 20px;
  977 + color: #333;
  978 + font-weight: 700;
  979 + padding: 20px 0;
  980 + .totals {
  981 + font-size: 16px;
  982 + color: #888;
  983 + font-weight: normal;
  984 + }
  985 +}
  986 +.question-title {
  987 + line-height: 40px;
  988 + .ipt {
  989 + width: 300px;
  990 + margin: 0 16px 0 10px;
  991 + :deep(.el-input__inner) {
  992 + border-radius: 20px;
  993 + border-color: #667ffd;
  994 + background: rgba($color: #667ffd, $alpha: 0.05);
  995 + }
  996 + }
  997 + .delete {
  998 + margin-right: 8px;
  999 + }
  1000 + .title-txt {
  1001 + margin-right: 20px;
  1002 + font-size: 16px;
  1003 + font-weight: 700;
  1004 + }
  1005 +}
  1006 +.set-ans-btn {
  1007 + width: 100%;
  1008 + padding: 10px 0 10px 630px;
  1009 + box-sizing: border-box;
  1010 + border-bottom: 1px solid #e2e2e2;
  1011 + border-right: 1px solid #e2e2e2;
  1012 +}
  1013 +.el-input-number {
  1014 + width: 140px;
  1015 +}
  1016 +
  1017 +.questions-ul {
  1018 + border-left: 1px solid #e2e2e2;
  1019 + border-top: 1px solid #e2e2e2;
  1020 + margin: 12px 0;
  1021 +}
  1022 +.sub-questions {
  1023 + width: 100%;
  1024 + display: flex;
  1025 + border-bottom: 1px solid #e2e2e2;
  1026 + & > div {
  1027 + min-height: 40px;
  1028 + padding: 5px;
  1029 + flex-shrink: 0;
  1030 + border-right: 1px solid #e2e2e2;
  1031 + display: flex;
  1032 + justify-content: center;
  1033 + align-items: center;
  1034 + }
  1035 + .qs-num {
  1036 + width: 80px;
  1037 + }
  1038 + .qs-type {
  1039 + width: 160px;
  1040 + }
  1041 + .qs-score,
  1042 + .qs-partScore {
  1043 + width: 160px;
  1044 + }
  1045 + .qs-set {
  1046 + width: 180px;
  1047 + }
  1048 + .qs-options {
  1049 + flex: 1;
  1050 + }
  1051 + .qs-options2 {
  1052 + text-align: left;
  1053 + justify-content: flex-start;
  1054 + padding-left: 20px;
  1055 + }
  1056 + .answer-s {
  1057 + cursor: pointer;
  1058 + }
  1059 + :deep(.el-select) {
  1060 + .el-input__inner {
  1061 + border-radius: 20px;
  1062 + border-color: #667ffd;
  1063 + width: 150px;
  1064 + height: 32px;
  1065 + line-height: 32px;
  1066 + background: rgba($color: #667ffd, $alpha: 0.05);
  1067 + }
  1068 + .el-input__icon {
  1069 + line-height: 32px;
  1070 + }
  1071 + }
  1072 +}
  1073 +.set-questions {
  1074 + .answer-box {
  1075 + .answer-s {
  1076 + cursor: pointer;
  1077 + user-select: none;
  1078 + &:first-of-type {
  1079 + margin-left: 0;
  1080 + }
  1081 + }
  1082 + }
  1083 +}
  1084 +.upload-demo{
  1085 + text-align: center;
  1086 +}
  1087 +.stem-pic {
  1088 + width: 100%;
  1089 +}
  1090 +</style>
0 1091 \ No newline at end of file
... ...
src/views/personal/examinationPaper/index.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <back-box>
  4 + <template slot="title">
  5 + <span>备题组卷</span>
  6 + </template>
  7 + <template slot="btns">
  8 + <el-button
  9 + type="primary"
  10 + size="mini"
  11 + icon="el-icon-plus"
  12 + plain
  13 + @click="toAdd({})"
  14 + >
  15 + 添加答题卡</el-button
  16 + >
  17 + </template>
  18 + </back-box>
  19 +
  20 + <div class="answer-header">
  21 + <div class="sel-box">
  22 + <el-select
  23 + class="sel"
  24 + v-model="query.classId"
  25 + placeholder="选择班级"
  26 + @change="changClazz"
  27 + >
  28 + <el-option
  29 + v-for="item in classList"
  30 + :key="item.value"
  31 + :label="item.label"
  32 + :value="item.value"
  33 + >
  34 + </el-option>
  35 + </el-select>
  36 + <el-select
  37 + class="sel"
  38 + v-model="query.subjectName"
  39 + placeholder="选择科目"
  40 + @change="_QueryData()"
  41 + >
  42 + <el-option
  43 + v-for="item in subjectList"
  44 + :key="item.value"
  45 + :label="item.label"
  46 + :value="item.value"
  47 + >
  48 + </el-option>
  49 + </el-select>
  50 + <el-select
  51 + class="sel"
  52 + v-model="query.tagId"
  53 + placeholder="选择类型"
  54 + @change="_QueryData()"
  55 + >
  56 + <el-option
  57 + v-for="item in typeList"
  58 + :key="item.label"
  59 + :label="item.label"
  60 + :value="item.value"
  61 + >{{ item.label }}
  62 + </el-option>
  63 + </el-select>
  64 + <el-input
  65 + placeholder="试卷名称"
  66 + v-model="query.title"
  67 + class="input-with-select"
  68 + @keyup.enter.native="_QueryData(true)"
  69 + >
  70 + <el-button
  71 + slot="append"
  72 + icon="el-icon-search"
  73 + @click="_QueryData(true)"
  74 + ></el-button>
  75 + </el-input>
  76 + </div>
  77 + </div>
  78 + <p class="tips" v-show="archivedTotal">
  79 + <span>另有{{ archivedTotal }}份已经归档的答题卡,</span>
  80 + <router-link to="/examinationPaperRecycle">点击查看&gt;&gt;</router-link>
  81 + </p>
  82 + <ul class="content" v-if="tableData && tableData.length">
  83 + <li class="item" v-for="item in tableData" :key="item.id">
  84 + <div class="pic-box">
  85 + <p class="i-box"><i class="fa fa-map-o"></i></p>
  86 + <p class="ids">{{ item.id }}</p>
  87 + </div>
  88 + <div class="info">
  89 + <p class="title">
  90 + {{ item.title }}
  91 + <span class="label" v-if="!!item.tag">{{ item.tag }}</span>
  92 + </p>
  93 + <p class="num">
  94 + 总题数:{{ item.questionNum }}
  95 + <em class="s-line">|</em>
  96 + 预计时长:{{ item.examsDuration }}
  97 + <em class="s-line">|</em>
  98 + </p>
  99 + <p class="person">
  100 + {{ item.realName }}<em class="s-line">|</em
  101 + ><span class="date">{{ item.modifiedTime }}</span>
  102 + </p>
  103 + </div>
  104 + <div class="btn-box">
  105 + <el-tooltip effect="dark" content="修改答案" placement="bottom">
  106 + <el-button
  107 + class="edit"
  108 + type="primary"
  109 + size="mini"
  110 + circle
  111 + icon="el-icon-edit"
  112 + @click="toEdit(item)"
  113 + ></el-button>
  114 + </el-tooltip>
  115 + <el-dropdown
  116 + trigger="click"
  117 + @command="handleDropdownClick($event, item)"
  118 + >
  119 + <el-button
  120 + type="info"
  121 + size="mini"
  122 + circle
  123 + icon="el-icon-more"
  124 + ></el-button>
  125 + <el-dropdown-menu slot="dropdown">
  126 + <el-dropdown-item :command="2">复制</el-dropdown-item>
  127 + <el-dropdown-item :command="3">归档</el-dropdown-item>
  128 + </el-dropdown-menu>
  129 + </el-dropdown>
  130 + </div>
  131 + </li>
  132 + </ul>
  133 + <div class="pagination-box">
  134 + <el-pagination
  135 + small=""
  136 + layout="total,prev, pager, next"
  137 + :hide-on-single-page="true"
  138 + :total="total"
  139 + @current-change="changePage"
  140 + :current-page="page"
  141 + :page-size="size"
  142 + >
  143 + </el-pagination>
  144 + </div>
  145 + <el-empty
  146 + v-if="!loading && tableData.length == 0"
  147 + content="没有更多数据"
  148 + :image-size="100"
  149 + ></el-empty>
  150 +
  151 + </div>
  152 +</template>
  153 +
  154 +<script>
  155 +export default {
  156 + name: "examinationPaper",
  157 + data() {
  158 + return {
  159 + loading: false,
  160 + userName: "",
  161 + query: {
  162 + classId: "",
  163 + subjectName: "",
  164 + tagId: "",
  165 + title: "",
  166 + },
  167 + classList: [],
  168 + subjectList: [],
  169 + typeList: [],
  170 + archivedTotal: 0, //已归档答题卡数量
  171 + tableData: null,
  172 + total: 0,
  173 + page: 1,
  174 + size: 20,
  175 + };
  176 + },
  177 + async created() {
  178 + this.userName = this.$store.getters.info.name || "";
  179 + await this._QueryClassList();
  180 + await this._QuerySubjectList();
  181 + this._QueryData();
  182 + this._QueryTypeList();
  183 + },
  184 + methods: {
  185 + toAdd(query) {
  186 + let routerItem = {
  187 + path: "/examinationPaperAdd",
  188 + };
  189 + query ? (routerItem["query"] = { ...query }) : "";
  190 + this.$router.push(routerItem);
  191 + },
  192 + toEdit(item) {
  193 + this.$router.push({
  194 + path: "/examinationPaperEdit",
  195 + query: {
  196 + paperId: item.id,
  197 + },
  198 + });
  199 + },
  200 + handleDropdownClick(value, item) {
  201 + //更多
  202 + const that = this;
  203 + switch (value) {
  204 + case 2:
  205 + //复制
  206 + that.toAdd({ type: 2, paperId: item.id });
  207 + break;
  208 + case 3:
  209 + //归档
  210 + that.recovery(item);
  211 + break;
  212 + }
  213 + },
  214 + changePage(page) {
  215 + this.page = page;
  216 + this._QueryData(this.query.title);
  217 + },
  218 + async recovery(item) {
  219 + //归档
  220 + const { data, status, info } = await this.$request.modifyPaper({
  221 + paperId: item.id,
  222 + status: 2,
  223 + });
  224 + if (status === 0) {
  225 + let type = this.query.title ? 1 : 0;
  226 + this.page = 1;
  227 + this._QueryData(type);
  228 + } else {
  229 + this.$message.error(info);
  230 + }
  231 + },
  232 + //切换班级
  233 + async changClazz() {
  234 + await this._QuerySubjectList();
  235 + this._QueryData(false);
  236 + },
  237 + // 查找答题卡类型
  238 + async _QueryTypeList() {
  239 + const { data, status, info } = await this.$request.fetchTypeNames({
  240 + classId: this.query.classId,
  241 + type: 0,
  242 + });
  243 + if (status === 0) {
  244 + this.typeList =
  245 + data.list.map((item) => {
  246 + return {
  247 + value: item.tagId,
  248 + label: item.tag,
  249 + };
  250 + }) || [];
  251 + this.typeList.unshift({
  252 + value: "",
  253 + label: "请选择标签",
  254 + });
  255 + } else {
  256 + this.$message.error(info);
  257 + }
  258 + },
  259 + // 查找班级
  260 + async _QueryClassList() {
  261 + this.loading = true;
  262 + const { data, status, info } = await this.$request.fetchClassList();
  263 + console.log(status);
  264 + if (status === 0) {
  265 + if (!!data.list) {
  266 + this.classList =
  267 + data.list?.map((item) => {
  268 + return {
  269 + value: item.classId,
  270 + label: item.className,
  271 + };
  272 + }) || [];
  273 + this.query.classId = this.classList[0]?.value;
  274 + }
  275 + } else {
  276 + this.$message.error(info);
  277 + }
  278 + },
  279 + // 查找科目
  280 + async _QuerySubjectList() {
  281 + const { data, status, info } = await this.$request.fetchSubjectList({
  282 + classId: this.query.classId,
  283 + });
  284 + if (status === 0) {
  285 + this.subjectList =
  286 + data.subjectNames?.map((item) => {
  287 + return {
  288 + value: item,
  289 + label: item,
  290 + };
  291 + }) || [];
  292 + this.query.subjectName = this.subjectList[0]?.value;
  293 + } else {
  294 + this.$message.error(info);
  295 + }
  296 + },
  297 + async _QueryData(type) {
  298 + this.loading = true;
  299 + //获取答题卡列表
  300 + let query = {};
  301 + if (!type) {
  302 + this.query.title = "";
  303 + query = { ...this.query };
  304 + } else {
  305 + query = { title: this.query.title };
  306 + this.query.tagId = "";
  307 + // this.query.subjectName = "";
  308 + }
  309 + query.classId = this.query.classId;
  310 + query.subjectName = this.query.subjectName;
  311 + for (let key in query) {
  312 + if (!query[key]) {
  313 + query[key] = null;
  314 + }
  315 + }
  316 + if (!query.classId) {
  317 + this.total = 0;
  318 + this.tableData = [];
  319 + this.loading = false;
  320 + return;
  321 + }
  322 + this.loading = true;
  323 + const { data, status, info } = await this.$request.fetchPaperList({
  324 + ...query,
  325 + status: 1,
  326 + page: this.page,
  327 + size: this.size,
  328 + });
  329 + this.loading = false;
  330 + if (status === 0) {
  331 + this.archivedTotal = data.archivedTotal;
  332 + this.total = data.total;
  333 + this.tableData = (data.list && [...data.list]) || [];
  334 + } else {
  335 + this.$message.error(info);
  336 + }
  337 + },
  338 + },
  339 +};
  340 +</script>
  341 +
  342 +<style scoped lang="scss">
  343 +.tips {
  344 + display: flex;
  345 + padding-left: 30px;
  346 + line-height: 16px;
  347 + font-size: 14px;
  348 + color: #999;
  349 + margin-bottom: 10px;
  350 +}
  351 +.content {
  352 + margin: 0 20px;
  353 + background: #f8f8f8;
  354 + padding: 12px;
  355 + border-radius: 20px;
  356 + .item {
  357 + display: flex;
  358 + align-items: center;
  359 + width: 100%;
  360 + overflow: hidden;
  361 + box-sizing: border-box;
  362 + padding: 12px;
  363 + border-radius: 20px;
  364 + background: #fff;
  365 + margin-bottom: 12px;
  366 + &:last-of-type {
  367 + margin-bottom: 0;
  368 + }
  369 + .pic-box {
  370 + width: 80px;
  371 + height: 80px;
  372 + border-radius: 10px;
  373 + margin-right: 10px;
  374 + flex-shrink: 0;
  375 + background: #667ffd;
  376 + text-align: center;
  377 + color: #fff;
  378 + font-weight: 500;
  379 + .i-box {
  380 + padding-top: 10px;
  381 + font-size: 32px;
  382 + margin-bottom: 3px;
  383 + }
  384 + }
  385 + .info {
  386 + min-height: 80px;
  387 + flex: 1;
  388 + overflow: hidden;
  389 + display: flex;
  390 + flex-direction: column;
  391 + justify-content: space-between;
  392 + .s-line {
  393 + padding: 0 5px;
  394 + color: #e2e2e2;
  395 + }
  396 + .title {
  397 + font-size: 16px;
  398 + color: #222;
  399 + font-weight: 500;
  400 + .label {
  401 + display: inline-block;
  402 + font-size: 12px;
  403 + color: #2e9afe;
  404 + line-height: 16px;
  405 + padding: 0 10px;
  406 + border: 1px solid #2e9afe;
  407 + border-radius: 10px;
  408 + transform: translateY(-2px);
  409 + }
  410 + }
  411 + .person {
  412 + color: #666;
  413 + }
  414 + }
  415 + .clazz {
  416 + font-size: 14px;
  417 + font-weight: 500;
  418 + position: relative;
  419 + position: relative;
  420 + &.active {
  421 + color: #667ffd;
  422 + }
  423 + .el-icon-success {
  424 + position: absolute;
  425 + right: 0;
  426 + top: -5px;
  427 + color: #667ffd;
  428 + }
  429 + &:last-of-type {
  430 + .el-icon-success {
  431 + right: -18px;
  432 + }
  433 + }
  434 + }
  435 + .btn-box {
  436 + flex-shrink: 0;
  437 + .edit {
  438 + margin-right: 12px;
  439 + }
  440 + }
  441 + .icon-refresh{
  442 + margin-left:20px;
  443 + cursor: pointer;
  444 + &:hover{
  445 + color:#2e9afe
  446 + }
  447 + }
  448 + }
  449 +}
  450 +.answer-header {
  451 + .sel-box {
  452 + .sel {
  453 + min-width: 160px;
  454 + }
  455 + :deep(.el-cascader__tags) {
  456 + flex-wrap: nowrap;
  457 + }
  458 + }
  459 +}
  460 +.dialog-footer {
  461 + text-align: center;
  462 + :deep(.el-button) {
  463 + border-radius: 20px;
  464 + padding: 8px 20px 7px;
  465 + margin: 0 12px;
  466 + }
  467 +}
  468 +</style>
0 469 \ No newline at end of file
... ...
src/views/examinationPaper/recycle.vue renamed to src/views/personal/examinationPaper/recycle.vue
src/views/personal/portrait/detail.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <back-box>
  4 + <template slot="title">
  5 + <span>学生测练表现</span>
  6 + </template>
  7 + </back-box>
  8 + <div class="answer-header">
  9 + <div class="sel-box">
  10 + <el-select
  11 + class="sel"
  12 + multiple
  13 + v-model="query.subjectNames"
  14 + placeholder="选择科目"
  15 + collapse-tags
  16 + @change="changeSub"
  17 + >
  18 + <el-option
  19 + v-for="item in subjectList"
  20 + :key="item.value"
  21 + :label="item.label"
  22 + :value="item.value"
  23 + >
  24 + </el-option>
  25 + </el-select>
  26 + <div class="d1">
  27 + <el-date-picker
  28 + v-model="query.startDay"
  29 + type="date"
  30 + @change="handleChangeTimeStart"
  31 + placeholder="选择日期时间"
  32 + value-format="yyyy-MM-dd"
  33 + >
  34 + </el-date-picker>
  35 + ~
  36 + <el-date-picker
  37 + v-model="query.endDay"
  38 + type="date"
  39 + placeholder="选择日期时间"
  40 + @change="handleChangeTimeEnd"
  41 + value-format="yyyy-MM-dd"
  42 + >
  43 + </el-date-picker>
  44 + </div>
  45 + <p class="p1">
  46 + <span @click="setDate(1)" :class="[date == 1 ? 'active' : '', 's1']"
  47 + >今天</span
  48 + >
  49 + <span @click="setDate(2)" :class="[date == 2 ? 'active' : '', 's1']"
  50 + >本周</span
  51 + >
  52 + <span @click="setDate(3)" :class="[date == 3 ? 'active' : '', 's1']"
  53 + >本月</span
  54 + >
  55 + <span @click="setDate(4)" :class="[date == 4 ? 'active' : '', 's1']"
  56 + >本季度</span
  57 + >
  58 + </p>
  59 + <el-button type="primary" round @click="_QueryData()">筛选</el-button>
  60 + </div>
  61 + </div>
  62 + <div class="page-content">
  63 + <p class="tips">张三 (学号:1301) 测练总次数:16</p>
  64 + <div class="chart">
  65 + <template v-if="type == 1">
  66 + <radarChart id="radarChart" :params="radarChartData" />
  67 + </template>
  68 + <template v-else>
  69 + <lineChart id="lineChart" :params="lineChartData" :xAxis="xAxis" />
  70 + </template>
  71 + </div>
  72 + <div class="tab-box">
  73 + <el-table
  74 + v-show="type == 1"
  75 + :data="tableData"
  76 + border
  77 + style="width: 100%"
  78 + >
  79 + <el-table-column
  80 + prop="subjectName"
  81 + label="科目"
  82 + align="center"
  83 + ></el-table-column>
  84 + <el-table-column
  85 + prop="cl"
  86 + label="参与测练数"
  87 + align="center"
  88 + ></el-table-column>
  89 + <el-table-column
  90 + prop="bg"
  91 + label="班最高分"
  92 + align="center"
  93 + ></el-table-column>
  94 + <el-table-column
  95 + prop="bp"
  96 + label="班平均分"
  97 + align="center"
  98 + ></el-table-column>
  99 + <el-table-column
  100 + prop="br"
  101 + label="个人得分"
  102 + align="center"
  103 + ></el-table-column>
  104 + </el-table>
  105 + <el-table
  106 + v-show="type == 2"
  107 + :data="tableData"
  108 + border
  109 + style="width: 100%"
  110 + >
  111 + <el-table-column
  112 + prop="subjectName"
  113 + label="课堂测练"
  114 + align="center"
  115 + ></el-table-column>
  116 + <el-table-column
  117 + prop="br"
  118 + label="个人成绩"
  119 + align="center"
  120 + ></el-table-column>
  121 + <el-table-column
  122 + prop="bp"
  123 + label="平均成绩"
  124 + align="center"
  125 + ></el-table-column>
  126 + <el-table-column
  127 + prop="pm"
  128 + label="班级排名"
  129 + align="center"
  130 + ></el-table-column>
  131 + </el-table>
  132 + </div>
  133 + </div>
  134 + </div>
  135 +</template>
  136 +
  137 +<script>
  138 +import { formatDate } from "utils";
  139 +import radarChart from "@/components/charts/radarChart";
  140 +import lineChart from "@/components/charts/lineChart";
  141 +export default {
  142 + components: { radarChart, lineChart },
  143 + data() {
  144 + return {
  145 + id: "",
  146 + classId: "",
  147 + subjectNames: [],
  148 + type: 1,
  149 + query: {
  150 + //搜索条件
  151 + subjectNames: [],
  152 + startDay: "",
  153 + endDay: "",
  154 + day: "",
  155 + },
  156 + date: "",
  157 + subjectList: [], //科目
  158 + radarChartData: {
  159 + indicator: [
  160 + { name: "语文" },
  161 + { name: "数学" },
  162 + { name: "地理" },
  163 + { name: "物理" },
  164 + { name: "化学" },
  165 + { name: "生物" },
  166 + ],
  167 + num: [
  168 + {
  169 + name: "班最高分",
  170 + value: [100, 99, 98, 99, 100, 100],
  171 + },
  172 + {
  173 + name: "班平均分",
  174 + value: [95, 85, 90, 86, 92, 87],
  175 + },
  176 + {
  177 + name: "本人得分",
  178 + value: [90, 80, 95, 82, 95, 89],
  179 + },
  180 + ],
  181 + },
  182 + lineChartData: [
  183 + {
  184 + name: "班平均分",
  185 + value: [95, 85, 90, 86, 92],
  186 + },
  187 + {
  188 + name: "个人成绩",
  189 + value: [90, 80, 95, 82, 95],
  190 + },
  191 + {
  192 + name: "班级排名",
  193 + value: [2, 3, 1, 5, 6],
  194 + },
  195 + ],
  196 + xAxis: ["卷1", "卷2", "卷3", "卷4", "卷5"],
  197 + tableData: [
  198 + {
  199 + subjectName: "语文",
  200 + cl: 5,
  201 + bg: 100,
  202 + bp: 90,
  203 + br: 95,
  204 + pm: 1,
  205 + },
  206 + ],
  207 + };
  208 + },
  209 + async created() {
  210 + this.id = this.$route.query.id;
  211 + this.classId = this.$route.query.classId;
  212 + this.subjectNames = this.$route.query.subjectNames?.split(",") || [];
  213 + this.type =
  214 + this.subjectNames.length > 1 || this.subjectNames.length == 0 ? 1 : 2;
  215 + await this._QuerySubjectList();
  216 + await this.setDate(1);
  217 + let startDay = this.query?.startDay;
  218 + if (!startDay) {
  219 + this.query.startDay = new Date();
  220 + this.query.endDay = new Date();
  221 + }
  222 + },
  223 + methods: {
  224 + changeSub(val) {
  225 + let sub;
  226 + if (val && val.length) {
  227 + let leng = val.length - 1;
  228 + sub = val[leng];
  229 + }
  230 + this.query.subjectNames = val.filter((item) => {
  231 + return sub != "全部" ? item != "全部" : item == "全部";
  232 + });
  233 + this.type =
  234 + this.query.subjectNames.length > 1 ||
  235 + this.query.subjectNames.length == 0
  236 + ? 1
  237 + : 2;
  238 + },
  239 + setDate(index) {
  240 + const that = this;
  241 + this.date = index == this.date ? "" : index;
  242 + let aYear = new Date().getFullYear();
  243 + let aMonth = new Date().getMonth() + 1;
  244 + that.query.day = "";
  245 + that.query.startDay = "";
  246 + that.query.endDay = "";
  247 + switch (index) {
  248 + case 1:
  249 + that.query.day = formatDate(new Date(), "yyyy-MM-dd");
  250 + that.query.startDay = that.query.day;
  251 + that.query.endDay = that.query.day;
  252 + that.tabIndex = 1;
  253 + break;
  254 + case 2:
  255 + let day = new Date().getDay();
  256 + if (day == 0) {
  257 + //中国式星期天是一周的最后一天
  258 + day = 7;
  259 + }
  260 + day--;
  261 + let aTime = new Date().getTime() - 24 * 60 * 60 * 1000 * day;
  262 + that.query.startDay = formatDate(new Date(aTime), "yyyy-MM-dd");
  263 + that.query.endDay = formatDate(new Date(), "yyyy-MM-dd");
  264 + break;
  265 + case 3:
  266 + aMonth = aMonth < 10 ? "0" + aMonth : aMonth;
  267 + that.query.startDay = `${aYear}-${aMonth}-01`;
  268 + that.query.endDay = formatDate(new Date(), "yyyy-MM-dd");
  269 + break;
  270 + case 4:
  271 + if (aMonth > 0 && aMonth < 4) {
  272 + aMonth = "1";
  273 + } else if (aMonth > 3 && aMonth < 7) {
  274 + aMonth = "4";
  275 + } else if (aMonth > 6 && aMonth < 10) {
  276 + aMonth = "7";
  277 + } else {
  278 + aMonth = "10";
  279 + }
  280 +
  281 + aMonth = aMonth < 10 ? "0" + aMonth : aMonth;
  282 + that.query.startDay = `${aYear}-${aMonth}-01`;
  283 + that.query.endDay = formatDate(new Date(), "yyyy-MM-dd");
  284 + break;
  285 + }
  286 + this.page = 1;
  287 + this._QueryData();
  288 + },
  289 + handleChangeTimeStart(val) {
  290 + this.query.day = "";
  291 + this.date = "";
  292 + if (this.query.endDay) {
  293 + if (new Date(val).getTime() > new Date(this.query.endDay).getTime()) {
  294 + this.$message.error("任务结束时间不能任务开始时间前面,请重新设置");
  295 + this.query.startDay = "";
  296 + }
  297 + }
  298 + },
  299 + handleChangeTimeEnd(val) {
  300 + this.query.day = "";
  301 + this.date = "";
  302 + if (this.query.startDay) {
  303 + if (new Date(val).getTime() < new Date(this.query.startDay).getTime()) {
  304 + this.$message.error("任务结束时间不能任务开始时间前面,请重新设置");
  305 + this.query.endDay = "";
  306 + }
  307 + }
  308 + },
  309 + async _QuerySubjectList() {
  310 + const { data, status, info } = await this.$request.tSubjectList({
  311 + classId: this.classId,
  312 + });
  313 + if (status === 0) {
  314 + this.subjectList =
  315 + data.subjectNames?.map((item) => {
  316 + return {
  317 + value: item,
  318 + label: item,
  319 + };
  320 + }) || [];
  321 + this.subjectList.unshift({
  322 + value: "全部",
  323 + label: "全部",
  324 + });
  325 + if (this.subjectNames.length == 0) {
  326 + this.query.subjectNames.push(this.subjectList[0]?.value);
  327 + }
  328 + } else {
  329 + this.$message.error(info);
  330 + }
  331 + },
  332 + async _QueryData() {
  333 + this.loading = true;
  334 + let query = {};
  335 + for (let key in this.query) {
  336 + if (this.query[key] != "") {
  337 + query[key] = this.query[key];
  338 + }
  339 + }
  340 + return;
  341 + const { data, status, info } = await this.$request.studentList({
  342 + ...query,
  343 + });
  344 + this.loading = false;
  345 + if (status === 0) {
  346 + } else {
  347 + this.$message.error(info);
  348 + }
  349 + },
  350 + },
  351 +};
  352 +</script>
  353 +
  354 +<style lang="scss" scoped>
  355 +.page-content {
  356 + padding: 0 20px;
  357 +}
  358 +.tips {
  359 + padding-left: 20px;
  360 + font-size: 16px;
  361 + color: #a3a4a8;
  362 + font-weight: 400;
  363 + font-style: normal;
  364 +}
  365 +.chart {
  366 + height: 300px;
  367 +}
  368 +.tab-box {
  369 + padding: 20px;
  370 +}
  371 +</style>
0 372 \ No newline at end of file
... ...
src/views/personal/portrait/index.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <back-box>
  4 + <template slot="title">
  5 + <span>{{ "学生画像" }}</span>
  6 + </template>
  7 + </back-box>
  8 + <div class="answer-header">
  9 + <div class="sel-box">
  10 + <el-select
  11 + class="sel"
  12 + v-model="query.classId"
  13 + placeholder="选择班级"
  14 + @change="changeclass"
  15 + >
  16 + <el-option
  17 + v-for="item in classList"
  18 + :key="item.value"
  19 + :label="item.label"
  20 + :value="item.value"
  21 + >
  22 + </el-option>
  23 + </el-select>
  24 + <div class="d1">
  25 + <el-date-picker
  26 + v-model="query.startDay"
  27 + type="date"
  28 + @change="handleChangeTimeStart"
  29 + placeholder="选择日期时间"
  30 + value-format="yyyy-MM-dd"
  31 + >
  32 + </el-date-picker>
  33 + ~
  34 + <el-date-picker
  35 + v-model="query.endDay"
  36 + type="date"
  37 + placeholder="选择日期时间"
  38 + @change="handleChangeTimeEnd"
  39 + value-format="yyyy-MM-dd"
  40 + >
  41 + </el-date-picker>
  42 + </div>
  43 + <p class="p1">
  44 + <span @click="setDate(1)" :class="[date == 1 ? 'active' : '', 's1']"
  45 + >今天</span
  46 + >
  47 + <span @click="setDate(2)" :class="[date == 2 ? 'active' : '', 's1']"
  48 + >本周</span
  49 + >
  50 + <span @click="setDate(3)" :class="[date == 3 ? 'active' : '', 's1']"
  51 + >本月</span
  52 + >
  53 + <span @click="setDate(4)" :class="[date == 4 ? 'active' : '', 's1']"
  54 + >本季度</span
  55 + >
  56 + </p>
  57 + <el-button type="primary" round @click="_QueryData()">筛选</el-button>
  58 + </div>
  59 + </div>
  60 + <div class="page-content">
  61 + <ul class="stu-ul">
  62 + <li class="stu-li" v-for="item in tableData" :key="item.studentId">
  63 + <div class="tx">
  64 + <i class="fa fa-mortar-board"></i>
  65 + <p>{{ item.studentCode }}</p>
  66 + </div>
  67 + <div class="info">
  68 + <p class="p1">
  69 + {{ item.studentName }} <i class="fa fa-calculator"></i>
  70 + {{ item.clickerSn }}
  71 + </p>
  72 + <p class="p2">{{ item.sectionName + item.className }}</p>
  73 + <p class="p3">最近活跃:{{ item.time }}</p>
  74 + </div>
  75 + <el-button
  76 + class="btn"
  77 + type="primary"
  78 + round
  79 + circle
  80 + icon="el-icon-right"
  81 + @click="linkDetail(item)"
  82 + ></el-button>
  83 + </li>
  84 + </ul>
  85 + </div>
  86 + </div>
  87 +</template>
  88 +
  89 +<script>
  90 +import { formatDate } from "utils";
  91 +import BusEvent from "@/utils/busEvent";
  92 +export default {
  93 + data() {
  94 + return {
  95 + query: {
  96 + //搜索条件
  97 + classId: "",
  98 + startDay: "",
  99 + endDay: "",
  100 + day: "",
  101 + },
  102 + date: "",
  103 + classList: [],
  104 + tableData: [
  105 + {
  106 + studentId: "111",
  107 + studentName: "丁方菲",
  108 + studentCode: 123321,
  109 + clickerSn: 32312,
  110 + sectionName: "初一",
  111 + className: "三班",
  112 + classId:12,
  113 + time: "2022-11-04 18:09:49",
  114 + },
  115 + ],
  116 + page: 1,
  117 + size: 20,
  118 + total: 0,
  119 + };
  120 + },
  121 + async created() {
  122 + await this._QueryClassList();
  123 + await this.setDate(1);
  124 + let startDay = this.query?.startDay;
  125 + if (!startDay) {
  126 + this.query.startDay = new Date();
  127 + this.query.endDay = new Date();
  128 + }
  129 + },
  130 + activated() {
  131 + const that = this;
  132 + BusEvent.$on("keepAlive", async function () {
  133 + await that._QueryClassList();
  134 + await that.setDate(1);
  135 + let startDay = that.query?.startDay;
  136 + if (!startDay) {
  137 + that.query.startDay = new Date();
  138 + that.query.endDay = new Date();
  139 + }
  140 + });
  141 + },
  142 + methods: {
  143 + linkDetail(obj) {
  144 + this.$router.push({
  145 + path: "/portraitDetail",
  146 + query: {
  147 + id: obj.studentId,
  148 + classId:obj.classId,
  149 + },
  150 + });
  151 + },
  152 + changeclass() {
  153 + this.page = 1;
  154 + this._QueryData();
  155 + },
  156 + setDate(index) {
  157 + const that = this;
  158 + this.date = index == this.date ? "" : index;
  159 + let aYear = new Date().getFullYear();
  160 + let aMonth = new Date().getMonth() + 1;
  161 + that.query.day = "";
  162 + that.query.startDay = "";
  163 + that.query.endDay = "";
  164 + switch (index) {
  165 + case 1:
  166 + that.query.day = formatDate(new Date(), "yyyy-MM-dd");
  167 + that.query.startDay = that.query.day;
  168 + that.query.endDay = that.query.day;
  169 + that.tabIndex = 1;
  170 + break;
  171 + case 2:
  172 + let day = new Date().getDay();
  173 + if (day == 0) {
  174 + //中国式星期天是一周的最后一天
  175 + day = 7;
  176 + }
  177 + day--;
  178 + let aTime = new Date().getTime() - 24 * 60 * 60 * 1000 * day;
  179 + that.query.startDay = formatDate(new Date(aTime), "yyyy-MM-dd");
  180 + that.query.endDay = formatDate(new Date(), "yyyy-MM-dd");
  181 + break;
  182 + case 3:
  183 + aMonth = aMonth < 10 ? "0" + aMonth : aMonth;
  184 + that.query.startDay = `${aYear}-${aMonth}-01`;
  185 + that.query.endDay = formatDate(new Date(), "yyyy-MM-dd");
  186 + break;
  187 + case 4:
  188 + if (aMonth > 0 && aMonth < 4) {
  189 + aMonth = "1";
  190 + } else if (aMonth > 3 && aMonth < 7) {
  191 + aMonth = "4";
  192 + } else if (aMonth > 6 && aMonth < 10) {
  193 + aMonth = "7";
  194 + } else {
  195 + aMonth = "10";
  196 + }
  197 +
  198 + aMonth = aMonth < 10 ? "0" + aMonth : aMonth;
  199 + that.query.startDay = `${aYear}-${aMonth}-01`;
  200 + that.query.endDay = formatDate(new Date(), "yyyy-MM-dd");
  201 + break;
  202 + }
  203 + this.page = 1;
  204 + this._QueryData();
  205 + },
  206 + handleChangeTimeStart(val) {
  207 + this.query.day = "";
  208 + this.date = "";
  209 + if (this.query.endDay) {
  210 + if (new Date(val).getTime() > new Date(this.query.endDay).getTime()) {
  211 + this.$message.error("任务结束时间不能任务开始时间前面,请重新设置");
  212 + this.query.startDay = "";
  213 + }
  214 + }
  215 + },
  216 + handleChangeTimeEnd(val) {
  217 + this.query.day = "";
  218 + this.date = "";
  219 + if (this.query.startDay) {
  220 + if (new Date(val).getTime() < new Date(this.query.startDay).getTime()) {
  221 + this.$message.error("任务结束时间不能任务开始时间前面,请重新设置");
  222 + this.query.endDay = "";
  223 + }
  224 + }
  225 + },
  226 + async _QueryClassList() {
  227 + const { data, status, info } = await this.$request.tClassList();
  228 + if (status === 0) {
  229 + this.classList = data.list.map((item) => {
  230 + return {
  231 + value: item.classId,
  232 + label: item.className,
  233 + };
  234 + });
  235 + this.query.classId = this.classList[0]?.value;
  236 + } else {
  237 + this.$message.error(info);
  238 + }
  239 + },
  240 + async _QueryData() {
  241 + this.loading = true;
  242 + let query = {};
  243 + for (let key in this.query) {
  244 + if (this.query[key] != "") {
  245 + query[key] = this.query[key];
  246 + }
  247 + }
  248 + return;
  249 + const { data, status, info } = await this.$request.studentList({
  250 + ...query,
  251 + page: this.page,
  252 + size: this.size,
  253 + });
  254 + this.loading = false;
  255 + if (status === 0) {
  256 + this.tableData = (data?.list && [...data?.list]) || [];
  257 + this.total = data.count || 0;
  258 + } else {
  259 + this.$message.error(info);
  260 + }
  261 + },
  262 + },
  263 +};
  264 +</script>
  265 +
  266 +<style lang="scss" scoped>
  267 +.page-content {
  268 + padding: 0 20px;
  269 +}
  270 +.stu-ul {
  271 + border-radius: 20px;
  272 + background: #f8f8f8;
  273 + padding: 12px;
  274 + .stu-li {
  275 + border-radius: 12px;
  276 + background: #fff;
  277 + display: flex;
  278 + align-items: center;
  279 + padding: 12px 10px;
  280 + margin-bottom: 16px;
  281 + &:last-of-type {
  282 + margin-bottom: 0;
  283 + }
  284 + .tx {
  285 + width: 80px;
  286 + height: 80px;
  287 + background: #667ffd;
  288 + border-radius: 8px;
  289 + text-align: center;
  290 + display: flex;
  291 + flex-direction: column;
  292 + justify-content: center;
  293 + align-items: center;
  294 + font-size: 16px;
  295 + color: #fff;
  296 + margin-right: 12px;
  297 + .fa-mortar-board {
  298 + font-size: 32px;
  299 + }
  300 + }
  301 + .info {
  302 + flex: 1;
  303 + .p1 {
  304 + font-size: 18px;
  305 + color: #333;
  306 + .fa-calculator {
  307 + font-size: 20px;
  308 + color: #a6a6a6;
  309 + margin: 0 5px 10px 10px;
  310 + }
  311 + }
  312 + .p2 {
  313 + font-size: 16px;
  314 + padding-bottom: 6px;
  315 + }
  316 + .p3 {
  317 + font-size: 16px;
  318 + color: #7f7f7f;
  319 + }
  320 + }
  321 + .btn {
  322 + margin-right: 40px;
  323 + &.is-circle {
  324 + padding: 8px;
  325 + }
  326 + :deep(.el-icon-right) {
  327 + font-size: 22px;
  328 + font-weight: bold;
  329 + }
  330 + }
  331 + }
  332 +}
  333 +</style>
0 334 \ No newline at end of file
... ...
src/views/personal/setUp/student.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <back-box>
  4 + <template slot="title">
  5 + <span>学生管理</span>
  6 + </template>
  7 + <template slot="btns">
  8 + <el-tooltip effect="dark" content="学生导入" placement="bottom">
  9 + <el-button
  10 + type="primary"
  11 + icon="el-icon-upload2"
  12 + size="mini"
  13 + plain
  14 + circle
  15 + @click="diaUp = true"
  16 + ></el-button>
  17 + </el-tooltip>
  18 + <el-tooltip effect="dark" content="添加班级" placement="bottom">
  19 + <el-button
  20 + type="primary"
  21 + icon="el-icon-plus"
  22 + size="mini"
  23 + plain
  24 + circle
  25 + @click="addClass"
  26 + ></el-button>
  27 + </el-tooltip>
  28 + </template>
  29 + </back-box>
  30 +
  31 + <div class="page-content">
  32 + <div class="stu-box">
  33 + <div class="stu-list">
  34 + <p class="h-title">班级列表</p>
  35 + <ul class="stu-ul">
  36 + <li
  37 + class="stu-item"
  38 + v-for="item in classList"
  39 + :key="item.id"
  40 + :class="query.classId == item.id ? 'active' : ''"
  41 + @click="classDetail(item)"
  42 + >
  43 + <i class="el-icon-edit-outline" @click.stop="setClass(item)"></i>
  44 + {{ item.className }}({{ item.studentCount }})
  45 + </li>
  46 + </ul>
  47 + </div>
  48 + <div class="stu-detail">
  49 + <p class="stu-head">
  50 + {{ formStu.className }}所有学生({{ studentList.length }})
  51 + <i
  52 + v-if="query.classId"
  53 + class="el-icon-circle-plus-outline"
  54 + @click="openAddDia"
  55 + ></i>
  56 + </p>
  57 + <ul class="s-ul" v-loading="loading">
  58 + <li
  59 + class="s-li"
  60 + v-for="(item, index) in studentList"
  61 + :key="item.id"
  62 + >
  63 + <el-popconfirm
  64 + title="确定删除吗?"
  65 + @confirm="removeStu(item, index)"
  66 + >
  67 + <i class="el-icon-delete" slot="reference"></i>
  68 + </el-popconfirm>
  69 + <p class="name">{{ item.studentName }}</p>
  70 + <p class="p1">答题器:{{ item.clickerSn || "--" }}</p>
  71 + <p class="p1">长学号:{{ item.studentCode }}</p>
  72 + <p class="p1">短学号:{{ item.shortNumber || "--" }}</p>
  73 + </li>
  74 + </ul>
  75 + <el-empty
  76 + :image-size="100"
  77 + v-if="!studentList.length && loading == false"
  78 + description="没有更多数据"
  79 + ></el-empty>
  80 + </div>
  81 + </div>
  82 + </div>
  83 + <el-dialog title="添加学生" :visible.sync="diaStu" width="400">
  84 + <el-form
  85 + ref="formBox"
  86 + class="form-box"
  87 + :model="formStu"
  88 + :rules="rulesStu"
  89 + label-width="160px"
  90 + >
  91 + <el-form-item label="所在班级:">
  92 + <span>{{ formStu.className }}</span>
  93 + </el-form-item>
  94 + <el-form-item label="学生姓名:" prop="studentName">
  95 + <el-col :span="10">
  96 + <el-input
  97 + maxlength="30"
  98 + placeholder="输入学生姓名"
  99 + v-model.trim="formStu.studentName"
  100 + />
  101 + </el-col>
  102 + </el-form-item>
  103 + <el-form-item label="长学号:" prop="studentCode">
  104 + <el-col :span="10">
  105 + <el-input
  106 + maxlength="12"
  107 + placeholder="输入学生长学号"
  108 + v-model.trim="formStu.studentCode"
  109 + />
  110 + </el-col>
  111 + </el-form-item>
  112 + <el-form-item label="短学号:">
  113 + <el-col :span="10">
  114 + <el-input maxlength="12" v-model.trim="formStu.shortNumber" />
  115 + </el-col>
  116 + </el-form-item>
  117 + <el-form-item label="性别:">
  118 + <el-radio-group v-model="formStu.sex">
  119 + <el-radio :label="1">男</el-radio>
  120 + <el-radio :label="2">女</el-radio>
  121 + </el-radio-group>
  122 + </el-form-item>
  123 + <el-form-item label="答题器编码:">
  124 + <el-col :span="10">
  125 + <el-input v-model.trim="formStu.clickerSn" />
  126 + </el-col>
  127 + </el-form-item>
  128 + </el-form>
  129 + <div class="dialog-footer" slot="footer">
  130 + <el-button @click="addStu">确 定</el-button>
  131 + <el-button @click="diaStu = false">取 消</el-button>
  132 + </div>
  133 + </el-dialog>
  134 + <el-dialog
  135 + :title="formClass.classId ? '修改班级' : '添加班级'"
  136 + :visible.sync="diaClass"
  137 + width="400"
  138 + >
  139 + <el-form
  140 + class="form-box"
  141 + ref="formClass"
  142 + :model="formClass"
  143 + :rules="rulesClass"
  144 + label-width="160px"
  145 + >
  146 + <el-form-item label="班级名称:" prop="className">
  147 + <el-col :span="10">
  148 + <el-input maxlength="30" v-model.trim="formClass.className" />
  149 + </el-col>
  150 + </el-form-item>
  151 + <el-form-item label="入学年份:" prop="intoSchoolYear">
  152 + <el-col :span="10">
  153 + <el-date-picker
  154 + v-model="formClass.intoSchoolYear"
  155 + type="year"
  156 + value-format="yyyy"
  157 + placeholder="选择年"
  158 + >
  159 + </el-date-picker>
  160 + </el-col>
  161 + </el-form-item>
  162 + </el-form>
  163 + <div class="dialog-footer" slot="footer">
  164 + <el-button @click="saveClass">确 定</el-button>
  165 + <el-button @click="diaClass = false">取 消</el-button>
  166 + <el-button v-if="formClass.classId" @click="removeClass"
  167 + >删 除</el-button
  168 + >
  169 + </div>
  170 + </el-dialog>
  171 + <el-dialog title="学生导入" :visible.sync="diaUp" width="600">
  172 + <up-load
  173 + id="downTeacher"
  174 + :url="url"
  175 + @upSuccess="upSuccess"
  176 + fileName="学生模板"
  177 + >
  178 + <p class="down-txt" slot="down">
  179 + 通过Excel名单导入学生模板,点击
  180 + <el-link type="danger" @click="downExcel">模板下载</el-link> 。
  181 + </p>
  182 + </up-load>
  183 + <div class="dialog-footer" slot="footer">
  184 + <el-button @click="diaUp = false">取 消</el-button>
  185 + </div>
  186 + </el-dialog>
  187 + </div>
  188 +</template>
  189 +
  190 +<script>
  191 +import { downloadFile, getBlob } from "@/utils";
  192 +export default {
  193 + data() {
  194 + return {
  195 + diaUp: false,
  196 + url: "/api_html/school/manager/importStudentClicker",
  197 + diaStu: false,
  198 + diaClass: false,
  199 + loading: false,
  200 + query: {
  201 + classId: "",
  202 + },
  203 + formStu: {
  204 + className: "",
  205 + studentName: "",
  206 + studentCode: "",
  207 + shortNumber: "",
  208 + sex: 1,
  209 + clickerSn: "",
  210 + },
  211 + rulesStu: {
  212 + studentName: [
  213 + { required: true, message: "请输入学生名称", trigger: "blur" },
  214 + ],
  215 + studentCode: [
  216 + { required: true, message: "请输入学生长学号", trigger: "blur" },
  217 + ],
  218 + },
  219 + formClass: {
  220 + classId: "",
  221 + className: "",
  222 + intoSchoolYear: "",
  223 + },
  224 + rulesClass: {
  225 + className: [
  226 + { required: true, message: "请输入班级名称", trigger: "blur" },
  227 + ],
  228 + },
  229 + classList: [],
  230 + studentList: [],
  231 + };
  232 + },
  233 + async created() {
  234 + // await this._QueryClass();
  235 + // this._QueryData();
  236 + },
  237 + methods: {
  238 + addClass() {
  239 + this.formClass.classId = "";
  240 + this.formClass.className = "";
  241 + this.formClass.intoSchoolYear = "";
  242 + this.diaClass = true;
  243 + },
  244 + openAddDia() {
  245 + this.formStu.studentName = "";
  246 + this.formStu.studentCode = "";
  247 + this.formStu.shortNumber = "";
  248 + this.formStu.sex = 1;
  249 + this.formStu.clickerSn = "";
  250 + this.diaStu = true;
  251 + },
  252 + classDetail(obj) {
  253 + this.query.classId = obj.id;
  254 + this.formStu.className = obj.className;
  255 + this._QueryData();
  256 + },
  257 + setClass(obj) {
  258 + this.formClass.classId = obj.id;
  259 + this.formClass.className = obj.className;
  260 + this.formClass.intoSchoolYear = obj.intoSchoolYear + "";
  261 + this.diaClass = true;
  262 + },
  263 + saveClass() {
  264 + this.$refs.formClass.validate(async (valid) => {
  265 + if (valid) {
  266 + const { data, status, info } = await this.$request.updateClass({
  267 + classId: this.formClass.classId,
  268 + className: this.formClass.className,
  269 + intoSchoolYear: this.formClass.intoSchoolYear,
  270 + });
  271 + if (status === 0) {
  272 + this.$message.success("修改成功");
  273 + this.diaClass = false;
  274 + this._QueryClass();
  275 + } else {
  276 + this.$message.error(info);
  277 + }
  278 + } else {
  279 + this.$message.warning("输入有误请检查!");
  280 + return false;
  281 + }
  282 + });
  283 + },
  284 + async removeClass() {
  285 + let stuNum = 0;
  286 + this.classList.map((item) => {
  287 + if (item.classId == this.formClass.classId) {
  288 + stuNum = item.studentCount;
  289 + }
  290 + });
  291 + if (stuNum) {
  292 + this.$message.warning("有学生的班级不能删除!");
  293 + return;
  294 + }
  295 + const { data, status, info } = await this.$request.updateClass({
  296 + classId: this.formClass.classId,
  297 + });
  298 + if (status === 0) {
  299 + this.$message.success("删除成功");
  300 + this.diaClass = false;
  301 + this._QueryClass();
  302 + } else {
  303 + this.$message.error(info);
  304 + }
  305 + },
  306 + upSuccess(res) {
  307 + this.$message.closeAll();
  308 + this.$message({
  309 + showClose: true,
  310 + message: `成功(${res.data.success})`,
  311 + type: "success",
  312 + duration: 5000,
  313 + });
  314 + //导入成功
  315 + this.diaUp = false;
  316 + this._QueryData();
  317 + },
  318 + async downExcel() {
  319 + this.loadingDown = true;
  320 + let { data, info, status } =
  321 + await this.$request.studentClickerTemplateUrl();
  322 + this.loadingDown = false;
  323 + if (status == 0) {
  324 + getBlob(data.downloadUrl).then((res) => {
  325 + downloadFile("学生答题器绑定模板", res);
  326 + });
  327 + } else {
  328 + this.$message.error(info);
  329 + }
  330 + },
  331 + async removeStu(obj, index) {
  332 + const { data, status, info } = await this.$request.delStudent({
  333 + studentId: obj.id,
  334 + });
  335 + if (status === 0) {
  336 + this.$message.success("删除成功");
  337 + this.studentList.splice(index, 1);
  338 + this._QueryClass();
  339 + } else {
  340 + this.$message.error(info);
  341 + }
  342 + },
  343 + addStu() {
  344 + let query = {};
  345 + for (let key in this.formStu) {
  346 + if (key != "className" && this.formStu[key]) {
  347 + query[key] = this.formStu[key];
  348 + }
  349 + }
  350 + this.loading = true;
  351 + this.$refs.formBox.validate(async (valid) => {
  352 + if (valid) {
  353 + let hasName = this.studentList.find((item) => {
  354 + return item.studentName == this.formStu.studentName;
  355 + });
  356 + console.log(hasName);
  357 + if (hasName) {
  358 + this.$message.warning("学生姓名已存在");
  359 + return;
  360 + }
  361 + const { data, status, info } = await this.$request.addStudent({
  362 + classId: this.query.classId,
  363 + ...query,
  364 + });
  365 + this.loading = false;
  366 + console.log(status);
  367 + if (status === 0) {
  368 + this.$message.success(info);
  369 + this.diaStu = false;
  370 + this._QueryClass();
  371 + this._QueryData();
  372 + } else {
  373 + this.$message.error(info);
  374 + }
  375 + } else {
  376 + this.$message.error("数据有误,请检查!");
  377 + }
  378 + });
  379 + },
  380 + async _QueryData() {
  381 + this.loading = true;
  382 + const { data, status, info } = await this.$request.studentList({
  383 + ...this.query,
  384 + });
  385 + this.loading = false;
  386 + console.log(status);
  387 + if (status === 0) {
  388 + this.studentList = data.list || [];
  389 + } else {
  390 + this.$message.error(info);
  391 + }
  392 + },
  393 +
  394 + async _QueryClass() {
  395 + const { data, status, info } = await this.$request.schoolClassList();
  396 + if (status === 0) {
  397 + this.classList = [...data.list] || [];
  398 + this.query.classId = this.classList[0]?.id;
  399 + this.formStu.className = this.classList[0]?.className;
  400 + } else {
  401 + this.$message.error(info);
  402 + }
  403 + },
  404 + },
  405 +};
  406 +</script>
  407 +
  408 +<style lang="scss" scoped>
  409 +.page-content {
  410 + padding: 20px;
  411 +}
  412 +.stu-box {
  413 + display: flex;
  414 + background: #f8f8f8;
  415 + border-radius: 10px;
  416 + overflow: hidden;
  417 + .stu-list {
  418 + min-width: 200px;
  419 + max-height: 80vh;
  420 + .h-title {
  421 + height: 40px;
  422 + line-height: 40px;
  423 + background: #eee;
  424 + }
  425 + .stu-item {
  426 + font-size: 16px;
  427 + color: #7f7f7f;
  428 + line-height: 36px;
  429 + cursor: pointer;
  430 + padding-left: 12px;
  431 + position: relative;
  432 + .el-icon-edit-outline {
  433 + position: absolute;
  434 + top: 8px;
  435 + right: 12px;
  436 + font-size: 20px;
  437 + display: none;
  438 + &:hover {
  439 + color: #667ffd;
  440 + }
  441 + }
  442 + &:hover {
  443 + background: #eee;
  444 + .el-icon-edit-outline {
  445 + display: block;
  446 + }
  447 + }
  448 + &.active {
  449 + color: #667ffd;
  450 + background: #eee;
  451 + }
  452 + }
  453 + .sel {
  454 + width: 180px;
  455 + :deep(.el-input__inner) {
  456 + font-size: 16px;
  457 + padding-left: 0;
  458 + border: none;
  459 + background: transparent;
  460 + }
  461 + :deep(.el-icon-arrow-up:before) {
  462 + content: "\e78f";
  463 + }
  464 + }
  465 + }
  466 + .stu-detail {
  467 + flex: 1;
  468 + border-left: 0.5px solid #eee;
  469 + .stu-head {
  470 + padding: 12px 20px;
  471 + color: #7f7f7f;
  472 + position: relative;
  473 + .el-icon-circle-plus-outline {
  474 + position: absolute;
  475 + top: 10px;
  476 + right: 20px;
  477 + font-size: 24px;
  478 + color: #7f7f7f;
  479 + cursor: pointer;
  480 + }
  481 + }
  482 + .s-ul {
  483 + display: flex;
  484 + flex-wrap: wrap;
  485 + padding-left: 20px;
  486 + .s-li {
  487 + width: 180px;
  488 + height: 120px;
  489 + box-shadow: 2px 2px 5px #7f7f7f;
  490 + border-radius: 10px;
  491 + margin: 0 20px 20px 0;
  492 + padding: 0 12px 5px;
  493 + position: relative;
  494 + .el-icon-delete {
  495 + position: absolute;
  496 + top: 8px;
  497 + right: 8px;
  498 + font-size: 18px;
  499 + padding: 2px;
  500 + display: none;
  501 + cursor: pointer;
  502 + &:hover {
  503 + color: #667ffd;
  504 + }
  505 + }
  506 + &:hover {
  507 + .el-icon-delete {
  508 + display: block;
  509 + }
  510 + }
  511 + .name {
  512 + text-align: center;
  513 + font-size: 16px;
  514 + line-height: 18px;
  515 + padding: 10px 0;
  516 + }
  517 + .p1 {
  518 + color: #7f7f7f;
  519 + line-height: 20px;
  520 + padding-bottom: 5px;
  521 + }
  522 + }
  523 + }
  524 + }
  525 +}
  526 +</style>
0 527 \ No newline at end of file
... ...
src/views/personal/test/analysis.vue 0 → 100644
  1 +<template>
  2 + <div ref="main" class="page-container">
  3 + <back-box>
  4 + <template slot="title">
  5 + <span>单卷分析</span>
  6 + </template>
  7 + </back-box>
  8 + <div class="tips" v-if="paperModifyLog.modifiedTime">
  9 + <p class="tips-p">
  10 + <i class="fa fa-bell-o"></i>
  11 + {{
  12 + `${paperModifyLog.modifiedTime} ${paperModifyLog.realName}`
  13 + }}修改了答案,是否重新记分?
  14 + </p>
  15 + <div class="btn-box">
  16 + <el-button type="danger" round @click="_ReScore" size="mini"
  17 + >重新计分</el-button
  18 + >
  19 + <el-button
  20 + type="danger"
  21 + round
  22 + plain
  23 + size="mini"
  24 + @click="paperModifyLog.modifiedTime = ''"
  25 + >暂时不计</el-button
  26 + >
  27 + </div>
  28 + </div>
  29 + <div class="page-content">
  30 + <div class="tab-box">
  31 + <span
  32 + class="tab-item"
  33 + :class="type == 1 ? 'active' : ''"
  34 + @click="setType(1)"
  35 + >试题分析</span
  36 + >
  37 + <span
  38 + class="tab-item"
  39 + :class="type == 2 ? 'active' : ''"
  40 + @click="setType(2)"
  41 + >成绩排名</span
  42 + >
  43 + <span
  44 + class="tab-item"
  45 + :class="type == 3 ? 'active' : ''"
  46 + @click="setType(3)"
  47 + >小题分报表</span
  48 + >
  49 + <span
  50 + class="tab-item"
  51 + :class="type == 4 ? 'active' : ''"
  52 + @click="setType(4)"
  53 + >作答明细表</span
  54 + >
  55 + </div>
  56 + <div id="print-content" class="table-box" v-loading="loading">
  57 + <el-table
  58 + :max-height="tableMaxHeight"
  59 + v-show="type == 1"
  60 + :data="tableData"
  61 + border
  62 + style="width: 100%"
  63 + >
  64 + <el-table-column
  65 + prop="questionIndex"
  66 + label="题号"
  67 + align="center"
  68 + fixed
  69 + width="60"
  70 + ></el-table-column>
  71 + <el-table-column
  72 + prop="questionType"
  73 + label="题型"
  74 + align="center"
  75 + fixed
  76 + width="90"
  77 + ><template slot-scope="scope">{{
  78 + setSubPro(scope.row.questionType)
  79 + }}</template></el-table-column
  80 + >
  81 + <el-table-column
  82 + prop="score"
  83 + width="90"
  84 + label="满分值"
  85 + sortable
  86 + align="center"
  87 + ></el-table-column>
  88 + <el-table-column
  89 + width="100"
  90 + prop="highestScore"
  91 + label="班最高分"
  92 + sortable
  93 + align="center"
  94 + ></el-table-column>
  95 + <el-table-column
  96 + width="100"
  97 + prop="lowestScore"
  98 + label="班最低分"
  99 + sortable
  100 + align="center"
  101 + ></el-table-column>
  102 + <el-table-column
  103 + width="100"
  104 + prop="avgScore"
  105 + label="班平均分"
  106 + sortable
  107 + align="center"
  108 + ></el-table-column>
  109 + <el-table-column
  110 + prop="classScoringRate"
  111 + width="110"
  112 + sortable
  113 + label="班级得分率"
  114 + align="center"
  115 + ><template slot-scope="scoped"
  116 + ><span :class="scoped.row.classScoringRate < 60 ? 'error' : ''"
  117 + >{{ scoped.row.classScoringRate }}%</span
  118 + ></template
  119 + ></el-table-column
  120 + >
  121 + <el-table-column prop="correctAnswer" label="答案" align="center"
  122 + ><template slot-scope="scoped">{{
  123 + scoped.row.correctAnswer == 1
  124 + ? "✓"
  125 + : scoped.row.correctAnswer == 2
  126 + ? "✗"
  127 + : scoped.row.correctAnswer
  128 + }}</template>
  129 + </el-table-column>
  130 + <el-table-column
  131 + v-for="(item, index) in optionsList"
  132 + :key="index"
  133 + :label="item.title"
  134 + :prop="'count' + index"
  135 + align="center"
  136 + width="90"
  137 + ><template slot-scope="scope"
  138 + ><p class="persent">
  139 + {{
  140 + scope.row.questionType == "5"
  141 + ? ""
  142 + : scope.row["option" + index]
  143 + ? `${scope.row["option" + index]}(${
  144 + scope.row["persent" + index]
  145 + })`
  146 + : ""
  147 + }}
  148 + </p></template
  149 + >
  150 + </el-table-column>
  151 + </el-table>
  152 + <div class="hui-box" v-show="type == 1">
  153 + <span class="s-txt">汇总</span>
  154 + <ul class="hui-ul">
  155 + <li class="hui-li">
  156 + <span class="hui-s s1">主观题</span>
  157 + <span class="hui-s s1">{{ examReport.subjectiveScore }}</span>
  158 + <span class="hui-s s2">{{
  159 + examReport.subjectiveHighestScore
  160 + }}</span>
  161 + <span class="hui-s s2">{{
  162 + examReport.subjectiveLowestScore
  163 + }}</span>
  164 + <span class="hui-s s2">{{ examReport.subjectiveAvgScore }}</span>
  165 + <span
  166 + class="hui-s s3"
  167 + :class="
  168 + examReport.subjectiveClassScoringRate < 60 ? 'error' : ''
  169 + "
  170 + >{{ examReport.subjectiveClassScoringRate }}%</span
  171 + >
  172 + </li>
  173 + <li class="hui-li">
  174 + <span class="hui-s s1">客观题</span>
  175 + <span class="hui-s s1">{{ examReport.objectiveScore }}</span>
  176 + <span class="hui-s s2">{{
  177 + examReport.objectiveHighestScore
  178 + }}</span>
  179 + <span class="hui-s s2">{{
  180 + examReport.objectiveLowestScore
  181 + }}</span>
  182 + <span class="hui-s s2">{{ examReport.objectiveAvgScore }}</span>
  183 + <span
  184 + class="hui-s s3"
  185 + :class="
  186 + examReport.objectiveClassScoringRate < 60 ? 'error' : ''
  187 + "
  188 + >{{ examReport.objectiveClassScoringRate }}%</span
  189 + >
  190 + </li>
  191 + <li class="hui-li">
  192 + <span class="hui-s s1">整卷</span>
  193 + <span class="hui-s s1">{{ examReport.examPaperScore }}</span>
  194 + <span class="hui-s s2">{{ examReport.highestScore }}</span>
  195 + <span class="hui-s s2">{{ examReport.lowestScore }}</span>
  196 + <span class="hui-s s2">{{ examReport.avgScore }}</span>
  197 + <span
  198 + class="hui-s s3"
  199 + :class="examReport.classScoringRate < 60 ? 'error' : ''"
  200 + >{{ examReport.classScoringRate }}%</span
  201 + >
  202 + </li>
  203 + </ul>
  204 + </div>
  205 + <el-table
  206 + v-show="type == 2"
  207 + :max-height="tableMaxHeight"
  208 + :data="tableData2"
  209 + border
  210 + style="width: 100%"
  211 + :default-sort="{ prop: 'dadui', order: 'descending' }"
  212 + >
  213 + <el-table-column
  214 + prop="studentCode"
  215 + label="学号"
  216 + align="center"
  217 + fixed
  218 + ></el-table-column>
  219 + <el-table-column
  220 + prop="studentName"
  221 + label="姓名"
  222 + fixed
  223 + align="center"
  224 + ></el-table-column>
  225 + <el-table-column
  226 + prop="examScore"
  227 + label="总分"
  228 + sortable
  229 + align="center"
  230 + ></el-table-column>
  231 + <el-table-column
  232 + prop="scoringRate"
  233 + label="得分率"
  234 + sortable
  235 + align="center"
  236 + ><template slot-scope="scope"
  237 + >{{ scope.row.scoringRate }}%</template
  238 + ></el-table-column
  239 + >
  240 + <el-table-column
  241 + prop="classRank"
  242 + label="班名"
  243 + sortable
  244 + align="center"
  245 + ></el-table-column>
  246 + <el-table-column label="客观题" align="center">
  247 + <el-table-column
  248 + prop="objectiveExamScore"
  249 + label="得分"
  250 + align="center"
  251 + ></el-table-column>
  252 + <el-table-column
  253 + prop="objectiveScoringRate"
  254 + label="得分率"
  255 + align="center"
  256 + ><template slot-scope="scope"
  257 + >{{ scope.row.objectiveScoringRate }}%</template
  258 + ></el-table-column
  259 + >
  260 + </el-table-column>
  261 + <el-table-column label="主观题" align="center">
  262 + <el-table-column
  263 + prop="subjectiveExamScore"
  264 + label="得分"
  265 + align="center"
  266 + ></el-table-column>
  267 + <el-table-column
  268 + prop="subjectiveScoringRate"
  269 + label="得分率"
  270 + align="center"
  271 + ><template slot-scope="scope"
  272 + >{{ scope.row.subjectiveScoringRate }}%</template
  273 + ></el-table-column
  274 + >
  275 + </el-table-column>
  276 + </el-table>
  277 + <el-table
  278 + v-show="type == 3"
  279 + :max-height="tableMaxHeight"
  280 + :data="tableData2"
  281 + border
  282 + style="width: 100%"
  283 + :default-sort="{ prop: '', order: 'descending' }"
  284 + >
  285 + <el-table-column
  286 + prop="studentCode"
  287 + label="学号"
  288 + fixed
  289 + align="center"
  290 + ></el-table-column>
  291 + <el-table-column
  292 + prop="studentName"
  293 + label="姓名"
  294 + fixed
  295 + align="center"
  296 + ></el-table-column>
  297 + <el-table-column
  298 + prop="examScore"
  299 + label="总分"
  300 + sortable
  301 + align="center"
  302 + ></el-table-column>
  303 + <el-table-column label="分数组成" align="center">
  304 + <el-table-column
  305 + prop="objectiveExamScore"
  306 + label="客观题分"
  307 + align="center"
  308 + ></el-table-column>
  309 + <el-table-column
  310 + prop="subjectiveExamScore"
  311 + label="主观题分"
  312 + align="center"
  313 + ></el-table-column>
  314 + </el-table-column>
  315 + <el-table-column
  316 + align="center"
  317 + v-for="(item, index) in questionList"
  318 + :key="index"
  319 + :label="'Q' + item.id"
  320 + :prop="'score' + item.id"
  321 + :width="questionList.length > 9 ? 60 : 'auto'"
  322 + >
  323 + </el-table-column>
  324 + </el-table>
  325 + <el-table
  326 + :max-height="tableMaxHeight"
  327 + v-show="type == 4"
  328 + :data="tableData2"
  329 + border
  330 + style="width: 100%"
  331 + :default-sort="{ prop: '', order: 'descending' }"
  332 + >
  333 + <el-table-column
  334 + prop="studentCode"
  335 + label="学号"
  336 + fixed
  337 + align="center"
  338 + width="110"
  339 + ></el-table-column>
  340 + <el-table-column
  341 + prop="studentName"
  342 + label="姓名"
  343 + fixed
  344 + align="center"
  345 + ></el-table-column>
  346 + <el-table-column
  347 + prop="className"
  348 + label="班级"
  349 + align="center"
  350 + ></el-table-column>
  351 + <el-table-column
  352 + prop="examScore"
  353 + label="总分"
  354 + sortable
  355 + align="center"
  356 + ></el-table-column>
  357 + <el-table-column
  358 + align="center"
  359 + v-for="(item, index) in questionList"
  360 + :key="index"
  361 + :label="'Q' + item.id"
  362 + :width="questionList.length > 9 ? 60 : 'auto'"
  363 + >
  364 + <template slot-scope="scope">
  365 + <span v-if="tableData[index]?.questionType == 5">*</span>
  366 + <span
  367 + v-else-if="scope.row['answer' + item.id]"
  368 + :class="scope.row['isRight' + item.id] ? '' : 'error'"
  369 + >
  370 + {{ scope.row["answer" + item.id] }}
  371 + </span>
  372 + <span
  373 + v-else
  374 + :class="scope.row['questionType' + item.id] == 5 ? '' : 'error'"
  375 + >-</span
  376 + >
  377 + </template>
  378 + </el-table-column>
  379 + </el-table>
  380 + </div>
  381 +
  382 + <div class="down">
  383 + <div>
  384 + <el-button
  385 + @click="exportData"
  386 + type="primary"
  387 + plain
  388 + round
  389 + icon="fa fa-cloud-download"
  390 + >导出报表</el-button
  391 + >
  392 + <el-button
  393 + @click="print"
  394 + type="primary"
  395 + plain
  396 + round
  397 + icon="el-icon-printer"
  398 + >打印</el-button
  399 + >
  400 + </div>
  401 + <div>
  402 + <el-button
  403 + v-if="examReport.subjectiveScore != 0"
  404 + @click="diaUp = true"
  405 + type="primary"
  406 + round
  407 + v-loading="exportLoading"
  408 + >导入主观题分数</el-button
  409 + >
  410 + <el-button
  411 + @click="edit"
  412 + type="primary"
  413 + v-if="examReport.subjectiveScore != examReport.examPaperScore"
  414 + round
  415 + >查看题目</el-button
  416 + >
  417 + </div>
  418 + </div>
  419 + <el-dialog title="导入主观题分数" :visible.sync="diaUp" width="600">
  420 + <up-load
  421 + :url="url"
  422 + :examId="id"
  423 + @upSuccess="upSuccess"
  424 + fileName="教师名单"
  425 + >
  426 + <template slot="down">
  427 + <p class="down-txt">
  428 + 第一步:下载模板并编辑完成学生分数
  429 + <el-link type="danger" @click="downExcel">模板下载</el-link> 。
  430 + </p>
  431 + <p class="down-txt">第二步:上传完成编辑的模板文件并导入。</p>
  432 + </template>
  433 + </up-load>
  434 + <div class="dialog-footer" slot="footer">
  435 + <el-button @click="diaUp = false">取 消</el-button>
  436 + </div>
  437 + </el-dialog>
  438 + </div>
  439 + </div>
  440 +</template>
  441 +
  442 +<script>
  443 +import { downloadFile, tablePrint } from "@/utils";
  444 +export default {
  445 + data() {
  446 + return {
  447 + tableMaxHeight: 600,
  448 + loading: false,
  449 + exportLoading: false,
  450 + diaUp: false,
  451 + url: "/api_html/teaching/importSubjectiveScore",
  452 + id: "",
  453 + title: "",
  454 + score: "",
  455 + type: 1,
  456 + paperModifyLog: {
  457 + realName: "",
  458 + modifiedTime: "",
  459 + },
  460 + examReport: {
  461 + subjectiveScore: 0,
  462 + subjectiveHighestScore: "",
  463 + subjectiveLowestScore: "",
  464 + subjectiveAvgScore: "",
  465 + subjectiveClassScoringRate: "",
  466 + objectiveScore: "",
  467 + objectiveHighestScore: "",
  468 + objectiveLowestScore: "",
  469 + objectiveAvgScore: "",
  470 + objectiveClassScoringRate: "",
  471 + examPaperScore: "",
  472 + highestScore: "",
  473 + lowestScore: "",
  474 + avgScore: "",
  475 + classScoringRate: "",
  476 + },
  477 + tableData: [],
  478 + optionsList: [],
  479 + tableData2: [],
  480 + questionList: [],
  481 + page: 1,
  482 + size: 20,
  483 + total: 0,
  484 + };
  485 + },
  486 + created() {
  487 + this.id = this.$route.query.id;
  488 + this.title = this.$route.query.title || "";
  489 + this._QueryData();
  490 + },
  491 + methods: {
  492 + print() {
  493 + tablePrint("print-content");
  494 + },
  495 + upSuccess() {
  496 + //导入成功
  497 + this.diaUp = false;
  498 + this._QueryData();
  499 + },
  500 + setType(type) {
  501 + console.log(this.$refs.main.offsetHeight - 50);
  502 + this.tableMaxHeight = this.$refs.main.offsetHeight;
  503 + this.type = type;
  504 + },
  505 + setSubPro(type) {
  506 + let tit;
  507 + switch (type) {
  508 + case 2:
  509 + tit = "单选题";
  510 + break;
  511 + case 3:
  512 + tit = "多选题";
  513 + break;
  514 + case 4:
  515 + tit = "判断题";
  516 + break;
  517 + case 5:
  518 + tit = "主观题";
  519 + break;
  520 + }
  521 + return tit;
  522 + },
  523 + edit() {
  524 + this.$router.push({
  525 + path: "/examinationPaperEdit",
  526 + query: {
  527 + paperId: this.id,
  528 + title: this.title,
  529 + type: 2,
  530 + },
  531 + });
  532 + },
  533 + changePage(page) {
  534 + this.page = page;
  535 + this.examQuestionReport();
  536 + },
  537 + async downExcel() {
  538 + let data = await this.$request.subjectiveScoreTemplate({
  539 + examId: this.id,
  540 + });
  541 + if (data && !data.code) {
  542 + let blob = new Blob([data], {
  543 + type: "application/vnd.ms-excel;charset=utf-8",
  544 + });
  545 + downloadFile(`主观题模版.xlsx`, blob);
  546 + } else {
  547 + this.$message.error(data.info);
  548 + }
  549 + },
  550 + async _QueryData() {
  551 + this.examDetail();
  552 + this.examStudentReport();
  553 + this.examQuestionReport();
  554 + },
  555 + async examDetail() {
  556 + //详情
  557 + this.loading = true;
  558 + let { data, info, status } = await this.$request.examDetail({
  559 + examId: this.id,
  560 + });
  561 + this.loading = false;
  562 + if (status === 0) {
  563 + if (data.paperModifyLog) {
  564 + this.paperModifyLog = { ...data?.paperModifyLog };
  565 + }
  566 + this.examReport = { ...data?.examReport };
  567 + } else {
  568 + this.$message.error(info);
  569 + }
  570 + },
  571 + async _ReScore() {
  572 + //重新记分
  573 + this.loading = true;
  574 + let { data, info, status } = await this.$request.reScore({
  575 + examId: this.id,
  576 + });
  577 + this.loading = false;
  578 + if (status === 0) {
  579 + this.$message.success(info);
  580 + this._QueryData();
  581 + this.paperModifyLog.modifiedTime = "";
  582 + this.paperModifyLog.realName = "";
  583 + } else {
  584 + this.$message.error(info);
  585 + }
  586 + },
  587 + async examStudentReport() {
  588 + //成绩排名-小题分-作答明细
  589 + this.loading = true;
  590 + let { data, info, status } = await this.$request.examStudentReport({
  591 + examId: this.id,
  592 + });
  593 + this.loading = false;
  594 + if (status === 0) {
  595 + let optionsList = [];
  596 + this.tableData2 = data?.list.map((item) => {
  597 + let params = {};
  598 +
  599 + const detail = JSON.parse(item.detail);
  600 + if (detail.length > optionsList.length) {
  601 + optionsList = [...detail];
  602 + }
  603 + detail.map((items, index) => {
  604 + params["que" + items.id] = items.id;
  605 + params["score" + items.id] = String(items.score).includes(".")
  606 + ? Number(items.score).toFixed(2)
  607 + : items.score;
  608 + params["answer" + items.id] =
  609 + items.answer == 1 ? "✓" : items.answer == 2 ? "✗" : items.answer;
  610 + params["isRight" + items.id] = items.isRight;
  611 + params["questionType" + items.id] = items.questionType;
  612 + });
  613 + return {
  614 + ...item,
  615 + ...params,
  616 + };
  617 + });
  618 + console.log();
  619 + this.questionList = optionsList.sort((a, b) => {
  620 + return a.id - b.id;
  621 + });
  622 + } else {
  623 + this.$message.error(info);
  624 + }
  625 + },
  626 + async examQuestionReport() {
  627 + //试题分析
  628 + this.loading = true;
  629 + let { data, info, status } = await this.$request.examQuestionReport({
  630 + examId: this.id,
  631 + page: this.page,
  632 + // size: this.size,
  633 + size: 9999,
  634 + });
  635 + this.loading = false;
  636 + if (status === 0) {
  637 + let optionsList = [{}, {}, {}, {}, {}];
  638 + let tableData = data?.list.map((item) => {
  639 + let params = {};
  640 + const detail = JSON.parse(item.detail);
  641 + let lastOPtion = detail?.find((item) => {
  642 + return item.option == "未答";
  643 + });
  644 + let defaultArr = detail?.filter((item) => {
  645 + return item.option != "未答";
  646 + });
  647 +
  648 + optionsList.map((items, index) => {
  649 + if (index != 4) {
  650 + params["count" + index] =
  651 + defaultArr[index]?.option != "未答"
  652 + ? defaultArr[index]?.count
  653 + : "";
  654 + params["persent" + index] =
  655 + defaultArr[index]?.option != "未答"
  656 + ? defaultArr[index]?.persent
  657 + : "";
  658 + params["option" + index] =
  659 + defaultArr[index]?.option != "未答"
  660 + ? defaultArr[index]?.option == 1
  661 + ? "✓"
  662 + : defaultArr[index]?.option == 2
  663 + ? "✗"
  664 + : defaultArr[index]?.option
  665 + : "";
  666 + items["title"] = "选项" + (index + 1);
  667 + } else {
  668 + items["title"] = "未答";
  669 + params["count" + index] = lastOPtion.count;
  670 + params["persent" + index] = lastOPtion.persent;
  671 + params["option" + index] = "?";
  672 + }
  673 + });
  674 + return {
  675 + ...item,
  676 + ...params,
  677 + };
  678 + });
  679 + this.tableData = tableData.sort((a, b) => {
  680 + return a.questionIndex - b.questionIndex;
  681 + });
  682 + this.optionsList = [...optionsList];
  683 + this.total = data.count;
  684 + this.setType(1);
  685 + } else {
  686 + this.$message.error(info);
  687 + }
  688 + },
  689 + //导出
  690 + async exportData() {
  691 + if (this.exportLoading == true) return;
  692 + this.exportLoading = true;
  693 + const data = await this.$request.exportExamReport({
  694 + examId: this.id,
  695 + });
  696 + this.exportLoading = false;
  697 + if (data) {
  698 + let blob = new Blob([data], {
  699 + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  700 + });
  701 + downloadFile("即时测-单卷测练报表.xlsx", blob);
  702 + } else {
  703 + this.$message.error("下载失败");
  704 + }
  705 + },
  706 + },
  707 +};
  708 +</script>
  709 +<style>
  710 +div::-webkit-scrollbar {
  711 + width: 3px;
  712 + height: 10px;
  713 +}
  714 +div::-webkit-scrollbar-thumb {
  715 + border-radius: 10px;
  716 + background-color: #ccc;
  717 +}
  718 +</style>
  719 +<style lang="scss" scoped>
  720 +.page-container {
  721 + position: relative;
  722 + height: 100%;
  723 + .table-box {
  724 + min-height: 100%;
  725 + }
  726 +}
  727 +.print-box {
  728 + display: none;
  729 +}
  730 +.persent {
  731 + white-space: nowrap;
  732 + font-size: 14px;
  733 +}
  734 +.error {
  735 + color: #f30;
  736 +}
  737 +.page-content {
  738 + padding: 20px 20px 0;
  739 +}
  740 +.tips {
  741 + height: 48px;
  742 + box-sizing: border-box;
  743 + line-height: 48px;
  744 + padding: 0 16px;
  745 + border: 1px solid #fac7cc;
  746 + border-radius: 5px;
  747 + background-color: #ffebec;
  748 + font-size: 14px;
  749 + color: #fd9795;
  750 + margin: 10px 20px 0 20px;
  751 + display: flex;
  752 + &-p {
  753 + flex: 1;
  754 + }
  755 + .fa-bell-o {
  756 + font-size: 18px;
  757 + margin-right: 5px;
  758 + }
  759 +}
  760 +.tab-box {
  761 + width: 800px;
  762 + margin: 0 auto 12px;
  763 + background: #f8f8f8;
  764 + border-radius: 20px;
  765 + display: flex;
  766 + user-select: none;
  767 + .tab-item {
  768 + flex: 1;
  769 + height: 40px;
  770 + line-height: 40px;
  771 + text-align: center;
  772 + font-size: 16px;
  773 + color: #666;
  774 + font-weight: 500;
  775 + background: transparent;
  776 + border-radius: 20px;
  777 + cursor: pointer;
  778 + &.active {
  779 + background: #667ffd;
  780 + color: #fff;
  781 + }
  782 + }
  783 +}
  784 +.down {
  785 + padding-top: 20px;
  786 + width: 100%;
  787 + display: flex;
  788 + justify-content: space-between;
  789 +}
  790 +.hui-box {
  791 + display: flex;
  792 + text-align: center;
  793 + .s-txt {
  794 + width: 61px;
  795 + line-height: 144px;
  796 + background: #e2e2e2;
  797 + font-size: 16px;
  798 + color: #fff;
  799 + font-weight: 700;
  800 + }
  801 + .hui-ul {
  802 + border-top: 1px solid #e2e2e2;
  803 + }
  804 + .hui-li {
  805 + display: flex;
  806 + .hui-s {
  807 + height: 48px;
  808 + line-height: 48px;
  809 + border-right: 1px solid #e2e2e2;
  810 + border-bottom: 1px solid #e2e2e2;
  811 + box-sizing: border-box;
  812 + }
  813 + .s1 {
  814 + width: 90px;
  815 + }
  816 + .s2 {
  817 + width: 100px;
  818 + }
  819 + .s3 {
  820 + width: 110px;
  821 + }
  822 + }
  823 +}
  824 +</style>
0 825 \ No newline at end of file
... ...
src/views/personal/test/index.vue 0 → 100644
  1 +<template>
  2 + <div ref="main" class="page-container">
  3 + <back-box>
  4 + <template slot="title">
  5 + <span>即时测-数据报表</span>
  6 + </template>
  7 + </back-box>
  8 + <div class="answer-header">
  9 + <div class="sel-box">
  10 + <el-select
  11 + class="sel"
  12 + v-model="query.classId"
  13 + placeholder="选择班级"
  14 + @change="changeclass"
  15 + >
  16 + <el-option
  17 + v-for="item in classList"
  18 + :key="item.value"
  19 + :label="item.label"
  20 + :value="item.value"
  21 + >
  22 + </el-option>
  23 + </el-select>
  24 + <el-select
  25 + class="sel"
  26 + multiple
  27 + v-model="query.subjectNames"
  28 + placeholder="选择科目"
  29 + collapse-tags
  30 + @change="changeSub"
  31 + >
  32 + <el-option
  33 + v-for="item in subjectList"
  34 + :key="item.value"
  35 + :label="item.label"
  36 + :value="item.value"
  37 + >
  38 + </el-option>
  39 + </el-select>
  40 + <div class="d1">
  41 + <el-date-picker
  42 + v-model="query.startDay"
  43 + type="date"
  44 + @change="handleChangeTimeStart"
  45 + placeholder="选择日期时间"
  46 + value-format="yyyy-MM-dd"
  47 + >
  48 + </el-date-picker>
  49 + ~
  50 + <el-date-picker
  51 + v-model="query.endDay"
  52 + type="date"
  53 + placeholder="选择日期时间"
  54 + @change="handleChangeTimeEnd"
  55 + value-format="yyyy-MM-dd"
  56 + >
  57 + </el-date-picker>
  58 + </div>
  59 + <p class="p1">
  60 + <span @click="setDate(1)" :class="[date == 1 ? 'active' : '', 's1']"
  61 + >今天</span
  62 + >
  63 + <span @click="setDate(2)" :class="[date == 2 ? 'active' : '', 's1']"
  64 + >本周</span
  65 + >
  66 + <span @click="setDate(3)" :class="[date == 3 ? 'active' : '', 's1']"
  67 + >本月</span
  68 + >
  69 + <span @click="setDate(4)" :class="[date == 4 ? 'active' : '', 's1']"
  70 + >本季度</span
  71 + >
  72 + </p>
  73 + <el-button type="primary" round @click="_QueryData()">筛选</el-button>
  74 + </div>
  75 + </div>
  76 + <div class="table-box">
  77 + <el-radio-group
  78 + v-model="tabIndex"
  79 + @change="changeTab"
  80 + style="margin-bottom: 20px"
  81 + >
  82 + <el-radio-button :label="1">单卷测练报表</el-radio-button>
  83 + <el-radio-button
  84 + :label="2"
  85 + v-show="this.query.startDay != this.query.endDay"
  86 + >阶段测练报表</el-radio-button
  87 + >
  88 + </el-radio-group>
  89 + <div v-show="tabIndex == 1" v-loading="loading">
  90 + <el-table :data="tableData" border style="width: 100%">
  91 + <el-table-column
  92 + prop="title"
  93 + label="试卷名称"
  94 + fixed
  95 + align="center"
  96 + ></el-table-column>
  97 + <el-table-column
  98 + prop="examPaperScore"
  99 + label="卷面分"
  100 + align="center"
  101 + width="100"
  102 + ></el-table-column>
  103 + <el-table-column prop="answeredNum" label="测验人数" align="center"
  104 + ><template slot-scope="scoped">{{
  105 + `${scoped.row.answeredNum}/${scoped.row.classPersonNum}`
  106 + }}</template></el-table-column
  107 + >
  108 + <el-table-column
  109 + prop="examStartTime"
  110 + label="测验时间"
  111 + width="100"
  112 + align="center"
  113 + ></el-table-column>
  114 + <el-table-column prop="avgScore" label="班平均分" align="center"
  115 + ><template slot-scope="scoped">{{
  116 + (scoped.row.subjectiveScore == scoped.row.examPaperScore ||
  117 + scoped.row.answerNum == 0) &&
  118 + scoped.row.recordStatus == 0
  119 + ? "-"
  120 + : scoped.row.avgScore
  121 + }}</template></el-table-column
  122 + >
  123 + <el-table-column prop="highestScore" label="班最高分" align="center"
  124 + ><template slot-scope="scoped">{{
  125 + (scoped.row.subjectiveScore == scoped.row.examPaperScore ||
  126 + scoped.row.answerNum == 0) &&
  127 + scoped.row.recordStatus == 0
  128 + ? "-"
  129 + : scoped.row.highestScore
  130 + }}</template></el-table-column
  131 + >
  132 + <el-table-column prop="lowestScore" label="班最低分" align="center"
  133 + ><template slot-scope="scoped">{{
  134 + (scoped.row.subjectiveScore == scoped.row.examPaperScore ||
  135 + scoped.row.answerNum == 0) &&
  136 + scoped.row.recordStatus == 0
  137 + ? "-"
  138 + : scoped.row.lowestScore
  139 + }}</template></el-table-column
  140 + >
  141 + <el-table-column
  142 + prop="excellenRate"
  143 + label="优秀数(率)"
  144 + sortable
  145 + align="center"
  146 + width="110"
  147 + class-name="p0"
  148 + ><template slot-scope="scoped">
  149 + <p
  150 + v-if="
  151 + (scoped.row.subjectiveScore == scoped.row.examPaperScore ||
  152 + scoped.row.answerNum == 0) &&
  153 + scoped.row.arecordStatus == 0
  154 + "
  155 + >
  156 + "-"
  157 + </p>
  158 + <template v-else>
  159 + <p>{{ scoped.row.excellenNum }}</p>
  160 + <p v-if="scoped.row.excellenNum">
  161 + {{ `(${scoped.row.excellenRate}%)` }}
  162 + </p>
  163 + </template>
  164 + </template></el-table-column
  165 + >
  166 + <el-table-column
  167 + prop="goodRate"
  168 + label="良好数(率)"
  169 + sortable
  170 + align="center"
  171 + width="110"
  172 + class-name="p0"
  173 + ><template slot-scope="scoped">
  174 + <p
  175 + v-if="
  176 + (scoped.row.subjectiveScore == scoped.row.examPaperScore ||
  177 + scoped.row.answerNum == 0) &&
  178 + scoped.row.arecordStatus == 0
  179 + "
  180 + >
  181 + "-"
  182 + </p>
  183 + <template v-else>
  184 + <p>{{ scoped.row.goodNum }}</p>
  185 + <p v-if="scoped.row.goodNum">
  186 + {{ `(${scoped.row.goodRate}%)` }}
  187 + </p>
  188 + </template>
  189 + </template></el-table-column
  190 + >
  191 + <el-table-column
  192 + prop="passRate"
  193 + label="及格数(率)"
  194 + sortable
  195 + align="center"
  196 + width="110"
  197 + class-name="p0"
  198 + ><template slot-scope="scoped">
  199 + <p
  200 + v-if="
  201 + (scoped.row.subjectiveScore == scoped.row.examPaperScore ||
  202 + scoped.row.answerNum == 0) &&
  203 + scoped.row.arecordStatus == 0
  204 + "
  205 + >
  206 + "-"
  207 + </p>
  208 + <template v-else>
  209 + <p>{{ scoped.row.passNum }}</p>
  210 + <p v-if="scoped.row.passNum">
  211 + {{ `(${scoped.row.passRate}%)` }}
  212 + </p>
  213 + </template>
  214 + </template></el-table-column
  215 + >
  216 + <el-table-column
  217 + prop="failedRate"
  218 + label="不及格数(率)"
  219 + sortable
  220 + align="center"
  221 + width="130"
  222 + class-name="p0"
  223 + ><template slot-scope="scoped">
  224 + <p
  225 + v-if="
  226 + (scoped.row.subjectiveScore == scoped.row.examPaperScore ||
  227 + scoped.row.answerNum == 0) &&
  228 + scoped.row.arecordStatus == 0
  229 + "
  230 + >
  231 + "-"
  232 + </p>
  233 + <template v-else>
  234 + <p>{{ scoped.row.failedNum }}</p>
  235 + <p v-if="scoped.row.failedNum">
  236 + {{ `(${scoped.row.failedRate}%)` }}
  237 + </p>
  238 + </template>
  239 + </template></el-table-column
  240 + >
  241 + <el-table-column label="操作" align="center">
  242 + <template slot-scope="scoped">
  243 + <el-tooltip
  244 + v-if="
  245 + scoped.row.answerNum != 0 ||
  246 + (scoped.row.recordStatus != 0 &&
  247 + scoped.row.subjectiveScore == scoped.row.examPaperScore)
  248 + "
  249 + effect="dark"
  250 + content="详情"
  251 + placement="top"
  252 + >
  253 + <el-button
  254 + type="primary"
  255 + circle
  256 + size="mini"
  257 + icon="fa fa-arrow-right"
  258 + @click="linkTo(scoped.row)"
  259 + ></el-button>
  260 + </el-tooltip>
  261 + <el-tooltip
  262 + v-if="
  263 + scoped.row.answerNum == 0 &&
  264 + scoped.row.subjectiveScore != scoped.row.examPaperScore
  265 + "
  266 + effect="dark"
  267 + content="设置答案"
  268 + placement="top"
  269 + >
  270 + <el-button
  271 + type="primary"
  272 + circle
  273 + size="mini"
  274 + icon="fa fa-file-text"
  275 + @click="edit(scoped.row)"
  276 + ></el-button>
  277 + </el-tooltip>
  278 + <el-tooltip
  279 + v-if="
  280 + scoped.row.subjectiveScore == scoped.row.examPaperScore &&
  281 + scoped.row.recordStatus == 0
  282 + "
  283 + effect="dark"
  284 + content="导入主观题"
  285 + placement="top"
  286 + >
  287 + <el-button
  288 + type="primary"
  289 + circle
  290 + size="mini"
  291 + icon="fa fa-cloud"
  292 + @click="uploadSJ(scoped.row)"
  293 + ></el-button>
  294 + </el-tooltip>
  295 + </template>
  296 + </el-table-column>
  297 + </el-table>
  298 + <div class="pagination-box">
  299 + <el-pagination
  300 + small=""
  301 + layout="total,prev, pager, next"
  302 + :hide-on-single-page="true"
  303 + :total="total"
  304 + @current-change="changePage"
  305 + :current-page="page"
  306 + :page-size="size"
  307 + >
  308 + </el-pagination>
  309 + </div>
  310 + </div>
  311 + <div v-show="tabIndex == 2" v-loading="loading">
  312 + <el-empty
  313 + :image-size="100"
  314 + v-if="!tableData.length && loading == false"
  315 + description="没有更多数据"
  316 + ></el-empty>
  317 + <template v-if="tableData.length && loading == false">
  318 + <el-table
  319 + id="print-content"
  320 + :data="tableData"
  321 + :max-height="tableMaxHeight"
  322 + border
  323 + style="width: 100%"
  324 + >
  325 + <el-table-column
  326 + prop="studentCode"
  327 + label="学号"
  328 + align="center"
  329 + fixed
  330 + ></el-table-column>
  331 + <el-table-column
  332 + prop="studentName"
  333 + label="姓名"
  334 + fixed
  335 + align="center"
  336 + >
  337 + <template slot-scope="scoped"
  338 + ><span class="click-b" @click="toPortrait(scoped.row)">
  339 + {{ scoped.row.studentName }}
  340 + </span></template
  341 + >
  342 + </el-table-column>
  343 + <el-table-column
  344 + align="center"
  345 + v-for="(item, index) in answerList"
  346 + :key="index"
  347 + :label="item"
  348 + >
  349 + <el-table-column
  350 + :prop="'examCount' + item"
  351 + label="测练数"
  352 + align="center"
  353 + :class-name="index % 2 == 0 ? 'bg' : ''"
  354 + ></el-table-column>
  355 + <el-table-column
  356 + :prop="'participationCount' + item"
  357 + label="参与数"
  358 + align="center"
  359 + :class-name="index % 2 == 0 ? 'bg' : ''"
  360 + ></el-table-column>
  361 + <el-table-column
  362 + :prop="'score' + item"
  363 + label="总分"
  364 + align="center"
  365 + :class-name="index % 2 == 0 ? 'bg' : ''"
  366 + ></el-table-column>
  367 + <el-table-column
  368 + :prop="'classRank' + item"
  369 + label="班名"
  370 + align="center"
  371 + :class-name="index % 2 == 0 ? 'bg' : ''"
  372 + ></el-table-column>
  373 + </el-table-column>
  374 + </el-table>
  375 + </template>
  376 + </div>
  377 + <p class="down" v-if="tabIndex == 2 && tableData.length">
  378 + <el-button
  379 + type="primary"
  380 + plain
  381 + round
  382 + icon="fa fa-cloud-download"
  383 + @click="downExl"
  384 + >导出报表</el-button
  385 + >
  386 + <el-button
  387 + @click="print"
  388 + type="primary"
  389 + plain
  390 + round
  391 + icon="el-icon-printer"
  392 + >打印</el-button
  393 + >
  394 + </p>
  395 + </div>
  396 + <el-dialog title="导入主观题分数" :visible.sync="diaUp" width="600">
  397 + <up-load
  398 + :url="url"
  399 + :examId="examId"
  400 + @upSuccess="upSuccess"
  401 + fileName="主观题分数"
  402 + v-loading="loadingDown"
  403 + >
  404 + <template slot="down">
  405 + <p class="down-txt">
  406 + 第一步:下载模板并编辑完成学生分数
  407 + <el-link type="danger" @click="downExcel">模板下载</el-link> 。
  408 + </p>
  409 + <p class="down-txt">第二步:上传完成编辑的模板文件并导入。</p>
  410 + </template>
  411 + </up-load>
  412 + <div class="dialog-footer" slot="footer">
  413 + <el-button @click="diaUp = false">取 消</el-button>
  414 + </div>
  415 + </el-dialog>
  416 + </div>
  417 +</template>
  418 +
  419 +<script>
  420 +import { formatDate, downloadFile, tablePrint } from "utils";
  421 +import BusEvent from "@/utils/busEvent";
  422 +export default {
  423 + data() {
  424 + return {
  425 + exportLoading: false,
  426 + tableMaxHeight: 300,
  427 + loading: false,
  428 + diaUp: false,
  429 + loadingDown: false,
  430 + url: "/api_html/teaching/importSubjectiveScore",
  431 + examId: "",
  432 + date: "", //今天-昨天-本周
  433 + query: {
  434 + //搜索条件
  435 + classId: "",
  436 + subjectNames: [],
  437 + startDay: "",
  438 + endDay: "",
  439 + day: "",
  440 + },
  441 + classList: [], //班级
  442 + subjectList: [], //科目
  443 + tabIndex: 1, //选项卡
  444 + tableData: [],
  445 + answerList: [], //设置多卷内容供tableStage表格数据用
  446 + page: 1,
  447 + size: 20,
  448 + total: 0,
  449 + };
  450 + },
  451 + async created() {
  452 + await this._QueryClassList();
  453 + await this._QuerySubjectList();
  454 + await this.setDate(1);
  455 + let startDay = this.query?.startDay;
  456 + if (!startDay) {
  457 + this.query.startDay = new Date();
  458 + this.query.endDay = new Date();
  459 + }
  460 + },
  461 + activated() {
  462 + const that = this;
  463 + BusEvent.$on("keepAlive", async function () {
  464 + await that._QueryClassList();
  465 + await that._QuerySubjectList();
  466 + await that.setDate(1);
  467 + let startDay = that.query?.startDay;
  468 + if (!startDay) {
  469 + that.query.startDay = new Date();
  470 + that.query.endDay = new Date();
  471 + }
  472 + });
  473 + },
  474 + methods: {
  475 + print() {
  476 + tablePrint("print-content");
  477 + },
  478 + changeSub(val) {
  479 + let sub;
  480 + if (val && val.length) {
  481 + let leng = val.length - 1;
  482 + sub = val[leng];
  483 + }
  484 + console.log(val);
  485 + this.query.subjectNames = val.filter((item) => {
  486 + return sub != "全部" ? item != "全部" : item == "全部";
  487 + });
  488 + },
  489 + linkTo(obj) {
  490 + //去详情
  491 + this.$router.push({
  492 + path: "/testAnalysis",
  493 + query: {
  494 + id: obj.id,
  495 + title: obj.title,
  496 + score: obj.examPaperScore,
  497 + },
  498 + });
  499 + },
  500 + toPortrait(obj) {
  501 + let subjectNames = [];
  502 + subjectNames = [...this.query["subjectNames"]];
  503 + if (
  504 + this.query["subjectNames"] &&
  505 + this.query["subjectNames"]?.length == 1 &&
  506 + this.query["subjectNames"][0] == "全部"
  507 + ) {
  508 + subjectNames = this.subjectList.map((item) => {
  509 + return item.value;
  510 + });
  511 + subjectNames?.shift();
  512 + }
  513 + //去学生画像
  514 + this.$router.push({
  515 + path: "/portraitDetail",
  516 + query: {
  517 + id: obj.studentId,
  518 + classId: obj.classId,
  519 + subjectNames: subjectNames.join(","),
  520 + },
  521 + });
  522 + },
  523 + uploadSJ(obj) {
  524 + this.examId = obj.id;
  525 + this.diaUp = true;
  526 + },
  527 + setDate(index) {
  528 + const that = this;
  529 + this.date = index == this.date ? "" : index;
  530 + let aYear = new Date().getFullYear();
  531 + let aMonth = new Date().getMonth() + 1;
  532 + that.query.day = "";
  533 + that.query.startDay = "";
  534 + that.query.endDay = "";
  535 + switch (index) {
  536 + case 1:
  537 + that.query.day = formatDate(new Date(), "yyyy-MM-dd");
  538 + that.query.startDay = that.query.day;
  539 + that.query.endDay = that.query.day;
  540 + that.tabIndex = 1;
  541 + break;
  542 + case 2:
  543 + let day = new Date().getDay();
  544 + if (day == 0) {
  545 + //中国式星期天是一周的最后一天
  546 + day = 7;
  547 + }
  548 + day--;
  549 + let aTime = new Date().getTime() - 24 * 60 * 60 * 1000 * day;
  550 + that.query.startDay = formatDate(new Date(aTime), "yyyy-MM-dd");
  551 + that.query.endDay = formatDate(new Date(), "yyyy-MM-dd");
  552 + break;
  553 + case 3:
  554 + aMonth = aMonth < 10 ? "0" + aMonth : aMonth;
  555 + that.query.startDay = `${aYear}-${aMonth}-01`;
  556 + that.query.endDay = formatDate(new Date(), "yyyy-MM-dd");
  557 + break;
  558 + case 4:
  559 + if (aMonth > 0 && aMonth < 4) {
  560 + aMonth = "1";
  561 + } else if (aMonth > 3 && aMonth < 7) {
  562 + aMonth = "4";
  563 + } else if (aMonth > 6 && aMonth < 10) {
  564 + aMonth = "7";
  565 + } else {
  566 + aMonth = "10";
  567 + }
  568 +
  569 + aMonth = aMonth < 10 ? "0" + aMonth : aMonth;
  570 + that.query.startDay = `${aYear}-${aMonth}-01`;
  571 + that.query.endDay = formatDate(new Date(), "yyyy-MM-dd");
  572 + break;
  573 + }
  574 + this.page = 1;
  575 + this._QueryData();
  576 + },
  577 + handleChangeTimeStart(val) {
  578 + this.query.day = "";
  579 + this.date = "";
  580 + if (this.query.endDay) {
  581 + if (new Date(val).getTime() > new Date(this.query.endDay).getTime()) {
  582 + this.$message.error("任务结束时间不能任务开始时间前面,请重新设置");
  583 + this.query.startDay = "";
  584 + }
  585 + }
  586 + },
  587 + handleChangeTimeEnd(val) {
  588 + this.query.day = "";
  589 + this.date = "";
  590 + if (this.query.startDay) {
  591 + if (new Date(val).getTime() < new Date(this.query.startDay).getTime()) {
  592 + this.$message.error("任务结束时间不能任务开始时间前面,请重新设置");
  593 + this.query.endDay = "";
  594 + }
  595 + }
  596 + },
  597 + changePage(page) {
  598 + this.page = page;
  599 + this._QueryData();
  600 + },
  601 + edit(item) {
  602 + this.$router.push({
  603 + path: "/examinationPaperEdit",
  604 + query: {
  605 + paperId: item.id,
  606 + title: item.title,
  607 + type: 2,
  608 + },
  609 + });
  610 + },
  611 + changeTab() {
  612 + this.tableMaxHeight = this.$refs.main.offsetHeight;
  613 + this.page = 1;
  614 + this._QueryData();
  615 + },
  616 + upSuccess() {
  617 + //导入成功
  618 + this.diaUp = false;
  619 + this._QueryData();
  620 + },
  621 + async changeclass() {
  622 + await this._QuerySubjectList();
  623 + this.page = 1;
  624 + this._QueryData();
  625 + },
  626 + async downExcel() {
  627 + this.loadingDown = true;
  628 + let data = await this.$request.subjectiveScoreTemplate({
  629 + examId: this.examId,
  630 + });
  631 + this.loadingDown = false;
  632 + if (data && !data.code) {
  633 + let blob = new Blob([data], {
  634 + type: "application/vnd.ms-excel;charset=utf-8",
  635 + });
  636 + downloadFile(`主观题模版.xlsx`, blob);
  637 + } else {
  638 + this.$message.error(data.info);
  639 + }
  640 + },
  641 + async changClazz() {
  642 + this.page = 1;
  643 + await this._QuerySubjectList();
  644 + await this._QueryData();
  645 + },
  646 + async _QueryClassList() {
  647 + const { data, status, info } = await this.$request.tClassList();
  648 + if (status === 0) {
  649 + this.classList = data.list.map((item) => {
  650 + return {
  651 + value: item.classId,
  652 + label: item.className,
  653 + };
  654 + });
  655 + this.query.classId = this.classList[0]?.value;
  656 + } else {
  657 + this.$message.error(info);
  658 + }
  659 + },
  660 + async _QuerySubjectList() {
  661 + const { data, status, info } = await this.$request.tSubjectList({
  662 + classId: this.query.classId,
  663 + });
  664 + if (status === 0) {
  665 + this.query.subjectNames = [];
  666 + this.subjectList =
  667 + data.subjectNames?.map((item) => {
  668 + return {
  669 + value: item,
  670 + label: item,
  671 + };
  672 + }) || [];
  673 + this.subjectList.unshift({
  674 + value: "全部",
  675 + label: "全部",
  676 + });
  677 + this.query.subjectNames.push(this.subjectList[0]?.value);
  678 + } else {
  679 + this.$message.error(info);
  680 + }
  681 + },
  682 + async _QueryData() {
  683 + this.tableData = [];
  684 + if (this.tabIndex == 1) {
  685 + this.examReportList();
  686 + } else {
  687 + this.phaseExamReport();
  688 + }
  689 + },
  690 + //单卷测练
  691 + async examReportList() {
  692 + this.loading = true;
  693 + let query = {};
  694 + for (let key in this.query) {
  695 + if (this.query[key] != "") {
  696 + query[key] = this.query[key];
  697 + }
  698 + }
  699 +
  700 + if (
  701 + query["subjectNames"] &&
  702 + query["subjectNames"].length == 1 &&
  703 + query["subjectNames"][0] == "全部"
  704 + ) {
  705 + query["subjectNames"] = this.subjectList.map((item) => {
  706 + return item.value;
  707 + });
  708 + query["subjectNames"].shift();
  709 + }
  710 + if (!query["subjectNames"]) {
  711 + this.$message.warning("请选择科目");
  712 + return;
  713 + }
  714 + const { data, status, info } = await this.$request.examReportList({
  715 + ...query,
  716 + page: this.page,
  717 + size: this.size,
  718 + });
  719 + this.loading = false;
  720 + if (status === 0) {
  721 + this.tableData = (data?.list && [...data?.list]) || [];
  722 + this.total = data.count;
  723 + } else {
  724 + this.$message.error(info);
  725 + }
  726 + },
  727 + //多卷测练
  728 + async phaseExamReport() {
  729 + this.loading = true;
  730 + let query = {};
  731 + for (let key in this.query) {
  732 + if (this.query[key] != "") {
  733 + query[key] = this.query[key];
  734 + }
  735 + }
  736 +
  737 + if (
  738 + query["subjectNames"] &&
  739 + query["subjectNames"]?.length == 1 &&
  740 + query["subjectNames"][0] == "全部"
  741 + ) {
  742 + query["subjectNames"] = this.subjectList.map((item) => {
  743 + return item.value;
  744 + });
  745 + query["subjectNames"]?.shift();
  746 + }
  747 + // 测试
  748 + query["subjectName"] = "政治";
  749 + delete query["subjectNames"];
  750 + // end
  751 + const { data, status, info } = await this.$request.phaseExamReport({
  752 + ...query,
  753 + });
  754 + this.loading = false;
  755 + if (status === 0) {
  756 + this.total = data.count;
  757 + let subjectName = [];
  758 + this.tableData = data?.list.map((item) => {
  759 + let params = {};
  760 + item.dataList?.map((items, index) => {
  761 + if (!subjectName.includes(items.subjectName)) {
  762 + subjectName.push(items.subjectName);
  763 + }
  764 + params["examCount" + items.subjectName] = items.examCount;
  765 + params["participationCount" + items.subjectName] =
  766 + items.participationCount;
  767 + params["score" + items.subjectName] = items.score;
  768 + params["classRank" + items.subjectName] = items.classRank;
  769 + });
  770 + return {
  771 + ...item,
  772 + ...params,
  773 + };
  774 + });
  775 + this.answerList = [...subjectName];
  776 + } else {
  777 + this.$message.error(info);
  778 + }
  779 + },
  780 + async downExl() {
  781 + if (this.exportLoading == true) return;
  782 + let query = {};
  783 + for (let key in this.query) {
  784 + if (this.query[key] != "") {
  785 + query[key] = this.query[key];
  786 + }
  787 + }
  788 + if (
  789 + query["subjectNames"] &&
  790 + query["subjectNames"].length == 1 &&
  791 + query["subjectNames"][0] == "全部"
  792 + ) {
  793 + query["subjectNames"] = this.subjectList.map((item) => {
  794 + return item.value;
  795 + });
  796 + query["subjectNames"].shift();
  797 + }
  798 + if (!query["subjectNames"]) {
  799 + this.$message.warning("请选择科目");
  800 + return;
  801 + }
  802 + const data = await this.$request.exportPhaseExamReport({ ...query });
  803 + this.exportLoading = false;
  804 + if (data) {
  805 + let blob = new Blob([data], {
  806 + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  807 + });
  808 + downloadFile("即时测-阶段测练报表.xlsx", blob);
  809 + } else {
  810 + this.$message.error("下载失败");
  811 + }
  812 + },
  813 + },
  814 +};
  815 +</script>
  816 +
  817 +<style>
  818 +div::-webkit-scrollbar {
  819 + width: 3px;
  820 + height: 10px;
  821 +}
  822 +div::-webkit-scrollbar-thumb {
  823 + border-radius: 10px;
  824 + background-color: #ccc;
  825 +}
  826 +</style>
  827 +<style lang="scss" scoped>
  828 +.page-container {
  829 + position: relative;
  830 + height: 100%;
  831 + &.active {
  832 + overflow: hidden;
  833 + }
  834 +}
  835 +.table-box {
  836 + margin: 0 20px;
  837 + padding: 16px;
  838 + background: #f8f8f8;
  839 + border-radius: 5px;
  840 + :deep(.fa-arrow-right) {
  841 + padding-left: 2px;
  842 + }
  843 + :deep(.fa-file-text) {
  844 + padding-left: 2px;
  845 + }
  846 +}
  847 +.click-b {
  848 + cursor: pointer;
  849 +}
  850 +.down {
  851 + padding-top: 16px;
  852 +}
  853 +.edit-dia {
  854 + position: absolute;
  855 + left: 0;
  856 + top: 0;
  857 + right: 0;
  858 + bottom: 0;
  859 + width: 100%;
  860 + height: calc(100vh - 70px);
  861 + background: #fff;
  862 + overflow-y: auto;
  863 + z-index: 10;
  864 +}
  865 +</style>
0 866 \ No newline at end of file
... ...
src/views/personal/userInfo/index.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <back-box>
  4 + <template slot="title">
  5 + <span>个人信息</span>
  6 + </template>
  7 + </back-box>
  8 + <div class="page-content">
  9 + <div class="content-box">
  10 + <i class="el-icon-edit" @click="diaSchool = true"></i>
  11 + <ul class="school-info">
  12 + <li class="school-item">
  13 + <span class="s1">学校名称:</span>
  14 + <span class="s2">{{ school.schoolName || "--" }}</span>
  15 + </li>
  16 + <li class="school-item">
  17 + <span class="s1">授课端管理密码:</span>
  18 + <span class="s2">{{ school.managePwd || "--" }}</span>
  19 + </li>
  20 + <li class="school-item">
  21 + <span class="s1">教师姓名:</span>
  22 + <span class="s2">{{ school.contactPerson || "--" }}</span>
  23 + </li>
  24 + <li class="school-item">
  25 + <span class="s1">手机号码:</span>
  26 + <span class="s2">{{ school.contactPhone || "--" }}</span>
  27 + </li>
  28 + <li class="school-item">
  29 + <span class="s1">科目:</span>
  30 + <span class="s2">{{
  31 + school.sections | getSections(school.sections)
  32 + }}</span>
  33 + </li>
  34 +
  35 + </ul>
  36 + </div>
  37 + </div>
  38 + <el-dialog title="修改个人信息" :visible.sync="diaSchool" width="400">
  39 + <el-form
  40 + ref="formSchool"
  41 + class="form-box"
  42 + :model="formSchool"
  43 + :rules="rulesSchool"
  44 + label-width="160px"
  45 + >
  46 + <el-form-item label="授课端管理密码:" prop="managePwd">
  47 + <el-col :span="10">
  48 + <el-input
  49 + maxlength="20"
  50 + v-model="formSchool.managePwd"
  51 + show-password
  52 + placeholder="请输入授课端管理密码"
  53 + ></el-input>
  54 + </el-col>
  55 + </el-form-item>
  56 + <el-form-item label="教师姓名:" prop="contactPerson"
  57 + ><el-col :span="10"
  58 + ><el-input
  59 + maxlength="30"
  60 + v-model="formSchool.contactPerson"
  61 + placeholder="请输入教师姓名"
  62 + ></el-input></el-col
  63 + ></el-form-item>
  64 + <el-form-item label="手机号码:" prop="contactPhone"
  65 + ><el-col :span="10"
  66 + ><el-input
  67 + v-model="formSchool.contactPhone"
  68 + type="number"
  69 + oninput="if(value.length > 11) value = value.slice(0,11)"
  70 + placeholder="请输入教师姓名手机号码"
  71 + ></el-input></el-col
  72 + ></el-form-item>
  73 + </el-form>
  74 + <div class="dialog-footer" slot="footer">
  75 + <el-button @click="editSchool">确 定</el-button>
  76 + <el-button @click="diaSchool = false">取 消</el-button>
  77 + </div>
  78 + </el-dialog>
  79 + </div>
  80 +</template>
  81 +
  82 +<script>
  83 +import { downloadFile } from "@/utils";
  84 +export default {
  85 + filters: {
  86 + getSections(val) {
  87 + let arr = val.split(",");
  88 + let sections = arr.map((item) => {
  89 + let txt;
  90 + switch (item) {
  91 + case "0":
  92 + txt = "未知";
  93 + break;
  94 + case "1":
  95 + txt = "小学";
  96 + break;
  97 + case "2":
  98 + txt = "初中";
  99 + break;
  100 + case "3":
  101 + txt = "高中";
  102 + break;
  103 + case "4":
  104 + txt = "大学";
  105 + break;
  106 + case "7":
  107 + txt = "未知";
  108 + break;
  109 + default:
  110 + txt = "未知";
  111 + }
  112 + return txt;
  113 + });
  114 + return sections.join(",");
  115 + },
  116 + },
  117 + data() {
  118 + return {
  119 + loading: false,
  120 + diaSchool: false,
  121 + school: {
  122 + schoolName: "",
  123 + managePwd: "",
  124 + contactPerson: "",
  125 + contactPhone: "",
  126 + sections: "",
  127 + },
  128 + tableData: [],
  129 + formSchool: {
  130 + sections: "",
  131 + managePwd: "",
  132 + contactPerson: "",
  133 + contactPhone: "",
  134 + },
  135 + subjectName: "",
  136 + subjectList: [],
  137 + };
  138 + },
  139 + created() {
  140 + // this._QueryDataSchool();
  141 + // this._QuerySubject();
  142 + },
  143 + methods: {
  144 + addSubjectName() {
  145 + if (!this.subjectName) {
  146 + this.$message.warning("请填写科目名称");
  147 + return;
  148 + }else if(this.subjectList.includes(this.subjectName)){
  149 + this.$message.warning("科目已存在,请重新填写~");
  150 + return;
  151 + }
  152 + this.subjectList.push(this.subjectName);
  153 + this.subjectName = "";
  154 + },
  155 + editSchool() {
  156 + if (!this.formSchool.sections.length) {
  157 + this.$message.error("请选择科目!");
  158 + return;
  159 + }
  160 + if (!this.formSchool.managePwd) {
  161 + this.$message.error("请填写密码!");
  162 + return;
  163 + }
  164 + if (this.loading) {
  165 + return;
  166 + }
  167 + this.$refs.formSchool.validate(async (valid) => {
  168 + if (valid) {
  169 + this.loading = true;
  170 + let form = { ...this.formSchool };
  171 + form.sections = this.formSchool.sections.join(",");
  172 + const { data, status, info } = await this.$request.updateSchool({
  173 + ...form,
  174 + });
  175 + this.loading = false;
  176 + if (status === 0) {
  177 + this.$message.success("修改成功~");
  178 + this.diaSchool = false;
  179 + this._QueryDataSchool();
  180 + } else {
  181 + this.$message.error(info);
  182 + }
  183 + } else {
  184 + this.$message.error("数据有误,请检查!");
  185 + }
  186 + });
  187 + },
  188 + async _QueryDataSchool() {
  189 + this.loading = true;
  190 + const { data, status, info } = await this.$request.schoolDetail();
  191 + this.loading = false;
  192 + console.log(status);
  193 + if (status === 0) {
  194 + this.school = { ...data };
  195 + for (let key in this.formSchool) {
  196 + this.formSchool[key] = data[key] || "";
  197 + }
  198 + this.formSchool.sections = this.formSchool.sections.split(",");
  199 + } else {
  200 + this.$message.error(info);
  201 + }
  202 + },
  203 + async _QuerySubject() {
  204 + const { data, status, info } = await this.$request.subjectList();
  205 + if (status === 0) {
  206 + this.subjectList = [...data.subjectNames] || [];
  207 + } else {
  208 + this.$message.error(info);
  209 + }
  210 + },
  211 + async downExcel() {
  212 + let data = await this.$request.downDevice({
  213 + id: this.id,
  214 + });
  215 + if (data && !data.code) {
  216 + let blob = new Blob([data], {
  217 + type: "application/vnd.ms-excel;charset=utf-8",
  218 + });
  219 + downloadFile(`设备信息.xlsx`, blob);
  220 + } else {
  221 + this.$message.error(data.info);
  222 + }
  223 + },
  224 + },
  225 +};
  226 +</script>
  227 +
  228 +<style lang="scss" scoped>
  229 +.page-content {
  230 + padding: 20px;
  231 + .content-box {
  232 + background: #f8f8f8;
  233 + border-radius: 16px;
  234 + position: relative;
  235 + .el-icon-edit {
  236 + position: absolute;
  237 + top: 12px;
  238 + right: 12px;
  239 + padding: 5px;
  240 + font-size: 18px;
  241 + cursor: pointer;
  242 + &:hover {
  243 + color: #36f;
  244 + }
  245 + }
  246 + }
  247 + .school-info {
  248 + display: flex;
  249 + flex-wrap: wrap;
  250 + padding: 16px 0;
  251 + border-bottom: 0.5px solid #f2f2f2;
  252 + .school-item {
  253 + width: 50%;
  254 + line-height: 48px;
  255 + padding-left: 100px;
  256 + display: flex;
  257 + box-sizing: border-box;
  258 + .s1 {
  259 + width: 160px;
  260 + font-size: 15px;
  261 + color: #888;
  262 + }
  263 + .s2 {
  264 + flex: 1;
  265 + }
  266 + }
  267 + }
  268 +}
  269 +.form-box {
  270 + margin: 0 20px;
  271 + .subject-box {
  272 + height: 90px;
  273 + overflow: hidden;
  274 + position: relative;
  275 + &.active {
  276 + height: auto;
  277 + overflow: auto;
  278 + }
  279 + .showAll {
  280 + position: absolute;
  281 + bottom: 0;
  282 + right: 10px;
  283 + font-size: 12px;
  284 + color: #7f7f7f;
  285 + cursor: pointer;
  286 + padding: 2px;
  287 + &:hover {
  288 + color: #667ffd;
  289 + }
  290 + }
  291 + }
  292 +}
  293 +.el-icon-plus {
  294 + cursor: pointer;
  295 + &:hover {
  296 + color: #667ffd;
  297 + }
  298 +}
  299 +</style>
0 300 \ No newline at end of file
... ...
src/views/analysis/index.vue renamed to src/views/standard/analysis/index.vue
src/views/ask/analysis.vue renamed to src/views/standard/ask/analysis.vue
src/views/ask/index.vue renamed to src/views/standard/ask/index.vue
... ... @@ -1056,7 +1056,7 @@ div::-webkit-scrollbar-thumb {
1056 1056 }
1057 1057 .delButton {
1058 1058 border-color: #ff6868;
1059   - background: #ff6868 url("../../assets/images/arrow.png") no-repeat center;
  1059 + background: #ff6868 url("../../../assets/images/arrow.png") no-repeat center;
1060 1060 background-size: 19px;
1061 1061 color: transparent;
1062 1062 }
... ...
src/views/standard/card/index.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <back-box>
  4 + <template slot="title">
  5 + <span>发卡记录</span>
  6 + </template>
  7 + </back-box>
  8 +
  9 + <div class="page-content">
  10 + <div class="answer-header">
  11 + <div class="sel-box">
  12 + <el-cascader
  13 + @change="_QueryData(1)"
  14 + size="small"
  15 + class="sel"
  16 + clearable
  17 + placeholder="选择班级"
  18 + v-model="query.classId"
  19 + :options="gradeList"
  20 + :props="props"
  21 + :show-all-levels="false"
  22 + ></el-cascader>
  23 + <el-input
  24 + placeholder="请输入学生姓名"
  25 + v-model="query.studentName"
  26 + class="input-with-select"
  27 + @keyup.enter.native="_QueryData(2)"
  28 + >
  29 + <el-button
  30 + slot="append"
  31 + icon="el-icon-search"
  32 + @click="_QueryData(2)"
  33 + ></el-button>
  34 + </el-input>
  35 + <el-input
  36 + placeholder="请输入学生学号"
  37 + v-model="query.studentCode"
  38 + class="input-with-select"
  39 + @keyup.enter.native="_QueryData(3)"
  40 + >
  41 + <el-button
  42 + slot="append"
  43 + icon="el-icon-search"
  44 + @click="_QueryData(3)"
  45 + ></el-button>
  46 + </el-input>
  47 + <el-button type="primary" round @click="_QueryData(4)"
  48 + >筛选</el-button
  49 + >
  50 + </div>
  51 + </div>
  52 + <el-empty
  53 + :image-size="100"
  54 + v-if="!tableData.length && !loading"
  55 + description="暂无数据"
  56 + ></el-empty>
  57 + <div v-else class="table-box" v-loading="loading">
  58 + <el-table :data="tableData" border style="width: 100%">
  59 + <el-table-column
  60 + align="center"
  61 + label="答题器编码"
  62 + prop="clickerSn"
  63 + ></el-table-column>
  64 + <el-table-column align="center" label="班级">
  65 + <template slot-scope="scope">
  66 + <span v-for="item in scope.row.classList" :key="item.classCode">{{
  67 + item.className
  68 + }}</span>
  69 + </template>
  70 + </el-table-column>
  71 + <el-table-column
  72 + align="center"
  73 + label="学生姓名"
  74 + prop="studentName"
  75 + ></el-table-column>
  76 + <el-table-column
  77 + align="center"
  78 + label="学号"
  79 + prop="studentCode"
  80 + ></el-table-column>
  81 + <el-table-column align="center" label="类型">
  82 + <template slot-scope="scope">
  83 + {{ scope.row.operationType == 0 ? "发卡" : "补卡" }}
  84 + </template></el-table-column
  85 + >
  86 + <el-table-column align="center" label="描述">
  87 + <template slot-scope="scope">
  88 + {{
  89 + scope.row.operationType == 0
  90 + ? "--"
  91 + : scope.row.reason == 0
  92 + ? "丢失"
  93 + : "损坏"
  94 + }}
  95 + </template></el-table-column
  96 + >
  97 + <el-table-column
  98 + align="center"
  99 + label="操作时间"
  100 + prop="modifiedTime"
  101 + ></el-table-column>
  102 + </el-table>
  103 + <div class="pagination-box">
  104 + <el-pagination
  105 + small=""
  106 + layout="total,prev, pager, next"
  107 + :hide-on-single-page="true"
  108 + :total="total"
  109 + @current-change="changePage"
  110 + :current-page="page"
  111 + :page-size="size"
  112 + >
  113 + </el-pagination>
  114 + </div>
  115 + </div>
  116 + </div>
  117 + </div>
  118 +</template>
  119 +
  120 +<script>
  121 +export default {
  122 + data() {
  123 + return {
  124 + loading: false,
  125 + props: { multiple: false },
  126 + query: {
  127 + classId: "",
  128 + studentName: "",
  129 + studentCode: "",
  130 + },
  131 + gradeList: [],
  132 + tableData: [],
  133 + page: 1,
  134 + size: 20,
  135 + total: 0,
  136 + };
  137 + },
  138 + created() {
  139 + this._QueryGradeList();
  140 + this._QueryData();
  141 + },
  142 + methods: {
  143 + // 查找班级
  144 + async _QueryGradeList() {
  145 + this.loading = true;
  146 + const { data, status, info } = await this.$request.gradeList();
  147 + console.log(status);
  148 + if (status === 0) {
  149 + if (!!data.list) {
  150 + this.gradeList =
  151 + data.list?.map((item) => {
  152 + let gradeList = {
  153 + value: item.grade,
  154 + label: item.gradeName,
  155 + };
  156 + gradeList.children =
  157 + item.classList?.map((items) => {
  158 + return {
  159 + value: items.id,
  160 + label: items.className,
  161 + };
  162 + }) || [];
  163 + return gradeList;
  164 + }) || [];
  165 + }
  166 + } else {
  167 + this.$message.error(info);
  168 + }
  169 + },
  170 + changePage(page) {
  171 + this.page = page;
  172 + this._QueryData(4);
  173 + },
  174 + async _QueryData(type) {
  175 + let query = {};
  176 + query.gradeName = this.query.gradeName;
  177 + if (type == 1) {
  178 + query.classId = this.query.classId[1] ? this.query.classId[1] : "";
  179 + this.query.studentCode = "";
  180 + this.query.studentName = "";
  181 + } else if (type == 2) {
  182 + query.studentName = this.query.studentName;
  183 + this.query.classId = "";
  184 + this.query.studentCode = "";
  185 + } else if (type == 3) {
  186 + query.studentCode = this.query.studentCode;
  187 + this.query.classId = "";
  188 + this.query.studentName = "";
  189 + } else {
  190 + query = { ...this.query };
  191 + }
  192 + this.loading = true;
  193 + const { data, status, info } = await this.$request.cardList({
  194 + ...query,
  195 + page: this.page,
  196 + size: 20,
  197 + });
  198 + this.loading = false;
  199 + console.log(status);
  200 + if (status === 0) {
  201 + this.tableData = data.list || [];
  202 + this.total = data.count;
  203 + } else {
  204 + this.$message.error(info);
  205 + }
  206 + },
  207 + },
  208 +};
  209 +</script>
  210 +
  211 +<style lang="scss" scoped>
  212 +.table-box {
  213 + padding: 0 20px;
  214 +}
  215 +</style>
0 216 \ No newline at end of file
... ...
src/views/standard/dataSync/index.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <back-box>
  4 + <template slot="title">
  5 + <span>数据同步</span>
  6 + </template>
  7 + </back-box>
  8 + <div class="page-content">
  9 + <div class="down-item">
  10 + <p class="h-title">从U盘上传</p>
  11 + <p class="txt">
  12 + 本功能帮助无法上网的授课端软件,将本地数据同步到云平台。
  13 + </p>
  14 + <el-upload
  15 + class="upload-demo"
  16 + ref="upload"
  17 + :action="url"
  18 + :multiple="false"
  19 + :with-credentials="true"
  20 + :limit="1"
  21 + :on-change="change"
  22 + :on-success="upSuccess"
  23 + :on-error="upError"
  24 + >
  25 + <div class="btn-box">
  26 + <i class="fa fa-cloud-upload"></i>
  27 + <el-button type="primary" round>选择文件</el-button>
  28 + </div>
  29 + </el-upload>
  30 + </div>
  31 +
  32 + <div class="down-item">
  33 + <p class="h-title">数据导出至U盘</p>
  34 + <p class="txt">本功能将云平台的数据导出到U盘。</p>
  35 + <div class="btn-box btn-box2" v-loading="downLoading">
  36 + <i class="fa fa-cloud-download" @click="downloadFile"></i>
  37 + <el-button type="primary" round @click="downloadFile"
  38 + >文件下载</el-button
  39 + >
  40 + </div>
  41 + </div>
  42 + </div>
  43 + <el-dialog title="" :visible.sync="dialogVisible" width="300" center>
  44 + <el-result icon="success" title="上传成功"> </el-result>
  45 + <el-descriptions title="" :column="1">
  46 + <el-descriptions-item label="导入答题卡数量">{{tipData.paperNum}}</el-descriptions-item>
  47 + <el-descriptions-item label="导入随堂问报表数量">{{tipData.periodNum}}</el-descriptions-item>
  48 + <el-descriptions-item label="导入即时测报表数量">{{tipData.examNum}}</el-descriptions-item>
  49 + </el-descriptions>
  50 + <span slot="footer" class="dialog-footer">
  51 + <el-button type="primary" @click="dialogVisible = false"
  52 + >确 定</el-button
  53 + >
  54 + </span>
  55 + </el-dialog>
  56 + </div>
  57 +</template>
  58 +
  59 +<script>
  60 +import { formatDate } from "@/utils";
  61 +export default {
  62 + data() {
  63 + return {
  64 + downLoading: false,
  65 + url: "/api_html/teaching/importData",
  66 + file: {},
  67 + dialogVisible: false,
  68 + tipData: {
  69 + paperNum: 0,
  70 + periodNum: 0,
  71 + examNum: 0,
  72 + },
  73 + };
  74 + },
  75 + methods: {
  76 + async downloadFile() {
  77 + if (this.downLoading) return;
  78 + this.downLoading = true;
  79 + const data = await this.$request.exportData();
  80 + this.downLoading = false;
  81 + console.log(data);
  82 + if (data) {
  83 + let blob = new Blob([data], { type: "application/octet-stream" });
  84 + const url = URL.createObjectURL(blob);
  85 + const link = document.createElement("a");
  86 + document.body.appendChild(link);
  87 + link.download =
  88 + this.$store.getters.info.name +
  89 + formatDate(new Date(), "yyyy_MM_dd_hh_mm_ss") +
  90 + "文件.json";
  91 + link.href = url;
  92 + link.click();
  93 + document.body.removeChild(link);
  94 + URL.revokeObjectURL(url);
  95 + } else {
  96 + this.$message.error("下载失败,请重试");
  97 + }
  98 + },
  99 + async submitUpload() {
  100 + this.$refs.upload.submit();
  101 +
  102 + // const formData = new FormData()
  103 + // formData.append('id',this.componentId)
  104 + // formData.append('file',new File(this.file.raw))
  105 + // let {status,info} = await uploadExcel(formData);
  106 + // if(status===0){
  107 + // this.$message.success(info);
  108 + // this.$emit("upSuccess")
  109 + // } else {
  110 + // this.$message.error(info);
  111 + // }
  112 + },
  113 + upSuccess(res) {
  114 + if (res && res.status == 0) {
  115 + this.tipData = res.data
  116 + this.dialogVisible = true
  117 + } else {
  118 + this.$message.error(res.info);
  119 + }
  120 + },
  121 + upError(res) {
  122 + debugger;
  123 + if (res && res.status == 0) {
  124 + this.$message.error("上传失败");
  125 + } else {
  126 + this.$message.error(res.message);
  127 + }
  128 + },
  129 + change(file) {
  130 + this.file = file;
  131 + },
  132 + },
  133 +};
  134 +</script>
  135 +
  136 +<style lang="scss" scoped>
  137 +.page-content {
  138 + padding: 50px;
  139 + display: flex;
  140 + justify-content: center;
  141 + .down-item {
  142 + width: 400px;
  143 + height:330px;
  144 + border-radius: 20px;
  145 + margin: 20px;
  146 + background: #f8f8f8;
  147 + box-shadow: 2px 2px 5px #ccc;
  148 + .h-title {
  149 + font-size: 16px;
  150 + color: #667ffd;
  151 + padding: 16px 0 16px 12px;
  152 + }
  153 + .txt {
  154 + height: 80px;
  155 + padding: 0 20px;
  156 + font-size: 16px;
  157 + color: #7f7f7f;
  158 + line-height: 24px;
  159 + text-align: center;
  160 + }
  161 + }
  162 + .upload-demo {
  163 + padding:0 20px 20px;
  164 + :deep(.el-upload--text){
  165 + display: block;
  166 + }
  167 + }
  168 + :deep(.el-upload) {
  169 + margin: 0 auto;
  170 + }
  171 + .btn-box {
  172 + display: flex;
  173 + flex-direction: column;
  174 + justify-content: center;
  175 + align-items: center;
  176 + padding-bottom: 10px;
  177 + .fa {
  178 + font-size: 80px;
  179 + color: #aeaeae;
  180 + padding-bottom: 10px;
  181 + cursor: pointer;
  182 + }
  183 + }
  184 + .btn-box {
  185 + width: 100%;
  186 + }
  187 +}
  188 +</style>
0 189 \ No newline at end of file
... ...
src/views/device/index.vue renamed to src/views/standard/device/index.vue
src/views/standard/device/log.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <back-box>
  4 + <template slot="title">
  5 + <span>设备日志</span>
  6 + </template>
  7 + </back-box>
  8 + <div class="page-content">
  9 + <el-descriptions :column="3" border>
  10 + <el-descriptions-item label="设备编码">{{
  11 + device.sn
  12 + }}</el-descriptions-item>
  13 + <el-descriptions-item label="配对码">{{
  14 + device.pairingCode
  15 + }}</el-descriptions-item>
  16 + <el-descriptions-item label="关联班级">
  17 + <span v-for="item in device.classList" :key="item.classId">{{
  18 + item.className
  19 + }}</span>
  20 + </el-descriptions-item>
  21 + <el-descriptions-item label="操作时间">{{
  22 + device.modifiedTime
  23 + }}</el-descriptions-item>
  24 + <el-descriptions-item v-if="type == 1" label="所在教室">{{
  25 + device.roomName
  26 + }}</el-descriptions-item>
  27 + <el-descriptions-item v-if="type == 1" label="固件版本号">{{
  28 + device.otaVersionName
  29 + }}</el-descriptions-item>
  30 + <!-- <el-descriptions-item v-if="type == 2" label="电量">{{
  31 + device.electricity
  32 + }}</el-descriptions-item> -->
  33 + <el-descriptions-item v-if="type == 2" label="答题次数">{{
  34 + device.answerTimes
  35 + }}</el-descriptions-item>
  36 + </el-descriptions>
  37 + </div>
  38 + <div class="answer-header">
  39 + <div class="sel-box">
  40 + <div class="d1">
  41 + <el-date-picker
  42 + v-model="query.startDay"
  43 + type="date"
  44 + @change="handleChangeTimeStart"
  45 + placeholder="选择日期时间"
  46 + value-format="yyyy-MM-dd"
  47 + >
  48 + </el-date-picker>
  49 + ~
  50 + <el-date-picker
  51 + v-model="query.endDay"
  52 + type="date"
  53 + placeholder="选择日期时间"
  54 + @change="handleChangeTimeEnd"
  55 + value-format="yyyy-MM-dd"
  56 + >
  57 + </el-date-picker>
  58 + </div>
  59 + <p class="p1">
  60 + <span @click="setDate(1)" :class="[date == 1 ? 'active' : '', 's1']"
  61 + >今天</span
  62 + >
  63 + <span @click="setDate(2)" :class="[date == 2 ? 'active' : '', 's1']"
  64 + >本周</span
  65 + >
  66 + <span @click="setDate(3)" :class="[date == 3 ? 'active' : '', 's1']"
  67 + >本月</span
  68 + >
  69 + <span @click="setDate(4)" :class="[date == 4 ? 'active' : '', 's1']"
  70 + >本季度</span
  71 + >
  72 + </p>
  73 + <el-button type="primary" class="serach-box" round @click="_QueryData"
  74 + >筛选</el-button
  75 + >
  76 + </div>
  77 + </div>
  78 + <div class="table-box">
  79 + <el-table :data="tableData" border style="width: 100%">
  80 + <el-table-column
  81 + prop="operationType"
  82 + label="请求类型"
  83 + align="center"
  84 + width="160"
  85 + ><template slot-scope="scoped">{{
  86 + setOperationType(scoped.row.operationType)
  87 + }}</template></el-table-column
  88 + >
  89 + <!-- <el-table-column
  90 + v-if="type == 2"
  91 + prop="electricity"
  92 + width="100"
  93 + label="电量"
  94 + align="center"
  95 + ></el-table-column> -->
  96 + <el-table-column
  97 + prop="operationTime"
  98 + label="时间"
  99 + align="center"
  100 + width="200"
  101 + ></el-table-column>
  102 + <el-table-column prop="content" label="日志内容" align="center"
  103 + ><template slot-scope="scoped">
  104 + <p style="text-align: left">{{ scoped.row.content }}</p></template
  105 + ></el-table-column
  106 + >
  107 + </el-table>
  108 + </div>
  109 + <div class="pagination-box">
  110 + <el-pagination
  111 + small=""
  112 + layout="total,prev, pager, next"
  113 + :hide-on-single-page="true"
  114 + :total="total"
  115 + @current-change="changePage"
  116 + :current-page="page"
  117 + :page-size="size"
  118 + >
  119 + </el-pagination>
  120 + </div>
  121 + </div>
  122 +</template>
  123 +
  124 +<script>
  125 +import { formatDate } from "utils";
  126 +export default {
  127 + data() {
  128 + return {
  129 + type: 1,
  130 + id: "",
  131 + date: "", //今天-昨天-本周
  132 + query: {
  133 + //搜索条件
  134 + startDay: "",
  135 + endDay: "",
  136 + day: "",
  137 + },
  138 + tableData: [],
  139 + device: {
  140 + sn: "",
  141 + electricity: "",
  142 + pairingCode: "",
  143 + classList: [],
  144 + modifiedTime: "",
  145 + answerTimes: "",
  146 + roomName: "",
  147 + otaVersionName: "",
  148 + },
  149 + total: 0,
  150 + page: 1,
  151 + size: 20,
  152 + };
  153 + },
  154 + created() {
  155 + this.id = this.$route.query.id;
  156 + this.type = this.$route.query.type;
  157 + this._QueryDetail();
  158 + this.setDate(1);
  159 + let startDay = this.query?.startDay;
  160 + if (!startDay) {
  161 + this.query.startDay = new Date();
  162 + this.query.endDay = new Date();
  163 + }
  164 + },
  165 + methods: {
  166 + setOperationType(type) {
  167 + let txt;
  168 + switch (type) {
  169 + case 0:
  170 + txt = "连接异常";
  171 + break;
  172 + case 1:
  173 + txt = "问";
  174 + break;
  175 + case 2:
  176 + txt = "测";
  177 + break;
  178 + case 3:
  179 + txt = "考";
  180 + break;
  181 + case 4:
  182 + txt = "抢答";
  183 + break;
  184 + case 5:
  185 + txt = "抽答";
  186 + break;
  187 + case 6:
  188 + txt = "再答";
  189 + break;
  190 + case 7:
  191 + txt = "签到";
  192 + break;
  193 + }
  194 + return txt;
  195 + },
  196 + setDate(index) {
  197 + const that = this;
  198 + this.date = index == this.date ? "" : index;
  199 + let aYear = new Date().getFullYear();
  200 + let aMonth = new Date().getMonth() + 1;
  201 + that.query.day = "";
  202 + that.query.startDay = "";
  203 + that.query.endDay = "";
  204 + switch (index) {
  205 + case 1:
  206 + that.query.day = formatDate(new Date(), "yyyy-MM-dd");
  207 + that.query.startDay = that.query.day;
  208 + that.query.endDay = that.query.day;
  209 + break;
  210 + case 2:
  211 + let day = new Date().getDay();
  212 + if (day == 0) {
  213 + //中国式星期天是一周的最后一天
  214 + day = 7;
  215 + }
  216 + day--;
  217 + let aTime = new Date().getTime() - 24 * 60 * 60 * 1000 * day;
  218 + that.query.startDay = formatDate(new Date(aTime), "yyyy-MM-dd");
  219 + that.query.endDay = formatDate(new Date(), "yyyy-MM-dd");
  220 + break;
  221 + case 3:
  222 + aMonth = aMonth < 10 ? "0" + aMonth : aMonth;
  223 + that.query.startDay = `${aYear}-${aMonth}-01`;
  224 + that.query.endDay = formatDate(new Date(), "yyyy-MM-dd");
  225 + break;
  226 + case 4:
  227 + if (aMonth > 0 && aMonth < 4) {
  228 + aMonth = "1";
  229 + } else if (aMonth > 3 && aMonth < 7) {
  230 + aMonth = "4";
  231 + } else if (aMonth > 6 && aMonth < 10) {
  232 + aMonth = "7";
  233 + } else {
  234 + aMonth = "10";
  235 + }
  236 +
  237 + aMonth = aMonth < 10 ? "0" + aMonth : aMonth;
  238 + that.query.startDay = `${aYear}-${aMonth}-01`;
  239 + that.query.endDay = formatDate(new Date(), "yyyy-MM-dd");
  240 + break;
  241 + }
  242 + this.page = 1;
  243 + this._QueryData();
  244 + },
  245 + handleChangeTimeStart(val) {
  246 + this.query.day = "";
  247 + this.date = "";
  248 + if (this.query.endDay) {
  249 + if (new Date(val).getTime() > new Date(this.query.endDay).getTime()) {
  250 + this.$message.error("任务结束时间不能任务开始时间前面,请重新设置");
  251 + this.query.startDay = "";
  252 + }
  253 + }
  254 + },
  255 + handleChangeTimeEnd(val) {
  256 + this.query.day = "";
  257 + this.date = "";
  258 + if (this.query.startDay) {
  259 + if (new Date(val).getTime() < new Date(this.query.startDay).getTime()) {
  260 + this.$message.error("任务结束时间不能任务开始时间前面,请重新设置");
  261 + this.query.endDay = "";
  262 + }
  263 + }
  264 + },
  265 + changePage(page) {
  266 + this.page = page;
  267 + this._QueryData();
  268 + },
  269 + async _QueryDetail() {
  270 + const { data, status, info } = await this.$request.deviceDetail({
  271 + deviceId: this.id,
  272 + });
  273 + if (status === 0) {
  274 + this.device = { ...data };
  275 + for (let key in this.device) {
  276 + this.device[key] = data[key];
  277 + }
  278 + } else {
  279 + this.$message.error(info);
  280 + }
  281 + },
  282 + async _QueryData() {
  283 + this.loading = true;
  284 + //多课时对比
  285 + let query = {};
  286 + for (let key in this.query) {
  287 + if (this.query[key] != "") {
  288 + if (key == "day" || key == "startDay" || key == "endDay") {
  289 + query[key] = this.query[key].split("-").join("");
  290 + } else {
  291 + query[key] = this.query[key];
  292 + }
  293 + }
  294 + }
  295 + const {
  296 + data = {},
  297 + status,
  298 + info,
  299 + } = await this.$request.deviceLogList({
  300 + ...query,
  301 + deviceId: Number(this.id),
  302 + page: this.page,
  303 + size: this.size,
  304 + });
  305 + this.loading = false;
  306 + if (status === 0) {
  307 + this.tableData = (data?.list && [...data?.list]) || [];
  308 + this.total = data.count;
  309 + } else {
  310 + this.$message.error(info);
  311 + }
  312 + },
  313 + },
  314 +};
  315 +</script>
  316 +
  317 +<style lang="scss" scoped>
  318 +.page-content {
  319 + padding: 20px;
  320 +}
  321 +.table-box {
  322 + padding: 0 20px;
  323 +}
  324 +.serach-box {
  325 + margin-left: 20px;
  326 +}
  327 +</style>
0 328 \ No newline at end of file
... ...
src/views/down/client.vue renamed to src/views/standard/down/client.vue
src/views/down/index.vue renamed to src/views/standard/down/index.vue
src/views/examinationPaper/add.vue renamed to src/views/standard/examinationPaper/add.vue
... ... @@ -1757,7 +1757,7 @@ export default {
1757 1757 .delButton {
1758 1758 text-indent: -9999999px;
1759 1759 border-color: #ff6868;
1760   - background: #ff6868 url("../../assets/images/arrow.png") no-repeat center;
  1760 + background: #ff6868 url("../../../assets/images/arrow.png") no-repeat center;
1761 1761 background-size: 19px;
1762 1762 color: transparent;
1763 1763 }
... ...
src/views/standard/examinationPaper/detail.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <back-box>
  4 + <template slot="title">
  5 + <span>修改答案</span>
  6 + </template>
  7 + </back-box>
  8 + <div class="content">
  9 + <p class="tips">
  10 + <i class="fa fa-bell-o"></i> 2022-11-24 14:30张老师修改了答案。
  11 + </p>
  12 + <div class="answer-title">
  13 + <p class="name">{{ form.title }}</p>
  14 + <p class="totals">卷面总分:{{ allScore }}分</p>
  15 + </div>
  16 + <div v-for="(question, index) in form.questionList" :key="index">
  17 + <p class="question-title">
  18 + <span>{{ setBigNum(index) }}、</span>
  19 + <span class="title-txt">{{ question.questionTitle }}</span>
  20 + <span>共 {{ question.score }} 分</span>
  21 + </p>
  22 + <ul class="questions-ul">
  23 + <li class="sub-questions">
  24 + <div class="qs-num">题号</div>
  25 + <div class="qs-type">题型</div>
  26 + <div class="qs-score">分数</div>
  27 + <div class="qs-partScore">部分分值</div>
  28 + <div class="qs-answer">答案</div>
  29 + </li>
  30 + <li
  31 + class="sub-questions"
  32 + v-for="(subQuestions, indexs) in question.subQuestions"
  33 + :key="indexs"
  34 + >
  35 + <div class="qs-num">{{ subQuestions.questionIndex }}</div>
  36 + <div class="qs-type">
  37 + {{ setSubPro(subQuestions.questionType) }}
  38 + </div>
  39 + <div class="qs-score">
  40 + {{ subQuestions.score }}
  41 + </div>
  42 + <div class="qs-partScore">
  43 + {{ subQuestions.partScore }}
  44 + </div>
  45 + <div class="qs-answer">
  46 + {{ setAns(subQuestions.correctAnswer) }}
  47 + </div>
  48 + </li>
  49 + </ul>
  50 + </div>
  51 + <div class="btn-box">
  52 + <el-button type="danger" plain round @click="linkBack">取消</el-button>
  53 + <el-button type="primary" round @click="save">保存</el-button>
  54 + </div>
  55 + </div>
  56 + </div>
  57 +</template>
  58 +
  59 +<script>
  60 +export default {
  61 + methods: {
  62 + setSubPro(type) {
  63 + let tit;
  64 + switch (type) {
  65 + case 2:
  66 + tit = "单选题";
  67 + break;
  68 + case 3:
  69 + tit = "多选题";
  70 + break;
  71 + case 4:
  72 + tit = "判断题";
  73 + break;
  74 + case 5:
  75 + tit = "主观题";
  76 + break;
  77 + }
  78 + return tit;
  79 + },
  80 + setBigNum(num) {
  81 + let txt = "";
  82 + let bigNum = [
  83 + "一",
  84 + "二",
  85 + "三",
  86 + "四",
  87 + "五",
  88 + "六",
  89 + "七",
  90 + "八",
  91 + "九",
  92 + "十",
  93 + "十一",
  94 + "十二",
  95 + "十三",
  96 + "十四",
  97 + "十五",
  98 + "十六",
  99 + "十七",
  100 + "十八",
  101 + "十九",
  102 + "二十",
  103 + ];
  104 + txt = bigNum[num];
  105 +
  106 + return txt;
  107 + },
  108 + setAns(answer) {
  109 + let txt;
  110 + switch (answer) {
  111 + case 1:
  112 + case "1":
  113 + txt = "✓";
  114 + break;
  115 + case 2:
  116 + case "2":
  117 + txt = "✗";
  118 + break;
  119 + case "":
  120 + txt = "--";
  121 + break;
  122 + default:
  123 + txt = answer;
  124 + }
  125 + return txt
  126 + },
  127 + },
  128 +};
  129 +</script>
  130 +
  131 +<style lang="scss" scoped>
  132 +.content {
  133 + width: 100%;
  134 + box-sizing: border-box;
  135 + padding: 0 50px;
  136 + .ml-20 {
  137 + margin-left: 20px;
  138 + }
  139 + .btn-box {
  140 + text-align: right;
  141 + margin-left: 140px;
  142 + }
  143 + .tips {
  144 + height: 48px;
  145 + line-height: 48px;
  146 + padding: 0 16px;
  147 + border: 1px solid #fac7cc;
  148 + background-color: #ffebec;
  149 + font-size: 14px;
  150 + color: #fd9795;
  151 + margin: 10px 0 20px 0;
  152 + .fa-bell-o {
  153 + font-size: 18px;
  154 + margin-right: 5px;
  155 + }
  156 + }
  157 +}
  158 +.answer-title {
  159 + text-align: center;
  160 + font-size: 20px;
  161 + color: #333;
  162 + font-weight: 700;
  163 + padding-bottom: 20px;
  164 + .totals {
  165 + font-size: 16px;
  166 + color: #888;
  167 + font-weight: normal;
  168 + }
  169 +}
  170 +.question-title {
  171 + line-height: 40px;
  172 + .ipt {
  173 + width: 300px;
  174 + margin: 0 16px 0 10px;
  175 + :deep(.el-input__inner) {
  176 + border-radius: 20px;
  177 + border-color: #667ffd;
  178 + background: rgba($color: #667ffd, $alpha: 0.05);
  179 + }
  180 + }
  181 + .delete {
  182 + margin-right: 8px;
  183 + }
  184 + .title-txt {
  185 + margin-right: 20px;
  186 + font-size: 16px;
  187 + font-weight: 700;
  188 + }
  189 +}
  190 +</style>
0 191 \ No newline at end of file
... ...
src/views/examinationPaper/edit.vue renamed to src/views/standard/examinationPaper/edit.vue
src/views/examinationPaper/index.vue renamed to src/views/standard/examinationPaper/index.vue
src/views/standard/examinationPaper/recycle.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <back-box>
  4 + <template slot="title">
  5 + <span>已归档答题卡</span>
  6 + </template>
  7 + </back-box>
  8 + <div class="answer-header">
  9 + <div class="sel-box">
  10 + <!-- <el-cascader size="small"
  11 + class="sel sel2"
  12 + :options="options"
  13 + :props="props"
  14 + collapse-tags
  15 + clearable
  16 + placeholder="选择年级-班级"
  17 + v-model="query.gradeClass"
  18 + @change="changeGrade"
  19 + ><template slot-scope="{ node, data }">
  20 + <span>{{ data.label }}</span>
  21 + <span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
  22 + </template></el-cascader
  23 + > -->
  24 + <el-select
  25 + class="sel"
  26 + v-model="query.classId"
  27 + placeholder="选择班级"
  28 + @change="changClazz"
  29 + >
  30 + <el-option
  31 + v-for="item in classList"
  32 + :key="item.value"
  33 + :label="item.label"
  34 + :value="item.value"
  35 + >
  36 + </el-option>
  37 + </el-select>
  38 + <el-select
  39 + class="sel"
  40 + v-model="query.subjectName"
  41 + placeholder="选择科目"
  42 + @change="_QueryData()"
  43 + >
  44 + <el-option
  45 + v-for="item in subjectList"
  46 + :key="item.value"
  47 + :label="item.label"
  48 + :value="item.value"
  49 + >
  50 + </el-option>
  51 + </el-select>
  52 + <el-select
  53 + class="sel"
  54 + v-model="query.tagId"
  55 + placeholder="选择类型"
  56 + @change="_QueryData()"
  57 + >
  58 + <el-option
  59 + v-for="item in typeList"
  60 + :key="item.label"
  61 + :label="item.label"
  62 + :value="item.value"
  63 + >{{ item.label }}
  64 + </el-option>
  65 + </el-select>
  66 +
  67 + <el-input
  68 + placeholder="输入试卷名称"
  69 + v-model="query.title"
  70 + class="input-with-select"
  71 + @keyup.enter.native="_QueryData(true)"
  72 + >
  73 + <el-button
  74 + slot="append"
  75 + icon="el-icon-search"
  76 + @click="_QueryData(true)"
  77 + ></el-button>
  78 + </el-input>
  79 + </div>
  80 + </div>
  81 + <ul class="content" v-loading="loading">
  82 + <li class="item" v-for="item in tableData" :key="item.id">
  83 + <div class="pic-box">
  84 + <p class="i-box"><i class="fa fa-map-o"></i></p>
  85 + <p class="ids">{{ item.id }}</p>
  86 + </div>
  87 + <div class="info">
  88 + <p class="title">
  89 + {{ item.title }} <span class="label" v-if="item.tag">{{ item.tag }}</span>
  90 + </p>
  91 + <p class="num">
  92 + {{ item.gradeName }}
  93 + <em class="s-line">|</em>
  94 + 总题数:{{ item.questionNum }}
  95 + <em class="s-line">|</em>
  96 + 预计时长:{{ item.examsDuration }}
  97 + </p>
  98 + <p class="person">
  99 + {{ item.realName }}<em class="s-line">|</em
  100 + ><span class="date">{{ item.modifiedTime }}</span>
  101 + </p>
  102 + </div>
  103 + <div class="btn-box">
  104 + <el-tooltip effect="dark" content="恢复" placement="bottom">
  105 + <el-button
  106 + class="edit"
  107 + type="info"
  108 + size="mini"
  109 + circle
  110 + icon="fa fa-mail-reply"
  111 + @click="modify(item)"
  112 + ></el-button>
  113 + </el-tooltip>
  114 + <el-popconfirm title="确定删除这张答题卡吗?" @confirm="remove(item)">
  115 + <el-button
  116 + slot="reference"
  117 + class="delete"
  118 + type="info"
  119 + size="mini"
  120 + circle
  121 + icon="el-icon-delete"
  122 + ></el-button>
  123 + </el-popconfirm>
  124 + </div>
  125 + </li>
  126 + </ul>
  127 + <div class="pagination-box">
  128 + <el-pagination
  129 + small=""
  130 + layout="total,prev, pager, next"
  131 + :hide-on-single-page="true"
  132 + :total="total"
  133 + @current-change="changePage"
  134 + :current-page="page"
  135 + :page-size="size"
  136 + >
  137 + </el-pagination>
  138 + </div>
  139 + <el-empty :image-size="100" v-if="!tableData.length&&loading==false" description="没有更多数据"></el-empty>
  140 + </div>
  141 +</template>
  142 +
  143 +<script>
  144 +export default {
  145 + data() {
  146 + return {
  147 + loading:false,
  148 + props: { multiple: true, checkStrictly: true },
  149 + options: [
  150 + {
  151 + value: 1,
  152 + label: "一年级",
  153 + children: [
  154 + {
  155 + value: 2,
  156 + label: "二班",
  157 + },
  158 + {
  159 + value: 3,
  160 + label: "三班",
  161 + },
  162 + ],
  163 + },
  164 + {
  165 + value: 4,
  166 + label: "二年级",
  167 + children: [
  168 + {
  169 + value: 5,
  170 + label: "二班",
  171 + },
  172 + {
  173 + value: 6,
  174 + label: "三班",
  175 + },
  176 + ],
  177 + },
  178 + ],
  179 + query: {
  180 + classId: "",
  181 + subjectName: "",
  182 + tagId: "",
  183 + title: "",
  184 + },
  185 + classList: [],
  186 + subjectList: [],
  187 + typeList: [],
  188 + tableData: [],
  189 + total: 0,
  190 + page:1,
  191 + size:20
  192 + };
  193 + },
  194 + async created() {
  195 + await this._QueryClassList();
  196 + await this._QuerySubjectList();
  197 + this._QueryTypeList();
  198 + this._QueryData(false);
  199 + },
  200 + methods: {
  201 + async modify(obj) {
  202 + //恢复答题卡
  203 + const { data, status, info } = await this.$request.modifyPaper({
  204 + paperId: obj.id,
  205 + status: 1,
  206 + });
  207 + if (status == 0) {
  208 + let type = this.query.title ? 1 : 0;
  209 + this._QueryData(type);
  210 + } else {
  211 + this.$message.error(info);
  212 + }
  213 + },
  214 + async remove(obj) {
  215 + //删除答题卡
  216 + const { data, status, info } = await this.$request.delPaper({
  217 + paperId: obj.id,
  218 + });
  219 + if (status == 0) {
  220 + let type = this.query.title ? 1 : 0;
  221 + this._QueryData(type);
  222 + } else {
  223 + this.$message.error(info);
  224 + }
  225 + },
  226 + //切换班级
  227 + async changClazz() {
  228 + await this._QuerySubjectList();
  229 + this._QueryData(false);
  230 + },
  231 + changePage(page){
  232 + this.page = page
  233 + this._QueryData(this.query.title)
  234 + },
  235 + async _QueryData(type) {
  236 + //获取答题卡列表
  237 + this.loading=true
  238 + let query = {};
  239 + if (!type) {
  240 + this.query.title = "";
  241 + query = { ...this.query };
  242 + } else {
  243 + query = { title: this.query.title };
  244 + this.query.type = "";
  245 + this.query.subjectId = "";
  246 + }
  247 + query.classId = this.query.classId;
  248 + for (let key in query) {
  249 + if (!query[key]) {
  250 + query[key] = null;
  251 + }
  252 + }
  253 + const { data, status, info } = await this.$request.fetchPaperList({
  254 + ...query,
  255 + status:2,
  256 + page:this.page,
  257 + size:this.size
  258 + });
  259 + this.loading = false;
  260 + if (status === 0) {
  261 + this.total = data.total;
  262 + this.tableData = (data.list && [...data.list]) || [];
  263 + } else {
  264 + this.$message.error(info);
  265 + }
  266 + },
  267 + // 查找班级
  268 + async _QueryClassList() {
  269 + const { data, status, info } = await this.$request.fetchClassList();
  270 + console.log(status);
  271 + if (status === 0) {
  272 + if (!!data.list) {
  273 + this.classList = data.list?.map((item) => {
  274 + return {
  275 + value: item.classId,
  276 + label: item.className,
  277 + };
  278 + })||[];
  279 + this.query.classId = this.classList[0]?.value;
  280 + }
  281 + } else {
  282 + this.$message.error(info);
  283 + }
  284 + },
  285 + // 查找科目
  286 + async _QuerySubjectList() {
  287 + const { data, status, info } = await this.$request.fetchSubjectList({
  288 + classId: this.query.classId,
  289 + });
  290 + if (status === 0) {
  291 + this.subjectList = data.subjectNames?.map((item) => {
  292 + return {
  293 + value: item,
  294 + label: item,
  295 + };
  296 + })||[];
  297 + this.query.subjectName = this.subjectList[0]?.value;
  298 + } else {
  299 + this.$message.error(info);
  300 + }
  301 + },
  302 + // 查找答题卡类型
  303 + async _QueryTypeList() {
  304 + const { data, status, info } = await this.$request.fetchTypeNames({
  305 + classId: this.query.classId,
  306 + tyle:0
  307 + });
  308 + if (status === 0) {
  309 + this.typeList =
  310 + data.list?.map((item) => {
  311 + return {
  312 + value: item.tagId,
  313 + label: item.tag,
  314 + };
  315 + }) || [];
  316 + this.typeList.unshift({
  317 + value: "",
  318 + label: "请选择标签",
  319 + });
  320 + } else {
  321 + this.$message.error(info);
  322 + }
  323 + },
  324 + },
  325 +};
  326 +</script>
  327 +
  328 +<style lang="scss" scoped>
  329 +.answer-header {
  330 + .sel-box {
  331 + .sel {
  332 + min-width: 160px;
  333 + }
  334 + :deep(.el-cascader__tags) {
  335 + flex-wrap: nowrap;
  336 + }
  337 + }
  338 +}
  339 +.content {
  340 + margin: 0 20px;
  341 + background: #f8f8f8;
  342 + padding: 12px;
  343 + border-radius: 20px;
  344 + .item {
  345 + display: flex;
  346 + align-items: center;
  347 + width: 100%;
  348 + overflow: hidden;
  349 + box-sizing: border-box;
  350 + padding: 12px;
  351 + border-radius: 20px;
  352 + background: #fff;
  353 + margin-bottom: 12px;
  354 + &:last-of-type {
  355 + margin-bottom: 0;
  356 + }
  357 + .pic-box {
  358 + width: 80px;
  359 + height: 80px;
  360 + border-radius: 10px;
  361 + margin-right: 10px;
  362 + flex-shrink: 0;
  363 + background: #d7d7d7;
  364 + text-align: center;
  365 + color: #fff;
  366 + font-weight: 500;
  367 + .i-box {
  368 + padding-top: 10px;
  369 + font-size: 32px;
  370 + margin-bottom: 3px;
  371 + }
  372 + }
  373 + .info {
  374 + height: 80px;
  375 + flex: 1;
  376 + overflow: hidden;
  377 + display: flex;
  378 + flex-direction: column;
  379 + justify-content: space-between;
  380 + .s-line {
  381 + padding: 0 5px;
  382 + color: #e2e2e2;
  383 + }
  384 + .title {
  385 + font-size: 16px;
  386 + color: #222;
  387 + font-weight: 500;
  388 + .label {
  389 + display: inline-block;
  390 + font-size: 12px;
  391 + color: #2e9afe;
  392 + line-height: 16px;
  393 + padding: 0 10px;
  394 + border: 1px solid #2e9afe;
  395 + border-radius: 10px;
  396 + transform: translateY(-2px);
  397 + }
  398 + }
  399 + .person {
  400 + color: #666;
  401 + }
  402 + }
  403 + .btn-box {
  404 + flex-shrink: 0;
  405 + .edit {
  406 + margin-right: 12px;
  407 + }
  408 + }
  409 + }
  410 +}
  411 +</style>
0 412 \ No newline at end of file
... ...
src/views/portrait/index.vue renamed to src/views/standard/portrait/index.vue
src/views/setUp/account.vue renamed to src/views/standard/setUp/account.vue
src/views/setUp/conglomerate.vue renamed to src/views/standard/setUp/conglomerate.vue
... ... @@ -57,27 +57,27 @@
57 57 <div class="info">
58 58 <p class="info-item">
59 59 <span class="txt">基站</span>
60   - <img class="pic" src="../../assets/images/jizhan.png" />
  60 + <img class="pic" src="../../../assets/images/jizhan.png" />
61 61 <span>{{ item.stationCount }}</span>
62 62 </p>
63 63 <p class="info-item">
64 64 <span class="txt">键盘</span>
65   - <img class="pic" src="../../assets/images/jianpan.png" />
  65 + <img class="pic" src="../../../assets/images/jianpan.png" />
66 66 <span>{{ item.keyboardCount }}</span>
67 67 </p>
68 68 <p class="info-item">
69 69 <span class="txt">班级</span>
70   - <img class="pic" src="../../assets/images/banji.png" />
  70 + <img class="pic" src="../../../assets/images/banji.png" />
71 71 <span>{{ item.classCount }}</span>
72 72 </p>
73 73 <p class="info-item">
74 74 <span class="txt">学生</span>
75   - <img class="pic" src="../../assets/images/xuesheng.png" />
  75 + <img class="pic" src="../../../assets/images/xuesheng.png" />
76 76 <span>{{ item.studentCount }}</span>
77 77 </p>
78 78 <p class="info-item">
79 79 <span class="txt">老师</span>
80   - <img class="pic" src="../../assets/images/laoshi.png" />
  80 + <img class="pic" src="../../../assets/images/laoshi.png" />
81 81 <span>{{ item.teacherCount }}</span>
82 82 </p>
83 83 </div>
... ...
src/views/setUp/school.vue renamed to src/views/standard/setUp/school.vue
src/views/setUp/student.vue renamed to src/views/standard/setUp/student.vue
src/views/setUp/teacher.vue renamed to src/views/standard/setUp/teacher.vue
src/views/test/analysis.vue renamed to src/views/standard/test/analysis.vue
src/views/test/editAnswer.vue renamed to src/views/standard/test/editAnswer.vue
... ... @@ -919,7 +919,7 @@ export default {
919 919 }
920 920 .delButton {
921 921 border-color: #ff6868;
922   - background: #ff6868 url("../../assets/images/arrow.png") no-repeat center;
  922 + background: #ff6868 url("../../../assets/images/arrow.png") no-repeat center;
923 923 background-size: 19px;
924 924 cursor: pointer;
925 925 color: transparent;
... ...
src/views/test/index.vue renamed to src/views/standard/test/index.vue