wifiSearch.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. <template>
  2. <view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
  3. <cus-header title='配置网络'></cus-header>
  4. <template v-if="!nodevice">
  5. <div class="search adffcac">
  6. <image src="https://transcend.ringzle.com/xiaozhi-app/profile/2025/06/04/379c752c-13f5-4668-9977-e8cad9cd0fa4.gif"></image>
  7. <p>正在搜索可配网设备</p>
  8. </div>
  9. </template>
  10. <template v-else>
  11. <div class="empty adffcac">
  12. <div class="title">{{ emptyState.title }}</div>
  13. <image src="https://transcend.ringzle.com/xiaozhi-app/profile/2025/06/04/5824159f-0bd8-4399-be6c-7d309e2a35dc.png"></image>
  14. <div class="tip">{{ emptyState.tip }}</div>
  15. <div class="warn adfac" @tap="alertShow=true">
  16. <image src="https://transcend.ringzle.com/xiaozhi-app/profile/2025/06/04/19ca1b06-9bfd-4ae6-9c1c-eec45a86f0a0.png"></image>
  17. <span>找不到设备怎么办 ></span>
  18. </div>
  19. </div>
  20. </template>
  21. <div class="zt_btn" v-if="nodevice" @tap="searchAgain">再次搜索</div>
  22. <u-popup :show="alertShow" :round="56" mode="center" @close="alertShow=false">
  23. <div class="alert">
  24. <div class="title">找不到设备怎么办</div>
  25. <p style="margin-top: 53rpx;">1.请确保可配网的设备在手机旁边</p>
  26. <p>2.如果没有开机,请先按住开关键直到指示灯亮起</p>
  27. <p>3.开机状态下,按配网键1次进入配网模式</p>
  28. <div class="btn" @tap="alertShow=false">我知道了</div>
  29. </div>
  30. </u-popup>
  31. <u-popup :show="show" :round="64" mode="bottom" @close="show=false">
  32. <div class="connect adffcac">
  33. <div class="title">找到一个待配网的设备</div>
  34. <image src="https://transcend.ringzle.com/xiaozhi-app/profile/2025/06/04/6f06a2de-5d6c-4596-9a0c-77971d6ea230.png"></image>
  35. <div class="tip">序列号:{{lanya.deviceId}}</div>
  36. <div class="btn" @tap="connectWiFi">连接</div>
  37. </div>
  38. </u-popup>
  39. </view>
  40. </template>
  41. <script>
  42. var xBlufi = require("@/utils/blufi/xBlufi.js");
  43. let _this = null;
  44. export default {
  45. data() {
  46. return {
  47. nodevice: false, // 是否未找到设备
  48. show: false, // 是否显示连接弹窗
  49. alertShow: false, // 是否显示帮助弹窗
  50. lanya: null, // 找到的蓝牙设备信息
  51. devicesList: [],
  52. searching: false, // 是否正在搜索
  53. type: '', // 设备类型
  54. errorType: '', // 'timeout' 或 'bluetoothOff',用于区分错误类型
  55. timeoutTimer: null // 搜索超时定时器
  56. }
  57. },
  58. computed: {
  59. // 根据错误类型动态生成提示文案
  60. emptyState() {
  61. if (this.errorType === 'bluetoothOff') {
  62. return {
  63. title: '蓝牙未开启',
  64. tip: '请检查手机蓝牙是否已开启'
  65. };
  66. }
  67. if (this.errorType === 'timeout') {
  68. return {
  69. title: '搜索超时',
  70. tip: '没有找到可用设备?'
  71. };
  72. }
  73. // 默认情况
  74. return {
  75. title: '搜索失败',
  76. tip: '没有找到可用设备?'
  77. };
  78. }
  79. },
  80. onShow() {
  81. this.init();
  82. },
  83. onLoad: function(option) {
  84. this.type = option.type || '';
  85. _this = this; // 保留页面实例的引用,供回调函数使用
  86. },
  87. onUnload() {
  88. // 页面卸载时,停止监听并清除定时器,防止内存泄漏
  89. xBlufi.listenDeviceMsgEvent(false, this.funListenDeviceMsgEvent);
  90. if (this.timeoutTimer) {
  91. clearTimeout(this.timeoutTimer);
  92. this.timeoutTimer = null;
  93. }
  94. },
  95. methods: {
  96. // 初始化页面状态
  97. init() {
  98. this.nodevice = false;
  99. this.show = false;
  100. this.alertShow = false;
  101. this.lanya = null;
  102. this.devicesList = [];
  103. this.searching = false;
  104. this.errorType = '';
  105. // 清理上一次的定时器
  106. if (this.timeoutTimer) {
  107. clearTimeout(this.timeoutTimer);
  108. this.timeoutTimer = null;
  109. }
  110. // 重新注册监听事件并开始搜索
  111. xBlufi.listenDeviceMsgEvent(false, this.funListenDeviceMsgEvent); // 先移除旧监听
  112. this.Search();
  113. xBlufi.listenDeviceMsgEvent(true, this.funListenDeviceMsgEvent); // 再添加新监听
  114. },
  115. // 再次搜索
  116. searchAgain() {
  117. this.init();
  118. },
  119. // 蓝牙事件回调处理
  120. funListenDeviceMsgEvent: function(options) {
  121. switch (options.type) {
  122. case xBlufi.XBLUFI_TYPE.TYPE_STATUS_CONNECTED:
  123. console.log(options, 'TYPE_STATUS_CONNECTED')
  124. break;
  125. case xBlufi.XBLUFI_TYPE.TYPE_GET_DEVICE_LISTS:
  126. if (options.result && options.data.length > 0) {
  127. _this.devicesList = options.data;
  128. // 优先查找 'cx-' 前缀的设备,其次是 'BLUFI_DEVICE'
  129. let ly = _this.devicesList.find(d => d.name.indexOf('cx-') > -1);
  130. if (!ly) ly = _this.devicesList.find(d => d.name.indexOf('BLUFI_DEVICE') > -1);
  131. if (ly) {
  132. console.log(ly, '找到设备');
  133. // 找到设备后,清除超时定时器
  134. if (_this.timeoutTimer) {
  135. clearTimeout(_this.timeoutTimer);
  136. _this.timeoutTimer = null;
  137. }
  138. // 停止蓝牙搜索
  139. xBlufi.notifyStartDiscoverBle({
  140. 'isStart': false
  141. });
  142. _this.lanya = ly;
  143. _this.nodevice = false;
  144. _this.show = true;
  145. }
  146. }
  147. break;
  148. case xBlufi.XBLUFI_TYPE.TYPE_CONNECTED:
  149. wx.hideLoading();
  150. if (options.result) {
  151. wx.showToast({
  152. title: '连接成功',
  153. icon: 'none'
  154. });
  155. console.log(options.data, '连接成功');
  156. wx.navigateTo({
  157. url: '/pagesMy/wifiSet?deviceId=' + options.data.deviceId + '&name=' +
  158. options.data.name + '&type=' + _this.type
  159. });
  160. } else {
  161. wx.showModal({
  162. title: '连接失败',
  163. content: '请检查:\n1.设备是否离手机太远;\n2.设备是否已被其他手机连接;\n3.请尝试重启设备后再试。',
  164. showCancel: false
  165. });
  166. }
  167. break;
  168. case xBlufi.XBLUFI_TYPE.TYPE_GET_DEVICE_LISTS_START:
  169. if (!options.result) {
  170. // 蓝牙未开启
  171. wx.showToast({
  172. title: '蓝牙未开启',
  173. icon: 'none'
  174. });
  175. _this.nodevice = true;
  176. _this.errorType = 'bluetoothOff'; // 设置错误类型为蓝牙未开启
  177. } else {
  178. // 蓝牙搜索开始
  179. _this.searching = true;
  180. }
  181. break;
  182. case xBlufi.XBLUFI_TYPE.TYPE_GET_DEVICE_LISTS_STOP:
  183. _this.searching = false;
  184. if (options.result) {
  185. console.log('蓝牙停止搜索ok');
  186. } else {
  187. console.log('蓝牙停止搜索失败');
  188. }
  189. break;
  190. }
  191. },
  192. // 开始或停止搜索设备
  193. Search: function() {
  194. if (this.searching) {
  195. xBlufi.notifyStartDiscoverBle({
  196. 'isStart': false
  197. });
  198. } else {
  199. xBlufi.notifyStartDiscoverBle({
  200. 'isStart': true
  201. });
  202. // 启动搜索时,设置一个超时定时器
  203. this.timeoutTimer = setTimeout(() => {
  204. // 检查15秒后是否仍未找到设备
  205. if (!_this.lanya) {
  206. xBlufi.notifyStartDiscoverBle({ 'isStart': false }); // 停止搜索
  207. _this.nodevice = true;
  208. _this.errorType = 'timeout'; // 设置错误类型为超时
  209. console.log('搜索超时');
  210. }
  211. }, 15000); // 设置15秒超时
  212. }
  213. },
  214. // 连接WiFi
  215. connectWiFi: function() {
  216. wx.showLoading({
  217. title: '连接蓝牙设备中...',
  218. })
  219. xBlufi.notifyConnectBle({
  220. isStart: true,
  221. deviceId: _this.lanya.deviceId,
  222. name: _this.lanya.name
  223. });
  224. }
  225. }
  226. }
  227. </script>
  228. <style scoped lang="less">
  229. .page {
  230. width: 100%;
  231. padding: 0 0 300rpx;
  232. background: #FFFFFF;
  233. box-sizing: border-box;
  234. }
  235. .search {
  236. image {
  237. width: 620rpx;
  238. height: 620rpx;
  239. margin-top: 120rpx;
  240. }
  241. p {
  242. font-family: PingFang-SC, PingFang-SC;
  243. font-weight: bold;
  244. font-size: 36rpx;
  245. color: #111111;
  246. line-height: 36rpx;
  247. text-align: center;
  248. margin-top: 80rpx;
  249. }
  250. }
  251. .empty {
  252. .title {
  253. font-family: PingFang-SC, PingFang-SC;
  254. font-weight: bold;
  255. font-size: 36rpx;
  256. color: #111111;
  257. line-height: 36rpx;
  258. text-align: center;
  259. margin-top: 158rpx;
  260. }
  261. &>image {
  262. width: 400rpx;
  263. height: 366rpx;
  264. margin-top: 48rpx;
  265. }
  266. .tip {
  267. font-family: PingFang-SC, PingFang-SC;
  268. font-weight: bold;
  269. font-size: 32rpx;
  270. color: #111111;
  271. line-height: 36rpx;
  272. text-align: center;
  273. margin-top: 53rpx;
  274. }
  275. .warn {
  276. background: rgba(27, 80, 255, 0.06);
  277. border-radius: 28rpx;
  278. padding: 10rpx 24rpx;
  279. margin-top: 48rpx;
  280. image {
  281. width: 26rpx;
  282. height: 26rpx;
  283. }
  284. span {
  285. font-family: PingFangSC, PingFang SC;
  286. font-weight: 400;
  287. font-size: 26rpx;
  288. color: #1B50FF;
  289. line-height: 37rpx;
  290. margin-left: 8rpx;
  291. }
  292. }
  293. }
  294. .zt_btn {
  295. width: calc(100% - 120rpx);
  296. position: fixed;
  297. left: 60rpx;
  298. bottom: 201rpx;
  299. }
  300. .alert {
  301. padding: 54rpx 40rpx 0;
  302. .title {
  303. font-family: PingFang-SC, PingFang-SC;
  304. font-weight: bold;
  305. font-size: 36rpx;
  306. color: #111111;
  307. line-height: 36rpx;
  308. text-align: center;
  309. margin-bottom: 19rpx;
  310. }
  311. p {
  312. font-family: PingFangSC, PingFang SC;
  313. font-weight: 400;
  314. font-size: 30rpx;
  315. color: #111111;
  316. line-height: 65rpx;
  317. text-align: left;
  318. margin-top: 24rpx;
  319. }
  320. .btn {
  321. width: calc(100% - 108rpx);
  322. height: 90rpx;
  323. background: #D9F159;
  324. border-radius: 16rpx;
  325. font-family: PingFang-SC, PingFang-SC;
  326. font-weight: bold;
  327. font-size: 32rpx;
  328. color: #252525;
  329. line-height: 90rpx;
  330. text-align: center;
  331. margin: 113rpx 54rpx 0;
  332. letter-spacing: 2rpx;
  333. }
  334. }
  335. .connect {
  336. padding: 66rpx 40rpx 30rpx;
  337. .title {
  338. font-family: PingFangSC, PingFang SC;
  339. font-weight: 600;
  340. font-size: 36rpx;
  341. color: #111111;
  342. line-height: 36rpx;
  343. text-align: center;
  344. }
  345. image {
  346. width: 504rpx;
  347. height: 254rpx;
  348. margin-top: 37rpx;
  349. }
  350. .tip {
  351. font-family: PingFangSC, PingFang SC;
  352. font-weight: 400;
  353. font-size: 30rpx;
  354. color: #111111;
  355. line-height: 36rpx;
  356. margin-top: 40rpx;
  357. }
  358. .btn {
  359. width: 100%;
  360. height: 90rpx;
  361. background: #D9F159;
  362. border-radius: 16rpx;
  363. font-family: PingFang-SC, PingFang-SC;
  364. font-weight: bold;
  365. font-size: 32rpx;
  366. color: #252525;
  367. line-height: 90rpx;
  368. text-align: center;
  369. margin-top: 101rpx;
  370. letter-spacing: 2rpx;
  371. }
  372. }
  373. /* 公共样式,用于居中 */
  374. .adffcac{
  375. display: flex;
  376. flex-direction: column;
  377. align-items: center;
  378. }
  379. .adfac{
  380. display: flex;
  381. align-items: center;
  382. }
  383. </style>