reshui.vue 35 KB

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