reshui.vue 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318
  1. <template>
  2. <view class="content">
  3. <view class="container">
  4. <view class="header">
  5. <view class="scan" @tap="scan()" hover-class="scan_hover">
  6. <text class="iconfont icon-saoma"></text>
  7. <text>扫码连接</text>
  8. </view>
  9. <view class="qinshihao">
  10. <text class="iconfont icon-zhuye"></text>
  11. <text class="txt">{{dorm_number || '宿舍未知'}}</text>
  12. </view>
  13. </view>
  14. <view class="start_text">
  15. <view class="start_top">
  16. <view class="item_left">
  17. <text class="iconfont icon-shuibiao"></text>
  18. <text>开启设备</text>
  19. </view>
  20. <view>
  21. <switch :color="colorPrimary" :checked="isChecked" @change="switchChange" />
  22. </view>
  23. </view>
  24. <view class="start_mid">
  25. <view class="item_left">
  26. <text class="iconfont icon-qian"></text>
  27. <text class="start_mid_txt">¥{{amount}}</text>
  28. </view>
  29. <view class="start_payamount" @tap="chongzhi_yemian()" hover-class="scan_hover">
  30. <text>点击充值</text>
  31. <text class="iconfont icon-dayuhao"></text>
  32. </view>
  33. </view>
  34. <view class="start_bot">
  35. <text class="start_bot_txt" v-if="false">时长:00:00:00</text>
  36. <text class="start_bot_txt">当前结算消费:¥{{use_amount}}</text>
  37. </view>
  38. </view>
  39. <view class="start_up_process">
  40. <uni-steps :options="list1" :active-color="colorPrimary" :active="active" />
  41. </view>
  42. <view class="title">
  43. 消费记录
  44. </view>
  45. <view class="record-item">
  46. <view class="uni-list-cell-db">
  47. <picker mode="date" :value="date" fields="month" :start="startDate" :end="endDate"
  48. @change="bindDateChange">
  49. <view class="uni-input">
  50. <text>{{date}}</text>
  51. <text class="iconfont icon-arrow-down"></text>
  52. </view>
  53. </picker>
  54. </view>
  55. <view class="item-list" v-for="(item, i) in xiaofei_items" :key="i">
  56. <text class="item-list-txt"><text class="iconfont icon-shijian"></text>{{item.begin_time}}</text>
  57. <text class="item-list-txt"><text class="iconfont icon-shuidi"></text>{{item.use_size}}升</text>
  58. <text class="item-list-txt">{{item.use_amount}}</text>
  59. </view>
  60. </view>
  61. </view>
  62. <instructions v-if="flag" @parent_cancel="cancel"></instructions>
  63. </view>
  64. </template>
  65. <script>
  66. import instructions from '../../components/instructions.vue'
  67. export default {
  68. /**
  69. * 声明组件
  70. */
  71. components: {
  72. instructions
  73. },
  74. data() {
  75. return {
  76. colorPrimary: getApp().globalData.primaryColor,
  77. date: getDate({
  78. format: true
  79. }),
  80. startDate: getDate('start_date'),
  81. endDate: getDate('end_date'),
  82. flag: true,
  83. isUserClose: false, // 是否是用户自己关闭
  84. isChecked: false,
  85. isConnected: false, // 是否连接上
  86. isScan: false,
  87. active: -1,
  88. list1: [{
  89. title: '连接设备'
  90. }, {
  91. title: '连设备服务'
  92. }, {
  93. title: '数据通讯'
  94. }, {
  95. title: '启动'
  96. }],
  97. devices: [], // 保存设备
  98. device_code: '', // 设备编号
  99. deviceId: '',
  100. serviceId: '',
  101. characteristicIdForWrite: '',
  102. characteristicIdForNotity: '',
  103. deviceIsNotify: false,
  104. deviceIsWrite: false,
  105. byte35: '', // 70 个字符
  106. byte35_tmp: '', // 70 个字符 临时变量
  107. start_code: '',
  108. // start_code: '3459A8C660F1C284E41003E80000120000',
  109. // order_no: '', // 收集结算的订单号
  110. xiaofei_items: [],
  111. userinfo: '',
  112. card_number: '',
  113. user_name: '',
  114. use_amount: 0.0.toFixed(2), // 结算金额
  115. amount: 0.0.toFixed(2), // 余额
  116. dorm_number: '' // 宿舍号
  117. }
  118. },
  119. onLoad(options) {
  120. try {
  121. // 获取存储的用户数据
  122. const value = uni.getStorageSync('userinfo_storage_key')
  123. let item = decodeURIComponent(options.item)
  124. // console.log(value)
  125. // console.log(item)
  126. if (item === '{}' && value == '') { // 如果没有用户信息,返回首页
  127. uni.redirectTo({
  128. url: '../index/index'
  129. });
  130. return;
  131. }
  132. if (item != '') {
  133. // 处理JSON字符串
  134. // this.userinfo = item.replace(/"/g, "'");
  135. this.userinfo = JSON.parse(item);
  136. } else if (value != '') {
  137. // 处理JSON字符串
  138. // this.userinfo = value.replace(/"/g, "'");
  139. this.userinfo = value;
  140. }
  141. } catch (e) {
  142. console.log(e)
  143. }
  144. this.card_number = this.userinfo.card_number; // 卡号
  145. // 查询用户信息
  146. this.select_user_info()
  147. },
  148. methods: {
  149. /**
  150. * 选择消费年月
  151. */
  152. bindDateChange(e) {
  153. this.date = e.detail.value
  154. // 请求选定的月份消费记录
  155. this.request_consumption_records()
  156. },
  157. /**
  158. * 导航页面取消
  159. */
  160. cancel(param) {
  161. this.flag = param
  162. },
  163. /**
  164. * 请求选定的月份消费记录
  165. */
  166. async request_consumption_records() {
  167. if (this.card_number != '') {
  168. this.xiaofei_items = []
  169. const res = await this.$myRequest({
  170. host: 'code',
  171. url: '/HotWater/wxpayqueryConsume.action',
  172. method: 'POST',
  173. header: {
  174. 'content-type': 'application/x-www-form-urlencoded'
  175. },
  176. data: {
  177. card_number: this.card_number,
  178. begin_time: this.date
  179. }
  180. });
  181. // console.log(res.data.mess, 'request_consumption_records');
  182. if (typeof(res.data.mess) != 'undefined') {
  183. let items = res.data.mess
  184. // 按时间倒序排列
  185. // items.sort((a, b) => {
  186. // return a.begin_time < b.begin_time ? 1 : -1;
  187. // })
  188. for (var i = 0; i < items.length; i++) {
  189. // 过滤掉为0的记录,只显示消费大于0的记录
  190. // if (items[i].use_amount > 0.00) {
  191. this.xiaofei_items.push(items[i])
  192. // }
  193. }
  194. } else {
  195. uni.showToast({
  196. icon: 'success',
  197. title: '该月无消费记录'
  198. })
  199. }
  200. } else {
  201. uni.showToast({
  202. icon: 'success',
  203. title: '用户卡号为空'
  204. })
  205. }
  206. },
  207. /**
  208. * 查询用户信息
  209. */
  210. async select_user_info() {
  211. const res = await this.$myRequest({
  212. host: 'code',
  213. url: '/HotWater/userget_stu.action',
  214. method: 'POST',
  215. header: {
  216. 'content-type': 'application/x-www-form-urlencoded'
  217. },
  218. data: {
  219. card_number: this.userinfo.card_number
  220. }
  221. });
  222. // console.log(res, 'select_user_info');
  223. if (res.data.info) {
  224. // 将JSON字符串转化为JSON对象,取余额
  225. let userinfo = res.data.info[0]
  226. let amount = userinfo.balance // 余额
  227. let cnumber = this.userinfo.card_number
  228. this.user_name = userinfo.user_name;
  229. this.dorm_number = userinfo.dom // 宿舍号
  230. if (amount != 0) {
  231. this.amount = amount.toFixed(2)
  232. }
  233. if (cnumber == null || cnumber == undefined || cnumber == '') {
  234. // 之前未绑定用户信息,现在去绑定用户信息
  235. this.send_save_user_info()
  236. }
  237. // 请求选定的月份消费记录
  238. this.request_consumption_records()
  239. } else {
  240. uni.showToast({
  241. icon: 'success',
  242. title: '获取用户信息失败'
  243. })
  244. }
  245. },
  246. /**
  247. * 绑定用户信息
  248. */
  249. async send_save_user_info() {
  250. const res = await this.$myRequest({
  251. host: 'code',
  252. url: '/HotWater/userstu_info.action',
  253. method: 'POST',
  254. header: {
  255. 'content-type': 'application/x-www-form-urlencoded'
  256. },
  257. data: {
  258. user: JSON.stringify(this.userinfo)
  259. }
  260. });
  261. console.log('send_save_user_info:', res)
  262. let error = res.data.erro;
  263. if (error == '未获取到数据!') {
  264. uni.showToast({
  265. title: '绑定用户失败',
  266. duration: 2000
  267. })
  268. } else {
  269. uni.showToast({
  270. icon: 'success',
  271. title: '绑定用户成功',
  272. duration: 2000
  273. })
  274. }
  275. },
  276. /**
  277. * 控制进度条显示
  278. * @param {Object} active_value
  279. */
  280. process_control(active_value) {
  281. if (active_value <= this.list1.length - 1) {
  282. this.active = active_value
  283. }
  284. },
  285. /**
  286. * 开始或停止用水
  287. */
  288. switchChange(e) {
  289. this.isChecked = e.target.value
  290. if (this.isChecked) {
  291. if (this.deviceId && this.serviceId && this.devices.length != 0) {
  292. uni.showModal({
  293. title: '提示',
  294. content: '确定要启动设备?',
  295. success: (res) => {
  296. if (res.confirm) {
  297. this.isChecked = true
  298. } else if (res.cancel) {
  299. setTimeout(() => {
  300. this.isChecked = false
  301. }, 300)
  302. }
  303. }
  304. })
  305. // 开始连接匹配的蓝牙设备
  306. setTimeout(async () => {
  307. // 请求获取设备启动码
  308. await this.request_device_start_code()
  309. }, 3000)
  310. } else {
  311. uni.showToast({
  312. icon: 'none',
  313. title: '先【扫码连接】,后【开启设备】',
  314. duration: 1500
  315. })
  316. setTimeout(() => {
  317. this.isChecked = false
  318. }, 300)
  319. }
  320. } else {
  321. uni.showModal({
  322. title: '提示',
  323. content: '确定要关闭设备?',
  324. success: (res) => {
  325. if (res.confirm) {
  326. this.isChecked = false
  327. if (this.deviceId && this.serviceId) {
  328. this.send('stop')
  329. } else {
  330. uni.showToast({
  331. icon: 'none',
  332. title: '与设备连接中断!请在设备上手动停止即可!',
  333. duration: 5000
  334. })
  335. setTimeout(() => {
  336. this.isChecked = true
  337. }, 300)
  338. }
  339. } else if (res.cancel) {
  340. setTimeout(() => {
  341. this.isChecked = true
  342. }, 300)
  343. }
  344. }
  345. })
  346. }
  347. },
  348. /**
  349. * 跳转到充值页面并传参数
  350. */
  351. chongzhi_yemian() {
  352. //在起始页面跳转到recharge.vue页面并传递参数
  353. uni.navigateTo({
  354. url: '../recharge/recharge?item=' + encodeURIComponent(JSON.stringify(this.userinfo))
  355. });
  356. },
  357. /**
  358. * 111111111111111111111111111111
  359. * 扫码连接设备
  360. */
  361. scan() {
  362. // 启动最低金额限制
  363. if (this.amount < 10) {
  364. uni.showModal({
  365. title: '提示',
  366. content: '当前余额不足¥10元',
  367. cancelText: '我知道了',
  368. confirmText: '我要充值',
  369. success: (res) => {
  370. if (res.confirm) {
  371. // 跳转充值页面并传参数
  372. this.chongzhi_yemian()
  373. } else if (res.cancel) {
  374. return
  375. }
  376. }
  377. })
  378. } else {
  379. // 已经启动扫描连接设备中
  380. if (this.isScan) {
  381. uni.showToast({
  382. icon: 'none',
  383. title: '正在扫描,请耐心等待',
  384. })
  385. return
  386. }
  387. // 已经启动设备,不启动扫描
  388. if (this.isChecked) {
  389. uni.showToast({
  390. icon: 'none',
  391. title: '设备已启动,无需重新扫码',
  392. })
  393. return
  394. }
  395. if (this.deviceId) {
  396. uni.showModal({
  397. title: '提示',
  398. content: '设备已经连接,需要重新连接吗?',
  399. success: res => {
  400. if (res.confirm) {
  401. this.closeBLEConnection()
  402. this.scan_qr()
  403. } else if (res.cancel) {
  404. return
  405. }
  406. }
  407. })
  408. } else {
  409. this.scan_qr()
  410. }
  411. }
  412. },
  413. /**
  414. * 启动摄像头,扫描二维码
  415. */
  416. scan_qr() {
  417. uni.scanCode({
  418. onlyFromCamera: true,
  419. success: (res) => {
  420. this.isScan = true
  421. // console.log(res)
  422. // charSet: "utf-8"
  423. // codeVersion: 3
  424. // errMsg: "scanCode:ok"
  425. // rawData: "aHR0cDovL3Rvb2xzLjlrYnMuY29tLz93eG1pbmljb2RlPUtCNTgwMDAyNjUy"
  426. // result: "http://tools.9kbs.com/?wxminicode=KB580002652"
  427. // scanType: "QR_CODE"
  428. this.device_code = res.result.split('?')[1].split('=')[1]
  429. if (this.device_code === '') {
  430. uni.showToast({
  431. icon: 'success',
  432. title: '获取设备码失败',
  433. duration: 3000
  434. })
  435. this.isScan = false
  436. } else {
  437. // 初始化蓝牙
  438. this.openBluetoothAdapter()
  439. }
  440. }
  441. })
  442. },
  443. /**
  444. * 22222222222222222222222222222
  445. * 初始化蓝牙
  446. */
  447. openBluetoothAdapter() {
  448. uni.openBluetoothAdapter({
  449. success: (res) => { // 已打开
  450. uni.getBluetoothAdapterState({ // 蓝牙的匹配状态
  451. success: (res1) => {
  452. // console.log(res1, '“本机设备的蓝牙已打开”');
  453. // 本机设备的蓝牙已打开
  454. // 开始搜索蓝牙设备
  455. if (this.deviceId == '' && this.serviceId == '')
  456. this.startBluetoothDeviceDiscovery()
  457. },
  458. fail: (error) => {
  459. // console.log(error)
  460. uni.showToast({
  461. title: "请开启手机蓝牙",
  462. icon: "none",
  463. duration: 3000
  464. })
  465. this.isScan = false
  466. }
  467. });
  468. },
  469. fail: (err) => { // 未打开
  470. // console.log(err)
  471. this.isScan = false
  472. if (err.errCode == 10001) {
  473. uni.showModal({
  474. content: '请开启手机蓝牙!',
  475. showCancel: false,
  476. })
  477. } else {
  478. uni.showToast({
  479. title: err.errMsg,
  480. duration: 2000
  481. })
  482. }
  483. }
  484. });
  485. },
  486. /**
  487. * 333333333333333333333333333
  488. * 开始搜索蓝牙设备
  489. */
  490. startBluetoothDeviceDiscovery() {
  491. uni.startBluetoothDevicesDiscovery({ // 开始搜寻附近的蓝牙外围设备
  492. // services: ['FEE7'], // 要搜索但蓝牙设备主 service 的 uuid 列表
  493. allowDuplicatesKey: true,
  494. success: (res) => {
  495. this.process_control(0);
  496. this.onBluetoothDeviceFound() //监听搜索到新设备 同时进行
  497. },
  498. fail: err => {
  499. // console.log(err, '错误信息');
  500. uni.showToast({
  501. title: err.errMsg,
  502. duration: 2000
  503. })
  504. this.isScan = false
  505. }
  506. })
  507. },
  508. stopBluetoothDevicesDiscovery: function() { //停止搜索
  509. uni.stopBluetoothDevicesDiscovery({
  510. success: (res) => {
  511. // console.log(res, '停止蓝牙搜索')
  512. // uni.showToast({
  513. // title: '停止蓝牙搜索',
  514. // duration: 2000
  515. // })
  516. this.process_control(0);
  517. // 开始连接匹配的蓝牙设备
  518. this.createBLEConnection()
  519. },
  520. fail: e => {
  521. // console.log('停止搜索蓝牙设备失败,错误码:' + e.errCode);
  522. this.isScan = false
  523. uni.showToast({
  524. title: err.errCode,
  525. duration: 2000
  526. })
  527. }
  528. })
  529. },
  530. /**
  531. * 444444444444444444444444444444444
  532. * 发现外围设备
  533. */
  534. onBluetoothDeviceFound() {
  535. uni.onBluetoothDeviceFound((res) => {
  536. res.devices.forEach(device => { // 筛选找到的蓝牙中对名称匹配
  537. // 过滤掉没有名字的设备
  538. if (!device.name && !device.localName) {
  539. return
  540. }
  541. if (device.name == this.device_code) {
  542. //data里面建立一个deviceId、device_code,存储起来
  543. // this.device_code = device.name;
  544. this.deviceId = device.deviceId
  545. // 把搜索到的设备存储起来,如有需要可以在页面上展示
  546. if (this.devices.indexOf(res.devices[0].deviceId) == -1) {
  547. this.devices.push(res.devices[0].deviceId)
  548. }
  549. this.process_control(0);
  550. this.stopBluetoothDevicesDiscovery()
  551. }
  552. })
  553. });
  554. },
  555. /**
  556. * 555555555555555555555555555555555
  557. * 连接蓝牙设备
  558. */
  559. createBLEConnection() {
  560. //连接蓝牙
  561. uni.createBLEConnection({
  562. deviceId: this.deviceId,
  563. timeout: 3000,
  564. success: (res) => {
  565. // console.log(res)
  566. this.connect_BLE()
  567. },
  568. fail: (err) => {
  569. // console.log(err)
  570. this.isScan = false
  571. if (err.errMsg.indexOf('already connect') != -1) {
  572. this.connect_BLE()
  573. } else {
  574. uni.showToast({
  575. title: '建立连接失败',
  576. icon: 'none',
  577. duration: 3000,
  578. success: (res1) => {
  579. this.setInit()
  580. }
  581. })
  582. }
  583. }
  584. })
  585. },
  586. connect_BLE() {
  587. if (this.isConnected === false) {
  588. this.isConnected = true
  589. this.process_control(1);
  590. // 获取蓝牙设备所有服务(service)
  591. this.getBLEDeviceServices()
  592. }
  593. },
  594. /**
  595. * 6666666666666666666666666666666666
  596. * 获取蓝牙的所有服务
  597. */
  598. getBLEDeviceServices() {
  599. uni.getBLEDeviceServices({
  600. deviceId: this.deviceId,
  601. success: (res) => {
  602. // console.log(res)
  603. // res1.services.forEach((item) => {
  604. // this.getBLEDeviceCharacteristics()
  605. // })
  606. this.serviceId = res.services[1].uuid
  607. this.process_control(1);
  608. this.getBLEDeviceCharacteristics(this.deviceId, res.services[1].uuid)
  609. },
  610. fail: (err) => {
  611. // console.log(err)
  612. this.isScan = false
  613. uni.showToast({
  614. title: '获取服务失败',
  615. icon: 'none',
  616. duration: 3000
  617. })
  618. return
  619. }
  620. });
  621. },
  622. /**
  623. * 777777777777777777777777777777777
  624. * 获取蓝牙特征
  625. */
  626. getBLEDeviceCharacteristics() {
  627. // 获取蓝牙设备某个服务中所有特征值(characteristic)
  628. uni.getBLEDeviceCharacteristics({
  629. deviceId: this.deviceId,
  630. // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
  631. serviceId: this.serviceId,
  632. success: (res) => {
  633. this.process_control(2);
  634. // console.log(res)
  635. this.characteristicIdForWrite = res.characteristics[0].uuid
  636. this.characteristicIdForNotity = res.characteristics[1].uuid
  637. this.deviceIsWrite = res.characteristics[0].properties.write
  638. this.deviceIsNotify = res.characteristics[1].properties.notify
  639. // console.log(this.characteristicIdForWrite, this.characteristicIdForNotity)
  640. // 确保对应服务id下的特征值id具备监听数据变化的特性
  641. if (res.characteristics[1].properties.notify) {
  642. this.notifyBLECharacteristicValueChange()
  643. }
  644. },
  645. fail: (err) => {
  646. this.isScan = false
  647. console.error('获取蓝牙设备特征值失败(getBLEDeviceCharacteristics)', err)
  648. }
  649. });
  650. },
  651. /**
  652. * 888888888888888888888888888888888888
  653. * 启用 notify 功能
  654. */
  655. notifyBLECharacteristicValueChange() {
  656. // console.log(characteristicId, 'characteristicId')
  657. uni.notifyBLECharacteristicValueChange({
  658. state: true, // 启用 notify 功能
  659. deviceId: this.deviceId,
  660. // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
  661. serviceId: this.serviceId,
  662. // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
  663. characteristicId: this.characteristicIdForNotity,
  664. success: (res) => {
  665. this.process_control(2)
  666. this.isScan = false
  667. // console.log('通知启用(notifyBLECharacteristicValueChange)',res);
  668. this.onBLECharacteristicValueChange()
  669. setTimeout(() => {
  670. // 发送 AAABAC,采集数据(会返回 35字节 的数据)
  671. this.send('')
  672. }, 300)
  673. // // 请求设备信息
  674. // this.request_device_info()
  675. // // 显示设备启动授权金额
  676. // this.display_device_startup_amount()
  677. },
  678. fail: (err) => {
  679. // console.log(err)
  680. this.isScan = false
  681. uni.showModal({
  682. content: err.errMsg,
  683. showCancel: false
  684. })
  685. }
  686. })
  687. },
  688. // /**
  689. // * 请求设备信息
  690. // */
  691. // async request_device_info() {
  692. // const res = await this.$myRequest({
  693. // url: '',
  694. // data: {
  695. // }
  696. // })
  697. // console.log('', res)
  698. // },
  699. // /**
  700. // * 显示设备启动授权金额
  701. // */
  702. // display_device_startup_amount() {
  703. // // 待开发
  704. // },
  705. /**
  706. * 999999999999999999999999999999999
  707. * 监听低功耗蓝牙设备的特征值变化
  708. */
  709. onBLECharacteristicValueChange() {
  710. uni.onBLECharacteristicValueChange((characteristic) => {
  711. let condition = ab2hex(characteristic.value)
  712. // console.log(condition)
  713. if (condition.length > 2) {
  714. this.byte35_tmp += condition
  715. if (this.byte35_tmp.length > 40) {
  716. this.byte35 = this.byte35_tmp
  717. this.byte35_tmp = ''
  718. if (this.byte35.startsWith('ad')) {
  719. // console.log(this.byte35, '结束用水成功')
  720. // 关闭蓝牙连接
  721. this.closeBLEConnection()
  722. // 处理 35字节
  723. // APP 接收后,根据用户操作情况主要做如下几个方面的处理。
  724. // 1、向后台提交【结算数据】
  725. // 2、提交【结算数据】并生成【启动代码】
  726. // 3、提示:用户进行下面的流程
  727. // 请求结算
  728. this.request_pay()
  729. } else {
  730. // 发送 AAABAC,采集数据(会返回 35字节 的数据)
  731. // b58000265243eb3dd4bb3c5a2ab9042efcf0d2a30a000000000000000014392a0103bc
  732. // uni.showToast({
  733. // icon: 'success',
  734. // title: '采集数据成功!',
  735. // duration: 2000
  736. // })
  737. // console.log(this.byte35, '采集上一条消费数据')
  738. }
  739. }
  740. }
  741. switch (condition) {
  742. case 'bb':
  743. // uni.showToast({
  744. // title: '设备启动成功',
  745. // duration: 3000,
  746. // icon: 'none'
  747. // })
  748. this.process_control(3)
  749. this.isChecked = true
  750. break
  751. case 'bc':
  752. uni.showToast({
  753. title: '启动失败,设备检验启动代码不通过',
  754. duration: 3000,
  755. icon: 'none'
  756. })
  757. break
  758. case 'aa':
  759. uni.showToast({
  760. title: '设备正在使用',
  761. duration: 3000,
  762. icon: 'none'
  763. })
  764. break
  765. case 'ab':
  766. uni.showToast({
  767. title: '结束失败,设备对结束代码检验失败',
  768. duration: 3000,
  769. icon: 'none'
  770. })
  771. break
  772. default:
  773. break
  774. }
  775. })
  776. },
  777. /**
  778. * 关闭连接
  779. */
  780. closeBLEConnection() {
  781. uni.closeBLEConnection({
  782. deviceId: this.deviceId,
  783. success: () => {
  784. // 关闭蓝牙模块
  785. this.closeBluetoothAdapter()
  786. },
  787. fail: () => {
  788. uni.showToast({
  789. icon: 'none',
  790. title: '蓝牙连接断开失败',
  791. duration: 3000,
  792. success: (res1) => {
  793. this.setInit()
  794. }
  795. })
  796. }
  797. })
  798. },
  799. closeBluetoothAdapter() {
  800. // 关闭蓝牙模块
  801. uni.closeBluetoothAdapter({
  802. success: () => {
  803. this.setInit()
  804. if (this.isUserClose == false) { // 设备端结束用水
  805. if (this.isChecked) {
  806. // console.log('设备端已结束用水,蓝牙断开成功')
  807. uni.showToast({
  808. icon: 'none',
  809. title: '设备端已结束用水,蓝牙断开成功',
  810. duration: 3000
  811. })
  812. } else {
  813. // console.log('用户重启扫描,蓝牙断开成功')
  814. uni.showToast({
  815. icon: 'none',
  816. title: '用户重启扫描,蓝牙断开成功',
  817. duration: 3000
  818. })
  819. }
  820. } else { // 手机移动端结束用水
  821. // console.log('APP端已结束用水,蓝牙断开成功')
  822. uni.showToast({
  823. icon: 'none',
  824. title: 'APP端已结束用水,蓝牙断开成功',
  825. duration: 3000
  826. })
  827. }
  828. }
  829. })
  830. },
  831. setInit() {
  832. setTimeout(() => {
  833. this.isUserClose = false
  834. this.isChecked = false
  835. this.isConnected = false
  836. this.isScan = false
  837. this.active = -1
  838. this.devices = [] // 保存设备
  839. this.device_code = '' // 设备编号
  840. this.deviceId = ''
  841. this.serviceId = ''
  842. this.characteristicIdForWrite = ''
  843. this.characteristicIdForNotity = ''
  844. this.deviceIsNotify = false
  845. this.deviceIsWrite = false
  846. this.byte35 = '' // 70 个字符
  847. this.byte35_tmp = '' // 70 个字符 临时变量
  848. // this.start_code = ''
  849. })
  850. },
  851. /**
  852. * 10===========10==================10
  853. * 发送数据到设备
  854. */
  855. send(val) {
  856. let buffer
  857. let dataView
  858. let newArr
  859. if (val == 'start') { // 启动设备
  860. this.isUserClose = false
  861. this.process_control(3)
  862. // this.createBLEConnection()
  863. // 发送 17字节 启动代码(会返回 35字节 的数据)
  864. // b58000265243eb3dd4bb3c5a2ab9042efcf0d2a30a000000000000000014392a0103bc
  865. newArr = str2StrArray(this.start_code)
  866. } else if (val == 'stop') { // 结束当前使用,设备成功返回:AD+35字节 消费结算代码
  867. this.isUserClose = true
  868. // 发送 AAABAD+17字节 启动代码(会返回 ad+35字节 的数据)
  869. // adb58000265243eb3dd4bb3c5a2ab9042efcf0d2a30a000000000000000014392a0104bc
  870. newArr = ['AA', 'AB', 'AD'].concat(str2StrArray(this.start_code))
  871. } else {
  872. // 发送 AAABAC 采集数据(会返回 35字节 的数据)
  873. // b58000265243eb3dd4bb3c5a2ab9042efcf0d2a30a000000000000000014392a0103bc
  874. newArr = ['AA', 'AB', 'AC']
  875. }
  876. buffer = new ArrayBuffer(newArr.length)
  877. dataView = new DataView(buffer)
  878. newArr.forEach((item, i) => {
  879. dataView.setUint8(i, sixteen_to_ten(item))
  880. })
  881. // 写特征值数据到设备
  882. if (this.deviceIsWrite) {
  883. // 写数据到设备
  884. this.writeBLECharacteristicValue(val, buffer)
  885. } else {
  886. uni.showToast({
  887. title: '设备不可写',
  888. duration: 3000
  889. })
  890. }
  891. },
  892. /**
  893. * 11====================11=====================11
  894. * 写数据到设备
  895. */
  896. writeBLECharacteristicValue(val, buffer) {
  897. uni.writeBLECharacteristicValue({
  898. deviceId: this.deviceId,
  899. // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
  900. serviceId: this.serviceId,
  901. // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
  902. characteristicId: this.characteristicIdForWrite,
  903. // 这里的value是ArrayBuffer类型
  904. value: buffer,
  905. success: (res) => {
  906. // console.log(res)
  907. if (val == 'start') {
  908. setTimeout(() => {
  909. this.isChecked = true
  910. this.isConnected = true
  911. }, 300)
  912. this.process_control(3)
  913. } else if (val == 'stop') {
  914. setTimeout(() => {
  915. this.isChecked = false
  916. }, 300)
  917. }
  918. },
  919. fail: (err) => {
  920. // console.log(err)
  921. if (err.errCode == 10006) {
  922. setTimeout(() => {
  923. this.setInit()
  924. }, 300)
  925. if (this.isConnected) {
  926. uni.showToast({
  927. title: '设备连接已断开!请在水表上手动停止!',
  928. icon: 'none'
  929. })
  930. } else {
  931. uni.showToast({
  932. title: '设备连接失败!请重试扫描连接!',
  933. icon: 'none'
  934. })
  935. }
  936. }
  937. }
  938. })
  939. },
  940. /**
  941. * 请求获取设备启动码
  942. */
  943. async request_device_start_code() {
  944. const res = await this.$myRequest({
  945. host: 'code',
  946. url: '/HotWater/wxpaystart.action',
  947. data: {
  948. collect_code: this.byte35,
  949. card_number: this.card_number,
  950. user_name: this.user_name
  951. }
  952. })
  953. // console.log(res.data.start_code)
  954. if (res.data.start_code != '') {
  955. this.start_code = res.data.start_code
  956. this.send('start')
  957. } else {
  958. uni.showToast({
  959. icon: 'none',
  960. title: '未获得启动码',
  961. duration: 3000
  962. })
  963. }
  964. },
  965. /**
  966. * 请求结算
  967. */
  968. async request_pay() {
  969. const res = await this.$myRequest({
  970. host: 'code',
  971. url: '/HotWater/wxpayconsume.action',
  972. data: {
  973. collect_code: this.byte35.substring(2)
  974. }
  975. })
  976. // console.log(res)
  977. if (res.data.mess === "支付成功!") {
  978. uni.showToast({
  979. icon: 'success',
  980. title: '结算成功!',
  981. success: (res1) => {
  982. this.use_amount = res.data.use_amount.toFixed(2)
  983. this.amount = (this.amount - this.use_amount).toFixed(2)
  984. // 刷新选定的月份消费记录
  985. this.request_consumption_records()
  986. }
  987. })
  988. } else {
  989. uni.showToast({
  990. icon: 'success',
  991. title: '结算失败!',
  992. duration: 3000
  993. })
  994. }
  995. }
  996. }
  997. }
  998. /**
  999. * 返回字符处理
  1000. */
  1001. function str2StrArray(start_code) {
  1002. if (start_code.length != 34) {
  1003. console.log('启动码长度错误')
  1004. throw new Error("启动码长度错误");
  1005. return
  1006. }
  1007. var resultArray = new Array()
  1008. for (var i = 0; i < start_code.length; i += 2) {
  1009. resultArray.push(start_code.substr(i, 2));
  1010. }
  1011. return resultArray;
  1012. }
  1013. /**
  1014. * ArrayBuffer转16进度字符串
  1015. */
  1016. function ab2hex(buffer) {
  1017. const hexArr = Array.prototype.map.call(
  1018. new Uint8Array(buffer),
  1019. function(bit) {
  1020. // console.log('====================' + bit + '==========================')
  1021. return ('00' + bit.toString(16)).slice(-2)
  1022. }
  1023. )
  1024. return hexArr.join('');
  1025. }
  1026. /**
  1027. * 16进制转10进制
  1028. */
  1029. function sixteen_to_ten(str) {
  1030. return parseInt(str, 16);
  1031. }
  1032. /**
  1033. * 时间计算
  1034. */
  1035. function getDate(type) {
  1036. const date = new Date();
  1037. let year = date.getFullYear();
  1038. let month = date.getMonth() + 1;
  1039. // let day = date.getDate();
  1040. if (type === 'start_date') {
  1041. year = year - 4;
  1042. } else if (type === 'end_date') {
  1043. year = year;
  1044. }
  1045. month = month > 9 ? month : '0' + month;
  1046. // day = day > 9 ? day : '0' + day;
  1047. return `${year}-${month}`;
  1048. }
  1049. </script>
  1050. <style scoped lang="scss">
  1051. .content {
  1052. width: 100%;
  1053. height: 100%;
  1054. .container {
  1055. width: 100%;
  1056. padding: 20rpx 40rpx;
  1057. box-sizing: border-box;
  1058. .header {
  1059. display: flex;
  1060. justify-content: center;
  1061. position: relative;
  1062. height: 200rpx;
  1063. .scan {
  1064. display: flex;
  1065. flex-direction: column;
  1066. justify-content: center;
  1067. align-items: center;
  1068. height: 200rpx;
  1069. width: 30%;
  1070. .iconfont {
  1071. margin-bottom: 10rpx;
  1072. font-size: 100rpx;
  1073. color: $my-color-primary;
  1074. }
  1075. }
  1076. .scan_hover {
  1077. border-radius: 15rpx;
  1078. background-color: $my-color-btn-background;
  1079. }
  1080. .qinshihao {
  1081. position: fixed;
  1082. top: 20rpx;
  1083. right: 40rpx;
  1084. font-size: 28rpx;
  1085. .icon-zhuye {
  1086. margin-right: 10rpx;
  1087. font-family: Microsoft YaHei-3970(82674968);
  1088. color: $my-color-primary;
  1089. }
  1090. }
  1091. }
  1092. .start_text {
  1093. .start_top,
  1094. .start_mid {
  1095. display: flex;
  1096. justify-content: space-between;
  1097. height: 100rpx;
  1098. align-items: center;
  1099. border-bottom: 2rpx #ccc solid;
  1100. .item_left {
  1101. display: flex;
  1102. align-items: center;
  1103. }
  1104. .start_mid_txt {
  1105. color: #333;
  1106. font-size: 28rpx;
  1107. }
  1108. .iconfont {
  1109. font-size: 50rpx;
  1110. color: $my-color-primary;
  1111. margin-right: 20rpx;
  1112. }
  1113. .start_payamount {
  1114. display: flex;
  1115. justify-content: flex-end;
  1116. align-items: center;
  1117. font-size: 38rpx;
  1118. padding: 10rpx;
  1119. font-family: Microsoft YaHei-3970(82674968);
  1120. color: #B3B3B3;
  1121. border-radius: 10rpx;
  1122. .icon-dayuhao {
  1123. margin: 0 0 0 20rpx;
  1124. font-size: 38rpx;
  1125. color: #B3B3B3;
  1126. }
  1127. }
  1128. }
  1129. .start_bot {
  1130. display: flex;
  1131. justify-content: flex-end;
  1132. height: 100rpx;
  1133. align-items: center;
  1134. .start_bot_txt {
  1135. margin-left: 20rpx;
  1136. color: #333;
  1137. font-size: 28rpx;
  1138. }
  1139. }
  1140. }
  1141. .start_up_process {
  1142. padding: 20rpx 0;
  1143. color: #007AFF;
  1144. }
  1145. .title {
  1146. height: 100rpx;
  1147. margin: 0 -40rpx;
  1148. padding: 0 40rpx;
  1149. line-height: 100rpx;
  1150. color: #808080;
  1151. font-size: 32rpx;
  1152. background: #f2f2f2;
  1153. }
  1154. .record-item {
  1155. width: 100%;
  1156. .uni-list-cell-db {
  1157. height: 100rpx;
  1158. line-height: 100rpx;
  1159. font-weight: bold;
  1160. .uni-input {
  1161. display: flex;
  1162. align-items: center;
  1163. text:nth-child(1) {
  1164. margin-right: 10rpx;
  1165. }
  1166. }
  1167. }
  1168. .item-list {
  1169. height: 80rpx;
  1170. display: flex;
  1171. align-items: center;
  1172. justify-content: space-between;
  1173. border-top: 2rpx #ccc solid;
  1174. .icon-shijian {
  1175. margin-right: 8rpx;
  1176. }
  1177. .icon-shuidi {
  1178. margin-right: 3rpx;
  1179. }
  1180. .item-list-txt {
  1181. font-size: 28rpx;
  1182. &:nth-of-type(2) {
  1183. color: #515151;
  1184. }
  1185. &:nth-of-type(3) {
  1186. color: #f76200;
  1187. font-size: 32rpx;
  1188. }
  1189. }
  1190. }
  1191. }
  1192. }
  1193. }
  1194. </style>