auditDocumentsMain.vue 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343
  1. <template>
  2. <div class="catalog-manage">
  3. <div class="documents-layout">
  4. <!-- 左侧文书类型列表 -->
  5. <div class="documents-type-list">
  6. <h3>监审文书类型:</h3>
  7. <div
  8. v-for="type in documentData.documentTypes"
  9. :key="type.id"
  10. class="type-item"
  11. :class="{ active: activeDocumentTypeId === type.id }"
  12. @click="handleDocumentTypeClick(type)"
  13. >
  14. {{ type.documentName }}
  15. </div>
  16. </div>
  17. <!-- 右侧文书列表表格 -->
  18. <div class="documents-content">
  19. <div class="operation-bar">
  20. <el-button
  21. v-if="!isView"
  22. plain
  23. type="success"
  24. icon="el-icon-circle-plus"
  25. @click="handleGenerateDocument"
  26. >
  27. 生成文书
  28. </el-button>
  29. </div>
  30. <CostAuditTable
  31. :table-data="documentData.list"
  32. :columns="documentData.documentColumns"
  33. :show-index="true"
  34. :show-pagination="true"
  35. :show-action-column="true"
  36. :pagination="documentData.pagination"
  37. @pagination-change="handlePaginationChange"
  38. >
  39. <template #documentId="{ row }">
  40. {{ getDocumenType(row) }}
  41. </template>
  42. <template #enterpriseId="{ row }">
  43. {{ getEnterpriseName(row) }}
  44. </template>
  45. <template #generateTime="{ row }">
  46. <div>
  47. {{ row.generateTime ? row.generateTime.split(' ')[0] : '' }}
  48. </div>
  49. <div>
  50. {{ row.generateTime ? row.generateTime.split(' ')[1] : '' }}
  51. </div>
  52. </template>
  53. <template #scanDocumentUrl="scope">
  54. <el-button
  55. v-if="!isView && getDocumenType(scope.row).includes('送达回证')"
  56. type="text"
  57. size="mini"
  58. @click="handleUploadScan(scope.row, 'scanDocumentUrl')"
  59. >
  60. 上传附件
  61. </el-button>
  62. <el-button
  63. type="text"
  64. size="mini"
  65. @click="handleViewScan(scope.row.scanDocumentUrl)"
  66. >
  67. 查看附件
  68. </el-button>
  69. </template>
  70. <template #feedbackDocumentUrl="scope">
  71. <!-- <el-button
  72. type="text"
  73. size="mini"
  74. :disabled="isView"
  75. @click="handleUploadScan(scope.row, 'feedbackDocumentUrl')"
  76. >
  77. 上传附件
  78. </el-button> -->
  79. <div v-if="getDocumenType(scope.row).includes('送达回证')">
  80. <!-- <span>
  81. {{ scope.row.feedbackDocumentUrl ? '已回传' : '未回传' }}
  82. </span> -->
  83. <el-button
  84. v-if="scope.row.feedbackDocumentUrl"
  85. type="text"
  86. size="mini"
  87. @click="handleViewScan(scope.row.feedbackDocumentUrl)"
  88. >
  89. 查看附件
  90. </el-button>
  91. </div>
  92. </template>
  93. <template #electronicDocumentUrl="scope">
  94. <el-button
  95. v-if="!isView"
  96. type="text"
  97. size="mini"
  98. @click="handleEditDocument(scope.row)"
  99. >
  100. 修改
  101. </el-button>
  102. <!-- <el-button
  103. v-if="!isView"
  104. type="text"
  105. size="mini"
  106. @click="handleSignDocument(scope.row)"
  107. >
  108. 签章
  109. </el-button> -->
  110. <el-button
  111. v-if="!isView"
  112. type="text"
  113. size="mini"
  114. @click="handleDeleteDocument(scope.row)"
  115. >
  116. 删除
  117. </el-button>
  118. <el-button
  119. type="text"
  120. size="mini"
  121. @click="handleDownloadDocument(scope.row)"
  122. >
  123. 下载
  124. </el-button>
  125. </template>
  126. </CostAuditTable>
  127. </div>
  128. </div>
  129. <!-- <div style="margin-top: 20px; font-size: 14px" class="table-description">
  130. 说明:此处只能生成各被监审单位的《成本监审通知书》和《送达回证》,同时接收或上传被监审单位的反馈的《送达回证》。
  131. </div> -->
  132. <!-- 编辑监审通知书 -->
  133. <CostAuditDialog
  134. :title="documentDialogTitle"
  135. :visible="documentDialogVisible"
  136. width="82%"
  137. :close-on-click-modal="false"
  138. :z-index="9200"
  139. @cancel="handleCancel"
  140. @confirm="handleConfirm"
  141. >
  142. <div class="document-edit-container">
  143. <!-- 左侧:文书参数设置 -->
  144. <div class="document-params">
  145. <h4>文书参数设置:</h4>
  146. <el-form
  147. ref="documentForm"
  148. v-loading="loading.saveDocument"
  149. :model="document"
  150. label-width="170px"
  151. size="small"
  152. :rules="documentRules"
  153. >
  154. <el-form-item label="选择模板:" prop="documentId">
  155. <el-select
  156. v-model="document.documentId"
  157. placeholder="请选择模板"
  158. style="width: 100%"
  159. @change="handleTemplateChange"
  160. >
  161. <el-option
  162. v-for="item in documentData.documentTypes"
  163. :key="item.id"
  164. :label="item.documentName"
  165. :value="item.id"
  166. ></el-option>
  167. </el-select>
  168. </el-form-item>
  169. <el-form-item label="通知书文号:" prop="documentNumber">
  170. <el-input
  171. v-model="document.documentNumber"
  172. placeholder="请选择通知书文号"
  173. style="width: 74%"
  174. disabled
  175. ></el-input>
  176. <!-- disabled -->
  177. <el-button
  178. type="primary"
  179. size="small"
  180. class="ml10"
  181. @click="selectClick"
  182. >
  183. 选择文号
  184. </el-button>
  185. </el-form-item>
  186. <el-form-item label="被监审单位" prop="enterpriseId">
  187. <div style="display: flex; align-items: center; gap: 15px">
  188. <el-select
  189. v-model="document.enterpriseId"
  190. placeholder="请选择被监审单位"
  191. style="width: 100%"
  192. clearable
  193. :multiple="isMultipleMode"
  194. @change="handleEnterpriseChange"
  195. >
  196. <el-option
  197. v-for="item in allUnits"
  198. :key="item.unitId"
  199. :label="item.unitName"
  200. :value="item.unitId"
  201. ></el-option>
  202. </el-select>
  203. </div>
  204. </el-form-item>
  205. <el-form-item label="是否推送被监审单位:" prop="isPushed">
  206. <!-- 是否推送被监审单位 -->
  207. <el-radio-group v-model="document.isPushed">
  208. <el-radio label="1">是</el-radio>
  209. <el-radio label="0">否</el-radio>
  210. </el-radio-group>
  211. </el-form-item>
  212. <!-- <el-form-item label="被监审单位:">
  213. <el-select
  214. v-model="document.enterpriseId"
  215. placeholder="请选择被监审单位"
  216. style="width: 100%"
  217. clearable
  218. >
  219. <el-option
  220. v-for="item in allUnits"
  221. :key="item.unitId"
  222. :label="item.unitName"
  223. :value="item.unitId"
  224. ></el-option>
  225. </el-select>
  226. </el-form-item> -->
  227. <!-- 数据内容区域 -->
  228. <div style="margin-top: 20px">
  229. <h4 style="margin-bottom: 10px">数据内容:</h4>
  230. <el-table
  231. :data="costDocumentTemplateFiles"
  232. style="
  233. width: 100%;
  234. border: 1px solid #dcdfe6;
  235. border-radius: 4px;
  236. "
  237. >
  238. <el-table-column
  239. prop="originalText"
  240. label="数据项"
  241. width="120"
  242. align="center"
  243. show-overflow-tooltip
  244. ></el-table-column>
  245. <!-- <el-table-column
  246. prop="labelValue"
  247. label="标签"
  248. width="100"
  249. align="center"
  250. show-overflow-tooltip
  251. ></el-table-column> -->
  252. <el-table-column
  253. prop="originalText"
  254. label="描述"
  255. min-width="120"
  256. align="center"
  257. show-overflow-tooltip
  258. ></el-table-column>
  259. <el-table-column
  260. prop="dataValue"
  261. label="数据值"
  262. min-width="150"
  263. align="center"
  264. show-overflow-tooltip
  265. >
  266. <template slot-scope="scope">
  267. <el-input
  268. v-if="scope.row.originalText !== '需要提供材料'"
  269. v-model="scope.row.dataValue"
  270. size="small"
  271. placeholder="请输入数据值"
  272. ></el-input>
  273. <!-- 否则显示上传按钮 -->
  274. <div v-else>
  275. <el-button
  276. type="text"
  277. size="small"
  278. @click="handleUploadClick(scope.row)"
  279. >
  280. 上传附件
  281. </el-button>
  282. <el-button
  283. type="text"
  284. size="small"
  285. @click="handleViewScan(scope.row.dataValue)"
  286. >
  287. 查看附件
  288. </el-button>
  289. </div>
  290. </template>
  291. </el-table-column>
  292. </el-table>
  293. <div style="margin-top: 10px; font-size: 12px; color: #909399">
  294. 说明:数据内容可以在此处直接修改,修改后的数据将用于生成监审文书。
  295. </div>
  296. </div>
  297. </el-form>
  298. </div>
  299. <!-- 右侧:模板预览和编辑区 -->
  300. <div class="document-preview">
  301. <!-- 预览/修改标签页 -->
  302. <TemplatePreviewEdit :active-tab="activeTab" :file-url="fileUrl" />
  303. </div>
  304. </div>
  305. </CostAuditDialog>
  306. <CostAuditDialog
  307. :title="dialogTitle"
  308. :visible="dialogVisible"
  309. :width="dialogWidth"
  310. :close-on-click-modal="false"
  311. :z-index="9300"
  312. @cancel="handleCancel"
  313. @confirm="handleConfirm"
  314. >
  315. <cost-audit-table
  316. :table-data="selectDocumentWhData"
  317. :columns="selectDocumentWhColumns"
  318. :show-selection="true"
  319. :show-pagination="true"
  320. :pagination="selectDocumentWhPagination"
  321. @pagination-change="selectDocumentWhPaginationChange"
  322. @selection-change="selectDocumentWhSelectionChange"
  323. >
  324. <!-- 创建时间自定义单元格 -->
  325. <template #createTime="{ row }">
  326. <div>{{ row.createTime ? row.createTime.split(' ')[0] : '-' }}</div>
  327. <div>{{ row.createTime ? row.createTime.split(' ')[1] : '-' }}</div>
  328. </template>
  329. </cost-audit-table>
  330. </CostAuditDialog>
  331. </div>
  332. </template>
  333. <script>
  334. import CostAuditTable from '@/components/costAudit/CostAuditTable.vue'
  335. import CostAuditDialog from '@/components/costAudit/CostAuditDialog.vue'
  336. import TemplatePreviewEdit from '@/components/costAudit/TemplatePreviewEdit.vue'
  337. import { getAllUnitList } from '@/api/auditEntityManage'
  338. import {
  339. getDocList,
  340. getCostProjectDocumentFile,
  341. queryByDocumentIdandWhereValue,
  342. } from '@/api/auditReviewDocManage.js'
  343. import { getData } from '@/api/auditDocNoManage.js'
  344. import {
  345. addCostProjectDocument,
  346. updateCostProjectDocument,
  347. deleteCostProjectDocument,
  348. getCostProjectDocumentDetail,
  349. updateScan,
  350. downDocument,
  351. getCostProjectDocumentPageList,
  352. } from '@/api/taskCustomizedRelease.js'
  353. import { dictMixin, regionMixin } from '@/mixins/useDict'
  354. import { uploadFile } from '@/api/file'
  355. export default {
  356. components: { CostAuditTable, CostAuditDialog, TemplatePreviewEdit },
  357. mixins: [dictMixin, regionMixin],
  358. props: {
  359. // 父组件传递的参数
  360. project: {
  361. type: Object,
  362. default: () => {},
  363. },
  364. id: {
  365. type: String,
  366. default: '',
  367. },
  368. isView: {
  369. type: Boolean,
  370. default: false,
  371. },
  372. },
  373. data() {
  374. return {
  375. isMultipleMode: false,
  376. dictData: {
  377. whGenerateType: [],
  378. },
  379. activeDocumentTypeId: '',
  380. document: {
  381. createBy: '',
  382. createTime: '',
  383. documentAlias: '',
  384. documentId: '',
  385. documentName: '',
  386. documentNumber: '',
  387. documentType: '',
  388. documentWhId: '',
  389. electronicDocumentUrl: '',
  390. enterpriseId: [],
  391. feedbackDocumentUrl: '',
  392. feedbackTime: '',
  393. generateTime: '',
  394. id: '',
  395. isDeleted: '',
  396. isPushed: '',
  397. orderNum: 0,
  398. pkVal: '',
  399. projectId: '',
  400. pushTime: '',
  401. scanDocumentUrl: '',
  402. updateBy: '',
  403. updateTime: '',
  404. },
  405. loading: {
  406. saveDocument: false,
  407. },
  408. activeView: 'list', // list edit
  409. activeTab: 'preview', // 当前标签页,preview:预览,edit:修改
  410. // 所有单位列表
  411. allUnits: [],
  412. dialogVisible: false,
  413. dialogTitle: '选择文号',
  414. documentDialogVisible: false,
  415. documentDialogTitle: '添加监审通知书',
  416. dialogWidth: '80%',
  417. fileUrl: '',
  418. selectDocumentWhData: [],
  419. selectDocumentWhPagination: {
  420. currentPage: 1,
  421. pageSize: 10,
  422. total: 0,
  423. },
  424. selectDocumentWhSelection: [],
  425. costDocumentTemplateFiles: [],
  426. documentData: {
  427. documentTypes: [],
  428. selectedDoc: 'notice',
  429. list: [],
  430. pagination: {
  431. currentPage: 1,
  432. pageSize: 10,
  433. total: 0,
  434. },
  435. documentColumns: [
  436. {
  437. prop: 'documentId',
  438. label: '文书类型',
  439. align: 'center',
  440. slotName: 'documentId',
  441. },
  442. {
  443. prop: 'documentNumber',
  444. label: '文书文号',
  445. width: 200,
  446. align: 'center',
  447. },
  448. {
  449. prop: 'enterpriseId',
  450. label: '被监审单位',
  451. width: 200,
  452. align: 'left',
  453. slotName: 'enterpriseId',
  454. },
  455. {
  456. prop: 'generateTime',
  457. label: '生成时间',
  458. width: 100,
  459. align: 'center',
  460. slotName: 'generateTime',
  461. },
  462. {
  463. prop: 'electronicDocumentUrl',
  464. label: '电子文书',
  465. width: 200,
  466. align: 'center',
  467. slotName: 'electronicDocumentUrl',
  468. },
  469. {
  470. prop: 'scanDocumentUrl',
  471. label: '上传扫描件',
  472. width: 150,
  473. align: 'center',
  474. slotName: 'scanDocumentUrl',
  475. },
  476. {
  477. prop: 'isPushed',
  478. label: '是否推送被监审单位',
  479. width: 100,
  480. align: 'center',
  481. formatter: (row) => (row.isPushed == 1 ? '是' : '否'),
  482. },
  483. {
  484. prop: 'feedbackDocumentUrl',
  485. label: '被监审单位反馈资料',
  486. width: 150,
  487. align: 'center',
  488. slotName: 'feedbackDocumentUrl',
  489. },
  490. ],
  491. },
  492. documentRules: {
  493. // documentNumber: [
  494. // {
  495. // required: true,
  496. // message: '请选择通知书文号',
  497. // trigger: 'change',
  498. // },
  499. // ],
  500. enterpriseId: [
  501. {
  502. required: true,
  503. message: '请选择被监审单位',
  504. trigger: 'change',
  505. },
  506. ],
  507. documentId: [
  508. {
  509. required: true,
  510. message: '请选择模板',
  511. trigger: 'change',
  512. },
  513. ],
  514. isPushed: [
  515. {
  516. required: true,
  517. message: '请选择否推送被监审单位',
  518. trigger: 'change',
  519. },
  520. ],
  521. },
  522. dataUploadUrl: [],
  523. }
  524. },
  525. computed: {
  526. selectDocumentWhColumns() {
  527. return [
  528. {
  529. prop: 'whType',
  530. label: '文号分类',
  531. showOverflowTooltip: true,
  532. align: 'center',
  533. formatter: (row) => {
  534. let documentName =
  535. this.documentData.documentTypes.find(
  536. (item) => item.id == row.whType
  537. )?.documentName || '-'
  538. return documentName
  539. },
  540. },
  541. {
  542. prop: 'whName',
  543. label: '文号名称',
  544. showOverflowTooltip: true,
  545. align: 'center',
  546. },
  547. {
  548. prop: 'areaCode',
  549. label: '适用区域',
  550. showOverflowTooltip: true,
  551. align: 'center',
  552. formatter: (row) => this.regionNameMap[row.areaCode] || '-',
  553. },
  554. {
  555. prop: 'generateType',
  556. label: '生成类型',
  557. showOverflowTooltip: true,
  558. align: 'center',
  559. width: 120,
  560. formatter: (row) =>
  561. this.getDictName('whGenerateType', row.generateType),
  562. },
  563. ]
  564. },
  565. },
  566. watch: {
  567. id: {
  568. handler(newVal) {
  569. if (newVal) {
  570. this.getData()
  571. }
  572. },
  573. deep: true,
  574. immediate: true,
  575. },
  576. costDocumentTemplateFiles: {
  577. handler(newVal, oldVal) {
  578. if (newVal.length > 0) {
  579. console.log(this.costDocumentTemplateFiles)
  580. this.costDocumentTemplateFiles.forEach((item) => {
  581. if (
  582. item.pinyin.includes('ShiJian') &&
  583. (item.dataValue == null || item.dataValue == '')
  584. ) {
  585. // 获取当前时间,格式为YYYY-MM-DD HH:mm:ss
  586. let date = new Date()
  587. let year = date.getFullYear()
  588. let month = String(date.getMonth() + 1).padStart(2, '0')
  589. let day = String(date.getDate()).padStart(2, '0')
  590. let hours = String(date.getHours()).padStart(2, '0')
  591. let minutes = String(date.getMinutes()).padStart(2, '0')
  592. let seconds = String(date.getSeconds()).padStart(2, '0')
  593. item.dataValue = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
  594. }
  595. if (
  596. item.originalText.includes('需要提供材料') &&
  597. item.dataValue
  598. ) {
  599. this.dataUploadUrl = item.dataValue
  600. }
  601. })
  602. }
  603. },
  604. deep: true,
  605. },
  606. },
  607. mounted() {
  608. this.getData()
  609. this.loadOpts()
  610. },
  611. methods: {
  612. handleDocumentTypeClick(data) {
  613. this.activeDocumentTypeId = data.id
  614. this.getData(data)
  615. },
  616. // 获取监审通知数据
  617. async getData(data) {
  618. const res = await getDocList({
  619. page: 1,
  620. pageSize: 50,
  621. })
  622. this.documentData.documentTypes = res.value.records || []
  623. const pid = this.project.projectId
  624. if (!pid) return
  625. this.getlist(data)
  626. },
  627. getlist(data) {
  628. getCostProjectDocumentPageList({
  629. pageNum: this.documentData.pagination.currentPage,
  630. pageSize: this.documentData.pagination.pageSize,
  631. projectId: this.project.projectId,
  632. documentName: data ? data.documentName : '',
  633. permissionType: '0',
  634. }).then((res) => {
  635. this.documentData.list = res.value.value.records
  636. this.documentData.pagination.total = res.value.value.total
  637. })
  638. },
  639. getEnterpriseName(row) {
  640. // 处理enterpriseId,无论是数组还是逗号分隔的字符串
  641. let enterpriseIds = []
  642. if (Array.isArray(row.enterpriseId)) {
  643. enterpriseIds = row.enterpriseId
  644. } else if (typeof row.enterpriseId === 'string') {
  645. // 处理逗号分隔的字符串
  646. enterpriseIds = row.enterpriseId
  647. .split(',')
  648. .map((id) => id.trim())
  649. .filter((id) => id)
  650. } else if (row.enterpriseId) {
  651. // 处理其他可能的非空值
  652. enterpriseIds = [row.enterpriseId]
  653. }
  654. if (enterpriseIds.length > 0) {
  655. // 返回多个企业名称,用逗号分隔
  656. return enterpriseIds
  657. .map(
  658. (id) => this.allUnits.find((item) => item.unitId == id)?.unitName
  659. )
  660. .filter((name) => name)
  661. .join(', ')
  662. }
  663. return '-'
  664. },
  665. getDocumenType(row) {
  666. return this.documentData.documentTypes.find(
  667. (item) => item.id == row.documentId
  668. )?.documentName
  669. },
  670. handlePaginationChange({ currentPage, pageSize }) {
  671. this.documentData.pagination.currentPage = currentPage
  672. this.documentData.pagination.pageSize = pageSize
  673. this.getlist()
  674. },
  675. // 加载选项数据
  676. loadOpts() {
  677. // 加载所有单位列表
  678. getAllUnitList().then((res) => {
  679. this.allUnits = res.value || []
  680. // 过滤掉状态为停用的数据
  681. this.allUnits = this.allUnits.filter((item) => item.status == 1)
  682. // 筛选this.project.auditedUnitId中的单位,支持多种格式
  683. if (this.project.auditedUnitId) {
  684. // 确保将project.auditedUnitId转换为数组格式
  685. let auditedUnitIds = []
  686. if (Array.isArray(this.project.auditedUnitId)) {
  687. auditedUnitIds = this.project.auditedUnitId
  688. } else if (
  689. typeof this.project.auditedUnitId === 'string' &&
  690. this.project.auditedUnitId.includes(',')
  691. ) {
  692. // 如果是逗号分隔的字符串,转换为数组
  693. auditedUnitIds = this.project.auditedUnitId
  694. .split(',')
  695. .map((id) => id.trim())
  696. } else {
  697. // 单个ID也转换为数组
  698. auditedUnitIds = [this.project.auditedUnitId]
  699. }
  700. // 使用数组进行筛选
  701. this.allUnits = this.allUnits.filter((item) =>
  702. auditedUnitIds.includes(item.unitId)
  703. )
  704. }
  705. })
  706. },
  707. // 生成文书
  708. handleGenerateDocument() {
  709. this.documentDialogTitle = '添加监审通知书'
  710. this.documentDialogVisible = true
  711. this.activeView = 'form'
  712. this.costDocumentTemplateFiles = []
  713. this.document = {
  714. createBy: '',
  715. createTime: '',
  716. documentAlias: '',
  717. documentId: '',
  718. documentName: '',
  719. documentNumber: '',
  720. documentType: '',
  721. documentWhId: '',
  722. electronicDocumentUrl: '',
  723. enterpriseId: this.isMultipleMode ? [] : '',
  724. feedbackDocumentUrl: '',
  725. feedbackTime: '',
  726. generateTime: '',
  727. id: '',
  728. isDeleted: '',
  729. isPushed: '1',
  730. orderNum: this.documentData.list.length + 1,
  731. pkVal: '',
  732. projectId: '',
  733. pushTime: '',
  734. scanDocumentUrl: '',
  735. updateBy: '',
  736. updateTime: '',
  737. }
  738. // if(this.isMultipleMode){
  739. // this.document.enterpriseId = this.project.auditedUnitId ? this.project.auditedUnitId.split(',') : []
  740. // console.log('this.document.enterpriseId',this.document.enterpriseId)
  741. // }else{
  742. // this.document.enterpriseId = this.project.auditedUnitId
  743. // }
  744. this.loadOpts()
  745. if (this.activeDocumentTypeId) {
  746. this.document.documentId = this.activeDocumentTypeId
  747. this.handleTemplateChange()
  748. }
  749. },
  750. getDetail() {
  751. getCostProjectDocumentDetail({
  752. projectId: this.project.projectId,
  753. }).then((res) => {
  754. if (res.value) {
  755. this.document = {
  756. ...this.document,
  757. ...res.value,
  758. }
  759. this.loadOpts()
  760. }
  761. })
  762. },
  763. selectClick() {
  764. this.dialogVisible = true
  765. this.activeView = 'table'
  766. this.getWhListData()
  767. },
  768. getWhListData() {
  769. getData({
  770. page: this.selectDocumentWhPagination.currentPage,
  771. pageSize: this.selectDocumentWhPagination.pageSize,
  772. whType: this.document.documentId,
  773. }).then((res) => {
  774. this.selectDocumentWhData = res.rows || []
  775. this.selectDocumentWhPagination.total = res.total || 0
  776. // 获取区域名称,填充regionNameMap
  777. if (this.selectDocumentWhData.length > 0) {
  778. this.fetchRegionNames(this.selectDocumentWhData, 'areaCode')
  779. }
  780. })
  781. },
  782. selectDocumentWhPaginationChange({ currentPage, pageSize }) {
  783. this.selectDocumentWhPagination.currentPage = currentPage
  784. this.selectDocumentWhPagination.pageSize = pageSize
  785. },
  786. selectDocumentWhSelectionChange(selection) {
  787. if (selection.length > 1) {
  788. this.$message.error('只能选择一个文号!')
  789. return
  790. } else {
  791. this.selectDocumentWhSelection = selection
  792. }
  793. },
  794. handleTemplateChange() {
  795. let data = this.documentData.documentTypes.find(
  796. (item) => item.id === this.document.documentId
  797. )
  798. this.fileUrl = data.fileUrl
  799. this.document.documentName = data.documentName
  800. this.document.documentNumber = ''
  801. this.document.documentWhId = ''
  802. this.getDocumentData()
  803. },
  804. getDocumentData() {
  805. if (this.document.id === null || this.document.id === '') {
  806. queryByDocumentIdandWhereValue({
  807. documentId: this.document.documentId,
  808. whereValue: this.project.projectId,
  809. }).then((res) => {
  810. this.costDocumentTemplateFiles = res.value || []
  811. })
  812. } else {
  813. getCostProjectDocumentFile({
  814. // documentId: this.document.documentId,
  815. // whereValue: this.project.projectId,
  816. id: this.document.id,
  817. }).then((res) => {
  818. this.costDocumentTemplateFiles = res.value || []
  819. })
  820. }
  821. },
  822. handleConfirm() {
  823. switch (this.activeView) {
  824. case 'table':
  825. this.handleConfirmSelect()
  826. break
  827. case 'form':
  828. this.handleSaveDocument()
  829. break
  830. default:
  831. break
  832. }
  833. },
  834. handleConfirmSelect() {
  835. if (this.selectDocumentWhSelection.length !== 1) {
  836. this.$message.error('请选择一个文号!')
  837. return
  838. }
  839. // 获取选中的文号信息
  840. const selectedDocument = this.selectDocumentWhSelection[0]
  841. this.document.documentNumber = selectedDocument.whNo
  842. this.document.documentWhId = selectedDocument.id
  843. // 数据表格回显文号
  844. this.costDocumentTemplateFiles.forEach((item) => {
  845. if (
  846. item.pinyin.includes('WenHao') ||
  847. item.pinyin.includes('WenJianHao')
  848. ) {
  849. item.dataValue = selectedDocument.whNo
  850. }
  851. if (item.pinyin.includes('SongDaWenShuMingCheng')) {
  852. item.dataValue = selectedDocument.whName
  853. }
  854. })
  855. this.dialogVisible = false
  856. this.activeView = 'form'
  857. },
  858. handleEnterpriseChange(val) {
  859. if (this.document.enterpriseId) {
  860. // BeiJianShenDanWei
  861. let unit = this.allUnits.find(
  862. (item) => item.unitId === this.document.enterpriseId
  863. )
  864. this.costDocumentTemplateFiles.forEach((item) => {
  865. if (item.pinyin.includes('BeiJianShenDanWei')) {
  866. item.dataValue = unit.unitName
  867. }
  868. if (item.pinyin.includes('ShouSongDaRen')) {
  869. item.dataValue = unit.contactName
  870. }
  871. if (item.pinyin.includes('BeiJianShenDanWeiBanGongDiDian')) {
  872. item.dataValue = unit.address
  873. }
  874. if (item.pinyin.includes('BeiJianShenDanWeiLianXiRenDianHua')) {
  875. item.dataValue = unit.contactMobile
  876. }
  877. })
  878. }
  879. },
  880. // 保存文档
  881. handleSaveDocument() {
  882. // 校验表单
  883. this.$refs.documentForm.validate((valid) => {
  884. if (!valid) {
  885. this.$message.error('请填写必填项!')
  886. return false
  887. }
  888. this.loading.saveDocument = true
  889. if (this.document.id) {
  890. updateCostProjectDocument({
  891. ...this.document,
  892. costProjectDocumentFiles: this.costDocumentTemplateFiles,
  893. projectId: this.project.projectId,
  894. electronicDocumentUrl:
  895. this.document.electronicDocumentUrl || this.fileUrl,
  896. enterpriseId: this.isMultipleMode
  897. ? this.document.enterpriseId.join(',')
  898. : this.document.enterpriseId, // 保存时转换为逗号分隔的字符串
  899. })
  900. .then((res) => {
  901. this.loading.saveDocument = false
  902. this.$message.success('保存成功!')
  903. this.documentDialogVisible = false
  904. this.activeView = ''
  905. this.getlist()
  906. })
  907. .catch((err) => {
  908. this.loading.saveDocument = false
  909. })
  910. } else {
  911. addCostProjectDocument({
  912. ...this.document,
  913. projectId: this.project.projectId,
  914. costProjectDocumentFiles: this.costDocumentTemplateFiles || [],
  915. enterpriseId: this.isMultipleMode
  916. ? this.document.enterpriseId.join(',')
  917. : this.document.enterpriseId,
  918. electronicDocumentUrl: this.fileUrl,
  919. })
  920. .then(() => {
  921. this.loading.saveDocument = false
  922. this.$message.success('保存成功!')
  923. this.documentDialogVisible = false
  924. this.activeView = ''
  925. this.getlist()
  926. })
  927. .catch((err) => {
  928. this.loading.saveDocument = false
  929. })
  930. }
  931. })
  932. },
  933. // 处理取消
  934. handleCancel() {
  935. if (this.activeView === 'form') {
  936. this.documentDialogVisible = false
  937. } else {
  938. this.activeView = 'form'
  939. this.dialogVisible = false
  940. }
  941. },
  942. // 上传扫描件
  943. handleUploadScan(row, type) {
  944. let loading = null
  945. // 第一步:创建文件选择器
  946. const input = document.createElement('input')
  947. input.type = 'file'
  948. input.accept = '.pdf,.doc,.docx,.xls,.xlsx,.csv' // 允许的文件类型
  949. input.onchange = async (event) => {
  950. const file = event.target.files[0]
  951. if (!file) return
  952. try {
  953. // 校验文件大小(50MB)
  954. const maxSize = 50 * 1024 * 1024 // 50MB
  955. if (file.size > maxSize) {
  956. this.$message.error('文件大小不能超过50MB!')
  957. return
  958. }
  959. // 校验文件格式
  960. const allowedFormats = [
  961. '.pdf',
  962. '.doc',
  963. '.docx',
  964. '.xls',
  965. '.xlsx',
  966. 'csv',
  967. ]
  968. const fileName = file.name.toLowerCase()
  969. const isValidFormat = allowedFormats.some((format) =>
  970. fileName.endsWith(format)
  971. )
  972. if (!isValidFormat) {
  973. this.$message.error(
  974. '只允许上传.pdf,.doc,.docx,.xls,.xlsx,.csv格式的文件!'
  975. )
  976. return
  977. }
  978. // 显示遮罩层
  979. loading = this.$baseLoading(1, '文件上传中...')
  980. // 第三步:创建FormData并上传文件
  981. const formData = new FormData()
  982. formData.append('file', file)
  983. // 先调用上传API
  984. const uploadRes = await uploadFile('/api/file/v1/upload', formData)
  985. // 第四步:检查上传结果
  986. if (!uploadRes || !uploadRes.value) {
  987. // this.$message.error('文件上传失败!');
  988. return
  989. }
  990. // 第五步:文件上传成功后,再更新数据
  991. const fileInfo = uploadRes.value
  992. // 创建更新数据对象
  993. const updateData = {
  994. id: row.id,
  995. scanDocumentUrl: fileInfo?.savePath, // 更新扫描件URL
  996. }
  997. // 第六步:调用更新API
  998. await updateScan(updateData)
  999. // 第七步:更新成功,显示提示并刷新
  1000. this.$message.success('文件上传成功并更新数据!')
  1001. this.getlist()
  1002. } catch (error) {
  1003. // 错误处理
  1004. // this.$message.error('操作失败:' + (error.message || '未知错误'))
  1005. } finally {
  1006. // 关闭遮罩层
  1007. loading.close()
  1008. }
  1009. }
  1010. // 触发文件选择
  1011. input.click()
  1012. },
  1013. // 查看扫描件
  1014. handleViewScan(fileUrl) {
  1015. if (!fileUrl) {
  1016. this.$message.error('暂无文件!')
  1017. return
  1018. }
  1019. // 对文件URL进行Base64编码
  1020. const encodedUrl = encodeURIComponent(
  1021. Base64.encode(window.context.form + fileUrl)
  1022. )
  1023. // 构建 kkFileView 预览URL
  1024. // onlinePreview - 在线预览
  1025. // onlinePreview?type=pdf - 强制使用PDF模式预览
  1026. window.open(`${host}:8012/onlinePreview?url=${encodedUrl}`)
  1027. },
  1028. // 编辑文档
  1029. handleEditDocument(row) {
  1030. this.documentDialogTitle = '修改监审通知书'
  1031. this.documentDialogVisible = true
  1032. this.activeView = 'form'
  1033. this.loadOpts()
  1034. // 确保enterpriseId是数组格式,处理可能的逗号分隔字符串
  1035. const enterpriseId = this.isMultipleMode
  1036. ? row.enterpriseId.split(',')
  1037. : row.enterpriseId
  1038. this.document = {
  1039. ...row,
  1040. documentId: Number(row.documentId),
  1041. enterpriseId,
  1042. // 确保isPushed有值,如果row中没有,设置默认值'1'
  1043. isPushed: row.isPushed !== undefined ? row.isPushed : '1',
  1044. }
  1045. this.fileUrl = this.document.electronicDocumentUrl
  1046. this.getDocumentData()
  1047. },
  1048. // 签章
  1049. handleSignDocument(row) {
  1050. this.$message.warning('签章功能待实现!')
  1051. },
  1052. // 删除文档
  1053. handleDeleteDocument(row) {
  1054. this.$confirm('确定要删除该数据吗?', '提示', {
  1055. confirmButtonText: '确定',
  1056. cancelButtonText: '取消',
  1057. type: 'warning',
  1058. }).then(() => {
  1059. deleteCostProjectDocument(row.id).then((res) => {
  1060. this.$message.success('删除成功!')
  1061. this.getlist()
  1062. })
  1063. })
  1064. },
  1065. handleDownloadDocument1(row) {
  1066. // 显示加载状态
  1067. this.$loading({
  1068. lock: true,
  1069. text: '文件下载中...',
  1070. spinner: 'el-icon-loading',
  1071. background: 'rgba(0, 0, 0, 0.7)',
  1072. })
  1073. let fileUrl = window.context.form + row.electronicDocumentUrl
  1074. let fileName = ''
  1075. // 从URL中提取文件名
  1076. const urlParts = fileUrl.split('/')
  1077. let urlFileName = urlParts[urlParts.length - 1]
  1078. // 处理URL可能包含查询参数的情况
  1079. if (urlFileName.includes('?')) {
  1080. urlFileName = urlFileName.split('?')[0]
  1081. }
  1082. // 检查从URL提取的文件名是否有效
  1083. if (urlFileName && /\.[a-zA-Z0-9]+$/.test(urlFileName)) {
  1084. fileName = urlFileName
  1085. } else {
  1086. // URL中无法提取有效文件名时,使用row.documentName作为备选
  1087. fileName = row.documentName || `监审通知书_${new Date().getTime()}`
  1088. // 确保备选文件名有扩展名
  1089. if (!/\.[a-zA-Z0-9]+$/.test(fileName)) {
  1090. fileName += '.pdf'
  1091. }
  1092. }
  1093. // 创建隐藏的a标签进行下载
  1094. const link = document.createElement('a')
  1095. link.style.display = 'none'
  1096. link.href = fileUrl
  1097. // 设置下载文件名
  1098. link.download = fileName
  1099. document.body.appendChild(link)
  1100. link.click()
  1101. document.body.removeChild(link)
  1102. // 关闭加载状态
  1103. this.$loading().close()
  1104. },
  1105. // 下载文档
  1106. handleDownloadDocument(row) {
  1107. // 显示加载状态
  1108. this.$loading({
  1109. lock: true,
  1110. text: '文件下载中...',
  1111. spinner: 'el-icon-loading',
  1112. background: 'rgba(0, 0, 0, 0.7)',
  1113. })
  1114. // 从API中获取文件URL
  1115. downDocument({
  1116. id: row.id,
  1117. })
  1118. .then((res) => {
  1119. // 关闭加载状态
  1120. this.$loading().close()
  1121. // 检查返回结果是否成功
  1122. if (!res || !res.state) {
  1123. this.$message.error(
  1124. `下载失败:${res?.message || '未获取到文件数据'}`
  1125. )
  1126. return
  1127. }
  1128. // 获取文件URL
  1129. const fileUrl = res.value
  1130. if (!fileUrl) {
  1131. this.$message.error('下载失败:未获取到文件URL')
  1132. return
  1133. }
  1134. // 优先从URL中提取文件名
  1135. let fileName = ''
  1136. // 从URL中提取文件名
  1137. const urlParts = fileUrl.split('/')
  1138. let urlFileName = urlParts[urlParts.length - 1]
  1139. // 处理URL可能包含查询参数的情况
  1140. if (urlFileName.includes('?')) {
  1141. urlFileName = urlFileName.split('?')[0]
  1142. }
  1143. // 检查从URL提取的文件名是否有效
  1144. if (urlFileName && /\.[a-zA-Z0-9]+$/.test(urlFileName)) {
  1145. fileName = urlFileName
  1146. } else {
  1147. // URL中无法提取有效文件名时,使用row.documentName作为备选
  1148. fileName =
  1149. row.documentName || `监审通知书_${new Date().getTime()}`
  1150. // 确保备选文件名有扩展名
  1151. if (!/\.[a-zA-Z0-9]+$/.test(fileName)) {
  1152. fileName += '.pdf'
  1153. }
  1154. }
  1155. // 创建隐藏的a标签进行下载
  1156. const link = document.createElement('a')
  1157. link.style.display = 'none'
  1158. link.href = fileUrl
  1159. // link.href = window.context.form + row.electronicDocumentUrl
  1160. // 设置下载文件名
  1161. link.download = fileName
  1162. document.body.appendChild(link)
  1163. link.click()
  1164. document.body.removeChild(link)
  1165. })
  1166. .catch((error) => {
  1167. // 关闭加载状态
  1168. this.$loading().close()
  1169. console.error('获取文件URL失败:', error)
  1170. })
  1171. },
  1172. handleUploadClick(row) {
  1173. console.log('handleUploadClick', row)
  1174. let loading = null
  1175. // 第一步:创建文件选择器
  1176. const input = document.createElement('input')
  1177. input.type = 'file'
  1178. input.accept = '.pdf,.doc,.docx,.xls,.xlsx,.csv' // 允许的文件类型
  1179. input.onchange = async (event) => {
  1180. const file = event.target.files[0]
  1181. if (!file) return
  1182. try {
  1183. // 校验文件大小(50MB)
  1184. const maxSize = 50 * 1024 * 1024 // 50MB
  1185. if (file.size > maxSize) {
  1186. this.$message.error('文件大小不能超过50MB!')
  1187. return
  1188. }
  1189. // 校验文件格式
  1190. const allowedFormats = [
  1191. '.pdf',
  1192. '.doc',
  1193. '.docx',
  1194. '.xls',
  1195. '.xlsx',
  1196. 'csv',
  1197. ]
  1198. const fileName = file.name.toLowerCase()
  1199. const isValidFormat = allowedFormats.some((format) =>
  1200. fileName.endsWith(format)
  1201. )
  1202. if (!isValidFormat) {
  1203. this.$message.error(
  1204. '只允许上传.pdf,.doc,.docx,.xls,.xlsx,.csv格式的文件!'
  1205. )
  1206. return
  1207. }
  1208. // 显示遮罩层
  1209. loading = this.$baseLoading(1, '文件上传中...')
  1210. // 第三步:创建FormData并上传文件
  1211. const formData = new FormData()
  1212. formData.append('file', file)
  1213. // 先调用上传API
  1214. const uploadRes = await uploadFile('/api/file/v1/upload', formData)
  1215. // 第四步:检查上传结果
  1216. if (!uploadRes || !uploadRes.value) {
  1217. // this.$message.error('文件上传失败!');
  1218. return
  1219. }
  1220. // 第五步:文件上传成功后,再更新数据
  1221. const fileInfo = uploadRes.value
  1222. this.costDocumentTemplateFiles.find(
  1223. (item) => item.originalText === row.originalText
  1224. ).dataValue = fileInfo.savePath
  1225. this.$message.success('文件上传成功!')
  1226. } catch (error) {
  1227. console.error('文件上传失败:', error)
  1228. } finally {
  1229. // 关闭遮罩层
  1230. loading.close()
  1231. }
  1232. }
  1233. // 触发文件选择对话框显示
  1234. input.click()
  1235. },
  1236. },
  1237. }
  1238. </script>
  1239. <style lang="scss" scoped>
  1240. @import '@/styles/costAudit.scss';
  1241. .documents-layout {
  1242. display: flex;
  1243. margin-bottom: 20px;
  1244. }
  1245. .documents-type-list {
  1246. width: 200px;
  1247. border: 1px solid #ebeef5;
  1248. border-radius: 5px;
  1249. padding: 10px;
  1250. margin-right: 20px;
  1251. }
  1252. .documents-type-list h3 {
  1253. margin-bottom: 10px;
  1254. font-size: 14px;
  1255. font-weight: bold;
  1256. }
  1257. .type-item {
  1258. padding: 5px 0;
  1259. cursor: pointer;
  1260. font-size: 12px;
  1261. }
  1262. .type-item:hover {
  1263. color: #409eff;
  1264. }
  1265. .documents-content {
  1266. flex: 1;
  1267. }
  1268. .generate-btn {
  1269. margin-bottom: 10px;
  1270. }
  1271. .cursor-pointer {
  1272. cursor: pointer;
  1273. }
  1274. .mt10 {
  1275. margin-top: 10px;
  1276. }
  1277. .mt20 {
  1278. margin-top: 20px;
  1279. }
  1280. .document-edit-container {
  1281. display: flex;
  1282. .document-params {
  1283. width: 50%;
  1284. }
  1285. .document-preview {
  1286. width: 50%;
  1287. }
  1288. }
  1289. </style>