|
@@ -0,0 +1,219 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <view class="wrap pr">
|
|
|
|
|
+ <view class="w-full pa tw-top-0 tw-left-0 tw-z-0">
|
|
|
|
|
+ <image class="w-full" :src="config.ossPathPerfixs + '/index-bg.png'" mode="widthFix"></image>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="w-full pr tw-z-10">
|
|
|
|
|
+ <view class="tw-p-[30rpx]">
|
|
|
|
|
+ <view class="tw-flex tw-items-center tw-justify-start tw-mt-[60rpx]">
|
|
|
|
|
+ <fs-avatar size="100rpx" src="/static/images/tool/logo.png"></fs-avatar>
|
|
|
|
|
+ <text class="tw-text-[#fff] tw-text-[26rpx] tw-ml-[4rpx]"
|
|
|
|
|
+ style="letter-spacing: 1rpx;">太原市中小学学生卫生保健所</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="tw-mt-[20rpx] tw-text-[#fff] tw-text-[32rpx] tw-font-bold">
|
|
|
|
|
+ 学校:{{ schoolName }}
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="tw-px-[30rpx] tw-mt-[40rpx] tw-pb-[100rpx]">
|
|
|
|
|
+ <view class="type-list">
|
|
|
|
|
+ <view class="type-card" @click="handleManualInput">
|
|
|
|
|
+ <view class="type-name">手工录入</view>
|
|
|
|
|
+ <van-icon name="arrow" />
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="type-card" @click="handleScanInput">
|
|
|
|
|
+ <view class="type-name">扫码录入</view>
|
|
|
|
|
+ <van-icon name="arrow" />
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <van-popup v-model:show="showScanner" position="center" :style="{ width: '90%', height: '80%' }">
|
|
|
|
|
+ <view class="scanner-popup">
|
|
|
|
|
+ <view class="scanner-header">
|
|
|
|
|
+ <text>扫描二维码</text>
|
|
|
|
|
+ <van-icon name="cross" @click="closeScanner" />
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="scanner-body">
|
|
|
|
|
+ <video ref="videoRef" class="scanner-video" autoplay playsinline></video>
|
|
|
|
|
+ <canvas ref="canvasRef" style="display: none;"></canvas>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </van-popup>
|
|
|
|
|
+ </view>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script setup>
|
|
|
|
|
+import config from '@/utils/config'
|
|
|
|
|
+import { getSchoolInfo } from '@/services/common'
|
|
|
|
|
+import jsQR from 'jsqr'
|
|
|
|
|
+
|
|
|
|
|
+const STORAGE_KEY = 'eye_examine_user_info'
|
|
|
|
|
+const schoolName = ref('')
|
|
|
|
|
+const showScanner = ref(false)
|
|
|
|
|
+const videoRef = ref(null)
|
|
|
|
|
+const canvasRef = ref(null)
|
|
|
|
|
+let stream = null
|
|
|
|
|
+let animationId = null
|
|
|
|
|
+
|
|
|
|
|
+onLoad(async () => {
|
|
|
|
|
+ const savedData = uni.getStorageSync(STORAGE_KEY)
|
|
|
|
|
+ if (savedData) {
|
|
|
|
|
+ const userInfo = JSON.parse(savedData)
|
|
|
|
|
+ const schoolId = userInfo.schoolId || ''
|
|
|
|
|
+
|
|
|
|
|
+ if (schoolId) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await getSchoolInfo(schoolId)
|
|
|
|
|
+ if (res && res.code === 200) {
|
|
|
|
|
+ schoolName.value = res.data.name || ''
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('获取学校信息失败', error)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const handleManualInput = () => {
|
|
|
|
|
+ uni.navigateTo({
|
|
|
|
|
+ url: '/pages/index/eye-examine-index'
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const handleScanInput = async () => {
|
|
|
|
|
+ showScanner.value = true
|
|
|
|
|
+ await nextTick()
|
|
|
|
|
+ startCamera()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const startCamera = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ stream = await navigator.mediaDevices.getUserMedia({
|
|
|
|
|
+ video: { facingMode: 'environment' }
|
|
|
|
|
+ })
|
|
|
|
|
+ const video = videoRef.value
|
|
|
|
|
+ if (video) {
|
|
|
|
|
+ video.srcObject = stream
|
|
|
|
|
+ video.play()
|
|
|
|
|
+ scanQRCode()
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('摄像头启动失败', error)
|
|
|
|
|
+ uni.showToast({
|
|
|
|
|
+ title: '摄像头启动失败',
|
|
|
|
|
+ icon: 'none'
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const scanQRCode = () => {
|
|
|
|
|
+ const video = videoRef.value
|
|
|
|
|
+ const canvas = canvasRef.value
|
|
|
|
|
+
|
|
|
|
|
+ if (!video || !canvas) return
|
|
|
|
|
+
|
|
|
|
|
+ const ctx = canvas.getContext('2d')
|
|
|
|
|
+
|
|
|
|
|
+ const scan = () => {
|
|
|
|
|
+ if (video.readyState === video.HAVE_ENOUGH_DATA) {
|
|
|
|
|
+ canvas.width = video.videoWidth
|
|
|
|
|
+ canvas.height = video.videoHeight
|
|
|
|
|
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
|
|
|
|
|
+
|
|
|
|
|
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
|
|
|
|
|
+ const code = jsQR(imageData.data, imageData.width, imageData.height)
|
|
|
|
|
+
|
|
|
|
|
+ if (code) {
|
|
|
|
|
+ onDecode(code.data)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ animationId = requestAnimationFrame(scan)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ scan()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const onDecode = (result) => {
|
|
|
|
|
+ closeScanner()
|
|
|
|
|
+ console.log('扫码结果:', result)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const closeScanner = () => {
|
|
|
|
|
+ if (animationId) {
|
|
|
|
|
+ cancelAnimationFrame(animationId)
|
|
|
|
|
+ animationId = null
|
|
|
|
|
+ }
|
|
|
|
|
+ if (stream) {
|
|
|
|
|
+ stream.getTracks().forEach(track => track.stop())
|
|
|
|
|
+ stream = null
|
|
|
|
|
+ }
|
|
|
|
|
+ showScanner.value = false
|
|
|
|
|
+}
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style lang="scss" scoped>
|
|
|
|
|
+.type-list {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 20rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.type-card {
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ border-radius: 20rpx;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ padding: 50rpx 30rpx;
|
|
|
|
|
+ box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.type-name {
|
|
|
|
|
+ font-size: 32rpx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.type-card :deep(.van-icon) {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ right: 30rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.scanner-popup {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ background: #000;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.scanner-header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ padding: 20rpx;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.scanner-body {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.scanner-video {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ object-fit: cover;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+</style>
|