certificateQualificationsDialog.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. <template>
  2. <ele-modal
  3. custom-class="ele-dialog-form long-dialog-form"
  4. :centered="true"
  5. :visible.sync="certificateQualificationsDialogFlag"
  6. :title="title"
  7. append-to-body
  8. :close-on-click-modal="false"
  9. width="80%"
  10. :before-close="cancel"
  11. >
  12. <el-form ref="form" :model="form">
  13. <headerTitle title="基本信息" />
  14. <el-row :gutter="20">
  15. <el-col :span="12">
  16. <el-form-item
  17. label-width="50px"
  18. label="名称"
  19. prop="name"
  20. :rules="{ required: true, message: '请输入', trigger: 'blur' }"
  21. >
  22. <el-input
  23. v-model="form.name"
  24. :disabled="type == 'view'"
  25. clearable
  26. ></el-input>
  27. </el-form-item>
  28. </el-col>
  29. <el-col :span="12">
  30. <el-form-item label="有效时间:" prop="date" label-width="90px">
  31. <el-date-picker
  32. :disabled="type == 'view'"
  33. v-model="form.date"
  34. style="width: 100%"
  35. type="daterange"
  36. value-format="yyyy-MM-dd"
  37. range-separator="至"
  38. start-placeholder="开始日期"
  39. end-placeholder="结束日期"
  40. >
  41. </el-date-picker>
  42. </el-form-item>
  43. </el-col>
  44. <el-col :span="12">
  45. <el-form-item
  46. label="资质类型:"
  47. prop="certificationType"
  48. label-width="90px"
  49. >
  50. <el-select
  51. style="width: 100%"
  52. :disabled="type == 'view'"
  53. v-model="form.certificationType"
  54. @change="changeCertificationType"
  55. filterable
  56. >
  57. <el-option
  58. v-for="item in qualificationOptions"
  59. :key="item.value"
  60. :label="item.label"
  61. :value="item.value"
  62. >
  63. </el-option>
  64. </el-select>
  65. </el-form-item>
  66. </el-col>
  67. <el-col :span="12">
  68. <el-form-item
  69. label="关联类型:"
  70. prop="relationName"
  71. label-width="90px"
  72. >
  73. <el-input
  74. :disabled="type == 'view'"
  75. v-model="form.relationName"
  76. readonly
  77. clearable
  78. @click.native="handleClick"
  79. placeholder="请选择"
  80. />
  81. </el-form-item>
  82. </el-col>
  83. <el-col :span="12">
  84. <el-form-item
  85. label="关联类型:"
  86. prop="relationName"
  87. label-width="90px"
  88. >
  89. <el-input
  90. :disabled="type == 'view'"
  91. v-model="form.relationName"
  92. readonly
  93. clearable
  94. @click.native="handleClick"
  95. placeholder="请选择"
  96. />
  97. </el-form-item>
  98. </el-col>
  99. <el-col :span="12">
  100. <el-form-item label="附件:" prop="accessory" label-width="90px">
  101. <fileMain v-model="form.accessory" :type="type"></fileMain>
  102. </el-form-item>
  103. </el-col>
  104. <el-col :span="12">
  105. <el-form-item label-width="50px" label="备注">
  106. <el-input
  107. type="textarea"
  108. v-model="form.remark"
  109. :disabled="type == 'view'"
  110. clearable
  111. ></el-input>
  112. </el-form-item>
  113. </el-col>
  114. </el-row>
  115. <headerTitle title="资质信息" />
  116. <ele-pro-table
  117. ref="linkTable"
  118. :columns="columns"
  119. :datasource="form.detailsList"
  120. :toolkit="[]"
  121. height="300px"
  122. :need-page="false"
  123. >
  124. <!-- 表头工具栏 -->
  125. <template v-slot:toolbar>
  126. <el-button v-if="type !== 'view'" type="primary" @click="handleAdd"
  127. >添加</el-button
  128. >
  129. </template>
  130. <template v-slot:name="scope">
  131. <el-form-item
  132. :prop="'detailsList.' + scope.$index + '.name'"
  133. :rules="{
  134. required: true,
  135. message: '',
  136. trigger: 'change'
  137. }"
  138. >
  139. <el-select
  140. v-if="type !== 'view'"
  141. v-model="scope.row.name"
  142. clearable
  143. @change="(val) => handleChangeType(val, scope.row)"
  144. >
  145. <el-option
  146. :disabled="disabledToType(scope.row).includes(item.dictCode)"
  147. v-for="item in dictList"
  148. :value="item.dictValue"
  149. :label="item.dictValue"
  150. ></el-option>
  151. </el-select>
  152. <span v-else>{{ scope.row.name }}</span>
  153. </el-form-item>
  154. </template>
  155. <template v-slot:code="scope">
  156. <el-form-item :prop="'detailsList.' + scope.$index + '.code'">
  157. <el-input
  158. v-model="scope.row.code"
  159. :disabled="type == 'view'"
  160. clearable
  161. ></el-input>
  162. </el-form-item>
  163. </template>
  164. <template v-slot:businessRange="scope">
  165. <el-form-item
  166. :prop="'detailsList.' + scope.$index + '.businessRange'"
  167. >
  168. <el-input
  169. type="textarea"
  170. v-model="scope.row.businessRange"
  171. :disabled="type == 'view'"
  172. clearable
  173. ></el-input>
  174. </el-form-item>
  175. </template>
  176. <template v-slot:startTime="scope">
  177. <el-form-item
  178. inline-message
  179. :prop="'detailsList.' + scope.$index + '.startTime'"
  180. :rules="{
  181. required: false,
  182. message: '',
  183. trigger: 'change'
  184. }"
  185. >
  186. <el-date-picker
  187. :disabled="type == 'view'"
  188. v-model="scope.row.startTime"
  189. type="date"
  190. style="width: 100%"
  191. value-format="yyyy-MM-dd"
  192. placeholder="选择日期"
  193. >
  194. </el-date-picker>
  195. </el-form-item>
  196. </template>
  197. <template v-slot:endTime="scope">
  198. <el-form-item
  199. :prop="'detailsList.' + scope.$index + '.endTime'"
  200. :rules="{
  201. required: false,
  202. validator: validateEndDate(scope.row, scope.$index),
  203. trigger: ['blur', 'change']
  204. }"
  205. >
  206. <el-date-picker
  207. :disabled="type == 'view'"
  208. v-model="scope.row.endTime"
  209. type="date"
  210. style="width: 100%"
  211. value-format="yyyy-MM-dd"
  212. placeholder="选择日期"
  213. >
  214. </el-date-picker>
  215. </el-form-item>
  216. </template>
  217. <template v-slot:noticePersonName="scope">
  218. <el-form-item
  219. :prop="'detailsList.' + scope.$index + '.noticePersonName'"
  220. >
  221. <el-input
  222. :disabled="type == 'view'"
  223. @click.native="openStaffSelection(scope.$index)"
  224. v-model="scope.row.noticePersonName"
  225. placeholder="请选择"
  226. ></el-input>
  227. </el-form-item>
  228. </template>
  229. <template v-slot:accessory="scope">
  230. <el-form-item
  231. :prop="'detailsList.' + scope.$index + '.accessory'"
  232. :rules="{
  233. required: true,
  234. message: '',
  235. trigger: ['change', 'blur']
  236. }"
  237. >
  238. <fileMain v-model="scope.row.accessory" :type="type"></fileMain>
  239. </el-form-item>
  240. </template>
  241. <template v-slot:type="scope">
  242. <el-form-item :prop="'detailsList.' + scope.$index + '.type'">
  243. <el-select
  244. v-if="type !== 'view'"
  245. v-model="scope.row.type"
  246. clearable
  247. >
  248. <el-option
  249. v-for="item in typeList"
  250. :value="item.dictCode"
  251. :label="item.dictValue"
  252. ></el-option>
  253. </el-select>
  254. <span v-else>{{ getLabelName(typeList, scope.row.type) }}</span>
  255. </el-form-item>
  256. </template>
  257. <template v-slot:level="scope">
  258. <el-form-item :prop="'detailsList.' + scope.$index + '.level'">
  259. <el-select
  260. v-if="type !== 'view'"
  261. v-model="scope.row.level"
  262. clearable
  263. >
  264. <el-option
  265. v-for="item in levelOptions"
  266. :value="item.dictCode"
  267. :label="item.dictValue"
  268. ></el-option>
  269. </el-select>
  270. <span v-else>{{ getLabelName(levelOptions, scope.row.type) }}</span>
  271. </el-form-item>
  272. </template>
  273. <template v-slot:remark="scope">
  274. <el-form-item :prop="'detailsList.' + scope.$index + '.remark'">
  275. <el-input
  276. type="textarea"
  277. :disabled="type == 'view'"
  278. v-model="scope.row.remark"
  279. ></el-input>
  280. </el-form-item>
  281. </template>
  282. <template v-slot:status="scope">
  283. <el-form-item :prop="'detailsList.' + scope.$index + '.status'">
  284. <el-tag
  285. v-if="scope.row.status"
  286. :type="statusTagTypeList[scope.row.status]"
  287. >
  288. {{ statusList[scope.row.status] }}
  289. </el-tag>
  290. </el-form-item>
  291. </template>
  292. <template v-slot:isRequired="{ column }">
  293. <span class="is-required">{{ column.label }}</span>
  294. </template>
  295. <template v-slot:action="{ row, $index }">
  296. <el-popconfirm
  297. class="ele-action"
  298. title="确定要删除该信息吗?"
  299. @confirm="handleRemove($index)"
  300. >
  301. <template v-slot:reference>
  302. <el-link
  303. v-if="type !== 'view'"
  304. type="danger"
  305. :underline="false"
  306. icon="el-icon-delete"
  307. >
  308. 删除
  309. </el-link>
  310. </template>
  311. </el-popconfirm>
  312. </template>
  313. </ele-pro-table>
  314. </el-form>
  315. <div slot="footer">
  316. <el-button
  317. type="primary"
  318. v-if="type !== 'view'"
  319. @click="handleSave(false)"
  320. >保存</el-button
  321. >
  322. <!-- <el-button type="primary" v-if="type!=='view'" plan @click="handleSave(true)">提交</el-button>-->
  323. <el-button @click="cancel">返回</el-button>
  324. </div>
  325. <staffSelection
  326. ref="staffSelection"
  327. @confirm="confirmStaffSelection"
  328. ></staffSelection>
  329. </ele-modal>
  330. </template>
  331. <script>
  332. import FileUpload from '@/components/upload/fileUpload.vue';
  333. import staffSelection from '@/components/staffSelection/staffSelection.vue';
  334. import {
  335. contactQcPackDetailAPI,
  336. contactQcPackSaveAPI,
  337. contactQcPackUpdateAPI,
  338. contactQcSubmit
  339. } from '@/api/bpm/components/supplierManage/contact';
  340. import { getFile } from '@/api/system/file';
  341. import { mapActions, mapGetters } from 'vuex';
  342. import dictEnum from '@/enum/dict';
  343. import fileMain from '@/components/addDoc/index.vue';
  344. export default {
  345. name: 'certificateQualificationsDialog',
  346. components: { fileMain, FileUpload, staffSelection },
  347. props: {
  348. certificateQualificationsDialogFlag: Boolean,
  349. contactId: String,
  350. typeInfo: String
  351. },
  352. computed: {
  353. ...mapGetters(['dict']),
  354. dictList() {
  355. return this.dict[dictEnum['客户/供应商资质类型']] || [];
  356. },
  357. typeList() {
  358. return this.dict[dictEnum['工种类型']] || [];
  359. },
  360. columns() {
  361. return [
  362. {
  363. type: 'index',
  364. width: 55,
  365. align: 'center'
  366. },
  367. {
  368. label: '名称',
  369. prop: 'name',
  370. slot: 'name',
  371. headerSlot: 'isRequired',
  372. minWidth: 180,
  373. align: 'center'
  374. },
  375. {
  376. label: '编号',
  377. prop: 'code',
  378. slot: 'code',
  379. // headerSlot: 'isRequired',
  380. minWidth: 120,
  381. align: 'center'
  382. },
  383. {
  384. label: '许可/经营范围',
  385. prop: 'businessRange',
  386. slot: 'businessRange',
  387. minWidth: 140,
  388. align: 'center'
  389. },
  390. {
  391. label: '有效期起始日期',
  392. prop: 'startTime',
  393. slot: 'startTime',
  394. // headerSlot: 'isRequired',
  395. minWidth: 160,
  396. align: 'center'
  397. },
  398. {
  399. label: '有效期截止日期',
  400. prop: 'endTime',
  401. slot: 'endTime',
  402. // headerSlot: 'isRequired',
  403. minWidth: 160,
  404. align: 'center'
  405. },
  406. {
  407. label: '通知人',
  408. prop: 'noticePersonName',
  409. slot: 'noticePersonName',
  410. // headerSlot: 'isRequired',
  411. minWidth: 140,
  412. align: 'center'
  413. },
  414. {
  415. label: '附件',
  416. prop: 'accessory',
  417. slot: 'accessory',
  418. headerSlot: 'isRequired',
  419. minWidth: 140
  420. },
  421. {
  422. label: '等级',
  423. prop: 'level',
  424. slot: 'level',
  425. minWidth: 140,
  426. align: 'center'
  427. },
  428. {
  429. label: '分类',
  430. prop: 'type',
  431. slot: 'type',
  432. minWidth: 140,
  433. align: 'center'
  434. },
  435. {
  436. label: '备注',
  437. prop: 'remark',
  438. slot: 'remark',
  439. minWidth: 140,
  440. align: 'center'
  441. },
  442. {
  443. label: '状态',
  444. prop: 'status',
  445. slot: 'status',
  446. align: 'center'
  447. },
  448. {
  449. action: 'action',
  450. slot: 'action',
  451. label: '操作',
  452. align: 'center',
  453. fixed: 'right'
  454. }
  455. ];
  456. },
  457. disabledToType() {
  458. return (row) => {
  459. let list = this.form.detailsList.map((item) => item.type);
  460. let dictCodeList = this.dictList.map((item) => item.dictCode);
  461. let intersectionList = list.filter(
  462. (v) => dictCodeList.indexOf(v) > -1
  463. );
  464. intersectionList = intersectionList.filter((v) => row.type !== v);
  465. return intersectionList;
  466. };
  467. }
  468. },
  469. created() {
  470. this.requestDict('客户/供应商资质类型');
  471. },
  472. data() {
  473. return {
  474. title: '',
  475. type: '',
  476. defaultData: {
  477. files: [],
  478. name: '',
  479. notifyUserId: '',
  480. notifyUserName: '',
  481. num: '',
  482. remark: '',
  483. scope: '',
  484. type: '',
  485. validityPeriodEnd: '',
  486. validityPeriodStart: ''
  487. },
  488. qualificationOptions: [
  489. {
  490. label: '客户资质',
  491. value: '1'
  492. },
  493. {
  494. label: '供应商资质',
  495. value: '2'
  496. },
  497. {
  498. label: '个人资质',
  499. value: '3'
  500. }
  501. ],
  502. form: {
  503. detailsList: [],
  504. contactId: '',
  505. name: '',
  506. remark: ''
  507. },
  508. curIndex: null,
  509. statusList: {
  510. 10: '有效',
  511. 20: '无效',
  512. 30: '已过期'
  513. },
  514. statusTagTypeList: {
  515. 10: 'success',
  516. 20: 'info',
  517. 30: 'danger'
  518. },
  519. levelOptions: [
  520. {
  521. dictValue: '初级',
  522. dictCode: '1'
  523. },
  524. {
  525. dictValue: '中级',
  526. dictCode: '2'
  527. },
  528. {
  529. dictValue: '高级',
  530. dictCode: '3'
  531. }
  532. ]
  533. };
  534. },
  535. methods: {
  536. ...mapActions('dict', ['requestDict']),
  537. getLabelName(arr, id) {
  538. if (!id) return '';
  539. return arr.find((item) => item.dictCode == id)?.dictValue;
  540. },
  541. //结束日期验证
  542. validateEndDate(row, index) {
  543. return (rule, value, callback) => {
  544. if (!value) return callback(new Error(''));
  545. if (
  546. row.validityPeriodEnd &&
  547. row.validityPeriodStart &&
  548. value < row.validityPeriodStart
  549. ) {
  550. callback(new Error('截止日期不能小于起始日期'));
  551. } else {
  552. callback();
  553. }
  554. };
  555. },
  556. //页面初始化
  557. init(type, row = {}) {
  558. this.title =
  559. type == 'add' ? '新增' : type == 'update' ? '修改' : '详情';
  560. this.type = type;
  561. if (type !== 'add') {
  562. this.getCertificateInfo(row);
  563. }
  564. },
  565. async getCertificateInfo(row) {
  566. this.form = await contactQcPackDetailAPI(row.id);
  567. },
  568. //打开选择负责人弹窗
  569. openStaffSelection(index) {
  570. this.curIndex = index;
  571. this.$refs.staffSelection.open([]);
  572. },
  573. //选择负责人回调
  574. confirmStaffSelection(data) {
  575. this.form.detailsList[this.curIndex].notifyUserName = data
  576. .map((item) => item.name)
  577. .toString();
  578. this.form.detailsList[this.curIndex].notifyUserId = data
  579. .map((item) => item.id)
  580. .toString();
  581. },
  582. //删除资质
  583. handleRemove(index) {
  584. this.form.detailsList.splice(index, 1);
  585. },
  586. //新增
  587. handleAdd() {
  588. this.form.detailsList.push({ ...this.defaultData });
  589. },
  590. //修改资质证书
  591. handleChangeType(val, row) {
  592. if (!val) return (row.name = '');
  593. row.name =
  594. this.dictList.find((i) => i.dictCode == val)?.dictValue || '';
  595. },
  596. downloadFile(file) {
  597. getFile({ objectName: file.storePath }, file.name);
  598. },
  599. //保存/提交
  600. handleSave(isSub) {
  601. if (!this.contactId)
  602. return this.$message.success('请保存合同信息后新增资质');
  603. this.$refs.form.validate(async (valid) => {
  604. if (!valid) return this.$message.warning('有必填项未填写,请检查');
  605. if (!this.form.detailsList.length)
  606. return this.$message.warning('至少保存一条资质信息');
  607. this.form.contactId = this.contactId;
  608. let api =
  609. this.type == 'add' ? contactQcPackSaveAPI : contactQcPackUpdateAPI;
  610. let id = await api(this.form);
  611. if (isSub) {
  612. await contactQcSubmit({ businessId: id, type: this.typeInfo });
  613. }
  614. this.$message.success('保存成功');
  615. this.$emit('reload');
  616. this.cancel();
  617. });
  618. },
  619. //关闭弹窗
  620. cancel() {
  621. this.$emit('update:certificateQualificationsDialogFlag', false);
  622. }
  623. }
  624. };
  625. </script>
  626. <style scoped lang="scss">
  627. :deep.el-form-item {
  628. margin-bottom: 0;
  629. }
  630. </style>