powergrid.vue 21 KB

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