index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. <template>
  2. <div class="task-supervision">
  3. <!-- 页面标题 -->
  4. <!-- <h2>成本监审督办事项管理</h2> -->
  5. <!-- 查询区域 -->
  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. clearable
  16. ></el-date-picker>
  17. </el-form-item>
  18. <el-form-item label="监审项目名称:">
  19. <el-input
  20. v-model="searchForm.projectName"
  21. placeholder="请输入监审项目名称"
  22. clearable
  23. />
  24. </el-form-item>
  25. <el-form-item>
  26. <el-button
  27. type="primary"
  28. icon="el-icon-search"
  29. @click="generateTableData"
  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. <el-table
  46. v-loading="loading"
  47. :data="tableData"
  48. border
  49. style="width: 100%"
  50. row-key="id"
  51. :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
  52. :row-class-name="getRowClassName"
  53. default-expand-all
  54. >
  55. <el-table-column
  56. prop="serialNumber"
  57. label="序号"
  58. width="60"
  59. align="center"
  60. >
  61. <template slot-scope="scope">
  62. <span v-if="scope.row.pid == 0">
  63. {{ scope.row.parentIndex }}
  64. </span>
  65. </template>
  66. </el-table-column>
  67. <el-table-column
  68. prop="year"
  69. label="立项年度"
  70. width="100"
  71. align="center"
  72. />
  73. <el-table-column
  74. prop="projectName"
  75. label="成本监审项目名称"
  76. align="center"
  77. show-overflow-tooltip
  78. >
  79. <template slot-scope="scope">
  80. <span :style="scope.row.style">{{ scope.row.projectName }}</span>
  81. </template>
  82. </el-table-column>
  83. <el-table-column
  84. prop="auditedUnitName"
  85. label="被监审单位"
  86. align="center"
  87. show-overflow-tooltip
  88. >
  89. <template slot-scope="scope">
  90. <span :style="scope.row.style">{{ scope.row.auditedUnitName }}</span>
  91. </template>
  92. </el-table-column>
  93. <el-table-column
  94. prop="auditPeriod"
  95. label="监审期间"
  96. width="150"
  97. align="center"
  98. >
  99. <template slot-scope="scope">
  100. <span :style="scope.row.style">{{ scope.row.auditPeriod }}</span>
  101. </template>
  102. </el-table-column>
  103. <el-table-column
  104. prop="sourceType"
  105. label="立项来源"
  106. width="120"
  107. align="center"
  108. >
  109. <template slot-scope="scope">
  110. {{ getDictName('projectProposal', scope.row.sourceType) }}
  111. </template>
  112. </el-table-column>
  113. <el-table-column
  114. prop="auditType"
  115. label="监审形式"
  116. width="120"
  117. align="center"
  118. >
  119. <template slot-scope="scope">
  120. {{ getDictName('auditType', scope.row.auditType) }}
  121. </template>
  122. </el-table-column>
  123. <el-table-column
  124. prop="superviseStatus"
  125. label="状态"
  126. width="100"
  127. align="center"
  128. >
  129. <template slot-scope="scope">
  130. <span
  131. v-if="!(scope.row.isSubTask || scope.row.pid != 0)"
  132. :style="scope.row.style"
  133. >
  134. {{ getSuperviseStatusName(scope.row.superviseStatus) }}
  135. </span>
  136. </template>
  137. </el-table-column>
  138. <el-table-column prop="warning" label="预警" width="80" align="center">
  139. <template slot-scope="scope">
  140. <span :class="['warning-point', computeWarning(scope.row)]"></span>
  141. </template>
  142. </el-table-column>
  143. <el-table-column label="操作" width="180" align="center">
  144. <template slot-scope="scope">
  145. <template v-if="scope.row.isSubTask || scope.row.pid != 0">
  146. <el-button size="mini" type="text" @click="handleView(scope.row)">
  147. 查看
  148. </el-button>
  149. </template>
  150. <template v-else>
  151. <el-button
  152. size="mini"
  153. type="text"
  154. @click="handleDetails(scope.row)"
  155. >
  156. 详情
  157. </el-button>
  158. <el-button size="mini" type="text" @click="handleReport(scope.row)">
  159. 督办报告
  160. </el-button>
  161. </template>
  162. </template>
  163. </el-table-column>
  164. </el-table>
  165. <!-- 督办报告弹出框 -->
  166. <el-dialog
  167. title="督办报告"
  168. :visible.sync="isReport"
  169. width="30%"
  170. @close="handleCancel"
  171. >
  172. <el-form label-width="120px">
  173. <el-form-item label="报告内容:">
  174. <el-input
  175. v-model="reportForm.projectName"
  176. placeholder="请输入报告内容"
  177. />
  178. </el-form-item>
  179. <el-form-item label="督办材料:">
  180. <UploadComponent
  181. :upload-mode="'single'"
  182. :max-size="50 * 1024 * 1024"
  183. :allowed-types="['xlsx', 'xls', 'doc', 'docx', 'pdf']"
  184. :files-list="reportForm.uploadUrl"
  185. @removeFile="removeFile"
  186. @saveFiles="saveFiles"
  187. />
  188. </el-form-item>
  189. </el-form>
  190. <div class="button-group">
  191. <el-button type="primary" @click="handleSubmit">确认</el-button>
  192. <el-button @click="handleCancel">取消</el-button>
  193. </div>
  194. </el-dialog>
  195. <!-- isDetail -->
  196. <el-dialog
  197. title="查看子任务"
  198. :visible.sync="isView"
  199. width="80%"
  200. @close="handleViewDialogClose"
  201. >
  202. <!-- todo -->
  203. 详情内容
  204. </el-dialog>
  205. </div>
  206. </template>
  207. <script>
  208. import { getSupervisorList } from '@/api/superviseMattersManage'
  209. import { dictMixin } from '@/mixins/useDict'
  210. import UploadComponent from '@/components/costAudit/UploadComponent.vue'
  211. import { updateSuperviseTask } from '@/api/audit/supervise'
  212. export default {
  213. name: 'TaskSupervision',
  214. components: {
  215. UploadComponent,
  216. },
  217. mixins: [dictMixin],
  218. data() {
  219. return {
  220. dictData: {
  221. auditType: [], //监审形式
  222. projectProposal: [], //立项来源
  223. },
  224. loading: false,
  225. searchForm: {
  226. projectName: '',
  227. year: '',
  228. },
  229. projectList: [],
  230. tableData: [],
  231. reportForm: {
  232. content: '',
  233. uploadUrl: [],
  234. },
  235. fileList: [{ name: '督办说明.pdf', url: '' }],
  236. currentTask: null,
  237. activeTab: '0',
  238. isReport: false,
  239. isView: false,
  240. isDetail: false,
  241. }
  242. },
  243. mounted() {
  244. this.generateTableData()
  245. },
  246. methods: {
  247. handleReset() {
  248. this.searchForm = {
  249. projectName: '',
  250. year: '',
  251. }
  252. this.generateTableData()
  253. },
  254. generateTableData() {
  255. // 将主任务和子任务展平为表格数据
  256. getSupervisorList({
  257. projectName: this.searchForm.projectName,
  258. year: this.searchForm.year,
  259. }).then((res) => {
  260. this.tableData = res.value
  261. this.tableData = this.removeItemFromTree(this.tableData)
  262. let parentIndex = 1
  263. this.tableData.forEach((item, index) => {
  264. if (item.pid == 0) {
  265. item.parentIndex = parentIndex++
  266. }
  267. })
  268. })
  269. },
  270. removeItemFromTree(treeData) {
  271. // 边界条件检查
  272. if (!treeData || !Array.isArray(treeData)) {
  273. return []
  274. }
  275. // 创建新数组,避免修改原数据
  276. return treeData.map((item) => {
  277. // 创建当前节点的副本
  278. const newItem = { ...item }
  279. // 如果有hasChildren属性则删除
  280. if ('hasChildren' in newItem) {
  281. delete newItem.hasChildren
  282. }
  283. // 递归处理子节点 先检查children是否存在且为数组
  284. if (
  285. newItem.children &&
  286. Array.isArray(newItem.children) &&
  287. newItem.children.length > 0
  288. ) {
  289. newItem.children = this.removeItemFromTree(newItem.children)
  290. }
  291. return newItem
  292. })
  293. },
  294. getRowClassName({ row }) {
  295. if (row.isSubTask) {
  296. return 'sub-task-row'
  297. }
  298. return ''
  299. },
  300. computeWarning(row) {
  301. if (!row) return ''
  302. const ws = (row.warningStatus || '').toString().toLowerCase()
  303. if (ws === 'green') return 'green'
  304. if (ws === 'yellow') return 'yellow'
  305. if (ws === 'res' || ws === 'red') return 'red'
  306. const parse = (v) => (v ? new Date(v) : null)
  307. const now = new Date()
  308. const nodeDdl = parse(row.nodeDeadline)
  309. const procDdl = parse(row.processDeadline)
  310. if (nodeDdl && now <= nodeDdl) return 'green'
  311. if (nodeDdl && procDdl) {
  312. if (now > nodeDdl && now <= procDdl) return 'yellow'
  313. if (now > procDdl) return 'red'
  314. }
  315. if (!nodeDdl && procDdl) return now <= procDdl ? 'green' : 'red'
  316. return ''
  317. },
  318. getSuperviseStatusName(v) {
  319. const val = String(v)
  320. if (val === '0') return '在办'
  321. if (val === '1') return '办结'
  322. return v
  323. },
  324. handleView(row) {
  325. console.log('查看子任务:', row)
  326. this.isView = true
  327. },
  328. handleDetails(row) {
  329. this.isDetail = true
  330. console.log('查看详情:', row)
  331. },
  332. handleReport(row) {
  333. this.currentTask = row
  334. this.activeTab = '0'
  335. this.reportForm = { content: '', uploadUrl: [] }
  336. this.fileList = []
  337. this.isReport = true
  338. },
  339. // 与 UploadComponent 事件签名保持一致
  340. // saveFiles(files, props)
  341. saveFiles(files) {
  342. const list = Array.isArray(files) ? files : []
  343. this.reportForm.uploadUrl = list
  344. this.fileList = list
  345. },
  346. // removeFile(index, removedFile, files, props)
  347. removeFile(index, removedFile, files) {
  348. const list = Array.isArray(files) ? files : []
  349. this.reportForm.uploadUrl = list
  350. this.fileList = list
  351. },
  352. handleSubmit() {
  353. const reportContent = (
  354. (this.reportForm &&
  355. (this.reportForm.content || this.reportForm.projectName)) ||
  356. ''
  357. ).trim()
  358. const files = [
  359. ...(this.reportForm && Array.isArray(this.reportForm.uploadUrl)
  360. ? this.reportForm.uploadUrl
  361. : []),
  362. ...(Array.isArray(this.fileList) ? this.fileList : []),
  363. ]
  364. .map((f) =>
  365. typeof f === 'string'
  366. ? f
  367. : (f.response && f.response.value && f.response.value.savePath) ||
  368. f.savePath ||
  369. (f.response && f.response.data && f.response.data.savePath) ||
  370. ''
  371. )
  372. .filter(Boolean)
  373. console.log(files, 'files')
  374. const attachmentUrls = files.join(',')
  375. if (!reportContent) {
  376. this.$message.warning('请输入报告内容')
  377. return
  378. }
  379. const id =
  380. (this.currentTask &&
  381. (this.currentTask.superviseId || this.currentTask.ID)) ||
  382. ''
  383. if (!id) {
  384. this.$message.warning('缺少ID,无法提交督办报告')
  385. return
  386. }
  387. const loading = this.$loading({
  388. lock: true,
  389. text: '提交中...',
  390. spinner: 'el-icon-loading',
  391. background: 'rgba(0, 0, 0, 0.3)',
  392. })
  393. updateSuperviseTask({ id, reportContent, attachmentUrls })
  394. .then((res) => {
  395. if (res && Number(res.code) === 200) {
  396. this.$message.success('提交成功')
  397. this.isReport = false
  398. this.reportForm = { content: '', uploadUrl: [] }
  399. this.currentTask = null
  400. this.generateTableData()
  401. } else {
  402. this.$message.error((res && res.message) || '提交失败')
  403. }
  404. })
  405. .catch(() => {
  406. this.$message.error('提交失败')
  407. })
  408. .finally(() => {
  409. loading && loading.close && loading.close()
  410. })
  411. },
  412. handleCancel() {
  413. console.log('取消')
  414. this.isReport = false
  415. // 这里可以添加取消逻辑
  416. },
  417. handleDetailDialogClose() {
  418. this.isDetail = false
  419. },
  420. handleViewDialogClose() {
  421. this.isView = false
  422. },
  423. },
  424. }
  425. </script>
  426. <style scoped>
  427. .task-supervision {
  428. padding: 20px;
  429. }
  430. h2 {
  431. margin-bottom: 20px;
  432. font-size: 18px;
  433. color: #303133;
  434. }
  435. h3 {
  436. margin-bottom: 15px;
  437. font-size: 16px;
  438. color: #303133;
  439. }
  440. .demo-form-inline {
  441. display: flex;
  442. align-items: center;
  443. flex-wrap: wrap;
  444. }
  445. .description {
  446. margin-top: 15px;
  447. margin-bottom: 20px;
  448. padding: 10px;
  449. background-color: #fff7e6;
  450. border: 1px solid #ffe7ba;
  451. border-radius: 4px;
  452. }
  453. .supervision-report {
  454. margin-top: 20px;
  455. padding: 15px;
  456. background-color: #fafafa;
  457. border: 1px solid #ebeef5;
  458. border-radius: 4px;
  459. }
  460. .report-content {
  461. background-color: #fff;
  462. padding: 15px;
  463. border-radius: 4px;
  464. }
  465. .warning-dot {
  466. display: inline-block;
  467. width: 12px;
  468. height: 12px;
  469. border-radius: 50%;
  470. border: 2px solid;
  471. }
  472. .warning-dot.red {
  473. background-color: red;
  474. border-color: red;
  475. }
  476. .warning-dot.yellow {
  477. background-color: #ffcc00;
  478. border-color: #ffcc00;
  479. }
  480. .file-list-container {
  481. border: 1px dashed #d9d9d9;
  482. padding: 20px;
  483. text-align: center;
  484. min-height: 100px;
  485. }
  486. .file-item {
  487. display: flex;
  488. justify-content: space-between;
  489. align-items: center;
  490. padding: 8px 15px;
  491. margin-bottom: 8px;
  492. background-color: #fafafa;
  493. border-radius: 4px;
  494. text-align: left;
  495. }
  496. .upload-btn {
  497. margin-top: 10px;
  498. }
  499. .button-group {
  500. margin-top: 20px;
  501. text-align: right;
  502. }
  503. /* 子任务样式 */
  504. .el-table .sub-task-row {
  505. background-color: #fafafa !important;
  506. }
  507. .el-table .sub-task-cell {
  508. padding-left: 40px !important;
  509. }
  510. /* 预警点样式,保持与任务进度页面一致 */
  511. .warning-point {
  512. display: inline-block;
  513. width: 12px;
  514. height: 12px;
  515. border-radius: 50%;
  516. }
  517. .warning-point.red {
  518. background-color: #ff4949;
  519. }
  520. .warning-point.yellow {
  521. background-color: #e6a23c;
  522. }
  523. .warning-point.green {
  524. background-color: #67c23a;
  525. }
  526. </style>