deviceDetail.vue 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584
  1. <template>
  2. <!-- 设备详情 -->
  3. <view class="content">
  4. <view class="name_box">
  5. <view class="name_content">{{ devInfo.devName }}</view>
  6. </view>
  7. <view class="radar-box">
  8. <view
  9. :style="{
  10. width: `${length / 200}px`,
  11. height: `${width / 200}px`,
  12. position: 'relative',
  13. transform: `scale(${zoomTimes})`,
  14. border: `${9 / zoomTimes}rpx solid #333333`,
  15. }"
  16. :class="[
  17. width < 25500 && length < 25000 ? 'tranStyle' : 'center',
  18. ]"
  19. >
  20. <template v-if="modules.length > 0">
  21. <view
  22. v-for="(item, index) in modules"
  23. :key="index"
  24. class="moduleContent"
  25. >
  26. <view
  27. :class="item.type"
  28. :style="{
  29. width: `${item.width / 2}px`,
  30. height: `${item.length / 2}px`,
  31. top: `${item.top / 2}px`,
  32. left: `${item.left / 2}px`,
  33. transform: `rotate(${item.rotate}deg)`,
  34. 'transform-origin': 'center center',
  35. zIndex: 4444,
  36. }"
  37. >
  38. <image
  39. style="
  40. width: 100%;
  41. height: 100%;
  42. display: block;
  43. "
  44. :src="`../../static/furnitures/${item.type}.png`"
  45. mode=""
  46. />
  47. </view>
  48. </view>
  49. </template>
  50. <template>
  51. <view v-for="item in targetPoints" :key="item.id">
  52. <image
  53. class="action-icon-M"
  54. :style="{
  55. position: 'absolute',
  56. transform: `translate3d(${
  57. item.displayX / 2
  58. }px, ${
  59. -item.displayY / 2
  60. }px, 0) translate(-50%, -50%) scale(${
  61. 1.5 / zoomTimes
  62. })`,
  63. zIndex: 9999,
  64. transition: 'transform 1s linear',
  65. willChange: 'transform',
  66. }"
  67. :src="`../../static/${lnbAction}.png`"
  68. mode=""
  69. />
  70. </view>
  71. </template>
  72. </view>
  73. <!-- <image
  74. class="redar-pic"
  75. src="../../static/rander.png"
  76. mode=""
  77. :style="{
  78. transform:
  79. 'translate(' +
  80. -xOffset / 100 +
  81. 'rpx,' +
  82. -yOffset / 100 +
  83. 'rpx)',
  84. }"
  85. /> -->
  86. </view>
  87. <view class="switchBox">
  88. <text class="name">呼吸灯</text>
  89. <switch
  90. :checked="statusLight == 1"
  91. @change="handleLightChange"
  92. :active-value="1"
  93. :inactive-value="0"
  94. size="24px"
  95. active-color="#07c160"
  96. inactive-color="#eeeff1"
  97. style="transform: scale(0.8)"
  98. />
  99. </view>
  100. <view class="notice-info">
  101. <view class="notice_title" @click="discrepancy">
  102. <text style="color: #95a4b3; font-size: 28rpx">{{
  103. todayDate
  104. }}</text>
  105. <image src="../../static/rightArrow.png"></image>
  106. </view>
  107. <view
  108. @click="getFrenEcharts()"
  109. class="title"
  110. v-if="
  111. devInfo.installPosition == 'Bedroom' ||
  112. (breathRate !== 0 && breathRate !== '')
  113. "
  114. >
  115. <view
  116. class="title-text"
  117. style="color: #111111; font-size: 30rpx"
  118. >当前呼吸率</view
  119. >
  120. <view
  121. style="
  122. display: flex;
  123. justify-content: center;
  124. align-items: center;
  125. "
  126. >
  127. <view
  128. class="title-btn"
  129. style="color: #111111; font-size: 30rpx"
  130. v-if="breathRate !== ''"
  131. >
  132. {{ breathRate }}次/分钟</view
  133. >
  134. <view
  135. v-else
  136. class="title-btn"
  137. style="color: #111111; font-size: 30rpx"
  138. >
  139. 暂无
  140. </view>
  141. <image
  142. src="../../static/rightArrow.png"
  143. style="margin-left: auto; width: 30rpx; height: 30rpx"
  144. ></image>
  145. </view>
  146. </view>
  147. <view class="title">
  148. <view class="title-text" style="color: #22dea7"
  149. >今日进出频次</view
  150. >
  151. <view class="title-btn" style="color: #22dea7">
  152. {{ freQuenceList.length || 0 }}次
  153. </view>
  154. </view>
  155. <view class="stayDetail" v-if="freQuenceList.length > 0">
  156. <!-- 只有一条时直接展示 -->
  157. <view v-if="freQuenceList.length === 1">
  158. <view
  159. v-for="item in freQuenceList"
  160. :key="item.id"
  161. class="stayDetail-item"
  162. >
  163. <view class="stayDetail-text">
  164. {{ formatTime(item.enterTime) }}目标进入
  165. </view>
  166. <view class="stayDetail-btn">
  167. {{ formatTime(item.leaveTime) }}目标离开
  168. </view>
  169. </view>
  170. </view>
  171. <!-- 多条时用 swiper 展示 -->
  172. <swiper
  173. v-else
  174. class="auto-scroll-swiper"
  175. :indicator-dots="false"
  176. :autoplay="false"
  177. :interval="3000"
  178. :circular="true"
  179. :display-multiple-items="1"
  180. :vertical="false"
  181. :current="currentIndex"
  182. @change="onSwiperChange"
  183. >
  184. <swiper-item
  185. v-for="item in freQuenceList"
  186. :key="item.id"
  187. class="stayDetail-item"
  188. >
  189. <view class="stayDetail-text">
  190. {{ formatTime(item.enterTime) }}目标进入
  191. </view>
  192. <view class="stayDetail-btn">
  193. {{ formatTime(item.leaveTime) }}目标离开
  194. </view>
  195. </swiper-item>
  196. </swiper>
  197. </view>
  198. </view>
  199. <view class="box" v-if="!breathShow">
  200. <view class="handle-btn">
  201. <view class="btn1" @click="shareDevice" v-if="shareJudge"
  202. >分享</view
  203. >
  204. <view class="noShareFlag" @click="noShareFlag" v-else
  205. >分享</view
  206. >
  207. <view
  208. class="btn1"
  209. @click="gotoSetting"
  210. v-if="devInfo.online == 1"
  211. >设置</view
  212. >
  213. <view class="noShareFlag" @click="gotoSetting" v-else
  214. >设置</view
  215. >
  216. <view class="btn2" @click="healthAlarm">守护计划</view>
  217. </view>
  218. </view>
  219. <view class="box" v-else>
  220. <view class="handle-btn">
  221. <view class="closeBreath" @click="breathShow = false"
  222. >关闭呼吸率曲线图</view
  223. >
  224. <view class="btn2" @click="saveBreath"> 保存呼吸率曲线图 </view>
  225. </view>
  226. </view>
  227. <!-- 遮罩层 -->
  228. <view class="mask" v-if="shareModel" @click="shareModel = false"></view>
  229. <!-- 弹窗内容 -->
  230. <view class="share-modal" v-if="shareModel">
  231. <view class="modal-header">
  232. <text class="cancel-btn" @click="shareModel = false">取消</text>
  233. <text class="modal-title">分享</text>
  234. <text class="confirm-btn" @click="onShareConfirm()">确认</text>
  235. </view>
  236. <view class="modal-body">
  237. <view class="info-row">
  238. <view class="label">设备序列号:</view>
  239. <view class="value">{{ devInfo.clientId }}</view>
  240. </view>
  241. <view class="info-row">
  242. <view class="label">设备名称:</view>
  243. <view class="value">{{ devInfo.devName }}</view>
  244. </view>
  245. <view class="input-row phoneInfo">
  246. <view>被分享人</view>
  247. <input
  248. class="input"
  249. placeholder="请输入手机号"
  250. type="number"
  251. v-model="sharedPhone"
  252. />
  253. </view>
  254. <view class="funChoice">
  255. <view class="funItem" @click="smChange()">
  256. <label class="simple-radio">
  257. <radio
  258. :value="messageFlag"
  259. color="#7c5345"
  260. :checked="messageFlag == true"
  261. class="hide-original"
  262. />
  263. <text>短信权限</text>
  264. </label>
  265. </view>
  266. <view class="funItem">
  267. <label class="simple-radio" @click="snChange()">
  268. <radio
  269. :value="serviceNumberFlag"
  270. color="#7c5345"
  271. :checked="serviceNumberFlag == true"
  272. class="hide-original"
  273. />
  274. <text>服务号通知</text>
  275. </label>
  276. </view>
  277. <view class="funItem">
  278. <label class="simple-radio" @click="vfChange()">
  279. <radio
  280. :value="voipFlag"
  281. color="#7c5345"
  282. :checked="voipFlag == true"
  283. class="hide-original"
  284. />
  285. <text>语音通话</text>
  286. </label>
  287. </view>
  288. </view>
  289. </view>
  290. </view>
  291. <view class="modal-mask" v-if="choiceVisible" @click="closeChoice">
  292. <view class="modal-container">
  293. <view class="modal-header">
  294. <text class="title">请选择分享方式</text>
  295. </view>
  296. <view class="modal-buttons">
  297. <!-- 手机号分析按钮 -->
  298. <button class="btn phone-btn" @click="handlePhoneAnalysis">
  299. <text>手机号分享</text>
  300. </button>
  301. <!-- 链接分享按钮 -->
  302. <button class="btn link-btn" @click="handleLinkShare">
  303. <text>链接分享</text>
  304. </button>
  305. </view>
  306. </view>
  307. </view>
  308. <!-- ECharts图标模块 -->
  309. <view v-if="breathShow" class="echartsClass">
  310. <image
  311. src="../../static/closePng.png"
  312. class="closePng"
  313. @click="breathShow = false"
  314. ></image>
  315. <l-echart
  316. id="chart-canvas"
  317. canvas-id="chart-canvas"
  318. ref="chartRef"
  319. @finished="initChart"
  320. v-if="breathShow"
  321. @click="breathShow = false"
  322. ></l-echart>
  323. </view>
  324. <alarModel v-if="showModle" ref="alarmModel" />
  325. </view>
  326. </template>
  327. <script>
  328. import * as echarts from "../../uni_modules/lime-echart/static/echarts.min";
  329. import MqttService from "../../utils/globalMqtt.js";
  330. export default {
  331. data() {
  332. return {
  333. clientId: "",
  334. width: 0, //检测区域宽度
  335. length: 0, //检测区域长度
  336. xOffset: 0,
  337. yOffset: 0,
  338. devInfo: "",
  339. actionName: "",
  340. startDate: "",
  341. endDate: "",
  342. softWare: "",
  343. statusLight: 0,
  344. currentDate: new Date().getTime(),
  345. lnbAction: "",
  346. wsj: false,
  347. todayWcTimes: "",
  348. stayDetail: "",
  349. todayDate: "",
  350. dev_id: "",
  351. nowTime: "",
  352. devName: "",
  353. devType: "",
  354. localPhone: uni.getStorageSync("phone"),
  355. startArr: [],
  356. freQuenceList: [],
  357. // mqtt相关
  358. currentIndex: 0,
  359. modules: [],
  360. autoPlayinterval: "",
  361. choiceVisible: "",
  362. // 手机号分享授权模块
  363. shareModel: false,
  364. alarmModel: false,
  365. sharedPhone: "",
  366. messageFlag: false,
  367. serviceNumberFlag: false,
  368. voipFlag: false,
  369. // mqtt模块
  370. targetPoints: {},
  371. inactivityTimer: null,
  372. left: 0,
  373. top: 0,
  374. breathRate: "",
  375. breathShow: false,
  376. breathRpmList: [],
  377. option: {
  378. title: [
  379. {
  380. text: "呼吸率曲线",
  381. left: "center",
  382. top: 20,
  383. textStyle: { fontSize: 14 },
  384. },
  385. {
  386. text: "BPM: 0 次/分钟",
  387. left: "center",
  388. top: 50,
  389. textStyle: { fontSize: 12, color: "#805246" },
  390. },
  391. {
  392. text: "时间 (秒)",
  393. left: "right",
  394. top: "bottom",
  395. textStyle: { fontSize: 12, color: "#888888" },
  396. },
  397. ],
  398. grid: { left: 60, right: 20, top: 80, bottom: 50 },
  399. xAxis: {
  400. type: "category",
  401. data: Array.from({ length: 60 }, (_, i) => i + 1),
  402. boundaryGap: false,
  403. splitLine: { show: false },
  404. },
  405. yAxis: {
  406. type: "value",
  407. min: 0,
  408. max: 40,
  409. name: "呼吸率(次)",
  410. nameLocation: "end",
  411. nameGap: 20,
  412. nameTextStyle: { fontSize: 12, color: "#888888" },
  413. splitLine: { show: true },
  414. },
  415. series: [
  416. {
  417. type: "line",
  418. smooth: true,
  419. symbol: "none",
  420. lineStyle: { color: "#805246", width: 2 },
  421. data: Array(60).fill(0),
  422. },
  423. ],
  424. animation: true,
  425. animationDuration: 100,
  426. },
  427. index: 0,
  428. showModle: false,
  429. zoomTimes: 2,
  430. // 设备分享权限判断
  431. shareJudge: false,
  432. unsubscribeFn: null,
  433. fallingEventChange: null,
  434. lnbActionJudean: false,
  435. setIntervalVal: null,
  436. };
  437. },
  438. computed: {},
  439. methods: {
  440. getdevInfo(devId) {
  441. this.$http
  442. .get(`wap/device/queryDeviceInfoById`, {
  443. devId: devId,
  444. })
  445. .then((res) => {
  446. if (res.data.data) {
  447. this.devInfo = res.data.data;
  448. this.devType = this.devInfo.devType;
  449. this.width =
  450. Math.abs(
  451. this.devInfo.yyEnd - this.devInfo.yyStart
  452. ) * 100;
  453. this.length =
  454. Math.abs(
  455. this.devInfo.xxEnd - this.devInfo.xxStart
  456. ) * 100;
  457. this.xOffset =
  458. (this.devInfo.xxStart + this.devInfo.xxEnd) * 50;
  459. this.yOffset =
  460. -(this.devInfo.yyStart + this.devInfo.yyEnd) * 50;
  461. this.statusLight = this.devInfo.statusLight;
  462. this.calculate(this.width, this.length);
  463. // 设备分享权限判断
  464. this.shareJudge =
  465. this.devInfo.userId == uni.getStorageSync("userId")
  466. ? true
  467. : false;
  468. }
  469. })
  470. .catch((err) => {});
  471. },
  472. getdevRoomInfo(devId) {
  473. this.$http
  474. .get(`wap/room/readRoom`, {
  475. devId: devId,
  476. })
  477. .then((res) => {
  478. if (res.data.data) {
  479. this.modules = res.data.data.furnitures;
  480. }
  481. });
  482. },
  483. handleLightChange(e) {
  484. let newValue = e.detail.value == true ? 1 : 0;
  485. this.$http
  486. .post(`wap/device/statusLight`, {
  487. statusLight: newValue,
  488. devId: this.devInfo.devId,
  489. })
  490. .then((res) => {
  491. if (res.data.code == 200) {
  492. uni.showToast({
  493. title: "操作成功",
  494. icon: "success",
  495. });
  496. this.statusLight = newValue;
  497. } else {
  498. wx.showToast({
  499. title: res.data.message,
  500. icon: "none",
  501. });
  502. }
  503. });
  504. },
  505. // 分享功能模块
  506. shareDevice() {
  507. this.choiceVisible = true;
  508. },
  509. noShareFlag() {
  510. uni.showToast({
  511. title: "您没有分享权限",
  512. icon: "none",
  513. });
  514. },
  515. onShareConfirm() {
  516. if (!this.sharedPhone) {
  517. uni.showModal({
  518. content: "请填写手机号!",
  519. showCancel: false,
  520. });
  521. return;
  522. }
  523. let reg_tel =
  524. /^(13[0-9]|14[01456879]|15[0-3,5-9]|16[2567]|17[0-8]|18[0-9]|19[0-3,5-9])\d{8}$/;
  525. if (!reg_tel.test(this.sharedPhone)) {
  526. uni.showModal({
  527. content: "请填写正确手机号!",
  528. showCancel: false,
  529. });
  530. return;
  531. }
  532. let shareParam = {
  533. sharerUserId: uni.getStorageSync("userId"),
  534. devId: this.devInfo.devId,
  535. sharerPhone: uni.getStorageSync("phone"),
  536. sharedUserId: "",
  537. sharedPhone: this.sharedPhone,
  538. messageFlag: this.messageFlag == true ? 0 : 1,
  539. serviceNumberFlag: this.serviceNumberFlag == true ? 0 : 1,
  540. voipFlag: this.voipFlag == true ? 0 : 1,
  541. };
  542. this.$http
  543. .post("wap/share/deviceShare", shareParam, {
  544. header: {
  545. "Content-Type": "application/json;charset=UTF-8",
  546. },
  547. })
  548. .then((res) => {
  549. if (res.data.code == 200) {
  550. uni.showToast({
  551. title: "分享成功",
  552. icon: "success",
  553. duration: 1500,
  554. });
  555. } else {
  556. uni.showToast({
  557. title: res.data.message,
  558. icon: "none",
  559. duration: 1500,
  560. });
  561. }
  562. });
  563. this.shareModel = false;
  564. },
  565. closeChoice() {
  566. this.choiceVisible = false;
  567. },
  568. handlePhoneAnalysis() {
  569. this.choiceVisible = false;
  570. this.shareModel = true;
  571. },
  572. handleLinkShare() {
  573. this.choiceVisible = false;
  574. uni.navigateTo({
  575. url:
  576. "/pagesA/linkShare/linkShare?devInfo=" +
  577. JSON.stringify(this.devInfo),
  578. });
  579. },
  580. smChange() {
  581. this.messageFlag = !this.messageFlag;
  582. },
  583. snChange() {
  584. this.serviceNumberFlag = !this.serviceNumberFlag;
  585. },
  586. vfChange() {
  587. this.voipFlag = !this.voipFlag;
  588. },
  589. gotoSetting() {
  590. if (this.devInfo.online == 0) {
  591. uni.showToast({
  592. title: "离线设备不支持设置",
  593. icon: "none",
  594. });
  595. return;
  596. }
  597. uni.navigateTo({
  598. url:
  599. "/pagesA/deviceSetting/deviceSetting?devInfo=" +
  600. JSON.stringify(this.devInfo),
  601. });
  602. },
  603. getFrequency(devId) {
  604. let params = {
  605. devId: devId,
  606. createTimeStart: this.$time(new Date()),
  607. createTimeEnd: this.$time(new Date()),
  608. pageNo: 1,
  609. pageSize: 999,
  610. };
  611. this.$http
  612. .post(`wap/stats/inOutEventQuery`, params, {
  613. header: {
  614. "Content-Type": "application/json;charset=UTF-8",
  615. },
  616. })
  617. .then((res) => {
  618. if (res.data.code == 200) {
  619. if (res.data.data.rows.length > 0) {
  620. this.freQuenceList = res.data.data.rows;
  621. } else {
  622. this.freQuenceList = [];
  623. }
  624. } else {
  625. uni.showToast({
  626. title: res.data.message,
  627. icon: "none",
  628. });
  629. }
  630. });
  631. },
  632. // 健康闹钟方法
  633. healthAlarm() {
  634. uni.navigateTo({
  635. url:
  636. "/pagesA/healthAlarm/healthAlarm?devInfo=" +
  637. JSON.stringify(this.devInfo),
  638. });
  639. },
  640. onSwiperChange(event) {
  641. const current = event.detail.current;
  642. const totalItems = this.freQuenceList.length;
  643. if (current === totalItems - 1) {
  644. this.autoplay = false;
  645. }
  646. this.currentIndex = current;
  647. },
  648. autoSwipe() {
  649. if (this.freQuenceList && this.freQuenceList.length > 0) {
  650. this.setIntervalVal = setInterval(() => {
  651. let nextIndex = this.currentIndex + 1;
  652. if (nextIndex >= this.freQuenceList.length) {
  653. nextIndex = 0; // 循环到第一个项目
  654. }
  655. this.currentIndex = nextIndex;
  656. }, 3000); // 每3秒自动滚动一次
  657. }
  658. },
  659. discrepancy() {
  660. uni.navigateTo({
  661. url:
  662. "/pagesA/discrepancy/discrepancy?freQuenceList=" +
  663. JSON.stringify(this.freQuenceList),
  664. });
  665. },
  666. getCurrentDate() {
  667. const now = new Date();
  668. this.currentDate = `${now.getFullYear()}-${
  669. now.getMonth() + 1
  670. }月${now.getDate().toString().padStart(2, "0")}日`;
  671. },
  672. receptionChange(val) {
  673. this.targetPoints = val;
  674. },
  675. receptHealth(val) {
  676. this.breathRate = val;
  677. this.setEcharts(val);
  678. },
  679. // echarts图表模块
  680. getOption(list) {
  681. // 固定 X 轴 [0 ~ 60]
  682. const xData = Array.from({ length: 61 }, (_, i) => i);
  683. const recent = list.slice(-61);
  684. const data = new Array(61).fill(null);
  685. for (let i = 0; i < recent.length; i++) {
  686. data[i] = recent[i];
  687. }
  688. return {
  689. title: [
  690. { text: "呼吸率曲线", left: "center", top: 20 },
  691. {
  692. text: `BPM: ${recent[recent.length - 1] || 0} 次/分钟`,
  693. left: "center",
  694. top: 50,
  695. textStyle: { fontSize: 12, color: "#805246" },
  696. },
  697. {
  698. text: "时间 (秒)",
  699. left: "right",
  700. top: "bottom",
  701. textStyle: {
  702. fontSize: 10,
  703. color: "#888888",
  704. fontWeight: "normal",
  705. },
  706. },
  707. ],
  708. grid: { left: 60, right: 20, top: 80, bottom: 50 },
  709. xAxis: {
  710. type: "category",
  711. data: xData,
  712. boundaryGap: false,
  713. },
  714. yAxis: {
  715. type: "value",
  716. min: 0,
  717. max: 40,
  718. name: "呼吸率(次)",
  719. nameLocation: "end",
  720. nameGap: 20,
  721. nameTextStyle: {
  722. fontSize: 10,
  723. color: "#888888",
  724. fontWeight: "normal",
  725. },
  726. splitLine: { show: true },
  727. },
  728. series: [
  729. {
  730. type: "line",
  731. smooth: true,
  732. symbol: "none",
  733. data: data,
  734. showSymbol: false,
  735. lineStyle: { color: "#7a4e42", width: 2 },
  736. },
  737. ],
  738. };
  739. },
  740. getFrenEcharts() {
  741. if (this.breathRate === "" || this.breathRate === null) {
  742. uni.showToast({
  743. title: "暂无呼吸率",
  744. icon: "none",
  745. duration: 1500,
  746. });
  747. return;
  748. }
  749. this.breathShow = true;
  750. this.$nextTick(() => {
  751. this.initChart();
  752. });
  753. },
  754. initChart() {
  755. if (!this.$refs.chartRef) return;
  756. if (this.chartInstance) {
  757. this.chartInstance.dispose();
  758. this.chartInstance = null;
  759. }
  760. this.$refs.chartRef.init(echarts, (chart) => {
  761. this.chartInstance = chart;
  762. chart.setOption(this.option, true);
  763. });
  764. },
  765. setEcharts(val) {
  766. if (!Array.isArray(this.breathRpmList)) {
  767. this.breathRpmList = [];
  768. }
  769. this.breathRpmList.push(val);
  770. if (!this.chartInstance) return;
  771. const option = this.getOption(this.breathRpmList);
  772. this.chartInstance.setOption(option, { notMerge: true });
  773. },
  774. saveBreath() {
  775. const chart = this.$refs.chartRef;
  776. if (!chart) {
  777. uni.showToast({ title: "图表未渲染", icon: "none" });
  778. return;
  779. }
  780. chart.canvasToTempFilePath({
  781. success: (res) => {
  782. uni.saveImageToPhotosAlbum({
  783. filePath: res.tempFilePath,
  784. success: () => {
  785. uni.showToast({
  786. title: "保存成功",
  787. icon: "success",
  788. });
  789. },
  790. fail: (err) => {
  791. uni.showToast({
  792. title: "保存失败",
  793. icon: "none",
  794. });
  795. },
  796. });
  797. },
  798. fail: (err) => {
  799. uni.showToast({
  800. title: "导出失败",
  801. icon: "none",
  802. });
  803. },
  804. });
  805. },
  806. handleMessage(topic, message, clientId) {
  807. // 清除不活动定时器
  808. clearTimeout(this.inactivityTimer);
  809. this.inactivityTimer = setTimeout(() => {
  810. this.targetPoints = {};
  811. console.log("长时间没有点位消除");
  812. }, 1500);
  813. // 验证topic格式
  814. const match = topic.match(/^\/dev\/(.+)\/tracker_targets$/);
  815. if (!match || match[1] !== clientId) return;
  816. try {
  817. const data = JSON.parse(message.toString());
  818. if (data.health) {
  819. if (
  820. data.health.breath_rpm ||
  821. data.health.breath_rpm === 0
  822. ) {
  823. this.receptHealth(Math.floor(data.health.breath_rpm));
  824. } else {
  825. }
  826. }
  827. this.processTrackerData(data.tracker_targets);
  828. // console.log(data.tracker_targets, "MQTT消息解析成功22222");
  829. } catch (e) {
  830. console.error("MQTT消息解析失败", e);
  831. }
  832. },
  833. processTrackerData(arr) {
  834. if (Array.isArray(arr) && arr.length > 0 && Array.isArray(arr[0])) {
  835. this.targetPoints = {};
  836. const currentIds = new Set();
  837. const newTargetPoints = {};
  838. // 处理每个追踪目标
  839. arr.forEach((item) => {
  840. if (!Array.isArray(item) || item.length < 4) return;
  841. const [x, y, z, id] = item;
  842. currentIds.add(id.toString());
  843. // 处理新点或更新现有点
  844. if (!this.targetPoints[id]) {
  845. newTargetPoints[id] = this.createNewTargetPoint(
  846. x,
  847. y,
  848. z,
  849. id
  850. );
  851. } else {
  852. newTargetPoints[id] = this.updateExistingTargetPoint(
  853. this.targetPoints[id],
  854. x,
  855. y,
  856. z,
  857. 2
  858. );
  859. }
  860. });
  861. // 移除不存在的点
  862. Object.keys(this.targetPoints).forEach((id) => {
  863. if (!currentIds.has(id)) {
  864. delete this.targetPoints[id];
  865. }
  866. });
  867. // 更新目标点
  868. this.targetPoints = {
  869. ...this.targetPoints,
  870. ...newTargetPoints,
  871. };
  872. if (Array.isArray(this.targetPoints)) {
  873. this.targetPoints = this.targetPoints.filter(
  874. (item) => item !== null && item !== undefined
  875. );
  876. }
  877. }
  878. },
  879. createNewTargetPoint(x, y, z, id) {
  880. return {
  881. x,
  882. y,
  883. z,
  884. id,
  885. displayX: x - Number(this.devInfo.xxStart),
  886. displayY: y - Number(this.devInfo.yyEnd),
  887. lastX: x,
  888. lastY: y,
  889. };
  890. },
  891. updateExistingTargetPoint(existingPoint, x, y, z, THRESHOLD) {
  892. const dx = x - existingPoint.lastX;
  893. const dy = y - existingPoint.lastY;
  894. const distance = Math.sqrt(dx * dx + dy * dy);
  895. if (distance > THRESHOLD) {
  896. return {
  897. ...existingPoint,
  898. x,
  899. y,
  900. z,
  901. lastX: x,
  902. lastY: y,
  903. displayX: x - Number(this.devInfo.xxStart),
  904. displayY: y - Number(this.devInfo.yyEnd),
  905. };
  906. }
  907. return existingPoint;
  908. },
  909. calculate(width, length) {
  910. let zoomTime = width > length ? width : length;
  911. let practical = zoomTime / 100;
  912. this.zoomTimes = 710 / practical;
  913. this.normalizeZoomTimes(this.zoomTimes);
  914. this.zoomTimes = this.zoomTimes - 0.2;
  915. this.lnbActionJudean = true;
  916. this.lnbAction = "action8";
  917. },
  918. normalizeZoomTimes(num) {
  919. const intPart = Math.floor(num);
  920. const decimalPart = num - intPart;
  921. if (decimalPart >= 0.1 && decimalPart < 0.5) {
  922. return intPart;
  923. } else if (decimalPart >= 0.5 && decimalPart < 0.9) {
  924. return intPart + 0.5;
  925. }
  926. return num;
  927. },
  928. formatTime(time) {
  929. return time ? time.slice(11, 19) : "暂无";
  930. },
  931. initSubscriptions() {
  932. const topicList = [
  933. {
  934. topic: `/dev/${this.clientId}/tracker_targets`,
  935. key: "unsubscribeFn",
  936. callback: (message, msgTopic) => {
  937. const dataMatch = msgTopic.match(
  938. /^\/dev\/(.+)\/tracker_targets$/
  939. );
  940. const cmdMatch = msgTopic.match(
  941. /^\/mps\/wx_(.+)\/notice$/
  942. );
  943. if (dataMatch && dataMatch[1] === this.clientId) {
  944. this.handleMessage(
  945. msgTopic,
  946. message,
  947. this.clientId
  948. );
  949. } else if (cmdMatch) {
  950. this.$refs.alarmModel.hanOtherMessage(
  951. msgTopic,
  952. message
  953. );
  954. }
  955. },
  956. },
  957. {
  958. topic: `/dev/${this.clientId}/falling_event_change`,
  959. key: "fallingEventChange",
  960. callback: (message, msgTopic) => {
  961. const dataMatch = msgTopic.match(
  962. /^\/dev\/(.+)\/falling_event_change$/
  963. );
  964. if (dataMatch && dataMatch[1] === this.clientId) {
  965. const dataMessage = JSON.parse(message.toString());
  966. console.log(dataMessage, 888888);
  967. if (dataMessage.falling == 1) {
  968. this.falling = dataMessage.falling;
  969. this.lnbAction = "actionWarn";
  970. } else if (
  971. dataMessage.falling == 2 ||
  972. dataMessage.falling == 3
  973. ) {
  974. this.falling = dataMessage.falling;
  975. this.lnbAction = "actionSerious";
  976. } else {
  977. this.lnbAction = "action8";
  978. }
  979. }
  980. },
  981. },
  982. ];
  983. topicList.forEach((item) => {
  984. // 避免重复订阅
  985. if (this[item.key]) return;
  986. const subscribeFunc = () => {
  987. const unsubscribe = MqttService.subscribe(
  988. "DATA",
  989. item.topic,
  990. item.callback
  991. );
  992. if (unsubscribe) {
  993. this[item.key] = unsubscribe;
  994. console.log(`✅ 已成功订阅主题: ${item.topic}`);
  995. }
  996. };
  997. if (MqttService.dataConnected) {
  998. subscribeFunc();
  999. } else {
  1000. // MQTT 未连接,等待重连成功再订阅
  1001. const handler = () => {
  1002. subscribeFunc();
  1003. uni.$off("mqttData-ready", handler);
  1004. };
  1005. uni.$on("mqttData-ready", handler);
  1006. }
  1007. });
  1008. },
  1009. },
  1010. onLoad(options) {
  1011. const devItem = JSON.parse(options.devItem);
  1012. const { devId, clientId } = devItem;
  1013. // 缓存到页面实例
  1014. this.clientId = clientId;
  1015. uni.setStorageSync("currentDevItem", devItem);
  1016. this.getFrequency(devId);
  1017. this.getdevInfo(devId);
  1018. this.getdevRoomInfo(devId);
  1019. this.getCurrentDate();
  1020. this.todayDate = this.$time(new Date(), 2);
  1021. // 清理旧定时器
  1022. clearTimeout(this.autoPlayinterval);
  1023. this.autoPlayinterval = setTimeout(() => this.autoSwipe(), 3000);
  1024. if (MqttService.dataConnected && MqttService.dataClient) {
  1025. // 已连接,直接订阅
  1026. this.initSubscriptions();
  1027. }
  1028. },
  1029. onShow() {
  1030. this.showModle = true;
  1031. // MQTT 初始化
  1032. },
  1033. onUnload() {
  1034. // 清理定时器
  1035. clearTimeout(this.autoPlayinterval);
  1036. // 清理自动滑动定时器
  1037. clearInterval(this.setIntervalVal);
  1038. this.setIntervalVal = null;
  1039. this.autoPlayinterval = null;
  1040. // 取消订阅
  1041. ["unsubscribeFn", "fallingEventChange"].forEach((key) => {
  1042. if (this[key]) {
  1043. this[key]();
  1044. this[key] = null;
  1045. }
  1046. });
  1047. },
  1048. onHide() {
  1049. this.showModle = false;
  1050. },
  1051. };
  1052. </script>
  1053. <style lang="less" scoped>
  1054. .content {
  1055. height: 100vh;
  1056. background: linear-gradient(180deg, #faede2 0%, #f4f4f4 100%);
  1057. box-sizing: border-box;
  1058. padding-top: 20rpx;
  1059. .name_box {
  1060. display: flex;
  1061. justify-content: center;
  1062. align-items: center;
  1063. margin: 0 auto 37rpx 0;
  1064. .name_content {
  1065. display: inline-flex;
  1066. background: rgba(255, 255, 255, 0.65);
  1067. border-radius: 37rpx;
  1068. padding: 6rpx 18rpx;
  1069. }
  1070. }
  1071. .radar-box {
  1072. margin: 0 auto;
  1073. position: relative;
  1074. display: flex;
  1075. align-items: center;
  1076. justify-content: center;
  1077. width: 710rpx;
  1078. height: 710rpx;
  1079. background: #ffffff;
  1080. border-radius: 37.5rpx;
  1081. box-sizing: border-box;
  1082. .center {
  1083. position: absolute;
  1084. overflow: hidden;
  1085. border: none;
  1086. background-repeat: no-repeat;
  1087. background-position: center;
  1088. &::before {
  1089. content: "";
  1090. position: absolute;
  1091. inset: 0;
  1092. background: url("https://hflnxx.oss-cn-shanghai.aliyuncs.com/IMAGE/20250919/toilet_bg.png")
  1093. no-repeat center;
  1094. background-size: 100% 100%;
  1095. z-index: 0;
  1096. }
  1097. .moduleContent {
  1098. position: relative;
  1099. view {
  1100. position: absolute;
  1101. }
  1102. .module-img {
  1103. width: 100%;
  1104. height: 100%;
  1105. display: block;
  1106. }
  1107. }
  1108. }
  1109. .tranStyle {
  1110. position: absolute;
  1111. overflow: hidden;
  1112. background-color: #fff;
  1113. border: none;
  1114. // outline: 10rpx solid #333333;
  1115. // background-image: url("https://hflnxx.oss-cn-shanghai.aliyuncs.com/IMAGE/20250919/toilet_bg.png");
  1116. background-repeat: no-repeat;
  1117. background-position: center;
  1118. &::before {
  1119. content: "";
  1120. position: absolute;
  1121. inset: 0;
  1122. background: url("https://hflnxx.oss-cn-shanghai.aliyuncs.com/IMAGE/20250919/toilet_bg.png")
  1123. no-repeat center;
  1124. background-size: 100% 100%;
  1125. z-index: 0; // 在最底层
  1126. }
  1127. .moduleContent {
  1128. // overflow: hidden;
  1129. // position: relative;
  1130. view {
  1131. position: absolute;
  1132. }
  1133. .module-img {
  1134. width: 100%;
  1135. height: 100%;
  1136. display: block;
  1137. }
  1138. }
  1139. }
  1140. .action-icon-G {
  1141. position: absolute;
  1142. width: 100rpx;
  1143. height: 100rpx;
  1144. }
  1145. .action-icon-M {
  1146. // position: absolute;
  1147. width: 50rpx;
  1148. height: 50rpx;
  1149. }
  1150. .redar-pic {
  1151. position: absolute;
  1152. left: 50%;
  1153. top: 50%;
  1154. width: 40rpx;
  1155. height: 40rpx;
  1156. transform: translate(-50%, -50%); /* 先居中 */
  1157. }
  1158. }
  1159. .switchBox {
  1160. width: 710rpx;
  1161. height: 94rpx;
  1162. margin: 18rpx auto;
  1163. padding: 0 37rpx;
  1164. display: flex;
  1165. align-items: center;
  1166. background: #ffffff;
  1167. border-radius: 37rpx;
  1168. box-sizing: border-box;
  1169. .name {
  1170. color: #111111;
  1171. font-size: 32rpx;
  1172. }
  1173. }
  1174. .notice-info {
  1175. width: 710rpx;
  1176. max-height: 300rpx;
  1177. // overflow: hidden;
  1178. padding: 30rpx 37rpx 10rpx 37rpx;
  1179. background: #ffffff;
  1180. border-radius: 37rpx;
  1181. box-sizing: border-box;
  1182. margin: 0 auto;
  1183. .notice_title {
  1184. display: flex;
  1185. align-items: space-between;
  1186. image {
  1187. margin-left: auto;
  1188. width: 30rpx;
  1189. height: 30rpx;
  1190. }
  1191. }
  1192. .title {
  1193. margin-top: 10rpx;
  1194. margin-bottom: 10rpx;
  1195. font-family: PingFang SC, PingFang SC;
  1196. font-weight: 500;
  1197. font-size: 34rpx;
  1198. color: #1757dd;
  1199. display: flex;
  1200. align-items: center;
  1201. justify-content: space-between;
  1202. .title-text {
  1203. font-size: 28rpx;
  1204. font-weight: 500;
  1205. color: #1757dd;
  1206. }
  1207. .title-btn {
  1208. font-size: 28rpx;
  1209. font-weight: 500;
  1210. color: #5a5a5a;
  1211. }
  1212. }
  1213. .stayDetail {
  1214. height: 80rpx;
  1215. // overflow: hidden;
  1216. margin-top: 10rpx;
  1217. margin-bottom: 10rpx;
  1218. padding-left: 20rpx;
  1219. font-family: PingFang SC, PingFang SC;
  1220. font-weight: 500;
  1221. font-size: 34rpx;
  1222. .stayDetail-item {
  1223. display: flex;
  1224. padding-bottom: 5rpx !important;
  1225. padding-top: 10rpx !important;
  1226. box-sizing: border-box;
  1227. line-height: 60rpx;
  1228. .stayDetail-text {
  1229. width: 40%;
  1230. font-size: 28rpx;
  1231. height: 30rpx !important;
  1232. }
  1233. .stayDetail-btn {
  1234. padding-left: 30rpx;
  1235. font-size: 28rpx;
  1236. height: 30rpx !important;
  1237. }
  1238. }
  1239. }
  1240. }
  1241. .box {
  1242. position: fixed;
  1243. bottom: 0;
  1244. left: 0;
  1245. width: 100vw;
  1246. height: 168rpx;
  1247. padding: 0 37rpx;
  1248. background: #ffffff;
  1249. box-sizing: border-box;
  1250. .handle-btn {
  1251. margin-top: 40rpx;
  1252. display: flex;
  1253. align-items: center;
  1254. justify-content: space-between;
  1255. .btn1 {
  1256. display: flex;
  1257. align-items: center;
  1258. justify-content: center;
  1259. width: 155rpx;
  1260. height: 94rpx;
  1261. background: #ffebe4;
  1262. border-radius: 28rpx;
  1263. font-weight: 500;
  1264. color: #111111;
  1265. font-size: 32rpx;
  1266. text-align: center;
  1267. }
  1268. .noShareFlag {
  1269. display: flex;
  1270. align-items: center;
  1271. justify-content: center;
  1272. width: 155rpx;
  1273. height: 94rpx;
  1274. border-radius: 28rpx;
  1275. font-weight: 500;
  1276. font-size: 32rpx;
  1277. text-align: center;
  1278. background: #f0f0f0;
  1279. color: #999999;
  1280. opacity: 0.6;
  1281. }
  1282. .btn2 {
  1283. display: flex;
  1284. align-items: center;
  1285. justify-content: center;
  1286. width: 328rpx;
  1287. height: 94rpx;
  1288. background: linear-gradient(
  1289. 105.95deg,
  1290. #a27867 0%,
  1291. #74483d 100%
  1292. );
  1293. border-radius: 28rpx;
  1294. box-shadow: 0rpx 4.69rpx 18.75rpx rgba(72, 41, 29, 0.15),
  1295. 0rpx 9.38rpx 9.38rpx rgba(154, 132, 89, 0.2),
  1296. 0rpx -4.69rpx 28.13rpx 4.69rpx #a16647 inset;
  1297. font-family: MiSans;
  1298. font-weight: 500;
  1299. color: #ffffff;
  1300. font-size: 32rpx;
  1301. }
  1302. .closeBreath {
  1303. display: flex;
  1304. align-items: center;
  1305. justify-content: center;
  1306. width: 328rpx;
  1307. height: 94rpx;
  1308. background: #ffebe4;
  1309. border-radius: 28rpx;
  1310. font-weight: 500;
  1311. color: #111111;
  1312. font-size: 32rpx;
  1313. text-align: center;
  1314. }
  1315. }
  1316. }
  1317. /* 弹窗部分样式 */
  1318. .mask {
  1319. position: fixed;
  1320. left: 0;
  1321. top: 0;
  1322. right: 0;
  1323. bottom: 0;
  1324. background: rgba(0, 0, 0, 0.3);
  1325. z-index: 100;
  1326. }
  1327. .share-modal {
  1328. position: fixed;
  1329. left: 0;
  1330. right: 0;
  1331. bottom: 0;
  1332. background: #fff;
  1333. border-radius: 24rpx 24rpx 0 0;
  1334. z-index: 101;
  1335. height: 750rpx;
  1336. .modal-header {
  1337. display: flex;
  1338. justify-content: space-between;
  1339. align-items: center;
  1340. padding: 32rpx 32rpx 0 32rpx;
  1341. font-size: 30rpx;
  1342. .cancel-btn {
  1343. color: #888;
  1344. font-size: 32rpx;
  1345. }
  1346. .modal-title {
  1347. color: #111111;
  1348. font-size: 36rpx;
  1349. }
  1350. .confirm-btn {
  1351. color: #996e5f;
  1352. font-size: 32rpx;
  1353. }
  1354. }
  1355. .modal-body {
  1356. padding: 50rpx;
  1357. .info-row {
  1358. display: flex;
  1359. margin-bottom: 24rpx;
  1360. font-size: 28rpx;
  1361. padding: 0 20rpx;
  1362. .label {
  1363. color: #111111;
  1364. width: 200rpx;
  1365. font-size: 32rpx;
  1366. text-align-last: justify;
  1367. }
  1368. .value {
  1369. text-align: right;
  1370. width: 65%;
  1371. color: #111111;
  1372. font-size: 32rpx;
  1373. }
  1374. }
  1375. .input-row {
  1376. display: flex;
  1377. align-items: center;
  1378. margin-top: 32rpx;
  1379. border-radius: 40rpx;
  1380. background: #f7f7f7;
  1381. padding: 0 24rpx;
  1382. height: 80rpx;
  1383. .input {
  1384. text-align: right;
  1385. flex: 1;
  1386. border: none;
  1387. background: transparent;
  1388. font-size: 28rpx;
  1389. color: #333;
  1390. }
  1391. }
  1392. .funChoice {
  1393. .funItem {
  1394. .simple-radio {
  1395. display: flex;
  1396. align-items: center;
  1397. padding: 20rpx 0;
  1398. }
  1399. }
  1400. }
  1401. }
  1402. }
  1403. // 分享弹窗样式
  1404. .modal-mask {
  1405. position: fixed;
  1406. top: 0;
  1407. left: 0;
  1408. right: 0;
  1409. bottom: 0;
  1410. background: rgba(0, 0, 0, 0.5);
  1411. display: flex;
  1412. justify-content: center;
  1413. align-items: center;
  1414. z-index: 333;
  1415. .modal-container {
  1416. width: 80%;
  1417. background: #fff;
  1418. border-radius: 16rpx;
  1419. // overflow: hidden;
  1420. animation: fadeIn 0.3s;
  1421. .modal-header {
  1422. padding: 30rpx;
  1423. text-align: center;
  1424. border-bottom: 1rpx solid #f5f5f5;
  1425. .title {
  1426. font-size: 36rpx;
  1427. display: block;
  1428. margin-bottom: 10rpx;
  1429. }
  1430. }
  1431. .modal-buttons {
  1432. display: flex;
  1433. flex-direction: column;
  1434. padding: 20rpx;
  1435. .btn {
  1436. flex: 1;
  1437. height: 90rpx;
  1438. margin: 15rpx 0;
  1439. border-radius: 45rpx;
  1440. display: flex;
  1441. align-items: center;
  1442. justify-content: center;
  1443. font-size: 32rpx;
  1444. border: none;
  1445. background: none;
  1446. position: relative;
  1447. }
  1448. .btn-icon {
  1449. width: 40rpx;
  1450. height: 40rpx;
  1451. margin-right: 15rpx;
  1452. }
  1453. .phone-btn {
  1454. background: linear-gradient(
  1455. 105.95deg,
  1456. #ba978a 0%,
  1457. #a27867 100%
  1458. );
  1459. color: white;
  1460. }
  1461. .link-btn {
  1462. background: linear-gradient(
  1463. 105.95deg,
  1464. #a27867 0%,
  1465. #74483d 100%
  1466. );
  1467. color: white;
  1468. }
  1469. }
  1470. }
  1471. }
  1472. .echartsClass {
  1473. position: absolute;
  1474. width: 650rpx;
  1475. height: 600rpx;
  1476. top: -8%;
  1477. left: -37%;
  1478. transform: translate(50%, 50%);
  1479. background: #ffffff;
  1480. .closePng {
  1481. width: 40rpx;
  1482. height: 40rpx;
  1483. position: fixed;
  1484. top: 10rpx;
  1485. right: 10rpx;
  1486. z-index: 333;
  1487. }
  1488. }
  1489. }
  1490. </style>