index.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. <script setup name="rateAnalysis">
  2. import excelCop from '@/components/excel.vue'
  3. import treeCop from '@/components/tree.vue'
  4. import barChartCop from './components/barChart.vue'
  5. import lineChartCop from './components/lineChart.vue'
  6. import CurrentScatterChartCop from './components/current-scatter-chart.vue'
  7. import SubmitBtn from '../../../components/SubmitBtn.vue'
  8. // import { ElMessage } from 'element-plus';
  9. import { onMounted, ref, onActivated, shallowRef, reactive, nextTick } from 'vue'
  10. import request from '@/api/axios.js'
  11. import tools from '@tools/htmlToPdf.js'
  12. // import flowerRes from '@/data/flower.json'
  13. // import lineChartRes from '@/data/lineNew.json'
  14. /**配置参数 */
  15. const treeHeight = ref(window.innerHeight - 150 + 'px') //tree高度
  16. const excelHeight = ref(window.innerHeight - 150 + 'px') //excel高度
  17. const tableHeight = ref(window.innerHeight - 150 + 'px')
  18. /**excel 开始 */
  19. const excelCheckIds = ref([])
  20. const excelList = ref([])
  21. //点击excel项时
  22. const funExcelChange = async (obj) => {
  23. excelCheckIds.value = [obj.id] //当为单选展示风机图表时
  24. chartExcelList.value = excelList.value.map(o=> {
  25. return {
  26. ...o,
  27. name: o.windturbine
  28. }
  29. }) // 选中excel当前项时, excel列表赋值给dialog 下拉框
  30. queryForm.checkIds = excelList.value.map(o => o.id)
  31. checkAll.value = true
  32. funSubmit()
  33. }
  34. const funExcelCheckChange = ({ checkArr, data }) => {
  35. excelCheckIds.value = checkArr
  36. }
  37. /**tree 开始 */
  38. const treeData = ref([])
  39. const actTreeNode = ref(null)
  40. const funRepeatMap = (arr) => {
  41. return arr.map(o => {
  42. if (o.children) {
  43. const findIndex = o.children.findIndex(p => !!p.type)
  44. if (findIndex !== -1) {
  45. o.childs = o.children
  46. o.children = []
  47. if(!actTreeNode.value){
  48. actTreeNode.value = o
  49. }
  50. }
  51. }
  52. return {
  53. ...o,
  54. children: o.children?.length ? funRepeatMap(o.children) : []
  55. }
  56. })
  57. }
  58. const funGetTree = async () => {
  59. const res = await request.get("/power/process/tree")
  60. actTreeNode.value = null
  61. excelList.value = []
  62. treeData.value = funRepeatMap(res.data)
  63. if(actTreeNode.value){
  64. funCurrentChange({current: actTreeNode.value, currentNode: null})
  65. funExcelChange({id: actTreeNode.value.childs[0].id})
  66. }
  67. }
  68. const funTreeCheckChange = ({ current, checkedNodes, checkedKeys, halfCheckedNodes, halfCheckedKeys }) => { //tree change -> excel change
  69. funCurrentChange({ current, currentNode: '' })
  70. const checkIds = []
  71. if (checkedNodes.length) {
  72. for (const node of checkedNodes) {
  73. if (node.childs && node.childs.length) {
  74. for (const child of node.childs) {
  75. checkIds.push(child.id)
  76. }
  77. }
  78. }
  79. }
  80. excelCheckIds.value = checkIds
  81. }
  82. const funCurrentChange = ({ current, currentNode }) => {
  83. if (current.childs) {
  84. excelList.value = current.childs.map(o => {
  85. return {
  86. id: o.id,
  87. interval: o.interval,
  88. path: o.path,
  89. prepareid: o.prepareid,
  90. station: o.station,
  91. time: o.time,
  92. type: o.type,
  93. windturbine: o.windturbine,
  94. name: o.path.substring(o.path.indexOf(o.station + '_') + (o.station + '_').length)
  95. }
  96. })
  97. } else {
  98. excelList.value = []
  99. }
  100. }
  101. /**chart */
  102. let chartId = 1
  103. /**submit */
  104. const funSubmit = async () => {
  105. const tempRes = await request.get('/temperature/rated/power', {
  106. params: {
  107. ids: excelCheckIds.value.join(','),
  108. }
  109. })
  110. // const lineRes = await request.get('/wind/deviation/ratio', {
  111. // params: {
  112. // ids: excelCheckIds.value.join(','),
  113. // mode: 0
  114. // }
  115. // })
  116. // const rosesRes = flowerRes
  117. // const lineRes = lineChartRes
  118. if (tempRes.code === 200) {
  119. if (tempRes.data?.length) {
  120. console.log(tempRes.data)
  121. for (const chart of tempRes.data) {
  122. barxAxis.data = Object.keys(chart.res1)
  123. barSeries[0].data = Object.values(chart.res1)
  124. chartId++
  125. lineSeries.value = [
  126. {
  127. type: 'effectScatter',
  128. showEffectOn: "emphasis",
  129. rippleEffect: {
  130. scale: 1
  131. },
  132. legendHoverLink: false,
  133. name: '',
  134. symbolSize: 5,
  135. data: chart.res2,
  136. yAxisIndex: 0,
  137. }
  138. ]
  139. chartId++
  140. }
  141. }
  142. }
  143. // if(lineRes.code === 200){
  144. // if(lineRes.data?.length){
  145. // lineDataSet.value[0].source = lineRes.data[0].scatter.map(o => {
  146. // return [o.x+'', o.y]
  147. // })
  148. // lineSeries.value = [{
  149. // name: "对风频次",
  150. // type: "line",
  151. // symbol: "line", //设定为实心点
  152. // symbolSize: 0, //设定实心点的大小
  153. // smooth: true, //这个是把线变成曲线
  154. // data: lineRes.data[0].count,
  155. // yAxisIndex: 1,
  156. // },
  157. // {
  158. // type: 'effectScatter',
  159. // showEffectOn: "emphasis",
  160. // rippleEffect: {
  161. // scale: 1
  162. // },
  163. // legendHoverLink: false,
  164. // name: '数据散点',
  165. // symbolSize: 5,
  166. // datasetIndex: 0,
  167. // encode: {
  168. // x: 'x',
  169. // y: 'y'
  170. // },
  171. // yAxisIndex: 0,
  172. // }]
  173. // }
  174. // }
  175. }
  176. /**lineChart */
  177. const linexAxis = reactive({
  178. type: 'value',
  179. name: '°C',
  180. splitLine: {
  181. show: false
  182. },
  183. axisTick: {
  184. show: true
  185. },
  186. axisLine: {
  187. onZero: false
  188. }
  189. })
  190. const lineyAxis = reactive([
  191. {
  192. type: 'value',
  193. name: 'kW',
  194. splitLine: {
  195. show: false
  196. },
  197. axisTick: {
  198. show: true
  199. },
  200. axisLine: {
  201. onZero: false
  202. }
  203. }
  204. ])
  205. const lineSeries = ref([])
  206. const lineDataSet = reactive([
  207. {
  208. source: []
  209. }
  210. ])
  211. // 圈选散点触发函数
  212. const funChartSelect = async (batch) => {
  213. const wDataArr = []
  214. const yDataArr = []
  215. let scatterls = []
  216. let dataSetObj = []
  217. wtData.value = []
  218. if (batch?.length && actCopList.value[0]?.dataset) {
  219. scatterls = batch[0].selected[1].dataIndex
  220. if (scatterls?.length) {
  221. dataSetObj = JSON.parse(actCopList.value[0].dataset)
  222. if (scatterls?.length) {
  223. for (const scatterIndex of scatterls) {
  224. wDataArr.push(dataSetObj[0].source[scatterIndex].k)
  225. }
  226. }
  227. const wtRes = await request.get('/power/fitting/filter', { params: { yk: yDataArr.join(','), wk: wDataArr.join(',') } })
  228. if (wtRes.code === 200) {
  229. let id = 1
  230. const tempArr = [] //用于以风机id 聚合dataArr
  231. if (wtRes.data?.length) {
  232. for (const data of wtRes.data) {
  233. if (tempArr.length) {
  234. const findIndex = tempArr.findIndex(o => o.wtId === data.wtId)
  235. if (findIndex !== -1) {
  236. if (!tempArr[findIndex].children) {
  237. tempArr[findIndex].children = []
  238. }
  239. tempArr[findIndex].children.push({ ...data, id: id, filter: data.filter === 0 ? '是' : '否' })
  240. id++
  241. } else {
  242. tempArr.push({ ...data, id: id, filter: data.filter === 0 ? '是' : '否' })
  243. id++
  244. }
  245. } else {
  246. tempArr.push({ ...data, id: id, filter: data.filter === 0 ? '是' : '否' })
  247. id++
  248. }
  249. }
  250. wtDialog.value = true
  251. nextTick(() => {
  252. wtTab.value = 'table'
  253. wtData.value = tempArr
  254. })
  255. }
  256. }
  257. }
  258. }
  259. }
  260. /**barChart */
  261. const barxAxis = reactive({
  262. type: 'category',
  263. name: '℃',
  264. data: [],
  265. splitLine: {
  266. show: false
  267. },
  268. axisTick: {
  269. show: true
  270. },
  271. axisLine: {
  272. onZero: false
  273. }
  274. })
  275. const baryAxis = reactive({
  276. type: 'value',
  277. name: 'kW',
  278. splitLine: {
  279. show: false
  280. },
  281. axisTick: {
  282. show: true
  283. },
  284. axisLine: {
  285. onZero: false
  286. }
  287. })
  288. const barSeries = reactive([{
  289. name: "",
  290. type: "bar",
  291. data: [],
  292. }])
  293. /**dialog 数据 */
  294. const wtDialog = ref(false)
  295. const wtData = ref([])
  296. const wtTab = ref('table')
  297. /**dialog */
  298. const dialog = ref(false)
  299. const actChartName = ref('')
  300. const actDiaTitle = ref('')
  301. const diaPanelRef = ref()
  302. const exportLoading = ref(false)
  303. const actCopList = ref([
  304. // {
  305. // xAxis: [],
  306. // subtext: '',
  307. // title: '',
  308. // isRadar: false,
  309. // series: [],
  310. // yAxis: [],
  311. // dataset: []
  312. // }
  313. ])
  314. // 作为actCopList的备份 在actCopList赋值多个时 同时赋值, 在dialog弹出时清空. 作用: 在actCopList变化时, 重新赋值原始数据
  315. const actCopListBak = ref([])
  316. const checkAll = ref(true)
  317. const queryForm = reactive({
  318. checkIds: []
  319. })
  320. const funCheckAll = () => {
  321. checkAll.value = !checkAll.value
  322. if(checkAll.value){
  323. queryForm.checkIds = chartExcelList.value.map(o => o.id)
  324. }else{
  325. queryForm.checkIds = []
  326. }
  327. }
  328. const chartExcelList = ref([]) //dialog 下拉项
  329. const funActCop = (obj, type) => {
  330. switch(type){
  331. case 'barChartCop':
  332. actChartName.value = 'barChartCop'
  333. obj.actCop = shallowRef(barChartCop)
  334. actDiaTitle.value = '平均功率-额定功率'
  335. break
  336. case 'lineChartCop':
  337. actChartName.value = 'lineChartCop'
  338. obj.actCop = shallowRef(lineChartCop)
  339. actDiaTitle.value = '额定功率温度分析'
  340. break
  341. // case 'CurrentScatterChartCop':
  342. // actChartName.value = 'CurrentScatterChartCop'
  343. // obj.actCop = shallowRef(CurrentScatterChartCop)
  344. // actDiaTitle.value = '静态偏航对风分析图'
  345. // break
  346. }
  347. obj.isBrush = false
  348. obj.id = chartId
  349. chartId ++
  350. dialog.value = true
  351. actCopListBak.value = []
  352. nextTick(() => {
  353. actCopList.value = [obj]
  354. })
  355. }
  356. const funDiaSubmit = async () => {
  357. let url = ''
  358. switch(actChartName.value){
  359. case 'barChartCop':
  360. url = '/temperature/rated/power'
  361. break
  362. case 'lineChartCop':
  363. url = '/temperature/rated/power'
  364. break
  365. // case 'CurrentScatterChartCop':
  366. // url = '' //暂无接口
  367. // break
  368. }
  369. if(url){
  370. const res = await request.get(url, {
  371. params: {
  372. ids: queryForm.checkIds.join(','),
  373. mode: 0
  374. }
  375. })
  376. console.log(res)
  377. if(res.code===200){
  378. actCopList.value = []
  379. actCopListBak.value = [] //清空备份
  380. if(res.data?.length){
  381. for(const chart of res.data){
  382. if(actChartName.value==='barChartCop'){
  383. actCopList.value.push({
  384. id: chartId,
  385. isBrush: false,
  386. actCop: shallowRef(barChartCop),
  387. title: chart.wt,
  388. subtext: '平均功率-额定功率',
  389. xAxis: {
  390. ...barxAxis,
  391. data: Object.keys(chart.res1)
  392. },
  393. yAxis: baryAxis,
  394. series: [{
  395. name: "",
  396. type: "bar",
  397. data: Object.values(chart.res1),
  398. }]
  399. })
  400. chartId++
  401. }
  402. if(actChartName.value === 'lineChartCop'){
  403. actCopList.value.push({
  404. id: chartId,
  405. isBrush: false,
  406. actCop: shallowRef(lineChartCop),
  407. title: chart.wt,
  408. subtext: '额定功率温度分析',
  409. xAxis: linexAxis,
  410. yAxis: lineyAxis,
  411. dataset: lineDataSet,
  412. series: [
  413. {
  414. type: 'effectScatter',
  415. showEffectOn: "emphasis",
  416. rippleEffect: {
  417. scale: 1
  418. },
  419. legendHoverLink: false,
  420. name: '',
  421. symbolSize: 5,
  422. data: chart.res2,
  423. yAxisIndex: 0,
  424. }
  425. ]
  426. })
  427. chartId++
  428. }
  429. }
  430. actCopListBak.value = actCopList.value
  431. }
  432. }
  433. }
  434. }
  435. const funDiaExport = () => {
  436. exportLoading.value = true
  437. tools.scrollToPDF(diaPanelRef.value, actDiaTitle.value, () => {
  438. exportLoading.value = false
  439. })
  440. }
  441. const funDbClick = (obj) => {
  442. if(actCopListBak.value.length > 1){ //判断大于1时, 才有双击放大功能
  443. if(actCopList.value.length === 1){
  444. actCopList.value = actCopListBak.value
  445. }else{
  446. actCopList.value = [obj]
  447. }
  448. }
  449. }
  450. /**created */
  451. // funGetTree()
  452. /**activated */
  453. onMounted(() => {
  454. //test
  455. // funSubmit()
  456. //
  457. tableHeight.value = window.innerHeight - 150 + 'px'
  458. excelHeight.value =(window.innerHeight - 150) + 'px'
  459. treeHeight.value = (window.innerHeight - 150) + 'px'
  460. window.addEventListener('resize', () => {
  461. tableHeight.value = window.innerHeight - 150 + 'px'
  462. excelHeight.value = (window.innerHeight - 150) + 'px'
  463. treeHeight.value = (window.innerHeight - 150) + 'px'
  464. })
  465. })
  466. onActivated(() => {
  467. funGetTree()
  468. })
  469. </script>
  470. <template>
  471. <div class="bg-white py-[10px] px-[10px] relative s-dialog-body">
  472. <!-- <el-dialog v-model="wtDialog" draggable title="风机功率点位">
  473. <el-tabs v-model="wtTab">
  474. <el-tab-pane label="数据" name="table">
  475. <el-table :data="wtData" row-key="id" :max-height="550">
  476. <el-table-column property="wtId" align="center" label="风机" />
  477. <el-table-column property="time" sortable :width="160" align="center" label="时间" />
  478. <el-table-column property="speed" sortable align="center" label="风速(m/s)" />
  479. <el-table-column property="power" sortable align="center" label="功率(kw)" />
  480. <el-table-column property="rr" sortable align="center" label="转速" />
  481. <el-table-column property="filter" sortable align="center" label="是否有用点" />
  482. </el-table>
  483. </el-tab-pane>
  484. <el-tab-pane label="故障" name="problem" disabled>
  485. </el-tab-pane>
  486. <el-tab-pane label="预警" name="warning" disabled>
  487. </el-tab-pane>
  488. </el-tabs>
  489. </el-dialog> -->
  490. <el-dialog draggable width="80%" v-model="dialog" :title="actDiaTitle">
  491. <el-form class="whitespace-nowrap" :inline="true" :model="queryForm">
  492. <el-form-item label="" class="!mb-0">
  493. <el-select v-model="queryForm.checkIds" clearable @clear="checkAll = false" collapse-tags multiple>
  494. <el-option label="全选" :class="{'selected': checkAll}" @click="funCheckAll"></el-option>
  495. <el-option v-for="item in chartExcelList" :key="item.id" :value="item.id" :label="item.name"></el-option>
  496. </el-select>
  497. </el-form-item>
  498. <el-form-item class="!mb-0">
  499. <submit-btn desc="查询" @click="funDiaSubmit"></submit-btn>
  500. <submit-btn desc="导出" @click="funDiaExport"></submit-btn>
  501. </el-form-item>
  502. </el-form>
  503. <div v-loading="exportLoading">
  504. <div ref="diaPanelRef" class="flex flex-wrap justify-center items-center h-[650px] overflow-y-auto overflow-x-hidden">
  505. <component :is="item.actCop" :width="actCopList.length > 1 ? '50%' : '100%'" height="100%" v-for="item in actCopList"
  506. :key="item.id" :xAxis="item.xAxis" :subtext="item.subtext" :title="item.title"
  507. :series="item.series" :isDiaAlone="(actCopList.length === 1)" @dblclick="funDbClick(item)" :yAxis="item.yAxis" :dataset="item.dataset" :brush="item.isBrush" @getSelected="funChartSelect"></component>
  508. </div>
  509. </div>
  510. </el-dialog>
  511. <div class="relative shadow rounded-[6px] shadow-blue-500 px-[10px] pt-[10px] pb-[10px]">
  512. <div class="text-[14px] absolute top-[-7px] text-[#B3B3B3] left-[20px]">数据展示</div>
  513. <el-row :gutter="10">
  514. <el-col :span="5">
  515. <tree-cop :data="treeData" @checkChange="funTreeCheckChange" :show-checkbox="false" :height="treeHeight"
  516. @currentChange="funCurrentChange" @refresh="funGetTree">
  517. </tree-cop>
  518. </el-col>
  519. <el-col :span="3">
  520. <excel-cop :checkIds="excelCheckIds" :showCheckbox="false" :data="excelList" :height="excelHeight"
  521. @excelChange="funExcelChange" @checkChange="funExcelCheckChange"></excel-cop>
  522. </el-col>
  523. <el-col :span="16">
  524. <div :style="{ height: tableHeight }"
  525. class="flex flex-wrap justify-center items-center overflow-x-hidden overflow-y-auto ">
  526. <div class="mb-[10px] w-[100%] h-[49%] flex flex-col items-end shadow rounded-[6px] shadow-blue-500">
  527. <el-icon class="mr-[10px] mt-[10px] cursor-pointer" size="18"
  528. @click="funActCop({xAxis:barxAxis, yAxis:baryAxis, series: barSeries}, 'barChartCop')">
  529. <ZoomIn />
  530. </el-icon>
  531. <bar-chart-cop width="100%" height="100%" subtext="平均功率-额定功率" :xAxis="barxAxis" :yAxis="baryAxis" :series="barSeries"></bar-chart-cop>
  532. </div>
  533. <div class="w-[100%] h-[49%] flex flex-col items-end shadow rounded-[6px] shadow-blue-500">
  534. <el-icon class="mr-[10px] mt-[10px] cursor-pointer" size="18"
  535. @click="funActCop({xAxis:linexAxis, yAxis:lineyAxis, series: lineSeries}, 'lineChartCop')">
  536. <ZoomIn />
  537. </el-icon>
  538. <line-chart-cop class="" height="100%" width="100%" :xAxis="linexAxis" :yAxis="lineyAxis"
  539. :series="lineSeries" subtext="额定功率温度分析" :dataset="lineDataSet"></line-chart-cop>
  540. </div>
  541. <!-- <div class="mr-[10px] w-[49%] h-[49%] flex flex-col items-end shadow rounded-[6px] shadow-blue-500" v-if="!!lineSeries.length"> -->
  542. <!-- <el-icon class="mr-[10px] mt-[10px] cursor-pointer" size="18"
  543. @click="funActCop({ xAxis: linexAxis, yAxis: lineyAxis, series: lineSeries, dataset: lineDataSet }, 'lineChartCop')">
  544. <ZoomIn />
  545. </el-icon> -->
  546. <!-- <current-scatter-chart-cop class="" height="100%" width="100%" :xAxis="linexAxis" :yAxis="lineyAxis"
  547. :series="lineSeries" subtext="风速功率-温度分析" :dataset="lineDataSet"></current-scatter-chart-cop> -->
  548. <!-- </div> -->
  549. <!-- <div class="w-[49%] h-[49%] flex flex-col items-end shadow rounded-[6px] shadow-blue-500" v-if="!!lineSeries.length"> -->
  550. <!-- <el-icon class="mr-[10px] mt-[10px] cursor-pointer" size="18"
  551. @click="funActCop({ xAxis: xAxisData, yAxis: { splitLine: { show: false } }, series: seriesData, dataSet: dataSet }, 'CurrentScatterChartCop')">
  552. <ZoomIn />
  553. </el-icon> -->
  554. <!-- <current-scatter-chart-cop class="" height="100%" width="100%" :xAxis="linexAxis" :yAxis="lineyAxis"
  555. :series="lineSeries" subtext="对风偏差分析图" :dataset="lineDataSet"></current-scatter-chart-cop> -->
  556. <!-- </div> -->
  557. </div>
  558. </el-col>
  559. </el-row>
  560. </div>
  561. </div>
  562. </template>
  563. <style scoped>
  564. .s-dialog-body /deep/ .el-dialog__body{
  565. padding: 0px 20px;
  566. }
  567. </style>