auditNoticeTab.vue 49 KB

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