index.vue 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. <template>
  2. <view class="page">
  3. <view class="camera-area">
  4. <camera
  5. v-if="!isDevTool"
  6. device-position="back"
  7. mode="scanCode"
  8. :flash="cameraFlash"
  9. @scancode="debounceScan"
  10. @ready="handleReady"
  11. @error="handleError"
  12. :style="{ width: '100%', height: `${cameraHeight}px` }"
  13. ></camera>
  14. <view
  15. v-else
  16. class="mock-camera"
  17. :style="{ width: '100%', height: `${cameraHeight}px` }"
  18. ></view>
  19. </view>
  20. <view class="action-bar">
  21. <cover-view class="flash-toggle" @click="toggleFlash">
  22. <cover-image
  23. style="width: 28px; height: 28px; margin: 0 auto"
  24. src="/static/scan/scan-code-light.png"
  25. ></cover-image>
  26. <cover-view style="color: rgba(255,255,255,0.7); font-size: 13px; margin-top: 10px;">
  27. {{ cameraFlash === "off" ? "轻触照亮" : "轻触关闭" }}
  28. </cover-view>
  29. </cover-view>
  30. <!-- <view class="action-buttons">
  31. <view class="action-btn" @click="chooseImage">
  32. <image class="action-icon" mode="widthFix" src="/static/scan/scan-code-album.png"></image>
  33. <view class="action-label">打开相册</view>
  34. </view>
  35. </view> -->
  36. </view>
  37. </view>
  38. </template>
  39. <script setup lang="ts">
  40. import { ref } from "vue";
  41. import { isDevTool } from "@/utils/device";
  42. import { onLoad } from "@dcloudio/uni-app";
  43. import { debounce } from "@/utils/common";
  44. const cameraHeight = ref(0);
  45. const cameraFlash = ref("off");
  46. onLoad(() => {
  47. const windowInfo = uni.getWindowInfo();
  48. const rpxRatio = windowInfo.windowWidth / 750;
  49. const actionBarH = 222 * rpxRatio;
  50. const systemInfo = uni.getSystemInfoSync();
  51. let safeBottom = 0;
  52. if (systemInfo.safeAreaInsets) {
  53. safeBottom = systemInfo.safeAreaInsets.bottom || 0;
  54. } else if (systemInfo.safeArea) {
  55. safeBottom = systemInfo.screenHeight - systemInfo.safeArea.bottom;
  56. }
  57. cameraHeight.value = windowInfo.windowHeight - actionBarH - safeBottom;
  58. });
  59. const handleReady = () => {
  60. // 相机就绪,页面已可正常使用
  61. };
  62. const handleScancode = (e: any) => {
  63. if (!e.detail || !e.detail.result) return;
  64. const shortId = e.detail.result.split("#")[1];
  65. uni.vibrateShort({});
  66. uni.navigateTo({
  67. url: '/pages-wash/device/index?shortId=' + shortId
  68. });
  69. };
  70. const debounceScan = debounce(handleScancode, 2000);
  71. const handleError = (e: any) => {
  72. if (e.detail && e.detail.errMsg && /auth/.test(e.detail.errMsg)) {
  73. uni.showModal({
  74. content: "请打开摄像头权限开关",
  75. success: () => {
  76. uni.openSetting();
  77. },
  78. });
  79. return;
  80. }
  81. uni.showModal({
  82. content: e.detail.errMsg,
  83. });
  84. };
  85. const toggleFlash = () => {
  86. cameraFlash.value = cameraFlash.value === "off" ? "on" : "off";
  87. };
  88. const chooseImage = () => {
  89. uni.chooseImage({
  90. count: 1,
  91. sizeType: ["compressed"],
  92. sourceType: ["album"],
  93. success: () => {
  94. uni.showLoading({ title: "正在识别" });
  95. },
  96. fail: (err) => {
  97. if (/cancel/.test(err.errMsg)) return;
  98. uni.showModal({ content: err.errMsg || "出现错误,请重试" });
  99. },
  100. });
  101. };
  102. </script>
  103. <style lang="scss" scoped>
  104. .page {
  105. position: relative;
  106. height: 100vh;
  107. background-color: #0d0d0d;
  108. overflow: hidden;
  109. }
  110. .camera-area {
  111. position: relative;
  112. overflow: hidden;
  113. }
  114. .mock-camera {
  115. background-color: $uni-text-color-tertiary;
  116. }
  117. .action-bar {
  118. position: relative;
  119. box-sizing: content-box;
  120. padding-bottom: constant(safe-area-inset-bottom);
  121. padding-bottom: env(safe-area-inset-bottom);
  122. }
  123. .flash-toggle {
  124. text-align: center;
  125. position: absolute;
  126. width: 100%;
  127. left: 0;
  128. top: -96px;
  129. }
  130. .action-buttons {
  131. height: 222rpx;
  132. display: flex;
  133. }
  134. .action-btn {
  135. width: 50%;
  136. height: 222rpx;
  137. display: flex;
  138. flex-direction: column;
  139. align-items: center;
  140. justify-content: center;
  141. }
  142. .action-icon {
  143. width: 96rpx;
  144. }
  145. .action-label {
  146. font-size: 24rpx;
  147. color: #fff;
  148. margin-top: 20rpx;
  149. }
  150. </style>