| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- <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>
|