auditDocumentsMain.vue 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365
  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 && !getDocumenType(scope.row).includes('通知书')"
  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="9400"
  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="9500"
  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: 50,
  422. total: 0,
  423. },
  424. selectDocumentWhSelection: [],
  425. costDocumentTemplateFiles: [],
  426. documentData: {
  427. documentTypes: [],
  428. selectedDoc: 'notice',
  429. list: [],
  430. pagination: {
  431. currentPage: 1,
  432. pageSize: 50,
  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. },
  803. getDocumentData() {
  804. if (this.document.id === null || this.document.id === '') {
  805. queryByDocumentIdandWhereValue({
  806. documentId: this.document.documentId,
  807. whereValue: this.project.projectId,
  808. unitId: this.document.enterpriseId,
  809. }).then((res) => {
  810. this.costDocumentTemplateFiles = res.value || []
  811. let unit = this.allUnits.find(
  812. (item) => item.unitId === this.document.enterpriseId
  813. )
  814. this.costDocumentTemplateFiles.forEach((item) => {
  815. if (item.pinyin.includes('BeiJianShenDanWei')) {
  816. item.dataValue = unit.unitName
  817. }
  818. if (item.pinyin.includes('ShouSongDaRen')) {
  819. item.dataValue = unit.contactName
  820. }
  821. if (item.pinyin.includes('BeiJianShenDanWeiBanGongDiDian')) {
  822. item.dataValue = unit.address
  823. }
  824. if (item.pinyin.includes('BeiJianShenDanWeiLianXiRenDianHua')) {
  825. item.dataValue = unit.contactMobile
  826. }
  827. // 回显文号
  828. // 获取选中的文号信息
  829. const selectedDocument = this.selectDocumentWhSelection[0]
  830. if (
  831. item.pinyin.includes('WenHao') ||
  832. item.pinyin.includes('WenJianHao')
  833. ) {
  834. item.dataValue = selectedDocument.whNo
  835. }
  836. if (item.pinyin.includes('SongDaWenShuMingCheng')) {
  837. item.dataValue = selectedDocument.whName
  838. }
  839. })
  840. })
  841. } else {
  842. getCostProjectDocumentFile({
  843. // documentId: this.document.documentId,
  844. // whereValue: this.project.projectId,
  845. id: this.document.id,
  846. }).then((res) => {
  847. this.costDocumentTemplateFiles = res.value || []
  848. })
  849. }
  850. },
  851. handleConfirm() {
  852. switch (this.activeView) {
  853. case 'table':
  854. this.handleConfirmSelect()
  855. break
  856. case 'form':
  857. this.handleSaveDocument()
  858. break
  859. default:
  860. break
  861. }
  862. },
  863. handleConfirmSelect() {
  864. if (this.selectDocumentWhSelection.length !== 1) {
  865. this.$message.error('请选择一个文号!')
  866. return
  867. }
  868. // 获取选中的文号信息
  869. const selectedDocument = this.selectDocumentWhSelection[0]
  870. this.document.documentNumber = selectedDocument.whNo
  871. this.document.documentWhId = selectedDocument.id
  872. // 数据表格回显文号
  873. this.costDocumentTemplateFiles.forEach((item) => {
  874. if (
  875. item.pinyin.includes('WenHao') ||
  876. item.pinyin.includes('WenJianHao')
  877. ) {
  878. item.dataValue = selectedDocument.whNo
  879. }
  880. if (item.pinyin.includes('SongDaWenShuMingCheng')) {
  881. item.dataValue = selectedDocument.whName
  882. }
  883. })
  884. this.dialogVisible = false
  885. this.activeView = 'form'
  886. },
  887. handleEnterpriseChange(val) {
  888. if (this.document.enterpriseId) {
  889. // BeiJianShenDanWei
  890. this.getDocumentData()
  891. }
  892. },
  893. // 保存文档
  894. handleSaveDocument() {
  895. // 校验表单
  896. this.$refs.documentForm.validate((valid) => {
  897. if (!valid) {
  898. this.$message.error('请填写必填项!')
  899. return false
  900. }
  901. this.loading.saveDocument = true
  902. if (this.document.id) {
  903. updateCostProjectDocument({
  904. ...this.document,
  905. costProjectDocumentFiles: this.costDocumentTemplateFiles,
  906. projectId: this.project.projectId,
  907. electronicDocumentUrl:
  908. this.document.electronicDocumentUrl || this.fileUrl,
  909. enterpriseId: this.isMultipleMode
  910. ? this.document.enterpriseId.join(',')
  911. : this.document.enterpriseId, // 保存时转换为逗号分隔的字符串
  912. })
  913. .then((res) => {
  914. this.loading.saveDocument = false
  915. this.$message.success('保存成功!')
  916. this.documentDialogVisible = false
  917. this.activeView = ''
  918. this.getlist()
  919. })
  920. .catch((err) => {
  921. this.loading.saveDocument = false
  922. })
  923. } else {
  924. addCostProjectDocument({
  925. ...this.document,
  926. projectId: this.project.projectId,
  927. costProjectDocumentFiles: this.costDocumentTemplateFiles || [],
  928. enterpriseId: this.isMultipleMode
  929. ? this.document.enterpriseId.join(',')
  930. : this.document.enterpriseId,
  931. electronicDocumentUrl: this.fileUrl,
  932. })
  933. .then(() => {
  934. this.loading.saveDocument = false
  935. this.$message.success('保存成功!')
  936. this.documentDialogVisible = false
  937. this.activeView = ''
  938. this.getlist()
  939. })
  940. .catch((err) => {
  941. this.loading.saveDocument = false
  942. })
  943. }
  944. })
  945. },
  946. // 处理取消
  947. handleCancel() {
  948. if (this.activeView === 'form') {
  949. this.documentDialogVisible = false
  950. } else {
  951. this.activeView = 'form'
  952. this.dialogVisible = false
  953. }
  954. },
  955. // 上传扫描件
  956. handleUploadScan(row, type) {
  957. let loading = null
  958. // 第一步:创建文件选择器
  959. const input = document.createElement('input')
  960. input.type = 'file'
  961. input.accept = '.pdf,.doc,.docx,.xls,.xlsx,.csv' // 允许的文件类型
  962. input.onchange = async (event) => {
  963. const file = event.target.files[0]
  964. if (!file) return
  965. try {
  966. // 校验文件大小(50MB)
  967. const maxSize = 50 * 1024 * 1024 // 50MB
  968. if (file.size > maxSize) {
  969. this.$message.error('文件大小不能超过50MB!')
  970. return
  971. }
  972. // 校验文件格式
  973. const allowedFormats = [
  974. '.pdf',
  975. '.doc',
  976. '.docx',
  977. '.xls',
  978. '.xlsx',
  979. 'csv',
  980. ]
  981. const fileName = file.name.toLowerCase()
  982. const isValidFormat = allowedFormats.some((format) =>
  983. fileName.endsWith(format)
  984. )
  985. if (!isValidFormat) {
  986. this.$message.error(
  987. '只允许上传.pdf,.doc,.docx,.xls,.xlsx,.csv格式的文件!'
  988. )
  989. return
  990. }
  991. // 显示遮罩层
  992. loading = this.$baseLoading(1, '文件上传中...')
  993. // 第三步:创建FormData并上传文件
  994. const formData = new FormData()
  995. formData.append('file', file)
  996. // 先调用上传API
  997. const uploadRes = await uploadFile('/api/file/v1/upload', formData)
  998. // 第四步:检查上传结果
  999. if (!uploadRes || !uploadRes.value) {
  1000. // this.$message.error('文件上传失败!');
  1001. return
  1002. }
  1003. // 第五步:文件上传成功后,再更新数据
  1004. const fileInfo = uploadRes.value
  1005. // 创建更新数据对象
  1006. const updateData = {
  1007. id: row.id,
  1008. scanDocumentUrl: fileInfo?.savePath, // 更新扫描件URL
  1009. }
  1010. // 第六步:调用更新API
  1011. await updateScan(updateData)
  1012. // 第七步:更新成功,显示提示并刷新
  1013. this.$message.success('文件上传成功并更新数据!')
  1014. this.getlist()
  1015. } catch (error) {
  1016. // 错误处理
  1017. // this.$message.error('操作失败:' + (error.message || '未知错误'))
  1018. } finally {
  1019. // 关闭遮罩层
  1020. loading.close()
  1021. }
  1022. }
  1023. // 触发文件选择
  1024. input.click()
  1025. },
  1026. // 查看扫描件
  1027. handleViewScan(fileUrl) {
  1028. if (!fileUrl) {
  1029. this.$message.error('暂无文件!')
  1030. return
  1031. }
  1032. // 对文件URL进行Base64编码
  1033. const encodedUrl = encodeURIComponent(
  1034. Base64.encode(window.context.form + fileUrl)
  1035. )
  1036. // 构建 kkFileView 预览URL
  1037. // onlinePreview - 在线预览
  1038. // onlinePreview?type=pdf - 强制使用PDF模式预览
  1039. window.open(`${host}:8012/onlinePreview?url=${encodedUrl}`)
  1040. },
  1041. // 编辑文档
  1042. handleEditDocument(row) {
  1043. this.documentDialogTitle = '修改监审通知书'
  1044. this.documentDialogVisible = true
  1045. this.activeView = 'form'
  1046. this.loadOpts()
  1047. // 确保enterpriseId是数组格式,处理可能的逗号分隔字符串
  1048. const enterpriseId = this.isMultipleMode
  1049. ? row.enterpriseId.split(',')
  1050. : row.enterpriseId
  1051. this.document = {
  1052. ...row,
  1053. documentId: Number(row.documentId),
  1054. enterpriseId,
  1055. // 确保isPushed有值,如果row中没有,设置默认值'1'
  1056. isPushed: row.isPushed !== undefined ? row.isPushed : '1',
  1057. }
  1058. // 从API中获取文件URL
  1059. downDocument({
  1060. id: row.id,
  1061. }).then((res) => {
  1062. if (res.state) {
  1063. this.fileUrl = res.value || ''
  1064. } else {
  1065. this.$message.error('获取文件URL失败')
  1066. }
  1067. })
  1068. this.getDocumentData()
  1069. },
  1070. // 签章
  1071. handleSignDocument(row) {
  1072. this.$message.warning('签章功能待实现!')
  1073. },
  1074. // 删除文档
  1075. handleDeleteDocument(row) {
  1076. this.$confirm('确定要删除该数据吗?', '提示', {
  1077. confirmButtonText: '确定',
  1078. cancelButtonText: '取消',
  1079. type: 'warning',
  1080. }).then(() => {
  1081. deleteCostProjectDocument(row.id).then((res) => {
  1082. this.$message.success('删除成功!')
  1083. this.getlist()
  1084. })
  1085. })
  1086. },
  1087. handleDownloadDocument1(row) {
  1088. // 显示加载状态
  1089. this.$loading({
  1090. lock: true,
  1091. text: '文件下载中...',
  1092. spinner: 'el-icon-loading',
  1093. background: 'rgba(0, 0, 0, 0.7)',
  1094. })
  1095. let fileUrl = window.context.form + row.electronicDocumentUrl
  1096. let fileName = ''
  1097. // 从URL中提取文件名
  1098. const urlParts = fileUrl.split('/')
  1099. let urlFileName = urlParts[urlParts.length - 1]
  1100. // 处理URL可能包含查询参数的情况
  1101. if (urlFileName.includes('?')) {
  1102. urlFileName = urlFileName.split('?')[0]
  1103. }
  1104. // 检查从URL提取的文件名是否有效
  1105. if (urlFileName && /\.[a-zA-Z0-9]+$/.test(urlFileName)) {
  1106. fileName = urlFileName
  1107. } else {
  1108. // URL中无法提取有效文件名时,使用row.documentName作为备选
  1109. fileName = row.documentName || `监审通知书_${new Date().getTime()}`
  1110. // 确保备选文件名有扩展名
  1111. if (!/\.[a-zA-Z0-9]+$/.test(fileName)) {
  1112. fileName += '.pdf'
  1113. }
  1114. }
  1115. // 创建隐藏的a标签进行下载
  1116. const link = document.createElement('a')
  1117. link.style.display = 'none'
  1118. link.href = fileUrl
  1119. // 设置下载文件名
  1120. link.download = fileName
  1121. document.body.appendChild(link)
  1122. link.click()
  1123. document.body.removeChild(link)
  1124. // 关闭加载状态
  1125. this.$loading().close()
  1126. },
  1127. // 下载文档
  1128. handleDownloadDocument(row) {
  1129. // 显示加载状态
  1130. this.$loading({
  1131. lock: true,
  1132. text: '文件下载中...',
  1133. spinner: 'el-icon-loading',
  1134. background: 'rgba(0, 0, 0, 0.7)',
  1135. })
  1136. // 从API中获取文件URL
  1137. downDocument({
  1138. id: row.id,
  1139. })
  1140. .then((res) => {
  1141. // 关闭加载状态
  1142. this.$loading().close()
  1143. // 检查返回结果是否成功
  1144. if (!res || !res.state) {
  1145. this.$message.error(
  1146. `下载失败:${res?.message || '未获取到文件数据'}`
  1147. )
  1148. return
  1149. }
  1150. // 获取文件URL
  1151. const fileUrl = res.value
  1152. if (!fileUrl) {
  1153. this.$message.error('下载失败:未获取到文件URL')
  1154. return
  1155. }
  1156. // 优先从URL中提取文件名
  1157. let fileName = ''
  1158. // 从URL中提取文件名
  1159. const urlParts = fileUrl.split('/')
  1160. let urlFileName = urlParts[urlParts.length - 1]
  1161. // 处理URL可能包含查询参数的情况
  1162. if (urlFileName.includes('?')) {
  1163. urlFileName = urlFileName.split('?')[0]
  1164. }
  1165. // 检查从URL提取的文件名是否有效
  1166. if (urlFileName && /\.[a-zA-Z0-9]+$/.test(urlFileName)) {
  1167. fileName = urlFileName
  1168. } else {
  1169. // URL中无法提取有效文件名时,使用row.documentName作为备选
  1170. fileName =
  1171. row.documentName || `监审通知书_${new Date().getTime()}`
  1172. // 确保备选文件名有扩展名
  1173. if (!/\.[a-zA-Z0-9]+$/.test(fileName)) {
  1174. fileName += '.pdf'
  1175. }
  1176. }
  1177. // 创建隐藏的a标签进行下载
  1178. const link = document.createElement('a')
  1179. link.style.display = 'none'
  1180. link.href = fileUrl
  1181. // link.href = window.context.form + row.electronicDocumentUrl
  1182. // 设置下载文件名
  1183. link.download = fileName
  1184. document.body.appendChild(link)
  1185. link.click()
  1186. document.body.removeChild(link)
  1187. })
  1188. .catch((error) => {
  1189. // 关闭加载状态
  1190. this.$loading().close()
  1191. console.error('获取文件URL失败:', error)
  1192. })
  1193. },
  1194. handleUploadClick(row) {
  1195. console.log('handleUploadClick', row)
  1196. let loading = null
  1197. // 第一步:创建文件选择器
  1198. const input = document.createElement('input')
  1199. input.type = 'file'
  1200. input.accept = '.pdf,.doc,.docx,.xls,.xlsx,.csv' // 允许的文件类型
  1201. input.onchange = async (event) => {
  1202. const file = event.target.files[0]
  1203. if (!file) return
  1204. try {
  1205. // 校验文件大小(50MB)
  1206. const maxSize = 50 * 1024 * 1024 // 50MB
  1207. if (file.size > maxSize) {
  1208. this.$message.error('文件大小不能超过50MB!')
  1209. return
  1210. }
  1211. // 校验文件格式
  1212. const allowedFormats = [
  1213. '.pdf',
  1214. '.doc',
  1215. '.docx',
  1216. '.xls',
  1217. '.xlsx',
  1218. 'csv',
  1219. ]
  1220. const fileName = file.name.toLowerCase()
  1221. const isValidFormat = allowedFormats.some((format) =>
  1222. fileName.endsWith(format)
  1223. )
  1224. if (!isValidFormat) {
  1225. this.$message.error(
  1226. '只允许上传.pdf,.doc,.docx,.xls,.xlsx,.csv格式的文件!'
  1227. )
  1228. return
  1229. }
  1230. // 显示遮罩层
  1231. loading = this.$baseLoading(1, '文件上传中...')
  1232. // 第三步:创建FormData并上传文件
  1233. const formData = new FormData()
  1234. formData.append('file', file)
  1235. // 先调用上传API
  1236. const uploadRes = await uploadFile('/api/file/v1/upload', formData)
  1237. // 第四步:检查上传结果
  1238. if (!uploadRes || !uploadRes.value) {
  1239. // this.$message.error('文件上传失败!');
  1240. return
  1241. }
  1242. // 第五步:文件上传成功后,再更新数据
  1243. const fileInfo = uploadRes.value
  1244. this.costDocumentTemplateFiles.find(
  1245. (item) => item.originalText === row.originalText
  1246. ).dataValue = fileInfo.savePath
  1247. this.$message.success('文件上传成功!')
  1248. } catch (error) {
  1249. console.error('文件上传失败:', error)
  1250. } finally {
  1251. // 关闭遮罩层
  1252. loading.close()
  1253. }
  1254. }
  1255. // 触发文件选择对话框显示
  1256. input.click()
  1257. },
  1258. },
  1259. }
  1260. </script>
  1261. <style lang="scss" scoped>
  1262. @import '@/styles/costAudit.scss';
  1263. .documents-layout {
  1264. display: flex;
  1265. margin-bottom: 20px;
  1266. }
  1267. .documents-type-list {
  1268. width: 200px;
  1269. border: 1px solid #ebeef5;
  1270. border-radius: 5px;
  1271. padding: 10px;
  1272. margin-right: 20px;
  1273. }
  1274. .documents-type-list h3 {
  1275. margin-bottom: 10px;
  1276. font-size: 14px;
  1277. font-weight: bold;
  1278. }
  1279. .type-item {
  1280. padding: 5px 0;
  1281. cursor: pointer;
  1282. font-size: 12px;
  1283. }
  1284. .type-item:hover {
  1285. color: #409eff;
  1286. }
  1287. .documents-content {
  1288. flex: 1;
  1289. }
  1290. .generate-btn {
  1291. margin-bottom: 10px;
  1292. }
  1293. .cursor-pointer {
  1294. cursor: pointer;
  1295. }
  1296. .mt10 {
  1297. margin-top: 10px;
  1298. }
  1299. .mt20 {
  1300. margin-top: 20px;
  1301. }
  1302. .document-edit-container {
  1303. display: flex;
  1304. .document-params {
  1305. width: 50%;
  1306. }
  1307. .document-preview {
  1308. width: 50%;
  1309. }
  1310. }
  1311. </style>