index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. <template>
  2. <view class="kk-printer">
  3. <view class="kk-printer-btn">
  4. <u-button v-if="printBtnStyle=='blue-large'" @tap="handlePrintTap(1)" :loading="isPrinting||isConnecting" shape="circle" :custom-style="{background:$config('primaryColor')}" type="primary">{{isPrinting?printingText:defaultText}}</u-button>
  5. <u-button v-if="printBtnStyle=='default-mid'" shape="circle" size="medium" hover-class="none" @tap="handlePrintTap(1)" :loading="isPrinting||isConnecting">{{isPrinting?printingText:defaultText}}</u-button>
  6. </view>
  7. <view class="kk-shadow" :class="isShowSearch?'show':''" @tap="handleSearchClose">
  8. <view class="kk-modal" @tap.stop="doNothing">
  9. <view class="kk-search-device">
  10. <!-- <view class="kk-filter-wrap">
  11. <view class="filter-title">根据SRRI过滤设备</view>
  12. <slider @change="handleSRRIChange" max='-20' min='-100' value="-95" show-value/>
  13. </view> -->
  14. <!-- <view class="kk-filter-wrap">
  15. <view class="filter-title">根据蓝牙名过滤</view>
  16. <input type="text" placeholder-class="kk-placeholder-class" placeholder="请输入蓝牙名字或设备ID搜索" v-model="filterName" />
  17. </view> -->
  18. <view style="text-align: left;">
  19. <view>注意事项:</view>
  20. <view>1、连接打印机之前,先打开手机蓝牙开关</view>
  21. <view>2、请检查打印机是否已被其它手机连接,如果是请关闭其它手机蓝牙,然后再用您的手机连接</view>
  22. </view>
  23. <view class="kk-btn-wrap">
  24. <view class="kk-btn-item confirm-btn" @tap="searchBtnTap" v-if="!isSearching">
  25. 搜索设备
  26. </view>
  27. <view class="kk-btn-item confirm-btn" v-else>
  28. 搜索中...
  29. </view>
  30. <view class="kk-btn-item" @tap="stopSearchBtnTap">
  31. 停止搜索
  32. </view>
  33. </view>
  34. <view class="kk-devices-wrap">
  35. <view class="empty-wrap" v-if="filterDeviceList.length <= 0">
  36. <view class="empty-icon"></view>
  37. <view class="empty-text">~ 无可搜索到的设备 ~</view>
  38. </view>
  39. <view class="" v-else>
  40. <view class="kk-devices-item flex align_center justify_between" v-for="(item,index) in filterDeviceList" :key="index" @tap="handleConnectDevice(item)">
  41. <view style="text-align: left;flex-grow: 1;">
  42. <view class="name">
  43. <text>设备名称:</text>
  44. <text>{{item.name?item.name:'未命名'}}</text>
  45. </view>
  46. <view class="rssi">
  47. <text>信号强度:</text>
  48. <text>({{Math.max(0, item.RSSI + 100)}}%)</text>
  49. </view>
  50. <view class="deviceid">
  51. <text>设备ID:</text>
  52. <text>{{item.deviceId}}</text>
  53. </view>
  54. </view>
  55. <view style="width: 120rpx;">
  56. <!-- <u-icon :size="60" color="#55aaff" name="plus-circle"></u-icon> -->
  57. <u-button @tap="handleConnectDevice(item)" :loading="isConnecting" shape="circle" size="mini" :custom-style="{background:$config('primaryColor')}" type="primary">{{isConnecting?'连接中':'连接'}}</u-button>
  58. </view>
  59. </view>
  60. </view>
  61. </view>
  62. <view style="margin-top: 20upx;">
  63. <view v-if="!isConnecting" style="text-align: center;padding: 20upx;border:2upx solid #eee;border-radius: 15upx;" @tap="handleSearchClose">取消打印</view>
  64. </view>
  65. </view>
  66. </view>
  67. </view>
  68. </view>
  69. </template>
  70. <script>
  71. import tsc from '@/components/kk-printer/printer/tsc.js'
  72. // import esc from '@/components/kk-printer/printer/esc.js'
  73. import * as blesdk from './utils/bluetoolth';
  74. import util from './utils/util.js';
  75. export default {
  76. data(){
  77. return{
  78. //是否正在打印
  79. isPrinting:false,
  80. //是否正在搜索设备
  81. isSearching:false,
  82. // 是否正在连接
  83. isConnecting: false,
  84. //是否显示蓝牙列表
  85. isShowSearch:false,
  86. //按蓝牙名过滤
  87. filterName:'DL-',
  88. //按信号过滤
  89. filterRSSI:-95,
  90. //设备列表
  91. devicesList:[],
  92. //连接的设备ID
  93. deviceId:'',
  94. //根据设备ID获取的服务
  95. services:'',
  96. //获取特征值时返回的三要素
  97. serviceId: '',
  98. writeId: '',
  99. readId: ''
  100. }
  101. },
  102. props:{
  103. //按钮默认文字
  104. defaultText:{
  105. type:String,
  106. default:'快速打印'
  107. },
  108. //按钮打印中的文字
  109. printingText:{
  110. type:String,
  111. default:'打印中...'
  112. },
  113. // 按钮样式
  114. printBtnStyle:{
  115. type: String,
  116. default: 'blue-large'
  117. },
  118. roomcode:{
  119. type:String,
  120. require:true
  121. },
  122. CustName:{
  123. type:String,
  124. require:true
  125. },
  126. roomid:{
  127. type:String,
  128. require:true
  129. }
  130. },
  131. computed:{
  132. mapFilterRSSI(){
  133. return (0 - this.filterRSSI)
  134. },
  135. filterDeviceList(){
  136. let devices = this.devicesList;
  137. let name = this.filterName;
  138. let rssi = this.filterRSSI;
  139. //按RSSI过滤
  140. let filterDevices1 = devices.filter((item)=>{
  141. return item.RSSI > rssi
  142. })
  143. // 按名字过滤
  144. let filterDevices2
  145. if(name!=''){
  146. filterDevices2 = filterDevices1.filter((item)=>{
  147. return (item.name.indexOf(name) >= 0 || item.deviceId.indexOf(name) >= 0)
  148. })
  149. }else{
  150. filterDevices2 = filterDevices1
  151. }
  152. // 根据广播数据提取MAC地址
  153. for (let i = 0; i < filterDevices2.length;i++) {
  154. if (filterDevices2[i].hasOwnProperty('advertisData')){
  155. if (filterDevices2[i].advertisData.byteLength == 8) {
  156. filterDevices2[i].advMac = util.buf2hex(filterDevices2[i].advertisData.slice(2, 7));
  157. }
  158. }
  159. }
  160. return filterDevices2
  161. }
  162. },
  163. mounted() {
  164. console.log('mounted')
  165. this.handlePrintTap(0)
  166. },
  167. beforeDestroy(){
  168. this.stopSearchBtnTap();
  169. },
  170. methods:{
  171. doNothing(){
  172. return;
  173. },
  174. //点击打印按钮,flag : true 如果已连接直接执行打印命令,false,不执行
  175. handlePrintTap(flag){
  176. //打开蓝牙适配器
  177. blesdk.openBlue().then((res)=>{
  178. //获取已连接设备
  179. blesdk.getConnectedBluetoothDevices().then((res)=>{
  180. const lastDevice = uni.getStorageSync('vuex_lastBuleDevice')
  181. console.log(lastDevice)
  182. console.log(res,this.deviceId,this.serviceId,this.writeId,this.onPrintSuccess)
  183. // 如果之前连接过设备
  184. if(lastDevice){
  185. // 有正在连接的设备,且和上次连接设备一样
  186. if(res.devices.length && res.devices[0].deviceId == lastDevice.deviceId){
  187. this.deviceId = lastDevice.deviceId
  188. this.serviceId = lastDevice.serviceId
  189. this.writeId = lastDevice.writeId
  190. this.readId = lastDevice.readId
  191. if(flag){
  192. this.isPrinting = true;
  193. this.$nextTick(()=>{
  194. this.$emit("startPrint",{
  195. deviceId: this.deviceId,
  196. serviceId: this.serviceId,
  197. writeId: this.writeId
  198. },tsc,blesdk)
  199. })
  200. }
  201. }else{
  202. // 重新直接连接
  203. this.handleConnectDevice(lastDevice)
  204. }
  205. }else{
  206. //若没有已连接设备,弹框搜索设备
  207. if(res.devices.length == 0){
  208. this.isShowSearch = true
  209. this.devicesList = []
  210. if(this.deviceId){
  211. this.closeConnect(this.deviceId)
  212. }
  213. }
  214. }
  215. }).catch((err)=>{
  216. blesdk.catchToast(err);
  217. })
  218. }).catch((err)=>{
  219. console.log(err)
  220. blesdk.catchToast(err);
  221. })
  222. },
  223. onGetDevice(res){
  224. this.devicesList = res;
  225. },
  226. handleSearchClose(){
  227. if(!this.isConnecting){
  228. this.isShowSearch = false
  229. this.stopSearchBtnTap()
  230. }
  231. },
  232. handleSRRIChange(e){
  233. this.filterRSSI = e.detail.value
  234. },
  235. //开始搜索设备
  236. searchBtnTap(){
  237. blesdk.startBluetoothDevicesDiscovery();
  238. this.isSearching = true;
  239. blesdk.onfindBlueDevices(this.onGetDevice)
  240. },
  241. //停止搜索设备
  242. stopSearchBtnTap(){
  243. blesdk.stopBlueDevicesDiscovery();
  244. this.isSearching = false;
  245. },
  246. //点击连接设备
  247. handleConnectDevice(device){
  248. const _this = this
  249. let deviceId = device.deviceId;
  250. let name = device.name;
  251. this.deviceId = deviceId;
  252. console.log('deviceId',this.deviceId)
  253. this.isConnecting = true
  254. this.stopSearchBtnTap()
  255. uni.onBLEConnectionStateChange(function(res){
  256. console.log('连接',res)
  257. if(res.connected){
  258. plus.nativeUI.toast('设备'+ res.deviceId + '已连接',{
  259. verticalAlign:'center'
  260. })
  261. }else{
  262. _this.closeConnect(deviceId)
  263. plus.nativeUI.toast('设备'+ res.deviceId + '已断开连接',{
  264. verticalAlign:'center'
  265. })
  266. }
  267. _this.isConnecting = false
  268. })
  269. blesdk.createBLEConnection(deviceId, this.onConnectSuccess, this.onConnectFail);
  270. },
  271. onConnectSuccess(res){
  272. this.stopSearchBtnTap()
  273. this.isConnecting = false
  274. blesdk.getBLEDeviceServices(this.deviceId, this.onGetServicesSuccess, this.onGetServicesFail);
  275. },
  276. onConnectFail(err){
  277. console.log('链接失败',err)
  278. this.isConnecting = false
  279. blesdk.catchToast(err.res)
  280. },
  281. onGetServicesSuccess(res){
  282. console.log('获取服务',res)
  283. this.services = res.serviceId;
  284. blesdk.getDeviceCharacteristics(this.deviceId, this.services, this.onGetCharacterSuccess, this.onGetCharacterFail);
  285. },
  286. onGetServicesFail(err){
  287. console.log('获取服务失败')
  288. },
  289. onGetCharacterSuccess(res){
  290. console.log('获取特征值成功',res)
  291. this.serviceId = res.serviceId;
  292. this.writeId = res.writeId;
  293. this.readId = res.readId;
  294. this.isShowSearch = false;
  295. try {
  296. uni.setStorageSync('vuex_lastBuleDevice', {deviceId:this.deviceId,serviceId:this.serviceId,writeId:this.writeId,readId:this.readId});
  297. } catch (e) {
  298. // error
  299. }
  300. },
  301. onGetCharacterFail(err){
  302. console.log('特征值获取失败')
  303. },
  304. onPrintSuccess(){
  305. this.isPrinting = false;
  306. console.log('打印成功')
  307. // this.$emit('onPrintSuccess')
  308. },
  309. onPrintFail(){
  310. console.log('打印失败')
  311. this.isPrinting = false;
  312. },
  313. closeConnect(){
  314. blesdk.closeBLEConnection(this.deviceId)
  315. },
  316. }
  317. }
  318. </script>
  319. <style lang="scss" scoped>
  320. .kk-printer{
  321. &-btn{
  322. width:100%;
  323. height:100%;
  324. }
  325. .kk-shadow{
  326. display: none;
  327. &.show{
  328. display: block;
  329. width:100vw;
  330. height:100vh;
  331. background: rgba(0,0,0,0.4);
  332. position: fixed;
  333. top: 0;
  334. left: 0;
  335. display: flex;
  336. justify-content: center;
  337. align-items: center;
  338. z-index: 10000;
  339. .kk-modal{
  340. width:680upx;
  341. height: 60%;
  342. padding:24upx;
  343. box-sizing: border-box;
  344. overflow-y: auto;
  345. border-radius: 20upx;
  346. background: #ffffff;
  347. display: flex;
  348. justify-content: center;
  349. align-items: center;
  350. .kk-search-device{
  351. width:100%;
  352. height: 100%;
  353. .kk-filter-wrap{
  354. width:100%;
  355. height: 200upx;
  356. display: flex;
  357. flex-direction: column;
  358. justify-content: flex-start;
  359. align-items: flex-start;
  360. .filter-title{
  361. line-height: 70upx;
  362. font-size: 30upx;
  363. color: #999999;
  364. }
  365. &>slider{
  366. width:90%;
  367. height: 90upx;
  368. }
  369. &>input{
  370. padding:0 20upx;
  371. box-sizing: border-box;
  372. border-radius: 10upx;
  373. height: 90upx;
  374. width:100%;
  375. border: 1upx solid #ebebeb;
  376. }
  377. }
  378. .kk-btn-wrap{
  379. width:100%;
  380. height: 140upx;
  381. display: flex;
  382. justify-content: space-between;
  383. align-items: center;
  384. &>view{
  385. flex:1 1 auto;
  386. height: 80upx;
  387. line-height: 80upx;
  388. border-radius: 100upx;
  389. text-align: center;
  390. color:#ffffff;
  391. &.confirm-btn{
  392. background: #007AFF;
  393. margin-right:30upx;
  394. }
  395. &:nth-last-child(1){
  396. background: #DD524D;
  397. }
  398. }
  399. }
  400. .kk-devices-wrap{
  401. height: calc(100% - 460upx);
  402. overflow-y:auto;
  403. padding:10upx 20upx;
  404. box-sizing: border-box;
  405. border: 1upx solid #ebebeb;
  406. box-sizing: border-box;
  407. border-radius: 20upx;
  408. .empty-wrap{
  409. width:100%;
  410. height: 100%;
  411. display: flex;
  412. flex-direction: column;
  413. justify-content: center;
  414. align-items: center;
  415. .empty-icon{
  416. width:268upx;
  417. height: 240upx;
  418. background: url('./empty-icon.png') no-repeat;
  419. background-size:100% 100%;
  420. margin-bottom: 26upx;
  421. }
  422. .empty-text{
  423. width: 100%;
  424. line-height: 50upx;
  425. font-size: 30upx;
  426. text-align: center;
  427. color: #999999;
  428. }
  429. }
  430. .kk-devices-item{
  431. width:100%;
  432. border-bottom: 1upx solid #ebebeb;
  433. padding:10upx 0;
  434. box-sizing: border-box;
  435. &:nth-last-child(1){
  436. border-bottom: none;
  437. }
  438. > view{
  439. &:first-child{
  440. flex-grow: 1;
  441. width: 80%;
  442. }
  443. }
  444. button{
  445. width: auto;
  446. }
  447. }
  448. }
  449. }
  450. }
  451. }
  452. }
  453. }
  454. .kk-placeholder-class{
  455. font-size: 30upx;
  456. color:#999999;
  457. }
  458. </style>