ProjectProgressNew.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. <template>
  2. <transition
  3. appear
  4. name="animate__animated animate__move"
  5. enter-active-class="animate__slideInRight"
  6. leave-active-class="animate__slideOutRight"
  7. >
  8. <div class="widget-ProjectProgress">
  9. <div class="head">
  10. <div class="title">
  11. <div class="icon"></div>
  12. <span class="site-info">项目进展</span>
  13. </div>
  14. </div>
  15. <div class="content-info">
  16. <!-- -->
  17. <div class="content-item progress">
  18. <div class="title">
  19. <div class="icon"></div>
  20. <span class="item-name">施工情况</span>
  21. </div>
  22. <div class="content contruct-content">
  23. {{ contructContent }}
  24. </div>
  25. </div>
  26. <!-- -->
  27. <div class="content-item settlement">
  28. <div class="title">
  29. <div class="icon"></div>
  30. <span class="item-name">本年结算情况</span>
  31. </div>
  32. <div class="content">
  33. <div class="chart-container">
  34. <ComRightAngelPercentChart :value="0" />
  35. </div>
  36. <div class="dataInfo">
  37. <div class="data-item" v-for="(item, index) of settleList" :key="item.name">
  38. <div
  39. class="pointSymbol"
  40. :style="index == 0 ? 'background:#2BA7FF;box-shadow: 0px 0px 10px #2BA7FF;' : ''"
  41. ></div>
  42. <div class="data-wrap">
  43. <div class="data-title">{{ item.name }}</div>
  44. <div class="data-value data-value-settle">{{ item.value }} {{ item.unit }}</div>
  45. </div>
  46. </div>
  47. </div>
  48. </div>
  49. </div>
  50. <!-- -->
  51. <div class="content-item outputValue">
  52. <div class="title">
  53. <div class="icon"></div>
  54. <span class="item-name">建管本年产值统计</span>
  55. </div>
  56. <div class="content">
  57. <div class="chart-container">
  58. <div class="chart-title">
  59. 完成占比:<span>{{ complishPercent }}%</span>
  60. </div>
  61. <ComThreeDimensionsChart v-on="{ fontSize }" :chartData="staList" :distance="150" />
  62. <!-- 底座背景 -->
  63. <div class="bg" :style="{ 'background-image': `url('${url}')` }"></div>
  64. </div>
  65. <div class="dataInfo">
  66. <div class="data-item" v-for="(item, index) of staList" :key="item.name">
  67. <div
  68. class="pointSymbol"
  69. :style="index == 0 ? 'background:#13D28A;box-shadow: 0px 0px 10px #13D28A;' : ''"
  70. ></div>
  71. <div class="data-wrap">
  72. <div class="data-title">{{ item.name }}</div>
  73. <div class="data-value">{{ item.value }} {{ item.unit }}</div>
  74. </div>
  75. </div>
  76. </div>
  77. </div>
  78. </div>
  79. <!-- -->
  80. <div class="content-item outputValueSta">
  81. <div class="title">
  82. <div class="icon"></div>
  83. <span class="item-name">建管本年产值统计</span>
  84. </div>
  85. <div class="content OVSContent">
  86. <div class="bc-item">
  87. <div class="bc-title">人员配置</div>
  88. <div class="bc-value">
  89. 总人数:<span>{{ totalPerson }}</span>
  90. </div>
  91. </div>
  92. <div class="bc-content">
  93. <div class="bcb-item" v-for="item in staffingList" :key="item.title">
  94. <div class="bcb-title">{{ item.title }}</div>
  95. <div class="bcb-value">{{ item.value }}{{ item.unit }}</div>
  96. </div>
  97. </div>
  98. <div class="bc-item">
  99. <div class="bc-title">设备配置</div>
  100. <div class="bc-value">
  101. 总数量:<span>{{ totalDevice }}</span>
  102. </div>
  103. </div>
  104. <div class="bcb-content">
  105. <el-carousel :autoplay="true" direction="vertical">
  106. <el-carousel-item v-for="(part, index) in devicesList" :key="index">
  107. <div class="bcbc-item" v-for="item in part.list" :key="item.title">
  108. <div class="bcbc-title">{{ item.title }}</div>
  109. <div class="bcbc-value">{{ item.value }}{{ item.unit }}</div>
  110. </div>
  111. </el-carousel-item>
  112. </el-carousel>
  113. </div>
  114. </div>
  115. </div>
  116. </div>
  117. </div>
  118. </transition>
  119. </template>
  120. <script lang="ts">
  121. import url from '../../images/base64/3dBaseBack'
  122. import { getRequestResult, getEpcBuilding, getWeekCountData } from '../../apis'
  123. import { setNullAndUndefinedEmpty, fontSize as fs } from '../../util'
  124. import { Vue, Component, Prop, Watch } from 'vue-property-decorator'
  125. import ComOneBatteryChart from '../../components/BatteryChart/ComOneBatteryChart.vue'
  126. import ComProgressChart from '../../components/BarChart/ComProgressChart.vue'
  127. import ComProgressPieChart from '../../components/PieChart/ComProgressPieChart.vue'
  128. import ComThreeDimensionsChart from '../../components/OthersChart/ComThreeDimensionsChart.vue'
  129. import ComRightAngelPercentChart from '../../components/PieChart/ComRightAngelPercentChart.vue'
  130. const staffTypeList = [
  131. { code: 'person_all', name: '总包部' },
  132. { code: 'person_build', name: '建设部' },
  133. { code: 'person_survey', name: '勘察单位' },
  134. { code: 'person_design', name: '设计单位' },
  135. { code: 'person_construct', name: '施工单位' }
  136. ]
  137. const deviceTypeList = [
  138. { code: 'device_gdjc', name: '管道检修设备' },
  139. { code: 'device_kw', name: '开挖设备' },
  140. { code: 'device_other', name: '其他设备' },
  141. { code: 'device_cl', name: '测量设备' },
  142. { code: 'device_fdj', name: '发电机' },
  143. { code: 'device_dj', name: '地基处理设备' },
  144. { code: 'device_zh', name: '支护设备' },
  145. { code: 'device_tz', name: '填筑设备' },
  146. { code: 'device_ly', name: '拉运设备' }
  147. ]
  148. //项目进展
  149. @Component({
  150. name: 'ProjectProgress',
  151. components: {
  152. ComOneBatteryChart,
  153. ComProgressChart,
  154. ComProgressPieChart,
  155. ComThreeDimensionsChart,
  156. ComRightAngelPercentChart
  157. }
  158. })
  159. export default class ProjectProgress extends Vue {
  160. @Prop({ type: Object, default: () => {} }) dataInfo!: any
  161. url = url
  162. investData: object = {}
  163. contructContent: string = ''
  164. progressData: Array<any> = [{}, {}, {}]
  165. buildStatusData: object = {}
  166. buildDredgingData: object = {}
  167. complishPercent: number = 0 //完成占比
  168. settleList: Array<any> = [{}, {}]
  169. staList: Array<any> = [{}, {}]
  170. totalPerson: number = 0
  171. totalDevice: number = 0
  172. staffingList: Array<any> = []
  173. devicesList: Array<any> = []
  174. get fontSize() {
  175. return fs
  176. }
  177. get setNoNull() {
  178. return setNullAndUndefinedEmpty
  179. }
  180. get projectCode() {
  181. return this.$store.state.bigScreen.currentProjectCode
  182. }
  183. roundFun(value, n) {
  184. return Math.round(value * Math.pow(10, n)) / Math.pow(10, n)
  185. }
  186. reset() {
  187. this.staffingList = []
  188. this.devicesList = []
  189. }
  190. @Watch('dataInfo')
  191. onChangMehod() {
  192. this.getPageData()
  193. }
  194. async getPageData() {
  195. this.reset()
  196. if (!this.dataInfo) return
  197. const { contract_epc_money, contract_ppp_money } = this.dataInfo || {}
  198. //投资完成情况
  199. this.investData = {
  200. num: this.roundFun(contract_epc_money / 10 ** 8, 4),
  201. numUnit: '亿',
  202. total: this.roundFun(contract_ppp_money / 10 ** 8, 4),
  203. totalUnit: '亿'
  204. }
  205. //获取施工状态
  206. this.getBuildStatusData()
  207. //周报统计
  208. const weekCountData = await getWeekCountData({ code: this.projectCode })
  209. const wcd = weekCountData.result
  210. if (!wcd) return
  211. const { year_finish_ratio, year_finish, year_plan, contruct_content } = wcd || {}
  212. this.contructContent = contruct_content
  213. this.complishPercent = year_finish_ratio
  214. this.staList = [
  215. { name: '本年完成产值', value: +year_finish, unit: '万元' },
  216. { name: '年度产值计划', value: +year_plan, unit: '万元' }
  217. ]
  218. this.settleList = [
  219. { name: '本年投资计划', value: 0, unit: '万元' },
  220. { name: '本年结算金额', value: 0, unit: '万元' }
  221. ]
  222. let staff = [],
  223. devices = []
  224. for (const key in wcd) {
  225. if (key.includes('person_') && key != 'person_num') {
  226. staff.push(this.assembleStuff(key, wcd))
  227. }
  228. if (key.includes('device_') && key != 'device_num') {
  229. devices.push(this.assembleDevice(key, wcd))
  230. }
  231. }
  232. this.staffingList = staff
  233. this.totalPerson = this.countTotal(staff, 'value')
  234. this.devicesList = this.splitArr(devices, 4)
  235. this.totalDevice = this.countTotal(devices, 'value')
  236. }
  237. splitArr(res, splitNum = 5) {
  238. let temp = [],
  239. symbol = 0
  240. res.forEach((item: any, index) => {
  241. if ((index + 1) % splitNum != 0) {
  242. temp[symbol] ? temp[symbol].list.push({ ...item }) : temp.push({ list: [{ ...item }] })
  243. } else {
  244. temp[symbol].list.length < splitNum ? temp[symbol].list.push({ ...item }) : temp.push({ list: [{ ...item }] })
  245. symbol++
  246. }
  247. })
  248. return temp
  249. }
  250. countTotal(arr, keyName) {
  251. let total = 0
  252. total = arr.reduce(function (total, currentValue, currentIndex, arr) {
  253. return currentValue[keyName] ? total + currentValue[keyName] : total
  254. }, 0)
  255. return total
  256. }
  257. assembleStuff(key, obj) {
  258. let target: any = staffTypeList.find((i) => i.code == key) || {}
  259. return { title: target.name, value: +obj[key], unit: '人' }
  260. }
  261. assembleDevice(key, obj) {
  262. let target: any = deviceTypeList.find((i) => i.code == key) || {}
  263. return { title: target.name, value: +obj[key], unit: '台' }
  264. }
  265. //施工状态
  266. async getBuildStatusData() {
  267. const buildStatusData = await getEpcBuilding({ code: this.projectCode })
  268. if (buildStatusData.result.length == 0) return
  269. const bsd = buildStatusData.result
  270. this.buildStatusData = {
  271. building: bsd.filter((i) => i.node_status == 'doing').length,
  272. finished: bsd.filter((i) => i.node_status == 'done').length,
  273. nobuilding: bsd.filter((i) => i.node_status == 'normal').length,
  274. unit: '总作业面'
  275. }
  276. }
  277. }
  278. </script>
  279. <style lang='scss' scoped>
  280. .animate__slideInRight,
  281. .animate__slideOutRight {
  282. animation-duration: 3s; //动画持续时间
  283. animation-delay: 0s; //动画延迟时间
  284. }
  285. .widget-ProjectProgress {
  286. $size10: 0.052083rem /* 10/192 */;
  287. $size20: 0.104167rem /* 20/192 */;
  288. z-index: 2;
  289. //position
  290. bottom: $size10 /* 10/192 */;
  291. margin-right: $size20 /* 20/192 */;
  292. right: 0;
  293. position: absolute;
  294. //size
  295. height: calc(100% - 0.557292rem /* 107/192 */);
  296. width: 2.083333rem /* 400/192 */;
  297. color: #eee;
  298. .head {
  299. height: 0.166667rem /* 32/192 */;
  300. width: 100%;
  301. background: linear-gradient(-90deg, rgba(43, 167, 255, 0.2) 0%, rgba(43, 167, 255, 0.08) 100%);
  302. font-family: Source Han Sans CN-HEAVY;
  303. .title {
  304. width: 100%;
  305. height: 100%;
  306. display: flex;
  307. font-weight: 400;
  308. align-items: center;
  309. .icon {
  310. height: 0.166667rem /* 32/192 */;
  311. width: 0.34375rem /* 66/192 */;
  312. background: url('~@/views/groupPage/images/模块图标/项目进展.png') no-repeat center center;
  313. background-size: 100% 100%;
  314. }
  315. span {
  316. flex: 1;
  317. font-weight: bold;
  318. font-size: 0.083333rem /* 16/192 */;
  319. color: #ffffff;
  320. padding: 0.041667rem /* 8/192 */;
  321. background: linear-gradient(0deg, #9bd2fa 0%, #ffffff 100%);
  322. background-clip: text;
  323. -webkit-text-fill-color: transparent;
  324. }
  325. }
  326. }
  327. .content-info {
  328. width: 100%;
  329. height: calc(100% - 0.166667rem);
  330. overflow: auto;
  331. .content-item {
  332. margin-top: 0.052083rem /* 10/192 */;
  333. padding: 0.088542rem /* 17/192 */ 0.046875rem /* 9/192 */ 0.083333rem /* 16/192 */ 0.046875rem /* 9/192 */;
  334. width: 100%;
  335. float: left;
  336. overflow: hidden;
  337. background: linear-gradient(0deg, rgba(14, 167, 255, 0.1) 0%, rgba(14, 167, 255, 0.1) 100%);
  338. .title {
  339. width: 100%;
  340. display: flex;
  341. margin-bottom: 0.0625rem /* 12/192 */;
  342. .icon {
  343. height: 0.072917rem /* 14/192 */;
  344. width: 0.078125rem /* 15/192 */;
  345. margin-right: 0.046875rem /* 9/192 */;
  346. background: url('~@/views/groupPage/images/三角.png') no-repeat center center;
  347. background-size: 100% 100%;
  348. }
  349. .item-name {
  350. font-family: Source Han Sans CN;
  351. color: #0ea7ff;
  352. font-size: 0.072917rem /* 14/192 */;
  353. font-weight: 500;
  354. }
  355. .item-info {
  356. color: #2ba7ff;
  357. font-size: 0.072917rem /* 14/192 */;
  358. font-weight: 500;
  359. }
  360. }
  361. .content {
  362. height: calc(100% - 0.125rem /* 24/192 */);
  363. width: 100%;
  364. display: flex;
  365. }
  366. .contruct-content {
  367. overflow: auto;
  368. text-indent: 1cm; //28px
  369. color: #feffff;
  370. font-family: Source Han Sans CN;
  371. font-size: 0.07292rem;
  372. line-height: 0.109375rem /* 21/192 */;
  373. word-break: break-all;
  374. }
  375. .chart-container {
  376. width: 50%;
  377. position: relative;
  378. .chart-title {
  379. position: absolute;
  380. left: 0.15625rem /* 30/192 */;
  381. font-size: 0.083333rem /* 16/192 */;
  382. font-family: Source Han Sans CN;
  383. font-weight: bold;
  384. top: 0.052083rem /* 10/192 */;
  385. color: #ffffff;
  386. white-space: normal;
  387. span {
  388. font-family: AgencyFB-Bold;
  389. font-weight: bold;
  390. font-size: 0.104167rem /* 20/192 */;
  391. }
  392. }
  393. .bg {
  394. width: 100%;
  395. height: 100%;
  396. }
  397. .bg {
  398. position: absolute;
  399. top: 0;
  400. left: -0.026042rem /* 5/192 */;
  401. right: 0;
  402. margin: 0 auto;
  403. width: 80%;
  404. // width: 55%;
  405. // height: 95%;
  406. // left: 0.041667rem /* 8/192 */;
  407. z-index: -1;
  408. // width: 0.46875rem /* 90/192 */;
  409. // height: 0.390625rem /* 75/192 */;
  410. background: no-repeat center;
  411. // background-image: url('~@/views/groupPage/images/底座样式.png');
  412. background-size: 100% 100%;
  413. }
  414. }
  415. .dataInfo {
  416. width: 50%;
  417. display: flex;
  418. flex-flow: column;
  419. justify-content: space-around;
  420. font-family: Source Han Sans CN;
  421. .data-item {
  422. display: flex;
  423. align-items: center;
  424. padding: 0.052083rem /* 10/192 */;
  425. color: #ffffff;
  426. height: 45%;
  427. position: relative;
  428. .pointSymbol,
  429. .data-title {
  430. margin-right: 0.052083rem /* 10/192 */;
  431. white-space: nowrap;
  432. }
  433. .pointSymbol {
  434. border-radius: 50%;
  435. height: 0.052083rem /* 10/192 */;
  436. width: 0.052083rem /* 10/192 */;
  437. }
  438. .data-wrap {
  439. display: flex;
  440. flex-flow: column;
  441. }
  442. .data-title {
  443. font-size: 0.072917rem /* 14/192 */;
  444. font-weight: 400;
  445. color: #87bfe8;
  446. }
  447. .data-value {
  448. font-size: 0.09375rem /* 18/192 */;
  449. font-family: AgencyFB-Bold;
  450. font-weight: bold;
  451. // color: #ffffff;
  452. padding: 0.052083rem /* 10/192 */ 0;
  453. }
  454. }
  455. .data-item:first-child {
  456. .data-value {
  457. color: #13d28a;
  458. background: linear-gradient(0deg, #01b174 0%, #35ffcb 100%);
  459. background-clip: text;
  460. -webkit-text-fill-color: transparent;
  461. }
  462. .data-value-settle {
  463. color: #12d3ff;
  464. background: linear-gradient(180deg, #50d6fd 0%, #2d92fa 100%);
  465. background-clip: text;
  466. -webkit-text-fill-color: transparent;
  467. }
  468. }
  469. .data-item:first-child::after {
  470. content: '';
  471. height: 0.005208rem /* 1/192 */;
  472. width: 90%;
  473. background: rgba(11, 122, 192, 0.3);
  474. left: 0;
  475. right: 0;
  476. bottom: 0;
  477. margin: 0 auto;
  478. position: absolute;
  479. }
  480. }
  481. .OVSContent {
  482. width: 100%;
  483. height: calc(100% - 0.135417rem /* 26/192 */);
  484. display: flex;
  485. flex-flow: column;
  486. .bc-item {
  487. display: flex;
  488. justify-content: space-between;
  489. align-items: center;
  490. width: 100%;
  491. height: 0.208333rem /* 40/192 */;
  492. background: rgba(14, 167, 255, 0.14);
  493. padding: 0.052083rem /* 10/192 */;
  494. margin-bottom: 0.03125rem /* 6/192 */;
  495. .bc-title {
  496. font-size: 0.072917rem /* 14/192 */;
  497. font-weight: 500;
  498. color: #2ba7ff;
  499. }
  500. .bc-value {
  501. font-size: 0.072917rem /* 14/192 */;
  502. font-weight: 400;
  503. color: rgba(139, 191, 228, 1);
  504. & > span {
  505. font-family: AgencyFB-Bold;
  506. font-weight: bold;
  507. color: rgba(43, 167, 255, 1);
  508. }
  509. }
  510. }
  511. .bc-content {
  512. height: 0.3125rem /* 60/192 */;
  513. width: 100%;
  514. background: rgba(255, 255, 255, 0.1);
  515. margin-bottom: 0.052083rem /* 10/192 */;
  516. display: flex;
  517. justify-content: space-around;
  518. .bcb-item {
  519. height: 100%;
  520. display: flex;
  521. flex-flow: column;
  522. justify-content: space-around;
  523. .bcb-title {
  524. font-size: 0.072917rem /* 14/192 */;
  525. font-weight: 400;
  526. color: #ffffff;
  527. }
  528. .bcb-value {
  529. font-size: 0.072917rem /* 14/192 */;
  530. font-weight: 500;
  531. color: #ffffff;
  532. text-align: center;
  533. }
  534. }
  535. }
  536. .bcb-content {
  537. width: 100%;
  538. height: 0.442708rem /* 85/192 */;
  539. overflow: auto;
  540. /deep/ .el-carousel {
  541. height: 100%;
  542. width: 100%;
  543. .el-carousel__container {
  544. height: inherit;
  545. width: inherit;
  546. }
  547. .el-carousel__indicators {
  548. display: none;
  549. }
  550. }
  551. .bcbc-item {
  552. width: calc(50% - 0.020833rem /* 4/192 */);
  553. height: 0.208333rem /* 40/192 */;
  554. background: rgba(255, 255, 255, 0.1);
  555. float: left;
  556. margin-bottom: 0.026042rem /* 5/192 */;
  557. display: flex;
  558. align-items: center;
  559. justify-content: space-between;
  560. padding: 0 0.052083rem /* 10/192 */;
  561. font-size: 0.072917rem /* 14/192 */;
  562. color: #ffffff;
  563. .bcbc-title {
  564. font-family: Source Han Sans CN;
  565. font-weight: 400;
  566. }
  567. .bcbc-value {
  568. font-family: Source Han Sans CN-MEDIUM;
  569. font-weight: 500;
  570. }
  571. }
  572. .bcbc-item:nth-child(odd) {
  573. margin-right: 0.041667rem /* 8/192 */;
  574. }
  575. }
  576. }
  577. }
  578. .progress {
  579. // height: 0.859375rem /* 165/192 */ !important;
  580. height: calc(100% - 3.666667rem /* 704/192 */) !important;
  581. }
  582. .settlement,
  583. .outputValue {
  584. height: 0.9375rem /* 180/192 */ !important;
  585. }
  586. .outputValueSta {
  587. // height: calc(100% - 3.151042rem /* 605/192 */) !important;
  588. height: 1.583333rem /* 304/192 */ !important;
  589. }
  590. }
  591. }
  592. </style>