index.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. <template>
  2. <div class="cost-audit-management">
  3. <!-- 成本审核项目列表页面 -->
  4. <div class="audit-list-container">
  5. <div class="search-section">
  6. 监审项目名称:
  7. <el-input
  8. v-model="searchQuery"
  9. placeholder="请输入监审项目名称"
  10. style="width: 300px; margin-right: 10px"
  11. clearable
  12. />
  13. <el-button
  14. type="primary"
  15. icon="iconfont-5039297 icon-chaxun"
  16. @click="handleSearch"
  17. >
  18. 查询
  19. </el-button>
  20. <el-button
  21. type="primary"
  22. plain
  23. icon="iconfont-5039297 icon-zhongzhi"
  24. style="margin-left: 10px"
  25. @click="handleReset"
  26. >
  27. 重置
  28. </el-button>
  29. </div>
  30. <el-table
  31. v-loading="loading"
  32. class="mb10"
  33. :data="auditProjectList"
  34. style="width: 100%"
  35. border
  36. default-expand-all
  37. row-key="id"
  38. :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
  39. >
  40. <el-table-column
  41. type="index"
  42. label="序号"
  43. width="80"
  44. header-align="center"
  45. align="center"
  46. >
  47. <template slot-scope="scope">
  48. <!-- 只显示父节点的序号 -->
  49. <span v-if="scope.row.children">
  50. {{ getParentNodeIndex(scope.row) }}
  51. </span>
  52. <span v-else></span>
  53. </template>
  54. </el-table-column>
  55. <el-table-column
  56. prop="projectName"
  57. label="成本监审项目名称"
  58. show-overflow-tooltip
  59. header-align="center"
  60. align="left"
  61. />
  62. <el-table-column
  63. prop="auditObject"
  64. label="监审对象"
  65. header-align="center"
  66. align="left"
  67. />
  68. <el-table-column
  69. prop="auditPeriod"
  70. label="监审期间"
  71. header-align="center"
  72. align="center"
  73. width="120"
  74. />
  75. <el-table-column
  76. prop="source"
  77. label="立项来源"
  78. header-align="center"
  79. align="center"
  80. width="100"
  81. />
  82. <el-table-column
  83. prop="form"
  84. label="监审形式"
  85. header-align="center"
  86. align="center"
  87. width="100"
  88. />
  89. <el-table-column
  90. prop="currentNodeName"
  91. label="状态"
  92. align="center"
  93. width="150"
  94. >
  95. <template slot-scope="scope">
  96. <span v-if="!scope.row.isSubTask">
  97. <span v-if="scope.row.currentNode === 'gd'">
  98. {{ scope.row.currentNodeName }}
  99. </span>
  100. <span v-else>
  101. {{ scope.row.currentNodeName }}-{{ scope.row.statusName }}
  102. </span>
  103. </span>
  104. <span v-else>{{ scope.row.statusName }}</span>
  105. </template>
  106. </el-table-column>
  107. <el-table-column label="操作" align="center" width="260">
  108. <template slot-scope="scope">
  109. <span v-if="!scope.row.isSubTask" class="action-buttons">
  110. <el-button type="text" @click="handleViewTaskDetail(scope.row)">
  111. 任务详情
  112. </el-button>
  113. <el-button
  114. v-if="scope.row.status != '300'"
  115. type="text"
  116. @click="handleOpenMainDetails(scope.row)"
  117. >
  118. 任务办理
  119. </el-button>
  120. <el-button type="text" @click="handleCheckRecord(scope.row)">
  121. 备忘录
  122. </el-button>
  123. </span>
  124. <span v-if="scope.row.isSubTask" class="action-buttons">
  125. <el-button
  126. v-if="
  127. scope.row.currentNode === 'clcs' &&
  128. (scope.row.status === '200' || scope.row.status === '600')
  129. "
  130. type="text"
  131. @click="handleOpenDetails(scope.row)"
  132. >
  133. 资料初审
  134. </el-button>
  135. <el-button
  136. v-if="
  137. (scope.row.currentNode === 'sdsh' ||
  138. scope.row.currentNode === 'yjfk') &&
  139. scope.row.status === '200'
  140. "
  141. type="text"
  142. @click="handleOpenDetails(scope.row)"
  143. >
  144. 成本审核
  145. </el-button>
  146. <el-button
  147. v-if="
  148. scope.row.currentNode === 'yjgz' && scope.row.status === '200'
  149. "
  150. type="text"
  151. @click="handleOpenDetails(scope.row)"
  152. >
  153. 意见告知
  154. </el-button>
  155. <el-button
  156. v-if="
  157. scope.row.currentNode === 'jtsy' && scope.row.status === '200'
  158. "
  159. type="text"
  160. @click="handleOpenDetails(scope.row, 'shenhe')"
  161. >
  162. 审核
  163. </el-button>
  164. <el-button
  165. v-if="
  166. scope.row.currentNode === 'jtsy' && scope.row.status === '260'
  167. "
  168. type="text"
  169. @click="handleOpenDetails(scope.row, 'auditOpinion')"
  170. >
  171. 审核
  172. </el-button>
  173. <!-- <el-button
  174. v-if="scope.row.status === '300'"
  175. type="text"
  176. @click="handleHf(scope.row)"
  177. >
  178. 恢复
  179. </el-button> -->
  180. <el-button
  181. type="text"
  182. @click="handleMessage(scope.row, 'chengben')"
  183. >
  184. 查看
  185. </el-button>
  186. </span>
  187. </template>
  188. </el-table-column>
  189. </el-table>
  190. <el-pagination
  191. background
  192. layout="total, sizes, prev, pager, next"
  193. :current-page="pageNum"
  194. :page-sizes="[10, 20, 30, 40]"
  195. :page-size="pageSize"
  196. :total="total"
  197. @current-change="handleCurrentChange"
  198. @size-change="handleSizeChange"
  199. />
  200. </div>
  201. <!-- 详情弹窗组件 -->
  202. <details-dialog
  203. :id="selectedProject && selectedProject.id"
  204. ref="detailsRef"
  205. :task-info="selectedProject"
  206. :selected-project="selectedProject"
  207. :visible.sync="detailsVisible"
  208. :current-node="selectedProject && selectedProject.currentNode"
  209. :current-status="selectedProject && selectedProject.status"
  210. :is-shenhe="isShenhe"
  211. @close="handleDetailsClose"
  212. @refresh="handleRefresh"
  213. />
  214. <!-- 主详情弹窗组件 -->
  215. <mainDetailsDialog
  216. :id="selectedProject && selectedProject.id"
  217. ref="mainDetailsRef"
  218. :project="selectedProject"
  219. :visible.sync="mainDetailsVisible"
  220. :current-node="selectedProject && selectedProject.currentNode"
  221. :current-status="selectedProject && selectedProject.status"
  222. @close="handleMainDetailsClose"
  223. @refresh="handleMainRefresh"
  224. />
  225. <taskInfo ref="taskInfo" />
  226. <!-- 成本监审信息弹窗 -->
  227. <cbjs-info
  228. :id="cbjsInfoData && cbjsInfoData.id"
  229. :selected-project="cbjsInfoData"
  230. :visible.sync="cbjsInfoVisible"
  231. :current-node="cbjsInfoData && cbjsInfoData.currentNode"
  232. :current-status="cbjsInfoData && cbjsInfoData.status"
  233. />
  234. <taskDetail ref="taskDetail" />
  235. <!-- 成本监审任务制定弹窗(用于“任务详情”只读查看) -->
  236. <task-customized-release-dialog
  237. :visible.sync="taskReleaseDialogVisible"
  238. :project="project"
  239. :selected-project="selectedProject"
  240. :is-view="true"
  241. @backToList="taskReleaseDialogVisible = false"
  242. @close="taskReleaseDialogVisible = false"
  243. />
  244. </div>
  245. </template>
  246. <script>
  247. import { doProcessBtn } from '@/api/dataPreliminaryReview'
  248. import detailsDialog from './details.vue'
  249. import mainDetailsDialog from './mainDetails.vue'
  250. // 成本监审任务列表API
  251. import { getReviewTaskList } from '@/api/audit/auditIndex'
  252. import taskInfo from '@/components/task/taskInfo.vue'
  253. import cbjsInfo from '@/components/task/cbjsInfo.vue'
  254. import taskDetail from '@/components/task/taskDetail.vue'
  255. import TaskCustomizedReleaseDialog from '@/components/task/TaskCustomizedReleaseDialog.vue'
  256. import { getCostProjectDetail } from '@/api/taskCustomizedRelease.js'
  257. import { dictMixin } from '@/mixins/useDict'
  258. export default {
  259. name: 'CostAuditManagement',
  260. components: {
  261. detailsDialog,
  262. taskInfo,
  263. cbjsInfo,
  264. mainDetailsDialog,
  265. taskDetail,
  266. TaskCustomizedReleaseDialog,
  267. },
  268. mixins: [dictMixin],
  269. data() {
  270. return {
  271. isShenhe: false,
  272. dictData: {
  273. projectProposal: [],
  274. },
  275. // 分页相关
  276. pageNum: 1,
  277. pageSize: 10,
  278. total: 0,
  279. // 搜索相关
  280. searchQuery: '',
  281. // 列表数据
  282. auditProjectList: [],
  283. // 加载状态
  284. loading: false,
  285. // 详情弹窗相关
  286. detailsVisible: false,
  287. selectedProject: null,
  288. // cbjsInfo弹窗相关
  289. cbjsInfoVisible: false,
  290. cbjsInfoData: null,
  291. mainDetailsVisible: false,
  292. // 任务详情(项目)弹窗
  293. taskReleaseDialogVisible: false,
  294. project: {},
  295. isView: true,
  296. }
  297. },
  298. created() {
  299. this.loadAuditProjectList()
  300. },
  301. methods: {
  302. // 获取父节点的连续序号
  303. getParentNodeIndex(row) {
  304. // 过滤出所有父节点
  305. const parentNodes = this.auditProjectList.filter(
  306. (item) => item.children && item.children.length > 0
  307. )
  308. // 找到当前行在父节点数组中的索引
  309. const index = parentNodes.findIndex((item) => item.id === row.id)
  310. // 返回序号(索引+1)
  311. return index + 1
  312. },
  313. // 加载审核项目列表
  314. async loadAuditProjectList() {
  315. try {
  316. this.loading = true
  317. // 调用API获取数据
  318. const params = {
  319. pageNum: this.pageNum,
  320. pageSize: this.pageSize,
  321. projectName: this.searchQuery,
  322. }
  323. const response = await getReviewTaskList(params)
  324. // 根据API返回格式处理数据
  325. if (response.state && response.value) {
  326. // 获取记录列表
  327. const records = response.value.records || []
  328. // 转换数据格式,将childTasks转换为children以适应表格组件
  329. this.auditProjectList = records.map((record) => {
  330. return {
  331. id: record.id,
  332. projectName: record.projectName,
  333. auditObject: record.auditedUnitName,
  334. auditPeriod: record.auditPeriod,
  335. source: this.getSourceTypeText(record.sourceType),
  336. // form: this.getAuditTypeText(record.auditType),
  337. form: record.auditTypeName,
  338. status: this.getStatusText(record.status),
  339. statusName: record.statusName,
  340. isSubTask: record.pid !== '0',
  341. currentNodeName: record.currentNodeName,
  342. currentNode: record.currentNode,
  343. projectId: record.projectId,
  344. auditedUnitId: record.auditedUnitId,
  345. children: record.childTasks
  346. ? record.childTasks.map((child) => ({
  347. id: child.id,
  348. projectName: child.projectName,
  349. auditObject: child.auditedUnitName,
  350. auditPeriod: record.auditPeriod, // 子任务可能使用父任务的审核期间
  351. source: '',
  352. form: '',
  353. currentNode: child.currentNode,
  354. status: child.status,
  355. statusName: child.statusName,
  356. isSubTask: true,
  357. projectId: child.projectId,
  358. auditedUnitId: child.auditedUnitId,
  359. taskId: child.id,
  360. catalogId: child.catalogId,
  361. }))
  362. : [],
  363. }
  364. })
  365. // 设置总数
  366. this.total = response.value.total || 0
  367. } else {
  368. this.auditProjectList = []
  369. this.total = 0
  370. this.$message.warning('未获取到审核项目数据')
  371. }
  372. } catch (error) {
  373. // this.$message.error('加载审核项目列表失败')
  374. console.error('加载审核项目列表失败:', error)
  375. } finally {
  376. this.loading = false
  377. }
  378. },
  379. // 获取来源类型文本
  380. getSourceTypeText(type) {
  381. return this.getDictName('projectProposal', type)
  382. },
  383. // 获取审核类型文本
  384. getAuditTypeText(type) {
  385. const typeMap = {
  386. 1: '定期监审',
  387. 2: '定调价监审',
  388. // 可根据实际需求补充其他类型
  389. }
  390. return typeMap[type] || type
  391. },
  392. // 获取状态文本
  393. getStatusText(status) {
  394. const statusMap = {
  395. ccls: '资料初审',
  396. 200: '审核通过',
  397. clcs: '审核中', // 添加clcs状态映射为审核中
  398. // 可根据实际需求补充其他状态
  399. }
  400. return statusMap[status] || status
  401. },
  402. // 搜索
  403. handleSearch() {
  404. // 查询时重置到第一页
  405. this.pageNum = 1
  406. this.loadAuditProjectList()
  407. },
  408. // 重置
  409. handleReset() {
  410. // 重置搜索条件
  411. this.searchQuery = ''
  412. // 重新加载数据
  413. this.loadAuditProjectList()
  414. },
  415. // 查看任务详情
  416. handleViewTaskDetail(row) {
  417. // 使用成本监审任务制定弹窗(只读)
  418. this.openTaskReleaseDialog(row)
  419. },
  420. // 打开成本监审任务制定弹窗(只读查看)
  421. openTaskReleaseDialog(row) {
  422. if (!row) return
  423. const projectId =
  424. row.projectId || row.projectID || row.id || row.taskId || ''
  425. if (!projectId) {
  426. this.$message &&
  427. this.$message.warning &&
  428. this.$message.warning('缺少项目ID,无法查看详情')
  429. return
  430. }
  431. this.isView = true
  432. this.selectedProject = row
  433. getCostProjectDetail({ id: projectId })
  434. .then((res) => {
  435. this.project = (res && res.value) || {}
  436. this.taskReleaseDialogVisible = true
  437. })
  438. .catch(() => {
  439. // 回退:接口失败时至少展示当前行数据
  440. this.project = row || {}
  441. this.taskReleaseDialogVisible = true
  442. })
  443. },
  444. // 打开详情弹窗
  445. handleOpenDetails(project, type) {
  446. this.selectedProject = project
  447. this.isShenhe = type === 'shenhe'
  448. this.detailsVisible = true
  449. this.$nextTick(() => {
  450. if (this.$refs.detailsRef) {
  451. this.$refs.detailsRef.open()
  452. }
  453. // “审核1”:打开弹窗后,自动切换到“成本审核意见”页签
  454. // 注意:details.vue 在 visible watcher 里会调用 setActiveTab(),可能覆盖父组件设置
  455. // 因此这里用一次异步延迟,确保在其默认逻辑之后再强制切换
  456. if (type === 'auditOpinion') {
  457. setTimeout(() => {
  458. const dlg = this.$refs.detailsRef
  459. if (!dlg) return
  460. dlg.activeTab = 'auditOpinion'
  461. // 主动触发一次 tab-click 对应逻辑(刷新接口)
  462. if (typeof dlg.handleTabClick === 'function') {
  463. dlg.handleTabClick({ name: 'auditOpinion' })
  464. }
  465. }, 80)
  466. }
  467. })
  468. },
  469. handleOpenMainDetails(project) {
  470. // console.log('project', project)
  471. this.selectedProject = project
  472. this.mainDetailsVisible = true
  473. },
  474. // 详情弹窗关闭处理
  475. handleDetailsClose() {
  476. this.selectedProject = null
  477. this.detailsVisible = false
  478. // 可以在这里添加刷新列表的逻辑
  479. },
  480. // 刷新表格数据(可选重置到第一页)
  481. handleRefresh(payload) {
  482. if (payload && payload.resetToFirst) {
  483. this.pageNum = 1
  484. }
  485. this.loadAuditProjectList()
  486. },
  487. // 主详情弹窗关闭处理
  488. handleMainDetailsClose() {
  489. this.selectedProject = null
  490. this.mainDetailsVisible = false
  491. },
  492. // 主详情刷新处理
  493. handleMainRefresh() {
  494. // 刷新列表数据
  495. this.loadAuditProjectList()
  496. },
  497. // 查记录
  498. handleCheckRecord(project) {
  499. // memoManage
  500. this.$router.push({
  501. name: 'memoManage',
  502. // params: { projectId: project.id }
  503. })
  504. },
  505. // 恢复任务
  506. async handleHf(row) {
  507. if (!row || !row.id) {
  508. this.$message.error('缺少任务ID')
  509. return
  510. }
  511. // 弹出确认对话框
  512. this.$confirm('确定要恢复此任务吗?', '恢复任务', {
  513. confirmButtonText: '确定',
  514. cancelButtonText: '取消',
  515. type: 'warning',
  516. })
  517. .then(async () => {
  518. try {
  519. const params = {
  520. taskId: row.id,
  521. key: 2,
  522. status: 200,
  523. processNodeKey: row.currentNode,
  524. }
  525. const response = await doProcessBtn(params)
  526. if (response && response.code === 200) {
  527. this.$message.success('恢复任务成功')
  528. // 刷新列表
  529. this.loadAuditProjectList()
  530. } else {
  531. this.$message.error(response?.message || '恢复任务失败')
  532. }
  533. } catch (error) {
  534. // this.$message.error('恢复任务失败')
  535. console.error('恢复任务失败:', error)
  536. }
  537. })
  538. .catch(() => {
  539. // 用户取消操作
  540. this.$message.info('已取消恢复任务')
  541. })
  542. },
  543. // 分页处理
  544. handleSizeChange(size) {
  545. this.pageSize = size
  546. this.loadAuditProjectList()
  547. },
  548. handleCurrentChange(current) {
  549. this.pageNum = current
  550. this.loadAuditProjectList()
  551. },
  552. // 查看 - 修改为打开cbjsInfo弹窗
  553. handleMessage(row, type) {
  554. if (type === 'chengben') {
  555. this.cbjsInfoData = row
  556. this.cbjsInfoVisible = true
  557. } else {
  558. this.$refs.taskInfo.open(row, type)
  559. }
  560. },
  561. },
  562. }
  563. </script>
  564. <style scoped>
  565. .cost-audit-management {
  566. padding: 20px;
  567. }
  568. /* 列表页面样式 */
  569. .search-section {
  570. margin-bottom: 20px;
  571. }
  572. .action-buttons {
  573. font-size: 12px;
  574. }
  575. .action-buttons a {
  576. color: #409eff;
  577. text-decoration: none;
  578. }
  579. .separator {
  580. margin: 0 5px;
  581. color: #999;
  582. }
  583. .note-section {
  584. margin-top: 20px;
  585. }
  586. .note-text {
  587. color: #f56c6c;
  588. font-size: 12px;
  589. margin: 5px 0;
  590. }
  591. /* 详情页面样式 */
  592. .audit-detail-container {
  593. margin-top: 20px;
  594. }
  595. .detail-form {
  596. margin-top: 20px;
  597. }
  598. .tab-content {
  599. padding: 20px;
  600. background-color: #f9f9f9;
  601. min-height: 200px;
  602. }
  603. /* 办理页面样式 */
  604. .audit-process-container {
  605. margin-top: 20px;
  606. }
  607. .process-actions {
  608. margin-bottom: 20px;
  609. }
  610. .process-actions .el-button {
  611. margin-right: 10px;
  612. }
  613. /* 响应式设计 */
  614. @media (max-width: 768px) {
  615. .process-steps {
  616. flex-direction: column;
  617. }
  618. .step-line {
  619. width: 2px;
  620. height: 20px;
  621. margin: 5px 0;
  622. }
  623. .documents-layout {
  624. flex-direction: column;
  625. }
  626. .documents-type-list {
  627. width: 100%;
  628. margin-right: 0;
  629. margin-bottom: 20px;
  630. }
  631. .meeting-form .el-row {
  632. flex-direction: column;
  633. }
  634. .meeting-form .el-col {
  635. width: 100%;
  636. }
  637. }
  638. </style>