index.vue 12 KB

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