taskFillIn.vue 75 KB

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