examScheduling.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  1. <template>
  2. <el-container>
  3. <el-main v-loading="loading" class="el-main-padding">
  4. <commonFormVue :form="form" :getExamCondition="getExamCondition">
  5. <el-row v-show="showAllCondition">
  6. <el-col :span="6">
  7. <el-form-item label="完成状态">
  8. <el-select
  9. v-if="form.examType == '' || form.examType == 'ONLINE'"
  10. class="form_search_width"
  11. size="small"
  12. v-model="form.finished"
  13. clearable
  14. placeholder="全部"
  15. >
  16. <el-option value="1" label="已完成"></el-option>
  17. <el-option value="0" label="未完成"></el-option>
  18. </el-select>
  19. <el-select
  20. v-if="form.examType == 'OFFLINE'"
  21. class="form_search_width"
  22. size="small"
  23. v-model="form.finished"
  24. clearable
  25. placeholder="全部"
  26. >
  27. <el-option value="0" label="未抽题"></el-option>
  28. <el-option value="1" label="已抽题"></el-option>
  29. <el-option value="2" label="已上传"></el-option>
  30. </el-select>
  31. </el-form-item>
  32. </el-col>
  33. <el-col :span="6">
  34. <el-form-item label="采集人">
  35. <el-input
  36. class="form_search_width"
  37. size="small"
  38. v-model="form.infoCollector"
  39. placeholder="采集人"
  40. ></el-input>
  41. </el-form-item>
  42. </el-col>
  43. </el-row>
  44. </commonFormVue>
  45. <el-col :span="24">
  46. <el-button
  47. @click="search('clickSelectBtn')"
  48. size="small"
  49. type="primary"
  50. icon="el-icon-search"
  51. >查询</el-button
  52. >
  53. <el-button
  54. size="small"
  55. type="primary"
  56. icon="el-icon-more"
  57. v-if="!showAllCondition"
  58. @click="showMoreCondition"
  59. >高级查询</el-button
  60. >
  61. <el-button
  62. size="small"
  63. type="primary"
  64. v-if="showAllCondition"
  65. @click="showSimpleCondition"
  66. >简单查询</el-button
  67. >
  68. <el-button
  69. size="small"
  70. icon="el-icon-refresh"
  71. @click="resetForm"
  72. class="margin-bottom-10"
  73. >重置</el-button
  74. >
  75. </el-col>
  76. <el-row>
  77. <el-col
  78. :span="24"
  79. v-show="currentPagePrivileges.EXAM_PARTICULARS_EXPORT"
  80. >
  81. <div class="block-seperator"></div>
  82. <span>操作:</span>
  83. <commonExportVue
  84. :form="form"
  85. :exportUrl="exportUrl"
  86. :exportFileName="exportFileName"
  87. ></commonExportVue>
  88. </el-col>
  89. </el-row>
  90. <el-row class="margin-top-10">
  91. <el-col :span="24">
  92. <el-table
  93. v-loading="tableLoading"
  94. element-loading-text="数据加载中"
  95. ref="multipleTable"
  96. @selection-change="handleSelectionChange"
  97. :data="tableData"
  98. border
  99. >
  100. <el-table-column
  101. sortable
  102. label="学习中心"
  103. prop="orgName"
  104. width="120"
  105. ></el-table-column>
  106. <el-table-column
  107. sortable
  108. label="姓名"
  109. prop="studentName"
  110. width="120"
  111. ></el-table-column>
  112. <el-table-column
  113. sortable
  114. label="身份证号"
  115. prop="identityNumber"
  116. width="120"
  117. ></el-table-column>
  118. <el-table-column
  119. sortable
  120. label="学号"
  121. prop="studentCode"
  122. width="120"
  123. ></el-table-column>
  124. <el-table-column
  125. sortable
  126. label="课程"
  127. prop="courseName"
  128. width="120"
  129. ></el-table-column>
  130. <el-table-column
  131. sortable
  132. label="课程层次"
  133. prop="courseLevel"
  134. width="120"
  135. ></el-table-column>
  136. <el-table-column
  137. sortable
  138. label="专业"
  139. prop="specialtyName"
  140. width="120"
  141. ></el-table-column>
  142. <el-table-column
  143. sortable
  144. label="已考次数"
  145. prop="normalExamTimes"
  146. width="120"
  147. ></el-table-column>
  148. <el-table-column
  149. sortable
  150. label="学生电话"
  151. prop="phone"
  152. width="120"
  153. ></el-table-column>
  154. <el-table-column
  155. sortable
  156. label="年级"
  157. prop="grade"
  158. width="120"
  159. ></el-table-column>
  160. <el-table-column
  161. sortable
  162. label="采集人"
  163. prop="infoCollector"
  164. width="120"
  165. ></el-table-column>
  166. <el-table-column
  167. sortable
  168. label="完成状态"
  169. prop="finishedStatus"
  170. width="120"
  171. ></el-table-column>
  172. <!-- <el-table-column sortable label="完成状态" width="120">
  173. <template slot-scope="scope">
  174. <span> {{ scope.row.finishedStatus }} </span>
  175. </template>
  176. </el-table-column>-->
  177. <el-table-column fixed="right" label="操作" width="120">
  178. <template slot-scope="scope">
  179. <el-row class="operateRow">
  180. <el-col :span="24">
  181. <el-button
  182. plain
  183. type="primary"
  184. size="mini"
  185. icon="el-icon-view"
  186. @click="previewPaper(scope.row.examStudentId)"
  187. v-if="scope.row.examType == 'OFFLINE'"
  188. >查看考题</el-button
  189. >
  190. </el-col>
  191. </el-row>
  192. <el-row class="operateRow">
  193. <el-col :span="24">
  194. <el-button
  195. plain
  196. type="primary"
  197. size="mini"
  198. icon="el-icon-download"
  199. @click="exportPaper(scope.row.examStudentId)"
  200. v-if="scope.row.examType == 'OFFLINE'"
  201. >下载考题</el-button
  202. >
  203. </el-col>
  204. </el-row>
  205. <el-row class="operateRow">
  206. <el-col :span="24">
  207. <el-button
  208. plain
  209. type="primary"
  210. size="mini"
  211. icon="el-icon-upload2"
  212. @click="openUploadAnswerDialog(scope.row.examStudentId)"
  213. v-if="
  214. scope.row.examType == 'OFFLINE' &&
  215. scope.row.canUploadAttachment
  216. "
  217. >上传作答</el-button
  218. >
  219. </el-col>
  220. </el-row>
  221. <el-row class="operateRow">
  222. <el-col :span="24">
  223. <el-button
  224. plain
  225. type="primary"
  226. size="mini"
  227. icon="el-icon-download"
  228. @click="downloadAnswer(scope.row.offlineFileUrl)"
  229. v-if="
  230. scope.row.examType == 'OFFLINE' &&
  231. scope.row.offlineFileUrl
  232. "
  233. >下载作答</el-button
  234. >
  235. </el-col>
  236. </el-row>
  237. </template>
  238. </el-table-column>
  239. </el-table>
  240. <div class="page pull-right">
  241. <el-pagination
  242. @size-change="handleSizeChange"
  243. @current-change="handleCurrentChange"
  244. :current-page.sync="form.pageNo"
  245. :page-sizes="[10, 20, 50, 100]"
  246. :page-size="form.pageSize"
  247. layout="total, sizes, prev, pager, next, jumper"
  248. :total="total"
  249. ></el-pagination>
  250. </div>
  251. </el-col>
  252. </el-row>
  253. <div>
  254. <el-dialog
  255. title="上传作答"
  256. v-loading="uploadAnswerDialogLoading"
  257. :visible.sync="uploadAnswerDialogVisible"
  258. @closed="cleanOfflineFile"
  259. >
  260. <el-form>
  261. <el-form-item label="选择文件">
  262. <input
  263. type="file"
  264. accept="application/pdf, application/zip"
  265. id="importFile"
  266. ref="offlineFileInput"
  267. @change="uploadAnswerChange"
  268. v-bind:class="{ offline_file: offlineAnswerFile }"
  269. />
  270. <div>温馨提示:仅支持pdf和zip文件,文件大小请不要超过30M!</div>
  271. </el-form-item>
  272. <div class="dialog-footer">
  273. <el-button @click="uploadAnswerDialogVisible = false"
  274. >取 消</el-button
  275. >
  276. <el-button
  277. :disabled="!offlineAnswerFile"
  278. type="primary"
  279. @click="doUploadAnswer"
  280. >确 定</el-button
  281. >
  282. </div>
  283. </el-form>
  284. </el-dialog>
  285. </div>
  286. </el-main>
  287. </el-container>
  288. </template>
  289. <script>
  290. import { mapState } from "vuex";
  291. import commonFormVue from "../component/commonForm.vue";
  292. import commonExportVue from "../component/commonExport.vue";
  293. import pagePrivilege from "../mixin/pagePrivilege.js";
  294. export default {
  295. components: { commonFormVue, commonExportVue },
  296. mixins: [pagePrivilege],
  297. data() {
  298. return {
  299. loading: false,
  300. uploadAnswerDialogLoading: false,
  301. uploadAnswerDialogVisible: false,
  302. total: 0,
  303. tableLoading: false,
  304. showAllCondition: false,
  305. form: {
  306. examRecordDataId: null,
  307. hasStranger: null,
  308. courseId: null,
  309. courseLevel: null,
  310. examId: null,
  311. examRecordId: null,
  312. faceSuccessPercentLower: null,
  313. faceSuccessPercentUpper: null,
  314. livenessSuccessPercentLower: null,
  315. livenessSuccessPercentUpper: null,
  316. identityNumber: null,
  317. orgId: null,
  318. studentCode: null,
  319. studentName: null,
  320. isWarn: null,
  321. pageNo: 1,
  322. pageSize: 10,
  323. examType: ""
  324. },
  325. getExamCondition: {
  326. params: {
  327. name: "",
  328. examTypes: "ONLINE#OFFLINE"
  329. },
  330. filterCondition: ""
  331. },
  332. tableData: [],
  333. exportUrl: "/api/ecs_oe_admin/exam/student/examScheduling/list/export",
  334. exportFileName: "考试进度详情",
  335. currentOfflineExamRecordDataId: "",
  336. offlineAnswerFile: "",
  337. currentPagePrivileges: {
  338. EXAM_PARTICULARS_EXPORT: false //导出
  339. }
  340. };
  341. },
  342. computed: {
  343. ...mapState({ user: state => state.user })
  344. },
  345. methods: {
  346. resetForm() {
  347. this.form = {
  348. examRecordDataId: null,
  349. hasStranger: null,
  350. courseId: null,
  351. courseLevel: null,
  352. examId: null,
  353. examRecordId: null,
  354. faceSuccessPercentLower: null,
  355. faceSuccessPercentUpper: null,
  356. livenessSuccessPercentLower: null,
  357. livenessSuccessPercentUpper: null,
  358. identityNumber: null,
  359. orgId: null,
  360. studentCode: null,
  361. studentName: null,
  362. isWarn: null,
  363. pageNo: 1,
  364. pageSize: 10,
  365. examType: ""
  366. };
  367. },
  368. showMoreCondition() {
  369. this.showAllCondition = true;
  370. },
  371. showSimpleCondition() {
  372. this.$notify({
  373. title: "提示",
  374. message: "高级查询条件值已重置",
  375. type: "info",
  376. duration: 2000
  377. });
  378. this.resetForm();
  379. this.showAllCondition = false;
  380. },
  381. search(type) {
  382. if (!this.form.examId) {
  383. this.$notify({
  384. title: "警告",
  385. message: "请选择考试批次",
  386. type: "warning",
  387. duration: 2000
  388. });
  389. return false;
  390. }
  391. if (type && type == "clickSelectBtn") {
  392. this.form.pageNo = 1;
  393. }
  394. this.tableLoading = true;
  395. this.$http
  396. .post("/api/ecs_oe_admin/exam/student/examScheduling/list", this.form)
  397. .then(response => {
  398. if (response.data) {
  399. var dataList = response.data.content;
  400. this.tableData = dataList;
  401. this.total = response.data.totalElements;
  402. } else {
  403. this.tableData = [];
  404. }
  405. this.tableLoading = false;
  406. });
  407. },
  408. selectable(row) {
  409. return row.isWarn;
  410. },
  411. handleSelectionChange(val) {
  412. this.multipleSelection = val;
  413. },
  414. /**
  415. * pagesize改变时触发
  416. */
  417. handleSizeChange(val) {
  418. this.form.pageSize = val;
  419. this.search();
  420. },
  421. /**
  422. * 当前页改变时触发
  423. */
  424. handleCurrentChange() {
  425. this.search();
  426. },
  427. previewPaper(examStudentId) {
  428. this.$http
  429. .get("/api/ecs_oe_admin/exam/record/select/byExamStudentId", {
  430. params: { examStudentId: examStudentId }
  431. })
  432. .then(response => {
  433. if (response.data) {
  434. var examRecordList = response.data;
  435. if (examRecordList && examRecordList.length > 0) {
  436. window.open(
  437. "/admin/preview_paper/" + examRecordList[0].basePaperId
  438. );
  439. } else {
  440. this.$notify({
  441. title: "提示",
  442. message: "该考生未参加考试",
  443. type: "error",
  444. duration: 2000
  445. });
  446. }
  447. }
  448. });
  449. },
  450. exportPaper(examStudentId) {
  451. this.loading = true;
  452. var currentUser = this.user;
  453. this.$http
  454. .get("/api/ecs_oe_admin/exam/record/select/byExamStudentId", {
  455. params: { examStudentId: examStudentId }
  456. })
  457. .then(response => {
  458. if (response.data) {
  459. var examRecordList = response.data;
  460. if (examRecordList && examRecordList.length > 0) {
  461. var basePaperId = examRecordList[0].basePaperId;
  462. var rootOrgName = currentUser.rootOrgName;
  463. this.$http
  464. .get(
  465. "/api/ecs_ques/paper/export/" +
  466. basePaperId +
  467. "/PAPER/" +
  468. rootOrgName +
  469. "/" +
  470. basePaperId +
  471. "/offLine",
  472. {
  473. responseType: "arraybuffer",
  474. filename: "utf-8"
  475. }
  476. )
  477. .then(response => {
  478. if (response.data && response.data.byteLength > 0) {
  479. var blob = new Blob([response.data], {
  480. type: "application/zip"
  481. });
  482. var url = URL.createObjectURL(blob);
  483. var fileName = response.headers["content-disposition"]
  484. .split(";")[1]
  485. .replace("filename=", "");
  486. var a = document.createElement("a");
  487. a.href = url;
  488. a.download = decodeURI(fileName);
  489. a.target = "_blank";
  490. a.click();
  491. URL.revokeObjectURL(url);
  492. } else {
  493. this.$notify({
  494. title: "提示",
  495. message: "无相关文件",
  496. type: "error",
  497. duration: 2000
  498. });
  499. }
  500. this.loading = false;
  501. })
  502. .catch(() => {
  503. this.loading = false;
  504. });
  505. } else {
  506. this.loading = false;
  507. this.$notify({
  508. title: "提示",
  509. message: "该考生未参加考试",
  510. type: "error",
  511. duration: 2000
  512. });
  513. }
  514. }
  515. });
  516. },
  517. openUploadAnswerDialog(examStudentId) {
  518. this.$http
  519. .get("/api/ecs_oe_admin/exam/record/data/findByExamStudentId", {
  520. params: { examStudentId: examStudentId }
  521. })
  522. .then(response => {
  523. var examRecordDataList = response.data;
  524. if (examRecordDataList.length == 0) {
  525. this.$notify({
  526. title: "提示",
  527. message: "该考生未参加考试",
  528. type: "error",
  529. duration: 2000
  530. });
  531. } else {
  532. this.uploadAnswerDialogVisible = true;
  533. this.currentOfflineExamStudentId = examStudentId;
  534. this.currentOfflineExamRecordDataId = examRecordDataList[0].id;
  535. }
  536. });
  537. },
  538. uploadAnswerChange(event) {
  539. if (event.target.files.length > 0) {
  540. this.offlineAnswerFile = event.target.files[0];
  541. } else {
  542. this.offlineAnswerFile = "";
  543. }
  544. },
  545. doUploadAnswer() {
  546. var index = this.offlineAnswerFile.name.lastIndexOf(".");
  547. var fileNameLength = this.offlineAnswerFile.name.length;
  548. var fileSuffix = this.offlineAnswerFile.name
  549. .substring(index + 1, fileNameLength)
  550. .toUpperCase();
  551. this.$http
  552. .get(
  553. "/api/ecs_exam_work/exam/property/" +
  554. this.form.examId +
  555. "/OFFLINE_UPLOAD_FILE_TYPE"
  556. )
  557. .then(response => {
  558. var allowfileSuffixs = response.data;
  559. if (!allowfileSuffixs || allowfileSuffixs.length == 0) {
  560. this.$notify({
  561. title: "提示",
  562. message: "当前考试设置不允许上传附件",
  563. type: "error",
  564. duration: 2000
  565. });
  566. return false;
  567. }
  568. if (allowfileSuffixs.toString().indexOf(fileSuffix) < 0) {
  569. this.$notify({
  570. title: "提示",
  571. message:
  572. "当前考试允许上传文件格式为:" + allowfileSuffixs.toString(),
  573. type: "error",
  574. duration: 2000
  575. });
  576. return false;
  577. }
  578. this.uploadAnswerDialogLoading = true;
  579. let config = {
  580. headers: { "Content-Type": "multipart/form-data" }
  581. };
  582. let param = new FormData();
  583. param.append("file", this.offlineAnswerFile);
  584. param.append("examRecordDataId", this.currentOfflineExamRecordDataId);
  585. this.$http
  586. .post("/api/ecs_oe_student/offlineExam/submitPaper", param, config)
  587. .then(() => {
  588. this.$notify({
  589. title: "提示",
  590. message: "上传成功",
  591. type: "success",
  592. duration: 2000
  593. });
  594. this.uploadAnswerDialogVisible = false;
  595. this.uploadAnswerDialogLoading = false;
  596. this.$refs.offlineFileInput.value = "";
  597. this.offlineAnswerFile = "";
  598. this.search();
  599. })
  600. .catch(() => {
  601. this.$notify({
  602. title: "提示",
  603. message: "上传失败",
  604. type: "error",
  605. duration: 2000
  606. });
  607. this.uploadAnswerDialogLoading = false;
  608. this.$refs.offlineFileInput.value = "";
  609. this.offlineAnswerFile = "";
  610. });
  611. });
  612. },
  613. cleanOfflineFile() {
  614. this.$refs.offlineFileInput.value = "";
  615. this.offlineAnswerFile = "";
  616. },
  617. downloadAnswer(offlineFileUrl) {
  618. window.open(offlineFileUrl);
  619. }
  620. },
  621. created() {}
  622. };
  623. </script>
  624. <style scoped>
  625. .offline_file {
  626. color: blue;
  627. }
  628. </style>
  629. <style scoped src="../style/common.css"></style>