index.vue 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062
  1. <template>
  2. <div class="task-progress-manage">
  3. <!-- 页面标题 -->
  4. <!-- <h2>成本监审任务进度跟踪</h2> -->
  5. <div v-if="activeView == 'list'">
  6. <div class="search-container">
  7. <el-form :inline="true" :model="searchForm" class="demo-form-inline">
  8. <el-form-item label="年度:">
  9. <el-date-picker
  10. v-model="searchForm.year"
  11. type="year"
  12. placeholder="选择年"
  13. format="yyyy"
  14. value-format="yyyy"
  15. ></el-date-picker>
  16. </el-form-item>
  17. <el-form-item label="监审项目名称:">
  18. <el-input
  19. v-model="searchForm.projectName"
  20. placeholder="请输入监审项目名称"
  21. clearable
  22. maxlength="30"
  23. ></el-input>
  24. </el-form-item>
  25. <el-form-item>
  26. <el-button
  27. type="primary"
  28. icon="el-icon-search"
  29. @click="handleSearch"
  30. >
  31. 搜索
  32. </el-button>
  33. <el-button
  34. plain
  35. type="primary"
  36. icon="el-icon-refresh"
  37. @click="handleReset"
  38. >
  39. 重置
  40. </el-button>
  41. </el-form-item>
  42. </el-form>
  43. </div>
  44. <!-- 数据表格 -->
  45. <cost-audit-table
  46. v-loading="loading"
  47. :table-data="tableData"
  48. :columns="tableColumns"
  49. :border="true"
  50. :row-class-name="getRowClassName"
  51. :show-pagination="true"
  52. :pagination="pagination"
  53. :table-props="{
  54. rowKey: 'id',
  55. treeProps: { children: 'children', hasChildren: 'hasChildren' },
  56. defaultExpandAll: true,
  57. showExpandColumn: false,
  58. }"
  59. @pagination-change="handlePaginationChange"
  60. >
  61. <template #projectName="{ row }">
  62. <span v-if="row.isSubTask" class="link-text" @click="handleView(row)">
  63. {{ row.projectName }}
  64. </span>
  65. <span v-else class="link-text" @click="handleViewTaskDetail(row)">
  66. {{ row.projectName }}
  67. </span>
  68. </template>
  69. <!-- 预警列自定义内容 -->
  70. <template #warning="{ row }">
  71. <span :class="['warning-point', computeWarning(row)]"></span>
  72. </template>
  73. <!-- 操作列自定义内容 -->
  74. <template #action="{ row }">
  75. <template v-if="row.isSubTask">
  76. <el-button size="mini" type="text" @click="handleView(row)">
  77. 查看
  78. </el-button>
  79. <el-button
  80. v-if="row.status != 400 && row.status != 300"
  81. size="mini"
  82. type="text"
  83. @click="handlePause(row)"
  84. >
  85. 中止
  86. </el-button>
  87. <el-button
  88. v-if="row.status != 400 && row.status != 300"
  89. size="mini"
  90. type="text"
  91. @click="handleUrgeReporting(row)"
  92. >
  93. 催报
  94. </el-button>
  95. </template>
  96. <template v-else>
  97. <el-button
  98. size="mini"
  99. type="text"
  100. @click="handleViewTaskDetail(row)"
  101. >
  102. 详情
  103. </el-button>
  104. <el-button
  105. v-if="row.status != 400 && row.status != 300"
  106. size="mini"
  107. type="text"
  108. @click="handleDelegate(row)"
  109. >
  110. 催办
  111. </el-button>
  112. <el-button
  113. v-if="row.status != 400 && row.status != 300"
  114. size="mini"
  115. type="text"
  116. @click="handleUrge(row)"
  117. >
  118. 督办
  119. </el-button>
  120. <el-button
  121. v-if="row.status != 400 && row.status != 300"
  122. size="mini"
  123. type="text"
  124. @click="handleToDo(row)"
  125. >
  126. 代办
  127. </el-button>
  128. </template>
  129. </template>
  130. <template slot="empty">
  131. <Empty></Empty>
  132. </template>
  133. </cost-audit-table>
  134. </div>
  135. <!-- 详情内容 -->
  136. <!-- <div v-if="activeView == 'detail'" class="detail-content">
  137. <detail-tabs :task-data="taskData" :project="project" @detailClose="handleDetailClose"></detail-tabs>
  138. </div> -->
  139. <!-- 任务中止表单弹窗 -->
  140. <el-dialog
  141. title="任务中止"
  142. :visible.sync="showPauseForm"
  143. width="40%"
  144. :close-on-click-modal="false"
  145. >
  146. <div class="dialog-content">
  147. <el-form ref="pauseForm" :model="pauseForm" label-width="100px">
  148. <el-form-item label="中止原因:">
  149. <el-input
  150. v-model="pauseForm.reason"
  151. type="textarea"
  152. rows="5"
  153. placeholder="请输入中止原因"
  154. maxlength="500"
  155. show-word-limit
  156. ></el-input>
  157. </el-form-item>
  158. </el-form>
  159. </div>
  160. <div slot="footer" class="dialog-footer">
  161. <el-button type="primary" @click="handlePauseFormSubmit">
  162. 确定
  163. </el-button>
  164. <el-button @click="handlePauseFormCancel">取消</el-button>
  165. </div>
  166. </el-dialog>
  167. <!-- 任务督办弹窗 -->
  168. <el-dialog
  169. title="任务督办"
  170. :visible.sync="showUrgeForm"
  171. width="50%"
  172. :modal="true"
  173. :close-on-click-modal="false"
  174. >
  175. <div class="dialog-content">
  176. <el-form ref="urgeForm" :model="urgeForm" label-width="120px">
  177. <el-form-item label="选择督办人员:">
  178. <el-select
  179. v-model="urgeForm.remindPerson"
  180. placeholder="请选择选择督办人员"
  181. style="width: 100%"
  182. >
  183. <el-option
  184. v-for="(item, index) in userList"
  185. :key="index"
  186. :label="item.fullname"
  187. :value="item.userId"
  188. ></el-option>
  189. </el-select>
  190. </el-form-item>
  191. <el-form-item label="督办要求:">
  192. <el-input
  193. v-model="urgeForm.content"
  194. type="textarea"
  195. rows="5"
  196. placeholder="请输入督办要求"
  197. style="width: 100%"
  198. maxlength="500"
  199. show-word-limit
  200. ></el-input>
  201. </el-form-item>
  202. <!-- <el-form-item label="发送方式:">
  203. <el-checkbox-group v-model="urgeForm.sendType">
  204. <el-checkbox label="站内消息">站内消息</el-checkbox>
  205. <el-checkbox label="短信通知">短信通知</el-checkbox>
  206. </el-checkbox-group>
  207. </el-form-item> -->
  208. </el-form>
  209. </div>
  210. <div slot="footer" class="dialog-footer">
  211. <el-button @click="handleUrgeCancel">取消</el-button>
  212. <el-button type="primary" @click="handleUrgeSubmit">发送</el-button>
  213. </div>
  214. </el-dialog>
  215. <!-- 任务催报弹窗 -->
  216. <el-dialog
  217. title="任务催报"
  218. :visible.sync="showUrgeReportingForm"
  219. width="50%"
  220. :modal="true"
  221. :close-on-click-modal="false"
  222. >
  223. <div class="dialog-content">
  224. <el-form
  225. ref="urgeReportingFormRef"
  226. :model="urgeReportingForm"
  227. label-width="100px"
  228. >
  229. <el-form-item label="被监审单位:">
  230. <el-select
  231. v-model="urgeReportingForm.auditedUnitIds"
  232. multiple
  233. clearable
  234. placeholder="请选择被监审单位"
  235. style="width: 100%"
  236. >
  237. <el-option
  238. v-for="(item, index) in unitList"
  239. :key="index"
  240. :label="item.unitName"
  241. :value="item.unitId"
  242. ></el-option>
  243. </el-select>
  244. </el-form-item>
  245. <el-form-item label="催办内容:">
  246. <el-input
  247. v-model="urgeReportingForm.content"
  248. type="textarea"
  249. rows="5"
  250. placeholder="请输入催办内容"
  251. style="width: 100%"
  252. maxlength="500"
  253. show-word-limit
  254. ></el-input>
  255. </el-form-item>
  256. </el-form>
  257. </div>
  258. <div slot="footer" class="dialog-footer">
  259. <el-button @click="handleUrgeReportingCancel">取消</el-button>
  260. <el-button type="primary" @click="handleUrgeReportingSubmit">
  261. 发送
  262. </el-button>
  263. </div>
  264. </el-dialog>
  265. <!-- 任务催办弹窗 -->
  266. <el-dialog
  267. title="任务催办"
  268. :visible.sync="showDelegateForm"
  269. width="50%"
  270. :modal="true"
  271. :close-on-click-modal="false"
  272. >
  273. <div class="dialog-content">
  274. <el-form ref="delegateForm" :model="delegateForm" label-width="100px">
  275. <el-form-item label="监审组人员:">
  276. <el-select
  277. v-model="delegateForm.userIds"
  278. multiple
  279. clearable
  280. placeholder="请选择监审组人员"
  281. style="width: 100%"
  282. >
  283. <el-option
  284. v-for="(item, index) in userList"
  285. :key="index"
  286. :label="item.fullname"
  287. :value="item.userId"
  288. ></el-option>
  289. </el-select>
  290. </el-form-item>
  291. <el-form-item label="催办内容:">
  292. <el-input
  293. v-model="delegateForm.content"
  294. type="textarea"
  295. rows="5"
  296. placeholder="请输入催办内容"
  297. style="width: 100%"
  298. maxlength="500"
  299. show-word-limit
  300. ></el-input>
  301. </el-form-item>
  302. <!-- <el-form-item label="发送方式:">
  303. <el-checkbox-group v-model="delegateForm.sendType">
  304. <el-checkbox label="站内消息">站内消息</el-checkbox>
  305. <el-checkbox label="短信通知">短信通知</el-checkbox>
  306. </el-checkbox-group>
  307. </el-form-item> -->
  308. </el-form>
  309. </div>
  310. <div slot="footer" class="dialog-footer">
  311. <el-button @click="handleDelegateCancel">取消</el-button>
  312. <el-button type="primary" @click="handleDelegateSubmit">发送</el-button>
  313. </div>
  314. </el-dialog>
  315. <!-- 任务详情弹窗(原组件暂保留,不再从列表入口打开) -->
  316. <task-detail
  317. ref="taskDetail"
  318. :visible.sync="taskDetailVisible"
  319. :is-view="isView"
  320. />
  321. <!-- 成本监审任务制定弹窗(封装自 tabs.vue,列表“详情/查看”入口使用) -->
  322. <task-customized-release-dialog
  323. :visible.sync="taskReleaseDialogVisible"
  324. :project="project"
  325. :is-view="true"
  326. @backToList="taskReleaseDialogVisible = false"
  327. @close="taskReleaseDialogVisible = false"
  328. />
  329. </div>
  330. </template>
  331. <script>
  332. import { taskList } from '@/api/taskProgressManage'
  333. import { dictMixin } from '@/mixins/useDict'
  334. import { getAllUnitList } from '@/api/auditEntityManage'
  335. import CostAuditTable from '@/components/costAudit/CostAuditTable.vue'
  336. import { getAllUserList } from '@/api/uc'
  337. import { getCostProjectDetail } from '@/api/taskCustomizedRelease.js'
  338. import { doProcessBtn } from '@/api/dataPreliminaryReview'
  339. import TaskDetail from '@/components/task/taskDetail.vue'
  340. import TaskCustomizedReleaseDialog from '@/components/task/TaskCustomizedReleaseDialog.vue'
  341. import { createSuperviseTask } from '@/api/audit/supervise'
  342. export default {
  343. components: {
  344. CostAuditTable,
  345. TaskDetail,
  346. TaskCustomizedReleaseDialog,
  347. },
  348. mixins: [dictMixin],
  349. data() {
  350. return {
  351. dictData: {
  352. auditType: [], //监审形式
  353. projectProposal: [], //立项来源
  354. },
  355. activeView: 'list',
  356. loading: false,
  357. isView: true,
  358. searchForm: {
  359. year: '',
  360. projectName: '',
  361. },
  362. taskData: {},
  363. project: {},
  364. tableData: [],
  365. // 分页
  366. pagination: {
  367. currentPage: 1,
  368. pageSize: 10,
  369. pageSizes: [10, 20, 50, 100],
  370. total: 0,
  371. },
  372. // 表格列配置
  373. tableColumns: [
  374. {
  375. prop: 'serialNumber',
  376. label: '序号',
  377. width: 80,
  378. align: 'center',
  379. formatter: (row) => {
  380. return row.pid == 0 ? row.parentIndex : ''
  381. },
  382. },
  383. {
  384. prop: 'year',
  385. label: '立项年度',
  386. width: 100,
  387. align: 'center',
  388. formatter: (row) => {
  389. return row.year || ''
  390. },
  391. renderHeader: ({ column, $index }) => {
  392. return <span>{column.label}</span>
  393. },
  394. slotName: 'expand',
  395. },
  396. {
  397. prop: 'projectName',
  398. label: '成本监审项目名称',
  399. align: 'left',
  400. headerAlign: 'center',
  401. showOverflowTooltip: true,
  402. slotName: 'projectName',
  403. },
  404. {
  405. prop: 'auditedUnitId',
  406. label: '被监审单位',
  407. align: 'left',
  408. headerAlign: 'center',
  409. showOverflowTooltip: true,
  410. formatter: (row) => {
  411. // 优先使用后端返回的名称,兼容多个名称逗号分隔
  412. if (row && row.auditedUnitName) return row.auditedUnitName
  413. return this.getUnitName(row.auditedUnitId)
  414. },
  415. },
  416. {
  417. prop: 'auditPeriod',
  418. label: '监审期间',
  419. width: 150,
  420. align: 'center',
  421. },
  422. {
  423. prop: 'sourceType',
  424. label: '立项来源',
  425. width: 100,
  426. align: 'center',
  427. formatter: (row) => {
  428. return this.getDictName(
  429. 'projectProposal',
  430. row && row.sourceType !== undefined && row.sourceType !== null
  431. ? String(row.sourceType)
  432. : ''
  433. )
  434. },
  435. },
  436. {
  437. prop: 'auditType',
  438. label: '监审形式',
  439. width: 100,
  440. align: 'center',
  441. formatter: (row) => {
  442. return this.getDictName(
  443. 'auditType',
  444. row && row.auditType !== undefined && row.auditType !== null
  445. ? String(row.auditType)
  446. : ''
  447. )
  448. },
  449. },
  450. {
  451. prop: 'status',
  452. label: '状态',
  453. width: 150,
  454. align: 'center',
  455. formatter: (row) => {
  456. const left = row.currentNodeName || ''
  457. const right = row.statusName || this.getStatusName(row.status)
  458. return left ? `${left}-${right}` : right
  459. },
  460. },
  461. {
  462. prop: 'warning',
  463. label: '预警',
  464. width: 80,
  465. align: 'center',
  466. slotName: 'warning',
  467. },
  468. {
  469. prop: 'action',
  470. label: '操作',
  471. width: 220,
  472. align: 'center',
  473. slotName: 'action',
  474. },
  475. ],
  476. // 弹窗状态
  477. showPauseConfirm: false,
  478. showPauseForm: false,
  479. showUrgeForm: false,
  480. showDelegateForm: false,
  481. showUrgeReportingForm: false,
  482. // 当前选中的任务
  483. currentTask: null,
  484. // 表单数据
  485. pauseForm: {
  486. reason: '',
  487. },
  488. urgeForm: {
  489. remindPerson: '',
  490. content: '',
  491. // sendType: '站内消息',
  492. },
  493. delegateForm: {
  494. userIds: [],
  495. content: '',
  496. // sendType: '站内消息',
  497. },
  498. urgeReportingForm: {
  499. auditedUnitIds: [],
  500. content: '',
  501. },
  502. unitList: [],
  503. userList: [],
  504. // 任务详情弹窗
  505. taskDetailVisible: false,
  506. // 成本监审任务制定弹窗
  507. taskReleaseDialogVisible: false,
  508. }
  509. },
  510. mounted() {
  511. this.getAllUnitList()
  512. this.getUser()
  513. this.generateTableData()
  514. },
  515. methods: {
  516. getUser() {
  517. getAllUserList()
  518. .then((res) => {
  519. this.userList = res.value || []
  520. })
  521. .catch(() => {})
  522. },
  523. getStatusName(status) {
  524. // 100待提交、200审核中、400办结、300中止
  525. switch (status) {
  526. case '100':
  527. return '待提交'
  528. case '200':
  529. return '审核中'
  530. case '400':
  531. return '办结'
  532. case '300':
  533. return '中止'
  534. default:
  535. return ''
  536. }
  537. },
  538. getAllUnitList() {
  539. getAllUnitList().then((res) => {
  540. this.unitList = res.value || []
  541. })
  542. },
  543. getUnitName(unitId) {
  544. // 直接处理unitId值,而不是row对象
  545. if (unitId && typeof unitId === 'string' && unitId.includes(',')) {
  546. // 如果包含逗号,分割成数组并查找对应的unitName
  547. const unitIds = unitId.split(',')
  548. return unitIds
  549. .map((id) => {
  550. const unit = this.unitList.find((item) => item.unitId == id)
  551. return unit ? unit.unitName : ''
  552. })
  553. .filter((name) => name) // 过滤空值
  554. .join(',')
  555. } else {
  556. // 单个unitId的情况
  557. const unit = this.unitList.find((item) => item.unitId == unitId)
  558. return unit ? unit.unitName : ''
  559. }
  560. },
  561. computeWarning(row) {
  562. // 优先使用后端返回的 warningStatus:green/yellow/res(red)
  563. if (!row) return ''
  564. const ws = (row.warningStatus || '').toString().toLowerCase()
  565. if (ws === 'green') return 'green'
  566. if (ws === 'yellow') return 'yellow'
  567. if (ws === 'res' || ws === 'red') return 'red'
  568. // 兼容无 warningStatus 时按截止期计算
  569. const parse = (v) => (v ? new Date(v) : null)
  570. const now = new Date()
  571. const nodeDdl = parse(row.nodeDeadline)
  572. const procDdl = parse(row.processDeadline)
  573. if (nodeDdl && now <= nodeDdl) return 'green'
  574. if (nodeDdl && procDdl) {
  575. if (now > nodeDdl && now <= procDdl) return 'yellow'
  576. if (now > procDdl) return 'red'
  577. }
  578. if (!nodeDdl && procDdl) return now <= procDdl ? 'green' : 'red'
  579. return ''
  580. },
  581. generateTableData() {
  582. this.loading = true
  583. taskList({
  584. projectName: this.searchForm.projectName,
  585. year: this.searchForm.year,
  586. pageNum: this.pagination.currentPage,
  587. pageSize: this.pagination.pageSize,
  588. })
  589. .then((res) => {
  590. // console.log(res,'表格')
  591. // 兼容分页与非分页返回
  592. const records = res?.value?.records || res?.value || []
  593. const total =
  594. Number(res?.value?.total) ||
  595. Number(res?.total) ||
  596. (Array.isArray(records) ? records.length : 0)
  597. // 统一将 childTasks 规范化为 children,并补齐 pid / isSubTask
  598. const normalized = this.normalizeChildren(
  599. Array.isArray(records) ? records : []
  600. )
  601. this.tableData = normalized
  602. this.pagination.total = Number(total) || 0
  603. // 同步后端分页信息(如有)
  604. if (Number.isFinite(Number(res?.value?.current))) {
  605. this.pagination.currentPage = Number(res.value.current)
  606. }
  607. if (Number.isFinite(Number(res?.value?.size))) {
  608. this.pagination.pageSize = Number(res.value.size)
  609. }
  610. // 移除不需要的属性
  611. this.tableData = this.removeItemFromTree(this.tableData)
  612. let parentIndex = 1
  613. this.tableData.forEach((item) => {
  614. if (item.pid == 0) {
  615. item.parentIndex = parentIndex++
  616. }
  617. })
  618. this.loading = false
  619. })
  620. .catch(() => {
  621. this.loading = false
  622. this.$message.error('获取数据失败')
  623. })
  624. },
  625. handlePaginationChange({ currentPage, pageSize }) {
  626. this.pagination.currentPage = currentPage
  627. this.pagination.pageSize = pageSize
  628. this.generateTableData()
  629. },
  630. removeItemFromTree(treeData) {
  631. // 边界条件检查
  632. if (!treeData || !Array.isArray(treeData)) {
  633. return []
  634. }
  635. // 创建新数组,避免修改原数据
  636. return treeData.map((item) => {
  637. // 创建当前节点的副本
  638. const newItem = { ...item }
  639. // 如果有hasChildren属性则删除
  640. if ('hasChildren' in newItem) {
  641. delete newItem.hasChildren
  642. }
  643. // 递归处理子节点 先检查children是否存在且为数组
  644. if (
  645. newItem.children &&
  646. Array.isArray(newItem.children) &&
  647. newItem.children.length > 0
  648. ) {
  649. newItem.children = this.removeItemFromTree(newItem.children)
  650. }
  651. return newItem
  652. })
  653. },
  654. normalizeChildren(list, parentId = 0) {
  655. if (!Array.isArray(list)) return []
  656. return list.map((node) => {
  657. const copied = { ...node }
  658. const rawChildren = Array.isArray(node.children)
  659. ? node.children
  660. : Array.isArray(node.childTasks)
  661. ? node.childTasks
  662. : []
  663. // 设置父子关系与子任务标识
  664. copied.pid =
  665. node.pid !== undefined && node.pid !== null ? node.pid : parentId
  666. // 注意:后端可能返回字符串 '0',需要统一判断
  667. const pidStr = copied.pid != null ? String(copied.pid) : '0'
  668. copied.isSubTask = pidStr !== '0'
  669. // 递归规范化子节点
  670. const children = this.normalizeChildren(rawChildren, node.id)
  671. delete copied.childTasks
  672. copied.children = children
  673. return copied
  674. })
  675. },
  676. handleReset() {
  677. this.searchForm = {
  678. projectName: '',
  679. year: '',
  680. }
  681. this.pagination.currentPage = 1
  682. this.generateTableData()
  683. },
  684. handleSearch() {
  685. this.pagination.currentPage = 1
  686. this.generateTableData()
  687. },
  688. getRowClassName({ row }) {
  689. if (row.isSubTask) {
  690. return 'sub-task-row'
  691. }
  692. return ''
  693. },
  694. // handleView(row) {
  695. // // 子任务查看:统一走任务制定弹窗(只读)
  696. // this.openTaskReleaseDialog(row)
  697. // },
  698. // 任务中止相关方法
  699. handlePause(row) {
  700. this.currentTask = row
  701. this.$confirm(
  702. '如您选择中止操作,该任务将停止办理,是否中止?',
  703. '中止确认',
  704. {
  705. confirmButtonText: '确定',
  706. cancelButtonText: '取消',
  707. type: 'warning',
  708. }
  709. )
  710. .then(() => {
  711. this.showPauseForm = true
  712. })
  713. .catch(() => {
  714. this.$message({
  715. type: 'info',
  716. message: '已取消',
  717. })
  718. })
  719. },
  720. handlePauseCancel() {
  721. this.showPauseConfirm = false
  722. this.currentTask = null
  723. },
  724. handlePauseConfirm() {
  725. this.showPauseConfirm = false
  726. this.showPauseForm = true
  727. },
  728. handlePauseFormCancel() {
  729. this.showPauseForm = false
  730. this.pauseForm.reason = ''
  731. },
  732. handlePauseFormSubmit() {
  733. if (!this.currentTask) return
  734. const params = {
  735. taskId: this.currentTask.id,
  736. key: 2,
  737. status: 300,
  738. processNodeKey: this.currentTask.currentNode,
  739. remark: this.pauseForm.reason,
  740. }
  741. doProcessBtn(params)
  742. .then((res) => {
  743. if (res && Number(res.code) === 200) {
  744. this.$message.success('任务已中止')
  745. this.showPauseForm = false
  746. this.pauseForm.reason = ''
  747. this.currentTask = null
  748. this.generateTableData()
  749. } else {
  750. this.$message.error(res?.message || '操作失败')
  751. }
  752. })
  753. .catch(() => {
  754. this.$message.error('操作失败')
  755. })
  756. },
  757. // 任务督办相关方法
  758. handleUrge(row) {
  759. console.log('任务督办:', row)
  760. this.currentTask = row
  761. this.urgeForm = {
  762. remindPerson: '',
  763. content: '',
  764. // sendType: [],
  765. }
  766. this.showUrgeForm = true
  767. },
  768. handleUrgeCancel() {
  769. this.showUrgeForm = false
  770. this.urgeForm = {
  771. remindPerson: '',
  772. content: '',
  773. // sendType: [],
  774. }
  775. },
  776. handleUrgeSubmit() {
  777. if (!this.currentTask) return
  778. const projectId =
  779. this.currentTask.projectId || this.currentTask.projectID || ''
  780. const supervisorId = this.urgeForm.remindPerson
  781. const requireContent = (this.urgeForm.content || '').trim()
  782. if (!projectId) {
  783. this.$message.warning('缺少项目ID,无法发送督办')
  784. return
  785. }
  786. if (!supervisorId) {
  787. this.$message.warning('请选择督办人员')
  788. return
  789. }
  790. if (!requireContent) {
  791. this.$message.warning('请输入督办要求')
  792. return
  793. }
  794. const loading = this.$loading({
  795. lock: true,
  796. text: '发送中...',
  797. spinner: 'el-icon-loading',
  798. background: 'rgba(0,0,0,0.3)',
  799. })
  800. createSuperviseTask({ projectId, supervisorId, requireContent })
  801. .then((res) => {
  802. if (res && Number(res.code) === 200) {
  803. this.$message.success('督办信息已发送')
  804. this.showUrgeForm = false
  805. this.urgeForm = { remindPerson: '', content: '' }
  806. this.currentTask = null
  807. this.generateTableData()
  808. } else {
  809. this.$message.error(res?.message || '发送失败')
  810. }
  811. })
  812. .catch(() => {
  813. this.$message.error('发送失败')
  814. })
  815. .finally(() => {
  816. loading && loading.close && loading.close()
  817. })
  818. },
  819. // 任务代办相关方法
  820. handleDelegate(row) {
  821. console.log('任务代办:', row)
  822. this.currentTask = row
  823. this.showDelegateForm = true
  824. this.delegateForm = {
  825. userIds: [],
  826. content: '',
  827. }
  828. },
  829. handleDelegateCancel() {
  830. this.showDelegateForm = false
  831. this.delegateForm = {
  832. userIds: [],
  833. content: '',
  834. // sendType: '站内消息',
  835. }
  836. },
  837. handleDelegateSubmit() {
  838. if (!this.currentTask) return
  839. if (
  840. !Array.isArray(this.delegateForm.userIds) ||
  841. this.delegateForm.userIds.length === 0
  842. ) {
  843. this.$message.warning('请选择监审组人员')
  844. return
  845. }
  846. const params = {
  847. taskId: this.currentTask.id,
  848. key: 5,
  849. status: this.currentTask.status,
  850. processNodeKey: this.currentTask.currentNode,
  851. remark: this.delegateForm.content,
  852. userIds: this.delegateForm.userIds.join(','),
  853. }
  854. doProcessBtn(params)
  855. .then((res) => {
  856. if (res && Number(res.code) === 200) {
  857. this.$message.success('催办信息已发送')
  858. this.showDelegateForm = false
  859. this.delegateForm = { userIds: [], content: '' }
  860. this.currentTask = null
  861. this.generateTableData()
  862. } else {
  863. this.$message.error(res?.message || '操作失败')
  864. }
  865. })
  866. .catch(() => {
  867. this.$message.error('操作失败')
  868. })
  869. },
  870. // 任务详情相关方法
  871. handleViewTaskDetail(row) {
  872. // 主任务详情:打开成本监审任务制定弹窗(只读)
  873. this.openTaskReleaseDialog(row)
  874. },
  875. handleView(row) {
  876. this.$refs.taskDetail.open(row, 'chengben')
  877. },
  878. // 打开成本监审任务制定弹窗(从进度列表“详情/查看”入口)
  879. openTaskReleaseDialog(row) {
  880. if (!row) return
  881. const projectId =
  882. row.projectId || row.projectID || row.id || row.taskId || ''
  883. if (!projectId) {
  884. this.$message &&
  885. this.$message.warning &&
  886. this.$message.warning('缺少项目ID,无法查看详情')
  887. return
  888. }
  889. this.isView = true
  890. // 加载项目详情后再打开弹窗
  891. getCostProjectDetail({ id: projectId })
  892. .then((res) => {
  893. this.project = (res && res.value) || {}
  894. this.taskReleaseDialogVisible = true
  895. })
  896. .catch(() => {
  897. // 回退:若接口失败,至少把当前行数据带入
  898. this.project = row || {}
  899. this.taskReleaseDialogVisible = true
  900. })
  901. },
  902. getProject() {
  903. getCostProjectDetail({
  904. id: this.taskData.projectId,
  905. })
  906. .then((res) => {
  907. this.project = {
  908. ...res.value,
  909. }
  910. })
  911. .catch(() => {
  912. this.project = this.taskData
  913. })
  914. },
  915. handleDetailClose() {
  916. this.activeView = 'list'
  917. this.taskData = null
  918. },
  919. // 代办
  920. handleToDo(row) {
  921. console.log('代办', row)
  922. },
  923. // 催报
  924. handleUrgeReporting(row) {
  925. this.currentTask = row
  926. const ids =
  927. row && row.auditedUnitId
  928. ? String(row.auditedUnitId).split(',').filter(Boolean)
  929. : []
  930. this.urgeReportingForm = {
  931. auditedUnitIds: ids,
  932. content: '',
  933. }
  934. this.showUrgeReportingForm = true
  935. },
  936. handleUrgeReportingCancel() {
  937. this.showUrgeReportingForm = false
  938. this.urgeReportingForm = { auditedUnitIds: [], content: '' }
  939. this.currentTask = null
  940. },
  941. handleUrgeReportingSubmit() {
  942. if (!this.currentTask) return
  943. if (
  944. !Array.isArray(this.urgeReportingForm.auditedUnitIds) ||
  945. this.urgeReportingForm.auditedUnitIds.length === 0
  946. ) {
  947. this.$message.warning('请选择被监审单位')
  948. return
  949. }
  950. const params = {
  951. taskId: this.currentTask.id,
  952. key: 10,
  953. status: this.currentTask.status,
  954. processNodeKey: this.currentTask.currentNode,
  955. remark: this.urgeReportingForm.content,
  956. auditedUnitIds: this.urgeReportingForm.auditedUnitIds.join(','),
  957. }
  958. doProcessBtn(params)
  959. .then((res) => {
  960. if (res && Number(res.code) === 200) {
  961. this.$message.success('催报信息已发送')
  962. this.showUrgeReportingForm = false
  963. this.urgeReportingForm = { auditedUnitIds: [], content: '' }
  964. this.currentTask = null
  965. this.generateTableData()
  966. } else {
  967. this.$message.error(res?.message || '操作失败')
  968. }
  969. })
  970. .catch(() => {
  971. this.$message.error('操作失败')
  972. })
  973. },
  974. },
  975. }
  976. </script>
  977. <style scoped lang="scss">
  978. @import '@/styles/costAudit.scss';
  979. .task-progress-manage {
  980. padding: 20px;
  981. }
  982. h2 {
  983. margin-bottom: 20px;
  984. font-size: 18px;
  985. color: #303133;
  986. }
  987. .demo-form-inline {
  988. display: flex;
  989. align-items: center;
  990. flex-wrap: wrap;
  991. }
  992. .description {
  993. margin-top: 15px;
  994. margin-bottom: 20px;
  995. padding: 15px;
  996. background-color: #fff7e6;
  997. border: 1px solid #ffe7ba;
  998. border-radius: 4px;
  999. }
  1000. .description p {
  1001. margin: 5px 0;
  1002. line-height: 1.6;
  1003. }
  1004. /* 子任务样式 */
  1005. .el-table .sub-task-row {
  1006. background-color: #fafafa !important;
  1007. }
  1008. /* 预警点样式 */
  1009. .warning-point {
  1010. display: inline-block;
  1011. width: 12px;
  1012. height: 12px;
  1013. border-radius: 50%;
  1014. }
  1015. .warning-point.red {
  1016. background-color: #ff4949;
  1017. }
  1018. .warning-point.yellow {
  1019. background-color: #e6a23c;
  1020. }
  1021. .warning-point.green {
  1022. background-color: #67c23a;
  1023. }
  1024. .back-button-container {
  1025. margin-bottom: 20px;
  1026. }
  1027. .process-tab {
  1028. padding: 20px;
  1029. }
  1030. .process-time-form {
  1031. display: flex;
  1032. align-items: center;
  1033. }
  1034. </style>