index.vue 16 KB

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