auditNoticeTab.vue 44 KB

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