taskFillIn.vue 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831
  1. <template>
  2. <el-drawer
  3. :visible.sync="drawerVisible"
  4. :size="drawerSize"
  5. :with-header="true"
  6. :modal="true"
  7. :close-on-click-modal="false"
  8. :show-close="true"
  9. @close="handleDrawerClose"
  10. @update:visible="(val) => $emit('update:visible', val)"
  11. >
  12. <div slot="title" class="drawer-title">
  13. <span>{{ pageTitle || '填报任务' }}</span>
  14. </div>
  15. <div class="audit-task-manage-container">
  16. <!-- 操作按钮 -->
  17. <div class="action-buttons" style="margin-bottom: 20px">
  18. <el-button
  19. type="primary"
  20. :loading="loading.submit"
  21. :disabled="isViewMode"
  22. @click="handleSubmit"
  23. >
  24. 提交
  25. </el-button>
  26. <!-- <el-button type="primary" :loading="loading.save" @click="handleSave" :disabled="isViewMode">
  27. 保存
  28. </el-button> -->
  29. <!-- <el-button type="primary" class="ml10" @click="handleBack">
  30. 返回
  31. </el-button> -->
  32. </div>
  33. <!-- 标签页容器 -->
  34. <div
  35. v-loading="tabLoading[activeTab]"
  36. class="tabs-container"
  37. element-loading-text="数据加载中..."
  38. >
  39. <el-tabs
  40. v-model="activeTab"
  41. type="border-card"
  42. @tab-click="handleTabClick"
  43. >
  44. <!-- 立项信息 -->
  45. <el-tab-pane label="立项信息" name="projectInfo">
  46. <project-info-tab
  47. :form-data="formData.basicInfo"
  48. :is-view-mode="isViewMode"
  49. :unit-list="unitList"
  50. :org-list="OrgList"
  51. :user-list="userList"
  52. :dict-data="dictData"
  53. @change="handleProjectInfoChange"
  54. @handleCatalogChange="handleCatalogChange"
  55. @handleRegionChange="handleRegionChange"
  56. @addCostYear="addCostYear"
  57. @deleteCostYear="deleteCostYear"
  58. />
  59. </el-tab-pane>
  60. <!-- 监审文书 -->
  61. <el-tab-pane label="监审文书" name="auditDocument">
  62. <audit-document-tab
  63. :form-data="formData.auditDocument"
  64. :is-view-mode="isViewMode"
  65. @handleClick="handleClick"
  66. @handleDownload="handleDownload"
  67. @handleUpload="handleUpload"
  68. />
  69. </el-tab-pane>
  70. <!-- 报送资料 -->
  71. <el-tab-pane
  72. v-if="currentNode === 'clcs' || currentNode === 'tjcl'"
  73. label="报送资料"
  74. name="dataRequirements"
  75. >
  76. <data-requirements-tab
  77. :data-requirements="groupedDataRequirements"
  78. :is-view-mode="isViewMode"
  79. :dict-data="dictData"
  80. @handleAddMaterial="handleAddMaterial"
  81. @handleFileView="handleFileView"
  82. @handleFileDownload="handleFileDownload"
  83. @handleFileUpload="handleFileUpload"
  84. @handleTemplateDownload="handleTemplateDownload"
  85. @handleDataUpload="handleDataUpload"
  86. />
  87. </el-tab-pane>
  88. <!-- 成本调查表 -->
  89. <el-tab-pane
  90. v-if="currentNode === 'clcs' || currentNode === 'tjcl'"
  91. label="成本调查表"
  92. name="costSurvey"
  93. >
  94. <cost-survey-tab
  95. :paginated-data="paginatedData"
  96. :pagination="pagination"
  97. :is-view-mode="isViewMode"
  98. @handle-modify="handleModify"
  99. @handle-data-download="handleDataDownload"
  100. @handle-data-upload="handleDataUpload"
  101. @handle-preview="handlePreview"
  102. @handlePageChange="handlePageChange"
  103. />
  104. </el-tab-pane>
  105. <!-- 监审意见 -->
  106. <el-tab-pane
  107. v-if="currentNode === 'yjfk'"
  108. label="监审意见"
  109. name="auditOpinion"
  110. >
  111. <audit-opinion-tab
  112. :id="taskId"
  113. ref="auditOpinionTabRef"
  114. :form-data="formData.auditOpinion"
  115. :is-view-mode="isViewMode"
  116. @change="handleAuditOpinionChange"
  117. @data-loaded="handleAuditOpinionDataLoaded"
  118. />
  119. </el-tab-pane>
  120. <!-- 消息通知 -->
  121. <el-tab-pane label="消息通知" name="messageNotice">
  122. <message-notice-tab
  123. :form-data="formData.messageNotice"
  124. :pagination="messageNoticePagination"
  125. @handleCurrentChange="handleMessageNoticePageChange"
  126. @handleSizeChange="handleMessageNoticeSizeChange"
  127. />
  128. </el-tab-pane>
  129. </el-tabs>
  130. </div>
  131. <!-- 补充材料弹窗 -->
  132. <el-dialog
  133. title="补充材料"
  134. :visible.sync="materialDialogVisible"
  135. width="600px"
  136. :close-on-click-modal="false"
  137. :modal="false"
  138. append-to-body
  139. >
  140. <el-form
  141. ref="materialForm"
  142. :model="materialForm"
  143. :rules="materialRules"
  144. label-width="100px"
  145. >
  146. <el-form-item label="资料类别" prop="informationType">
  147. <el-select
  148. v-model="materialForm.informationType"
  149. placeholder="请选择资料类别"
  150. style="width: 100%"
  151. :disabled="isViewMode"
  152. >
  153. <el-option
  154. v-for="item in dictData['materialType']"
  155. :key="item.key"
  156. :label="item.name"
  157. :value="item.key"
  158. ></el-option>
  159. </el-select>
  160. </el-form-item>
  161. <el-form-item label="资料名称" prop="informationName">
  162. <el-input
  163. v-model="materialForm.informationName"
  164. placeholder="请输入资料名称"
  165. style="width: 100%"
  166. :disabled="isViewMode"
  167. ></el-input>
  168. </el-form-item>
  169. <el-form-item label="资料要求:" prop="informationRequire">
  170. <el-input
  171. v-model="materialForm.informationRequire"
  172. placeholder="请输入资料要求"
  173. :disabled="isViewMode"
  174. ></el-input>
  175. </el-form-item>
  176. <el-form-item label="格式要求:" prop="formatRequired">
  177. <el-select
  178. v-model="materialForm.formatRequired"
  179. placeholder="请选择格式要求"
  180. style="width: 100%"
  181. :disabled="isViewMode"
  182. >
  183. <el-option
  184. v-for="item in dictData['formatAsk']"
  185. :key="item.id"
  186. :label="item.name"
  187. :value="item.key"
  188. ></el-option>
  189. </el-select>
  190. </el-form-item>
  191. <el-form-item label="上传附件:" prop="fileList">
  192. <el-upload
  193. class="upload-demo"
  194. :action="''"
  195. :http-request="handleMaterialUpload"
  196. :on-remove="handleMaterialRemove"
  197. :before-upload="beforeMaterialUpload"
  198. :on-success="handleMaterialUploadSuccess"
  199. :on-error="handleMaterialUploadError"
  200. :on-progress="handleMaterialUploadProgress"
  201. :file-list="materialForm.fileList"
  202. :limit="1"
  203. :on-exceed="handleMaterialExceed"
  204. >
  205. <el-button
  206. v-show="
  207. !materialForm.fileList || materialForm.fileList.length === 0
  208. "
  209. size="small"
  210. type="primary"
  211. :disabled="isViewMode"
  212. >
  213. 选择文件
  214. </el-button>
  215. <!-- <div slot="tip" class="el-upload__tip">
  216. 最多上传1个文件,支持 pdf, doc, docx, xls, xlsx, csv 格式,单个文件不超过50MB
  217. </div> -->
  218. </el-upload>
  219. </el-form-item>
  220. <!-- <el-button
  221. type="text"
  222. size="mini"
  223. @click="handleMaterialUpload"
  224. >
  225. 上传附件
  226. </el-button> -->
  227. </el-form>
  228. <div slot="footer" class="dialog-footer">
  229. <el-button
  230. type="primary"
  231. :disabled="isViewMode"
  232. @click="handleMaterialSubmit"
  233. >
  234. 保 存
  235. </el-button>
  236. <el-button :disabled="isViewMode" @click="handleMaterialCancel">
  237. 取 消
  238. </el-button>
  239. </div>
  240. </el-dialog>
  241. <!-- 在线填报弹窗 -->
  242. <OnlineFillDialog
  243. :visible.sync="onlineFillDialogVisible"
  244. :table-type="currentSurveyData ? currentSurveyData.tableType : '单记录'"
  245. :survey-data="currentSurveyData || {}"
  246. />
  247. </div>
  248. </el-drawer>
  249. </template>
  250. <script>
  251. import { Message } from 'element-ui'
  252. import OnlineFillDialog from './OnlineFill.vue'
  253. import {
  254. getProjectInformationInfo,
  255. getTaskRequirementList,
  256. addOrUpdateTaskRequirement,
  257. submitTaskRequirement,
  258. sendMessage,
  259. } from '@/api/auditTaskProcessing'
  260. // import UploadComponent from '@/components/costAudit/UploadComponent.vue'
  261. import { getAllUnitList } from '@/api/auditEntityManage'
  262. import { getDefaultDem, getOrgListByDemId } from '@/api/annualReviewPlan'
  263. import { getAllUserList } from '@/api/uc'
  264. import { dictMixin } from '@/mixins/useDict'
  265. import { uploadFile } from '@/api/file'
  266. import { submitPreliminaryOpinion } from '@/api/audit/preliminaryOpinion'
  267. import ProjectInfoTab from './components/ProjectInfoTab.vue'
  268. import AuditDocumentTab from './components/AuditDocumentTab.vue'
  269. import DataRequirementsTab from './components/DataRequirementsTab.vue'
  270. import CostSurveyTab from './components/CostSurveyTab.vue'
  271. import AuditOpinionTab from './components/AuditOpinionTab.vue'
  272. import MessageNoticeTab from './components/MessageNoticeTab.vue'
  273. export default {
  274. name: 'TaskFillIn',
  275. components: {
  276. OnlineFillDialog,
  277. // UploadComponent,
  278. ProjectInfoTab,
  279. AuditDocumentTab,
  280. DataRequirementsTab,
  281. CostSurveyTab,
  282. AuditOpinionTab,
  283. MessageNoticeTab,
  284. },
  285. mixins: [dictMixin],
  286. props: {
  287. // 侧边弹窗是否显示
  288. visible: {
  289. type: Boolean,
  290. default: false,
  291. },
  292. // 侧边弹窗尺寸
  293. drawerSize: {
  294. type: String,
  295. default: '85%',
  296. },
  297. // 任务信息(从props传入,替代路由query)
  298. taskInfo: {
  299. type: Object,
  300. default: () => ({}),
  301. },
  302. // 是否为查看模式
  303. viewMode: {
  304. type: Boolean,
  305. default: false,
  306. },
  307. },
  308. data() {
  309. return {
  310. drawerVisible: false, // 侧边弹窗显示状态
  311. isViewMode: false, // 是否为查看模式
  312. localTaskInfo: {}, // 本地任务信息(用于存储)
  313. unitList: [],
  314. OrgList: [],
  315. userList: [],
  316. dictData: {
  317. attributionYear: [], // 归属年度
  318. projectProposal: [], // 立项来源
  319. auditType: [], //监审形式
  320. materialType: [], // 资料类别
  321. materialCategory: [], // 资料类别
  322. formatAsk: [], // 资料类型
  323. clshzt: [], // 是否通过
  324. },
  325. // 立项依据文件列表
  326. accordingFileList: [],
  327. // 其他材料文件列表
  328. otherFileList: [],
  329. // 页面标题
  330. pageTitle: '立项信息',
  331. projectId: '', // 项目ID
  332. taskId: '', // 任务ID
  333. auditedUnitId: '', // 被监审单位ID
  334. // 当前激活的标签页
  335. activeTab: 'projectInfo',
  336. // 加载状态管理
  337. loading: {
  338. submit: false,
  339. save: false,
  340. },
  341. // 标签页加载状态
  342. tabLoading: {
  343. projectInfo: false,
  344. auditDocument: false,
  345. dataRequirements: false,
  346. costSurvey: false,
  347. auditOpinion: false,
  348. messageNotice: false,
  349. },
  350. isDisabled: false,
  351. tabCheck: '', // 状态判断监审意见是否显示
  352. tabVisiable: true, // 标签页是否显示
  353. // 成本调查表分页相关
  354. costSurveyPagination: {
  355. currentPage: 1,
  356. pageSize: 10,
  357. total: 0,
  358. },
  359. // 消息通知分页相关
  360. messageNoticePagination: {
  361. pageNo: 1,
  362. pageSize: 10,
  363. total: 0,
  364. },
  365. // 所有表单数据聚合
  366. formData: {
  367. basicInfo: {
  368. projectName: '',
  369. catalogId: '',
  370. areaCode: '',
  371. auditUnitId: [],
  372. auditUnitName: '',
  373. orgId: '',
  374. orgName: '',
  375. projectYear: '',
  376. sourceType: '',
  377. auditType: '',
  378. auditPeriod: '',
  379. auditPeriodArray: [{ value: '' }],
  380. needHearing: 1,
  381. isEmergency: 1,
  382. establishmentReason: '',
  383. accordingFileUrl: '',
  384. otherFileUrl: '',
  385. auditGroup: '',
  386. auditTeamMembers: [],
  387. expertStr: '',
  388. plannedAuditStartDate: '',
  389. plannedAuditEndDate: '',
  390. },
  391. auditDocument: [
  392. {
  393. date: '2016-05-02',
  394. name: '王小虎',
  395. province: '上海',
  396. city: '普陀区',
  397. address: '上海市普陀区金沙江路 1518 弄',
  398. zip: 200333,
  399. },
  400. {
  401. date: '2016-05-04',
  402. name: '王小虎',
  403. province: '上海',
  404. city: '普陀区',
  405. address: '上海市普陀区金沙江路 1517 弄',
  406. zip: 200333,
  407. },
  408. ],
  409. dataRequirements: [],
  410. costSurveyData: [
  411. {
  412. index: 1,
  413. name: '封面',
  414. dataType: '模版定制',
  415. tableType: '单记录',
  416. isRequired: '是',
  417. isUploaded: false,
  418. isDisabled: false,
  419. },
  420. {
  421. index: 2,
  422. name: '企业基本情况调查表',
  423. dataType: '模版定制',
  424. tableType: '固定表',
  425. isRequired: '是',
  426. isUploaded: true,
  427. isDisabled: false,
  428. },
  429. {
  430. index: 3,
  431. name: '企业成本费用调查表',
  432. dataType: '模版定制',
  433. tableType: '固定表',
  434. isRequired: '是',
  435. isUploaded: true,
  436. isDisabled: false,
  437. },
  438. {
  439. index: 4,
  440. name: '企业期间费用调查表',
  441. dataType: '模版定制',
  442. tableType: '动态表',
  443. isRequired: '是',
  444. isUploaded: false,
  445. isDisabled: true, // 灰色不可点击
  446. },
  447. {
  448. index: 5,
  449. name: '企业职工薪酬调查表',
  450. dataType: '模版定制',
  451. tableType: '动态表',
  452. isRequired: '是',
  453. isUploaded: false,
  454. isDisabled: true, // 灰色不可点击
  455. },
  456. {
  457. index: 6,
  458. name: '……',
  459. dataType: '模版定制',
  460. tableType: '动态表',
  461. isRequired: '是',
  462. isUploaded: false,
  463. isDisabled: false,
  464. },
  465. ],
  466. auditOpinion: {
  467. basicFinancialInfo:
  468. '此处为被监审单位基本情况及主要财务状况的内容...',
  469. priceStandard: '此处为监审项目现行执行的价格标准的内容...',
  470. costComposition:
  471. '此处为监审项目的成本构成、数据核增核减情况、依据及理由的内容...',
  472. preliminaryOpinion: '此处为成本审核初步意见的内容...',
  473. feedbackOpinion: '', // 被监审单位可输入反馈意见
  474. feedbackMaterials: [], // 被监审单位上传的反馈文件
  475. },
  476. messageNotice: [], // 消息通知列表
  477. },
  478. // 监审地区级联选择器
  479. regionOptions: [
  480. {
  481. value: 'zhinan',
  482. label: '指南',
  483. children: [
  484. {
  485. value: 'shejiyuanze',
  486. label: '设计原则',
  487. children: [
  488. {
  489. value: 'yizhi',
  490. label: '一致',
  491. },
  492. {
  493. value: 'fankui',
  494. label: '反馈',
  495. },
  496. {
  497. value: 'xiaolv',
  498. label: '效率',
  499. },
  500. {
  501. value: 'kekong',
  502. label: '可控',
  503. },
  504. ],
  505. },
  506. ],
  507. },
  508. ],
  509. dialogVisible: false,
  510. catalogueData: [
  511. {
  512. label: '一级 1',
  513. children: [
  514. {
  515. label: '二级 1-1',
  516. children: [
  517. {
  518. label: '三级 1-1-1',
  519. },
  520. ],
  521. },
  522. ],
  523. },
  524. {
  525. label: '一级 2',
  526. children: [
  527. {
  528. label: '二级 2-1',
  529. children: [
  530. {
  531. label: '三级 2-1-1',
  532. },
  533. ],
  534. },
  535. {
  536. label: '二级 2-2',
  537. children: [
  538. {
  539. label: '三级 2-2-1',
  540. },
  541. ],
  542. },
  543. ],
  544. },
  545. {
  546. label: '一级 3',
  547. children: [
  548. {
  549. label: '二级 3-1',
  550. children: [
  551. {
  552. label: '三级 3-1-1',
  553. },
  554. ],
  555. },
  556. {
  557. label: '二级 3-2',
  558. children: [
  559. {
  560. label: '三级 3-2-1',
  561. },
  562. ],
  563. },
  564. ],
  565. },
  566. ],
  567. defaultProps: {
  568. children: 'children',
  569. label: 'label',
  570. },
  571. // 补充材料弹窗相关数据
  572. materialDialogVisible: false,
  573. materialForm: {
  574. informationType: '',
  575. informationName: '',
  576. informationRequire: '',
  577. formatRequired: '',
  578. fileList: [],
  579. fileUrl: '',
  580. },
  581. materialRules: {
  582. informationType: [
  583. { required: true, message: '请选择资料类别', trigger: 'change' },
  584. ],
  585. informationName: [
  586. { required: true, message: '请输入资料名称', trigger: 'blur' },
  587. ],
  588. formatRequired: [
  589. { required: true, message: '请选择格式要求', trigger: 'change' },
  590. ],
  591. informationRequire: [
  592. { required: true, message: '请输入资料要求', trigger: 'blur' },
  593. ],
  594. },
  595. materialCategoryOptions: [
  596. { label: '综合性资料', value: 'comprehensive' },
  597. { label: '财务会计资料', value: 'financial' },
  598. { label: '其他资料', value: 'other' },
  599. ],
  600. // 修复未定义的变量
  601. value: [], // 监审地区级联选择器的值
  602. value3: null, // 年份选择器的值
  603. // 在线填报弹窗相关数据
  604. onlineFillDialogVisible: false,
  605. currentSurveyData: null,
  606. }
  607. },
  608. computed: {
  609. // 按类别分组的报送资料列表
  610. groupedDataRequirements() {
  611. if (
  612. !this.formData.dataRequirements ||
  613. !Array.isArray(this.formData.dataRequirements) ||
  614. this.formData.dataRequirements.length === 0
  615. ) {
  616. return []
  617. }
  618. // 按类别分组
  619. const groups = {}
  620. this.formData.dataRequirements.forEach((item, index) => {
  621. // 获取类别标识,使用 informationType 作为类别 key
  622. const categoryKey =
  623. item.informationType ||
  624. item.category ||
  625. item.categoryId ||
  626. item.categoryName ||
  627. 'other'
  628. // 优先从字典中获取类别名称
  629. let categoryName = ''
  630. if (categoryKey) {
  631. // 优先使用 getDictNameByValue 根据 value 查找
  632. categoryName = this.getDictNameByValue(
  633. 'materialCategory',
  634. categoryKey
  635. )
  636. // 如果 getDictNameByValue 没找到,尝试使用 getDictName 根据 key 查找
  637. if (!categoryName || categoryName === String(categoryKey)) {
  638. categoryName = this.getDictName('materialCategory', categoryKey)
  639. }
  640. // 如果字典中都找不到,使用 item.categoryName 作为后备
  641. if (!categoryName || categoryName === String(categoryKey)) {
  642. categoryName = item.informationTypeName || categoryKey
  643. }
  644. }
  645. if (!groups[categoryKey]) {
  646. groups[categoryKey] = {
  647. categoryKey,
  648. categoryName: categoryName || String(categoryKey),
  649. items: [],
  650. }
  651. }
  652. // 添加序号(每个类别内从1开始)
  653. const itemWithSeq = {
  654. ...item,
  655. index: index + 1,
  656. seq: groups[categoryKey].items.length + 1,
  657. }
  658. groups[categoryKey].items.push(itemWithSeq)
  659. })
  660. // 将分组转换为扁平数组,插入类别标题行
  661. const result = []
  662. Object.keys(groups).forEach((categoryKey) => {
  663. const group = groups[categoryKey]
  664. // 添加类别标题行
  665. result.push({
  666. isCategoryHeader: true,
  667. categoryName: group.categoryName,
  668. categoryKey: group.categoryKey,
  669. })
  670. // 添加该类别下的所有项目
  671. group.items.forEach((item) => {
  672. result.push({
  673. ...item,
  674. isCategoryHeader: false,
  675. })
  676. })
  677. })
  678. return result
  679. },
  680. // 分页后的成本调查表数据
  681. paginatedCostSurveyData() {
  682. const start =
  683. (this.costSurveyPagination.currentPage - 1) *
  684. this.costSurveyPagination.pageSize
  685. const end = start + this.costSurveyPagination.pageSize
  686. return this.formData.costSurveyData.slice(start, end)
  687. },
  688. },
  689. watch: {
  690. visible: {
  691. immediate: true,
  692. handler(newVal) {
  693. this.drawerVisible = newVal
  694. if (
  695. newVal &&
  696. this.taskInfo &&
  697. Object.keys(this.taskInfo).length > 0
  698. ) {
  699. // 弹窗打开时初始化数据
  700. this.initData()
  701. }
  702. },
  703. },
  704. drawerVisible(newVal) {
  705. if (!newVal) {
  706. this.$emit('update:visible', false)
  707. this.$emit('close')
  708. }
  709. },
  710. taskInfo: {
  711. immediate: true,
  712. deep: true,
  713. handler(newVal) {
  714. // 如果props传入taskInfo且有值,且弹窗已打开,则初始化数据
  715. if (newVal && Object.keys(newVal).length > 0 && this.drawerVisible) {
  716. this.initData()
  717. }
  718. },
  719. },
  720. },
  721. mounted() {
  722. this.drawerVisible = this.visible
  723. this.getAllUnitList()
  724. this.getDefaultDem()
  725. this.getUser()
  726. // 默认设置立项信息标签页
  727. this.activeTab = 'projectInfo'
  728. this.pageTitle = '立项信息'
  729. // 如果有传入taskInfo,初始化数据
  730. if (this.taskInfo && Object.keys(this.taskInfo).length > 0) {
  731. this.initData()
  732. }
  733. // 初始化成本调查表分页总数
  734. this.costSurveyPagination.total = this.formData.costSurveyData.length
  735. },
  736. methods: {
  737. // 初始化数据
  738. initData() {
  739. // 优先使用props传入的taskInfo,其次使用localTaskInfo
  740. const info =
  741. this.taskInfo && Object.keys(this.taskInfo).length > 0
  742. ? this.taskInfo
  743. : this.localTaskInfo
  744. // 判断是否为查看模式
  745. this.isViewMode = this.viewMode || false
  746. this.projectId = info.projectId || ''
  747. this.taskId = (info.userTask && info.userTask.id) || info.taskId || ''
  748. this.tabCheck = info.status || ''
  749. this.auditedUnitId = info.auditedUnitId || ''
  750. this.currentNode = info.currentNode || ''
  751. if (this.tabCheck === 'jtsy') {
  752. this.tabVisiable = false
  753. }
  754. // 默认显示立项信息标签页
  755. this.activeTab = 'projectInfo'
  756. this.pageTitle = '立项信息'
  757. // 加载立项信息数据
  758. if (this.projectId) {
  759. this.getProjectInformation()
  760. }
  761. },
  762. // 打开弹窗方法(供外部调用)
  763. open(taskInfo, viewMode = false) {
  764. this.localTaskInfo = taskInfo || {}
  765. this.initData()
  766. this.drawerVisible = true
  767. },
  768. // 关闭弹窗方法
  769. close() {
  770. this.drawerVisible = false
  771. },
  772. getAllUnitList() {
  773. getAllUnitList().then((res) => {
  774. this.unitList = res.value || []
  775. })
  776. },
  777. // 获取默认维度
  778. getDefaultDem() {
  779. getDefaultDem().then((res) => {
  780. if (res && res.code === 200) {
  781. const demId = res.value ? res.value.id : null
  782. if (demId) {
  783. this.getOrgListByDemId(demId)
  784. }
  785. }
  786. })
  787. },
  788. // 根据维度ID获取单位列表
  789. getOrgListByDemId(demId) {
  790. getOrgListByDemId({ demId }).then((res) => {
  791. if (res && res.code === 200) {
  792. this.OrgList = res.value || []
  793. }
  794. })
  795. },
  796. // 获取用户信息
  797. getUser() {
  798. getAllUserList()
  799. .then((res) => {
  800. this.userList = res.value || []
  801. })
  802. .catch(() => {})
  803. },
  804. // 添加监审年份
  805. addCostYear() {
  806. this.formData.basicInfo.auditPeriodArray.push({ value: '' })
  807. },
  808. // 删除监审年份
  809. deleteCostYear(index) {
  810. // 若只剩1条,禁止删除并提示
  811. if (this.formData.basicInfo.auditPeriodArray.length <= 1) {
  812. Message.warning('监审期间至少需保留1条,无法删除')
  813. return
  814. }
  815. this.formData.basicInfo.auditPeriodArray.splice(index, 1)
  816. },
  817. // 更新auditPeriod字符串
  818. updateAuditPeriodString() {
  819. const values = this.formData.basicInfo.auditPeriodArray
  820. .map((item) => item.value)
  821. .filter(Boolean)
  822. const uniqueValues = [...new Set(values)]
  823. this.formData.basicInfo.auditPeriod = uniqueValues.join(',')
  824. },
  825. // 立项依据文件处理
  826. saveAccordingFiles(data) {
  827. this.accordingFileList = data
  828. this.formData.basicInfo.accordingFileUrl = data[0]?.savePath || ''
  829. },
  830. // 移除立项依据文件
  831. removeAccordingFile() {
  832. this.accordingFileList = []
  833. this.formData.basicInfo.accordingFileUrl = ''
  834. },
  835. // 其他材料文件处理
  836. saveOtherFiles(data) {
  837. this.otherFileList = data
  838. this.formData.basicInfo.otherFileUrl = data[0]?.savePath || ''
  839. },
  840. // 移除其他材料文件
  841. removeOtherFile() {
  842. this.otherFileList = []
  843. this.formData.basicInfo.otherFileUrl = ''
  844. },
  845. // 处理地区选择变化
  846. handleRegionChange(region) {
  847. this.formData.basicInfo.areaCode = region.code
  848. },
  849. // 处理监审目录选择后的回调
  850. handleCatalogChange(value) {
  851. // this.formData.catalogId = value.join(',')
  852. this.formData.basicInfo.catalogId = value
  853. },
  854. // 处理立项信息变化
  855. handleProjectInfoChange(data) {
  856. this.formData.basicInfo = { ...data }
  857. },
  858. // 处理监审意见变化
  859. handleAuditOpinionChange(data) {
  860. this.formData.auditOpinion = { ...data }
  861. },
  862. // 处理监审意见数据加载完成
  863. handleAuditOpinionDataLoaded() {
  864. // 数据加载完成后,关闭加载状态
  865. this.tabLoading.auditOpinion = false
  866. },
  867. // 表格行样式
  868. getRowClassName({ row }) {
  869. if (row.isCategoryHeader) {
  870. return 'category-header-row'
  871. }
  872. return ''
  873. },
  874. // 标签页切换
  875. handleTabClick(tab) {
  876. console.log(tab, 'qiehuan')
  877. if (tab.name === 'projectInfo') {
  878. // 立项项目信息
  879. this.getProjectInformation()
  880. } else if (tab.name === 'dataRequirements') {
  881. // 报送资料要求
  882. this.getTaskRequirementPage()
  883. } else if (tab.name === 'messageNotice') {
  884. // 消息通知
  885. this.getSendMessage()
  886. } else if (tab.name === 'auditOpinion') {
  887. // 监审意见标签页,设置加载状态并调用接口
  888. this.tabLoading.auditOpinion = true
  889. // 通过 ref 调用子组件方法,确保接口被调用
  890. this.$nextTick(() => {
  891. this.$nextTick(() => {
  892. const auditOpinionTab = this.$refs.auditOpinionTabRef
  893. if (
  894. auditOpinionTab &&
  895. typeof auditOpinionTab.getPreliminaryOpinionData === 'function'
  896. ) {
  897. auditOpinionTab.getPreliminaryOpinionData()
  898. } else {
  899. // 如果找不到组件,可能是组件还未创建,延迟调用
  900. setTimeout(() => {
  901. const tab = this.$refs.auditOpinionTabRef
  902. if (
  903. tab &&
  904. typeof tab.getPreliminaryOpinionData === 'function'
  905. ) {
  906. tab.getPreliminaryOpinionData()
  907. } else {
  908. this.tabLoading.auditOpinion = false
  909. }
  910. }, 200)
  911. }
  912. })
  913. })
  914. }
  915. // 其他标签页(如 auditDocument、costSurvey)如果不需要异步加载数据,则不需要设置加载状态
  916. // 可添加标签页切换时的逻辑
  917. this.pageTitle = tab.label
  918. },
  919. // 获取立项项目信息
  920. getProjectInformation() {
  921. this.tabLoading.projectInfo = true
  922. getProjectInformationInfo(this.projectId)
  923. .then((res) => {
  924. console.log('立项项目信息', res)
  925. if (res && res.value) {
  926. const data = res.value
  927. // 处理监审期间数组
  928. let auditPeriodArray = [{ value: '' }]
  929. if (data.auditPeriod && typeof data.auditPeriod === 'string') {
  930. const periods = data.auditPeriod.split(',').filter(Boolean)
  931. auditPeriodArray = periods.map((p) => ({ value: p }))
  932. }
  933. // 赋值并添加转换后的数组
  934. this.formData.basicInfo = {
  935. ...data,
  936. auditPeriodArray: auditPeriodArray,
  937. }
  938. }
  939. })
  940. .catch((err) => {
  941. console.error('获取立项项目信息失败', err)
  942. })
  943. .finally(() => {
  944. this.tabLoading.projectInfo = false
  945. })
  946. },
  947. // 获取报送资料要求列表
  948. getTaskRequirementPage() {
  949. this.tabLoading.dataRequirements = true
  950. console.log('送报资料接口', this.taskId)
  951. getTaskRequirementList(this.taskId)
  952. .then((res) => {
  953. console.log('报送资料要求', res)
  954. if (res && res.code === 200 && res.value) {
  955. this.formData.dataRequirements = Array.isArray(res.value)
  956. ? res.value
  957. : []
  958. } else {
  959. this.formData.dataRequirements = []
  960. }
  961. })
  962. .catch((err) => {
  963. console.error('获取报送资料失败', err)
  964. this.formData.dataRequirements = []
  965. })
  966. .finally(() => {
  967. this.tabLoading.dataRequirements = false
  968. })
  969. },
  970. // 消息通知
  971. getSendMessage() {
  972. this.tabLoading.messageNotice = true
  973. const params = {
  974. taskId: this.taskId,
  975. auditedUnitId: this.auditedUnitId,
  976. projectId: this.projectId,
  977. pageNum: this.messageNoticePagination.pageNo,
  978. pageSize: this.messageNoticePagination.pageSize,
  979. }
  980. sendMessage(params)
  981. .then((res) => {
  982. console.log('消息通知', res)
  983. if (res && res.code === 200 && res.value) {
  984. this.formData.messageNotice = res.value.records || []
  985. this.messageNoticePagination.total = res.value.total || 0
  986. } else {
  987. this.formData.messageNotice = []
  988. this.messageNoticePagination.total = 0
  989. }
  990. })
  991. .catch((err) => {
  992. console.error('获取消息通知失败', err)
  993. this.formData.messageNoticeData = []
  994. this.messageNoticePagination.total = 0
  995. })
  996. .finally(() => {
  997. this.tabLoading.messageNotice = false
  998. })
  999. },
  1000. // 消息通知分页处理
  1001. handleMessageNoticePageChange(pageNo) {
  1002. this.messageNoticePagination.pageNo = pageNo
  1003. this.getSendMessage()
  1004. },
  1005. // 消息通知每页大小变化
  1006. handleMessageNoticeSizeChange(pageSize) {
  1007. this.messageNoticePagination.pageSize = pageSize
  1008. this.messageNoticePagination.pageNo = 1
  1009. this.getSendMessage()
  1010. },
  1011. // 提交
  1012. handleSubmit() {
  1013. this.loading.submit = true
  1014. // 判断是否为监审意见提交(currentNode === 'yjfk')
  1015. if (this.currentNode === 'yjfk') {
  1016. // 企业监审意见提交
  1017. // 处理文件列表,提取文件URL
  1018. // let fileUrlList = []
  1019. // if (this.formData.auditOpinion?.feedbackMaterials && Array.isArray(this.formData.auditOpinion.feedbackMaterials)) {
  1020. // fileUrlList = this.formData.auditOpinion.feedbackMaterials.map((file) => {
  1021. // // 如果file是字符串,直接返回
  1022. // if (typeof file === 'string') {
  1023. // return file
  1024. // }
  1025. // // 如果file是对象,提取url字段
  1026. // return file.url || file.response?.savePath || file.savePath || ''
  1027. // }).filter((url) => url) // 过滤空值
  1028. // }
  1029. const submitData = {
  1030. taskId: this.taskId,
  1031. // 成本监审意见数据
  1032. basicSituation: this.formData.auditOpinion?.basicSituation || '',
  1033. currentPriceStandard:
  1034. this.formData.auditOpinion?.currentPriceStandard || '',
  1035. costComposition: this.formData.auditOpinion?.costComposition || '',
  1036. preliminaryOpinion:
  1037. this.formData.auditOpinion?.preliminaryOpinion || '',
  1038. // 被监审单位反馈意见数据
  1039. feedbackOpinion: this.formData.auditOpinion?.feedbackOpinion || '',
  1040. feedbackMaterials:
  1041. this.formData.auditOpinion?.feedbackMaterials[0]?.url || '', // 附件URL
  1042. }
  1043. submitPreliminaryOpinion(submitData)
  1044. .then((res) => {
  1045. this.loading.submit = false
  1046. console.log('企业监审意见提交成功', res)
  1047. if (res && res.code === 200) {
  1048. Message.success('提交成功')
  1049. // 关闭弹窗
  1050. this.handleBack()
  1051. // 通知父组件刷新列表
  1052. this.$emit('refresh')
  1053. } else {
  1054. Message.error(res?.message || '提交失败')
  1055. }
  1056. })
  1057. .catch((err) => {
  1058. this.loading.submit = false
  1059. console.error('企业监审意见提交失败', err)
  1060. Message.error('提交失败:' + (err.message || '未知错误'))
  1061. })
  1062. } else {
  1063. // 其他情况走原有逻辑
  1064. setTimeout(() => {
  1065. submitTaskRequirement({ taskId: this.taskId })
  1066. .then((res) => {
  1067. this.loading.submit = false
  1068. console.log('提交成功', res)
  1069. if (res && res.code === 200) {
  1070. Message.success('提交成功')
  1071. this.handleBack()
  1072. // 通知父组件刷新列表
  1073. this.$emit('refresh')
  1074. } else {
  1075. Message.error('提交失败')
  1076. }
  1077. })
  1078. .catch((err) => {
  1079. this.loading.submit = false
  1080. console.error('提交失败', err)
  1081. Message.error('提交失败')
  1082. })
  1083. }, 500)
  1084. }
  1085. },
  1086. // 保存
  1087. handleSave() {
  1088. this.loading.save = true
  1089. // 保存逻辑
  1090. setTimeout(() => {
  1091. this.loading.save = false
  1092. Message.success('保存成功')
  1093. }, 500)
  1094. },
  1095. // 返回
  1096. handleBack() {
  1097. this.drawerVisible = false
  1098. this.$emit('close')
  1099. },
  1100. // 侧边弹窗关闭事件
  1101. handleDrawerClose() {
  1102. this.$emit('update:visible', false)
  1103. this.$emit('close')
  1104. },
  1105. // 监审地区级联选择器
  1106. handleChange(value) {
  1107. console.log(value)
  1108. },
  1109. // 监审地区级联选择器节点点击
  1110. handleNodeClick(data) {
  1111. console.log(data)
  1112. this.formData.basicInfo.projectName = data.label
  1113. },
  1114. // 查看文书
  1115. handleClick(row) {
  1116. console.log(row)
  1117. if (row.fileUrl) {
  1118. window.open(row.fileUrl, '_blank')
  1119. } else {
  1120. this.$message.warning('暂无文件可查看')
  1121. }
  1122. },
  1123. // 下载文书
  1124. handleDownload(row) {
  1125. console.log(row)
  1126. if (!row.fileUrl) {
  1127. this.$message.warning('该文书暂无文件可下载')
  1128. return
  1129. }
  1130. // 创建隐藏的a标签进行下载
  1131. const link = document.createElement('a')
  1132. link.style.display = 'none'
  1133. link.href = row.fileUrl
  1134. // 设置下载文件名
  1135. link.download = row.name || '文书文件'
  1136. document.body.appendChild(link)
  1137. link.click()
  1138. document.body.removeChild(link)
  1139. this.$message.success('开始下载文件')
  1140. },
  1141. // 上传附件
  1142. handleUpload(row) {
  1143. console.log(row)
  1144. },
  1145. // 报送资料相关操作方法
  1146. handleFileView(row) {
  1147. console.log('查看文件:', row)
  1148. // 实际开发中可添加文件预览逻辑(如打开新窗口、弹窗展示等)
  1149. if (row.fileUrl) {
  1150. window.open(row.fileUrl, '_blank')
  1151. } else {
  1152. this.$message.warning('暂无文件可查看')
  1153. }
  1154. },
  1155. handleFileDownload(row) {
  1156. console.log('下载文件:', row)
  1157. if (!row.fileUrl) {
  1158. this.$message.warning('该资料暂无文件可下载')
  1159. return
  1160. }
  1161. // 创建隐藏的a标签进行下载
  1162. const link = document.createElement('a')
  1163. link.style.display = 'none'
  1164. link.href = row.fileUrl
  1165. // 设置下载文件名,优先使用资料名称
  1166. link.download = row.informationName || '下载文件'
  1167. document.body.appendChild(link)
  1168. link.click()
  1169. document.body.removeChild(link)
  1170. this.$message.success('开始下载文件')
  1171. },
  1172. // 报送资料文件上传
  1173. async handleFileUpload(row) {
  1174. // 创建文件选择器
  1175. const input = document.createElement('input')
  1176. input.type = 'file'
  1177. input.accept = '.pdf,.doc,.docx,.xls,.xlsx,.csv' // 允许的文件类型
  1178. input.onchange = async (event) => {
  1179. const file = event.target.files[0]
  1180. if (!file) return
  1181. try {
  1182. // 校验文件大小(50MB)
  1183. const maxSize = 50 * 1024 * 1024 // 50MB
  1184. if (file.size > maxSize) {
  1185. this.$message.error('文件大小不能超过50MB!')
  1186. return
  1187. }
  1188. // 校验文件格式
  1189. const allowedFormats = [
  1190. '.pdf',
  1191. '.doc',
  1192. '.docx',
  1193. '.xls',
  1194. '.xlsx',
  1195. '.csv',
  1196. ]
  1197. const fileName = file.name.toLowerCase()
  1198. const isValidFormat = allowedFormats.some((format) =>
  1199. fileName.endsWith(format)
  1200. )
  1201. if (!isValidFormat) {
  1202. this.$message.error(
  1203. '只允许上传.pdf,.doc,.docx,.xls,.xlsx,.csv格式的文件!'
  1204. )
  1205. return
  1206. }
  1207. // 显示上传加载提示
  1208. const loading = this.$baseLoading(1, '文件上传中...')
  1209. // 创建FormData并上传文件
  1210. const formData = new FormData()
  1211. formData.append('file', file)
  1212. // 调用上传API
  1213. const uploadRes = await uploadFile('/api/file/v1/upload', formData)
  1214. // 检查上传结果
  1215. if (!uploadRes || uploadRes.code !== 200 || !uploadRes.value) {
  1216. loading.close()
  1217. this.$message.error('文件上传失败,请稍后重试')
  1218. return
  1219. }
  1220. // 文件上传成功后,更新报送资料项
  1221. const fileInfo = uploadRes.value
  1222. const updateData = {
  1223. // id: row.id,
  1224. // taskId: this.taskId,
  1225. // projectId: this.projectId,
  1226. // informationType: row.informationType,
  1227. // informationName: row.informationName,
  1228. // informationRequire: row.informationRequire,
  1229. // formatRequired: row.formatRequired,
  1230. // isRequired: row.isRequired,
  1231. ...row,
  1232. fileUrl: fileInfo.savePath || fileInfo.url, // 更新文件URL
  1233. }
  1234. // 调用更新API
  1235. const updateRes = await addOrUpdateTaskRequirement(updateData)
  1236. loading.close()
  1237. if (updateRes && updateRes.code === 200) {
  1238. this.$message.success('文件上传并更新成功!')
  1239. // 刷新报送资料列表
  1240. this.getTaskRequirementPage()
  1241. } else {
  1242. this.$message.error('文件上传成功,但更新资料失败')
  1243. }
  1244. } catch (error) {
  1245. console.error('文件上传失败:', error)
  1246. this.$message.error('操作失败:' + (error.message || '未知错误'))
  1247. }
  1248. }
  1249. // 触发文件选择
  1250. input.click()
  1251. },
  1252. handleTemplateDownload(row) {
  1253. console.log('下载预置模版:', row)
  1254. if (!row.templateUrl && !row.fileUrl) {
  1255. this.$message.warning('该模版暂无文件可下载')
  1256. return
  1257. }
  1258. const downloadUrl = row.templateUrl || row.fileUrl
  1259. const link = document.createElement('a')
  1260. link.style.display = 'none'
  1261. link.href = downloadUrl
  1262. link.download = row.name ? `${row.name}_模版` : '模版文件'
  1263. document.body.appendChild(link)
  1264. link.click()
  1265. document.body.removeChild(link)
  1266. this.$message.success('开始下载模版')
  1267. },
  1268. handleDataUpload(row) {
  1269. console.log('上传模版数据:', row)
  1270. // 实际开发中可添加基于模版的数据上传逻辑
  1271. },
  1272. handleRemove(file, fileList) {
  1273. console.log(file, fileList)
  1274. },
  1275. handlePreview(file) {
  1276. console.log(file)
  1277. },
  1278. handleExceed(files, fileList) {
  1279. this.$message.warning(
  1280. `当前限制选择 3 个文件,本次选择了 ${files.length} 个文件,共选择了 ${
  1281. files.length + fileList.length
  1282. } 个文件`
  1283. )
  1284. },
  1285. beforeRemove(file, fileList) {
  1286. return this.$confirm(`确定移除 ${file.name}?`)
  1287. },
  1288. // 成本调查表点击事件(可点击时触发)
  1289. handleSurveyClick(row) {
  1290. console.log('点击成本调查表:', row.name)
  1291. // 实际逻辑:跳转页面、打开弹窗等
  1292. },
  1293. // 在线填报
  1294. handleOnlineFill(row) {
  1295. console.log('在线填报:', row.name)
  1296. this.currentSurveyData = row
  1297. this.onlineFillDialogVisible = true
  1298. },
  1299. // 修改已上传的表
  1300. handleModify(row) {
  1301. console.log('修改:', row.name)
  1302. // 实际逻辑:打开修改表单
  1303. },
  1304. // 数据下载
  1305. handleDataDownload(row) {
  1306. console.log('数据下载:', row.name)
  1307. if (!row.fileUrl && !row.dataUrl) {
  1308. this.$message.warning('该数据暂无文件可下载')
  1309. return
  1310. }
  1311. const downloadUrl = row.dataUrl || row.fileUrl
  1312. const link = document.createElement('a')
  1313. link.style.display = 'none'
  1314. link.href = downloadUrl
  1315. link.download = row.name ? `${row.name}_数据` : '数据文件'
  1316. document.body.appendChild(link)
  1317. link.click()
  1318. document.body.removeChild(link)
  1319. this.$message.success('开始下载数据')
  1320. },
  1321. // 补充材料相关方法
  1322. handleAddMaterial() {
  1323. this.materialDialogVisible = true
  1324. this.resetMaterialForm()
  1325. },
  1326. // 重置补充材料表单
  1327. resetMaterialForm() {
  1328. this.materialForm = {
  1329. informationType: '',
  1330. informationName: '',
  1331. informationRequire: '',
  1332. formatRequired: '',
  1333. fileList: [],
  1334. fileUrl: '',
  1335. }
  1336. if (this.$refs.materialForm) {
  1337. this.$refs.materialForm.resetFields()
  1338. }
  1339. },
  1340. // 补充材料弹窗取消
  1341. handleMaterialCancel() {
  1342. this.materialDialogVisible = false
  1343. this.resetMaterialForm()
  1344. },
  1345. // 补充材料弹窗确定
  1346. handleMaterialSubmit() {
  1347. this.$refs.materialForm.validate((valid) => {
  1348. if (valid) {
  1349. // 这里可以添加提交逻辑
  1350. console.log('补充材料表单数据:', this.materialForm)
  1351. addOrUpdateTaskRequirement({
  1352. taskId: this.taskId,
  1353. projectId: this.projectId,
  1354. informationType: this.materialForm.informationType,
  1355. informationName: this.materialForm.informationName,
  1356. informationRequire: this.materialForm.informationRequire,
  1357. formatRequired: this.materialForm.formatRequired,
  1358. fileUrl: this.materialForm.fileUrl,
  1359. })
  1360. .then((res) => {
  1361. console.log('补充材料提交成功', res)
  1362. if (res && res.code === 200) {
  1363. Message.success('补充材料提交成功')
  1364. this.materialDialogVisible = false
  1365. this.resetMaterialForm()
  1366. this.getTaskRequirementPage()
  1367. } else {
  1368. Message.error('补充材料提交失败')
  1369. }
  1370. })
  1371. .catch((err) => {
  1372. console.error('补充材料提交失败', err)
  1373. Message.error('补充材料提交失败')
  1374. })
  1375. } else {
  1376. Message.error('请完善表单信息')
  1377. return false
  1378. }
  1379. })
  1380. },
  1381. // 上传
  1382. // handleMaterialUpload(row, type) {
  1383. // let loading = null
  1384. // // 第一步:创建文件选择器
  1385. // const input = document.createElement('input')
  1386. // input.type = 'file'
  1387. // input.accept = '.pdf,.doc,.docx,.xls,.xlsx,.csv' // 允许的文件类型
  1388. // input.onchange = async (event) => {
  1389. // const file = event.target.files[0]
  1390. // if (!file) return
  1391. // try {
  1392. // // 校验文件大小(50MB)
  1393. // const maxSize = 50 * 1024 * 1024 // 50MB
  1394. // if (file.size > maxSize) {
  1395. // this.$message.error('文件大小不能超过50MB!')
  1396. // return
  1397. // }
  1398. // // 校验文件格式
  1399. // const allowedFormats = [
  1400. // '.pdf',
  1401. // '.doc',
  1402. // '.docx',
  1403. // '.xls',
  1404. // '.xlsx',
  1405. // 'csv',
  1406. // ]
  1407. // const fileName = file.name.toLowerCase()
  1408. // const isValidFormat = allowedFormats.some((format) =>
  1409. // fileName.endsWith(format)
  1410. // )
  1411. // if (!isValidFormat) {
  1412. // this.$message.error(
  1413. // '只允许上传.pdf,.doc,.docx,.xls,.xlsx,.csv格式的文件!'
  1414. // )
  1415. // return
  1416. // }
  1417. // // 显示遮罩层
  1418. // loading = this.$baseLoading(1, '文件上传中...')
  1419. // // 第三步:创建FormData并上传文件
  1420. // const formData = new FormData()
  1421. // formData.append('file', file)
  1422. // // 先调用上传API
  1423. // const uploadRes = await uploadFile('/api/file/v1/upload', formData)
  1424. // // 第四步:检查上传结果
  1425. // if (!uploadRes || !uploadRes.value) {
  1426. // // this.$message.error('文件上传失败!');
  1427. // return
  1428. // }
  1429. // // 第五步:文件上传成功后,再更新数据
  1430. // const fileInfo = uploadRes.value
  1431. // // 创建更新数据对象
  1432. // const updateData = {
  1433. // ...row,
  1434. // fileUrl: fileInfo?.savePath, // 更新扫描件URL
  1435. // }
  1436. // // 第六步:调用更新API
  1437. // // await updateCostProjectDocument(updateData)
  1438. // // // 第七步:更新成功,显示提示并刷新
  1439. // // this.$message.success('文件上传成功并更新数据!')
  1440. // // this.$emit('refresh', this.project.projectId) // 通知父组件刷新
  1441. // } catch (error) {
  1442. // // 错误处理
  1443. // this.$message.error('操作失败:' + (error.message || '未知错误'))
  1444. // } finally {
  1445. // // 关闭遮罩层
  1446. // loading.close()
  1447. // }
  1448. // }
  1449. // // 触发文件选择
  1450. // input.click()
  1451. // },
  1452. // 文件上传前验证
  1453. beforeMaterialUpload(file) {
  1454. const allowedTypes = [
  1455. 'application/pdf',
  1456. 'application/msword',
  1457. 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  1458. 'application/vnd.ms-excel',
  1459. 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  1460. 'text/csv',
  1461. ]
  1462. const allowedExtensions = [
  1463. '.pdf',
  1464. '.doc',
  1465. '.docx',
  1466. '.xls',
  1467. '.xlsx',
  1468. '.csv',
  1469. ]
  1470. const fileExtension = '.' + file.name.split('.').pop().toLowerCase()
  1471. // 检查文件格式
  1472. const isCorrectType = allowedTypes.includes(file.type)
  1473. const isCorrectExtension = allowedExtensions.includes(fileExtension)
  1474. if (!isCorrectType && !isCorrectExtension) {
  1475. this.$message.error(
  1476. '只允许上传 pdf, doc, docx, xls, xlsx, csv 格式的文件!'
  1477. )
  1478. return false
  1479. }
  1480. // 检查文件大小 (50MB)
  1481. const isLt50M = file.size / 1024 / 1024 < 50
  1482. if (!isLt50M) {
  1483. this.$message.error('文件大小不能超过 50MB!')
  1484. return false
  1485. }
  1486. return true
  1487. },
  1488. // 自定义上传方法
  1489. async handleMaterialUpload(options) {
  1490. const { file, onProgress, onSuccess, onError } = options
  1491. // 检查是否已经上传了文件
  1492. if (
  1493. this.materialForm.fileList &&
  1494. this.materialForm.fileList.length >= 1
  1495. ) {
  1496. this.$message.warning('只能上传一个文件,请先删除已上传的文件')
  1497. // 手动触发onError回调
  1498. if (onError) {
  1499. onError(new Error('只能上传一个文件'))
  1500. }
  1501. return
  1502. }
  1503. const formData = new FormData()
  1504. formData.append('file', file)
  1505. try {
  1506. // 显示上传进度
  1507. if (onProgress) {
  1508. onProgress({ percent: 0 })
  1509. }
  1510. // 调用上传API
  1511. const uploadRes = await uploadFile('/api/file/v1/upload', formData, {
  1512. onUploadProgress: (progressEvent) => {
  1513. // 计算上传进度
  1514. if (progressEvent.total) {
  1515. const percent = Math.round(
  1516. (progressEvent.loaded * 100) / progressEvent.total
  1517. )
  1518. if (onProgress) {
  1519. onProgress({ percent })
  1520. }
  1521. }
  1522. },
  1523. })
  1524. // 检查上传结果
  1525. if (uploadRes && uploadRes.code === 200 && uploadRes.value) {
  1526. const fileInfo = uploadRes.value
  1527. // 构造文件信息对象,符合element-ui upload组件的格式
  1528. const fileObj = {
  1529. uid: file.uid,
  1530. name: file.name,
  1531. status: 'success',
  1532. size: file.size,
  1533. response: fileInfo,
  1534. url: fileInfo.savePath || fileInfo.url,
  1535. }
  1536. if (onSuccess) {
  1537. onSuccess(fileInfo, fileObj)
  1538. this.materialForm.fileUrl = fileInfo.savePath || fileInfo.url
  1539. }
  1540. this.$message.success(`${file.name} 上传成功`)
  1541. } else {
  1542. throw new Error(uploadRes?.message || '上传失败,请稍后重试')
  1543. }
  1544. } catch (error) {
  1545. console.error('文件上传失败:', error)
  1546. this.$message.error(`文件上传失败:${error.message || '未知错误'}`)
  1547. if (onError) {
  1548. onError(error)
  1549. }
  1550. }
  1551. },
  1552. // 上传成功回调
  1553. handleMaterialUploadSuccess(response, file, fileList) {
  1554. // 更新文件列表,添加文件URL信息
  1555. this.materialForm.fileList = fileList.map((item) => {
  1556. if (item.uid === file.uid && response) {
  1557. return {
  1558. ...item,
  1559. url: response.savePath || response.url || item.url,
  1560. response: response,
  1561. }
  1562. }
  1563. return item
  1564. })
  1565. },
  1566. // 上传进度回调
  1567. handleMaterialUploadProgress(event, file, fileList) {
  1568. // 可以在这里显示上传进度,element-ui 会自动处理
  1569. },
  1570. // 上传失败回调
  1571. handleMaterialUploadError(err, file, fileList) {
  1572. console.error('文件上传错误:', err)
  1573. this.$message.error(`${file.name} 上传失败`)
  1574. // 从文件列表中移除失败的文件
  1575. this.materialForm.fileList = fileList.filter(
  1576. (item) => item.uid !== file.uid
  1577. )
  1578. },
  1579. // 超出文件数量限制
  1580. handleMaterialExceed(files, fileList) {
  1581. this.$message.warning(
  1582. `当前限制选择 5 个文件,本次选择了 ${files.length} 个文件,共选择了 ${fileList.length} 个文件`
  1583. )
  1584. },
  1585. // 移除文件
  1586. handleMaterialRemove(file, fileList) {
  1587. this.materialForm.fileList = fileList
  1588. this.$message.info(`${file.name} 已移除`)
  1589. },
  1590. // 成本调查表分页 - 页码改变
  1591. handleCostSurveyPageChange(page) {
  1592. this.costSurveyPagination.currentPage = page
  1593. // 如果成本调查表数据来自API,可以在这里调用API
  1594. // this.getCostSurveyData()
  1595. },
  1596. // 成本调查表分页 - 每页条数改变
  1597. handleCostSurveySizeChange(size) {
  1598. this.costSurveyPagination.pageSize = size
  1599. this.costSurveyPagination.currentPage = 1
  1600. // 如果成本调查表数据来自API,可以在这里调用API
  1601. // this.getCostSurveyData()
  1602. },
  1603. },
  1604. }
  1605. </script>
  1606. <style scoped lang="scss">
  1607. .drawer-title {
  1608. font-size: 18px;
  1609. font-weight: 600;
  1610. color: #303133;
  1611. }
  1612. .audit-task-manage-container {
  1613. padding: 20px;
  1614. height: 100%;
  1615. overflow-y: auto;
  1616. h3 {
  1617. text-align: center;
  1618. margin-top: 0;
  1619. margin-bottom: 20px;
  1620. font-size: 18px;
  1621. font-weight: bold;
  1622. }
  1623. }
  1624. .cost-opinion-section,
  1625. .feedback-section {
  1626. border: 1px solid #dcdcdc;
  1627. border-radius: 4px;
  1628. padding: 20px;
  1629. margin-bottom: 20px;
  1630. background-color: #f9f9f9;
  1631. }
  1632. .opinion-item,
  1633. .feedback-item {
  1634. margin-bottom: 20px;
  1635. display: flex;
  1636. align-items: center;
  1637. label {
  1638. width: 120px;
  1639. margin-bottom: 8px;
  1640. font-weight: bold;
  1641. color: #1549ad;
  1642. }
  1643. }
  1644. .file-info {
  1645. margin-bottom: 10px;
  1646. }
  1647. .file-info a {
  1648. color: #0066cc;
  1649. text-decoration: none;
  1650. }
  1651. .file-info a:hover {
  1652. text-decoration: underline;
  1653. }
  1654. .upload-btn {
  1655. background-color: #007bff;
  1656. color: #fff;
  1657. border: none;
  1658. border-radius: 4px;
  1659. padding: 8px 16px;
  1660. cursor: pointer;
  1661. }
  1662. .upload-btn:hover {
  1663. background-color: #0056b3;
  1664. }
  1665. .cost-period-container {
  1666. display: flex;
  1667. align-items: flex-start;
  1668. }
  1669. .add-cost-year-btn {
  1670. margin-right: 10px;
  1671. }
  1672. .delete-cost-year-btn {
  1673. margin-left: 10px;
  1674. }
  1675. .cost-years-wrapper {
  1676. flex: 1;
  1677. }
  1678. .cost-year-item {
  1679. margin-bottom: 10px;
  1680. display: flex;
  1681. align-items: center;
  1682. }
  1683. .ml10 {
  1684. margin-left: 10px;
  1685. }
  1686. .text-danger {
  1687. color: #d9001b;
  1688. }
  1689. // 类别头行样式
  1690. ::v-deep .category-header-row {
  1691. background-color: #f5f7fa !important;
  1692. td {
  1693. background-color: #f5f7fa !important;
  1694. padding: 12px 16px !important;
  1695. }
  1696. }
  1697. .category-header-cell {
  1698. font-weight: 700;
  1699. color: #303133;
  1700. padding-left: 16px;
  1701. }
  1702. // 侧边弹窗样式优化
  1703. ::v-deep .el-drawer__body {
  1704. overflow-y: auto;
  1705. padding: 0;
  1706. }
  1707. ::v-deep .el-drawer__header {
  1708. margin-bottom: 0;
  1709. padding: 20px;
  1710. border-bottom: 1px solid #e4e7ed;
  1711. }
  1712. ::v-deep .el-drawer__body {
  1713. padding: 0;
  1714. }
  1715. </style>