reshui.vue 35 KB

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