details.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  1. <template>
  2. <view class="box">
  3. <!-- 上面要素值 -->
  4. <view :class="`bigBox ${show ? 'bigOpen' : ''}`">
  5. <view class="topInfo">
  6. <u-row gutter="10">
  7. <u-col span="3" v-for="item in ElementList">
  8. <view class="item">
  9. <view>{{item.value}}</view>
  10. <view>{{item.unit}}</view>
  11. <view>{{item.name}}</view>
  12. </view>
  13. </u-col>
  14. </u-row>
  15. <view class="readmore" v-if="!show" @click="show=true;getHistoryData()">
  16. 查看详情<u-icon name="arrow-down" color="#93FFDE" size="28"></u-icon>
  17. </view>
  18. </view>
  19. <!-- 统计图 -->
  20. <view class="hisBox" v-show="show">
  21. <view class="axiosBox">
  22. <view class="title">
  23. 历史数据
  24. </view>
  25. <view class="histimeBox">
  26. <u-icon name="calendar" color="#C1C1C1" size="24"></u-icon>
  27. <view class="time" @click="calendarshow = true">{{historyTime.startDate ? historyTime.startDate : '开始'}}</view>
  28. <view class="">至</view>
  29. <view class="time" @click="calendarshow = true">{{historyTime.endDate ? historyTime.endDate : '结束'}}</view>
  30. <u-icon name="close-circle" color="#C1C1C1" size="28" @click="historyTime={};getHistoryData()" v-if="historyTime.startDate"></u-icon>
  31. </view>
  32. <u-calendar v-model="calendarshow" mode="range" @change="changeDate"></u-calendar>
  33. <view class="chartBox">
  34. <canvas canvas-id="canvasColumnA" id="canvasColumnA" class="charts"
  35. @touchstart="touchLineA($event)" @touchmove="moveLineA($event)"
  36. @touchend="touchEndLineA($event)"></canvas>
  37. </view>
  38. </view>
  39. <view class="readmore" @click="show=false">
  40. 收起详情<u-icon name="arrow-up" color="#14A478" size="28"></u-icon>
  41. </view>
  42. </view>
  43. </view>
  44. <!-- 历史记录弹框 -->
  45. <u-tabs :list="list" active-color="#14A478" bar-width="300" :is-scroll="false" :current="current"
  46. @change="change"></u-tabs>
  47. <!-- 设备图 -->
  48. <view class="devicePhoto" v-if="current == 0">
  49. </view>
  50. <!-- 操作记录 -->
  51. <view class="consoleList" v-else>
  52. <view class="histimeBox">
  53. <u-icon name="calendar" color="#C1C1C1" size="24"></u-icon>
  54. <view class="time" @click="consoleshow = true">{{consoleTime.startDate ? consoleTime.startDate : '开始'}}</view>
  55. <view class="">至</view>
  56. <view class="time" @click="consoleshow = true">{{consoleTime.endDate ? consoleTime.endDate : '结束'}}</view>
  57. <u-icon name="close-circle" color="#C1C1C1" size="28" @click="clearContime" v-if="consoleTime.startDate"></u-icon>
  58. </view>
  59. <u-calendar v-model="consoleshow" mode="range" @change="changeConsoleDate"></u-calendar>
  60. <view class="tableList tableTitle">
  61. <view>设备名称</view>
  62. <view>操作内容</view>
  63. <view>操作时间</view>
  64. </view>
  65. <view class="tableList" v-for="item in tableData" :key="item.uptime">
  66. <view>{{item.device_name}}</view>
  67. <view>{{item.operation_content}}</view>
  68. <view>{{item.uptime}}</view>
  69. </view>
  70. <u-loadmore :status="status" />
  71. </view>
  72. </view>
  73. </template>
  74. <script>
  75. import uCharts from '../../components/js_sdk/u-charts/u-charts/u-charts.js';
  76. var canvaColumnA = null;
  77. var canvasRing = null;
  78. export default {
  79. data() {
  80. return {
  81. device_id: '',
  82. mainBeng: false,
  83. feiBeng: false,
  84. calendarshow: false,
  85. historyTime: {},
  86. cWidth: '350',
  87. cHeight: '350',
  88. pixelRatio: 1,
  89. facilityvalve: [],
  90. ElementList: [],
  91. typename: [
  92. "-",
  93. "风机",
  94. "水泵",
  95. "增氧机",
  96. "湿帘",
  97. "遮阳",
  98. "开窗",
  99. "保温",
  100. "投食机",
  101. ],
  102. show: false,
  103. list: [{
  104. name: '设备控制'
  105. }, {
  106. name: '操作记录'
  107. }],
  108. current: 1,
  109. consoleshow: false,
  110. consoleTime: {},
  111. tableData: [],
  112. page: 1,
  113. total: 0,
  114. status: 'loadmore'
  115. }
  116. },
  117. methods: {
  118. // 选择日期
  119. changeDate(e) {
  120. console.log(e)
  121. this.historyTime = e;
  122. this.getHistoryData()
  123. },
  124. // 绘制折线图
  125. showColumn(id, xtitle, xinfo) {
  126. var _self = this
  127. canvaColumnA = new uCharts({
  128. canvasId: id,
  129. type: 'line',
  130. legend: {
  131. position: "top"
  132. },
  133. fontSize: 11,
  134. background: '#FFFFFF',
  135. pixelRatio: 1,
  136. animation: true,
  137. dataLabel: false,
  138. categories: xtitle,
  139. series: xinfo,
  140. enableScroll: true, //开启图表拖拽功能
  141. xAxis: {
  142. disableGrid: true,
  143. type: 'grid',
  144. gridType: 'dash',
  145. itemCount: 4, //x轴单屏显示数据的数量,默认为5个
  146. scrollShow: true, //新增是否显示滚动条,默认false
  147. // scrollAlign: 'left', //滚动条初始位置
  148. scrollBackgroundColor: '#F7F7FF', //默认为 #EFEBEF
  149. scrollColor: '#DEE7F7', //默认为 #A6A6A6
  150. },
  151. yAxis: {},
  152. width: _self.cWidth * 1,
  153. height: _self.cHeight * 1,
  154. extra: {
  155. line: {
  156. type: 'curve'
  157. }
  158. }
  159. });
  160. },
  161. touchLineA(e) {
  162. // console.log(e)
  163. canvaColumnA.scrollStart(e);
  164. },
  165. moveLineA(e) {
  166. canvaColumnA.scroll(e);
  167. },
  168. touchEndLineA(e) {
  169. canvaColumnA.touchLegend(e);
  170. // canvaColumnA.showToolTip(e);
  171. canvaColumnA.scrollEnd(e);
  172. //下面是toolTip事件,如果滚动后不需要显示,可不填写
  173. canvaColumnA.showToolTip(e, {
  174. format: function(item, category) {
  175. return category + ' ' + item.name + ':' + item.data
  176. }
  177. });
  178. },
  179. // 时间戳转换
  180. timestampToDateTime(timestamp) {
  181. var date = new Date(timestamp * 1000); // 将时间戳转换为毫秒并创建一个日期对象
  182. var year = date.getFullYear(); // 获取年份
  183. var month = ('0' + (date.getMonth() + 1)).slice(-2); // 获取月份,并补零
  184. var day = ('0' + date.getDate()).slice(-2); // 获取日期,并补零
  185. return year + '-' + month + '-' + day;
  186. },
  187. // 历史数据
  188. async getHistoryData() {
  189. const {
  190. historyTime
  191. } = this;
  192. let begin = historyTime.startDate ? new Date(historyTime.startDate).getTime() / 1000 : '';
  193. let end = historyTime.endDate ? new Date(historyTime.endDate).getTime() / 1000 : '';
  194. //折线图数据
  195. let res = await this.$myRequest({
  196. url: "/api/api_gateway?method=weather.weather.sf_data_chart",
  197. data: {
  198. device_id: this.device_id,
  199. begin: begin,
  200. end: end,
  201. },
  202. })
  203. console.log(res);
  204. var conf = res.conf.eleName;
  205. var dat = res.dat;
  206. //console.log(dat);
  207. conf = conf.split("/");
  208. // var arr1 = [];
  209. var regroupData = []; //重组数据
  210. let options = [];
  211. var timeList = [];
  212. for (var i = 0; i < conf.length; i++) {
  213. // var arr = [];
  214. if (conf[i] != "-") {
  215. var optionobj = {};
  216. optionobj.value = i;
  217. optionobj.label = conf[i];
  218. options.push(optionobj);
  219. var reddata = [];
  220. dat.forEach((item, index) => {
  221. if (timeList.length < dat.length) {
  222. timeList.push(this.timestampToDateTime(item.uptime));
  223. }
  224. var status = item.device_status.split(",");
  225. // //console.log(status);
  226. var arr = [];
  227. for (var j = 0; j < status.length; j++) {
  228. if (status[j].indexOf("e") + 1) {
  229. arr.push(status[j]);
  230. }
  231. }
  232. // //console.log(arr)
  233. var dataobj = {};
  234. for (var k = 0; k < arr.length; k++) {
  235. var indexs = arr[k].indexOf(":");
  236. // //console.log(arr[k])
  237. if (k == 0) {
  238. dataobj[arr[k].slice(1, indexs).trim()] = arr[k]
  239. .substr(indexs + 1)
  240. .trim();
  241. } else {
  242. dataobj[arr[k].slice(0, indexs).trim()] = arr[k]
  243. .substr(indexs + 1)
  244. .trim();
  245. }
  246. }
  247. // //console.log(dataobj)
  248. for (var key in dataobj) {
  249. // //console.log(i)
  250. var str = "'e" + (i + 1) + "'";
  251. // //console.log(key,str);
  252. if (key == str) {
  253. if (Number(dataobj[key]) == 32767) {
  254. // reddata.push([]);
  255. } else {
  256. reddata.push(Number(dataobj[key]));
  257. }
  258. }
  259. }
  260. });
  261. regroupData.push({
  262. name: conf[i],
  263. type: "spline",
  264. fillColor: {
  265. linearGradient: [0, 0, 0, 300],
  266. },
  267. data: reddata,
  268. });
  269. }
  270. }
  271. // console.log(timeList);
  272. this.showColumn('canvasColumnA', timeList, regroupData)
  273. },
  274. // 切换选项卡
  275. change(index) {
  276. this.current = index;
  277. },
  278. // 获取要素实时数据
  279. async getElmentInfo() {
  280. let res = await this.$myRequest({
  281. url: "/api/api_gateway?method=xphsp.views.device_elements",
  282. data: {
  283. device_id: this.device_id,
  284. },
  285. })
  286. var conf = res.conf.eleName.split('/');
  287. var dataList = res.data;
  288. if (dataList.length == 0) return
  289. this.ElementList = [];
  290. conf.forEach((item, index) => {
  291. let obj = {}
  292. if (item == '-') return
  293. obj.name = item;
  294. let valList = dataList[0].device_data[`e${index + 1}`].split('#');
  295. obj.value = valList[0];
  296. obj.unit = valList[1];
  297. obj.time = dataList[0].uptime;
  298. this.ElementList.push(obj)
  299. })
  300. // console.log(this.ElementList)
  301. },
  302. async getEquipstatus() {
  303. this.facilityvalve = [];
  304. let res = await this.$myRequest({
  305. url: "/api/api_gateway?method=xphsp.views.device_info",
  306. data: {
  307. device_id: this.device_id,
  308. },
  309. })
  310. console.log(res)
  311. var conf = res.conf.relayName;
  312. var dataobj = res.device_status;
  313. var relayNum = res.conf.relayNum;
  314. var arr = [];
  315. function numberToArray(number) {
  316. // 将数字转换为字符串
  317. let numString = number.toString();
  318. // 创建一个空数组,用于存储结果
  319. let result = [];
  320. // 遍历字符串的每个字符,并将其转换为对应的数字
  321. for (let i = 0; i < numString.length; i++) {
  322. result.push(parseInt(numString.charAt(i)));
  323. }
  324. return result;
  325. }
  326. for (var i = 0; i < dataobj.length; i++) {
  327. // if (offobj[i].indexOf("j") + 1) {
  328. arr.push(offobj[i]);
  329. // }
  330. }
  331. conf = conf.split("/");
  332. relayNum = relayNum.split("/");
  333. var arr1 = [];
  334. var reg = /\d/;
  335. let totalP = Math.ceil((conf.length) / 32); //总圈数
  336. for (var i = 0; i < conf.length; i++) {
  337. var obj = {};
  338. obj.name = conf[i];
  339. // if (reg.test(conf[i])) {
  340. // obj.name2 = conf[i].slice(0, conf[i].length - 1);
  341. // } else {
  342. // obj.name2 = conf[i];
  343. // }
  344. obj.name2 = this.typename[relayNum[i]];
  345. obj.number = i;
  346. obj.type = `j${i + 1}`;
  347. obj.disabled = false;
  348. // "state": 1 继电器打开 ;"state": 0 继电器关闭
  349. // 处理开关值
  350. // J开头表示状态,总共只有32长度,超过32则从头赋值 位数多一位
  351. // 例:1和33是都在J1里面,返回值是一个二进制数 倒数第一位表示第一个 第二位表示33状态 以此类推
  352. // 根据总长度 为超过32的j赋值
  353. // 当前是循环的第几轮
  354. let baseP = Math.ceil((i + 1) / 32);
  355. if (i > 31) {
  356. dataobj[`j${i + 1}`] = dataobj[
  357. `j${(i + 1) % 32 == 0 ? 32 : (i + 1) % 32}`]; // 当前脚标/32取余 当恰好整除的时候 取32
  358. }
  359. dataobj[`j${i + 1}`] = dataobj[`j${i + 1}`].slice(-totalP); // 截取当前圈数的最后几位数字
  360. let baseOff = numberToArray(dataobj[`j${i + 1}`]); // 把当前值根据位数 转换为数组
  361. obj.off = baseOff[baseP - 1] == '0' ? false : true; // 根据当前圈数,取对应的数组的值
  362. arr1.push(obj);
  363. }
  364. for (var i = 0; i < arr1.length; i++) {
  365. if (arr1[i].name != "-") {
  366. this.facilityvalve.push(arr1[i]);
  367. }
  368. }
  369. this.facilityvalve.forEach(item => {
  370. if (item.type == 'j9' && item.off == true) {
  371. // 判断注水泵是否打开
  372. this.mainBeng = true;
  373. }
  374. if (item.type == 'j10' && item.off == true) {
  375. // 判断施肥阀是否打开
  376. this.feiBeng = true;
  377. }
  378. })
  379. console.log(this.facilityvalve)
  380. },
  381. getEquipcontrol(num, state) {
  382. //设备控制
  383. this.$axios({
  384. method: "POST",
  385. url: "/api/api_gateway?method=xphsp.views.control_order",
  386. data: this.qs.stringify({
  387. device_id: this.device_id,
  388. relayNum: num,
  389. relayState: state,
  390. }),
  391. }).then((res) => {
  392. //console.log(res.data.data);
  393. if (res.data.data && res.data.data.controlState) {
  394. this.$message({
  395. message: "指令下发成功",
  396. type: "success",
  397. });
  398. } else {
  399. // for (var i = 0; i < this.facilityvalve.length; i++) {
  400. // //console.log(this.facilityvalve[i].number, res.data.data.relayNum);
  401. // if (
  402. // this.facilityvalve[i].number == Number(num)
  403. // ) {
  404. // //console.log(this.facilityvalve[i]);
  405. // this.facilityvalve[i].off = !state;
  406. // }
  407. // }
  408. this.getEquipstatus()
  409. this.$message({
  410. message: res.data.message,
  411. type: "warning",
  412. });
  413. }
  414. });
  415. },
  416. // sf_control_data
  417. // 操作记录日期
  418. changeConsoleDate(e) {
  419. this.consoleTime = e;
  420. this.tableData = [];
  421. this.page = 1;
  422. this.getEquipcontroldata()
  423. },
  424. clearContime() {
  425. this.consoleTime = {};
  426. this.tableData = [];
  427. this.page = 1;
  428. this.getEquipcontroldata()
  429. },
  430. //操作记录
  431. async getEquipcontroldata() {
  432. const {
  433. consoleTime
  434. } = this;
  435. let begin = consoleTime.startDate ? new Date(consoleTime.startDate).getTime() / 1000 : '';
  436. let end = consoleTime.endDate ? new Date(consoleTime.endDate).getTime() / 1000 : '';
  437. //操作记录
  438. let res = await this.$myRequest({
  439. method: "POST",
  440. url: "/api/api_gateway?method=xphsp.views.control_log",
  441. data: {
  442. device_id: this.device_id,
  443. page_num: this.page,
  444. page_size: 15,
  445. start: begin,
  446. end: end,
  447. },
  448. })
  449. // this.tableData = [];
  450. var record = res.result;
  451. this.tableData = [...this.tableData, ...record];
  452. this.total = res.count;
  453. if (this.page * 15 > this.total) {
  454. this.status = 'nomore'
  455. } else {
  456. this.status = 'loadmore'
  457. }
  458. console.log(this.tableData);
  459. },
  460. switchchange(e, state, item, index) {
  461. item.off = e;
  462. if (item.type == 'j9') {
  463. // 判断注水泵是否打开
  464. this.mainBeng = e;
  465. }
  466. if (item.type == 'j10') {
  467. // 判断施肥阀是否打开
  468. this.feiBeng = e;
  469. }
  470. if (['j1', 'j2', 'j3', 'j4', 'j5', 'j6'].indexOf(item.type) > 0) {
  471. let changeIndex = index;
  472. if ((index + 1) % 2 == 0) {
  473. // 偶数 往前取值
  474. this.facilityvalve[index - 1].disabled = true;
  475. changeIndex = index - 1;
  476. } else if ((index + 1) % 2 == 1) {
  477. // 基数 往后取值
  478. this.facilityvalve[index + 1].disabled = true;
  479. changeIndex = index + 1;
  480. }
  481. item.disabled = true;
  482. this.changeTimeStatus(item, changeIndex);
  483. }
  484. //console.log(Number(e), state);
  485. this.getEquipcontrol(state, Number(e));
  486. },
  487. },
  488. onReachBottom() {
  489. if (this.current == 1 && this.status == 'loadmore') {
  490. this.page++;
  491. this.getEquipcontroldata()
  492. }
  493. },
  494. onLoad(option) {
  495. let deviceItem = JSON.parse(option.shebei)
  496. this.device_id = deviceItem.device_id;
  497. this.getEquipstatus();
  498. this.getElmentInfo();
  499. this.getEquipcontroldata();
  500. }
  501. }
  502. </script>
  503. <style scoped lang="less">
  504. .box {
  505. position: relative;
  506. width: 100%;
  507. height: 100vh;
  508. }
  509. .bigBox {
  510. width: 100%;
  511. }
  512. .bigOpen {
  513. overflow: hidden;
  514. height: 100%;
  515. }
  516. /deep/ .u-flex {
  517. display: flex;
  518. justify-content: center;
  519. }
  520. .topInfo {
  521. background: #14A478;
  522. color: #fff;
  523. font-size: 28rpx;
  524. padding-bottom: 32rpx;
  525. box-sizing: border-box;
  526. border-bottom-left-radius: 16rpx;
  527. border-bottom-right-radius: 16rpx;
  528. .u-col {
  529. box-sizing: border-box;
  530. margin-top: 32rpx;
  531. .item {
  532. text-align: center;
  533. view:first-child {
  534. font-size: 40rpx;
  535. }
  536. view:nth-child(2) {
  537. font-size: 24rpx;
  538. line-height: 40rpx;
  539. opacity: .72;
  540. }
  541. }
  542. }
  543. }
  544. .readmore {
  545. text-align: center;
  546. font-size: 26rpx;
  547. line-height: 40rpx;
  548. margin-top: 32rpx;
  549. color: #93FFDE;
  550. .u-icon {
  551. margin-left: 15rpx;
  552. }
  553. }
  554. .histimeBox {
  555. display: flex;
  556. align-items: center;
  557. justify-content: space-between;
  558. height: 56rpx;
  559. padding: 6rpx 24rpx;
  560. box-sizing: border-box;
  561. border-radius: 8rpx;
  562. margin-top: 54rpx;
  563. border: 2rpx solid var(--neutral-color-border-light, #E4EDEA);
  564. .u-icon{
  565. z-index: 10;
  566. }
  567. .time {
  568. width: 280rpx;
  569. color: #C1C1C1;
  570. }
  571. view {
  572. color: #333333;
  573. text-align: center;
  574. }
  575. }
  576. .hisBox {
  577. padding-bottom: 100rpx;
  578. .axiosBox {
  579. padding: 48rpx 32rpx;
  580. .title {
  581. color: #333333;
  582. font-size: 28rpx;
  583. font-weight: 700;
  584. }
  585. .chartBox {
  586. margin-top: 32rpx;
  587. height: 700rpx;
  588. #canvasColumnA {
  589. width: 100%;
  590. height: 700rpx;
  591. }
  592. }
  593. }
  594. .readmore {
  595. color: #14A478;
  596. }
  597. }
  598. .consoleList {
  599. padding: 0 32rpx;
  600. .histimeBox{
  601. margin: 32rpx 0;
  602. }
  603. .tableTitle {
  604. background: #E8F3F0;
  605. color: #333;
  606. font-weight: 600;
  607. }
  608. .tableList {
  609. font-size: 28rpx;
  610. height: 78rpx;
  611. color: #666666;
  612. display: flex;
  613. justify-content: space-between;
  614. align-items: center;
  615. border-bottom: 2rpx solid var(--neutral-color-border-base, #DCE6E3);
  616. view {
  617. text-align: center;
  618. }
  619. view:nth-child(1) {
  620. width: 120rpx;
  621. }
  622. view:nth-child(2) {
  623. width: 280rpx;
  624. }
  625. view:nth-child(3) {
  626. width: 280rpx;
  627. }
  628. }
  629. }
  630. </style>