index.vue 14 KB

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