ApplyContent.vue 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172
  1. <template>
  2. <div class="apply-content task-detail">
  3. <div class="task-body">
  4. <div v-if="IS_APPLY" class="mb-2 text-right">
  5. <el-button
  6. type="info"
  7. icon="el-icon-circle-plus-outline"
  8. @click="addAtachment"
  9. >增加卷型</el-button
  10. >
  11. </div>
  12. <table class="table mb-2">
  13. <tr>
  14. <th>试卷类型</th>
  15. <th>试卷文件</th>
  16. <th>答题卡创建方式</th>
  17. <th>答题卡</th>
  18. <th v-if="IS_APPLY">操作</th>
  19. </tr>
  20. <tr v-for="(attachment, index) in paperAttachments" :key="index">
  21. <td>
  22. <span>{{ attachment.name }}卷</span>
  23. <span class="color-gray-2" v-if="attachment.isExposed"
  24. >(已曝光)</span
  25. >
  26. </td>
  27. <td>
  28. <el-button
  29. v-if="!attachment.isExposed && IS_APPLY"
  30. type="text"
  31. class="btn-primary"
  32. @click="toUpload(attachment)"
  33. >
  34. <i
  35. :class="[
  36. 'icon',
  37. attachment.attachmentId ? 'icon-files-act' : 'icon-files'
  38. ]"
  39. ></i
  40. >{{
  41. attachment.attachmentId
  42. ? attachment.filename
  43. : "点击上传试卷文件"
  44. }}
  45. </el-button>
  46. <el-button
  47. v-else
  48. type="text"
  49. class="btn-primary"
  50. @click="downloadPaper(attachment)"
  51. >
  52. <i
  53. class="icon icon-download mr-1"
  54. v-if="attachment.attachmentId"
  55. ></i>
  56. <i>{{ attachment.filename }}</i>
  57. </el-button>
  58. </td>
  59. <td :rowspan="paperAttachments.length" v-if="index === 0">
  60. {{ createCardTypeName }}
  61. </td>
  62. <td :rowspan="paperAttachments.length" v-if="index === 0">
  63. <el-button
  64. type="text"
  65. class="btn-primary"
  66. v-if="!exposedMode && IS_APPLY"
  67. @click="toCreateOrViewCard"
  68. >{{ cardTodoName }}</el-button
  69. >
  70. <el-button
  71. v-else
  72. type="text"
  73. class="btn-primary"
  74. @click="toViewCard"
  75. >
  76. 查看题卡
  77. </el-button>
  78. <el-button
  79. v-if="
  80. curTaskApply.makeMethod &&
  81. curTaskApply.makeMethod !== 'CUST' &&
  82. !exposedMode &&
  83. IS_APPLY
  84. "
  85. type="text"
  86. class="btn-danger"
  87. @click="changeCreateCardType"
  88. >切换题卡创建方式</el-button
  89. >
  90. </td>
  91. <td v-if="IS_APPLY">
  92. <el-button
  93. v-if="attachment.canDelete"
  94. class="btn-danger"
  95. type="text"
  96. @click="deleteAttachment(index)"
  97. >删除</el-button
  98. >
  99. </td>
  100. </tr>
  101. <tr v-if="!paperAttachments.length">
  102. <td colspan="5">
  103. <p class="tips-info text-center">暂无数据</p>
  104. </td>
  105. </tr>
  106. </table>
  107. <p class="tips-info tips-dark mb-2">
  108. 提示:多卷型试卷由于会绑定一个答题卡模板,因此试卷结构必须相同。多卷型试卷之间客观题要求试题内容相同,可允许大题内的小题题序不同。
  109. </p>
  110. <el-form>
  111. <el-form-item label="单次抽卷卷型数量:" label-width="150">
  112. <el-input-number
  113. v-model="curTaskApply.drawCount"
  114. :min="1"
  115. :max="maxFetchCount"
  116. :step="1"
  117. :controls="false"
  118. step-strictly
  119. disabled
  120. ></el-input-number>
  121. <!-- :disabled="!IS_APPLY || exposedMode" -->
  122. </el-form-item>
  123. </el-form>
  124. <h4 class="mb-2">附件<span v-if="IS_APPLY">(最多4张)</span>:</h4>
  125. <div class="image-list">
  126. <div
  127. class="image-item"
  128. v-for="(img, index) in paperConfirmAttachments"
  129. :key="index"
  130. >
  131. <img
  132. :src="img.url"
  133. :alt="img.filename"
  134. title="点击查看大图"
  135. @click="toPreview(index)"
  136. />
  137. <div v-if="IS_APPLY" class="image-delete">
  138. <i
  139. class="el-icon-delete-solid"
  140. @click="deletePaperConfirmAttachment(index)"
  141. ></i>
  142. </div>
  143. </div>
  144. <div
  145. v-if="paperConfirmAttachments.length < 4 && IS_APPLY"
  146. class="image-item image-add"
  147. title="上传附件"
  148. @click="toUploadPaperConfirm"
  149. >
  150. <i class="el-icon-plus"></i>
  151. </div>
  152. </div>
  153. <div
  154. v-if="!IS_APPLY && !paperConfirmAttachments.length"
  155. class="image-list-none"
  156. >
  157. 暂无数据
  158. </div>
  159. <h4 class="mb-2">附件说明:</h4>
  160. <el-input
  161. v-if="IS_APPLY"
  162. class="mb-2"
  163. v-model="curTaskApply.remark"
  164. type="textarea"
  165. resize="none"
  166. :rows="2"
  167. :maxlength="100"
  168. clearable
  169. show-word-limit
  170. placeholder="建议不超过100个字"
  171. ></el-input>
  172. <div class="color-gray-2" v-else>
  173. <p v-if="curTaskApply.remark">{{ curTaskApply.remark }}</p>
  174. <p v-else>暂无</p>
  175. </div>
  176. <!-- 入库申请-审核阶段 -->
  177. <!-- audit history -->
  178. <div
  179. v-if="flowHistoryList.length && curTaskApply.flowStatus !== 'START'"
  180. class="task-audit-history flow-timeline"
  181. >
  182. <h4 class="mb-4">审核记录:</h4>
  183. <el-timeline>
  184. <el-timeline-item
  185. v-for="flow in flowHistoryList"
  186. :key="flow.stepKey"
  187. :type="flow.type"
  188. hide-timestamp
  189. size="large"
  190. placement="top"
  191. :class="{ 'timeline-item-stop': flow.nextIsNewFlow }"
  192. >
  193. <div class="flow-item">
  194. <div class="flow-item-content">
  195. <p v-if="flow.createTime" class="flow-item-time">
  196. {{ flow.createTime | timestampFilter }}
  197. </p>
  198. <h4 class="flow-item-title">{{ flow.approveUserName }}</h4>
  199. <p class="flow-item-desc">
  200. <span
  201. v-if="flow.approveOperation"
  202. :class="{
  203. 'color-danger': flow.approveOperation === 'REJECT'
  204. }"
  205. >{{
  206. flow.approveOperation | flowApproveOperationTypeFilter
  207. }}</span
  208. >
  209. <span>{{ flow.approveRemark }}</span>
  210. </p>
  211. <div
  212. v-if="flow.attachments.length"
  213. class="flow-item-attachment"
  214. >
  215. <span>附件:</span>
  216. <el-button
  217. v-for="item in flow.attachments"
  218. :key="item.name"
  219. type="text"
  220. class="btn-primary"
  221. @click="downloadPaper(item)"
  222. >{{ item.name }}卷:{{ item.filename }}</el-button
  223. >
  224. </div>
  225. <div
  226. v-if="flow.isApproveSetFlowNextNode && approveUsers.length"
  227. class="flow-item-users"
  228. >
  229. <span>审批人:</span>
  230. <el-tag
  231. v-for="user in approveUsers"
  232. :key="user.id"
  233. size="small"
  234. :disable-transitions="false"
  235. >
  236. {{ user.name }}
  237. </el-tag>
  238. </div>
  239. </div>
  240. <div
  241. v-if="
  242. flow.isApproveSetFlowNextNode &&
  243. auditModal.approvePass === 'PASS'
  244. "
  245. class="flow-item-action"
  246. >
  247. <el-button
  248. class="user-select"
  249. icon="el-icon-plus"
  250. @click="toSelectNextFlowUser"
  251. ></el-button>
  252. <p v-if="!approveUsers.length" class="tips-info tips-error">
  253. 请选择审核人
  254. </p>
  255. </div>
  256. </div>
  257. </el-timeline-item>
  258. </el-timeline>
  259. </div>
  260. <!-- 入库申请-申请阶段 -->
  261. <div
  262. v-if="flowList.length && curTaskApply.flowStatus === 'START'"
  263. class="task-audit-history flow-timeline"
  264. >
  265. <h4 class="mb-4">流程:</h4>
  266. <el-timeline>
  267. <el-timeline-item
  268. v-for="flow in flowList"
  269. :key="flow.taskKey"
  270. :type="flow.type"
  271. >
  272. <div class="flow-item">
  273. <div class="flow-item-content">
  274. <h4 class="flow-item-title">{{ flow.taskName }}</h4>
  275. <p v-if="flow.approveUserNames" class="flow-item-desc">
  276. {{ flow.approveUserNames }}
  277. </p>
  278. <div
  279. v-if="flow.isApproveSetFlowNextNode && approveUsers.length"
  280. class="flow-item-users"
  281. >
  282. <span>审批人:</span>
  283. <el-tag
  284. v-for="user in approveUsers"
  285. :key="user.id"
  286. size="small"
  287. :disable-transitions="false"
  288. >
  289. {{ user.name }}
  290. </el-tag>
  291. </div>
  292. </div>
  293. <div
  294. v-if="flow.isApproveSetFlowNextNode"
  295. class="flow-item-action"
  296. >
  297. <el-button
  298. class="user-select"
  299. icon="el-icon-plus"
  300. @click="toSelectNextFlowUser"
  301. ></el-button>
  302. <p v-if="!approveUsers.length" class="tips-info tips-error">
  303. 请选择审核人
  304. </p>
  305. </div>
  306. </div>
  307. </el-timeline-item>
  308. </el-timeline>
  309. </div>
  310. <!-- audit -->
  311. <div v-if="IS_AUDIT" class="task-audit">
  312. <el-form
  313. ref="auditModalComp"
  314. :model="auditModal"
  315. :rules="auditRules"
  316. label-width="90px"
  317. >
  318. <el-form-item prop="approvePass" label="审批操作:">
  319. <el-radio-group
  320. v-model="auditModal.approvePass"
  321. @change="approvePassChange"
  322. >
  323. <el-radio
  324. v-for="(val, key) in TASK_AUDIT_RESULT"
  325. :key="key"
  326. :label="key"
  327. >{{ val }}</el-radio
  328. >
  329. </el-radio-group>
  330. </el-form-item>
  331. <el-form-item
  332. v-if="auditModal.approvePass === 'REJECT'"
  333. prop="setup"
  334. label="驳回节点:"
  335. >
  336. <el-select
  337. v-model="auditModal.setup"
  338. placeholder="请选择"
  339. style="width: 100%"
  340. >
  341. <el-option
  342. v-for="item in rejectSetupList"
  343. :key="item.taskKey"
  344. :value="item.setup"
  345. :label="item.name"
  346. >
  347. </el-option>
  348. </el-select>
  349. </el-form-item>
  350. <el-form-item
  351. v-if="auditModal.approvePass !== 'EXCHANGE'"
  352. :key="auditModal.approvePass"
  353. prop="remark"
  354. label="审批意见:"
  355. >
  356. <el-input
  357. class="mb-2"
  358. v-model="auditModal.remark"
  359. type="textarea"
  360. resize="none"
  361. :rows="5"
  362. :maxlength="100"
  363. clearable
  364. show-word-limit
  365. placeholder="建议不超过100个字"
  366. ref="ReasonInput"
  367. ></el-input>
  368. </el-form-item>
  369. <el-form-item
  370. v-if="auditModal.approvePass === 'EXCHANGE'"
  371. prop="userId"
  372. label="审批人:"
  373. >
  374. <el-tag
  375. v-for="user in exchangeUsers"
  376. :key="user.id"
  377. :disable-transitions="false"
  378. size="medium"
  379. >
  380. {{ user.name }}
  381. </el-tag>
  382. <el-button
  383. class="ml-2"
  384. size="mini"
  385. type="primary"
  386. @click="toSelectExchangeUser"
  387. >选择用户</el-button
  388. >
  389. </el-form-item>
  390. </el-form>
  391. </div>
  392. </div>
  393. <div class="task-action">
  394. <el-button
  395. v-if="IS_APPLY"
  396. type="primary"
  397. :disabled="isSubmit"
  398. @click="submit"
  399. >确认提交</el-button
  400. >
  401. <el-button
  402. v-if="IS_APPLY"
  403. type="primary"
  404. :disabled="isSubmit"
  405. @click="toSave"
  406. >暂存</el-button
  407. >
  408. <el-button
  409. v-if="IS_AUDIT"
  410. type="primary"
  411. :disabled="isSubmit"
  412. @click="toAuditSubmit"
  413. >确定</el-button
  414. >
  415. <!-- <el-button
  416. v-if="IS_AUDIT_APPLY"
  417. type="primary"
  418. :disabled="isSubmit"
  419. @click="toAuditApply"
  420. >提交</el-button
  421. > -->
  422. <el-button @click="cancel">取消</el-button>
  423. </div>
  424. <!-- upload-paper-dialog -->
  425. <upload-paper-dialog
  426. :paper-attachment="curAttachment"
  427. :upload-type="curUploadType"
  428. @confirm="uploadConfirm"
  429. ref="UploadPaperDialog"
  430. ></upload-paper-dialog>
  431. <!-- card-option-dialog -->
  432. <card-option-dialog
  433. ref="CardOptionDialog"
  434. :data="task"
  435. @confirm="cardConfirm"
  436. ></card-option-dialog>
  437. <!-- image-preview -->
  438. <simple-image-preview
  439. :cur-image="curImage"
  440. @on-prev="toPrevImage"
  441. @on-next="toNextImage"
  442. ref="SimpleImagePreview"
  443. ></simple-image-preview>
  444. <!-- select-user-dialog -->
  445. <select-user-dialog
  446. v-if="IS_AUDIT || IS_NEED_SELECT_APPROVE_USER"
  447. ref="SelectUserDialog"
  448. :user-limit-count="userLimitCount"
  449. :filter-roles="userFilterRoles"
  450. :users="curSelectedUsers"
  451. @modified="userSelected"
  452. ></select-user-dialog>
  453. <!-- ModifyCard -->
  454. <modify-card ref="ModifyCard" @modified="cardModified"></modify-card>
  455. </div>
  456. </template>
  457. <script>
  458. import UploadPaperDialog from "./UploadPaperDialog";
  459. import CardOptionDialog from "./CardOptionDialog";
  460. import {
  461. taskApplyDetail,
  462. updateTaskApply,
  463. // updateTaskReview,
  464. taskAuditApply,
  465. switchCardCreateMethod
  466. // taskAllFlowSetups,
  467. // taskAllApproverPeople,
  468. // taskAllApproverExchangePeople,
  469. // taskApproverNextPeople
  470. } from "../api";
  471. import { attachmentPreview } from "../../login/api";
  472. import SimpleImagePreview from "@/components/SimpleImagePreview";
  473. import SelectUserDialog from "../../base/components/SelectUserDialog";
  474. import ModifyCard from "../../card/components/ModifyCard";
  475. import { CARD_SOURCE_TYPE, TASK_AUDIT_RESULT } from "@/constants/enumerate";
  476. import {
  477. taskFlowDetail,
  478. taskFlowApproverExchange,
  479. taskFlowNodeInfo,
  480. taskFlowApprover,
  481. flowDetailByFlowId
  482. } from "../../base/api";
  483. const initTaskApply = {
  484. examId: "",
  485. examTaskId: "",
  486. paperType: "A",
  487. paperAttachmentIds: "",
  488. paperConfirmAttachmentIds: "",
  489. cardId: "",
  490. cardRuleId: "",
  491. makeMethod: "",
  492. remark: "",
  493. courseCode: "",
  494. courseName: "",
  495. drawCount: 1,
  496. exposedPaperType: "",
  497. // 流程
  498. flowId: "",
  499. flowStatus: "",
  500. setup: 0,
  501. // 工作台任务id
  502. flowTaskId: "",
  503. // 题卡状态
  504. status: "",
  505. // 考务规则
  506. review: false,
  507. includePaper: false,
  508. customCard: false
  509. };
  510. export default {
  511. name: "apply-content",
  512. components: {
  513. UploadPaperDialog,
  514. CardOptionDialog,
  515. SimpleImagePreview,
  516. SelectUserDialog,
  517. ModifyCard
  518. },
  519. props: {
  520. examTask: {
  521. type: Object,
  522. default() {
  523. return {};
  524. }
  525. },
  526. editType: {
  527. type: String,
  528. default: ""
  529. }
  530. },
  531. data() {
  532. return {
  533. isSubmit: false,
  534. curTaskApply: { ...initTaskApply },
  535. paperConfirmAttachmentId: { attachmentId: "", filename: "", url: "" },
  536. paperAttachments: [],
  537. paperConfirmAttachments: [],
  538. curAttachment: {},
  539. curUploadType: "paper",
  540. attachmentLimitCount: 26,
  541. abc: "abcdefghijklmnopqrstuvwxyz".toUpperCase(),
  542. task: {},
  543. reason: "",
  544. TASK_AUDIT_RESULT: { ...TASK_AUDIT_RESULT, EXCHANGE: "转他人审批" },
  545. // audit
  546. flowList: [],
  547. flowInfo: {},
  548. auditModal: {
  549. approvePass: "PASS",
  550. setup: "",
  551. remark: "",
  552. userId: ""
  553. },
  554. auditRules: {
  555. approvePass: [
  556. {
  557. required: true
  558. }
  559. ],
  560. setup: [
  561. {
  562. required: true,
  563. validator: (rule, value, callback) => {
  564. if (this.auditModal.approvePass === "REJECT" && !value) {
  565. callback(new Error(`请选择要驳回到的节点`));
  566. } else {
  567. callback();
  568. }
  569. },
  570. trigger: "change"
  571. }
  572. ],
  573. remark: [
  574. {
  575. required: false,
  576. validator: (rule, value, callback) => {
  577. if (this.auditModal.approvePass === "REJECT" && !value) {
  578. callback(new Error(`请输入审批意见`));
  579. } else {
  580. callback();
  581. }
  582. },
  583. trigger: "change"
  584. }
  585. ],
  586. userId: [
  587. {
  588. required: true,
  589. message: "请选择审批人",
  590. trigger: "change"
  591. }
  592. ]
  593. },
  594. flowHistoryList: [],
  595. rejectSetupList: [], // 可以驳回的节点
  596. exchangeUsers: [],
  597. selectUserType: "exchange", // exchange:转审,approve:下一节点审核
  598. curSelectedUsers: [],
  599. // 选择下一节点审批人
  600. IS_NEED_SELECT_APPROVE_USER: false,
  601. nextFlowTaskResult: {}, //下一节点信息
  602. approveUsers: [],
  603. userLimitCount: 1,
  604. userFilterRoles: [],
  605. // image-preview
  606. curImage: {},
  607. curImageIndex: 0
  608. };
  609. },
  610. computed: {
  611. IS_APPLY() {
  612. return this.editType
  613. ? this.editType === "APPLY"
  614. : this.curTaskApply.setup === 1 || this.curTaskApply.setup === null;
  615. },
  616. IS_PREVIEW() {
  617. return this.editType
  618. ? this.editType === "PREVIEW"
  619. : this.curTaskApply.setup !== null && this.curTaskApply.setup <= 0;
  620. },
  621. IS_AUDIT() {
  622. const IS_COMMON_AUDIT = this.editType
  623. ? this.editType === "AUDIT"
  624. : this.curTaskApply.setup > 1;
  625. return IS_COMMON_AUDIT;
  626. },
  627. cardTodoName() {
  628. let name = "创建答题卡";
  629. if (this.curTaskApply.cardId) {
  630. if (this.curTaskApply.makeMethod === "SELECT") {
  631. name = "选择题卡";
  632. } else if (this.curTaskApply.makeMethod === "SELF") {
  633. name = "编辑题卡";
  634. } else {
  635. // 已经审核的题卡可以自行编辑,未审核的题卡只能查看
  636. name =
  637. this.curTaskApply.status === "SUBMIT" ? "编辑题卡" : "查看题卡";
  638. }
  639. }
  640. return name;
  641. },
  642. createCardTypeName() {
  643. return CARD_SOURCE_TYPE[this.curTaskApply.makeMethod] || "";
  644. },
  645. maxFetchCount() {
  646. return this.paperAttachments.length < 1
  647. ? 1
  648. : this.paperAttachments.length;
  649. },
  650. exposedMode() {
  651. return !!this.curTaskApply.exposedPaperType;
  652. }
  653. },
  654. mounted() {
  655. this.initData();
  656. },
  657. methods: {
  658. async initData() {
  659. const data = await taskApplyDetail(
  660. this.examTask.id,
  661. this.examTask.source
  662. );
  663. this.$emit("info-update", {
  664. semesterName: data.semesterName,
  665. examName: data.examName,
  666. examModel: data.examModel
  667. });
  668. this.curTaskApply = this.$objAssign(initTaskApply, data || {});
  669. this.curTaskApply.examId =
  670. this.curTaskApply.examId || this.examTask.examId;
  671. this.curTaskApply.examTaskId = this.examTask.id;
  672. this.curTaskApply.courseCode = this.examTask.courseCode;
  673. this.curTaskApply.courseName = this.examTask.courseName;
  674. this.curTaskApply.cardRuleId = this.examTask.cardRuleId;
  675. this.curTaskApply.customCard = this.examTask.customCard;
  676. this.curTaskApply.setup = this.examTask.setup;
  677. this.paperAttachments = this.curTaskApply.paperAttachmentIds
  678. ? JSON.parse(this.curTaskApply.paperAttachmentIds)
  679. : [];
  680. const exposedPaperType = data.exposedPaperType || "";
  681. let exposedPaperTypes = exposedPaperType.split(",");
  682. exposedPaperTypes.sort((a, b) => (a > b ? -1 : 1));
  683. const maxExposedPaperType = exposedPaperTypes[0];
  684. this.paperAttachments.forEach(paper => {
  685. paper.canDelete = maxExposedPaperType
  686. ? paper.name > maxExposedPaperType
  687. : true;
  688. paper.isExposed = exposedPaperTypes.includes(paper.name);
  689. });
  690. this.paperConfirmAttachments = this.curTaskApply.paperConfirmAttachmentIds
  691. ? JSON.parse(this.curTaskApply.paperConfirmAttachmentIds)
  692. : [];
  693. if (this.curTaskApply.flowStatus === "START") {
  694. await this.getFlowList();
  695. } else {
  696. await this.getFlowHistory();
  697. }
  698. if (this.IS_AUDIT) this.getCurFlowNodeInfo();
  699. },
  700. async getFlowHistory() {
  701. if (!this.curTaskApply.flowId) return;
  702. const data = await taskFlowDetail(
  703. this.curTaskApply.flowId
  704. ).catch(() => {});
  705. if (!data) return;
  706. const FLOW_STATUS = {
  707. SUBMIT: "success",
  708. APPROVE: "success",
  709. EXCHANGE: "success",
  710. REJECT: "danger",
  711. END: "success"
  712. };
  713. let nextFlowId = "";
  714. this.flowHistoryList = data.tfFlowViewLogResultList.map((item, index) => {
  715. const nextItem = data.tfFlowViewLogResultList[index + 1];
  716. nextFlowId = nextItem ? nextItem.flowId : item.flowId;
  717. item.nextIsNewFlow = nextFlowId !== item.flowId;
  718. item.type = FLOW_STATUS[item.approveOperation];
  719. item.attachments = item.paperAttachmentId
  720. ? JSON.parse(item.paperAttachmentId)
  721. : [];
  722. item.isApproveSetFlowNextNode = false;
  723. return item;
  724. });
  725. const flowIsEnd = data.currFlowTaskResult.taskKey === "end";
  726. this.flowHistoryList.push({
  727. type: flowIsEnd ? "success" : "current",
  728. stepKey: this.$randomCode(),
  729. approveSetup: data.currFlowTaskResult.setup, //审批人节点
  730. approveRemark: data.currFlowTaskResult.taskName, //审批信息
  731. approveOperation: flowIsEnd ? "END" : "",
  732. approveUserName: data.currFlowTaskResult.approveUserNames, //审批人
  733. createTime: null,
  734. nextIsNewFlow: false,
  735. isApproveSetFlowNextNode: false,
  736. attachments: []
  737. });
  738. },
  739. async getFlowList() {
  740. if (!this.curTaskApply.flowId) return;
  741. const data = await flowDetailByFlowId(this.curTaskApply.flowId);
  742. if (!data) return;
  743. const modelType =
  744. data.flowTaskResultList[0] && data.flowTaskResultList[0].modelType;
  745. this.flowInfo = {
  746. customFlowId: data.id,
  747. version: data.version
  748. };
  749. const nextFlowNodeIndex = 1;
  750. this.IS_NEED_SELECT_APPROVE_USER = modelType === "APPROVE_SET";
  751. const flowList = data.flowTaskResultList || [];
  752. this.flowList = flowList.map((flow, index) => {
  753. flow.isApproveSetFlowNextNode =
  754. this.IS_NEED_SELECT_APPROVE_USER && index === nextFlowNodeIndex;
  755. return flow;
  756. });
  757. if (this.flowList.length) {
  758. this.flowList[0].type = "success";
  759. }
  760. this.nextFlowTaskResult = this.flowList[nextFlowNodeIndex];
  761. },
  762. async getCurFlowNodeInfo() {
  763. const data = await taskFlowNodeInfo(this.curTaskApply.flowTaskId);
  764. this.rejectSetupList = (data && data.rejectSetupList) || [];
  765. this.rejectSetupList.forEach(item => {
  766. item.name = `【${item.taskName}】${item.approveUserNames}`;
  767. });
  768. this.nextFlowTaskResult = data.nextFlowTaskResult;
  769. // 被打回给命题老师之后,命题老师只能通过。
  770. if (data.setup === 1 && !this.rejectSetupList.length) {
  771. this.TASK_AUDIT_RESULT = { PASS: "通过" };
  772. this.auditModal.approvePass = "PASS";
  773. }
  774. // 判断发起人自选,是否需要选择下个审核人员
  775. const { modelType, approveUserNames, taskKey } = this.nextFlowTaskResult;
  776. this.IS_NEED_SELECT_APPROVE_USER =
  777. modelType === "APPROVE_SET" &&
  778. taskKey.toLowerCase() !== "end" &&
  779. !approveUserNames;
  780. this.flowHistoryList[
  781. this.flowHistoryList.length - 1
  782. ].isApproveSetFlowNextNode = this.IS_NEED_SELECT_APPROVE_USER;
  783. },
  784. approvePassChange() {
  785. this.auditRules.remark[0].required =
  786. this.auditModal.approvePass === "REJECT";
  787. },
  788. addAtachment() {
  789. if (this.paperAttachments.length >= this.attachmentLimitCount) return;
  790. const name = this.abc[this.paperAttachments.length];
  791. const newAttachment = {
  792. name,
  793. attachmentId: "",
  794. filename: "",
  795. pages: 0,
  796. canDelete: true,
  797. isExposed: false
  798. };
  799. this.paperAttachments.push(newAttachment);
  800. },
  801. deleteAttachment(index) {
  802. if (this.paperAttachments.length <= 1) {
  803. this.$message.error("试卷类型数量不得少于1");
  804. return;
  805. }
  806. this.paperAttachments.splice(index, 1);
  807. this.paperAttachments.forEach((item, itemIndex) => {
  808. item.name = this.abc[itemIndex];
  809. });
  810. if (
  811. this.curTaskApply.drawCount &&
  812. this.curTaskApply.drawCount > this.paperAttachments.length
  813. ) {
  814. this.curTaskApply.drawCount = this.paperAttachments.length;
  815. }
  816. },
  817. toUpload(attachment) {
  818. this.curUploadType = "paper";
  819. this.curAttachment = {
  820. ...attachment
  821. };
  822. this.$refs.UploadPaperDialog.open();
  823. },
  824. toUploadPaperConfirm() {
  825. if (this.paperConfirmAttachments.length >= 4) return;
  826. this.curUploadType = "paperConfirm";
  827. this.curAttachment = {
  828. ...this.paperConfirmAttachmentId
  829. };
  830. this.$refs.UploadPaperDialog.open();
  831. },
  832. uploadConfirm(attachment, uploadType) {
  833. if (uploadType === "paper") {
  834. const index = this.paperAttachments.findIndex(
  835. item => item.name === attachment.name
  836. );
  837. this.paperAttachments.splice(index, 1, { ...attachment });
  838. } else {
  839. this.paperConfirmAttachments.push(attachment);
  840. }
  841. },
  842. deletePaperConfirmAttachment(index) {
  843. this.paperConfirmAttachments.splice(index, 1);
  844. },
  845. toViewCard() {
  846. window.open(
  847. this.getRouterPath({
  848. name: "CardPreview",
  849. params: {
  850. cardId: this.curTaskApply.cardId,
  851. viewType: "view"
  852. }
  853. })
  854. );
  855. },
  856. toEditCard() {
  857. this.$ls.set("prepareTcPCard", {
  858. cardId: this.task.cardId,
  859. examTaskId: this.task.examTaskId,
  860. courseCode: this.task.courseCode,
  861. courseName: this.task.courseName,
  862. makeMethod: this.task.makeMethod,
  863. cardRuleId: this.task.cardRuleId,
  864. type: "CUSTOM",
  865. createMethod: "STANDARD"
  866. });
  867. this.$refs.ModifyCard.open();
  868. },
  869. async toCreateOrViewCard() {
  870. await this.silentSave();
  871. this.task = this.getTaskData();
  872. if (!this.curTaskApply.cardId) {
  873. this.$refs.CardOptionDialog.open();
  874. return;
  875. }
  876. if (this.curTaskApply.makeMethod === "SELECT") {
  877. this.$refs.CardOptionDialog.open();
  878. } else if (this.curTaskApply.makeMethod === "SELF") {
  879. this.toEditCard();
  880. } else {
  881. // 客服制卡:制作完毕则可以编辑,未制作完毕则可以查看
  882. if (this.curTaskApply.status === "SUBMIT") {
  883. this.toEditCard();
  884. } else {
  885. this.toViewCard();
  886. }
  887. }
  888. },
  889. cancel() {
  890. this.$emit("cancel");
  891. },
  892. async downloadPaper(attachment) {
  893. if (!attachment.attachmentId) return;
  894. const data = await attachmentPreview(attachment.attachmentId);
  895. window.open(data.url);
  896. },
  897. cardConfirm(data) {
  898. const { makeMethod } = data;
  899. if (makeMethod === "CUST") {
  900. this.silentSave();
  901. this.curTaskApply = this.$objAssign(this.curTaskApply, data);
  902. } else if (makeMethod === "SELECT") {
  903. this.curTaskApply = this.$objAssign(this.curTaskApply, data);
  904. } else {
  905. this.$ls.set("prepareTcPCard", data);
  906. this.$refs.ModifyCard.open();
  907. }
  908. },
  909. async cardModified(cardId) {
  910. if (!cardId) return;
  911. this.curTaskApply = this.$objAssign(this.curTaskApply, {
  912. cardId,
  913. makeMethod: "SELF"
  914. });
  915. this.silentSave();
  916. },
  917. async changeCreateCardType() {
  918. const h = this.$createElement;
  919. const result = await this.$msgbox({
  920. title: "切换题卡操作说明",
  921. message: h("div", null, [
  922. h("p", null, "1、切换题卡会将之前选择题卡数据删除。"),
  923. h(
  924. "p",
  925. null,
  926. "2、之前选择专卡进行绘制,切换题卡后再次选择专卡,需要重新开始绘制。"
  927. )
  928. ]),
  929. showCancelButton: true,
  930. type: "warning"
  931. }).catch(() => {});
  932. if (result !== "confirm") return;
  933. await this.clearMakeMethod();
  934. this.toCreateOrViewCard();
  935. },
  936. async clearMakeMethod() {
  937. // 清除后台记录的题卡
  938. if (this.curTaskApply.cardId && this.curTaskApply.makeMethod !== "CUST") {
  939. await switchCardCreateMethod(this.examTask.id);
  940. }
  941. this.curTaskApply.makeMethod = "";
  942. this.curTaskApply.cardId = "";
  943. },
  944. toSelectNextFlowUser() {
  945. if (!this.IS_NEED_SELECT_APPROVE_USER) return;
  946. this.userLimitCount =
  947. this.nextFlowTaskResult.approveUserCountType === "ONE" ? 1 : 0;
  948. this.userFilterRoles =
  949. this.nextFlowTaskResult.approveUserSelectRange === "ROLE"
  950. ? this.nextFlowTaskResult.approveUserSelectRoles.map(item => item.id)
  951. : [];
  952. this.selectUserType = "approve";
  953. this.curSelectedUsers = this.approveUsers;
  954. this.$refs.SelectUserDialog.open();
  955. },
  956. toSelectExchangeUser() {
  957. this.userLimitCount = 1;
  958. this.userFilterRoles = [];
  959. this.curSelectedUsers = this.exchangeUsers;
  960. this.selectUserType = "exchange";
  961. this.$refs.SelectUserDialog.open();
  962. },
  963. userSelected(users) {
  964. if (this.selectUserType === "exchange") {
  965. this.exchangeUsers = users;
  966. this.auditModal.userId = users[0].id;
  967. } else {
  968. this.approveUsers = users;
  969. }
  970. },
  971. getTaskData() {
  972. let data = { ...this.curTaskApply };
  973. data.paperType = this.paperAttachments.map(item => item.name).join(",");
  974. data.paperAttachmentIds = JSON.stringify(this.paperAttachments, (k, v) =>
  975. k === "url" ? undefined : v
  976. );
  977. data.paperConfirmAttachmentIds = JSON.stringify(
  978. this.paperConfirmAttachments
  979. );
  980. if (this.IS_NEED_SELECT_APPROVE_USER)
  981. data.approveUserIds = this.approveUsers.map(item => item.id);
  982. return data;
  983. },
  984. checkDataValid() {
  985. const attachmentValid = !this.paperAttachments.some(
  986. item => !item.attachmentId
  987. );
  988. // 设置了入库强制包含试卷时,校验试卷是否上传。
  989. if (this.curTaskApply.includePaper && !attachmentValid) {
  990. this.$message.error("请完成试卷文件上传!");
  991. return;
  992. }
  993. // if (!this.paperConfirmAttachments.length) {
  994. // this.$message.error("请上传附件!");
  995. // return;
  996. // }
  997. if (!this.curTaskApply.cardId) {
  998. this.$message.error("请选择题卡创建方式!");
  999. return;
  1000. }
  1001. if (
  1002. this.curTaskApply.makeMethod !== "SELECT" &&
  1003. this.curTaskApply.status !== "SUBMIT"
  1004. ) {
  1005. this.$message.error("请先提交题卡!");
  1006. return;
  1007. }
  1008. if (this.IS_NEED_SELECT_APPROVE_USER && !this.approveUsers.length) {
  1009. this.$message.error("请设置审核人员!");
  1010. return;
  1011. }
  1012. return true;
  1013. },
  1014. async toSave() {
  1015. if (this.isSubmit) return;
  1016. this.isSubmit = true;
  1017. const datas = this.getTaskData();
  1018. datas.operateType = "STAGE";
  1019. const data = await updateTaskApply(datas).catch(() => {});
  1020. this.isSubmit = false;
  1021. if (!data) return;
  1022. this.$message.success("保存成功!");
  1023. },
  1024. async silentSave() {
  1025. const datas = this.getTaskData();
  1026. datas.operateType = "STAGE";
  1027. await updateTaskApply(datas).catch(() => {});
  1028. },
  1029. async submit() {
  1030. if (!this.checkDataValid()) return;
  1031. const result = await this.$confirm(
  1032. "任务确定提交后,则不可更改试卷及答题卡内容,确定提交该任务?",
  1033. "提示",
  1034. {
  1035. type: "warning"
  1036. }
  1037. ).catch(() => {});
  1038. if (result !== "confirm") return;
  1039. const datas = this.getTaskData();
  1040. datas.operateType = "SUBMIT";
  1041. const data = await updateTaskApply(datas).catch(() => {});
  1042. if (!data) return;
  1043. this.$message.success("提交成功!");
  1044. this.$emit("modified");
  1045. },
  1046. async toAuditSubmit() {
  1047. const valid = await this.$refs.auditModalComp.validate().catch(() => {});
  1048. if (!valid) return;
  1049. if (
  1050. this.auditModal.approvePass === "PASS" &&
  1051. this.IS_NEED_SELECT_APPROVE_USER &&
  1052. !this.approveUsers.length
  1053. ) {
  1054. this.$message.error("请设置审核人员!");
  1055. return;
  1056. }
  1057. const actionName = this.TASK_AUDIT_RESULT[this.auditModal.approvePass];
  1058. const result = await this.$confirm(
  1059. `确定${actionName}该申请吗?`,
  1060. "提示",
  1061. {
  1062. type: "warning"
  1063. }
  1064. ).catch(() => {});
  1065. if (result !== "confirm") return;
  1066. if (this.auditModal.approvePass === "EXCHANGE") {
  1067. let datas = {
  1068. taskId: this.curTaskApply.flowTaskId,
  1069. userId: this.auditModal.userId
  1070. };
  1071. const data = await taskFlowApproverExchange(datas).catch(() => {});
  1072. if (!data) return;
  1073. } else {
  1074. let datas = { ...this.auditModal };
  1075. datas.taskId = this.curTaskApply.flowTaskId;
  1076. if (
  1077. this.auditModal.approvePass === "PASS" &&
  1078. this.IS_NEED_SELECT_APPROVE_USER
  1079. )
  1080. datas.approveUserIds = this.approveUsers.map(item => item.id);
  1081. const data = await taskFlowApprover(datas).catch(() => {});
  1082. if (!data) return;
  1083. }
  1084. this.$message.success("审批成功!");
  1085. this.$emit("modified");
  1086. },
  1087. async toAuditApply() {
  1088. const result = await this.$confirm("确定提交该任务吗?", "提示", {
  1089. type: "warning"
  1090. }).catch(() => {});
  1091. if (result !== "confirm") return;
  1092. const datas = {
  1093. examTaskDetail: this.getTaskData(),
  1094. flowTaskId: this.curTaskApply.flowTaskId,
  1095. approvePass: "PASS"
  1096. };
  1097. const data = await taskAuditApply(datas).catch(() => {});
  1098. if (!data) return;
  1099. this.$message.success("审批成功!");
  1100. this.$emit("modified");
  1101. },
  1102. // image-preview
  1103. toPreview(index) {
  1104. this.curImageIndex = index;
  1105. this.selectImage(index);
  1106. this.$refs.SimpleImagePreview.open();
  1107. },
  1108. selectImage(index) {
  1109. this.curImage = this.paperConfirmAttachments[index];
  1110. },
  1111. toPrevImage() {
  1112. if (this.curImageIndex === 0) {
  1113. this.curImageIndex = this.paperConfirmAttachments.length - 1;
  1114. } else {
  1115. this.curImageIndex--;
  1116. }
  1117. this.selectImage(this.curImageIndex);
  1118. },
  1119. toNextImage() {
  1120. if (this.curImageIndex === this.paperConfirmAttachments.length - 1) {
  1121. this.curImageIndex = 0;
  1122. } else {
  1123. this.curImageIndex++;
  1124. }
  1125. this.selectImage(this.curImageIndex);
  1126. }
  1127. }
  1128. };
  1129. </script>