dash.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  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 !== 0' 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. refreshBtn: false,
  194. tip: false,
  195. lazy: true,
  196. columnBtn: false,
  197. searchShow: true,
  198. selection: true,
  199. editBtn: true,
  200. editBtnText: '资料管理',
  201. editBtnIcon: 'Document',
  202. addBtn: false,
  203. delBtn: true,
  204. border: true,
  205. index: true,
  206. reserveSelection: true,
  207. align: 'center',
  208. viewBtn: true,
  209. viewBtnText: '详情',
  210. menuWidth: 380,
  211. dialogClickModal: false,
  212. column: [
  213. {
  214. label: '项目名称',
  215. prop: 'name',
  216. addDisplay: false,
  217. editDisplay: false,
  218. width: 480
  219. },
  220. {
  221. label: '项目情况',
  222. prop: 'project_stage',
  223. type: 'select',
  224. width: 180,
  225. dicUrl: '/api/blade-system/dict-biz/dictionary?code=project-situation',
  226. props: {
  227. label: 'dictValue',
  228. value: 'dictKey'
  229. }
  230. },
  231. {
  232. label: '项目总投资(万元)',
  233. prop: 'total_amount',
  234. width: 180,
  235. type: 'number',
  236. precision: 2,
  237. formatter: (val, value, label) => {
  238. return val.total_amount.toLocaleString()
  239. }
  240. },
  241. {
  242. label: '责任单位',
  243. prop: 'create_dept_name',
  244. width: 180
  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: 180
  296. }]
  297. },
  298. page: {
  299. size: 10,
  300. current: 1,
  301. total: 0
  302. },
  303. stage: [],
  304. numList: [],
  305. typeList: [],
  306. tagsList: [],
  307. num: '',
  308. projectForm: {
  309. name: '',
  310. totalAmount: '',
  311. projectType: '1602565634250186753',
  312. tags: '',
  313. introduction: ''
  314. },
  315. rules: {
  316. name: [
  317. { required: true, message: '请输入项目名称', trigger: 'blur' }
  318. ],
  319. projectType: [
  320. {
  321. required: true,
  322. message: '请选择项目类型',
  323. trigger: 'change'
  324. }
  325. ],
  326. tags: [
  327. {
  328. required: true,
  329. message: '请选择项目标签',
  330. trigger: 'change'
  331. }
  332. ]
  333. },
  334. diaType: -1,
  335. parentId: 0,
  336. queryData: null,
  337. owerQuery: {},
  338. projectStageQuery: {},
  339. selectList: []
  340. }
  341. },
  342. created () {
  343. const index = this.option.column.findIndex(sub => sub.prop === 'lot')
  344. console.log(index)
  345. if (this.user.info.viewStage === 1) {
  346. this.option.column[index].hide = true
  347. } else {
  348. this.option.column[index].hide = false
  349. }
  350. this.$bus.on('serach', (res, type) => {
  351. this.owerQuery = res
  352. if (res.type === false) {
  353. this.projectStageQuery.projectStage = ''
  354. this.active = 0
  355. }
  356. this.onLoad(Object.assign(this.owerQuery, this.projectStageQuery))
  357. })
  358. this.getTypeList()
  359. },
  360. unmounted () {
  361. sessionStorage.removeItem('selectList')
  362. },
  363. methods: {
  364. switchTab (item, index) {
  365. this.active = index
  366. this.projectStageQuery = { projectStage: item.dictKey }
  367. this.onLoad(Object.assign(this.owerQuery, this.projectStageQuery))
  368. },
  369. onLoad (query = {}) {
  370. this.loading = true
  371. const data = { ...query, parentId: this.parentId }
  372. this.queryData = data
  373. this.getNumList(data)
  374. this.$api.project.projectList(this.page.currentPage, this.page.pageSize, data).then(res => {
  375. this.loading = false
  376. if (res.code === 200) {
  377. this.data = res.data.records.map(e => {
  378. e.projectStage = e.projectStage + ''
  379. e.selected = true
  380. return e
  381. })
  382. this.page.total = res.data.total
  383. }
  384. }).finally(() => {
  385. this.loading = false
  386. })
  387. },
  388. selectionChange (list) {
  389. this.selectList = list.map(sub => sub.id)
  390. },
  391. beforeOpen (done, type) {
  392. if (['edit'].includes(type)) {
  393. this.$router.push({
  394. path: '/home/details',
  395. query: { id: this.form.id, type: '0', ownerId: this.form.createUser }
  396. })
  397. } else if (type === 'view') {
  398. this.$router.push({
  399. path: '/home/pro_detail',
  400. query: { id: this.form.id, projectStage: this.form.project_stage }
  401. })
  402. }
  403. },
  404. currentChange (currentPage) {
  405. this.page.current = currentPage
  406. },
  407. sizeChange (pageSize) {
  408. this.page.size = pageSize
  409. },
  410. refreshChange () {
  411. this.onLoad()
  412. },
  413. treeLoad (tree, treeNode, resolve) {
  414. this.loading = true
  415. getLazyList(tree.id).then(res => {
  416. this.loading = false
  417. resolve(res.data.data.childrenList.map(e => {
  418. e.project_stage = e.project_stage.toString()
  419. return e
  420. }))
  421. })
  422. },
  423. rowDel (row) {
  424. this.$confirm('确定删除选择的项目?', {
  425. confirmButtonText: '确定',
  426. cancelButtonText: '取消',
  427. type: 'warning'
  428. })
  429. .then(() => {
  430. this.$api.project.projectRemove({ ids: row.id }).then(res => {
  431. if (res.code === 200) {
  432. this.$message.success(res.msg)
  433. this.onLoad()
  434. } else {
  435. this.$message.error(res.msg)
  436. }
  437. })
  438. })
  439. },
  440. getNumList (data) {
  441. if (this.numList.length > 0) {
  442. return
  443. }
  444. this.$api.project.userNunList(data).then(res => {
  445. if (res.code === 200) {
  446. this.numList = res.data.projectStage
  447. this.num = res.data.projectStage[0].totalAmount
  448. }
  449. })
  450. },
  451. getTypeList () {
  452. this.$api.project.typeList({ type: 1, size: 999, current: 1 }).then(res => {
  453. this.typeList = res.data.records
  454. })
  455. this.$api.common.dicList({ code: 'project-tags' }).then(res => {
  456. if (res.code === 200) {
  457. this.tagsList = res.data
  458. }
  459. })
  460. },
  461. track (res) {
  462. if (res.isReport === 1) {
  463. this.$message.error('该项目已经上报')
  464. return
  465. }
  466. this.$confirm('是否确定进行项目上报', {
  467. confirmButtonText: '确定',
  468. cancelButtonText: '取消',
  469. type: 'warning'
  470. }).then(() => {
  471. const data = { id: res.id, is_report: 1 }
  472. this.$api.project.proUpdate(data).then(res => {
  473. if (res.code === 200) {
  474. this.onLoad()
  475. this.$message.success(res.msg)
  476. } else {
  477. this.$message.error(res.msg)
  478. }
  479. })
  480. })
  481. },
  482. projectSave () {
  483. if (this.disable) {
  484. this.$message.error('正在处理,请稍后...')
  485. return
  486. }
  487. this.$refs.form.validate((valid) => {
  488. if (valid) {
  489. this.disable = true
  490. this.addLoading = true
  491. this.$api.project.projectAdd(this.projectForm).then(res => {
  492. this.disable = false
  493. this.addLoading = false
  494. if (res.code === 200) {
  495. this.showAdd = false
  496. this.$message.success(res.msg)
  497. this.onLoad()
  498. } else {
  499. this.showAdd = false
  500. this.$message.error(res.msg)
  501. }
  502. })
  503. }
  504. })
  505. },
  506. formDialogClose () {
  507. this.diaType = -1
  508. this.onLoad()
  509. },
  510. exportExcel (res) {
  511. const data = Object.assign(this.queryData, { columnName: res, projectIds: this.selectList.join(',') })
  512. this.$api.params.exportResult(data).then(res => {
  513. if (res.hasOwnProperty('code')) {
  514. this.$message.error(res.msg)
  515. return
  516. }
  517. this.download(res)
  518. })
  519. },
  520. exportExcelTotal (item) {
  521. const data = { ...this.queryData, projectIds: this.selectList.join(',') }
  522. this.$api.params.summaryExport(Object.assign(item, data)).then(res => {
  523. if (res.hasOwnProperty('code')) {
  524. this.$message.error(res.msg)
  525. return
  526. }
  527. this.download(res)
  528. })
  529. },
  530. download (res) {
  531. const url = window.URL.createObjectURL(new Blob([res], { type: 'application/vnd.ms-excel' }))
  532. const link = document.createElement('a')
  533. link.style.display = 'none'
  534. link.href = url
  535. const excelName = new Date().getTime() + '.xlsx'
  536. link.setAttribute('download', excelName)
  537. document.body.appendChild(link)
  538. link.click()
  539. link.remove()
  540. this.diaType = -1
  541. this.$message.success('导出成功')
  542. }
  543. }
  544. }
  545. </script>
  546. <style lang="scss" scoped>
  547. .tip {
  548. width: 260px;
  549. height: 38px;
  550. background-color: #FBF6ED;
  551. font-weight: 500;
  552. flex-wrap: nowrap;
  553. margin-left: 20px;
  554. }
  555. .total-s {
  556. width: 200px;
  557. height: 50px;
  558. border: 1px solid #825618;
  559. border-radius: 10px;
  560. margin-right: 20px;
  561. box-shadow: 2px 2px 10px 2px rgba(113, 73, 39, 0.3);
  562. .sp {
  563. color: #ECAB56;
  564. white-space: nowrap;
  565. margin-left: 20px;
  566. }
  567. .sp1 {
  568. color: #ECAB56;
  569. margin-right: 20px;
  570. }
  571. }
  572. .total {
  573. width: 200px;
  574. height: 50px;
  575. border-radius: 10px;
  576. margin-right: 20px;
  577. background-color: #F0F2F7;
  578. .sp {
  579. color: #707070;
  580. white-space: nowrap;
  581. margin-left: 20px;
  582. }
  583. .sp1 {
  584. color: #825618;
  585. margin-right: 20px;
  586. }
  587. }
  588. .curd {
  589. :deep(.avue-crud__menu) {
  590. min-height: 10px;
  591. }
  592. }
  593. </style>