index.vue 14 KB

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