taskInfo.vue 89 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647
  1. <template>
  2. <el-dialog
  3. :visible.sync="dialogVisible"
  4. title="任务详情"
  5. width="80%"
  6. top="5vh"
  7. :close-on-click-modal="false"
  8. :before-close="handleClose"
  9. class="task-info-dialog"
  10. >
  11. <div class="task-info-container">
  12. <!-- 报送资料预置模版查看弹窗:复用 SurveyFormDialog / FixedTableDialog / DynamicTableDialog,只读模式 -->
  13. <survey-form-dialog
  14. :visible.sync="surveyFormDialogVisible"
  15. :survey-data="{ ...(currentTemplateRow || {}), ...surveyDetailData }"
  16. :form-fields="formFields"
  17. :is-view-mode="true"
  18. :audited-unit-id="auditedUnitId"
  19. :request-type="viewContext === 'costSurvey' ? 1 : 2"
  20. :upload-id="
  21. (currentTemplateRow &&
  22. (currentTemplateRow.uploadId || currentTemplateRow.id)) ||
  23. ''
  24. "
  25. :survey-template-id="getSurveyTemplateId(currentTemplateRow)"
  26. :catalog-id="(currentTemplateRow && currentTemplateRow.catalogId) || ''"
  27. />
  28. <fixed-table-dialog
  29. :visible.sync="fixedTableDialogVisible"
  30. :survey-data="{ ...(currentTemplateRow || {}), fixedHeaders }"
  31. :fixed-fields="fixedFields || ''"
  32. :fixed-fieldids="fixedFieldids || ''"
  33. :table-items="tableItems"
  34. :audit-periods="auditPeriods"
  35. :is-view-mode="true"
  36. :request-type="viewContext === 'costSurvey' ? 1 : 2"
  37. :audited-unit-id="auditedUnitId"
  38. :upload-id="
  39. (currentTemplateRow &&
  40. (currentTemplateRow.uploadId || currentTemplateRow.id)) ||
  41. ''
  42. "
  43. :survey-template-id="getSurveyTemplateId(currentTemplateRow)"
  44. :catalog-id="(currentTemplateRow && currentTemplateRow.catalogId) || ''"
  45. />
  46. <dynamic-table-dialog
  47. :key="dynamicDialogKey"
  48. :visible.sync="dynamicTableDialogVisible"
  49. :survey-data="currentTemplateRow || {}"
  50. :table-data="dynamicTableData"
  51. :table-items="tableItems"
  52. :is-view-mode="true"
  53. :request-type="viewContext === 'costSurvey' ? 1 : 2"
  54. :audited-unit-id="auditedUnitId"
  55. :upload-id="
  56. (currentTemplateRow &&
  57. (currentTemplateRow.uploadId || currentTemplateRow.id)) ||
  58. ''
  59. "
  60. :survey-template-id="getSurveyTemplateId(currentTemplateRow)"
  61. :catalog-id="(currentTemplateRow && currentTemplateRow.catalogId) || ''"
  62. />
  63. <!-- 标签页容器 -->
  64. <el-tabs
  65. v-model="activeTab"
  66. type="border-card"
  67. @tab-click="handleTabClick"
  68. >
  69. <!-- 立项信息 -->
  70. <el-tab-pane label="立项信息" name="projectInfo">
  71. <div v-loading="loading" element-loading-text="加载中...">
  72. <el-form
  73. :model="formData.basicInfo"
  74. label-width="180px"
  75. disabled
  76. class="readonly-form"
  77. >
  78. <el-form-item label="成本监审项目名称:">
  79. <el-input
  80. v-model="formData.basicInfo.projectName"
  81. style="width: 400px"
  82. ></el-input>
  83. </el-form-item>
  84. <el-form-item label="关联成本监审目录:">
  85. <CatalogCascader
  86. ref="catalogCascader"
  87. :key="formData.basicInfo.catalogId"
  88. :form-item="{ placeholder: '请选择监审目录' }"
  89. style="width: 100%"
  90. :value="formData.basicInfo.catalogId"
  91. :disabled="isViewMode"
  92. @change="handleCatalogChange"
  93. />
  94. </el-form-item>
  95. <el-form-item label="监审地区:">
  96. <RegionSelector
  97. :key="formData.basicInfo.areaCode"
  98. :initial-area-code="formData.basicInfo.areaCode"
  99. :disabled="isViewMode"
  100. @region-change="handleRegionChange"
  101. ></RegionSelector>
  102. </el-form-item>
  103. <el-form-item label="被监审单位:">
  104. <el-select
  105. v-if="
  106. formData.basicInfo.auditUnitId &&
  107. formData.basicInfo.auditUnitId.length > 0
  108. "
  109. v-model="formData.basicInfo.auditUnitId"
  110. placeholder="请选择单位"
  111. clearable
  112. multiple
  113. style="width: 100%"
  114. :disabled="isViewMode"
  115. >
  116. <el-option
  117. v-for="unit in unitList"
  118. :key="unit.unitId"
  119. :label="unit.unitName"
  120. :value="unit.unitId"
  121. />
  122. </el-select>
  123. <el-input
  124. v-else
  125. v-model="formData.basicInfo.auditUnitName"
  126. placeholder="请输入单位"
  127. style="width: 100%"
  128. :disabled="isViewMode"
  129. ></el-input>
  130. </el-form-item>
  131. <el-form-item label="监审主体:">
  132. <el-select
  133. v-model="formData.basicInfo.orgId"
  134. placeholder="请选择监审主体"
  135. style="width: 100%"
  136. clearable
  137. :disabled="isViewMode"
  138. >
  139. <el-option
  140. v-for="Org in OrgList"
  141. :key="Org.id"
  142. :label="Org.name"
  143. :value="Org.id"
  144. />
  145. </el-select>
  146. </el-form-item>
  147. <el-form-item label="归属年度:">
  148. <el-date-picker
  149. v-model="formData.basicInfo.projectYear"
  150. style="width: 100%"
  151. type="year"
  152. placeholder="请选择归属年度"
  153. format="yyyy"
  154. value-format="yyyy"
  155. clearable
  156. :disabled="isViewMode"
  157. ></el-date-picker>
  158. </el-form-item>
  159. <el-form-item label="立项来源:">
  160. <el-select
  161. v-model="formData.basicInfo.sourceType"
  162. placeholder="请选择立项类型"
  163. style="width: 100%"
  164. :disabled="isViewMode"
  165. >
  166. <el-option
  167. v-for="item in dictData['projectProposal']"
  168. :key="item.key"
  169. :label="item.name"
  170. :value="item.key"
  171. ></el-option>
  172. </el-select>
  173. </el-form-item>
  174. <el-form-item label="监审形式:">
  175. <el-select
  176. v-model="formData.basicInfo.auditType"
  177. placeholder="请选择监审形式"
  178. style="width: 100%"
  179. :disabled="isViewMode"
  180. >
  181. <el-option
  182. v-for="item in dictData['auditType']"
  183. :key="item.key"
  184. :label="item.name"
  185. :value="item.key"
  186. ></el-option>
  187. </el-select>
  188. </el-form-item>
  189. <el-form-item label="监审期间:">
  190. <div class="cost-period-container">
  191. <!-- <el-button
  192. type="primary"
  193. size="small"
  194. class="add-cost-year-btn"
  195. :disabled="isViewMode || true"
  196. @click="addCostYear"
  197. >
  198. +
  199. </el-button> -->
  200. <div class="cost-years-wrapper">
  201. <div
  202. v-for="(year, index) in formData.basicInfo
  203. .auditPeriodArray"
  204. :key="index"
  205. class="cost-year-item"
  206. >
  207. <el-date-picker
  208. v-model="year.value"
  209. style="width: 82%; margin-bottom: 5px"
  210. type="year"
  211. placeholder="请选择年份"
  212. format="yyyy"
  213. value-format="yyyy"
  214. clearable
  215. :disabled="isViewMode"
  216. ></el-date-picker>
  217. <!-- <el-button
  218. type="danger"
  219. size="small"
  220. class="delete-cost-year-btn"
  221. :disabled="isViewMode"
  222. @click="deleteCostYear(index)"
  223. >
  224. 删除
  225. </el-button> -->
  226. </div>
  227. </div>
  228. </div>
  229. </el-form-item>
  230. <el-form-item label="是否参加听证:">
  231. <el-radio-group v-model="formData.basicInfo.needHearing">
  232. <el-radio :label="'0'">是</el-radio>
  233. <el-radio :label="'1'">否</el-radio>
  234. </el-radio-group>
  235. </el-form-item>
  236. <el-form-item label="是否应急项目:">
  237. <el-radio-group v-model="formData.basicInfo.isEmergency">
  238. <el-radio :label="'0'">是</el-radio>
  239. <el-radio :label="'1'">否</el-radio>
  240. </el-radio-group>
  241. </el-form-item>
  242. <el-form-item label="立项理由:">
  243. <el-input
  244. v-model="formData.basicInfo.establishmentReason"
  245. style="width: 100%"
  246. type="textarea"
  247. rows="4"
  248. ></el-input>
  249. </el-form-item>
  250. <el-form-item label="监审任务负责人:">
  251. <el-select
  252. v-model="formData.basicInfo.leaderId"
  253. placeholder="请选择负责人"
  254. style="width: 100%"
  255. :disabled="isViewMode"
  256. >
  257. <el-option
  258. v-for="(item, index) in userList"
  259. :key="index"
  260. :label="item.fullname"
  261. :value="item.userId"
  262. ></el-option>
  263. </el-select>
  264. </el-form-item>
  265. <el-form-item label="监审任务组成员:">
  266. <el-select
  267. v-model="formData.basicInfo.projectMembers"
  268. placeholder="请选择成员"
  269. multiple
  270. style="width: 100%"
  271. :disabled="isViewMode"
  272. >
  273. <el-option
  274. v-for="(item, index) in userList"
  275. :key="index"
  276. :label="item.fullname"
  277. :value="item.userId"
  278. ></el-option>
  279. </el-select>
  280. </el-form-item>
  281. <el-form-item label="其他专家:">
  282. <el-input
  283. v-model="formData.basicInfo.expertStr"
  284. style="width: 100%"
  285. ></el-input>
  286. </el-form-item>
  287. <el-form-item label="预定的监审工作起止时间:">
  288. <el-date-picker
  289. v-model="formData.basicInfo.plannedAuditStartDate"
  290. type="date"
  291. placeholder="开始日期"
  292. format="yyyy-MM-dd"
  293. value-format="yyyy-MM-dd"
  294. style="width: 150px"
  295. :disabled="isViewMode"
  296. ></el-date-picker>
  297. <span style="margin: 0 10px">—</span>
  298. <el-date-picker
  299. v-model="formData.basicInfo.plannedAuditEndDate"
  300. type="date"
  301. placeholder="结束日期"
  302. format="yyyy-MM-dd"
  303. value-format="yyyy-MM-dd"
  304. style="width: 150px"
  305. :disabled="isViewMode"
  306. ></el-date-picker>
  307. </el-form-item>
  308. </el-form>
  309. </div>
  310. </el-tab-pane>
  311. <!-- 企业-详情 监审文书 -->
  312. <el-tab-pane label="监审文书" name="auditDocument">
  313. <div v-loading="loading" element-loading-text="加载中...">
  314. <div style="font-size: 14px; margin-bottom: 15px; color: #606266">
  315. <strong>说明:</strong>
  316. 被监审单位接收《送达回证》后需签名并反馈监审主体。
  317. </div>
  318. <el-table
  319. style="width: 100%"
  320. border
  321. :data="formData.auditDocument"
  322. size="small"
  323. >
  324. <el-table-column prop="id" label="序号" width="80" align="center">
  325. <template slot-scope="scope">
  326. {{ scope.$index + 1 }}
  327. </template>
  328. </el-table-column>
  329. <el-table-column
  330. prop="documentName"
  331. label="文书类型"
  332. min-width="150"
  333. align="center"
  334. ></el-table-column>
  335. <el-table-column
  336. prop="documentNumber"
  337. label="文书文号"
  338. min-width="200"
  339. align="center"
  340. ></el-table-column>
  341. <el-table-column
  342. prop="generateTime"
  343. label="推送时间"
  344. min-width="180"
  345. align="center"
  346. ></el-table-column>
  347. <el-table-column
  348. prop="electronicDocumentUrl"
  349. label="文书操作"
  350. min-width="120"
  351. align="center"
  352. >
  353. <template slot-scope="scope">
  354. <el-button
  355. type="text"
  356. size="mini"
  357. @click="handleDocView(scope.row)"
  358. >
  359. 查看
  360. </el-button>
  361. <el-button
  362. type="text"
  363. size="mini"
  364. @click="handleDownloadDocument(scope.row)"
  365. >
  366. 下载
  367. </el-button>
  368. </template>
  369. </el-table-column>
  370. <el-table-column
  371. prop="feedbackDocumentUrl"
  372. label="向监审主体反馈文件"
  373. min-width="120"
  374. align="center"
  375. >
  376. <template
  377. v-if="scope.row.documentName.includes('送达回证')"
  378. slot-scope="scope"
  379. >
  380. <!-- <el-button
  381. :disabled="isViewMode"
  382. type="text"
  383. size="mini"
  384. @click="handleUploadScan(scope.row, 'feedbackDocumentUrl')"
  385. >
  386. 上传附件
  387. </el-button> -->
  388. <!-- <span>
  389. {{ scope.row.feedbackDocumentUrl ? '已回传' : '未回传' }}
  390. </span> -->
  391. <el-button
  392. v-if="scope.row.feedbackDocumentUrl"
  393. type="text"
  394. size="mini"
  395. @click="handleViewFeedback(scope.row.feedbackDocumentUrl)"
  396. >
  397. 查看附件
  398. </el-button>
  399. </template>
  400. </el-table-column>
  401. </el-table>
  402. <!-- 分页 -->
  403. <el-pagination
  404. style="margin: 20px 0"
  405. :current-page.sync="auditDocumentPagination.page"
  406. :page-sizes="[50, 100]"
  407. :page-size.sync="auditDocumentPagination.pageSize"
  408. layout="total, sizes, prev, pager, next, jumper"
  409. :total="auditDocumentPagination.total"
  410. @size-change="handleSizeChange"
  411. @current-change="handleCurrentChange"
  412. />
  413. </div>
  414. </el-tab-pane>
  415. <!-- 报送资料 -->
  416. <el-tab-pane label="报送资料" name="dataRequirements">
  417. <div v-loading="loading" element-loading-text="加载中...">
  418. <el-table
  419. border
  420. :data="groupedDataRequirements"
  421. size="small"
  422. :show-header="true"
  423. :row-class-name="getRowClassName"
  424. >
  425. <el-table-column
  426. prop="seq"
  427. label="序号"
  428. width="80"
  429. align="center"
  430. >
  431. <template slot-scope="scope">
  432. <span v-if="!scope.row.isCategoryHeader">
  433. {{ scope.row.seq || scope.row.index }}
  434. </span>
  435. </template>
  436. </el-table-column>
  437. <el-table-column
  438. prop="informationName"
  439. label="报送资料"
  440. min-width="280"
  441. header-align="center"
  442. align="left"
  443. >
  444. <template slot-scope="scope">
  445. <div
  446. v-if="scope.row.isCategoryHeader"
  447. class="category-header-cell"
  448. >
  449. {{ scope.row.categoryName }}
  450. </div>
  451. <span v-else>{{ scope.row.informationName || '-' }}</span>
  452. </template>
  453. </el-table-column>
  454. <el-table-column
  455. prop="formatRequired"
  456. label="资料类型"
  457. width="130"
  458. align="center"
  459. >
  460. <template slot-scope="scope">
  461. <span
  462. v-if="
  463. scope.row.formatRequired !== null &&
  464. scope.row.formatRequired !== undefined
  465. "
  466. >
  467. {{
  468. getDictName(
  469. 'formatAsk',
  470. String(scope.row.formatRequired)
  471. ) || scope.row.formatRequired
  472. }}
  473. </span>
  474. </template>
  475. </el-table-column>
  476. <el-table-column
  477. prop="isRequired"
  478. label="是否必填"
  479. width="110"
  480. align="center"
  481. >
  482. <template slot-scope="scope">
  483. <span v-if="!scope.row.isCategoryHeader">
  484. {{ scope.row.isRequired === '1' ? '是' : '否' }}
  485. </span>
  486. </template>
  487. </el-table-column>
  488. <el-table-column
  489. prop="isUpload"
  490. label="是否上传"
  491. width="110"
  492. align="center"
  493. >
  494. <template slot-scope="scope">
  495. <span v-if="!scope.row.isCategoryHeader">
  496. <span
  497. v-if="
  498. scope.row.isUpload === 1 || scope.row.isUpload === '1'
  499. "
  500. style="color: #67c23a"
  501. >
  502. 已上传
  503. </span>
  504. <span v-else style="color: #f56c6c">未上传</span>
  505. </span>
  506. </template>
  507. </el-table-column>
  508. <el-table-column
  509. prop="isRequired"
  510. label="审核状态"
  511. width="110"
  512. align="center"
  513. >
  514. <template slot-scope="scope">
  515. <span v-if="!scope.row.isCategoryHeader">
  516. <span
  517. v-if="
  518. scope.row.auditedStatus !== null &&
  519. scope.row.auditedStatus !== undefined
  520. "
  521. >
  522. {{
  523. getDictName(
  524. 'clshzt',
  525. String(scope.row.auditedStatus)
  526. ) || scope.row.auditedStatus
  527. }}
  528. </span>
  529. <span v-else>-</span>
  530. </span>
  531. </template>
  532. </el-table-column>
  533. <el-table-column
  534. prop="operation"
  535. label="操作"
  536. width="220"
  537. align="center"
  538. >
  539. <template slot-scope="scope">
  540. <template v-if="!scope.row.isCategoryHeader">
  541. <template v-if="scope.row.formatRequired !== '3'">
  542. <el-button
  543. v-if="
  544. scope.row.isUpload === 1 || scope.row.isUpload === '1'
  545. "
  546. type="text"
  547. size="small"
  548. @click="handleFileView(scope.row)"
  549. >
  550. 查看
  551. </el-button>
  552. <el-button
  553. v-if="
  554. scope.row.isUpload === 1 || scope.row.isUpload === '1'
  555. "
  556. type="text"
  557. size="small"
  558. @click="handleFileDownload(scope.row)"
  559. >
  560. 下载
  561. </el-button>
  562. <!-- <el-button
  563. v-if="scope.row.isUpload === 0 || scope.row.isUpload === '0'"
  564. type="text"
  565. size="small"
  566. :disabled="isViewMode"
  567. @click="$emit('handleFileUpload', scope.row)"
  568. >
  569. 上传
  570. </el-button> -->
  571. </template>
  572. <template v-if="scope.row.formatRequired == '3'">
  573. <el-button
  574. v-if="
  575. scope.row.isUpload === 1 || scope.row.isUpload === '1'
  576. "
  577. type="text"
  578. size="small"
  579. @click="handleViewTemplate(scope.row)"
  580. >
  581. 查看
  582. </el-button>
  583. <el-button
  584. v-if="
  585. (scope.row.isUpload === 1 ||
  586. scope.row.isUpload === '1') &&
  587. scope.row.templateType !== '1'
  588. "
  589. type="text"
  590. size="small"
  591. @click="handleTemplateDownload(scope.row)"
  592. >
  593. 模版下载
  594. </el-button>
  595. <!-- <el-button
  596. v-if="scope.row.isUpload === 0 || scope.row.isUpload === '0'"
  597. type="text"
  598. size="small"
  599. :disabled="isViewMode"
  600. @click="$emit('handleDataUpload', scope.row)"
  601. >
  602. 数据上传
  603. </el-button> -->
  604. </template>
  605. </template>
  606. </template>
  607. </el-table-column>
  608. </el-table>
  609. </div>
  610. </el-tab-pane>
  611. <!-- 成本调查表 -->
  612. <el-tab-pane label="成本调查表" name="costSurvey">
  613. <div v-loading="loading" element-loading-text="加载中...">
  614. <el-table
  615. style="width: 100%"
  616. :data="formData.costSurveyData"
  617. border
  618. size="small"
  619. >
  620. <el-table-column
  621. prop="index"
  622. label="序号"
  623. width="60"
  624. align="center"
  625. ></el-table-column>
  626. <el-table-column
  627. label="成本调查表"
  628. min-width="220"
  629. align="center"
  630. >
  631. <template slot-scope="scope">
  632. <span>{{ scope.row.name }}</span>
  633. </template>
  634. </el-table-column>
  635. <el-table-column
  636. prop="dataType"
  637. label="资料类型"
  638. width="120"
  639. align="center"
  640. ></el-table-column>
  641. <el-table-column
  642. prop="tableType"
  643. label="表格类型"
  644. width="120"
  645. align="center"
  646. ></el-table-column>
  647. <el-table-column
  648. prop="isRequired"
  649. label="是否必填"
  650. width="100"
  651. align="center"
  652. ></el-table-column>
  653. <el-table-column label="是否上传" width="100" align="center">
  654. <template slot-scope="scope">
  655. <span
  656. :style="{
  657. color:
  658. scope.row.isUpload === true ? '#67c23a' : '#f56c6c',
  659. }"
  660. >
  661. {{ scope.row.isUpload === true ? '已上传' : '未上传' }}
  662. </span>
  663. </template>
  664. </el-table-column>
  665. <!-- 操作列:查看单记录/固定表/动态表 -->
  666. <el-table-column label="操作" width="120" align="center">
  667. <template slot-scope="scope">
  668. <el-button
  669. type="text"
  670. size="small"
  671. @click="handleCostSurveyView(scope.row)"
  672. >
  673. 查看
  674. </el-button>
  675. </template>
  676. </el-table-column>
  677. </el-table>
  678. <el-pagination
  679. background
  680. layout="total, sizes, prev, pager, next"
  681. :current-page="costSurveyPagination.currentPage"
  682. :page-sizes="[10, 20, 30, 50]"
  683. :page-size="costSurveyPagination.pageSize"
  684. :total="costSurveyPagination.total"
  685. style="margin-top: 20px; text-align: right"
  686. @current-change="handleCostSurveyPageChange"
  687. @size-change="handleCostSurveySizeChange"
  688. />
  689. </div>
  690. </el-tab-pane>
  691. <!-- 监审意见 -->
  692. <el-tab-pane
  693. v-if="currentNode === 'yjfk'"
  694. label="监审意见"
  695. name="auditOpinion"
  696. >
  697. <div v-loading="loading" element-loading-text="加载中...">
  698. <div class="cost-supervision-container">
  699. <div class="cost-opinion-section">
  700. <h3>成本监审意见</h3>
  701. <div class="opinion-item">
  702. <label>被监审单位基本情况及主要财务状况</label>
  703. <el-input
  704. v-model="formData.auditOpinion.basicFinancialInfo"
  705. type="textarea"
  706. rows="5"
  707. disabled
  708. />
  709. </div>
  710. <div class="opinion-item">
  711. <label>监审项目现行执行的价格标准</label>
  712. <el-input
  713. v-model="formData.auditOpinion.priceStandard"
  714. type="textarea"
  715. rows="5"
  716. disabled
  717. />
  718. </div>
  719. <div class="opinion-item">
  720. <label>
  721. 监审项目的成本构成、数据核增核减情况、依据及理由
  722. </label>
  723. <el-input
  724. v-model="formData.auditOpinion.costComposition"
  725. type="textarea"
  726. rows="5"
  727. disabled
  728. />
  729. </div>
  730. <div class="opinion-item">
  731. <label>成本审核初步意见</label>
  732. <el-input
  733. v-model="formData.auditOpinion.preliminaryOpinion"
  734. type="textarea"
  735. rows="5"
  736. disabled
  737. />
  738. </div>
  739. </div>
  740. <div class="feedback-section">
  741. <h3>被监审单位反馈意见</h3>
  742. <div class="feedback-item">
  743. <label>被监审单位反馈意见</label>
  744. <el-input
  745. v-model="formData.auditOpinion.enterpriseFeedback"
  746. type="textarea"
  747. rows="5"
  748. disabled
  749. />
  750. </div>
  751. <div class="feedback-item">
  752. <label>被监审单位反馈资料</label>
  753. <div class="file-list-display">
  754. <div
  755. v-for="(file, index) in formData.auditOpinion.fileList"
  756. :key="index"
  757. class="file-item"
  758. >
  759. <i class="el-icon-document"></i>
  760. <span>{{ file.name }}</span>
  761. </div>
  762. <div
  763. v-if="
  764. !formData.auditOpinion.fileList ||
  765. formData.auditOpinion.fileList.length === 0
  766. "
  767. style="color: #909399"
  768. >
  769. 暂无文件
  770. </div>
  771. </div>
  772. </div>
  773. </div>
  774. </div>
  775. </div>
  776. </el-tab-pane>
  777. <!-- 消息通知 -->
  778. <el-tab-pane label="消息通知" name="messageNotice">
  779. <div v-loading="loading" element-loading-text="加载中...">
  780. <el-table
  781. style="width: 100%"
  782. border
  783. :data="formData.messageNotice"
  784. size="small"
  785. >
  786. <el-table-column prop="id" label="序号" width="80" align="center">
  787. <template slot-scope="scope">
  788. {{ scope.$index + 1 }}
  789. </template>
  790. </el-table-column>
  791. <el-table-column
  792. prop="noticeTitle"
  793. label="消息主题"
  794. width="200"
  795. align="center"
  796. ></el-table-column>
  797. <el-table-column
  798. prop="noticeSource"
  799. label="消息来源"
  800. width="150"
  801. align="center"
  802. ></el-table-column>
  803. <el-table-column
  804. prop="noticeContent"
  805. label="消息内容"
  806. min-width="350"
  807. align="center"
  808. ></el-table-column>
  809. <el-table-column
  810. prop="createTime"
  811. label="发送时间"
  812. width="180"
  813. align="center"
  814. ></el-table-column>
  815. </el-table>
  816. <el-pagination
  817. background
  818. layout="total, sizes, prev, pager, next"
  819. :current-page="messageNoticePagination.currentPage"
  820. :page-sizes="[10, 20, 30, 50]"
  821. :page-size="messageNoticePagination.pageSize"
  822. :total="messageNoticePagination.total"
  823. style="margin-top: 20px; text-align: right"
  824. @current-change="handleMessageNoticePageChange"
  825. @size-change="handleMessageNoticeSizeChange"
  826. />
  827. </div>
  828. </el-tab-pane>
  829. </el-tabs>
  830. </div>
  831. <!-- 查看监审通知书 -->
  832. <CostAuditDialog
  833. :title="documentDialogTitle"
  834. :visible="documentDialogVisible"
  835. width="82%"
  836. :close-on-click-modal="false"
  837. :z-index="9400"
  838. :show-confirm-btn="false"
  839. cancel-text="关闭"
  840. @cancel="handleDocCancel"
  841. >
  842. <div class="document-edit-container">
  843. <!-- 左侧:文书参数设置 -->
  844. <div class="document-params">
  845. <h4>文书参数设置:</h4>
  846. <el-form
  847. ref="documentForm"
  848. :model="document"
  849. label-width="170px"
  850. size="small"
  851. disabled
  852. >
  853. <el-form-item label="模板:" prop="documentId">
  854. {{ document.documentName }}
  855. </el-form-item>
  856. <el-form-item label="通知书文号:" prop="documentNumber">
  857. {{ document.documentNumber }}
  858. </el-form-item>
  859. <el-form-item label="被监审单位" prop="enterpriseId">
  860. <div style="display: flex; align-items: center; gap: 15px">
  861. <el-select
  862. v-model="document.enterpriseId"
  863. placeholder="请选择被监审单位"
  864. style="width: 100%"
  865. clearable
  866. >
  867. <el-option
  868. v-for="item in unitList"
  869. :key="item.unitId"
  870. :label="item.unitName"
  871. :value="item.unitId"
  872. ></el-option>
  873. </el-select>
  874. </div>
  875. </el-form-item>
  876. <el-form-item label="是否推送被监审单位:" prop="isPushed">
  877. <!-- 是否推送被监审单位 -->
  878. <el-radio-group v-model="document.isPushed">
  879. <el-radio label="1">是</el-radio>
  880. <el-radio label="0">否</el-radio>
  881. </el-radio-group>
  882. </el-form-item>
  883. <!-- 数据内容区域 -->
  884. <div style="margin-top: 20px">
  885. <h4 style="margin-bottom: 10px">数据内容:</h4>
  886. <el-table
  887. :data="costDocumentTemplateFiles"
  888. style="
  889. width: 100%;
  890. border: 1px solid #dcdfe6;
  891. border-radius: 4px;
  892. "
  893. >
  894. <el-table-column
  895. prop="originalText"
  896. label="数据项"
  897. width="120"
  898. align="center"
  899. show-overflow-tooltip
  900. ></el-table-column>
  901. <!-- <el-table-column
  902. prop="labelValue"
  903. label="标签"
  904. width="100"
  905. align="center"
  906. show-overflow-tooltip
  907. ></el-table-column> -->
  908. <el-table-column
  909. prop="originalText"
  910. label="描述"
  911. min-width="120"
  912. align="center"
  913. show-overflow-tooltip
  914. ></el-table-column>
  915. <el-table-column
  916. prop="dataValue"
  917. label="数据值"
  918. min-width="150"
  919. align="center"
  920. show-overflow-tooltip
  921. >
  922. <template slot-scope="scope">
  923. <el-input
  924. v-if="scope.row.originalText !== '需要提供材料'"
  925. v-model="scope.row.dataValue"
  926. size="small"
  927. placeholder="请输入数据值"
  928. disabled
  929. ></el-input>
  930. <!-- 否则显示上传按钮 -->
  931. <div v-else>
  932. <!-- <el-button
  933. type="primary"
  934. size="small"
  935. @click="handleUploadClick(scope.row)"
  936. >
  937. 上传附件
  938. </el-button> -->
  939. <el-button
  940. v-if="scope.row.dataValue"
  941. type="primary"
  942. size="small"
  943. :disabled="false"
  944. @click="handleViewScan(scope.row.dataValue)"
  945. >
  946. 查看附件
  947. </el-button>
  948. </div>
  949. </template>
  950. </el-table-column>
  951. </el-table>
  952. </div>
  953. </el-form>
  954. </div>
  955. <!-- 右侧:模板预览和编辑区 -->
  956. <div class="document-preview">
  957. <!-- 预览/修改标签页 -->
  958. <TemplatePreviewEdit
  959. :active-tab="activeDocTab"
  960. :file-url="fileUrl"
  961. :is-show-edit="false"
  962. />
  963. </div>
  964. </div>
  965. </CostAuditDialog>
  966. <!-- 多附件查看弹窗 -->
  967. <multi-attachment-dialog
  968. :visible="dialogMultiVisible"
  969. :attachments="attachments"
  970. @close="dialogMultiVisible = false"
  971. ></multi-attachment-dialog>
  972. <div slot="footer" class="dialog-footer">
  973. <el-button type="primary" @click="handleClose">关闭</el-button>
  974. </div>
  975. </el-dialog>
  976. </template>
  977. <script>
  978. import RegionSelector from '@/views/costAudit/projectInfo/auditProjectManage/annualReviewPlan/RegionSelector.vue'
  979. import CatalogCascader from '@/views/costAudit/projectInfo/auditProjectManage/annualReviewPlan/CatalogCascader.vue'
  980. import { getAllUnitList } from '@/api/auditEntityManage'
  981. import { getDefaultDem, getOrgListByDemId } from '@/api/annualReviewPlan'
  982. import { getAllUserList } from '@/api/uc'
  983. import { dictMixin } from '@/mixins/useDict'
  984. import {
  985. getProjectInformationInfo,
  986. getTaskRequirementList,
  987. sendMessage,
  988. } from '@/api/auditTaskProcessing'
  989. import { getSurveyList } from '@/api/audit/survey'
  990. import {
  991. getCostProjectDocumentPageList,
  992. updateFeedback,
  993. downDocument,
  994. } from '@/api/taskCustomizedRelease.js'
  995. import { getCostProjectDocumentFile } from '@/api/auditReviewDocManage.js'
  996. import CostAuditDialog from '@/components/costAudit/CostAuditDialog.vue'
  997. import TemplatePreviewEdit from '@/components/costAudit/TemplatePreviewEdit.vue'
  998. import MultiAttachmentDialog from '@/components/costAudit/MultiAttachmentDialog'
  999. import { uploadFile } from '@/api/file'
  1000. import SurveyFormDialog from '@/views/EntDeclaration/auditTaskManagement/components/SurveyFormDialog.vue'
  1001. import FixedTableDialog from '@/views/EntDeclaration/auditTaskManagement/components/FixedTableDialog.vue'
  1002. import DynamicTableDialog from '@/views/EntDeclaration/auditTaskManagement/components/DynamicTableDialog.vue'
  1003. import {
  1004. getSingleRecordSurveyList,
  1005. getSurveyDetail,
  1006. getDynamicTableData,
  1007. downloadTemplate,
  1008. } from '@/api/audit/survey'
  1009. import { getListBySurveyFdTemplateIdAndVersion } from '@/api/costSurveyTemplateHeaders'
  1010. export default {
  1011. name: 'TaskInfo',
  1012. components: {
  1013. RegionSelector,
  1014. CatalogCascader,
  1015. CostAuditDialog,
  1016. TemplatePreviewEdit,
  1017. MultiAttachmentDialog,
  1018. SurveyFormDialog,
  1019. FixedTableDialog,
  1020. DynamicTableDialog,
  1021. },
  1022. mixins: [dictMixin],
  1023. props: {
  1024. visible: {
  1025. type: Boolean,
  1026. default: false,
  1027. },
  1028. taskData: {
  1029. type: Object,
  1030. default: () => ({}),
  1031. },
  1032. },
  1033. data() {
  1034. return {
  1035. dialogVisible: false,
  1036. activeTab: 'projectInfo',
  1037. isViewMode: true, // 只读模式,始终为true
  1038. currentTaskInfo: null, // 当前任务信息(包含projectId和taskId)
  1039. loading: false, // 数据加载状态
  1040. // 各个标签页的加载状态,避免重复加载
  1041. tabLoadedStatus: {
  1042. projectInfo: false,
  1043. auditDocument: false,
  1044. dataRequirements: false,
  1045. costSurvey: false,
  1046. auditOpinion: false,
  1047. messageNotice: false,
  1048. },
  1049. // 成本调查表分页相关
  1050. costSurveyPagination: {
  1051. currentPage: 1,
  1052. pageSize: 10,
  1053. total: 0,
  1054. },
  1055. // 消息通知分页相关
  1056. messageNoticePagination: {
  1057. currentPage: 1,
  1058. pageSize: 10,
  1059. total: 0,
  1060. },
  1061. auditDocumentPagination: {
  1062. page: 1,
  1063. pageSize: 50,
  1064. total: 0,
  1065. },
  1066. unitList: [],
  1067. OrgList: [],
  1068. userList: [],
  1069. dictData: {
  1070. projectProposal: [], // 立项来源
  1071. auditType: [], // 监审形式
  1072. attributionYear: [], // 归属年度
  1073. materialType: [], // 资料类别
  1074. materialCategory: [], // 资料类别
  1075. formatAsk: [], // 资料类型
  1076. clshzt: [], // 是否通过
  1077. },
  1078. formData: {
  1079. basicInfo: {
  1080. projectName: '',
  1081. catalogId: '',
  1082. areaCode: '',
  1083. auditUnitId: [],
  1084. auditUnitName: '',
  1085. orgId: '',
  1086. orgName: '',
  1087. projectYear: '',
  1088. sourceType: '',
  1089. sourceTypeText: '',
  1090. auditType: '',
  1091. auditTypeText: '',
  1092. auditPeriod: '',
  1093. auditPeriodArray: [{ value: '' }],
  1094. needHearing: 1,
  1095. isEmergency: 1,
  1096. establishmentReason: '',
  1097. projectMembers: '',
  1098. auditGroupName: '',
  1099. leaderId: [],
  1100. auditTeamMembersText: '',
  1101. expertStr: '',
  1102. plannedAuditStartDate: '',
  1103. plannedAuditEndDate: '',
  1104. },
  1105. auditDocument: [],
  1106. dataRequirements: [],
  1107. costSurveyData: [],
  1108. auditOpinion: {
  1109. basicFinancialInfo: '',
  1110. priceStandard: '',
  1111. costComposition: '',
  1112. preliminaryOpinion: '',
  1113. enterpriseFeedback: '',
  1114. fileList: [],
  1115. },
  1116. messageNotice: [],
  1117. },
  1118. showAuditOpinion: true,
  1119. currentNode: '',
  1120. documentDialogVisible: false,
  1121. documentDialogTitle: '查看监审通知书',
  1122. dialogWidth: '80%',
  1123. fileUrl: '',
  1124. costDocumentTemplateFiles: [],
  1125. document: {},
  1126. activeDocTab: 'preview', // 当前标签页,preview:预览,edit:修改
  1127. dialogMultiVisible: false,
  1128. attachments: [],
  1129. // 报送资料预置模版查看相关
  1130. currentTemplateRow: null,
  1131. surveyFormDialogVisible: false,
  1132. fixedTableDialogVisible: false,
  1133. dynamicTableDialogVisible: false,
  1134. // 视图上下文:'costSurvey' 或 'dataRequirements'
  1135. viewContext: 'dataRequirements',
  1136. formFields: [],
  1137. surveyDetailData: {},
  1138. tableItems: [],
  1139. auditPeriods: [],
  1140. dynamicTableData: [],
  1141. dynamicDialogKey: 0,
  1142. fixedHeaders: null,
  1143. fixedFields: '',
  1144. fixedFieldids: '',
  1145. }
  1146. },
  1147. computed: {
  1148. // 从传入 taskData/formData 中派生 catalogId/auditedUnitId,供成本调查表接口使用
  1149. catalogId() {
  1150. return (
  1151. (this.taskData &&
  1152. (this.taskData.catalogId || this.taskData.catalogid)) ||
  1153. (this.formData &&
  1154. this.formData.basicInfo &&
  1155. (this.formData.basicInfo.catalogId ||
  1156. this.formData.basicInfo.catalogid)) ||
  1157. ''
  1158. )
  1159. },
  1160. auditedUnitId() {
  1161. return (
  1162. (this.taskData &&
  1163. (this.taskData.auditedUnitId || this.taskData.auditedunitid)) ||
  1164. ''
  1165. )
  1166. },
  1167. groupedDataRequirements() {
  1168. if (
  1169. !this.formData.dataRequirements ||
  1170. !Array.isArray(this.formData.dataRequirements) ||
  1171. this.formData.dataRequirements.length === 0
  1172. ) {
  1173. return []
  1174. }
  1175. const groups = {}
  1176. this.formData.dataRequirements.forEach((item, index) => {
  1177. const categoryKey = item.informationType || 'other'
  1178. let categoryName = item.informationTypeName || categoryKey
  1179. if (!groups[categoryKey]) {
  1180. groups[categoryKey] = {
  1181. categoryKey,
  1182. categoryName: categoryName,
  1183. items: [],
  1184. }
  1185. }
  1186. const itemWithSeq = {
  1187. ...item,
  1188. index: index + 1,
  1189. seq: groups[categoryKey].items.length + 1,
  1190. }
  1191. groups[categoryKey].items.push(itemWithSeq)
  1192. })
  1193. const result = []
  1194. Object.keys(groups).forEach((categoryKey) => {
  1195. const group = groups[categoryKey]
  1196. result.push({
  1197. isCategoryHeader: true,
  1198. categoryName: group.categoryName,
  1199. categoryKey: group.categoryKey,
  1200. })
  1201. group.items.forEach((item) => {
  1202. result.push({
  1203. ...item,
  1204. isCategoryHeader: false,
  1205. })
  1206. })
  1207. })
  1208. return result
  1209. },
  1210. // 分页后的成本调查表数据
  1211. paginatedCostSurveyData() {
  1212. const start =
  1213. (this.costSurveyPagination.currentPage - 1) *
  1214. this.costSurveyPagination.pageSize
  1215. const end = start + this.costSurveyPagination.pageSize
  1216. return this.formData.costSurveyData.slice(start, end)
  1217. },
  1218. },
  1219. watch: {
  1220. visible: {
  1221. handler(val) {
  1222. this.dialogVisible = val
  1223. if (val) {
  1224. this.loadData()
  1225. // 若默认即在成本调查表页且尚未加载,则加载
  1226. if (
  1227. this.activeTab === 'costSurvey' &&
  1228. !this.tabLoadedStatus.costSurvey
  1229. ) {
  1230. this.loadCostSurvey()
  1231. }
  1232. }
  1233. },
  1234. immediate: true,
  1235. },
  1236. },
  1237. mounted() {
  1238. this.initData()
  1239. },
  1240. methods: {
  1241. // 报送资料-查看(同 taskFillIn)
  1242. handleFileView(row) {
  1243. console.log(row, '这一行数据')
  1244. try {
  1245. const filePath =
  1246. row?.filePath ||
  1247. row?.filepath ||
  1248. row?.fileUrl ||
  1249. row?.url ||
  1250. row?.path ||
  1251. ''
  1252. if (!filePath) {
  1253. this.$message &&
  1254. this.$message.warning &&
  1255. this.$message.warning('未找到可预览的文件路径')
  1256. return
  1257. }
  1258. const encodedUrl = encodeURIComponent(
  1259. Base64.encode((window.context && window.context.form) + filePath)
  1260. )
  1261. window.open(`${host}:8012/onlinePreview?url=${encodedUrl}`)
  1262. // 兼容保留:通知父组件
  1263. this.$emit('handleFileView', row)
  1264. } catch (e) {
  1265. console.error('文件预览失败: ', e)
  1266. // this.$message &&
  1267. // this.$message.error &&
  1268. // this.$message.error('文件预览失败')
  1269. }
  1270. },
  1271. // 报送资料-下载(同 taskFillIn)
  1272. handleFileDownload(row) {
  1273. if (!row || !row.fileUrl) {
  1274. this.$message &&
  1275. this.$message.warning &&
  1276. this.$message.warning('该资料暂无文件可下载')
  1277. return
  1278. }
  1279. this.downloadByFetch(row.fileUrl, row.informationName || '下载文件')
  1280. },
  1281. // 模版下载(对齐 CostSurveyTab,兼容两种上下文)
  1282. async handleTemplateDownload(row) {
  1283. try {
  1284. const loading = this.$loading({
  1285. lock: true,
  1286. text: '模板下载中...',
  1287. spinner: 'el-icon-loading',
  1288. background: 'rgba(0, 0, 0, 0.7)',
  1289. })
  1290. const surveyTemplateId = this.getSurveyTemplateId(row)
  1291. const versionId =
  1292. (row && (row.versionId || row.version || row.templateVersionId)) ||
  1293. ''
  1294. const params = {
  1295. type: this.viewContext === 'costSurvey' ? 1 : 2,
  1296. }
  1297. if (surveyTemplateId) params.surveyTemplateId = surveyTemplateId
  1298. if (versionId) params.versionId = versionId
  1299. const taskId = (row && (row.taskId || row.taskID)) || this.taskId
  1300. if (taskId) params.taskId = taskId
  1301. const res = await downloadTemplate(params)
  1302. loading.close()
  1303. const headers = (res && res.headers) || {}
  1304. const contentDisposition =
  1305. headers['content-disposition'] || headers['Content-Disposition']
  1306. let fileName =
  1307. this.extractFileNameFromHeader(contentDisposition) ||
  1308. `${row.informationName || row.name || '模板文件'}.xlsx`
  1309. if (!/\.[a-zA-Z0-9]+$/.test(fileName)) fileName += '.xlsx'
  1310. const blobData = (res && res.data) || res
  1311. const blob =
  1312. blobData instanceof Blob ? blobData : new Blob([blobData])
  1313. const url = window.URL.createObjectURL(blob)
  1314. const link = document.createElement('a')
  1315. link.style.display = 'none'
  1316. link.href = url
  1317. link.download = fileName
  1318. document.body.appendChild(link)
  1319. link.click()
  1320. document.body.removeChild(link)
  1321. window.URL.revokeObjectURL(url)
  1322. this.$message &&
  1323. this.$message.success &&
  1324. this.$message.success('开始下载文件')
  1325. } catch (error) {
  1326. console.error('模板下载失败:', error)
  1327. this.$message &&
  1328. this.$message.error &&
  1329. this.$message.error(error.message || '模板下载失败,请稍后重试')
  1330. }
  1331. },
  1332. extractFileNameFromHeader(contentDisposition) {
  1333. if (!contentDisposition) return ''
  1334. const match = /filename[^;=\n]*=((['"])?.*?\2|[^;\n]*)/i.exec(
  1335. contentDisposition
  1336. )
  1337. if (match && match[1]) {
  1338. try {
  1339. return decodeURIComponent(match[1].replace(/['"]/g, ''))
  1340. } catch (e) {
  1341. return match[1].replace(/['"]/g, '')
  1342. }
  1343. }
  1344. return ''
  1345. },
  1346. async downloadByFetch(rawUrl, fallbackName) {
  1347. const url = this.normalizeUrl(rawUrl)
  1348. let loading
  1349. try {
  1350. loading = this.$baseLoading
  1351. ? this.$baseLoading(1, '文件下载中...')
  1352. : this.$loading({
  1353. lock: true,
  1354. text: '文件下载中...',
  1355. spinner: 'el-icon-loading',
  1356. background: 'rgba(0,0,0,0.7)',
  1357. })
  1358. const res = await fetch(url, { method: 'GET' })
  1359. if (!res.ok) throw new Error('下载失败')
  1360. const blob = await res.blob()
  1361. let fileName =
  1362. this.extractFileName(res.headers.get('content-disposition')) ||
  1363. fallbackName ||
  1364. '下载文件'
  1365. if (!/\.[a-zA-Z0-9]+$/.test(fileName)) {
  1366. const extFromUrl = (
  1367. url.split('?')[0].split('#')[0].split('.').pop() || ''
  1368. ).toLowerCase()
  1369. fileName = extFromUrl ? `${fileName}.${extFromUrl}` : fileName
  1370. }
  1371. const objectUrl = window.URL.createObjectURL(blob)
  1372. const link = document.createElement('a')
  1373. link.style.display = 'none'
  1374. link.href = objectUrl
  1375. link.download = fileName
  1376. document.body.appendChild(link)
  1377. link.click()
  1378. document.body.removeChild(link)
  1379. window.URL.revokeObjectURL(objectUrl)
  1380. this.$message &&
  1381. this.$message.success &&
  1382. this.$message.success('开始下载文件')
  1383. } catch (e) {
  1384. console.log(e.message || '文件下载失败')
  1385. // this.$message &&
  1386. // this.$message.error &&
  1387. // this.$message.error(e.message || '文件下载失败')
  1388. } finally {
  1389. if (loading && loading.close) loading.close()
  1390. }
  1391. },
  1392. normalizeUrl(u) {
  1393. if (!u) return ''
  1394. if (/^https?:\/\//i.test(u)) return u
  1395. const base = (window.context && window.context.form) || ''
  1396. if (!base) return u
  1397. if (u.startsWith('/')) return base + u
  1398. return base.replace(/\/$/, '') + '/' + u
  1399. },
  1400. extractFileName(contentDisposition) {
  1401. if (!contentDisposition) return ''
  1402. const match = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/i.exec(
  1403. contentDisposition
  1404. )
  1405. if (match && match[1]) {
  1406. try {
  1407. return decodeURIComponent(match[1].replace(/['"]/g, ''))
  1408. } catch (e) {
  1409. return match[1].replace(/['"]/g, '')
  1410. }
  1411. }
  1412. return ''
  1413. },
  1414. // 初始化数据
  1415. initData() {
  1416. this.getAllUnitList()
  1417. this.getDefaultDem()
  1418. this.getUser()
  1419. },
  1420. // 获取所有单位列表
  1421. getAllUnitList() {
  1422. getAllUnitList()
  1423. .then((res) => {
  1424. this.unitList = res.value || []
  1425. })
  1426. .catch(() => {})
  1427. },
  1428. // 获取默认维度
  1429. getDefaultDem() {
  1430. getDefaultDem()
  1431. .then((res) => {
  1432. if (res && res.code === 200) {
  1433. const demId = res.value ? res.value.id : null
  1434. if (demId) {
  1435. this.getOrgListByDemId(demId)
  1436. }
  1437. }
  1438. })
  1439. .catch(() => {})
  1440. },
  1441. // 根据维度ID获取组织列表
  1442. getOrgListByDemId(demId) {
  1443. getOrgListByDemId({ demId })
  1444. .then((res) => {
  1445. if (res && res.code === 200) {
  1446. this.OrgList = res.value || []
  1447. }
  1448. })
  1449. .catch(() => {})
  1450. },
  1451. // 获取用户列表
  1452. getUser() {
  1453. getAllUserList()
  1454. .then((res) => {
  1455. this.userList = res.value || []
  1456. })
  1457. .catch(() => {})
  1458. },
  1459. // 对外暴露的打开方法
  1460. open(data, type) {
  1461. console.log('data', data)
  1462. console.log('type', type)
  1463. this.dialogVisible = true
  1464. this.activeTab = 'projectInfo'
  1465. // 重置加载状态
  1466. Object.keys(this.tabLoadedStatus).forEach((key) => {
  1467. this.tabLoadedStatus[key] = false
  1468. })
  1469. // 重置分页状态
  1470. this.costSurveyPagination.currentPage = 1
  1471. this.costSurveyPagination.total = 0
  1472. this.messageNoticePagination.currentPage = 1
  1473. this.messageNoticePagination.total = 0
  1474. if (data) {
  1475. // 保存任务信息
  1476. this.currentTaskInfo = {
  1477. projectId: type === 'chengben' ? data.projectId : data.id,
  1478. taskId:
  1479. type === 'chengben'
  1480. ? data.taskId
  1481. : data.userTask?.id || data.taskId,
  1482. currentNode: data.currentNode,
  1483. ...data,
  1484. }
  1485. // 根据状态决定是否显示监审意见标签页
  1486. // this.showAuditOpinion = data.currentNode !== 'jtsy'
  1487. this.currentNode = data.currentNode
  1488. // 立即加载立项信息
  1489. this.loadProjectInfo()
  1490. }
  1491. },
  1492. // 标签页切换事件
  1493. handleTabClick(tab) {
  1494. const tabName = tab.name
  1495. // 如果该tab还未加载过数据,则加载
  1496. if (!this.tabLoadedStatus[tabName]) {
  1497. this.loadTabData(tabName)
  1498. }
  1499. },
  1500. // 根据tab名称加载对应的数据
  1501. async loadTabData(tabName) {
  1502. switch (tabName) {
  1503. case 'projectInfo':
  1504. await this.loadProjectInfo()
  1505. break
  1506. case 'auditDocument':
  1507. await this.loadAuditDocument()
  1508. break
  1509. case 'dataRequirements':
  1510. await this.loadDataRequirements()
  1511. break
  1512. case 'costSurvey':
  1513. await this.loadCostSurvey()
  1514. break
  1515. case 'auditOpinion':
  1516. await this.loadAuditOpinion()
  1517. break
  1518. case 'messageNotice':
  1519. await this.loadMessageNotice()
  1520. break
  1521. }
  1522. },
  1523. // 加载立项信息
  1524. async loadProjectInfo() {
  1525. console.log(this.currentTaskInfo.projectId)
  1526. if (!this.currentTaskInfo?.projectId) {
  1527. return
  1528. }
  1529. try {
  1530. this.loading = true
  1531. const res = await getProjectInformationInfo(
  1532. this.currentTaskInfo.projectId
  1533. )
  1534. if (res && res.code === 200 && res.value) {
  1535. const data = res.value
  1536. // 处理监审期间数组
  1537. let auditPeriodArray = [{ value: '' }]
  1538. if (data.auditPeriod && typeof data.auditPeriod === 'string') {
  1539. const periods = data.auditPeriod.split(',').filter(Boolean)
  1540. auditPeriodArray = periods.map((p) => ({ value: p }))
  1541. }
  1542. // 处理被监审单位ID(可能是字符串或数组)
  1543. let auditUnitId = []
  1544. if (data.auditUnitId) {
  1545. if (typeof data.auditUnitId === 'string') {
  1546. // 如果是字符串,分割成数组
  1547. auditUnitId = data.auditUnitId.split(',').filter(Boolean)
  1548. } else if (Array.isArray(data.auditUnitId)) {
  1549. // 如果已经是数组,直接使用
  1550. auditUnitId = data.auditUnitId
  1551. }
  1552. }
  1553. // 处理监审任务组成员(可能是字符串或数组)
  1554. let auditTeamMembers = []
  1555. if (data.projectMembers) {
  1556. if (typeof data.projectMembers === 'string') {
  1557. auditTeamMembers = data.projectMembers
  1558. .split(',')
  1559. .filter(Boolean)
  1560. } else if (Array.isArray(data.projectMembers)) {
  1561. auditTeamMembers = data.projectMembers
  1562. }
  1563. }
  1564. // 处理监审任务组成员(可能是字符串或数组)
  1565. let leaderId = []
  1566. if (data.leaderId) {
  1567. if (typeof data.leaderId === 'string') {
  1568. leaderId = data.leaderId
  1569. } else if (Array.isArray(data.leaderId)) {
  1570. leaderId = data.leaderId[0] || ''
  1571. }
  1572. }
  1573. this.formData.basicInfo = {
  1574. projectName: data.projectName || '',
  1575. catalogId: data.catalogId || '',
  1576. areaCode: data.areaCode || '',
  1577. auditUnitId: auditUnitId,
  1578. auditUnitName: data.auditUnitName || '',
  1579. orgId: data.orgId || '',
  1580. orgName: data.orgName || '',
  1581. projectYear: data.projectYear || '',
  1582. sourceType: data.sourceType || '',
  1583. auditType: data.auditType || '',
  1584. auditPeriod: data.auditPeriod || '',
  1585. auditPeriodArray: auditPeriodArray,
  1586. needHearing:
  1587. data.needHearing !== undefined ? data.needHearing : 1,
  1588. isEmergency:
  1589. data.isEmergency !== undefined ? data.isEmergency : 1,
  1590. establishmentReason: data.establishmentReason || '',
  1591. // projectMembers: data.projectMembers || '',
  1592. projectMembers: auditTeamMembers,
  1593. leaderId: leaderId,
  1594. expertStr: data.expertStr || '',
  1595. plannedAuditStartDate: data.plannedAuditStartDate || '',
  1596. plannedAuditEndDate: data.plannedAuditEndDate || '',
  1597. }
  1598. // 打印日志以便调试
  1599. console.log('立项信息加载完成:', {
  1600. areaCode: this.formData.basicInfo.areaCode,
  1601. auditUnitId: this.formData.basicInfo.auditUnitId,
  1602. auditUnitName: this.formData.basicInfo.auditUnitName,
  1603. })
  1604. this.tabLoadedStatus.projectInfo = true
  1605. }
  1606. } catch (error) {
  1607. console.error('加载立项信息失败:', error)
  1608. // this.$message.error('加载立项信息失败')
  1609. } finally {
  1610. this.loading = false
  1611. }
  1612. },
  1613. // 报送资料预置模版-查看:单记录/固定表/动态表只读查看并回显
  1614. async handleOnlineSubmission(row) {
  1615. if (!row) return
  1616. this.currentTemplateRow = row
  1617. this.surveyDetailData = {}
  1618. const t = String(row.templateType || row.templatetype || '').trim()
  1619. if (t === '1') {
  1620. if (row.uploadId || row.id) {
  1621. try {
  1622. const params = {
  1623. uploadId: row.uploadId || row.id,
  1624. auditedUnitId: this.auditedUnitId,
  1625. type: this.viewContext === 'costSurvey' ? 1 : 2,
  1626. }
  1627. const res = await getSurveyDetail(params)
  1628. if (res && res.code === 200 && res.value) {
  1629. const detailData = {}
  1630. if (Array.isArray(res.value)) {
  1631. res.value.forEach((item) => {
  1632. if (item.rowid && item.rvalue !== undefined) {
  1633. detailData[item.rowid] = item.rvalue
  1634. }
  1635. })
  1636. } else if (res.value && typeof res.value === 'object') {
  1637. Object.assign(detailData, res.value)
  1638. }
  1639. this.surveyDetailData = detailData
  1640. }
  1641. } catch (err) {
  1642. console.error('获取单记录详情失败', err)
  1643. }
  1644. }
  1645. await this.initFormFields()
  1646. } else if (t === '2') {
  1647. await this.initFixedTableData()
  1648. } else if (t === '3') {
  1649. await this.initDynamicTableData()
  1650. }
  1651. },
  1652. handleViewTemplate(row) {
  1653. this.currentTemplateRow = row || null
  1654. const t = String(
  1655. (row && (row.templateType || row.templatetype)) || ''
  1656. ).trim()
  1657. if (t === '1' || t === '2' || t === '3') {
  1658. // 报送资料查看
  1659. this.viewContext = 'dataRequirements'
  1660. this.handleOnlineSubmission(row)
  1661. } else {
  1662. this.$message &&
  1663. this.$message.warning &&
  1664. this.$message.warning('未知的模板类型,无法打开预置模板')
  1665. }
  1666. },
  1667. handleCostSurveyView(row) {
  1668. // 成本调查表查看
  1669. this.viewContext = 'costSurvey'
  1670. const templateRow = {
  1671. ...row,
  1672. uploadId: row.id,
  1673. surveyTemplateId: row.surveyTemplateId || row.templateId || '',
  1674. catalogId: row.catalogId || this.catalogId || '',
  1675. auditedUnitId: row.auditedUnitId || this.auditedUnitId || '',
  1676. informationName: row.name,
  1677. }
  1678. this.handleOnlineSubmission(templateRow)
  1679. },
  1680. async initDynamicTableData() {
  1681. try {
  1682. const uploadId =
  1683. (this.currentTemplateRow &&
  1684. (this.currentTemplateRow.uploadId ||
  1685. this.currentTemplateRow.id)) ||
  1686. ''
  1687. const auditedUnitId = this.auditedUnitId || ''
  1688. const catalogId =
  1689. (this.currentTemplateRow && this.currentTemplateRow.catalogId) || ''
  1690. const surveyTemplateId = this.getSurveyTemplateId(
  1691. this.currentTemplateRow
  1692. )
  1693. const params = {
  1694. uploadId,
  1695. auditedUnitId,
  1696. catalogId,
  1697. surveyTemplateId,
  1698. type: this.viewContext === 'costSurvey' ? 1 : 2,
  1699. }
  1700. const res = await getDynamicTableData(params)
  1701. if (res && res.code === 200) {
  1702. const records = res.value?.records || res.value || []
  1703. this.dynamicTableData = Array.isArray(records) ? records : []
  1704. } else {
  1705. this.dynamicTableData =
  1706. this.currentTemplateRow?.dynamicTableData || []
  1707. }
  1708. if (
  1709. this.currentTemplateRow &&
  1710. this.currentTemplateRow.tableItems &&
  1711. this.currentTemplateRow.tableItems.length > 0
  1712. ) {
  1713. this.tableItems = this.currentTemplateRow.tableItems
  1714. } else {
  1715. this.tableItems = this.getMockTableItems()
  1716. }
  1717. this.dynamicTableDialogVisible = true
  1718. } catch (error) {
  1719. console.error('获取动态表数据失败', error)
  1720. this.dynamicTableData =
  1721. this.currentTemplateRow?.dynamicTableData || []
  1722. this.tableItems =
  1723. this.currentTemplateRow?.tableItems || this.getMockTableItems()
  1724. this.dynamicTableDialogVisible = true
  1725. }
  1726. },
  1727. async initFormFields() {
  1728. if (
  1729. this.currentTemplateRow &&
  1730. this.getSurveyTemplateId(this.currentTemplateRow)
  1731. ) {
  1732. try {
  1733. const params = {
  1734. surveyTemplateId: this.getSurveyTemplateId(
  1735. this.currentTemplateRow
  1736. ),
  1737. type: this.viewContext === 'costSurvey' ? 1 : 2,
  1738. }
  1739. const res = await getListBySurveyFdTemplateIdAndVersion(params)
  1740. if (res && res.code === 200) {
  1741. let mapped = []
  1742. if (Array.isArray(res.value)) {
  1743. mapped = res.value
  1744. .map((item, index) =>
  1745. this.mapApiFieldToFormField(item, index)
  1746. )
  1747. .filter(Boolean)
  1748. } else if (res.value && typeof res.value === 'object') {
  1749. const { fixedFields, fixedFieldids } = res.value
  1750. if (fixedFields && fixedFieldids) {
  1751. const labels = fixedFields.split(',').map((i) => i.trim())
  1752. const ids = fixedFieldids.split(',').map((i) => i.trim())
  1753. mapped = labels.map((label, index) => ({
  1754. prop: ids[index] || `field_${index}`,
  1755. label,
  1756. type: 'input',
  1757. colSpan: 12,
  1758. placeholder: `请输入${label}`,
  1759. rules: [],
  1760. defaultValue: '',
  1761. disabled: false,
  1762. clearable: true,
  1763. multiple: false,
  1764. required: false,
  1765. }))
  1766. }
  1767. }
  1768. this.formFields =
  1769. mapped.length > 0 ? mapped : this.getMockFormFields()
  1770. } else {
  1771. this.formFields = this.getMockFormFields()
  1772. }
  1773. this.surveyFormDialogVisible = true
  1774. } catch (err) {
  1775. console.error('获取单记录表单字段配置失败', err)
  1776. this.formFields = this.getMockFormFields()
  1777. this.surveyFormDialogVisible = true
  1778. }
  1779. } else {
  1780. this.formFields = this.getMockFormFields()
  1781. this.surveyFormDialogVisible = true
  1782. }
  1783. },
  1784. async initFixedTableData() {
  1785. if (
  1786. this.currentTemplateRow &&
  1787. this.getSurveyTemplateId(this.currentTemplateRow)
  1788. ) {
  1789. try {
  1790. const params = {
  1791. surveyTemplateId: this.getSurveyTemplateId(
  1792. this.currentTemplateRow
  1793. ),
  1794. type: this.viewContext === 'costSurvey' ? 1 : 2,
  1795. }
  1796. const res = await getSingleRecordSurveyList(params)
  1797. if (res && res.code === 200 && res.value) {
  1798. const { itemlist } = res.value
  1799. if (itemlist && Array.isArray(itemlist) && itemlist.length > 0) {
  1800. this.tableItems = itemlist.map((item) => ({
  1801. id: item.id || item.itemId || '',
  1802. rowid: item.rowid || item.id || item.itemId || '',
  1803. seq: item.序号,
  1804. itemName: item.项目 || '',
  1805. unit: item.unit || '',
  1806. isCategory: item.isCategory || false,
  1807. categorySeq: item.categorySeq || '',
  1808. categoryId: item.categoryId || '',
  1809. parentid:
  1810. item.parentid !== undefined
  1811. ? item.parentid
  1812. : item.parentId !== undefined
  1813. ? item.parentId
  1814. : '-1',
  1815. validateRules: item.validateRules || {},
  1816. linkageRules: item.linkageRules || {},
  1817. children: item.children || [],
  1818. ...item,
  1819. }))
  1820. // 如果接口返回 fixedFields/fixedFieldids,一并保存,供弹窗表头渲染
  1821. if (res.value.fixedFields && res.value.fixedFieldids) {
  1822. this.fixedFields = res.value.fixedFields
  1823. this.fixedFieldids = res.value.fixedFieldids
  1824. }
  1825. } else {
  1826. this.tableItems = this.getMockTableItems()
  1827. }
  1828. } else {
  1829. this.tableItems = this.getMockTableItems()
  1830. }
  1831. } catch (err) {
  1832. console.error('获取固定表配置失败', err)
  1833. this.tableItems = this.getMockTableItems()
  1834. }
  1835. } else if (
  1836. this.currentTemplateRow &&
  1837. this.currentTemplateRow.tableItems
  1838. ) {
  1839. this.tableItems = this.currentTemplateRow.tableItems
  1840. } else {
  1841. this.tableItems = this.getMockTableItems()
  1842. }
  1843. const currentYear = new Date().getFullYear()
  1844. this.auditPeriods = [
  1845. String(currentYear - 2),
  1846. String(currentYear - 1),
  1847. String(currentYear),
  1848. ]
  1849. try {
  1850. const headerRes = await getListBySurveyFdTemplateIdAndVersion({
  1851. surveyTemplateId: this.getSurveyTemplateId(this.currentTemplateRow),
  1852. type: this.viewContext === 'costSurvey' ? 1 : 2,
  1853. })
  1854. if (headerRes && headerRes.code === 200) {
  1855. this.fixedHeaders = headerRes.value || null
  1856. } else {
  1857. this.fixedHeaders = null
  1858. }
  1859. } catch (err) {
  1860. console.error('获取固定表表头配置失败', err)
  1861. this.fixedHeaders = null
  1862. }
  1863. this.fixedTableDialogVisible = true
  1864. },
  1865. getSurveyTemplateId(row) {
  1866. return (
  1867. row?.surveyTemplateId ||
  1868. row?.templateId ||
  1869. row?.surveyTemplateID ||
  1870. row?.templateID ||
  1871. ''
  1872. )
  1873. },
  1874. getMockFormFields() {
  1875. return []
  1876. },
  1877. getMockTableItems() {
  1878. return []
  1879. },
  1880. mapApiFieldToFormField(item, index = 0) {
  1881. if (!item) return null
  1882. const getVal = (keys, fallback) => {
  1883. for (const key of keys) {
  1884. if (
  1885. key &&
  1886. item[key] !== undefined &&
  1887. item[key] !== null &&
  1888. item[key] !== ''
  1889. ) {
  1890. return item[key]
  1891. }
  1892. }
  1893. return fallback
  1894. }
  1895. const prop =
  1896. getVal(
  1897. [
  1898. 'fieldName',
  1899. 'field_name',
  1900. 'columnName',
  1901. 'column_name',
  1902. 'fieldCode',
  1903. ],
  1904. undefined
  1905. ) || `field_${index}`
  1906. const label =
  1907. getVal(
  1908. [
  1909. 'columnComment',
  1910. 'column_comment',
  1911. 'fieldCname',
  1912. 'field_cname',
  1913. 'fieldLabel',
  1914. 'field_label',
  1915. ],
  1916. prop
  1917. ) || prop
  1918. return {
  1919. prop,
  1920. label,
  1921. type: 'input',
  1922. colSpan: 12,
  1923. placeholder: `请输入${label}`,
  1924. rules: [],
  1925. defaultValue: '',
  1926. disabled: false,
  1927. clearable: true,
  1928. multiple: false,
  1929. required: false,
  1930. }
  1931. },
  1932. // 加载监审文书
  1933. async loadAuditDocument() {
  1934. try {
  1935. this.loading = true
  1936. //调用监审文书接口
  1937. this.formData.auditDocument = []
  1938. this.tabLoadedStatus.auditDocument = true
  1939. getCostProjectDocumentPageList({
  1940. pageNum: this.auditDocumentPagination.page,
  1941. pageSize: this.auditDocumentPagination.pageSize,
  1942. projectId: this.currentTaskInfo.projectId,
  1943. // documentName: '',
  1944. permissionType: 1,
  1945. }).then((res) => {
  1946. let records = res.value.value.records
  1947. this.formData.auditDocument = records.filter((item) => {
  1948. const excludeTypes = [
  1949. '成本监审工作底稿-送达回证',
  1950. '成本监审提取资料登记表-送达回证',
  1951. '成本审核初步意见表1-送达回证',
  1952. '成本监审集体审议记录-送达回证',
  1953. ]
  1954. return !excludeTypes.includes(item.documentName)
  1955. })
  1956. this.auditDocumentPagination.total = res.value.value.total
  1957. })
  1958. } catch (error) {
  1959. console.error('加载监审文书失败:', error)
  1960. // this.$message.error('加载监审文书失败')
  1961. } finally {
  1962. this.loading = false
  1963. }
  1964. },
  1965. handleTemplateChange() {
  1966. let data = this.documentData.documentTypes.find(
  1967. (item) => item.id === this.document.documentId
  1968. )
  1969. this.fileUrl = data.fileUrl
  1970. },
  1971. // 查看监审文书
  1972. handleDocView(row) {
  1973. this.document = {
  1974. ...row,
  1975. }
  1976. // 从API中获取文件URL
  1977. downDocument({
  1978. id: row.id,
  1979. }).then((res) => {
  1980. if (res.state) {
  1981. // this.fileUrl = res.value || ''
  1982. this.handleViewScan(res.value || '')
  1983. } else {
  1984. this.$message.error('获取文件URL失败')
  1985. }
  1986. })
  1987. // this.handleTemplateChange()
  1988. // this.documentDialogVisible = true
  1989. // getCostProjectDocumentFile({
  1990. // id: row.id,
  1991. // }).then((res) => {
  1992. // this.costDocumentTemplateFiles = res.value || []
  1993. // })
  1994. },
  1995. // 关闭监审文书查看弹窗
  1996. handleDocCancel() {
  1997. this.documentDialogVisible = false
  1998. // 重置相关数据状态,避免再次打开时显示上一次的数据
  1999. this.document = {}
  2000. this.fileUrl = ''
  2001. this.costDocumentTemplateFiles = []
  2002. },
  2003. // 查看文件
  2004. handleViewScan(fileUrl) {
  2005. if (!fileUrl) {
  2006. this.$message.error('暂无文件!')
  2007. return
  2008. }
  2009. let _fileUrl = ''
  2010. if (fileUrl.startsWith('http')) {
  2011. _fileUrl = fileUrl
  2012. } else {
  2013. _fileUrl = window.context.form + fileUrl
  2014. }
  2015. // 对文件URL进行Base64编码
  2016. const encodedUrl = encodeURIComponent(Base64.encode(_fileUrl))
  2017. // 构建 kkFileView 预览URL
  2018. // onlinePreview - 在线预览
  2019. // onlinePreview?type=pdf - 强制使用PDF模式预览
  2020. window.open(`${host}:8012/onlinePreview?url=${encodedUrl}`)
  2021. },
  2022. // 查看反馈
  2023. handleViewFeedback(feedbackDocumentUrl) {
  2024. if (!feedbackDocumentUrl) {
  2025. this.$message.error('暂无文件!')
  2026. return
  2027. }
  2028. // 确保attachments是数组格式
  2029. if (feedbackDocumentUrl) {
  2030. if (typeof feedbackDocumentUrl === 'string') {
  2031. // 如果是逗号分隔的字符串,转换为数组
  2032. this.attachments = feedbackDocumentUrl.split(',')
  2033. } else {
  2034. // 其他情况,包装为数组
  2035. this.attachments = [feedbackDocumentUrl]
  2036. }
  2037. } else {
  2038. this.attachments = []
  2039. }
  2040. // 显示附件弹窗
  2041. this.dialogMultiVisible = true
  2042. },
  2043. // 上传文件
  2044. handleUploadScan(row, type) {
  2045. let loading = null
  2046. // 第一步:创建文件选择器
  2047. const input = document.createElement('input')
  2048. input.type = 'file'
  2049. input.accept = '.pdf,.doc,.docx,.xls,.xlsx,.csv' // 允许的文件类型
  2050. input.onchange = async (event) => {
  2051. const file = event.target.files[0]
  2052. if (!file) return
  2053. try {
  2054. // 校验文件大小(50MB)
  2055. const maxSize = 50 * 1024 * 1024 // 50MB
  2056. if (file.size > maxSize) {
  2057. this.$message.error('文件大小不能超过50MB!')
  2058. return
  2059. }
  2060. // 校验文件格式
  2061. const allowedFormats = [
  2062. '.pdf',
  2063. '.doc',
  2064. '.docx',
  2065. '.xls',
  2066. '.xlsx',
  2067. '.csv',
  2068. ]
  2069. const fileName = file.name.toLowerCase()
  2070. const isValidFormat = allowedFormats.some((format) =>
  2071. fileName.endsWith(format)
  2072. )
  2073. if (!isValidFormat) {
  2074. this.$message.error(
  2075. '只允许上传.pdf,.doc,.docx,.xls,.xlsx,.csv格式的文件!'
  2076. )
  2077. return
  2078. }
  2079. // 显示遮罩层
  2080. loading = this.$baseLoading(1, '文件上传中...')
  2081. // 第三步:创建FormData并上传文件
  2082. const formData = new FormData()
  2083. formData.append('file', file)
  2084. // 先调用上传API
  2085. const uploadRes = await uploadFile('/api/file/v1/upload', formData)
  2086. // 第四步:检查上传结果
  2087. if (!uploadRes || !uploadRes.value) {
  2088. // this.$message.error('文件上传失败!');
  2089. return
  2090. }
  2091. // 第五步:文件上传成功后,再更新数据
  2092. const fileInfo = uploadRes.value
  2093. let feedbackDocumentUrl = row.feedbackDocumentUrl
  2094. if (feedbackDocumentUrl) {
  2095. feedbackDocumentUrl = feedbackDocumentUrl.split(',')
  2096. feedbackDocumentUrl.push(fileInfo?.savePath)
  2097. feedbackDocumentUrl = feedbackDocumentUrl.join(',')
  2098. } else {
  2099. feedbackDocumentUrl = fileInfo?.savePath
  2100. }
  2101. // 创建更新数据对象
  2102. const updateData = {
  2103. id: row.id,
  2104. feedbackDocumentUrl: feedbackDocumentUrl, // 更新扫描件URL
  2105. }
  2106. // 第六步:调用更新API
  2107. await updateFeedback(updateData)
  2108. // 第七步:更新成功,显示提示并刷新
  2109. this.$message.success('文件上传成功并更新数据!')
  2110. this.loadAuditDocument()
  2111. } catch (error) {
  2112. // 错误处理
  2113. this.$message.error('操作失败:' + (error.message || '未知错误'))
  2114. console.error('文件上传失败:', error)
  2115. } finally {
  2116. // 关闭遮罩层
  2117. loading.close()
  2118. }
  2119. }
  2120. // 触发文件选择
  2121. input.click()
  2122. },
  2123. // 下载文档
  2124. handleDownloadDocument(row) {
  2125. // 显示加载状态
  2126. this.$loading({
  2127. lock: true,
  2128. text: '文件下载中...',
  2129. spinner: 'el-icon-loading',
  2130. background: 'rgba(0, 0, 0, 0.7)',
  2131. })
  2132. // 从API中获取文件URL
  2133. downDocument({
  2134. id: row.id,
  2135. })
  2136. .then((res) => {
  2137. // 关闭加载状态
  2138. this.$loading().close()
  2139. // 检查返回结果是否成功
  2140. if (!res || !res.state) {
  2141. this.$message.error(
  2142. `下载失败:${res?.message || '未获取到文件数据'}`
  2143. )
  2144. return
  2145. }
  2146. // 获取文件URL
  2147. const fileUrl = res.value
  2148. if (!fileUrl) {
  2149. this.$message.error('下载失败:未获取到文件URL')
  2150. return
  2151. }
  2152. // 优先从URL中提取文件名
  2153. let fileName = ''
  2154. // 从URL中提取文件名
  2155. const urlParts = fileUrl.split('/')
  2156. let urlFileName = urlParts[urlParts.length - 1]
  2157. // 处理URL可能包含查询参数的情况
  2158. if (urlFileName.includes('?')) {
  2159. urlFileName = urlFileName.split('?')[0]
  2160. }
  2161. // 检查从URL提取的文件名是否有效
  2162. if (urlFileName && /\.[a-zA-Z0-9]+$/.test(urlFileName)) {
  2163. fileName = urlFileName
  2164. } else {
  2165. // URL中无法提取有效文件名时,使用row.documentName作为备选
  2166. fileName =
  2167. row.documentName ||
  2168. `${row.documentName}_${new Date().getTime()}`
  2169. // 确保备选文件名有扩展名
  2170. if (!/\.[a-zA-Z0-9]+$/.test(fileName)) {
  2171. fileName += '.pdf'
  2172. }
  2173. }
  2174. // 创建隐藏的a标签进行下载
  2175. const link = document.createElement('a')
  2176. link.style.display = 'none'
  2177. link.href = fileUrl
  2178. // link.href = window.context.form + row.electronicDocumentUrl
  2179. // 设置下载文件名
  2180. link.download = fileName
  2181. document.body.appendChild(link)
  2182. link.click()
  2183. document.body.removeChild(link)
  2184. })
  2185. .catch((error) => {
  2186. // 关闭加载状态
  2187. this.$loading().close()
  2188. console.error('获取文件URL失败:', error)
  2189. })
  2190. },
  2191. // 加载报送资料
  2192. async loadDataRequirements() {
  2193. if (!this.currentTaskInfo?.taskId) {
  2194. return
  2195. }
  2196. try {
  2197. this.loading = true
  2198. const res = await getTaskRequirementList(this.currentTaskInfo.taskId)
  2199. console.log('报送资料', res)
  2200. if (res && res.code === 200) {
  2201. this.formData.dataRequirements = Array.isArray(res.value)
  2202. ? res.value
  2203. : []
  2204. this.tabLoadedStatus.dataRequirements = true
  2205. }
  2206. } catch (error) {
  2207. console.error('加载报送资料失败:', error)
  2208. // this.$message.error('加载报送资料失败')
  2209. } finally {
  2210. this.loading = false
  2211. }
  2212. },
  2213. // 加载成本调查表
  2214. async loadCostSurvey() {
  2215. console.log(1111111)
  2216. // if (!this.catalogId) {
  2217. // this.formData.costSurveyData = []
  2218. // this.costSurveyPagination.total = 0
  2219. // this.tabLoadedStatus.costSurvey = true
  2220. // return
  2221. // }
  2222. try {
  2223. this.loading = true
  2224. // getSurveyList 现在按 taskId 查询
  2225. if (!this.currentTaskInfo || !this.currentTaskInfo.taskId) {
  2226. this.formData.costSurveyData = []
  2227. this.costSurveyPagination.total = 0
  2228. this.tabLoadedStatus.costSurvey = true
  2229. this.loading = false
  2230. return
  2231. }
  2232. const params = {
  2233. taskId: this.currentTaskInfo.taskId,
  2234. pageNum: this.costSurveyPagination.currentPage,
  2235. pageSize: this.costSurveyPagination.pageSize,
  2236. type: 1,
  2237. }
  2238. if (this.auditedUnitId) params.auditedUnitId = this.auditedUnitId
  2239. const res = await getSurveyList(params)
  2240. console.log(res, 'res')
  2241. // 兼容不同返回结构
  2242. const raw =
  2243. (res &&
  2244. ((res.value && (res.value.records || res.value)) ||
  2245. res.data ||
  2246. res)) ||
  2247. []
  2248. const list = Array.isArray(raw)
  2249. ? raw
  2250. : Array.isArray(raw.records)
  2251. ? raw.records
  2252. : []
  2253. const rows = list.map((it, idx) => {
  2254. const t = String(it.templateType || it.templatetype || '')
  2255. const tableType =
  2256. t === '1'
  2257. ? '单记录'
  2258. : t === '2'
  2259. ? '固定表'
  2260. : t === '3'
  2261. ? '动态表'
  2262. : ''
  2263. return {
  2264. index:
  2265. (this.costSurveyPagination.currentPage - 1) *
  2266. this.costSurveyPagination.pageSize +
  2267. idx +
  2268. 1,
  2269. id: it.uploadId || it.id || '',
  2270. name:
  2271. it.surveyTemplateName || it.informationName || it.name || '',
  2272. dataType: it.dataType || '预置模板',
  2273. tableType,
  2274. isRequired: String(it.isRequired) === '1' ? '是' : '否',
  2275. isUpload: String(it.isUpload) === '1' || it.isUpload === true,
  2276. uploadId: it.uploadId || it.id || '',
  2277. surveyTemplateId: it.templateId || it.surveyTemplateId || '',
  2278. catalogId: it.catalogId || this.catalogId || '',
  2279. auditedUnitId: it.auditedUnitId || this.auditedUnitId || '',
  2280. templateType: t,
  2281. }
  2282. })
  2283. this.formData.costSurveyData = rows
  2284. // 设置总数
  2285. const total =
  2286. (res &&
  2287. res.value &&
  2288. (res.value.total ||
  2289. (Array.isArray(res.value.records)
  2290. ? res.value.records.length
  2291. : Array.isArray(res.value)
  2292. ? res.value.length
  2293. : 0))) ||
  2294. (Array.isArray(raw) ? raw.length : raw.total || 0)
  2295. this.costSurveyPagination.total = total
  2296. this.tabLoadedStatus.costSurvey = true
  2297. } catch (err) {
  2298. console.error('加载成本调查表失败:', err)
  2299. // this.$message &&
  2300. // this.$message.error &&
  2301. // this.$message.error('加载成本调查表失败')
  2302. this.formData.costSurveyData = []
  2303. this.costSurveyPagination.total = 0
  2304. this.tabLoadedStatus.costSurvey = true
  2305. } finally {
  2306. this.loading = false
  2307. }
  2308. },
  2309. // 加载监审意见
  2310. async loadAuditOpinion() {
  2311. try {
  2312. this.loading = true
  2313. // TODO: 调用监审意见接口
  2314. // const res = await getAuditOpinion(this.currentTaskInfo.taskId)
  2315. // if (res && res.code === 200) {
  2316. // this.formData.auditOpinion = res.value || {}
  2317. // }
  2318. // 暂时使用默认数据
  2319. this.tabLoadedStatus.auditOpinion = true
  2320. } catch (error) {
  2321. console.error('加载监审意见失败:', error)
  2322. // this.$message.error('加载监审意见失败')
  2323. } finally {
  2324. this.loading = false
  2325. }
  2326. },
  2327. // 加载消息通知
  2328. async loadMessageNotice() {
  2329. if (!this.currentTaskInfo?.taskId) {
  2330. return
  2331. }
  2332. try {
  2333. this.loading = true
  2334. const params = {
  2335. taskId: this.currentTaskInfo.taskId,
  2336. auditedUnitId: this.currentTaskInfo.auditedUnitId,
  2337. projectId: this.currentTaskInfo.projectId,
  2338. pageNum: this.messageNoticePagination.currentPage,
  2339. pageSize: this.messageNoticePagination.pageSize,
  2340. }
  2341. const res = await sendMessage(params)
  2342. console.log('消息通知接口返回:', res)
  2343. if (res && res.code === 200 && res.value) {
  2344. this.formData.messageNotice = res.value.records || []
  2345. this.messageNoticePagination.total = res.value.total || 0
  2346. } else {
  2347. this.formData.messageNotice = []
  2348. this.messageNoticePagination.total = 0
  2349. }
  2350. this.tabLoadedStatus.messageNotice = true
  2351. } catch (error) {
  2352. console.error('加载消息通知失败:', error)
  2353. // this.$message.error('加载消息通知失败')
  2354. this.formData.messageNotice = []
  2355. this.messageNoticePagination.total = 0
  2356. } finally {
  2357. this.loading = false
  2358. }
  2359. },
  2360. loadData() {
  2361. if (this.taskData && Object.keys(this.taskData).length > 0) {
  2362. // 加载传入的数据
  2363. this.formData = { ...this.formData, ...this.taskData }
  2364. // 处理监审期间数组
  2365. if (
  2366. this.formData.basicInfo.auditPeriod &&
  2367. typeof this.formData.basicInfo.auditPeriod === 'string'
  2368. ) {
  2369. const periods = this.formData.basicInfo.auditPeriod
  2370. .split(',')
  2371. .filter(Boolean)
  2372. this.formData.basicInfo.auditPeriodArray = periods.map((p) => ({
  2373. value: p,
  2374. }))
  2375. }
  2376. }
  2377. },
  2378. handleClose() {
  2379. this.dialogVisible = false
  2380. this.$emit('update:visible', false)
  2381. this.$emit('close')
  2382. },
  2383. getRowClassName({ row }) {
  2384. if (row.isCategoryHeader) {
  2385. return 'category-header-row'
  2386. }
  2387. return ''
  2388. },
  2389. // 处理监审目录变化(只读模式下不触发)
  2390. handleCatalogChange(value) {
  2391. // 只读模式,无需处理
  2392. },
  2393. // 处理地区变化(只读模式下不触发)
  2394. handleRegionChange(region) {
  2395. // 只读模式,无需处理
  2396. },
  2397. // 添加监审年份(只读模式下禁用)
  2398. addCostYear() {
  2399. // 只读模式,无需处理
  2400. },
  2401. // 删除监审年份(只读模式下禁用)
  2402. deleteCostYear(index) {
  2403. // 只读模式,无需处理
  2404. },
  2405. // 成本调查表分页 - 页码改变
  2406. handleCostSurveyPageChange(page) {
  2407. this.costSurveyPagination.currentPage = page
  2408. this.loadCostSurvey()
  2409. },
  2410. // 成本调查表分页 - 每页条数改变
  2411. handleCostSurveySizeChange(size) {
  2412. this.costSurveyPagination.pageSize = size
  2413. this.costSurveyPagination.currentPage = 1
  2414. this.loadCostSurvey()
  2415. },
  2416. // 消息通知分页 - 页码改变
  2417. handleMessageNoticePageChange(page) {
  2418. this.messageNoticePagination.currentPage = page
  2419. this.loadMessageNotice()
  2420. },
  2421. // 消息通知分页 - 每页条数改变
  2422. handleMessageNoticeSizeChange(size) {
  2423. this.messageNoticePagination.pageSize = size
  2424. this.messageNoticePagination.currentPage = 1
  2425. this.loadMessageNotice()
  2426. },
  2427. // 监审文书分页 - 每页条数改变
  2428. handleSizeChange(size) {
  2429. this.auditDocumentPagination.pageSize = size
  2430. this.auditDocumentPagination.page = 1
  2431. this.loadAuditDocument()
  2432. },
  2433. // 监审文书分页 - 页码改变
  2434. handleCurrentChange(page) {
  2435. this.auditDocumentPagination.page = page
  2436. this.loadAuditDocument()
  2437. },
  2438. },
  2439. }
  2440. </script>
  2441. <style scoped lang="scss">
  2442. .task-info-dialog {
  2443. ::v-deep .el-dialog__body {
  2444. padding: 20px;
  2445. max-height: 70vh;
  2446. overflow-y: auto;
  2447. }
  2448. }
  2449. .task-info-container {
  2450. width: 100%;
  2451. }
  2452. .readonly-form {
  2453. ::v-deep .el-input.is-disabled .el-input__inner {
  2454. color: #606266;
  2455. background-color: #f5f7fa;
  2456. }
  2457. ::v-deep .el-textarea.is-disabled .el-textarea__inner {
  2458. color: #606266;
  2459. background-color: #f5f7fa;
  2460. }
  2461. ::v-deep .el-radio__input.is-disabled + span.el-radio__label {
  2462. color: #606266;
  2463. }
  2464. }
  2465. .cost-period-container {
  2466. display: flex;
  2467. align-items: flex-start;
  2468. }
  2469. .add-cost-year-btn {
  2470. margin-right: 10px;
  2471. }
  2472. .delete-cost-year-btn {
  2473. margin-left: 10px;
  2474. }
  2475. .cost-years-wrapper {
  2476. flex: 1;
  2477. }
  2478. .cost-year-item {
  2479. margin-bottom: 10px;
  2480. display: flex;
  2481. align-items: center;
  2482. }
  2483. .cost-supervision-container {
  2484. width: 100%;
  2485. h3 {
  2486. text-align: center;
  2487. margin-top: 0;
  2488. margin-bottom: 20px;
  2489. font-size: 16px;
  2490. font-weight: bold;
  2491. }
  2492. }
  2493. .cost-opinion-section,
  2494. .feedback-section {
  2495. border: 1px solid #dcdcdc;
  2496. border-radius: 4px;
  2497. padding: 20px;
  2498. margin-bottom: 20px;
  2499. background-color: #f9f9f9;
  2500. }
  2501. .opinion-item,
  2502. .feedback-item {
  2503. margin-bottom: 20px;
  2504. display: flex;
  2505. flex-direction: column;
  2506. label {
  2507. margin-bottom: 8px;
  2508. font-weight: bold;
  2509. color: #1549ad;
  2510. }
  2511. }
  2512. .file-list-display {
  2513. border: 1px solid #e9e9e9;
  2514. border-radius: 4px;
  2515. padding: 15px;
  2516. min-height: 60px;
  2517. background-color: #fff;
  2518. }
  2519. .file-item {
  2520. padding: 8px;
  2521. margin-bottom: 5px;
  2522. color: #606266;
  2523. i {
  2524. margin-right: 5px;
  2525. color: #409eff;
  2526. }
  2527. }
  2528. ::v-deep .category-header-row {
  2529. background-color: #f5f7fa !important;
  2530. td {
  2531. background-color: #f5f7fa !important;
  2532. padding: 12px 16px !important;
  2533. }
  2534. }
  2535. .category-header-cell {
  2536. font-weight: 700;
  2537. color: #303133;
  2538. padding-left: 16px;
  2539. }
  2540. .dialog-footer {
  2541. text-align: right;
  2542. padding-top: 10px;
  2543. }
  2544. .document-edit-container {
  2545. display: flex;
  2546. .document-params {
  2547. width: 55%;
  2548. }
  2549. .document-preview {
  2550. width: 45%;
  2551. }
  2552. }
  2553. </style>