index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. <template>
  2. <view class="bg">
  3. <!-- 背景动画 -->
  4. <view class="windmill">
  5. <view class="windmill__sun"></view>
  6. <view class="windmill__slice-4">
  7. <view></view>
  8. </view>
  9. <view class="windmill__slice-3">
  10. <view class="windmill__tree-left"></view>
  11. <view class="windmill__tree-right"></view>
  12. </view>
  13. <view class="windmill__slice-2">
  14. <view class="windmill__tree-right"></view>
  15. <view class="windmill__tree-middle"></view>
  16. <view class="windmill__tree-left"></view>
  17. </view><view class="windmill__clouds">
  18. <view class="windmill__cloud"></view>
  19. <view class="windmill__cloud"></view>
  20. <view class="windmill__cloud"></view>
  21. <view class="windmill__cloud"></view>
  22. <view class="windmill__cloud"></view>
  23. <view class="windmill__cloud"></view>
  24. </view>
  25. <!-- <view class="windmill__windmill">
  26. <view class="windmill__blades">
  27. <view class="windmill__blade"></view>
  28. <view class="windmill__blade"></view>
  29. <view class="windmill__blade"></view>
  30. <view class="windmill__blade"></view>
  31. </view>
  32. </view> -->
  33. <view class="windmill__snowflakes">
  34. <view class="windmill__snowflake"></view>
  35. <view class="windmill__snowflake"></view>
  36. <view class="windmill__snowflake"></view>
  37. <view class="windmill__snowflake"></view>
  38. <view class="windmill__snowflake"></view>
  39. <view class="windmill__snowflake"></view>
  40. <view class="windmill__snowflake"></view>
  41. <view class="windmill__snowflake"></view>
  42. <view class="windmill__snowflake"></view>
  43. <view class="windmill__snowflake"></view>
  44. <view class="windmill__snowflake"></view>
  45. <view class="windmill__snowflake"></view>
  46. <view class="windmill__snowflake"></view>
  47. <view class="windmill__snowflake"></view>
  48. <view class="windmill__snowflake"></view>
  49. <view class="windmill__snowflake"></view>
  50. <view class="windmill__snowflake"></view>
  51. <view class="windmill__snowflake"></view>
  52. <view class="windmill__snowflake"></view>
  53. <view class="windmill__snowflake"></view>
  54. <view class="windmill__snowflake"></view>
  55. <view class="windmill__snowflake"></view>
  56. </view>
  57. </view>
  58. <!-- 称重 -->
  59. <view class="content">
  60. <view @click="toTdRecord" class="tdrecord">投递记录</view>
  61. <view class="cz-box" @click="toWork">
  62. <view :class="loading?'turn':''">
  63. <u-image width="100rpx" height="100rpx" src="/static/chengzhong.png"></u-image>
  64. <view class="tits">投放称重</view>
  65. </view>
  66. </view>
  67. <view class="bindDev" @click="toInit">
  68. {{statusStr}}
  69. </view>
  70. <view class="devList">
  71. <view v-for="item in deviceList" :key="item.deviceId">
  72. <view class="names">
  73. <view>设备名称:{{item.name}}</view>
  74. <view class="texts">MAC:{{item.deviceId}}</view>
  75. </view>
  76. <view class="btns" @click="connectBlue(item)" v-if="!connectStatus">点击连接</view>
  77. <view class="btns" @click="closeBlue" v-else>断开连接</view>
  78. </view>
  79. </view>
  80. </view>
  81. </view>
  82. </template>
  83. <script>
  84. export default {
  85. data() {
  86. return {
  87. deviceList:[], // 设备列表
  88. deviceId: null,
  89. connectedDeviceId: '',
  90. services: [],
  91. readsId:'',
  92. writeId:'',
  93. notifyServicweId: '',
  94. writeDatas: '',
  95. balanceData: '',
  96. hexstr: '',
  97. statusStr: '绑定电子秤',
  98. connectStatus: false ,// 设备连接状态
  99. loading: false ,// 正在连接
  100. }
  101. },
  102. onLoad() {
  103. console.log('onload')
  104. let _this = this
  105. uni.$on('blueReConnect',function(){
  106. uni.showLoading({
  107. mask: true,
  108. title: '正在重连中...'
  109. })
  110. _this.connectBlue()
  111. })
  112. },
  113. onUnload() {
  114. uni.$off('blueReConnect')
  115. },
  116. methods: {
  117. // 查看投递记录
  118. toTdRecord(){
  119. uni.navigateTo({
  120. url: '/pages/tdRecord/tdRecord'
  121. })
  122. },
  123. // 去认证用户并开始称重
  124. toWork(){
  125. let _this = this
  126. // 连接成功
  127. if(this.connectStatus){
  128. uni.showModal({
  129. title:'提示',
  130. content:'请检查物品是否已经放在电子秤上?',
  131. complete(e) {
  132. console.log(e)
  133. if(e.confirm){
  134. uni.navigateTo({
  135. url: '/pages/userAuth/userAuth?deviceId=' + _this.deviceId
  136. })
  137. }
  138. }
  139. })
  140. }else{
  141. let hasDev = this.deviceList.length
  142. if(!this.loading){
  143. uni.showModal({
  144. title:'提示',
  145. content:hasDev ? '请先连接设备':'请先绑定设备',
  146. confirmText: hasDev ? '确定':'去绑定',
  147. complete(e) {
  148. console.log(e)
  149. if(e.confirm){
  150. if(hasDev == 1){
  151. _this.connectBlue(_this.deviceList[0])
  152. }
  153. if(hasDev == 0){
  154. _this.toInit()
  155. }
  156. }
  157. }
  158. })
  159. }
  160. }
  161. },
  162. // 初始化蓝牙
  163. toInit(){
  164. let _this = this
  165. console.log(this.connectStatus,this.deviceList.length,this.loading)
  166. if(this.connectStatus || this.deviceList.length > 0 || this.loading){
  167. return
  168. }
  169. uni.openBluetoothAdapter({
  170. success(res) {
  171. console.log(res, 'openBluetoothAdapter success')
  172. _this.onBluetoothAdapterStateChange()
  173. _this.loading = true
  174. setTimeout(()=>{
  175. _this.toFindDev()
  176. },600)
  177. },
  178. fail(err){
  179. console.log(err,'openBluetoothAdapter error')
  180. _this.loading = false
  181. uni.showModal({
  182. title:'提示',
  183. content:'请开启手机蓝牙',
  184. showCancel: false,
  185. complete(e) {
  186. console.log(e)
  187. _this.statusStr = '请开启手机蓝牙'
  188. }
  189. })
  190. }
  191. })
  192. },
  193. toFindDev(){
  194. let _this = this
  195. _this.statusStr = '开始搜寻附近的电子秤,请确认设备已开启'
  196. uni.getBluetoothAdapterState({
  197. success(res) {
  198. console.log(res)
  199. if(res.available){
  200. // 开始搜寻附近的蓝牙外围设备
  201. _this.startBluetoothDevicesDiscovery()
  202. }else{
  203. _this.statusStr = '请开启手机蓝牙'
  204. }
  205. },
  206. fail(err) {
  207. console.log(err)
  208. }
  209. })
  210. },
  211. // 监听蓝牙状态
  212. onBluetoothAdapterStateChange(){
  213. let _this = this
  214. // 监听蓝牙适配器状态变化事件
  215. uni.onBluetoothAdapterStateChange(function (res) {
  216. console.log(res,'适配器变化')
  217. if(!res.available){
  218. // 关闭连接
  219. _this.statusStr = '蓝牙已关闭,请开启手机蓝牙'
  220. _this.connectedDeviceId = null
  221. _this.closeConnect()
  222. }else{
  223. if(!res.discovering && !_this.loading){
  224. _this.statusStr = '蓝牙已启用,请绑定设备'
  225. }
  226. }
  227. })
  228. },
  229. startBluetoothDevicesDiscovery(){
  230. let _this = this
  231. uni.startBluetoothDevicesDiscovery({
  232. services: [],
  233. success(res) {
  234. console.log(res)
  235. // 监听寻找到新设备的事件
  236. uni.onBluetoothDeviceFound(function (res) {
  237. console.log(res,'devices list')
  238. let devices = res.devices.filter(item => item.name == 'FAYA')
  239. console.log(devices,'devices list')
  240. if(devices.length){
  241. _this.statusStr = '已发现设备'
  242. _this.deviceList = res.devices
  243. if(res.devices.length == 1){
  244. _this.connectBlue(_this.deviceList[0])
  245. }else{
  246. _this.loading = false
  247. }
  248. }
  249. })
  250. }
  251. })
  252. },
  253. // 连接蓝牙设备
  254. connectBlue(item){
  255. if(this.connectStatus){
  256. return
  257. }
  258. if(item){
  259. this.deviceId = item.deviceId
  260. }
  261. this.statusStr = '正在连接设备...'
  262. this.loading = true
  263. this.createBLEConnection()
  264. },
  265. // 断开蓝牙设备
  266. closeBlue(){
  267. this.statusStr = '连接已断开,请重新绑定设备'
  268. this.closeConnect()
  269. },
  270. // 开始连接指定设备
  271. createBLEConnection(){
  272. let _this = this
  273. uni.createBLEConnection({
  274. // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
  275. deviceId:this.deviceId,
  276. success(res) {
  277. console.log(res)
  278. _this.connectedDeviceId = _this.deviceId
  279. // 获取连接设备的service服务
  280. setTimeout(()=>{
  281. _this.getBLEDeviceServices()
  282. },1000)
  283. },
  284. fail(err) {
  285. console.log(err,'createBLEConnection error')
  286. _this.statusStr = '设备连接失败,请检查后重试'
  287. _this.connectStatus = false
  288. _this.loading = false
  289. uni.$emit('blueConnectCallback',0)
  290. uni.hideLoading()
  291. }
  292. })
  293. },
  294. // 获取连接设备的service服务
  295. getBLEDeviceServices(){
  296. let _this = this
  297. uni.getBLEDeviceServices({
  298. // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
  299. deviceId: _this.deviceId,
  300. success(res) {
  301. console.log('device services:', res.services)
  302. // _this.statusStr = '获取连接设备的service服务成功'
  303. _this.services = res.services
  304. /* 获取连接设备的所有特征值 */
  305. setTimeout(()=>{
  306. _this.getBLEDeviceCharacteristics()
  307. },500)
  308. // 停止搜索
  309. uni.stopBluetoothDevicesDiscovery({
  310. success: function(res) {
  311. console.log(res, '停止搜索')
  312. }
  313. })
  314. }
  315. })
  316. },
  317. // 获取连接设备的所有特征值
  318. getBLEDeviceCharacteristics(){
  319. let _this = this
  320. this.notifyServicweId = this.services[1].uuid
  321. uni.getBLEDeviceCharacteristics({
  322. deviceId: this.connectedDeviceId,
  323. serviceId: this.notifyServicweId,
  324. success: function(res) {
  325. console.log(res)
  326. // _this.statusStr = '获取连接设备的所有特征值成功'
  327. for (let i = 0; i < res.characteristics.length; i++) {
  328. let properties = res.characteristics[i].properties
  329. // 读id
  330. if(properties.notify&&!properties.read&&!properties.write){
  331. _this.readsId = res.characteristics[i].uuid
  332. }
  333. // 写id
  334. if(!properties.notify&&!properties.read&&properties.write){
  335. _this.writeId = res.characteristics[i].uuid
  336. }
  337. console.log(_this.readsId,_this.writeId, '蓝牙特征值 ==========')
  338. // 启用低功耗蓝牙设备特征值变化时的 notify 功能
  339. setTimeout(()=>{
  340. _this.notifyBLECharacteristicValueChange()
  341. },500)
  342. }
  343. },
  344. fail: function(res) {
  345. console.log(res)
  346. }
  347. })
  348. },
  349. // 启用低功耗蓝牙设备特征值变化时的 notify 功能
  350. notifyBLECharacteristicValueChange() {
  351. let that = this;
  352. uni.notifyBLECharacteristicValueChange({
  353. state: true,
  354. deviceId: that.connectedDeviceId,
  355. serviceId: that.notifyServicweId,
  356. characteristicId: that.readsId,
  357. success(res) {
  358. console.log(res,'notifyBLECharacteristicValueChange')
  359. that.statusStr = '连接成功,请投放称重'
  360. that.connectStatus = true
  361. that.loading = false
  362. uni.$emit('blueConnectCallback',1)
  363. uni.hideLoading()
  364. /*用来监听手机蓝牙设备的数据变化*/
  365. uni.onBLECharacteristicValueChange(function(res) {
  366. // console.log(res,'read data')
  367. that.balanceData = that.buf2string(res.value)
  368. that.$u.vuex('vuex_balanceData',that.balanceData)
  369. })
  370. // 该方法回调中可以用于处理连接意外断开等异常情况
  371. uni.onBLEConnectionStateChange(function (res) {
  372. console.log(`device ${res.deviceId} state has changed, connected: ${res.connected}`)
  373. if(!res.connected){
  374. uni.$emit('blueConnectClose',res)
  375. that.statusStr = '设备连接已断开,请重新连接'
  376. that.connectStatus = false
  377. that.loading = false
  378. }
  379. })
  380. },
  381. fail(res) {
  382. console.log(res, '启用低功耗蓝牙设备监听失败')
  383. that.statusStr = '设备连接失败,请点击重试'
  384. that.closeConnect()
  385. }
  386. })
  387. },
  388. /*转换成需要的格式*/
  389. buf2string(buffer) {
  390. let arr = Array.prototype.map.call(new Uint8Array(buffer), x => x)
  391. return arr.map((char, i) => {
  392. return String.fromCharCode(char);
  393. }).join('');
  394. },
  395. receiveData(buf) {
  396. return this.hexCharCodeToStr(this.ab2hex(buf))
  397. },
  398. /*转成二进制*/
  399. ab2hex (buffer) {
  400. let hexArr = Array.prototype.map.call(
  401. new Uint8Array(buffer), function (bit) {
  402. return ('00' + bit.toString(16)).slice(-2)
  403. }
  404. )
  405. return hexArr.join('')
  406. },
  407. /*转成可展会的文字*/
  408. hexCharCodeToStr(hexCharCodeStr) {
  409. let trimedStr = hexCharCodeStr.trim();
  410. let rawStr = trimedStr.substr(0, 2).toLowerCase() === '0x' ? trimedStr.substr(2) : trimedStr;
  411. let len = rawStr.length;
  412. let curCharCode;
  413. let resultStr = [];
  414. for (let i = 0; i < len; i = i + 2) {
  415. curCharCode = parseInt(rawStr.substr(i, 2), 16);
  416. resultStr.push(String.fromCharCode(curCharCode));
  417. }
  418. return resultStr.join('');
  419. },
  420. // 发送数据
  421. sendData(str) {
  422. let that = this;
  423. let dataBuffer = new ArrayBuffer(3)
  424. let dataView = new DataView(dataBuffer)
  425. dataView.setUint8(0, 0x02)
  426. dataView.setUint8(1, 0x41)
  427. dataView.setUint8(2, 0x03)
  428. let dataHex = that.ab2hex(dataBuffer);
  429. this.writeDatas = that.hexCharCodeToStr(dataHex);
  430. uni.writeBLECharacteristicValue({
  431. deviceId: that.connectedDeviceId,
  432. serviceId: that.notifyServicweId,
  433. characteristicId: that.writeId,
  434. value: dataBuffer,
  435. success: function (res) {
  436. console.log('发送数据成功:' + that.writeDatas)
  437. },
  438. fail: function (res) {
  439. console.log('发送失败:' + res)
  440. },
  441. complete: function (res) {
  442. }
  443. })
  444. },
  445. // 断开设备连接
  446. closeConnect() {
  447. let that = this;
  448. if (that.connectedDeviceId) {
  449. uni.closeBLEConnection({
  450. deviceId: that.connectedDeviceId,
  451. success: function(res) {
  452. console.log(res)
  453. that.deviceId = null
  454. that.closeBluetoothAdapter()
  455. },
  456. fail(res) {
  457. console.log(res)
  458. }
  459. })
  460. } else {
  461. that.closeBluetoothAdapter()
  462. }
  463. },
  464. // 关闭蓝牙模块
  465. closeBluetoothAdapter() {
  466. let that = this;
  467. uni.closeBluetoothAdapter({
  468. success: function(res) {
  469. console.log(res,'closeBluetoothAdapter')
  470. that.connectStatus = false
  471. that.loading = false
  472. that.balanceData = ''
  473. that.deviceList = []
  474. },
  475. fail: function(err) {
  476. }
  477. })
  478. },
  479. },
  480. onUnload() {
  481. this.closeConnect()
  482. }
  483. }
  484. </script>
  485. <style lang="less">
  486. @import url("bg.less");
  487. .bg{
  488. height: 100vh;
  489. width: 100%;
  490. position: relative;
  491. }
  492. .content{
  493. display: flex;
  494. align-items: center;
  495. justify-content: center;
  496. flex-direction: column;
  497. height: 100vh;
  498. width: 100%;
  499. position: absolute;
  500. z-index: 1000;
  501. }
  502. .tdrecord{
  503. position: absolute;
  504. right: 0;
  505. top: 5%;
  506. background: rgba(255,255,255,0.8);
  507. border-radius: 150rpx 0 0 150rpx;
  508. box-shadow: 2rpx 2rpx 6rpx rgba(0,0,0,0.4);
  509. padding: 15rpx 20rpx 15rpx 30rpx;
  510. font-size: 26rpx;
  511. }
  512. .cz-box{
  513. text-align: center;
  514. background: #fff;
  515. border-radius: 300rpx;
  516. box-shadow: 3rpx 3rpx 10rpx rgba(0,0,0,0.4);
  517. }
  518. .cz-box > view{
  519. width: 250rpx;
  520. height: 250rpx;
  521. padding: 50rpx 50rpx 30rpx;
  522. display: flex;
  523. align-items: center;
  524. justify-content: center;
  525. flex-direction: column;
  526. }
  527. .tits{
  528. padding: 20rpx 0;
  529. color: #ef9f2e;
  530. }
  531. .devList{
  532. width: 80%;
  533. padding:10rpx 30rpx;
  534. }
  535. .bindDev{
  536. margin: 20rpx 0;
  537. padding: 20rpx 40rpx;
  538. background: rgba(255,255,255,0.8);
  539. box-shadow: 2rpx 2rpx 8rpx rgba(0,0,0,0.2);
  540. border-radius: 100rpx;
  541. color: #666666;
  542. }
  543. .devList > view{
  544. display: flex;
  545. align-items: center;
  546. color: #666666;
  547. border-radius: 20rpx;
  548. background: rgba(255,255,255,0.8);
  549. box-shadow: 2rpx 2rpx 5rpx rgba(0,0,0,0.2);
  550. }
  551. .devList > view .names{
  552. flex-grow: 1;
  553. padding: 15rpx 40rpx;
  554. }
  555. .devList > view .texts{
  556. font-size: 24rpx;
  557. }
  558. .devList > view .btns{
  559. text-align: center;
  560. padding: 15rpx 40rpx;
  561. color: #ff5500;
  562. }
  563. .turn{
  564. animation:turn 1s linear infinite;
  565. }
  566. @keyframes turn{
  567. 0%{-webkit-transform:rotate(0deg);}
  568. 25%{-webkit-transform:rotate(90deg);}
  569. 50%{-webkit-transform:rotate(180deg);}
  570. 75%{-webkit-transform:rotate(270deg);}
  571. 100%{-webkit-transform:rotate(360deg);}
  572. }
  573. </style>