index.vue 16 KB

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