roomSetting.vue 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588
  1. <template>
  2. <view class="home-warpTwo">
  3. <view class="header_two">
  4. <view class="radar-box">
  5. <view
  6. :style="{
  7. width: `${rotatedRect.width}rpx`,
  8. height: `${rotatedRect.height}rpx`,
  9. position: 'absolute',
  10. top: `${rotatedRect.top - 8}rpx`,
  11. left: `${rotatedRect.left - 8}rpx`,
  12. border: `4px solid #333333`,
  13. }"
  14. class="tranStyle"
  15. >
  16. </view>
  17. <view
  18. catchtouchmove="true"
  19. v-for="(item, index) in modules"
  20. :key="index"
  21. class="moduleContent"
  22. >
  23. <view
  24. :class="item.type"
  25. :style="{
  26. width: `${item.width}rpx`,
  27. height: `${item.height}rpx`,
  28. top: `${item.top}rpx`,
  29. left: `${item.left}rpx`,
  30. transform: `rotate(${item.rotate}deg)`,
  31. 'transform-origin': 'center center',
  32. }"
  33. @touchmove.prevent="onTouchMove(index, $event)"
  34. @touchstart="onTouchStart(index, $event)"
  35. @touchend="onTouchEnd(index, $event)"
  36. >
  37. <image
  38. class="module-img"
  39. :src="`../../static/furnitures/${item.type}.png`"
  40. mode=""
  41. />
  42. </view>
  43. </view>
  44. <template>
  45. <image
  46. v-for="item in targetPoints"
  47. :key="item.id"
  48. class="action-icon-M"
  49. :src="`../../static/${lnbAction}.png`"
  50. :style="{
  51. position: 'absolute',
  52. transform: `translate(-50%, -50%) translate3d(${item.displayX}rpx, ${item.displayY}rpx, 0)`,
  53. transition: 'transform 1s linear',
  54. willChange: 'transform',
  55. zIndex: 9999,
  56. width: '50rpx',
  57. height: '50rpx',
  58. }"
  59. />
  60. </template>
  61. <image
  62. class="redar-pic"
  63. src="../../static/rander.png"
  64. mode=""
  65. />
  66. </view>
  67. <view class="airbody">
  68. <view class="header_top">
  69. <view class="airTitle">室内布置</view>
  70. <view class="addfnt" @click="showAddFt">
  71. <image src="../../static/addfnt.png" alt=""> </image
  72. ><view class="add_btn">添加</view></view
  73. >
  74. </view>
  75. <view class="module">
  76. <view class="device-bottom">
  77. <view v-if="modules.length == 0" class="no-data">
  78. 请先点击添加,添加家具
  79. </view>
  80. <view
  81. class="info-box"
  82. v-for="(item, index) in modules"
  83. :key="index"
  84. >
  85. <image
  86. :src="`../../static/furnitures/${item.type}.png`"
  87. mode=""
  88. />
  89. <view class="info-text">
  90. <text>{{ item.name }}</text>
  91. <text
  92. >({{ item.width }}*{{
  93. item.height
  94. }})cm</text
  95. >
  96. </view>
  97. <view class="edit_del">
  98. <image
  99. @click="deleteItem(index)"
  100. src="../../static/delete.png"
  101. alt=""
  102. ></image>
  103. <image
  104. @click="editItem(index)"
  105. style="margin-left: 10rpx"
  106. src="../../static/edit.png"
  107. alt=""
  108. ></image>
  109. </view>
  110. </view>
  111. </view>
  112. </view>
  113. </view>
  114. <view class="bottomTwo" @click="saveRoom">
  115. <view class="previousTip">保存</view>
  116. </view>
  117. </view>
  118. <!-- 选择家具弹窗 -->
  119. <view>
  120. <!-- 底部弹窗 -->
  121. <view class="bottom-modal" v-if="addfntShow">
  122. <!-- 遮罩层 -->
  123. <view class="modal-mask" @click="addfntShow = false"></view>
  124. <!-- 弹窗内容 -->
  125. <view class="modal-container">
  126. <view class="modal-header">
  127. <text class="header-title">选择家具</text>
  128. </view>
  129. <view class="modal-content">
  130. <view class="device-bottom">
  131. <view
  132. class="item-box"
  133. v-for="(item, index) in selectfntLists"
  134. :key="index"
  135. @click="generate(item.type)"
  136. >
  137. <image
  138. :src="`../../static/furnitures/${item.type}.png`"
  139. mode=""
  140. />
  141. <text
  142. >{{ item.name }}({{ item.width }}*{{
  143. item.length
  144. }})</text
  145. >
  146. </view>
  147. </view>
  148. </view>
  149. <view class="modal-footer">
  150. <button
  151. class="footer-btn cancel"
  152. @click="addfntShow = false"
  153. >
  154. 取消
  155. </button>
  156. <button
  157. class="footer-btn confirm"
  158. @click="addfntShow = false"
  159. >
  160. 确定
  161. </button>
  162. </view>
  163. </view>
  164. </view>
  165. </view>
  166. <!-- 编辑家具弹窗 -->
  167. <view class="device_control" v-if="editfntShow">
  168. <view class="control_info">
  169. <view class="control_left">
  170. <image
  171. :src="`../../static/furnitures/${selectImg}.png`"
  172. class="image_class"
  173. ></image>
  174. </view>
  175. <view class="control_right">
  176. <view class="control_right_top">
  177. <view class="title">长(cm)</view>
  178. <input
  179. type="number"
  180. v-model="selectWidth"
  181. placeholder="请输入长"
  182. @blur="changeSize"
  183. />
  184. </view>
  185. <view class="control_right_bottom">
  186. <view class="title">宽(cm)</view>
  187. <input
  188. type="number"
  189. v-model="selectHeight"
  190. placeholder="请输入宽"
  191. @blur="changeSize"
  192. />
  193. </view>
  194. </view>
  195. </view>
  196. <view class="control_operat" v-if="editfntShow">
  197. <view class="operat_left">
  198. <view class="top">
  199. <image
  200. src="../../static/btn_top.png"
  201. mode="aspectFit"
  202. @click="movefut(0, -10)"
  203. ></image>
  204. </view>
  205. <view class="middle">
  206. <image
  207. src="../../static/btn_left.png"
  208. mode="aspectFit"
  209. @click="movefut(-10, 0)"
  210. ></image>
  211. <image
  212. src="../../static/btn_right.png"
  213. mode="aspectFit"
  214. @click="movefut(10, 0)"
  215. ></image>
  216. </view>
  217. <view class="bottom">
  218. <image
  219. src="../../static/btn_bot.png"
  220. mode="aspectFit"
  221. @click="movefut(0, 10)"
  222. ></image>
  223. </view>
  224. </view>
  225. <view class="operat_right">
  226. <view class="rotate">
  227. <view class="rotate_left" @click="rotatefut(-90)">
  228. <image src="../../static/rote_left.png"></image>
  229. </view>
  230. <view class="rotate_rigt" @click="rotatefut(90)">
  231. <image src="../../static/rote_right.png"></image>
  232. </view>
  233. </view>
  234. <view class="del_funit" @click="editfntShow = false"
  235. >完成</view
  236. >
  237. </view>
  238. </view>
  239. </view>
  240. </view>
  241. </template>
  242. <script>
  243. import MqttService from "../../utils/globalMqtt.js";
  244. import {
  245. convert_region_r2c,
  246. rotateRect_cw,
  247. convert_furniture_r2c,
  248. rotateRect_ccw,
  249. convert_region_c2r,
  250. convert_point_r2c,
  251. } from "../../utils/changezb.js";
  252. export default {
  253. name: "my",
  254. data() {
  255. return {
  256. // 设备参数
  257. deviceWidth: 400,
  258. deviceHeight: 500,
  259. modules: [],
  260. width: 0,
  261. length: 0,
  262. selectedIndex: 0,
  263. selectIndex: 0,
  264. controlOptions: [],
  265. startX: 0, // 记录触摸开始时的X坐标
  266. startY: 0, // 记录触摸开始时的Y坐标
  267. draggingIndex: null, // 记录当前正在拖动的模块的索引
  268. selectedItem: [],
  269. rotateShow: false,
  270. selectRotate: "",
  271. selectedRotate: "",
  272. softWare: "",
  273. xOffset: 0,
  274. yOffset: 0,
  275. angle: 0,
  276. xxStart: "",
  277. xxEnd: "",
  278. yyStart: "",
  279. yyEnd: "",
  280. // 控制部分参数
  281. selectImg: "setting",
  282. selectWidth: "",
  283. selectHeight: "",
  284. addfntShow: false,
  285. editfntShow: false,
  286. selectfntLists: [
  287. {
  288. name: "床",
  289. type: "bed",
  290. width: 80,
  291. length: 70,
  292. },
  293. {
  294. name: "柜子",
  295. type: "bed_cabinet",
  296. width: 40,
  297. length: 80,
  298. },
  299. {
  300. name: "化妆椅",
  301. type: "bed_dressing_chair",
  302. width: 40,
  303. length: 40,
  304. },
  305. {
  306. name: "化妆镜",
  307. type: "bed_dressing_mirror",
  308. width: 40,
  309. length: 80,
  310. },
  311. {
  312. name: "床头柜",
  313. type: "bed_table",
  314. width: 40,
  315. length: 40,
  316. },
  317. {
  318. name: "脸盆",
  319. type: "bath_basin",
  320. width: 40,
  321. length: 80,
  322. },
  323. {
  324. name: "门",
  325. type: "bath_door",
  326. width: 40,
  327. length: 40,
  328. },
  329. {
  330. name: "淋浴",
  331. type: "bath_shower",
  332. width: 40,
  333. length: 60,
  334. },
  335. {
  336. name: "马桶",
  337. type: "bath_toilet",
  338. width: 40,
  339. length: 40,
  340. },
  341. {
  342. name: "餐桌(方形)",
  343. type: "dining_table_rect",
  344. width: 40,
  345. length: 40,
  346. },
  347. {
  348. name: "餐桌",
  349. type: "dining_table",
  350. width: 40,
  351. length: 80,
  352. },
  353. {
  354. name: "餐椅",
  355. type: "dining_chair",
  356. width: 40,
  357. length: 80,
  358. },
  359. {
  360. name: "冰箱",
  361. type: "dining_fridge",
  362. width: 40,
  363. length: 80,
  364. },
  365. {
  366. name: "书柜",
  367. type: "living_bookcase",
  368. width: 40,
  369. length: 80,
  370. },
  371. {
  372. name: "沙发",
  373. type: "living_sofa",
  374. width: 40,
  375. length: 80,
  376. },
  377. {
  378. name: "茶几",
  379. type: "living_tea_table",
  380. width: 40,
  381. length: 80,
  382. },
  383. {
  384. name: "电视柜",
  385. type: "living_tv_stand",
  386. width: 40,
  387. length: 80,
  388. },
  389. {
  390. name: "单人沙发",
  391. type: "living_sofa_single",
  392. width: 40,
  393. length: 40,
  394. },
  395. ],
  396. lnbAction: "action8",
  397. // mqtt模块
  398. targetPoints: {},
  399. inactivityTimer: null,
  400. left: 0,
  401. top: 0,
  402. clientIdProp: null,
  403. clientId: null,
  404. // 重新计算雷达坐标位置
  405. x_radar: 300,
  406. y_radar: 300,
  407. rotatedRect: {},
  408. // 处理点位
  409. point: "",
  410. };
  411. },
  412. computed: {},
  413. methods: {
  414. onTouchMove(index, event) {
  415. if (this.draggingIndex === null) return;
  416. event.preventDefault && event.preventDefault();
  417. event.stopPropagation && event.stopPropagation();
  418. const { modules, startX, startY } = this;
  419. const currentX = event.touches[0].clientX;
  420. const currentY = event.touches[0].clientY;
  421. const deltaX = Math.round(currentX - startX);
  422. const deltaY = Math.round(currentY - startY);
  423. // let x = Math.round(
  424. // this.xxStart + event.currentTarget.offsetLeft * 2
  425. // );
  426. // let y = Math.round(this.yyEnd - event.currentTarget.offsetTop * 2);
  427. // 创建新数组避免直接修改原数组
  428. const updatedModules = [...this.modules];
  429. updatedModules[this.draggingIndex] = {
  430. ...updatedModules[this.draggingIndex],
  431. left: Math.round(
  432. updatedModules[this.draggingIndex].left + deltaX
  433. ),
  434. top: Math.round(
  435. updatedModules[this.draggingIndex].top + deltaY
  436. ),
  437. // x,
  438. // y,
  439. };
  440. // Vue 的响应式更新
  441. this.modules = updatedModules;
  442. this.startX = currentX;
  443. this.startY = currentY;
  444. },
  445. onTouchStart(index, event) {
  446. const selectedItem = this.modules[index];
  447. const selectedItemWidth = selectedItem.width / 40;
  448. const selectedItemHeight = selectedItem.height / 40;
  449. this.draggingIndex = index;
  450. this.selectedIndex = index;
  451. this.startX = event.touches[0].clientX;
  452. this.startY = event.touches[0].clientY;
  453. this.selectedItem = selectedItem;
  454. this.selectedItemWidth = selectedItemWidth;
  455. this.selectedItemHeight = selectedItemHeight;
  456. this.selectImg = this.modules[index].type;
  457. this.selectWidth = this.modules[index].width;
  458. this.selectHeight = this.modules[index].height;
  459. },
  460. onTouchEnd(index, event) {
  461. // this.draggingIndex = null;
  462. },
  463. generate(featuriesType) {
  464. let component = {
  465. name: "",
  466. type: featuriesType,
  467. width: 0,
  468. length: 0,
  469. top: this.y_radar,
  470. left: this.x_radar,
  471. rotate: 0,
  472. };
  473. switch (featuriesType) {
  474. case "bed":
  475. component.width = 80;
  476. component.length = 120;
  477. component.height = 120;
  478. component.name = "床";
  479. break;
  480. case "bed_cabinet":
  481. component.width = 40;
  482. component.length = 80;
  483. component.height = 80;
  484. component.name = "柜子";
  485. break;
  486. case "bed_dressing_chair":
  487. component.width = 40;
  488. component.length = 40;
  489. component.height = 40;
  490. component.name = "化妆椅";
  491. break;
  492. case "bed_dressing_mirror":
  493. component.width = 40;
  494. component.length = 80;
  495. component.height = 80;
  496. component.name = "化妆镜";
  497. break;
  498. case "bed_table":
  499. component.width = 40;
  500. component.length = 40;
  501. component.height = 40;
  502. component.name = "床头柜";
  503. break;
  504. case "bath_basin":
  505. component.width = 40;
  506. component.length = 80;
  507. component.height = 80;
  508. component.name = "脸盆";
  509. break;
  510. case "bath_door":
  511. component.width = 40;
  512. component.length = 40;
  513. component.height = 40;
  514. component.name = "门";
  515. break;
  516. case "bath_shower":
  517. component.width = 40;
  518. component.length = 60;
  519. component.height = 60;
  520. component.name = "淋浴";
  521. break;
  522. case "bath_toilet":
  523. component.width = 40;
  524. component.length = 40;
  525. component.height = 40;
  526. component.name = "马桶";
  527. break;
  528. case "dining_table_rect":
  529. component.width = 40;
  530. component.length = 40;
  531. component.height = 40;
  532. component.name = "餐桌(方形)";
  533. break;
  534. case "dining_table":
  535. component.width = 40;
  536. component.length = 80;
  537. component.height = 80;
  538. component.name = "餐桌";
  539. break;
  540. case "dining_chair":
  541. component.width = 40;
  542. component.length = 80;
  543. component.height = 80;
  544. component.name = "餐椅";
  545. break;
  546. case "dining_fridge":
  547. component.width = 40;
  548. component.length = 80;
  549. component.height = 80;
  550. component.name = "冰箱";
  551. break;
  552. case "living_bookcase":
  553. component.width = 40;
  554. component.length = 80;
  555. component.height = 80;
  556. component.name = "书柜";
  557. break;
  558. case "living_sofa":
  559. component.width = 40;
  560. component.length = 80;
  561. component.height = 80;
  562. component.name = "沙发";
  563. break;
  564. case "living_tea_table":
  565. component.width = 40;
  566. component.length = 80;
  567. component.height = 80;
  568. component.name = "茶几";
  569. break;
  570. case "living_tv_stand":
  571. component.width = 40;
  572. component.length = 80;
  573. component.height = 80;
  574. component.name = "电视柜";
  575. break;
  576. case "living_sofa_single":
  577. component.width = 40;
  578. component.length = 40;
  579. component.height = 40;
  580. component.name = "单人沙发";
  581. break;
  582. default:
  583. break;
  584. }
  585. component.type = featuriesType;
  586. if (component !== null) {
  587. this.modules.push(component);
  588. }
  589. },
  590. // 输入宽高改变家具大小
  591. changeSize() {
  592. if (this.selectHeight <= 0 || this.selectWidth <= 0) {
  593. this.selectHeight = this.modules[this.draggingIndex].length;
  594. this.selectWidth = this.modules[this.draggingIndex].width;
  595. return this.showModal("提示", "家具长宽不能小于0");
  596. }
  597. this.modules[this.draggingIndex].length = this.selectHeight;
  598. this.modules[this.draggingIndex].height = this.selectHeight;
  599. this.modules[this.draggingIndex].width = this.selectWidth;
  600. },
  601. movefut(left, top) {
  602. this.modules[this.draggingIndex].left =
  603. this.modules[this.draggingIndex].left + left;
  604. this.modules[this.draggingIndex].top =
  605. this.modules[this.draggingIndex].top + top;
  606. },
  607. rotatefut(rotate) {
  608. let item = this.modules[this.draggingIndex];
  609. if (item.rotate == 0) {
  610. item.rotate = 360;
  611. }
  612. let currentRotate = item.rotate || 0;
  613. let newAngle = (currentRotate + rotate) % 360;
  614. this.modules[this.draggingIndex].rotate = newAngle;
  615. },
  616. deleteItem(index) {
  617. this.draggingIndex = index;
  618. if (this.draggingIndex === null) {
  619. return this.showModal("提示", "请先选择家具");
  620. }
  621. this.showModal("提示", "是否确认删除该家具", (res) => {
  622. if (res.confirm) {
  623. this.modules.splice(this.draggingIndex, 1);
  624. this.draggingIndex = null;
  625. }
  626. });
  627. },
  628. editItem(index) {
  629. this.draggingIndex = index;
  630. this.editfntShow = true;
  631. const selectedItem = this.modules[index];
  632. const selectedItemWidth = selectedItem.width / 40;
  633. const selectedItemHeight = selectedItem.height / 40;
  634. this.draggingIndex = index;
  635. this.selectedIndex = index;
  636. this.selectedItem = selectedItem;
  637. this.selectedItemWidth = selectedItemWidth;
  638. this.selectedItemHeight = selectedItemHeight;
  639. this.selectImg = this.modules[index].type;
  640. this.selectWidth = this.modules[index].width;
  641. this.selectHeight = this.modules[index].height;
  642. },
  643. // 提取公共的模态框方法
  644. showModal(title, content, confirmCallback) {
  645. wx.showModal({
  646. title,
  647. content,
  648. success: (res) => {
  649. if (res.confirm && confirmCallback) {
  650. confirmCallback(res);
  651. }
  652. },
  653. });
  654. },
  655. saveRoom() {
  656. const pRadar = {
  657. x: this.x_radar,
  658. y: this.y_radar,
  659. angle: uni.getStorageSync("northAngle"),
  660. mountPlain: "wall",
  661. };
  662. console.log(this.modules, pRadar, "下发之前");
  663. let copyModule = JSON.parse(JSON.stringify(this.modules));
  664. copyModule.forEach((item) => {
  665. item.length = item.height;
  666. const rect = rotateRect_ccw(item, pRadar, pRadar.angle);
  667. const rotatedRect = convert_region_c2r(rect, pRadar);
  668. item.width = item.width;
  669. item.length = item.length;
  670. item.name = item.name;
  671. item.type = item.type;
  672. item.rotate = item.rotate || 0;
  673. item.x = rotatedRect.x_cm_start;
  674. item.y = rotatedRect.y_cm_stop;
  675. });
  676. for (const item of copyModule) {
  677. if (
  678. item.top < 0 ||
  679. item.left < 0 ||
  680. item.top + (item.length - 0) > 600 ||
  681. item.left + (item.width - 0) > 600
  682. ) {
  683. uni.showToast({
  684. title: `${item.name}超出画布区域,请调整位置后再保存`,
  685. icon: "none",
  686. duration: 1500,
  687. });
  688. return; // 这里能真正退出整个函数
  689. }
  690. }
  691. this.$http
  692. .post(
  693. "wap/room/saveRoom",
  694. JSON.stringify({
  695. devId: this.devId,
  696. furnitures: copyModule,
  697. }),
  698. {
  699. header: {
  700. "Content-Type": "application/json;charset=UTF-8",
  701. },
  702. }
  703. )
  704. .then((res) => {
  705. if (res.data.code == 200) {
  706. uni.showToast({
  707. title: "保存成功",
  708. icon: "success",
  709. duration: 1500,
  710. });
  711. setTimeout(() => {
  712. uni.navigateBack({
  713. delta: 1,
  714. });
  715. }, 1500);
  716. } else {
  717. uni.showToast({
  718. title: res.data.msg,
  719. icon: "none",
  720. duration: 1500,
  721. });
  722. }
  723. })
  724. .catch((err) => {});
  725. },
  726. showAddFt() {
  727. this.addfntShow = true;
  728. },
  729. getdevInfo(devId) {
  730. this.$http
  731. .get(`wap/device/queryDeviceInfoById`, {
  732. devId: devId,
  733. })
  734. .then((res) => {
  735. if (res.data.data) {
  736. this.devInfo = res.data.data;
  737. this.width = Math.abs(
  738. this.devInfo.yyEnd - this.devInfo.yyStart
  739. );
  740. this.length = Math.abs(
  741. this.devInfo.xxEnd - this.devInfo.xxStart
  742. );
  743. this.calculateRegion();
  744. this.statusLight = this.devInfo.statusLight;
  745. this.xxStart = this.devInfo.xxStart;
  746. this.xxEnd = this.devInfo.xxEnd;
  747. this.yyStart = this.devInfo.yyStart;
  748. this.yyEnd = this.devInfo.yyEnd;
  749. } else {
  750. uni.showToast({
  751. title: res.data.message,
  752. icon: "none",
  753. });
  754. }
  755. })
  756. .catch((err) => {
  757. console.log(err, 8888);
  758. });
  759. },
  760. // 计算检测区域问题
  761. calculateRegion() {
  762. let x_cm_start = this.devInfo.xxStart;
  763. let y_cm_start = this.devInfo.yyStart;
  764. let x_cm_stop = this.devInfo.xxEnd;
  765. let y_cm_stop = this.devInfo.yyEnd;
  766. const trackingRegion = {
  767. x_cm_start,
  768. y_cm_start,
  769. x_cm_stop,
  770. y_cm_stop,
  771. };
  772. const pRadar = {
  773. x: this.x_radar,
  774. y: this.y_radar,
  775. angle: uni.getStorageSync("northAngle"),
  776. mountPlain: this.devInfo.mountPlain,
  777. };
  778. const rect = convert_region_r2c(trackingRegion, pRadar);
  779. this.rotatedRect = rotateRect_cw(rect, pRadar, pRadar.angle);
  780. console.log(this.rotatedRect, "rotatedRect");
  781. },
  782. getRoomInfo(devId) {
  783. const pRadar = {
  784. x: this.x_radar,
  785. y: this.y_radar,
  786. angle: uni.getStorageSync("northAngle"),
  787. mountPlain: "wall",
  788. };
  789. this.$http
  790. .get(`wap/room/readRoom`, {
  791. devId: devId,
  792. })
  793. .then((res) => {
  794. if (res.data.data) {
  795. this.modules = res.data.data.furnitures;
  796. }
  797. console.log(this.modules, 88888);
  798. this.modules.forEach((item) => {
  799. const rect = convert_furniture_r2c(item, pRadar);
  800. const rotatedRect = rotateRect_cw(
  801. rect,
  802. pRadar,
  803. pRadar.angle
  804. );
  805. item.left = rotatedRect.left;
  806. item.top = rotatedRect.top;
  807. item.width = rotatedRect.width;
  808. item.height = rotatedRect.height;
  809. item.name = item.name;
  810. item.type = item.type;
  811. item.rotate = item.rotate;
  812. });
  813. console.log(this.modules, 99999);
  814. });
  815. },
  816. handleMessage(topic, message, clientId) {
  817. // 清除不活动定时器
  818. clearTimeout(this.inactivityTimer);
  819. this.inactivityTimer = setTimeout(() => {
  820. this.targetPoints = {};
  821. // console.log("长时间没有点位消除");
  822. }, 1500);
  823. // 验证topic格式
  824. const match = topic.match(/^\/dev\/(.+)\/tracker_targets$/);
  825. if (!match || match[1] !== clientId) return;
  826. try {
  827. const data = JSON.parse(message.toString());
  828. // if (data.health) {
  829. // if (
  830. // data.health.breath_rpm ||
  831. // data.health.breath_rpm === 0
  832. // ) {
  833. // this.receptHealth(Math.floor(data.health.breath_rpm));
  834. // } else {
  835. // }
  836. // }
  837. // console.log(data.tracker_targets, "MQTT消息解析成功22222");
  838. this.processTrackerData(data.tracker_targets);
  839. } catch (e) {
  840. console.error("MQTT消息解析失败", e);
  841. }
  842. },
  843. processTrackerData(arr) {
  844. if (Array.isArray(arr) && arr.length > 0 && Array.isArray(arr[0])) {
  845. this.targetPoints = {};
  846. const currentIds = new Set();
  847. const newTargetPoints = {};
  848. // 处理每个追踪目标
  849. arr.forEach((item) => {
  850. if (!Array.isArray(item) || item.length < 4) return;
  851. const [x, y, z, id] = item;
  852. currentIds.add(id.toString());
  853. const pRadar = { x: this.x_radar, y: this.y_radar };
  854. this.point = convert_point_r2c(
  855. { x, y },
  856. pRadar,
  857. uni.getStorageSync("northAngle")
  858. );
  859. // 处理新点或更新现有点
  860. if (!this.targetPoints[id]) {
  861. newTargetPoints[id] = this.createNewTargetPoint(
  862. x,
  863. y,
  864. z,
  865. id
  866. );
  867. } else {
  868. newTargetPoints[id] = this.updateExistingTargetPoint(
  869. this.targetPoints[id],
  870. x,
  871. y,
  872. z,
  873. 2
  874. );
  875. }
  876. });
  877. // 移除不存在的点
  878. Object.keys(this.targetPoints).forEach((id) => {
  879. if (!currentIds.has(id)) {
  880. delete this.targetPoints[id];
  881. }
  882. });
  883. // 更新目标点
  884. this.targetPoints = {
  885. ...this.targetPoints,
  886. ...newTargetPoints,
  887. };
  888. }
  889. },
  890. createNewTargetPoint(x, y, z, id) {
  891. return {
  892. x,
  893. y,
  894. z,
  895. id,
  896. displayX: this.point.x,
  897. displayY: this.point.y - 12,
  898. lastX: x,
  899. lastY: y,
  900. };
  901. },
  902. updateExistingTargetPoint(existingPoint, x, y, z, THRESHOLD) {
  903. const dx = x - existingPoint.lastX;
  904. const dy = y - existingPoint.lastY;
  905. const distance = Math.sqrt(dx * dx + dy * dy);
  906. if (distance > THRESHOLD) {
  907. return {
  908. ...existingPoint,
  909. x,
  910. y,
  911. z,
  912. lastX: x,
  913. lastY: y,
  914. displayX: this.point.x,
  915. displayY: this.point.y - 12,
  916. };
  917. }
  918. return existingPoint;
  919. },
  920. initSubscriptions() {
  921. const topicList = [
  922. {
  923. topic: `/dev/${this.clientId}/tracker_targets`,
  924. key: "unsubscribeFn",
  925. callback: (message, msgTopic) => {
  926. const dataMatch = msgTopic.match(
  927. /^\/dev\/(.+)\/tracker_targets$/
  928. );
  929. const cmdMatch = msgTopic.match(
  930. /^\/mps\/wx_(.+)\/notice$/
  931. );
  932. if (dataMatch && dataMatch[1] === this.clientId) {
  933. this.handleMessage(
  934. msgTopic,
  935. message,
  936. this.clientId
  937. );
  938. } else if (cmdMatch) {
  939. this.$refs.alarmModel.hanOtherMessage(
  940. msgTopic,
  941. message
  942. );
  943. }
  944. },
  945. },
  946. {
  947. topic: `/dev/${this.clientId}/falling_event_change`,
  948. key: "fallingEventChange",
  949. callback: (message, msgTopic) => {
  950. const dataMatch = msgTopic.match(
  951. /^\/dev\/(.+)\/falling_event_change$/
  952. );
  953. if (dataMatch && dataMatch[1] === this.clientId) {
  954. const dataMessage = JSON.parse(message.toString());
  955. console.log(dataMessage, 888888);
  956. if (dataMessage.falling == 1) {
  957. this.falling = dataMessage.falling;
  958. this.lnbAction = "actionWarn";
  959. } else if (
  960. dataMessage.falling == 2 ||
  961. dataMessage.falling == 3
  962. ) {
  963. this.falling = dataMessage.falling;
  964. this.lnbAction = "actionSerious";
  965. } else {
  966. this.lnbAction = "action8";
  967. }
  968. }
  969. },
  970. },
  971. ];
  972. topicList.forEach((item) => {
  973. // 避免重复订阅
  974. if (this[item.key]) return;
  975. const subscribeFunc = () => {
  976. const unsubscribe = MqttService.subscribe(
  977. "DATA",
  978. item.topic,
  979. item.callback
  980. );
  981. if (unsubscribe) {
  982. this[item.key] = unsubscribe;
  983. console.log(`✅ 已成功订阅主题: ${item.topic}`);
  984. }
  985. };
  986. if (MqttService.dataConnected) {
  987. subscribeFunc();
  988. } else {
  989. // MQTT 未连接,等待重连成功再订阅
  990. const handler = () => {
  991. subscribeFunc();
  992. uni.$off("mqttData-ready", handler);
  993. };
  994. uni.$on("mqttData-ready", handler);
  995. }
  996. });
  997. },
  998. },
  999. onLoad(options) {
  1000. this.devId = options.devId;
  1001. this.clientId = options.clientId;
  1002. console.log(options, "options111111");
  1003. this.getdevInfo(this.devId);
  1004. this.getRoomInfo(this.devId);
  1005. },
  1006. onShow() {
  1007. // const topic = `/dev/${this.clientId}/tracker_targets`;
  1008. // this.unsubscribeFn = MqttService.subscribe(
  1009. // "DATA",
  1010. // topic,
  1011. // (message, msgTopic) => {
  1012. // const dataMatch = msgTopic.match(
  1013. // /^\/dev\/(.+)\/tracker_targets$/
  1014. // );
  1015. // if (dataMatch && dataMatch[1] === this.clientId) {
  1016. // this.handleMessage(msgTopic, message, this.clientId);
  1017. // }
  1018. // }
  1019. // );
  1020. // if (this.unsubscribeFn) {
  1021. // console.log(`✅ 已成功订阅主题: ${topic}`);
  1022. // }
  1023. if (MqttService.dataConnected && MqttService.dataClient) {
  1024. // 已连接,直接订阅
  1025. this.initSubscriptions();
  1026. }
  1027. },
  1028. onHide() {
  1029. ["unsubscribeFn", "fallingEventChange"].forEach((key) => {
  1030. if (this[key]) {
  1031. this[key]();
  1032. this[key] = null;
  1033. }
  1034. });
  1035. },
  1036. onUnload() {
  1037. // 清理定时器
  1038. clearTimeout(this.autoPlayinterval);
  1039. // 清理自动滑动定时器
  1040. clearInterval(this.setIntervalVal);
  1041. this.setIntervalVal = null;
  1042. this.autoPlayinterval = null;
  1043. // 取消订阅
  1044. ["unsubscribeFn", "fallingEventChange"].forEach((key) => {
  1045. if (this[key]) {
  1046. this[key]();
  1047. this[key] = null;
  1048. }
  1049. });
  1050. },
  1051. };
  1052. </script>
  1053. <style lang="less" scoped>
  1054. .home-warpTwo {
  1055. position: relative;
  1056. height: 100vh;
  1057. background: #f4f4f4;
  1058. .header_two {
  1059. width: 750rpx;
  1060. padding-top: 20rpx;
  1061. background: linear-gradient(180deg, #faede2 0%, #ffffff 100%);
  1062. .radar-box {
  1063. margin: 0 auto;
  1064. position: relative;
  1065. width: 600rpx;
  1066. height: 600rpx;
  1067. background: #ffffff;
  1068. border-radius: 37.5rpx;
  1069. box-sizing: border-box;
  1070. .tranStyle {
  1071. position: absolute;
  1072. overflow: hidden;
  1073. background-color: #fff;
  1074. border: 9rpx solid #333333;
  1075. background-image: url("https://hflnxx.oss-cn-shanghai.aliyuncs.com/IMAGE/20250919/toilet_bg.png");
  1076. background-repeat: no-repeat;
  1077. background-position: center;
  1078. }
  1079. .moduleContent {
  1080. view {
  1081. position: absolute;
  1082. }
  1083. .module-img {
  1084. width: 100%;
  1085. height: 100%;
  1086. display: block;
  1087. }
  1088. }
  1089. .action-icon-G {
  1090. position: absolute;
  1091. width: 100rpx;
  1092. height: 100rpx;
  1093. }
  1094. .action-icon-M {
  1095. // position: absolute;
  1096. width: 50rpx;
  1097. height: 50rpx;
  1098. }
  1099. .redar-pic {
  1100. position: absolute;
  1101. top: 50%;
  1102. left: 50%;
  1103. width: 40rpx;
  1104. height: 40rpx;
  1105. transform: translate(-50%, -50%);
  1106. z-index: 20;
  1107. }
  1108. }
  1109. .airbody {
  1110. margin: 20rpx auto 0 auto;
  1111. width: 700rpx;
  1112. background: #ffffff;
  1113. border-radius: 38rpx;
  1114. box-sizing: border-box;
  1115. .header_top {
  1116. padding: 0rpx 40rpx;
  1117. display: flex;
  1118. justify-content: space-between;
  1119. align-items: center;
  1120. .airTitle {
  1121. font-weight: 500;
  1122. color: #784c41;
  1123. font-size: 32rpx;
  1124. padding-left: 20rpx;
  1125. padding-top: 20rpx;
  1126. }
  1127. .addfnt {
  1128. display: flex;
  1129. align-items: center;
  1130. image {
  1131. margin-top: 7rpx;
  1132. width: 25rpx;
  1133. height: 25rpx;
  1134. }
  1135. .add_btn {
  1136. margin-left: 10rpx;
  1137. font-size: 32rpx;
  1138. }
  1139. }
  1140. }
  1141. .module {
  1142. padding: 10rpx 30rpx;
  1143. bottom: 0;
  1144. box-sizing: border-box;
  1145. width: 100%;
  1146. background: #ffffff;
  1147. border-radius: 20rpx 20rpx 20rpx 20rpx;
  1148. .device-bottom {
  1149. margin-top: 20rpx;
  1150. height: 500rpx;
  1151. overflow-y: scroll;
  1152. .no-data {
  1153. margin-top: 100rpx;
  1154. text-align: center;
  1155. }
  1156. .info-box {
  1157. display: flex;
  1158. align-items: center;
  1159. // width: 620rpx;
  1160. padding: 0 30rpx;
  1161. height: 110rpx;
  1162. background: #f8f8f8;
  1163. border-radius: 38rpx;
  1164. border-radius: 20rpx 20rpx 20rpx 20rpx;
  1165. font-family: PingFang SC, PingFang SC;
  1166. font-weight: 400;
  1167. font-size: 26rpx;
  1168. .info-text {
  1169. display: flex;
  1170. flex-direction: column;
  1171. margin-left: 20rpx;
  1172. }
  1173. image {
  1174. margin-bottom: 10rpx;
  1175. width: 75rpx;
  1176. height: 75rpx;
  1177. }
  1178. .edit_del {
  1179. margin-left: auto;
  1180. image {
  1181. width: 40rpx;
  1182. height: 40rpx;
  1183. }
  1184. }
  1185. }
  1186. .info-box:not(:first-child) {
  1187. margin-top: 20rpx;
  1188. }
  1189. }
  1190. .device_control {
  1191. overflow: hidden;
  1192. margin-top: 20rpx;
  1193. .control_info {
  1194. height: 160rpx;
  1195. background: #f8f8f8;
  1196. border-radius: 37rpx;
  1197. box-sizing: border-box;
  1198. padding: 20rpx 30rpx;
  1199. display: flex;
  1200. align-content: center;
  1201. justify-content: center;
  1202. .control_left {
  1203. width: 110rpx;
  1204. height: 110rpx;
  1205. .image_class {
  1206. width: 110rpx;
  1207. height: 110rpx;
  1208. }
  1209. }
  1210. .control_right {
  1211. margin-left: 100rpx;
  1212. .control_right_top {
  1213. width: 280rpx;
  1214. height: 50rpx;
  1215. display: flex;
  1216. justify-content: space-around;
  1217. align-items: center;
  1218. background: #ffffff;
  1219. border-radius: 18rpx;
  1220. .title {
  1221. color: #a0acbe;
  1222. font-size: 28rpx;
  1223. }
  1224. input {
  1225. width: 130rpx;
  1226. color: #a0acbe;
  1227. }
  1228. }
  1229. .control_right_bottom {
  1230. width: 280rpx;
  1231. height: 50rpx;
  1232. display: flex;
  1233. justify-content: space-around;
  1234. align-items: center;
  1235. margin-top: 10rpx;
  1236. background: #ffffff;
  1237. border-radius: 18rpx;
  1238. .title {
  1239. color: #a0acbe;
  1240. font-size: 28rpx;
  1241. }
  1242. input {
  1243. width: 130rpx;
  1244. color: #a0acbe;
  1245. }
  1246. }
  1247. }
  1248. }
  1249. }
  1250. }
  1251. }
  1252. .bottomTwo {
  1253. display: flex;
  1254. align-items: center;
  1255. justify-content: center;
  1256. position: fixed;
  1257. bottom: 0;
  1258. left: 0;
  1259. width: 750rpx;
  1260. height: 120rpx;
  1261. background: #f3e2dd;
  1262. .previousTip {
  1263. font-weight: 500;
  1264. color: #111111;
  1265. font-size: 32rpx;
  1266. }
  1267. }
  1268. }
  1269. .bottom-modal {
  1270. position: fixed;
  1271. top: 0;
  1272. left: 0;
  1273. right: 0;
  1274. bottom: 0;
  1275. z-index: 999;
  1276. display: flex;
  1277. flex-direction: column;
  1278. // 遮罩层
  1279. .modal-mask {
  1280. position: absolute;
  1281. top: 0;
  1282. left: 0;
  1283. right: 0;
  1284. bottom: 0;
  1285. background-color: rgba(0, 0, 0, 0.5);
  1286. transition: all 0.3s ease;
  1287. }
  1288. // 弹窗容器
  1289. .modal-container {
  1290. position: fixed;
  1291. left: 0;
  1292. right: 0;
  1293. bottom: 0;
  1294. background-color: #fff;
  1295. border-radius: 16rpx 16rpx 0 0;
  1296. max-height: 70vh;
  1297. display: flex;
  1298. flex-direction: column;
  1299. transform: translateY(0);
  1300. transition: transform 0.3s ease;
  1301. box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.1);
  1302. // 头部样式
  1303. .modal-header {
  1304. position: relative;
  1305. padding: 30rpx;
  1306. text-align: center;
  1307. border-bottom: 1rpx solid #f5f5f5;
  1308. .header-title {
  1309. font-size: 32rpx;
  1310. font-weight: 500;
  1311. color: #333;
  1312. }
  1313. }
  1314. // 内容区域
  1315. .modal-content {
  1316. height: 600rpx;
  1317. overflow-y: scroll;
  1318. .device-bottom {
  1319. display: flex;
  1320. flex-wrap: wrap;
  1321. justify-content: space-between;
  1322. gap: 20rpx;
  1323. padding: 0 30rpx;
  1324. box-sizing: border-box;
  1325. .item-box {
  1326. display: flex;
  1327. flex-direction: column;
  1328. align-items: center;
  1329. justify-content: center;
  1330. width: 200rpx;
  1331. height: 160rpx;
  1332. background: #f5f7fa;
  1333. border-radius: 20rpx 20rpx 20rpx 20rpx;
  1334. font-family: PingFang SC, PingFang SC;
  1335. font-weight: 400;
  1336. font-size: 26rpx;
  1337. image {
  1338. margin-bottom: 20rpx;
  1339. width: 40rpx;
  1340. height: 40rpx;
  1341. }
  1342. }
  1343. }
  1344. }
  1345. // 底部按钮
  1346. .modal-footer {
  1347. display: flex;
  1348. padding: 20rpx 30rpx;
  1349. border-top: 1rpx solid #f5f5f5;
  1350. .footer-btn {
  1351. flex: 1;
  1352. height: 80rpx;
  1353. line-height: 80rpx;
  1354. border-radius: 40rpx;
  1355. font-size: 28rpx;
  1356. margin: 0 10rpx;
  1357. &.cancel {
  1358. border: none;
  1359. background-color: #f5f5f5;
  1360. color: #666;
  1361. }
  1362. &.confirm {
  1363. border: none;
  1364. background-color: #f3e2dd;
  1365. color: #111111;
  1366. }
  1367. }
  1368. }
  1369. }
  1370. }
  1371. // 动画效果
  1372. .modal-enter-active,
  1373. .modal-leave-active {
  1374. transition: all 0.7s;
  1375. .modal-mask {
  1376. opacity: 1;
  1377. }
  1378. .modal-container {
  1379. transform: translateY(0);
  1380. }
  1381. }
  1382. .modal-enter,
  1383. .modal-leave-to {
  1384. .modal-mask {
  1385. opacity: 0;
  1386. }
  1387. .modal-container {
  1388. transform: translateY(100%);
  1389. }
  1390. }
  1391. // 编辑弹窗
  1392. .device_control {
  1393. position: fixed;
  1394. left: 0;
  1395. right: 0;
  1396. bottom: 0;
  1397. z-index: 999;
  1398. height: 550rpx;
  1399. background: #f8f8f8;
  1400. border-radius: 37rpx 37rpx 0 0;
  1401. box-shadow: 0 -5rpx 20rpx rgba(0, 0, 0, 0.1);
  1402. padding: 30rpx;
  1403. display: flex;
  1404. flex-direction: column;
  1405. // 控制区域(原control_info)
  1406. .control_info {
  1407. display: flex;
  1408. align-items: space-around;
  1409. margin-bottom: 40rpx; // 与操作区域间距
  1410. .control_left {
  1411. margin-left: 100rpx;
  1412. .image_class {
  1413. width: 120rpx;
  1414. height: 120rpx;
  1415. }
  1416. }
  1417. .control_right {
  1418. flex: 1;
  1419. margin-left: 100rpx;
  1420. display: flex;
  1421. flex-direction: column;
  1422. justify-content: space-between;
  1423. .control_right_top,
  1424. .control_right_bottom {
  1425. display: flex;
  1426. align-items: center;
  1427. .title {
  1428. font-size: 28rpx;
  1429. color: #666;
  1430. margin-bottom: 10rpx;
  1431. }
  1432. input {
  1433. width: 40%;
  1434. height: 60rpx;
  1435. background: #fff;
  1436. border-radius: 12rpx;
  1437. padding: 0 20rpx;
  1438. }
  1439. }
  1440. }
  1441. }
  1442. .control_operat {
  1443. display: flex;
  1444. flex: 1;
  1445. padding: 0 30rpx;
  1446. .operat_left {
  1447. width: 300rpx;
  1448. display: flex;
  1449. flex-direction: column;
  1450. align-items: center;
  1451. .top,
  1452. .middle,
  1453. .bottom {
  1454. width: 100%;
  1455. display: flex;
  1456. justify-content: center;
  1457. }
  1458. .middle {
  1459. justify-content: space-between;
  1460. margin: 15rpx 0;
  1461. }
  1462. image {
  1463. width: 80rpx;
  1464. height: 80rpx;
  1465. background: #fff;
  1466. border-radius: 50%;
  1467. padding: 15rpx;
  1468. box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
  1469. }
  1470. }
  1471. .operat_right {
  1472. flex: 1;
  1473. display: flex;
  1474. flex-direction: column;
  1475. align-items: flex-end;
  1476. padding-top: 70rpx;
  1477. .rotate {
  1478. display: flex;
  1479. margin-bottom: 30rpx;
  1480. .rotate_left,
  1481. .rotate_rigt {
  1482. width: 100rpx;
  1483. height: 70rpx;
  1484. background: #fff;
  1485. border-radius: 12rpx;
  1486. display: flex;
  1487. align-items: center;
  1488. justify-content: center;
  1489. box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
  1490. image {
  1491. width: 40rpx;
  1492. height: 40rpx;
  1493. }
  1494. }
  1495. .rotate_rigt {
  1496. margin-left: 20rpx;
  1497. }
  1498. }
  1499. .del_funit {
  1500. width: 220rpx;
  1501. height: 70rpx;
  1502. background: #7b4f43;
  1503. color: white;
  1504. border-radius: 12rpx;
  1505. display: flex;
  1506. align-items: center;
  1507. justify-content: center;
  1508. font-size: 28rpx;
  1509. }
  1510. }
  1511. }
  1512. }
  1513. }
  1514. </style>