index.vue 16 KB

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