index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  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. _this.loading = false
  186. }
  187. })
  188. }
  189. })
  190. },
  191. // 连接蓝牙设备
  192. connectBlue(item){
  193. if(this.loading){
  194. return
  195. }
  196. if(item){
  197. this.deviceId = item.deviceId
  198. }
  199. this.statusStr = '正在连接设备...'
  200. this.loading = true
  201. this.createBLEConnection()
  202. },
  203. // 断开蓝牙设备
  204. closeBlue(){
  205. this.statusStr = '连接已断开,请重新绑定设备'
  206. this.closeConnect()
  207. },
  208. // 开始连接指定设备
  209. createBLEConnection(){
  210. let _this = this
  211. uni.createBLEConnection({
  212. // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
  213. deviceId:this.deviceId,
  214. success(res) {
  215. console.log(res)
  216. _this.connectedDeviceId = _this.deviceId
  217. // 获取连接设备的service服务
  218. setTimeout(()=>{
  219. _this.getBLEDeviceServices()
  220. },1000)
  221. },
  222. fail(err) {
  223. console.log(err,'createBLEConnection error')
  224. _this.statusStr = '设备连接失败,请检查后重试'
  225. _this.connectStatus = false
  226. _this.loading = false
  227. uni.$emit('blueConnectCallback',0)
  228. uni.hideLoading()
  229. }
  230. })
  231. },
  232. // 获取连接设备的service服务
  233. getBLEDeviceServices(){
  234. let _this = this
  235. uni.getBLEDeviceServices({
  236. // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
  237. deviceId: _this.deviceId,
  238. success(res) {
  239. console.log('device services:', res.services)
  240. // _this.statusStr = '获取连接设备的service服务成功'
  241. _this.services = res.services
  242. /* 获取连接设备的所有特征值 */
  243. setTimeout(()=>{
  244. _this.getBLEDeviceCharacteristics()
  245. },500)
  246. // 停止搜索
  247. uni.stopBluetoothDevicesDiscovery({
  248. success: function(res) {
  249. console.log(res, '停止搜索')
  250. }
  251. })
  252. }
  253. })
  254. },
  255. // 获取连接设备的所有特征值
  256. getBLEDeviceCharacteristics(){
  257. let _this = this
  258. this.notifyServicweId = this.services[1].uuid
  259. uni.getBLEDeviceCharacteristics({
  260. deviceId: this.connectedDeviceId,
  261. serviceId: this.notifyServicweId,
  262. success: function(res) {
  263. console.log(res)
  264. // _this.statusStr = '获取连接设备的所有特征值成功'
  265. for (let i = 0; i < res.characteristics.length; i++) {
  266. let properties = res.characteristics[i].properties
  267. // 读id
  268. if(properties.notify&&!properties.read&&!properties.write){
  269. _this.readsId = res.characteristics[i].uuid
  270. }
  271. // 写id
  272. if(!properties.notify&&!properties.read&&properties.write){
  273. _this.writeId = res.characteristics[i].uuid
  274. }
  275. console.log(_this.readsId,_this.writeId, '蓝牙特征值 ==========')
  276. // 启用低功耗蓝牙设备特征值变化时的 notify 功能
  277. setTimeout(()=>{
  278. _this.notifyBLECharacteristicValueChange()
  279. },500)
  280. }
  281. },
  282. fail: function(res) {
  283. console.log(res)
  284. }
  285. })
  286. },
  287. // 启用低功耗蓝牙设备特征值变化时的 notify 功能
  288. notifyBLECharacteristicValueChange() {
  289. let that = this;
  290. uni.notifyBLECharacteristicValueChange({
  291. state: true,
  292. deviceId: that.connectedDeviceId,
  293. serviceId: that.notifyServicweId,
  294. characteristicId: that.readsId,
  295. success(res) {
  296. console.log(res,'notifyBLECharacteristicValueChange')
  297. that.statusStr = '连接成功,请投放称重'
  298. that.connectStatus = true
  299. that.loading = false
  300. uni.$emit('blueConnectCallback',1)
  301. uni.hideLoading()
  302. /*用来监听手机蓝牙设备的数据变化*/
  303. uni.onBLECharacteristicValueChange(function(res) {
  304. // console.log(res,'read data')
  305. that.balanceData = that.buf2string(res.value)
  306. that.$u.vuex('vuex_balanceData',that.balanceData)
  307. })
  308. // 该方法回调中可以用于处理连接意外断开等异常情况
  309. uni.onBLEConnectionStateChange(function (res) {
  310. console.log(`device ${res.deviceId} state has changed, connected: ${res.connected}`)
  311. if(!res.connected){
  312. uni.$emit('blueConnectClose',res)
  313. that.statusStr = '设备连接已断开,请重新连接'
  314. that.connectStatus = false
  315. that.loading = false
  316. }
  317. })
  318. },
  319. fail(res) {
  320. console.log(res, '启用低功耗蓝牙设备监听失败')
  321. that.statusStr = '设备连接失败,请点击重试'
  322. that.closeConnect()
  323. }
  324. })
  325. },
  326. /*转换成需要的格式*/
  327. buf2string(buffer) {
  328. let arr = Array.prototype.map.call(new Uint8Array(buffer), x => x)
  329. return arr.map((char, i) => {
  330. return String.fromCharCode(char);
  331. }).join('');
  332. },
  333. receiveData(buf) {
  334. return this.hexCharCodeToStr(this.ab2hex(buf))
  335. },
  336. /*转成二进制*/
  337. ab2hex (buffer) {
  338. let hexArr = Array.prototype.map.call(
  339. new Uint8Array(buffer), function (bit) {
  340. return ('00' + bit.toString(16)).slice(-2)
  341. }
  342. )
  343. return hexArr.join('')
  344. },
  345. /*转成可展会的文字*/
  346. hexCharCodeToStr(hexCharCodeStr) {
  347. let trimedStr = hexCharCodeStr.trim();
  348. let rawStr = trimedStr.substr(0, 2).toLowerCase() === '0x' ? trimedStr.substr(2) : trimedStr;
  349. let len = rawStr.length;
  350. let curCharCode;
  351. let resultStr = [];
  352. for (let i = 0; i < len; i = i + 2) {
  353. curCharCode = parseInt(rawStr.substr(i, 2), 16);
  354. resultStr.push(String.fromCharCode(curCharCode));
  355. }
  356. return resultStr.join('');
  357. },
  358. // 发送数据
  359. sendData(str) {
  360. let that = this;
  361. let dataBuffer = new ArrayBuffer(3)
  362. let dataView = new DataView(dataBuffer)
  363. dataView.setUint8(0, 0x02)
  364. dataView.setUint8(1, 0x41)
  365. dataView.setUint8(2, 0x03)
  366. let dataHex = that.ab2hex(dataBuffer);
  367. this.writeDatas = that.hexCharCodeToStr(dataHex);
  368. uni.writeBLECharacteristicValue({
  369. deviceId: that.connectedDeviceId,
  370. serviceId: that.notifyServicweId,
  371. characteristicId: that.writeId,
  372. value: dataBuffer,
  373. success: function (res) {
  374. console.log('发送数据成功:' + that.writeDatas)
  375. },
  376. fail: function (res) {
  377. console.log('发送失败:' + res)
  378. },
  379. complete: function (res) {
  380. }
  381. })
  382. },
  383. // 断开设备连接
  384. closeConnect() {
  385. let that = this;
  386. if (that.connectedDeviceId) {
  387. uni.closeBLEConnection({
  388. deviceId: that.connectedDeviceId,
  389. success: function(res) {
  390. console.log(res)
  391. that.deviceId = null
  392. that.closeBluetoothAdapter()
  393. },
  394. fail(res) {
  395. console.log(res)
  396. }
  397. })
  398. } else {
  399. that.closeBluetoothAdapter()
  400. }
  401. },
  402. // 关闭蓝牙模块
  403. closeBluetoothAdapter() {
  404. let that = this;
  405. uni.closeBluetoothAdapter({
  406. success: function(res) {
  407. console.log(res,'closeBluetoothAdapter')
  408. that.connectStatus = false
  409. that.loading = false
  410. that.balanceData = ''
  411. that.deviceList = []
  412. },
  413. fail: function(err) {
  414. }
  415. })
  416. },
  417. },
  418. onUnload() {
  419. this.closeConnect()
  420. }
  421. }
  422. </script>
  423. <style>
  424. .content{
  425. display: flex;
  426. align-items: center;
  427. justify-content: center;
  428. flex-direction: column;
  429. background-image: radial-gradient( #2ab4e5 0%, #0081c1 100%);
  430. height: 100vh;
  431. width: 100%;
  432. position: relative;
  433. }
  434. .tdrecord{
  435. position: absolute;
  436. right: 0;
  437. top: 5%;
  438. background: rgba(255,255,255,0.8);
  439. border-radius: 150rpx 0 0 150rpx;
  440. box-shadow: 2rpx 2rpx 6rpx rgba(0,0,0,0.4);
  441. padding: 15rpx 20rpx 15rpx 30rpx;
  442. font-size: 26rpx;
  443. }
  444. .cz-box{
  445. text-align: center;
  446. background: #fff;
  447. border-radius: 300rpx;
  448. box-shadow: 3rpx 3rpx 10rpx rgba(0,0,0,0.4);
  449. }
  450. .cz-box > view{
  451. width: 250rpx;
  452. height: 250rpx;
  453. padding: 50rpx 50rpx 30rpx;
  454. display: flex;
  455. align-items: center;
  456. justify-content: center;
  457. flex-direction: column;
  458. }
  459. .tits{
  460. padding: 20rpx 0;
  461. color: #ef9f2e;
  462. }
  463. .devList{
  464. width: 80%;
  465. padding:10rpx 30rpx;
  466. }
  467. .bindDev{
  468. margin: 20rpx 0;
  469. padding: 20rpx 40rpx;
  470. background: rgba(255,255,255,0.8);
  471. box-shadow: 2rpx 2rpx 8rpx rgba(0,0,0,0.2);
  472. border-radius: 100rpx;
  473. color: #666666;
  474. }
  475. .devList > view{
  476. display: flex;
  477. align-items: center;
  478. color: #666666;
  479. border-radius: 20rpx;
  480. background: rgba(255,255,255,0.8);
  481. box-shadow: 2rpx 2rpx 5rpx rgba(0,0,0,0.2);
  482. }
  483. .devList > view .names{
  484. flex-grow: 1;
  485. padding: 15rpx 40rpx;
  486. }
  487. .devList > view .texts{
  488. font-size: 24rpx;
  489. }
  490. .devList > view .btns{
  491. text-align: center;
  492. padding: 15rpx 40rpx;
  493. color: #ff5500;
  494. }
  495. .turn{
  496. animation:turn 1s linear infinite;
  497. }
  498. @keyframes turn{
  499. 0%{-webkit-transform:rotate(0deg);}
  500. 25%{-webkit-transform:rotate(90deg);}
  501. 50%{-webkit-transform:rotate(180deg);}
  502. 75%{-webkit-transform:rotate(270deg);}
  503. 100%{-webkit-transform:rotate(360deg);}
  504. }
  505. </style>