index.vue 15 KB

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