This commit is contained in:
@@ -18,7 +18,8 @@
|
|||||||
},
|
},
|
||||||
/* 模块配置 */
|
/* 模块配置 */
|
||||||
"modules" : {
|
"modules" : {
|
||||||
"Barcode" : {}
|
"Barcode" : {},
|
||||||
|
"Camera" : {}
|
||||||
},
|
},
|
||||||
/* 应用发布信息 */
|
/* 应用发布信息 */
|
||||||
"distribute" : {
|
"distribute" : {
|
||||||
|
|||||||
7
package-lock.json
generated
7
package-lock.json
generated
@@ -11,6 +11,7 @@
|
|||||||
"clipboard": "^2.0.11",
|
"clipboard": "^2.0.11",
|
||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.19",
|
||||||
"html5-qrcode": "^2.3.8",
|
"html5-qrcode": "^2.3.8",
|
||||||
|
"jsqr": "^1.4.0",
|
||||||
"uview-plus": "^3.6.29",
|
"uview-plus": "^3.6.29",
|
||||||
"vue": "^3.4.0"
|
"vue": "^3.4.0"
|
||||||
},
|
},
|
||||||
@@ -5103,6 +5104,12 @@
|
|||||||
"graceful-fs": "^4.1.6"
|
"graceful-fs": "^4.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jsqr": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/jsqr/-/jsqr-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-dxLob7q65Xg2DvstYkRpkYtmKm2sPJ9oFhrhmudT1dZvNFFTlroai3AWSpLey/w5vMcLBXRgOJsbXpdN9HzU/A==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/lcid": {
|
"node_modules/lcid": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmmirror.com/lcid/-/lcid-3.1.1.tgz",
|
"resolved": "https://registry.npmmirror.com/lcid/-/lcid-3.1.1.tgz",
|
||||||
|
|||||||
@@ -16,10 +16,11 @@
|
|||||||
"@dcloudio/uni-app": "3.0.0-4080720251210001",
|
"@dcloudio/uni-app": "3.0.0-4080720251210001",
|
||||||
"@dcloudio/uni-components": "3.0.0-4080720251210001",
|
"@dcloudio/uni-components": "3.0.0-4080720251210001",
|
||||||
"@dcloudio/uni-h5": "3.0.0-4080720251210001",
|
"@dcloudio/uni-h5": "3.0.0-4080720251210001",
|
||||||
"vue": "^3.4.0",
|
|
||||||
"clipboard": "^2.0.11",
|
"clipboard": "^2.0.11",
|
||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.19",
|
||||||
"html5-qrcode": "^2.3.8",
|
"html5-qrcode": "^2.3.8",
|
||||||
"uview-plus": "^3.6.29"
|
"jsqr": "^1.4.0",
|
||||||
|
"uview-plus": "^3.6.29",
|
||||||
|
"vue": "^3.4.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,25 +62,32 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-if="showScanner" class="scanner-overlay">
|
<view v-if="showScanner" class="scanner-overlay">
|
||||||
<view class="scanner-container">
|
<view class="scanner-container">
|
||||||
<view class="scanner-header">
|
<view class="scanner-header">
|
||||||
<text>扫一扫</text>
|
<text>扫一扫</text>
|
||||||
<text class="close-btn" @tap="closeScanner">×</text>
|
<text class="close-btn" @tap="closeScanner">×</text>
|
||||||
|
</view>
|
||||||
|
<view class="scanner-body">
|
||||||
|
<input type="file" accept="image/*" capture="environment" @change="onFileSelected" class="file-input" id="file-input" />
|
||||||
|
<view class="scan-prompt">
|
||||||
|
<image src="/static/scan-code.png" mode="aspectFit" class="scan-icon-large"></image>
|
||||||
|
<text class="tip">点击下方按钮选择相机拍照</text>
|
||||||
|
<label for="file-input" class="btn-take-photo">拍照/选择图片</label>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="scanner-tip">将二维码放入框内即可自动扫描</view>
|
||||||
</view>
|
</view>
|
||||||
<view id="scanner-region" class="scanner-region"></view>
|
|
||||||
<view class="scanner-tip">将二维码放入框内即可自动扫描</view>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, onUnmounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { authApi } from '@/api/index.js';
|
import { authApi } from '@/api/index.js';
|
||||||
import { useUserStore } from '@/store/index.js';
|
import { useUserStore } from '@/store/index.js';
|
||||||
import { APP_ID, USE_WECHAT_AUTH } from '@/utils/env.js';
|
import { APP_ID, USE_WECHAT_AUTH } from '@/utils/env.js';
|
||||||
import { Html5Qrcode } from 'html5-qrcode';
|
import jsQR from 'jsqr';
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
||||||
@@ -93,19 +100,12 @@
|
|||||||
getPathDeviceId();
|
getPathDeviceId();
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
if (html5QrCode) {
|
|
||||||
html5QrCode.stop().catch(() => {});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const identifier = ref('');
|
const identifier = ref('');
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const agreed = ref(true);
|
const agreed = ref(true);
|
||||||
const inputFocus = ref(false);
|
const inputFocus = ref(false);
|
||||||
const showError = ref(false);
|
const showError = ref(false);
|
||||||
const showScanner = ref(false);
|
const showScanner = ref(false);
|
||||||
let html5QrCode = null;
|
|
||||||
|
|
||||||
const getCode = () => {
|
const getCode = () => {
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
@@ -147,17 +147,26 @@
|
|||||||
|
|
||||||
const handleScanLogin = async () => {
|
const handleScanLogin = async () => {
|
||||||
showScanner.value = true;
|
showScanner.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
const onFileSelected = async (e) => {
|
||||||
html5QrCode = new Html5Qrcode('scanner-region');
|
const file = e.target.files[0];
|
||||||
await html5QrCode.start(
|
if (!file) return;
|
||||||
{ facingMode: 'environment' },
|
|
||||||
{
|
const reader = new FileReader();
|
||||||
fps: 10,
|
reader.onload = (event) => {
|
||||||
qrbox: { width: 250, height: 250 }
|
const img = new Image();
|
||||||
},
|
img.onload = () => {
|
||||||
(decodedText) => {
|
const canvas = document.createElement('canvas');
|
||||||
const match = decodedText.match(/=([^=]+)$/);
|
canvas.width = img.width;
|
||||||
|
canvas.height = img.height;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.drawImage(img, 0, 0);
|
||||||
|
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
|
const code = jsQR(imageData.data, imageData.width, imageData.height);
|
||||||
|
|
||||||
|
if (code) {
|
||||||
|
const match = code.data.match(/=([^=]+)$/);
|
||||||
if (match && match[1]) {
|
if (match && match[1]) {
|
||||||
identifier.value = match[1];
|
identifier.value = match[1];
|
||||||
closeScanner();
|
closeScanner();
|
||||||
@@ -165,24 +174,18 @@
|
|||||||
} else {
|
} else {
|
||||||
uni.showToast({ title: '无效的二维码', icon: 'none' });
|
uni.showToast({ title: '无效的二维码', icon: 'none' });
|
||||||
}
|
}
|
||||||
},
|
} else {
|
||||||
() => {}
|
uni.showToast({ title: '无法识别二维码', icon: 'none' });
|
||||||
);
|
}
|
||||||
} catch (err) {
|
};
|
||||||
console.error('扫码失败', err);
|
img.src = event.target.result;
|
||||||
uni.showToast({ title: '无法访问摄像头', icon: 'none' });
|
};
|
||||||
closeScanner();
|
reader.readAsDataURL(file);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeScanner = async () => {
|
const closeScanner = async () => {
|
||||||
showScanner.value = false;
|
showScanner.value = false;
|
||||||
if (html5QrCode) {
|
document.getElementById('file-input').value = '';
|
||||||
try {
|
|
||||||
await html5QrCode.stop();
|
|
||||||
} catch (e) {}
|
|
||||||
html5QrCode = null;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPathDeviceId = () => {
|
const getPathDeviceId = () => {
|
||||||
@@ -546,4 +549,43 @@
|
|||||||
font-size: 26rpx;
|
font-size: 26rpx;
|
||||||
color: var(--text-tertiary);
|
color: var(--text-tertiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.scanner-body {
|
||||||
|
padding: 60rpx 40rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scan-prompt {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scan-icon-large {
|
||||||
|
width: 160rpx;
|
||||||
|
height: 160rpx;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-take-photo {
|
||||||
|
padding: 24rpx 48rpx;
|
||||||
|
background: var(--primary);
|
||||||
|
color: var(--text-inverse);
|
||||||
|
border-radius: var(--radius-medium);
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user