deviceDetail.vue 55 KB

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