index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. <script setup name="prepare">
  2. import searchCop from './components/search.vue'
  3. import excelCop from '@/components/excel.vue'
  4. import treeCop from '@/components/tree.vue'
  5. import tableCop from './components/table.vue'
  6. import SubmitBtn from '@/components/SubmitBtn.vue'
  7. import { ref, nextTick, onActivated, onMounted, reactive } from 'vue'
  8. import request from '@/api/axios.js'
  9. import { ElMessage } from 'element-plus'
  10. import util from "@tools/util";
  11. import CurrentScatterChart from './components/current-scatter-chart.vue'
  12. // import dotRes from '@/data/dot.json'
  13. // import tableRes from '@/data/table.json'
  14. // import areaDataRes from '@/data/areaData.json'
  15. /**配置参数 */
  16. const treeHeight = ref((window.innerHeight - 210) / 2 + 'px') //tree高度
  17. const excelHeight = ref((window.innerHeight - 210) / 2 + 'px') //excel高度
  18. const tableHeight = ref(window.innerHeight - 254 + 'px')
  19. /**excel 开始 */
  20. const excelCheckboxShow = ref(false)
  21. const excelType = ref('')
  22. const excelCheckIds = ref([])
  23. const excelList = ref([])
  24. const funExcelChange = async (obj) => { //点击excel项时
  25. activeTab.value = '1'
  26. isChartArea.value = false
  27. tableShowId.value = obj.id
  28. tableName.value = obj.name
  29. excelType.value = obj.type // 接收excel的type 用于控制右侧tab展示
  30. let res = null
  31. let chartRes = {
  32. scatterhs: [[]],
  33. scatterls: [[]],
  34. sjgl: [[]],
  35. llgl: [[]],
  36. cpz: [[]]
  37. }
  38. let poiRes = null
  39. let chartResponse = null
  40. tableLoading.value = true
  41. if (obj.type === 'process') {
  42. res = await request.get('/power/process/show', { params: { id: obj.id } })
  43. } else if (obj.type === 'fitting') {
  44. activeTab.value = '2'
  45. res = await request.get('/power/fitting/show', { params: { id: obj.id } })
  46. // res = tableRes
  47. // chartResponse = dotRes
  48. chartResponse = await request.get('/power/fitting/curve', { params: { id: obj.id, p: 1 } })
  49. poiRes = await request.get('/power/fitting/curve/ratio', {params: {id: obj.id}})
  50. // poiRes = areaDataRes
  51. }
  52. tableColumn.value = res.data.title.map(o => {
  53. return {
  54. prop: o.key,
  55. width: o.des==='时间'? 100: 80,
  56. label: o.des,
  57. }
  58. })
  59. tableData.value = res.data.data
  60. tableLoading.value = false
  61. // markDot
  62. if(poiRes && poiRes.code=== 200){
  63. markDot.pcl5 = poiRes.data.pcl5
  64. markDot.pcl10 = poiRes.data.pcl10
  65. markDot.pcl12 = poiRes.data.pcl12
  66. markDot.pcl25 = poiRes.data.pcl25
  67. }
  68. if (chartResponse && chartResponse.code === 200) {
  69. chartRes = chartResponse.data
  70. avgObj.title = chartRes.obj.path.substring(chartRes.obj.path.indexOf(chartRes.obj.station + '_') + (chartRes.obj.station + '_').length).split('_')[0];
  71. avgObj.cpavg = Number(chartRes.obj.cpavg).toFixed(2)
  72. avgObj.frequency = Number(chartRes.obj.frequency).toFixed(2)
  73. avgObj.pcratio = Number(chartRes.obj.pcratio).toFixed(2)
  74. dataSet.value = JSON.stringify([
  75. {
  76. source: chartRes.wyd
  77. // source: chartRes.scatterls
  78. },
  79. {
  80. source: chartRes.yyd
  81. // source: chartRes.scatterhs
  82. }
  83. ])
  84. const color = ["#1C99FF", "#FF8700", "#3D54BE", "#fa8c16", "#1DA0D7", "#DD5044"]
  85. seriesData.value = [
  86. {
  87. name: "拟合功率",
  88. type: "line",
  89. symbol: "line", //设定为实心点
  90. symbolSize: 0, //设定实心点的大小
  91. smooth: true, //这个是把线变成曲线
  92. data: chartRes.sjgl,
  93. xAxisIndex: 0,
  94. },
  95. {
  96. name: "保证功率",
  97. type: "line",
  98. symbol: "line", //设定为实心点
  99. symbolSize: 0, //设定实心点的大小
  100. smooth: true, //这个是把线变成曲线
  101. data: chartRes.llgl,
  102. xAxisIndex: 0,
  103. },
  104. {
  105. type: 'effectScatter',
  106. showEffectOn: "emphasis",
  107. rippleEffect: {
  108. scale: 1
  109. },
  110. name: '无用点',
  111. symbolSize: (data) => {
  112. return data.s ? data.s > 10 ? 10 : data.s : 4
  113. },
  114. datasetIndex: 0,
  115. encode: {
  116. x: 'x',
  117. y: 'y'
  118. },
  119. xAxisIndex: 0,
  120. yAxisIndex: 0,
  121. },
  122. {
  123. type: 'effectScatter',
  124. showEffectOn: "emphasis",
  125. rippleEffect: {
  126. scale: 1
  127. },
  128. name: '有用点',
  129. symbolSize: (data) => {
  130. return data.s ? data.s > 10 ? 10 : data.s : 4
  131. },
  132. datasetIndex: 1,
  133. encode: {
  134. x: 'x',
  135. y: 'y'
  136. },
  137. xAxisIndex: 0,
  138. yAxisIndex: 0,
  139. },
  140. {
  141. name: "Cp值",
  142. type: "line",
  143. symbol: "line", //设定为实心点
  144. symbolSize: 0, //设定实心点的大小
  145. smooth: true, //这个是把线变成曲线
  146. data: chartRes.cpz,
  147. xAxisIndex: 0,
  148. yAxisIndex: 1,
  149. },
  150. ]
  151. }
  152. }
  153. const funExcelCheckChange = ({ checkArr, data }) => { //bug
  154. excelCheckIds.value = checkArr
  155. }
  156. /**excel fitData */
  157. const excelFitList = ref([])
  158. /**prepare tree 开始 */
  159. const treeData = ref([])
  160. const actTreeNode = ref(null) //当前激活的treeNode
  161. const funRepeatMap = (arr, type='prepare') => {
  162. return arr.map(o => {
  163. if (o.children) {
  164. const findIndex = o.children.findIndex(p => !!p.type)
  165. if (findIndex !== -1) {
  166. o.childs = o.children
  167. o.children = []
  168. if(!actTreeNode.value && type === 'process'){ //判断当且仅有process获取tree时 赋值
  169. actTreeNode.value = o
  170. }
  171. }
  172. }
  173. return {
  174. ...o,
  175. children: o.children ? funRepeatMap(o.children, type) : []
  176. }
  177. })
  178. }
  179. const funGetTree = async () => {
  180. const res = await request.get("/power/process/tree")
  181. treeData.value = funRepeatMap(res.data)
  182. excelList.value = []
  183. }
  184. const funCurrentChange = ({ current, currentNode }) => {
  185. excelCheckboxShow.value = true
  186. if (current.childs) {
  187. excelList.value = current.childs.map(o => {
  188. return {
  189. id: o.id,
  190. interval: o.interval,
  191. path: o.path,
  192. prepareid: o.prepareid,
  193. station: o.station,
  194. time: o.time,
  195. type: o.type,
  196. windturbine: o.windturbine,
  197. name: o.path.substring(o.path.indexOf(o.station + '_') + (o.station + '_').length)
  198. }
  199. })
  200. } else {
  201. excelList.value = []
  202. }
  203. }
  204. const funTreeCheckChange = ({ current, checkedNodes, checkedKeys, halfCheckedNodes, halfCheckedKeys }) => { //tree change -> excel change
  205. funCurrentChange({ current, currentNode: '' })
  206. const checkIds = []
  207. if (checkedNodes.length) {
  208. for (const node of checkedNodes) {
  209. if (node.childs && node.childs.length) {
  210. for (const child of node.childs) {
  211. checkIds.push(child.id)
  212. }
  213. }
  214. }
  215. }
  216. excelCheckIds.value = checkIds
  217. }
  218. /**process tree 开始 */
  219. const processTreeData = ref([])
  220. const funGetProcessTree = async (flag = true) => { //flag控制是否获取tree的第一项 true为可获取
  221. actTreeNode.value = null
  222. const res = await request.get("/power/fitting/tree")
  223. excelFitList.value = []
  224. processTreeData.value = funRepeatMap(res.data, flag? 'process' : 'prepare') //flag控制对actTreeNode赋值
  225. if(actTreeNode.value){
  226. funProcessCurrentChange({current: actTreeNode.value, currentNode: null})
  227. const child = actTreeNode.value.childs[0]
  228. const obj = {
  229. id: child.id,
  230. interval: child.interval,
  231. path: child.path,
  232. prepareid: child.prepareid,
  233. station: child.station,
  234. time: child.time,
  235. type: child.type,
  236. windturbine: child.windturbine,
  237. name: child.path.substring(child.path.indexOf(child.station + '_') + (child.station + '_').length)
  238. }
  239. funExcelChange(obj)
  240. }
  241. }
  242. const funProcessCurrentChange = ({ current, currentNode }) => {
  243. if (current.childs) {
  244. excelFitList.value = current.childs.map(o => {
  245. return {
  246. id: o.id,
  247. interval: o.interval,
  248. path: o.path,
  249. prepareid: o.prepareid,
  250. station: o.station,
  251. time: o.time,
  252. type: o.type,
  253. windturbine: o.windturbine,
  254. name: o.path.substring(o.path.indexOf(o.station + '_') + (o.station + '_').length)
  255. }
  256. })
  257. } else {
  258. excelFitList.value = []
  259. }
  260. }
  261. /**table 开始 */
  262. const tableShowId = ref('')
  263. const tableColumn = ref([])
  264. const tableLoading = ref(false)
  265. const tableName = ref('')
  266. const tableData = ref([])
  267. /**table 结束 */
  268. /**search 开始 */
  269. const funSubmit = async (query) => {
  270. if (!excelCheckIds.value.length) {
  271. ElMessage.error('请勾选要预处理的项')
  272. return false
  273. }
  274. const params = {
  275. ...query,
  276. ids: excelCheckIds.value.join(',')
  277. }
  278. const res = await request.get('/power/fitting/data', { params: params })
  279. if (res.code === 200) {
  280. ElMessage.success(res.msg)
  281. funGetProcessTree(false) //阻止获取tree第一项数据及图表
  282. const excelInfo = res.data
  283. /**拟合完成后 显示右侧图表及数据 */
  284. funExcelChange({
  285. id: excelInfo.id,
  286. name: excelInfo.path.substring(excelInfo.path.indexOf(excelInfo.station + '_') + (excelInfo.station + '_').length),
  287. type: 'fitting'
  288. })
  289. }
  290. }
  291. /**chart Data */
  292. const avgObj = reactive({ //平均cpz等
  293. title: '',
  294. cpavg: '',
  295. frequency: '',
  296. pcratio: ''
  297. })
  298. const markDot = reactive({ //3-5 point点等
  299. pcl5: null,
  300. pcl10: null,
  301. pcl12: null,
  302. pcl25: null
  303. })
  304. const xAxisData = ref([])
  305. const chartRef = ref() //chart 的ref
  306. const seriesData = ref([])
  307. const isChartArea = ref(false) // 用来控制图表是否区域划分
  308. const dataSet = ref('')
  309. const funChartSelect = async (batch) => {
  310. const wDataArr = []
  311. const yDataArr = []
  312. let scatterls = []
  313. let scatterhs = []
  314. let dataSetObj = []
  315. wtData.value = []
  316. if (batch?.length && dataSet.value) {
  317. scatterls = batch[0].selected[2].dataIndex
  318. scatterhs = batch[0].selected[3].dataIndex
  319. if (scatterls?.length || scatterhs?.length) {
  320. dataSetObj = JSON.parse(dataSet.value)
  321. if (scatterls?.length) {
  322. for (const scatterIndex of scatterls) {
  323. wDataArr.push(dataSetObj[0].source[scatterIndex].k)
  324. }
  325. }
  326. if (scatterhs?.length) {
  327. for (const scatterIndex of scatterhs) {
  328. yDataArr.push(dataSetObj[1].source[scatterIndex].k)
  329. }
  330. }
  331. const wtRes = await request.get('/power/fitting/filter', { params: { yk: yDataArr.join(','), wk: wDataArr.join(',') } })
  332. if (wtRes.code === 200) {
  333. let id = 1
  334. const tempArr = [] //用于以风机id 聚合dataArr
  335. if (wtRes.data?.length) {
  336. for (const data of wtRes.data) {
  337. if (tempArr.length) {
  338. const findIndex = tempArr.findIndex(o => o.wtId === data.wtId)
  339. if (findIndex !== -1) {
  340. if (!tempArr[findIndex].children) {
  341. tempArr[findIndex].children = []
  342. }
  343. tempArr[findIndex].children.push({ ...data, id: id, filter: data.filter === 0 ? '是' : '否' })
  344. id++
  345. } else {
  346. tempArr.push({ ...data, id: id, filter: data.filter === 0 ? '是' : '否' })
  347. id++
  348. }
  349. } else {
  350. tempArr.push({ ...data, id: id, filter: data.filter === 0 ? '是' : '否' })
  351. id++
  352. }
  353. }
  354. wtDialog.value = true
  355. nextTick(() => {
  356. wtTab.value = 'table'
  357. wtData.value = tempArr
  358. })
  359. }
  360. }
  361. }
  362. }
  363. }
  364. const funChartArea = () => {
  365. if (seriesData.value?.length) {
  366. if (!isChartArea.value) {
  367. // 请求一下
  368. seriesData.value[0] = {
  369. ...seriesData.value[0],
  370. markLine: {
  371. symbol: 'none',
  372. label: {
  373. show: false
  374. },
  375. lineStyle: {
  376. color: 'rgba(96,174,255, 1)'
  377. },
  378. data: [
  379. {
  380. xAxis: 3,
  381. valueIndex: 0,
  382. },
  383. {
  384. xAxis: 5,
  385. valueIndex: 0
  386. },
  387. {
  388. xAxis: 10,
  389. valueIndex: 0
  390. },
  391. {
  392. xAxis: 12,
  393. valueIndex: 0
  394. },
  395. {
  396. xAxis: 25,
  397. valueIndex: 0
  398. },
  399. ]
  400. },
  401. markArea: {
  402. label: {
  403. fontSize: util.vh(12),
  404. },
  405. itemStyle: {
  406. color: 'rgba(236,245,255, 0)'
  407. },
  408. emphasis: {
  409. itemStyle: {
  410. color: 'rgba(96,174,255, 0.5)'
  411. }
  412. },
  413. data: [
  414. [
  415. {
  416. name: `3~5m 偏差率: ${markDot.pcl5}%`,
  417. xAxis: 3,
  418. },
  419. {
  420. xAxis: 5,
  421. }
  422. ],
  423. [
  424. {
  425. name: `5~10m 偏差率: ${markDot.pcl10}%`,
  426. xAxis: 5,
  427. },
  428. {
  429. xAxis: 10,
  430. }
  431. ],
  432. [
  433. {
  434. name: `10~12m 偏差率: ${markDot.pcl12}%`,
  435. xAxis: 10,
  436. },
  437. {
  438. xAxis: 12,
  439. }
  440. ],
  441. [
  442. {
  443. name: `12~25m 偏差率: ${markDot.pcl25}%`,
  444. xAxis: 12,
  445. },
  446. {
  447. xAxis: 25,
  448. }
  449. ],
  450. ]
  451. },
  452. }
  453. isChartArea.value = true
  454. } else {
  455. seriesData.value[0] = {
  456. ...seriesData.value[0],
  457. markLine: null,
  458. markArea: null,
  459. }
  460. isChartArea.value = false
  461. }
  462. }
  463. }
  464. /**dialog 数据 */
  465. const wtDialog = ref(false)
  466. const wtData = ref([])
  467. const wtTab = ref('table')
  468. /**tab */
  469. const activeTab = ref('1')
  470. /**created */
  471. // funGetTree()
  472. // funGetProcessTree()
  473. /**mounted */
  474. onMounted(() => {
  475. tableHeight.value = window.innerHeight - 254 + 'px'
  476. excelHeight.value =(window.innerHeight - 210) / 2 + 'px'
  477. treeHeight.value = (window.innerHeight - 210) / 2 + 'px'
  478. window.addEventListener('resize', () => {
  479. tableHeight.value = window.innerHeight - 254 + 'px'
  480. excelHeight.value = (window.innerHeight - 210) / 2 + 'px'
  481. treeHeight.value = (window.innerHeight - 210) / 2 + 'px'
  482. })
  483. /**test */
  484. // funExcelChange({
  485. // id: 1,
  486. // name: 'excel',
  487. // type: 'fitting',
  488. // })
  489. })
  490. /**activated */
  491. onActivated(() => {
  492. funGetTree()
  493. funGetProcessTree()
  494. })
  495. </script>
  496. <template>
  497. <div class="bg-white py-[10px] px-[10px]">
  498. <search-cop class="mb-[20px] shadow rounded-[6px] shadow-blue-500" @submit="funSubmit">
  499. </search-cop>
  500. <el-dialog v-model="wtDialog" draggable title="风机功率点位">
  501. <el-tabs v-model="wtTab">
  502. <el-tab-pane label="数据" name="table">
  503. <el-table :data="wtData" row-key="id" :max-height="550">
  504. <el-table-column property="wtId" align="center" label="风机" />
  505. <el-table-column property="time" sortable :width="160" align="center" label="时间" />
  506. <el-table-column property="speed" sortable align="center" label="风速(m/s)" />
  507. <el-table-column property="power" sortable align="center" label="功率(kw)" />
  508. <el-table-column property="rr" sortable align="center" label="转速" />
  509. <el-table-column property="filter" sortable align="center" label="是否有用点" />
  510. </el-table>
  511. </el-tab-pane>
  512. <el-tab-pane label="故障" name="problem" disabled>
  513. </el-tab-pane>
  514. <el-tab-pane label="预警" name="warning" disabled>
  515. </el-tab-pane>
  516. </el-tabs>
  517. </el-dialog>
  518. <div class="relative shadow rounded-[6px] shadow-blue-500 px-[10px] pt-[20px] pb-[10px]">
  519. <div class="text-[14px] absolute top-[-7px] text-[#B3B3B3] left-[20px]">数据展示</div>
  520. <el-row :gutter="10">
  521. <el-col :span="5">
  522. <tree-cop :data="treeData" @checkChange="funTreeCheckChange" :show-checkbox="true" :height="treeHeight"
  523. @currentChange="funCurrentChange" @refresh="funGetTree"></tree-cop>
  524. <tree-cop class="mt-[10px]" :data="processTreeData" :height="treeHeight"
  525. @currentChange="funProcessCurrentChange" @refresh="funGetProcessTree"></tree-cop>
  526. </el-col>
  527. <el-col :span="3">
  528. <excel-cop :checkIds="excelCheckIds" :showCheckbox="excelCheckboxShow" :data="excelList" :height="excelHeight"
  529. @excelChange="funExcelChange" @checkChange="funExcelCheckChange"></excel-cop>
  530. <excel-cop class="mt-[10px]" :data="excelFitList" :height="excelHeight" @excelChange="funExcelChange">
  531. </excel-cop>
  532. </el-col>
  533. <el-col :span="16">
  534. <div class="px-[10px] shadow rounded-[6px] shadow-blue-500 ">
  535. <SubmitBtn class="absolute right-[16px] top-[6px] z-10" desc="区域划分" v-if="activeTab === '2' && excelType === 'fitting'" @click="funChartArea"></SubmitBtn>
  536. <el-tabs v-model="activeTab">
  537. <el-tab-pane label="表格数据" name="1">
  538. </el-tab-pane>
  539. <el-tab-pane label="图表展示" name="2" v-if="excelType === 'fitting'">
  540. </el-tab-pane>
  541. <table-cop v-show="activeTab === '1'" :data="tableData" :loading="tableLoading" :column="tableColumn"
  542. :height="tableHeight" :tableId="tableShowId" :tableName="tableName"></table-cop>
  543. <div v-show="activeTab === '2'"
  544. :style="{ height: typeof tableHeight === 'string' ? tableHeight : tableHeight + 'px' }"
  545. class="p-[10px]">
  546. <CurrentScatterChart ref="chartRef" width="100%" height="calc( 100% - 20px )" :chartTitle="avgObj.title+ '&nbsp;&nbsp;' +'平均Cp值:'+avgObj.cpavg+'; 静风频率:'+avgObj.frequency+'%; 曲线偏差率:'+avgObj.pcratio+'%'"
  547. :xAxisData="xAxisData" :yAxisData="{ splitLine: { show: false } }" :seriesData="seriesData"
  548. :showLegend="true" :brushSelected="!isChartArea" :dataSet="dataSet" @getSelected="funChartSelect" />
  549. </div>
  550. </el-tabs>
  551. </div>
  552. </el-col>
  553. </el-row>
  554. </div>
  555. </div>
  556. </template>