powergrid.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
  1. <template>
  2. <div class="app-container">
  3. <el-tabs v-model="activeName" @tab-click="tabClick">
  4. <el-tab-pane label="总览" name="summery">
  5. <el-row type="flex" :gutter="20" style="margin-top: 20px">
  6. <el-col :span="12">
  7. <PieChartBlock title="当日供电量【单位:kW·h】" :opt-cfg="elecQuantity">
  8. </PieChartBlock>
  9. </el-col>
  10. <el-col :span="12">
  11. <PieChartBlock title="当日电费【单位:元】" :opt-cfg="elecCost">
  12. </PieChartBlock>
  13. </el-col>
  14. </el-row>
  15. <el-row type="flex" :gutter="20" style="margin-top: 20px">
  16. <el-col :span="24">
  17. <BarChartBlock title="当日供电量柱状图" :opt-cfg="pvSupplyIndex" />
  18. </el-col>
  19. </el-row>
  20. </el-tab-pane>
  21. <el-tab-pane label="市电" name="first">
  22. <el-col :span="4" :xs="24">
  23. <div class="head-container">
  24. <el-input v-model="areaName" placeholder="请输入区域名称" clearable size="small" prefix-icon="el-icon-search"
  25. style="margin-bottom: 20px" />
  26. </div>
  27. <div class="head-container" style="height: 100vh; overflow: hidden; position: relative;">
  28. <el-tree :data="areaOptions" :props="defaultProps" :expand-on-click-node="false"
  29. :filter-node-method="filterNode" ref="tree" node-key="id" default-expand-all highlight-current
  30. @node-click="handleNodeClick" style="height: calc(100vh - 50px); overflow-y: auto;" />
  31. </div>
  32. </el-col>
  33. <el-col :span="20" :xs="24">
  34. <div class="container-block">
  35. <SubTitle :title="`时段供电数据【${selectedLabel}】`" />
  36. <BaseChart width="100%" height="300px" :option="elecOptions" />
  37. </div>
  38. <div class="container-block">
  39. <div class="ctl-container">
  40. <el-date-picker v-model="dateRange" type="datetimerange" @change="getList"
  41. value-format="yyyy-MM-dd hh:mm:ss" :picker-options="pickerOptions" range-separator="至"
  42. start-placeholder="开始日期" end-placeholder="结束日期" align="right">
  43. </el-date-picker>
  44. </div>
  45. <el-table v-loading="loading" :data="pgSupplyHList">
  46. <el-table-column label="日期" align="center" prop="date" width="180">
  47. <template slot-scope="scope">
  48. <span>{{ parseTime(scope.row.date, '{y}-{m}-{d}') }}</span>
  49. </template>
  50. </el-table-column>
  51. <el-table-column label="时间" align="center" prop="time">
  52. <template slot-scope="scope">
  53. <span>{{ scope.row.time }}</span>
  54. </template>
  55. </el-table-column>
  56. <el-table-column label="计量类型" align="center" prop="meterType">
  57. <template slot-scope="scope">
  58. <span>{{ getMeterTypeName(scope.row.meterType) }}</span>
  59. </template>
  60. </el-table-column>
  61. <el-table-column label="单位电价(¥)" align="center" prop="meterUnitPrice" />
  62. <el-table-column label="供电量(kW·h)" align="center" prop="useElecQuantity" />
  63. <el-table-column label="供电电费(¥)" align="center" prop="useElecCost" />
  64. </el-table>
  65. <pagination :total="total" :page-size.sync="queryParams.pageSize" :page-sizes="[10, 20, 50]"
  66. :page.sync="queryParams.pageNum" @pagination="getList" />
  67. </div>
  68. </el-col>
  69. </el-tab-pane>
  70. <el-tab-pane label="光伏" name="second">
  71. <el-col :span="4" :xs="24">
  72. <div class="head-container">
  73. <el-input v-model="areaName" placeholder="请输入区域名称" clearable size="small" prefix-icon="el-icon-search"
  74. style="margin-bottom: 20px" />
  75. </div>
  76. <div class="head-container" style="height: 100vh; overflow: hidden; position: relative;">
  77. <el-tree :data="areaOptions" :props="defaultProps" :expand-on-click-node="false"
  78. :filter-node-method="filterNode" ref="tree" node-key="id" default-expand-all highlight-current
  79. @node-click="handleNodeClick" style="height: calc(100vh - 50px); overflow-y: auto;" />
  80. </div>
  81. </el-col>
  82. <el-col :span="20" :xs="24">
  83. <div class="container-block">
  84. <SubTitle :title="`时段发电数据【${selectedLabel}】`" />
  85. <BaseChart width="100%" height="300px" :option="pvOptions" />
  86. </div>
  87. <div class="container-block">
  88. <div class="ctl-container">
  89. <el-date-picker v-model="dateRange" type="datetimerange" @change="getList" :picker-options="pickerOptions"
  90. value-format="yyyy-MM-dd hh:mm:ss" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"
  91. align="right">
  92. </el-date-picker>
  93. </div>
  94. <el-table v-loading="loading" :data="pvSupplyHList">
  95. <el-table-column label="日期" align="center" prop="date" width="180">
  96. <template slot-scope="scope">
  97. <span>{{ parseTime(scope.row.date, '{y}-{m}-{d}') }}</span>
  98. </template>
  99. </el-table-column>
  100. <el-table-column label="时间" align="center" prop="time">
  101. <template slot-scope="scope">
  102. <span>{{ scope.row.time }}</span>
  103. </template>
  104. </el-table-column>
  105. <el-table-column label="总发电量(kW·h)" align="center" prop="genElecQuantity" />
  106. <el-table-column label="自用电量(kW·h)" align="center" prop="useElecQuantity" />
  107. <el-table-column label="上网电量(kW·h)" align="center" prop="upElecQuantity" />
  108. <el-table-column label="上网收益(¥)" align="center" prop="upElecEarn" />
  109. </el-table>
  110. <pagination :total="total" :page-size.sync="queryParams.pageSize" :page-sizes="[10, 20, 50]"
  111. :page.sync="queryParams.pageNum" @pagination="getList" />
  112. </div>
  113. </el-col>
  114. </el-tab-pane>
  115. </el-tabs>
  116. </div>
  117. </template>
  118. <script>
  119. import {ApiCode} from '@/api/apiEmums'
  120. import {areaWithFacsCategoryAsTree} from '@/api/basecfg/area'
  121. import {get} from '@/api/commonApi'
  122. import {listPgSupplyH, listPvSupplyH} from '@/api/mgr/pgSupplyH'
  123. import BaseChart from '@/components/BaseChart'
  124. import BarChartBlock from '@/components/Block/charts/BarChartBlock.vue'
  125. import PieChartBlock from '@/components/Block/charts/PieChartBlock.vue'
  126. import SubTitle from '@/components/SubTitle'
  127. import {DateTool} from '@/utils/DateTool'
  128. import dayjs from 'dayjs'
  129. export default {
  130. name: 'PgSupplyH',
  131. dicts: ['meter_type'],
  132. components: {
  133. BarChartBlock,
  134. PieChartBlock,
  135. BaseChart,
  136. SubTitle
  137. },
  138. data () {
  139. return {
  140. activeName: 'summery',
  141. // 遮罩层
  142. loading: true,
  143. // 总条数
  144. total: 0,
  145. facsCategory: '',
  146. facsSubCategory: '',
  147. areaName: undefined,
  148. selectedLabel: '全部',
  149. pgSupplyHList: [],
  150. pgSupplyTodayList: [],
  151. pvSupplyHList: [],
  152. pvSupplyTodayList: [],
  153. defaultProps: {
  154. children: 'children',
  155. label: 'label'
  156. },
  157. // 查询参数
  158. queryParams: {
  159. areaCode: '-1',
  160. startRecTime: dayjs().subtract(1, 'month').format(DateTool.DateFormat.YYYY_MM_DD_HH_mm),
  161. endRecTime: dayjs().format(DateTool.DateFormat.YYYY_MM_DD_HH_mm)
  162. },
  163. areaOptions: [],
  164. elecData: [],
  165. dateRange: [dayjs().format(DateTool.DateFormat.YYYY_MM_DD_00_00_00), dayjs().format(DateTool.DateFormat.YYYY_MM_DD_23_59_59)],
  166. pickerOptions: {
  167. shortcuts: [
  168. {
  169. text: '最近一周',
  170. onClick (picker) {
  171. const end = new Date()
  172. const start = new Date()
  173. start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
  174. picker.$emit('pick', [start, end])
  175. }
  176. }, {
  177. text: '最近一个月',
  178. onClick (picker) {
  179. const end = new Date()
  180. const start = new Date()
  181. start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
  182. picker.$emit('pick', [start, end])
  183. }
  184. }, {
  185. text: '最近三个月',
  186. onClick (picker) {
  187. const end = new Date()
  188. const start = new Date()
  189. start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
  190. picker.$emit('pick', [start, end])
  191. }
  192. }
  193. ]
  194. },
  195. elecQuantity: {
  196. series: [
  197. {
  198. type: 'pie',
  199. data: [],
  200. label: {
  201. show: true,
  202. position: 'center',
  203. rich: {
  204. totalSupply: {
  205. fontSize: 12,
  206. color: '#000',
  207. lineHeight: 20,
  208. align: 'center'
  209. }
  210. },
  211. formatter: this.getLabelContentForElecQuantity // 使用方法生成标签内容
  212. }
  213. }
  214. ]
  215. },
  216. elecCost: {
  217. series: [
  218. {
  219. type: 'pie',
  220. data: [],
  221. label: {
  222. show: true,
  223. position: 'center',
  224. rich: {
  225. totalCost: {
  226. fontSize: 12,
  227. color: '#000',
  228. lineHeight: 20,
  229. align: 'center'
  230. }
  231. },
  232. formatter: this.getLabelContentForElecCost // 使用方法生成标签内容
  233. }
  234. }
  235. ]
  236. },
  237. // elecQuantity: {
  238. // series: [
  239. // {
  240. // type: 'pie',
  241. // data: []
  242. // }
  243. // ]
  244. // },
  245. // elecCost: {
  246. // series: [
  247. // {
  248. // type: 'pie',
  249. // data: []
  250. // }
  251. // ]
  252. // },
  253. pvSupplyIndex: {
  254. unit: ' ',
  255. xAxis: {
  256. type: 'category',
  257. data: []
  258. },
  259. series: []
  260. },
  261. totalSupply: 0,
  262. totalCost: 0
  263. }
  264. },
  265. computed: {
  266. elecOptions () {
  267. const xData = this.pgSupplyTodayList.map(item => item.time)
  268. const quantity = this.pgSupplyTodayList.map(item => item.useElecQuantity)
  269. const cost = this.pgSupplyTodayList.map(item => item.useElecCost)
  270. const option = {
  271. tooltip: {
  272. trigger: 'axis',
  273. axisPointer: {
  274. type: 'cross',
  275. crossStyle: {
  276. color: '#999'
  277. }
  278. }
  279. },
  280. legend: {
  281. data: ['供电量', '供电电费']
  282. },
  283. xAxis: {
  284. type: 'category',
  285. data: xData.reverse(),
  286. axisPointer: {
  287. type: 'shadow'
  288. }
  289. },
  290. yAxis: [
  291. {
  292. name: 'kW·h(千瓦时)',
  293. type: 'value'
  294. },
  295. {
  296. name: '¥(元)',
  297. type: 'value'
  298. }
  299. ],
  300. series: [
  301. {
  302. name: '供电量',
  303. type: 'bar',
  304. stack: '总数',
  305. barWidth: 30,
  306. label: {
  307. show: false,
  308. position: 'insideRight'
  309. },
  310. data: quantity.reverse(),
  311. itemStyle: {
  312. normal: {
  313. color: '#6395FA',
  314. }
  315. }
  316. },
  317. {
  318. name: '供电电费',
  319. type: 'line',
  320. yAxisIndex: 1,
  321. data: cost.reverse(),
  322. smooth: false,
  323. itemStyle: {
  324. normal: {
  325. color: '#5BD9A5',
  326. }
  327. }
  328. }
  329. ]
  330. }
  331. return option
  332. },
  333. pvOptions () {
  334. const option = {
  335. tooltip: {
  336. trigger: 'axis',
  337. axisPointer: {
  338. type: 'cross',
  339. crossStyle: {
  340. color: '#999'
  341. }
  342. }
  343. },
  344. legend: {
  345. data: ['自用电量', '上网电量', '上网收益']
  346. },
  347. xAxis: {
  348. type: 'category',
  349. data: this.pvSupplyTodayList.map(item => item.time).reverse(),
  350. axisPointer: {
  351. type: 'shadow'
  352. }
  353. },
  354. yAxis: [
  355. {
  356. name: 'kW·h(千瓦时)',
  357. type: 'value'
  358. },
  359. {
  360. name: '¥(元)',
  361. type: 'value'
  362. }
  363. ],
  364. series: [
  365. {
  366. name: '自用电量',
  367. type: 'bar',
  368. stack: '总数',
  369. barWidth: 30,
  370. label: {
  371. show: false,
  372. position: 'insideRight'
  373. },
  374. data: this.pvSupplyTodayList.map(item => item.useElecQuantity).reverse(),
  375. itemStyle: {
  376. normal: {
  377. color: '#6395FA',
  378. label: {
  379. show: true, // 开启显示
  380. position: 'top', // 在上方显示
  381. textStyle: {
  382. // 数值样式
  383. color: '#000',
  384. fontSize: 14,
  385. fontWeight: 600
  386. }
  387. }
  388. }
  389. }
  390. },
  391. {
  392. name: '上网电量',
  393. type: 'bar',
  394. stack: '总数',
  395. barWidth: 30,
  396. label: {
  397. show: false,
  398. position: 'insideRight'
  399. },
  400. data: this.pvSupplyTodayList.map(item => item.upElecQuantity).reverse(),
  401. itemStyle: {
  402. normal: {
  403. color: '#8CDF6C',
  404. label: {
  405. show: true, // 开启显示
  406. position: 'top', // 在上方显示
  407. textStyle: {
  408. // 数值样式
  409. color: '#000',
  410. fontSize: 14,
  411. fontWeight: 600
  412. }
  413. }
  414. }
  415. }
  416. },
  417. {
  418. name: '上网收益',
  419. type: 'line',
  420. yAxisIndex: 1,
  421. data: this.pvSupplyTodayList.map(item => item.upElecEarn).reverse(),
  422. smooth: false
  423. }
  424. ]
  425. }
  426. return option
  427. }
  428. },
  429. watch: {
  430. // 根据名称筛选区域树
  431. areaName (val) {
  432. this.$refs.tree.filter(val)
  433. }
  434. },
  435. async created () {
  436. await this.getSummery()
  437. },
  438. methods: {
  439. getLabelContentForElecQuantity () {
  440. return `总供电{totalSupply|${this.totalSupply} kW·h}`;
  441. },
  442. getLabelContentForElecCost () {
  443. return `供电成本{totalCost|${this.totalCost} 元}`;
  444. },
  445. tabClick () {
  446. if (this.activeName !== 'summery') {
  447. this.dateRange = [dayjs().format(DateTool.DateFormat.YYYY_MM_DD_00_00_00), dayjs().format(DateTool.DateFormat.YYYY_MM_DD_23_59_59)]
  448. this.queryParams.areaCode = '-1'
  449. this.queryParams.pageNum = 1
  450. this.selectedLabel = '全部'
  451. this.getTodayList()
  452. this.getList()
  453. } else {
  454. this.getSummery()
  455. }
  456. },
  457. async getSummery () {
  458. await this.getHSummery()
  459. await this.getThisDaySummery()
  460. },
  461. async getThisDaySummery () {
  462. const {
  463. data,
  464. code
  465. } = await get('pg/supply/hour/summery/this/day')
  466. if (ApiCode.SUCCESS !== code || !data) {
  467. this.elecQuantity.series[0].data = []
  468. this.elecCost.series[0].data = []
  469. return
  470. }
  471. const {
  472. pv,
  473. supply
  474. } = data
  475. // 计算总供电量
  476. const totalSupply = (supply?.quantity || 0) + (pv?.useQuantity || 0);
  477. // 计算总电费
  478. const totalCost = (supply?.cost || 0) + (pv?.upEarn || 0);
  479. this.elecQuantity.series[0].data = [
  480. {
  481. value: supply?.quantity,
  482. name: '市电',
  483. itemStyle: {
  484. color: '#6395FA'
  485. }
  486. },
  487. {
  488. value: pv?.useQuantity,
  489. name: '光伏',
  490. itemStyle: {
  491. color: '#8CDF6C'
  492. }
  493. }
  494. ]
  495. this.elecCost.series[0].data = [
  496. {
  497. value: supply?.cost,
  498. name: '实际电费',
  499. itemStyle: {
  500. color: '#6395FA'
  501. }
  502. },
  503. {
  504. value: pv?.upEarn,
  505. name: '节省电费',
  506. itemStyle: {
  507. color: '#8CDF6C'
  508. }
  509. }
  510. ]
  511. // 更新饼图中间显示的总和
  512. this.totalSupply = totalSupply;
  513. this.totalCost = totalCost;
  514. },
  515. async getHSummery () {
  516. const {
  517. data,
  518. code
  519. } = await get('pg/supply/hour/summery/h')
  520. if (ApiCode.SUCCESS !== code || !data || data.length < 1) {
  521. return
  522. }
  523. const xaxis = DateTool.getTime(60)
  524. const {
  525. supply,
  526. pv
  527. } = data
  528. const series = [
  529. {
  530. name: '市电',
  531. type: 'bar',
  532. barWidth: '30%', // 调整柱状图宽度
  533. data: [],
  534. itemStyle: {
  535. color: '#6395FA'
  536. }
  537. },
  538. {
  539. name: '光伏',
  540. type: 'bar',
  541. barWidth: '30%', // 调整柱状图宽度
  542. data: [],
  543. itemStyle: {
  544. color: '#8CDF6C'
  545. }
  546. }
  547. ]
  548. xaxis.forEach((item, index) => {
  549. let timeIndex = index + 1
  550. if (!pv || !pv[timeIndex]) {
  551. series[1].data.push(0)
  552. } else {
  553. series[1].data.push(pv[timeIndex].useQuantity)
  554. }
  555. if (!supply || !supply[timeIndex]) {
  556. series[0].data.push(0)
  557. } else {
  558. series[0].data.push(supply[timeIndex].quantity)
  559. }
  560. })
  561. this.pvSupplyIndex.series = series
  562. this.pvSupplyIndex.xAxis.data = xaxis
  563. },
  564. getTodayList () {
  565. if (this.activeName === 'first') {
  566. areaWithFacsCategoryAsTree('W', this.facsSubCategory).then(response => {
  567. this.areaOptions = [{
  568. id: '-1',
  569. label: '全部',
  570. children: response.data
  571. }]
  572. })
  573. listPgSupplyH({
  574. startRecTime: dayjs().format(DateTool.DateFormat.YYYY_MM_DD_00_00_00),
  575. endRecTime: dayjs().format(DateTool.DateFormat.YYYY_MM_DD_23_59_59),
  576. areaCode: this.queryParams.areaCode,
  577. pageNum: 1,
  578. pageSize: 999
  579. }).then(response => {
  580. this.pgSupplyTodayList = response.rows
  581. })
  582. } else {
  583. areaWithFacsCategoryAsTree('E', this.facsSubCategory).then(response => {
  584. this.areaOptions = [{
  585. id: '-1',
  586. label: '全部',
  587. children: response.data
  588. }]
  589. })
  590. listPvSupplyH({
  591. startRecTime: dayjs().format(DateTool.DateFormat.YYYY_MM_DD_00_00_00),
  592. endRecTime: dayjs().format(DateTool.DateFormat.YYYY_MM_DD_23_59_59),
  593. areaCode: this.queryParams.areaCode,
  594. pageNum: 1,
  595. pageSize: 999
  596. }).then(response => {
  597. this.pvSupplyTodayList = response.rows
  598. })
  599. }
  600. },
  601. getList () {
  602. this.loading = true
  603. this.queryParams.startRecTime = ''
  604. this.queryParams.endRecTime = ''
  605. if (this.dateRange && this.dateRange.length) {
  606. const [startRecTime, endRecTime] = this.dateRange
  607. this.queryParams.startRecTime = startRecTime
  608. this.queryParams.endRecTime = endRecTime
  609. }
  610. if (this.activeName === 'first') {
  611. listPgSupplyH(
  612. {
  613. startRecTime: this.queryParams.startRecTime,
  614. endRecTime: this.queryParams.endRecTime,
  615. areaCode: this.queryParams.areaCode,
  616. pageNum: 1,
  617. pageSize: 999
  618. }
  619. ).then(response => {
  620. this.pgSupplyHList = response.rows
  621. this.total = response.total
  622. this.loading = false
  623. })
  624. } else {
  625. listPvSupplyH({
  626. startRecTime: this.queryParams.startRecTime,
  627. endRecTime: this.queryParams.endRecTime,
  628. areaCode: this.queryParams.areaCode,
  629. pageNum: 1,
  630. pageSize: 999
  631. }).then(response => {
  632. this.pvSupplyHList = response.rows
  633. this.total = response.total
  634. this.loading = false
  635. })
  636. }
  637. },
  638. // 筛选节点
  639. filterNode (value, data) {
  640. if (!value) return true
  641. return data.label.indexOf(value) !== -1
  642. },
  643. // 节点单击事件
  644. handleNodeClick (data) {
  645. this.queryParams.areaCode = data.id
  646. this.selectedLabel = data.label
  647. this.handleQuery()
  648. },
  649. /** 搜索按钮操作 */
  650. handleQuery () {
  651. this.queryParams.pageNum = 1
  652. this.getTodayList()
  653. this.getList()
  654. },
  655. getMeterTypeName (meterType) {
  656. const meterTypeMap = {
  657. '-1': '低谷电',
  658. '0': '平峰电',
  659. '1': '高峰点',
  660. '2': '尖峰电'
  661. }
  662. return meterTypeMap[meterType] || '平峰电'
  663. }
  664. }
  665. }
  666. </script>
  667. <style lang="scss" scoped>
  668. .app-container {
  669. ::v-deep .el-tabs__content {
  670. overflow: initial;
  671. }
  672. }
  673. </style>
  674. <style lang="scss" scoped src="./index.scss"></style>