index.vue 14 KB

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