dash.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. <template>
  2. <div class="full-height full-width flex flex-col">
  3. <div class="flex flex-col padding white-bg">
  4. <div class="flex flex-align-center tip">
  5. <el-icon class="ml-20" color="#BC002D">
  6. <WarningFilled/>
  7. </el-icon>
  8. <span class="ml-5">项目总投资额<span
  9. class="main-color bold">{{ num ? Number.parseFloat(num).toLocaleString() : '0' }}</span>万元</span>
  10. </div>
  11. <div class="flex ml-20 hide-scrollbar" style="overflow-x: scroll;width: 86vw;"
  12. v-show='numList && numList.length > 0'>
  13. <div v-for="(item,index) in numList" :key='item.id' :class="active === index ? 'total-s' : 'total'"
  14. class="flex flex-center flex-justify-between mt-20 bold font-16 pointer" @click='switchTab(item,index)'
  15. >
  16. <span class=" sp">{{ item.name }}</span>
  17. <span class=" sp1 ">{{ item.number }}<span class="grey font-13">个</span></span>
  18. </div>
  19. </div>
  20. <div class='flex flex-center flex-justify-start'>
  21. <base-button class="ml-20 mt-20" icon="Plus" title="新增"
  22. @click="showAdd = true"/>
  23. <base-button class="ml-20 mt-20" icon="el-icon-upload" title="数据导入"
  24. @click="diaType = 0"/>
  25. <base-button class="ml-20 mt-20" icon="el-icon-download" title="数据导出"
  26. @click="diaType = 1"/>
  27. <base-button class="ml-20 mt-20" type="0" icon="el-icon-download" title="汇总数据导出"
  28. @click="diaType = 2"/>
  29. </div>
  30. </div>
  31. <avue-crud ref="crud"
  32. v-model="form"
  33. v-model:page="page"
  34. :before-open="beforeOpen"
  35. :data="data"
  36. :option="option"
  37. :permission="permissionList"
  38. :table-loading="loading"
  39. class="curd"
  40. @row-del="rowDel"
  41. @tree-load="treeLoad"
  42. @current-change="currentChange"
  43. @size-change="sizeChange"
  44. @refresh-change="refreshChange"
  45. @selection-change="selectionChange"
  46. @on-load="onLoad">
  47. <template #menu="{row}">
  48. <el-button v-if='user.info.viewStage !== 1' icon="Upload" type='primary' text @click="track(row)"> {{
  49. row.isReport === 1 ? "已经上报" : "项目上报"
  50. }}
  51. </el-button>
  52. </template>
  53. <template #plan_storage_time-header="{column}">
  54. <div class='flex flex-center'>
  55. <div class='mr-5'>{{ (column || {}).label }}</div>
  56. <el-tooltip content='红色为计划开工时间,蓝色为实际开工时间'>
  57. <el-icon>
  58. <InfoFilled/>
  59. </el-icon>
  60. </el-tooltip>
  61. </div>
  62. </template>
  63. <template #plan_storage_time="{row}">
  64. <div class='flex flex-center'>
  65. <div class='mr-5 red' v-if='row.is_storage === 0 '>{{ row.plan_storage_time }}</div>
  66. <div class='mr-5 blue' v-else>{{ row.storage_time }}</div>
  67. </div>
  68. </template>
  69. <template #plan_commencement_time-header="{column}">
  70. <div class='flex flex-center'>
  71. <div class='mr-5'>{{ (column || {}).label }}</div>
  72. <el-tooltip content='红色为计划开工时间,蓝色为实际开工时间'>
  73. <el-icon>
  74. <InfoFilled/>
  75. </el-icon>
  76. </el-tooltip>
  77. </div>
  78. </template>
  79. <template #plan_commencement_time="{row}">
  80. <div class='flex flex-center'>
  81. <div class='mr-5 red' v-if='row.is_start === undefined ||row.is_start === 0 '>{{
  82. row.plan_commencement_time
  83. }}
  84. </div>
  85. <div class='mr-5 blue' v-else>{{ row.start_time }}</div>
  86. </div>
  87. </template>
  88. </avue-crud>
  89. <!-- 新增-->
  90. <el-dialog v-model="showAdd"
  91. append-to-body
  92. center
  93. title="新增项目"
  94. width="35%">
  95. <div v-loading='addLoading'>
  96. <el-form ref='form' :model="projectForm" class="lab mt-20" label-width="100px" :rules="rules">
  97. <div class="flex flex-center flex-col mr-20">
  98. <el-form-item class="full-width" label="项目名称" prop='name'>
  99. <el-input
  100. v-model="projectForm.name"
  101. clearable
  102. placeholder="输入项目名称"
  103. />
  104. </el-form-item>
  105. <el-form-item class="full-width" label="项目总投" prop='totalAmount'>
  106. <el-input
  107. v-model="projectForm.totalAmount"
  108. clearable
  109. placeholder="输入项目总投(万元)"
  110. >
  111. <template #append>(万元)</template>
  112. </el-input>
  113. </el-form-item>
  114. <!-- <el-form-item class="full-width" label="项目类型" prop='projectType'>-->
  115. <!-- <el-select-->
  116. <!-- v-model="projectForm.projectType"-->
  117. <!-- clearable-->
  118. <!-- placeholder="选择项目类型"-->
  119. <!-- style="width: 100%"-->
  120. <!-- >-->
  121. <!-- <el-option-->
  122. <!-- v-for="item in typeList"-->
  123. <!-- :key="item.id"-->
  124. <!-- :label="item.name"-->
  125. <!-- :value="item.id"-->
  126. <!-- />-->
  127. <!-- </el-select>-->
  128. <!-- </el-form-item>-->
  129. <el-form-item class="full-width" label="项目标签" prop='tags'>
  130. <el-select
  131. v-model="projectForm.tags"
  132. clearable
  133. placeholder="选择项目标签"
  134. style="width: 100%"
  135. >
  136. <el-option
  137. v-for="item in tagsList"
  138. :key="item.dictKey"
  139. :label="item.dictValue"
  140. :value="item.dictKey"
  141. />
  142. </el-select>
  143. </el-form-item>
  144. <el-form-item class="full-width" label="建设内容">
  145. <el-input
  146. v-model="projectForm.introduction"
  147. :rows="6"
  148. clearable
  149. placeholder="输入项目建设内容"
  150. type="textarea"
  151. />
  152. </el-form-item>
  153. <div class="flex flex-center mt-10">
  154. <base-button class="mr-20" icon="Close" title="取消" type="0" @click="showAdd = false"/>
  155. <base-button icon="Check" title="保存" @click="projectSave"/>
  156. </div>
  157. </div>
  158. </el-form>
  159. </div>
  160. </el-dialog>
  161. <form-dialog :dialogType="diaType" @close="formDialogClose" @export='exportExcel' :ids='selectList'/>
  162. <summary-dialog :dialogType="diaType" @close="diaType = -1"
  163. :select-num='selectList.length === 0 ? page.total : selectList.length'
  164. @export='exportExcelTotal'/>
  165. </div>
  166. </template>
  167. <script>
  168. import BaseButton from '../../../components/base-button.vue'
  169. import permissionStore from '@/store/permission.js'
  170. import formDialog from '@/views/home/component/form_dialog.vue'
  171. import { getLazyList } from '@/api/project/index.js'
  172. import summaryDialog from '@/views/home/component/summary_dialog.vue'
  173. import { useStore } from '@/store/user.js'
  174. export default {
  175. name: 'dash',
  176. components: { BaseButton, formDialog, summaryDialog },
  177. setup () {
  178. const permissions = permissionStore()
  179. const user = useStore()
  180. return { permissions, user }
  181. },
  182. data () {
  183. return {
  184. dialogLoading: false,
  185. disable: false,
  186. showAdd: false,
  187. active: 0,
  188. loading: false,
  189. addLoading: false,
  190. data: [],
  191. form: {},
  192. option: {
  193. menuType: 'menu',
  194. menuBtnTitle: '操作',
  195. refreshBtn: false,
  196. tip: false,
  197. lazy: true,
  198. columnBtn: false,
  199. searchShow: true,
  200. selection: true,
  201. editBtn: true,
  202. editBtnText: '资料管理',
  203. editBtnIcon: 'Document',
  204. addBtn: false,
  205. delBtn: true,
  206. border: true,
  207. reserveSelection: true,
  208. align: 'center',
  209. viewBtn: true,
  210. viewBtnText: '详情',
  211. dialogClickModal: false,
  212. column: [
  213. {
  214. label: '项目名称',
  215. prop: 'name',
  216. addDisplay: false,
  217. editDisplay: false,
  218. fixed: true,
  219. minWidth: 200
  220. },
  221. {
  222. label: '项目情况',
  223. prop: 'project_stage',
  224. type: 'select',
  225. width: 120,
  226. dicUrl: '/api/blade-system/dict-biz/dictionary?code=project-situation',
  227. props: {
  228. label: 'dictValue',
  229. value: 'dictKey'
  230. }
  231. },
  232. {
  233. label: '项目总投资(万元)',
  234. prop: 'total_amount',
  235. width: 120,
  236. type: 'number',
  237. precision: 2,
  238. formatter: (val, value, label) => {
  239. return val.total_amount.toLocaleString()
  240. }
  241. },
  242. {
  243. label: '责任单位',
  244. prop: 'create_dept_name'
  245. },
  246. {
  247. label: '子项目数量',
  248. prop: 'lot',
  249. width: 120,
  250. hide: true
  251. },
  252. {
  253. label: '是否入库',
  254. prop: 'is_storage',
  255. width: 120,
  256. dicData: [
  257. {
  258. label: '否',
  259. value: 0
  260. },
  261. {
  262. label: '是',
  263. value: 1
  264. }
  265. ]
  266. },
  267. {
  268. label: '计划(实际)入库时间',
  269. prop: 'plan_storage_time',
  270. width: 120
  271. },
  272. {
  273. label: '是否开工',
  274. prop: 'is_start',
  275. width: 120,
  276. dicData: [
  277. {
  278. label: '否',
  279. value: 0
  280. },
  281. {
  282. label: '是',
  283. value: 1
  284. }
  285. ]
  286. },
  287. {
  288. label: '计划(实际)开工时间',
  289. prop: 'plan_commencement_time',
  290. width: 120
  291. },
  292. {
  293. label: '创建时间',
  294. prop: 'create_time',
  295. width: 120,
  296. formatter: (val, value, label) => {
  297. return value.substring(0, 10)
  298. }
  299. }]
  300. },
  301. page: {
  302. size: 10,
  303. current: 1,
  304. total: 0
  305. },
  306. stage: [],
  307. numList: [],
  308. typeList: [],
  309. tagsList: [],
  310. num: '',
  311. projectForm: {
  312. name: '',
  313. totalAmount: '',
  314. projectType: '1602565634250186753',
  315. tags: '',
  316. introduction: ''
  317. },
  318. rules: {
  319. name: [
  320. { required: true, message: '请输入项目名称', trigger: 'blur' }
  321. ],
  322. projectType: [
  323. {
  324. required: true,
  325. message: '请选择项目类型',
  326. trigger: 'change'
  327. }
  328. ],
  329. tags: [
  330. {
  331. required: true,
  332. message: '请选择项目标签',
  333. trigger: 'change'
  334. }
  335. ]
  336. },
  337. diaType: -1,
  338. parentId: 0,
  339. queryData: null,
  340. owerQuery: {},
  341. projectStageQuery: {},
  342. selectList: []
  343. }
  344. },
  345. created () {
  346. const index = this.option.column.findIndex(sub => sub.prop === 'lot')
  347. console.log(index)
  348. if (this.user.info.viewStage === 1) {
  349. this.option.column[index].hide = true
  350. } else {
  351. this.option.column[index].hide = false
  352. }
  353. this.$bus.on('serach', (res, type) => {
  354. this.owerQuery = res
  355. if (res.type === false) {
  356. this.projectStageQuery.projectStage = ''
  357. this.active = 0
  358. }
  359. this.onLoad(Object.assign(this.owerQuery, this.projectStageQuery))
  360. })
  361. this.getTypeList()
  362. },
  363. unmounted () {
  364. sessionStorage.removeItem('selectList')
  365. },
  366. methods: {
  367. switchTab (item, index) {
  368. this.active = index
  369. this.projectStageQuery = { projectStage: item.dictKey }
  370. this.onLoad(Object.assign(this.owerQuery, this.projectStageQuery))
  371. },
  372. onLoad (query = {}) {
  373. this.loading = true
  374. const data = { ...query, parentId: this.parentId }
  375. this.queryData = data
  376. this.getNumList(data)
  377. this.$api.project.projectList(this.page.currentPage, this.page.pageSize, data).then(res => {
  378. this.loading = false
  379. if (res.code === 200) {
  380. this.data = res.data.records.map(e => {
  381. e.projectStage = e.projectStage + ''
  382. e.selected = true
  383. return e
  384. })
  385. this.page.total = res.data.total
  386. }
  387. }).finally(() => {
  388. this.loading = false
  389. })
  390. },
  391. selectionChange (list) {
  392. this.selectList = list.map(sub => sub.id)
  393. },
  394. beforeOpen (done, type) {
  395. if (['edit'].includes(type)) {
  396. this.$router.push({
  397. path: '/home/details',
  398. query: { id: this.form.id, type: '0', ownerId: this.form.createUser }
  399. })
  400. } else if (type === 'view') {
  401. this.$router.push({
  402. path: '/home/pro_detail',
  403. query: { id: this.form.id, projectStage: this.form.project_stage }
  404. })
  405. }
  406. },
  407. currentChange (currentPage) {
  408. this.page.current = currentPage
  409. },
  410. sizeChange (pageSize) {
  411. this.page.size = pageSize
  412. },
  413. refreshChange () {
  414. this.onLoad()
  415. },
  416. treeLoad (tree, treeNode, resolve) {
  417. this.loading = true
  418. getLazyList(tree.id).then(res => {
  419. this.loading = false
  420. resolve(res.data.data.childrenList.map(e => {
  421. e.project_stage = e.project_stage.toString()
  422. return e
  423. }))
  424. })
  425. },
  426. rowDel (row) {
  427. this.$confirm('确定删除选择的项目?', {
  428. confirmButtonText: '确定',
  429. cancelButtonText: '取消',
  430. type: 'warning'
  431. })
  432. .then(() => {
  433. this.$api.project.projectRemove({ ids: row.id }).then(res => {
  434. if (res.code === 200) {
  435. this.$message.success(res.msg)
  436. this.onLoad()
  437. } else {
  438. this.$message.error(res.msg)
  439. }
  440. })
  441. })
  442. },
  443. getNumList (data) {
  444. if (this.numList.length > 0) {
  445. return
  446. }
  447. this.$api.project.userNunList(data).then(res => {
  448. if (res.code === 200) {
  449. this.numList = res.data.projectStage
  450. this.num = res.data.projectStage[0].totalAmount
  451. }
  452. })
  453. },
  454. getTypeList () {
  455. this.$api.project.typeList({ type: 1, size: 999, current: 1 }).then(res => {
  456. this.typeList = res.data.records
  457. })
  458. this.$api.common.dicList({ code: 'project-tags' }).then(res => {
  459. if (res.code === 200) {
  460. this.tagsList = res.data
  461. }
  462. })
  463. },
  464. track (res) {
  465. if (res.isReport === 1) {
  466. this.$message.error('该项目已经上报')
  467. return
  468. }
  469. this.$confirm('是否确定进行项目上报', {
  470. confirmButtonText: '确定',
  471. cancelButtonText: '取消',
  472. type: 'warning'
  473. }).then(() => {
  474. const data = { id: res.id, is_report: 1 }
  475. this.$api.project.proUpdate(data).then(res => {
  476. if (res.code === 200) {
  477. this.onLoad()
  478. this.$message.success(res.msg)
  479. } else {
  480. this.$message.error(res.msg)
  481. }
  482. })
  483. })
  484. },
  485. projectSave () {
  486. if (this.disable) {
  487. this.$message.error('正在处理,请稍后...')
  488. return
  489. }
  490. this.$refs.form.validate((valid) => {
  491. if (valid) {
  492. this.disable = true
  493. this.addLoading = true
  494. this.$api.project.projectAdd(this.projectForm).then(res => {
  495. this.disable = false
  496. this.addLoading = false
  497. if (res.code === 200) {
  498. this.showAdd = false
  499. this.$message.success(res.msg)
  500. this.onLoad()
  501. } else {
  502. this.showAdd = false
  503. this.$message.error(res.msg)
  504. }
  505. })
  506. }
  507. })
  508. },
  509. formDialogClose () {
  510. this.diaType = -1
  511. this.onLoad()
  512. },
  513. exportExcel (res) {
  514. const data = Object.assign(this.queryData, { columnName: res, projectIds: this.selectList.join(',') })
  515. this.$api.params.exportResult(data).then(res => {
  516. if (res.hasOwnProperty('code')) {
  517. this.$message.error(res.msg)
  518. return
  519. }
  520. this.download(res)
  521. })
  522. },
  523. exportExcelTotal (item) {
  524. const data = { ...this.queryData, projectIds: this.selectList.join(',') }
  525. this.$api.params.summaryExport(Object.assign(item, data)).then(res => {
  526. if (res.hasOwnProperty('code')) {
  527. this.$message.error(res.msg)
  528. return
  529. }
  530. this.download(res)
  531. })
  532. },
  533. download (res) {
  534. const url = window.URL.createObjectURL(new Blob([res], { type: 'application/vnd.ms-excel' }))
  535. const link = document.createElement('a')
  536. link.style.display = 'none'
  537. link.href = url
  538. const excelName = new Date().getTime() + '.xlsx'
  539. link.setAttribute('download', excelName)
  540. document.body.appendChild(link)
  541. link.click()
  542. link.remove()
  543. this.diaType = -1
  544. this.$message.success('导出成功')
  545. }
  546. }
  547. }
  548. </script>
  549. <style lang="scss" scoped>
  550. .tip {
  551. width: 260px;
  552. height: 38px;
  553. background-color: #FBF6ED;
  554. font-weight: 500;
  555. flex-wrap: nowrap;
  556. margin-left: 20px;
  557. }
  558. .total-s {
  559. width: 200px;
  560. height: 50px;
  561. border: 1px solid #825618;
  562. border-radius: 10px;
  563. margin-right: 20px;
  564. box-shadow: 2px 2px 10px 2px rgba(113, 73, 39, 0.3);
  565. .sp {
  566. color: #ECAB56;
  567. white-space: nowrap;
  568. margin-left: 20px;
  569. }
  570. .sp1 {
  571. color: #ECAB56;
  572. margin-right: 20px;
  573. }
  574. }
  575. .total {
  576. width: 200px;
  577. height: 50px;
  578. border-radius: 10px;
  579. margin-right: 20px;
  580. background-color: #F0F2F7;
  581. .sp {
  582. color: #707070;
  583. white-space: nowrap;
  584. margin-left: 20px;
  585. }
  586. .sp1 {
  587. color: #825618;
  588. margin-right: 20px;
  589. }
  590. }
  591. .curd {
  592. :deep(.avue-crud__menu) {
  593. min-height: 10px;
  594. }
  595. }
  596. </style>