auditNoticeTab.vue 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054
  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"
  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="
  66. handleViewScan(scope.row.scanDocumentUrl, 'scanDocumentUrl')
  67. "
  68. >
  69. 查看附件
  70. </el-button>
  71. </template>
  72. <template #feedbackDocumentUrl="scope">
  73. <!-- <el-button
  74. type="text"
  75. size="mini"
  76. :disabled="isView"
  77. @click="handleUploadScan(scope.row, 'feedbackDocumentUrl')"
  78. >
  79. 上传附件
  80. </el-button> -->
  81. <el-button
  82. type="text"
  83. size="mini"
  84. @click="
  85. handleViewScan(
  86. scope.row.feedbackDocumentUrl,
  87. 'feedbackDocumentUrl'
  88. )
  89. "
  90. >
  91. 查看附件
  92. </el-button>
  93. </template>
  94. <template #electronicDocumentUrl="scope">
  95. <el-button
  96. v-if="!isView"
  97. type="text"
  98. size="mini"
  99. @click="handleEditDocument(scope.row)"
  100. >
  101. 修改
  102. </el-button>
  103. <el-button
  104. v-if="!isView"
  105. type="text"
  106. size="mini"
  107. @click="handleSignDocument(scope.row)"
  108. >
  109. 签章
  110. </el-button>
  111. <el-button
  112. v-if="!isView"
  113. type="text"
  114. size="mini"
  115. @click="handleDeleteDocument(scope.row)"
  116. >
  117. 删除
  118. </el-button>
  119. <el-button
  120. type="text"
  121. size="mini"
  122. @click="handleDownloadDocument(scope.row)"
  123. >
  124. 下载
  125. </el-button>
  126. </template>
  127. </CostAuditTable>
  128. </div>
  129. </div>
  130. <div style="margin-top: 20px; font-size: 14px" class="table-description">
  131. 说明:此处只能生成各被监审单位的《成本监审通知书》和《送达回证》,同时接收或上传被监审单位的反馈的《送达回证》。
  132. </div>
  133. <!-- 编辑监审通知书 -->
  134. <CostAuditDialog
  135. :title="documentDialogTitle"
  136. :visible="documentDialogVisible"
  137. :width="dialogWidth"
  138. :close-on-click-modal="false"
  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. ></el-input>
  175. <!-- disabled -->
  176. <el-button
  177. type="primary"
  178. size="small"
  179. class="ml10"
  180. @click="selectClick"
  181. >
  182. 选择文号
  183. </el-button>
  184. </el-form-item>
  185. <el-form-item label="被监审单位" prop="enterpriseId">
  186. <div style="display: flex; align-items: center; gap: 15px">
  187. <el-select
  188. v-model="document.enterpriseId"
  189. placeholder="请选择被监审单位"
  190. style="width: 100%"
  191. clearable
  192. :multiple="isMultipleMode"
  193. >
  194. <el-option
  195. v-for="item in allUnits"
  196. :key="item.unitId"
  197. :label="item.unitName"
  198. :value="item.unitId"
  199. ></el-option>
  200. </el-select>
  201. </div>
  202. </el-form-item>
  203. <el-form-item label="是否推送被监审单位:" prop="isPushed">
  204. <!-- 是否推送被监审单位 -->
  205. <el-radio-group v-model="document.isPushed">
  206. <el-radio label="1">是</el-radio>
  207. <el-radio label="0">否</el-radio>
  208. </el-radio-group>
  209. </el-form-item>
  210. <!-- <el-form-item label="被监审单位:">
  211. <el-select
  212. v-model="document.enterpriseId"
  213. placeholder="请选择被监审单位"
  214. style="width: 100%"
  215. clearable
  216. >
  217. <el-option
  218. v-for="item in allUnits"
  219. :key="item.unitId"
  220. :label="item.unitName"
  221. :value="item.unitId"
  222. ></el-option>
  223. </el-select>
  224. </el-form-item> -->
  225. <!-- 数据内容区域 -->
  226. <div style="margin-top: 20px">
  227. <h4 style="margin-bottom: 10px">数据内容:</h4>
  228. <el-table
  229. :data="costDocumentTemplateFiles"
  230. style="
  231. width: 100%;
  232. border: 1px solid #dcdfe6;
  233. border-radius: 4px;
  234. "
  235. >
  236. <el-table-column
  237. prop="originalText"
  238. label="数据项"
  239. width="120"
  240. align="center"
  241. show-overflow-tooltip
  242. ></el-table-column>
  243. <el-table-column
  244. prop="labelValue"
  245. label="标签"
  246. width="100"
  247. align="center"
  248. show-overflow-tooltip
  249. ></el-table-column>
  250. <el-table-column
  251. prop="originalText"
  252. label="描述"
  253. min-width="120"
  254. align="center"
  255. show-overflow-tooltip
  256. ></el-table-column>
  257. <el-table-column
  258. prop="dataValue"
  259. label="数据值"
  260. min-width="150"
  261. align="center"
  262. show-overflow-tooltip
  263. ></el-table-column>
  264. </el-table>
  265. <div style="margin-top: 10px; font-size: 12px; color: #909399">
  266. 说明:数据内容不可修改,已在监审文书管理中配置完成,数据值为本次监审项目的相关数据。
  267. </div>
  268. </div>
  269. </el-form>
  270. </div>
  271. <!-- 右侧:模板预览和编辑区 -->
  272. <div class="document-preview">
  273. <!-- 预览/修改标签页 -->
  274. <TemplatePreviewEdit :active-tab="activeTab" :file-url="fileUrl" />
  275. </div>
  276. </div>
  277. </CostAuditDialog>
  278. <CostAuditDialog
  279. :title="dialogTitle"
  280. :visible="dialogVisible"
  281. :width="dialogWidth"
  282. :close-on-click-modal="false"
  283. @cancel="handleCancel"
  284. @confirm="handleConfirm"
  285. >
  286. <cost-audit-table
  287. :table-data="selectDocumentWhData"
  288. :columns="selectDocumentWhColumns"
  289. :show-selection="true"
  290. :show-pagination="true"
  291. :pagination="selectDocumentWhPagination"
  292. @pagination-change="selectDocumentWhPaginationChange"
  293. @selection-change="selectDocumentWhSelectionChange"
  294. >
  295. <!-- 创建时间自定义单元格 -->
  296. <template #createTime="{ row }">
  297. <div>{{ row.createTime ? row.createTime.split(' ')[0] : '-' }}</div>
  298. <div>{{ row.createTime ? row.createTime.split(' ')[1] : '-' }}</div>
  299. </template>
  300. </cost-audit-table>
  301. </CostAuditDialog>
  302. </div>
  303. </template>
  304. <script>
  305. // import { taskMixin } from './index.js'
  306. import CostAuditTable from '@/components/costAudit/CostAuditTable.vue'
  307. import CostAuditDialog from '@/components/costAudit/CostAuditDialog.vue'
  308. import TemplatePreviewEdit from '@/components/costAudit/TemplatePreviewEdit.vue'
  309. import { getAllUnitList } from '@/api/auditEntityManage'
  310. import { queryByDocumentIdandWhereValue } from '@/api/auditReviewDocManage.js'
  311. import { getData } from '@/api/auditDocNoManage.js'
  312. import { updateScan, downDocument } from '@/api/taskCustomizedRelease.js'
  313. import {
  314. addCostProjectDocument,
  315. updateCostProjectDocument,
  316. deleteCostProjectDocument,
  317. getCostProjectDocumentDetail,
  318. } from '@/api/taskCustomizedRelease.js'
  319. import { dictMixin, regionMixin } from '@/mixins/useDict'
  320. import { uploadFile } from '@/api/file'
  321. export default {
  322. components: { CostAuditTable, CostAuditDialog, TemplatePreviewEdit },
  323. mixins: [dictMixin, regionMixin],
  324. props: {
  325. // 父组件传递的参数
  326. project: {
  327. type: Object,
  328. default: () => {},
  329. },
  330. isView: {
  331. type: Boolean,
  332. default: false,
  333. },
  334. documentData: {
  335. type: Object,
  336. default: () => {},
  337. },
  338. },
  339. data() {
  340. return {
  341. isMultipleMode: false,
  342. dictData: {
  343. whGenerateType: [],
  344. },
  345. activeDocumentTypeId: '',
  346. document: {
  347. createBy: '',
  348. createTime: '',
  349. documentAlias: '',
  350. documentId: '',
  351. documentName: '',
  352. documentNumber: '',
  353. documentType: '',
  354. documentWhId: '',
  355. electronicDocumentUrl: '',
  356. enterpriseId: [],
  357. feedbackDocumentUrl: '',
  358. feedbackTime: '',
  359. generateTime: '',
  360. id: '',
  361. isDeleted: '',
  362. isPushed: '',
  363. orderNum: 0,
  364. pkVal: '',
  365. projectId: '',
  366. pushTime: '',
  367. scanDocumentUrl: '',
  368. updateBy: '',
  369. updateTime: '',
  370. },
  371. loading: {
  372. saveDocument: false,
  373. },
  374. activeView: 'list', // list edit
  375. activeTab: 'preview', // 当前标签页,preview:预览,edit:修改
  376. // 所有单位列表
  377. allUnits: [],
  378. dialogVisible: false,
  379. dialogTitle: '选择文号',
  380. documentDialogVisible: false,
  381. documentDialogTitle: '添加监审通知书',
  382. dialogWidth: '70%',
  383. fileUrl: '',
  384. selectDocumentWhData: [],
  385. selectDocumentWhPagination: {
  386. currentPage: 1,
  387. pageSize: 10,
  388. total: 0,
  389. },
  390. selectDocumentWhSelection: [],
  391. costDocumentTemplateFiles: [],
  392. documentRules: {
  393. documentNumber: [
  394. {
  395. required: true,
  396. message: '请选择通知书文号',
  397. trigger: 'change',
  398. },
  399. ],
  400. enterpriseId: [
  401. {
  402. required: true,
  403. message: '请选择被监审单位',
  404. trigger: 'change',
  405. },
  406. ],
  407. documentId: [
  408. {
  409. required: true,
  410. message: '请选择模板',
  411. trigger: 'change',
  412. },
  413. ],
  414. isPushed: [
  415. {
  416. required: true,
  417. message: '请选择否推送被监审单位',
  418. trigger: 'change',
  419. },
  420. ],
  421. },
  422. }
  423. },
  424. computed: {
  425. selectDocumentWhColumns() {
  426. return [
  427. {
  428. prop: 'whType',
  429. label: '文号分类',
  430. showOverflowTooltip: true,
  431. align: 'center',
  432. formatter: (row) => {
  433. let documentName =
  434. this.documentData.documentTypes.find(
  435. (item) => item.id == row.whType
  436. )?.documentName || '-'
  437. return documentName
  438. },
  439. },
  440. {
  441. prop: 'whName',
  442. label: '文号名称',
  443. showOverflowTooltip: true,
  444. align: 'center',
  445. },
  446. {
  447. prop: 'areaCode',
  448. label: '适用区域',
  449. showOverflowTooltip: true,
  450. align: 'center',
  451. formatter: (row) => this.regionNameMap[row.areaCode] || '-',
  452. },
  453. {
  454. prop: 'generateType',
  455. label: '生成类型',
  456. showOverflowTooltip: true,
  457. align: 'center',
  458. width: 120,
  459. formatter: (row) =>
  460. this.getDictName('whGenerateType', row.generateType),
  461. },
  462. ]
  463. },
  464. },
  465. watch: {},
  466. mounted() {},
  467. methods: {
  468. handleDocumentTypeClick(data) {
  469. this.activeDocumentTypeId = data.id
  470. this.$emit('refresh', data)
  471. },
  472. getEnterpriseName(row) {
  473. // 处理enterpriseId,无论是数组还是逗号分隔的字符串
  474. let enterpriseIds = []
  475. if (Array.isArray(row.enterpriseId)) {
  476. enterpriseIds = row.enterpriseId
  477. } else if (typeof row.enterpriseId === 'string') {
  478. // 处理逗号分隔的字符串
  479. enterpriseIds = row.enterpriseId
  480. .split(',')
  481. .map((id) => id.trim())
  482. .filter((id) => id)
  483. } else if (row.enterpriseId) {
  484. // 处理其他可能的非空值
  485. enterpriseIds = [row.enterpriseId]
  486. }
  487. if (enterpriseIds.length > 0) {
  488. // 返回多个企业名称,用逗号分隔
  489. return enterpriseIds
  490. .map(
  491. (id) => this.allUnits.find((item) => item.unitId == id)?.unitName
  492. )
  493. .filter((name) => name)
  494. .join(', ')
  495. }
  496. return '-'
  497. },
  498. getDocumenType(row) {
  499. return this.documentData.documentTypes.find(
  500. (item) => item.id == row.documentId
  501. )?.documentName
  502. },
  503. handlePaginationChange({ currentPage, pageSize }) {
  504. this.$emit('paginationChange', { currentPage, pageSize })
  505. },
  506. // 加载选项数据
  507. loadOpts() {
  508. // 加载所有单位列表
  509. getAllUnitList().then((res) => {
  510. this.allUnits = res.value || []
  511. // 过滤掉状态为停用的数据
  512. this.allUnits = this.allUnits.filter((item) => item.status == 1)
  513. // 筛选this.project.auditedUnitId中的单位,支持多种格式
  514. if (this.project.auditedUnitId) {
  515. // 确保将project.auditedUnitId转换为数组格式
  516. let auditedUnitIds = []
  517. if (Array.isArray(this.project.auditedUnitId)) {
  518. auditedUnitIds = this.project.auditedUnitId
  519. } else if (
  520. typeof this.project.auditedUnitId === 'string' &&
  521. this.project.auditedUnitId.includes(',')
  522. ) {
  523. // 如果是逗号分隔的字符串,转换为数组
  524. auditedUnitIds = this.project.auditedUnitId
  525. .split(',')
  526. .map((id) => id.trim())
  527. } else {
  528. // 单个ID也转换为数组
  529. auditedUnitIds = [this.project.auditedUnitId]
  530. }
  531. // 使用数组进行筛选
  532. this.allUnits = this.allUnits.filter((item) =>
  533. auditedUnitIds.includes(item.unitId)
  534. )
  535. }
  536. })
  537. },
  538. // 生成文书
  539. handleGenerateDocument() {
  540. this.documentDialogTitle = '添加监审通知书'
  541. this.documentDialogVisible = true
  542. this.activeView = 'form'
  543. this.document = {
  544. createBy: '',
  545. createTime: '',
  546. documentAlias: '',
  547. documentId: '',
  548. documentName: '',
  549. documentNumber: '',
  550. documentType: '',
  551. documentWhId: '',
  552. electronicDocumentUrl: '',
  553. enterpriseId: this.isMultipleMode ? [] : '',
  554. feedbackDocumentUrl: '',
  555. feedbackTime: '',
  556. generateTime: '',
  557. id: '',
  558. isDeleted: '',
  559. isPushed: '1',
  560. orderNum: this.documentData.list.length + 1,
  561. pkVal: '',
  562. projectId: '',
  563. pushTime: '',
  564. scanDocumentUrl: '',
  565. updateBy: '',
  566. updateTime: '',
  567. }
  568. // if(this.isMultipleMode){
  569. // this.document.enterpriseId = this.project.auditedUnitId ? this.project.auditedUnitId.split(',') : []
  570. // console.log('this.document.enterpriseId',this.document.enterpriseId)
  571. // }else{
  572. // this.document.enterpriseId = this.project.auditedUnitId
  573. // }
  574. this.loadOpts()
  575. if (this.activeDocumentTypeId) {
  576. this.document.documentId = this.activeDocumentTypeId
  577. this.handleTemplateChange()
  578. }
  579. this.costProjectDocumentFiles = []
  580. },
  581. getDetail() {
  582. getCostProjectDocumentDetail({
  583. projectId: this.project.projectId,
  584. }).then((res) => {
  585. if (res.value) {
  586. this.document = {
  587. ...this.document,
  588. ...res.value,
  589. }
  590. this.loadOpts()
  591. }
  592. })
  593. },
  594. selectClick() {
  595. this.dialogVisible = true
  596. this.activeView = 'table'
  597. this.getWhListData()
  598. },
  599. getWhListData() {
  600. getData({
  601. pageNum: this.selectDocumentWhPagination.currentPage,
  602. pageSize: this.selectDocumentWhPagination.pageSize,
  603. whType: this.document.documentId,
  604. }).then((res) => {
  605. this.selectDocumentWhData = res.value.records || []
  606. this.selectDocumentWhPagination.total = res.value.total || 0
  607. })
  608. },
  609. selectDocumentWhPaginationChange({ currentPage, pageSize }) {
  610. this.selectDocumentWhPagination.currentPage = currentPage
  611. this.selectDocumentWhPagination.pageSize = pageSize
  612. },
  613. selectDocumentWhSelectionChange(selection) {
  614. if (selection.length > 1) {
  615. this.$message.error('只能选择一个文号!')
  616. return
  617. } else {
  618. this.selectDocumentWhSelection = selection
  619. }
  620. },
  621. handleTemplateChange() {
  622. let data = this.documentData.documentTypes.find(
  623. (item) => item.id === this.document.documentId
  624. )
  625. this.fileUrl = data.fileUrl
  626. this.document.documentName = data.documentName
  627. this.getDocumentData()
  628. },
  629. getDocumentData() {
  630. if (this.document.documentId) {
  631. queryByDocumentIdandWhereValue({
  632. documentId: this.document.documentId,
  633. whereValue: this.project.projectId,
  634. }).then((res) => {
  635. this.costDocumentTemplateFiles = res.value || []
  636. })
  637. }
  638. },
  639. handleConfirm() {
  640. switch (this.activeView) {
  641. case 'table':
  642. this.handleConfirmSelect()
  643. break
  644. case 'form':
  645. this.handleSaveDocument()
  646. break
  647. default:
  648. break
  649. }
  650. },
  651. handleConfirmSelect() {
  652. if (this.selectDocumentWhSelection.length !== 1) {
  653. this.$message.error('请选择一个文号!')
  654. return
  655. }
  656. this.document.documentNumber = this.selectDocumentWhSelection[0].whNo
  657. this.document.documentWhId = this.selectDocumentWhSelection[0].id // 假设这是正确的字段名
  658. this.dialogVisible = false
  659. this.activeView = 'form'
  660. },
  661. // 保存文档
  662. handleSaveDocument() {
  663. // 校验表单
  664. this.$refs.documentForm.validate((valid) => {
  665. if (!valid) {
  666. this.$message.error('请填写必填项!')
  667. return false
  668. }
  669. this.loading.saveDocument = true
  670. if (this.document.id) {
  671. updateCostProjectDocument({
  672. ...this.document,
  673. costProjectDocumentFiles: this.costDocumentTemplateFiles,
  674. projectId: this.project.projectId,
  675. electronicDocumentUrl:
  676. this.document.electronicDocumentUrl || this.fileUrl,
  677. enterpriseId: this.isMultipleMode
  678. ? this.document.enterpriseId.join(',')
  679. : this.document.enterpriseId, // 保存时转换为逗号分隔的字符串
  680. })
  681. .then((res) => {
  682. this.loading.saveDocument = false
  683. this.$message.success('保存成功!')
  684. this.documentDialogVisible = false
  685. this.activeView = ''
  686. this.$emit('refresh', this.project.projectId)
  687. })
  688. .catch((err) => {
  689. this.loading.saveDocument = false
  690. })
  691. } else {
  692. addCostProjectDocument({
  693. ...this.document,
  694. projectId: this.project.projectId,
  695. costProjectDocumentFiles: this.costDocumentTemplateFiles || [],
  696. enterpriseId: this.isMultipleMode
  697. ? this.document.enterpriseId.join(',')
  698. : this.document.enterpriseId,
  699. electronicDocumentUrl: this.fileUrl,
  700. })
  701. .then(() => {
  702. this.loading.saveDocument = false
  703. this.$message.success('保存成功!')
  704. this.documentDialogVisible = false
  705. this.activeView = ''
  706. this.$emit('refresh', this.project.projectId)
  707. })
  708. .catch((err) => {
  709. this.loading.saveDocument = false
  710. })
  711. }
  712. })
  713. },
  714. // 处理取消
  715. handleCancel() {
  716. if (this.activeView === 'form') {
  717. this.documentDialogVisible = false
  718. } else {
  719. this.activeView = 'form'
  720. this.dialogVisible = false
  721. }
  722. },
  723. // 上传扫描件
  724. handleUploadScan(row, type) {
  725. let loading = null
  726. // 第一步:创建文件选择器
  727. const input = document.createElement('input')
  728. input.type = 'file'
  729. input.accept = '.pdf,.doc,.docx,.xls,.xlsx,.csv' // 允许的文件类型
  730. input.onchange = async (event) => {
  731. const file = event.target.files[0]
  732. if (!file) return
  733. try {
  734. // 校验文件大小(50MB)
  735. const maxSize = 50 * 1024 * 1024 // 50MB
  736. if (file.size > maxSize) {
  737. this.$message.error('文件大小不能超过50MB!')
  738. return
  739. }
  740. // 校验文件格式
  741. const allowedFormats = [
  742. '.pdf',
  743. '.doc',
  744. '.docx',
  745. '.xls',
  746. '.xlsx',
  747. 'csv',
  748. ]
  749. const fileName = file.name.toLowerCase()
  750. const isValidFormat = allowedFormats.some((format) =>
  751. fileName.endsWith(format)
  752. )
  753. if (!isValidFormat) {
  754. this.$message.error(
  755. '只允许上传.pdf,.doc,.docx,.xls,.xlsx,.csv格式的文件!'
  756. )
  757. return
  758. }
  759. // 显示遮罩层
  760. loading = this.$baseLoading(1, '文件上传中...')
  761. // 第三步:创建FormData并上传文件
  762. const formData = new FormData()
  763. formData.append('file', file)
  764. // 先调用上传API
  765. const uploadRes = await uploadFile('/api/file/v1/upload', formData)
  766. // 第四步:检查上传结果
  767. if (!uploadRes || !uploadRes.value) {
  768. // this.$message.error('文件上传失败!');
  769. return
  770. }
  771. // 第五步:文件上传成功后,再更新数据
  772. const fileInfo = uploadRes.value
  773. // 创建更新数据对象
  774. const updateData = {
  775. id: row.id,
  776. scanDocumentUrl: fileInfo?.savePath, // 更新扫描件URL
  777. }
  778. // 第六步:调用更新API
  779. await updateScan(updateData)
  780. // 第七步:更新成功,显示提示并刷新
  781. this.$message.success('文件上传成功并更新数据!')
  782. this.$emit('refresh', this.project.projectId) // 通知父组件刷新
  783. } catch (error) {
  784. // 错误处理
  785. // this.$message.error('操作失败:' + (error.message || '未知错误'))
  786. } finally {
  787. // 关闭遮罩层
  788. loading.close()
  789. }
  790. }
  791. // 触发文件选择
  792. input.click()
  793. },
  794. // 查看扫描件
  795. handleViewScan(fileUrl, type) {
  796. if (!fileUrl) {
  797. this.$message.error('暂无文件!')
  798. return
  799. }
  800. // 对文件URL进行Base64编码
  801. const encodedUrl = encodeURIComponent(
  802. Base64.encode(window.context.form + fileUrl)
  803. )
  804. // 构建 kkFileView 预览URL
  805. // onlinePreview - 在线预览
  806. // onlinePreview?type=pdf - 强制使用PDF模式预览
  807. window.open(`${host}:8012/onlinePreview?url=${encodedUrl}`)
  808. },
  809. // 编辑文档
  810. handleEditDocument(row) {
  811. this.documentDialogTitle = '修改监审通知书'
  812. this.documentDialogVisible = true
  813. this.activeView = 'form'
  814. this.loadOpts()
  815. // 确保enterpriseId是数组格式,处理可能的逗号分隔字符串
  816. const enterpriseId = this.isMultipleMode
  817. ? row.enterpriseId.split(',')
  818. : row.enterpriseId
  819. this.document = {
  820. ...row,
  821. documentId: Number(row.documentId),
  822. enterpriseId,
  823. // 确保isPushed有值,如果row中没有,设置默认值'1'
  824. isPushed: row.isPushed !== undefined ? row.isPushed : '1',
  825. }
  826. this.fileUrl = this.document.electronicDocumentUrl
  827. this.getDocumentData()
  828. },
  829. // 签章
  830. handleSignDocument(row) {
  831. this.$message.warning('签章功能待实现!')
  832. },
  833. // 删除文档
  834. handleDeleteDocument(row) {
  835. this.$confirm('确定要删除该数据吗?', '提示', {
  836. confirmButtonText: '确定',
  837. cancelButtonText: '取消',
  838. type: 'warning',
  839. }).then(() => {
  840. deleteCostProjectDocument(row.id).then((res) => {
  841. this.$message.success('删除成功!')
  842. this.$emit('refresh', this.project.projectId)
  843. })
  844. })
  845. },
  846. handleDownloadDocument1(row) {
  847. // 显示加载状态
  848. this.$loading({
  849. lock: true,
  850. text: '文件下载中...',
  851. spinner: 'el-icon-loading',
  852. background: 'rgba(0, 0, 0, 0.7)',
  853. })
  854. let fileUrl = window.context.form + row.electronicDocumentUrl
  855. let fileName = ''
  856. // 从URL中提取文件名
  857. const urlParts = fileUrl.split('/')
  858. let urlFileName = urlParts[urlParts.length - 1]
  859. // 处理URL可能包含查询参数的情况
  860. if (urlFileName.includes('?')) {
  861. urlFileName = urlFileName.split('?')[0]
  862. }
  863. // 检查从URL提取的文件名是否有效
  864. if (urlFileName && /\.[a-zA-Z0-9]+$/.test(urlFileName)) {
  865. fileName = urlFileName
  866. } else {
  867. // URL中无法提取有效文件名时,使用row.documentName作为备选
  868. fileName = row.documentName || `监审通知书_${new Date().getTime()}`
  869. // 确保备选文件名有扩展名
  870. if (!/\.[a-zA-Z0-9]+$/.test(fileName)) {
  871. fileName += '.pdf'
  872. }
  873. }
  874. // 创建隐藏的a标签进行下载
  875. const link = document.createElement('a')
  876. link.style.display = 'none'
  877. link.href = fileUrl
  878. // 设置下载文件名
  879. link.download = fileName
  880. document.body.appendChild(link)
  881. link.click()
  882. document.body.removeChild(link)
  883. // 关闭加载状态
  884. this.$loading().close()
  885. },
  886. // 下载文档
  887. handleDownloadDocument(row) {
  888. // 显示加载状态
  889. this.$loading({
  890. lock: true,
  891. text: '文件下载中...',
  892. spinner: 'el-icon-loading',
  893. background: 'rgba(0, 0, 0, 0.7)',
  894. })
  895. // 从API中获取文件URL
  896. downDocument({
  897. id: row.id,
  898. })
  899. .then((res) => {
  900. // 关闭加载状态
  901. this.$loading().close()
  902. // 检查返回结果是否成功
  903. if (!res || !res.state) {
  904. this.$message.error(
  905. `下载失败:${res?.message || '未获取到文件数据'}`
  906. )
  907. return
  908. }
  909. // 获取文件URL
  910. const fileUrl = res.value
  911. if (!fileUrl) {
  912. this.$message.error('下载失败:未获取到文件URL')
  913. return
  914. }
  915. // 优先从URL中提取文件名
  916. let fileName = ''
  917. // 从URL中提取文件名
  918. const urlParts = fileUrl.split('/')
  919. let urlFileName = urlParts[urlParts.length - 1]
  920. // 处理URL可能包含查询参数的情况
  921. if (urlFileName.includes('?')) {
  922. urlFileName = urlFileName.split('?')[0]
  923. }
  924. // 检查从URL提取的文件名是否有效
  925. if (urlFileName && /\.[a-zA-Z0-9]+$/.test(urlFileName)) {
  926. fileName = urlFileName
  927. } else {
  928. // URL中无法提取有效文件名时,使用row.documentName作为备选
  929. fileName =
  930. row.documentName || `监审通知书_${new Date().getTime()}`
  931. // 确保备选文件名有扩展名
  932. if (!/\.[a-zA-Z0-9]+$/.test(fileName)) {
  933. fileName += '.pdf'
  934. }
  935. }
  936. // 创建隐藏的a标签进行下载
  937. const link = document.createElement('a')
  938. link.style.display = 'none'
  939. link.href = fileUrl
  940. // link.href = window.context.form + row.electronicDocumentUrl
  941. // 设置下载文件名
  942. link.download = fileName
  943. document.body.appendChild(link)
  944. link.click()
  945. document.body.removeChild(link)
  946. })
  947. .catch((error) => {
  948. // 关闭加载状态
  949. this.$loading().close()
  950. console.error('获取文件URL失败:', error)
  951. })
  952. },
  953. },
  954. }
  955. </script>
  956. <style lang="scss" scoped>
  957. @import '@/styles/costAudit.scss';
  958. .documents-layout {
  959. display: flex;
  960. margin-bottom: 20px;
  961. }
  962. .documents-type-list {
  963. width: 200px;
  964. border: 1px solid #ebeef5;
  965. border-radius: 5px;
  966. padding: 10px;
  967. margin-right: 20px;
  968. }
  969. .documents-type-list h3 {
  970. margin-bottom: 10px;
  971. font-size: 14px;
  972. font-weight: bold;
  973. }
  974. .type-item {
  975. padding: 5px 0;
  976. cursor: pointer;
  977. font-size: 12px;
  978. }
  979. .type-item:hover {
  980. color: $base-color-default;
  981. }
  982. .type-item.active {
  983. color: $base-color-default;
  984. }
  985. .documents-content {
  986. flex: 1;
  987. }
  988. .generate-btn {
  989. margin-bottom: 10px;
  990. }
  991. .cursor-pointer {
  992. cursor: pointer;
  993. }
  994. .mt10 {
  995. margin-top: 10px;
  996. }
  997. .mt20 {
  998. margin-top: 20px;
  999. }
  1000. .document-edit-container {
  1001. display: flex;
  1002. .document-params {
  1003. width: 50%;
  1004. }
  1005. .document-preview {
  1006. width: 50%;
  1007. }
  1008. }
  1009. </style>