deviceData.vue 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263
  1. <template>
  2. <view class="device-data-container">
  3. <!-- 顶部时间戳和图标区域 -->
  4. <view class="device-data-wraper">
  5. <view class="top-bar">
  6. <view class="timestamp">
  7. {{formatDate(deviceInfo.uptime)}}
  8. <u-icon name="reload" color="#0bbc58" style="margin-left: 12rpx" @click="refreshData"></u-icon>
  9. </view>
  10. <view class="icon-group">
  11. <!-- <view class="icon-item refresh-icon">
  12. <img class="copy-icon" :src="sim"/>
  13. </view>
  14. <view class="icon-item settings-icon" @click="openSettings">
  15. <img class="copy-icon" :src="setting"/>
  16. </view> -->
  17. <!-- <view class="icon-item info-icon">
  18. <img class="copy-icon" :src="edit"/>
  19. </view> -->
  20. </view>
  21. </view>
  22. <!-- 主要数据面板 -->
  23. <view class="main-data-panel">
  24. <view class="data-column-left" v-if="!isStatic">
  25. <view class="data-item">
  26. <text class="data-value">{{ objItem.ds == '1' ? '开机' : '关机' }}</text>
  27. <text class="data-label">设备开机</text>
  28. </view>
  29. <!-- <view class="data-item">
  30. <text class="data-value">{{ objItem.staytime || objItem.stay_time || objItem.step_time}}</text>
  31. <text class="data-label">已培养时间</text>
  32. </view> -->
  33. <view class="data-item">
  34. <text class="data-value">{{ objItem.dver }}</text>
  35. <text class="data-label">设备版本</text>
  36. </view>
  37. </view>
  38. <view class="data-column-left" v-else>
  39. <view class="data-item">
  40. <text class="data-value">{{ staticObj.vbat }}</text>
  41. <text class="data-label">电量</text>
  42. </view>
  43. <view class="data-item">
  44. <text class="data-value">{{ parseInt(staticObj.tps) == 1 ? '温控保护' : this.formartWorkSta(staticObj.work_sta) }}</text>
  45. <text class="data-label">工作状态</text>
  46. </view>
  47. <view class="data-item">
  48. <text class="data-value">{{ staticObj.at }}℃</text>
  49. <text class="data-label">环境温度</text>
  50. </view>
  51. <view class="data-item">
  52. <text class="data-value">{{ staticObj.csq }}</text>
  53. <text class="data-label">信号强度</text>
  54. </view>
  55. <view class="data-item">
  56. <text class="data-value">{{ staticObj.ds == 0 ? '开机' : '关机' }}</text>
  57. <text class="data-label">设备状态</text>
  58. </view>
  59. <view class="data-item">
  60. <text class="data-value">{{ staticObj.tps == 1 ? '温控保护' : '正常' }}</text>
  61. <text class="data-label">温控状态</text>
  62. </view>
  63. <view class="data-item">
  64. <text class="data-value">{{ staticObj.ah }}%RH</text>
  65. <text class="data-label">环境湿度</text>
  66. </view>
  67. <view class="data-item">
  68. <text class="data-value">{{ staticObj.dver }}</text>
  69. <text class="data-label">设备版本</text>
  70. </view>
  71. </view>
  72. <view class="data-column-right">
  73. <view class="device-image-container">
  74. <image class="device-image" :src="formatDevImg" mode="aspectFit"></image>
  75. </view>
  76. </view>
  77. </view>
  78. </view>
  79. <!-- 图表区域 -->
  80. <view class="chart-section">
  81. <view class="chart-header">
  82. <view class="chart-tabs">
  83. <view
  84. v-for="(tab, index) in chartTabs"
  85. :key="index"
  86. class="chart-tab-item"
  87. :class="{ active: activeChartTab === index }"
  88. @click="switchChartTab(index)"
  89. >
  90. {{ tab.name }}
  91. </view>
  92. </view>
  93. <!-- <view class="chart-dropdown">
  94. <text class="iconfont dropdown-icon">&#xe606;</text>
  95. </view> -->
  96. </view>
  97. <view class="chart-content">
  98. <view class="chart-canvas-container" v-show="!show" :key="chartKey">
  99. <!-- <div id="tempChart" class="chart-canvas"></div> -->
  100. <qiun-data-charts type="line" :chartData="chartData" :canvas2d="true" :inScrollView="true" :opts="opts" :ontouch="true" v-show="xData.length"/>
  101. </view>
  102. </view>
  103. </view>
  104. <!-- 历史数据表格 -->
  105. <view class="history-section">
  106. <view class="history-header">
  107. <text class="history-title">历史数据</text>
  108. </view>
  109. <view class="history-table">
  110. <view class="table-container">
  111. <!-- 固定列 -->
  112. <view class="fixed-column">
  113. <view class="fixed-header" style="width: 260rpx;">
  114. <text class="header-cell fixed">上报时间</text>
  115. </view>
  116. <view class="fixed-body">
  117. <view
  118. v-for="(item, index) in historyData"
  119. :key="index"
  120. class="fixed-row"
  121. style="width: 260rpx;"
  122. :class="{ even: index % 2 === 0 }"
  123. >
  124. <text class="body-cell fixed">{{ formatTime(item.addtime) }}</text>
  125. </view>
  126. </view>
  127. </view>
  128. <!-- 可滑动列 -->
  129. <view class="scrollable-column">
  130. <view class="scrollable-header">
  131. <text class="header-cell">设备开关</text>
  132. <text class="header-cell">环境温度(°C)</text>
  133. <text class="header-cell">环境湿度(%)</text>
  134. <text class="header-cell">电池状态</text>
  135. <text class="header-cell">工作状态</text>
  136. <text class="header-cell">设备版本</text>
  137. </view>
  138. <view class="scrollable-body">
  139. <view
  140. v-for="(item, index) in historyData"
  141. :key="index"
  142. class="scrollable-row"
  143. >
  144. <view class="body-cell">
  145. <view class="status-icon" :class="item.ds == 1 ? 'success-icon' : 'status-icon'"></view>
  146. {{ item.ds == 1 ? '打开' : '关闭' }}</view>
  147. <view class="body-cell">{{ item.at }}</view>
  148. <view class="body-cell">{{ item.ah }}</view>
  149. <view class="body-cell">
  150. <view class="status-icon" :class="item.batStatus == 0 ? 'success-icon' : 'status-icon'"></view>
  151. {{ item.batStatus == 0 ? '正常' : '欠压' }}</view>
  152. <view class="body-cell">
  153. <view class="status-icon" :class="item.work_sta == 0 ? 'warning-icon' : item.work_sta == 1 ? 'success-icon':'status-icon'"></view>
  154. {{ item.work_sta == 0 ? '待机' : item.work_sta == 1 ? '收集' : item.work_sta == 2 ? '培养' : '拍照' }}</view>
  155. <view class="body-cell">{{ item.dver }}</view>
  156. </view>
  157. </view>
  158. </view>
  159. </view>
  160. </view>
  161. <view class="pagination">
  162. <text class="pagination-btn prev-btn" @click="prevPage">上一页</text>
  163. <text class="pagination-info">{{currentPage}}/{{totalPages || 1}}</text>
  164. <text class="pagination-btn next-btn" @click="nextPage">下一页</text>
  165. </view>
  166. </view>
  167. <u-popup v-model="show" mode="bottom" border-radius="20">
  168. <view style="height:70vh">
  169. <view class="tabs-container">
  170. <view class="tabs-item" @click="switchTab(0)" :class="{ active: activeTab === 0 }" v-if="data_iccid">数据SIM卡流量</view>
  171. <view class="tabs-item" @click="switchTab(1)" :class="{ active: activeTab === 1 }">图片SIM卡流量</view>
  172. </view>
  173. <view class="sim-container" v-if="activeTab === 0">
  174. <view class="sim-item">
  175. <text class="sim-label">ICCID:</text>
  176. <text class="sim-value">{{ deviceInfoStatic.iccid }}</text>
  177. </view>
  178. <view class="sim-item">
  179. <text class="sim-label">总流量:</text>
  180. <text class="sim-value">{{ deviceInfoStatic.total }}</text>
  181. </view>
  182. <view class="sim-item">
  183. <text class="sim-label">状态:</text>
  184. <text class="sim-value">{{ deviceInfoStatic.status }}</text>
  185. </view>
  186. <view class="sim-item">
  187. <text class="sim-label">到期时间:</text>
  188. <text class="sim-value">{{ deviceInfoStatic.expire }}</text>
  189. </view>
  190. <view class="sim-item">
  191. <text class="sim-label">厂商名称:</text>
  192. <text class="sim-value">{{ deviceInfoStatic.company }}</text>
  193. </view>
  194. <view class="sim-item">
  195. <text class="sim-label">已使用:</text>
  196. <text class="sim-value"><text style="color: #0bbc58">{{ deviceInfoStatic.used || ''}}</text>/{{ deviceInfoStatic.total || '' }}</text>
  197. </view>
  198. <view class="sim-item">
  199. <text class="sim-value">
  200. <u-line-progress active-color="#0bbc58" :percent="deviceInfoStatic.used / deviceInfoStatic.total * 100" :show-percent="false"></u-line-progress>
  201. </text>
  202. </view>
  203. </view>
  204. <view class="sim-container" v-if="activeTab === 1">
  205. <view class="sim-item" style="display:flex;align-items: center;height:80rpx;">
  206. <text class="sim-label">ICCID:</text>
  207. <text v-if="show1" style="margin-right: 10rpx">{{ photo_iccid }}</text>
  208. <text class="sim-value" style="color: #0bbc58;" @click="changeSim" v-if="show1">
  209. 更换SIM卡
  210. </text>
  211. <view v-else class="sim-value-change">
  212. <u-input v-model="data_iccidInput" placeholder="请输入ICCID" class="sim-value"></u-input>
  213. <view class="sim-value-button" size="small" @click="changeSimHandler">确认</view>
  214. <view class="sim-value-button" size="small" @click="show1 = true">取消</view>
  215. </view>
  216. </view>
  217. </view>
  218. </view>
  219. </u-popup>
  220. <floatButton v-if="false">
  221. <view slot="icon">
  222. <view class="general-border">
  223. <image :src="general" class="general-icon"></image>
  224. </view>
  225. </view>
  226. <view slot="expanded">
  227. <view class="general-images">
  228. <view class="general-image edit-border" :class="{'edit-border2':isShowPhoto}" @click="editData">
  229. <image :src="editBorder" class="image-icon"></image>
  230. </view>
  231. <view class="general-image sim-border" :class="{'sim-border2':isShowPhoto}" @click="simData">
  232. <image :src="simBorder" class="image-icon"></image>
  233. </view>
  234. <view class="general-image setting-border" :class="{'setting-border2':isShowPhoto}" @click="openSettings">
  235. <image :src="settingBorder" class="image-icon"></image>
  236. </view>
  237. <view class="general-image photo-border" @click="openPhoto" v-if="isShowPhoto">
  238. <image :src="photoBorder" class="image-icon"></image>
  239. </view>
  240. </view>
  241. </view>
  242. </floatButton>
  243. </view>
  244. </template>
  245. <script>
  246. import Circulation from '../../../static/js/equipState_dict.json';
  247. import floatButton from './floating-button.vue';
  248. let chartInstance = null;
  249. export default {
  250. name: 'DeviceData',
  251. props:{
  252. deviceInfo:{
  253. type:Object,
  254. default:()=>({}),
  255. },
  256. deviceStatic:{
  257. type:Object,
  258. default:()=>({}),
  259. },
  260. polylineList:{
  261. type:Array,
  262. default:()=>[],
  263. },
  264. deviceHistoryList:{
  265. type:Array,
  266. default:()=>[],
  267. },
  268. totalPage:{
  269. type:Number,
  270. default:0
  271. },
  272. currentPage:{
  273. type:Number,
  274. default:0
  275. },
  276. page_size:{
  277. type:Number,
  278. default:10
  279. },
  280. },
  281. components: {
  282. floatButton,
  283. },
  284. data() {
  285. return {
  286. setting:'https://s3.hnyfwlw.com/webstaticimg/bigdata_app/newImg/home/setting.png',
  287. general:'https://s3.hnyfwlw.com/webstaticimg/bigdata_app/newImg/home/general.png',
  288. editBorder:'https://s3.hnyfwlw.com/webstaticimg/bigdata_app/newImg/home/editBorder.png',
  289. settingBorder:'https://s3.hnyfwlw.com/webstaticimg/bigdata_app/newImg/home/settingBorder.png',
  290. simBorder:'https://s3.hnyfwlw.com/webstaticimg/bigdata_app/newImg/home/simBorder.png',
  291. devImage:'https://s3.hnyfwlw.com/webstaticimg/bigdata_app/image/cbd.png',
  292. photoBorder:'https://s3.hnyfwlw.com/webstaticimg/bigdata_app/newImg/home/photoBorder.png',
  293. devImg:'',
  294. activeTab: 0,
  295. edit:'https://s3.hnyfwlw.com/webstaticimg/bigdata_app/newImg/home/edit.png',
  296. sim:'https://s3.hnyfwlw.com/webstaticimg/bigdata_app/newImg/home/sim.png',
  297. show:false,
  298. isShowPhoto:false,
  299. show1:true,
  300. equipStateDict:{},
  301. chartTabs: [
  302. { name: '温度', id: 'new_tem' },
  303. { name: '湿度', id: 'new_hum' },
  304. ],
  305. opts: {
  306. type: 'line',
  307. xAxis: {
  308. disableGrid: true,
  309. itemCount: 3,
  310. scrollShow: true
  311. },
  312. yAxis: {
  313. disableGrid: true,
  314. gridType: 'dash',
  315. splitNumber: 5,
  316. min: 0,
  317. format: (val) => {
  318. return Math.round(val)
  319. }
  320. },
  321. extra: {
  322. line: {
  323. type: 'curve'
  324. },
  325. tooltip: {
  326. format: {
  327. name: '',
  328. value: (val) => Math.round(val)
  329. }
  330. }
  331. },
  332. legend: {
  333. },
  334. enableScroll: true
  335. },
  336. xData:[],
  337. yData:[],
  338. activeChartTab: 0,
  339. historyData: [],
  340. data_iccid:'',
  341. data_iccidInput:'',
  342. deviceInfoStatic:{},
  343. photo_iccid:'',
  344. chartData: {},
  345. chartKey: 0,
  346. isStatic:false,
  347. staticObj:{},
  348. objItem:{}
  349. };
  350. },
  351. mounted(){
  352. this.equipStateDict = Circulation
  353. },
  354. computed: {
  355. totalPages(){
  356. return Math.ceil(this.totalPage / this.page_size)
  357. },
  358. formatDevImg() {
  359. return `https://s3.hnyfwlw.com/webstaticimg/bigdata_app/image/${this.devImg}.png`;
  360. },
  361. },
  362. watch:{
  363. polylineList:{
  364. handler(newVal, oldVal){
  365. this.xData = newVal.map(item => this.formatDate(new Date(item.addtime)));
  366. this.yData = newVal.map(item => Number(item.new_tem) || 0);
  367. if(this.xData.length){
  368. this.initChart();
  369. }
  370. },
  371. deep:true,
  372. immediate:true,
  373. },
  374. deviceInfo:{
  375. handler(newVal, oldVal){
  376. this.isStatic = false
  377. if(newVal.type_id == 7 && newVal.device_model == 52){
  378. this.isStatic = true
  379. this.getStatic()
  380. }
  381. console.log(newVal.device_model,'ssssssss')
  382. if (newVal.device_model == '0'){
  383. this.devImg = 'bzy'
  384. this.isShowPhoto = true
  385. } else{
  386. this.devImg = 'bzy1'
  387. this.isShowPhoto = true
  388. }
  389. },
  390. deep:true,
  391. immediate:true,
  392. },
  393. deviceHistoryList:{
  394. handler(newVal, oldVal){
  395. this.historyData = []
  396. if(newVal.length > 0){
  397. newVal.forEach(item=>{
  398. this.historyData.push(item.d_h_t)
  399. })
  400. this.objItem = this.historyData[0]
  401. }else{
  402. this.objItem = {}
  403. }
  404. },
  405. deep:true,
  406. immediate:true,
  407. },
  408. },
  409. methods: {
  410. formartWorkSta(work_sta){
  411. return this.equipStateDict.work_sta_bzy2.value[work_sta]
  412. },
  413. async getStatic(){
  414. const res = await this.$myRequest({
  415. url: '/api/api_gateway?method=forecast.bzy.device_status',
  416. method: 'POST',
  417. data: {
  418. device_type_id: this.deviceInfo.type,
  419. id: this.deviceInfo.d_id,
  420. },
  421. });
  422. this.staticObj = res
  423. },
  424. async openPhoto(){
  425. const res = await this.$myRequest({
  426. url: '/api/api_gateway?method=forecast.send_control.admin_device_control',
  427. method: 'POST',
  428. data: {
  429. device_type_id: this.deviceInfo.type,
  430. d_id: this.deviceInfo.d_id,
  431. cmd: 'takephoto',
  432. },
  433. });
  434. if(res){
  435. this.$u.toast('拍照成功')
  436. }
  437. },
  438. editData(){
  439. uni.navigateTo({
  440. url: '/pages/afterSale/addafter?d_id=' + this.deviceInfo.d_id +'&device_id='+this.deviceInfo.id + '&device_type=' + this.deviceInfo.type,
  441. })
  442. },
  443. prevPage(){
  444. this.$emit('prevPage', this.currentPage)
  445. },
  446. nextPage(){
  447. this.$emit('nextPage', this.currentPage)
  448. },
  449. changeSim(){
  450. this.show1 = false
  451. },
  452. changeSimHandler(){
  453. if(this.data_iccidInput){
  454. this.changeSimAction(this.data_iccidInput);
  455. }else{
  456. this.$u.toast('请输入ICCID')
  457. }
  458. },
  459. switchTab(index){
  460. this.activeTab = index
  461. },
  462. simData(){
  463. this.show = true
  464. },
  465. async changeSimAction(simid){
  466. const res = await this.$myRequest({
  467. url: '/api/api_gateway?method=forecast.send_control.device_sim_update',
  468. method: 'POST',
  469. data: {
  470. d_id: this.deviceInfo.d_id,
  471. simid,
  472. },
  473. });
  474. if(res){
  475. this.$u.toast('更换成功')
  476. this.show1 = true
  477. this.data_iccidInput = ''
  478. this.getSimNew()
  479. }
  480. },
  481. async getSimNew(){
  482. const res = await this.$myRequest({
  483. url: '/api/api_gateway?method=forecast.send_control.device_sim_new',
  484. method: 'POST',
  485. data: {
  486. d_id: this.deviceInfo.d_id,
  487. },
  488. });
  489. if(res){
  490. const data_iccid = res.data_iccid
  491. this.data_iccid = data_iccid
  492. this.photo_iccid = res.photo_iccid
  493. if(data_iccid){
  494. this.getQueryNew(data_iccid);
  495. }else{
  496. this.activeTab = 1
  497. }
  498. }
  499. },
  500. async getQueryNew(data_iccid){
  501. const res = await this.$myRequest({
  502. url: '/api/api_gateway?method=forecast.send_control.sim_query_new',
  503. method: 'POST',
  504. data: {
  505. iccid: data_iccid,
  506. },
  507. });
  508. this.deviceInfoStatic = res
  509. },
  510. async refreshData(){
  511. const res = await this.$myRequest({
  512. url: '/api/api_gateway?method=forecast.send_control.get_device_config',
  513. method: 'POST',
  514. data: {
  515. device_type_id: this.deviceInfo.type,
  516. d_id: this.deviceInfo.d_id,
  517. control_type:'data',
  518. device_model: this.deviceInfo.device_model || 0,
  519. },
  520. });
  521. if(res){
  522. // 弹出提示
  523. this.$u.toast('刷新成功')
  524. }
  525. },
  526. getStateDict(item){
  527. return item.ws == 0 ? '待机' : this.equipStateDict.lamp.value[item.lamp]
  528. },
  529. getRpsDict(item){
  530. return this.equipStateDict.rps.value[item.rps]
  531. },
  532. getTpsDict(item){
  533. return this.equipStateDict.tps.value[item.tps]
  534. },
  535. getLpsDict(item){
  536. return this.equipStateDict.lps.value[item.lps]
  537. },
  538. openSettings(){
  539. uni.navigateTo({
  540. url: '/pages/bzy/deviceControl?deviceId=' + this.deviceInfo.id + '&d_id=' + this.deviceInfo.d_id,
  541. });
  542. },
  543. initChart() {
  544. this.$nextTick(() => {
  545. this.updateChartsData();
  546. });
  547. },
  548. // 格式化时间
  549. formatDate(dateString) {
  550. const date = new Date(dateString*1000);
  551. const year = date.getFullYear();
  552. const month = String(date.getMonth() + 1).padStart(2, '0');
  553. const day = String(date.getDate()).padStart(2, '0');
  554. return `${year}-${month}-${day}`;
  555. },
  556. formatTime(dateString) {
  557. const date = new Date(dateString*1000);
  558. const year = date.getFullYear();
  559. const month = String(date.getMonth() + 1).padStart(2, '0');
  560. const day = String(date.getDate()).padStart(2, '0');
  561. const hour = String(date.getHours()).padStart(2, '0');
  562. const minute = String(date.getMinutes()).padStart(2, '0');
  563. const second = String(date.getSeconds()).padStart(2, '0');
  564. return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
  565. },
  566. drawChart() {
  567. // 销毁已有的图表实例
  568. if (chartInstance) {
  569. chartInstance.dispose();
  570. }
  571. // 初始化图表
  572. const chartDom = document.getElementById('tempChart');
  573. if (!chartDom) return;
  574. // chartInstance = echarts.init(chartDom);
  575. if(!chartInstance){
  576. return
  577. }
  578. const option = {
  579. backgroundColor: '#FFFFFF',
  580. tooltip: {
  581. trigger: 'axis'
  582. },
  583. legend: {
  584. show: false
  585. },
  586. grid: {
  587. left: '3%',
  588. right: '4%',
  589. bottom: '3%',
  590. top: '8%',
  591. containLabel: true
  592. },
  593. xAxis: {
  594. type: 'category',
  595. boundaryGap: false,
  596. data: this.xData,
  597. axisLine: {
  598. lineStyle: {
  599. color: '#CCCCCC'
  600. }
  601. },
  602. axisLabel: {
  603. fontSize: 10,
  604. color: '#999999'
  605. },
  606. splitLine: {
  607. show: false
  608. }
  609. },
  610. yAxis: {
  611. type: 'value',
  612. splitNumber: 4,
  613. axisLine: {
  614. show: true,
  615. lineStyle: {
  616. color: '#CCCCCC'
  617. }
  618. },
  619. axisLabel: {
  620. fontSize: 10,
  621. color: '#999999'
  622. },
  623. splitLine: {
  624. show: true,
  625. lineStyle: {
  626. color: '#E5E5E5',
  627. type: 'dashed'
  628. }
  629. }
  630. },
  631. series: [
  632. {
  633. name: this.chartTabs[this.activeChartTab].name,
  634. type: 'line',
  635. smooth: true,
  636. data: this.yData,
  637. lineStyle: {
  638. color: '#0bbc58',
  639. width: 2
  640. },
  641. itemStyle: {
  642. color: '#0bbc58',
  643. borderColor: '#0bbc58',
  644. borderWidth: 2
  645. },
  646. symbol: 'circle',
  647. symbolSize: 6,
  648. // areaStyle: {
  649. // color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
  650. // {
  651. // offset: 0,
  652. // color: 'rgba(11, 188, 88, 0.3)'
  653. // },
  654. // {
  655. // offset: 1,
  656. // color: 'rgba(11, 188, 88, 0)'
  657. // }
  658. // ])
  659. // }
  660. }
  661. ]
  662. };
  663. chartInstance.setOption(option);
  664. // 监听窗口大小变化,调整图表大小
  665. window.addEventListener('resize', () => {
  666. chartInstance.resize();
  667. });
  668. },
  669. switchChartTab(index) {
  670. this.activeChartTab = index;
  671. // 根据不同标签切换数据
  672. if (this.chartTabs[index].id === 'new_tem') {
  673. this.yData = this.polylineList.map(item => Number(item.new_tem) || 0);
  674. } else if (this.chartTabs[index].id === 'new_hum') {
  675. this.yData = this.polylineList.map(item => Number(item.new_hum) || 0);
  676. } else if (this.chartTabs[index].id === 'others') {
  677. this.yData = this.polylineList.map(item => Number(item.others) || 0);
  678. }
  679. this.$nextTick(() => {
  680. // this.drawChart();
  681. this.updateChartsData();
  682. });
  683. },
  684. updateChartsData(){
  685. const categories = this.xData.length ? this.xData : [];
  686. const lineData = {
  687. categories,
  688. series: [
  689. {
  690. name: this.chartTabs[this.activeChartTab].name,
  691. data: this.yData
  692. }
  693. ]
  694. };
  695. this.chartData = lineData;
  696. }
  697. }
  698. };
  699. </script>
  700. <style lang="scss" scoped>
  701. .device-data-container {
  702. width: 100%;
  703. }
  704. .device-data-wraper{
  705. padding: 16rpx;
  706. background: #ffffff;
  707. border-radius: 16rpx;
  708. }
  709. /* 顶部时间戳和图标区域 */
  710. .top-bar {
  711. display: flex;
  712. justify-content: space-between;
  713. align-items: center;
  714. padding: 16rpx 0;
  715. .timestamp {
  716. font-size: 28rpx;
  717. font-family: 'Source Han Sans CN VF', sans-serif;
  718. font-weight: 400;
  719. color: #999999;
  720. }
  721. .icon-group {
  722. display: flex;
  723. gap: 10rpx;
  724. .icon-item {
  725. width: 56rpx;
  726. height: 56rpx;
  727. display: flex;
  728. align-items: center;
  729. justify-content: center;
  730. border-radius: 50%;
  731. .iconfont {
  732. font-size: 36rpx;
  733. }
  734. &.refresh-icon .iconfont {
  735. color: #0bbc58;
  736. }
  737. &.settings-icon .iconfont {
  738. color: #999999;
  739. }
  740. &.info-icon .iconfont {
  741. color: #999999;
  742. }
  743. }
  744. }
  745. }
  746. .sim-container{
  747. padding: 0 20rpx;
  748. .sim-item{
  749. margin-bottom: 20rpx;
  750. .sim-value-change{
  751. display: flex;
  752. align-items: center;
  753. .sim-value-button{
  754. padding: 8rpx 16rpx;
  755. border-radius: 8rpx;
  756. font-size: 24rpx;
  757. font-family: 'Source Han Sans CN VF', sans-serif;
  758. font-weight: 400;
  759. color: #042118;
  760. background: #0bbc58;
  761. color: #ffffff;
  762. margin-left: 20rpx;
  763. }
  764. }
  765. }
  766. }
  767. .general-border{
  768. width: 100rpx;
  769. height: 100rpx;
  770. border-radius: 50%;
  771. display: flex;
  772. align-items: center;
  773. justify-content: center;
  774. transition: all 0.3s ease;
  775. background: #ffffff;
  776. box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  777. .general-icon{
  778. width: 50rpx;
  779. height: 50rpx;
  780. border-radius: 50%;
  781. }
  782. }
  783. .status-icon{
  784. width: 12rpx;
  785. height: 12rpx;
  786. border-radius: 50%;
  787. background: #B6BECA;
  788. margin-right: 8rpx;
  789. }
  790. .success-icon{
  791. background: #0BBC58;
  792. }
  793. .warning-icon{
  794. background: #F8B610;
  795. }
  796. .error-icon{
  797. background: #FF6B6B;
  798. }
  799. .general-images{
  800. display: flex;
  801. align-items: center;
  802. justify-content: center;
  803. position: relative;
  804. .general-image{
  805. position: absolute;
  806. width: 80rpx;
  807. height: 80rpx;
  808. border-radius: 50%;
  809. background: #ffffff;
  810. box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  811. display:flex;
  812. align-items: center;
  813. justify-content: center;
  814. .image-icon{
  815. width: 45rpx;
  816. height: 45rpx;
  817. }
  818. }
  819. .edit-border{
  820. top: 45rpx;
  821. left: -110rpx;
  822. }
  823. .sim-border{
  824. top: -125rpx;
  825. left: -110rpx;
  826. }
  827. .setting-border{
  828. top: -40rpx;
  829. left: -180rpx;
  830. }
  831. .edit-border2{
  832. top: 60rpx;
  833. left: -70rpx;
  834. }
  835. .sim-border2{
  836. top: -75rpx;
  837. left: -150rpx;
  838. }
  839. .setting-border2{
  840. top: 20rpx;
  841. left: -150rpx;
  842. }
  843. .photo-border{
  844. top: -135rpx;
  845. left: -70rpx;
  846. }
  847. }
  848. /* 主要数据面板 */
  849. .main-data-panel {
  850. display: flex;
  851. background: #FFFFFF;
  852. border-radius: 16rpx;
  853. margin-bottom: 16rpx;
  854. .data-column-left {
  855. flex: 1;
  856. gap: 24rpx;
  857. .data-item {
  858. display: flex;
  859. flex: 1;
  860. flex-direction: column;
  861. gap: 8rpx;
  862. margin-bottom: 16rpx;
  863. .data-label {
  864. font-size: 24rpx;
  865. font-family: 'Source Han Sans CN VF', sans-serif;
  866. font-weight: 400;
  867. color: #999999;
  868. }
  869. .data-value {
  870. font-size: 24rpx;
  871. font-family: 'Source Han Sans CN VF', sans-serif;
  872. font-weight: 700;
  873. color: #042118;
  874. &.temp-value {
  875. color: #FF6B6B;
  876. }
  877. }
  878. .data-unit {
  879. font-size: 24rpx;
  880. font-family: 'Source Han Sans CN VF', sans-serif;
  881. font-weight: 400;
  882. color: #999999;
  883. }
  884. }
  885. }
  886. .data-column-right {
  887. flex: 1;
  888. display: flex;
  889. flex-direction: column;
  890. align-items: center;
  891. gap: 16rpx;
  892. .device-image-container {
  893. position: relative;
  894. width: 200rpx;
  895. height: 200rpx;
  896. display: flex;
  897. align-items: center;
  898. justify-content: center;
  899. .device-glow {
  900. position: absolute;
  901. width: 180rpx;
  902. height: 180rpx;
  903. background: radial-gradient(circle, rgba(11, 188, 88, 0.3) 0%, rgba(11, 188, 88, 0) 70%);
  904. border-radius: 50%;
  905. }
  906. .device-image {
  907. position: relative;
  908. width: 200rpx;
  909. height: 280rpx;
  910. z-index: 1;
  911. }
  912. }
  913. .device-info {
  914. display: flex;
  915. flex-direction: column;
  916. gap: 12rpx;
  917. width: 100%;
  918. .info-row {
  919. display: flex;
  920. justify-content: space-between;
  921. align-items: center;
  922. .info-label {
  923. font-size: 24rpx;
  924. font-family: 'Source Han Sans CN VF', sans-serif;
  925. font-weight: 400;
  926. color: #999999;
  927. }
  928. .info-value {
  929. font-size: 36rpx;
  930. font-family: 'Source Han Sans CN VF', sans-serif;
  931. font-weight: 700;
  932. color: #042118;
  933. }
  934. .info-version {
  935. font-size: 24rpx;
  936. font-family: 'Source Han Sans CN VF', sans-serif;
  937. font-weight: 400;
  938. color: #999999;
  939. }
  940. .info-label-small {
  941. font-size: 24rpx;
  942. font-family: 'Source Han Sans CN VF', sans-serif;
  943. font-weight: 400;
  944. color: #999999;
  945. }
  946. }
  947. }
  948. }
  949. }
  950. .tabs-container{
  951. display:flex;
  952. align-items: center;
  953. gap: 24rpx;
  954. padding: 24rpx;
  955. .tabs-item{
  956. font-size: 28rpx;
  957. font-family: 'Source Han Sans CN VF', sans-serif;
  958. font-weight: 500;
  959. color: #999999;
  960. }
  961. .tabs-item.active{
  962. color: #0bbc58;
  963. }
  964. }
  965. //滚动条隐藏
  966. ::-webkit-scrollbar {
  967. display: none;
  968. }
  969. /* 图表区域 */
  970. .chart-section {
  971. background: #FFFFFF;
  972. border-radius: 16rpx;
  973. padding: 24rpx 32rpx 32rpx;
  974. margin: 24rpx 0;
  975. .chart-header {
  976. display: flex;
  977. justify-content: space-between;
  978. align-items: center;
  979. margin-bottom: 24rpx;
  980. .chart-tabs {
  981. display: flex;
  982. gap: 48rpx;
  983. overflow-x: auto;
  984. white-space: nowrap;
  985. .chart-tab-item {
  986. font-size: 28rpx;
  987. font-family: 'Source Han Sans CN VF', sans-serif;
  988. font-weight: 500;
  989. color: #999999;
  990. position: relative;
  991. padding-bottom: 8rpx;
  992. transition: all 0.3s;
  993. &.active {
  994. color: #0bbc58;
  995. font-weight: 700;
  996. &::after {
  997. content: '';
  998. position: absolute;
  999. bottom: 0;
  1000. left: 0;
  1001. right: 0;
  1002. height: 4rpx;
  1003. background: #0bbc58;
  1004. border-radius: 2rpx;
  1005. }
  1006. }
  1007. }
  1008. }
  1009. .chart-dropdown {
  1010. .dropdown-icon {
  1011. font-size: 28rpx;
  1012. color: #999999;
  1013. }
  1014. }
  1015. }
  1016. .chart-content {
  1017. .chart-legend {
  1018. display: flex;
  1019. justify-content: space-between;
  1020. align-items: center;
  1021. margin-bottom: 16rpx;
  1022. .legend-item {
  1023. display: flex;
  1024. align-items: center;
  1025. gap: 8rpx;
  1026. .legend-dot {
  1027. width: 16rpx;
  1028. height: 16rpx;
  1029. background: #0bbc58;
  1030. border-radius: 50%;
  1031. }
  1032. .legend-text {
  1033. font-size: 24rpx;
  1034. font-family: 'Source Han Sans CN VF', sans-serif;
  1035. font-weight: 400;
  1036. color: #042118;
  1037. }
  1038. }
  1039. .legend-average {
  1040. .average-text {
  1041. font-size: 24rpx;
  1042. font-family: 'Source Han Sans CN VF', sans-serif;
  1043. font-weight: 400;
  1044. color: #999999;
  1045. }
  1046. }
  1047. }
  1048. .chart-canvas-container {
  1049. width: 100%;
  1050. height: 400rpx;
  1051. .chart-canvas {
  1052. width: 100%;
  1053. height: 100%;
  1054. }
  1055. }
  1056. }
  1057. }
  1058. .copy-icon{
  1059. width: 40rpx;
  1060. height: 40rpx;
  1061. }
  1062. /* 历史数据表格 */
  1063. .history-section {
  1064. background: #FFFFFF;
  1065. border-radius: 16rpx;
  1066. padding: 24rpx 32rpx 32rpx;
  1067. .history-header {
  1068. margin-bottom: 24rpx;
  1069. .history-title {
  1070. font-size: 32rpx;
  1071. font-family: 'Source Han Sans CN VF', sans-serif;
  1072. font-weight: 700;
  1073. color: #042118;
  1074. }
  1075. }
  1076. .history-table {
  1077. margin-bottom: 24rpx;
  1078. .table-container {
  1079. display: flex;
  1080. position: relative;
  1081. /* 固定列 */
  1082. .fixed-column {
  1083. width: 240rpx;
  1084. position: relative;
  1085. z-index: 2;
  1086. background: #FFFFFF;
  1087. .fixed-header {
  1088. border-bottom: 2rpx solid #F0F0F0;
  1089. padding: 16rpx 0;
  1090. .header-cell.fixed {
  1091. font-size: 24rpx;
  1092. font-family: 'Source Han Sans CN VF', sans-serif;
  1093. font-weight: 500;
  1094. color: #666666;
  1095. text-align: left;
  1096. padding-left: 0;
  1097. }
  1098. }
  1099. .fixed-body {
  1100. .fixed-row {
  1101. padding: 16rpx 0;
  1102. border-bottom: 1rpx solid #F5F5F5;
  1103. height: 80rpx;
  1104. display: flex;
  1105. align-items: center;
  1106. .body-cell.fixed {
  1107. font-size: 24rpx;
  1108. font-family: 'Source Han Sans CN VF', sans-serif;
  1109. font-weight: 400;
  1110. color: #042118;
  1111. text-align: left;
  1112. padding-left: 0;
  1113. }
  1114. }
  1115. }
  1116. }
  1117. /* 可滑动列 */
  1118. .scrollable-column {
  1119. flex: 1;
  1120. overflow-x: auto;
  1121. white-space: nowrap;
  1122. /* 隐藏滚动条 */
  1123. &::-webkit-scrollbar {
  1124. display: none;
  1125. }
  1126. scrollbar-width: none;
  1127. .scrollable-header {
  1128. display: flex;
  1129. border-bottom: 2rpx solid #F0F0F0;
  1130. padding: 20rpx 0;
  1131. .header-cell {
  1132. min-width: 140rpx;
  1133. font-size: 24rpx;
  1134. font-family: 'Source Han Sans CN VF', sans-serif;
  1135. font-weight: 500;
  1136. color: #666666;
  1137. text-align: center;
  1138. // 超出隐藏
  1139. overflow: hidden;
  1140. text-overflow: ellipsis;
  1141. white-space: nowrap;
  1142. margin-right: 10rpx;
  1143. }
  1144. }
  1145. .scrollable-body {
  1146. .scrollable-row {
  1147. display: flex;
  1148. padding: 16rpx 0;
  1149. border-bottom: 1rpx solid #F5F5F5;
  1150. height: 80rpx;
  1151. align-items: center;
  1152. .body-cell {
  1153. min-width: 140rpx;
  1154. font-size: 24rpx;
  1155. font-family: 'Source Han Sans CN VF', sans-serif;
  1156. font-weight: 400;
  1157. color: #042118;
  1158. text-align: center;
  1159. margin-right: 10rpx;
  1160. display:flex;
  1161. align-items: center;
  1162. justify-content: center;
  1163. }
  1164. }
  1165. }
  1166. }
  1167. }
  1168. }
  1169. .pagination {
  1170. display: flex;
  1171. justify-content: space-between;
  1172. align-items: center;
  1173. .pagination-btn {
  1174. font-size: 24rpx;
  1175. font-family: 'Source Han Sans CN VF', sans-serif;
  1176. font-weight: 400;
  1177. padding: 6rpx 18rpx;
  1178. border-radius: 8rpx;
  1179. transition: all 0.3s;
  1180. &.prev-btn {
  1181. color: #656565;
  1182. border: 1px solid #E4E7ED;
  1183. }
  1184. &.next-btn {
  1185. color: #0bbc58;
  1186. border: 1px solid #0bbc58;
  1187. }
  1188. }
  1189. .pagination-info {
  1190. font-size: 28rpx;
  1191. font-family: 'Source Han Sans CN VF', sans-serif;
  1192. font-weight: 400;
  1193. color: #999999;
  1194. }
  1195. }
  1196. }
  1197. </style>